Est.

MCP Security Best Practices for Production Deployments

Editor at Large · · 13 min read
Cover illustration for “MCP Security Best Practices for Production Deployments”
Running AI-Generated Code Safely · August 8, 2026 · 13 min read · 2,871 words

The first serious signal came from scanning, not from an incident. Knostic audited nearly 2,000 publicly accessible MCP servers and found that every verified instance exposed internal tool listings without requiring authentication. Separately, Backslash Security identified a pool of servers bound to all network interfaces, many configured to allow arbitrary code execution. These were not edge cases in an immature ecosystem. They telegraphed the shape of what was coming.

The incidents that followed were more instructive. Invariant Labs demonstrated silent exfiltration of a complete WhatsApp message history through a malicious MCP server, no exploit required, just a server that the client trusted. In June 2025, a Supabase Cursor agent processing support tickets was tricked into leaking integration tokens via content embedded in the tickets themselves. CVE-2025-49596, scored at CVSS 9.4, enabled arbitrary command execution through unauthenticated MCP Inspector instances. In September 2025, the first confirmed malicious MCP package operated undetected for roughly two weeks before anyone noticed the email data leaving.

IBM's 2025 Cost of a Data Breach Report adds an uncomfortable footnote: a notable share of organizations had experienced breaches of AI models or applications, and of those, the vast majority lacked proper AI access controls at the time of breach.

What these incidents share is not a single point of failure. Unauthenticated exposure, prompt injection, supply chain compromise, and session hijacking each landed at a different boundary. MCP's architecture has three distinct security surfaces. The transport layer governs communication between client and server. The protocol layer covers JSON-RPC 2.0 messaging, lifecycle, and capability negotiation. The data layer is where tools, resources, prompts, and notifications actually reach the agent. No two of these incidents exploited the same boundary in the same way. That diversity of vector is precisely why I find the "just add authentication" response so frustrating to encounter in post-mortems. But what if a single control, however well-implemented, addresses only the boundary it was designed for — and every other boundary remains open?

Authentication as the first gate: what the June 2025 spec revision requires and why it matters

Venn diagram: MCP Security: Authentication vs. Access Control. Compares Authentication and Access Control; overlap: Identity + Capability.

The June 2025 specification revision formalized a structural separation that earlier deployments had consistently collapsed. MCP servers are now officially classified as OAuth Resource Servers. A dedicated authorization server handles user authentication, token issuance, and client registration. The MCP server validates tokens and enforces access controls; it does not perform authentication itself. When that division of responsibility is absent, you get a single point of failure spanning both functions, which is exactly what several early production incidents demonstrated.

For remote MCP servers, the November 2025 specification requires OAuth 2.1. PKCE must be enforced for public clients. TLS 1.2 or higher is required, with valid certificates from a recognized certificate authority. Self-signed certificates are unacceptable at any level of production maturity. These are baseline requirements, not suggestions with flexibility at the margins.

Static tokens in production are a documented failure mode. They are difficult to rotate and nearly impossible to audit at scale. Short-lived tokens with PKCE are the correct posture, and the specification is explicit about it. MCP servers must not accept tokens that were issued for a different server. Authorization verification applies to all inbound requests without exception. Session IDs must be cryptographically secure and non-deterministic.

There is also a subtler risk worth naming directly: the confused-deputy problem. Proxy servers connecting to third-party APIs can be exploited when static client IDs are combined with dynamic client registration and consent cookies, allowing an attacker to obtain authorization codes without proper user consent. The mitigation requires HTTPS for all OAuth-related URLs in production, rejection of http:// schemes except on loopback addresses in development, and routing OAuth discovery through an egress proxy to block internal destinations and prevent server-side request forgery.

Authentication, done correctly, establishes that a request comes from who it claims to come from. It says nothing about what that identity should actually be permitted to do once it arrives. That raises an important question: if authentication is only a starting point, what fills the space between verified identity and appropriate capability? Teams that treat authentication as a security destination rather than a starting point tend to discover the difference at an inopportune moment.

