Skip to main content

Securing Agents & MCP

Introduction

We've spent the last lessons on how agents get hijacked (L240 — Prompt Injection in Depth) and how a hijack turns into data loss (L241 — Data Exfiltration & Tool Abuse). This lesson zooms out to the security architecture of the whole agentic system — and especially of MCP, the protocol that wires agents to their tools.

The reframe that organizes everything: an AI agent is not one program — it's a distributed system. It has tools, persistent memory, talks to other agents, makes autonomous decisions, and connects to a whole supply chain of MCP servers — most of which you didn't write and shouldn't fully trust. Traditional AI security protected a stateless model answering a prompt; agentic security has to protect an autonomous, stateful, multi-component system. So you secure it the way you secure any distributed system of untrusted parts: identity & access control, supply-chain integrity, isolation, and monitoring — not a clever system prompt.

Two halves:

  • Securing MCP — the tool/connectivity supply chain: vetting servers, pinning tool definitions, scoping authorization, isolating servers.
  • Securing the agent — as a first-class identity with least privilege, sandboxed execution, human oversight, and defenses for the agentic-specific threats (memory poisoning, rogue/inter-agent attacks).

Scope: the foundations course introduced the MCP ecosystem; this is the deep security treatment, and it operationalizes the defenses from L240/L241 at the protocol and identity level. (Next: Red-Teaming Your AI Application — how to test all of this.)

Infographic titled 'Securing Agents & MCP', the fourth lesson of the Security & Red-Teaming section. Left: AN AGENT IS A DISTRIBUTED SYSTEM — a central AGENT (a non-human identity) fans out to untrusted components, each a trust boundary with its own threat: MCP server A (pinned, trusted), MCP server B (untrusted — tool poisoning, rug-pull), tools (confused deputy, over-scoped token), memory (memory poisoning, gradual), and other agents (insecure inter-agent comms). Right: HARDEN EVERY LAYER — IDENTITY & ACCESS (scoped non-human identity, OAuth with no token passthrough, least privilege), SUPPLY CHAIN (vet the source, cryptographically pin servers & tool-definition hashes), ISOLATION (each server is its own security domain, sandbox code + kill-switch), RUNTIME (human approval on high-impact, tamper-evident logging + anomaly monitoring); no single control secures the stack — layer them, and never auto-approve in a multi-server setup. Three cards: MCP is a supply chain (untrusted code; tool poisoning, rug-pulls, tool shadowing; vet, pin, isolate, scan); an agent is a first-class identity (non-human identity, least-privilege scopes, scoped OAuth no passthrough, sandbox + kill-switch, memory poisoning & rogue/inter-agent risks); secure it like a distributed system (classic security architecture — authn/authz, least privilege, supply-chain integrity, isolation, monitoring; layer them, human on high-impact). Section roadmap: OWASP LLM Top 10, prompt injection in depth, data exfiltration & tool abuse, securing agents & MCP, red-teaming.

An Agent Is a Distributed System

Picture a production agent. At the center is the model with a non-human identity; fanning out from it are the components it depends on — and each one is a trust boundary you have to defend:

  • MCP servers — the tools come from servers, often third-party, that you connect over the protocol. Untrusted code in your trust boundary.
  • Tools — each grants the agent authority (and a token/scope) it can misuse (the confused deputy from L241).
  • Memory — persistent state across sessions, which can be poisoned to alter behavior over time.
  • Other agents — multi-agent systems pass messages that can carry injected instructions between agents.

The OWASP Top 10 for Agentic Applications (the agent-specific companion to the LLM Top 10 from L239) catalogs the threats this creates: goal hijacking, tool misuse, identity & privilege abuse, supply-chain vulnerabilities, unsafe code execution, memory poisoning, insecure inter-agent communication, cascading failures, human-trust exploitation, and rogue agents.

The point isn't to memorize ten names — it's the mental shift: you're not hardening a chatbot, you're hardening a system. And we already know how to secure distributed systems of untrusted components: authenticate identities, enforce least privilege, verify the supply chain, isolate the parts, and monitor everything. The rest of the lesson applies each to the agent + MCP stack — and the lab lets you watch each threat close as you add a control.

Securing MCP — It's a Supply Chain

