(Spine Ch. 24.)
“The nice thing about standards is that there are so many to choose from. And if you do not like any of them, just wait a year or two.” Andrew S. Tanenbaum and David J. Wetherall, Computer Networks, 5th edition (2011)
Somebody spent fifteen releases being a good citizen. Fifteen clean
versions of an npm package called postmark-mcp, shipped,
tagged, downloaded, trusted. Version 1.0.16 quietly started BCC’ing your
outbound email to a server you have never heard of. Around the same
window, a separate package with 437,000 downloads turned out to run OS
commands on your box not when you called a bad tool, but the instant you
connected. You invited both in with one line in a config file, and
neither needed you to make a mistake. There is a reason I now read the
diff.
Bottom line: MCP is USB for tools. Before it, every agent framework hand-rolled its own connector shape, and every integration you wrote was a snowflake that died when you switched frameworks. MCP ended that argument, not because it’s brilliant, but because Anthropic and OpenAI agreed on it inside four months, and a standard two rivals agree on stops being a debate. Now the part nobody wants: the protocol is not the product. Nobody buys your MCP server because it speaks MCP. They buy what’s behind it. And the moment you plug an untrusted server into your loop, you have handed a stranger a tool call inside your process.
npx an MCP server off npm because a blog post said
to, and it inherits your shell and your filesystem with nobody reviewing
the diff. I have done this. Fast, at night, credentials still loaded.
That’s what she said, and she was right to be concerned.MCP does three things and you should be able to say them in one breath: tools (what the model can call), resources (what it can read), prompts (canned instructions the server offers the client). Anthropic open-sourced it on November 25, 2024 as “a new standard for connecting AI assistants to the systems where data lives.” Block and Apollo were integrating on day one; Zed, Replit, Codeium, and Sourcegraph were named in the same announcement.
For four months that was a nice Anthropic thing. Then on March 26, 2025, Sam Altman posted that OpenAI would add MCP support across its products: “People love MCP and we are excited to add support across our products.” Two months later the Responses API shipped remote MCP servers.
A competitor adopting your protocol is not a compliment. It’s a concession that the protocol layer is worthless as a moat: they’d rather commoditize it than fight over a connector shape. The plug is never the business.
The actual pattern:
Point 4 ate 2025 and 2026. When one protocol wins, attackers stop writing seventeen exploits and write one.
The receipts, in order. July 9, 2025: JFrog
discloses CVE-2025-6514 in mcp-remote (CVSS 9.6, OS command
injection, 437,000+ downloads), triggered when the client connects
to an untrusted MCP server. When you connect at
all.
September 25, 2025: postmark-mcp, a malicious npm
package impersonating Postmark. In Postmark’s own words: “A
malicious actor created a fake package on npm impersonating our name,
built trust over 15 versions, then added a backdoor in version 1.0.16
that secretly BCC’d emails to an external server.”
January to February 2026: researchers filed more than 30 CVEs against MCP servers, clients, and infrastructure. A survey of 2,614 servers found 82% path-traversal and 34% command-injection exposure. OWASP stood up its first MCP Top 10.
Then the one that should change how you write tool descriptions. The MCPTox benchmark evaluated 45 live MCP servers across 20 LLMs and measured an average tool-poisoning attack success rate of 36.5%, worst case 72%. The attack lives in the tool’s metadata, the description the model reads before calling it. Your agent can be tricked by a manifest, one time in three, against live servers.
None of this means don’t use MCP. It means the trust boundary is the connection, not the call, and if your plan was “I’ll validate the arguments,” you’re guarding the wrong door.
August 7, 2026: I spent a morning blaming a server for
killing my client. The client was killing itself. The Rust SDK
for MCP had a stack overflow in
SseAutoReconnectStream::poll_next, and the recursion fired
on every skipped SSE event, not every malformed one. Connect to
a server emitting frames the client cannot deserialize, and each skip
stacks another until the process takes a SIGABRT and dies.
From the inside that looks exactly like the other end being broken,
which is why I read someone else’s server code for an hour. Then I
reproduced it properly: 50,000 undeserializable frames at unmodified
main, stack overflow, dead process. The fix turned that
recursion into a loop, 164 lines added and 124 deleted. I filed
modelcontextprotocol/rust-sdk #1146 at 10:37Z and it merged
at 14:34Z.
Same day, 17:57Z, I filed #1152 for the sibling bug: a 401 or 403
carrying a WWW-Authenticate header on the SSE GET stream
came back as an opaque client error instead of AuthRequired
or InsufficientScope, so an expired token got refreshed on
the POST path and never on the GET path. Same connection, two doors, one
got the memo; 136 lines and four tests later, both doors read the same
header, merged 19:39Z.
The SDK for the standard could be killed by whatever it connected to, and the trigger was the events it chose to ignore. Not the payload you validate. The one you drop on the floor.
Also that month, four duller lessons. Seven merges into
mozilla-ai/mcpd between August 4 and 15 were config
validation and docs, and the config work buried the best one: a plugin
binary missing its execute bit looks exactly like a plugin that was
never installed, and I have burned hours hunting a chmod I
never ran. In mozilla/firefox-devtools-mcp #147 I deleted a
declared resources capability whose handlers were stubs, so
clients now get -32601 Method Not Found instead of
-32603 Internal Error, the difference between “I don’t do
that” and “I am broken”: a manifest that lies still lies. Shipping AIR
across 14 public repositories on August 15 worked because of a
conformance gate running end-to-end tests against three independent
CLIs, which caught a dataset_version type mismatch nobody
had caught by reading the code, and by “nobody” I mean me. And the
WorkOS MCP integration, built August 8 and 9 then left dormant, got read
wrong three ways by a model council before probes corrected all of it:
reading an integration and probing one produce different answers.
The tool floor, in code: eldritchdm. My D&D
agent project, shoemoney/eldritchdm (Python 3.11+, Apache
2.0, 514 commits as of September 9, 2026), connects to five MCP servers:
dm20, dice, dnd, fetch, and party_mode. It can discover their tools at
runtime like any other client. It ignores what discovery tells it.
src/eldritch_dm/mcp/tools.py holds a dictionary named
TOOL_TO_FUNCTION that maps roughly 40 named tools to the
functions allowed to run them, and anything else a server advertises
cannot be called. Discovery says what exists. The dictionary says what
happens. This is not elegant. It is a bouncer with a clipboard, and the
clipboard has 40 names on it. If you lift one thing from this chapter,
lift that: hard-code the allowlist before first run, and let discovery
add names to the menu, never to the clipboard.
I did not write it out of principle. My memory is that a server offered my dice bot a tool that had no business anywhere near a game night, and I decided rolling for initiative should not have reach over anything I would miss. I never wrote down which server or which day. I built a bouncer with a clipboard and kept no door log, which is about the most me thing in this book.
The loud failure is obvious: you rolled your own protocol and now maintain a dialect nobody else speaks. The quiet one is worse.
You treat adopting MCP as having done the work.
“We’re MCP-compatible” is the 2026 version of “we have an API”: table stakes stated as an achievement.
Second quiet failure: you count integrations instead of measuring them.
That already got caught once, and the guy holding the inflated number was me. On August 24, 2026, a council review of my own contribution claims found that counting merged PRs hid quality: a 95-line Agents SDK bug fix counted the same as a one-line doc-link fix.
Same disease, applied to tools. “We expose 40 MCP tools” tells you
nothing. Four are load-bearing, thirty-six are get_status
variants, one can write to prod. That last one pwns you. Count the
dangerous ones.
Do
Don’t
npx a server you haven’t read. You’re granting
process-level trust to a stranger’s dependency tree.Ch. 17 was tools. This chapter is the wire they travel on, and the argument that the wire is not where your value lives. Ch. 29 is agent-to-agent, where the thing on the other end has its own goals. Ch. 43 is who answers for tool poisoning. Ch. 54 is OSS opens doors: fixing a crash in the standard’s own SDK beats a hundred blog posts about it.
The plug is standard. Make what’s behind it worth plugging into, and know exactly what you’re plugging into yourself.
Thesis is Jeremy’s (protocol is plumbing, not a moat): argument, not citation.
Verified:
mcp-remote, OS command injection on
connecting to an untrusted server: JFrog, July 9, 2025.
https://jfrog.com/blog/2025-6514-critical-mcp-remote-rce-vulnerability/postmark-mcp: 15 clean versions, backdoor in 1.0.16:
Postmark, September 25, 2025.
https://postmarkapp.com/blog/information-regarding-malicious-postmark-mcp-packagedataset_version bug:
~/Projects/airank/blog/2026-08-15-shipping-air-everywhere.mdSseAutoReconnectStream::poll_next: GitHub,
modelcontextprotocol/rust-sdk #1146, August 7, 2026.
https://github.com/modelcontextprotocol/rust-sdk/pull/1146modelcontextprotocol/rust-sdk #1152, August 7, 2026.
https://github.com/modelcontextprotocol/rust-sdk/pull/1152mozilla-ai/mcpd, August 4 to 15, 2026.
https://github.com/mozilla-ai/mcpd/pulls?q=is:pr+author:shoemoney+merged:2026-08-04..2026-08-15resources capability and stubs removed: GitHub,
mozilla/firefox-devtools-mcp #147, August 3, 2026.
https://github.com/mozilla/firefox-devtools-mcp/pull/147TOOL_TO_FUNCTION deterministic tool floor, ~40 tools
across five servers (dm20, dice, dnd, fetch, party_mode), 514 commits:
GitHub, shoemoney/eldritchdm, verified September 9, 2026.
https://github.com/shoemoney/eldritchdm/blob/main/src/eldritch_dm/mcp/tools.pyNot sourced, so not claimed: “six months from launch to both labs shipping it” is my own framing of the November 2024 to May 2025 sequence, not a measured statistic. Several other stories I had heard about MCP failures never produced a report, a CVE, or a filing I could read, so they are not in this chapter at all.