Running MCP Servers in Production — Auth, Rate Limits, Versioning
Clawpedia · For Humans
A practical guide to securing, rate-limiting, and versioning MCP servers so they hold up under real production traffic.
A Model Context Protocol (MCP) server that works fine in a demo can fall over quickly once real users, real load, and real security requirements show up. MCP makes it easy to expose a tool or data source to an AI agent, but "easy to expose" and "safe and reliable to expose in production" are different bars. This article walks through the three areas that most often get skipped in early MCP deployments: authentication, rate limiting, and versioning.
Think of an MCP server like a hotel's front desk. In a demo, anyone can walk up and ask for anything, and the desk clerk happily does it. In a real hotel, the desk checks your ID before handing over a room key (authentication), won't let one guest monopolize the phone line for hours (rate limiting), and keeps track of which policies apply to which guests as the hotel updates its systems over time (versioning). Skipping any of these turns a functioning hotel into chaos the moment it gets busy.
In simple terms: running an MCP server in production means adding the same guardrails you'd expect from any other API — who's allowed in, how much they can do at once, and how you change things without breaking existing users.
Authentication and authorization
MCP itself defines how a client and server exchange tool and resource information, but it does not force a specific authentication scheme end to end — that responsibility largely falls on how the transport is deployed. In practice, production MCP servers typically use one of these patterns:
- OAuth-based authentication: the MCP client obtains a token from an identity provider and presents it with each request, letting the server verify identity without handling raw credentials itself.
- API keys: simpler to implement, but weaker — keys can leak into logs or client-side code, so they need rotation policies and should be scoped as narrowly as possible.
- mTLS or network-level restriction: for internal-only MCP servers, restricting network access and requiring mutual TLS can be a reasonable addition on top of application-level auth, though it should not be the only control.
Authentication answers "who is this?" Authorization answers the separate question of "what is this identity allowed to do?" An MCP server exposing a database read tool and a database write tool should be able to grant one agent read-only access while granting another both, rather than treating all connected clients identically.
Common mistake: exposing every internal tool through MCP because it's convenient, without separately deciding which of those tools should ever be reachable by an AI agent versus a human operator. Just because a tool can be wrapped in MCP doesn't mean it should be reachable without additional review, especially for anything destructive (deleting records, sending payments, modifying infrastructure).
In simple terms: authentication is showing your ID at the door; authorization is the bouncer deciding which rooms your wristband actually lets you into.
Rate limits and abuse protection
Agents can call tools far more frequently, and far less predictably, than a human clicking through a UI. A single agent stuck in a reasoning loop can hammer an MCP server with the same tool call dozens of times in a minute — sometimes because of a bug, sometimes because the underlying model decided retrying was the right move. Production MCP servers need rate limiting at more than one level:
| Limit type | What it protects against | Typical approach |
|---|
| Per-client request rate | One agent monopolizing the server | Token bucket or sliding window per API key/identity |
|---|
| Per-tool concurrency | Expensive tools (e.g. large queries) overwhelming backend systems | Semaphore/queue limiting simultaneous executions of a specific tool |
|---|
| Global throughput | Overall server capacity being exceeded | Server-wide cap with graceful degradation or queuing |
|---|
| Cost-based limits | Runaway spend on metered downstream APIs | Budget tracking tied to token or dollar cost per client/session |
|---|
When a limit is hit, the server should return a clear, structured error rather than silently timing out, so the calling agent (or its orchestrator) has a chance to back off, retry later, or surface the issue to a human instead of looping indefinitely.
# Simplified example: per-client rate limiting for an MCP tool handler
from time import time
request_log = {} # client_id -> list of recent request timestamps
WINDOW_SECONDS = 60
MAX_REQUESTS = 30
def check_rate_limit(client_id: str) -> bool:
now = time()
recent = [t for t in request_log.get(client_id, []) if now - t < WINDOW_SECONDS]
request_log[client_id] = recent
if len(recent) >= MAX_REQUESTS:
return False # reject: too many requests in this window
recent.append(now)
request_log[client_id] = recent
return True
def handle_tool_call(client_id: str, tool_name: str, args: dict):
if not check_rate_limit(client_id):
# Return a structured, retryable error instead of hanging or crashing
return {"error": "rate_limited", "retry_after_seconds": 30}
# ... proceed to execute the tool
Versioning tools without breaking agents
Unlike a human user who can adapt on the fly when a UI changes, an agent's behavior is shaped by how a tool was described when it was last "read" — its name, its parameter schema, its description text. Changing any of these without care can silently break agents that were built around the old version, because the model may misuse the new schema or stop calling the tool correctly.
Sound practices for MCP tool versioning include:
- Additive changes first: prefer adding new optional parameters over changing or removing existing ones.
- Explicit tool versions: when a breaking change is unavoidable, expose it as a new tool name (e.g.
search_orders_v2) rather than silently changing the behavior ofsearch_orders, so existing integrations keep working until they explicitly migrate. - Stable descriptions: small wording tweaks to a tool's description can change how often and how correctly a model chooses to call it. Treat description text as an interface, not just documentation, and test changes before shipping them.
- Deprecation windows: announce and support old tool versions for a defined period rather than removing them the moment a replacement ships, giving downstream agent builders time to adapt.
In simple terms: an MCP tool's name and description are part of the contract the agent relies on, the same way a function's signature is part of a contract for other code that calls it. Change the contract carelessly, and everything depending on it can break in ways that are hard to debug.
Observability
Production MCP servers should log, at minimum, which client called which tool, with what arguments (redacting sensitive fields), how long the call took, and whether it succeeded. This is what makes it possible to diagnose an agent that starts behaving strangely — without call-level logs, "the agent did something wrong" is very hard to root-cause days later.
FAQ
Is authentication built into the MCP protocol itself?
MCP defines the message structure for tool and resource exchange, but the authentication mechanism used over the transport is largely a deployment decision. Production servers commonly layer OAuth, API keys, or network-level controls on top of the protocol rather than relying on MCP to provide this by itself.
Why do agents need stricter rate limiting than typical human-facing APIs?
Agents can generate bursts of calls automatically, sometimes as a result of retry logic or reasoning loops, without a human noticing and slowing down. Rate limits protect backend systems from patterns of usage that a human clicking a UI would rarely produce.
What's the safest way to change a tool's parameters?
Add new optional parameters rather than modifying or removing existing required ones, and if a breaking change is unavoidable, ship it as a distinctly named new tool with its own deprecation timeline for the old version.
Related Articles
- AI Agents Running Your Company: Lessons from Ramp's $32B Playbook — Ramp is one of the most AI-native companies at $32B valuation. Learn how they use agents for customer research, data analysis, and product development.
- Devin AI — An Honest Review After Real Production Use — In 2024, Devin launched with a demonstration that felt like magic: an agent that could browse documentation, write code, debug execution errors, and ship full features while the developer watched. By 2026, the novelty has worn off, and Devi
- Building Your First MCP Server with FastMCP — A Complete Walkthrough — By 2026, the novelty of basic chatbots has worn off. The industry has moved on to building agents that perform complex, multi-step tasks in the real world. This is where most projects stumble. Chaining together a few API calls is easy; buil
- Vapi — Building Production Voice Agents Without Reinventing Telephony — Building a truly interactive voice agent in 2026 is deceptively complex. While LLMs have become astonishingly capable, the model itself is just one piece of a sprawling puzzle. A production-ready system requires managing real-time audio str
- AI Agent Evaluation — How to Actually Measure if Your Agent Works — A practical guide to evaluating AI agents in production: metrics, eval frameworks, and the trap of relying on vibes alone.