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
 
stackmasteraliza profile image
Aliza Ali •

"Response bloat is half the problem" deserves its own post. On long sessions I'd expect trimming the 40-field boto3 blob to be the bigger win, because every bloated result stays in the transcript and gets re-read on every later turn. One nuance on the 55K figure: with prompt caching, static tool definitions get much cheaper in dollars after the first call, but they eat exactly as much context window. So 15 to 4 tools mostly buys back context and tool-selection accuracy, while trimming responses buys back both. Seven calls down to two for the same question is the number that really sells it.

Collapse
 
sarvar_04 profile image
Sarvar Nadaf AWS Community Builders •

"response bloat is half the problem deserves its own post" is fair, i gave it one section when it probably earns its own piece, because you're right that it's the bigger win on long sessions. schema cost is paid once at load, but a fat result sits in the transcript and gets re-read on every later turn, so trimming the 40-field blob compounds in a way the schema cut doesn't. and your caching nuance is the one i had to be corrected on earlier in this thread too, caching makes those 55k of tool defs cheap in dollars but they still eat the exact same context window, so 15→4 mostly buys back context and tool-selection accuracy while trimming responses buys back both. that's the honest split. the 7-2 is the seller because it attacks the per-turn cost, not the one-time load. you clearly think about the transcript-replay cost, are you trimming historical tool results outof context after N turns, or keeping them full and just eating the re-read?

Collapse
 
stackmasteraliza profile image
Aliza Ali •

Honestly, I'd rather not eat the re-read. The pattern that makes most sense to me: keep the last few tool results verbatim, and swap older ones for a one-line summary plus an ID the agent can re-fetch if it needs the detail again. The transcript stays cheap, and the agent doesn't lose track of what it already checked. That would make a great follow-up post too!

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
 
vinmay profile image
Vinmay Nair •

Interesting results. I've been experimenting with a related problem in Whetherman, trying to figure out whether an agent actually needs to call a tool in the first place.

One thing I found was that even when an agent could answer correctly without tools, it would still call them surprisingly often and when I removed access to tools it sometimes generated more tokens or got the answer wrong.

I think there's another layer to optimizing MCP costs here. Reducing the number of tools makes sense, especially when several tools can be combined into one. But an agent can still make unnecessary calls with just 4 tools. So even after simplifying the server, there's still the question of whether a tool needed to be called at all.

Collapse
 
sarvar_04 profile image
Sarvar Nadaf AWS Community Builders •

this is a sharp layer on top of mine, and honestly the next question after the one i answered. i cut the number of tools, but you're pointing at whether a tool should've been called at all, which is a harder problem. the thing you found, that agents over-call even when they could answer cold, and sometimes do worse with tools removed, is a real tension: tools are a crutch and a liability at once. 4 tools still leaves room for an unnecessary call. what's steering it in whetherman, prompt discipline or something structural in the tool surface?

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
 
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
 
sarvar_04 profile image
Sarvar Nadaf AWS Community Builders •

honest answer, my four tools are scoped to cost triage so outside that lane they don't pretend to cover it, i'd rather return nothing than a confident wrong answer. your discovery-tool approach is the right pattern for a thousand-plus catalog though, one free ranking tool that returns only candidate schemas is basically deferral done server-side, you never load the catalog, you load the two that matched. did the discovery step add a noticeable latency hit before the real call, or was the ranking cheap enough that it disappeared?

Collapse
 
whiteknightonhorse profile image
whiteknightonhorse •

Measured it just now. On the server side a discovery call takes about 40 to 65 ms, against about 30 ms for a plain health check, so the ranking itself costs tens of milliseconds. What the agent feels is the extra network round trip: from my laptop in Bangkok that is around 0.7 s, the same as any other request. It usually happens once per task, and the agent then calls the tool it picked directly.

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
 
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
 
sarvar_04 profile image
Sarvar Nadaf AWS Community Builders •