MCP makes it trivially easy to give an agent new powers — just connect a server. That's the feature, and the danger: every server and tool you connect is effectively untrusted code running inside your trust boundary. The MCP-specific threats (per OWASP's MCP security guidance):

  • Tool poisoning — malicious instructions hidden in a tool's description, schema, or return values. The model reads the tool definition to decide how to use it, so a poisoned description is indirect prompt injection delivered through the tool itself. Inspect the full schema, not just the name; treat the whole definition as an injection surface; strip instruction-like patterns from tool outputs.
  • Rug-pull — a server is benign when you approve it, then changes its tool definitions later to turn malicious. The fix is integrity: cryptographically pin tool-definition hashes and alert on any change; re-consent when a schema changes.
  • Tool shadowing / cross-server escalation — a malicious server's tool description manipulates the behavior of tools from other trusted servers. Treat each server as its own security domain; route through an MCP gateway that isolates them; monitor cross-server data flow.
  • Supply-chain attacks — installing an untrusted/compromised server from a public registry (typosquatting, no review). Vet the source, verify signatures/checksums, scan dependencies — exactly like any package supply chain.
  • Confused deputy & token passthrough — a server acts with its own broad privileges, not the user's; or a token gets passed through and over-shared. Scoped, per-server, ephemeral credentials; narrow OAuth scopes (mail.readonly, not full mail); never share or pass tokens between servers.
  • Sandbox escape — a local server running with full host access can read files and steal creds. Run local servers in containers/sandboxes with file-system and network limits.

The discipline is the one software teams already know: a dependency you didn't write is untrusted until vetted, pinned, scoped, and isolated. MCP just made the dependencies executable tools with instructions the model obeys — so the bar is higher, and "never auto-approve tool calls, especially in a multi-server setup" is the golden rule.

# Treat every MCP server like an untrusted dependency: VET, PIN, SCOPE, ISOLATE.
PINNED = {"crm.search_customers": "sha256:9f2a…", "mail.send": "sha256:c41d…"}  # pinned tool-def hashes

def register_tool(server, tool):
    if sha256(tool.definition) != PINNED.get(tool.qualified_name):     # rug-pull / poisoning check
        raise SecurityError(f"{tool.qualified_name}: definition changed → re-consent required")
    if scan_for_injection(tool.description):                            # tool poisoning in the schema
        raise SecurityError("instruction-like text in tool description")

connect_mcp_server(
    "crm",
    creds=ephemeral_token(scopes=["crm.read"]),    # scoped, per-server, short-lived — no passthrough
    sandbox=True,                                  # isolate: container + FS/network limits
    isolation_domain="crm",                        # its own security domain; no cross-server reference
    auto_approve=False,                            # never auto-approve, especially multi-server
)

Securing the Agent — Identity, Isolation & Oversight

The agent itself needs the controls you'd put on any autonomous service that holds privileges:

  • A first-class identity (least privilege). Stop thinking of the agent as "the app" — give it its own non-human identity (NHI) with scoped, minimal privileges, just like a service account. A support agent's identity can read the knowledge base; it cannot touch billing or send mail. This is the single highest-leverage control (it shuts the confused deputy and excessive agency at once).
  • Sandbox execution + a kill-switch. Agents that run code or shell commands must do so in an isolated sandbox with resource limits and rollback — and you need a kill-switch to stop a runaway instantly.
  • Human oversight on high-impact actions. The approval gates from L230 (Agent UX: Showing Reasoning, Progress & Approvals) — a person signs off on consequential, irreversible, or regulated actions; never auto-approve them.
  • Defend the agentic-specific threats:
    • Memory poisoning — because agents have persistent memory, an attacker can plant content that gradually shifts behavior across sessions; it's hard to spot. Validate what enters memory, monitor for drift, and support rollback.
    • Insecure inter-agent comms — in multi-agent systems, authenticate agent-to-agent messages and treat another agent's output as untrusted input; add circuit breakers so one compromised agent can't cascade.
    • Rogue agents — keep an agent inventory (you can't govern what you can't see) and tamper-evident action logs (the audit discipline from L237 — Model Cards, Audit Logs & Incident Response).

Same theme as L241, one level up: assume any single component can be compromised, and make sure that doesn't compromise the system. Scoped identity + sandbox + oversight + monitoring are how.

See It — The Agent Hardening Matrix

Treat your agent like the distributed system it is and harden it layer by layer. Toggle the controls and watch the nine agent/MCP threats flip from EXPOSED to COVERED:

**An agent + its MCP servers is a distributed system of untrusted parts — harden it and watch the threats close.** Toggle the controls across three layers — **identity & access** (scoped identity + least privilege, scoped OAuth / no token passthrough), **supply chain & isolation** (vet + cryptographically pin servers, isolate each server), and **runtime** (sandbox + kill-switch, human approval, monitoring) — and the nine agent/MCP threats flip from **EXPOSED** to **COVERED**, with a live posture score. The experiment that lands it: enable **only approval + monitoring** — lots still **EXPOSED**, because you can’t *watch* your way out of a poisoned tool definition or a confused-deputy token. Add **vet + pin** and **least privilege** and the MCP and identity threats fall at once. **No single control secures the stack — and you need the WHOLE stack.**

The lesson the matrix forces: you can't watch your way to safety. Turn on only approval + monitoring (the runtime layer) and a wall of threats stays EXPOSED — tool poisoning, rug-pull, tool shadowing, the confused deputy — because those live in the MCP supply chain and the identity layer, not at runtime. Add vet + pin and scoped least-privilege identity and they fall at once. No single control secures the stack — and the cheapest, highest-leverage ones are least privilege and a vetted, pinned supply chain.

