Skip to main content

mcp-evals

Behavioral evaluation and description linting for Model Context Protocol (MCP) servers.

PyPI License: MIT Python 3.11+ CI

Status: v0.7 — stdio + HTTP JSON-RPC + Streamable HTTP/SSE discovery, description lint (including name-description-mismatch + missing-examples), mock/OpenAI-compatible tool-selection evals, and GitHub Action PR comments.

60-second try

pip install mcp-tool-evals
mcp-evals health
mcp-evals run examples/eval-suite.yaml --catalog examples/tools.yaml --model mock
# or with Docker (no local Python):
docker compose run --rm run-example

Why this vs alternatives

Approach Strength Gap
mcp-evals Lint + YAML evals + mock CI runner Not a full agent harness
MCP Inspector Interactive debugging No CI lint/eval suite
Protocol conformance tests JSON-RPC correctness Do not test tool selection
Hand-written LLM mocks Full control Drift from real descriptions

Problem

Protocol conformance tests verify JSON-RPC correctness. They do not verify whether an LLM agent selects the right tool, passes sensible arguments, or completes the task. Tool descriptions are now load-bearing API design, and there is no standard way to test them.

mcp-evals fills the behavioral layer: lint tool descriptions, define eval cases in YAML, and (next) measure tool-selection accuracy in CI.

Key features (v0.7)

  • Description linter — missing descriptions, undocumented required params, overlapping tools, ambiguous verbs, name-description-mismatch, missing-examples
  • Live discover — handshake a stdio, HTTP JSON-RPC, or Streamable HTTP/SSE MCP server (or load a catalog fixture)
  • Eval runnermcp-evals run with --model mock (CI), --live SERVER.yaml, or an OpenAI-compatible endpoint
  • CLI + Docker + GitHub Action — lint and optional eval in CI without a cloud key (mock selector); PR comment or job summary with accuracy

Architecture

┌─────────────┐     ┌──────────────────┐     ┌─────────────────┐
│  CLI        │────▶│  Linter / Runner │────▶│  Models (Pydantic)│
│  mcp-evals  │     │  spec_loader     │     │  EvalSuite, etc.  │
└─────────────┘     └──────────────────┘     └─────────────────┘
                            │
                    (next) MCP client + LLM
Component Technology Why
Language Python 3.11+ MCP SDK ecosystem, pytest integration
CLI Click Mature, composable commands
Schemas Pydantic v2 Strict validation, good errors
Logging structlog Structured, JSON-capable
Config pydantic-settings Env-based, typed
Tests pytest + ruff + mypy Standard Python OSS stack

Installation

pip install mcp-tool-evals
# or from source:
pip install -e ".[dev]"

Local development

python -m venv .venv && source .venv/bin/activate
pip install -e ".[dev]"
mcp-evals health
mcp-evals lint examples/tools.yaml
mcp-evals discover examples/server.yaml
mcp-evals lint --live examples/server.yaml
mcp-evals validate-spec examples/eval-suite.yaml
mcp-evals run examples/eval-suite.yaml --catalog examples/tools.yaml --model mock
pytest tests/ -v

Docker

# Health check
docker compose run --rm dev

# Run tests
docker compose run --rm test

# Lint a live stdio server (this example passes)
docker compose run --rm lint-example

# Discover tools from the example calc server
docker compose run --rm discover-example

# Mock eval run (no API key)
docker compose run --rm run-example

Configuration

Copy .env.example to .env:

Variable Default Description
MCP_EVALS_LOG_LEVEL INFO Log level
MCP_EVALS_LOG_JSON false JSON log output
MCP_EVALS_DEFAULT_MODEL gpt-4o-mini Model for eval runs (future)
MCP_EVALS_PASS_THRESHOLD 0.8 Minimum selection accuracy

Usage

Lint tool descriptions

mcp-evals lint examples/tools.yaml
mcp-evals lint --live examples/server.yaml
mcp-evals discover examples/server.yaml
mcp-evals lint examples/tools.yaml --format json
mcp-evals lint examples/tools.yaml --fail-on-warning

Run behavioral evals

