The Model Context Protocol made it trivial to hand an AI agent real capabilities. It did not make it safe. After building a production MCP server — 19 tools that let an agent draft, schedule, and publish social content on a human's behalf — here are the patterns that mattered.
The agent is a confused deputy waiting to happen
Treat every tool call as if it came from an over-eager intern with root access: probably well-intentioned, occasionally catastrophically wrong, and always capable of retrying something that already succeeded.
That framing produces most of the right decisions automatically.
Pattern 1: The agent never holds credentials
My server publishes to third-party channels. The agent never sees those channel credentials — not scoped versions, not short-lived versions, nothing. It holds one thing: an OAuth 2.1 token scoped to my API. Channel secrets live server-side, behind code paths the agent cannot influence beyond the tool contract.
If an agent can be prompt-injected into leaking a credential, the only winning move is for there to be no credential to leak.
Pattern 2: Idempotency keys on every mutating tool
Agents retry. Networks flake, context windows truncate mid-loop, orchestrators re-run steps. Every mutating tool on my server takes an idempotency key, and a retried call returns the original result instead of creating a duplicate. Without this, "schedule a post" becomes "schedule three posts" the first time an agent hiccups.
Pattern 3: Destructive actions need a second call
Tools that delete or publish are annotated as destructive, and the annotation is enforced, not advisory: the first call returns a confirmation token describing exactly what will happen, and only a second call carrying that token executes. An agent can't blow through the confirmation in one step, and a human reviewing the transcript sees the intent before the effect.
Pattern 4: Scopes that match blast radius, not features
API keys are scoped by what damage they can do, with step-up scopes for the risky tier. A key that can draft cannot publish. A key that can publish cannot delete history. When one leaks or one agent goes sideways, the blast radius was decided in advance, calmly, by you — not at 2am by the incident.
Pattern 5: A human approval stage that isn't theater
The approval queue is server-side state, not a UI nicety. Anything the agent stages sits in pending until a human accepts it — from a phone, in seconds. The agent gets delivery receipts either way, so it can learn what was rejected. Approval-as-architecture beats approval-as-checkbox: the agent literally has no code path to skip it.
What I'd tell a team adopting MCP
Start from the incident you fear, and design the tool contract so that incident is structurally impossible rather than merely discouraged. Prompts are suggestions; scopes, idempotency, and confirm handshakes are physics.
If you're building agent access to something you care about and want the guardrails done properly, that's exactly the work I take on.