mcp-tool-evals 0.4.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- mcp_tool_evals-0.4.0/.dockerignore +9 -0
- mcp_tool_evals-0.4.0/.env.example +4 -0
- mcp_tool_evals-0.4.0/.github/ISSUE_TEMPLATE/bug_report.yml +18 -0
- mcp_tool_evals-0.4.0/.github/pull_request_template.md +13 -0
- mcp_tool_evals-0.4.0/.github/workflows/ci.yml +19 -0
- mcp_tool_evals-0.4.0/.gitignore +16 -0
- mcp_tool_evals-0.4.0/.pre-commit-config.yaml +11 -0
- mcp_tool_evals-0.4.0/CHANGELOG.md +37 -0
- mcp_tool_evals-0.4.0/CODE_OF_CONDUCT.md +22 -0
- mcp_tool_evals-0.4.0/CONTRIBUTING.md +51 -0
- mcp_tool_evals-0.4.0/LICENSE +21 -0
- mcp_tool_evals-0.4.0/PKG-INFO +235 -0
- mcp_tool_evals-0.4.0/README.md +199 -0
- mcp_tool_evals-0.4.0/SECURITY.md +14 -0
- mcp_tool_evals-0.4.0/action.yml +34 -0
- mcp_tool_evals-0.4.0/compose.yaml +61 -0
- mcp_tool_evals-0.4.0/docker/Dockerfile +18 -0
- mcp_tool_evals-0.4.0/docs/architecture.md +25 -0
- mcp_tool_evals-0.4.0/examples/calc_server.py +106 -0
- mcp_tool_evals-0.4.0/examples/catalog-server.yaml +3 -0
- mcp_tool_evals-0.4.0/examples/eval-suite.yaml +18 -0
- mcp_tool_evals-0.4.0/examples/http-server.yaml +4 -0
- mcp_tool_evals-0.4.0/examples/http_server.py +93 -0
- mcp_tool_evals-0.4.0/examples/server.yaml +6 -0
- mcp_tool_evals-0.4.0/examples/tools.yaml +28 -0
- mcp_tool_evals-0.4.0/plans/README.md +12 -0
- mcp_tool_evals-0.4.0/plans/v0.1-completed-foundation.md +154 -0
- mcp_tool_evals-0.4.0/plans/v0.2-next-phase-roadmap.md +176 -0
- mcp_tool_evals-0.4.0/pyproject.toml +72 -0
- mcp_tool_evals-0.4.0/src/mcp_evals/__init__.py +3 -0
- mcp_tool_evals-0.4.0/src/mcp_evals/cli.py +246 -0
- mcp_tool_evals-0.4.0/src/mcp_evals/config.py +23 -0
- mcp_tool_evals-0.4.0/src/mcp_evals/errors.py +21 -0
- mcp_tool_evals-0.4.0/src/mcp_evals/linter/__init__.py +3 -0
- mcp_tool_evals-0.4.0/src/mcp_evals/linter/descriptions.py +203 -0
- mcp_tool_evals-0.4.0/src/mcp_evals/logging.py +35 -0
- mcp_tool_evals-0.4.0/src/mcp_evals/mcp_client/__init__.py +5 -0
- mcp_tool_evals-0.4.0/src/mcp_evals/mcp_client/discover.py +73 -0
- mcp_tool_evals-0.4.0/src/mcp_evals/mcp_client/http.py +59 -0
- mcp_tool_evals-0.4.0/src/mcp_evals/mcp_client/stdio.py +121 -0
- mcp_tool_evals-0.4.0/src/mcp_evals/models/__init__.py +11 -0
- mcp_tool_evals-0.4.0/src/mcp_evals/models/server.py +19 -0
- mcp_tool_evals-0.4.0/src/mcp_evals/models/spec.py +42 -0
- mcp_tool_evals-0.4.0/src/mcp_evals/runner/__init__.py +17 -0
- mcp_tool_evals-0.4.0/src/mcp_evals/runner/eval_runner.py +59 -0
- mcp_tool_evals-0.4.0/src/mcp_evals/runner/llm_runner.py +203 -0
- mcp_tool_evals-0.4.0/src/mcp_evals/spec_loader.py +34 -0
- mcp_tool_evals-0.4.0/tests/test_eval_runner.py +80 -0
- mcp_tool_evals-0.4.0/tests/test_linter.py +82 -0
- mcp_tool_evals-0.4.0/tests/test_spec_loader.py +33 -0
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: Bug report
|
|
3
|
+
about: Report a problem
|
|
4
|
+
title: "[bug] "
|
|
5
|
+
labels: bug
|
|
6
|
+
body:
|
|
7
|
+
- type: textarea
|
|
8
|
+
id: description
|
|
9
|
+
attributes:
|
|
10
|
+
label: What happened?
|
|
11
|
+
validations:
|
|
12
|
+
required: true
|
|
13
|
+
- type: textarea
|
|
14
|
+
id: reproduce
|
|
15
|
+
attributes:
|
|
16
|
+
label: Steps to reproduce
|
|
17
|
+
validations:
|
|
18
|
+
required: true
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
on:
|
|
3
|
+
push:
|
|
4
|
+
branches: [main]
|
|
5
|
+
pull_request:
|
|
6
|
+
branches: [main]
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
test:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
steps:
|
|
12
|
+
- uses: actions/checkout@v4
|
|
13
|
+
- uses: actions/setup-python@v5
|
|
14
|
+
with:
|
|
15
|
+
python-version: "3.12"
|
|
16
|
+
- run: pip install -e ".[dev]"
|
|
17
|
+
- run: ruff check src tests
|
|
18
|
+
- run: mypy src
|
|
19
|
+
- run: pytest tests/ -v --cov=mcp_evals
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
repos:
|
|
2
|
+
- repo: https://github.com/astral-sh/ruff-pre-commit
|
|
3
|
+
rev: v0.4.4
|
|
4
|
+
hooks:
|
|
5
|
+
- id: ruff
|
|
6
|
+
args: [--fix]
|
|
7
|
+
- repo: https://github.com/pre-commit/mirrors-mypy
|
|
8
|
+
rev: v1.9.0
|
|
9
|
+
hooks:
|
|
10
|
+
- id: mypy
|
|
11
|
+
additional_dependencies: [types-PyYAML, pydantic-settings]
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
## [0.4.0] - 2026-09-14
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
- `name-description-mismatch` linter rule — warns when distinctive tool-name tokens never appear in the description
|
|
9
|
+
|
|
10
|
+
## [0.3.0] - 2026-08-19
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
- HTTP JSON-RPC MCP discovery (`transport: http` + `mcp_evals.mcp_client.http`)
|
|
14
|
+
- Example HTTP calculator server in `examples/http_server.py`
|
|
15
|
+
|
|
16
|
+
## [0.2.0] - 2026-08-19
|
|
17
|
+
|
|
18
|
+
### Added
|
|
19
|
+
- `mcp-evals discover` for stdio MCP servers and catalog fixtures
|
|
20
|
+
- `mcp-evals lint --live` against a server config
|
|
21
|
+
- Behavioral `mcp-evals run` with mock keyword selector or OpenAI-compatible models
|
|
22
|
+
- `--samples` and `--pass-threshold` for CI
|
|
23
|
+
- Extra linter rules: `required-params-undocumented`, `ambiguous-verbs`
|
|
24
|
+
- Example stdio calculator server and GitHub Action (`action.yml`)
|
|
25
|
+
|
|
26
|
+
### Notes
|
|
27
|
+
- HTTP/Streamable MCP transport is not implemented yet
|
|
28
|
+
|
|
29
|
+
## [0.1.0] - 2026-08-18
|
|
30
|
+
|
|
31
|
+
### Added
|
|
32
|
+
- Initial project foundation
|
|
33
|
+
- `mcp-evals lint` — static tool description linter
|
|
34
|
+
- `mcp-evals validate-spec` — eval suite structure validation (dry-run)
|
|
35
|
+
- `mcp-evals health` — installation check
|
|
36
|
+
- Example tool catalog and eval suite fixtures
|
|
37
|
+
- Docker development and test environment
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# Contributor Covenant Code of Conduct
|
|
2
|
+
|
|
3
|
+
## Our pledge
|
|
4
|
+
|
|
5
|
+
We pledge to make participation in our community a harassment-free experience for everyone.
|
|
6
|
+
|
|
7
|
+
## Our standards
|
|
8
|
+
|
|
9
|
+
Examples of behavior that contributes to a positive environment:
|
|
10
|
+
|
|
11
|
+
- Using welcoming and inclusive language
|
|
12
|
+
- Being respectful of differing viewpoints
|
|
13
|
+
- Gracefully accepting constructive criticism
|
|
14
|
+
- Focusing on what is best for the community
|
|
15
|
+
|
|
16
|
+
## Enforcement
|
|
17
|
+
|
|
18
|
+
Project maintainers may remove, edit, or reject contributions that violate this Code of Conduct.
|
|
19
|
+
|
|
20
|
+
## Attribution
|
|
21
|
+
|
|
22
|
+
Adapted from the [Contributor Covenant](https://www.contributor-covenant.org/), version 2.1.
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# Contributing to mcp-evals
|
|
2
|
+
|
|
3
|
+
Thank you for your interest in contributing.
|
|
4
|
+
|
|
5
|
+
## Development setup
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
python -m venv .venv && source .venv/bin/activate
|
|
9
|
+
pip install -e ".[dev]"
|
|
10
|
+
pre-commit install # optional
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Running tests
|
|
14
|
+
|
|
15
|
+
Prefer Docker Compose so the environment matches CI:
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
docker compose run --rm test
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Locally:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
pytest tests/ -v
|
|
25
|
+
ruff check src tests
|
|
26
|
+
mypy src
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Other useful compose services: `dev`, `lint-example`, `discover-example`, `run-example`.
|
|
30
|
+
|
|
31
|
+
## Project layout
|
|
32
|
+
|
|
33
|
+
| Path | Purpose |
|
|
34
|
+
|------|---------|
|
|
35
|
+
| `src/mcp_evals/cli.py` | CLI entry point |
|
|
36
|
+
| `src/mcp_evals/linter/` | Static description analysis |
|
|
37
|
+
| `src/mcp_evals/runner/` | Eval execution |
|
|
38
|
+
| `src/mcp_evals/models/` | Pydantic schemas |
|
|
39
|
+
| `examples/` | Sample catalogs and eval suites |
|
|
40
|
+
|
|
41
|
+
## Pull requests
|
|
42
|
+
|
|
43
|
+
- Keep changes focused and tested
|
|
44
|
+
- Update README/CHANGELOG for user-facing changes
|
|
45
|
+
- Follow existing code style (ruff + mypy strict)
|
|
46
|
+
- Prefer small PRs with a clear test plan in the description
|
|
47
|
+
|
|
48
|
+
## Commit style
|
|
49
|
+
|
|
50
|
+
- Imperative subject line; mention the user-facing why when relevant
|
|
51
|
+
- Do not add AI co-author trailers (e.g. Co-authored-by: Cursor) to commits.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 mcp-evals contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: mcp-tool-evals
|
|
3
|
+
Version: 0.4.0
|
|
4
|
+
Summary: Behavioral evaluation and description linting for MCP servers
|
|
5
|
+
Project-URL: Homepage, https://github.com/yashshah9/mcp-evals
|
|
6
|
+
Project-URL: Repository, https://github.com/yashshah9/mcp-evals
|
|
7
|
+
Project-URL: Issues, https://github.com/yashshah9/mcp-evals/issues
|
|
8
|
+
Project-URL: Documentation, https://github.com/yashshah9/mcp-evals#readme
|
|
9
|
+
Author-email: Yash Shah <yash376351@gmail.com>
|
|
10
|
+
License-Expression: MIT
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Keywords: agents,llm,mcp,model-context-protocol,testing
|
|
13
|
+
Classifier: Development Status :: 3 - Alpha
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Topic :: Software Development :: Testing
|
|
19
|
+
Requires-Python: >=3.11
|
|
20
|
+
Requires-Dist: click>=8.1
|
|
21
|
+
Requires-Dist: pydantic-settings>=2.2
|
|
22
|
+
Requires-Dist: pydantic>=2.6
|
|
23
|
+
Requires-Dist: pyyaml>=6.0
|
|
24
|
+
Requires-Dist: rich>=13.7
|
|
25
|
+
Requires-Dist: structlog>=24.1
|
|
26
|
+
Provides-Extra: dev
|
|
27
|
+
Requires-Dist: mypy>=1.9; extra == 'dev'
|
|
28
|
+
Requires-Dist: pre-commit>=3.7; extra == 'dev'
|
|
29
|
+
Requires-Dist: pytest-cov>=5.0; extra == 'dev'
|
|
30
|
+
Requires-Dist: pytest>=8.0; extra == 'dev'
|
|
31
|
+
Requires-Dist: ruff>=0.4; extra == 'dev'
|
|
32
|
+
Requires-Dist: types-pyyaml>=6.0; extra == 'dev'
|
|
33
|
+
Provides-Extra: mcp
|
|
34
|
+
Requires-Dist: mcp>=1.0; extra == 'mcp'
|
|
35
|
+
Description-Content-Type: text/markdown
|
|
36
|
+
|
|
37
|
+
# mcp-evals
|
|
38
|
+
|
|
39
|
+
Behavioral evaluation and description linting for [Model Context Protocol](https://modelcontextprotocol.io) (MCP) servers.
|
|
40
|
+
|
|
41
|
+
[](LICENSE)
|
|
42
|
+
[](https://www.python.org/downloads/)
|
|
43
|
+
[](https://github.com/yashshah9/mcp-evals/actions/workflows/ci.yml)
|
|
44
|
+
|
|
45
|
+
> **Status:** v0.4 — stdio + HTTP JSON-RPC discovery, description lint (including `name-description-mismatch`), and mock/OpenAI-compatible tool-selection evals.
|
|
46
|
+
|
|
47
|
+
## 60-second try
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
docker compose run --rm run-example # mock eval, no API key
|
|
51
|
+
docker compose run --rm lint-example # live stdio lint
|
|
52
|
+
docker compose run --rm test # pytest
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Why this vs alternatives
|
|
56
|
+
|
|
57
|
+
| Approach | Strength | Gap |
|
|
58
|
+
|----------|----------|-----|
|
|
59
|
+
| **mcp-evals** | Lint + YAML evals + mock CI runner | Not a full agent harness |
|
|
60
|
+
| MCP Inspector | Interactive debugging | No CI lint/eval suite |
|
|
61
|
+
| Protocol conformance tests | JSON-RPC correctness | Do not test tool selection |
|
|
62
|
+
| Hand-written LLM mocks | Full control | Drift from real descriptions |
|
|
63
|
+
|
|
64
|
+
## Problem
|
|
65
|
+
|
|
66
|
+
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.
|
|
67
|
+
|
|
68
|
+
**mcp-evals** fills the behavioral layer: lint tool descriptions, define eval cases in YAML, and (next) measure tool-selection accuracy in CI.
|
|
69
|
+
|
|
70
|
+
## Key features (v0.4)
|
|
71
|
+
|
|
72
|
+
- **Description linter** — missing descriptions, undocumented required params, overlapping tools, ambiguous verbs, `name-description-mismatch`
|
|
73
|
+
- **Live discover** — handshake a stdio or HTTP JSON-RPC MCP server (or load a catalog fixture)
|
|
74
|
+
- **Eval runner** — `mcp-evals run` with `--model mock` (CI), `--live SERVER.yaml`, or an OpenAI-compatible endpoint
|
|
75
|
+
- **CLI + Docker + GitHub Action** — lint and optional eval in CI without a cloud key (mock selector)
|
|
76
|
+
|
|
77
|
+
## Architecture
|
|
78
|
+
|
|
79
|
+
```
|
|
80
|
+
┌─────────────┐ ┌──────────────────┐ ┌─────────────────┐
|
|
81
|
+
│ CLI │────▶│ Linter / Runner │────▶│ Models (Pydantic)│
|
|
82
|
+
│ mcp-evals │ │ spec_loader │ │ EvalSuite, etc. │
|
|
83
|
+
└─────────────┘ └──────────────────┘ └─────────────────┘
|
|
84
|
+
│
|
|
85
|
+
(next) MCP client + LLM
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
| Component | Technology | Why |
|
|
89
|
+
|-----------|------------|-----|
|
|
90
|
+
| Language | Python 3.11+ | MCP SDK ecosystem, pytest integration |
|
|
91
|
+
| CLI | Click | Mature, composable commands |
|
|
92
|
+
| Schemas | Pydantic v2 | Strict validation, good errors |
|
|
93
|
+
| Logging | structlog | Structured, JSON-capable |
|
|
94
|
+
| Config | pydantic-settings | Env-based, typed |
|
|
95
|
+
| Tests | pytest + ruff + mypy | Standard Python OSS stack |
|
|
96
|
+
|
|
97
|
+
## Installation
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
pip install mcp-tool-evals
|
|
101
|
+
# or from source:
|
|
102
|
+
pip install -e ".[dev]"
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
## Local development
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
python -m venv .venv && source .venv/bin/activate
|
|
109
|
+
pip install -e ".[dev]"
|
|
110
|
+
mcp-evals health
|
|
111
|
+
mcp-evals lint examples/tools.yaml
|
|
112
|
+
mcp-evals discover examples/server.yaml
|
|
113
|
+
mcp-evals lint --live examples/server.yaml
|
|
114
|
+
mcp-evals validate-spec examples/eval-suite.yaml
|
|
115
|
+
mcp-evals run examples/eval-suite.yaml --catalog examples/tools.yaml --model mock
|
|
116
|
+
pytest tests/ -v
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
## Docker
|
|
120
|
+
|
|
121
|
+
```bash
|
|
122
|
+
# Health check
|
|
123
|
+
docker compose run --rm dev
|
|
124
|
+
|
|
125
|
+
# Run tests
|
|
126
|
+
docker compose run --rm test
|
|
127
|
+
|
|
128
|
+
# Lint a live stdio server (this example passes)
|
|
129
|
+
docker compose run --rm lint-example
|
|
130
|
+
|
|
131
|
+
# Discover tools from the example calc server
|
|
132
|
+
docker compose run --rm discover-example
|
|
133
|
+
|
|
134
|
+
# Mock eval run (no API key)
|
|
135
|
+
docker compose run --rm run-example
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
## Configuration
|
|
139
|
+
|
|
140
|
+
Copy `.env.example` to `.env`:
|
|
141
|
+
|
|
142
|
+
| Variable | Default | Description |
|
|
143
|
+
|----------|---------|-------------|
|
|
144
|
+
| `MCP_EVALS_LOG_LEVEL` | `INFO` | Log level |
|
|
145
|
+
| `MCP_EVALS_LOG_JSON` | `false` | JSON log output |
|
|
146
|
+
| `MCP_EVALS_DEFAULT_MODEL` | `gpt-4o-mini` | Model for eval runs (future) |
|
|
147
|
+
| `MCP_EVALS_PASS_THRESHOLD` | `0.8` | Minimum selection accuracy |
|
|
148
|
+
|
|
149
|
+
## Usage
|
|
150
|
+
|
|
151
|
+
### Lint tool descriptions
|
|
152
|
+
|
|
153
|
+
```bash
|
|
154
|
+
mcp-evals lint examples/tools.yaml
|
|
155
|
+
mcp-evals lint --live examples/server.yaml
|
|
156
|
+
mcp-evals discover examples/server.yaml
|
|
157
|
+
mcp-evals lint examples/tools.yaml --format json
|
|
158
|
+
mcp-evals lint examples/tools.yaml --fail-on-warning
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
### Run behavioral evals
|
|
162
|
+
|
|
163
|
+
```bash
|
|
164
|
+
mcp-evals run examples/eval-suite.yaml --catalog examples/tools.yaml --model mock
|
|
165
|
+
mcp-evals run examples/eval-suite.yaml --catalog examples/tools.yaml \
|
|
166
|
+
--model llama3.2 --base-url http://localhost:11434/v1 --pass-threshold 0.8
|
|
167
|
+
# or discover tools from a live server that matches the suite:
|
|
168
|
+
# mcp-evals run suite.yaml --live server.yaml --model mock
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
### Validate eval suite
|
|
172
|
+
|
|
173
|
+
```bash
|
|
174
|
+
mcp-evals validate-spec examples/eval-suite.yaml
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
### Health check
|
|
178
|
+
|
|
179
|
+
```bash
|
|
180
|
+
mcp-evals health
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
## Example eval suite
|
|
184
|
+
|
|
185
|
+
See `examples/eval-suite.yaml`:
|
|
186
|
+
|
|
187
|
+
```yaml
|
|
188
|
+
name: document-server-behavior
|
|
189
|
+
cases:
|
|
190
|
+
- id: search-by-keyword
|
|
191
|
+
request: Find documents about quarterly revenue
|
|
192
|
+
expected_tool:
|
|
193
|
+
name: search_documents
|
|
194
|
+
arguments:
|
|
195
|
+
query: quarterly revenue
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
## Running tests
|
|
199
|
+
|
|
200
|
+
```bash
|
|
201
|
+
pytest tests/ -v
|
|
202
|
+
ruff check src tests
|
|
203
|
+
mypy src
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
## Development workflow
|
|
207
|
+
|
|
208
|
+
1. Add linter rules in `src/mcp_evals/linter/`
|
|
209
|
+
2. Add runner logic in `src/mcp_evals/runner/`
|
|
210
|
+
3. Add tests in `tests/`
|
|
211
|
+
4. Update examples in `examples/`
|
|
212
|
+
|
|
213
|
+
## Roadmap
|
|
214
|
+
|
|
215
|
+
- [x] MCP client: stdio discover + lint --live
|
|
216
|
+
- [x] LLM eval runner (mock + OpenAI-compatible)
|
|
217
|
+
- [x] HTTP JSON-RPC MCP transport
|
|
218
|
+
- [x] `name-description-mismatch` linter rule
|
|
219
|
+
- [ ] Streamable HTTP/SSE MCP transport
|
|
220
|
+
- [ ] GitHub Action PR comments and accuracy deltas
|
|
221
|
+
|
|
222
|
+
## Known limitations (v0.4)
|
|
223
|
+
|
|
224
|
+
- Streamable HTTP/SSE MCP is not implemented — HTTP is JSON-RPC POST only
|
|
225
|
+
- Mock selector does not fill tool arguments (accuracy is tool-name only)
|
|
226
|
+
- Live Ollama/OpenAI evals need a reachable `--base-url`; CI uses `--model mock`
|
|
227
|
+
- `examples/tools.yaml` is intentionally dirty so `mcp-evals lint` can show findings
|
|
228
|
+
|
|
229
|
+
## Contributing
|
|
230
|
+
|
|
231
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md).
|
|
232
|
+
|
|
233
|
+
## License
|
|
234
|
+
|
|
235
|
+
MIT — see [LICENSE](LICENSE).
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
# mcp-evals
|
|
2
|
+
|
|
3
|
+
Behavioral evaluation and description linting for [Model Context Protocol](https://modelcontextprotocol.io) (MCP) servers.
|
|
4
|
+
|
|
5
|
+
[](LICENSE)
|
|
6
|
+
[](https://www.python.org/downloads/)
|
|
7
|
+
[](https://github.com/yashshah9/mcp-evals/actions/workflows/ci.yml)
|
|
8
|
+
|
|
9
|
+
> **Status:** v0.4 — stdio + HTTP JSON-RPC discovery, description lint (including `name-description-mismatch`), and mock/OpenAI-compatible tool-selection evals.
|
|
10
|
+
|
|
11
|
+
## 60-second try
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
docker compose run --rm run-example # mock eval, no API key
|
|
15
|
+
docker compose run --rm lint-example # live stdio lint
|
|
16
|
+
docker compose run --rm test # pytest
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Why this vs alternatives
|
|
20
|
+
|
|
21
|
+
| Approach | Strength | Gap |
|
|
22
|
+
|----------|----------|-----|
|
|
23
|
+
| **mcp-evals** | Lint + YAML evals + mock CI runner | Not a full agent harness |
|
|
24
|
+
| MCP Inspector | Interactive debugging | No CI lint/eval suite |
|
|
25
|
+
| Protocol conformance tests | JSON-RPC correctness | Do not test tool selection |
|
|
26
|
+
| Hand-written LLM mocks | Full control | Drift from real descriptions |
|
|
27
|
+
|
|
28
|
+
## Problem
|
|
29
|
+
|
|
30
|
+
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.
|
|
31
|
+
|
|
32
|
+
**mcp-evals** fills the behavioral layer: lint tool descriptions, define eval cases in YAML, and (next) measure tool-selection accuracy in CI.
|
|
33
|
+
|
|
34
|
+
## Key features (v0.4)
|
|
35
|
+
|
|
36
|
+
- **Description linter** — missing descriptions, undocumented required params, overlapping tools, ambiguous verbs, `name-description-mismatch`
|
|
37
|
+
- **Live discover** — handshake a stdio or HTTP JSON-RPC MCP server (or load a catalog fixture)
|
|
38
|
+
- **Eval runner** — `mcp-evals run` with `--model mock` (CI), `--live SERVER.yaml`, or an OpenAI-compatible endpoint
|
|
39
|
+
- **CLI + Docker + GitHub Action** — lint and optional eval in CI without a cloud key (mock selector)
|
|
40
|
+
|
|
41
|
+
## Architecture
|
|
42
|
+
|
|
43
|
+
```
|
|
44
|
+
┌─────────────┐ ┌──────────────────┐ ┌─────────────────┐
|
|
45
|
+
│ CLI │────▶│ Linter / Runner │────▶│ Models (Pydantic)│
|
|
46
|
+
│ mcp-evals │ │ spec_loader │ │ EvalSuite, etc. │
|
|
47
|
+
└─────────────┘ └──────────────────┘ └─────────────────┘
|
|
48
|
+
│
|
|
49
|
+
(next) MCP client + LLM
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
| Component | Technology | Why |
|
|
53
|
+
|-----------|------------|-----|
|
|
54
|
+
| Language | Python 3.11+ | MCP SDK ecosystem, pytest integration |
|
|
55
|
+
| CLI | Click | Mature, composable commands |
|
|
56
|
+
| Schemas | Pydantic v2 | Strict validation, good errors |
|
|
57
|
+
| Logging | structlog | Structured, JSON-capable |
|
|
58
|
+
| Config | pydantic-settings | Env-based, typed |
|
|
59
|
+
| Tests | pytest + ruff + mypy | Standard Python OSS stack |
|
|
60
|
+
|
|
61
|
+
## Installation
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
pip install mcp-tool-evals
|
|
65
|
+
# or from source:
|
|
66
|
+
pip install -e ".[dev]"
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Local development
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
python -m venv .venv && source .venv/bin/activate
|
|
73
|
+
pip install -e ".[dev]"
|
|
74
|
+
mcp-evals health
|
|
75
|
+
mcp-evals lint examples/tools.yaml
|
|
76
|
+
mcp-evals discover examples/server.yaml
|
|
77
|
+
mcp-evals lint --live examples/server.yaml
|
|
78
|
+
mcp-evals validate-spec examples/eval-suite.yaml
|
|
79
|
+
mcp-evals run examples/eval-suite.yaml --catalog examples/tools.yaml --model mock
|
|
80
|
+
pytest tests/ -v
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
## Docker
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
# Health check
|
|
87
|
+
docker compose run --rm dev
|
|
88
|
+
|
|
89
|
+
# Run tests
|
|
90
|
+
docker compose run --rm test
|
|
91
|
+
|
|
92
|
+
# Lint a live stdio server (this example passes)
|
|
93
|
+
docker compose run --rm lint-example
|
|
94
|
+
|
|
95
|
+
# Discover tools from the example calc server
|
|
96
|
+
docker compose run --rm discover-example
|
|
97
|
+
|
|
98
|
+
# Mock eval run (no API key)
|
|
99
|
+
docker compose run --rm run-example
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
## Configuration
|
|
103
|
+
|
|
104
|
+
Copy `.env.example` to `.env`:
|
|
105
|
+
|
|
106
|
+
| Variable | Default | Description |
|
|
107
|
+
|----------|---------|-------------|
|
|
108
|
+
| `MCP_EVALS_LOG_LEVEL` | `INFO` | Log level |
|
|
109
|
+
| `MCP_EVALS_LOG_JSON` | `false` | JSON log output |
|
|
110
|
+
| `MCP_EVALS_DEFAULT_MODEL` | `gpt-4o-mini` | Model for eval runs (future) |
|
|
111
|
+
| `MCP_EVALS_PASS_THRESHOLD` | `0.8` | Minimum selection accuracy |
|
|
112
|
+
|
|
113
|
+
## Usage
|
|
114
|
+
|
|
115
|
+
### Lint tool descriptions
|
|
116
|
+
|
|
117
|
+
```bash
|
|
118
|
+
mcp-evals lint examples/tools.yaml
|
|
119
|
+
mcp-evals lint --live examples/server.yaml
|
|
120
|
+
mcp-evals discover examples/server.yaml
|
|
121
|
+
mcp-evals lint examples/tools.yaml --format json
|
|
122
|
+
mcp-evals lint examples/tools.yaml --fail-on-warning
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
### Run behavioral evals
|
|
126
|
+
|
|
127
|
+
```bash
|
|
128
|
+
mcp-evals run examples/eval-suite.yaml --catalog examples/tools.yaml --model mock
|
|
129
|
+
mcp-evals run examples/eval-suite.yaml --catalog examples/tools.yaml \
|
|
130
|
+
--model llama3.2 --base-url http://localhost:11434/v1 --pass-threshold 0.8
|
|
131
|
+
# or discover tools from a live server that matches the suite:
|
|
132
|
+
# mcp-evals run suite.yaml --live server.yaml --model mock
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
### Validate eval suite
|
|
136
|
+
|
|
137
|
+
```bash
|
|
138
|
+
mcp-evals validate-spec examples/eval-suite.yaml
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### Health check
|
|
142
|
+
|
|
143
|
+
```bash
|
|
144
|
+
mcp-evals health
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
## Example eval suite
|
|
148
|
+
|
|
149
|
+
See `examples/eval-suite.yaml`:
|
|
150
|
+
|
|
151
|
+
```yaml
|
|
152
|
+
name: document-server-behavior
|
|
153
|
+
cases:
|
|
154
|
+
- id: search-by-keyword
|
|
155
|
+
request: Find documents about quarterly revenue
|
|
156
|
+
expected_tool:
|
|
157
|
+
name: search_documents
|
|
158
|
+
arguments:
|
|
159
|
+
query: quarterly revenue
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
## Running tests
|
|
163
|
+
|
|
164
|
+
```bash
|
|
165
|
+
pytest tests/ -v
|
|
166
|
+
ruff check src tests
|
|
167
|
+
mypy src
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
## Development workflow
|
|
171
|
+
|
|
172
|
+
1. Add linter rules in `src/mcp_evals/linter/`
|
|
173
|
+
2. Add runner logic in `src/mcp_evals/runner/`
|
|
174
|
+
3. Add tests in `tests/`
|
|
175
|
+
4. Update examples in `examples/`
|
|
176
|
+
|
|
177
|
+
## Roadmap
|
|
178
|
+
|
|
179
|
+
- [x] MCP client: stdio discover + lint --live
|
|
180
|
+
- [x] LLM eval runner (mock + OpenAI-compatible)
|
|
181
|
+
- [x] HTTP JSON-RPC MCP transport
|
|
182
|
+
- [x] `name-description-mismatch` linter rule
|
|
183
|
+
- [ ] Streamable HTTP/SSE MCP transport
|
|
184
|
+
- [ ] GitHub Action PR comments and accuracy deltas
|
|
185
|
+
|
|
186
|
+
## Known limitations (v0.4)
|
|
187
|
+
|
|
188
|
+
- Streamable HTTP/SSE MCP is not implemented — HTTP is JSON-RPC POST only
|
|
189
|
+
- Mock selector does not fill tool arguments (accuracy is tool-name only)
|
|
190
|
+
- Live Ollama/OpenAI evals need a reachable `--base-url`; CI uses `--model mock`
|
|
191
|
+
- `examples/tools.yaml` is intentionally dirty so `mcp-evals lint` can show findings
|
|
192
|
+
|
|
193
|
+
## Contributing
|
|
194
|
+
|
|
195
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md).
|
|
196
|
+
|
|
197
|
+
## License
|
|
198
|
+
|
|
199
|
+
MIT — see [LICENSE](LICENSE).
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# Security Policy
|
|
2
|
+
|
|
3
|
+
## Reporting a vulnerability
|
|
4
|
+
|
|
5
|
+
Email **yash376351@gmail.com** with the repo name, a short description, and steps to reproduce. Please do not open a public issue for exploitable findings until we have had a reasonable chance to respond.
|
|
6
|
+
|
|
7
|
+
## Threat model (honest)
|
|
8
|
+
|
|
9
|
+
mcp-evals is a **developer/CI tool**. It talks to MCP servers and optional LLM endpoints you configure.
|
|
10
|
+
|
|
11
|
+
- It is **not** a sandbox for untrusted MCP servers or model output.
|
|
12
|
+
- Treat tool catalogs, eval fixtures, and live-server configs as trusted inputs for your environment.
|
|
13
|
+
- Do not pass production secrets into eval suites or commit them to cassettes/logs.
|
|
14
|
+
- Mock mode (`--model mock`) never calls a network model; live modes inherit whatever network and credential access your process already has.
|