DEV Community

MCP Token Cost: I Cut My MCP Server From 15 Tools to 4

Sarvar Nadaf on October 26, 2026

Here is a number that should bother you. GitHub's official MCP server loads about 55,000 tokens across 93 tool definitions before you ask it a sing...
Collapse
 
alexshev profile image
Alex Shev •

Outcome-level tools help only if their return contract remains inspectable. I would pair each aggregated tool with a compact provenance field for every finding—source check, region, and observation time—then make the detailed payload an explicit follow-up. That keeps the normal context footprint small while preserving a path to audit why the one-call answer is trustworthy.

Collapse
 
sarvar_04 profile image
Sarvar Nadaf AWS Community Builders •

Yeah, agreed. And there's a split worth making here. The cheap stuff source check, region, when it was seen can ride along on every finding. It's tiny and it's enough to trust the answer at a glance. The heavy stuff raw tags, full ARNs, the actual API response shouldn't. That's what detail=True is for.

The trap is treating them as one thing. The second you say "make it auditable" and shove everything into the default return, you've quietly dragged the 40-field blob back in and the saving's gone. So I keep provenance light by default and the real proof on demand, for the one finding I'm about to touch. Going to work this into the next version. Good nudge.

Collapse
 
alexshev profile image
Alex Shev •

Exactly. detail=True should be an explicit escalation path, not a default payload in disguise. I’d add a CI guardrail for the default response schema and size, so a well-meaning new field can’t quietly turn the cheap path back into the 40-field blob.

Thread Thread
 
stefan_07d23f966225156b50 profile image
Stefan •

Alex, I'd include a large result set in that CI check too. Five fields per finding stays small until the account returns 2,000 findings. A bounded page with an explicit "more results" signal would keep the response manageable without making a partial scan look complete.

Thread Thread
 
sarvar_04 profile image
Sarvar Nadaf AWS Community Builders •

yeah a ci check on the default return is exactly the right place for it. the thing that bit me isn't someone adding detail=True logic, it's someone appending "just one more useful field" to the default path over six months until it's quietly the 40-field blob again. so i'd assert two things in ci: the default key set is exactly the allowlist, and the per-finding char/token size stays under a cap. fails the build if either drifts. the schema toggle is easy to review, the slow creep isn't, so you pin it. are you running your tool schemas through ci already or is that a thing you'd be adding fresh?

Thread Thread
 
sarvar_04 profile image
Sarvar Nadaf AWS Community Builders •

Stefan

good point, and that's a real hole in the current demo. my account returns 3 findings so i never felt it, but at 2,000 the lean response stops being lean, five fields times two thousand is its own blob. the fix is a bounded page plus an explicit "truncated, N more" signal so a partial scan never reads as a complete one, that last bit matters most for a cost tool because a silently-cut list makes the agent think it's seen everything and stop. putting a cap plus the truncation flag in the ci size check is the right call. adding it to the next pass.

Collapse
 
tokenlat profile image
TokenLat •

Cutting the tool surface from 15 to 4 is the right reflex — fewer tools means less schema junk in every prompt, and the 88% drop is real. Two hidden costs survive the cleanup, though:

1) detail=True pulls a 40-field blob into context even when the call only needs 3 fields. That's a routing decision, not a schema toggle — route those result-heavy turns to a smaller model and the save holds.

2) Tool definitions get re-sent every turn, so you pay for them N times unless the gateway caches them. Cache them and the "default" per-turn cost approaches zero.

The quiet leak is almost always repeated context, not first-turn schema size. Do you meter per-turn tool-context cost separately, or only the first-turn number?

Collapse
 
sarvar_04 profile image
Sarvar Nadaf AWS Community Builders •

you're right and this is the honest gap in my table, i measured first-turn schema size, not the per-turn replay. tool defs ride at the front of every turn, so you pay them n times across a session unless the client caches the prefix, and with anthropic caching a hit is ~10% of base input so the per-turn cost basically collapses once it's warm. so the real number teams should watch is cached vs uncached per-turn, not the one-time load i put on screen. the routing idea is sharp too, detail=True turns are exactly the ones to send to a cheaper model since they're read-heavy and low-reasoning. i didn't meter per-turn separately, that's a fair hit. are you metering cached vs uncached turns in the gateway, or routing purely on result size?