Access control and least privilege: closing the gap between who is authenticated and what they should be allowed to do

Diagram: Least Privilege Cuts Incident Rate by 59 Points. Visualizes: Show a magnitude contrast between two incident rates from the 2026 Infrastructure Identity Survey: systems with least-privileged AI access had a 17% incident rate versus 76% for…

The OWASP MCP Top 10 identifies over-privileged access as a foundational risk, and the framing is apt: an authenticated agent with excessive permissions can cause serious damage while doing nothing that looks anomalous at the authentication layer. The space between "verified identity" and "appropriate capability" is precisely where access control lives, and it is larger than most teams initially appreciate.

The practical principle is straightforward, even if implementation is not. Tools that move money, delete data, change access controls, or touch production infrastructure warrant tighter eligibility requirements, additional approval steps, and narrower scope than low-risk tools. Dynamic policy evaluation, running access decisions in real time on every invocation rather than at session initiation, keeps policy current as tools are added and as context changes. A permission snapshot taken at deployment goes stale faster than teams expect, particularly when agent toolsets are evolving week over week.

One failure mode specific to SaaS agent platforms deserves its own treatment: the maker identity problem. When a business user creates an agent using their own credentials, every subsequent user's actions execute under the creator's identity. Downstream users inherit standing access they were never granted directly. This is an architectural property of platforms that tie agent identity to the builder's identity, and it is difficult to govern once the pattern is embedded in a deployed system.

The governance gap around AI agents is substantial. The 2026 Infrastructure Identity Survey found that only 44% of organizations had implemented any policies to manage AI agents, despite 92% agreeing that governing agents is critical to enterprise security. The same survey quantified the consequence: systems with least-privileged AI access had a 17% incident rate compared to 76% for over-privileged systems. That gap is not subtle, and it is not explained by technical complexity alone. It reflects how organizations tend to treat access policy as a cleanup task rather than a design constraint. One might argue that the gap is simply a matter of organizational maturity catching up to a fast-moving technology — but a 59-point difference in incident rates suggests something more structural than a lag in best-practice adoption.

Multi-layer rate limiting belongs here as well, applied at the user, session, tool, and resource levels. Adaptive rate limiting that adjusts to usage patterns and emerging threat indicators does more than throttle; it surfaces anomalies that static thresholds miss, a property that connects directly to what runtime monitoring can observe later.

Why standard containers are structurally insufficient for executing untrusted AI-generated code

Veracode's 2025 report found that a large proportion of AI-generated code fails security tests. That single figure reframes execution isolation from a nicety to a structural requirement. But why exactly does this happen — and why does the container boundary fail to contain it?

The core architectural problem with standard Docker containers is kernel sharing. Containers running on the same host share the host operating system kernel. If an AI agent produces a kernel exploit payload, the container boundary does not stop it. A successful exploit can escalate privileges and reach the host machine and any internal networks it touches. As gVisor's documentation states: "with standard containers, the workload is only one system call away from host compromise." CVE-2019-5736 and CVE-2024-21626 both exploited runc to enable container breakouts. These are not theoretical attack paths; they have assigned CVE numbers and documented exploitation.

The distinction that matters is threat model specificity. A coding agent sandbox exists to run code that was not written by a human, cannot be fully reviewed before execution, and may attempt destructive, resource-intensive, or insecure actions. Standard containers were designed for isolating known workloads from one another. The threat model is simply different, and I have watched teams miss this distinction repeatedly because the first few deployments went smoothly and nothing broke. That early success is misleading. The absence of an incident is not evidence that the boundary would hold under adversarial conditions.

Hardened Docker profiles are a reasonable starting point for development environments. They are not an acceptable production posture for agent execution handling untrusted code.

Production-grade isolation approaches and what each one trades off

Table: Production Isolation Approaches Compared. Compares Isolation Mechanism, Startup Overhead, Best For and Key Tradeoff by gVisor, Firecracker microVMs, libkrun microVMs and WebAssembly.

