The MCP Stack: What Production Teaches You About AI Agents
Six layers between a working demo and an enterprise deployment, learned the hard way
The demo was the easy part.
Last year I built an MCP server at a large enterprise. It connected an AI assistant to our finance systems, and suddenly questions that used to mean filing a ticket and waiting a day had conversational answers: which invoices failed to post overnight, what a cost center spent last quarter, where a payment got stuck between systems. It worked in the first demo. People who saw it stopped saying “someday” about agents and started saying “when.”
Then came the question of putting it in front of real users, which at an enterprise of this size means a security review. I knew the three questions that review would ask, because they are the questions every security review asks of every system. I could not answer any of them.
Who is the user behind this request? I didn’t know. The server ran on a shared service account. Every request, from every person, looked identical.
What is this user allowed to see? Whatever the service account could see. Which was everything.
Show me what the agent did last Tuesday. There was no log to show.
None of these questions were about AI. They were the questions enterprises have asked of every system for thirty years. And the uncomfortable truth was that my MCP server, the one that had impressed everyone, could not answer a single one of them.
If your MCP server doesn’t know who the end user is, you don’t have an enterprise architecture. You have a demo.
That gap, between a demo everyone loved and a security review I could not yet pass, is where the real work began, and closing it changed how I think about agent systems entirely. The features were never the hard part. The hard part was everything around them: identity, authorization, boundaries, shaping what comes back, and proving all of it happened the way you intended.
That finance assistant was not the last time I learned these lessons. Since then I have built MCP servers over an enterprise integration platform’s full API surface, and over people data, two domains with very different failure modes. The integration platform taught me what raw API responses do to an agent’s context. People data taught me what persona-based access really means when a wrong answer is a privacy incident. Every build surfaced the same gaps in a different order.
This essay is the architecture that came out of those builds. I call it the MCP Stack: six layers that sit between an AI agent and your enterprise systems. I have watched teams rediscover these layers one incident at a time. My goal here is to let you skip the incidents.
Why every MCP demo breaks the same way
Everyone’s first MCP server is a wrapper. You take an existing API, generate one tool per endpoint, hand the catalog to the model, and it works. That is precisely the trap: it works well enough to reach production traffic before anyone asks the production questions.
Two problems then compound.
The first is scale. I did this to myself once: I pointed a wrapper at an integration platform’s OpenAPI surface, one tool per endpoint, and got a catalog of about a hundred tools. The model’s context fills with tool definitions, accuracy drops, costs climb, and the agent starts confusing get_contact_v2 with get_contact_by_id. The ecosystem has made real progress here; tool search and programmatic tool calling can cut token usage dramatically. But those solve catalog scale, not catalog design.
Your tool catalog is a designed product, not a generated artifact.
The second problem is the one my security team found, and it is the one the ecosystem has not solved for you. A wrapped API inherits none of the enterprise controls that made the original API safe to operate. The API had a gateway that authenticated users. The database had roles and row-level permissions. The application had an audit trail. Your MCP wrapper bypasses all of it with one shared credential, and the agent on top will cheerfully exercise every permission that credential holds.
This is not a hypothetical. Later this month the Model Context Protocol publishes its largest specification release since launch; the release candidate is already public. It hardens OAuth, makes the protocol stateless, standardizes tracing. And it explicitly names what remains outside protocol scope: end-user identity propagation, tool risk classification, approval workflows, response filtering, centralized audit. The protocol gives you a socket. What you plug into it, and what stands between it and your systems of record, is your problem.
That gap, stated plainly, is the MCP Stack.
The six layers
Here is the whole architecture in one pass. Each layer has a single defining rule. Everything else is implementation detail.
Layer 1: Identity and Auth Context
The agent never mints identity. The agent only consumes it.
Every request carries three identities: the end user who asked, the agent acting on their behalf, and the service account doing the plumbing. The mistake in my finance server was collapsing all three into one. The fix is identity propagation: the user’s identity flows from the harness, through the agent, into the MCP server, and down to the system of record as signed context that infrastructure injects and no prompt can forge.
This is the layer the industry is converging on fastest. Anthropic’s Enterprise-Managed Authorization, shipped this June, lets organizations provision MCP connectors through their identity provider, so users inherit access from the groups and roles they already have. That is exactly the right direction, and if you build on it you get part of this layer from your platform. The principle stands regardless of vendor: identity is not a feature of your MCP server. It is the substrate everything else evaluates against.
Layer 2: Tool Discovery and Routing
What an agent sees is computed at call time, not configured at integration time.
Tools should represent capabilities, not endpoints: customer.find, not salesforce_get_contact. The agent should never need to know which backend serves a capability, and, more importantly, two different users should not necessarily see the same catalog. A support agent’s assistant has no reason to discover finance tools. In practice this means catalogs scoped by persona: the analyst, the manager, and the executive each discover a different tool surface computed from who they are. Filtering the catalog by identity shrinks both the attack surface and the context window, and it is work the current protocol leaves entirely to you.
Layer 3: Authorization and Policy
Every tool call is a policy decision evaluated against identity, tool, and arguments. Default-deny.
The instinct is to put permission checks inside tool code. That is the new version of if statements scattered across a hundred files: unreviewable, undeployable, and invisible to your security team. Policy belongs outside the code as data, in a policy engine your security team owns directly. They write and review policies; the MCP layer just enforces answers. And the default answer, when no policy matches, is no.
Authorization in tool code is the new IF statement scattered across a hundred files. Externalize it.
Layer 4: Action Boundaries
Every tool declares its risk class. The architecture computes the rails.
Reading a customer record and wiring a million dollars are not the same kind of action, and nothing in the protocol distinguishes them. This layer does: every tool declares one of four risk classes (read, safe write, risky write, destructive), and behavior follows from the declaration. Reads run autonomously. Writes get rails: confirmation, approval workflows, rate limits, snapshots before destructive operations.
The critical property is that risk is structural, declared at design time, not evaluated at runtime by the agent. You do not want a language model deciding in the moment whether an action feels dangerous. Of the six layers, this one has the least ecosystem support today. Nothing published addresses action class. Teams typically discover this layer retroactively, after an incident.
Reads are autonomous. Writes have rails. The cheapest tool to call is the wrong write.
Layer 5: Response Shaping
No response leaves the boundary unshaped. The agent receives what policy allows, never what the backend returned.
Everyone worries about what an agent can do. Fewer people worry about what an agent can see, and that is the quieter leak. When a backend returns a full customer record, every field in it enters the model’s context: reasoning, logs, and downstream tool calls included. The leak is not when the user sees the social security number. The leak happened earlier, when the agent’s reasoning included it.
Response shaping is field-level projection at the MCP boundary, driven by the caller’s identity: the support role gets contact fields, the finance role gets billing fields, and nobody gets fields their role has no claim to. This is policy enforcement, not a developer convenience, and it is the difference between “we filter in the prompt” (which is hope) and “the data never entered the context” (which is control).
Security is only half the payoff. The other half is context efficiency. Enterprise APIs return everything they know: one backend I wrapped sent back around a hundred fields per record when the agent needed five. Every extra field is noise in the model’s context; it dilutes attention, inflates cost, and occasionally derails the reasoning. So I started doing what I call response profiling: for each tool, profile which fields the task actually consumes and project the response down to exactly that. Answers got sharper, token usage dropped, and the shaping layer earned its place twice over.
People data taught me the subtler case: aggregates. A headcount or compensation query that comes back over a group of one is a disclosure wearing a report’s clothing. Real shaping includes minimum-group-size suppression: if the group behind a number is smaller than the threshold, the number does not come back, and the agent is told why so it can redirect the user instead of guessing. The NSA’s security guidance on MCP, published this spring, lands on the same point: filter chained outputs, constrain what flows outward.
Layer 6: Audit and Observability
Every layer logs every decision. If it’s not in the audit log, it didn’t happen.
The other five layers prevent harm. This one proves prevention. That distinction matters most in regulated industries, where “we trust the architecture” is not an answer anyone accepts; “show me the log” is the question.
Decision-level audit is richer than event logging. It captures intent (what was asked), decision (what policy allowed or denied, and why), and outcome (what actually happened), all chained to a correlation ID that ties one logical request across every layer it touched. One query against that ID reconstructs the full chain. That is the difference between intended controls and provable controls.
One request through the stack
Abstract layers become concrete the moment you trace a single call.
Two employees ask their assistant about the same customer. Same tool, same arguments, one word of difference between them: their role.
For the support user, Layer 1 injects their identity. Layer 2 already scoped their catalog so finance tools were never discoverable. Layer 3 evaluates the call and allows it: support may read customer details. Layer 4 notes it is a read and lets it run without ceremony. The backend returns forty fields. Layer 5 projects six of them: name, contact, ticket history. The social security number and billing terms in the raw record never leave the boundary, which means they never enter the model’s context. Layer 6 writes the whole story, decision by decision, under one correlation ID.
For the finance user, the same call travels the same path and comes back different: billing terms and balance, no ticket history, and still no SSN, because no role in the company has a policy claim to it through this tool.
Same tool. Two roles. Two shaped responses. One audit trail. No single layer did the work; the integration is the architecture.
Where to start (not where you think)
The instinct is to start with Layer 1, because “we need auth” is the finding every security review opens with. Resist it for one step.
Start with Layer 6. Audit first.
You can add decision-level logging to an existing MCP server without touching identity, policy, or tool design, and it immediately makes every subsequent step measurable. Before you migrate anything, you need to know what your agents actually call, how often, with what arguments, and what comes back. You cannot migrate what you cannot see.
You can’t migrate what you can’t see. Audit is the first layer, not the last.
Then identity (Layer 1), because three other layers evaluate against it. Then authorization and response shaping in parallel, then catalog design, and action boundaries last, once you know from your audit data which writes actually occur.
A realistic first year for a small team is Layer 6, Layer 1, and partial Layer 5. That is not failure; that is the baseline, and it puts you ahead of most deployments I have seen. Anything beyond it is genuine architectural achievement.
These layers should be declared, not hand-built
Here is the part I find most interesting, and where this series is headed.
Every team that reaches production builds some version of these six layers by hand, in application code, differently. That is exactly where API management stood fifteen years ago, before OpenAPI: everyone hand-rolling the same gateway concerns until a declarative contract standardized them.
The same move is available here. Identity requirements, catalog scoping, authorization policy, risk classes and their rails, response projections, audit obligations: all of it can be declared in a specification the runtime enforces, instead of implemented in code the runtime hopefully gets right. A tool declares riskClass: destructive and the rails follow. A response block declares which fields each role receives and the shaper enforces it. Policy is data. The contract is reviewable by security before a line of server code is written.
I have been designing exactly such a specification, with conformance levels so teams can adopt it incrementally, the way OpenAPI adoption actually happened. The new MCP extensions framework, arriving in this month’s release with exactly this kind of third-party path in mind, makes the timing right. That specification is the subject of the next essays in this series.
What’s still hard
Credibility requires naming what this architecture does not solve. Cross-tenant identity, when your agent serves customers rather than employees. Multi-agent delegation chains, where agent A asks agent B to act for user C. Long-running sessions that outlive the tokens they started with. Field-level redaction at analytical scale. Hallucinated arguments on write calls that are individually valid and collectively wrong. Adversarial robustness at the tool boundary. I have partial answers to some of these and honest uncertainty about the rest.
But notice what has happened in just the past few months: the protocol’s largest release formally scopes these concerns to the application layer, a major vendor productized the first slice of the identity layer, government security guidance arrived recommending output filtering and invocation logging, and analysts began naming gateway products as a category. OAuth took roughly five years to converge. MCP started that clock in late 2024. The layers are being discovered, in public, on schedule.
Which is really the point. Pretend none of this exists, deploy a naive wrapper, and production will teach you the same six layers anyway, at the cost of incidents, exposure, and a rebuild. The architecture is not an opinion. It is a description of what production teaches everyone who gets there.
Build the layers before the incidents. The field will catch up.
This is the first essay in a series on the MCP Stack. Next: why the authorization layer belongs to your security team, not your codebase, and the declarative specification that makes all six layers a contract instead of a codebase. If you work on agents in an enterprise, subscribe and argue with me in the comments; the open questions above are open for a reason.




