search_knowledge
Search a custom knowledge base to find relevant information chunks based on your query, returning the most pertinent results for your needs.
Instructions
Search the knowledge base for relevant information. Returns the most relevant chunks based on your query.
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| query | Yes | The search query (1-500 characters) | |
| top_k | No | Number of results to return (default: 5, max: 20) |
Implementation Reference
- src/search.ts:83-127 (handler)The handler function `textSearch` implements the actual search logic, performing keyword matching against chunks and calculating relevance scores.
export function textSearch( query: string, topK: number = 5 ): SearchResult[] { const knowledge = getKnowledge(); const queryLower = query.toLowerCase(); const queryTerms = queryLower.split(/\s+/).filter((term) => term.length > 2); const results: SearchResult[] = []; for (const chunk of knowledge.chunks) { const contentLower = chunk.content.toLowerCase(); const titleLower = chunk.title.toLowerCase(); const categoryLower = chunk.category.toLowerCase(); // Calculate match score based on term frequency let score = 0; for (const term of queryTerms) { if (titleLower.includes(term)) score += 3; // Title matches are worth more if (categoryLower.includes(term)) score += 2; // Count occurrences in content (safe, no regex) score += countOccurrences(contentLower, term); } // Check for exact phrase match if (contentLower.includes(queryLower)) { score += 5; } if (score > 0) { results.push({ id: chunk.id, title: chunk.title, category: chunk.category, content: chunk.content, score: score, }); } } // Sort by score descending and take top K results.sort((a, b) => b.score - a.score); return results.slice(0, topK); } - src/index.ts:21-80 (registration)The "search_knowledge" tool is registered with the MCP server, defining its schema and calling the textSearch handler.
server.tool( "search_knowledge", "Search the knowledge base for relevant information. Returns the most relevant chunks based on your query.", { query: z.string().min(1).max(500).describe("The search query (1-500 characters)"), top_k: z .number() .min(1) .max(20) .default(5) .describe("Number of results to return (default: 5, max: 20)"), }, async ({ query, top_k }) => { try { const results: SearchResult[] = textSearch(query, top_k); if (results.length === 0) { return { content: [ { type: "text" as const, text: `No results found for query: "${query}"`, }, ], }; } const formattedResults = results.map((result, index) => { return `## Result ${index + 1}: ${result.title} **Category:** ${result.category} **Chunk ID:** ${result.id} **Relevance Score:** ${result.score.toFixed(2)} ${result.content} `; }); return { content: [ { type: "text" as const, text: `Found ${results.length} relevant results for: "${query}"\n\n${formattedResults.join("\n---\n\n")}`, }, ], }; } catch (error) { const errorMessage = error instanceof Error ? error.message : "Unknown error"; return { content: [ { type: "text" as const, text: `Error searching knowledge base: ${errorMessage}`, }, ], isError: true, }; } } );