Skip to main content

pytest for MCP servers — the testing framework for the Model Context Protocol

Project description

pytest-mcp-plugin

pytest for MCP servers — the testing framework for the Model Context Protocol.

PyPI Python License: MIT

pip install pytest-mcp-plugin
mcp-test demo            # runs a bundled MCP server + tests in 5 seconds

Note on the name. This package is published on PyPI as pytest-mcp-plugin. The mcp-test name on PyPI is reserved by Anthropic for their official MCP SDK, and PyPI's name-similarity policy blocks close variants like mcptest. The CLI binary is still mcp-test, and the Python module is still mcp_test.


Why

MCP (Model Context Protocol) is the standard interface every AI agent now uses to talk to tools, files, databases, and APIs. Thousands of teams are shipping MCP servers in 2026. Almost none of them are tested.

pytest-mcp-plugin fixes that. Write tests for your MCP tools, resources, and prompts with the same developer experience you expect from pytest.

  • pytest plugin with auto-registered fixtures
  • stdio and HTTP/SSE transports
  • Schema validation, snapshot testing, coverage reports
  • Auth + policy assertions
  • Drop-in GitHub Action

60-second tour

pip install pytest-mcp-plugin
mcp-test demo

That spins up a bundled MCP server and runs a real test suite against it — no setup, no API keys, no servers to write first.

🚀 mcp-test demo
   server  : python -m mcp_test._demo_server
   workdir : /tmp/mcptest-demo-xxxx

test_demo.py::test_lists_tools         PASSED
test_demo.py::test_echo                PASSED
test_demo.py::test_add                 PASSED
test_demo.py::test_uppercase           PASSED
test_demo.py::test_fail_returns_error  PASSED

✅ Demo passed. Now write tests for your own MCP server:
   mcp-test init

Test your own server

cd my-mcp-server/
mcp-test init
pytest --mcp-command "python my_server.py" -v

Or pin the command in your pyproject.toml:

[tool.mcp-test]
command = "python my_server.py"
timeout = 10

[tool.mcp-test.timeouts]
"tools/list" = 5
"tools/call" = 30
"sampling/createMessage" = 60

…and just run:

mcp-test run

Write tests

# tests/test_my_server.py
from mcp_test import assert_tool_ok, assert_tool_error, assert_tool_text_contains


def test_search_returns_results(mcp_client):
    result = mcp_client.call_tool("search", query="machine learning")
    assert_tool_ok(result)
    assert len(result.content) > 0


def test_search_handles_empty_query(mcp_client):
    result = mcp_client.call_tool("search", query="")
    assert_tool_error(result)


def test_search_schema(mcp_client):
    tools = mcp_client.list_tools()
    search = tools.find("search")
    assert search.required == ["query"]
    assert search.properties["query"]["type"] == "string"

Use as a library

from mcp_test import MCPTestClient

with MCPTestClient.from_command("python my_server.py") as client:
    tools = client.list_tools()
    print(tools.names())

    result = client.call_tool("echo", message="hello")
    print(result.text())

Run on every PR (GitHub Action)

pytest-mcp-plugin ships as a composite action you can drop into any repo:

# .github/workflows/mcp-tests.yml
name: MCP Tests

on:
  pull_request:
  push:
    branches: [main]

jobs:
  mcp-test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: yagna-1/mcp-test@v0.2.0
        with:
          command: "python my_server.py"
          test-dir: "tests"

That's it. The action installs pytest-mcp-plugin, runs your suite against your MCP server, and posts a JUnit XML report.


CLI reference

Command Description
mcp-test demo Run the bundled demo server + tests (zero setup)
mcp-test init Scaffold tests/ with example MCP tests
mcp-test run -c "python server.py" Run pytest against your server
mcp-test snapshot -c "..." Run snapshot tests (--update to refresh)
mcp-test coverage -c "..." Print coverage report (tools/prompts/resources)
mcp-test validate -c "..." Validate tool input schemas
mcp-test conformance --url "..." Run the upstream MCP conformance suite through npx
mcp-test bench -c "..." Run lightweight p50/p95/p99 regression probes

All commands accept --help for full options.


Fixtures

The pytest plugin auto-registers three fixtures:

Fixture Scope Use for
mcp_client session Fast — one server process for the whole test run
mcp_client_fresh function Clean state per test
sandboxed_client function Fresh server with cwd=tmp_path and DATA_DIR=tmp_path
snapshot function Snapshot testing helper
pytest --mcp-command "python my_server.py" --mcp-timeout 15 \
  --mcp-timeout-method tools/call=30 \
  --mcp-trace .mcp-test/trace.jsonl

