Most MCP security writing, ours included, is about the review before launch. Enterprise reviewers ask a second set of questions, and they ask them in the contract rather than the questionnaire: what happens when this server is the thing that goes wrong. Who do you call, how fast do you revoke, what can you tell us about which of our records an agent touched, and how do we hear about a vulnerability someone else found.
A server that cannot answer those questions is not finished. This is the plan we write into handover, and the one we look for during an audit.
What counts as an incident here
An MCP server fails in ways a plain API does not. Write the trigger list before the runbook, because the list decides who gets paged:
- Cross-tenant exposure. A tool returned data belonging to a tenant other than the caller's. Treat as confirmed breach until the audit trail says otherwise.
- Token misuse. An access token used from an unexpected client, replayed after logout, or presented to a resource it was not issued for. Audience validation failures spiking is the signal.
- Prompt injection with effect. Content in a tool result steered an agent into calling a write tool. The injection is not the incident; the write is.
- Over-broad tool. A deployed tool turned out to ignore an entitlement check, so every user with the scope could act beyond their role.
- Client compromise. A registered client's credentials leaked, or a host application your users connect through is reported compromised.
- Upstream advisory. An MCP SDK, gateway, or framework you depend on publishes a vulnerability. You did not write the bug and you still own the exposure window.
Each trigger needs a named severity and a named first responder. "The platform team" is not a name.
The first hour: contain without destroying evidence
Containment for an MCP server has levers a web application does not have, and they are worth building before you need them.
Disable a single tool, not the server. A feature flag per tool, readable at tools/list time, lets you remove the write tool that is being abused while reads keep working. Clients handle a shrinking tool list better than they handle a dead endpoint. Changing the tool list changes the model's options immediately, which is the fastest control you have.
Revoke in bulk, by the right axis. Build revocation that takes a predicate, not a single token: all tokens for one user, for one tenant, for one client application, or issued before a timestamp. If your tokens are self-contained JWTs, you need a deny list the resource server consults, or short lifetimes plus refresh-token revocation at the authorization server, or both. Decide which before the incident; you cannot design a revocation path while on a bridge call.
Suspend the client registration. If the compromised party is a client, revoking its registration stops new authorization flows. Revoking its outstanding tokens is a separate action. Do both and record both.
Freeze, do not wipe. Snapshot audit events, request logs, and the tool definitions as deployed. Pin the SDK versions and the container digest that were live. Teams routinely redeploy during containment and lose the evidence of what the tool schema said an hour ago; tool definition hashes in your change log are what prevent that.
Write the time of each action down as you go. The notification you send later will be judged on whether its timeline is coherent.
Forensics: your audit schema is the investigation
The questions a customer asks after an incident are narrow and factual:
- Which of our users' sessions called tools in the window?
- Which records did those calls read or change?
- Which client application was on the other end?
- Did any call succeed that should have been denied?
If the audit events carry subject, tenant, client ID, tool name, outcome class, correlation ID, and a redacted argument summary, every one of those is a query. If they do not, the honest answer is "we cannot tell", and that answer ends enterprise deals. This is the operational reason the audit schema matters more than it seems at design time.
Two gaps show up repeatedly in audits. The first is denials that are not logged, which means you cannot distinguish probing from normal use. The second is missing correlation between a tool call and the downstream queries it caused, which means you can say a tool ran but not what it touched. Both are cheap to fix before an incident and impossible to fix during one.
The customer clock
Notification timing is a contract term, not a judgement call. Enterprise agreements commonly specify a window measured in hours from confirmation, and your data processing addendum may oblige you to notify a customer in time for them to meet their own regulatory deadlines. Read the clause now and know the number.
A notification that holds up contains: what happened in plain terms, the window, which of that customer's data and users are implicated, what you have done, what they should do, and when the next update comes. If an agent acted on a user's behalf, say which user and which tool. Vagueness here produces a security questionnaire from every other customer the following week.
Tell customers what they can do themselves. For an MCP server, that usually means: revoke the connection in their IdP, rotate any credentials they supplied for account linking, and review the audit events you ship into their SIEM. Customers who have your events in their own SIEM are far easier to notify, because they can verify your account rather than take it.
Coordinated disclosure, so researchers do not post first
MCP servers attract external testing. Agent tooling is new, a large share of deployed servers have weak authorization, and researchers are looking. You want the finding in your inbox rather than in a conference talk.
The minimum: a security.txt at /.well-known/security.txt naming a monitored address, a short public policy stating scope and safe harbour, an acknowledgement commitment in business days, and an internal owner. Say which hosts are in scope and say plainly that cross-tenant testing against other customers' data is not. If you have no bounty, say so; researchers mind silence more than they mind an unpaid report.
When a report lands, treat the clock as running from receipt. Agree a disclosure date with the reporter, fix, then publish an advisory. If you distribute an MCP server that customers run themselves, you also need a release channel they watch, and a version statement in the advisory, because they patch, not you.
Afterwards: re-submission, evidence, and the spec
Two tasks get forgotten once the incident closes.
First, the directory listing. If your connector is listed in a directory, the listing's terms may require disclosure of security incidents affecting it, and a material change to authorization or tool behaviour usually requires re-submission. A quiet fix that changes your tool surface can leave the listed description wrong, which is its own review problem later.
Second, the evidence pack. Add the incident to it. A threat model that now includes the abuse case that actually happened, plus a dated note of the control you added, reads better to the next reviewer than a pack that pretends the server has never been touched. Reviewers assume incidents. What they test is whether you noticed and what changed.
Then close the loop with your adversarial test plan: the exploited path becomes a permanent test case, run before every release.
The short version
Build four things before launch. A per-tool kill switch. Revocation that takes a predicate. Audit events that answer which user, which tool, which record, which outcome. A monitored disclosure address with a published scope. Everything else in an incident is writing, and the writing is easier when the facts are available.
We write this runbook as part of a server build and test it against the audit trail during an MCP security audit. If you already have a server in production and no answer to "who do we call", that is a conversation worth having before your first customer asks: get in touch.