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 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():
Events
A hosted call is reported like a tool call, withexecutedBy: 'provider' on
tool.start, tool.done and tool.error:
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 getsmetadata.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:
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 withhostedTool() where it works.
Replaying provider tool blocks to keep citations across turns is not
supported.