mcp-quality 0.1.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_quality-0.1.0/.github/ISSUE_TEMPLATE/bug_report.md +13 -0
- mcp_quality-0.1.0/.github/ISSUE_TEMPLATE/feature_request.md +11 -0
- mcp_quality-0.1.0/.github/good-first-issues.md +8 -0
- mcp_quality-0.1.0/.github/workflows/ci.yml +85 -0
- mcp_quality-0.1.0/.github/workflows/release.yml +46 -0
- mcp_quality-0.1.0/.gitignore +230 -0
- mcp_quality-0.1.0/.mcp-probe/cache/legibility-868b57b70e113604d1906f0c.json +1 -0
- mcp_quality-0.1.0/.mcp-quality.toml.example +30 -0
- mcp_quality-0.1.0/AGENTS.md +103 -0
- mcp_quality-0.1.0/CHANGELOG.md +65 -0
- mcp_quality-0.1.0/CITATION.cff +16 -0
- mcp_quality-0.1.0/CLAUDE.md +103 -0
- mcp_quality-0.1.0/CONTRIBUTING.md +21 -0
- mcp_quality-0.1.0/LICENSE +202 -0
- mcp_quality-0.1.0/PKG-INFO +119 -0
- mcp_quality-0.1.0/README.md +81 -0
- mcp_quality-0.1.0/ROADMAP.md +25 -0
- mcp_quality-0.1.0/SPEC.md +121 -0
- mcp_quality-0.1.0/docs/ARCHITECTURE.md +391 -0
- mcp_quality-0.1.0/docs/DECISIONS.md +94 -0
- mcp_quality-0.1.0/docs/DELIVERY-PLAN.md +165 -0
- mcp_quality-0.1.0/docs/PRD.md +233 -0
- mcp_quality-0.1.0/docs/RESEARCH.md +223 -0
- mcp_quality-0.1.0/docs/TEST-PLAN.md +185 -0
- mcp_quality-0.1.0/docs/demo.md +72 -0
- mcp_quality-0.1.0/docs/examples/score-delta-workflow.yml +50 -0
- mcp_quality-0.1.0/docs/launch/essay.md +112 -0
- mcp_quality-0.1.0/docs/leaderboard.json +107 -0
- mcp_quality-0.1.0/docs/leaderboard.md +22 -0
- mcp_quality-0.1.0/pyproject.toml +104 -0
- mcp_quality-0.1.0/scripts/demo.sh +33 -0
- mcp_quality-0.1.0/scripts/leaderboard.py +224 -0
- mcp_quality-0.1.0/src/mcp_quality/__init__.py +19 -0
- mcp_quality-0.1.0/src/mcp_quality/cli.py +431 -0
- mcp_quality-0.1.0/src/mcp_quality/config.py +139 -0
- mcp_quality-0.1.0/src/mcp_quality/connect/__init__.py +29 -0
- mcp_quality-0.1.0/src/mcp_quality/connect/client.py +88 -0
- mcp_quality-0.1.0/src/mcp_quality/connect/discover.py +99 -0
- mcp_quality-0.1.0/src/mcp_quality/connect/transport.py +164 -0
- mcp_quality-0.1.0/src/mcp_quality/contract/__init__.py +17 -0
- mcp_quality-0.1.0/src/mcp_quality/contract/schema.py +175 -0
- mcp_quality-0.1.0/src/mcp_quality/engines/__init__.py +39 -0
- mcp_quality-0.1.0/src/mcp_quality/engines/base.py +53 -0
- mcp_quality-0.1.0/src/mcp_quality/engines/contract.py +283 -0
- mcp_quality-0.1.0/src/mcp_quality/engines/cost.py +241 -0
- mcp_quality-0.1.0/src/mcp_quality/engines/legibility.py +230 -0
- mcp_quality-0.1.0/src/mcp_quality/engines/performance.py +156 -0
- mcp_quality-0.1.0/src/mcp_quality/engines/security.py +82 -0
- mcp_quality-0.1.0/src/mcp_quality/exit_codes.py +19 -0
- mcp_quality-0.1.0/src/mcp_quality/fix.py +127 -0
- mcp_quality-0.1.0/src/mcp_quality/handoff.py +80 -0
- mcp_quality-0.1.0/src/mcp_quality/history.py +146 -0
- mcp_quality-0.1.0/src/mcp_quality/legibility/__init__.py +19 -0
- mcp_quality-0.1.0/src/mcp_quality/legibility/cache.py +40 -0
- mcp_quality-0.1.0/src/mcp_quality/legibility/lints.py +53 -0
- mcp_quality-0.1.0/src/mcp_quality/legibility/model.py +178 -0
- mcp_quality-0.1.0/src/mcp_quality/legibility/similarity.py +45 -0
- mcp_quality-0.1.0/src/mcp_quality/models.py +321 -0
- mcp_quality-0.1.0/src/mcp_quality/perf/__init__.py +25 -0
- mcp_quality-0.1.0/src/mcp_quality/perf/load.py +149 -0
- mcp_quality-0.1.0/src/mcp_quality/pipeline.py +187 -0
- mcp_quality-0.1.0/src/mcp_quality/registry.py +89 -0
- mcp_quality-0.1.0/src/mcp_quality/report/__init__.py +13 -0
- mcp_quality-0.1.0/src/mcp_quality/report/badge.py +90 -0
- mcp_quality-0.1.0/src/mcp_quality/report/json_emitter.py +59 -0
- mcp_quality-0.1.0/src/mcp_quality/report/render.py +218 -0
- mcp_quality-0.1.0/src/mcp_quality/scoring/__init__.py +10 -0
- mcp_quality-0.1.0/src/mcp_quality/scoring/scorer.py +121 -0
- mcp_quality-0.1.0/src/mcp_quality/security/__init__.py +11 -0
- mcp_quality-0.1.0/src/mcp_quality/security/adapters.py +216 -0
- mcp_quality-0.1.0/src/mcp_quality/security/patterns.py +176 -0
- mcp_quality-0.1.0/src/mcp_quality/snapshot/__init__.py +17 -0
- mcp_quality-0.1.0/src/mcp_quality/snapshot/store.py +136 -0
- mcp_quality-0.1.0/src/mcp_quality/tokens.py +110 -0
- mcp_quality-0.1.0/src/mcp_quality/trace.py +72 -0
- mcp_quality-0.1.0/tests/__init__.py +0 -0
- mcp_quality-0.1.0/tests/conftest.py +79 -0
- mcp_quality-0.1.0/tests/servers/bloated_server.py +32 -0
- mcp_quality-0.1.0/tests/servers/confusable_server.py +23 -0
- mcp_quality-0.1.0/tests/servers/dump.mcp.json +376 -0
- mcp_quality-0.1.0/tests/servers/flaky_server.py +25 -0
- mcp_quality-0.1.0/tests/servers/good_server.py +34 -0
- mcp_quality-0.1.0/tests/servers/http_server.py +43 -0
- mcp_quality-0.1.0/tests/servers/injection_server.py +24 -0
- mcp_quality-0.1.0/tests/servers/writes_server.py +28 -0
- mcp_quality-0.1.0/tests/test_config.py +48 -0
- mcp_quality-0.1.0/tests/test_contract.py +135 -0
- mcp_quality-0.1.0/tests/test_contract_errorpath.py +60 -0
- mcp_quality-0.1.0/tests/test_cost.py +145 -0
- mcp_quality-0.1.0/tests/test_e2e.py +71 -0
- mcp_quality-0.1.0/tests/test_fix.py +105 -0
- mcp_quality-0.1.0/tests/test_hardening.py +76 -0
- mcp_quality-0.1.0/tests/test_history.py +91 -0
- mcp_quality-0.1.0/tests/test_integration_http.py +86 -0
- mcp_quality-0.1.0/tests/test_legibility.py +101 -0
- mcp_quality-0.1.0/tests/test_legibility_live.py +84 -0
- mcp_quality-0.1.0/tests/test_models.py +60 -0
- mcp_quality-0.1.0/tests/test_outputs.py +102 -0
- mcp_quality-0.1.0/tests/test_performance.py +95 -0
- mcp_quality-0.1.0/tests/test_pipeline.py +68 -0
- mcp_quality-0.1.0/tests/test_readiness.py +90 -0
- mcp_quality-0.1.0/tests/test_registry.py +83 -0
- mcp_quality-0.1.0/tests/test_scorer.py +75 -0
- mcp_quality-0.1.0/tests/test_security.py +119 -0
- mcp_quality-0.1.0/tests/test_snapshot.py +64 -0
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
# Seeded good-first-issues for mcp-quality
|
|
2
|
+
|
|
3
|
+
> Filed as real GitHub issues (label: `good-first-issue`) once the org repo exists.
|
|
4
|
+
|
|
5
|
+
1. Add a description-length lint to the Legibility family
|
|
6
|
+
2. Document the JSON output schema for CI
|
|
7
|
+
3. Add an example GitHub Action using --fail-under
|
|
8
|
+
4. Add a sample server to probe against in tests
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
test:
|
|
10
|
+
name: test (py${{ matrix.python }})
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
strategy:
|
|
13
|
+
matrix:
|
|
14
|
+
python: ["3.11", "3.12"]
|
|
15
|
+
steps:
|
|
16
|
+
- uses: actions/checkout@v4
|
|
17
|
+
- uses: actions/setup-python@v5
|
|
18
|
+
with:
|
|
19
|
+
python-version: ${{ matrix.python }}
|
|
20
|
+
- name: Install
|
|
21
|
+
run: pip install -e ".[dev]"
|
|
22
|
+
- name: Lint
|
|
23
|
+
run: ruff check src/ tests/
|
|
24
|
+
- name: Types
|
|
25
|
+
run: mypy src/
|
|
26
|
+
- name: Unit + component + integration + e2e (no live LLM)
|
|
27
|
+
run: pytest -m "not live_llm" -q
|
|
28
|
+
- name: Coverage floor on the correctness-critical core
|
|
29
|
+
# scorer/connect/snapshot are the parts that must not silently break (TEST-PLAN §9.3)
|
|
30
|
+
run: pytest -m "not live_llm" --cov=mcp_quality.scoring --cov=mcp_quality.snapshot --cov-fail-under=85 -q
|
|
31
|
+
|
|
32
|
+
dogfood:
|
|
33
|
+
name: dogfood the gate + badge
|
|
34
|
+
runs-on: ubuntu-latest
|
|
35
|
+
steps:
|
|
36
|
+
- uses: actions/checkout@v4
|
|
37
|
+
- uses: actions/setup-python@v5
|
|
38
|
+
with:
|
|
39
|
+
python-version: "3.12"
|
|
40
|
+
- run: pip install -e .
|
|
41
|
+
# The gate must pass a good server and fail a bloated one (proves the gate works live).
|
|
42
|
+
- name: good_server passes --fail-under B
|
|
43
|
+
run: mcp-quality run "python tests/servers/good_server.py" --fail-under B
|
|
44
|
+
- name: bloated_server fails --fail-under A
|
|
45
|
+
run: |
|
|
46
|
+
if mcp-quality run "python tests/servers/bloated_server.py" --fail-under A; then
|
|
47
|
+
echo "expected the gate to FAIL the bloated server"; exit 1
|
|
48
|
+
fi
|
|
49
|
+
- name: publish our own badge (the flywheel starts at home)
|
|
50
|
+
run: mcp-quality badge "python tests/servers/good_server.py" --out mcp-quality-badge.svg
|
|
51
|
+
|
|
52
|
+
determinism:
|
|
53
|
+
name: fast-path determinism guard
|
|
54
|
+
runs-on: ubuntu-latest
|
|
55
|
+
steps:
|
|
56
|
+
- uses: actions/checkout@v4
|
|
57
|
+
- uses: actions/setup-python@v5
|
|
58
|
+
with:
|
|
59
|
+
python-version: "3.12"
|
|
60
|
+
- run: pip install -e .
|
|
61
|
+
# Fast path must be byte-identical across runs (NFR-2). Meta/timing is excluded by
|
|
62
|
+
# comparing the report bodies with a stable serialization.
|
|
63
|
+
- name: run twice, diff the JSON
|
|
64
|
+
run: |
|
|
65
|
+
mcp-quality static tests/servers/dump.mcp.json --json > a.json
|
|
66
|
+
mcp-quality static tests/servers/dump.mcp.json --json > b.json
|
|
67
|
+
python - <<'PY'
|
|
68
|
+
import json
|
|
69
|
+
a = json.load(open("a.json")); b = json.load(open("b.json"))
|
|
70
|
+
a.pop("meta", None); b.pop("meta", None)
|
|
71
|
+
assert a == b, "fast-path output was not deterministic"
|
|
72
|
+
print("deterministic OK")
|
|
73
|
+
PY
|
|
74
|
+
|
|
75
|
+
offline:
|
|
76
|
+
name: static mode (air-gapped)
|
|
77
|
+
runs-on: ubuntu-latest
|
|
78
|
+
steps:
|
|
79
|
+
- uses: actions/checkout@v4
|
|
80
|
+
- uses: actions/setup-python@v5
|
|
81
|
+
with:
|
|
82
|
+
python-version: "3.12"
|
|
83
|
+
- run: pip install -e .
|
|
84
|
+
- name: static scan needs no network
|
|
85
|
+
run: mcp-quality static tests/servers/dump.mcp.json --json
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
|
|
3
|
+
# Publishes the `mcp-quality` package to PyPI when a GitHub Release is published. Uses
|
|
4
|
+
# PyPI Trusted Publishing (OIDC) — no API token/secret is stored anywhere. One-time setup:
|
|
5
|
+
# pypi.org → Publishing → add a pending publisher:
|
|
6
|
+
# PyPI project=mcp-quality owner=swarmproof repo=mcp-probe workflow=release.yml environment=pypi
|
|
7
|
+
# Then: create a GitHub Release tagged v0.1.0 → this workflow builds and uploads.
|
|
8
|
+
|
|
9
|
+
on:
|
|
10
|
+
release:
|
|
11
|
+
types: [published]
|
|
12
|
+
|
|
13
|
+
jobs:
|
|
14
|
+
build:
|
|
15
|
+
runs-on: ubuntu-latest
|
|
16
|
+
steps:
|
|
17
|
+
- uses: actions/checkout@v4
|
|
18
|
+
- uses: actions/setup-python@v5
|
|
19
|
+
with:
|
|
20
|
+
python-version: "3.12"
|
|
21
|
+
- name: Build sdist + wheel
|
|
22
|
+
run: |
|
|
23
|
+
pip install build
|
|
24
|
+
python -m build
|
|
25
|
+
- name: Verify metadata
|
|
26
|
+
run: |
|
|
27
|
+
pip install twine
|
|
28
|
+
twine check dist/*
|
|
29
|
+
- uses: actions/upload-artifact@v4
|
|
30
|
+
with:
|
|
31
|
+
name: dist
|
|
32
|
+
path: dist/
|
|
33
|
+
|
|
34
|
+
publish:
|
|
35
|
+
needs: build
|
|
36
|
+
runs-on: ubuntu-latest
|
|
37
|
+
environment: pypi
|
|
38
|
+
permissions:
|
|
39
|
+
id-token: write # required for Trusted Publishing (OIDC)
|
|
40
|
+
steps:
|
|
41
|
+
- uses: actions/download-artifact@v4
|
|
42
|
+
with:
|
|
43
|
+
name: dist
|
|
44
|
+
path: dist/
|
|
45
|
+
- name: Publish to PyPI
|
|
46
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
# Byte-compiled / optimized / DLL files
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[codz]
|
|
4
|
+
*$py.class
|
|
5
|
+
|
|
6
|
+
# C extensions
|
|
7
|
+
*.so
|
|
8
|
+
|
|
9
|
+
# Distribution / packaging
|
|
10
|
+
.Python
|
|
11
|
+
build/
|
|
12
|
+
develop-eggs/
|
|
13
|
+
dist/
|
|
14
|
+
downloads/
|
|
15
|
+
eggs/
|
|
16
|
+
.eggs/
|
|
17
|
+
lib/
|
|
18
|
+
lib64/
|
|
19
|
+
parts/
|
|
20
|
+
sdist/
|
|
21
|
+
var/
|
|
22
|
+
wheels/
|
|
23
|
+
share/python-wheels/
|
|
24
|
+
*.egg-info/
|
|
25
|
+
.installed.cfg
|
|
26
|
+
*.egg
|
|
27
|
+
MANIFEST
|
|
28
|
+
|
|
29
|
+
# PyInstaller
|
|
30
|
+
# Usually these files are written by a python script from a template
|
|
31
|
+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
|
32
|
+
*.manifest
|
|
33
|
+
*.spec
|
|
34
|
+
|
|
35
|
+
# Installer logs
|
|
36
|
+
pip-log.txt
|
|
37
|
+
pip-delete-this-directory.txt
|
|
38
|
+
|
|
39
|
+
# Unit test / coverage reports
|
|
40
|
+
htmlcov/
|
|
41
|
+
.tox/
|
|
42
|
+
.nox/
|
|
43
|
+
.coverage
|
|
44
|
+
.coverage.*
|
|
45
|
+
.cache
|
|
46
|
+
nosetests.xml
|
|
47
|
+
coverage.xml
|
|
48
|
+
*.cover
|
|
49
|
+
*.py.cover
|
|
50
|
+
.hypothesis/
|
|
51
|
+
.pytest_cache/
|
|
52
|
+
cover/
|
|
53
|
+
|
|
54
|
+
# Translations
|
|
55
|
+
*.mo
|
|
56
|
+
*.pot
|
|
57
|
+
|
|
58
|
+
# Django stuff:
|
|
59
|
+
*.log
|
|
60
|
+
local_settings.py
|
|
61
|
+
db.sqlite3
|
|
62
|
+
db.sqlite3-journal
|
|
63
|
+
|
|
64
|
+
# Flask stuff:
|
|
65
|
+
instance/
|
|
66
|
+
.webassets-cache
|
|
67
|
+
|
|
68
|
+
# Scrapy stuff:
|
|
69
|
+
.scrapy
|
|
70
|
+
|
|
71
|
+
# Sphinx documentation
|
|
72
|
+
docs/_build/
|
|
73
|
+
|
|
74
|
+
# PyBuilder
|
|
75
|
+
.pybuilder/
|
|
76
|
+
target/
|
|
77
|
+
|
|
78
|
+
# Jupyter Notebook
|
|
79
|
+
.ipynb_checkpoints
|
|
80
|
+
|
|
81
|
+
# IPython
|
|
82
|
+
profile_default/
|
|
83
|
+
ipython_config.py
|
|
84
|
+
|
|
85
|
+
# pyenv
|
|
86
|
+
# For a library or package, you might want to ignore these files since the code is
|
|
87
|
+
# intended to run in multiple environments; otherwise, check them in:
|
|
88
|
+
# .python-version
|
|
89
|
+
|
|
90
|
+
# pipenv
|
|
91
|
+
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
|
92
|
+
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
|
93
|
+
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
|
94
|
+
# install all needed dependencies.
|
|
95
|
+
# Pipfile.lock
|
|
96
|
+
|
|
97
|
+
# UV
|
|
98
|
+
# Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
|
|
99
|
+
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
|
100
|
+
# commonly ignored for libraries.
|
|
101
|
+
# uv.lock
|
|
102
|
+
|
|
103
|
+
# poetry
|
|
104
|
+
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
|
105
|
+
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
|
106
|
+
# commonly ignored for libraries.
|
|
107
|
+
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
|
108
|
+
# poetry.lock
|
|
109
|
+
# poetry.toml
|
|
110
|
+
|
|
111
|
+
# pdm
|
|
112
|
+
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
|
|
113
|
+
# pdm recommends including project-wide configuration in pdm.toml, but excluding .pdm-python.
|
|
114
|
+
# https://pdm-project.org/en/latest/usage/project/#working-with-version-control
|
|
115
|
+
# pdm.lock
|
|
116
|
+
# pdm.toml
|
|
117
|
+
.pdm-python
|
|
118
|
+
.pdm-build/
|
|
119
|
+
|
|
120
|
+
# pixi
|
|
121
|
+
# Similar to Pipfile.lock, it is generally recommended to include pixi.lock in version control.
|
|
122
|
+
# pixi.lock
|
|
123
|
+
# Pixi creates a virtual environment in the .pixi directory, just like venv module creates one
|
|
124
|
+
# in the .venv directory. It is recommended not to include this directory in version control.
|
|
125
|
+
.pixi
|
|
126
|
+
|
|
127
|
+
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
|
128
|
+
__pypackages__/
|
|
129
|
+
|
|
130
|
+
# Celery stuff
|
|
131
|
+
celerybeat-schedule
|
|
132
|
+
celerybeat.pid
|
|
133
|
+
|
|
134
|
+
# Redis
|
|
135
|
+
*.rdb
|
|
136
|
+
*.aof
|
|
137
|
+
*.pid
|
|
138
|
+
|
|
139
|
+
# RabbitMQ
|
|
140
|
+
mnesia/
|
|
141
|
+
rabbitmq/
|
|
142
|
+
rabbitmq-data/
|
|
143
|
+
|
|
144
|
+
# ActiveMQ
|
|
145
|
+
activemq-data/
|
|
146
|
+
|
|
147
|
+
# SageMath parsed files
|
|
148
|
+
*.sage.py
|
|
149
|
+
|
|
150
|
+
# Environments
|
|
151
|
+
.env
|
|
152
|
+
.envrc
|
|
153
|
+
.venv
|
|
154
|
+
env/
|
|
155
|
+
venv/
|
|
156
|
+
ENV/
|
|
157
|
+
env.bak/
|
|
158
|
+
venv.bak/
|
|
159
|
+
|
|
160
|
+
# Spyder project settings
|
|
161
|
+
.spyderproject
|
|
162
|
+
.spyproject
|
|
163
|
+
|
|
164
|
+
# Rope project settings
|
|
165
|
+
.ropeproject
|
|
166
|
+
|
|
167
|
+
# mkdocs documentation
|
|
168
|
+
/site
|
|
169
|
+
|
|
170
|
+
# mypy
|
|
171
|
+
.mypy_cache/
|
|
172
|
+
.dmypy.json
|
|
173
|
+
dmypy.json
|
|
174
|
+
|
|
175
|
+
# Pyre type checker
|
|
176
|
+
.pyre/
|
|
177
|
+
|
|
178
|
+
# pytype static type analyzer
|
|
179
|
+
.pytype/
|
|
180
|
+
|
|
181
|
+
# Cython debug symbols
|
|
182
|
+
cython_debug/
|
|
183
|
+
|
|
184
|
+
# PyCharm
|
|
185
|
+
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
|
186
|
+
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
|
187
|
+
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
|
188
|
+
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
|
189
|
+
# .idea/
|
|
190
|
+
|
|
191
|
+
# Abstra
|
|
192
|
+
# Abstra is an AI-powered process automation framework.
|
|
193
|
+
# Ignore directories containing user credentials, local state, and settings.
|
|
194
|
+
# Learn more at https://abstra.io/docs
|
|
195
|
+
.abstra/
|
|
196
|
+
|
|
197
|
+
# Visual Studio Code
|
|
198
|
+
# Visual Studio Code specific template is maintained in a separate VisualStudioCode.gitignore
|
|
199
|
+
# that can be found at https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore
|
|
200
|
+
# and can be added to the global gitignore or merged into this file. However, if you prefer,
|
|
201
|
+
# you could uncomment the following to ignore the entire vscode folder
|
|
202
|
+
# .vscode/
|
|
203
|
+
# Temporary file for partial code execution
|
|
204
|
+
tempCodeRunnerFile.py
|
|
205
|
+
|
|
206
|
+
# Ruff stuff:
|
|
207
|
+
.ruff_cache/
|
|
208
|
+
|
|
209
|
+
# PyPI configuration file
|
|
210
|
+
.pypirc
|
|
211
|
+
|
|
212
|
+
# Marimo
|
|
213
|
+
marimo/_static/
|
|
214
|
+
marimo/_lsp/
|
|
215
|
+
__marimo__/
|
|
216
|
+
|
|
217
|
+
# Streamlit
|
|
218
|
+
.streamlit/secrets.toml
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
# project
|
|
222
|
+
*.db
|
|
223
|
+
stampede-report.html
|
|
224
|
+
.env
|
|
225
|
+
.venv/
|
|
226
|
+
|
|
227
|
+
# mcp-quality runtime artifacts (the snapshot baseline IS committed; the cache is not)
|
|
228
|
+
.mcp-quality/cache/
|
|
229
|
+
*.badge.svg
|
|
230
|
+
mcp-quality-report.json
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"low_tools": ["archive_record"], "matrix": {"archive_record": {"delete_record": 3}, "delete_record": {}}, "mean_confusion": 1.0, "per_tool_total": {"archive_record": 3, "delete_record": 3}, "rewrites": [{"old": "Remove a record by id.", "rewrite": "Remove a record by its ID using `delete_record`. Use `archive_record` to move a record to the archive without deleting it.", "tool": "archive_record"}, {"old": "Remove a record by id.", "rewrite": "Delete a record permanently using its ID; archive a record temporarily using the 'archive_record' function.", "tool": "delete_record"}], "selection_rate": 0.5, "tool_order": ["delete_record", "archive_record"], "top_confusion": ["archive_record", "delete_record", 1.0]}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# Example mcp-quality configuration. Copy to `.mcp-quality.toml` (repo root).
|
|
2
|
+
# Precedence: CLI flags > this file > MCP_PROBE_* env vars > defaults.
|
|
3
|
+
# Either a flat table (below) or a [tool.mcp-quality] section is accepted.
|
|
4
|
+
|
|
5
|
+
# Which families to run. Default is the zero-LLM fast path: ["contract", "cost"].
|
|
6
|
+
families = ["contract", "cost", "security"]
|
|
7
|
+
|
|
8
|
+
# --- gating ---
|
|
9
|
+
fail_under = "B" # exit non-zero if overall grade < B
|
|
10
|
+
no_regressions = true # exit non-zero on any regression vs the committed snapshot
|
|
11
|
+
|
|
12
|
+
# --- safety ---
|
|
13
|
+
allow_writes = false # never invoke destructive tools unless true (NFR-9)
|
|
14
|
+
|
|
15
|
+
# --- cost ---
|
|
16
|
+
tokenizer = "o200k_base" # offline tiktoken encoding for deterministic counts
|
|
17
|
+
# price_points = ["premium:3.0", "cheap:0.15"] # $ per 1M input tokens
|
|
18
|
+
# token_model = "anthropic:claude-sonnet-5" # opt-in authoritative Claude count; needs
|
|
19
|
+
# # ANTHROPIC_API_KEY, else falls back to estimate
|
|
20
|
+
|
|
21
|
+
# --- legibility ([llm], opt-in) ---
|
|
22
|
+
# model = "ollama:qwen2.5-3b" # pinned local model is the canonical scorer (ADR-004)
|
|
23
|
+
seed = 42
|
|
24
|
+
goal_set_version = "1"
|
|
25
|
+
|
|
26
|
+
# --- performance ([net]) ---
|
|
27
|
+
concurrency = 50
|
|
28
|
+
|
|
29
|
+
# --- security ---
|
|
30
|
+
deep_security = false # true → shell out to mcp-scan / Cisco if on PATH
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
# AGENTS.md
|
|
2
|
+
|
|
3
|
+
This file provides guidance to Codex (Codex.ai/code) when working with code in this repository.
|
|
4
|
+
|
|
5
|
+
## Current state: v0.1 implemented (all five families)
|
|
6
|
+
|
|
7
|
+
The v0.1 fast path and all five check families are implemented, tested, and dogfooded.
|
|
8
|
+
Python 3.11+ (`src/` layout, `pip install`), official MCP SDK, `asyncio`. What deviates
|
|
9
|
+
from the spec is recorded in **`docs/DECISIONS.md`** — read it before changing security
|
|
10
|
+
IDs, the handshake, or the token counter.
|
|
11
|
+
|
|
12
|
+
### Commands
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
python -m venv .venv && .venv/bin/pip install -e ".[dev]" # setup
|
|
16
|
+
|
|
17
|
+
.venv/bin/pytest -m "not live_llm" -q # full suite (unit+component+integration+e2e)
|
|
18
|
+
.venv/bin/pytest tests/test_scorer.py -q # a single test file
|
|
19
|
+
.venv/bin/pytest -m e2e -q # only the live-fixture E2E scenarios
|
|
20
|
+
.venv/bin/pytest -m live_llm -q # opt-in, calls a real model (excluded by default)
|
|
21
|
+
.venv/bin/ruff check src/ tests/ # lint (line-length 110)
|
|
22
|
+
.venv/bin/mypy src/ # types (strict)
|
|
23
|
+
|
|
24
|
+
.venv/bin/mcp-quality run ".venv/bin/python tests/servers/good_server.py" # live probe
|
|
25
|
+
.venv/bin/mcp-quality static tests/servers/dump.mcp.json --json # offline
|
|
26
|
+
.venv/bin/mcp-quality run "…" --all --model ollama:qwen2.5-3b # all five families
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Tests spawn fixture servers with the **same interpreter running pytest** (`sys.executable`),
|
|
30
|
+
so they need the SDK installed in that env — always run via `.venv/bin/pytest`.
|
|
31
|
+
|
|
32
|
+
### Package layout (`src/mcp_quality/`)
|
|
33
|
+
|
|
34
|
+
- `models.py` — the frozen data model (`ServerSurface`/`Finding`/`FamilyScore`/`Report`/`CheckEngine`).
|
|
35
|
+
- `config.py` · `cli.py` · `exit_codes.py` — config precedence, the 4 subcommands, CI exit codes.
|
|
36
|
+
- `connect/` — `transport.py` is the **only** module importing the MCP SDK; `client.py` is the
|
|
37
|
+
façade + `FakeClient`; `discover.py` builds surfaces (live + static dump).
|
|
38
|
+
- `engines/` — one file per family, each a pure `CheckEngine`; registered in `engines/__init__.py`.
|
|
39
|
+
- `contract/`, `legibility/`, `security/`, `perf/`, `tokens.py` — engine-specific internals.
|
|
40
|
+
- `scoring/` · `snapshot/` · `report/` · `handoff.py` · `trace.py` — aggregation & outputs.
|
|
41
|
+
- `tests/servers/` — fixture MCP servers (the TEST-PLAN §2 matrix) + `dump.mcp.json`.
|
|
42
|
+
|
|
43
|
+
## What mcp-quality is
|
|
44
|
+
|
|
45
|
+
The **CI quality suite for MCP servers** — "the `pytest` + `lighthouse` for the servers agents depend on." It connects to an MCP server, discovers its surface, and grades it across **five check families** into a single A–F **MCP Quality Score**, designed to run as a CI gate with a README badge.
|
|
46
|
+
|
|
47
|
+
Positioning is load-bearing and deliberate: security scanners (mcp-scan, Cisco) answer *"is this server dangerous?"*; mcp-quality answers *"is this server good?"* It treats security as **one light check**, defers deep security to the incumbents via `--deep-security` integration (never reimplements them), and unifies the quality *point* tools (credits mcp-xray as prior art) into a CI-native **suite and gate**. Do not drift the messaging toward "another scanner."
|
|
48
|
+
|
|
49
|
+
The five families: **Contract** (LLM-free spec/schema/determinism), **Legibility** (the differentiator — agent-comprehension score + disambiguation matrix), **Cost** (toolset token weight), **Performance** (concurrent MCP-semantic load), **Security-lite** (OWASP MCP Top 10 basics + integration adapter).
|
|
50
|
+
|
|
51
|
+
## Documentation hierarchy (read in this order)
|
|
52
|
+
|
|
53
|
+
The docs are authoritative and layered — consult them before implementing anything:
|
|
54
|
+
|
|
55
|
+
1. **`SPEC.md`** — the frozen v1.0 design spec/PRD. The baseline.
|
|
56
|
+
2. **`docs/PRD.md`** — numbered, testable requirements: `REQ-*` (functional, per family) and `NFR-*` (non-functional). This is the requirement-of-record; code and tests reference these IDs.
|
|
57
|
+
3. **`docs/ARCHITECTURE.md`** — system design, the core data model (`ServerSurface`, `Finding`, `FamilyScore`, `Report`, `CheckEngine`), the JSON output schema, and the **ADRs** (ADR-001..009) that bind implementation choices.
|
|
58
|
+
4. **`docs/DELIVERY-PLAN.md`** — the WBS (`W0..W6`), effort sizing, critical path, and v0.1 Definition of Done.
|
|
59
|
+
5. **`docs/TEST-PLAN.md`** — the acceptance backbone: E2E scenarios (`E2E-1..10`), the fixture server matrix, the `StubModel` determinism harness, and CI gates.
|
|
60
|
+
6. **`docs/RESEARCH.md`** — the competitive/market analysis the positioning rests on.
|
|
61
|
+
|
|
62
|
+
**Conventions in the docs that carry into code:**
|
|
63
|
+
- The **`⊕ Beyond original spec`** marker flags anything extending the frozen v1.0 `SPEC.md`. Preserve it when editing docs; it tracks scope past the baseline.
|
|
64
|
+
- Requirement IDs (`REQ-C4`, `NFR-2`, etc.) and finding codes (`C5-nondeterminism`, `S1-owasp-mcp05`) are stable identifiers — reference them in commits, tests, and `Finding.code`.
|
|
65
|
+
- Tier labels: **[fast]** = zero-LLM deterministic, **[llm]** = needs a small model, **[net]** = needs a live server, **[static-ok]** = works offline in `static` mode.
|
|
66
|
+
|
|
67
|
+
## Architecture: the binding decisions
|
|
68
|
+
|
|
69
|
+
The system is a **pipeline**: `connect → discover → fan out to five engines → Scorer → Renderer/JSON/badge`. When implementing, these ADRs are constraints, not suggestions:
|
|
70
|
+
|
|
71
|
+
- **ADR-001 — Engines are pure functions of `ServerSurface` (+ optional live client) → `FamilyScore`.** No engine mutates shared state; the Scorer and Renderer are the *only* aggregators. This is what makes the fast path deterministic and every engine testable with a fixed `ServerSurface` and no network/LLM. Adding a sixth family = implement the `CheckEngine` protocol and register it. **Smuggling shared mutable state into an engine violates the architecture.**
|
|
72
|
+
- **ADR-002 — Zero-LLM fast path is the CI default.** Contract + Cost + Performance run with no model calls (`NFR-1`); Legibility is opt-in and off the critical path. The gate must be satisfiable by the fast path alone.
|
|
73
|
+
- **ADR-003 — Version-aware connect.** The MCP spec is mid-transition (2025-11-25 legacy `initialize` handshake ↔ 2026-07-28 `server/discover` + `_meta`). Negotiate both, grade the result, require neither. This is the hardest correctness surface.
|
|
74
|
+
- **ADR-004 — Canonical Legibility scorer = pinned local model, temp 0, fixed seed, cached by `(surface_hash, model_id, seed, goal_set_version)`.** Cloud models are opt-in and marked non-canonical. A rerun on an unchanged surface is a cache hit → ~$0, byte-identical.
|
|
75
|
+
- **ADR-005 — `--deep-security` shells out and normalizes; never reimplements scanners.** Missing scanner → "not measured", never a failure.
|
|
76
|
+
- **ADR-006 — `static` mode reports live-only checks as "not measured", never `0`.** Zeroing unmeasured checks is a bug (it punishes offline use and is gameable).
|
|
77
|
+
- **ADR-009 — Read-only by default** (`NFR-9`); destructive tools (destructiveHint / heuristic) are skipped unless `--allow-writes`. Probing a `delete_*` tool must not fire it.
|
|
78
|
+
|
|
79
|
+
### Determinism doctrine (the whole value prop)
|
|
80
|
+
|
|
81
|
+
The tool grades code in CI, so its own output must be trustworthy:
|
|
82
|
+
- **Fast path** (Contract/Cost/Security-lite built-in): **byte-identical** output for identical input, enforced by golden-file tests. No wall-clock or network-order dependence in scoring.
|
|
83
|
+
- **Legibility**: deterministic only *under a fixed (model, seed, goal-set)*; tests use `StubModel` (never a real LLM) and assert the cache serves reruns with `call_count == 0`.
|
|
84
|
+
- **Performance latencies**: inherently nondeterministic → assert *invariants* (percentile ordering `p50≤p95≤p99`, leak detection, degradation classification), never absolute ms.
|
|
85
|
+
|
|
86
|
+
### Scoring rubric
|
|
87
|
+
|
|
88
|
+
Weighted mean of five 0–100 sub-scores → letter grade. Default weights: **Cost 30%, Legibility 25%, Contract 20%, Performance 15%, Security-lite 10%** (see `docs/PRD.md` §7 for rationale). A family scoring **F caps overall at C** (the "hard-gate" — no A-grade server with a broken contract or critical security finding). Every report and badge carries `rubric_version` for cross-release comparability (`NFR-7`).
|
|
89
|
+
|
|
90
|
+
### Shared primitives (vendored, bound to stampede's contracts)
|
|
91
|
+
|
|
92
|
+
mcp-quality is project #3 of the seven-project **Swarm Proof** toolkit and reuses four primitives. The portfolio decision is **vendor-first**: copy minimal versions now rather than wait on extraction (~stampede v0.2). But the *contracts* are authoritative and must not be forked:
|
|
93
|
+
- **concurrency-core** — the Performance load driver imports stampede's `Scheduler`/`Executor` **Protocol** and supplies uniform MCP-client tasks (not persona logic).
|
|
94
|
+
- **report-renderer** — render via the shared `RunReport` model (oxblood style); register a `QualityScoreReport` view over it.
|
|
95
|
+
- **trace-format** — **the OpenTelemetry GenAI semantic-conventions *profile*** (`gen_ai.*` spans + the `swarmproof.*` extension), *not* a bespoke schema. The `stampede --from-probe` handoff depends on cross-tool trace compatibility — consume the profile, don't fork it.
|
|
96
|
+
- **persona-pack** — one minimal `naive` persona (`apiVersion: swarmproof.dev/persona/v1`) for the Legibility probe.
|
|
97
|
+
|
|
98
|
+
## Working conventions
|
|
99
|
+
|
|
100
|
+
- **Commits:** [Conventional Commits](https://www.conventionalcommits.org/) (`feat:`, `fix:`, `docs:`, `refactor:`, `test:`, `chore:`), atomic, imperative mood, no AI attribution/signatures. Commit progressively as you go.
|
|
101
|
+
- **Testability first:** because engines are pure functions, prefer a component test that feeds a fixed `ServerSurface` and asserts an exact `FamilyScore` over any test that needs a network or a real model. Live/LLM behavior belongs in integration/E2E with fakes (`StubModel`) or the opt-in, network-gated `-m live_llm` suite (excluded from the default/PR run).
|
|
102
|
+
- **Dogfood:** the intended CI runs `mcp-quality` against its own `tests/servers/` fixtures — the tool must grade its own sample servers correctly (see `docs/TEST-PLAN.md` §9).
|
|
103
|
+
- **Honest over impressive:** document boundaries; never zero an unmeasured check; mark non-canonical scores as such.
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to mcp-quality are documented here. Format follows
|
|
4
|
+
[Keep a Changelog](https://keepachangelog.com/); versions follow [SemVer](https://semver.org/).
|
|
5
|
+
|
|
6
|
+
## [0.1.0] — unreleased
|
|
7
|
+
|
|
8
|
+
First public release: the CI quality suite for MCP servers. Grades any MCP server across
|
|
9
|
+
five check families into a single **MCP Quality Score**, gates CI, and prints a badge.
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
- **Contract** `[fast]` — JSON-RPC/handshake conformance, JSON-Schema validity,
|
|
13
|
+
deterministic argument synthesis + invocation, output-shape conformance, and a
|
|
14
|
+
determinism probe. Read-only by default; destructive tools skipped unless `--allow-writes`.
|
|
15
|
+
- **Cost** `[fast]` — whole-toolset token count, leave-one-out per-tool attribution,
|
|
16
|
+
$-per-task. Offline-deterministic (tiktoken, labeled as an estimate) with an **opt-in**
|
|
17
|
+
authoritative Anthropic `count_tokens` path (`--token-model anthropic:<model>`) that
|
|
18
|
+
falls back silently without a key.
|
|
19
|
+
- **Security-lite** `[fast]` — injection/tool-poisoning, secrets (regex + entropy), and
|
|
20
|
+
dangerous-capability lints mapped to the **OWASP MCP Top 10** (`MCP01/03/05:2025`);
|
|
21
|
+
`--deep-security` folds in mcp-scan (Snyk) / Cisco findings when installed.
|
|
22
|
+
- **Performance** `[net]` — concurrent load with real MCP semantics, p50/p95/p99, max
|
|
23
|
+
stable concurrency, degradation classification, and connection-leak detection.
|
|
24
|
+
- **Legibility** `[llm]` — offline description lints + lexical confusable-shortlist
|
|
25
|
+
always; a seeded comprehension probe, an **N×N disambiguation matrix**, and proposed
|
|
26
|
+
rewrites when a model is configured. Results cached by
|
|
27
|
+
`(surface_hash, model, seed, goal_set)` — warm reruns invoke the model zero times.
|
|
28
|
+
- Transports: **stdio**, **Streamable-HTTP**, and legacy **SSE**; version-aware
|
|
29
|
+
`initialize` handshake (spec `2025-11-25`).
|
|
30
|
+
- CLI: `run` · `static` (offline/air-gapped) · `snapshot` (+ `--no-regressions`) · `badge`.
|
|
31
|
+
- Outputs: graded terminal report, HTML, versioned JSON (`mcp-quality/report@1`) with
|
|
32
|
+
`--fail-under`, an SVG + shields.io badge, and the `stampede --from-probe` handoff seed.
|
|
33
|
+
- Scoring: weighted mean (Cost 30 / Legibility 25 / Contract 20 / Performance 15 /
|
|
34
|
+
Security 10), hard-gate cap at C, versioned rubric (`2026.07.1`).
|
|
35
|
+
- 139 tests (unit + component + integration + E2E over stdio & HTTP/SSE), opt-in
|
|
36
|
+
`live_llm` / `deep_security` suites, and a dogfooding CI (test / dogfood / determinism /
|
|
37
|
+
offline jobs).
|
|
38
|
+
- A reproducible [leaderboard](docs/leaderboard.md) of real public MCP servers and a
|
|
39
|
+
captured [demo](docs/demo.md).
|
|
40
|
+
|
|
41
|
+
Also included (planned as the "v0.2" milestone, shipped in this first release):
|
|
42
|
+
- **Legibility auto-fix** — `mcp-quality fix` applies the proposed description rewrites to
|
|
43
|
+
source (name-anchored find/replace), with `--apply` / `--pr` (REQ-L7).
|
|
44
|
+
- **Historical tracking** — `mcp-quality run --record` + `mcp-quality compare` + a sticky PR
|
|
45
|
+
score-delta comment workflow.
|
|
46
|
+
- **Cost** — response-bloat sampling (`--response-bloat`) and lazy-loading remediation
|
|
47
|
+
hints (REQ-$5/$6).
|
|
48
|
+
- **Contract** — error-path conformance (malformed input must not crash) + deprecated-SSE
|
|
49
|
+
forward-compat lint (REQ-C9/C10).
|
|
50
|
+
- **Security** — Cisco `readiness` analyzer folded into Performance/Contract (REQ-S6).
|
|
51
|
+
- **Transport/gating** — HTTP/SSE auth headers (`--header`), per-family gates
|
|
52
|
+
(`--fail-under-family`).
|
|
53
|
+
- **Registry scoring API** — `mcp-quality serve` (`POST /score` · `/verify` · `/healthz`),
|
|
54
|
+
in the `[registry]` extra.
|
|
55
|
+
|
|
56
|
+
### Dependencies
|
|
57
|
+
- Pinned `mcp>=1.28,<2`: SDK v2 renamed `FastMCP`→`MCPServer` and changed result-field
|
|
58
|
+
casing. Migration to v2 is tracked separately.
|
|
59
|
+
|
|
60
|
+
### Notes
|
|
61
|
+
- Deviations from the design spec are recorded in [docs/DECISIONS.md](docs/DECISIONS.md)
|
|
62
|
+
(OWASP MCP Top 10 mapping; why the `2026-07-28` stateless `server/discover` path is not
|
|
63
|
+
yet implemented; the offline-token estimate).
|
|
64
|
+
|
|
65
|
+
[0.1.0]: https://github.com/swarmproof/mcp-probe/releases/tag/v0.1.0
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
cff-version: 1.2.0
|
|
2
|
+
message: "If you use this software or dataset, please cite it as below."
|
|
3
|
+
title: "mcp-quality"
|
|
4
|
+
abstract: "The CI quality suite for MCP server builders. Part of the Swarm Proof toolkit."
|
|
5
|
+
authors:
|
|
6
|
+
- family-names: Boudoukha
|
|
7
|
+
given-names: Maroua
|
|
8
|
+
repository-code: "https://github.com/swarmproof/mcp-probe"
|
|
9
|
+
url: "https://github.com/swarmproof/mcp-probe"
|
|
10
|
+
license: Apache-2.0
|
|
11
|
+
type: software
|
|
12
|
+
keywords:
|
|
13
|
+
- ai-agents
|
|
14
|
+
- agent-reliability
|
|
15
|
+
- llm
|
|
16
|
+
- mcp
|