Contract testing and regression detection for MCP servers.
Project description
mcpact
Contract testing for MCP servers.
You build an MCP server. You change something. How do you know the tools still work and the schemas haven't drifted? mcpact gives you a YAML contract, runs it against your running server, and fails CI when something breaks.
Install
pip install mcpact
Requires Python 3.10+.
A minimal contract
# contracts/my-server.yaml
server:
transport: stdio
command: python server.py
tools:
- name: search_files
description_contains: search
input_schema:
required: [query]
properties:
query: { type: string }
assertions:
- call:
args: { query: "hello" }
expect:
status: success
max_latency_ms: 1000
Run it:
mcpact run --contract contracts/my-server.yaml
search_files
✓ exists
✓ description_contains
✓ input_schema.required
✓ call#1.status [42ms]
✓ call#1.max_latency_ms [42ms]
5 checks · 5 passed · 0 failed
Exit code is 0 on success, 1 on any failure, 2 on contract errors.
Detect regressions
Capture the current surface, then compare future runs against it:
mcpact snapshot --contract contracts/my-server.yaml --out .mcpact/baseline.json
mcpact diff --contract contracts/my-server.yaml --baseline .mcpact/baseline.json
Snapshot diff:
+ get_file_info (new tool)
- delete_file (removed — breaking)
✗ search_files
└─ required removed: limit
└─ type changed on query: 'string' → 'integer'
diff exits non-zero on breaking changes (removed tools, removed required
fields, changed property types) so you can wire it into CI.
Transports
server:
transport: stdio # command: python server.py
transport: http # url: http://localhost:3000/mcp
transport: sse # url: http://localhost:3000/sse
Commands
| Command | Purpose |
|---|---|
mcpact run |
Run a contract against a server |
mcpact watch |
Re-run the contract on file changes |
mcpact snapshot |
Write the server's tool surface to a JSON file |
mcpact diff |
Compare a live server against a snapshot |
mcpact validate |
Validate a contract file without running it |
Reporters for run: console (default), json, junit, html. Output goes
to --out when provided, otherwise to stdout.
For a realistic example, see
packages/core/examples/notes_server.py
and the matching contract contracts/notes.yaml — a
tiny notes MCP server exercising list, read, search, and create tools.
GitHub Actions
- run: pip install mcpact
- run: mcpact run --contract contracts/my-server.yaml --reporter junit --out results.xml
- uses: mikepenz/action-junit-report@v4
if: always()
with:
report_paths: results.xml
Python API
import asyncio
from mcpact import build_client, load_contract, run_contract
async def main() -> None:
contract = load_contract("contracts/my-server.yaml")
client = build_client(contract.server)
await client.connect()
try:
report = await run_contract(contract, client)
finally:
await client.close()
print(f"{report.passed}/{report.total} passed")
asyncio.run(main())
Contract reference
server:
transport: stdio | http | sse
command: <cmd> # stdio only
args: [<arg>, ...] # stdio only
env: { KEY: value } # stdio only
url: <url> # http/sse only
tools:
- name: <tool>
must_exist: true # default
description_contains: <str> | [<str>, ...]
input_schema:
required: [<field>, ...]
properties:
<field>: { type: string|integer|... }
assertions:
- call:
args: { ... }
timeout_ms: <int>
expect:
status: success | error # default: success
response_contains: <str> | [<str>, ...]
max_latency_ms: <int>
schema: { ... } # JSON Schema for the response
Status
Pre-1.0. Both packages are functional and tested against a real MCP server. API may still change before 1.0.
Repository layout
packages/core/ Python engine + CLI
packages/sdk-node/ TypeScript SDK
contracts/ Example contracts
Contributing
See CONTRIBUTING.md. Issues and PRs welcome — please open an issue before starting non-trivial work. Security issues: see SECURITY.md.
License
Project details
Release history Release notifications | RSS feed
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 mcpact-0.1.0.tar.gz.
File metadata
- Download URL: mcpact-0.1.0.tar.gz
- Upload date:
- Size: 18.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a0ababb1d1f31a38e3f87e9d78f489724834428da6f2af2ed89c2af018ba5eab
|
|
| MD5 |
95d5a3b38ec7c07bf2d370ee26bf64de
|
|
| BLAKE2b-256 |
da2894fdbeb337f0d0c7193668c128e9de349178de62cab8763d9cbc6590be0e
|
Provenance
The following attestation bundles were made for mcpact-0.1.0.tar.gz:
Publisher:
publish-python.yml on jacquesbagui/mcpact
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
mcpact-0.1.0.tar.gz -
Subject digest:
a0ababb1d1f31a38e3f87e9d78f489724834428da6f2af2ed89c2af018ba5eab - Sigstore transparency entry: 1310621123
- Sigstore integration time:
-
Permalink:
jacquesbagui/mcpact@b405c9dfe4566c9837b928843c99e8f88b996fc7 -
Branch / Tag:
refs/tags/core-v0.1.0 - Owner: https://github.com/jacquesbagui
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish-python.yml@b405c9dfe4566c9837b928843c99e8f88b996fc7 -
Trigger Event:
push
-
Statement type:
File details
Details for the file mcpact-0.1.0-py3-none-any.whl.
File metadata
- Download URL: mcpact-0.1.0-py3-none-any.whl
- Upload date:
- Size: 22.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3d8565bb9cd94662538a34e166e58036c4a318db73385c54d978ef8735785f2c
|
|
| MD5 |
9ee84fdc0ea843ad89f3d0ea7a096222
|
|
| BLAKE2b-256 |
7a2fbf750c42c4e4c3a6dab36a3a61d686867e2dcf33d167c5d04b236a0ff750
|
Provenance
The following attestation bundles were made for mcpact-0.1.0-py3-none-any.whl:
Publisher:
publish-python.yml on jacquesbagui/mcpact
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
mcpact-0.1.0-py3-none-any.whl -
Subject digest:
3d8565bb9cd94662538a34e166e58036c4a318db73385c54d978ef8735785f2c - Sigstore transparency entry: 1310621237
- Sigstore integration time:
-
Permalink:
jacquesbagui/mcpact@b405c9dfe4566c9837b928843c99e8f88b996fc7 -
Branch / Tag:
refs/tags/core-v0.1.0 - Owner: https://github.com/jacquesbagui
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish-python.yml@b405c9dfe4566c9837b928843c99e8f88b996fc7 -
Trigger Event:
push
-
Statement type: