Est.

API Security Best Practices Applied to MCP Endpoints

Autonomous agents amplify API vulnerabilities because they operate unsupervised across sessions.

Staff Writer · · 12 min read
Cover illustration for “API Security Best Practices Applied to MCP Endpoints”
MCP 101 (mcp vs api, mcp servers, spec updates, mcp oauth) · July 25, 2026 · 12 min read · 2,702 words

Start with a number that should make anyone pause: 492 publicly exposed MCP servers with no authentication and no encryption. Not misconfigured in some subtle, sophisticated way. Simply open.

An audit of 17 popular MCP servers returned an average security score of 34 out of 100, and every single one lacked permission declarations. That's not a tail risk buried in some edge-case deployment. That is the median condition. A 2026 survey across more than 2,600 servers found 82% exposed to path traversal attacks and 34% to command injection. In roughly 60 days of early 2026, researchers filed more than 30 CVEs against MCP-related tooling. One of them, CVE-2025-6514 in the mcp-remote package, scored a 9.6 on the CVSS scale and had been downloaded more than 437,000 times before anyone disclosed the vulnerability.

Here's the thing that bothers me about those numbers: none of these are novel AI-native attacks. Path traversal and command injection have been in OWASP's Top 10 for web APIs for years. What's different is the propagation path. When a human using a REST API triggers an injection vulnerability, the damage is roughly bounded by what that human was doing at that moment. When an agent triggers the same vulnerability, it's operating autonomously, across multiple tool calls, with context that persists and propagates across sessions. The blast radius is structurally larger, and the person nominally responsible for the session has no idea any of it happened.

For most deployments right now, even achieving "correct classic controls" would represent a material improvement. The sophisticated problems come later. Getting to baseline is still the urgent work.

The two frameworks now shaping what "correct" looks like for MCP security

Two documents are currently doing most of the heavy lifting in defining what responsible MCP security looks like, and both deserve more attention than they're getting.

The first is the OWASP MCP Top 10, a beta framework led by Vandana Verma Sehgal. It's the first OWASP taxonomy dedicated specifically to MCP, and its ten risk categories map onto familiar API security territory in ways that are clarifying rather than redundant. MCP01 addresses token mismanagement and secret exposure. MCP02 covers privilege escalation via scope creep. MCP05 is command injection. MCP07 is insufficient authentication and authorization. MCP09 addresses shadow MCP servers. MCP10 covers context over-sharing. If you've spent time with OWASP's API Security Top 10, most of that reads like a translation exercise, which is both reassuring and a little frustrating given how many teams are treating MCP as uncharted territory.

But then there's MCP03 and MCP04: tool poisoning and software supply chain attacks. Those categories have no clean equivalent in traditional API security frameworks. Their presence in the taxonomy signals that the framework authors recognized something new about MCP's attack surface, not just a repackaging of old problems.

The second document is a Cybersecurity Information Sheet from the NSA, published in May 2026. It covers 23 discrete security issues spanning access control, prompt handling, tool execution, agent permissions, auditability, and third-party governance. Both documents share a core claim: the MCP server is a trust boundary that must be actively enforced, not passively assumed. Traditional API security makes that claim too, but rarely has to defend it against runtime context manipulation by an autonomous agent.

As of mid-2026, security training curricula had addressed 18 of the NSA's 23 issues. Five remained uncovered. That gap isn't a failure of the frameworks; it's evidence that coverage is still catching up to an attack surface that itself is still expanding. These aren't cited here as a recitation exercise. They're useful precisely because they demonstrate that the API-to-MCP translation problem is recognized at the standards level, formal acknowledgment that the mismatch is real, consequential, and not yet fully resolved.

Authentication: where OAuth 2.1 is the right answer but token passthrough breaks it

The canonical authentication answer for remote MCP servers is OAuth 2.1 with PKCE, mandated in the November 2025 MCP specification. Authorization Code Flow with PKCE for client applications; Client Credentials Flow for server-to-server authentication. PKCE works by cryptographically binding the authorization request to the token request, preventing authorization code interception. On paper, this maps cleanly from existing API security practice.

The MCP-specific failure mode is called token passthrough, and it's easy to stumble into when you're building quickly. What happens: an MCP server accepts a token that was issued to the client and passes it downstream to another API without validating that the token was actually issued for the MCP server itself. The MCP authorization specification explicitly prohibits this. MCP servers must not accept any token that wasn't explicitly issued for them. The prohibition exists precisely because the failure pattern is so natural to fall into, which is the kind of thing that should make you nervous about any team shipping fast without a security review.

Before you even get to token passthrough, though, about 38% of scanned MCP servers in 2026 lacked any authentication at all. The more architecturally sophisticated problem is downstream of a more fundamental failure still affecting a substantial share of deployments.

