An agent with many tools¶
The concept behind this page is explained in The tool router.
On this page you switch the tool router on for a key, choose it for an agent or for one request, and check in the trace and with an evaluation that the model still finds its tools. Use the router when an agent is given more tools than its model takes in one call, or so many that the model chooses badly.
You need:
- a key with the
toolingcapability; - an organisation that allows tool search. When it does not, only the organisation above or the operator switches it on, under Settings, tab Access & policy, card Tool search (An organisation's settings);
- an embedding model the key may use. The router works without one too, but then searches by keyword alone.
Switch the router on for a key¶
The router belongs to the key's tooling configuration:
{
"capabilities": {
"tooling": {
"system_tools": ["web_fetch"],
"tool_search": {
"enabled": true,
"default_mode": "direct",
"top_k": 5,
"budget_tokens": 4000
}
}
}
}
Send this with PATCH /keys/{key_id}. capabilities replaces the key's whole set, so read it first
with GET /keys/{key_id} and send every capability back with your change in it.
| Field | What it does |
|---|---|
enabled |
makes the router available to this key's turns. Off by default |
default_mode |
direct or router: the mode when neither agent nor request says. direct by default |
top_k |
how many tools one search returns when the model names no top_k of its own, 1 to 20. 5 by default. The model may ask for any number from 1 to 20 |
budget_tokens |
how large one search result may be, 100 to 32,000 tokens, whatever top_k the model asks for. 4,000 by default |
preload |
picking tools in advance, see below |
enabled alone changes nothing yet: a turn uses the router only once the request, the agent or
default_mode asks for router.
In the console the same settings are on the key's page, under the system tools of tooling: the Tool search switch, below it Default with Direct or Through search, and the numbers Results per search and Room for one result (tokens). An empty number means Core's default. When tool search is off for the organisation, the switch is held off and says why (Keys).
Choose the router for an agent¶
An agent version chooses the mode for every turn the agent runs. Add a version with tool_search:
curl -s -X POST "$CORE_URL/agents/agt_5Nj2.../versions" \
-H "Authorization: Bearer $CORE_KEY" -H "Content-Type: application/json" \
-d '{"model": "<a model id>",
"tools": [{"type": "toolset", "name": "orders"}],
"tool_search": {"mode": "router"}}'
You may set top_k and budget_tokens beside it; left out, the key's apply. null leaves the
choice to the key. In the console this is Offering tools, among the version's model settings,
with The key’s default, Direct or Through search.
When the version names router while the key making the request does not have tool search on, Core
refuses every request with this agent on tool_search.mode ("Tool search is not enabled for this
key."). An agent running as a tool in another turn follows its own mode.
Choose the router for one request¶
tool_search on a request wins over the agent and the key:
curl -s -X POST "$CORE_URL/responses" \
-H "Authorization: Bearer $CORE_KEY" -H "Content-Type: application/json" \
-d '{"agent": {"id": "support"},
"input": "Pause the order of customer 4411.",
"tool_search": {"mode": "router", "top_k": 8}}'
{"mode": "direct"} offers every tool directly again for this turn, even when the agent chooses
router. The same field works on /messages. A refused router gets a 400: on /responses with
tool_search.mode in error.param, on /messages with an error.message that starts with
tool_search.mode:.
A client that sends the vendors' fields gets the same router without any change of yours:
defer_loading: true on a tool, OpenAI's {"type": "tool_search"} entry on /responses, or
Anthropic's tool_search_tool_bm25 or tool_search_tool_regex on /messages. Each of them asks
for router for the whole request, never for one tool. A tool_search the request sends itself
wins over those fields (Connect a coding agent).
The router over MCP¶
An MCP client outside Core, such as Claude Code or Cursor, chooses the router with
?tool_search=router on the key endpoint or a toolset's address:
https://<your Core address>/api/v1/mcp?tool_search=router
https://<your Core address>/api/v1/mcp/toolsets/<name>?tool_search=router
The server then lists only builtin__tool_search and builtin__tool_run, with the key's top_k
and budget_tokens. The key's default_mode does not apply here: without the parameter the client
gets every tool (Use Core from an MCP client).
Pick tools in advance¶
With preload, a turn in router mode starts with the tools that best fit its message, as a
search result the model can use at once. That saves a search round when the message says clearly
what has to happen.
{
"tool_search": {
"enabled": true,
"preload": { "enabled": true, "limit": 5, "budget_tokens": 1500 }
}
}
| Field | What it does |
|---|---|
preload.enabled |
switches picking in advance on. Off by default |
preload.limit |
how many tools a turn is handed in advance at most, 1 to 20. 5 by default |
preload.budget_tokens |
how large that result may be, 100 to 8,000 tokens. 1,500 by default |
It is a setting of the key alone; no agent version or request can switch it on or off. In the console it sits under the Tool search switch, while that is on: Pick tools in advance, with Tools per turn and Room for the tools picked in advance (tokens).
Check that it works¶
Read a turn's trace with GET /responses/{response_id}/trace and open a call with GET
/traces/{call_id} (Read a trace):
interpretation.toolslists every tool that was behind the router with the decisionrouted. A tool you miss that hasdenied_by_policyorwithheld_by_capabilitythere could not be found by the model either.tool_callslists every search asbuiltin__tool_search, with inrefshow many tools were searched (candidates), which it found with their scores (hits) and whether it ranked by keyword alone (keyword_only).- A run is in
tool_callsunder the real tool's name, withrouted: true. - Tools picked in advance are in
interpretation.blocksas a block withsource: "tools": the picked tools inrecord_ids, how many the bounds left out indropped_by_countanddropped_by_budget, and what the result cost inestimated_tokens. In the conversation itself they appear as abuiltin__tool_searchcall with the arguments{"preloaded": true}and its result.
In the console the same information is on a call's page under Traces: in Which tools were offered the tools behind the router read Behind the router, and a search has the block The router’s search with Searched for and the tools it found. A run is marked Found through the router.
When the model does not find a tool it should have found, improve that tool's description: a search compares the model's query with each tool's name, description and parameters.
Measure with an evaluation¶
An evaluation shows whether the router makes your agent better or worse. Two checks serve that:
| Check | Holds when |
|---|---|
tool_found |
a builtin__tool_search of the turn, or the tools picked in advance, had expected among its hits |
must_use_tool |
the turn called expected, directly or through the router |
This is one step in a scenario's steps (Writing a
scenario):
{"input": "Pause the order of customer 4411.",
"checks": [{"kind": "tool_found", "expected": "api__orders_pause", "required": false},
{"kind": "must_use_tool", "expected": "api__orders_pause"}]}
Together they tell you where it goes wrong: when tool_found fails, the search did not find the
tool; when tool_found holds and must_use_tool fails, the model found the tool but did not use
it. A turn without a search fails tool_found, so in direct mode every step fails it and only
must_use_tool measures anything. That is why tool_found is "required": false above: in a
direct run it only adds a warning, and the step passes or fails on must_use_tool.
To compare the router on and off:
- Start a run of the suite while the agent's latest version is
direct. - Add a version with
"tool_search": {"mode": "router"}and start another run. - Compare the two runs on
must_use_tool. Each run records the agent version it ran against.
Errors¶
| Answer | Meaning | What you do |
|---|---|---|
400 on tool_search.mode (error.param on /responses, the start of error.message on /messages), "Tool search is not enabled for this key." |
a request or agent asks for router, but the key lacks tool_search.enabled or the organisation does not allow tool search |
switch it on for the key, or ask the organisation above to allow tool search |
422 on tool_search at an MCP address |
the same, for ?tool_search=router |
the same |
422 on tools (400 on /responses and /messages), "This request offers more than … tools; model '…' accepts at most …." |
the turn offers more tools than the model takes | switch the router on, or offer fewer tools |
422 on tools, "This request offers more tools than a turn may carry." |
more than 128 tools directly, or more than 2,048 behind the router | offer fewer tools, or switch the router on |