Skip to content

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 tooling capability;
  • 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.tools lists every tool that was behind the router with the decision routed. A tool you miss that has denied_by_policy or withheld_by_capability there could not be found by the model either.
  • tool_calls lists every search as builtin__tool_search, with in refs how 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_calls under the real tool's name, with routed: true.
  • Tools picked in advance are in interpretation.blocks as a block with source: "tools": the picked tools in record_ids, how many the bounds left out in dropped_by_count and dropped_by_budget, and what the result cost in estimated_tokens. In the conversation itself they appear as a builtin__tool_search call 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:

  1. Start a run of the suite while the agent's latest version is direct.
  2. Add a version with "tool_search": {"mode": "router"} and start another run.
  3. 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