Skip to main content

MCP in Production: What Nobody Tells You About the Model Context Protocol

· 10 min read
Tian Pan
Software Engineer

The "USB-C for AI" analogy is catchy. It's also wrong in the ways that matter most when you're the one responsible for keeping it running in production. The Model Context Protocol solves a real problem—the explosion of custom N×M integrations between AI models and external systems—but the gap between "it works in the demo" and "it handles Monday morning traffic without leaking data or melting your latency budget" is wider than most teams expect.

MCP saw an 8,000% growth in server downloads in the five months after its November 2024 launch, with 97 million monthly SDK downloads by April 2025. That adoption speed is both a sign of genuine utility and a warning: most of those servers went into production without the teams fully understanding what they were building on.

What MCP Actually Is (and Isn't)

At its core, MCP is a standardized JSON-RPC 2.0 protocol that lets AI applications—called hosts—connect to capability providers called servers through lightweight intermediaries called clients. Each client manages a 1:1 connection to one server. The host might run a dozen clients simultaneously, one pointing at a filesystem server, another at a database, another at a remote SaaS API.

The three primitives servers expose are:

  • Tools: Executable functions the AI can invoke (file reads, API calls, database queries). These are the workhorses—most integrations live here.
  • Resources: Static context data the AI reads (schemas, documentation, file contents). Designed for loading context into the LLM's window, not for real-time streaming.
  • Prompts: Predefined instruction templates for common tasks. Underused by most teams, but valuable for standardizing how the model approaches recurring problems.

Servers can also expose a fourth primitive on the client side: sampling, where a server requests that the host's LLM complete a reasoning step. This matters architecturally because it means your MCP server can offload AI reasoning without having its own model API keys—a useful pattern for tools that need dynamic judgment without tight coupling to a specific model vendor.

Transport-wise, there are two options. Stdio (standard input/output) is for local processes running on the same machine—zero network overhead, ideal for development and local tool use. Streamable HTTP is for remote servers in production, and as of March 2025, it requires OAuth 2.1 authentication. This change was necessary; the authentication vacuum that existed before was a genuine security hazard.

The Architectural Failure Modes

Most MCP implementation problems aren't bugs—they're category errors. Teams slot MCP into roles it wasn't designed for, and the result is an architecture that technically "works" but fails on latency, security, or operational complexity.

The universal router trap. MCP introduces 300–800ms of overhead per call due to the protocol handshake, serialization, and transport round-trip. If you route every API call through an MCP layer—treating it like an API gateway—you'll pay that cost everywhere. Customer-facing features that relied on sub-100ms responses will suddenly feel sluggish. MCP belongs in the orchestration layer, not in the request-response path of your production API.

The kitchen sink server. Monolithic MCP servers that expose 40 tools across five unrelated domains are a maintenance and security nightmare. When the server needs to be redeployed, everything goes down. When one tool has a permission issue, you have to audit all 40. The right model is microservice-style: one server per domain (filesystem access, CRM data, billing, etc.), each scoped to exactly the tools that belong together. This lets you scale, restart, and lock down each surface area independently.

Real-time context delusion. Resources are designed for context loading, not data streaming. If you're using MCP resources to feed a live dashboard or track rapidly-changing state, you're fighting the protocol. There's no built-in invalidation mechanism—cached resources go stale and agents will happily work with outdated data unless you build explicit cache-busting logic. MCP is an orchestration layer; real-time event streaming still belongs to WebSockets, SSE, or a message queue.

Security: The Problem Nobody Wants to Talk About

MCP's security track record in its first year of production deployment was rough. 492 publicly exposed MCP servers were identified as vulnerable to basic abuse. CVE-2025-6514, a command injection vulnerability in a popular npm package, affected 437,000+ downloads through a single library. A GitHub MCP vulnerability in May 2025 allowed injected text in issues to trigger data exfiltration from private repositories.

The fundamental attack surfaces are:

Prompt injection through tool results. When an MCP tool fetches content from an untrusted source—a web page, a GitHub issue, a customer support ticket—that content can contain instructions directed at the AI. The model has no reliable way to distinguish "this is data" from "this is a command." An attacker who controls content that gets fetched by an MCP tool can potentially hijack the agent's behavior.

Path traversal in filesystem servers. Several filesystem MCP implementations used naive prefix string checks to scope access (e.g., only allow reads under /data/project/). These checks were trivially bypassed with ../ sequences. The fix requires normalized, canonical path comparison—not string prefix matching.

Supply chain compromise. The MCP ecosystem is young, and many servers are third-party packages with minimal security review. A compromised MCP library sitting in your dependency tree has access to whatever your MCP client is authorized to do.

The practical mitigations are:

  • Least privilege at tool level. Don't authorize a tool for write access if it only needs read access. Don't give a tool filesystem access if it only needs database queries. Authorization should be scoped to the minimum required for each tool, not granted server-wide.
  • Input validation before execution. Treat all tool inputs as untrusted. Validate against the JSON schema, but also validate the semantic content—a filename parameter that resolves outside the expected directory should be rejected before any filesystem call.
  • Sandbox your servers. Run MCP servers in containers with minimal system capabilities. Network-isolated local servers can't exfiltrate data over the network. Filesystem servers should run in chrooted environments.
  • Treat tool results as untrusted data. Before returning content from external sources to the LLM, consider whether that content could contain adversarial instructions. In high-stakes contexts, a validation step before passing results back is worth the latency cost.

Designing Tools That Actually Work

The most common tool design mistake is mapping tools 1:1 with underlying API operations. If your CRM has createContact, updateContact, deleteContact, addContactNote, and setContactStatus, you might be tempted to expose all five as separate tools. Don't.

LLMs are good at describing intent, not at orchestrating low-level API operations. Tools should map to user-level workflows: manage_contact that handles create/update/delete with a required action parameter, add_contact_note for logging interactions. This reduces the decision space the LLM has to navigate and makes your tool surface easier to reason about and audit.

A few specific design rules that matter in practice:

Make tools idempotent. Agents retry. Network requests fail and get retried. If executing the same tool call twice has different effects (creates two records instead of one), you'll have a bad time. Accept client-generated request IDs and use them to deduplicate.

Paginate list operations. A tool that returns "all documents" works fine in testing when there are 50 documents. It fails in production when there are 50,000. Cursor-based pagination with explicit size limits is not optional.

Don't chain tools in your server. If a tool internally calls other tools, you've hidden dependencies and made the server harder to test and debug. Let the LLM compose tools through the orchestration layer. Each tool should do exactly one thing and return exactly what it found.

Return structured errors, not generic failures. "An error occurred" tells the LLM nothing useful. "Resource not found: contact ID 8823 does not exist in the CRM" lets the model make a recovery decision. Error messages are part of your API contract.

Transport and Performance in Production

For any production deployment where your MCP server is accessed remotely, use Streamable HTTP—not stdio. Stdio only works for local processes, and "deploy the MCP server on the same machine as everything else" doesn't scale beyond simple single-host setups.

Streamable HTTP with Server-Sent Events handles streaming results correctly, integrates with load balancers and proxies, and supports OAuth 2.1 for authentication. The tradeoff is that the first call to a cold MCP server has a warmup cost of ~2.5 seconds due to connection establishment and capability negotiation. Subsequent calls hit the cached channel at sub-millisecond overhead.

For latency-sensitive paths, keep MCP servers warm with synthetic health-check calls. For high-volume tools, batch independent operations—a single batched request for 10-25 operations cuts round-trip overhead significantly. For anything that produces large responses, stream results incrementally rather than waiting for complete computation.

Geographic distribution matters. US-hosted MCP servers see 100–300ms lower latencies than equivalent European or Asian deployments for US-originating agent traffic. If your agents are globally distributed, your MCP servers should be too.

On the observability side, track three categories of metrics:

  • System health: Memory, CPU, uptime, restart frequency
  • Protocol metrics: Request rate, per-tool latency (p50/p95/p99), error rate by tool and error type
  • Business metrics: Which tools are actually being used, data freshness age for resources

Error rate spikes on a specific tool are usually the first signal of a bad deployment or an external API change. Without per-tool metrics, you're flying blind.

When to Use MCP vs. Alternatives

MCP is not a replacement for function calling or OpenAPI tooling—it's a complement with a specific use case.

Use direct function calling when you're building tightly coupled functionality inside a single application, latency is critical (sub-100ms), and the integration will never be reused elsewhere. Function calling has near-zero overhead and maximum flexibility within a single host.

Use MCP when you want a tool to be reusable across multiple AI hosts (Claude Desktop, your IDE, your custom agent), when dynamic capability discovery matters (the LLM discovers what tools are available at runtime, not at compile time), or when you're building a capability that should work across multiple models without rewriting the integration layer.

Use OpenAPI tooling when you have an existing, mature API with rich documentation, or when the integration consumers are primarily humans or traditional software systems rather than AI agents.

The decision isn't binary. A common production pattern is using MCP for cross-cutting capabilities (filesystem access, company knowledge base, standard internal tools) while using direct function calling for application-specific logic that doesn't need to be shared.

The State of Production Readiness

The November 2025 MCP specification update addressed the most pressing enterprise blockers: mandatory OAuth 2.1 for HTTP transports, improved error signaling, and clearer lifecycle management. The protocol is no longer the barrier to production deployment that it was in early 2025.

The barriers that remain are organizational. Teams that adopted early without investing in security reviews are now retrofitting authentication and input validation onto servers that shipped without them. Teams that built kitchen-sink monoliths are discovering that operational complexity grows with the number of tools in a single process.

28% of Fortune 500 companies had implemented MCP servers as of Q1 2025. The other 72% are watching, and what they're watching for is evidence that production deployments at scale are survivable without heroic operational effort. That evidence is accumulating, but slowly.

If you're building with MCP today, the protocol is capable enough—the discipline required is in the design. Single-purpose servers, least-privilege authorization, validated inputs, and treating tool results as untrusted data will get you further than any framework choice. The teams that are running MCP reliably in production aren't doing anything exotic. They're just applying the same operational hygiene to MCP that good engineers apply to any distributed system.

References:Let's stay in touch and Follow me for more thoughts and updates