Dynamic Client Registration presents as a convenience feature before it presents as an attack surface. The concept is reasonable: allow clients to register themselves programmatically. The problem is that anyone can POST to a registration endpoint, client identities can be spoofed by registering under the same name as a trusted application, and at scale, authorization server databases accumulate stale entries that become a governance liability. None of that is unique to MCP, but MCP's rapid tooling ecosystem means DCR gets adopted quickly, without the surrounding controls that make it safe.

The practical adaptations aren't exotic: strict redirect URI validation using exact match rather than wildcards or substring checks; validating OAuth Authorization Server Metadata per RFC 8414 before initiating any flows, rather than trusting server-provided endpoints. These are existing best practices. They need to be applied with the same rigor here as they would be in any other OAuth context, which is precisely where they're currently being skipped.

Authorization: why server-level scopes are insufficient and tool-level RBAC is the minimum

Most API authorization operates at the endpoint level. You authenticate, you're granted scopes corresponding to which endpoints you can call, and enforcement happens at the boundary. That model breaks down in MCP because MCP servers expose tools, not endpoints in the traditional sense. If authorization is enforced only at the server level, every authenticated client becomes effectively a superuser over every tool that server exposes. You've authenticated correctly and then immediately over-privileged yourself, which is a special kind of irony.

The correct adaptation is scoping at the tool level. A tool that reads files must carry a different scope than a tool that deletes them. Access tokens should have lifespans measured in minutes rather than hours, with client-side refresh logic compensating for the shorter windows. OAuth scopes should be narrow and specific rather than wildcard-permissive. CoSAI's MCP Security guidance, approved in early 2026, is explicit on this point.

OWASP's MCP02 category describes what happens when this isn't done: temporary or loosely defined permissions within MCP servers expand over time through scope creep, and an attacker exploiting weak scope enforcement can escalate from limited access to repository modification, system control, or data exfiltration. The escalation path isn't hypothetical. It follows directly from the structural over-permissioning that server-level scopes produce.

Palo Alto Networks' Unit 42 research puts a concrete number on the blast radius. In a configuration with five connected MCP servers, a single compromised server achieved a 78.3% attack success rate. Tool-level scope controls are what limit how far that kind of lateral movement can travel through the mesh. The goal isn't restricting what AI can do in principle; it's ensuring that "what AI can do" reflects a deliberate organizational decision with a defined scope, rather than a default that emerged from a loose implementation.

Tool poisoning: the injection attack that doesn't come from user input

Classic injection defenses, SQL injection, command injection, share a foundational assumption: untrusted input comes from the user, and server-side logic is trusted. That's the line the defense is drawn at. In MCP, that assumption breaks in a specific and important way, and once you see it, you can't unsee it.

Tool descriptions are structural metadata. The model reads them and treats them as instructions from the system developer. That's not a bug; that's how the protocol is designed to work. But it means an attacker who controls a tool description doesn't need to touch user input at all. The user sees a tool name and a short summary. The model reads the full description and follows whatever instructions it contains.

This is not a per-request attack. A poisoned description needs to be injected once, and it affects every session where that tool is called. Two CVEs in 2025, MCPoison (CVE-2025-54136) and CurXecute (CVE-2025-54135), demonstrated this gap in different ways. Benchmark data shows tool poisoning attacks succeed 84.2% of the time when auto-approval is enabled. The failure mode scales directly with the degree of automation.

There's a variant called the rug pull that deserves particular attention. A previously clean server pushes an update that adds poisoned content. The host agent reloads tool descriptions without re-prompting the user. MCP clients that displayed the original tool descriptions do not notify users that the descriptions have changed. The Postmark MCP server incident in September 2025 made this concrete: a maintainer added BCC logic that silently copied every sent email to an attacker-controlled address. The backdoor was live across multiple versions before Koi Security identified it. That's a governance gap, not merely a validation failure. There's a meaningful difference between those two things, and the distinction matters for how you fix it.

The defensive adaptation requires a conceptual shift. Tool descriptions must be treated as untrusted input, not trusted system metadata. Users should be alerted when descriptions change between sessions. Re-approval should be required when tool definitions change. That's a harder operational posture than most current deployments maintain, but the alternative is treating the thing that tells your agent what to do as inherently reliable.

Input validation at the argument level and why parameterized APIs are non-negotiable

This should feel familiar, because the problem is familiar. Tool arguments passed to MCP servers are functionally equivalent to request parameters in a conventional API. The same rules apply: validate and sanitize before use; never construct queries or commands from untrusted strings; use parameterized interfaces wherever arguments touch system calls or file paths. The 2026 survey finding of 34% command injection exposure across thousands of servers shows the parameterized query problem is not being solved here any better than it was in early web development. That finding is not a comparison that flatters anyone.