Collapse
 
sarvar_04 profile image
Sarvar Nadaf AWS Community Builders •

🚀 Want to see the lean MCP approach in action or explore the code?

🎥 Full YouTube Demo:

youtu.be/1EGeu4jCaR0?si=gb0DZ1SZdw...

💻 GitHub - Complete Source Code:

github.com/simplynadaf/aws-lean-mcp

If you're building MCP servers for AWS or AI agents, feel free to fork the repo, experiment with it, and share what you improve. 🙌

⭐ If you find the project useful, consider starring the repo!

Picked as gem
Collapse
 
lin7c profile image
xcl •

The response-bloat point in section 8 changes the usual calculation: even with a lean tool list, returning a full 40-field blob just moves the context cost from the schema into the result. One question on the detail=True escape hatch — what should decide that the extra fields are worth requesting instead of answering from the five default fields? That decision rule seems as important as the field list itself, because an agent that routinely escalates to detail=True would quietly give back most of the 88% saving.

Collapse
 
sarvar_04 profile image
Sarvar Nadaf AWS Community Builders •

Great catch. You're right that detail=True is where the saving can leak back out, so the rule for when to pull it matters as much as the default field list.

The way I draw the line: the 5 default fields exist to answer "what should I do and where" (id, kind, size/ip, why, region). That's enough to decide and act. detail=True is for "prove it, I'm about to change something," not for routine reading. In practice the agent only escalates on two triggers:

  1. A human (or downstream step) explicitly asks for evidence (raw tags, timestamps, full ARNs, cost breakdown) before approving a destructive action.
  2. The 5 fields are genuinely ambiguous for that one finding, so you fetch detail for that id only, not for the whole result set.

The key is that detail=True is per-call on a narrowed scope, never the default path. A well-behaved agent reads the lean list, picks the one or two findings that matter, and only then asks for the blob on those, so you pay the full cost on 2 findings, not 40. The anti-pattern you're describing (routinely escalating) is really a prompt and discipline failure, and I'd treat it the same way I'd treat an agent that calls list-everything "just in case." Worth adding a line to the tool description saying detail=True is for verification before action, so the model doesn't reach for it by reflex.

Honestly this is a good argument for measuring it: log how often detail=True fires vs default calls. If the escalation rate creeps up, the field defaults are wrong, not the escape hatch.

Collapse
 
kami_butt_69b922ade8de012 profile image
Agentic Affairs •

The GitHub server number is the one that should embarrass the whole ecosystem — 55,000 tokens of tool definitions before the first question, a quarter of a 200K window gone. Your 7-to-2 rebuild proves the cost isn't the model, it's the tool surface. One pushback: collapsing 15 tools into 4 concentrates capability — did any question get harder to answer because the one big tool now returns a haystack the agent has to sift through?

Collapse
 
sarvar_04 profile image
Sarvar Nadaf AWS Community Builders •

honestly no, but only because i kept the response lean at the same time, which is the part that saves you from the haystack. collapsing 15 tools into find_waste would create exactly the problem you're describing if it returned everything, the agent gets one giant blob and has to sift it, you just moved the work. the thing that prevents it is the 5-field default, the agent gets a short decision-ready list, not the raw pile. so the two fixes are a pair, outcome-level tool and lean return, do one without the other and you're right, you've just hidden the mess inside the call instead of across seven calls.

Collapse
 
kami_butt_69b922ade8de012 profile image
Agentic Affairs •

That's the pairing I missed — the tool collapse only works because the return got lean at the same time, so the haystack never reaches the agent. It reframes the fix as two halves, not one: if either half slips, a fat return or a fragmented tool surface, the cost comes straight back. Have you found a question where the 5-field default hides something the agent actually needed, or has the short list held up so far?

Collapse
 
skillselion profile image
Skillselion •

