AI Security

MCP Security: Real Attacks, the Controls That Stop Them, and an Approval Checklist

MCP security is mostly your job: the spec makes authorization optional and cannot tell a poisoned tool from an honest one. This guide covers the real MCP attacks and CVEs to date, a threat-to-control table mapped to OWASP, and a server approval checklist where every item is a pass/fail test.

Chase Dillingham

Chase Dillingham

Founder & CEO, TrainMyAgent

11 min read 14 sources cited
MCP MCP Security AI Agent Security OWASP Supply Chain Security Enterprise AI
Security architecture diagram for MCP server deployment in enterprise environments

MCP security means making sure a Model Context Protocol server, and the agent that calls it, cannot become an attacker’s route into your data. The protocol gives you OAuth-based authorization for HTTP servers. It does not vet servers, read tool descriptions for you, or stop a trusted server from turning hostile. That part is yours.

Below: the real attacks so far, a threat-to-control table, and a server approval checklist of pass/fail tests.

What MCP security covers, and what the spec leaves to you

The current spec revision is dated 2026-07-28. Its authorization page says authorization is OPTIONAL. HTTP servers that support it SHOULD follow the OAuth 2.1 flow. Servers on the stdio transport SHOULD NOT. They “retrieve credentials from the environment” instead. So a local server is as safe as whatever its process can reach.

The tools page puts the rest on the server. Servers MUST validate all tool inputs, implement access controls, rate limit tool invocations, and sanitize tool outputs. Clients MUST treat tool annotations as untrusted unless they come from trusted servers. The protocol checks none of this. A server that skips every rule still speaks valid MCP.

Practice lags the spec. In a study published in October 2025, Astrix analyzed more than 5,200 open-source MCP server implementations. About 88% required credentials. 53% relied on static API keys or personal access tokens, which Astrix describes as long-lived and rarely rotated. Only 8.5% used OAuth.

Decision rule: “it speaks MCP” tells you nothing about security. Approve servers one at a time, against tests. For each spec revision’s changes, see the MCP roadmap and revision timeline.

What are the main MCP security risks? The attacks so far

Every row below is a published case, with the date it went public.

DateCaseWhat failedClass
Apr 1, 2025Invariant Labs tool poisoning demoHidden tool description text made Cursor read SSH keysTool poisoning
May 26, 2025GitHub MCP issue injectionA public issue made an agent leak private repo dataIndirect prompt injection
Jun 13, 2025CVE-2025-49596, MCP InspectorUnauthenticated local proxy; a web page could run commandsExposed local server
Jul 9, 2025CVE-2025-6514, mcp-remoteA server’s OAuth endpoint value became an OS commandMalicious server vs. client
Aug 5, 2025CVE-2025-54136, “MCPoison”Approved config swapped later, no new promptRug pull
Sep 25, 2025postmark-mcp backdoorFake Postmark server BCC’d outgoing emails to an attackerMalicious package
Jan 20, 2026mcp-server-git CVEsPath traversal and argument injection in the reference serverVulnerable server code
Aug 12, 2026Deadbugz campaignServer rewrote its tool metadata after three callsRug pull, supply chain

Three patterns stand out. The model reads tool metadata as instructions. Trust granted once is rarely re-checked. And many failures are ordinary bugs in software that happens to speak MCP. Standard AppSec handles the bugs. The first two need MCP-specific controls.

Tool poisoning and rug pulls: why install-time approval is not enough

Tool poisoning works because the client passes each tool’s description and schema to the model as context. Invariant defined it as “malicious instructions” in tool descriptions that “are invisible to users” but visible to the model. Their poisoned add tool told the agent to read SSH keys. A variant they called shadowing let a malicious server redirect a trusted server’s send_email tool to the attacker, even when the user named another recipient.

A rug pull is the same attack with a delay: pass review, then change. MCPoison showed the client side. Cursor tied trust to an MCP entry’s name, not its contents, until Cursor 1.3 (July 29, 2025) required re-approval for any config change. Deadbugz showed the server side. Pillar found 23 pull requests, opened within 74 minutes on August 10, 2026, adding a server called productivity-suite. After a client’s third tool call, its tools/list and prompts/get responses told the agent to collect SSH keys, AWS credentials, shell history, and Kubernetes config, and to hide it. None had been merged through GitHub’s merge mechanism when Pillar reviewed them: 19 were closed and 4 were still open.

The 2026-07-28 tools page helps here. The tool list “MUST NOT vary per-connection or as a side effect of other requests.” Deadbugz-style gating breaks that rule, so it is testable. But the list MAY change over time, with a list_changed notification. A legitimate update and a rug pull look the same on the wire.

Control: fingerprint the tool definitions at approval and re-approve on any change. The function takes the tools array, not the whole tools/list result, so merge all pages first if there is a nextCursor. It covers tools only. Deadbugz also changed prompts/get output, so hash each prompt the same way.

import hashlib, json

def tool_fingerprint(tools: list[dict]) -> str:
    """SHA-256 over the canonical JSON of a tools/list result."""
    canon = json.dumps(sorted(tools, key=lambda t: t["name"]),
                       sort_keys=True, separators=(",", ":"), ensure_ascii=False)
    return hashlib.sha256(canon.encode("utf-8")).hexdigest()

