Skip to content

A Better Way to Build MCP Servers with Laravel: Searchable Tool Catalogs

Laravel MCP 1.0 introduces searchable tool catalogs that keep the tools/list payload flat no matter how many tools you register — cutting context bloat by up to 97% and improving agent accuracy.

Published · Updated · 4 min read

A Better Way to Build MCP Servers with Laravel: Searchable Tool Catalogs

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_tools keeps tools/list constant 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.

Source: A better way to build MCP servers with Laravel

Frequently asked questions

Why does loading all MCP tools on every request hurt agent performance?

Every registered tool's name, description, and full JSON Schema enters the context window before the agent reads the user's message. At 50+ tools this can exceed 72,000 tokens upfront, and more definitions mean more chances for the model to select the wrong tool. Anthropic's benchmarks show accuracy dropping significantly as tool count grows.

How does the Laravel MCP searchable tool catalog work?

Two meta-tools — search_tools and execute_tools — are advertised instead of the full catalog. The agent searches lexically (no embeddings) and receives only the schemas it needs. Each execution call is routed through the same ToolInvoker class as a normal tools/call, and conditional registration is re-checked at execution time rather than at search time.

When should I use a tool catalog versus advertising tools directly?

The author's rule of thumb is that past 10–15 tools, advertising all of them is a cost that needs justification. For very small servers where every tool is used on nearly every request, direct advertising is simpler and has no overhead. A mix of both — keeping high-frequency tools advertised while placing the rest in a catalog — is also a valid approach.

Mohamed Said

About the author

Senior Backend Engineer specializing in Laravel, scalable SaaS platforms, APIs, and cloud infrastructure. I build secure, high-performance web applications that help businesses grow.

About GitHub LinkedIn WhatsApp Email Address My CV

Have a technical challenge?

Tell me what you’re building. I reply within two working days.

Discuss your project