Skip to main content
Server tools are tools the gateway runs. Declare them on a chat completion next to your own function tools and the model can call them like any other tool — except that when it does, the gateway executes the call, appends the result, and calls the model again until it answers. You get the final answer in one request; your code never sees a server tool call. Three tools, declared by a reserved 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

The model typically calls 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. 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

  1. The gateway removes the server-tool entries from tools and offers the model the equivalent function tools (aireserve__web_search, aireserve__web_fetch, aireserve__datetime) beside your own.
  2. 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 tool message per call to the conversation, and calls the model again. That is one round.
  3. 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.
  4. A turn with no tool calls is the answer and is returned as usual.
Errors inside a tool — a page that answers 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 most max_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

With stream: 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 }:
Headings come back as markdown headings, list items as bullets, links as 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 }:
An unknown zone is a tool error naming the problem, never a silent UTC.

Fetch guardrails

The model names the URL, so web_fetch treats every URL as untrusted:
  • http and https only. 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 address 169.254.169.254), carrier-grade NAT, multicast and the IPv6 equivalents (::1, fc00::/7, fe80::/10, IPv4-mapped and NAT64 forms) are refused, as are localhost and .internal / .local names. 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.
Fetched content is never stored. It goes to the model and nowhere else: the usage row and the gateway’s logs carry the URL’s host name and the byte count, never the path, the query string or any part of the page. Searches follow the live-search data-handling terms, including the data-retention consent gate on 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.
**web-fetch is priced at $0.000 per fetch today.** That is a placeholder, not a promise: the per-fetch price is a pending platform decision and will be announced on the [pricing page](https://aireserve.com/pricing) before it takes effect. The row is written at $0 now so your analytics already show fetch volume per key.
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.