Harden one route. A server, marketplace listing, gateway, or agent role is too broad to prove.
Every allowed route needs the closest dangerous call that must fail closed with typed evidence.
The route is not production-ready until authority, budget, retry, and receipt context survive repetition.
Most MCP hardening work starts too high: is this server safe? The operator question is narrower and more useful: which exact route can repeat, under which authority, against which denied neighbor, with which proof?
The current MCP production conversation keeps clustering around the same problems: unconstrained string parameters, remote-hosted authority ambiguity, shared credentials, tenant bleed, quota burn, generic denials, and receipts that arrive too late to prove the route should have executed.
Remote MCP auth launches make the same checklist more important, not less. OAuth, connector install, or gateway admission proves the front door works; the route card proves which authenticated client, tenant, visible tool slice, backend credential, quota owner, denied neighbor, and receipt standard survived after login.
Those are not independent issues. They are symptoms of one missing artifact: a route card that turns a tool call from a demo into a bounded production lane.
A route card is not documentation. It is the pre-execution decision record: who is calling, what may execute, what must refuse, which credential and budget are in play, how often it may repeat, and what evidence comes back.
The route card fields
- Route name: one MCP tool call or capability, not a whole server, catalog, or agent role.
- Allowed input: normalized path, URL, tenant, repo, customer, account, amount, or command lane that may execute.
- Denied neighbor: the closest dangerous value that must fail closed with a typed policy denial.
- Authority lane: caller, tenant, credential mode, backend principal, budget owner, and approval state.
- Repeat envelope: expected volume, retry ceiling, timeout, idempotency key, and recovery path.
- Evidence: receipt fields, trace identifiers, policy bundle, estimate, denial code, and post-call resource proof.
Copy-paste route card
If the route is real, do not send a paragraph about the server. Send the card. Empty fields are useful too: they tell you exactly why the route is not ready for repeated agent execution.
Route name / MCP tool call:
Why it must repeat:
Allowed input lane:
Denied neighbor that must fail closed:
Caller / tenant / workspace:
Credential lane / backend principal:
Budget or quota owner:
Expected repeat volume / retry ceiling:
Receipt fields or typed denial I would trust:
Current blocker: auth, permission scope, tenant bleed, quota burn, retry safety, receipt proof, or other:
Source: e008-route-hardening-quote-checklist The five-step MCP route hardening checklist
Narrow discovery to one route
Do not harden `filesystem`, `browser`, `github`, or `search` as a category. Pick the one tool call that must repeat: read this repo path, fetch this approved domain, create this ticket type, run this SSH runbook, or query this account-scoped object.
Bind the caller before the handler runs
The route card should be resolved before execution: org, agent, user, tenant, environment, credential rail, quota owner, and side-effect class. If any of those fields are missing, the right output is a typed no-call, not a best-effort provider attempt.
Test the unsafe neighbor
Every route needs one adjacent-danger fixture: the path just outside the allowlist, the sibling tenant, the internal host, the broader command, the write target near the allowed read, or the provider account the agent should not touch.
Preserve budget and retry context
Route hardening fails if the agent can burn shared quota while looking safe. Attach retry ceiling, timeout, expected volume, idempotency or replay key, and the budget owner to the same route decision that authorized the call.
Return proof, not vibes
A successful route should produce a receipt with route id, actor, credential lane, estimate, provider resource, policy bundle, trace id, and side-effect class. A denied route should preserve the same context plus the exact policy that refused it.
What fails when you harden the server instead of the route
Server-level safety language sounds strong and usually fails at the exact moment an agent needs to repeat a call. The proof has to survive the tool handler, gateway, credential broker, provider call, and receipt path as one coherent decision.
- The schema says `path: string`, but the server never normalizes the path before comparing it to the allowlist.
- Auth proves who connected, but tool visibility and backend credentials stay broader than the caller's route.
- A gateway enforces policy at discovery time, then the runtime handler uses a different provider credential or quota bucket.
- The denied neighbor returns a generic error, so the agent retries around a policy boundary as if the provider flaked.
- Receipts prove the call happened, but not whether the caller, credential rail, estimate, and side-effect class were the ones approved.
Examples of route-sized hardening
Allowed: read markdown under one repo docs directory. Denied neighbor: normalized path outside that directory. Proof: caller, repo, normalized path, policy id, and typed traversal refusal.
Allowed: extract public docs from approved domains. Denied neighbor: internal host, cross-domain login page, or disallowed data class. Proof: domain policy, egress decision, byte cap, and extraction receipt.
Allowed: one runbook command on one host/user with timeout and rollback. Denied neighbor: arbitrary shell, different host, PTY escalation, or environment read. Proof: command lane, working directory, exit code, and rollback evidence.
Allowed: create a support note on a named account. Denied neighbor: sibling tenant, owner reassignment, bulk export, or deletion. Proof: tenant binding, provider resource id, idempotency key, and side-effect receipt.
Allowed: one authenticated client runs one tenant-scoped tool after manifest filtering. Denied neighbor: the same token trying a sibling tenant, hidden tool, broader backend credential, or unowned quota bucket. Proof: client id, tenant/workspace, visible tool slice, credential lane, budget owner, typed denial, and receipt trace.
Send the one MCP route you would pay to make boring
If you have a route that is close to useful but too risky to let an agent repeat, send the route card as a quote request. The useful request names the route, unsafe neighbor, credential or budget owner, repeat volume, and receipt or typed denial you would trust.
Related production checks
Why unconstrained parameters turn model instructions into real blast radius.
How liveness differs from safe remote execution under real operator constraints.
Why route cards are the control plane between discovery and execution.
Why post-call receipts help only after pre-call authority is already bounded.