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.
Files changed (105) hide show
  1. mcp_quality-0.1.0/.github/ISSUE_TEMPLATE/bug_report.md +13 -0
  2. mcp_quality-0.1.0/.github/ISSUE_TEMPLATE/feature_request.md +11 -0
  3. mcp_quality-0.1.0/.github/good-first-issues.md +8 -0
  4. mcp_quality-0.1.0/.github/workflows/ci.yml +85 -0
  5. mcp_quality-0.1.0/.github/workflows/release.yml +46 -0
  6. mcp_quality-0.1.0/.gitignore +230 -0
  7. mcp_quality-0.1.0/.mcp-probe/cache/legibility-868b57b70e113604d1906f0c.json +1 -0
  8. mcp_quality-0.1.0/.mcp-quality.toml.example +30 -0
  9. mcp_quality-0.1.0/AGENTS.md +103 -0
  10. mcp_quality-0.1.0/CHANGELOG.md +65 -0
  11. mcp_quality-0.1.0/CITATION.cff +16 -0
  12. mcp_quality-0.1.0/CLAUDE.md +103 -0
  13. mcp_quality-0.1.0/CONTRIBUTING.md +21 -0
  14. mcp_quality-0.1.0/LICENSE +202 -0
  15. mcp_quality-0.1.0/PKG-INFO +119 -0
  16. mcp_quality-0.1.0/README.md +81 -0
  17. mcp_quality-0.1.0/ROADMAP.md +25 -0
  18. mcp_quality-0.1.0/SPEC.md +121 -0
  19. mcp_quality-0.1.0/docs/ARCHITECTURE.md +391 -0
  20. mcp_quality-0.1.0/docs/DECISIONS.md +94 -0
  21. mcp_quality-0.1.0/docs/DELIVERY-PLAN.md +165 -0
  22. mcp_quality-0.1.0/docs/PRD.md +233 -0
  23. mcp_quality-0.1.0/docs/RESEARCH.md +223 -0
  24. mcp_quality-0.1.0/docs/TEST-PLAN.md +185 -0
  25. mcp_quality-0.1.0/docs/demo.md +72 -0
  26. mcp_quality-0.1.0/docs/examples/score-delta-workflow.yml +50 -0
  27. mcp_quality-0.1.0/docs/launch/essay.md +112 -0
  28. mcp_quality-0.1.0/docs/leaderboard.json +107 -0
  29. mcp_quality-0.1.0/docs/leaderboard.md +22 -0
  30. mcp_quality-0.1.0/pyproject.toml +104 -0
  31. mcp_quality-0.1.0/scripts/demo.sh +33 -0
  32. mcp_quality-0.1.0/scripts/leaderboard.py +224 -0
  33. mcp_quality-0.1.0/src/mcp_quality/__init__.py +19 -0
  34. mcp_quality-0.1.0/src/mcp_quality/cli.py +431 -0
  35. mcp_quality-0.1.0/src/mcp_quality/config.py +139 -0
  36. mcp_quality-0.1.0/src/mcp_quality/connect/__init__.py +29 -0
  37. mcp_quality-0.1.0/src/mcp_quality/connect/client.py +88 -0
  38. mcp_quality-0.1.0/src/mcp_quality/connect/discover.py +99 -0
  39. mcp_quality-0.1.0/src/mcp_quality/connect/transport.py +164 -0
  40. mcp_quality-0.1.0/src/mcp_quality/contract/__init__.py +17 -0
  41. mcp_quality-0.1.0/src/mcp_quality/contract/schema.py +175 -0
  42. mcp_quality-0.1.0/src/mcp_quality/engines/__init__.py +39 -0
  43. mcp_quality-0.1.0/src/mcp_quality/engines/base.py +53 -0
  44. mcp_quality-0.1.0/src/mcp_quality/engines/contract.py +283 -0
  45. mcp_quality-0.1.0/src/mcp_quality/engines/cost.py +241 -0
  46. mcp_quality-0.1.0/src/mcp_quality/engines/legibility.py +230 -0
  47. mcp_quality-0.1.0/src/mcp_quality/engines/performance.py +156 -0
  48. mcp_quality-0.1.0/src/mcp_quality/engines/security.py +82 -0
  49. mcp_quality-0.1.0/src/mcp_quality/exit_codes.py +19 -0
  50. mcp_quality-0.1.0/src/mcp_quality/fix.py +127 -0
  51. mcp_quality-0.1.0/src/mcp_quality/handoff.py +80 -0
  52. mcp_quality-0.1.0/src/mcp_quality/history.py +146 -0
  53. mcp_quality-0.1.0/src/mcp_quality/legibility/__init__.py +19 -0
  54. mcp_quality-0.1.0/src/mcp_quality/legibility/cache.py +40 -0
  55. mcp_quality-0.1.0/src/mcp_quality/legibility/lints.py +53 -0
  56. mcp_quality-0.1.0/src/mcp_quality/legibility/model.py +178 -0
  57. mcp_quality-0.1.0/src/mcp_quality/legibility/similarity.py +45 -0
  58. mcp_quality-0.1.0/src/mcp_quality/models.py +321 -0
  59. mcp_quality-0.1.0/src/mcp_quality/perf/__init__.py +25 -0
  60. mcp_quality-0.1.0/src/mcp_quality/perf/load.py +149 -0
  61. mcp_quality-0.1.0/src/mcp_quality/pipeline.py +187 -0
  62. mcp_quality-0.1.0/src/mcp_quality/registry.py +89 -0
  63. mcp_quality-0.1.0/src/mcp_quality/report/__init__.py +13 -0
  64. mcp_quality-0.1.0/src/mcp_quality/report/badge.py +90 -0
  65. mcp_quality-0.1.0/src/mcp_quality/report/json_emitter.py +59 -0
  66. mcp_quality-0.1.0/src/mcp_quality/report/render.py +218 -0
  67. mcp_quality-0.1.0/src/mcp_quality/scoring/__init__.py +10 -0
  68. mcp_quality-0.1.0/src/mcp_quality/scoring/scorer.py +121 -0
  69. mcp_quality-0.1.0/src/mcp_quality/security/__init__.py +11 -0
  70. mcp_quality-0.1.0/src/mcp_quality/security/adapters.py +216 -0
  71. mcp_quality-0.1.0/src/mcp_quality/security/patterns.py +176 -0
  72. mcp_quality-0.1.0/src/mcp_quality/snapshot/__init__.py +17 -0
  73. mcp_quality-0.1.0/src/mcp_quality/snapshot/store.py +136 -0
  74. mcp_quality-0.1.0/src/mcp_quality/tokens.py +110 -0
  75. mcp_quality-0.1.0/src/mcp_quality/trace.py +72 -0
  76. mcp_quality-0.1.0/tests/__init__.py +0 -0
  77. mcp_quality-0.1.0/tests/conftest.py +79 -0
  78. mcp_quality-0.1.0/tests/servers/bloated_server.py +32 -0
  79. mcp_quality-0.1.0/tests/servers/confusable_server.py +23 -0
  80. mcp_quality-0.1.0/tests/servers/dump.mcp.json +376 -0
  81. mcp_quality-0.1.0/tests/servers/flaky_server.py +25 -0
  82. mcp_quality-0.1.0/tests/servers/good_server.py +34 -0
  83. mcp_quality-0.1.0/tests/servers/http_server.py +43 -0
  84. mcp_quality-0.1.0/tests/servers/injection_server.py +24 -0
  85. mcp_quality-0.1.0/tests/servers/writes_server.py +28 -0
  86. mcp_quality-0.1.0/tests/test_config.py +48 -0
  87. mcp_quality-0.1.0/tests/test_contract.py +135 -0
  88. mcp_quality-0.1.0/tests/test_contract_errorpath.py +60 -0
  89. mcp_quality-0.1.0/tests/test_cost.py +145 -0
  90. mcp_quality-0.1.0/tests/test_e2e.py +71 -0
  91. mcp_quality-0.1.0/tests/test_fix.py +105 -0
  92. mcp_quality-0.1.0/tests/test_hardening.py +76 -0
  93. mcp_quality-0.1.0/tests/test_history.py +91 -0
  94. mcp_quality-0.1.0/tests/test_integration_http.py +86 -0
  95. mcp_quality-0.1.0/tests/test_legibility.py +101 -0
  96. mcp_quality-0.1.0/tests/test_legibility_live.py +84 -0
  97. mcp_quality-0.1.0/tests/test_models.py +60 -0
  98. mcp_quality-0.1.0/tests/test_outputs.py +102 -0
  99. mcp_quality-0.1.0/tests/test_performance.py +95 -0
  100. mcp_quality-0.1.0/tests/test_pipeline.py +68 -0
  101. mcp_quality-0.1.0/tests/test_readiness.py +90 -0
  102. mcp_quality-0.1.0/tests/test_registry.py +83 -0
  103. mcp_quality-0.1.0/tests/test_scorer.py +75 -0
  104. mcp_quality-0.1.0/tests/test_security.py +119 -0
  105. mcp_quality-0.1.0/tests/test_snapshot.py +64 -0
@@ -0,0 +1,13 @@
1
+ ---
2
+ name: Bug report
3
+ about: Something behaves incorrectly
4
+ labels: bug
5
+ ---
6
+
7
+ **What happened**
8
+
9
+ **Expected**
10
+
11
+ **Minimal reproduction**
12
+
13
+ **Environment** (OS, Python version, model/provider)
@@ -0,0 +1,11 @@
1
+ ---
2
+ name: Feature request
3
+ about: Suggest an idea
4
+ labels: enhancement
5
+ ---
6
+
7
+ **Problem / motivation**
8
+
9
+ **Proposed solution**
10
+
11
+ **Alternatives considered**
@@ -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