AiBook · Jeremy Schoemaker · 2026 · ch-24.html

Stop Rolling Your Own USB

(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.


When it bites


The pattern

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:

  1. The connector layer standardizes fast and goes to zero. MCP went from launch to both major labs shipping it between November 2024 and May 2025, six months. Expect no pricing power from speaking it.
  2. Value moves up-stack, behind the socket. Your data, your permissions model, your latency, your reliability.
  3. Value also moves down-stack, to the runtime. Who supervises the process, scopes the token, logs the call, kills the loop.
  4. The standard’s success is its attack surface. Every client that trusts every server is a transitive-trust graph nobody drew.

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.


One worked example

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 quiet failure

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

Do

Don’t


Where this sits in the book

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.


Sources and receipts

Thesis is Jeremy’s (protocol is plumbing, not a moat): argument, not citation.

Verified:

Not 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.