Security Research
MCP Server OWASP Top 10: What the Threat Map Actually Looks Like After 101 Servers
We scanned 101 community and vendor-official MCP servers and tried to map every finding to an OWASP category. Fifty percent had SSRF. Thirty-eight percent leaked credentials. Forty-two ended with an F grade. The mapping exercise was instructive — not because OWASP fits cleanly, but because of exactly where it breaks down.
2026-05-31 · 12-min read · All posts
Why standard OWASP reviews miss MCP servers
When someone says "we do OWASP reviews," they usually mean one of two things: they run OWASP ZAP or a comparable DAST scanner against an HTTP endpoint, or they walk through the API Top 10 checklist as a threat-modeling exercise. Both approaches were designed for services that receive authenticated HTTP requests, return structured responses, and operate in an environment where the caller is a human or a human-written client with deterministic inputs.
MCP servers fail all three assumptions simultaneously. Here is what makes them structurally different, and why those differences matter for the threat shape.
First, the caller is an LLM, not a human or a deterministic client. In a normal API context, even a compromised caller has human-authored payloads that get filtered, rate-limited, and logged. An LLM caller generates its arguments by inference over a context window that can include arbitrary third-party content — a document the user asked it to summarize, a repository it was asked to review, a web page it fetched. Every one of those content surfaces is a potential injection vector into the tool arguments. This changes the effective attack surface of every input parameter in the entire server from "one attacker per auth token" to "anyone who can get content into the agent's context window."
Second, the threat model straddles two different OWASP lists. OWASP publishes an API Security Top 10 (last updated 2023) and a separate Top 10 for LLM Applications. An MCP server is simultaneously an API (it exposes a protocol surface with authentication, authorization, and resource handling) and an LLM plugin (it is invoked by a model, can receive model-influenced input, and can inject content back into a model context). No single checklist covers both halves. Reviewers who only know the API list miss the injection story. Reviewers who only know the LLM list miss the classic API failure modes that still apply.
Third, the consequence model is different from either archetype. An API vulnerability usually affects one tenant's data or one service boundary. An LLM plugin vulnerability affects every conversation the model is having while the server is connected — and because MCP servers are persistent across sessions rather than per-request auth objects, the blast radius of a single misconfiguration is the entire lifetime of that server install. A credential echo that returns an API key into a tool response does not just affect the current request; it hands a secret to the model's context, where it can be persisted in logs, summarized into memory, or relayed to other tools in the same session.
None of this means OWASP is wrong. It means OWASP was not designed for this specific composition. What follows is what the data actually looks like when you try to apply both lists to a real corpus.
The six classes that show up in real servers
Before we go category by category through OWASP, it is worth anchoring to the empirical finding distribution. Across 101 scanned servers — a mix of vendor-official releases (Stripe, PayPal, AWS, Azure, Cloudflare, MongoDB, GitHub, Heroku, Neon, CircleCI and dozens more) and widely-used community servers — six finding classes dominate.
In frequency order:
- SSRF — 50% of corpus (50 of 101 servers). A tool handler accepts a URL or URL component from the caller and passes it to an HTTP client without an allow-list. In MCP context this is particularly acute because the "caller" may be an LLM whose context has been poisoned by external content. Full technical breakdown at /seo/mcp-server-ssrf.
- Credential exposure — 38% (38 of 101). This covers literal secret patterns in non-test source, environment variables echoed to stdout or returned in tool responses, API keys returned from handlers back to the model, and
.envtemplates committed alongside production defaults. See the credential-leak anatomy post for the full pattern taxonomy. - Prompt injection — ~30% (~30 of 101). Tool descriptions, error messages, or response templates that embed unescaped external content — file names, user-provided strings, API response bodies — back into a surface the model reads. This includes both direct injection (malicious input in the current tool call) and indirect injection (adversarial content retrieved from a third-party resource and returned as tool output). Details at /seo/mcp-server-prompt-injection.
- Security misconfiguration — ~18% (~18 of 101). Debug logging enabled in production builds with sensitive fields unmasked; error handlers that echo raw exception strings (which can include stack frames, file paths, and query parameters) into tool responses; transports configured with overly permissive CORS or no authentication enforcement; default credentials left in configuration templates.
- Over-broad permission scope — ~25% (~25 of 101). The OAuth or API scopes requested at registration time are broader than what any of the declared tool handlers actually use. A server that requests write permissions but only reads, or requests admin scopes because the docs said to, is permanently over-privileged for the life of every install.
- Unrestricted resource consumption — ~12% (~12 of 101). Tool handlers that accept arbitrary pagination offsets, unbounded search queries, or recursive operations without depth limits. Because MCP tools are invoked by a model, a poorly-bounded query can be triggered at scale in automated loops without any human noticing until the bill arrives.
Supply chain issues (dependency CVEs, archived or unmaintained registries) appear in ~8% of the corpus, and path traversal variants appear in ~5%. Both are important but neither dominates the finding distribution the way SSRF does.
The full corpus numbers are in the State of MCP Server Security 2026 post. The grade breakdown: 19 A grades, 30 C grades, 10 D grades, 42 F grades. The F cluster is not marginal — those 42 servers have multiple concurrent axis failures, not a single borderline finding.
The OWASP API Top 10: six direct hits, four misses
The OWASP API Security Top 10 (2023) lists: API1 Broken Object Level Authorization (BOLA), API2 Broken Authentication, API3 Broken Object Property Level Authorization, API4 Unrestricted Resource Consumption, API5 Broken Function Level Authorization (BFLA), API6 Unrestricted Access to Sensitive Business Flows, API7 Server-Side Request Forgery, API8 Security Misconfiguration, API9 Improper Inventory Management, API10 Unsafe Consumption of APIs.
Here is how each one maps to the MCP server threat landscape:
API7 SSRF — direct hit, most prevalent
This is the cleanest mapping in the entire exercise. API7 describes exactly what we see in the majority of SSRF-positive servers: a tool handler that accepts attacker-influenced input and passes it to an HTTP client call without validating the target. The only wrinkle is that in classic API SSRF, "attacker-influenced" usually means a malicious API caller. In MCP context it can also mean an LLM that has been indirectly influenced by adversarial content in a document or web page. API7 as written does not call out this indirect path, but the mitigation (allow-list outbound requests, never use caller-controlled strings as URL targets) is identical. Call it a direct hit with a footnote.
API8 Security Misconfiguration — direct hit
Our security misconfiguration findings map well to API8: debug logs in production, verbose error messages echoed to callers, overly permissive transport configuration. The nuance specific to MCP is that "the caller who receives the verbose error" is an LLM, so a raw stack trace or database error message in a tool response is not just an information disclosure — it is content injected into the model's context window, potentially persisted in session memory, and potentially summarized into subsequent outputs. The severity of a misconfiguration finding in an MCP server is higher than in an equivalent REST API for this reason.
API4 Unrestricted Resource Consumption — direct hit
API4 maps directly to our resource consumption finding class. In fact, the MCP case is arguably a cleaner instantiation of API4 than most REST API examples: a tool that accepts an arbitrary search term or an unbounded offset can be called in a tight loop by an automated agent with no rate limiting. The model is not sleeping between calls and it is not watching a credit card charge — it will happily call a poorly-bounded tool hundreds of times if the task requires it. Servers without explicit pagination caps, depth limits, or per-session quotas are genuinely at risk of accidental denial-of-service from well-intentioned agents, not just malicious ones.
API2 Broken Authentication — direct hit, conditional
Most MCP servers we scanned do not implement their own authentication — they inherit it from the transport (stdio pipes for local installs, HTTP bearer tokens for remote transports). For servers with their own auth layer, API2 applies in the standard way: missing token validation, insecure default credentials, auth bypasses in specific tool routes. For servers running on stdio transports, the "authentication" is process ownership and the threat surface is different enough that API2 doesn't add much. Among the subset of servers that do implement bearer token validation, we found the standard API2 patterns: credentials hardcoded in configuration templates, tokens returned in tool responses (making them visible to the model), and auth checks missing from specific tool handlers while present on others.
API9 Improper Inventory Management — direct hit
API9 covers undocumented endpoints, orphaned tool versions, and API surfaces that exist but aren't reflected in the current documentation. In MCP servers this shows up in two ways. First, servers whose README describes three tools but whose source implements five — the undocumented two have never been reviewed and the model will use them anyway. Second, servers that have evolved across protocol versions (MCP 0.9 → 1.0 → 1.1) but haven't depreciated old handler paths, leaving legacy code paths active that no longer get security review. Both of these are API9 in substance: the inventory of callable surfaces doesn't match what the maintainer thinks it is.
API10 Unsafe Consumption of APIs — direct hit
This one is underappreciated. API10 covers applications that blindly trust and pass through data from third-party APIs without validation. In MCP servers that act as API proxies — and most of the vendor-official servers are exactly this — the server fetches a response from a third-party endpoint and surfaces it as a tool result. If that response contains adversarial content (a file name with path separators, a description field with injection strings, a webhook payload with embedded commands), and the server passes it through without sanitization, the model ingests it as trusted tool output. This is simultaneously API10 (unsafe third-party API consumption) and LLM01 (prompt injection via indirect path). It is one of the more dangerous compositions in the corpus.
API1 BOLA — stretch mapping (path traversal variant)
Broken Object Level Authorization typically describes a REST API where changing an ID in a URL gives you access to another user's resource. MCP servers don't have the same object-ownership model, but there is a structural analog: a file-system tool that accepts a relative path from the caller can be instructed to traverse outside its intended scope. This appears in about 5% of our corpus. It is not BOLA in the classic sense because there are no per-user objects, but the authorization failure is the same: the caller accesses a resource they should not have access to by manipulating an identifier the server was supposed to scope. We call these path traversal findings and they are in the BOLA family.
API3 Broken Object Property Level Authorization — marginal
API3 describes returning too many fields in a response, or allowing callers to modify object properties they should only be able to read. MCP servers usually return structured tool results, not full ORM objects, and the degree of property-level authorization is typically low. We see occasional over-sharing of response fields (a tool that returns an API response verbatim, including internal metadata fields), but it is not a dominant finding class. This one maps in edge cases, not as a category to prioritize.
API5 BFLA and API6 Business Flows — don't apply
Broken Function Level Authorization (API5) describes callers accessing admin-level functions by guessing routes or changing parameters. Most MCP servers don't have a tiered authorization model — there is one caller (the LLM) and one permission level (whatever the server's API key grants). Without a privilege hierarchy, there is nothing to escalate. The same is true for API6 (Unrestricted Access to Sensitive Business Flows), which is about abusing legitimate business logic like coupon codes or loyalty flows. MCP servers are not e-commerce backends. These categories describe threats that require a multi-tenant user model and a business-logic layer that virtually no MCP server has.
The OWASP LLM Top 10: the four that matter for MCP
The OWASP Top 10 for LLM Applications covers the threat model for applications built on top of language models — not for the infrastructure around them. Several categories are pure model-layer concerns (hallucination, training data poisoning, model theft) that don't translate to the MCP server review context at all. But four categories map directly to what we grade on.
LLM01 Prompt Injection — the most consequential LLM category
LLM01 describes attacks where adversarial content in an input overrides the model's intended instructions. For MCP servers, there are two distinct surfaces. The first is direct injection: a caller provides a tool argument that contains instruction text designed to manipulate the server's behavior or the model's subsequent outputs. This is a concern for servers that embed input parameters into free-form text before returning it as a tool result. The second is indirect injection: the server fetches content from a third-party source (a URL, a file, an API response) that contains adversarial instruction text, and returns it as a tool result the model then reads as trusted context.
The indirect path is harder to defend against and more prevalent in our corpus, because the vast majority of MCP servers are fetch proxies — they retrieve remote content and hand it back to the model. Any server that fetches user-specified URLs or arbitrary external content is, by construction, an indirect injection surface. The MCP security OWASP reference page has a structured mapping of LLM01 to specific server patterns.
LLM06 Sensitive Information Disclosure — equals our credential axis
LLM06 covers scenarios where the model outputs sensitive information it should not have access to. In MCP context this is almost always a credential-handling failure at the server layer: an API key returned from a tool handler ends up in the model's context, and the model may then echo it in a response, include it in a generated code snippet, or pass it to another tool in the same session. The server author typically doesn't think of this as an LLM vulnerability — they think of it as sloppy key handling — but the effect is an LLM that has been handed a secret it can now disclose. The 38% credential exposure rate in our corpus is, under LLM06 framing, a 38% sensitive information disclosure rate for connected agents.
LLM07 Insecure Plugin Design — the MCP review IS this category
LLM07 is the closest thing to a dedicated MCP threat category in the current LLM Top 10. It describes plugins that allow for excessive permissions, do not validate inputs, use insecure transport, or don't scope their tool surface tightly. Reading the LLM07 guidance is essentially reading a checklist for what we grade on the security and permissions axes. The fact that LLM07 exists is useful for organizations trying to explain why MCP security reviews are necessary — "we are doing LLM07 compliance for every plugin in our environment" is a framing that resonates with security teams who know the OWASP LLM list.
LLM08 Excessive Agency — equals our permissions axis
LLM08 describes agents that have been granted more capability than is necessary for their stated purpose, and which can therefore take consequential actions outside the intended scope. The permissions findings in our corpus are exactly this: a server that requests OAuth admin scopes when read-only scopes would suffice is an LLM08 violation. The mitigation — minimum necessary permissions, scopes that match declared tool functionality, no write access without an explicit user-confirming workflow — is standard least-privilege hygiene applied to the plugin layer. We flag it in roughly 25% of scanned servers.
What doesn't translate
LLM03 (Training Data Poisoning), LLM09 (Misinformation), and LLM10 (Unbounded Consumption at the model layer) are model-training or model-serving concerns that don't map to MCP server code. A server author cannot do anything about training data poisoning by writing better TypeScript. LLM04 (Model Denial of Service) and LLM05 (Supply Chain Vulnerabilities for the model itself) are partially relevant — we do grade on dependency CVEs — but they are not distinctively MCP-shaped threats. The LLM list is most valuable for MCP reviews in its first eight categories, and most valuable of those eight in the four listed above.
Three MCP threats with no OWASP home
After mapping everything we could to OWASP API and LLM categories, three finding patterns remain that don't fit cleanly anywhere. These are not edge cases — they collectively account for a substantial share of the most consequential findings in the corpus.
(a) Credential echo into tool response
This looks like it should be LLM06 (Sensitive Information Disclosure) or API2 (Broken Authentication). It is neither, cleanly. The pattern: a tool handler retrieves data from an API using a credential stored in an environment variable, and includes that credential — or a derived token, or a refresh token, or a connection string — in the structured tool response that goes back to the model. The credential is not logged to disk; the server does not "leak" it in the API sense. It is returned as first-class tool output.
LLM06 describes the model disclosing sensitive information. This is the server handing the model a secret it then can disclose. API2 describes authentication failures for callers authenticating to the server. This is about what the server does with its own downstream credentials. The gap is real: OWASP does not currently have a category that describes the specific risk of an MCP server returning its own credentials into a model context, where they become part of a potentially persistent, potentially shareable conversation thread.
The full taxonomy of how this happens in practice is in Anatomy of a Credential Leak. It is more varied than it sounds — we documented seven distinct patterns across the 38 credential-positive servers in the corpus.
(b) Permission scope versus handler implementation drift
LLM08 (Excessive Agency) covers over-broad permissions at the authorization layer. What it doesn't cover is the specific failure where the declared permission scope and the actual handler implementation have drifted. A server might declare in its manifest that it only reads from an API, request only read scopes, and then have a code path that — under specific input conditions or in an error-recovery flow — issues a write operation using a credential that happens to have write access because the operator provisioned it that way.
This is not quite LLM08 (the scope declaration is correct) and it is not quite API5 BFLA (there is no tiered authorization being bypassed — the credential just happens to be more powerful than the server's declared intent). It is a gap between what the server claims to do and what it actually does under certain conditions, and it is the kind of thing that only appears when you read handler code carefully rather than relying on the manifest declaration.
Our methodology page describes how we check for handler-manifest consistency. The finding is rare — it shows up in maybe a dozen servers in the corpus — but when it does appear, it tends to appear in servers that are otherwise well-written and therefore more trusted, which makes it more dangerous in practice than its low frequency suggests.
(c) Client compatibility drift
Neither OWASP list has a category for protocol version mismatch, and for web APIs that is reasonable — HTTP versioning is stable and well-managed. MCP is not. The protocol has evolved meaningfully across 0.9, 1.0, and 1.1, with capability negotiation mechanisms that differ between versions and client implementations (Claude Code, Cursor, Windsurf, Codex CLI) that do not all support the same feature set.
A server that implements tool annotations and sampling callbacks from MCP 1.1 and then gets installed into a client that only speaks MCP 1.0 will silently degrade. In the worst cases, capability negotiation failures cause a client to connect but skip security-relevant features — input schema validation, user confirmation prompts — that the server assumed would be enforced by the client. This is not a classic security vulnerability. It is a trust assumption about the client layer that fails silently in certain deployment configurations.
The public audits at /audits/ flag compatibility issues on the docs and compatibility axis. They do not map to any OWASP category because OWASP was not written for a protocol ecosystem this young and this fragmented.
Priority order from the corpus
Given the finding distribution, here is how we would prioritize a remediation backlog if we were a security team responsible for a fleet of MCP servers. The order is based on three factors: prevalence in the corpus, severity of a successful exploitation, and available fix complexity.
- SSRF first. Fifty percent prevalence and the highest single-finding impact in the corpus. A working SSRF primitive in a cloud-hosted agent environment is a path to cloud metadata, internal network enumeration, and cross-tenant data access. Fix: allow-list outbound HTTP at the transport layer; never accept caller-controlled strings as URL targets without parsing and validating the hostname against an explicit permit-list. This is a one-time structural change, not a per-handler fix.
- Credential echo second. Thirty-eight percent prevalence and the highest "invisible escalation" risk. A credential returned to the model is a secret that has escaped the server's control surface permanently — you cannot un-return it from a context window that may be persisted, logged, or summarized. Fix: audit every tool handler's return path; credentials, tokens, and connection strings should never appear in tool result payloads. Return opaque success indicators, not the raw API response that contains auth material.
- Prompt injection third. Approximately thirty percent prevalence and growing as more MCP servers act as fetch proxies. Direct injection is easier to test for; indirect injection (content fetched from third-party sources) is structurally harder to prevent. Fix: sanitize or clearly delimit external content before returning it as tool output; never interpolate unescaped external strings into tool description text or user-visible response templates.
- Security misconfiguration fourth. Eighteen percent prevalence, moderate severity individually, but misconfiguration findings tend to cluster — a server that has debug logging on in production usually has other configuration hygiene problems. Fix: environment-specific configuration management; production builds should never ship debug flags, and error handlers should return opaque error codes, not raw exception text.
- Unrestricted resource consumption fifth. Twelve percent prevalence and easy to miss during code review because the risk is operational (runaway billing, accidental DoS) rather than data-security. Fix: bound every tool handler that accepts pagination, recursion depth, or search scope parameters; set explicit server-side maximums that the caller cannot override.
- Permission scope drift sixth. Twenty-five percent prevalence, but the severity is conditional — a server with over-broad permissions is a latent risk that only materializes if another finding is also present. Fix: audit declared scopes against handler implementation at release time; add a scope-vs-handler diff to CI.
- Supply chain last. Eight percent prevalence, lowest immediate severity in most cases, but worth a quarterly pass. Fix: enable Dependabot or equivalent; pin to specific versions; maintain a SECURITY.md with a disclosure path.
This ordering is not universal. For a server handling financial data, the credential echo and permission scope issues might warrant swapping positions with SSRF. For a server that primarily fetches and summarizes external content, prompt injection deserves the top slot. But for a generic MCP server with no domain-specific risk amplification, this is the sequence the corpus data supports.
A consolidated view of how these priorities map to both OWASP frameworks is on the MCP security OWASP reference page, which is a structured table rather than a narrative — useful as a quick reference alongside this post.
Running an audit against the full framework
If you maintain a Claude skill or MCP server and want to know where you stand against this framework — both OWASP axes and the three MCP-specific categories that have no OWASP home — paste your GitHub URL at skillaudit.dev/#wl. The engine grades across all six axes (security, permissions, credential handling, maintenance, client compatibility, documentation), produces a permanent audit URL, and maps each finding to the relevant framework categories in the report body.
The 101 public reports are already at /audits/. If your server is in the corpus and the grade looks wrong, email hello@skillaudit.dev with the finding reference — we will re-scan and either re-grade or explain the call.
Further reading
- MCP Server Security and OWASP: structured mapping table — the reference version of the API Top 10 and LLM Top 10 mappings, organized for quick lookup
- Anatomy of a Credential Leak in an MCP Server — seven distinct credential exposure patterns from the corpus, with code-level examples
- State of MCP Server Security 2026 — the full corpus scan results, grade distribution, and per-server breakdown
- MCP Server SSRF: patterns, prevalence, and mitigations
- MCP Server Prompt Injection: direct and indirect surfaces
- SkillAudit methodology — how the six-axis grading works and what each finding class covers
- All public audit reports — the full index of 101 scanned servers with current grades