Skip to main content

pytest-mcptest

Test your MCP servers like you test everything else.

A pytest plugin for Model Context Protocol servers. It starts your server, connects a client, and gives you fixtures and assertions for tools, resources, and prompts. It works in-process, over stdio, or over Streamable HTTP. It also tests servers written in other languages, such as Node.js or Go, as long as they speak MCP over stdio or HTTP.

pip install pytest-mcptest

The import name is pytest_mcp. Requires Python 3.10+ and the official mcp SDK 2.x.

Quick start

# test_server.py
import pytest
from pytest_mcp import assert_tool_call, assert_tool_error

from my_server import mcp  # your MCPServer instance

pytestmark = pytest.mark.mcp_server(mcp)


async def test_add(mcp_client):
    await assert_tool_call(mcp_client, "add", {"a": 2, "b": 3}, expected=5)


async def test_divide_by_zero_fails_cleanly(mcp_client):
    await assert_tool_error(mcp_client, "divide", {"a": 1, "b": 0}, match="zero")
pytest

You don't need @pytest.mark.anyio or pytest-asyncio. Any async test that uses mcp_client runs automatically.

Test any server with zero code

pytest --pyargs pytest_mcp.suite --mcp-server "python server.py"
pytest --pyargs pytest_mcp.suite --mcp-server "npx -y @modelcontextprotocol/server-filesystem /tmp"
pytest --pyargs pytest_mcp.suite --mcp-server http://localhost:8000/mcp

The built-in suite checks these things:

  • The handshake completes.
  • Tool names follow SEP-986 and are unique.
  • Every tool is described.
  • inputSchema and outputSchema are valid JSON Schema.
  • Every listed resource is readable.
  • Prompt names are unique.

Has your server migrated to the 2026-07-28 spec?

pytest --pyargs pytest_mcp.suite --mcp-server "python server.py" --mcp-spec-version 2026-07-28

The suite fails if the server negotiates an older protocol version. For example, the official filesystem server still speaks 2025-11-25.

Pointing at your server

Pick one. They are listed from most specific to least specific.

Where Example
Marker on a test, class, or module @pytest.mark.mcp_server(mcp)
Fixture override in conftest.py def mcp_server(): return stdio("python", "server.py")
Command line pytest --mcp-server "python server.py"
Ini file mcp_server = python server.py under [pytest]

A target can be any of these:

Target Transport
An MCPServer or low-level Server instance In-process (fastest, no subprocess)
stdio("python", "server.py", env={...}, cwd=...) or "python server.py" stdio subprocess
"http://localhost:8000/mcp" Streamable HTTP, already running
http("http://127.0.0.1:8000/mcp", "python server.py --port 8000") Streamable HTTP. pytest-mcptest starts the command, waits for the port, and stops the server afterwards

Per-test setup, such as a fresh temp directory for each test:

@pytest.fixture
def mcp_server(tmp_path):
    return stdio("npx", "-y", "@modelcontextprotocol/server-filesystem", str(tmp_path))

Assertions

Each assertion is async, takes mcp_client first, and returns what it fetched, so you can keep asserting on the result.

Assertion Passes when
assert_tool_exists(c, name) The tool is listed. Returns the Tool.
assert_tool_call(c, name, args, expected=..., contains=..., match=..., check=...) The call succeeds and the result matches. expected compares structured output when present, otherwise text.
assert_tool_error(c, name, args, match=...) The call fails cleanly: isError with an explanation, or a JSON-RPC error.
assert_resource_exists(c, uri) The resource is listed.
assert_resource_readable(c, uri, contains=..., mime_type=...) The read returns content.
assert_prompt_exists(c, name) The prompt is listed.
assert_prompt_renders(c, name, args, contains=...) The prompt renders at least one message.
assert_conformance(c, spec_version=..., strict=...) No conformance errors. With strict=True, warnings fail too.

Failure messages show what the server actually returned:

AssertionError: tool 'add' returned an unexpected value
  expected: 6
  is_error: False
  text: '5'
  structured_content: {'result': 5}

