MCP Security

MCP servers hold credentials and take actions — that's power an attacker wants. Four threat classes every agent architect must design against: poisoning, over-permission, token leaks, and rogue servers.

▶ Watch this reel

What you'll learn

  1. Tool poisoning
  2. Over-permissioned servers
  3. Token & credential handling
  4. Defense in depth

Remember this

Tool poisoning

Over-permissioned servers

Token handling

Defense in depth

Vet → scope → confirm → monitor → respond. Keep current with official MCP security advisories.

Code: A side-effect gate: confirmation before consequence

SIDE_EFFECTS = {"send_email", "delete_file", "update_record", "make_payment"}

async def guarded_call(server, tool_name, args, user):
    call = audit.log(user, server, tool_name, args)   # always log

    if tool_name in SIDE_EFFECTS:
        # Show WHAT, not just which tool:
        summary = await describe_action(server, tool_name, args)
        approved = await ui.confirm(
            f"{user.name}: allow '{tool_name}'?\n\n{summary}\n\n"
            f"Args: {redact_secrets(args)}")          # scrub before display
        if not approved:
            audit.log_denied(call)
            return ToolError("denied by user — do NOT retry; "
                             "ask the user what they'd like instead")
    return await server.call(tool_name, args)

# Notes:
# - redact_secrets() runs on EVERY display/log path — defense at the sink
# - denial messages tell the model not to retry (no permission-loops)
# - the audit record exists whether approved or not