type:
They work with every model the gateway serves — the tools are presented to the model as
ordinary function tools, so any model that can call functions can use them, on any provider and
through fallback chains alike.
Quickstart
datetime to anchor “latest”, web_search to find the statement,
web_fetch to read it, and answers — three tool rounds, one HTTP request. The response is a
normal chat completion; the header x-aireserve-server-tools: web_search=1;web_fetch=1;datetime=1
says what ran.
The OpenAI SDKs type
tools as function tools; pass the server-tool entries through as shown
(the Python SDK accepts them as-is, in TypeScript cast the array if your compiler objects). The
gateway strips them before the request reaches any provider.Declaring the tools
Your own function tools stay in
tools and reach the model unchanged. Function names starting
with aireserve__ are reserved for the gateway; a request that declares one while server tools
are on is rejected with 400.
Compatibility with grounded search
tools: [{ "type": "web_search" }] on its own has meant
grounded search since it
shipped: one search on your latest user message, run by the gateway before the model call, with
the sources injected as context. That behaviour is unchanged. The rule is:
Inside the loop there is no pre-dispatch search and no injected context: if the model wants
sources it calls
web_search itself. The :online suffix, plugins and web_search_options
forms always mean grounded search and are independent of the loop (they can be combined, in
which case the grounded context is injected first and the loop runs as usual).
How the loop runs
- The gateway removes the server-tool entries from
toolsand offers the model the equivalent function tools (aireserve__web_search,aireserve__web_fetch,aireserve__datetime) beside your own. - If the model’s turn consists only of server-tool calls, the gateway executes them — in
parallel, each under its own timeout — appends the assistant turn and one
toolmessage per call to the conversation, and calls the model again. That is one round. - If the model’s turn contains any call to one of your own tools, the turn is yours: the gateway returns it exactly as the model produced it and executes nothing — not even the server-tool calls in the same turn. Run your tools, append the results and send the next request; the server tools are offered again on it.
- A turn with no tool calls is the answer and is returned as usual.
404, a URL the gateway refuses, a search that cannot
be billed, a timeout — are returned to the model as a tool result of the form
{"error": "web_fetch failed: the page answered HTTP 404"}. The model reads it and decides what
to do; the request itself never fails because a tool did.
Loop bounds
A request makes at mostmax_rounds rounds of server-tool calls (default 3, maximum 5). After
the last allowed round the model is called once more with the server tools withheld, so it
has to answer from what it gathered. Within a round at most 8 server-tool calls are executed; the
rest receive a tool error asking for fewer calls per turn. Per-call timeouts: 12 s for a search,
10 s for a fetch, 1 s for the clock. The whole request still runs under the route’s normal
deadline.
Streaming
Withstream: true the tool rounds complete before the first byte of the response; only the
answer round streams, as an ordinary SSE stream ending in [DONE]. Time to first token grows by
the rounds’ duration. The x-aireserve-server-tools header is on the stream’s head with the
final counts. A turn made of your own tool calls streams through untouched, tool-call deltas
included.
What the model receives
web_search — arguments { "query": string, "max_results"?: number }:
web_fetch — arguments { "url": string }:
text (absolute URL)
so the model can fetch them next. Text is capped at 20,000 characters (truncated: true when
cut). Plain-text, JSON, XML and markdown bodies are returned as-is under the same cap.
datetime — arguments { "timezone"?: string }:
Fetch guardrails
The model names the URL, soweb_fetch treats every URL as untrusted:
httpandhttpsonly. No credentials in the URL (https://user:pass@…is refused).- Public addresses only. The host is resolved and every address it maps to must be public:
private (
10/8,172.16/12,192.168/16), loopback, link-local (including the cloud metadata address169.254.169.254), carrier-grade NAT, multicast and the IPv6 equivalents (::1,fc00::/7,fe80::/10, IPv4-mapped and NAT64 forms) are refused, as arelocalhostand.internal/.localnames. The check is repeated for every redirect target. - At most 3 redirects, a 10-second budget for the whole fetch, and a 1 MB byte budget — the read stops at the budget and the result is marked truncated.
- No cookies, no stored credentials, a fixed
User-Agent(AIReserveBot/1.0 (+https://docs.aireserve.com/capabilities/server-tools)). - Only text-like bodies (HTML, plain text, JSON, XML) are read; PDFs, images and binaries are refused before download.
tako-search.
Billing
Every model call in the loop is a normal chat-completion row, billed at the model’s rate with the tool results counted in its prompt tokens:datetime writes no row. The final chat row’s usage metadata carries serverTools — what was
declared, rounds made, calls executed per tool, errors, whether the round cap was hit, and
whether the turn returned to you carried your own tool calls.
Spend caps and budgets apply to every row. If the wallet cannot cover a tool round, the loop stops
and the request answers 402 like any other request the wallet refuses; the model calls already
made are billed.
Errors
Tool failures are not HTTP errors: they reach the model as tool results (see
How the loop runs). Check
x-aireserve-server-tools and the serverTools
usage metadata when you need to know what ran.
Responses API.
/v1/responses is a transparent passthrough on the gateway today and does
not run the server-tool loop; declaring these tools there has no effect. Support on that surface
follows.