search_docs
Search MCP documentation by keywords or phrases to find relevant sections and context for development workflows, server building, and client implementation.
Instructions
Search through MCP documentation using keywords or phrases. Returns relevant documentation sections with context.
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| query | Yes | Search query - keywords, phrases, or specific concepts to find in the documentation | |
| category | No | Optional: limit search to specific documentation category | all |
Implementation Reference
- src/tools/searchDocs.ts:141-258 (handler)The complete search_docs tool object, including name, description, inputSchema reference, and the main execute handler that reads docs, searches, ranks, and formats results.export const searchDocs = { name: "search_docs", description: "Search through MCP documentation using keywords or phrases. Returns relevant documentation sections with context.", inputSchema: inputSchema.shape, async execute(args: z.infer<typeof inputSchema>) { const { query, category } = args; try { // Get path to scraped_docs directory const docsPath = docsDirectory; // Read all markdown files const files = readdirSync(docsPath).filter(file => file.endsWith('.md')); const results: SearchResult[] = []; for (const file of files) { const filePath = join(docsPath, file); const content = readFileSync(filePath, 'utf-8'); const fileCategory = categorizeFile(file); // Skip if category filter is specified and doesn't match if (category !== "all" && fileCategory !== category) { continue; } const relevance = calculateRelevance(content, query); // Only include files with some relevance if (relevance > 0) { const matches = findMatches(content, query); results.push({ file, title: extractTitle(content), content: content.substring(0, 500) + (content.length > 500 ? "..." : ""), category: fileCategory, relevance, matches }); } } // Sort by relevance results.sort((a, b) => b.relevance - a.relevance); if (results.length === 0) { return { content: [{ type: "text" as const, text: `No documentation found matching "${query}"${category !== "all" ? ` in category "${category}"` : ""}. Try: - Using different keywords - Searching in "all" categories - Using broader search terms - Checking spelling Available categories: getting_started, concepts, development, specification, tools, community` }] }; } // Format results let response = `# 🔍 Search Results for "${query}"\n\n`; response += `Found ${results.length} relevant document${results.length === 1 ? '' : 's'}`; if (category !== "all") { response += ` in category "${category}"`; } response += `:\n\n`; for (let i = 0; i < Math.min(results.length, 10); i++) { const result = results[i]; response += `## ${i + 1}. ${result.title}\n`; response += `**File**: \`${result.file}\` | **Category**: ${result.category} | **Relevance**: ${result.relevance.toFixed(0)}\n\n`; if (result.matches.length > 0) { response += `**Key matches:**\n`; for (const match of result.matches.slice(0, 3)) { response += `- Line ${match.line}: "${match.text.substring(0, 100)}${match.text.length > 100 ? '...' : ''}"\n`; } response += `\n`; } // Show snippet of content response += `**Preview**: ${result.content}\n\n`; response += `**Access full document**: Use resource \`mcp-docs://${result.file}\`\n\n`; response += `---\n\n`; } if (results.length > 10) { response += `*Showing top 10 results. ${results.length - 10} additional documents found.*\n\n`; } response += `## 💡 Tips\n`; response += `- Use \`get_docs_by_category\` to explore specific areas\n`; response += `- Access full documents using the resource URIs shown above\n`; response += `- Try more specific queries for better results\n`; return { content: [{ type: "text" as const, text: response }] }; } catch (error) { return { content: [{ type: "text" as const, text: `Error searching documentation: ${error instanceof Error ? error.message : String(error)}` }] }; } } };
- src/tools/searchDocs.ts:18-29 (schema)Zod input schema definition for the search_docs tool parameters.const inputSchema = z.object({ query: z.string().describe("Search query - keywords, phrases, or specific concepts to find in the documentation"), category: z.enum([ "all", "getting_started", "concepts", "development", "specification", "tools", "community" ]).default("all").describe("Optional: limit search to specific documentation category") });
- src/index.ts:125-154 (registration)Tool registration in the ListToolsRequestSchema handler, providing name, description, and static inputSchema for client discovery.{ name: "search_docs", description: "Search through MCP documentation using keywords or phrases. Returns relevant documentation sections with context.", inputSchema: { type: "object", properties: { query: { type: "string", description: "Search query - keywords, phrases, or specific concepts to find in the documentation", }, category: { type: "string", enum: [ "all", "getting_started", "concepts", "development", "specification", "tools", "community", ], description: "Optional: limit search to specific documentation category", default: "all", }, }, required: ["query"], }, },
- src/index.ts:192-193 (registration)Dispatcher switch case in CallToolRequestSchema handler that routes calls to search_docs.execute.case "search_docs": return await searchDocs.execute(args as any);
- src/tools/searchDocs.ts:79-113 (helper)Helper function to calculate relevance score of a document to the search query, used in ranking results.function calculateRelevance(content: string, query: string): number { const queryLower = query.toLowerCase(); const contentLower = content.toLowerCase(); let score = 0; // Title match (high weight) const title = extractTitle(content).toLowerCase(); if (title.includes(queryLower)) { score += 100; } // Exact phrase match (high weight) const exactMatches = (contentLower.match(new RegExp(queryLower, 'g')) || []).length; score += exactMatches * 10; // Individual word matches (medium weight) const queryWords = queryLower.split(/\s+/); for (const word of queryWords) { if (word.length > 2) { const wordMatches = (contentLower.match(new RegExp(`\\b${word}\\b`, 'g')) || []).length; score += wordMatches * 2; } } // Content length penalty (prefer more focused content) const contentLength = content.length; if (contentLength > 10000) { score *= 0.8; } else if (contentLength < 1000) { score *= 1.2; } return score; }