MCP contract testing

Every MCP server your agent calls is an API contract: tool names, input schemas, required fields, enums, output shapes, and descriptions. Contract testing verifies that the contract your code was written against still holds, before deploy, not after an incident.

Traditional contract testing (Pact for REST, buf for protobuf) never reached MCP. Zevruna closes that gap with three primitives: a snapshot (the canonical, hashed form of a server's advertised contract), a manifest (what your agent actually sends and reads, generated by scanning your code), and a check (does the manifest satisfy the pinned snapshot).

How the CI check works

`zevruna scan` walks your codebase and emits one manifest per agent with call sites down to file and line. `zevruna check --against pinned` compares those manifests to the snapshot you pinned. A field you send that no longer exists, a new required field you omit, or an output field you read that was removed all fail the build with exit 1 and a human-readable redline.

Because the check compares against the live pinned contract, it also fails when the server drifted while your code didn't change, run it nightly on a schedule, not just on push. That is the case no test suite in your repo can catch, because nothing in your repo changed.

What counts as a breaking change

Zevruna classifies every diff from the consumer's perspective: tool removed or renamed, required input added, input property removed, type narrowed, enum value removed, and output property removed are breaking. Optional additions, enum additions, default changes, and large description rewrites are risky, descriptions steer model tool selection, so they are part of the contract even though no traditional API differ treats them that way.

What a failing check tells you

A red build that only says the contract moved has handed the work back to you. The check names the change, the agent it affects, the field, and the file and line that has to change, because the manifest already recorded which call sites send and read what. That is the difference between a signal and a chore.

Severity is judged per consumer, not per diff. A removed field that none of your agents read fails nothing, and a check that fails on it would be trained away within a week.

Resolving without anyone clicking resolved

Accepting a new contract repoints every affected manifest at the new snapshot, which makes CI fail until your call sites actually match. That is deliberate: accepting means fix in progress, never fixed. The green check is the only thing that closes the incident, so a resolution is always something a machine verified rather than something a person asserted.

The gap between detection and that green check is the exposure window, recorded per incident. It is the number an engineering lead can act on, and the only honest measure of whether any of this is working.

Questions

Do I need to add an SDK to my agent?

No, not for contract monitoring: the CLI scans your code and the cloud handshakes your servers, and production traffic is untouched. Execution traces are the optional half, and there an SDK does run in your process by design, because reconstructing a run is the only way to show you one. If you already export OpenTelemetry you can point it at us instead and install nothing.

Does it work with stdio MCP servers?

Yes, stdio servers are snapshotted locally by the CLI and pushed, since the cloud only connects to remote transports.

What if my arguments are built dynamically?

zevruna record wraps the client in development and captures field names (never values) from real calls to complete the manifest.

Am I testing the server or my own code?

Your code, against the server's advertised contract. Zevruna makes no claim about whether the server is correct, only about whether what you send and read still fits what it now advertises. That is the question you can act on.

Does it need write access to my repository?

No. The CLI runs in your CI with whatever permissions that job already has, and the MCP server hands a coding agent the diff and suggested patch so the agent edits your files locally. Zevruna never holds a repo credential.

Why run it nightly if nothing changed?

Because that is exactly the case a push-triggered test can never catch. If your code did not change and the server did, only a scheduled run notices, and the drift is already live by then.

Next step

Run `npx zevruna` in your agent repo, first contract check in five minutes.

Check a server freeRead the docs