That holds in Kiro and in any client that loads tools upfront, but Claude Code now defers MCP tools by default. Its tool search loads only tool names and server instructions at session start, then pulls a full definition when Claude searches for it. So in Claude Code the 88% schema cut buys less than your table suggests, and the 7 to 2 call drop buys more, since each deferred tool can cost a search step before the call. It also changes how to write descriptions: the docs say search matching reads only the first 4,096 characters, so put the words an agent would search for near the start of the description.

Collapse
 
sarvar_04 profile image
Sarvar Nadaf AWS Community Builders •

this is the correction i was hoping someone would make. yeah, my table is a kiro/load-upfront world. in claude code with tool search the 88% schema cut buys way less up front because deferred tools aren't in context until searched, but like you said the 7→2 call drop buys more, since a wrong first pick can cost a search step on top of the call. and the description advice is the part people will miss, if matching only reads the first ~4096 chars then front-loading the searchable words isn't style, it's whether your tool gets found at all. outcome-level naming helps there too, "find_waste" is more searchable than three thin describe_* tools. i should add a line in the post splitting upfront-load clients from tool-search clients, because the fix is the same but the scoreboard isn't. appreciate this.

Collapse
 
useaifix profile image
AIFix •

Cutting 15 tools down to 4 and going from seven calls to two is a great concrete result. I'd be curious whether merging similar tools into one with a clear parameter was the biggest saver, or the shorter descriptions.

Collapse
 
sarvar_04 profile image
Sarvar Nadaf AWS Community Builders •

merging, by a lot. the char count makes shorter descriptions look like the win (7,898 → 897) but that's mostly a side effect of going from 15 tools to 4, fewer tools means fewer descriptions to load. the real saver for the agent was collapsing the workflow, 7 calls down to 2, because that's where the retries and wrong-tool picks were burning tokens turn after turn. shorter descriptions help, but a tight description on 15 thin tools still leaves the model stitching the workflow itself. merging into outcome-level tools is what took that job off the model.

Collapse
 
golflover2023 profile image
golflover •

The 88% schema shrink is the headline, but 7 calls down to 2 is the part teams miss: bloat taxes tokens once at load, confusion taxes them on every turn through wrong-tool picks and retries. One caution from experience - pruning pushed more parameter reasoning back onto the model until we wrote concrete examples into the remaining 4 descriptions. Lean schemas are not free, they shift work to the model's own inference. Did you compare answer quality on the cost-triage question, or only call count?

Collapse
 
sarvar_04 profile image
Sarvar Nadaf AWS Community Builders •

i compared findings and cited ids, not a formal quality eval, so fair hit. both versions landed the same 3 resource ids and the same read-only behavior, that's the "did it get the right answer" check. what i didn't do is run it n times to see variance, which is where your point lands, lean schemas push parameter reasoning back onto the model and that's exactly where flakiness would show up. with two params per tool and literals on scope i had less room to go wrong, but the "write concrete examples into the remaining descriptions" thing you hit is real, a thin description on a powerful tool is worse than a fat one on a dumb tool. did adding examples fix the parameter drift fully for you, or just reduce it?

Collapse
 
nexariza_ai profile image
Nexariza AI •

Spot-on breakdown on MCP schema bloat! We hit this exact wall in production enterprise deployments at Nexariza AI — once a single agent context crosses ~12-15 MCP tool definitions, not only do input token costs spike on every turn, parameter hallucination rates jump noticeably under noisy inputs. Consolidating CRUD primitives into coarse intent-scoped tools (or routing via a lightweight supervisor so each specialist sub-agent only mounts 3-5 domain tools) has been our highest-ROI fix for both latency and accuracy. Great benchmark numbers!

Collapse
 
sarvar_04 profile image
Sarvar Nadaf AWS Community Builders •

Yeah! Thank you so much

Collapse
 
alexcodebytes profile image
Oleksandr •

The 'outcome-level' tool design is the real unlock here. Wrapping every single API endpoint 1:1 is why agents get confused and start hallucinating.