the client-side twin is a real point and i didn't cover it. pinning the tool list in the url so the session starts short, then letting claude code defer even those, is the inbound version of what i did server-side. and you're dead right that deferral only helps until a tool loads, after that its whole schema rides every turn, so for a 30-input actor the field descriptions are the next thing to trim. the two approaches stack, they don't compete. that's a cleaner way to put it than i managed.

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!

Thread Thread
 
sarvar_04 profile image
Sarvar Nadaf AWS Community Builders •

love this. you nailed it better than my own post: decision-maker calling one high-level action, not a workflow orchestrator. yes please drop the before/after numbers here once you've tested it, the measured ones not the vibes. one thing that bit me on the refactor: watch the response payload too, not just the tool count. collapsing 12 wrappers into 4 wins nothing if each one still hands back the full 40-field blob. what's your context bloated with mostly, thin read wrappers or fat returns?

Thread Thread
 
alexcodebytes profile image
Oleksandr •

Yep, and this is where most refactors go to die. People obsess over the tool count and completely ignore the payload. I’ve seen servers go from 20 tools down to 5, but each call still dumps a 400-token JSON blob back into the context, so you didn't actually save any money.

To answer your question: mine was mostly the "fat returns" problem, but specifically, it was nested metadata. A single read call would return the object, then an array of tags, then another nested array of permissions. Stripping that down to just the essential IDs and the current state was what actually dropped my token usage, not the tool reduction itself.

Definitely post the before/after numbers when you have them. The "vibes" are always great after a refactor until you see the actual context window graphs flatline.

Collapse
 
sizzlebop profile image
Jessica Doering •

This is a great post. I've built several MCP servers myself, and context efficiency is something I've become increasingly conscious of, especially when using multiple servers together.

I think it's really easy to fall into the trap of exposing every little capability as its own tool because it seems like the most logical approach when you're building it. But then you start looking at everything the model has to work through, and suddenly all those individual tools don't seem so harmless anymore.

I especially appreciate that you tested both versions with the same task and showed the actual differences. And your point about response bloat is so important too. It's not just about how many tools you're exposing, but how much unnecessary information you're returning every time one gets called.

Really useful article.

Collapse
 
sarvar_04 profile image
Sarvar Nadaf AWS Community Builders •

thanks, and coming from someone who's built several this means more. the trap you named is the exact one: every little capability as its own tool feels like the most logical thing while you're building it, and only looks harmful once you see everything the model has to read on every turn. and yeah, the response-bloat half is the one people miss, it's not just how many tools, it's how much you return each time. testing both versions on the same task was the only way i'd believe my own numbers, so i'm glad that part landed.

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
 
sarvar_04 profile image
Sarvar Nadaf AWS Community Builders •

you've put your finger on the exact line i wouldn't cross. read-only is the only reason i felt safe collapsing five checks into one find_waste, nothing it does is reversible-sensitive, worst case it reads something it didn't need to. a write surface i'd keep one tool per action, and for the same reason your frontend-parity approach works: an outcome-level write tool hides what's actually about to change behind one friendly name, and that's precisely where you don't want the model guessing. your shape, agents get the same apis a person would and nothing more, is the safer default because the blast radius of a mistake is already a thing a human could've done and presumably can undo. so my honest rule is collapse reads for tokens, keep writes granular and explicit for safety, the token saving on writes isn't worth the loss of a clear confirmation surface. do you gate those write apis behind any human-in-the-loop step for the agent, or is frontend-parity the whole guardrail?

Collapse
 
panthpatel profile image
Panth Patel •

Collapse reads for tokens, keep writes explicit: that rule holds up. Parity isn't my whole guardrail. A token only narrows what its person can do, no MCP tool deletes a ticket, and an agent's work stops at Review until the owner moves it to Done. Would find_waste ever propose a fix, or stay report-only?

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
 
sarvar_04 profile image
Sarvar Nadaf AWS Community Builders •

