Building an MCP Server in Production: Architecture Patterns
BlogBuilding an MCP Server in Production: Architecture Patterns

Building an MCP Server in Production: Architecture Patterns

Inventiple Team5 min read

Building an MCP Server in Production: Architecture Patterns

The MCP quickstart gets you a working server in ten minutes. It does not prepare you for the decisions that matter once that server is handling real traffic, real credentials, and real failure modes. This is the architecture layer the docs skip.

1. Pick your transport deliberately, not by default

MCP supports stdio and HTTP-based transports (Streamable HTTP, formerly SSE). Most tutorials default to stdio because it's simplest for local dev — but stdio ties your server's lifecycle to a single parent process and gives you no path to horizontal scaling or multi-client access.

For production:

  • stdio — fine for single-user local tools (IDE integrations, CLI agents). Don't ship it as your only option if multiple clients or remote access are on the roadmap.
  • Streamable HTTP — the right default for anything served to more than one client or deployed remotely. Gives you standard load balancing, auth headers, and horizontal scaling.

Build the transport layer as a swappable adapter from day one. Retrofitting stdio-only servers to support HTTP later means rewriting your session and state assumptions, not just adding an endpoint.

2. Tools, Resources, and Prompts are different contracts — treat them differently

A common mistake is exposing everything as a "tool" because tools are the most familiar primitive. MCP gives you three:

  • Tools — actions with side effects (write to a database, call an external API, execute code). Model these as they'll be called mid-conversation, so keep parameters minimal and validate aggressively — the model will occasionally hallucinate parameter values.
  • Resources — read-only data the client can fetch (files, records, query results). No side effects, cacheable, and should be idempotent by design.
  • Prompts — reusable prompt templates the client can invoke. Underused in most implementations, but valuable for standardizing common workflows across a team.

Getting this split right up front avoids a server where every capability looks like a tool call, which makes the model's job harder (more ambiguous action-vs-lookup decisions) and your audit log noisier.

3. State management: stateless-first, session-scoped when you must

Default to stateless tool calls — each call carries everything it needs, and your server holds nothing between calls. This is what makes horizontal scaling trivial: any instance can handle any request.

When you genuinely need state (a multi-step workflow, a long-running job), scope it explicitly to a session ID passed by the client, and back it with an external store (Redis, a database) — never in-process memory. In-process session state is the single most common reason MCP servers can't scale past one instance.

// Anti-pattern: in-memory session state
const sessions = new Map(); // dies on restart, doesn't scale horizontally

// Better: externalized, session-scoped state
async function getSession(sessionId: string) {
return await redis.get(`mcp:session:${sessionId}`);
}

4. Auth is your responsibility, not the protocol's

MCP doesn't prescribe an auth mechanism — that's deliberate, but it means production servers need to own this explicitly:

  • Client-to-server auth: OAuth 2.1 with PKCE is the emerging standard for remote MCP servers as of 2026. Don't roll your own token scheme if you're exposing the server beyond a trusted internal network.
  • Server-to-downstream auth: your MCP server likely calls other APIs on the user's behalf. Store those credentials scoped per-user, never as a shared service credential across all sessions — a single compromised MCP server session shouldn't expose every user's downstream access.
  • Tool-level authorization: not every authenticated user should be able to call every tool. Build a permission check per tool call, not just at the connection level.

5. Observability: log every tool call, not just errors

Because the model decides which tools to call and with what arguments, you need visibility into that decision trail, not just your own code's execution path:

  • Log every tool invocation with its full argument payload (redacted for secrets) and the result summary.
  • Track latency per tool separately — a slow downstream API call inside one tool will look like a slow model response to the end user, and you need the breakdown to know which layer is actually slow.
  • Alert on repeated tool-call failures from the same client, since it often means the model is stuck retrying with malformed arguments rather than a genuine transient error.

6. Error handling: return errors the model can act on

A raw stack trace or an HTTP 500 gives the model nothing to work with. Return structured errors that describe what went wrong and, where possible, what a valid retry would look like:

// Weak
throw new Error("failed");

// Better
return {
isError: true,
content: [{
type: "text",
text: "Invalid date format. Expected ISO 8601 (YYYY-MM-DD), received: 'next tuesday'."
}]
};

This single change — writing error messages for the model, not for a human reading logs — measurably reduces the number of failed tool-call retries in production.

7. Deployment: containerize, and separate the MCP layer from business logic

Package the MCP server as its own container, calling into your existing services/APIs rather than reimplementing business logic inside the MCP layer. This keeps the MCP server thin, replaceable, and testable independent of protocol version changes — which matter, since MCP itself is still evolving quickly.

None of this is exotic. It's the same production discipline you'd apply to any API layer — statelessness, explicit auth boundaries, structured errors, real observability. The MCP-specific part is knowing where those decisions map onto the protocol's primitives, which is what the quickstart doesn't cover.

Inventiple builds production MCP servers and agentic AI systems for funded startups. See our MCP & Agentic AI Development work.

Share

Ready to Start Your Project?

Let's discuss how we can bring your vision to life with AI-powered solutions.

Let's Talk