Blog / Optimise your MCP tools
How to optimise your MCP tools by knowing how agents call them
Numbers are tokens, the unit AI models read and bill by (roughly ¾ of a word). Sources and method are in the technical details.
We pointed four coding agents at a real, 99-tool production MCP server and asked them ordinary read-only questions, the kind its users ask every day. Each agent ran its default settings with its owner's usual model, and we ran 40 to 80 jobs per agent on 16 October 2026. The server returned correct data every time. The agents still struggled:
- Results refused or cut off. Claude Code refused 76 results as too big across 80 runs, because the main list tool returns 46,253 tokens with default arguments. OpenCode cut off 32 results in 40 runs. Each agent then made over 100 extra file reads (138 and 123) to dig the data back out.
- 63,245 tokens of tool definitions in every request. OpenCode 1.x sends all 99 tool definitions with every model request, about 230k tokens per question before any data comes back.
- Tools not found. Codex didn't find the server's tools at all in 8 of 80 runs, then told the user no connection was available.
- A 12,689-token error for one malformed id, because the server echoed the tool's whole input specification back. The same mistake on two other tools cost 150 and 110 tokens.
- Every payload twice. In 27 of 34 read tools, the text content was an exact JSON copy of the structured content, roughly doubling each result.
- Wrong answers from an ambiguous label. One count question ("how many are qualified vs need review, per group?") failed because of the server's own label in 10 of 25 runs across the three agents. The label mixed two stages together.
What this costs you
- Tokens. Claude Code and Codex used about 220k input tokens per simple question on average (own usage, 80 runs each). Most of that was the same context re-read on every recovery step.
- Wrong answers. Across both rounds, Codex got 69 of 80 right and Claude Code 74 of 80. Every miss traced back to one of the problems above, not to bad data.
- Broken flows. An agent that can't find your tool falls back to the browser, asks the user to paste screenshots, or gives up. One validation error marked "a human must decide" stops a flow that the agent could have fixed in one retry.
None of this was the model being dim. It came from how each agent loads, searches and caps tools, combined with how the server was written. Once you know how the agents call your tools, most of it is cheap to fix. The rest of this post covers how each agent does it, then a checklist of fixes ranked by impact, then the reference details.
Get your MCP server tested across Claude Code, Codex, Grok Build and OpenCode. We run the jobs your users care about through each agent and show you what breaks, with numbers.
Talk to usWhy it happens: how each agent calls your tools
An MCP server is a menu of actions ("tools") that an AI agent can use, such as "list invoices" or "send invoice". When an agent connects, your server sends the whole menu: every tool's name, a description, and the list of inputs it takes. For the 99-tool server that menu was 68,286 tokens. (A token is the unit models read and bill by, roughly three-quarters of a word.)
That's too much to show the model on every turn, so each agent does something different with it:
- OpenCode 1.x shows the model the whole menu, every time. Nothing gets lost, but you pay for 63,245 tokens of menu on every request.
- Claude Code shows only the tool names. The model picks a few names and only then gets their full details.
- Codex shows nothing. The model has to write a small program that searches the tool list for a word it guesses.
- Grok Build says only "this server has 99 tools". The model types a search, and a keyword search returns the closest matches.
- OpenCode 2 (a newer, opt-in version) shows a short menu: 16 of the 99 tools, with one-line descriptions. For anything else, the model searches from inside a code tool.
So in three of the four agents the model has to find your tool before it can use it, and it finds it by matching words. If the user says "invoice" and your tool is called ledger_dispatch, nothing matches. The diagram shows the steps, and how much of the model's attention (in tokens) each step takes up.
Claude Code 2.1.301
- Start: the model gets 113 tool names in a reminder (~1,258 tokens). No schemas.
- Model calls
ToolSearchwithselect:and three exact names. Three full definitions (~573 each) are added as real tools. - Model calls the tool directly. Results over 25,000 tokens are saved to a file instead.
Codex 0.161.0
- Start: only
execand friends (first request 15,383). Not one MCP tool name. - Model writes JS:
ALL_TOOLS.filter(/server/i). Output is 56,619 tokens; Codex cuts it to 11,244. - Two narrower filters add 1,777 and 3,030.
- Model calls
await tools.mcp__server__tool(…)insideexec.
Grok Build 1.0.60
- Start: a 189-token reminder: which servers, how many tools. No names.
- Model runs
search_tool. Local BM25 returns the full schema of all 10 hits (4,957). - Three more searches to check alternatives (4,313 + 3,143 + 2,349).
- Model calls
use_tool({tool_name, tool_input}). Schemas stay as text in history.
OpenCode 1.18.34
- Start: all 99 definitions, 63,245 tokens, in the first request.
- The model picks a tool directly. Nothing to find.
- Every following request carries the same 63,245 again (3.7 requests per question in live runs).
OpenCode 2.0.31
- Start: a "partial catalog" listing 16 of the 99 tools with one-line descriptions (881 tokens), plus a
search()function usable insideexecute. Whole first request: about 5,270 tokens. - Model runs
search({query, namespace})insideexecute. It gets back 10 or 15 hits with full signatures: 2,879 and 5,059 tokens in two live runs. - Model calls the tool from code. Only what the code prints or returns reaches the model. A filtered answer was 188 tokens, although the code pulled 2.4 MB from the server.
- If the code returns a whole result, OpenCode keeps the first 51,200 bytes (14,098 tokens here) and saves the rest to a file.
The fixes, ranked by impact
We ranked these by what they would have saved on the 99-tool server. The "before" figures are measured. The "after" shapes are examples of the fix, not measurements.
Keep default results small and paged
Why first: it caused the most failures we saw (76 refusals, 32 cut-offs, 261 recovery reads), and the smallest cap, Grok Build's 20 KB, is only about 5,000 tokens.
Before: a list call with no arguments returns every record with every field: 46,253 tokens.
list_items {} → { items: [ …all records, all fields… ], nextOffset: null }After: a compact first page, a total, a cursor, and filters so the agent doesn't have to page through everything.
list_items { query?: "acme", status?: "open", limit: 20, fields?: ["id","name","status"] } → { summary: "47 items, 20 shown", items: [ {id,name,status} … ], next: "c_20" }Send the data once
Before: text content = a JSON copy of the structured content (25,806 of the 46,253 tokens in the biggest result).
After: the data lives in structured content, and the text is one line:
"47 items, 20 shown; next page: c_20".Put the user's words in tool names and the first line of descriptions
Claude Code picks tools from names alone, Codex greps names and descriptions with a regex the model writes, and Grok Build ranks by shared keywords. None of them understand synonyms. Codex missing the tools in 8 of 80 runs is what it looks like when the words don't line up.
Before:
ledger_dispatch: "Dispatches the document to the configured recipient."
After:invoice_send: "Email an invoice to a customer (also called a bill)."Make validation errors short and fixable
Before: one bad id returned the whole input specification (12,689 tokens) with the state
human_inputand "Do not repeat the change automatically."After:
{ "error": "groupId must be an id from groups_list", "fix": "call groups_list and pass one of its ids", "retryable": true }Save "ask the user" for real decisions, such as spending money.
Use one vocabulary for states and counts
Before: a group summary showed "To review 20", which really meant 19 qualified + 1 needing review. Agents reported 20 as "needs review" or said the split didn't exist.
After: return the counts users ask about, named the way the records name them:
{ "qualified": 19, "needs_review": 1 }.Shrink the tool list itself
This matters most for agents that show the whole menu every time (OpenCode 1.x, or Claude Code behind a company proxy): 63,245 tokens on every request. Merge near-duplicate tools, move long grammars out of parameter schemas, describe a format once rather than repeating the same regex in 40 places, and write server
instructions, which Claude Code loads even when it defers tools.Keep your own noise out of results
Before: the activity feed returned 200 events, 152 of them records of the agent's own tool calls (98,512 tokens).
After: leave tool-call records out by default and put them behind a filter.Label risky tools honestly
MCP lets each tool carry hints such as "this changes things" (
destructiveHint) or "safe to repeat" (idempotentHint). Agents use them to decide when to ask the user first.Before: two tools that spend money were marked
destructiveHint: false, and a tool that starts paid work was markedidempotentHint: true.
After:destructiveHint: true, idempotentHint: falseon anything that spends money or starts work, so agents ask first and don't retry it.
When a result is too big, agents cut it
Every agent limits how much of one tool result the model may see. The limits are all different, and what happens past them varies too:
- Claude Code refuses anything over about 25,000 tokens. It saves the result to a file and tells the model to read the file in pieces.
- OpenCode (versions 1 and 2) keeps the first 50 KB and saves the rest to a file.
- Codex keeps the start and end of about 10,000 tokens and drops the middle. Nothing is saved, so the model has to ask again more narrowly. In three live runs we watched, every large result was cut this way: 86,000 to 133,000 tokens down to about 10,000. Each time, the model ran the same call again and filtered it in code.
- Grok Build keeps the first 20 KB and saves the rest to a file.
In practice, this is where the extra work comes from. The server's biggest default result was 46,253 tokens. Claude Code refused it 76 times in 80 runs, OpenCode cut off 32 results in 40 runs, and each agent spent over 100 extra steps reading files to recover. Every one of those steps costs a model call and adds a chance of a wrong answer. If your default results stay under about 5,000 tokens, none of this happens in any of the four agents. The exact limits, settings and messages are in the technical details below.
How we measured
- We watched what the model actually receives. Each agent app talks to its AI model over the internet. We put a local proxy in the middle, with a temporary certificate trusted only by that one app, and recorded the raw requests. Passwords and keys were removed before anything was saved.
- We counted tokens one way for everyone, with OpenAI's public o200k tokenizer, so sizes compare fairly. Where we quote an agent's own usage figures, we say so.
- We asked real questions of a live production server: 8 everyday read-only questions, 5 times each. Claude Code 2.1.301 and Codex 0.161.0 ran this twice (80 runs each), OpenCode 1.18.34 once (40 runs). Grok Build 1.0.60 was captured on a single task. All on 16 October 2026, with every tool that could change data blocked.
- We checked the answers. We read the true values from the server before and after the runs (they didn't change), scored every answer against them automatically, then read every failed answer by hand.
- We repeated direct server calls three times to measure result and error sizes. All three gave identical numbers.
Follow-up checks (same evening): to settle two open questions, we watched the live traffic of 3 Codex runs and 2 OpenCode 2 runs, one run per question. These are small samples, meant to show what happens, not how often.
Limits: one server, one day, and one model per agent (each agent ran its owner's usual model), so the success rates don't rank the agents. These apps change weekly. Full method and the source of every number are in the technical details.
Technical details: how each agent works inside, exact limits, settings and sources of every number
o200k_base tokens (the open-source tiktoken library), taken from the recorded request or result as compact JSON. "Own usage" means the agent's or model provider's own reported input tokens, which use that model's tokenizer, so they don't compare exactly with o200k figures. Every number carries the agent version and the date. The server is anonymised; we call it "the 99-tool server". The wire captures used a local stub that served the server's exact recorded tool list, so the tool definitions are real while the data behind them wasn't. The result-size and failure numbers come from live read-only runs against the real server.Side by side
One task: "fetch the activity log for the newest records from the last 7 days", with the same 99 tool definitions. All three captured agents picked the right tool.
| Agent (version, 16 Oct 2026) | In context at start | After a search | On a simple tool call | Run total (own usage) |
|---|---|---|---|---|
| Claude Code 2.1.301 | 113 tool names (~1.3k o200k); 0 MCP schemas; first body 17,564 | full schemas of selected tools only (3 here; ~573 each), sent as real tools | direct call; result capped at 25k tokens, larger saved to a file | 76,334 input (3 requests) |
| Codex 0.161.0 | no MCP names or schemas; first body 15,383 (exec description 6,471) | whatever the model's JS prints: 56,619 for a broad regex (truncated to 11,244), 1.8–3k for narrow ones | called from JS inside exec | 137,344 input (6 requests) |
| Grok Build 1.0.60 | server names + tool counts (189); 0 schemas; first body 19,674 | full schemas of every hit as text: 4,957 for limit 10; 14,762 over four searches | through use_tool | 157,524 (5 requests) |
| OpenCode 1.18.34 | all 99 schemas: 63,245; body 82,603 | no search | direct call; definitions re-sent each request | not captured on this task; 341,615 avg per question in live runs |
| OpenCode 2.0.31 | partial catalog: 16 of 99 tools, one-line descriptions, 881; first body ~5,270 (live, interactive app) | search() inside execute: 2,879 and 5,059 (live) | called from JS inside execute | not run on this task; live: 12,451 peak request (9 requests) and 64,918 peak (10 requests) |
Each agent in detail
Claude Code: names up front, schemas on request
What we saw (Claude Code 2.1.301, claude-opus-5-5, 16 Oct 2026). The first request to the model carried 13 tools in a 17,564-token body. Those were the core tools, a tool called ToolSearch, and a stub placeholder marked defer_loading. None of the 99 MCP tools were in it. Instead, the first message had a reminder saying, roughly, "the following deferred tools are now available via ToolSearch; their schemas are NOT loaded", followed by a plain list of 113 names (99 MCP tools plus 14 built-ins). That list is about 1,258 tokens.
The model then called ToolSearch with a select: query naming three tools exactly. It picked them from the names alone. No keyword search ran. The result came back as tool_reference blocks. The next request carried those three definitions in full (16 tools in total), and the model called the right one. One full definition from this server was 573 tokens.
The model requests in that run were 23,110, 26,398 and 26,826 input tokens (own usage). Most of that was served from the prompt cache.
What the docs say. Anthropic's MCP docs describe the same design: "Only tool names and server instructions load at session start." Tool search is on by default, and the ENABLE_TOOL_SEARCH variable controls it:
- unset: defer all MCP tools (the default);
false: load everything up front;auto/auto:N: load up front while the definitions are under 10% (or N%) of the context window, and defer them all once they reach it.
What turns it off. We measured the extra first-request input (own usage, "say hi" prompt, 16 Oct 2026) with the 99-tool server attached compared to without it:
| Claude Code 2.1.301 setup | Extra input from 99 tools |
|---|---|
| Default (tool search on) | +1,715 |
ENABLE_TOOL_SEARCH=false | +107,541 |
Custom ANTHROPIC_BASE_URL (a local proxy) | +107,542 (all 99 schemas on the wire) |
Proxy plus ENABLE_TOOL_SEARCH=true | back to default (22,446 total) |
--tools "" | 110,998 total: preloaded |
The proxy case is documented: Claude Code turns tool search off when ANTHROPIC_BASE_URL points somewhere other than Anthropic, "since most proxies don't forward tool_reference blocks". The --tools "" case caught us out. That flag removes the built-in tools, ToolSearch included, so everything gets preloaded, and setting ENABLE_TOOL_SEARCH=true didn't bring it back. If you test Claude Code with a stripped tool set or through a gateway, you're probably testing a different product from what your users run.
The result limit. Separately from search, Claude Code caps the size of a single MCP result. The docs give a warning above 10,000 tokens and a default limit of 25,000 tokens (MAX_MCP_OUTPUT_TOKENS). A text result over 50,000 characters is saved to a file in any case. Over the limit, Claude Code saves the result under ~/.claude/projects/…/tool-results and gives the model a message naming the file (details and exact message below). A server can raise the limit for one tool with _meta["anthropic/maxResultSizeChars"].
The 99-tool server's main list tool returns 46,253 tokens with default arguments. In our live runs (Claude Code 2.1.301, 16 Oct 2026), the model got "Error: result (218,237 characters) exceeds maximum allowed tokens" instead of the data. That happened 40 times in 23 of 40 runs in one round and 36 times in 22 of 40 in a repeat. To recover, the model made 138 extra file reads and searches across the first round. It usually got there, but slowly.
Codex: tools live inside a code tool
What we saw (Codex CLI 0.161.0, gpt-6-astra, ChatGPT sign-in, 16 Oct 2026). Codex's code mode is on by default. The first request was 15,383 tokens. It held only a few namespaces, mainly exec, whose description alone is 6,471 tokens. There was no tool_search tool, and the server's name appeared nowhere in the request. The exec description tells the model that some nested tools may be omitted, and that it should filter ALL_TOOLS by name and description.
So the model wrote JavaScript. Its first attempt was a broad regex over every tool: ALL_TOOLS.filter(x => /servername/i.test(…)). That printed 56,619 tokens of tool metadata, which Codex cut down to 11,244 before the model saw it. The next two filters were narrower (1,777 and 3,030 tokens). Then the model called the tool from code (await tools.mcp__server__tool({…})) and got it right. Codex reported 137,344 input tokens for the turn (own usage, 120,576 of them cached).
No preload. With the "say hi" prompt, the first request was 17,098 input tokens (own usage) with or without the server attached, repeated in 4 of 5 control runs. The definitions cost nothing until the model goes looking.
What the source says. The public Codex repo defines ALL_TOOLS as a global inside the code-mode runtime. No ranking or BM25 sits in front of it. The "search" is whatever filter the model writes.
Where it breaks. If the model never thinks to look, it never finds the tools. In 80 live read-only runs against the real server (two rounds of 40, 16 Oct 2026), Codex didn't find the server's tools in 8. It said no connection was available and tried the browser instead. When it did find them, it mostly answered well. Also, turning code mode off (features.code_mode_host=false) didn't give classic direct tools in 0.161; the run just failed with "code-mode host is disabled".
Grok Build: a local BM25 index behind two tools
What we saw (Grok Build 1.0.60, grok-4.7, 16 Oct 2026). The first request had 28 tools (19,674 tokens), including search_tool and use_tool. No MCP tool definitions were in it. A reminder after the user's message said which servers were connected and how many tools each had, something like "MCP servers connected: … (99 tools)". It's 189 tokens. It lists no tool names, and if the server sends no instructions, no description either.
The model searched with a natural-language query and a limit of 10. The search ran on the client. It returned the full input schema of every hit, 4,957 tokens in one result, and the top hit was the right tool. The model searched three more times to check alternatives (4,313, 3,143 and 2,349 tokens). Then it called the tool as use_tool({tool_name, tool_input}). The schemas never become real tools in the request. They stay in the history as plain text and get re-read on every later turn.
What the source says. Grok Build's tool_index.rs checks for an exact match on the qualified or bare tool name first. Otherwise it ranks by BM25 over a document built from each tool. The MCP docs describe the two tools and the server__tool naming.
The --trust trap. Our first Grok run had the server configured in the project folder, and the folder wasn't trusted. In headless mode Grok didn't start the server and didn't warn us either; grok mcp doctor was the only thing that said so. The model ran 9 searches that found nothing, then spent 17 turns reading files around the home directory looking for the server. The run used 626k tokens. With --trust, the same task took 157.5k (38,868 input + 118,656 cache reads, own usage). If your docs tell people to add your server per project, mention this.
OpenCode: everything, every time (version 1)
What we saw (OpenCode 1.18.34 and 1.19.3, 16 Oct 2026). The first request carried 109 tools, the 99 MCP definitions among them, at 63,245 tokens. The whole body was 82,603. Version 1.19.3, the current default install, was identical. With the "say hi" prompt, attaching the server added 73,415 input tokens (own usage, default provider).
OpenCode 1.x sends that again with every model request. In 40 live runs with GLM-5.3-Flash (1,000,000-token window), it averaged 3.7 model requests per question, so roughly 230k tokens of definitions per question. Its largest single request was 131,714 tokens (own usage): big, but it fit. The window wasn't the problem in that test. The bill and the noise were. OpenCode also cuts off large tool results and saves the rest to a file. It did so with 32 results in 24 of its 40 runs.
The upside is that the model never has to find anything. In those 40 runs OpenCode got 39 right, the best of the three agents we ran live. With a large window and a cheap model, eager loading is a legitimate trade. It just isn't free.
OpenCode 2: Code Mode with a short menu
OpenCode 2.0.31 is a separate, opt-in release. Its docs say MCP tools go through Code Mode by default (codemode defaults to true), grouped as tools.server.tool(…).
What we saw (OpenCode 2.0.31, interactive app driven through a terminal, live read-only server, its default model, 16 Oct 2026, 2 runs). The system prompt carries a "partial catalog": - server (99 tools, 16 shown), then 16 tool signatures with descriptions cut to about 120 characters (881 tokens). It tells the model to call search(…) inside execute for everything else. The first request was about 5,270 tokens. In both runs the model searched once (2,879 and 5,059 tokens back), then called the tools from code. In the first run its code filtered the results, so only 188 to 1,001 tokens per step reached the model, although the code pulled 2.4 MB from the server over 8 calls. Both answers were correct.
Where it breaks. In the second run the model returned two whole activity pages. OpenCode cut each at 51,200 bytes (14,098 and 16,343 tokens), and the model then searched and read the saved files (4 extra steps). Peak request: 64,918 tokens.
Headless mode. In headless opencode run (with or without --server, two-turn, project config, mcp add, codemode:false) the server's tools never reached the model, although the logs said connected … tools=99. The same config works in the interactive app, where the first message was sent about 20 seconds after start. Our best guess, not confirmed: in the shipped binary the MCP tool registry is filled once when it starts and refreshed on a change event, so a headless run can build its first catalog before the server's tools arrive. If you test OpenCode 2 in CI, check that your tools are in the catalog.
Output caps in detail
Search decides whether your tool gets found. Output caps decide whether the model ever sees what your tool returned. All four agents cap a single tool result, and each does it differently: by tokens, by bytes, or by lines. All but one then save the full result to a file and leave the model to dig through it.
| Agent (version) | Default cap | Change it with | Over the cap | Verified by |
|---|---|---|---|---|
| Claude Code 2.1.301 | 25,000 tokens (warning above 10,000); text over 50,000 characters is saved to a file regardless | MAX_MCP_OUTPUT_TOKENS; per tool _meta["anthropic/maxResultSizeChars"] (up to 500,000 chars) | No data inline. Saved to a file, and the model is told to read it in chunks | docs + live runs |
| OpenCode 1.18.34 | 2,000 lines or 51,200 bytes, whichever comes first | config tool_output.max_lines / max_bytes | Keeps the head, saves the rest to a file (files are cleaned up after 7 days) | shipped binary + live runs |
| Codex 0.161.0 (code mode) | 10,000 tokens per exec output by Codex's own estimate (the call can ask for a different max_output_tokens) | per call | Keeps the start and end, drops the middle with a warning. No file | source + capture + 3 live runs (wire) |
| OpenCode 2.0.31 | 2,000 lines or 51,200 bytes (same as 1.x) | not checked | Keeps the head, saves the full output to a file: [showing lines 1-866 of 2589; full output saved to …] | shipped binary + 1 live run |
| Grok Build 1.0.60 | 20,000 bytes | GROK_MAX_MCP_OUTPUT_BYTES or MAX_MCP_OUTPUT_BYTES, or [mcp] max_output_bytes | Keeps the first 20 KB and saves the full result under the session's mcp/ folder | source only (not hit in our runs) |
What the model actually sees
Claude Code (live run, 16 Oct 2026, paths shortened):
Error: result (98,689 characters) exceeds maximum allowed tokens. Output has been saved to ~/.claude/projects/…/tool-results/mcp-server-list_tool-….txt.
Format: JSON with schema: {…, items: [{...}], limit: number, offset: number, nextOffset: null, …}
Use jq to make structured queries (find a value, filter by field).
REQUIREMENTS FOR SUMMARIZATION/ANALYSIS/REVIEW:
- You MUST read the content from the file at … in sequential chunks until 100% of the content has been read.
- Note: this file is JSON, so a long value (or the whole file) is a single line. Read's offset/limit cannot split a line, …
It arrives as an error, with a schema sketch, and it tells the model to read everything. On a one-line JSON file, that's awkward. In our first round of 40 live runs, this happened 40 times in 23 runs, and the model made 138 extra file reads and searches to recover. The repeat round: 36 times in 22 runs.
OpenCode 1.18.34 (live run, 16 Oct 2026, path shortened):
…80400 bytes truncated… The tool call succeeded but the output was truncated. Full output saved to: …/opencode/tool-output/tool_… Use Grep to search the full content or Read with offset/limit to view specific sections.
The model does get the first 50 KB, so it often has enough to start. In 40 live runs, 32 results in 24 runs were cut this way (29 from the server's main list tool), followed by 123 extra file reads and searches. When the session can use sub-agents, the binary has a different version of the message that tells the model not to read the file itself but to hand it to a sub-agent. We didn't see that one in our runs.
Codex 0.161.0 (capture, 16 Oct 2026, stub server with the real tool list):
Warning: truncated output (original token count: 56619)
Total output lines: 2
[{"name":"mcp__server__activity_since","description":"Everything that happened in …
There's no file to fall back on. The model has to write a narrower filter and run it again, which is what it did (1,777 and then 3,030 tokens).
Live check (3 runs against the real server, model traffic recorded, 16 Oct 2026, one run each of three questions). In every run the model's first two looks at the tool list were cut: 54,579 to 159,362 tokens (Codex's own count) down to about 9,000. Every result it printed whole was cut as well: the main list tool's page (133,088 and 129,770 tokens), an activity page (86,562) and the list of campaigns (19,746). Each time Codex kept about 10,000 tokens, with …N tokens truncated… in the middle. Each time, the model recovered by writing code that filtered the result or kept it in a variable, then printed only what it needed (22 to 5,115 tokens). In two of the three runs that meant calling the same tool again, so the server got one extra call of about 520 KB and 350 KB. All three answers were correct. Peak requests were 47,814, 55,145 and 79,856 tokens (own usage), over 8 to 9 model requests.
Grok Build 1.0.60: according to the source, the model gets the first 20 KB, then [MCP output truncated: showing first 20 KB of N. Full output written to: …]. For JSON it adds a hint to query the saved file with a shell tool. Our Grok runs never returned a result that large, so this part is from source only.
What it costs
- A cap protects the context window, but it doesn't make the call cheaper. Each recovery step is another model request that re-reads the whole conversation. Our Claude Code runs averaged about 210k to 235k input tokens per question (own usage). Most of that was cached, but none of it was free.
- The caps disagree. A 46,253-token result (218,237 characters in the live error message) is refused outright by Claude Code, cut to 50 KB by OpenCode and to 20 KB by Grok Build, and inside Codex code mode it gets the middle cut out. The same server gives four different experiences.
- JSON on one line defeats line-based reading. Both Claude Code's and Grok Build's messages warn about this.
For server authors: stay under every cap
- Aim for under about 5,000 tokens per default result (well under Grok's 20 KB, which is the smallest cap). Treat 10,000 tokens as the ceiling. Above that, Claude Code warns and Codex starts cutting.
- Page by default. A small
limit, a cursor, and a total count, so the agent can tell there's more. - Let callers pick fields (
fields: ["id","name","status"]) and return a compact shape unless they ask for more. - Lead with a summary ("47 results, 20 shown, next cursor …") so a cut-off head still answers the question.
- Don't send the same data twice as text and structured content. That alone roughly halves the largest results we measured.
- If a tool really must return a lot (a schema, a file tree), Claude Code lets you raise that one tool's limit with
anthropic/maxResultSizeChars. Use it sparingly.
Can I have 1,000 tools?
Technically, in some agents, yes. Claude Code's docs say it has no fixed per-server cap and the practical limit is your context budget. Grok Build and Codex never put definitions in the first request, so a long list doesn't cost anything up front. Whether it works is a different question, and here's what we measured with 99:
- Eager loaders pay in proportion. OpenCode 1 sent 63,245 tokens for 99 tools. At that rate (about 640 per tool on this server), 1,000 tools would be roughly 640k tokens per request. That doesn't fit most models' windows and would be absurdly expensive in the ones it does. Claude Code with tool search off (a gateway,
ENABLE_TOOL_SEARCH=false,--tools "") behaves the same way. - Search gets harder with more look-alikes. With 99 tools, Codex missed the server's tools entirely in 8 of 80 live runs. A Codex model that greps a 1,000-tool list for one word gets a bigger dump. Its broad filter already printed 56,619 tokens for 99 tools. In Grok, more near-matches means more full schemas pasted in per search.
- Names-only lists grow too. Claude Code's name list was about 1.3k tokens for 113 names. Scaled up, a 1,000-name list is still cheap, but the model is now picking from 1,000 names with no descriptions.
Our honest answer: you can expose 1,000 tools, but you shouldn't make an agent choose between them. Group them into fewer, broader tools with a parameter, or ship a small default set and put the long tail behind one documented search or a separate server. Then test with the agents your users run, in their default settings. We haven't measured a 1,000-tool server. Everything above comes from one 99-tool server.
What we didn't test
- Other servers. The cost per tool depends heavily on how long your schemas are.
- Cursor, Windsurf, VS Code, ChatGPT and other clients.
- More than one model per agent. Each agent ran its owner's usual model, so the live success rates don't rank the agents.
- Versions after 16 October 2026. These agents change weekly. Codex 0.161, for example, lists its older
tool_searchtool as removed.
If you'd like these measurements for your own MCP server, CLI or API, across the agents your users run, talk to us or read the FAQ. More notes are on the blog.
Get your MCP server tested across Claude Code, Codex, Grok Build and OpenCode. We run the jobs your users care about through each agent and show you what breaks, with numbers.
Talk to usSources
- Anthropic, Connect Claude Code to tools via MCP (tool search,
ENABLE_TOOL_SEARCH, output limits), read 16 Oct 2026. - OpenAI, codex-rs/code-mode-runtime globals.rs (
ALL_TOOLS). - xAI, grok-build tool_index.rs (BM25 tool search) and MCP Servers docs.
- OpenCode, V2 MCP servers docs (Code Mode default).
- Spirit Room captures and live runs, 16 Oct 2026: request bodies recorded with a process-scoped proxy, credentials redacted; o200k counts from
tiktoken.