Skip to main content

🔨 mcp-forge

pytest for MCP servers — test any server, no LLM required.

PyPI Python CI License: MIT

mcp-forge is a testing framework for Model Context Protocol (MCP) servers. It works with any MCP server — Python, TypeScript, Go, Rust — and requires zero LLM calls. Tests are deterministic, fast, and free to run in CI/CD.

Why?

Most MCP server testing today looks like this: spin up Claude Desktop, type a prompt, squint at the output, and hope it called the right tool. That's vibe-testing.

mcp-forge gives you real tests:

import pytest
from mcp_forge import MCPTestClient

@pytest.fixture
async def client():
    async with MCPTestClient.from_command("python my_server.py") as c:
        yield c

async def test_tool_exists(client):
    tools = await client.list_tools()
    assert "search_files" in [t.name for t in tools]

async def test_search_works(client):
    result = await client.call_tool("search_files", {"query": "README"})
    assert result.is_success
    assert "README.md" in result.text

async def test_search_snapshot(client, forge_snapshot):
    result = await client.call_tool("search_files", {"query": "README"})
    forge_snapshot.assert_match(result)

Installation

pip install forge-mcp

With FastMCP in-memory testing support:

pip install "forge-mcp[fastmcp]"

Quick Start

1. Write tests directly

from mcp_forge import MCPTestClient

async def test_my_server():
    async with MCPTestClient.from_command("python my_server.py") as client:
        # Check tools are exposed
        tools = await client.list_tools()
        assert len(tools) > 0

        # Call a tool
        result = await client.call_tool("greet", {"name": "World"})
        assert result.is_success
        assert "Hello" in result.text

2. Auto-generate tests from a running server

forge init "python my_server.py" -o test_my_server.py

This connects to your server, discovers all tools, and generates a complete test file with example calls for each tool.

3. Record fixtures for snapshot testing

forge record "python my_server.py" -o fixtures/

4. Validate schemas

forge schema "python my_server.py"

Connecting to Servers

STDIO (local servers — most common)

# Python server
async with MCPTestClient.from_command("python my_server.py") as client:
    ...

# TypeScript server
async with MCPTestClient.from_command("npx my-mcp-server") as client:
    ...

# uvx-installed server
async with MCPTestClient.from_command("uvx my-mcp-package") as client:
    ...

# With environment variables
async with MCPTestClient.from_command(
    "python my_server.py",
    env={"API_KEY": "test-key", "DATABASE_URL": "sqlite:///test.db"},
) as client:
    ...

FastMCP in-memory (zero overhead)

from my_server import mcp  # your FastMCP instance

async with MCPTestClient.from_fastmcp(mcp) as client:
    ...

Features

Tool Testing

async def test_tool_call(client):
    result = await client.call_tool("calculate", {"expression": "2 + 2"})
    assert result.is_success
    assert result.text == "4"

async def test_tool_error_handling(client):
    result = await client.call_tool("calculate", {"expression": ""})
    assert result.is_error

async def test_tool_timeout(client):
    # Tool calls have configurable timeouts
    result = await client.call_tool("slow_operation", timeout=5.0)

Snapshot Testing

async def test_snapshot(client, forge_snapshot):
    result = await client.call_tool("get_user", {"id": "123"})

    # First run: saves snapshot to __snapshots__/
    # Next runs: compares against saved snapshot
    forge_snapshot.assert_match(result)

    # Ignore volatile fields
    forge_snapshot.assert_match(result, ignore_keys=["timestamp", "request_id"])

async def test_schema_snapshot(client, forge_snapshot):
    tools = await client.list_tools()
    # Detect when tool schemas change unexpectedly
    forge_snapshot.assert_schema_unchanged(tools)

Update snapshots:

pytest --update-snapshots

Schema Validation

from mcp_forge import assert_tool_schema, assert_result_matches_schema

async def test_tool_has_correct_schema(client):
    tools = await client.list_tools()
    search_tool = next(t for t in tools if t.name == "search")

    assert_tool_schema(search_tool, {
        "type": "object",
        "required": ["query"],
        "properties": {
            "query": {"type": "string"},
            "limit": {"type": "integer"},
        },
    })

Resource Testing

async def test_resources(client):
    resources = await client.list_resources()
    assert len(resources) > 0

    content = await client.read_resource("file:///README.md")
    assert content.is_success

Prompt Testing

async def test_prompts(client):
    prompts = await client.list_prompts()
    assert "summarize" in [p.name for p in prompts]

    prompt = await client.get_prompt("summarize", {"text": "Hello world"})
    assert prompt.is_success

CLI Reference

forge init <command>       Generate test file from a running server
forge record <command>     Record tool responses as test fixtures
forge schema <command>     Dump tool schemas (JSON/YAML/table)
forge check <command>      Run built-in health checks

Pytest Fixtures

mcp-forge auto-registers these fixtures via the pytest plugin:

Fixture Description
forge_snapshot Snapshot manager for the current test
forge_server Factory for creating MCPTestClient instances

Comparison with Existing Tools

Feature mcp-forge FastMCP testing pytest-mcp mcp-validator
Any MCP server ✅ ❌ FastMCP only ✅ ✅
No LLM required ✅ ✅ ❌ Partial
pytest-native ✅ Manual ✅ ❌ CLI only
Snapshot testing ✅ ❌ ❌ ❌
Auto-gen fixtures ✅ ❌ ❌ ❌
Schema validation ✅ ❌ ❌ ✅

Roadmap

  • v0.1 (current): pytest plugin, STDIO transport, snapshots, schema validation
  • v0.2: Fuzz testing, contract testing, HTTP/SSE transport, GitHub Action
  • v0.3: VSCode extension, performance testing, coverage reports

Contributing

Contributions welcome! See CONTRIBUTING.md for guidelines.

License

MIT

Metadata

Release files for forge-mcp 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 forge-mcp 0.1.0
File Size Uploaded Provenance
forge_mcp-0.1.0.tar.gz 28.1 kB Without provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for forge-mcp 0.1.0
File Interpreter ABI Platform
forge_mcp-0.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 54.7 kB

Release files / forge_mcp-0.1.0.tar.gz

Download URL forge_mcp-0.1.0.tar.gz
Size 28.1 kB
Tags Source
SHA-256 checksum
How to use checksums
ba964ebfbcd500068aab6a0f8de3bd52056503722a367486b0b4db1304a54842
BLAKE2b-256 checksum
How to use checksums
76ea3dfe4a423d597def117466c45a70f7a149e0cc1e42e59a676821c79b0b3d
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.13.12

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

Download URL forge_mcp-0.1.0-py3-none-any.whl
Size 26.7 kB
Tags Python 3
SHA-256 checksum
How to use checksums
e0bcaf2c45980133a8b3cb237dd81d1c2d4e04e8e6f81f104ba4b721fbc15152
BLAKE2b-256 checksum
How to use checksums
6924c972811f271f2995b421bcf68b69871b826f433475796fe815dd33363b60
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.13.12

Security for forge-mcp 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.

No provenance found

None of the files in this release were uploaded with provenance metadata.

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 No 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