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.)

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:

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:
| Layer | Control | Closes |
|---|---|---|
| Identity & access | scoped NHI + least privilege · scoped OAuth, no passthrough | confused deputy, excessive autonomy |
| Supply chain | vet source · cryptographically pin tool defs | tool poisoning, rug-pull, supply chain |
| Isolation | each server a security domain (gateway) · sandbox + kill-switch | tool shadowing, unsafe code exec |
| Runtime | human approval · tamper-evident logging + monitoring | high-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:
- Enable only the runtime controls (approval + monitoring). Which threats stay EXPOSED, and why can't runtime controls close them?
- Add vet + cryptographically pin servers. Which three threats does that one control close at once?
- Add scoped identity + least privilege. What does it close, and why is it called the highest-leverage control?
- A teammate connects a new third-party MCP server and auto-approves its tools to move fast. Name two attacks that just became possible.
- 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.