Also, I really appreciate the honesty about the context meter. Most posts about MCP token bloat show a dramatic 70% drop on a tiny demo, which is mathematically impossible. Your point that it saves 0.3% on 15 tools but 20%+ on a production setup of 90+ tools perfectly explains why this matters at scale.

To answer your question: my primary MCP server has 12 tools, but reading this makes me want to go refactor them all into 3 or 4 outcome-level tools right now.

Collapse
 
sarvar_04 profile image
Sarvar Nadaf AWS Community Builders •

Thank you, you picked out the two bits I cared about most.

The 1:1 wrapping thing is the whole problem. When every tool is just a thin mirror of an API, the model has to be the one stitching the workflow together, and that stitching is where it starts guessing and hallucinating. Outcome-level tools take that job off the model it just says "where am I wasting money" instead of running five list-and-filter calls and hoping.

And yeah, I wasn't going to fake the number. On 15 tools it's basically nothing. This only earns its keep once you're past 90-odd tools always loaded, which is exactly where you end up after bolting on a few MCP servers. The "70% saved" posts running a 5-tool demo are selling a story the math doesn't back up.

12 is a nice place to start from enough to feel it, small enough that going to 3 or 4 is a weekend. If you do it, measure the context before and after on one real prompt and drop the number here. I'd love to see a 12→4 from an actual setup instead of another demo.

Collapse
 
alexcodebytes profile image
Oleksandr •

"Outcome-level tools" is the exact phrase I needed. The model shouldn't be a workflow orchestrator stitching together five different API endpoints; it should just be a decision-maker calling one high-level action.

I'll take you up on the 12→4 challenge. My context is currently bloated with exactly those thin 1:1 wrappers. I'll measure the token count on a complex prompt before the refactor, collapse them into 3-4 outcome tools, and drop the real numbers here once it's tested.

Thanks for keeping the benchmark honest!

Collapse
 
alextaylor369 profile image
Null Terminus •

This reminds me of why l love minimal software. We always try to build 15 features, but only 4 are actually doing the real work.🙌

Collapse
 
eternaclarity profile image
Jesse Gamble •

Four tools instead of fifteen is a big reduction.

Collapse
 
sarvar_04 profile image
Sarvar Nadaf AWS Community Builders •

Yes

Collapse
 
build996 profile image
build996 •

The read-only IAM guidance feels especially important once find_waste combines several services behind one call. I'd want consolidation to preserve a user's ability to request only checks their role permits, rather than making the broadest read policy a prerequisite for the smaller tool surface. Can the lean version run a useful subset under an EBS-only role? That seems like a useful second comparison alongside the full-permission account used in the demo.

Collapse
 
sarvar_04 profile image
Sarvar Nadaf AWS Community Builders •

really good question and it exposes a genuine tradeoff i glossed over. right now find_waste runs all the checks behind one call, so under an ebs-only role the volume check works but the eip/snapshot/nat checks throw on the describe calls. the honest behavior i'd want is partial: run what the role permits, and return a "skipped: no permission for X" note per check rather than failing the whole call or silently dropping it. that keeps the small tool surface without forcing a broad read policy as the price of entry, which is your actual point. the bloated v1 gets this "for free" because each check is its own tool the role can allow/deny, so an ebs-only vs full-permission comparison is a fair second scoreboard, consolidation shouldn't cost you least-privilege. noting it for the next version.

Collapse
 
heyvaldemar profile image
Vladimir Mikhalev AWS Community Builders •

Consolidation moves the permission check from the tool list into the tool body. Once find_waste fans out to several services, the server's read policy is the union of all of them, and every caller inherits it unless the tool filters for them.

The air-gapped agent I run has one search tool over thirteen company documents, each naming in its front matter who may read it. The MCP server ranks only the documents both the agent and the person it acts for may read, and the audit names every document a search returned, so a document beyond that person's rights never reaches the model.

The same shape fits find_waste: run only the checks the caller's role permits, and log which ones ran. Fewer tools, same boundary.

Collapse
 
build996 profile image
build996 •

