Commentary

The 11 Layers of a Production MCP Server: a Catalog of Hard Problems, Not a Checklist

A reaction to Fareed Khan's Atlas-MCP article: what I take from default-deny, approval gates and audit — and why an MCP server as the mandatory control point doesn't carry over to a personal read-only agent.

In his article on the 11 layers of a production-grade MCP server, Fareed Khan builds a fictional but thoroughly worked-out production MCP server, "Atlas-MCP": Postgres Row-Level Security for tenant isolation, OAuth 2.1, a default-deny policy engine, approval gates for destructive operations, a circuit breaker, a Redis token bucket, two-tier caching, observability. It reads like a catalog of everything that gets hard once an agent stops being a toy. Nearly all of it holds up. One caveat made me write this note.

What I'm taking

  • Default-deny everywhere. Unknown policy conditions fail closed; an empty outbound allowlist means zero outgoing requests. The right default for agents: prompt injection can't route around what is physically forbidden, as opposed to forbidden by a line in the prompt.
  • The approval gate as a control surface. A destructive tool doesn't execute — it creates a pending record with a TTL; a human approves, and both outcomes land in the audit log. The author compares this to confirming edits in Claude Code, and the comparison is exact: confirmation isn't ceremony, it's the control surface.
  • Audit with args_hash instead of raw arguments — event correlation without hoarding PII.
  • Structured errors (code / retryable / hint): "Agents cannot recover from Python tracebacks." Verified on my own agents — they don't.

Where I disagree

The article treats a dedicated MCP server as the mandatory control point and never once compares it with the alternative: the agent calling CLI/API directly. For the article's scenario — multi-tenant SaaS, other people's users, write operations — the choice is right: the MCP server is the one place where RLS, auth and rate limits live together. But I have the opposite production case in front of me: a read-only Kubernetes debugging agent whose author started with an MCP wrapper and deliberately dropped it in favour of Bash + curl + skills. The argument is simple: an MCP tool like query(query: string) types the way of calling, but the request body — the MetricsQL string, the kubectl filter — is still written by the LLM. Typing doesn't remove the main risk. Read/write segregation there came from an allow/deny list on the Bash tool plus GET-only curl — no MCP layer at all.

My own setup works the same way: repeatable procedures live in skills; MCP is wired in only where I need persistent typed access to an external service (a local LLM on a separate machine). The working rule: the layers in the article are properties of the deployment context, not of the protocol. Multi-tenant, write operations, other people's data — most of those layers are mandatory, and they're better off living in one place. A single developer, read-only, a CLI the model already knows from pretraining — a harness is enough: allowlist, GET-only, a confirmation gate.

One more reason to read the article with a pencil: the numbers. "A three-tier tool hierarchy cuts erroneous tool calls by ~40%" rests on an external case study and is essentially unverifiable. As an order of magnitude — maybe; as a benchmark — no.

Bottom line

As a catalog of the hard problems in production agent systems, the article is excellent. Reading it as a checklist for every agent would be the mistake. Layers get switched on by blast radius, not by fashion.