MCP vs REST: The Bill Comes in Tokens
What tool descriptions actually cost, and where each approach earns its keep
Written 5 August 2026. Every figure below is a measurement from late 2025 or the first seven months of 2026, taken before the specification revision of 28 July 2026 changed some of the rules they were measured under. Treat them as dated, and measure on your own workload before you plan around them.

What fits where
Attach GitHub's official MCP server to an assistant with every toolset switched on, and the conversation opens with a bill of roughly 17,600 tokens. Nobody has asked anything yet. That is the cost of the tool descriptions alone — the names, the sentence explaining what each tool does, the shape of the arguments it expects — and it is paid again on every request for as long as the server stays attached.
The "MCP versus REST" argument is almost always conducted as though it were a matter of taste, settled by whichever side sounds more modern. It is not a matter of taste. There is a price list, the numbers on it are fairly harsh, and once you have seen them the choice stops being about architecture and starts being about whether the thing you are building actually needs a model to decide anything.
This article is about that price list: what the descriptions cost, why adding more tools makes an assistant worse rather than better, where a plain HTTP call is still the right answer, and what can be done about the bill.
Start with the bill
Start with the bill
A token is the unit a language model is billed in and bounded by. The model here is any LLM you like; the brand changes nothing about the unit. It is a fragment of text — roughly three quarters of a word in English, rather less in Russian, where the same sentence costs more tokens to say. The bound is the context window: the fixed amount of text a model can hold in view at one time, covering the conversation, whatever you pasted into it, and everything the tools have said about themselves. Every number below is a share of that window, spent before the model does anything you would recognise as work.
- A model does not "connect" to a tool the way a program connects to a database. It is handed a written description of every available tool, in full, at the start of every request, and it reads that description again each time
- GitHub's official MCP server: roughly 17,600 tokens of tool descriptions with the full toolset enabled, before a question is asked. That number belongs to a configuration rather than to the product — the server groups its tools into toolsets that can be switched off, and switching the unused ones off cuts the figure several times over. Which is the point: the number cannot honestly be quoted without saying which toolsets were on
- Attach several servers at once — an issue tracker, a browser, a database — and 30,000 tokens and up is ordinary
- Estimates from early 2026 put a typical desktop assistant at 40–50% of its context window consumed before the first question. Percentages are the wrong unit here, and I quote them only because that is how the estimates were published. The tokens are the fact: 17,600 for one server with everything enabled, past 30,000 once several are attached. A larger context window makes the same descriptions look cheaper without making them cheaper — the share falls, the count does not move, and you pay it on every request
- The comparison that sobers people up: the same operation performed through MCP costs 4 to 32 times more tokens than the equivalent command-line call
Four to 32 times is not overhead. Overhead is a percentage. This is a different order of magnitude, and it deserves to be said plainly: for any task you can solve with a script, MCP is not worth paying for. A nightly job that reads a file and posts it to an endpoint needs no tool descriptions, no model reading them, and no bill. The price starts buying you something at exactly the point where you can no longer write the list of calls in advance.

