MCPcopy Index your code
hub / github.com/codeaashu/claude-code / searchToolsWithKeywords

Function searchToolsWithKeywords

src/tools/ToolSearchTool/ToolSearchTool.ts:186–302  ·  view source on GitHub ↗

* Keyword-based search over tool names and descriptions. * Handles both MCP tools (mcp__server__action) and regular tools (CamelCase). * * The model typically queries with: * - Server names when it knows the integration (e.g., "slack", "github") * - Action words when looking for functionality (

(
  query: string,
  deferredTools: Tools,
  tools: Tools,
  maxResults: number,
)

Source from the content-addressed store, hash-verified

184 * - Tool-specific terms (e.g., "notebook", "shell", "kill")
185 */
186async function searchToolsWithKeywords(
187 query: string,
188 deferredTools: Tools,
189 tools: Tools,
190 maxResults: number,
191): Promise<string[]> {
192 const queryLower = query.toLowerCase().trim()
193
194 // Fast path: if query matches a tool name exactly, return it directly.
195 // Handles models using a bare tool name instead of select: prefix (seen
196 // from subagents/post-compaction). Checks deferred first, then falls back
197 // to the full tool set — selecting an already-loaded tool is a harmless
198 // no-op that lets the model proceed without retry churn.
199 const exactMatch =
200 deferredTools.find(t => t.name.toLowerCase() === queryLower) ??
201 tools.find(t => t.name.toLowerCase() === queryLower)
202 if (exactMatch) {
203 return [exactMatch.name]
204 }
205
206 // If query looks like an MCP tool prefix (mcp__server), find matching tools.
207 // Handles models searching by server name with mcp__ prefix.
208 if (queryLower.startsWith('mcp__') && queryLower.length > 5) {
209 const prefixMatches = deferredTools
210 .filter(t => t.name.toLowerCase().startsWith(queryLower))
211 .slice(0, maxResults)
212 .map(t => t.name)
213 if (prefixMatches.length > 0) {
214 return prefixMatches
215 }
216 }
217
218 const queryTerms = queryLower.split(/\s+/).filter(term => term.length > 0)
219
220 // Partition into required (+prefixed) and optional terms
221 const requiredTerms: string[] = []
222 const optionalTerms: string[] = []
223 for (const term of queryTerms) {
224 if (term.startsWith('+') && term.length > 1) {
225 requiredTerms.push(term.slice(1))
226 } else {
227 optionalTerms.push(term)
228 }
229 }
230
231 const allScoringTerms =
232 requiredTerms.length > 0 ? [...requiredTerms, ...optionalTerms] : queryTerms
233 const termPatterns = compileTermPatterns(allScoringTerms)
234
235 // Pre-filter to tools matching ALL required terms in name or description
236 let candidateTools = deferredTools
237 if (requiredTerms.length > 0) {
238 const matches = await Promise.all(
239 deferredTools.map(async tool => {
240 const parsed = parseToolName(tool.name)
241 const description = await getToolDescriptionMemoized(tool.name, tools)
242 const descNormalized = description.toLowerCase()
243 const hintNormalized = tool.searchHint?.toLowerCase() ?? ''

Callers 1

callFunction · 0.85

Calls 4

compileTermPatternsFunction · 0.85
parseToolNameFunction · 0.85
getMethod · 0.65
pushMethod · 0.45

Tested by

no test coverage detected