The common requirement across production isolation approaches is consistent: each sandbox needs its own filesystem, its own network namespace, and its own resource allocation. Agents write files, install packages, and execute code. None of that should touch the host, other sandboxes, or production infrastructure.

Several architectural approaches address the kernel-sharing problem, each with a different tradeoff profile, and none is obviously correct for every workload.

gVisor intercepts system calls in userspace before they reach the host kernel. Research by Chen et al. applied it to safely run untrusted programs against unit tests. It is appropriate for workloads where the overhead of userspace syscall interception is acceptable and the isolation guarantee matters more than raw performance. Teams reach for it when they need a well-documented isolation primitive and can absorb some latency.

Firecracker microVMs provide hardware-level isolation through a lightweight virtual machine monitor, purpose-built for fast, secure multi-tenant execution. The isolation boundary is strong because there is no shared kernel, and startup times are measured in milliseconds rather than seconds. The operational overhead is higher than containers, but it addresses a correspondingly more serious threat model.

libkrun microVMs, as used in the microsandbox project, achieve sub-200 millisecond startup with hardware-level isolation, designed for self-hosted deployments where teams want full infrastructure control and cannot accept the data-residency tradeoffs of shared execution services.

WebAssembly offers capability-based isolation without a shared host kernel. It is an emerging research application for agent execution rather than a mainstream production choice today, but the underlying isolation model is worth watching as tooling matures.

Regardless of the underlying technology, certain hardening requirements apply universally. Mount only the project directory, not the home directory. Enforce operating-system-level primitives for resource limits. Network isolation is as critical as filesystem isolation. The Shai-Hulud supply chain campaigns of late 2025 make that concrete: hundreds of npm packages targeting AI agent workflows were compromised, and without network restrictions, a sandbox breach extends into a supply chain incident with a much larger blast radius.

Among purpose-built options, Daytona is designed specifically for this threat model. It offers sub-90 millisecond provisioning, Docker-native compatibility so teams can bring existing images, stateful sandboxes that persist across long-running agent sessions, and customer-managed compute within the team's own cloud infrastructure. That last property addresses the control and data-residency concerns that shared execution services cannot resolve. It is open-source, which matters when enterprise compliance requires auditability of the runtime itself rather than trust in a vendor's attestation.

Input validation and prompt injection: the attack surface that lives inside the protocol layer

Prompt injection is not a misconfiguration. It is an inherent property of any system where a language model processes external content and retains the ability to invoke tools. The Supabase Cursor incident makes that concrete: the agent was doing exactly what it was designed to do, processing user-supplied support ticket content. The attack vector was the content itself, not a failure of the surrounding infrastructure. That framing matters, because teams that categorize prompt injection as a misconfiguration tend to reach for configuration-level mitigations that do not address the underlying condition.

Tool definition poisoning extends this surface. A compromised or malicious MCP server can return tool descriptions that manipulate the model into invoking unintended capabilities, or that redirect context to an attacker-controlled endpoint. The agent has no native ability to distinguish a legitimate tool description from a crafted one. This is a problem that must be addressed structurally, which is a harder conversation to have with teams that prefer model-layer mitigations because they feel more tractable. It is also worth considering whether the model-layer framing persists precisely because it feels solvable — and whether that comfort is itself a vulnerability.

The concrete validation requirements for MCP follow from this. All tool parameters must be validated and sanitized before execution; every argument is untrusted input by assumption. Strict schema validation on tool invocations should reject anything outside the declared schema rather than normalizing it, because normalization can mask malicious payloads. Raw user-supplied content should not pass directly into tool calls without inspection. Tool descriptions from external MCP servers warrant scrutiny for instruction-following language that could redirect agent behavior.

The supply chain dimension deserves emphasis. The first malicious MCP package operated for roughly two weeks before detection. Static analysis on all MCP server code before deployment, combined with software composition analysis for dependencies, is now a baseline expectation rather than an advanced practice.

The defense sequence is worth being precise about: input sanitization, then static code validation, then pre-execution checks, then isolated execution, then runtime monitoring, then post-execution review. Input validation reduces what reaches the sandbox. Isolation contains what executes inside it. They address different moments in the attack chain and cannot substitute for each other.