--mcp-timeout-method METHOD=SECONDS may be passed multiple times, and the same values can live in [tool.mcp-test.timeouts]. --mcp-trace records JSONL wire frames; in CI, failing tests also dump recent frames to mcp-traces/.

Use mcp-test conformance --url ... --pytest-items when you want each parsed upstream scenario re-emitted as a normal pytest test item.

Spec-version markers

Mark tests by required MCP spec version; the plugin auto-skips tests against older servers.

import pytest

@pytest.mark.mcp_v3  # requires spec >= 2025-06-18
def test_uses_recent_feature(mcp_client):
    ...

Available markers: mcp_v2, mcp_v3, mcp_v4.

Assertion helpers

from mcp_test import (
    assert_tool_ok,
    assert_tool_error,
    assert_tool_error_code,
    assert_tool_text_contains,
    assert_tool_text_equals,
    assert_tool_content_count,
    assert_policy_allows,
    assert_policy_blocks,
    assert_task_completes_within,
    assert_task_cancelled,
    assert_task_failed,
)

Architecture

pytest-mcp-plugin runs your MCP server as a subprocess and speaks JSON-RPC 2.0 over stdio (or HTTP/SSE for the HTTP transport). A background message pump handles response routing, notification dispatching, and concurrent request support — so your tests just work.

Source modules:

mcp_test/
  client.py            # stdio JSON-RPC client
  http_client.py       # HTTP + SSE client
  plugin.py            # pytest plugin (fixtures, options, markers)
  cli.py               # mcp-test CLI
  assertions.py        # assert_tool_*, assert_policy_*, assert_task_*
  schema_validator.py  # JSON Schema validation for tool inputs
  coverage.py          # tools/prompts/resources coverage tracker
  snapshot.py          # snapshot testing
  auth.py              # OAuth / PKCE / RFC 9728 helpers
  bench.py             # lightweight regression benchmark probes
  conformance.py       # bridge to @modelcontextprotocol/conformance
  compliance.py        # conformance score helpers
  fastmcp.py           # in-process FastMCP harness adapter
  replay.py            # deterministic wire-trace replay lookup
  pagination.py        # cursor pagination helpers
  test_packs.py        # reusable server-shape test packs
  timeouts.py          # per-method timeout policy
  wire_trace.py        # JSONL wire trace recorder
  types.py             # ToolResult, ToolSchema, MCPError, ...
  _demo_server.py      # bundled demo server (used by `mcp-test demo`)

Status

pytest-mcp-plugin is beta. The CLI surface and plugin API are stable; minor internals (schema validator details, snapshot format) may still change.

Roadmap

See ROADMAP.md for what's next, what we explicitly won't build, and how we plan to complement (not compete with) Anthropic's official @modelcontextprotocol/conformance and @modelcontextprotocol/inspector.

Contributing

Bug reports, feature requests, and PRs welcome at github.com/yagna-1/mcp-test.

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

pytest_mcp_plugin-0.2.2.tar.gz (69.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

pytest_mcp_plugin-0.2.2-py3-none-any.whl (54.4 kB view details)

Uploaded Python 3

File details

Details for the file pytest_mcp_plugin-0.2.2.tar.gz.

File metadata

  • Download URL: pytest_mcp_plugin-0.2.2.tar.gz
  • Upload date:
  • Size: 69.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.10.20

File hashes

Hashes for pytest_mcp_plugin-0.2.2.tar.gz
Algorithm Hash digest
SHA256 8c42aecd05c325b395ab3b003356539affbaec8138c14d3fd05257c1d37245d8
MD5 496fd372137bb05276927e4531fdd87b
BLAKE2b-256 ad3391c20fdad3db7e7526195357f980dbd9c30db9c23801b92ad421183e3636

See more details on using hashes here.

File details

Details for the file pytest_mcp_plugin-0.2.2-py3-none-any.whl.

File metadata

File hashes

Hashes for pytest_mcp_plugin-0.2.2-py3-none-any.whl
Algorithm Hash digest
SHA256 58c53a7bb5b435d285518222ca1759b8cf12ba38bc27da2eb45a25a13f903f02
MD5 ec22e7e7fa92b8e614c8ff7a3e30c266
BLAKE2b-256 57d38175489662e6f34402f234b5ac5dc07192c99b53fcd90786bdf366a743ce

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page