MCP schema compatibility: input and output breaking changes
Learn why accepting a new input enum value preserves callers while returning a new output value can break them, with reproducible MCP contract diffs.
MCP input and output schemas have opposite compatibility directions. A server can usually accept more inputs without invalidating old calls. Returning more possible outputs can break consumers that relied on the old result contract. Checking whether a JSON Schema changed is not enough: you must check which side of the call it describes.
#Is adding an enum value a breaking change?
Suppose a tool accepts or returns a status property:
{ "type": "string", "enum": ["open", "closed"] }
The next release adds archived:
{ "type": "string", "enum": ["open", "closed", "archived"] }
| Where this schema appears | Effect on an existing caller | Engine rule | Severity |
|---|---|---|---|
inputSchema | Every previously accepted value remains accepted. | tool.input.enum.widened | NON_BREAKING |
outputSchema | A result can now contain a value the caller never handled. | tool.output.enum.widened | BREAKING |
This verdict concerns the declared contract. It does not prove that a handler implements the declaration correctly. See both complete tool definitions and engine findings in the account-free example lab. Its normalized before/after snapshots and JSON findings are downloadable.
#Is making an argument required a breaking change?
Yes, when an existing input property changes from optional to required. A call that omits the property used to validate and now fails.
For a search tool with query and limit, changing required: ["query"] to
required: ["query", "limit"] emits tool.input.required.added at BREAKING.
Adding an entirely new required input property is also breaking. Keeping an
existing default during migration lets callers adopt the property before it
becomes mandatory. Inspect the exact example.
Other common input changes include removing a tool or property, narrowing an enum, tightening a numeric bound, or disallowing additional properties. Read the generated rule reference for their individual rules and migration hints; not every keyword has the same behavior.
#What about output properties?
Consumers rely on the shape the server promised. Removing a guaranteed output property or making it optional can invalidate that assumption. An output schema is a declaration for structured results; a comparison cannot verify results the server actually returns. Keep handler tests that exercise those results.
The MCP specification describes tool schemas and structured content. The compatibility severities and stable rule ids here are MCP Inspect's product rules, rather than severities assigned by the protocol specification.
#What happens with unknown JSON Schema keywords?
If a changed keyword is not modeled by the engine, the finding is BREAKING.
It does not silently become an additive change. Review the keyword's semantics
and the raw declarations before approving an exception. This conservative rule
keeps an unrecognized constraint from producing a misleading clean check.
#Compare your server before shipping
For a public HTTP endpoint, create a hosted account, add its final MCP URL, and capture a baseline. After deploying a candidate to that endpoint, choose Capture again and review Changes. This tracks endpoint history; it cannot block a release that already happened.
For a check before merge, the CLI and CI Action
compare captured contracts with the same engine. These currently require
approved preview builds; public package releases are pending. The npm package
named mcp-inspect belongs to another project.
Schema compatibility is only one part of the contract. Continue with description and annotation changes or monitoring a third-party server.