this is the sharpest version of the failure-concentration point in the thread, and you're right that it's the real tax of merging. one honest correction on my end though, in v2 find_waste already wraps each scan in a try and appends a check_skipped finding instead of dying, so a single failed check doesn't sink the call. where you're still ahead of me is that check_skipped is buried in the same findings list as real results, so the agent can't cleanly tell "nothing found" from "this check never ran." your per-check status block, which ran / skipped / failed and why, is the fix, it makes coverage a first-class field next to the findings instead of a stray row. that's going into the next version. when you return that status, do you put it at the top of the payload or alongside each finding? a couple people here found the agent ignored it when it sat at the
bottom.

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
 
sarvar_04 profile image
Sarvar Nadaf AWS Community Builders •

this is a failure mode i hadn't even written down, and it's a real one. right now detail=True and explain_resource both re-run the scans live, so in practice they'd hit the current permissions, but i never designed that as a guarantee, it's an accident of the implementation, not a stated rule. the trap you're describing is treating the earlier lean finding as proof the caller is still allowed to see the detail, which turns a stale result into a quiet authorization grant. the right behavior is the detail path does itsown fresh permission check for that id, independent of whatever the list returned a few turns ago, revoke access mid-session and the detail call should fail even though the id is "known." that's a different test from filtering the initial list correctly, and i wasn't testing it at all. noting it. do you force that re-check even within a single call chain, or only across turns where revocation is more plausible?

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
 
sarvar_04 profile image
Sarvar Nadaf AWS Community Builders •

yeah, that's the right assertion and it's sharper than just checking the flag exists in the payload. a truncation flag the agent ignores is worse than no flag, because now it looks safe while still reporting an account-wide savings total off page one. so the real test is behavioral, truncate the result on purpose, then read the final summary and
check it says "findings reviewed so far" and not a total, if it still hands you an account number the flag is decoration. that's a prompt-and-eval check, not a schema one, which is probably why i'd have missed it. when you tested this on your own, did the model respect the "reviewed so far" framing on its own once the flag was there, or did you have to put that instruction in the tool description to stop it from totaling?

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
 
sarvar_04 profile image
Sarvar Nadaf AWS Community Builders •

honest answer, v2 fails the whole call right now, which is the weaker behavior and your throttle example is exactly where it bites. if one boto3 describe gets throttled the agent either loses everything or, worse, reads a partial as complete and reports on data it never got, same trap you hit. the fix is the per-section status you're describing, return what succeeded plus a status per check so the model can see the hole instead of confidently filling it. a few people in this thread landed on the same pattern so i'm convinced it belongs in the next version. did returning the per-section status actually change the agent's behavior, or did you also have to prompt it to check the status before summarizing?

Collapse
 
mudassirworks profile image
Mudassir Khan •

it helped but not on its own. the agent still summarized the partial payload when the status sat at the bottom of the json, so we moved it to the first key and added one line to the prompt: never summarize a section marked failed. after that it stopped filling the gap itself. are you planning to return the status per tool call in v3, or per section inside one call?

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
 
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
 
sarvar_04 profile image
Sarvar Nadaf AWS Community Builders •

good recap, and you pulled the right four threads out of it, outcome vs wrapper, response bloat, the ci check, and the bounded page. to your question, the honest meter didn't change which tools people wanted kept so much as which number they trusted. once i showed 1.1 → 0.8 instead of a fake 70%, the conversation moved off "how big is the drop" and onto the things that actually bite at scale, per-turn replay, partial-failure status, the detail=True leak. nobody asked me to drop a tool, but a few started asking whether 4 was even the right cut or whether find_waste should split by service for least-privilege, which is a better question than the one i started with. the honesty basically bought a better class of pushback, which is the whole reason i posted it.

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
 
sarvar_04 profile image
Sarvar Nadaf AWS Community Builders •

this is the best pushback i've gotten on the detail=True rule and it exposes a blind spot in how i framed it. i said a high escalation rate means the defaults are wrong, but i never said the quiet half, a near-zero rate doesn't mean they're right, it can just mean the agent never felt the gap. your example is exactly the dangerous one, two findings where id/kind/size/region/why all look present and consistent but only the tags disambiguate which one is real waste. nothing looks missing, so it acts, and the cost lands on the bill not the context meter, and those two dashboards never sit next to each other like you said. logging the inverse event, every action taken without detail with the id and the fields it leaned on, then sampling a handful a week and reading them as if the agent had been wrong, is the only honest way to tell "sufficient" from "looked sufficient." i was only instrumenting the loud failure. this is going in the next version. are you sampling those blind by id or weighting toward the findings that led to an
actual action?

