Skip to main content

toolproof

PyPI Python CI Docs License: MIT

Automated tests for Model Context Protocol servers. Check tool definitions, write YAML or pytest tests, fuzz every tool from its schema, benchmark latency, check that models pick the right tools, and fail the build when something breaks.

pip install mcp-toolproof
toolproof fuzz -- python my_server.py

Documentation · Getting started · Configuration · CLI · Changelog

Features

  • Static checks: every tool needs a name, a description, a valid JSON Schema and required fields that exist
  • YAML tests: assert on errors, text, regex, JSONPath values, latency and the tool's own outputSchema
  • Fuzzing: valid and invalid inputs generated from each tool's schema with Hypothesis; crashes, hangs, internal errors and leaked tracebacks, each shrunk to the smallest failing input
  • Benchmarks: p50/p95/p99 latency, throughput and error rate under concurrency, with thresholds
  • Tool-selection evals: give a model your tool list and check it picks the right tool and arguments for real requests; Anthropic, OpenAI-compatible or your own program, with cached answers
  • pytest plugin: an mcp_server fixture for tests written in Python
  • Built for CI: a GitHub Action, exit codes, JUnit, JSON and single-file HTML reports, a job summary, repeatable fuzz seeds, ${VAR} secrets
  • Any server: stdio or streamable HTTP, written in Python, TypeScript, Go or anything else; crashed servers are restarted and their stderr is shown

Quick start

See what a server offers and whether its tool definitions are sound:

toolproof inspect -- python my_server.py
toolproof inspect --url http://localhost:8000/mcp

Write tests in toolproof.yaml:

server:
  command: ["python", "my_server.py"]

tests:
  - name: toronto weather
    tool: get_weather
    args: { city: Toronto }
    expect:
      is_error: false
      jsonpath: { "$.temp_c": { type: number, min: -60, max: 60 } }
      max_latency_ms: 2000

  - name: rejects empty city
    tool: get_weather
    args: { city: "" }
    expect: { is_error: true }

bench:
  thresholds: { p95_ms: 200 }

fuzz:
  seed: 1
toolproof run toolproof.yaml --junit report.xml

Check that a model picks the right tool (pip install "mcp-toolproof[anthropic]" or [openai]):

evals:
  provider: anthropic
  model: your-model-name
  cases:
    - prompt: Is it raining in Montreal right now?
      expect_tool: get_weather
      expect_args: { city: Montreal }
toolproof eval -c toolproof.yaml

In GitHub Actions:

- uses: actions/setup-python@v7
  with:
    python-version: "3.12"
- run: pip install -e .              # your server and its dependencies
- uses: NilkanthSuthar/toolproof@v0.3.0
  with:
    config: toolproof.yaml
    python-version: ""               # run in the environment set up above

Or in Python, with the pytest plugin:

def test_weather(mcp_server):
    r = mcp_server.call("get_weather", city="Toronto")
    assert not r.is_error
    assert r.data["temp_c"] > -60

Fuzzing finds bugs without writing tests

examples/buggy_server.py has five planted bugs. toolproof fuzz finds all of them on its own:

$ toolproof fuzz --seed 3 -- python examples/buggy_server.py

