The NSA's MCP Security Checklist: Boundaries, Sandboxes, Signed Messages

·13 min read·Evergreen Tools Team

On May 20, 2026, the National Security Agency's Artificial Intelligence Security Center released a Cybersecurity Information Sheet titled Model Context Protocol (MCP): Security Design Considerations for AI-Driven Automation - the first formal MCP security guidance from a US national security agency. Its premise is not flattering to the ecosystem. MCP has become the de facto standard for AI-driven services, its adoption has outpaced the development of its security model, and the protocol reverses a familiar interaction pattern: instead of clients requesting data from servers, MCP often expects servers to query and sometimes execute actions for connected clients, which creates new attack paths that have not been well traced. If you run MCP in production, the sheet is worth working through line by line.

Trust boundaries first: agents, plugins, models and users are not one zone

Trust boundaries first: agents, plugins, models and users are not one zone

1. Why MCP Needs Its Own Guidance

The document is blunt about the starting condition: MCP was released with a flexible and underspecified design, giving implementers freedom and introducing ambiguity for safe usage - much like early web protocols. The risks are no longer theoretical. Public labs and security researchers have released vulnerable MCP server implementations specifically to show how easily insufficiently secured MCP can be exploited, and one class of those vulnerabilities is arbitrary code execution, which arises where user-provided logic or code is executed and is tracked under CWE-77, CWE-78, CWE-94, and CWE-95. The sheet names products deploying MCP across business, finance, legal, and software development - AutoGen Studio, Harvey AI, Agentverse, and Copilot among them - and notes that MCP is already used for sensitive tasks such as querying personally identifiable information. Its conclusion is that secure-by-default behaviour has to be enforced through implementation rigour, coding practice, clearer specifications, and robust validation tooling. In other words: the protocol will not make you safe.

# 1. Design for boundaries: put tools in data classification zones
trust_zones:
  public:
    tools: [weather_lookup, public_docs_search]
    data: public
  internal:
    tools: [ticket_search, deploy_status]
    data: internal
  regulated:
    tools: [ehr_query, payment_ledger]
    data: regulated
    runtime: local                        # prefer a local MCP server
    approval: human_in_the_loop

rules:
  - user_facing_plugin_output: never_passes_to_privileged_backend_unchecked
  - dynamic_tool_discovery: require_origin_verification_or_authorization

2. Start With Boundaries and Data Classification

The first recommendation is to design for boundaries. Organisations should define trust boundaries between MCP components - agents, plugins, models, end users - and treat each as living in a different trust zone with its own assumptions and controls. A concrete example from the sheet: data originating from a user-facing plugin should not be blindly accepted by a privileged backend model. Second, treat dynamic tool discovery with caution, because that flexibility is only safe when it can be coupled with origin verification or authorization checks. Third, align tools and models with data classification zones: publicly available tools handle public datasets, while tools touching sensitive or regulated information - national security information, controlled unclassified information, health records, financial data - must be explicitly controlled and segregated. When processing private data, prefer a local MCP server instance to reduce leakage risk. Finally, constrain outbound connections with a filtering egress proxy such as Squid or tinyproxy, or an enterprise DLP solution, specifying the resource URLs and access methods that are permitted.

// 2. Validate parameters against schema, range, AND execution context
import Ajv from "ajv";
import { execContext } from "./runtime";

const ajv = new Ajv({ allErrors: true });

function validateInvocation(tool, args, caller) {
  const schema = tool.inputSchema;
  if (!ajv.validate(schema, args)) {
    throw new Error("schema violation: " + JSON.stringify(ajv.errors));
  }
  if (!tool.allowedContexts.includes(execContext.name)) {
    throw new Error("context mismatch: refusing to execute in " + execContext.name);
  }
  if (caller.isUserSupplied && !tool.allowsParameterForwarding) {
    // NSA guidance: parameter forwarding should be blocked or restricted when
    // the source of the data is ambiguous or potentially user-supplied.
    throw new Error("ambiguous origin: forwarding blocked");
  }
  return true;
}
Every tool invocation is a high-risk action until constrained

Every tool invocation is a high-risk action until constrained

3. Validate Parameters, Then Constrain Execution

The sheet's definition of parameter validation is broader than most teams assume: validate the input, and also understand the context and configuration of the execution environment. Its example is persuasive - in an MCP-connected mathematical interpreter, a malicious actor can manipulate a context parameter to trigger unintended file I/O while the mathematical input and output look perfectly valid. Every tool invocation or model execution request should therefore be validated against well-defined schemas, expected ranges, and the intended context in which the data will be processed, including malformed inputs, missing fields, and excessive sizes, any of which can be leveraged for prompt injection or denial of service. Parameter forwarding should be explicitly blocked or restricted when the source of the data is ambiguous or potentially user-supplied, otherwise inputs intended for one component get misinterpreted downstream. Execution is even harder: treat any MCP-triggered tool execution as potentially high-risk, and isolate each tool's execution context with operating system frameworks - AppContainers on Windows, seccomp, AppArmor, SELinux - following least privilege so that filesystems, model and data files, and internal network paths a server does not require are denied at runtime.

# 3. Constrain and sandbox tool execution (one sandbox per tool)
#    OS-level isolation examples from the CSI: AppContainer (Windows),
#    seccomp, AppArmor, SELinux.
services:
  mcp-tool-filesystem:
    image: internal/mcp-tool-fs:1.4.0
    read_only: true
    security_opt:
      - no-new-privileges:true
      - apparmor:docker-default
    cap_drop: [ALL]
    network_mode: none                  # deny by default, add nothing back
    volumes:
      - /srv/workspaces/agent-42:/workspace:rw
    tmpfs:
      - /tmp:size=64m
