search_tool_bm25
Search the hidden tool-discovery index and activate the top matches for the current session.
Source
- Entry:
packages/coding-agent/src/tools/search-tool-bm25.ts - Model-facing prompt:
packages/coding-agent/src/prompts/tools/search-tool-bm25.md - Key collaborators:
packages/coding-agent/src/tool-discovery/tool-index.ts— discoverable-tool metadata and BM25 index/search.packages/coding-agent/src/session/agent-session.ts— session discovery mode, corpus assembly, activation, cache invalidation.packages/coding-agent/src/sdk.ts— initial hiding of discoverable built-ins and prompt-time discoverable summary.packages/coding-agent/src/tools/index.ts— tool-session discovery hooks, essential/discoverable load modes, registry wiring.packages/coding-agent/src/config/settings-schema.ts—tools.discoveryModeand legacymcp.discoveryModesettings.
Inputs
| Field | Type | Required | Description |
|---|---|---|---|
query |
string |
Yes | Natural-language or keyword query. Trimmed before search; empty-after-trim is rejected. |
limit |
integer |
No | Max matches to return and activate. Minimum 1. Defaults to 8 (DEFAULT_LIMIT). |
Outputs
- Single-shot
AgentToolResult. - Model-visible
contentis one text part containing JSON with:
{"query":"...","activated_tools":["..."],"match_count":2,"total_tools":17}
- Runtime-only
detailscarries the ranked matches used by the TUI renderer:query,limit,total_toolsactivated_tools: tool names activated by this callactive_selected_tools: cumulative discovered-tool selections still activetools: array of match objects withnamelabeldescription(tool.summary; this is the only snippet-like field)- optional
server_name - optional
mcp_tool_name schema_keysscorerounded to 6 decimals
- The renderer shows a status line plus up to 5 collapsed tree items by default (
COLLAPSED_MATCH_LIMIT), each with label, optional server name, score to 3 decimals, and truncated description. The ranked match list is not serialized intocontent.
Flow
SearchToolBm25Tool.createIf()inpackages/coding-agent/src/tools/search-tool-bm25.tsexposes the tool only whentools.discoveryModeis set to a non-"off"value or legacymcp.discoveryMode === true, and only if the session implements the discovery hooks.descriptionis rendered frompackages/coding-agent/src/prompts/tools/search-tool-bm25.mdviarenderSearchToolBm25Description(), using the current discoverable-tool list plus per-server summary/count.execute()re-checks capability and settings:- missing discovery hooks ->
ToolError("Tool discovery is unavailable in this session.") - discovery disabled ->
ToolError("Tool discovery is disabled. Enable tools.discoveryMode or mcp.discoveryMode to use search_tool_bm25.")
- missing discovery hooks ->
queryis trimmed and validated;limitis defaulted/validated.getDiscoverableToolSearchIndexForExecution()fetches the cached generic search index from the session when available, otherwise falls back to the legacy MCP cache, otherwise rebuilds an index from the current discoverable-tool list.getSelectedToolNames()reads the current discovered selections so already-selected tools can be excluded from fresh results.searchDiscoverableTools()inpackages/coding-agent/src/tool-discovery/tool-index.tstokenizes the query, scores every document with BM25, sorts by descending score thentool.name, and returns up tosearchIndex.documents.lengthresults;execute()then filters already-selected names and slices tolimit.- If any matches remain,
activateTools()activates all matched tool names throughsession.activateDiscoveredTools()or legacyactivateDiscoveredMCPTools(). detailsis assembled from the activated names, current selected names, corpus size, and formatted matches;contentis reduced to the compact JSON summary frombuildSearchToolBm25Content().searchToolBm25Rendererrenders either:
- the structured
detailsview, or - a fallback text-only warning block if
detailsis absent.
Modes / Variants
- Discovery-mode gating:
tools.discoveryMode = "all": searches hidden discoverable built-ins plus hidden MCP tools.tools.discoveryMode = "mcp-only": searches hidden MCP tools only.- legacy
mcp.discoveryMode = truewithtools.discoveryMode = "off": same as MCP-only.
- Search-index source:
- generic cached discoverable index from the session
- legacy cached MCP index, cast to the generic shape
- rebuilt ad hoc from the current discoverable-tool list if neither cache path works
- Activation backend:
- generic
activateDiscoveredTools() - legacy
activateDiscoveredMCPTools()fallback
- generic
Side Effects
- Session state
- Adds matched tools to the active session tool set through
activateDiscoveredTools()/activateDiscoveredMCPTools(). - Updates discovered-tool selection state so repeated searches accumulate selections instead of replacing them.
- Invalidates the cached discoverable search index when newly activated built-ins change the hidden corpus (
packages/coding-agent/src/session/agent-session.ts). - Tool availability changes before the next model call in the same turn; the prompt text says this explicitly.
- Adds matched tools to the active session tool set through
- User-visible prompts / interactive UI
- The tool description includes discoverable server summaries and total discoverable-tool count.
- The TUI renderer shows ranked matches, but the model-visible text summary does not.
Limits & Caps
- Default result cap:
8(DEFAULT_LIMITinpackages/coding-agent/src/tools/search-tool-bm25.ts). limitmust be a positive integer; no tool-level upper bound beyond corpus size.- Renderer collapsed list cap:
5(COLLAPSED_MATCH_LIMIT). - Renderer truncation widths:
- label:
72chars (MATCH_LABEL_LEN) - description:
96chars (MATCH_DESCRIPTION_LEN)
- label:
- BM25+ parameters in
packages/coding-agent/src/tool-discovery/tool-index.ts:BM25_K1 = 1.2BM25_B = 0.75BM25_DELTA = 1.0
- Weighted corpus fields (
FIELD_WEIGHTS):name:6label:4mcpToolName:4serverName:2summary:2- each
schemaKey:1
- Summary fallback length for discoverable metadata: first
200chars ofdescriptionwhen no explicit summary exists (getDiscoverableTool()inpackages/coding-agent/src/tool-discovery/tool-index.ts).
Errors
execute()throwsToolErrorfor unavailable discovery hooks, disabled discovery mode, empty trimmed query, and non-positive/non-integerlimit.searchDiscoverableTools()throwsError("Query must contain at least one letter or number.")if tokenization produces no letter/number tokens;execute()catchesErrorand rethrowsToolError(error.message).- Empty corpus is not an error; search returns
[], activation is skipped, and the renderer message becomes eitherNo discoverable tools are currently loaded.orNo matching tools found. getDiscoverableToolsForDescription()andgetDiscoverableToolSearchIndexForExecution()swallow discovery-hook/cache errors and fall back to an empty corpus or rebuilt index.
Notes
- The tool wire name stays
search_tool_bm25for persisted-session back-compat, even though the source file issearch-tool-bm25.ts. - Corpus composition is session-dependent and excludes already-active tools:
- MCP entries come from
#discoverableMCPTools, filtered to names not currently active, mapped withsummary = description. - Built-in entries appear only in
"all"mode and only for registry tools whoseloadMode === "discoverable"and are not currently active. - Hidden/internal built-ins are intentionally excluded from the built-in corpus:
resolve,yield,report_finding,report_tool_issueare called out in the#collectDiscoverableBuiltinTools()comment.
- MCP entries come from
DiscoverableToolSourceincludes"extension"and"custom", butAgentSession.getDiscoverableTools()currently assembles only built-in and MCP sources.- On startup,
packages/coding-agent/src/sdk.tshides non-essential discoverable built-ins intools.discoveryMode = "all"; defaults areread,bash, andeditunlesstools.essentialOverridechanges them. - Query tokenization is simple and deterministic: Unicode is NFKD-normalized, combining marks are dropped, acronym/camelCase and digit-to-capital boundaries are split, non-letter/non-number characters become spaces, tokens are lowercased, and only non-empty tokens survive.
- Scores are rounded differently by surface:
details.tools[].scorekeeps 6 decimals; the TUI line renders 3.