# MCP Tool Router Pattern Guide A practical guide for building MCP servers with 50-500+ tools without destroying context windows or confusing the LLM. --- ## The Problem When your MCP server exposes too many tools: 1. **Token bloat**: 50 tools ≈ 30-50K tokens consumed before the user says anything 2. **Selection errors**: Claude sees `add_component`, `place_component`, `add_footprint`, `place_footprint` and guesses wrong 3. **Context starvation**: Less room for actual conversation and results 4. **Accuracy degradation**: More tools = more confusion about which to use Real-world example: A KiCAD MCP server with 52 tools consumes ~40K tokens. An IDA Pro MCP server could easily hit 100+ tools. --- ## Solution Overview Implement a **router pattern** within your MCP server that: 1. Exposes only essential tools directly (10-15 tools) 2. Provides discovery tools for everything else 3. Routes execution through a single `execute_tool` endpoint **Result**: Claude sees ~18 tools instead of 100+, but can still access everything. --- ## Architecture ``` ┌─────────────────────────────────────────────────────────────┐ │ MCP Client (Claude) │ └─────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────┐ │ Your MCP Server │ │ ┌─────────────────────────────────────────────────────────┐│ │ │ DIRECT TOOLS (Always Visible) ││ │ │ • create_project • open_project • save_project ││ │ │ • add_component • add_track • get_info ││ │ │ • (10-15 high-frequency tools) ││ │ └─────────────────────────────────────────────────────────┘│ │ ┌─────────────────────────────────────────────────────────┐│ │ │ ROUTER TOOLS ││ │ │ • list_tool_categories • get_category_tools ││ │ │ • execute_tool • search_tools ││ │ └─────────────────────────────────────────────────────────┘│ │ │ │ │ ▼ │ │ ┌─────────────────────────────────────────────────────────┐│ │ │ ROUTED TOOLS (Hidden) ││ │ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ ││ │ │ │ export │ │ drc │ │ zones │ │ advanced │ ││ │ │ │ (8 tools)│ │(5 tools) │ │(6 tools) │ │(12 tools)│ ││ │ │ └──────────┘ └──────────┘ └──────────┘ └──────────┘ ││ │ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ ││ │ │ │schematic │ │ layers │ │ graphics │ ... more ││ │ │ │(15 tools)│ │(4 tools) │ │(10 tools)│ ││ │ │ └──────────┘ └──────────┘ └──────────┘ ││ │ └─────────────────────────────────────────────────────────┘│ └─────────────────────────────────────────────────────────────┘ ``` --- ## Implementation ### Step 1: Define Your Tool Registry Create a central registry that organizes all tools by category. ```typescript // src/tools/registry.ts export interface ToolDefinition { name: string; description: string; inputSchema: { type: "object"; properties: Record; required?: string[]; }; handler: (params: any) => Promise; } export interface ToolCategory { name: string; description: string; tools: ToolDefinition[]; } // Define all your categories export const toolCategories: ToolCategory[] = [ { name: "export", description: "Generate output files: Gerber, drill, BOM, PDF, 3D models (STEP/VRML), SVG", tools: [ { name: "export_gerber", description: "Export Gerber files for PCB fabrication", inputSchema: { type: "object", properties: { output_dir: { type: "string", description: "Output directory path", }, layers: { type: "array", items: { type: "string" }, description: "Layers to export (default: all copper + silkscreen + mask)", }, format: { type: "string", enum: ["rs274x", "x2"], description: "Gerber format version", }, }, required: ["output_dir"], }, handler: async (params) => { // Your implementation return { success: true, files: ["..."] }; }, }, { name: "export_drill", description: "Export Excellon drill files", inputSchema: { type: "object", properties: { output_dir: { type: "string" }, format: { type: "string", enum: ["excellon", "excellon2"] }, }, required: ["output_dir"], }, handler: async (params) => { /* ... */ }, }, { name: "export_bom", description: "Export bill of materials as CSV or XML", inputSchema: { /* ... */ }, handler: async (params) => { /* ... */ }, }, // ... more export tools ], }, { name: "drc", description: "Design rule checking: clearance validation, electrical rules, manufacturing constraints", tools: [ { name: "run_drc", description: "Run full design rule check on current board", inputSchema: { type: "object", properties: { report_all: { type: "boolean", description: "Report all violations or stop at first", }, }, }, handler: async (params) => { /* ... */ }, }, { name: "get_drc_errors", description: "Get current DRC violations without re-running check", inputSchema: { type: "object", properties: {} }, handler: async (params) => { /* ... */ }, }, { name: "set_design_rules", description: "Configure design rules: clearance, track width, via size, etc.", inputSchema: { type: "object", properties: { min_clearance: { type: "number", description: "Minimum clearance in mm" }, min_track_width: { type: "number", description: "Minimum track width in mm" }, min_via_diameter: { type: "number", description: "Minimum via diameter in mm" }, min_via_drill: { type: "number", description: "Minimum via drill size in mm" }, }, }, handler: async (params) => { /* ... */ }, }, ], }, { name: "zones", description: "Copper zones/pours: ground planes, power fills, keep-out areas", tools: [ { name: "add_zone", description: "Add copper zone/pour to PCB", inputSchema: { type: "object", properties: { net_name: { type: "string", description: "Net to connect (e.g., 'GND', 'VCC')" }, layer: { type: "string", description: "Layer name (e.g., 'F.Cu', 'B.Cu')" }, points: { type: "array", items: { type: "object", properties: { x: { type: "number" }, y: { type: "number" }, }, }, description: "Polygon vertices in mm", }, priority: { type: "number", description: "Fill priority (higher fills first)" }, }, required: ["net_name", "layer", "points"], }, handler: async (params) => { /* ... */ }, }, { name: "fill_zones", description: "Recalculate and fill all copper zones", inputSchema: { type: "object", properties: {} }, handler: async (params) => { /* ... */ }, }, { name: "remove_zone", description: "Remove a copper zone by ID", inputSchema: { type: "object", properties: { zone_id: { type: "string", description: "Zone identifier" }, }, required: ["zone_id"], }, handler: async (params) => { /* ... */ }, }, ], }, // Add more categories... ]; ``` ### Step 2: Build Lookup Maps Create efficient lookups for routing. ```typescript // src/tools/registry.ts (continued) // Build lookup maps at module load time const categoryMap = new Map(); const toolMap = new Map(); export function initializeRegistry() { for (const category of toolCategories) { categoryMap.set(category.name, category); for (const tool of category.tools) { toolMap.set(tool.name, { category: category.name, tool }); } } } export function getCategory(name: string): ToolCategory | undefined { return categoryMap.get(name); } export function getTool(name: string): { category: string; tool: ToolDefinition } | undefined { return toolMap.get(name); } export function getAllCategories(): ToolCategory[] { return toolCategories; } export function searchTools(query: string): Array<{ category: string; tool: string; description: string; }> { const q = query.toLowerCase(); const matches: Array<{ category: string; tool: string; description: string }> = []; for (const category of toolCategories) { for (const tool of category.tools) { if ( tool.name.toLowerCase().includes(q) || tool.description.toLowerCase().includes(q) || category.name.toLowerCase().includes(q) ) { matches.push({ category: category.name, tool: tool.name, description: tool.description, }); } } } return matches; } // Initialize on load initializeRegistry(); ``` ### Step 3: Create Router Tools These are the tools that enable discovery and execution. ```typescript // src/tools/router.ts import { getAllCategories, getCategory, getTool, searchTools } from "./registry.js"; export const routerTools = { list_tool_categories: { name: "list_tool_categories", description: "List all available tool categories. Use this to discover what operations " + "are available beyond the basic tools exposed directly.", inputSchema: { type: "object" as const, properties: {}, required: [], }, handler: async () => { const categories = getAllCategories(); return { total_categories: categories.length, total_tools: categories.reduce((sum, c) => sum + c.tools.length, 0), categories: categories.map((c) => ({ name: c.name, description: c.description, tool_count: c.tools.length, })), }; }, }, get_category_tools: { name: "get_category_tools", description: "Get detailed information about tools in a specific category, " + "including their parameters. Use after list_tool_categories to " + "see what's available in a category.", inputSchema: { type: "object" as const, properties: { category: { type: "string", description: "Category name from list_tool_categories", }, }, required: ["category"], }, handler: async (params: { category: string }) => { const category = getCategory(params.category); if (!category) { return { error: `Unknown category: ${params.category}`, available_categories: getAllCategories().map((c) => c.name), }; } return { category: category.name, description: category.description, tools: category.tools.map((t) => ({ name: t.name, description: t.description, parameters: t.inputSchema, })), }; }, }, execute_tool: { name: "execute_tool", description: "Execute a tool from any category. First use list_tool_categories " + "and get_category_tools to discover available tools and their parameters.", inputSchema: { type: "object" as const, properties: { tool_name: { type: "string", description: "Tool name (from get_category_tools)", }, params: { type: "object", description: "Tool parameters (see get_category_tools for schema)", }, }, required: ["tool_name"], }, handler: async (input: { tool_name: string; params?: Record }) => { const entry = getTool(input.tool_name); if (!entry) { return { error: `Unknown tool: ${input.tool_name}`, hint: "Use list_tool_categories and get_category_tools to find available tools", }; } try { const result = await entry.tool.handler(input.params || {}); return { tool: input.tool_name, category: entry.category, result, }; } catch (err) { return { error: `Tool execution failed: ${(err as Error).message}`, tool: input.tool_name, category: entry.category, }; } }, }, search_tools: { name: "search_tools", description: "Search for tools by keyword across all categories. " + "Useful when you know what you want to do but not which category it's in.", inputSchema: { type: "object" as const, properties: { query: { type: "string", description: "Search term (e.g., 'gerber', 'zone', 'differential', 'export')", }, }, required: ["query"], }, handler: async (params: { query: string }) => { const matches = searchTools(params.query); return { query: params.query, count: matches.length, matches: matches.slice(0, 20), // Limit results }; }, }, }; ``` ### Step 4: Define Direct Tools These are your high-frequency tools that stay visible always. ```typescript // src/tools/direct.ts import { ToolDefinition } from "./registry.js"; // These tools are ALWAYS visible to Claude - no routing needed // Pick your 10-15 most common operations export const directTools: ToolDefinition[] = [ // === PROJECT LIFECYCLE === { name: "create_project", description: "Create a new project with initial files and configuration", inputSchema: { type: "object", properties: { name: { type: "string", description: "Project name" }, path: { type: "string", description: "Directory path for project" }, template: { type: "string", description: "Optional template to use", enum: ["blank", "arduino", "raspberry-pi"], }, }, required: ["name", "path"], }, handler: async (params) => { // Implementation return { success: true, project_path: `${params.path}/${params.name}` }; }, }, { name: "open_project", description: "Open an existing project", inputSchema: { type: "object", properties: { path: { type: "string", description: "Path to project file or directory" }, }, required: ["path"], }, handler: async (params) => { /* ... */ }, }, { name: "save_project", description: "Save all project files", inputSchema: { type: "object", properties: {}, }, handler: async (params) => { /* ... */ }, }, { name: "get_project_info", description: "Get current project information: path, files, status", inputSchema: { type: "object", properties: {}, }, handler: async (params) => { /* ... */ }, }, // === PRIMARY OPERATIONS (your core workflow) === { name: "add_component", description: "Add a component at specified position", inputSchema: { type: "object", properties: { type: { type: "string", description: "Component type or library reference" }, reference: { type: "string", description: "Reference designator (e.g., R1, U1)" }, x: { type: "number", description: "X position" }, y: { type: "number", description: "Y position" }, rotation: { type: "number", description: "Rotation in degrees", default: 0 }, }, required: ["type", "reference", "x", "y"], }, handler: async (params) => { /* ... */ }, }, { name: "move_component", description: "Move a component to new position", inputSchema: { type: "object", properties: { reference: { type: "string", description: "Component reference (e.g., R1)" }, x: { type: "number", description: "New X position" }, y: { type: "number", description: "New Y position" }, }, required: ["reference", "x", "y"], }, handler: async (params) => { /* ... */ }, }, { name: "list_components", description: "List all components with their positions and properties", inputSchema: { type: "object", properties: { filter: { type: "string", description: "Optional filter (e.g., 'R*' for resistors)" }, }, }, handler: async (params) => { /* ... */ }, }, // === SECONDARY OPERATIONS (still common) === { name: "add_connection", description: "Add a connection/trace between two points", inputSchema: { type: "object", properties: { start: { type: "object", properties: { x: { type: "number" }, y: { type: "number" } }, }, end: { type: "object", properties: { x: { type: "number" }, y: { type: "number" } }, }, net: { type: "string", description: "Net name" }, }, required: ["start", "end"], }, handler: async (params) => { /* ... */ }, }, { name: "list_nets", description: "List all nets/connections", inputSchema: { type: "object", properties: {}, }, handler: async (params) => { /* ... */ }, }, { name: "get_info", description: "Get general information about current state", inputSchema: { type: "object", properties: {}, }, handler: async (params) => { /* ... */ }, }, ]; ``` ### Step 5: Register with MCP Server Wire everything together in your main server file. ```typescript // src/index.ts import { Server } from "@modelcontextprotocol/sdk/server/index.js"; import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; import { CallToolRequestSchema, ListToolsRequestSchema } from "@modelcontextprotocol/sdk/types.js"; import { directTools } from "./tools/direct.js"; import { routerTools } from "./tools/router.js"; import { initializeRegistry } from "./tools/registry.js"; // Initialize the tool registry initializeRegistry(); const server = new Server( { name: "your-mcp-server", version: "1.0.0", }, { capabilities: { tools: {}, }, }, ); // Combine all visible tools const allVisibleTools = [...directTools, ...Object.values(routerTools)]; // Build a handler map for quick lookup const toolHandlers = new Map Promise>(); for (const tool of directTools) { toolHandlers.set(tool.name, tool.handler); } for (const tool of Object.values(routerTools)) { toolHandlers.set(tool.name, tool.handler); } // List tools handler - returns only direct + router tools server.setRequestHandler(ListToolsRequestSchema, async () => { return { tools: allVisibleTools.map((tool) => ({ name: tool.name, description: tool.description, inputSchema: tool.inputSchema, })), }; }); // Call tool handler server.setRequestHandler(CallToolRequestSchema, async (request) => { const { name, arguments: args } = request.params; const handler = toolHandlers.get(name); if (!handler) { return { content: [ { type: "text", text: JSON.stringify({ error: `Unknown tool: ${name}`, hint: "Use list_tool_categories and search_tools to find available tools", }), }, ], isError: true, }; } try { const result = await handler(args || {}); return { content: [ { type: "text", text: JSON.stringify(result, null, 2), }, ], }; } catch (error) { return { content: [ { type: "text", text: JSON.stringify({ error: `Tool execution failed: ${(error as Error).message}`, }), }, ], isError: true, }; } }); // Start the server async function main() { const transport = new StdioServerTransport(); await server.connect(transport); console.error("MCP Server running on stdio"); } main().catch(console.error); ``` --- ## Choosing Direct vs Routed Tools ### Direct Tools (Always Visible) Include tools that are: - Used in 80%+ of sessions - Essential for basic workflows - Required for project lifecycle (open, save, create) - Needed for the core operation loop **Examples by domain:** | Domain | Direct Tools | | ------------ | -------------------------------------------------------------------------------------------------------------------------------- | | **KiCAD** | create_project, open_project, save_project, add_component, move_component, add_track, list_components, list_nets, get_board_info | | **IDA Pro** | open_database, save_database, get_function, list_functions, add_comment, rename, get_xrefs, decompile | | **Git** | status, add, commit, push, pull, checkout, branch, log | | **Database** | connect, query, list_tables, describe_table | ### Routed Tools (Hidden Behind Router) Include tools that are: - Used in <50% of sessions - Part of specialized workflows - End-of-workflow operations (export, report) - Advanced features - Bulk or batch operations **Examples:** | Category | Why Route It | | ----------------- | ---------------------------- | | `export` | Only used at end of workflow | | `drc/validation` | Used during review phase | | `advanced_*` | Specialty operations | | `bulk_*` | Batch operations | | `config/settings` | One-time setup | --- ## Category Design Guidelines ### Good Category Names - Short, memorable (1-2 words) - Verb-oriented or domain-specific - Clear scope ``` ✓ export, import, drc, zones, routing, schematic, layers, analysis ✗ miscellaneous, utilities, other, stuff, tools2 ``` ### Good Category Descriptions Include: - What the category does - Key operations included - When you'd use it ```typescript // Good { name: "export", description: "Generate manufacturing files: Gerber, drill, BOM, PDF, 3D models (STEP/VRML), SVG" } // Bad { name: "export", description: "Export tools" } ``` ### Suggested Category Structure (Generic) ```typescript const categories = [ // Core operations (might be direct tools instead) { name: "project", description: "Project lifecycle: create, open, save, close" }, // Domain-specific operations { name: "analysis", description: "Analyze and inspect: find patterns, validate, check" }, { name: "modification", description: "Modify and transform: edit, rename, restructure" }, { name: "navigation", description: "Navigate and search: find, list, filter, locate" }, // Output operations { name: "export", description: "Export and generate: reports, files, documentation" }, { name: "import", description: "Import from external sources: files, formats, APIs" }, // Configuration { name: "config", description: "Configuration and settings: preferences, rules, templates" }, // Advanced/specialized { name: "advanced", description: "Advanced operations: batch processing, automation, scripting" }, ]; ``` --- ## IDA Pro Example Structure For an IDA Pro MCP server with 100+ tools: ### Direct Tools (~12) ```typescript const directTools = [ "open_database", // Load IDB "save_database", // Save IDB "get_function", // Get function by address/name "list_functions", // List all functions "decompile", // Decompile function (Hex-Rays) "add_comment", // Add comment at address "rename", // Rename address/function "get_xrefs_to", // Get cross-references to address "get_xrefs_from", // Get cross-references from address "get_strings", // List strings "search_bytes", // Search for byte pattern "get_segments", // List segments ]; ``` ### Routed Categories ```typescript const categories = [ { name: "disassembly", description: "Disassembly operations: undefine, make code/data, change types", tools: ["make_code", "make_data", "undefine", "set_type", "make_array", "make_struct"], }, { name: "functions", description: "Function management: create, delete, modify boundaries, set types", tools: ["create_function", "delete_function", "set_func_end", "set_func_type", "add_func_arg"], }, { name: "types", description: "Type system: structs, enums, typedefs, parse headers", tools: ["create_struct", "add_struct_member", "create_enum", "parse_header", "import_types"], }, { name: "patching", description: "Binary patching: modify bytes, assemble, apply patches", tools: ["patch_bytes", "patch_word", "patch_dword", "assemble", "apply_patches"], }, { name: "scripting", description: "IDAPython scripting: run scripts, evaluate expressions", tools: ["run_script", "eval_python", "get_global", "set_global"], }, { name: "signatures", description: "Signatures and patterns: FLIRT, Lumina, create signatures", tools: ["apply_flirt", "query_lumina", "create_sig", "find_pattern"], }, { name: "debugging", description: "Debugger control: breakpoints, stepping, memory", tools: ["set_breakpoint", "step_into", "step_over", "read_memory", "write_memory", "get_regs"], }, { name: "export", description: "Export: ASM listing, pseudocode, database info, reports", tools: ["export_asm", "export_c", "export_json", "generate_report"], }, { name: "import", description: "Import: symbols, types, comments from external sources", tools: ["import_symbols", "import_pdb", "import_map", "import_comments"], }, { name: "analysis", description: "Analysis control: reanalyze, find patterns, auto-analysis settings", tools: ["reanalyze", "find_crypto", "find_strings", "analyze_calls", "set_analysis_options"], }, ]; ``` --- ## Usage Flow Examples ### Example 1: User wants to export Gerbers ``` User: "Export gerbers for this board" Claude's thinking: 1. Don't see an export_gerber tool directly 2. Call search_tools({ query: "gerber" }) → Returns: { category: "export", tool: "export_gerber", ... } 3. Call execute_tool({ tool_name: "export_gerber", params: { output_dir: "./gerbers" }}) → Returns: { success: true, files: [...] } Claude: "I've exported the Gerber files to ./gerbers/" ``` ### Example 2: User wants to place a component ``` User: "Add a 0805 resistor at position 10,20" Claude's thinking: 1. See add_component in direct tools 2. Call add_component({ type: "R_0805", reference: "R1", x: 10, y: 20 }) → Returns: { success: true, reference: "R1" } Claude: "Added R1 (0805 resistor) at position (10, 20)" ``` ### Example 3: User wants to do something unfamiliar ``` User: "I need to add length matching to these traces" Claude's thinking: 1. Don't see length matching in direct tools 2. Call search_tools({ query: "length matching" }) → Returns: { category: "advanced_routing", tool: "add_length_tuning", ... } 3. Call get_category_tools({ category: "advanced_routing" }) → Returns full parameter schema for add_length_tuning 4. Call execute_tool({ tool_name: "add_length_tuning", params: {...} }) Claude: "I've added length tuning meanders to match the trace lengths" ``` --- ## Testing Your Router ### Unit Tests ```typescript // tests/router.test.ts import { describe, it, expect } from "vitest"; import { searchTools, getCategory, getTool, getAllCategories } from "../src/tools/registry.js"; import { routerTools } from "../src/tools/router.js"; describe("Tool Registry", () => { it("should find tools by keyword", () => { const results = searchTools("export"); expect(results.length).toBeGreaterThan(0); expect(results.some((r) => r.tool.includes("export"))).toBe(true); }); it("should return category info", () => { const category = getCategory("export"); expect(category).toBeDefined(); expect(category!.tools.length).toBeGreaterThan(0); }); it("should return tool info with category", () => { const tool = getTool("export_gerber"); expect(tool).toBeDefined(); expect(tool!.category).toBe("export"); }); }); describe("Router Tools", () => { it("list_tool_categories returns all categories", async () => { const result = await routerTools.list_tool_categories.handler({}); expect(result.categories.length).toBe(getAllCategories().length); }); it("get_category_tools returns tools for valid category", async () => { const result = await routerTools.get_category_tools.handler({ category: "export", }); expect(result.tools).toBeDefined(); expect(result.tools.length).toBeGreaterThan(0); }); it("get_category_tools returns error for invalid category", async () => { const result = await routerTools.get_category_tools.handler({ category: "nonexistent", }); expect(result.error).toBeDefined(); }); it("execute_tool runs valid tool", async () => { const result = await routerTools.execute_tool.handler({ tool_name: "export_gerber", params: { output_dir: "/tmp/test" }, }); expect(result.error).toBeUndefined(); }); it("execute_tool returns error for invalid tool", async () => { const result = await routerTools.execute_tool.handler({ tool_name: "nonexistent_tool", params: {}, }); expect(result.error).toBeDefined(); }); }); ``` ### Integration Test Test with an actual MCP client: ```bash # Using MCP Inspector npx @modelcontextprotocol/inspector your-server # Or with Claude Desktop - add to config { "mcpServers": { "your-server": { "command": "node", "args": ["./dist/index.js"] } } } ``` --- ## Advanced: Combining with Anthropic's Tool Search Tool If you control the client (building your own app with Claude API), you can use Anthropic's native Tool Search Tool for even better results: ```python import anthropic client = anthropic.Anthropic() response = client.beta.messages.create( model="claude-sonnet-4-5-20250929", max_tokens=4096, betas=["advanced-tool-use-2025-11-20"], tools=[ # Tool search tool for dynamic discovery { "type": "tool_search_tool_regex_20251119", "name": "tool_search_tool_regex" }, # Always-loaded tools { "name": "create_project", "description": "Create new project", "input_schema": {...}, "defer_loading": False # Always visible }, # Deferred tools - only loaded when searched { "name": "export_gerber", "description": "Export Gerber files for PCB fabrication", "input_schema": {...}, "defer_loading": True # Hidden until searched }, # ... 100 more deferred tools ], messages=[{"role": "user", "content": "Export gerbers for my board"}] ) ``` This approach works at the API level rather than MCP level, giving you: - Native search with regex or BM25 - Automatic tool expansion - Works with any MCP client See [Tool Search Tool Documentation](https://platform.claude.com/docs/en/agents-and-tools/tool-use/tool-search-tool) for details. --- ## References ### MCP Documentation - [MCP Specification](https://modelcontextprotocol.io/specification/2025-03-26) - [MCP Tools Documentation](https://modelcontextprotocol.info/docs/concepts/tools/) - [MCP TypeScript SDK](https://github.com/modelcontextprotocol/typescript-sdk) - [MCP Python SDK](https://github.com/modelcontextprotocol/python-sdk) - [MCP GitHub Organization](https://github.com/modelcontextprotocol) ### Anthropic Advanced Tool Use - [Advanced Tool Use Blog Post](https://www.anthropic.com/engineering/advanced-tool-use) - [Tool Search Tool Documentation](https://platform.claude.com/docs/en/agents-and-tools/tool-use/tool-search-tool) - [Programmatic Tool Calling Documentation](https://platform.claude.com/docs/en/agents-and-tools/tool-use/programmatic-tool-calling) - [Claude Code Issue: Tool Search Support](https://github.com/anthropics/claude-code/issues/12836) ### Example Implementations - [KiCAD MCP Server](https://github.com/mixelpixx/KiCAD-MCP-Server) - 52+ tools with natural language PCB design - [MCP Servers Repository](https://github.com/modelcontextprotocol/servers) - Official reference implementations --- ## Checklist Before shipping your router-based MCP server: - [ ] Direct tools cover 80% of common use cases - [ ] Categories have clear, descriptive names - [ ] Category descriptions explain what's included - [ ] All tools have good descriptions - [ ] `search_tools` finds tools by common keywords - [ ] `execute_tool` handles errors gracefully - [ ] Unit tests pass for registry and router - [ ] Tested with actual MCP client (Claude Desktop, Cline, etc.) - [ ] README documents the router pattern for users --- ## Summary | Before | After | | ------------------------ | --------------------- | | 100 tools visible | 15-18 tools visible | | ~60K+ tokens consumed | ~10K tokens consumed | | Tool selection confusion | Clear categories | | Context starvation | Room for conversation | The router pattern gives you unlimited tool capacity while keeping Claude focused and efficient.