Putting It Together — Defense in Depth for the Agentic Stack

Assemble the controls into a posture, layered like any secure system:

LayerControlCloses
Identity & accessscoped NHI + least privilege · scoped OAuth, no passthroughconfused deputy, excessive autonomy
Supply chainvet source · cryptographically pin tool defstool poisoning, rug-pull, supply chain
Isolationeach server a security domain (gateway) · sandbox + kill-switchtool shadowing, unsafe code exec
Runtimehuman approval · tamper-evident logging + monitoringhigh-impact misuse, memory poisoning, rogue agents

Two rules to leave with:

  • Never auto-approve in a multi-server setup. The combination of multiple untrusted servers + autonomous tool selection is exactly where tool shadowing and confused-deputy attacks live; a human (or a strict policy) in the loop on consequential calls is non-negotiable.
  • Inventory and monitor. You can't secure an agent you don't know exists, and the agentic attacks (memory poisoning, rogue agents, slow privilege creep) only show up if you're watching action logs and inter-agent traffic (L223 — The Monitoring & Observability Layer, L237).

The whole lesson in one line: an agent is a distributed system of untrusted, autonomous parts — so apply distributed-systems security to it. Identity, supply chain, isolation, monitoring — layered, with a human on the high-impact calls. There is no single control, and there is no "set it and forget it."

🧪 Try It Yourself

Use the Agent Hardening Matrix and what you've learned:

  1. Enable only the runtime controls (approval + monitoring). Which threats stay EXPOSED, and why can't runtime controls close them?
  2. Add vet + cryptographically pin servers. Which three threats does that one control close at once?
  3. Add scoped identity + least privilege. What does it close, and why is it called the highest-leverage control?
  4. A teammate connects a new third-party MCP server and auto-approves its tools to move fast. Name two attacks that just became possible.
  5. Why is memory poisoning uniquely hard to catch, and which control is your best bet against it?

(1) Tool poisoning, rug-pull, tool shadowing, supply chain, and the confused deputy stay EXPOSED — they live in the MCP supply chain and identity layers. You can't monitor your way out of a poisoned tool definition or an over-shared token; you have to vet/pin and scope them. (2) Tool poisoning, rug-pull, and supply-chain attacks — all three are integrity problems that cryptographically pinning tool definitions (and vetting the source) closes. (3) It closes the confused deputy and excessive autonomy — giving the agent a non-human identity with minimal scopes means even a hijacked agent can't reach what it was never granted, so it shuts whole classes of misuse at once. (4) A rug-pull (the server changes its tool defs after approval) and tool poisoning (a malicious description hijacks the agent) — which is why the golden rule is never auto-approve, especially in multi-server setups. (5) Because memory persists across sessions and an attacker can shift behavior gradually, so there's no single obvious bad event — your best bet is monitoring for behavioral drift (plus validating what enters memory and supporting rollback).

Mental-Model Corrections

  • “Securing an agent = securing the model.” An agent is a distributed system — tools, memory, other agents, MCP servers. Secure the system, not just the model.
  • “MCP servers are like libraries I trust.” They're untrusted, executable code with instructions the model obeys. Vet, cryptographically pin tool defs, scope, and isolate each one.
  • “I approved the tool once, so it's safe.” Rug-pull: definitions can change after approval. Pin hashes and re-consent on change.
  • “Give the agent broad access so it's capable.” That's the confused deputy waiting to happen. A scoped non-human identity with least privilege is the highest-leverage control.
  • “Monitoring will catch attacks.” Monitoring catches runtime anomalies — it can't stop a poisoned tool definition or an over-scoped token. Those need supply-chain and identity controls.
  • “Auto-approve tools to move fast.” Never auto-approve, especially multi-server — it's exactly where tool shadowing and rug-pulls strike.
  • “Agents are just stateless LLM calls with extra steps.” They have persistent memory and autonomy — so memory poisoning, rogue agents, and inter-agent attacks are real, and need monitoring, sandboxing, and authenticated comms.

Key Takeaways

  • An agent is a distributed system of untrusted, autonomous parts (tools, memory, other agents, MCP servers) — secure it with distributed-systems discipline: identity, supply chain, isolation, monitoring.
  • MCP is a supply chain: every server/tool is untrusted code. Vet the source, cryptographically pin tool-definition hashes (stops poisoning & rug-pulls), scope OAuth narrowly / no token passthrough (stops the confused deputy), and isolate each server (stops tool shadowing).
  • The agent is a first-class identity: give it a non-human identity with least privilege (the lever), sandbox code execution with a kill-switch, and keep human approval on high-impact actions (L230).
  • Mind the agentic-specific threats: memory poisoning (monitor for drift), insecure inter-agent comms (authenticate + circuit-break), and rogue agents (inventory + tamper-evident logs, L237).
  • No single control secures the stack — layer them, never auto-approve in multi-server setups, and monitor (L223). Next: Red-Teaming Your AI Application — how to actually test all of this.