The Problem: Every Tool Loads on Every Request
Model Context Protocol (MCP) servers expose tools to AI agents, but there is a hidden cost most developers overlook. Before an agent reads a single user message, the name, description, and full JSON Schema of every registered tool is already in the context window. Anthropic's own benchmarks put a 50-tool MCP setup at roughly 72,000 tokens upfront — around 1,000 tokens per tool.
Token cost is only part of the story. On Anthropic's MCP evaluations with large tool libraries, deferring definitions and searching instead moved Opus 4 from 49% to 74% accuracy, and Opus 4.5 from 79.5% to 88.1%. Forty tool descriptions really is forty chances to pick the wrong one.
Four Versions, Three Thrown Away
Building Laravel MCP 1.0, the author worked through four design iterations before landing on the shipped solution.
Version 1: Eval-Based Code Execution (Tinker)
The first attempt generated PHP function signatures from tool definitions and ran agent-written code through eval(). It worked well in tests — but those tests only exercised expected snippets. Nothing prevented the model from writing arbitrary PHP with full access to the application container, database, and environment. The sandbox was absent.
Version 2: Adding a Real Sandbox
Cloudflare's Code Mode runs generated JavaScript in V8 isolates. PHP has no equivalent in-process sandbox. Every viable option — containers, Firecracker microVMs, hosted sandboxes like E2B, WASM via Extism — added infrastructure that a Composer package cannot safely impose on thousands of downstream applications.
Version 3: A Restricted PHP Dialect
Using nikic/PHP-Parser, the next attempt parsed agent-written PHP and rejected any AST node outside a small allowlist. Tests passed — because the tests were written against the same spec that defined the allowlist. A model that had never read that spec immediately produced valid PHP constructs (foreach, if, array appends) that fell outside the subset. A partial language is worse than no language.
Version 4: JSON Batch Format
Dropping PHP-Parser entirely, the final version switched to a JSON format the executor supported completely. No hidden subset, no priors to fight. The model receives a JSON Schema that states exactly what is allowed, and there is no boundary to accidentally cross.
What Shipped: Searchable Tool Catalogs
Laravel MCP 1.0 ships two meta-tools: search_tools and execute_tools. You declare a catalog using ToolSearch::class as a key in your server's $tools array:
public array $tools = [
CurrentWeatherTool::class,
ToolSearch::class => [
StationReadingsTool::class,
RainfallForecastTool::class,
// ... more tools
],
];
Frequently used tools stay advertised directly. Everything else becomes discoverable on demand. The tools/list payload returns three tools instead of forty, regardless of catalog size.
Search is deterministic and lexical — no embeddings required. Exact name matches score highest, followed by terms in the name, description, and serialized input schema. Parameter names count as documentation.
Each call in a batch becomes a real JsonRpcRequest routed through ToolInvoker, the same class a normal tools/call uses. Conditional registration is re-checked at execution time, so a search result is never a capability grant.
Measured Results
| Tools | Direct (bytes) | Catalog (bytes) | Reduction | |-------|---------------|-----------------|----------| | 10 | 5,843 | 1,431 | 75.5% | | 20 | 11,703 | 1,431 | 87.8% | | 40 | 23,423 | 1,431 | 93.9% | | 100 | 58,585 | 1,431 | 97.6% |
The catalog column does not grow. Every tool added after the first is free at rest. The crossover pays off at as few as 10 tools.
Where This Fits
There are three points on the spectrum:
- Advertise every tool directly — no infrastructure, fine below ~10 tools.
- Search and batch (what shipped) — flat payload at any scale, no control flow, nothing to install.
- Generated code in a sandbox — full control flow and intermediate filtering, but requires infrastructure to operate.
The middle option is the only one a package can ship with zero installation requirements.
Key Takeaways
- Tool schema bloat degrades both token efficiency and agent accuracy at scale.
search_tools+execute_toolskeepstools/listconstant regardless of catalog size.- Lexical search scores parameter names, making them first-class documentation.
- Security is enforced at execution time via
ToolInvoker, not at search time. - When your API surface exceeds 10–15 tools, advertising all of them needs justification, not the reverse.
- Tests written by the same author who defined the constraints are not evidence of correctness when a language model is the consumer.