[ LEARN // MCP SECURITY ]
The MCP threat model
A threat model for the Model Context Protocol enumerates the trust boundaries between agent, MCP client, MCP server, and the downstream systems servers act on — and the attack vectors that cross each boundary. The recurring result: the model layer cannot be made trustworthy, so enforcement must live at the protocol layer.
[ ACTORS AND TRUST BOUNDARIES ]
Four actors form the MCP chain, and each pair defines a trust boundary with its own question. Get the classifications right first — most bad MCP security advice comes from implicitly trusting an actor the model says you cannot.
Every MCP attack vector crosses one of these three boundaries.
Model / agent — untrusted
Untrusted by definition, not by accident. The model consumes untrusted content — issues, web pages, documents, tool outputs — and any of it can steer what the agent decides to do next. No amount of alignment work changes the classification: an actor whose behavior is a function of attacker-writable input cannot be a trusted component.
MCP client / host — semi-trusted
Claude Code, Cursor, or your own harness. It runs on your infrastructure and you control its code — but it executes whatever the model decides. The client is trustworthy as software and untrustworthy as a decision-maker, which is exactly why it cannot be the enforcement point for its own calls.
MCP servers — third-party code holding your credentials
Most MCP servers are community or vendor code that you install and hand a token. They see every call parameter and every response, and they act on downstream systems with whatever scope their credential carries. Each server is a dependency with production access.
Downstream systems — the assets
GitHub, AWS, databases, internal APIs. These are what the whole model exists to protect. They never see the agent — they see the MCP server's credential, which is why bounding that credential is a boundary of its own.
[ THE VECTOR CATALOG ]
Six vectors account for the MCP attacks worth planning against. Each is tagged with the boundary it crosses; each maps to a mitigation that works even when the model is fooled — the test any proposed MCP control has to pass.
Prompt injection → tool misuse
crosses: model → client
Content the agent reads instructs it to act: a README tells it to push a backdoor, an issue comment tells it to dump a secrets file. The attacker needs no access to your systems — only to something the agent will read. The injected agent then issues tool calls that are syntactically legitimate.
▸ Per-call policy enforced outside the agent process — a fooled model cannot execute what the enforcement point blocks.
Tool poisoning
crosses: client → server
A malicious or compromised server ships tool descriptions crafted to manipulate the model — hidden instructions in the description text, or a benign-looking tool whose metadata changes after you have approved it (a rug pull). The model reads tool metadata as trusted context; attackers know that.
▸ Vet and pin servers; review any change to tool names, descriptions, or schemas before agents see it.
Confused deputy
crosses: server → downstream
The server holds a broad credential — an org-wide GitHub token, an admin database role — and acts with that full authority for every caller. An under-privileged agent asks; the over-privileged deputy performs. Authorization the downstream system would have refused is laundered through the server.
▸ Per-agent authorization at the protocol layer, plus server credentials scoped to the minimum the server legitimately needs.
Credential theft
crosses: server & client at rest
MCP server configs and client config files routinely hold long-lived tokens in plaintext. Anyone who reads the file — malware, a leaked laptop image, a committed dotfile — inherits everything those tokens can do, with no agent involved at all.
▸ Short-lived, narrowly scoped tokens; one credential per server; no shared accounts that blur who held what.
Malicious server reading traffic
crosses: client → server
Every tool call routes parameters through the server, and every response routes data back through it. A hostile or compromised server is a passive tap on that stream: query results, file contents, customer records — whatever your agents touch, it sees.
▸ Server vetting, data-minimal calls, egress control — and a governance layer deployed in your own network, so oversight itself adds no new observer.
Response injection
crosses: server → model, then model → client
The poisoned input arrives as tool output: a fetched web page, a file's contents, an API response containing instructions the model obeys on its next step. Call one was legitimate; call two is the attack. Chains, not single calls, are the unit of compromise.
▸ Policy on every call in a chain, not just the first — plus alerts on anomalous call sequences.
[ WHAT A MITIGATED DEPLOYMENT LOOKS LIKE ]
No single control covers all six vectors, but the layered result is short: MCP servers are vetted and pinned before any agent can reach them; both sides of every connection are authenticated; a proxy enforcement point applies per-call authorization between client and servers; each agent acts under a scoped identity of its own rather than a shared credential; and every decision lands in a complete log. The first two layers handle the server-side vectors; the proxy handles everything the untrusted model can be steered into; identity and evidence make the confused deputy attributable and the incident reconstructable.
The proxy's policy is where the threat model becomes concrete. Rules that map directly to the vectors above:
# Default deny: an injected agent's improvised call matches nothing.
- agent: "*"
server: "*"
tool: "*"
action: block
# Tool poisoning / rug pulls: unreviewed servers get no wildcard trust.
- agent: "*"
server: fetch-server # added last week, not yet reviewed
tool: "*"
action: block
# Confused deputy: only this agent reaches this server, on named tools.
- agent: release-bot
server: github
tool: [read_file, create_comment, merge_pull_request]
action: permit
# Credential-adjacent tools pause for a human — always.
- agent: "*"
server: "*"
tool: [create_token, add_deploy_key, update_secret]
action: require_approval
# Response injection: the write after the read is still policy-checked.
- agent: research-bot
server: postgres
tool: execute_query
when: { statement: write }
action: require_approvalEach rule counters a specific vector — the policy file is the threat model, executable.
Note what the rules do not rely on: the model behaving, the prompt being clean, or the server being honest about itself. Every rule binds an identity to an action at the protocol layer, where the untrusted actors cannot rewrite the outcome.
[ WHAT THIS MODEL PREDICTS ]
A threat model earns its keep by predicting which defenses fail. This one makes three predictions. First: because the model layer stays untrusted, guardrails-only defenses fail under injection — filtering prompts and outputs asks the component being attacked to defend itself, and a filter the model can be talked around is not a boundary. Second: because MCP servers are third-party code, gateway-style aggregation without authorization widens the blast radius — one broker fronting twenty servers with pooled credentials is a larger confused deputy, not a control.
Third: the stable equilibrium is AI agent runtime governance enforced at the protocol boundary — identity, per-call policy, and evidence at the one point in the chain that every vector must cross and no untrusted actor controls. That is not a claim that the other layers are optional; vetting, scoped credentials, and monitoring all still appear in the mitigated deployment above. It is a claim about where enforcement can actually hold when the layers above it are compromised — which, for a system whose decision-maker reads attacker-writable content, is the default case to plan for.
[ HOW SENTNELOPS IMPLEMENTS THIS ]
[ FREQUENTLY ASKED QUESTIONS ]
What is the MCP threat model in one sentence?
It is the enumeration of trust boundaries between the AI agent, the MCP client, MCP servers, and the downstream systems servers act on — with the attack vectors that cross each boundary and the mitigation that holds at each one, given that the model layer itself can never be made trustworthy.
Which MCP threat matters most in practice?
Prompt injection driving tool misuse. It requires no attacker access to your infrastructure — only content the agent reads, which for a coding agent includes issues, pull requests, documentation, and web pages. The injected calls are well-formed and authenticated, so only per-call authorization outside the agent process contains them.
What is a confused deputy in MCP terms?
An MCP server holding a broad credential (say, an org-wide GitHub token) that performs actions on behalf of a less-privileged caller. The downstream system sees only the server's authority, so an agent that should never touch a repository can reach it through the deputy. The fix is per-agent authorization at the protocol layer plus scoped server credentials.
Do trusted, official MCP servers remove the need for a threat model?
No. A well-built official server closes the tool-poisoning and malicious-server vectors, but the dominant vector — prompt injection steering the agent — exists regardless of who wrote the server, and confused-deputy and credential-theft risks depend on how the server is credentialed, not on who published it.
Where should mitigations live — client, server, or between?
Between. The client executes what the untrusted model decides, so it cannot referee its own calls; servers are third-party code and each would need its own bespoke controls. An enforcement point on the protocol path between client and servers covers every server at once, cannot be talked around by the model, and produces one uniform decision log.
[ REFERENCES ]
- Model Context Protocol — specification
- MCP specification — security best practices
- OWASP Top 10 for LLM Applications — Prompt Injection & Excessive Agency
- MITRE ATLAS — adversarial threat landscape for AI systems
- What is AI Agent Runtime Governance?
- What is MCP Security?
- SentnelOps: MCP firewall (capability page)