mcp-evals run examples/eval-suite.yaml --catalog examples/tools.yaml --model mock
mcp-evals run examples/eval-suite.yaml --catalog examples/tools.yaml \
  --model llama3.2 --base-url http://localhost:11434/v1 --pass-threshold 0.8
# or discover tools from a live server that matches the suite:
# mcp-evals run suite.yaml --live server.yaml --model mock

Validate eval suite

mcp-evals validate-spec examples/eval-suite.yaml

Health check

mcp-evals health

GitHub Action

On pull_request, the composite action posts (or updates) a comment with selection accuracy and a per-case table. On other events it writes the same markdown to $GITHUB_STEP_SUMMARY. Set comment: false to skip PR comments.

permissions:
  contents: read
  pull-requests: write
steps:
  - uses: actions/checkout@v4
  - uses: yashshah9/mcp-evals@v0.7
    with:
      catalog: examples/tools.yaml
      suite: examples/eval-suite.yaml
      model: mock
      threshold: "0.8"
      # comment: false  # optional; default true

Example eval suite

See examples/eval-suite.yaml:

name: document-server-behavior
cases:
  - id: search-by-keyword
    request: Find documents about quarterly revenue
    expected_tool:
      name: search_documents
      arguments:
        query: quarterly revenue

Running tests

pytest tests/ -v
ruff check src tests
mypy src

Development workflow

  1. Add linter rules in src/mcp_evals/linter/
  2. Add runner logic in src/mcp_evals/runner/
  3. Add tests in tests/
  4. Update examples in examples/

Roadmap

  • MCP client: stdio discover + lint --live
  • LLM eval runner (mock + OpenAI-compatible)
  • HTTP JSON-RPC MCP transport
  • name-description-mismatch linter rule
  • Streamable HTTP/SSE MCP transport
  • GitHub Action PR comments (accuracy + per-case table; deltas later)

Known limitations (v0.7)

  • Streamable HTTP/SSE discovery is initialize + tools/list only (no long-lived GET stream / resume)
  • Mock selector does not fill tool arguments (accuracy is tool-name only)
  • Live Ollama/OpenAI evals need a reachable --base-url; CI uses --model mock
  • examples/tools.yaml is intentionally dirty so mcp-evals lint can show findings

Contributing

See CONTRIBUTING.md.

License

MIT — see LICENSE.

Download files

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

Source Distribution

mcp_tool_evals-0.7.0.tar.gz (125.6 kB view details)

Uploaded Source

Built Distribution

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

mcp_tool_evals-0.7.0-py3-none-any.whl (23.7 kB view details)

Uploaded Python 3

File details

Details for the file mcp_tool_evals-0.7.0.tar.gz.

File metadata

  • Download URL: mcp_tool_evals-0.7.0.tar.gz
  • Upload date:
  • Size: 125.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.12.14

File hashes

Hashes for mcp_tool_evals-0.7.0.tar.gz
Algorithm Hash digest
SHA256 f47c6df4daf3211e336733003e4441452fe6440e70299a2b44c9f28175ed18e6
MD5 2502de4ff55096b1e8ef2e43ea257139
BLAKE2b-256 8a43911ead923f6280afe4f45e2f188caaa7961aeb72c82640d62c1f674f8b97

See more details on using hashes here.

File details

Details for the file mcp_tool_evals-0.7.0-py3-none-any.whl.

File metadata

  • Download URL: mcp_tool_evals-0.7.0-py3-none-any.whl
  • Upload date:
  • Size: 23.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.12.14

File hashes

Hashes for mcp_tool_evals-0.7.0-py3-none-any.whl
Algorithm Hash digest
SHA256 dfe269c945c12357954da15892051fc40ff5c1449eb494bab737ed9188c36c1b
MD5 22dc0f840de58e929b0f1ed2c7e6e1cf
BLAKE2b-256 078a86feae4891bc3ecc364d7745433e4387a90d081f5614686ecc0b8fd68545

See more details on using hashes here.

Release history Release notifications | RSS feed

0.8.0

2 files

This release

0.7.0 This release

2 files

0.6.0

2 files

0.5.0

2 files

0.4.0

2 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