Collapse
 
sherotree profile image
Sherotree •

The 55k tokens-before-you-ask number is brutal. Cutting schemas until the agent only sees a handful of tools feels like the same hygiene as keeping a public API small — fewer choices, fewer wasted turns.

Collapse
 
sarvar_04 profile image
Sarvar Nadaf AWS Community Builders •

yeah, the 55k number is the one that got me to actually measure my own instead of assuming. and the public-api analogy is exactly right: fewer choices, fewer wasted turns, same hygiene as not shipping a 200-endpoint api surface nobody can navigate. the only thing i'd add is the response half, keep the returns small too, or a tiny tool list still bloats the transcript with fat payloads.

Collapse
 
tomzafred profile image
Tom Zafred •

Consolidating 15 granular tools down to 4 cuts the standing per-turn declaration tax immediately, though there is a subtle second-order failure mode worth watching once multiple actions share a single polymorphic JSON Schema.

When a consolidated tool merges 4 distinct actions into one flat parameter bag, optional fields that belong only to branch B still sit in the active schema while the model calls branch A. Adding prose warnings inside property descriptions ("DO NOT pass filter_expr when action is summary") often backfires under transformer self-attention because naming the forbidden field primes its token salience in the KV cache.

Across my own zero-dependency stdio MCP servers, pairing two structural boundaries kept both standing schema tokens and parameter hallucination near zero:

  1. Hot-path eager vs cold-path lazy partitioning: Only 1 to 4 high-frequency tools stay eagerly declared in the per-turn prompt; administrative, rollback, and inspection tools stay lazy-loaded on demand (0 standing schema tokens on normal turns).
  2. Response payload compaction (compact: true): Input schemas pay a flat tax per turn, whereas verbose JSON tool outputs append permanently to the transcript and compound O(N) across a 50-turn session. Returning dense single-line telemetry receipts cut my cumulative transcript token growth by roughly 75%.

Curious whether your 4 consolidated AWS tools dispatch via a single action enum or separate domain-scoped schemas, and whether you saw any parameter bleed across branches on smaller models.

Collapse
 
sarvar_04 profile image
Sarvar Nadaf AWS Community Builders •

this is the most useful comment on the post, thank you for the depth. the polymorphic-schema failure mode is real and i hand-waved past it: a flat parameter bag where branch-B fields sit live while the model calls branch A. and your point that prose warnings backfire under self-attention, naming the forbidden field primes its salience in the kv cache, is something i genuinely hadn't thought through. to answer you directly: my 4 tools dispatch via a scope enum (Literal account/region/service) on find_waste, not separate domain-scoped schemas, so each tool is one action, not one tool merging four branches. that's why i dodged the parameter-bleed problem, i sidestepped it rather than solving it. your hot/cold eager-vs-lazy split plus compact:true single-line receipts is a level past what i shipped, especially the point that input schemas are paid per turn but verbose outputs compound O(N) across the session. i didn't test on smaller models, so i can't claim anything about branch bleed there, and i won't pretend otherwise. saving this comment.

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
 
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
 
sarvar_04 profile image
Sarvar Nadaf AWS Community Builders •

sharp catch, and you're right about the order. with scope typed as a literal fastmcp/pydantic rejects a bad value during argument validation, before find_waste runs, so the model gets the pydantic validation error, not my hand-written "scope must be one of" string. that custom message only fires if a bad value somehow reaches the function body, which with the literal it basically never does. so in practice the model retries on the pydantic text. if i wanted my friendlier message to be the thing it reads i'd have to validate inside and loosen the type, which i don't think is worth it, the pydantic error is clear enough to retry on. good question, i should clarify that in the post because the way it's written implies my message is what reaches the model.

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.

Collapse
 
sarvar_04 profile image
Sarvar Nadaf AWS Community Builders •