# Least privilege: if a server does not need a sensitive filesystem, model or
# data file, or an internal network, deny that path at runtime.

4. Sign Messages and Stop Trusting the Transport

The sheet warns that message authenticity and confidentiality should never be assumed, especially when processing sensitive data or making security-critical decisions. MCP currently relies on transport layer encryption such as TLS, but the protocol itself cannot enforce or verify encryption and is unaware of message integrity. The recommendation is to extend the standard with cryptographic signatures directly inside the JSON payload using capabilities available in modern libraries, adding time-bound signatures where session protection is needed. Beyond signatures, MCP messages should carry expiration timestamps and replay protection metadata to guard against delayed or duplicated messages, a known risk in distributed and event-driven systems. The sheet points at OWASP's Application Security Verification Standard, section V7 on session management, as directly applicable: requests should be cryptographically bound to time and context to prevent tampering, intentional replay, and unintended re-execution.

# 4. Sign MCP messages and bind them to time and context
import hmac, hashlib, json, time, secrets

SIGNING_KEY = b"...from-key-vault-not-from-env..."
SEEN_NONCES = set()
MAX_SKEW_S = 30

def sign_message(payload: dict) -> dict:
    envelope = {
        "payload": payload,
        "iat": int(time.time()),
        "exp": int(time.time()) + MAX_SKEW_S,
        "nonce": secrets.token_urlsafe(16),      # replay protection
        "aud": "mcp-server-inventory",           # bind to the intended receiver
    }
    body = json.dumps(envelope, sort_keys=True, separators=(",", ":"))
    envelope["sig"] = hmac.new(SIGNING_KEY, body.encode(), hashlib.sha256).hexdigest()
    return envelope

def verify_message(envelope: dict) -> bool:
    body = json.dumps({k: envelope[k] for k in ("payload", "iat", "exp", "nonce", "aud")},
                      sort_keys=True, separators=(",", ":"))
    expected = hmac.new(SIGNING_KEY, body.encode(), hashlib.sha256).hexdigest()
    if not hmac.compare_digest(expected, envelope["sig"]):
        return False
    if envelope["exp"] < time.time() or envelope["nonce"] in SEEN_NONCES:
        return False
    SEEN_NONCES.add(envelope["nonce"])
    return True

# The CSI recommends signatures inside the JSON payload, plus expiration
# timestamps and replay-protection metadata; OWASP ASVS V7 session management
# is directly applicable.
Log the parameters, the identity, and a hash of the result

Log the parameters, the identity, and a hash of the result

5. Filter Outputs, Log Everything, Track Vulnerabilities

Outputs are treated as untrusted input in this document. Even when an output comes from a previously vetted component, it must be treated as untrusted input to the next phase of the pipeline and scrutinised before being processed or displayed. Filtering should include content length checks, disallowed keyword scanning, rate limiting, and application-specific policy enforcement - and because models and agents generate text that looks benign while carrying hidden logic or prompt manipulation, output filtering must include detection of indirect prompt injection and toolchain pivot attempts. In multi-component pipelines that means logging and inspecting the output of each tool before passing it to the next. On observability, the sheet requires that all tool and model invocations be logged with exact parameters, the identities involved, and where feasible cryptographic hashes of results, with telemetry routed into existing SIEM, threat detection, or compliance tooling. It also asks organisations to establish a formal process for tracking MCP-related vulnerabilities. One closing caution is worth repeating: MCP-aware security proxies remain limited and are still maturing, so they may offer partial mitigations and should be used carefully, especially with sensitive data.

# 5. Treat every output as untrusted input to the next hop
def inspect_output(tool_name, output, expected_schema, limits):
    findings = []
    if len(output) > limits.max_bytes:
        findings.append("oversized_output")
    for pattern in limits.injection_signals:
        if pattern.search(output):
            findings.append("possible_indirect_prompt_injection")
    if not schema_ok(output, expected_schema):
        findings.append("schema_violation")

    log_event({
        "tool": tool_name,
        "params_hash": sha256(canonical(args)),   # exact parameters, hashed
        "identity": caller_identity,               # who invoked it
        "result_hash": sha256(output),             # hash of the result
        "findings": findings,
        "sink": "siem",
    })
    return {"forward": not findings, "findings": findings}

📌 Frequently Asked Questions

What is the NSA MCP security guidance?

A Cybersecurity Information Sheet from the NSA's Artificial Intelligence Security Center titled Model Context Protocol (MCP): Security Design Considerations for AI-Driven Automation, numbered U/OO/6030316-26 and PP-26-1834, May 2026 Ver. 1.0. The accompanying NSA press release is dated May 20, 2026.

Why did the NSA single out MCP?

Because adoption has outpaced the protocol's security model. The sheet says MCP is now found in production across business, finance, legal, and software development, and that the protocol reverses the usual client-server pattern - servers query and sometimes execute actions for clients - producing new, not-well-traced attack paths that established cyber defence strategies do not adequately address.

Which vulnerability classes does it map to?

It highlights arbitrary code execution, which arises in MCP environments where user-provided logic or code is executed, tracked under CWE-77, CWE-78, CWE-94, and CWE-95 for vulnerability management.

How should MCP messages be protected?

Add cryptographic signatures inside the JSON payload using modern libraries, include time-bound signatures where needed, and attach expiration timestamps and replay protection metadata so requests are bound to time and context. OWASP ASVS V7 session management guidance applies directly.

Are MCP-aware security proxies a solution?

Not yet. The sheet describes them as limited and still maturing, offering partial mitigations that should be used with caution - particularly when handling sensitive data - rather than as a substitute for boundary design, parameter validation, and execution sandboxing.