laravel/mcp: Port searchable tool catalogs and inspector route prompts from 0.x
Contributed by pushpak1300
Part of the mcp v1.0.1 release
Laravel MCP now reaches feature parity with the 0.x line for two capabilities that main was missing. Pull request #324 cherry-picks searchable tool catalogs (#317) and route parameter prompting in mcp:inspector (#294) forward onto main. If you are upgrading from v0.9.6, both features should behave the way you remember; the only adaptation needed was dropping the sessionId argument from the nested JsonRpcRequest inside ToolSearch, since #285 already removed transport session state from main.
Searchable tool catalogs
Servers that register dozens or hundreds of tools pay for every one of them: each definition lands in the client's context window and competes for the model's attention. Tool catalogs flip that around. Tools you list under the ToolSearch::class key are hidden from direct registration and become discoverable through two generated meta-tools instead:
protected array $tools = [
AlwaysVisibleTool::class,
ToolSearch::class => [
RarelyUsedTool::class,
AnotherTool::class,
],
];
Here AlwaysVisibleTool stays registered as before, while the grouped tools are only reachable via search_tools (find tools) and execute_tools (run them). A catalog may mix class names and instances, and grouped tools resolve through the container like any other tool.
How the meta-tools behave
search_tools is marked read-only and accepts a query (up to 4096 characters) and a limit between 1 and 50, defaulting to 10. An empty query browses the catalog. Matching scores an exact name match at 8 points, a name substring at 4, a description hit at 2, and a schema hit at 1. Results come back with names, descriptions, complete input schemas, any annotations such as destructiveHint, and a hasMore flag when the list was truncated.
execute_tools is marked open-world and takes a calls array of {name, arguments} entries, running them synchronously in order and stopping at the first error. Streaming notifications — stream/progress, for example — are forwarded to the client, and each nested call sees a correctly bound mcp.request instance.
Both meta-tools are bounded by new config values:
'tool_search' => [
'max_tool_calls' => 10,
'max_output_bytes' => 65_536,
],
max_tool_calls caps the size of a single execute_tools batch, while max_output_bytes (64 KB by default) caps the encoded size of any search or execution result; exceeding it returns an OutputLimitExceeded error rather than flooding the client.
Inspector prompts for route parameters
If your web server route contains parameters, mcp:inspector previously built the URL from the raw URI and produced something the inspector could not connect to. The command now asks for each parameter before launching:
Mcp::web('mcp/{organisation:uuid}', OrganizationServer::class);
php artisan mcp:inspector
# What is the value for the [organisation] route parameter?
The answer is substituted through the URL generator, so the inspector targets http://localhost/mcp/4f8a1c2e instead of a literal {organisation} placeholder. Leaving an answer blank (or whitespace-only) aborts with "Every route parameter needs a value to inspect this server" and a failure exit code rather than launching a broken session.
Upgrade impact
Coming from v0.9.6 there is little to migrate, but four things are worth checking:
- Republish your config. The new
tool_searchblock inconfig/mcp.phpcontrols call and output limits. Republish the config or paste the block in manually; if the keys are absent, the code falls back to 25 calls and 65,536 bytes, which may not match what you had tuned. - Tighter validation when catalogs are present. An array value under any key other than
ToolSearch::classthrows, theToolSearch::classentry must map to an array, and duplicate tool names now throw both inside a catalog and across the server (a custom tool namedsearch_toolscolliding with the generated meta-tool is rejected). Servers without a catalog are unaffected. CallToolwas refactored. Invocation logic moved into a newLaravel\Mcp\Server\ToolInvokerclass, which is also used for nested catalog executions. This only matters if you extended or replacedCallTool.- No more session state in nested requests. The nested
JsonRpcRequestno longer carries asessionId, matching main's session-free transports.
Takeaways
ToolSearch::class => [...]in$toolshides grouped tools behindsearch_tools/execute_toolsmeta-tools, keeping client context windows small.- Search results include full schemas and annotations, with
hasMorepaging and configurable call and output limits. mcp:inspectornow prompts for route parameters and fails fast on blank answers.- Republish
config/mcp.phpfor thetool_searchlimits, and expect new duplicate-name validation once catalogs are enabled.