Skip to main content

pytest-mcp-contract

Discussions

Pytest helpers for domain MCP tool contracts: registered names, annotations, input schemas, and in-memory handler calls.

This is not protocol conformance. It does not wrap npx @modelcontextprotocol/conformance, does not ship security payloads, and does not run an LLM in CI.

Install

pip install pytest-mcp-contract
# or pinned tag:
pip install git+https://github.com/gmhoward9289-ops/pytest-mcp-contract@v0.1.6

For in-memory registry access against the MCP Python SDK:

pip install "pytest-mcp-contract[mcp]"

The mcp extra pins the MCP Python SDK the same way swamp-ops does today: mcp[cli]>=2.0.0 (SDK 2.x MCPServer).

Develop from a git checkout:

pip install -e ".[mcp]"

Usage

Override the mcp_server fixture with your server object. The plugin lists names from the in-memory registry (no stdio subprocess).

import pytest
from mcp_contract.assert_mcp import assert_tools_named
from mcp_contract.session import list_tool_names

@pytest.fixture
def mcp_server():
    from swamp_ops.server import server
    return server

def test_tool_names(mcp_server):
    assert_tools_named(list_tool_names(mcp_server), {"swamp_estate_status", ...})

list_tool_names reads MCPServer._tool_manager._tools (or tool_manager._tools). That is the same private registry swamp-ops already inspects. If the shape is unknown, the helper fails instead of skipping — registry drift is what this plugin is for.

Pin write tools and their safety annotations:

from mcp_contract.assert_mcp import (
    assert_non_readonly_tools_non_destructive,
    assert_tools_prefixed,
)
from mcp_contract.session import list_tool_names, tool_registry

def test_write_tools(mcp_server):
    names = list_tool_names(mcp_server)
    assert_tools_prefixed(names, "swamp_")
    assert_non_readonly_tools_non_destructive(
        tool_registry(mcp_server),
        {"swamp_enqueue_job", "swamp_post_discussion", ...},
    )

Calls go through the registered Tool.fn handler, not mcp.Client. v1 is in-memory only. Async handlers are supported: call_registered_tool uses asyncio.run; use acall_registered_tool inside async tests.

Input schema snapshots

Pin tool input JSON schemas and fail CI on accidental drift:

import json
from pathlib import Path
from mcp_contract.schemas import assert_tool_input_schemas_match

@pytest.fixture
def mcp_server():
    from swamp_ops.server import server
    return server

def test_schemas(mcp_server):
    expected = json.loads(Path("tests/fixtures/mcp_schemas.json").read_text())
    assert_tool_input_schemas_match(mcp_server, expected, tools=set(expected))

Refresh a snapshot from a live server module:

python -m mcp_contract snapshot tests/fixtures/mcp_schemas.json --module swamp_ops.server

On drift, failures show dotted field paths (properties.repo.type) and a unified diff — not two full JSON blobs.

Output schema snapshots

Pin structured handler output JSON schemas the same way (MCP SDK Tool.output_schema):

from mcp_contract.schemas import assert_tool_output_schemas_match

def test_output_schemas(mcp_server):
    expected = json.loads(Path("tests/fixtures/mcp_output_schemas.json").read_text())
    assert_tool_output_schemas_match(mcp_server, expected, tools=set(expected))

Refresh:

python -m mcp_contract snapshot tests/fixtures/mcp_output_schemas.json --output --module swamp_ops.server

Publish health

After each tag release, CI runs packaging/publish-doctor.sh (also daily) to verify PyPI serves the same version as src/mcp_contract/__init__.py. Local check:

bash packaging/publish-doctor.sh

Proof stack (pair with pytest-session-trace)

Plugin Asserts
pytest-mcp-contract (this repo) MCP server registers the right tool names, annotations, input schemas, and handlers
pytest-session-trace A saved agent session (JSONL) actually called those tools in order

Use both in the same repo: registry correctness and agent behavior — still no LLM in CI. swamp-ops dogfoods the pair in test_mcp_contract.py, test_session_trace.py, and docs/SESSION_TRACE.md.

Public starter (no private ops repo): examples/proof_stack/ — synthetic JSONL, pinned schemas, and both test files in one folder.

See also

Those cover protocol and transport. This plugin asserts your tool set.

License

Apache-2.0

Release files for pytest-mcp-contract 0.1.6

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for pytest-mcp-contract 0.1.6
File Size Uploaded
pytest_mcp_contract-0.1.6.tar.gz 20.0 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for pytest-mcp-contract 0.1.6
File Interpreter ABI Platform
pytest_mcp_contract-0.1.6-py3-none-any.whl Python 3 none any Details

Total release size: 37.0 kB

Release files / pytest_mcp_contract-0.1.6.tar.gz

Download URL pytest_mcp_contract-0.1.6.tar.gz
Size 20.0 kB
Tags Source
SHA-256 checksum
How to use checksums
0960a9ce490f9968f4fde89cd8fe438663e0dc8ed81f502e0a2e663977810f78
BLAKE2b-256 checksum
How to use checksums
77a5445f3dec1628f854c8ffb411f57f44b60e47ed44b2ce1b0e4903c7e487f0
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 20, 2026.

Transparency log

Release files / pytest_mcp_contract-0.1.6-py3-none-any.whl

Download URL pytest_mcp_contract-0.1.6-py3-none-any.whl
Size 17.0 kB
Tags Python 3
SHA-256 checksum
How to use checksums
40af139d7fb7bfa88d6bb456e02aafabc6d91e899eaec6277f151d2a5e459f5f
BLAKE2b-256 checksum
How to use checksums
3ea0b1282d96396c0cdb4aedbeb523afb4a6813f96ccd956e991e014d15835fe
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 20, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.1.6 This release

2 release files

0.1.5

2 release files

0.1.4

2 release files

0.1.3

2 release files

0.1.2

2 release files

0.1.1

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page