Anthropic released the Model Context Protocol (MCP) in November 2024 as an open protocol to connect models with external tools: databases, repositories, and ticketing systems. Today, that same protocol runs in Claude Desktop, Cursor, VS Code, and OpenAI’s Agents SDK. Adoption has moved faster than operational maturity: some teams have already granted their agents write access to real systems without per-tool permissions, usage limits, or any logging. Deploying MCP in production with guarantees boils down to three layers: permissions, quotas, and logs.
What MCP Solves and What It Leaves Pending
Technically, MCP is a protocol based on JSON-RPC 2.0 where a server exposes tools, resources, and prompts, and the client discovers and invokes them at runtime. The official transports are stdio for processes on the same machine and Streamable HTTP for remote servers; the 2025-03-26 revision removed the previous SSE-based transport.
The specification evolves through dated revisions (2024-11-05, 2025-03-26, and 2025-06-18), and none of the three define quotas, sandboxing, or per-tool policies. That remains the responsibility of the system operator. The healthiest approach is to treat MCP as a transport layer and each tool as a production object with an owner and a budget.
Next step: inventory the MCP servers that touch your production environments and note for each one which external system it accesses and with what credential.
Permissions: OAuth 2.1 and Least Privilege
The current revision, 2025-06-18, defines MCP servers as OAuth 2.1 resource servers: short-lived tokens, with a scoped audience and validated against the intended audience. Apply the same least-privilege principle to each server that you would expect from a microservice. If a tool wraps a database, its credential should be a read-only role for the necessary tables, not the administrator account.
Next comes per-tool control. Clients are already paving the way for this: Claude Code asks for confirmation before executing actions and allows you to decide if authorization is permanent, one-time, or disabled. Based on this, maintain an allowlist per agent and enforce explicit human confirmation for destructive tools: deployments, deletions, and sends.
The most treacherous risk is not in your code, but in third-party descriptions. Invariant Labs documented "tool poisoning" attacks in 2025, featuring malicious instructions hidden in a tool’s description, as well as cases where that description changes after the user has approved it. Their open-source utility mcp-scan inspects servers and flags suspicious descriptions.
Next step: fix the version or hash of each allowed server, rescan with every update, and treat the diff of tool descriptions as an auditable event.
Quotas: Limits per Call, User, and Token
No part of the specification discusses rate limiting, maximum concurrency, or accumulated spend, so the common practice is to place a gateway between the agent and the servers and measure there. Tag every call with the tool, user, and outcome, and define three caps: calls per minute per user, concurrent calls per server, and a monthly budget for high-cost tools. When a cap is hit, the gateway responds with an error, and the agent must handle it like any other tool failure.
At the model layer, LiteLLM supports budgets and limits for requests per minute (RPM) and tokens per minute (TPM) per key, returning a 429 error when the quota is exhausted. At the MCP layer, Docker released its MCP Gateway in 2025, which exposes servers as managed endpoints: a natural point to inject policies. Add caching where the architecture permits: many tool calls repeat the same query and do not need to be re-executed.
Next step: define an SLO per tool, with a target p95 latency and an error budget, and configure an alert for when a tool consumes its monthly quota before the expected date.
MCP Logs in Production: What to Record and What to Redact
Useful logging for auditing agents resides in the tool call, not in the model’s response. For every invocation, save the tool name, the identity of the agent and the end-user, the redacted arguments, the size and status of the result, the latency, and a trace ID that links the call to its conversation turn. Without that identifier, correlating an incident with the conversation that caused it becomes digital archaeology.
MCP provides native logging, but it is not auditing: the server can emit messages to the client via notifications/message, and the client controls the level via logging/setLevel. This is useful for debugging the server process, not for reconstructing what the agent did. For the latter, wrap every call in an OpenTelemetry span; GenAI semantic conventions, though still experimental, already standardize attribute names.
There is a consequence many discover too late: these logs contain prompts, arguments, and results, and in real systems, that almost always includes personal data. Treat the log store as a first-class system with restricted access, minimization, and short retention—on the order of weeks rather than years. If this is a concern, we have already covered what should be kept within your own infrastructure under GDPR: Privacy and GDPR with local AI: which data never leaves your server.
Next step: redact arguments before persisting them and decide from day one the retention and access control for the log store.
Where to Start
A mediocre model with good telemetry is operable; a brilliant model without tool logs is a black box. Start small: pick the most dangerous MCP tool in your system and, this week, place it behind a permission control, give it a call cap, and log every invocation with redacted arguments. Once that pattern works for a single tool, replicating it for the rest is mechanical work, and your first production incident will thank you for it.