Filtering before ranking is the useful detail in your document example: filtering afterward could already expose a forbidden title or snippet. For find_waste, I'd want the allowed checks derived from the authenticated caller, with the requested subset only narrowing that set. The audit would then say both what ran and what was excluded by policy. Does your server also enforce that intersection on direct document retrieval, so a known document ID can't bypass search filtering?

Thread Thread
 
heyvaldemar profile image
Vladimir Mikhalev AWS Community Builders •

There is nothing to bypass: the server has no tool or resource that returns a document by id. search_documents is the only way to one, and it scores only documents whose audience is in what grant() returns, the agent's audiences intersected with the person's, before any ranking happens.

So a known id buys the model nothing. It can name q3-margins all it likes; no call it was offered takes that name. If a fetch-by-id tool is ever added, it has to read the same intersection, or the search filter becomes decoration.

Collapse
 
kevinhall profile image
Kevin Hall •

The server-side half of this has a client-side twin that's easy to miss. With Apify's MCP server I pin the tool list in the URL (?tools= with the two Actors I need), so the session starts with a short list instead of the whole catalog. Claude Code then defers even those: in a session today the agent's first move was a tool search that loaded one schema, the job-search Actor's, later one dataset reader, and nothing else.

Your v2 rules still matter more, though, because deferral only helps until a tool is loaded. After that its whole schema rides along on every turn, so for an Actor with thirty-odd inputs the field descriptions are the next thing to trim.

Collapse
 
dexoryn profile image
Dexoryn •

The point about response bloat is especially important.

Even if you reduce the number of tools, returning huge JSON payloads can quietly bring the context cost back.

Designing tools around outcomes and returning only the fields needed for the decision seems like a much better approach than simply trying to make every tool description shorter.

Collapse
 
stefan_07d23f966225156b50 profile image
Stefan •

Thanks for picking that up, Sarvar. I'd check the agent's final summary in that larger test too. If it only reads page one, it should say "these are the findings reviewed so far" rather than give an account-wide savings total. That would show whether it's actually using the truncation flag.

Collapse
 
eye_java_420f1faa10ae8b86 profile image
Nan •

Your rule is that an escalation rate that's too high means the defaults are wrong. The inverse failure is quieter: an escalation rate near zero doesn't prove the five fields are enough, it can just mean the agent never felt the gap.

The shape I keep hitting in unattended runs is a finding where id, kind, size, region and why are all present and consistent, but they don't disambiguate. Two resources look like the same waste pattern and only the tags or the observation window tell you which one is real. Nothing in the default payload looks missing, so the agent decides and acts, and the token meter looks excellent. The cost of that decision shows up on the account, not in the context window, and the two dashboards never sit next to each other.

So alongside the escalation rate I'd log the inverse event: every action taken without detail=True, with the finding id and the fields it was based on. Sampling a handful a week and reading them as if the agent had been wrong is the only way I know to tell "the defaults were sufficient" from "the defaults looked sufficient".

Collapse
 
ascendra_ventures_92c21f2 profile image
Ascendra Ventures •

The GitHub MCP figure is the number that should get attention: 55K tokens across 93 tools before the agent has done anything. Going from 15 tools to 4, and from 7 calls to 2 for the same task, shows that the cost was in the shape of the interface, not the model.

Oleksandr's framing of outcome level tools versus 1:1 API wrappers is the part I would repeat to any team building one of these. A wrapper mirrors how the backend is organised, while an outcome tool mirrors what the caller is trying to finish.

xcl's point about response bloat and a detail flag that defaults to off covers the other half, since a lean schema still burns tokens if every response returns everything. Stefan's bounded pages with a "truncated, N more" signal and Alex Shev's CI check on the default schema are good ways to keep it lean after the first release.

Did the honest context meter change which tools people asked you to keep?

Collapse
 
panthpatel profile image
Panth Patel •

The read-only part is what makes the merge safe. find_waste can run five checks behind one call because none of them change anything. On my ticket tracker I went the other way for agents: they get the same APIs the frontend uses, nothing more, so anything an agent does to the board is something a person could have done the same way. That is close to your v1 shape. Would you collapse a write surface like that too, or keep writes one tool per action?

