MCP in Production: Gateways, Tool Budgets, and What Changed in the 2026-07-28 Spec
- Updated for MCP spec 2026-07-28. The initialize handshake and the Mcp-Session-Id header are gone, routing moved into Mcp-Method and Mcp-Name headers, and list results are cacheable via ttlMs and cacheScope.
- Statelessness is the architectural gift: MCP servers are now ordinary horizontally-scalable HTTP services, and a gateway can route and authorise on headers without parsing the JSON-RPC body.
- Sixty tool schemas cost about 9,000 tokens on every turn. Anthropic reports that on-demand tool discovery cut context from 77,000 to 8,700 tokens while raising tool-selection accuracy from 79.5% to 88.1% on Opus 4.5.
What changed in the 2026-07-28 MCP spec, and does it break your deployment?
Yes, if you stored sessions. The 28 July 2026 revision retired the initialize and initialized exchange along with the Mcp-Session-Id header. Every request now travels on its own, carrying its protocol version, client identity and capabilities in the _meta field, and a new optional server/discover RPC exists for clients that want capabilities up front. The maintainers state the consequence plainly: any request can land on any server instance behind a plain round-robin load balancer without shared storage.
Four other changes matter architecturally. Streamable HTTP requests must now carry Mcp-Method and Mcp-Name headers, so a gateway, rate limiter or WAF can route and meter without parsing a JSON body. Responses from tools/list, prompts/list, resources/list and resources/read now carry ttlMs and cacheScope, which makes the tool catalogue a cache key rather than a per-run fetch. Multi Round-Trip Requests replace server-initiated elicitation, sampling and roots: the server returns a result type of input_required with the questions it needs answered, and the client retries the original call with the answers attached. And authorization hardened: authorization servers should return the iss parameter per RFC 9207, and clients must validate it before redeeming a code.
The deprecation list is long enough to plan around. Dynamic Client Registration is formally deprecated in favour of Client ID Metadata Documents, though it still works. Roots, Sampling and Logging are deprecated and will keep working for at least twelve months. The legacy HTTP+SSE transport is deprecated with a year-long offramp. Tasks moved out of the experimental core into the io.modelcontextprotocol/tasks extension with a poll-based tasks/get and a new tasks/update. There is now a formal deprecation policy with a twelve-month minimum window, the most useful governance change in the release.
The migration itself is small if your servers were already close to stateless, painful if they were not. Remove session storage. Emit the two routing headers from the client. Add a cache layer keyed on cacheScope. Replace server-initiated request handlers with MRTR retry handling. Validate iss. Plan the move off DCR. All four Tier 1 SDKs, TypeScript, Python, Go and C#, shipped support the same day, with the Rust SDK in beta.
| Change | Old behaviour | New behaviour | Breaking? | What you change | Architectural consequence |
|---|---|---|---|---|---|
| Stateless core | initialize / initialized handshake; Mcp-Session-Id header pins a client to an instance | Every request self-describing; protocol version, client identity and capabilities in _meta | Yes | Delete session storage and sticky routing | Servers become ordinary horizontally-scalable HTTP services behind round-robin |
| server/discover | Capabilities learned only via the handshake | Optional RPC for clients that want capabilities up front | No — additive | Call it if you need capabilities before the first tool call | Discovery is now a choice, not a cost on every connection |
| Mcp-Method / Mcp-Name headers | Routing required parsing the JSON-RPC body | Both headers mandatory on Streamable HTTP | Yes | Emit both from the client; route on them at the gateway | A policy gateway can authorise and rate-limit on headers alone — this is what makes it cheap |
| ttlMs / cacheScope on list results | Catalogue re-fetched per connection | List responses carry cache hints and deterministic ordering | No — additive | Add a cache keyed on cacheScope, honour ttlMs | The catalogue becomes a cache key, and deterministic order keeps upstream prompt caches stable |
| Multi Round-Trip Requests | Server-initiated elicitation/create, sampling/createMessage, roots/list over a held-open stream | Server returns input_required plus the requests; client retries with inputResponses | Yes, for those flows | Handle input_required and re-issue the call | No bidirectional stream needed, so mid-call user confirmation works on stateless infrastructure |
| RFC 9207 iss validation | Not required | Authorization servers should return iss; clients must validate before redeeming a code | Yes, for clients | Validate iss in the OAuth callback | Closes an authorization-server mix-up hole |
| DCR to CIMD | Dynamic Client Registration standard | Client ID Metadata Documents standard; DCR deprecated but working; application_type now set during DCR | Deprecation clock | Plan the move; set application_type so localhost redirects stop being rejected | Fewer moving parts in client onboarding; CLI OAuth flows stop failing on redirect_uri |
| Roots / Sampling / Logging | Supported | Deprecated, working for at least twelve months | Deprecation clock | Stop adopting them in new work | The protocol surface narrows, which makes gateways simpler |
| Legacy HTTP+SSE transport | Supported alongside Streamable HTTP | Deprecated with a year-long offramp | Deprecation clock | Move to Streamable HTTP | One transport to secure, meter and audit instead of two |
Should agents call MCP servers directly, or through a gateway?
Through a gateway for anything you did not write, and directly only for a first-party server inside your own trust boundary. The 2026-07-28 changes are what make this practical rather than merely advisable, because a gateway that can route and authorise on Mcp-Method and Mcp-Name headers does not have to parse and re-serialise a JSON-RPC body on the hot path.
What breaks without a gateway is a list, and every item on it is discovered late. There is no inventory, so nobody can answer which agents can reach which tools. There is no allowlist, so a server that adds a tool has silently added a capability to your agents. There is no per-agent identity, so calls arrive at downstream systems bearing a standing service credential rather than the scope of the user who caused them. There is no audit trail. There is no rate limiting per tenant and per server. And crucially, a tool schema written by someone else reaches your model completely unvetted.
The gateway's request-order matters and most of it is cheap. Authenticate the caller, resolve the agent identity, check the tool allowlist, look up the catalogue cache, validate and normalise the schema against the pinned hash, inject the scoped credential, rate limit on the tenant and server pair, forward, bound and record the response, write the audit entry. The first seven stages read headers and your own registry. Only schema validation and response bounding touch payloads.
State the cost rather than hiding it. Vendor benchmarks for pure AI-gateway routing put the overhead in microseconds, but that is not what is being measured here: a policy gateway doing credential injection, registry lookups and schema validation is realistically a few milliseconds at p50, more at p95 under a cold catalogue cache. Measure yours, publish the number internally, and put a cache in front of the registry. Then note the offset: the catalogue cache removes a tools/list round trip per run, which usually more than pays for the hop.
| Gateway stage | Reads header or body? | What it costs | Fails how? | Skippable? |
|---|---|---|---|---|
| Authenticate the caller | Header | Sub-millisecond with a cached JWKS | 401; agent cannot call any tool | No |
| Resolve agent identity | Header | Registry lookup, cached | Falls back to no permissions, not to all permissions | No |
| Check the tool allowlist | Header (Mcp-Name) | In-memory set lookup | 403 with the tool named in the audit log | No — this is the control |
| Catalogue cache lookup | Header (cacheScope) | Saves a tools/list round trip per run | Miss: one upstream fetch | Yes, at the cost of a round trip |
| Validate + pin schema hash | Body | 1-4ms for a typical schema | Block or alert, per policy; a changed hash is a rug-pull signal | No — this is the rug-pull defence |
| Inject scoped credential | Neither — registry reference | One secret-store fetch, cached | The agent never holds the credential, so a leak is contained | No |
| Rate limit per (tenant, server) | Header | Sub-millisecond | 429 with a retry hint the agent can act on | Only if you have one tenant |
| Forward | Body | Network hop to the server | Upstream 5xx surfaces as a tool business error, not an exception | No |
| Bound + record the response | Body | Truncation plus a journal write | Oversized result truncated with a pointer, never silently dropped | No |
| Audit log | Both | One append | Missing audit is a compliance finding, not an outage | No |
version: 1
defaults:
on_schema_change: block # block | alert
max_result_tokens: 2000
rate_limit: { per: [tenant, server], rps: 20, burst: 40 }
agents:
support-triage:
always_include: [request_more_tools, escalate_to_human]
allow:
- server: jira
tools:
- name: create_ticket
pinned_schema_hash: "sha256:7c1f...e3a9"
requires_human_approval: true # irreversible, externally visible
- name: search_tickets
pinned_schema_hash: "sha256:04bd...11c7"
- server: internal-kb
tools: ["*"] # first-party, inside the boundary
- server: web-search
tools:
- name: search
pinned_schema_hash: "sha256:9a20...bb41"
treat_results_as: untrusted # never as instructions
servers:
jira:
url: https://mcp.internal/jira
credential_ref: secretstore://jira/agent-scoped # reference, never a literal
identity: oauth2.1
token_audience: https://mcp.internal/jira
on_behalf_of: caller # inherit the user's scope
internal-kb:
url: http://mcp-kb.svc.cluster.local:8080
credential_ref: secretstore://kb/read-only
web-search:
url: https://mcp.internal/websearch
credential_ref: secretstore://websearch/metered
network:
deny_direct_agent_egress: true # a direct path is an audit gapHow many tools can an agent actually handle?
Fewer than you have attached, and the limit is an accuracy limit before it is a cost limit. The cost arithmetic is straightforward: sixty tools at roughly 150 tokens of schema each is about 9,000 tokens on every turn, which across a twenty-turn run is 180,000 tokens of catalogue before the agent has done anything. Description length dominates that average, and a verbose description can run to 400 tokens on its own.
The accuracy problem is separate and worse, because it does not show up on a bill. Anthropic's guidance names 15 to 20 tools as the point where a model starts spending significant attention on understanding its options, and describes agents with 20 or more struggling to select the right tool, particularly when tools span unrelated domains and similar operations exist across them. Their own measurements on the Tool Search Tool are the cleanest published numbers here: deferring tool definitions and discovering them on demand cut context consumption from 77,000 tokens to 8,700, an 85% reduction, while tool-selection accuracy on their internal evaluation moved from 49% to 74% on Claude Opus 4 and from 79.5% to 88.1% on Claude Opus 4.5.
The production pattern is tool subsetting resolved at context-assembly time: given a task class, a tenant and an agent identity, query the registry for allowlisted tools, rank by stored embedding similarity against the task description plus a usage prior, and return the top N plus an always-include set. Seven to ten tools per run rather than sixty. The critical implementation detail, and the one that only comes from having done it: resolve the subset once per run, not once per turn. A catalogue that changes between turns invalidates the cached prefix and costs more than the subsetting saved.
Give the wrong subset an escape hatch. A request_more_tools meta-tool that always sits in the always-include set turns a bad subset into one extra turn instead of a failed run. And write tool descriptions as prompt, not documentation: the description is read by the model on every turn, it is the highest-leverage forty words in the tool, and enums beat free strings because an invalid enum is a schema rejection at the gateway rather than a plausible-wrong call at the tool. The tokens this saves show up directly in the tool-catalogue arithmetic inside an agent loop.
| Tools attached | Schema tokens/turn | Tokens across a 20-turn run | Uncached cost | Cached cost | Recommendation |
|---|---|---|---|---|---|
| 5 | 750 | 15,000 | $0.030 | $0.0047 | Comfortable. No subsetting needed |
| 12 | 1,800 | 36,000 | $0.072 | $0.0113 | Comfortable. Watch for domain confusion if they span unrelated systems |
| 25 | 3,750 | 75,000 | $0.150 | $0.0236 | Past the point Anthropic flags for attention cost. Start subsetting |
| 50 | 7,500 | 150,000 | $0.300 | $0.0473 | Subset. The catalogue is now 28% of an uncached run |
| 100 | 15,000 | 300,000 | $0.600 | $0.0945 | Subset, and audit the catalogue — nobody needs 100 tools in one agent |
| 7-tool resolved subset | 1,050 | 21,000 | $0.042 | $0.0066 | The target. Plus request_more_tools as the escape hatch |
| Selection accuracy | Measure it on your own catalogue with the harness below. Anthropic reports 79.5% to 88.1% on Opus 4.5 with on-demand discovery; your corpus of tools is not theirs |
- Tools visible to the model
- 60
- Schema tokens per turn
- 9,000
- Across a 20-turn run
- 180,000 tokens
- Uncached catalogue cost per run
- $0.360
- Cached catalogue cost per run
- $0.0567
- Attention cost
- Past the 15-20 tool point Anthropic flags
- Failure when the model picks wrong
- Silent — the wrong tool often succeeds
- Escape hatch
- None
- Tools visible to the model
- 7 + always_include
- Schema tokens per turn
- 1,050
- Across a 20-turn run
- 21,000 tokens
- Uncached catalogue cost per run
- $0.042
- Cached catalogue cost per run
- $0.0066
- Attention cost
- Well inside the comfortable range
- Failure when the subset is wrong
- One extra turn via request_more_tools
- Escape hatch
- request_more_tools, always included
const ALWAYS = ["request_more_tools", "escalate_to_human"];
export async function resolveToolSubset(
db: Db, run: { id: string; taskClass: string; tenantId: string; agentId: string },
n = 7,
): Promise<ToolDef[]> {
const cached = await db.getRunToolSubset(run.id);
if (cached) return cached; // ONCE per run, never per turn
const allowed = await db.allowlistedTools(run.agentId, run.tenantId);
const taskVec = await embed(run.taskClass);
const ranked = allowed
.map(t => ({
t,
score: 0.75 * cosine(taskVec, t.descriptionEmbedding) +
0.25 * t.usagePrior, // prior from your own traces
}))
.sort((a, b) => b.score - a.score || a.t.name.localeCompare(b.t.name));
const picked = [
...ranked.slice(0, n).map(r => r.t),
...allowed.filter(t => ALWAYS.includes(t.name)),
].sort((a, b) => a.name.localeCompare(b.name)); // deterministic order
await db.setRunToolSubset(run.id, picked);
return picked;
}
// The escape hatch. Without it, a wrong subset is a failed run.
export const requestMoreTools: ToolDef = {
name: "request_more_tools",
description:
"Call this when no available tool can accomplish the current step. " +
"Describe what you need to do. You will receive additional tools next turn.",
inputSchema: {
type: "object",
required: ["capability_needed"],
properties: {
capability_needed: { type: "string" },
why_existing_tools_insufficient: { type: "string" },
},
},
annotations: { readOnlyHint: true, idempotentHint: true },
};How do you secure an MCP deployment?
With five controls, none optional, and one honest admission: the last class of attack is mitigated, not solved.
The core problem is that a tool description is read by the model on every turn and is not sanitised by the protocol. That makes the description field an attack surface: tool poisoning, where instructions are planted in metadata the model reads and the user never sees. It is now well documented. OWASP designates tool poisoning as the third entry in its MCP Top 10, security research published in mid-2026 found command-injection vulnerabilities in a meaningful fraction of public MCP servers, and Microsoft's June 2026 guidance formally classifies MCP tool descriptions as supply-chain assets requiring the same review rigour as production code. A CISA advisory on MCP security was published in the same period.
The five controls that the 2026 guidance converges on: a gateway acting as zero-trust ingress that inspects every schema before it reaches a model; an enforced per-agent tool allowlist with deny by default; OAuth 2.1 identity binding with PKCE and audience-bound tokens, so an agent inherits the calling user's scope instead of carrying a standing credential; version pinning of approved schemas with a defined on-change behaviour; and human-in-the-loop confirmation on irreversible tools. Add audit logging and a network policy that makes public MCP endpoints unreachable, and you have the posture a security reviewer will sign.
Now the admission. Prompt injection delivered through tool results (not descriptions, results) is mitigated, not prevented. You can truncate, wrap results in an explicit delimiter with a system-level statement that the content inside is data and not instructions, and strip anything resembling a tool-call directive. None of that is a proof. Any post claiming the problem is solved in 2026 is wrong. The correct architectural response is to assume a tool result may be adversarial and to require human confirmation on the irreversible actions an adversarial result might try to trigger. If you need that reviewed independently, a technical review of an agent platform's tool surface is a two-week engagement, not a two-quarter one.
| Threat | How it is exploited | Control | Residual risk | Detection |
|---|---|---|---|---|
| Tool poisoning | Instructions planted in a tool description, which the model reads on every turn and the user never sees | Gateway inspects every schema before it reaches the model; allowlist deny-by-default; description reviewed like code | A malicious description that reads as legitimate documentation passes review | Schema diff on change; description entropy and imperative-verb heuristics |
| Rug-pull update | A server changes a tool's schema or description after you approved it | pinned_schema_hash per tool with on_schema_change: block | A compromised server can still fail closed and cause an outage — that is the correct trade | Hash mismatch rate, alerted per server |
| Confused deputy | The agent uses its own standing credential to act on a user's request, exceeding that user's scope | OAuth 2.1 with PKCE, audience-bound tokens, on_behalf_of the caller; credential injected at the gateway | A first-party server with broad scope can still over-reach internally | Audit log: calls where agent scope exceeds caller scope |
| Prompt injection via tool results | A retrieved web page or ticket body contains instructions the model follows | Truncate, delimit explicitly as data, strip tool-call directives, require confirmation on irreversible actions | Real and unsolved. No 2026 technique fully prevents this | Trajectory eval: did the agent do something the task never asked for? |
| Over-scoped token | One token grants every tool on a server | Least-privilege credential per (agent, server) pair, injected at the gateway, never held by the agent | Scope granularity is limited by what the downstream API exposes | Periodic scope audit against actual tool usage from the audit log |
| Exposed public MCP endpoint | A server intended for internal use is reachable from the internet | Network policy; servers are never publicly reachable; the gateway is the only ingress | Shadow deployments outside the policy | External attack-surface scanning; egress deny by default |
What breaks in production?
Ten things, and the worst of them is a tool call that succeeds. An agent picking a plausible-but-wrong tool from a sixty-tool catalogue does not produce an error, a latency spike or a failed run. It produces a confidently wrong outcome that passes every check you have.
That failure mode is the argument for trajectory evaluation, and the reason tool-selection accuracy belongs in your eval suite rather than your monitoring. Given a task, did the agent choose the right tool? That cannot be answered from outcomes alone, which is why I treat it separately in trajectory evals that catch a wrong-but-successful tool call.
The second-worst is the stale catalogue cache after a revocation, and it is worse than it sounds because its blast radius is security, not availability. You revoke a tool from an agent's allowlist; a gateway node holds a cached catalogue with a ttlMs of five minutes; for those five minutes the agent can still call it. Cache the catalogue by all means, that is the point of the new cache hints, but scope revocation to invalidate rather than wait.
The rest are ordinary distributed-systems failures wearing MCP clothes: a schema drifting under in-flight calls, an oversized tool result, a slow server holding a turn open past the run's wall-clock budget, a cross-tenant credential mistake, and a deprecated transport being removed by a server operator mid-quarter now that the twelve-month clock is running.
| Failure | Blast radius | Detection | Time to detect | Auto-recoverable? | Mitigation |
|---|---|---|---|---|---|
| Agent picks a plausible-but-wrong tool and it succeeds | Single request, wrong outcome | Trajectory eval only — no metric moves | Only via eval | No | Tool subsetting; disambiguating descriptions; enums over free strings |
| Server changes a tool schema; in-flight calls become invalid | All agents using that tool | Schema hash mismatch at the gateway | Seconds | Yes — block and alert | pinned_schema_hash with on_schema_change: block |
| Catalogue cache serves a revoked tool | Security | Revocation timestamp versus cache entry age | Up to ttlMs | Yes | Invalidate on revocation; do not rely on TTL expiry |
| Tool returns 40K tokens | Single run, large cost | result_size p99 by tool name | Seconds | Yes | Bound at the gateway before it reaches the agent, with a journal pointer |
| Slow MCP server exceeds the run's wall-clock budget | Single run | Per-server p95 and p99 latency | Minutes | Yes — run terminates as timed_out | Per-server timeout below the run budget; circuit breaker |
| One tenant's call authorised with another tenant's token | Security, cross-tenant | Audit log: token audience versus caller tenant | Only if you audit for it | No | Credential injection keyed on (agent, server, tenant); never a shared token |
| Gateway is a single point of failure | All tool calls | Gateway availability | Seconds | Yes | Stateless gateway, three replicas — which the 2026-07-28 spec now makes trivial |
| MRTR round trip stalls on an unresponsive client | Single run | Rate of input_required results with no follow-up | Minutes | Yes | Timeout the pending request; terminate the run into waiting_input, not into a hang |
| A server drops the deprecated HTTP+SSE transport mid-quarter | All agents using that server | Transport negotiation failures | Minutes | No | Move to Streamable HTTP now; the offramp is twelve months, not forever |
| Audit gap on a direct connection someone left open | Compliance | Egress logs showing traffic that bypassed the gateway | Audit time | No | deny_direct_agent_egress in network policy, not in documentation |
- An execute_tool span nested under the agent spanAttributes: server, tool name, canonical arg hash, result size in tokens, latency, cache status, gateway decision.
- Tool-selection accuracy against a golden setThe only metric that catches a wrong-but-successful call. Measure it per catalogue size.
- Tool error rate broken out by server, not just by toolA whole server degrading looks like six unrelated tool failures until you group by server.
- Result-size distribution, p50 and p99, per toolThis is your early warning for context blowouts, and it moves days before anyone complains.
- Catalogue cache hit rateNew in 2026-07-28 and directly convertible into dollars. A hit rate under 80% means your cacheScope key is wrong.
- Arg hash cardinality per toolHigh cardinality on a tool you expected to be repetitive usually means the model is guessing at parameters.
What does the MCP layer cost?
Between four cents and sixty cents per twenty-turn run depending almost entirely on how many tool schemas the model can see, plus a gateway hop that is cheap and a catalogue cache that pays for it. The catalogue is the whole story.
At the volumes that matter, the subsetting decision is a budget line, not an optimisation. Going from a sixty-tool catalogue to a seven-tool resolved subset saves about 31.8 cents per uncached run and about 5 cents per cached run. At ten thousand runs a month that is a rounding error. At two million runs a month it is roughly $101,000 a month on cached pricing, the kind of absolute figure that gets a change approved where a percentage would not.
Two smaller lines deserve naming. Gateway compute is genuinely small: a stateless Go or Node service handling header inspection and a registry lookup, sized in single-digit vCPUs at these volumes, which is why the 2026-07-28 statelessness change matters commercially and not just architecturally. Audit log storage grows with tool calls, not runs, so a twenty-turn run with twelve tool calls writes twelve audit rows, and at two million runs a month that is a genuine storage line rather than a footnote.
The catalogue cache is the one component that is cost-negative. Under the old spec you re-fetched tools/list per connection; under the new one, ttlMs and cacheScope let the gateway serve it from memory with a deterministic ordering that also keeps your upstream prompt cache stable. That second-order effect, a stable catalogue meaning a stable cached prefix, is worth more than the round trip it saves, and it is why why a stable catalogue protects your cached prefix is a sentence worth internalising.
| Line | Per 20-turn run | 10K runs/mo | 200K runs/mo | 2M runs/mo |
|---|---|---|---|---|
| Catalogue tokens — 60 tools, uncached | $0.3600 | $3,600 | $72,000 | $720,000 |
| Catalogue tokens — 60 tools, cached prefix | $0.0567 | $567 | $11,340 | $113,400 |
| Catalogue tokens — 7-tool subset, cached prefix | $0.0066 | $66 | $1,320 | $13,200 |
| Saving from subsetting (cached basis) | $0.0501 | $501 | $10,020 | $100,200 |
| Gateway compute | ~$0.00002 | $40 (floor) | $120 | $700 |
| Catalogue cache storage | negligible | $5 | $15 | $60 |
| Audit log storage (12 calls/run) | ~$0.00004 | $10 | $60 | $480 |
| Assumptions | 150 tokens per tool schema; mid tier $2/1M input, cached at $0.20/1M; write 1.25x; prices checked 24 Aug 2026 |
When should you not use MCP?
For tools you own, inside your own process, a plain function call is cheaper and safer. MCP buys you a standard interface across a trust boundary. If there is no trust boundary (the tool is your code, in your repository, running in your process), you are paying a network hop, a schema-translation layer and an operational surface for an abstraction that solves a problem you do not have.
On a latency-critical path, the extra hop matters. A tool that must return in under fifty milliseconds inside a voice turn or an interactive UI does not want a gateway, a schema validation step and a network round trip in front of it. Call the function, and expose the MCP version for external consumers separately if you need one.
And before you can enforce an allowlist, do not roll out servers at all. This is the sequencing mistake I see most often: teams stand up eight MCP servers across the organisation, then discover there is no inventory, no per-agent identity and no audit trail, and spend a quarter retrofitting a gateway in front of something already in production. Ship the gateway first. It is a week of work and it is the difference between a rollout and a remediation.
The honest summary: MCP is excellent at exactly one thing, making third-party and cross-team tools available to agents under a common contract with a common security model, and unnecessary for everything else. If your agent's tools are all first-party, you may not need MCP this year at all, and that is a perfectly good answer.
No trust boundary means no protocol. You would be paying a hop and a schema layer for nothing.
Gateway, validation and a network round trip do not fit inside an interactive turn. Expose MCP separately for external consumers.
This is the case the protocol exists for: a common contract and a common security model across a boundary you do not control.
A week of work now versus a quarter of retrofitting later. Servers before controls is the most common MCP sequencing mistake.
Deterministic list ordering, honest ttlMs, enums over free strings, and a description written as prompt rather than as documentation.
MCP in production: common questions
→What changed in the MCP spec in 2026?
The 28 July 2026 revision made the protocol stateless. The initialize and initialized handshake and the Mcp-Session-Id header were retired, with every request now carrying its protocol version, client identity and capabilities in _meta and an optional server/discover RPC for clients that want capabilities up front. Routing moved into mandatory Mcp-Method and Mcp-Name headers, list results became cacheable via ttlMs and cacheScope, Multi Round-Trip Requests replaced server-initiated elicitation and sampling, RFC 9207 issuer validation became required for clients, and Dynamic Client Registration, Roots, Sampling, Logging and the legacy HTTP+SSE transport were deprecated on a twelve-month minimum clock.
→Do you need an MCP gateway in production?
For any server you did not write, yes. Without one there is no tool inventory, no per-agent allowlist, no identity binding, no audit trail, no rate limiting, and a third party's tool schema reaches your model unvetted. The 2026-07-28 header-routing change is what makes the gateway cheap enough to sit in the hot path: seven of its ten stages read HTTP headers and your own registry rather than parsing the JSON-RPC body. For a single first-party server inside your own trust boundary, a direct connection is fine.
→How many MCP tools can an agent use at once?
Practically, seven to twelve resolved per task rather than everything you have connected. Anthropic's guidance names 15 to 20 tools as the point where attention starts to degrade and describes agents with 20 or more struggling to select correctly, particularly across unrelated domains. Their published measurement on on-demand tool discovery is the cleanest number available: context fell from 77,000 tokens to 8,700 while tool-selection accuracy rose from 79.5% to 88.1% on Claude Opus 4.5.
→What is MCP tool poisoning and how do you prevent it?
Tool poisoning is planting instructions inside a tool's description or metadata, text the model reads on every turn and the user never sees. OWASP lists it third in its MCP Top 10. You prevent the description vector by putting a gateway in front that inspects every schema before it reaches a model, pinning an approved schema hash per tool with a block-on-change policy, and reviewing tool descriptions with the same rigour as production code. You cannot fully prevent injection delivered through tool results: that is mitigated by truncation, explicit data delimiters and human confirmation on irreversible actions, not solved.
→Is MCP secure enough for enterprise use?
With five controls, yes; without them, no. The controls that 2026 guidance converges on are a gateway as zero-trust ingress, an enforced per-agent allowlist with deny by default, OAuth 2.1 identity binding with PKCE and audience-bound tokens so the agent inherits the user's scope rather than a standing credential, version-pinned schemas with a defined on-change behaviour, and human-in-the-loop confirmation on irreversible tools. Add audit logging and a network policy that makes servers unreachable except through the gateway.
→When should you use MCP instead of a normal API call?
When there is a trust boundary. MCP gives you a common contract and a common security model across third-party services and other teams' systems. For a tool that is your own code running in your own process, a function call is cheaper, faster and has less to secure. For a tool on a latency-critical path with a budget under about fifty milliseconds, the extra hop and validation step do not fit. And if you do not yet have a gateway and an allowlist, ship those before you ship servers.