that's exactly the line i wanted people to take from it. the 1.1 → 0.8 is real and small, and if i'd dressed it up as a dramatic drop the whole post would deserve the eye-roll. the principle only pays at scale, 0.3% on 15 tools, 20%+ on the 90-odd tools a real setup loads, and the honest move is to test it in your own /context before trusting anyone's headline, including mine.

Collapse
 
kevinhall profile image
Kevin Hall •

Response bloat and definitions, yes. One more half worth checking: on a hosted MCP server I measured 20 tools, and the output schema inferred from sample rows was the larger half in 9 of them, about 32K tokens for one. Per tool numbers:dev.to/kevinhall/mcp-tool-cost-20-...

Collapse
 
sarvar_04 profile image
Sarvar Nadaf AWS Community Builders •

This really insightful information Great work 🙌🏻

Collapse
 
tsdc profile image
TSDC Company •

Hello Dev Community

Collapse
 
sarvar_04 profile image
Sarvar Nadaf AWS Community Builders •

Hey

Collapse
 
acckess profile image
acckess •

Thread Thread
 
sarvar_04 profile image
Sarvar Nadaf AWS Community Builders •

What's this?

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
 
sarvar_04 profile image
Sarvar Nadaf AWS Community Builders •

exactly that. the 15-to-4 wasn't really about tokens in the end, it was the same lesson minimal software keeps teaching, most of the surface is there to feel complete, not to do work. the agent just makes the cost of that visible faster.

Collapse
 
uumuse profile image
Rory •

Totally agree that provenance metadata should be lightweight by default. Keeping the default schema bounded while having an explicit high-detail mode is the right design for agent tools.

Collapse
 
sarvar_04 profile image
Sarvar Nadaf AWS Community Builders •

Exactly 💯

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
 
pebbles111 profile image
Pebbles •

This is really interestiing things that you cover

Collapse
 
sarvar_04 profile image
Sarvar Nadaf AWS Community Builders •

Yes thanks 👍🏻

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!

Picked as gem
Collapse
 
sarvar_04 profile image
Sarvar Nadaf AWS Community Builders •

Thank You So Much for your kind words 💯

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
 
build996 profile image
build996 •

The per-check skipped result sounds useful, especially if the final summary distinguishes "nothing found" from "not checked." Otherwise an EBS-only run could still look like a clean bill for the whole account even after the permission errors are handled. Would you include the checks requested, completed, and skipped alongside the savings total? That would let a caller compare two runs without mistaking broader permissions for newly discovered waste.

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
 
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
 
sinarezaei profile image
Sina Rezaei •

One edge case I'd test is partial failure across regions. Say find_waste(scope="account") scans EBS volumes, snapshots, and NAT gateways across several regions, but DescribeSnapshots gets throttled in eu-west-1 after a few pages. If the tool returns the findings it collected without making that gap explicit, the agent could report an incomplete cost audit as if it had checked the whole account.

I'd include a compact scan status per service and region, with pagination completion and failed checks, alongside the findings. That keeps the default payload lean without confusing "nothing found" with "not fully checked." Fewer tool calls are great, but the aggregated result still needs to tell the agent what it couldn't verify.

Collapse
 
alexshev profile image
Alex Shev •

This is a stronger optimization than simply shortening descriptions: it reduces the number of capabilities the model must distinguish. I’d keep a small task→tool acceptance test suite alongside the server, so every tool removal or consolidation proves common intents still resolve correctly and ambiguous ones fail closed.

Collapse
 
alexshev profile image
Alex Shev •

The reduction is compelling because it treats tool design as an interface problem, not just a token problem. I would keep a regression suite of representative questions beside the smaller surface: fewer tools are only a win if the agent still chooses the right capability at the boundary cases.

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
 
konstantin_0e4c2bb99bde9d profile image
Konstantin • • Edited

I was using local tools even before MCP was invented, so I never ran into such issues. When the universal standard was released, it was immediately obvious to me that it had been designed by a complete fool, so I fixed everything. I have never experienced any of the problems you described. It seemed obvious to me, and everyone had taken measures.