# 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](/docs/rules)
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:

```bash
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:

```bash
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](/docs/severities).

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](/docs/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](/docs/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.