Fuzz (seed 3)
┌──────┬─────────────────┬───────┬────────────────────────────┬─────────────────┬─────────────────────────────────────────────┐
│      │ Tool            │ Calls │ Finding                    │ Smallest input  │ Details                                     │
├──────┼─────────────────┼───────┼────────────────────────────┼─────────────────┼─────────────────────────────────────────────┤
│ FAIL │ get_weather     │    21 │ crash (valid)              │ {"city": ""}    │ connection lost, server probably crashed    │
│      │                 │       │                            │                 │ (Connection closed)                         │
│      │                 │       │ accepted-invalid (invalid) │ {"city": []}    │ invalid input returned a success result     │
│      │                 │       │ crash (invalid)            │ {}              │ connection lost, server probably crashed    │
│      │                 │       │                            │                 │ (Connection closed)                         │
│ FAIL │ search_cities   │     0 │ bad-schema (valid)         │ {}              │ can't generate inputs: 'strng' is not valid │
│      │                 │       │                            │                 │ under any of the given schemas              │
│ FAIL │ slow_report     │     1 │ hang (valid)               │ {"city": ""}    │ timed out after 1.0s                        │
│ FAIL │ get_temperature │    24 │ output-schema (valid)      │ {"city": ""}    │ output schema: $.temp_c: '12.5' is not of   │
│      │                 │       │                            │                 │ type 'number'                               │
│      │                 │       │ accepted-invalid (invalid) │ {}              │ invalid input returned a success result     │
│ FAIL │ lookup          │    15 │ internal-error (valid)     │ {"city_id": {}} │ JSON-RPC error -32603: Internal server      │
│      │                 │       │                            │                 │ error                                       │
│      │                 │       │ internal-error (invalid)   │ {}              │ JSON-RPC error -32603: Internal server      │
│      │                 │       │                            │                 │ error                                       │
└──────┴─────────────────┴───────┴────────────────────────────┴─────────────────┴─────────────────────────────────────────────┘
Repeat this run with --seed 3

FAILED  |  fuzz: 9 findings in 5 tools  |  24.33s

Warning: fuzzing calls your tools for real, many times, with odd arguments. Point it at a test instance, and skip tools that send, delete or spend. See the fuzzing guide.

Tested on

toolproof is run against the official MCP reference servers: everything, filesystem, git, memory, sequential-thinking, time and fetch. In October 2026 that was about 3,000 fuzz calls, 15 security boundary tests and benchmarks. All of them held up; the reference servers validate their input well.

How it compares

MCP Inspector is for exploring a server by hand; toolproof is for tests that run unattended in CI. Other projects cover declarative YAML tests, snapshot regression gates, agent evaluations and security scanning. toolproof's focus is behaviour testing with schema-driven fuzzing and benchmarks, offline and without an LLM; only the optional evals use a model. See the comparison for details.

Requirements

  • Python 3.11 to 3.14 on Linux, macOS or Windows
  • The server can be written in any language

Contributing

Bug reports, fixes and new checks are welcome. See CONTRIBUTING.md for the development setup, and SECURITY.md to report a security problem privately.

License

MIT

Metadata

Release files for mcp-toolproof 0.3.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 mcp-toolproof 0.3.0
File Size Uploaded
mcp_toolproof-0.3.0.tar.gz 91.5 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for mcp-toolproof 0.3.0
File Interpreter ABI Platform
mcp_toolproof-0.3.0-py3-none-any.whl Python 3 none any Details

Total release size: 139.2 kB

Release files / mcp_toolproof-0.3.0.tar.gz

Download URL mcp_toolproof-0.3.0.tar.gz
Size 91.5 kB
Tags Source
SHA-256 checksum
How to use checksums
56041534de9c30468d151e0ed955dec33c65c459dae71030ea108b0e472269fc
BLAKE2b-256 checksum
How to use checksums
91fd99f4bb97761c24b523868dd820845e03963533eedbdc61928d0e1fd1ebb2
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 Oct 15, 2026.

Transparency log

Release files / mcp_toolproof-0.3.0-py3-none-any.whl

Download URL mcp_toolproof-0.3.0-py3-none-any.whl
Size 47.7 kB
Tags Python 3
SHA-256 checksum
How to use checksums
21407dbb3629b2b731a0538a45ea8f459c71dd446e19affc1e039fcd2ae8126b
BLAKE2b-256 checksum
How to use checksums
3709d318eeea32b8de26df30fa3d3c56ee559eef527df0708115a40b41d36129
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 Oct 15, 2026.

Transparency log

Release history Release notifications | RSS feed

0.3.1

2 release files

This release

0.3.0 This release

2 release files

0.2.0

2 release files

0.1.0

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