Detect MCP breaking changes before merge
Compare MCP tool schemas, descriptions and capabilities with a deterministic contract check in your pull request.
An MCP server exposes more than JSON Schema. Tools, resources, resource templates, prompts, capabilities and server instructions all form its contract. An agent uses descriptions and annotations to decide what to call, so a change can alter behavior even when an existing request still validates.
#Which changes can break an MCP caller?
| Change | Why it matters | MCP Inspect severity |
|---|---|---|
| Remove a tool | A caller can still request its old name. | BREAKING |
| Make an optional input required | Previously valid calls can omit it. | BREAKING |
| Narrow an input enum | A caller can send a previously accepted value. | BREAKING |
| Rewrite a tool description substantially | An agent may change its tool selection. | POTENTIALLY_BREAKING |
Change an annotation such as readOnlyHint | The host or agent may treat the tool differently. | POTENTIALLY_BREAKING |
| Add a tool | Existing tool contracts remain available. | NON_BREAKING |
These are examples, not an exhaustive checklist. The rule reference documents every emitted rule id and its migration hint. Input and output schemas have different compatibility directions. An unmodeled schema keyword is reported as breaking rather than silently ignored.
Description classification is a deterministic heuristic. It identifies changes worth reviewing; it does not predict a particular model’s behavior. Re-run your tool-selection evaluations when the meaning changes.
#Compare before and after
Run this in your MCP server repository before editing the contract:
mcp-inspect init --snapshot
This writes configuration and captures .mcp-inspect/surface.json. The inspector
discovers the advertised contract over stdio or streamable HTTP without invoking
tools. A baseline capture by itself is not a compatibility verdict.
After making your change and rebuilding the server, compare against that baseline:
mcp-inspect check --baseline-file .mcp-inspect/surface.json
The check captures the current surface without overwriting your baseline. By
default a breaking change exits 1; a connection or capture failure exits 2.
Set fail_on: POTENTIALLY_BREAKING in .mcp-inspect.yml if substantial prose and
annotation changes should also block CI. Severity details.
For two existing captures, use mcp-inspect diff before.json after.json.
The verdict needs no hosted account or LLM API key.
#Gate a pull request
Run mcp-inspect init --workflow and review the generated workflow, server
build command and secrets. Commit the configuration and baseline to the base
branch. The GitHub Action compares against that branch and
updates one PR comment with findings and migration hints.
A first setup PR can report a skipped comparison. That is not a clean comparison;
the Action’s compared=true output tells you whether analysis actually ran.
Once an intentional change is reviewed, capture and commit the updated baseline
with it. The PR still compares against the previous baseline in the base branch.
#What contract checks do not measure
A snapshot captures the surface a particular caller can discover. It does not execute handlers, test business logic or prove an agent completed its task. Keep integration tests and tool-selection evaluations alongside the contract check.
Optional telemetry adds observed tool and argument-path usage. It records no argument values. Observation windows and collection limits matter: absence of observed usage is not proof that removing a tool will affect nobody.
If you consume a third-party server, the hosted console can schedule captures
under watched-server settings and alert when its contract changes. Local
mcp-inspect watch instead rechecks your own server on source-file changes;
these are separate workflows.