Skip to main content
Some model providers run tools themselves, inside the model request: OpenAI’s web search, code interpreter and file search are the common ones. Put them in tools next to your own tools; the provider runs them, and the run reports each call in its events, keeps it on the transcript and counts it in usage.
The SDK never executes a hosted tool. It sends it with every model call of the run and reports what the provider did.

The helpers

isHostedTool(value) tells a hosted tool from a local one. Each helper’s name is also the name the model, the events and usage.hostedToolCalls use. In the record form of tools a hosted tool’s key must be its name (tools: { web_search: webSearch(), lookup }). A hosted tool with the name of another tool is a LOUSHO_CONFIG_INVALID error when the agent is created. A provider uses the options it supports and drops the rest with one console.warn. OpenAI takes searchContextSize, userLocation and allowedDomains (no maxUses, no blockedDomains). Anthropic takes maxUses, allowedDomains, blockedDomains and userLocation (no searchContextSize; its code execution has no container). OpenRouter takes maxUses, allowedDomains, blockedDomains (sent as its excluded_domains) and searchContextSize (no userLocation).

Which provider runs which

Hosted tools need ai 6 or 7, except OpenRouter’s web search, which works on ai 4 too. With ai 4 (and its @ai-sdk/openai 0.0.x or 1.x) every other hosted tool is refused. On OpenAI the model is the Responses API model, which @ai-sdk/openai gives from version 2 on. On Anthropic the helpers use the newest dated server tool the installed @ai-sdk/anthropic exports: webSearch() the newest tools.webSearch_*(), codeInterpreter() the newest tools.codeExecution_*() (Anthropic calls it code_execution; the SDK keeps the name code_interpreter in events and in usage.hostedToolCalls, on every provider). A package that has neither is refused with the pairing that would work. For other Anthropic server tools (web fetch, for one) use hostedTool('web_fetch', anthropic.tools.webFetch_20260318()). On OpenRouter, webSearch() adds OpenRouter’s openrouter:web_search server tool to the request, so the search runs on OpenRouter’s side for any model. OpenRouter picks the search engine (the model’s own search where it has one, else Exa) and bills each search on top of the tokens, at the rates on OpenRouter’s pricing page; see its web search guide for the engines. Because the server runs the search inside the request, the model reports no tool call. The SDK reports one hosted call per model step that searched, read from OpenRouter’s response: name: 'web_search', empty args, the cited urls from its url_citation annotations as sources, and result: { requests } when OpenRouter reports usage.server_tool_use.web_search_requests (otherwise result is {}; the live test did not receive a count). The query the model searched for is not reported. An unsupported pairing rejects the run before the first model call with LOUSHO_HOSTED_TOOL_UNSUPPORTED, naming the provider, the tool and what would work. A custom LLMProvider declares what it can send with supportsHostedTool(type) (type is 'web_search', 'code_interpreter', 'file_search' or 'custom' for hostedTool()) and receives the tools in GenerateOptions.hostedTools. A provider without that method supports none. withRetry() and withFallback() ask the (first) wrapped provider.

Any AI SDK provider tool: hostedTool()

hostedTool(name, tool) sends a provider tool object you built with the provider’s package, unchanged, on ai 6 or 7. It works with the built-in providers and with any AI SDK model wrapped by fromAiSdk():
Use the name the provider package documents for its tool.

Events

A hosted call is reported like a tool call, with executedBy: 'provider' on tool.start, tool.done and tool.error:
In a streamed model call the events arrive as the provider reports the call; in a call made without streaming they follow the call, in call order, before its text. tool.done.result is capped at 20,000 characters of JSON (a longer result becomes the cut JSON text ending in ... [truncated N characters]); tool.error has error.name 'HostedToolError' and the provider’s message. The AI SDK UI stream (toUIMessageStream()) marks these tool parts providerExecuted: true. In traces, the chat span of the model call carries lousho.hosted_tool_calls (the names of the tools the provider ran in it); there is no execute_tool span for them, since the SDK ran nothing.

Transcript and replay

The assistant message of the step gets metadata.hostedToolCalls: each call’s id, name, args, result (capped as above), isError and the url sources the provider cited after it. Find them in result.messages. Only the assistant’s text is sent back to the model on later calls, never the provider’s tool blocks. That works on every provider and model; the cost is that citations from earlier turns are not replayed to the model.

Usage and cost

result.usage.hostedToolCalls (and run.done usage.hostedToolCalls) counts the calls per tool name, for example { web_search: 2 }; a sub-agent’s calls are added to the lead’s. The tokens a hosted tool adds (search results the model reads, code output) are in the token counts the provider reports. costUsd covers tokens only. Per-call fees for hosted tools (a web search or a code interpreter session) are billed by the provider and are not included; check the provider’s pricing and use the counts above.

Approvals and permissions

The SDK cannot gate what it does not execute. A hosted tool runs inside the provider’s request, so no permission rule, tool guardrail, needsApproval, preToolCall / postToolCall hook or onToolCall sees it, and a hosted tool cannot be paused for approval. If a run must not search or run code without a human, leave the tool out of tools, or choose tools per run with a function:
Permission modes cannot refuse a hosted call either, so they decide which hosted tools are sent with each model call: A code interpreter runs code, and a hostedTool() is a tool the SDK knows nothing about, so plan mode treats both as tools with side effects. The mode is read before every model call, so a switch (session.setPermissionMode() or a function mode) applies from the next call. No permission.decision entry is written for a hosted tool left out: no call was made. Sub-agents do not inherit the lead’s hosted tools; give a sub-agent its own in its tools. A run resumed from a checkpoint or an approval sends the same hosted tools; a resuming agent with a different set reports agent drift.

Testing

mockModel takes hosted calls in a turn and records hostedTools on each request, so an agent with hosted tools runs offline:

Not covered yet

Image generation, computer use, hosted MCP and hosted shell have no helpers; pass the provider package’s tool with hostedTool() where it works. Replaying provider tool blocks to keep citations across turns is not supported.