Tool Definition Integrity: Change Control for MCP Servers That Ship Weekly

A reviewer asked us a question last year that most MCP server teams cannot answer: "On the fourteenth of the month, what exactly did your server tell our agent that create_refund does?"

It is a fair question. A tool definition is not documentation. The name, the description, and the input schema are sent to the client, placed in the model's context, and used to decide what to call and with what arguments. That makes a tool description executable text under your control that runs inside your customer's agent. If it can change between two sessions without anyone recording that it changed, neither you nor your customer can reconstruct what happened.

The attack version of this has a name in the research literature: a server that presents benign tools during evaluation and different ones later. Call it what you like. The engineering problem underneath is the same whether the change is malicious, a copy edit, or a schema fix shipped on a Friday: tool definitions change, and most servers have no change control over them.

This post is about building that change control. It assumes you already validate tokens and scope data access; if not, start with the five findings that fail reviews.

What the protocol gives you, and what it does not

MCP has tools/list and the notifications/tools/list_changed notification. A client lists tools at session start; if the server's set changes mid-session, the server notifies and the client re-lists. That is a liveness mechanism. It is not integrity.

The protocol does not define a version for a tool, a hash of a definition, a signature, or any way for a client to say "I only accept the tool set I reviewed." Some hosts cache definitions and show the user a diff; most do not, and you cannot depend on host behaviour you do not control. Everything that follows is server-side work you do because no one else will do it for you.

Version the definitions, not just the code

Treat the tool set as a released artifact with its own identity, separate from your service's deployment version.

  • Keep tool definitions in declarative files in the repository, one file per tool, not as decorators scattered through handler code. You want a diff that a non-author can read.
  • Compute a stable digest over each tool's canonical definition: name, description, input schema, output schema, and annotations such as read-only or destructive hints. Canonicalize first (sorted keys, normalized whitespace) so formatting churn does not produce a new digest.
  • Compute a digest over the whole set, and give the set a monotonic version.
  • Fail the build when any digest changes without a matching entry in the change log. This single check is what converts a policy into a control.

The digest is cheap and it is the thing that makes the rest of the evidence credible. "We reviewed every tool change" is a claim. "Here is the manifest, here are the digests, here is the commit that changed this one and the approver" is evidence.

Publish the manifest

Expose the current tool manifest at a stable, unauthenticated URL: version, generation date, and for each tool the name, digest, and whether it can change state. Two audiences use it.

Your customer's security team uses it to pin a state at review time and to check later whether anything moved. Your own monitoring uses it to detect drift between what a given environment serves and what the release intended.

Keep the historical versions addressable. A reviewer who finds an incident on the fourteenth wants the manifest that was live on the fourteenth, not the one live today.

Classify the change before you ship it

Not every edit deserves the same process. Three bands work in practice:

Cosmetic. Typographical fixes in a description that do not alter what the tool does or what the model will infer about when to call it. Reviewed by one engineer, logged, no notification.

Behavioural. A new optional parameter, a widened enumeration, a description that changes when the model will select the tool, a bound that gets looser. These change agent behaviour in the field. They need a second reviewer, a change-log entry, and a notification to subscribed customers before the release, not after.

Privilege. A new tool, a read tool that becomes capable of writing, a destructive action, a new downstream system touched, or any widening of what data a tool can return. These need the same treatment as a new API endpoint: threat model delta, a new scope where appropriate rather than folding the capability into an existing one, an entry in the evidence pack, and advance notice with a date. Enterprise customers will want to re-run an internal approval; give them the lead time to do it.

The temptation is to mark everything cosmetic. Resist it by making the classification a required field in the pull request template and auditing a sample of past classifications each quarter.

Emit the change as an audit event

Your audit schema already covers tool calls. Add a second event type for tool-set changes: timestamp, previous set version, new set version, the tools added, removed, or modified with old and new digests, the change band, and the release identifier. Ship it to the same sink.

The payoff is correlation. When a customer investigates an odd agent action, the first thing their analyst wants to know is whether the tool changed shortly before. Without the event they are guessing. With it the question is answered in one query.

Handle the mid-session case honestly

If your tool set can change while sessions are open, decide what happens to those sessions and write it down.

The conservative option: keep a session on the tool set it started with, send notifications/tools/list_changed, and let a new session pick up the new set. Calls to a tool whose definition has changed under an open session are rejected with a clear error telling the client to re-list. This costs you a small amount of client friction and buys you a server where the definition in the model's context always matches the definition the handler enforces.

The permissive option is to apply the change immediately to all sessions. If you choose it, be explicit that a model may be operating with a stale description, and make sure server-side validation is strict enough that a stale description cannot cause an unauthorized call. Either way, the handler validates against the current schema. A description in a model's context is never an authorization decision.

Pinning, for the customers who ask

Some enterprise customers will ask whether they can pin a reviewed tool set. You can offer a limited form of this without inventing a protocol extension: let a customer's administrator record an approved set version, and have the server refuse to advertise a newer set to that tenant until an administrator approves it, with an expiry so the pin cannot become permanent abandonware.

Be honest about the cost. Pinning means a tenant can sit on a definition with a known bug, and it means you carry more than one live tool set. We do not recommend it for most servers. It is worth building when you have a customer in a regulated environment whose change-approval process genuinely requires it, and it is worth saying no otherwise.

What to put in the evidence pack

For a vendor review, four artifacts answer nearly every question in this area: the current manifest with digests, the change log for the last twelve months with change bands, a sample tool-change audit event, and a one-page description of the review and notification process including who approves a privilege-band change. Add the retention period for historical manifests.

That set takes an afternoon to assemble once the build check exists, and it removes an entire category of follow-up questions from the review. If you want the rest of that pack specified, we wrote it up in the MCP security evidence pack.

The short version

Tool definitions are an interface you ship into other people's agents. Treat them like one: declarative source, digests, a published manifest, a classification on every change, an audit event, and a stated policy for open sessions. None of it is difficult. It is only difficult later, after a customer asks what their agent was told on a day you no longer have a record of.

If you want this reviewed against an existing server, that is part of what our MCP security audit covers.