Cover image for “MCP and the new agent supply chain”
← blog · 2026-04-15

MCP and the new agent supply chain

Every MCP server you connect is third-party code your model trusts by reading. A pragmatic threat model and the controls that work.

A Model Context Protocol server is a dependency, except worse than the ones you're used to. An npm package runs code you can read. An MCP server runs code and feeds text straight into your model's context, where the model treats it as instruction. So on top of trusting the maintainer, you're trusting that every tool description and every tool result is benign, because your agent will act on both.

The npm and PyPI ecosystems took a decade of typosquatting, dependency confusion, and post-install scripts before "vet your dependencies" became reflex. MCP is running through the same lessons much faster, and researchers have already published working attacks.

The attack surface

Here are six failure modes, roughly in order of how much they surprise people:

  • Tool poisoning: a server's tool description is serialized into the model's context before you type anything. Instructions hidden there read as trusted guidance and never show up in any UI. Invariant Labs demonstrated this in April 2025: a benign-looking add tool whose description told the agent to quietly read ~/.ssh/id_rsa and the local MCP config and exfiltrate them through tool parameters, with cover text to hide the move from the user.
  • Prompt injection via tool results: anything a tool fetches (a GitHub issue, a doc, a web page) enters context as semi-trusted data an attacker can pre-load with instructions.
  • Rug pulls: the spec has no re-consent step when a server changes its tool definitions, so an attacker can ship a clean version, get approved, and then push a malicious update. In September 2025 the postmark-mcp npm package did exactly this: fifteen clean releases, then a version that BCC'd every outgoing email to an attacker.
  • Confused deputy: the GitHub MCP server holds a token scoped to all your repos. Invariant showed (May 2025) that a poisoned public issue could steer an agent into reading the user's private repos and leaking their contents back through a pull request. The server behaved exactly as designed, and that design is what let the attack through.
  • Cross-server shadowing: load several servers and their descriptions share one context. A malicious server can override how the model uses a legitimate one, for example by hijacking a trusted email tool to redirect mail.
  • Plain old RCE: plenty of servers pass model-supplied arguments to a shell. CVE-2025-6514 in mcp-remote (CVSS 9.6, ~437,000 installs before the patch) let a malicious server execute OS commands on the client just by being connected to. In April 2026 OX Security traced the same STDIO command-injection root cause across a dozen-plus AI platforms.

Most of these share one cause: the agent merges trusted instructions with untrusted content in a single context while holding a token broader than the task needs.

Gaps in the MCP trust model

There is no mandatory signing or provenance for MCP servers. They're executables you install from anywhere (a GitHub clone, an npm package, a local binary), and once running they have whatever privileges your user does. The model trusts tool descriptions that no user has validated: you see a summary, and the model sees the full text. Nothing pins a server's definitions, so what you approved on Tuesday can change on Wednesday without a prompt. Today's registries and marketplaces list servers without verifying them.

Controls that reduce the risk

Most of these are the controls you'd apply to any production dependency. A few are specific to having a model in the loop:

  • Allowlist and pin: maintain an approved set of servers, pinned to a version or commit, and re-review every update. That's the rug-pull defense.
  • Scope tokens down and keep them short-lived: the GitHub case is a token problem as much as an injection problem. Make tokens read-only, limited to specific repos, and time-boxed. Never hand a server a credential broader than its documented job.
  • Human approval for consequential calls: anything that sends data out, touches credentials, writes files, or runs a shell command gets an explicit confirmation. At runtime, this is the main thing that stops an injected instruction from becoming an action.
  • Sandbox the server: run it in a container or under a restricted user account with a narrowly bind-mounted directory. The Anthropic filesystem-server escape CVEs (2025) were bad because the process had broad filesystem reach; a sandbox shrinks the blast radius.
  • Review before you deploy, scan after: read the server for unsanitized exec calls, surprising network egress, and credential reads. Invariant's mcp-scan will flag toxic-flow patterns across what you've already installed.
  • Treat tool I/O as zero-trust: design the agent so issue text, document contents, and search results are never followed as instructions, regardless of which server returned them.

Running MCP servers in production

Treat an MCP server as a production dependency with potential OS access and a direct line into your model's reasoning. It gets what production dependencies get: a security review, version management, a patch cadence, and an owner.

For an enterprise running agents, that means a governance function maintaining an approved-server catalog with version control and triggered re-reviews, token scopes enforced at the permission layer rather than by policy text, and monitoring on agent processes for anomalous outbound traffic. Connect as few servers as you can, since each one adds code that can attack you and descriptions that share your model's context. The postmark-mcp backdoor was published to npm in September 2025, and the spec's trust model is still being written. Build as if the next server you add is the compromised one.

> esc
$ keyboard_shortcuts.md esc

// navigation

  • ⌘K or ctrl+K — open command palette
  • ? — toggle this overlay
  • esc — close any overlay
  • / — jump to command palette input

// command palette

  • ↑ ↓ — navigate results
  • ↵ — select / navigate
  • type a tag like #agents — search by tag
  • type a service like workshop — find related pages

// 404 page

  • cd <path> — navigate
  • ls — list pages
  • help — show commands
  • clear — clear history

// agent swarm

  • click anywhere — spawn a task
  • drag a rectangle — dispatch a team
  • hover over a worker — see its model
based on conventions from cmd+k, vercel, linear, and most claude code workflows