Server tools and MCP servers

One /v1 endpoint for every provider. Point your OpenAI client at a new base URL and key, bring your own provider keys, and pay 0% markup on tokens.

A server tool is one the gateway runs itself, inside the request. You declare it in tools beside your own functions, as {"type": "autonomousrelay:<name>"}; when the model calls it, the gateway executes it, hands the result back to the model and continues, and you receive only the final answer. Three kinds exist: the built-in tools, your organisation’s own MCP servers, and marketplace plugins you have installed.

Built-in tools

json
{
  "model": "anthropic/claude-sonnet-5",
  "messages": [{"role": "user", "content": "What day is it in Toronto, and which Claude models can I use?"}],
  "tools": [
    {"type": "autonomousrelay:datetime", "parameters": {"timezone": "America/Toronto"}},
    {"type": "autonomousrelay:experimental__search_models"}
  ]
}
  • autonomousrelay:datetime — the current date and time. parameters.timezone sets the default zone (IANA, e.g. America/Toronto); the model may name another; otherwise UTC.
  • autonomousrelay:experimental__search_models — the catalogue, searched by words in a model’s id or name (query, at most limit 10): ids, names and context lengths.

Those two are the tools that run today. The catalogue (GET /v1/tools, and the Tools page in your workspace) also lists web_search, web_fetch, advisor, subagent, fusion, shell, bash, apply_patch, image_generation and tool_search, each marked as not available. A request that declares one of them is refused with a 400 naming it, never silently served without the tool. PUT /v1/tools with {"disabled": [...]} turns a built tool off for the organisation (a seat with config:write); a request that then declares it is refused.

Web search is a plugin, not a server tool. "plugins": [{"id": "web"}] lets the model search with its provider’s own search — Anthropic’s web search on Claude, Google Search grounding on Gemini 3, OpenAI’s and xAI’s web_search on GPT-5 and later and Grok 4 — so a query goes nowhere the request was not already going. Only those endpoints are candidates; each search is metered at the provider’s published price per thousand; the pages cited come back as url_citation annotations. Outside engines are refused. The options (max_uses, allowed_domains, excluded_domains) are in the API reference under plugins.

Your own MCP servers

POST/v1/mcp-servers
json
{
  "name": "crm",
  "url": "https://mcp.example.com/mcp",
  "allowed_tools": ["search_accounts", "get_account"],
  "header_name": "Authorization",
  "header_value": "Bearer ..."
}

Register a server once: a name (1 to 32 lowercase letters, digits or hyphens), its public https URL, optionally the tools it may offer and one credential header. The header value is sealed on arrival and never shown again (the API returns its last four characters); up to 20 servers per organisation. GET /v1/mcp-servers/:id/tools asks the server for its tools live, which is how a registration is checked. The MCP servers tab of the Tools page does the same.

json
"tools": [{"type": "autonomousrelay:mcp", "server": "crm", "allowed_tools": ["search_accounts"]}]

In a request, that declaration offers the server’s tools to the model as functions named mcp__<server>__<tool>; the gateway calls the server on its stored credential and returns the result to the model. At most five servers in one request. A name that is not registered is a 404, a server turned off is a 403, and one that cannot list its tools is a 502 — before any provider is called.

Marketplace plugins

json
"tools": [{"type": "autonomousrelay:plugin", "plugin": "slack", "allowed_tools": ["search_messages"]}]

An installed plugin is an MCP server its developer hosts. Its tools reach the model as plugin__<slug>__<tool>, called on the credential the install holds; a plugin that is not installed is a 404 and one turned off a 403. Up to five per request. Installing is done from the Plugins tab of the Tools page; building one is its own guide.

All connected tools

json
"tools": [{"type": "autonomousrelay:connected"}]

Offers the model every connection your organisation has — each installed plugin and each registered MCP server — without naming them, so a chatbot picks up a connector the day it is connected. include and exclude take plugin slugs and MCP server names to narrow it. An explicit autonomousrelay:plugin or autonomousrelay:mcp declaration in the same request wins for its connection.

  • Read-only by default. Only a connection’s read tools are offered: a tool counts as one when it says it is read-only and, for a plugin, its manifest agrees. A write tool is offered only when writes names it exactly as the model sees it (plugin__zendesk__add_note), and a named write always waits for a person: the request needs a run_id whose agent has an approval policy, and is refused with a 400 otherwise.
  • A connection that does not work is skipped, not an error: one never connected, expired, turned off, or that does not answer within five seconds. The answer comes from the rest, or from the model alone. The X-Autonomous-Relay-Connected-Tools response header says what was used and skipped (used=notion;skipped=slack:expired), as does agentrouter_metadata.connected_tools when you ask for metadata.
  • Your organisation is told, not your customer. A skipped connection goes to the alert channels that take usage alerts, or by email to the organisation’s owners when there are none, at most once a day per connection and reason, with how to fix it. The person chatting sees only the answer.
  • Limits. At most 10 connections and 128 functions per request, taken plugins first, then MCP servers, each by name; the rest are reported as skipped with the reason limit, which is never announced. A connection’s tools are offered whole or not at all.
GET/v1/tools/available

What the declaration would draw on: each connection with its status (ok, not_connected, expired, disabled, unreachable) and its tools, each named as the model sees it and marked read or write. Asked live with an inference or management key; a plugin that cannot be asked shows its stored manifest’s tools, with tools_source: "manifest". 30 requests a minute per key.

How the loop runs

  • Rounds. Each round is a complete, non-streaming completion. A round’s server-tool calls run together, their results go back as tool messages, and the next round begins. The loop ends when the model answers without calling a server tool — or when it calls one of your functions, which is returned to you as usual.
  • A ceiling. max_tool_calls (1 to 30, default 30) caps the executions in one request. Past it the last answer is returned with native_finish_reason autonomousrelay:max_tool_calls; a later round the provider fails returns the previous answer with autonomousrelay:round_failed.
  • Streaming. With stream: true the loop still runs whole rounds. Progress arrives as SSE comment lines (: autonomousrelay {"type": "tool.called", ...} — turn.completed, tool.called, tool.result), which SDKs ignore, then the answer as ordinary chunks.
  • Usage. usage.server_tool_use counts the calls by tool. The built-in tools cost nothing; the model’s tokens for every round are metered as usual.
  • Not combined with the response cache or batches: a tool runs inside a live request.
  • Approvals. A request made inside a hosted agent’s run (run_id) whose approval policy covers a tool stops before running it: the answer carries native_finish_reason autonomousrelay:approval_required and an autonomousrelay_approval object, and the run waits on the Agents page.