# A reference architecture for MCP servers in production

> Source: <https://pournasserian.com/writing/mcp-2026-reference-architecture>
> Published: 2026-10-24 00:00:00+00:00

# A reference architecture for MCP servers in production

Most parts of this series added a duty to a Model Context Protocol (MCP) server, and almost every duty leaves a mark on how you deploy it. This part gathers those marks into one generic architecture, one way to host it on Azure, and a build pipeline for MCP’s own artifacts.

This is part 34 of my series on what an MCP server does under the 2026-07-28 specification, after [part 33 on putting an AI agent behind an MCP tool](https://pournasserian.com/writing/mcp-2026-agent-behind-a-tool). The facts are as I read them in October 2026.

## In brief

1. **Stateless requests let any instance serve any request,** so the server tier scales horizontally with no sticky sessions, and handles and task records live in a shared, durable store.
2. **Two things still need routing care:** a`subscriptions/listen` stream stays on the instance that accepted it, so change events fan out over publish/subscribe (pub/sub); task calls carry their`taskId` in`Mcp-Name` , so you can partition the task store or route on the header.
3. **The edge routes and applies policy from the `Mcp-Method` and `Mcp-Name` headers,** and can check them against the body; on a server that uses OAuth, each request’s bearer token is validated against cached signing keys.
4. **On Azure, keep at least one replica running for interactive clients,** as Microsoft recommends; one mapping runs the server on Container Apps behind Front Door and API Management.
5. **My advice: give MCP’s own artifacts a build stage** that checks schemas against their declared dialect, recomputes skill digests from the served bytes, and runs the official conformance suite and tests in both protocol eras.

## What drives the design

Each driver comes from an earlier part:

| Driver | Consequence | 
|---|---|
| [Stateless requests](https://pournasserian.com/writing/mcp-2026-stateless-requests) : any instance can serve any request | Horizontal scaling, serverless-friendly, no sticky sessions | 
| Explicit handles and task records | A shared, durable store | 
| [`subscriptions/listen`](https://pournasserian.com/writing/mcp-2026-subscriptions-listen) streams pinned to instances | Pub/sub fan-out, and idle timeouts sized for long-lived streams | 
| [Tasks](https://pournasserian.com/writing/mcp-2026-tasks) routed by`Mcp-Name` =`taskId` | A partitioned store, or routing on the header | 
| [Bearer token](https://pournasserian.com/writing/mcp-2026-oauth-resource-server) on every request, where the server uses OAuth | Fast token validation with a cached JSON Web Key Set (JWKS) | 
| [`Mcp-Method` and `Mcp-Name` headers](https://pournasserian.com/writing/mcp-2026-transports) | Policy and routing at the edge without parsing bodies | 
| [Trace context in `_meta`](https://pournasserian.com/writing/mcp-2026-opentelemetry) | An OpenTelemetry pipeline end to end | 
| [Skills](https://pournasserian.com/writing/mcp-2026-skills) digests and[user interface (UI) templates](https://pournasserian.com/writing/mcp-2026-apps) | Build-time artifacts with stable bytes | 

## The generic architecture

Here’s the architecture I’d start from: hosts, from Claude to agents in integrated development environments (IDEs), reach a tier of MCP server instances through an edge, with identity, state, backends and operations around it:

**The edge**, an application programming interface (API) gateway or web application firewall (WAF), terminates Transport Layer Security (TLS), applies rate limits and policy on the `Mcp-Method` and `Mcp-Name` headers, and can reject a request whose headers disagree with its body; the server itself answers that mismatch with `-32020` ([part 7](https://pournasserian.com/writing/mcp-2026-transports)).

**Identity** sits with an authorization server or identity provider (IdP), where the server tier fetches the JWKS and exchanges tokens. The server tier itself publishes the Protected Resource Metadata that points clients there ([part 18](https://pournasserian.com/writing/mcp-2026-oauth-resource-server)), and [part 28](https://pournasserian.com/writing/mcp-2026-enterprise-auth) covers the Identity Assertion JSON Web Token (JWT) Authorization Grant (ID-JAG).

**The MCP server tier** is a set of stateless instances. Their codec seals the `requestState` of Multi Round-Trip Requests (MRTR) with Authenticated Encryption with Associated Data (AEAD) keys, which every instance must share to open a retry ([part 14](https://pournasserian.com/writing/mcp-2026-request-state)).

**State, async work and backends:** a job queue feeds workers that execute tasks, write handles and task records to a durable store ([part 26](https://pournasserian.com/writing/mcp-2026-tasks)) and publish change events on a pub/sub bus to the instances holding listen streams. The server tier and the workers call your business APIs, databases and search index, and a large language model (LLM) provider only for agentic tools ([part 33](https://pournasserian.com/writing/mcp-2026-agent-behind-a-tool)).

**Operations:** an OpenTelemetry (OTel) collector carries traces and metrics to application performance monitoring (APM) and logs ([part 20](https://pournasserian.com/writing/mcp-2026-opentelemetry)), and a store holds secrets and keys, the shared AEAD keys among them.

[Part 7](https://pournasserian.com/writing/mcp-2026-transports) covers the much smaller picture of a local stdio server.

## One way to host it on Azure

Hosts reach Azure Container Apps through Front Door with its WAF and then API Management, with Microsoft Entra ID for identity. Cosmos DB holds handles and task records, Service Bus the task queue and change topics, and Key Vault the AEAD keys. The services are examples as of October 2026; the table names a few alternatives.

| Concern | Azure option | Notes | 
|---|---|---|
| Edge, WAF | Front Door + WAF | Size idle timeouts for long-lived listen streams | 
| Gateway policy | API Management | Validate JWTs, rate-limit per tool using `Mcp-Name` , reject header and body mismatches | 
| Identity | Entra ID | Usually pre-registered clients; on-behalf-of for downstream calls ( [part 19](https://pournasserian.com/writing/mcp-2026-oauth-step-up-and-entra) ) | 
| Compute | Container Apps (or App Service) | Stateless instances; scale on Hypertext Transfer Protocol (HTTP) concurrency | 
| State | Cosmos DB | Partition task records by `taskId` | 
| Async | Service Bus + Container Apps Jobs or Functions | Idempotent workers; dead-letter queues | 
| Change fan-out | Service Bus topics (or Redis pub/sub) | Instances holding listen streams subscribe | 
| Secrets, keys | Key Vault | Rotate `requestState` AEAD keys with key IDs in the blob | 
| Observability | Azure Monitor OpenTelemetry to Application Insights | End-to-end traces through `_meta.traceparent` | 

I’d add two cautions.

First, Container Apps can scale to zero, which suits low-traffic or non-interactive servers. For interactive clients, [Microsoft recommends](https://learn.microsoft.com/azure/container-apps/mcp-overview) a minimum of one replica, and its troubleshooting notes report a first request taking 10 to 30 seconds after a period of inactivity.

Second, listen streams keep instances alive, are cut when an instance recycles, and are reopened only within limits. As of October 2026, [Claude Code](https://code.claude.com/docs/en/mcp) reopens a stream that closes again within 10 seconds up to three times, then stops for that connection. If it stayed open longer, Claude Code keeps reopening it, but after five reopens in one hour it waits about six hours, keeping the tools, prompts and resources it last fetched until then ([part 17](https://pournasserian.com/writing/mcp-2026-subscriptions-listen)). My advice: make change events re-fetchable rather than one-shot.

## A build pipeline for MCP-specific artifacts

Besides its code, an MCP server has artifacts with rules of their own: tool schemas and names, `x-mcp-header` marks on parameters ([part 7](https://pournasserian.com/writing/mcp-2026-transports)), `SKILL.md` front matter, a UI bundled into one HyperText Markup Language (HTML) file, and digests that must match the served bytes. And stateless instances can be deployed blue/green:

These are the checks I’d automate:

- **Schema validation.** Validate every`inputSchema` and`outputSchema` against the meta-schema of its declared dialect, which the[specification](https://modelcontextprotocol.io/specification/2026-07-28/server/tools) sets to JSON Schema 2020-12 when no`$schema` is given. As of October 2026, Claude Code excludes tools whose input schemas fail its 2020-12 meta-schema and property-name checks.
- **Deterministic ordering.** Snapshot-test`tools/list` and compare it across builds ([part 6](https://pournasserian.com/writing/mcp-2026-caching-and-pagination) explains why).
- **Description budget.** Fail the build if the instructions or a description run past about 2,000 characters ([part 4](https://pournasserian.com/writing/mcp-2026-server-instructions) explains why).
- **Skill integrity.** In a test, recompute each digest from the exact bytes you serve.
- **Conformance.** Run the official conformance suite (it has a GitHub Action);[my review of MCP testing tools](https://pournasserian.com/writing/mcp-server-testing-tools-2026) records that it carries per-revision requirement sets for 2025-11-25 and 2026-07-28. The MCP Inspector’s own conformance runner is still on its roadmap, for November 2026 to January 2027.
- **Two eras.** Test against one 2026-07-28 client and one legacy client ([part 22](https://pournasserian.com/writing/mcp-2026-migration-two-eras) ), and run the fake-host integration test of[part 30](https://pournasserian.com/writing/mcp-2026-end-to-end) .

## Method and caveats

- Built from my guide’s reference-architecture chapter, written against the 2026-07-28 specification; vendor documentation as checked on 8 October 2026.
- Where Claude Code’s documentation is more precise than my notes (fixed limits on reopening listen streams; schema checks on input schemas only), this article follows it.
- Where the specification is more precise than my notes (2020-12 is the default dialect, not the only one), this article follows the specification.
- Where Microsoft’s documentation qualifies scale-to-zero (at least one replica for interactive clients), this article follows it.
- Where my notes and my review of testing tools differ (the Inspector’s conformance runner is planned, not shipped), this article follows the review, which recorded the tools on 1 October 2026.