The cost in tokens
Why "just add more tools" makes things worse
Why "just add more tools" makes things worse
A task that ran reliably with ten tools starts coming apart at fifty. The instinct is to blame volume — more text, a fuller window, worse answers — and volume is the smaller half of the problem. The real problem is selection. The model is choosing one item from a list, and every item it did not pick is a distraction it had to read and rule out. Two tools whose descriptions differ by a single clause are not two options; they are a coin toss the model performs on your behalf, once per call, silently. Long lists also encourage the model to plan in tool calls rather than in steps, which is how a job that needed three calls turns into eleven.
Tool responses cost more than tool descriptions
Tool responses cost more than tool descriptions
The second cost gets discussed less and is frequently larger than the first: the responses. Descriptions are a fixed charge, paid per request. Responses are metered. A call that lists pull requests returns author objects, timestamps, avatar URLs and review metadata that nobody asked for, and all of it lands in the same window as the descriptions. It is entirely normal for a session to spend more context on what the tools said back than on the catalogue of what they could do.
The cost that appears on no invoice
The cost that appears on no invoice
The third cost is the one I paid personally, and it appears on no invoice at all. I run a small MCP server that manages a publication calendar and posts articles. Its Medium adapter drives a signed-in Chrome over the debugging protocol, because Medium's write API has been left to rot. The adapter considered its work finished the moment the browser's address changed to the editing URL of a new draft, and then closed the tab. Medium, meanwhile, saves the editor's contents incrementally, over background requests that had not yet finished. Everything that had not reached the server by the time the tab closed was lost, silently. The adapter returned a valid URL and a success.
The audit on 25 July 2026 covered all 25 calendar entries that carried a published URL. Twelve live posts on Medium were truncated, retaining between 41% and 72% of their text, and some had lost code blocks. A thirteenth URL answered with HTTP 410. Short announcements of around 1,500 characters had survived, because they finished saving before the tab closed. Nothing else was affected: dev.to, Stack Overflow and Hacker News all came through intact. That is the detail worth pausing on, because Stack Overflow and Hacker News are driven through a browser exactly as Medium is. The dividing line was never browser against API. It was one submission against many: those three hand over the whole text in a single POST, while Medium accepted it in instalments and my adapter stopped watching after the first one.
The reason it went unnoticed for weeks is the ordinary reason. The check that existed asked whether the URL was alive, not whether the article was all there. The post loads. The link works. Only the second half is missing.
Two things follow, and neither of them is about Medium. The first is that a tool returning success has told you nothing about whether the work happened. MCP distinguishes two levels of failure — the protocol broke, or the tool ran and reported bad news — and this is a third kind that neither level can see, because from the protocol's point of view the call went perfectly. The second is about tokens. On that request the model paid for twenty tool descriptions, paid again for the call, received ok, and moved on to the next item in its plan. The spend was real. The work was not done. Anonymous statistics about tool calls that never complete get quoted a lot and sourced almost never. I cannot tell you what is behind them. I can tell you what was behind mine, and that nothing will tell you when it is happening to you.
There was a second bug sitting beside the first, and it is the more instructive of the two. The adapter tried to detect Medium's save-failure banner by searching the page text for phrases like "something is wrong". One of the articles being published contained the sentence "if something is wrong you flip back". A successful run was declared a failure, and the retry created a duplicate draft. Status read out of prose is not status.
Where REST is still the better answer
Where REST is still the better answer
REST did not get worse when models arrived. It stayed exactly as good as it was, and the cases it was good at are still the majority of cases.
Keep it wherever the calls are known in advance and made by a program rather than chosen by a model. If you can enumerate the calls while writing the code, enumerate them: you get a compiler, a code review, a stack trace and a test suite, none of which a model's choice of tool will ever give you. High volumes of similar requests are the other obvious case — a quarter of a century of HTTP caching, ETag, CDNs, load balancers and per-key limits are sitting there, built and debugged by other people, and none of it knows what an MCP server is. A public interface has its own reason to stay a REST API: outside developers want stability, versioning and an OpenAPI description they read once at integration time, not flexibility to be rediscovered on every call. And there is cost. A REST call has a number you can put in a spreadsheet; an MCP call costs whatever the user happened to have attached that day.
The simple rule holds up better than any diagram: if you can write the list of calls in advance, write it. An MCP server sitting inside a scheduled job, with no human in the loop and nothing for a model to decide, is a very expensive HTTP client.
Where MCP earns its keep
Where MCP earns its keep
The price buys one thing, and it is the thing that makes an AI agent an agent rather than a script: the ability not to know, at the time you write the program, what the program will be asked to do. Where that ignorance is genuine, the bill is fair. Where it is a habit of mind, you are paying to avoid making a decision.
- The set of actions is unknown when the program is written
- Users attach what they need, and you cannot know in advance what that is
- The task has several steps, and the next one depends on the result of the last
- You want a human in the loop: to confirm, to refuse, to see exactly what is about to be sent
- One socket instead of nine adapters — provided there really are nine adapters
The fork, in practice
The fork, in practice
Two questions decide it, and they have to be asked in order.
The first is who makes the call: a programmer, or a model? If a programmer, the argument is over before it starts. You have documentation, types, a compiler and a review process, and none of the machinery MCP exists to provide is any use to you. Make the HTTP call.
The second question only applies if the answer to the first was "a model": is the list of actions known when you ship? If it is known, small and fixed — three actions, always the same three — then function calling against your own API will get you most of the way there. You describe the three functions to the model directly, in the request, and skip the protocol between the parts entirely. Model Context Protocol starts paying for itself when the set is genuinely open: when the user attaches what they need, when a server you have never seen announces its tools at runtime, when the next step depends on what the last one returned in a way you cannot enumerate ahead of time.
In practice the answer is almost always "both", and not as a diplomatic compromise. It is what the systems look like. The MCP server I run is a wrapper — five hundred lines of tool definitions, and about as much again in the adapters and verifiers behind them — sitting in front of the same admin API the web interface uses. The endpoints are identical; only the authentication differs — a session cookie for the browser, an API key for the server. Nothing in the calendar was redesigned to be MCP-shaped, and nothing needed to be. What the wrapper adds is exactly the set of things a model needs and a program does not: descriptions written to be read rather than parsed, a deliberately narrower set of actions than the API exposes, and a point where a human can look at what is about to be sent and say no.
So the honest summary is MCP on the outside, REST underneath. It is worth stating the inverse too, because it is the mistake I see most often: if your design has MCP underneath as well — services calling each other over it, batch jobs speaking it, a scheduler holding a tool catalogue — you have bought the price without buying the reason.

