Laravel AI v0.11.0 adds ToolSearch, contributed by behzadsp in pull request #697. Until now, every tool definition an agent exposed was sent to the provider on each request, a fixed cost that grows painful with dozens of tools and degrades the model's tool selection accuracy. The wrapper maps onto the hosted tool search that OpenAI and Anthropic already offer: the provider receives a search entry plus deferred definitions, and loads a tool's full definition only when it decides to use it.

app/Ai/SupportAgent.php
use Laravel\Ai\Providers\Tools\ToolSearch;

public function tools(): iterable
{
    return [
        new LookupAccount,
        new ToolSearch(tools: [
            new IssueRefund,
            new ChangePlan,
            new ResendInvoice,
            new TransferSeat,
        ]),
    ];
}

ToolSearch takes the tools to defer as its first constructor argument. Anything outside the wrapper is sent as before, and tool classes need no changes: no interface, no per-tool option. The same class can be deferred in one agent and sent up front in another. A second argument selects Anthropic's search strategy, regex (the default) or bm25, validated at construction time with an InvalidArgumentException for anything else. Additional provider fields such as cache_control pass through withProviderOptions() on the wrapper; on Anthropic this is the only place a cache breakpoint can go, because their API rejects tools carrying both defer_loading: true and cache_control.

On OpenAI the wrapper becomes a {"type": "tool_search"} entry; on Anthropic a versioned type such as tool_search_tool_regex_20251119. Deferred tools follow as ordinary definitions flagged defer_loading: true. When the model calls a deferred tool, the gateway resolves it by name from inside the wrapper, so execution behaves identically to a top-level tool. Anthropic reports the search as server_tool_use and tool_search_tool_result blocks (which must not be answered with a tool_result), while OpenAI reports tool_search_call and tool_search_output items; the SDK maps only the request side and does not parse either shape.

Only OpenAiProvider and AnthropicProvider implement the SupportsToolSearch marker. Every other provider, including Azure, Groq, DeepSeek, Mistral, OpenRouter, Gemini, xAI, Bedrock and Ollama, throws a LogicException before any request goes out. Three further rules apply: the support check runs even for an empty wrapper, only one ToolSearch may be registered per request, and on OpenAI the package rejects ToolSearch combined with store=false, since hosted search requires stored responses. An empty wrapper on a supported provider emits nothing.

ToolSearch::budget() expands each wrapper into its deferred tool count when the SDK derives the default maxSteps budget, so wrapping twenty tools does not shrink the step budget. Deferred tools never add steps of their own because the provider loads them inside its own call. Testing works by faking the HTTP call and asserting that deferred definitions carry defer_loading: true, with names resolved by ToolNameResolver from the class basename unless the class defines a name() method.

The article's advice: deferring pays off only for large catalogs. Four tools are not worth it, since the model must search before calling. Failover chains throw when they reach an unsupported provider, and applications using store=false cannot use OpenAI's hosted search. A client-side ranking approach working on every provider was proposed in PR #805 but closed unmerged. ToolSearch shipped alongside run lifecycle events, wider provider failover, and stateless output replay for OpenAI in v0.11.0.