Collapse
 
paullovelace profile image
Paul Lovelace •

The bit that stuck with me was the honesty about the context meter — 1.1% to 0.8% is hardly a headline, and that's exactly why I believed the rest of it. I'd been assuming the win was mostly in the schema, but your point about the 40-field boto3 blob made me rethink that: a lean tool list doesn't help much if find_waste still hands back everything by default. How are you deciding when detail=True is actually worth it in practice, or is that still a judgement call per finding?

Collapse
 
sarvar_04 profile image
Sarvar Nadaf AWS Community Builders •

still a per-finding judgement, but i try to make it a rule not a vibe. the 5 defaults answer "what and where" which is enough to decide and act. detail=True is for "i'm about to touch this one, prove it", so it fires on two triggers only: a human asks for evidence before a destructive step, or those 5 fields are genuinely ambiguous for that single id. the key is it's per-call on one narrowed finding, never the whole list, so you pay the full blob on 2 findings not 40. if an agent reaches for it by reflex that's a prompt problem, and i'd log the escalation rate to catch it, if detail fires on most calls the defaults are wrong, not the hatch.

Collapse
 
whiteknightonhorse profile image
whiteknightonhorse •

The 55,000 tokens for 93 GitHub tools is a number more MCP READMEs should show. We hit the same wall with a catalog of well over a thousand tools and ended up with one free discovery tool that ranks candidates and returns only their schemas. Did the four-tool version get worse on questions outside cost triage, or did it still cover them?

Collapse
 
kartik-nvjk profile image
Kartik N V J K •

With scope typed as a Literal, I'd expect FastMCP to reject a bad value while validating arguments, before find_waste even runs. Did the "scope must be one of" message from fix 5 ever reach the model in a test, or does it get the pydantic validation error instead? That text is what the model retries on, so I'm curious which one it actually reads.

Collapse
 
agentwareshq profile image
agentwares •

One trade-off worth naming alongside the token savings: an outcome-level tool also concentrates failure. With seven thin tools, a single failed check is easy to isolate and retry. With find_waste doing the fan-out internally, a failure in one sub-check should not sink the whole answer.

A useful pattern is partial results: return the findings that succeeded, plus a small per-check status saying which checks ran, which were skipped, and which failed and why. That keeps the two-call flow, but the agent still knows what is missing instead of guessing whether no waste means no findings or a broken check. It also makes the lean response easier to trust, since the summary and its coverage are in the same payload.

Collapse
 
mudassirworks profile image
Mudassir Khan •

the 7 calls down to 2 is the number that sells it, but watch what fat composite tools do to failure handling. we collapsed a few thin tools into one triage tool and a single failing sub call suddenly hid behind a partial result, so the agent confidently reported on data it never got.

we now return a per section status next to the payload so the model can see which part is missing. did your v2 tools return partial results when one boto3 call gets throttled, or fail the whole call?

Collapse
 
rivenpark profile image
Riven Park •

A follow-up to the permission discussion: I'd test what happens when access is revoked between the lean result and the detail request. Fetch a finding, remove the caller's access, then request detail for that same ID in the same session. The earlier result should not act as an authorization grant; the detail path needs a fresh permission check. That would test a different failure mode from filtering the initial list correctly.

Collapse
 
steven_r_404 profile image
Steven Ray •

Really solid work! I like how you went beyond explaining MCP token overhead and actually tested both approaches with the same AWS account and question. The reduction from 15 tools to 4, along with 7 tool calls down to 2, makes the benefits much easier to understand. The part about keeping responses lean instead of returning huge JSON payloads was especially useful. Practical experiments like this make MCP design much easier to learn. Great work sharing the code and the actual results!

Collapse
 
aifrontierpost profile image
AI Frontier Post •

Cutting schemas from 7,898 to 897 chars is a clean win, but the honest number is the context meter only moving from 1.1% to 0.8% on the small demo. That gap is what teams should test before claiming big token savings in production.