The fork
What to do about the cost
What to do about the cost
Four mechanisms exist, and one habit beats most of them.
Tool search: moving the catalogue out of the request
Tool search: moving the catalogue out of the request
Tool search shipped in late 2025 as part of Anthropic's advanced tool-use release, and it moves the catalogue out of the request: the client keeps the full list, and the model is handed only the tools that match the task in front of it. The reduction in startup cost reported in that announcement is 95%. I do not believe that as a general rule. The figure is true for setups that had far too many tools attached to begin with, which means it measures somebody's earlier mistake rather than the effect of the mechanism. Run it against a considered set of fifteen tools and you will not see anything like 95%, because there was never 95% of waste there to remove.
Code execution instead of chains of tool calls
Code execution instead of chains of tool calls
Executing code instead of chaining calls is the interesting one, and the only one of the four that attacks the larger cost. Instead of asking the model to make eleven calls and read eleven responses, you let it write a short program that makes the calls itself. The intermediate data — the four-thousand-line list it filtered down to three rows — never passes through the context window at all. Only the three rows do. Since responses are usually where the money goes, this is where the money comes back.
Sub-agents do not save tokens
Sub-agents do not save tokens
Sub-agents are worth stating carefully, because the claim is usually made backwards. Sub-agents do not reduce total token spend. They generally increase it: each one re-reads its own instructions and its own slice of the tool catalogue, and the coordinator pays again for the summaries coming back. What they protect is the coordinator's context, which stays clear of forty tool descriptions and thousand-line outputs it will never need. That is a real benefit and often worth the money. It is not a saving, and budgeting for it as one is how a bill doubles.
Cacheable tool lists (2026-07-28)
Cacheable tool lists (2026-07-28)
Cacheable tool lists came with the 2026-07-28 revision: a hint from the server saying, in effect, that the client need not ask for this again yet — a direct consequence of the protocol becoming stateless. Mechanical, modest, and most useful in exactly the setups where descriptions dominate the bill.
The cheapest fix: fewer tools, shorter descriptions
The cheapest fix: fewer tools, shorter descriptions
Then there is the discipline that requires no technology whatsoever, works today, and outperforms everything above: fewer tools, shorter descriptions, filtered responses. Fifteen tools with one-line descriptions, returning only the fields somebody actually reads, will beat every mechanism in this section, and you can do it this afternoon without waiting for a specification revision or an SDK.
You will not find "MCP versus REST" speed benchmarks here. They measure implementations rather than protocols, and the number that comes out says more about the server somebody tested than about the choice in front of you.
This is the second of three articles. The first is about where MCP and REST came from and why the July 2026 revision turned MCP back toward REST; the third is about what an MCP server is actually made of.
Sources
Sources
Token cost measurements: public analyses of the GitHub MCP Server and survey write-ups of context overhead from late 2025 and early 2026; the discussion in issue #2808 of the specification repository on schema overhead. The MCP specification is published at modelcontextprotocol.io; this used revisions 2025-06-18 and 2026-07-28. Anthropic's write-up on code execution in place of call chains. The truncation incident of 25 July 2026 is documented in this project's publishing runbook, docs/publishing.md, along with the audit that found it.