Threat Modeling an MCP Server: Trust Boundaries, Abuse Cases, and the Diagram Reviewers Ask For

Most MCP servers we audit have controls but no model. The team can tell you that tokens are validated and that scopes exist, and cannot tell you which boundary each control defends or what happens if one of them fails. That gap is what a vendor security questionnaire is designed to find, and it is why the first document a reviewer asks for is usually a data-flow diagram, not a code sample.

Threat modeling an MCP server is a half-day exercise if you do it in the right order. This is the order we use.

Why the generic model does not fit

Standard web-application threat models assume a caller that is either a browser session or a machine client, and a request whose parameters were chosen by whoever is authenticated. An MCP server breaks both assumptions.

The caller is a client application acting for a human, and the parameters of each tool call are chosen by a language model whose context includes content the server itself returned. So the arguments arriving at your handler are attacker-influenceable even when the user is legitimate and the token is valid. A model that treats tool arguments as authenticated intent will miss the class of finding that fails reviews.

Step one: draw the five boundaries

Draw the system as boxes and arrows, then mark where trust changes. In practice an MCP server has five boundaries, and it is worth naming them explicitly because reviewers ask about them one at a time.

  1. Client to server. The MCP client presents an access token over the transport. Trust changes here on token validation: issuer, signature, expiry, and audience. Everything downstream depends on this check being correct. See our notes on validating token audience.
  2. Authorization server to resource server. Your server is a protected resource. It consumes tokens it did not issue and publishes metadata that tells clients where to get them. Trust changes on which issuers you accept and what you advertise in protected resource metadata.
  3. Model to tool. The language model chooses the tool and its arguments. Trust changes here in one direction only: nothing the model supplies is an authority. Identity, tenant, and role come from the token context in the handler, never from an argument.
  4. Tool to backing system. The handler calls your database, search index, or internal API. Trust changes on whether the per-user and per-tenant constraint is enforced at that system or merely passed along as a hope.
  5. Tool result to model context. Data leaving your server enters the model's context, where instruction-like text may be acted on. Trust changes because your own output becomes input to the next decision.

If your diagram has fewer than five boundaries, you have collapsed one. The most commonly collapsed is the fourth: the team draws one arrow to "backend" and never records where the tenant filter is applied.

Step two: enumerate abuse cases, not vulnerabilities

Work boundary by boundary and ask what an actor at that boundary would try. Keep each item in the form "actor does X, and the consequence is Y", because that is the form a reviewer can evaluate. STRIDE is a usable prompt list here, though the useful subset for MCP is narrower than the full six.

  • Spoofing. A token from a different issuer, or a token issued for a different resource and replayed at your server. Consequence: authentication bypass across tenants.
  • Tampering. A tool argument carrying tenant_id or user_id chosen by the model after reading an untrusted document. Consequence: cross-tenant read or write with a valid token.
  • Repudiation. A tool call with no audit event naming the subject, the client, and the tool. Consequence: the customer cannot reconstruct who did what, which is a finding on its own.
  • Information disclosure. A read tool that returns an entire record where the task needed three fields, or a stack trace in an error result. Consequence: data in the model context and in the client's logs that the customer never agreed to send there.
  • Elevation of privilege. A tool with no human equivalent in the product, or a write tool reachable by a scope intended for reads. Consequence: the agent can do something the employee cannot, which is the single question every security lead asks.

Denial of service belongs in the model too, but as a cost and availability question rather than a confidentiality one; we treat it separately under rate limiting and abuse controls.

Two abuse cases are specific to MCP and get missed by teams reusing an API threat model. The first is indirect prompt injection through your own tool results: content your customer stored, returned by your read tool, instructing the model to call your write tool. The second is the confused deputy across a linked third-party account, where the server holds a credential for a system the current user has no right to touch.

Step three: map each abuse case to a control and an owner

One row per abuse case. Columns: boundary, abuse case, control, where the control lives, how it is tested, and who owns it. This table is the control matrix, and it is the artifact that turns a diagram into evidence.

Two rules make the matrix honest. Every control must name a location in the system, not a practice: "tenant predicate applied in the repository layer, enforced by row-level security in Postgres" rather than "we scope queries". And every control must name a test that fails when the control is removed. A control with no failing test is an intention.

Rows that resolve to "accepted risk" are fine, and a reviewer will accept several. Write the acceptance down with the reason and the compensating control. Silence on a row reads as an oversight; a stated acceptance reads as engineering judgment.

Step four: decide what the model tells you to change

A threat model that produces no design change was decoration. Expect it to produce three kinds of change.

Some tools should not exist. If an abuse case has no control that keeps the agent inside the human's authority, the tool is out of scope for this release. Some tools should be split, because a single tool that both discovers records and modifies them lets one injected instruction complete an attack in one step. And some enforcement should move down, from the handler into the system that owns the data, so that a future handler written by someone else inherits the constraint.

Keeping it current

The model is versioned with the server. Any new tool, any new backing system, any change to the token's issuer or audience, and any spec revision that alters the authorization flow means a revisit. In practice a quarterly review plus a required diff whenever a tool is added is enough to keep it from going stale.

When the questionnaire arrives, the diagram and the matrix answer most of it directly, and the remaining answers are short because the reasoning already exists in writing. That is the practical return on the half day.

Where this sits in an engagement

We build the threat model in the design week, before code, and it becomes part of the security evidence pack handed over at the end. You do not need us to run the exercise: the four steps above are the whole method, and a senior engineer on your team can work through them with the tool list and a whiteboard.

If you would rather have an independent read, an MCP security audit produces the model, the abuse-case list, and the findings ranked by review impact. If you are still deciding whether a server needs one at all, say what you have built so far when you get in touch and we will tell you plainly.