mcp-pact
Consumer-driven contract testing for MCP servers.
Agents declare the tools and arguments they depend on. CI verifies that a server surface still satisfies those contracts before you deploy.
Why
Most MCP tooling snapshots the provider and diffs schemas. That answers “did the server change?”
mcp-pact answers a different question:
If I ship this MCP server change, which agents break?
Install
pip install mcp-pact
Requires Python 3.10+.
Quickstart
1. Write a consumer contract
# contracts/support_agent.yaml
consumer: support-agent
provider: docs-mcp
interactions:
- tool: search_docs
arguments: { query: "refund policy", limit: 5 }
required_arguments: [query]
- tool: get_doc
arguments: { doc_id: "POL-12" }
required_arguments: [doc_id]
2. Snapshot the provider surface
Export your MCP tools/list result to JSON (see examples/provider_v1.json).
3. Verify in CI
# one consumer
mcp-pact verify \
--contract examples/consumer_support_agent.yaml \
--provider examples/provider_v1.json
# all consumers before release
mcp-pact can-deploy \
--provider examples/provider_v1.json \
--contracts contracts/*.yaml
# schema diff + consumer impact
mcp-pact diff \
--old examples/provider_v1.json \
--new examples/provider_v2_breaking.json \
--contracts examples/consumer_support_agent.yaml
# generate adversarial argument cases from schemas
mcp-pact fuzz --provider examples/provider_v1.json --out fuzz_cases.json
Exit code 1 means at least one consumer would break.
What is checked?
| Check | Breaking when |
|---|---|
| Tool presence | Consumer tool is missing on the provider |
| Required args | Consumer needs an argument the provider removed |
| Provider-required args | Provider newly requires an argument the consumer never sends |
| Types | Provider input type no longer accepts what the consumer sends |
| Description-only | Classified as routing (agent selection risk); not a hard fail by default |
Python API
from mcp_pact import (
load_consumer_contract,
load_provider_surface,
can_i_deploy,
generate_fuzz_cases,
)
provider = load_provider_surface("provider.json")
contract = load_consumer_contract("agent.yaml")
decision = can_i_deploy(provider, [contract])
assert decision.ok, decision.to_dict()
Pytest plugin
pytest --mcp-provider-surface=provider.json \
--mcp-consumer-contracts=contracts/a.yaml,contracts/b.yaml
def test_agents_still_compatible(mcp_can_i_deploy):
assert mcp_can_i_deploy.ok, mcp_can_i_deploy.to_dict()
Comparison
| Library | Model |
|---|---|
mcp-contract, mcp-diff, … |
Provider snapshot + schema lockfile |
| mcp-pact | Consumer-driven contracts, can-i-deploy, fuzz case generation |
Development
python -m venv .venv
source .venv/bin/activate
pip install -e ".[dev]"
pytest
Status
Alpha (0.1.0). Offline-first: works on committed JSON/YAML provider surfaces. Live MCP probing is planned.
License
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file mcp_pact-0.1.0.tar.gz.
File metadata
- Download URL: mcp_pact-0.1.0.tar.gz
- Upload date:
- Size: 13.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/7.0.0 CPython/3.13.5
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3b58e19a24b7afa22db929cc383601982b1b37ea5e86f02461c41e0d8d8b496e
|
|
| MD5 |
b8d57c27c9baadf9b170166d16121db2
|
|
| BLAKE2b-256 |
b074d75ba20103262c5e35d7ea8be75af810638bdf832077c2b4e0d447318119
|
File details
Details for the file mcp_pact-0.1.0-py3-none-any.whl.
File metadata
- Download URL: mcp_pact-0.1.0-py3-none-any.whl
- Upload date:
- Size: 15.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/7.0.0 CPython/3.13.5
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
70221312ee1670a8fba10ff089ba86f179953a1db05f9a7aec8953d9fcd05ba8
|
|
| MD5 |
67c8635b427a4d7a5a376884e07574e1
|
|
| BLAKE2b-256 |
07d18f3917aa997c2e67da61bdf69254deb5a69f8daa7215518cf52afbdc27ef
|