Sixteen months after Anthropic released the Model Context Protocol, the numbers look like a finished revolution: 97 million monthly SDK downloads by March 2026 — a 970x increase in 18 months — and more than 10,000 public MCP servers indexed across registries by May 2026, with every major AI provider shipping native support and the protocol itself donated to the Linux Foundation's Agentic AI Foundation in December 2025. Connecting an agent to tools is a solved problem. Letting an agent operate production is not.
The gap matters the moment the tool on the other end can ship code. A demo where Claude restarts a staging deployment through an MCP server takes an afternoon to build and looks like the future. The production version of that demo — an agent that can deploy and roll back real apps on a Kubernetes fleet while you sleep — needs three things the protocol barely standardizes today: who the caller is (identity propagation), how long a tool may take (timeout budgets), and what failure looks like to a model (structured errors). Miss any one of them and the demo stays a demo.
So here is the concrete artifact up front — the deploy-and-rollback MCP surface this post designs, one row per tool, with the identity scope, latency budget, error contract, and approval tier each tool must carry before an agent touches a fleet:
| Tool | Identity scope | Timeout budget | Error contract | Approval |
|---|---|---|---|---|
get_status | read-only ServiceAccount, target namespace | 10s, synchronous | retryable UPSTREAM_TIMEOUT vs fatal NOT_FOUND | none |
get_logs | read-only ServiceAccount, target namespace | 15s, bounded byte cap | TRUNCATED with resume cursor | none |
deploy | deployer ServiceAccount, target app only | async: 5s ack + operation handle, poll to 10min | ROLLOUT_STALLED with progress snapshot | human approve via elicitation |
rollback | deployer ServiceAccount, target app only | async: 5s ack + operation handle, poll to 10min | NO_PREVIOUS_REVISION (fatal) vs ROLLOUT_STALLED | human approve via elicitation |
scale | deployer ServiceAccount, target app only | 30s, synchronous | QUOTA_EXCEEDED (fatal, with current usage) | auto within bounds, approve above |
The rest of this post earns each cell: why identity, timeouts, and errors are gaps rather than details, what the ecosystem data says about each, and what fills them.
Gap 1: the server knows the agent is "someone" but not who
The starkest number in the MCP security literature is not about prompt injection. It is about authentication adoption: roughly 8.5% of surveyed servers use OAuth, while 53% rely on static API keys, per 2025 ecosystem survey data widely cited in 2026 security analyses. Ten thousand servers, and nine in ten of the surveyed ones identify their caller with a shared secret or nothing at all. For a read-only docs fetcher that is a smell. For a server whose tools mutate production, it is a disqualifier.
The protocol's own specification names the failure mode explicitly: the confused deputy problem, where an MCP server calling downstream services performs privileged actions without understanding whose authority it is exercising. The July 2026 specification update (the 2026-07-28 release) folded six authorization enhancements into the spec — OAuth 2.1 profile alignment, RFC 9207 issuer validation, client credentials bound to the issuer that minted them — precisely because clients were connecting to OpenID Connect-flavored authorization servers in ways the original profile did not pin down. That is real progress, and it is also plumbing.
It standardizes how the server proves who it is to the authorization server, not which human principal stands behind any given tool call.
The deeper hole is per-tool authorization. As one 2026 agent-security survey puts it, the specification provides no standard for per-tool authorization: a server declares tools, and every connected agent can call every tool regardless of role, trust level, or the human who authorized the session. The first production implementation of tool-level ACLs cited in that literature — Kong's MCP gateway ACLs, generally available January 2026 — lives outside the protocol, in the gateway layer. Until the protocol catches up, the enforcement boundary has to be built by the operator, and for a Kubernetes fleet the operator already owns the right enforcement layer: RBAC.
The design that falls out is deliberately boring. One dedicated ServiceAccount per agent identity — never the cluster-admin kubeconfig the demo used — with namespace-scoped Roles granting the minimum verbs each tool needs: get and list on pods and deployments for the read tools, patch on the specific deployment for the mutating ones. The MCP server maps each tool to exactly one ServiceAccount and passes no ambient authority. If the deploy tool's token can only patch one deployment in one namespace, a confused or compromised agent cannot promote itself to cluster admin — there is no cluster-admin credential anywhere in the tool path. Community Kubernetes MCP servers already converge on this shape — read-only by default, a separate scoped entry for anything that writes — because operators who have been paged at 3am converge on least privilege faster than specification committees do.
Identity propagation, stated as a rule: the tool call must carry a caller identity narrow enough that the worst authorized call is still a survivable incident. A deploy key scoped to one app is a survivable incident. A shared admin kubeconfig is a resume event.
Gap 2: deploys take minutes, agent turns take seconds
The second gap is temporal. An agent reasons in turns measured in seconds; a rollout takes minutes; a human approval takes longer still. The protocol's request lifecycle assumes a tool call returns, but a deploy that blocks the connection for nine minutes while pods roll is a design that fails at the first slow image pull — and a deploy that returns instantly with "started!" and no handle is a design that fails the moment the agent needs to know whether it worked.
The ecosystem's answer to the slow-human part of this problem is elicitation: the server pauses a tool call to request structured input — an approval decision — from the user through the client, instead of executing blindly. Production elicitation implementations share two properties worth stealing. First, approvals are fail-closed: a denied, modified, or timed-out approval refuses the operation, and several implementations explicitly deny on timeout by default.
Second, approval timeouts are generous but finite — one design documents a five-minute default on the explicit theory that a human reading a credential prompt is slower than a checkpoint click. Both properties encode the same judgment: an unanswered "may I ship this?" is a "no."
But elicitation has a sharp edge the demos skip: not every MCP client implements it. The capability is negotiated at initialization, and servers written defensively must refuse the mutating call outright when the client never declared elicitation support — otherwise "requires approval" silently degrades to "executes without approval" on exactly the clients where nobody is watching. The tool table above assumes this: deploy and rollback require an elicitation-capable client, and the server's contract states the refusal reason in machine-readable form rather than hanging or, worse, proceeding.
For the slow-rollout part, the fix is the oldest pattern in distributed systems: make mutating tools asynchronous with handles. The deploy call acknowledges within five seconds with an operation ID, and a separate cheap get_status-style poll (or a bounded follow-up call) reports progress until a ten-minute wall-clock deadline converts a stalled rollout into a ROLLOUT_STALLED error carrying the last-known progress snapshot. Cancellation must be a first-class tool, not an afterthought: while an approval is pending, the originating request stays cancellable, and an overall deadline keeps one hung rollout from wedging the agent's whole session.
Reads stay synchronous with single-digit-second budgets because they can be; mutating calls get the async treatment because physics demands it.
Timeout budgets, stated as a rule: every tool declares its worst case in the contract, and the worst case always resolves to a value the agent can act on — never a hung connection, never a silent default-allow.
Gap 3: free-text errors make the model guess, and guessing ships bugs
The third gap is the least dramatic and the most expensive in practice. When a tool fails with a paragraph of prose — Failed to update deployment: ... followed by three stack frames' worth of context — the agent must reason about what happened before it can act. Every re-reasoning step burns tokens on failure analysis instead of recovery, and worse, it invites the model to improvise: retry the unretryable, escalate the routine, or reinterpret a fatal error as a transient one. A deploy agent that guesses wrong about QUOTA_EXCEEDED retries a rollout that can never succeed; one that guesses wrong about a timeout fires a second deploy on top of the first.
The fix is a machine-readable error contract: every failure returns a stable code, a retryable-or-fatal classification, and the exact next action. The shape already exists in the wild — production approval-gate implementations define codes like APPROVAL_PENDING, APPROVAL_TIMEOUT, and APPROVAL_DENIED precisely so the agent never has to parse a sentence to learn it is still waiting, timed out, or refused. Generalize that pattern across the surface:
UPSTREAM_TIMEOUT(retryable, back off and re-poll) versusNOT_FOUND(fatal, stop and report) on reads.TRUNCATEDwith a resume cursor on logs, so a bounded response is information rather than data loss.APPROVAL_PENDING(keep waiting),APPROVAL_TIMEOUT(treat as denied, tell the human),APPROVAL_DENIED(stop, summarize what was refused) on the mutating tools.NO_PREVIOUS_REVISION(fatal — there is nothing to roll back to, say so immediately) versusROLLOUT_STALLED(retryable-with-judgment — attach the progress snapshot so the agent can decide between waiting, escalating, or rolling back) on rollback.QUOTA_EXCEEDED(fatal, with current usage attached so the agent's report tells the human exactly which quota and how much headroom is missing) on scale.
Note what this buys beyond correctness: it converts the dominant cost of failure handling — model reasoning over prose — into a table lookup. The agent's policy becomes "match the code, take the prescribed action," which is auditable, testable, and cheap. You can write a conformance test for an error contract. You cannot write one for "the model usually understands the error message."
Structured errors, stated as a rule: if two different failures require two different agent behaviors, they must be two different codes — never two wordings of the same string.
The assembled surface: what "ready for the fleet" actually checks
Put the three gaps together and the demo-to-production checklist writes itself. Each row replays the table from the top of this post, now with the reasoning attached:
- Read-first, write-rare. The agent gets
get_statusandget_logswith no approval because they are scoped to read-only credentials and bounded in time and bytes. Everything the agent needs to decide is free; everything that changes state costs an approval. This mirrors how the better community Kubernetes servers already ship: read-only by default, writes behind a separately scoped entry. - Writes are async, approved, and cancellable.
deploy,rollback, and out-of-boundsscaleacknowledge fast, report progress by handle, die loudly on deadline, and require a human decision delivered through elicitation — fail-closed, with an explicit refusal when the client cannot do approvals at all. - Every failure is a code with a prescribed action. No prose parsing, no improvised retries, no second deploy stacked on a timed-out first one.
- Audit evidence per call. Who (the propagated caller identity), what (tool name plus arguments hash), when (timestamps for call, approval, and completion), and result (success or error code). Kubernetes API-server audit logs already record the downstream half; the MCP server must record the upstream half — which agent, acting for which human, approved by whom — or the incident review will have a hole exactly where the interesting decision happened.
- RBAC is the enforcement; the MCP server is the interface. Say this plainly because demos invert it: if the deploy tool's ServiceAccount can only patch one deployment, the MCP layer can have bugs without becoming a privilege-escalation path. The server enforces the contract; Kubernetes enforces the blast radius. Defense in depth is not a slogan here — it is the reason a tool-poisoning attack (malicious instructions hidden in tool metadata, the OWASP MCP top-ten entry that Microsoft's security teams have flagged in production guidance) degrades to "the agent tried something odd within its namespace" instead of "the agent owned the fleet."
One honest boundary: the 2026-07-28 specification hardening — issuer validation, bound credentials, the OAuth 2.1 profile cleanup — is direction, not arrival. It closes real credential-reuse holes between authorization servers, but per-tool authorization still has no protocol standard, elicitation support is still client-dependent, and nine-in-ten surveyed servers still identify callers with static keys. Build the surface above against today's ecosystem and the spec's trajectory makes it stronger; wait for the spec to mandate it and the agents operate your fleet through someone else's demo scaffolding.
The protocol won connection; operation is still yours to build
A year and a half took MCP from 100,000 SDK downloads in its first month to 97 million a month, from a handful of reference servers to ten thousand public ones and a Linux Foundation home. The connection problem — how does an agent call a tool — is standardized, governed, and growing at a pace no proprietary integration ever matched. That victory is real, and it is also what makes the three gaps urgent: the easier it is to connect an agent to anything, the more production systems sit one auto-connected server away from an agent with no identity, no timeout, and no idea what just failed.
The operators who close those gaps first get the actual prize — deploy-from-chat against a fleet they own, with approvals, audit trails, and error contracts holding the line while humans sleep. Identity narrow enough to survive misuse, budgets that always resolve to action, errors the model never has to interpret: build those three into the surface before the agent gets the kubeconfig, and the ten-thousand-server ecosystem becomes leverage instead of attack surface.
Bex.co is the open-source, AI-native Render alternative — push a git repo, get a running HTTPS service on machines you own. Star the repo on GitHub or deploy your first app today.



