The tool router¶
The tool router lets a turn reach far more tools than a model accepts in one call. The model is not given all your tools in its list, but two of Core's own: one to search tools by meaning and one to run a tool it found. Everything behind those two stays as it is for a direct call: policy, budgets, allowed hosts, usage and traces apply per real tool.
Why a router¶
Every tool you offer a model travels with its name, description and parameter schema in every call. That has two consequences:
- A model has a limit. A vendor refuses a call with more tools than its model takes, so Core holds you to the model's limit (How many tools a model takes).
- Many tools cost context and choices. A long list costs tokens before the model reads your input, and past a few dozen tools a model chooses less well.
An agent that has to reach hundreds of tools, such as an agent over Core's whole API or over a large MCP server, is therefore put behind the router. An agent with a handful of tools gains nothing from it: it spends an extra round searching for what it could have seen directly.
What the model sees¶
In router mode the model is given two tools in place of the turn's tools:
| Tool | What the model does with it |
|---|---|
builtin__tool_search |
searches tools by a description in plain words (query), optionally with its own top_k (1 to 20). The answer is a list, best first, with for each tool the name, description and parameters the model would otherwise have seen in its list, and a score |
builtin__tool_run |
runs one tool by name (name) with its arguments (arguments) |
A search returns the number of tools the model asks for with top_k. When it names none, the
top_k of the request, the agent or the key applies (5 by default, 1 to 20). Either way the answer
stops before it grows past budget_tokens (4,000 by default). A run first checks that the name
belongs to this turn and that the arguments fit the tool's schema. When they do not, the model gets
a tool result with a sentence it can act on: that no tool of that name is available and it should
search for one, or which argument breaks which rule. It can correct the call in its next round. In
the trace that tool call carries the error_code unknown_tool or invalid_arguments. A name the
model already knows may be run without a search.
A few tools stay directly in the list, in router mode too:
- the tools Core adds without being asked: loading a skill (
builtin__load_skill), the task tools and the search over your uploaded files (knowledge__uploads); - your own custom tools (free text with a grammar), because those only work for a tool the model has in its list;
- the tool you force with
tool_choice.
Your own function tools do go behind the router. When the model finds and runs one, you get the
same function_call (tool_use on /messages) as for a direct call, under the tool's own name,
and you answer it as always.
A turn that puts nothing behind the router is offered neither tool. A conversation can switch mode
between two turns: Core shows an earlier run to a turn in direct mode as a plain call of the tool.
Policy decides as for a direct call¶
The router lets the model reach nothing the turn could not have used without it:
- Before the model sees anything. A tool the key's policy refuses, or one a capability withholds, drops out of the turn before the router knows it. A search cannot find, rank or count such a tool.
- On every run. A run is decided on the real name, with the tool's labels and the person, and
then executed with that tool's timeout, allowed hosts, stored credentials and budgets. A refusal
is the same
denied_by_policyas for a direct call. - An unknown name says nothing. A name that does not exist, a name policy refused and a name a capability withheld all get the same answer, so a run is no way to find out which tools exist.
The two router tools are not policy subjects themselves: a rule about builtin__tool_* has no
effect, because the decision always falls on the real tool.
A tool from a sealed publication can appear in the search results, with the name, description and parameters the publisher leaves visible. Such a result is marked sealed, so reading the conversation back later does not show it (Publish and install through the API).
What is recorded and billed¶
| Event | What you see back | Usage |
|---|---|---|
| A search | a tool call builtin__tool_search with the query and the names it found |
one embedding of the query with your organisation's embedding model, as embedding_tokens; no tool_invocation row in usage |
| A run | the real tool's call and result, marked routed: true |
what that tool would have cost directly, at that tool's price |
| A refused run | the call under the name the model asked to run, with the error_code unknown_tool, invalid_arguments or denied_by_policy |
nothing |
Searches count toward your organisation's monthly cap. When that cap is reached, or your organisation has no embedding model the key may use, a search ranks by keyword alone. The trace says so on the search.
Core keeps your tools' descriptions ready to search, but not for every kind of tool in the same
way. API connectors, aliases, scripts and the tools of an MCP server are prepared in the background
when you add or change them, and so is everything once tool search is switched on for the
organisation. Core's own tools (core__ and builtin__), datastore, knowledge and memory tools,
the search over your uploads, and every tool of a newly created organisation get no background run
of their own: they are prepared at the next background run one of those changes starts, or when a
search needs them. Each search embeds at most 64 missing descriptions itself, billed as the query
is, and finds the rest by keyword. Until one of the two has happened, such a tool is never picked
in advance.
How many tools a model takes¶
Every model has a limit on the number of tools in one call. GET /models answers it as
max_tools, and the console shows it in the Max. tools column of the models list. Core itself
offers at most 128 tools per turn, and a model that declares no limit of its own takes 128 as well.
When a turn offers more tools, Core refuses the request before any model is called: 422 on
tools, and 400 in the vendor's shape on /responses and /messages. When the model's limit is
below 128, the message names the model and its limit, such as "This request offers more than 64
tools; model 'M' accepts at most 64."; otherwise it reads "This request offers more tools than a
turn may carry." When a turn can end up on several models through fallback or a model router, the
smallest limit counts.
Core never switches the router on by itself because a turn has too many tools. You choose it, and
then only your own custom tools and the two router tools count toward the model's limit. Everything
behind the router counts toward a limit of its own, your function tools and the agents a turn offers
as tools included. Behind the router one turn holds at most 2,048 tools; past that Core answers
422 on tools.
Tools picked in advance¶
A key can have a turn in router mode start with the tools that best fit its message, already
found. The model then gets those tools as the result of a search at the start of the turn and runs
them with builtin__tool_run, without searching first. It can still search for other tools.
- It is off by default and is a setting of the key alone; no agent version or request can switch it on or off.
- It only picks from the tools the turn has behind the router, under the same policy. A tool whose description Core does not have ready yet is not picked in advance but can still be searched.
- It uses the one embedding of the message the turn also makes for memory and skills, and does not count as a search in your usage.
- It does not change the model's list. That stays the same for the whole conversation, so the vendor's cache keeps working.
Where you switch it on¶
The router is chosen per turn, never per tool. Any tool can be offered directly or through the router, and you mark nothing on a tool or a toolset.
| Level | Field | What it does |
|---|---|---|
| Organisation | tool_search_permitted |
the ceiling. When it is off, no key, agent or request below it can use the router. A new organisation takes the value of the one above it; a top-level organisation starts with it on. Only the organisation above or the operator sets it |
| Key | tooling.tool_search |
enabled makes the router available to the key's turns; default_mode (direct or router) applies when neither agent nor request says; beside them top_k, budget_tokens and preload |
| Agent version | tool_search |
the agent's choice, within what the key allows |
| Request | tool_search on /responses and /messages |
this call's choice |
A turn picks its mode in this order: the request, then the agent version, then the key's
default_mode. When a request or an agent names router while the key or the organisation does not
allow it, Core refuses the request on tool_search.mode with "Tool search is not enabled for this
key." A key's default_mode of router under an organisation that does not allow it quietly
becomes direct. An agent running as a tool in another turn follows its own mode, not that of the
turn calling it.
An MCP client outside Core chooses the router with ?tool_search=router on the address, under the
same conditions (Use Core from an MCP client).
Read on¶
| Part | Read |
|---|---|
| Switching the router on and checking it | An agent with many tools |
The tool_search field on a request |
Make a model call |
| Tools and their sources | Tools |
| The vendors' tool search fields | Connect a coding agent |