Early detection: alert when a fingerprint differs from the approved one. Also alert when it changes between two calls with the same token and no list_changed notification arrived. Servers SHOULD send one to clients subscribed through subscriptions/listen. If you subscribed, a silent change has no spec-sanctioned explanation. Treat it as high severity and investigate.

Confused deputy, token passthrough, and other auth failures

A confused deputy attack hits MCP servers that proxy a third-party API. The MCP security best practices list four conditions: one static client ID at the third party, dynamic client registration, a third-party consent cookie, and no per-client consent at the proxy. With all four, an attacker registers a client with their own redirect URI and sends the victim a link. The cookie skips the consent screen, and the authorization code lands on the attacker’s server. The fix is a MUST: per-client consent before the third-party flow, and exact matching on redirect URIs.

Token passthrough means forwarding whatever token the client sends. The authorization spec forbids it: servers “MUST NOT accept or transit any other tokens.” Audience binding enforces the rule. Clients send an RFC 8707 resource parameter, and servers reject any token not issued for them. Passthrough also breaks attribution: downstream logs “may show requests that appear to come from a different source with a different identity.”

Three smaller rules close the remaining gaps. Clients MUST validate the iss value in authorization responses per RFC 9207, which blocks mix-up attacks between authorization servers. Clients deployed on servers MUST consider SSRF, because a malicious server can point OAuth discovery at addresses like 169.254.169.254, the cloud metadata endpoint. And wildcard scopes such as files:* turn one stolen token into broad access.

The roadmap post covers how the 2026-07-28 auth changes work, including the deprecation of Dynamic Client Registration. Per-agent credentials are covered in AI agent identity.

Malicious and vulnerable servers: the supply chain problem

A third-party MCP server is a dependency that runs with your credentials. The cases above show four ways that goes wrong.

Impersonation. The postmark-mcp package was not from Postmark. Per The Register, the attacker “built trust over 15 versions, then added a backdoor in version 1.0.16.” It drew about 1,500 downloads in a week.

Hostile server, vulnerable client. In CVE-2025-6514 (CVSS 9.6), mcp-remote 0.0.5 to 0.1.15 passed a server-supplied authorization_endpoint to a URL opener that, on Windows, ran PowerShell. Version 0.1.16 fixed it. The security best practices now say clients MUST NOT use shell commands to open URLs and MUST reject javascript: and similar schemes.

Exposed local server. MCP Inspector before 0.14.1 (CVE-2025-49596, CVSS 9.4) ran a local proxy that launched commands without authentication. A malicious web page could reach it through 0.0.0.0 or DNS rebinding. The fix added a session token and origin checks.

Ordinary bugs in reference code. Cyata found path traversal and argument injection in Anthropic’s own mcp-server-git, fixed in versions 2025.9.25 and 2025.12.18. Chained with the Filesystem server and triggered by prompt injection, they reached code execution. Cyata’s CEO called it “the canonical Git MCP server, the one developers are expected to copy.”

Decision rule: a local stdio server is code execution with your user’s privileges. Run it in a sandbox with no access to home-directory secrets. Approve a remote server like a SaaS vendor that receives your data.

How OWASP classifies MCP risk

The OWASP Top 10 for Agentic Applications, released December 9, 2025, gives your risk register shared IDs. Four entries cover most MCP risk. ASI02, Tool Misuse and Exploitation, defines tool poisoning as compromising “MCP tool descriptors, schemas, metadata, or routing information.” ASI03 is Identity and Privilege Abuse. ASI04, Agentic Supply Chain Vulnerabilities, names a “Compromised MCP / Registry Server” as an attack scenario. ASI05 is Unexpected Code Execution. The document also defines “least agency”: avoid unnecessary autonomy, because “deploying agentic behavior where it is not needed expands the attack surface without adding value.”

The OWASP Top 10 for LLM Applications now has a 2026 edition, which replaces the 2025 list. For MCP, the key entry is LLM03:2026, Excessive Agency. Its three root causes are excessive functionality, excessive permissions, and excessive autonomy. One prevention item: “execute tools in user’s context.”

My recommendation: tag every MCP finding with an ASI or LLM ID, since auditors already read those lists. For goal hijack through retrieved content, see prompt injection in AI agents.

The MCP threat-to-control table

Each row maps a threat to the control that stops it and the layer that owns that control.