Observability and runtime monitoring as the detection layer no static control can replace

Static controls are configuration-time decisions. Authentication policies, access rules, and input filters are set before execution begins and cannot detect anomalous behavior that emerges during a session. This is not a criticism of those controls; it is a description of their temporal boundary. They have already done their work by the time execution starts.

Runtime monitoring for an MCP deployment needs to cover specific signals. Every tool invocation should be logged with agent identity, parameters, and return values. Outbound network calls from within a sandbox are particularly important; unexpected egress is frequently the first observable signal of exfiltration. Resource consumption anomalies, CPU, memory, or I/O spikes outside normal patterns suggest resource-exhaustion payloads or cryptomining activity. Session-level behavioral drift, a session that begins with routine tool use and shifts to querying sensitive data or calling unusual endpoints is harder to detect but often the most consequential signal. It is also the one most likely to be missed by teams whose observability stack was designed for traditional application workloads rather than agent behavior, because the behavioral baseline looks entirely different.

The Cloud Security Alliance's four-level MCP Security Maturity Model provides useful framing. Level 1 addresses unauthenticated access and unencrypted communications. Level 3 adds supply chain governance and behavioral monitoring. Level 4 applies zero-trust principles across the full tool invocation lifecycle. For enterprise production deployments, Level 3 is a reasonable floor; Level 4 is appropriate for high-value or high-sensitivity workloads.

The first malicious MCP package ran undetected for roughly two weeks. That window existed because no one was watching what the package was doing at runtime. Observability infrastructure is what makes policy enforceable rather than aspirational, and the distinction between those two states is where breach costs actually accumulate.

Applying the layered model to three deployment patterns teams actually run in production

The layered model lands differently depending on how a team has actually built its agent infrastructure. Three patterns dominate production deployments, and each one surfaces a different gap.

Homegrown agents on cloud infrastructure

Teams building on AWS Bedrock, GCP Vertex, or LangChain-based orchestration have maximum architectural control. The primary risk in this pattern is sprawl. Hundreds of agents distributed across repositories and cloud accounts can outpace centralized governance faster than anyone expects, particularly when agent creation is distributed across engineering teams without a unified registration process. Least-privilege enforcement and centralized policy management are the priority controls here. The flexibility that makes this pattern powerful also makes it difficult to govern at scale, and the difficulty compounds quietly until something breaks.

SaaS agent platforms

Low-code and no-code agent platforms expose the maker identity problem most acutely. The platform typically handles authentication at the platform level, which can create a false sense of security around the layers below. Per-user permission scoping is rarely solved by the platform itself. Access control and token isolation become the priority, and teams need to audit carefully what identity is actually executing downstream tool calls when a business user's agent runs on behalf of someone else. The governance gap here is structural, not a configuration setting to adjust.

Purpose-built agent runtimes

Teams that have chosen infrastructure specifically designed for agent execution are working with a threat model that was considered from the start, rather than retrofitted onto general-purpose compute. The security profile of this pattern depends heavily on the specific runtime's design choices around isolation, network policy, and auditability. The tradeoffs here are not between security and convenience but between different isolation architectures, each carrying the performance and operational characteristics covered in the isolation section above.

Across all three patterns, the layered model applies the same way, even if the implementation differs. Authentication establishes identity. Access control constrains capability. Isolation contains execution. Input validation reduces the attack surface that reaches isolation. Observability detects what the other layers miss. No layer is optional, and none substitutes for any other. Teams that have worked through one serious incident tend to know exactly which layer failed them. The harder problem, and the one worth sitting with, is whether you can identify your most exposed layer before that incident happens.

Sources

  1. labs.cloudsecurityalliance.org
  2. descope.com
  3. modelcontextprotocol.io
  4. truefoundry.com
  5. akto.io
  6. sentinelone.com
  7. github.com
  8. apisecuniversity.com

More in Running AI-Generated Code Safely