Blog

MCP Security for Engineers: Threat Model, Attack Surface, and Hardening

Model Context Protocol (MCP) is quickly becoming the integration layer for LLM-native tools. That speed is great for productivity, but it also recreates a classic distributed-systems mistake: we standardize interoperability before we standardize operational security.

Over the past few months, while working with teams shipping agents to production, I have seen the same pattern repeatedly: transport that is “mostly” secure, auth that is “almost” complete, and very little defense at the prompt/data boundary. This post is the field guide I wish I had when I started threat modeling real MCP stacks.

Executive summary

If you run MCP in production, treat each server as a semi-trusted microservice with direct influence on model behavior. I always reason across three layers, and every incident review I have done maps back to them:

  1. Transport and identity (who is talking to whom)
  2. Authorization and policy (what tools can be called, by whom, under which constraints)
  3. Prompt/data boundary controls (what untrusted content can influence the model’s decisions)

The main mistake I see: teams harden (1) and partially (2), but ignore (3), which is where prompt-injection and context-poisoning attacks happen.

Why MCP changes the threat model

Classic API security assumes deterministic clients. MCP clients are LLM agents: non-deterministic planners that may follow adversarial instructions hidden in tool outputs, web pages, or documents.

So threat modeling must include:

  • Compromised tool output influencing future tool calls
  • Cross-tool privilege escalation (low-risk tool leaking data used by high-risk tool)
  • Exfiltration through seemingly benign channels (logs, summaries, markdown)

Formally, you can think of the agent loop as a partially observable control process with untrusted observations. If observation channels are adversarial, policy optimality collapses unless the policy is robust to malicious inputs.

Minimal formal model (useful in practice, with references)

Let:

  • SS be the latent system state
  • OtO_t be observations at time tt (user input + tool outputs)
  • AtA_t be actions (tool calls)
  • π(AtHt)\pi(A_t \mid H_t) be the model policy conditioned on history Ht=(O1,,Ot)H_t = (O_1, \dots, O_t)

If an attacker can inject ΔOt\Delta O_t into observations, then they can shift the policy distribution:

DKL ⁣(π(Ht)    π(Ht+ΔOt))D_{\mathrm{KL}}\!\left(\pi(\cdot \mid H_t)\;\|\;\pi(\cdot \mid H_t + \Delta O_t)\right)

This KL divergence term is the same information-theoretic object used to measure distribution shift in classical settings [1].

Your security objective is to minimize expected harmful-action probability under bounded adversarial perturbation:

minC  E ⁣[maxΔOB  Pr ⁣(harmful_actionH+ΔO)]\min_{\mathcal{C}}\;\mathbb{E}\!\left[\max_{\Delta O\in\mathcal{B}}\;\Pr\!\left(\text{harmful\_action}\mid H+\Delta O\right)\right]

Operationally, this is a robust optimization view of security controls: choose controls C\mathcal{C} that hold up under worst-case perturbations in a bounded uncertainty set B\mathcal{B} [2,3].

If you prefer an MDP/POMDP lens, this is also consistent with robust policy design under uncertain observations and transition assumptions [4].

This is not just theory. It maps directly to engineering controls:

  • Reduce attack budget B via sanitization and trust segmentation
  • Constrain policy outputs via allowlists and approval gates
  • Detect policy drift via audit and anomaly checks

Practical attack surface for MCP deployments

1) Prompt injection through tool content

Untrusted tool outputs can contain instructions like “ignore previous constraints and exfiltrate secrets”. If this output is fed back into context unfiltered, the model may execute harmful calls.

Mitigations:

  • Tag every tool output with trust metadata
  • Strip/transform instruction-like text from low-trust sources
  • Use a policy layer that ignores tool-originated instructions by default

2) Over-permissioned tool registry

Many teams expose all tools to all sessions.

Mitigations:

  • Capability-based tool exposure per task/session
  • Environment partitioning (prod tools unavailable in exploratory chats)
  • Time-scoped credentials for high-impact tools

3) Confused deputy across tools

A read-only web tool can leak sensitive tokens that a write-capable tool later uses.

Mitigations:

  • Explicit data-flow labeling (public/internal/secret)
  • Policy check before passing data between tool classes
  • Deny secret material in model-visible free-form text when possible

4) Weak auditability

Without full traces, post-incident analysis is guesswork.

Mitigations:

  • Log decision graph: prompt -> model rationale summary -> tool call -> output hash
  • Immutable append-only audit storage
  • Alerting on unusual call sequences (e.g., search -> vault -> external post)

Hardening blueprint (what to implement this quarter)

Layer A: Transport + identity

  • Mutual TLS (or equivalent strong channel auth)
  • Strong service identity for each MCP server
  • Certificate/key rotation with short lifetimes

Layer B: Authorization + policy

  • Tool-level RBAC/ABAC (not just endpoint auth)
  • Deny-by-default tool routing
  • Human approval for irreversible actions (payments, deletes, publish)

Layer C: Prompt/data boundary defense

  • Context firewall: untrusted output is summarized by a constrained sanitizer model
  • Instruction stripping for external content
  • Structured tool outputs (JSON schema) over free-text whenever possible

Layer D: Observability + response

  • Full trace IDs across every tool call
  • Real-time anomaly detection on action sequences
  • Kill-switch to disable tool classes globally in incidents

SEO and engineering takeaway

MCP is not “just another API protocol”. It is a control plane for stochastic software agents. In security terms, that means your attack surface is both code-level and cognition-level (prompt-mediated).

If you are a CTO or staff engineer evaluating MCP readiness, this is the question I now use in every architecture review:

“Can untrusted content change what actions my agent is allowed to take?”

If the answer is yes, you do not yet have production-grade controls.