ThreatReal caseControl that stops itOwnerOWASP
Tool poisoningInvariant demoReview full descriptions before approvalClient, approval teamASI02
Rug pullMCPoison, DeadbugzFingerprint definitions; re-approve on changeClient or gatewayASI04
Tool shadowing and name collisionsInvariant send_emailPrefix tool names by serverClient or gatewayASI02
Malicious packagepostmark-mcpVerify publisher; pin by hashApproval teamASI04, LLM04
Vulnerable server codemcp-server-gitInput validation; track advisoriesServer ownerASI05
Indirect injection via tool resultsGitHub MCPLeast data per agent; confirm writesClient, serverASI01, LLM01
Confused deputySpec attack flowPer-client consent; exact redirect URI matchProxy serverASI03
Token passthrough, wrong audienceSpec anti-patternRFC 8707 audience check; separate downstream credentialServerASI03
Mix-up attackSpec attack flowValidate iss per RFC 9207ClientASI03
Malicious OAuth URL or SSRFCVE-2025-6514https only; no shell; block private IPsClientASI05
Exposed local serverCVE-2025-49596Loopback, token, origin checksServer, clientASI05
Over-broad or static credentialsAstrix: 53% static keysNarrow, short-lived, per-server tokensServer ownerASI03, LLM03

The pattern: narrow what each server can do, pin what it says it does, and check every token’s audience.

MCP server approval checklist: pass/fail tests

The MCP gateway guide covers the approval workflow. This checklist is the test step inside it. Run it in a sandbox with fake data and fresh tokens.

#TestHow to run itPass if
1Publisher and pinCompare publisher with vendor docs; read the lockfileVendor-published or your own fork; exact version and hash
2Known CVEsCheck GitHub advisories and OSV for the packageNo open advisory at the pinned version
3DescriptionsRead every tool, parameter, and prompt descriptionNo instructions about other tools, files, secrets, or hiding
4FingerprintHash the merged tools array with the function above; hash each prompt tooHashes stored in the register
5StabilityHash the tools array, make five calls, hash again, same tokenIdentical hash
6No tokenCall the endpoint with no Authorization header401; resource metadata found via the WWW-Authenticate header or the well-known URI
7Wrong audienceSend a valid token issued for a different resource401
8ScopeCall a write tool with a read-only token403 with insufficient_scope
9PassthroughMake one call; read the downstream audit logDownstream sees the server’s own credential
10State handlesCreate a handle as user A; use it as user BRejected
11Input validationSend ../ paths and arguments that start with --Rejected with isError: true; nothing written
12EgressLog traffic during sample tasksOnly declared hosts; email goes only to the recipients you set
13Local reachCall the local port from a browser page; read ~/.ssh from inside the server’s runtimeBoth refused

Scoring. Tests 1, 3, 5, 7, and 9 are hard gates. One failure means no approval: in each case above, one gap was enough. For a read-only server on non-sensitive data, tests 8 and 10 can be marked not applicable. Re-run tests 2 to 5 on every version change, and run test 5 on a schedule in production. The gate choices are my judgment, not a standard.

Bottom Line

MCP security is an approval and monitoring problem more than a protocol problem.

The 2026-07-28 spec has strong auth rules for HTTP servers, but it cannot vet a server or stop one from changing. Real attacks so far came through poisoned descriptions, swapped configs, fake packages, and plain bugs.

Run the 13 tests on every server you already use, starting with anything that can send, write, or execute. Then put the fingerprint check on a schedule.

FAQ

Is MCP secure by default?

No. Authorization is optional, and stdio servers take credentials from the environment. In Astrix’s October 2025 study, 79% of API keys were passed through environment variables.

What is the difference between tool poisoning and prompt injection?

Invariant describes tool poisoning as a specialized form of indirect prompt injection. The difference is timing. Poisoned metadata reaches the model when a client lists tools, before any call. A typical injection arrives later, inside data a tool returns.

Can two MCP servers expose tools with the same name?

Yes. The spec scopes tool name uniqueness to a single server, and says the server name a client sees is not guaranteed unique either. Clients that combine servers should prefix tool names with a server identifier they control.

Do stdio MCP servers need OAuth?

No. The spec says stdio implementations should not follow the OAuth flow. The control is consent plus a process boundary. A client that offers one-click local setup must show the exact command, untruncated, and get explicit approval before running it.

Which MCP vulnerabilities should I check for first?

Confirm these minimum versions: mcp-remote 0.1.16, MCP Inspector 0.14.1, mcp-server-git 2025.12.18, and Cursor 1.3. Then search every MCP client config for productivity-suite, the Deadbugz server name.

Should I still use the OWASP LLM Top 10 2025?

Use the 2026 edition for new work. In it, Prompt Injection is LLM01, Excessive Agency is LLM03, Supply Chain is LLM04, and Hidden Context Exposure is LLM08. Pair it with the Agentic Top 10 for agent and MCP risks.

Three Ways to Work With TMA

Need an agent built? We deploy production AI agents in your infrastructure. Working pilot. Real data. Measurable ROI. → Schedule Demo

Want to co-build a product? We’re not a dev agency. We’re co-builders. Shared cost. Shared upside. → Partner with Us

Want to join the Guild? Ship pilots, earn bounties, share profit. Community + equity + path to exit. → Become an AI Architect

Need this implemented?

We design and deploy enterprise AI agents in your environment with measurable ROI and production guardrails.

About the Author

Chase Dillingham

Chase Dillingham

Founder & CEO, TrainMyAgent

Chase Dillingham is the founder and CEO of TrainMyAgent. He spent 15+ years as an enterprise architect deploying production systems. Now he builds AI agent platforms that deliver measurable ROI.

Connect with Chase Dillingham on LinkedIn