mcp_client also exposes every mcp.Client method (call_tool, read_resource, get_prompt, and so on), plus call(name, **kwargs), tool_names(), and list_all_tools(), list_all_resources(), and list_all_prompts(), which follow pagination. Use result_text(result) to get the text of any result.

Fixtures and options

Fixture What it is
mcp_client A connected MCPTestClient, with a fresh connection for each test
mcp_server The target. Override it to configure your server
mcp_client_kwargs Extra mcp.Client arguments, such as sampling_callback, elicitation_callback, or mode="legacy"
mcp_timeout Timeout in seconds for each request. Defaults to 30
mcp_spec_version The value of --mcp-spec-version, or None
Option / ini key Meaning
--mcp-server / mcp_server The default target
--mcp-timeout / mcp_timeout The timeout for each request, including the handshake
--mcp-spec-version / mcp_spec_version The protocol version that the conformance checks require

Outside pytest, async with pytest_mcp.connect(target) as client: gives you the same client.

GitHub Actions

name: test
on: [push, pull_request]
jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5
        with: { python-version: "3.13" }
      - run: pip install -e . pytest-mcptest
      - run: pytest
      - run: pytest --pyargs pytest_mcp.suite --mcp-server "python server.py"

Tips

  • With MCPServer, raise ToolError (from mcp.server.mcpserver.exceptions) for expected failures. The client only sees messages from ToolError. Any other exception is masked as "Error executing tool <name>", and assert_tool_error(..., match=...) makes that easy to catch.
  • Use in-process targets for speed and stdio targets for realism. Run both if you can.

Roadmap

  • v0.1 (this release): fixtures, assertions, stdio, HTTP, and in-process transports, plus the conformance suite.
  • v0.2: mcp-mock, which adds record/replay and mock MCP servers for testing agents.
  • Later: session-scoped clients, tool-description poisoning checks, and Windows CI.

Contributing

python -m venv .venv && source .venv/bin/activate
pip install -e ".[dev]"
pytest && ruff check . && ruff format --check .

Releasing

  1. Bump version in pyproject.toml (and __version__ in pytest_mcp/__init__.py), then merge to main.
  2. Run git tag v0.1.1 && git push origin v0.1.1.

The Release workflow checks the tag matches the version, runs the tests, builds, and publishes to PyPI with trusted publishing.

MIT licensed.

Metadata

Release files for pytest-mcptest 0.1.0

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-mcptest 0.1.0
File Size Uploaded Provenance
pytest_mcptest-0.1.0.tar.gz 17.9 kB With provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for pytest-mcptest 0.1.0
File Interpreter ABI Platform
pytest_mcptest-0.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 34.3 kB

Release files / pytest_mcptest-0.1.0.tar.gz

Download URL pytest_mcptest-0.1.0.tar.gz
Size 17.9 kB
Tags Source
SHA-256 checksum
How to use checksums
7ab707bb37305c8a0f6028af6bb92685b548ce74e72cf0f46260adb08a73b8ac
BLAKE2b-256 checksum
How to use checksums
f031471df89258e5baca3ba991ebeb1ca93e1a191c758996d282bf30108b164e
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

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 Oct 20, 2026.

Transparency log

Release files / pytest_mcptest-0.1.0-py3-none-any.whl

Download URL pytest_mcptest-0.1.0-py3-none-any.whl
Size 16.5 kB
Tags Python 3
SHA-256 checksum
How to use checksums
290662640e19c64f8a2e15ef4ce7217c160b287a3d28e2a49b8926baefec6e11
BLAKE2b-256 checksum
How to use checksums
8b04031ae0ca7ce8bc51e8d901f0391c1fd8b41f79c24f3ada9efbb4333a076d
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

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 Oct 20, 2026.

Transparency log

Security for pytest-mcptest 0.1.0

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.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for pytest_mcptest-0.1.0.tar.gz

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 Oct 20, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files Full provenance

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