search_docs
Search Fumadocs documentation by keyword to find specific topics, features, or sections. Returns matching pages with titles, descriptions, and paths for integration assistance.
Instructions
Search Fumadocs documentation by keyword. Returns matching documentation pages with titles, descriptions, and paths. Use this to find specific topics or features.
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| query | Yes | Search query to find in documentation | |
| section | No | Filter results to a specific documentation section |
Implementation Reference
- src/tools/search-docs.ts:17-67 (handler)The main searchDocs handler function that processes search requests. It validates the query, calls the searchDocsIndex helper, formats results, and returns formatted output with up to 15 matches including suggestions when no results are found.export async function searchDocs(params: SearchDocsParams): Promise<string> { const { query, section } = params; if (!query || query.trim().length === 0) { return "Error: Please provide a search query."; } const sectionFilter = section === "all" ? undefined : (section as SectionId); const results = searchDocsIndex(query, sectionFilter); if (results.length === 0) { const suggestions = [ "No results found for: " + query, "", "Suggestions:", "- Try different keywords", "- Use more general terms", "- Check spelling", "", "Popular topics:", "- 'manual installation' - Setup guides for existing projects", "- 'components' - UI component documentation", "- 'search' - Search implementation", "- 'mdx' - MDX content source setup", ]; return suggestions.join("\n"); } const output: string[] = [ `# Search Results for "${query}"`, `Found ${results.length} result${results.length === 1 ? "" : "s"}${sectionFilter ? ` in ${SECTIONS[sectionFilter]}` : ""}:\n`, ]; // Limit to top 15 results const topResults = results.slice(0, 15); for (const entry of topResults) { output.push(`## ${entry.title}`); output.push(`**Section:** ${SECTIONS[entry.section as SectionId]} | **Path:** ${entry.path}`); output.push(`${entry.description}\n`); } if (results.length > 15) { output.push(`\n---\n*Showing top 15 of ${results.length} results. Refine your search for more specific results.*`); } output.push("\n---"); output.push("Use `get_page` with a path to fetch the full content of a documentation page."); return output.join("\n"); }
- src/tools/search-docs.ts:4-10 (schema)The searchDocsSchema definition using Zod for input validation. Defines a required 'query' string parameter and an optional 'section' enum parameter with values: all, cli, headless, framework, mdx, ui.export const searchDocsSchema = { query: z.string().describe("Search query to find in documentation"), section: z .enum(["all", "cli", "headless", "framework", "mdx", "ui"]) .optional() .describe("Filter results to a specific documentation section"), };
- src/tools/search-docs.ts:12-15 (schema)TypeScript type definition SearchDocsParams that defines the shape of parameters for the searchDocs function, including the query string and optional section filter.export type SearchDocsParams = { query: string; section?: "all" | SectionId; };
- src/index.ts:39-50 (registration)Tool registration with the MCP server. Registers 'search_docs' tool with description, schema, and handler that wraps the searchDocs function and returns results as text content.// Register search_docs tool server.tool( "search_docs", "Search Fumadocs documentation by keyword. Returns matching documentation pages with titles, descriptions, and paths. Use this to find specific topics or features.", searchDocsSchema, async (params) => { const result = await searchDocs(params); return { content: [{ type: "text", text: result }], }; } );
- src/data/docs-index.ts:478-535 (helper)The searchDocsIndex helper function that performs the actual search logic. It filters entries by section, scores matches based on title, description, keywords, and path, then returns sorted results by relevance score.export function searchDocsIndex( query: string, section?: SectionId ): DocEntry[] { const normalizedQuery = query.toLowerCase(); const queryWords = normalizedQuery.split(/\s+/); let results = DOCS_INDEX; // Filter by section if specified if (section) { results = results.filter((entry) => entry.section === section); } // Score and filter results const scored = results .map((entry) => { let score = 0; const titleLower = entry.title.toLowerCase(); const descLower = entry.description.toLowerCase(); for (const word of queryWords) { // Title matches (highest weight) if (titleLower.includes(word)) { score += 10; if (titleLower === word || titleLower.startsWith(word)) { score += 5; } } // Description matches if (descLower.includes(word)) { score += 5; } // Keyword matches for (const keyword of entry.keywords) { if (keyword.includes(word)) { score += 3; if (keyword === word) { score += 2; } } } // Path matches if (entry.path.toLowerCase().includes(word)) { score += 2; } } return { entry, score }; }) .filter((item) => item.score > 0) .sort((a, b) => b.score - a.score); return scored.map((item) => item.entry); }