CVE-2025-68144 is a useful concrete case. It involved passing user-controlled arguments to GitPython, which allowed injection of flags like --output=/path/to/file in git diff calls, enabling an attacker to overwrite arbitrary files. Chained with the Filesystem MCP server, that produced remote code execution through a malicious .git/config. The chain isn't exotic. It's a well-understood class of vulnerability, present because argument-level validation wasn't applied. CVE-2025-49596 in MCP-Inspector followed similar logic: unverified inputs accepted via crafted messages led to RCE, fixed in version 0.14.1.

The 82% path traversal exposure in the same survey is the more sobering number. Its prevalence in MCP deployments is not evidence that MCP created a new vulnerability class. It's evidence that practitioners building MCP servers didn't bring the same validation discipline they would bring, or should bring, to a conventional API.

There's an outbound dimension that's unique to agentic systems. When an MCP server makes outbound HTTP calls based on tool arguments, unvalidated URLs create a Server-Side Request Forgery exposure: an attacker can route requests to internal network resources or cloud metadata endpoints. Input validation in MCP is not just about what comes in. It's also about where the server goes when it acts on what came in.

Sandboxing and execution controls where rate limiting alone is too blunt

Rate limiting works by capping request volume. It's a coarse but effective control against abuse patterns that require scale: credential stuffing, scraping, brute force. The problem in MCP is that damage doesn't require scale. A single tool call can trigger file system access, shell execution, or network egress. Volume-based controls don't catch the damage that fits inside one request, and in agentic systems, a lot of damage fits inside one request.

CoSAI's 2026 guidance is direct on this: MCP servers that interact with the host environment, accessing files, running commands, issuing network connections, executing LLM-generated code, should run in a sandbox. That guidance is broader than most deployments currently honor.

In practice, sandboxing means running local MCP servers in containers or restricted environments with explicit egress rules. It means applying OS-level process isolation so a compromised server cannot reach the host filesystem or other server processes. It means enforcing least privilege at the process level, not just at the token level. None of these are new concepts. They're the same isolation principles that have governed containerized microservice deployments for years, applied consistently here rather than selectively.

There's a human approval layer that functions as a rate-limiting equivalent for high-risk individual calls. Requiring explicit human confirmation before tool calls that involve file deletion, outbound email, or credential access isn't a workaround for weak automated controls; it's an appropriate control in its own right for calls where the cost of a single wrong execution is unacceptably high. That's what disabling auto-approval addresses in the tool poisoning benchmark: the 84.2% success rate is conditional on auto-approval being enabled. Turn that off and the number drops substantially.

Sandboxing must also be paired with server identity verification, because a sandbox correctly configured around a server running poisoned code is still compromised. MCP servers can modify tool definitions between sessions. Without versioning and integrity checking at load time, execution controls operate on untrustworthy inputs. Rate limiting and sandboxing are necessary; they are not sufficient without supply chain controls running alongside them.

Audit logging adapted for MCP: capturing what the agent touched, not just what the user requested

A conventional audit log captures who made a request, when, and what response they received. For a human-operated API, that's usually enough to reconstruct accountability. The human who initiated the request is the human who bears responsibility for what happened.

In MCP, the entity making tool calls is an agent acting autonomously. The human who initiated a session has no visibility into which tools were invoked, what arguments were passed, or what data was accessed between the original prompt and the final response they received. Logging what the user requested is logging the beginning of a chain that has dozens of autonomous steps the user never saw. You're capturing the input to a black box and calling it an audit trail.

OWASP names logging and monitoring failures as a top-10 risk for MCP specifically, not as a general operational concern. The reason it's a named attack-enabler rather than just an operational gap: gaps in logging obscure the lateral movement and privilege escalation that happen inside agentic workflows. If you can't reconstruct what the agent touched, you can't detect anomalous behavior, you can't scope an incident, and establishing accountability after the fact becomes very difficult. You're essentially flying blind through your own security posture.

What MCP audit logging actually requires is a different level of granularity than most teams are accustomed to capturing. Every tool invocation needs to be recorded with its full argument set. Every piece of data the agent accessed needs to be logged with enough context to understand why it was accessed. The agent's reasoning chain, wherever it's available, should be preserved alongside the tool calls it produced. Session context needs to be tracked across the full lifecycle, not just at the entry point.

The NSA's May 2026 guidance addresses auditability as one of its core issue categories. Auditability in agentic systems isn't an operational nicety you layer on after you've secured everything else. It's a foundational control, because without it, you have no reliable way to know whether any of the other controls you've implemented are actually working. That's the uncomfortable position a lot of teams are in right now, and most of them don't know it yet.

Sources

  1. esentire.com
  2. arxiv.org
  3. strobes.co
  4. arxiv.org
  5. nsa.gov
  6. truefoundry.com

More in MCP 101 (mcp vs api, mcp servers, spec updates, mcp oauth)