tool_search can omit a deferred MCP tool even when the user/model query names that tool exactly.
In a real Codex session with codebase-memory-mcp configured and working, a broad discovery query like:
search_graph trace_path get_code_snippet query_graph search_code index_repository codebase-memory-mcp
did not surface search_graph in the first tool_search result set. A second exact-name query for:
did surface the tool.
That creates a false negative: the agent may conclude an MCP tool is unavailable when it is actually installed, callable, and discoverable through a more specific query.
This matters because deferred tool discovery is now a core path for MCP-heavy workflows. Agents are commonly instructed to search for a family of tools first, then fall back only if expected tools are unavailable. A top-N ranking miss makes that workflow unreliable and pushes agents toward worse fallbacks like shell search.
Likely root cause from main: codex-rs/core/src/tools/handlers/tool_search.rs ranks deferred tools through BM25 over ToolSearchEntry.search_text. ToolSearchEntry includes canonical/callable/raw names in the search text, but the handler does not guarantee that exact tool-name matches survive the requested/default result limit.
What steps can reproduce the bug?
- Configure an MCP server whose tools are deferred behind
tool_search.
- Include a tool with a specific callable name, for example
search_graph.
- Have enough deferred tools with overlapping descriptions/server vocabulary that top-N BM25 ranking is competitive.
- Run
tool_search with a broad discovery query that includes the exact tool name plus related tool names/server names, for example:
search_graph trace_path get_code_snippet query_graph search_code index_repository codebase-memory-mcp
- Observe that the exact named tool can be omitted from the first result set.
- Run
tool_search again with only the exact name:
- Observe that the tool is returned.
What is the expected behavior?
If a tool_search query contains an exact deferred tool name, callable name, or canonical MCP name, matching tools should be included ahead of lower-confidence BM25-only matches, while still respecting the requested/default result limit.
Exact-name matches should not require a second retry query to prove the tool exists.
Additional information
I investigated the current implementation and prototyped a small fix locally.
Prototype shape:
- Add normalized exact-match terms to
ToolSearchEntry.
- For MCP tools, exact terms include canonical MCP name, callable name, and raw tool name.
- For dynamic tools, exact terms include tool name and namespaced tool name.
- In
ToolSearchHandler, prepend exact-name matches before BM25 results, dedupe by entry id, then truncate to the requested/default limit.
- Preserve the existing
computer-use expanded default cap behavior.
Local validation:
cargo test -p codex-core tools::handlers::tool_search::tests
cargo clippy -p codex-core --lib
Results:
tool_search handler tests: 6 passed.
- clippy: no issues found.
I noticed open PR #19881 overlaps the deferred MCP / tool_search area. If maintainers prefer, this could be handled as a follow-up to that direction rather than as a standalone change.
I am happy to share the patch or open a PR if invited.
tool_searchcan omit a deferred MCP tool even when the user/model query names that tool exactly.In a real Codex session with
codebase-memory-mcpconfigured and working, a broad discovery query like:did not surface
search_graphin the firsttool_searchresult set. A second exact-name query for:did surface the tool.
That creates a false negative: the agent may conclude an MCP tool is unavailable when it is actually installed, callable, and discoverable through a more specific query.
This matters because deferred tool discovery is now a core path for MCP-heavy workflows. Agents are commonly instructed to search for a family of tools first, then fall back only if expected tools are unavailable. A top-N ranking miss makes that workflow unreliable and pushes agents toward worse fallbacks like shell search.
Likely root cause from
main:codex-rs/core/src/tools/handlers/tool_search.rsranks deferred tools through BM25 overToolSearchEntry.search_text.ToolSearchEntryincludes canonical/callable/raw names in the search text, but the handler does not guarantee that exact tool-name matches survive the requested/default result limit.What steps can reproduce the bug?
tool_search.search_graph.tool_searchwith a broad discovery query that includes the exact tool name plus related tool names/server names, for example:tool_searchagain with only the exact name:What is the expected behavior?
If a
tool_searchquery contains an exact deferred tool name, callable name, or canonical MCP name, matching tools should be included ahead of lower-confidence BM25-only matches, while still respecting the requested/default result limit.Exact-name matches should not require a second retry query to prove the tool exists.
Additional information
I investigated the current implementation and prototyped a small fix locally.
Prototype shape:
ToolSearchEntry.ToolSearchHandler, prepend exact-name matches before BM25 results, dedupe by entry id, then truncate to the requested/default limit.computer-useexpanded default cap behavior.Local validation:
cargo test -p codex-core tools::handlers::tool_search::tests cargo clippy -p codex-core --libResults:
tool_searchhandler tests: 6 passed.I noticed open PR
#19881overlaps the deferred MCP /tool_searcharea. If maintainers prefer, this could be handled as a follow-up to that direction rather than as a standalone change.I am happy to share the patch or open a PR if invited.