mcplint-cli 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 (150) hide show
  1. mcplint_cli-0.1.0/.github/ISSUE_TEMPLATE/bug_report.md +29 -0
  2. mcplint_cli-0.1.0/.github/ISSUE_TEMPLATE/feature_request.md +23 -0
  3. mcplint_cli-0.1.0/.github/PULL_REQUEST_TEMPLATE.md +11 -0
  4. mcplint_cli-0.1.0/.github/workflows/ci.yml +101 -0
  5. mcplint_cli-0.1.0/.github/workflows/example-scan-mcp-server.yml +54 -0
  6. mcplint_cli-0.1.0/.github/workflows/release.yml +125 -0
  7. mcplint_cli-0.1.0/.gitignore +11 -0
  8. mcplint_cli-0.1.0/.pre-commit-config.yaml +27 -0
  9. mcplint_cli-0.1.0/CHANGELOG.md +96 -0
  10. mcplint_cli-0.1.0/CODE_OF_CONDUCT.md +51 -0
  11. mcplint_cli-0.1.0/CONTRIBUTING.md +75 -0
  12. mcplint_cli-0.1.0/Dockerfile +17 -0
  13. mcplint_cli-0.1.0/IMPLEMENTATION_STATUS.md +271 -0
  14. mcplint_cli-0.1.0/LICENSE +21 -0
  15. mcplint_cli-0.1.0/PKG-INFO +392 -0
  16. mcplint_cli-0.1.0/README.md +341 -0
  17. mcplint_cli-0.1.0/SECURITY.md +52 -0
  18. mcplint_cli-0.1.0/docs/superpowers/plans/2026-07-26-mcplint-phase1.md +1208 -0
  19. mcplint_cli-0.1.0/docs/superpowers/plans/2026-07-26-mcplint-phase2.md +1647 -0
  20. mcplint_cli-0.1.0/examples/ambiguous_customer_server/customer-tools.evals.yaml +35 -0
  21. mcplint_cli-0.1.0/examples/ambiguous_customer_server/pyproject.toml +5 -0
  22. mcplint_cli-0.1.0/examples/ambiguous_customer_server/server.py +46 -0
  23. mcplint_cli-0.1.0/examples/bad_server/pyproject.toml +5 -0
  24. mcplint_cli-0.1.0/examples/bad_server/server.py +133 -0
  25. mcplint_cli-0.1.0/examples/good_server/pyproject.toml +5 -0
  26. mcplint_cli-0.1.0/examples/good_server/server.py +53 -0
  27. mcplint_cli-0.1.0/pyproject.toml +102 -0
  28. mcplint_cli-0.1.0/src/mcplint/__about__.py +1 -0
  29. mcplint_cli-0.1.0/src/mcplint/__init__.py +3 -0
  30. mcplint_cli-0.1.0/src/mcplint/benchmark/__init__.py +0 -0
  31. mcplint_cli-0.1.0/src/mcplint/benchmark/dataset.py +34 -0
  32. mcplint_cli-0.1.0/src/mcplint/benchmark/providers/__init__.py +0 -0
  33. mcplint_cli-0.1.0/src/mcplint/benchmark/providers/anthropic_provider.py +92 -0
  34. mcplint_cli-0.1.0/src/mcplint/benchmark/providers/base.py +26 -0
  35. mcplint_cli-0.1.0/src/mcplint/benchmark/providers/factory.py +51 -0
  36. mcplint_cli-0.1.0/src/mcplint/benchmark/providers/fake.py +26 -0
  37. mcplint_cli-0.1.0/src/mcplint/benchmark/providers/openai_provider.py +25 -0
  38. mcplint_cli-0.1.0/src/mcplint/benchmark/runner.py +43 -0
  39. mcplint_cli-0.1.0/src/mcplint/benchmark/scorer.py +147 -0
  40. mcplint_cli-0.1.0/src/mcplint/cli/__init__.py +0 -0
  41. mcplint_cli-0.1.0/src/mcplint/cli/commands/__init__.py +0 -0
  42. mcplint_cli-0.1.0/src/mcplint/cli/commands/benchmark_cmd.py +88 -0
  43. mcplint_cli-0.1.0/src/mcplint/cli/commands/compare_cmd.py +139 -0
  44. mcplint_cli-0.1.0/src/mcplint/cli/commands/fix_cmd.py +60 -0
  45. mcplint_cli-0.1.0/src/mcplint/cli/commands/inspect_cmd.py +44 -0
  46. mcplint_cli-0.1.0/src/mcplint/cli/commands/rules_cmd.py +30 -0
  47. mcplint_cli-0.1.0/src/mcplint/cli/commands/scan_cmd.py +113 -0
  48. mcplint_cli-0.1.0/src/mcplint/cli/commands/snapshot_cmd.py +33 -0
  49. mcplint_cli-0.1.0/src/mcplint/cli/main.py +49 -0
  50. mcplint_cli-0.1.0/src/mcplint/compare/__init__.py +0 -0
  51. mcplint_cli-0.1.0/src/mcplint/compare/differ.py +148 -0
  52. mcplint_cli-0.1.0/src/mcplint/config/__init__.py +0 -0
  53. mcplint_cli-0.1.0/src/mcplint/config/loader.py +36 -0
  54. mcplint_cli-0.1.0/src/mcplint/config/schema.py +40 -0
  55. mcplint_cli-0.1.0/src/mcplint/core/__init__.py +0 -0
  56. mcplint_cli-0.1.0/src/mcplint/core/engine.py +40 -0
  57. mcplint_cli-0.1.0/src/mcplint/core/registry.py +52 -0
  58. mcplint_cli-0.1.0/src/mcplint/core/rules/__init__.py +0 -0
  59. mcplint_cli-0.1.0/src/mcplint/core/rules/ambiguity.py +157 -0
  60. mcplint_cli-0.1.0/src/mcplint/core/rules/ambiguity_rules.py +109 -0
  61. mcplint_cli-0.1.0/src/mcplint/core/rules/base.py +39 -0
  62. mcplint_cli-0.1.0/src/mcplint/core/rules/builtin.py +45 -0
  63. mcplint_cli-0.1.0/src/mcplint/core/rules/completeness_rules.py +217 -0
  64. mcplint_cli-0.1.0/src/mcplint/core/rules/description_rules.py +110 -0
  65. mcplint_cli-0.1.0/src/mcplint/core/rules/safety_rules.py +139 -0
  66. mcplint_cli-0.1.0/src/mcplint/core/rules/schema_rules.py +101 -0
  67. mcplint_cli-0.1.0/src/mcplint/core/score.py +132 -0
  68. mcplint_cli-0.1.0/src/mcplint/fix/__init__.py +0 -0
  69. mcplint_cli-0.1.0/src/mcplint/fix/suggest.py +183 -0
  70. mcplint_cli-0.1.0/src/mcplint/mcp_client/__init__.py +0 -0
  71. mcplint_cli-0.1.0/src/mcplint/mcp_client/canonical.py +25 -0
  72. mcplint_cli-0.1.0/src/mcplint/mcp_client/persistence.py +17 -0
  73. mcplint_cli-0.1.0/src/mcplint/mcp_client/session.py +80 -0
  74. mcplint_cli-0.1.0/src/mcplint/mcp_client/stdio.py +17 -0
  75. mcplint_cli-0.1.0/src/mcplint/models/__init__.py +0 -0
  76. mcplint_cli-0.1.0/src/mcplint/models/benchmark.py +67 -0
  77. mcplint_cli-0.1.0/src/mcplint/models/common.py +23 -0
  78. mcplint_cli-0.1.0/src/mcplint/models/comparison.py +53 -0
  79. mcplint_cli-0.1.0/src/mcplint/models/contracts.py +39 -0
  80. mcplint_cli-0.1.0/src/mcplint/models/findings.py +44 -0
  81. mcplint_cli-0.1.0/src/mcplint/models/fixes.py +13 -0
  82. mcplint_cli-0.1.0/src/mcplint/models/score.py +25 -0
  83. mcplint_cli-0.1.0/src/mcplint/models/snapshot.py +27 -0
  84. mcplint_cli-0.1.0/src/mcplint/py.typed +0 -0
  85. mcplint_cli-0.1.0/src/mcplint/reporters/__init__.py +0 -0
  86. mcplint_cli-0.1.0/src/mcplint/reporters/benchmark_terminal.py +46 -0
  87. mcplint_cli-0.1.0/src/mcplint/reporters/comparison_terminal.py +61 -0
  88. mcplint_cli-0.1.0/src/mcplint/reporters/fix_markdown.py +34 -0
  89. mcplint_cli-0.1.0/src/mcplint/reporters/html.py +71 -0
  90. mcplint_cli-0.1.0/src/mcplint/reporters/json_reporter.py +9 -0
  91. mcplint_cli-0.1.0/src/mcplint/reporters/sarif.py +77 -0
  92. mcplint_cli-0.1.0/src/mcplint/reporters/templates/report.html.j2 +176 -0
  93. mcplint_cli-0.1.0/src/mcplint/reporters/terminal.py +52 -0
  94. mcplint_cli-0.1.0/tests/__init__.py +0 -0
  95. mcplint_cli-0.1.0/tests/cli/__init__.py +0 -0
  96. mcplint_cli-0.1.0/tests/cli/test_benchmark.py +97 -0
  97. mcplint_cli-0.1.0/tests/cli/test_compare.py +100 -0
  98. mcplint_cli-0.1.0/tests/cli/test_fix.py +77 -0
  99. mcplint_cli-0.1.0/tests/cli/test_inspect.py +21 -0
  100. mcplint_cli-0.1.0/tests/cli/test_rules.py +13 -0
  101. mcplint_cli-0.1.0/tests/cli/test_scan.py +108 -0
  102. mcplint_cli-0.1.0/tests/cli/test_snapshot.py +22 -0
  103. mcplint_cli-0.1.0/tests/integration/__init__.py +0 -0
  104. mcplint_cli-0.1.0/tests/integration/test_ambiguous_customer_server.py +28 -0
  105. mcplint_cli-0.1.0/tests/integration/test_bad_server.py +38 -0
  106. mcplint_cli-0.1.0/tests/integration/test_example_servers.py +31 -0
  107. mcplint_cli-0.1.0/tests/packaging/__init__.py +0 -0
  108. mcplint_cli-0.1.0/tests/packaging/test_distribution_name.py +112 -0
  109. mcplint_cli-0.1.0/tests/unit/__init__.py +0 -0
  110. mcplint_cli-0.1.0/tests/unit/benchmark/__init__.py +0 -0
  111. mcplint_cli-0.1.0/tests/unit/benchmark/test_anthropic_provider.py +84 -0
  112. mcplint_cli-0.1.0/tests/unit/benchmark/test_dataset.py +50 -0
  113. mcplint_cli-0.1.0/tests/unit/benchmark/test_factory.py +34 -0
  114. mcplint_cli-0.1.0/tests/unit/benchmark/test_openai_provider.py +25 -0
  115. mcplint_cli-0.1.0/tests/unit/benchmark/test_runner.py +83 -0
  116. mcplint_cli-0.1.0/tests/unit/benchmark/test_scorer.py +122 -0
  117. mcplint_cli-0.1.0/tests/unit/compare/__init__.py +0 -0
  118. mcplint_cli-0.1.0/tests/unit/compare/test_differ.py +158 -0
  119. mcplint_cli-0.1.0/tests/unit/config/__init__.py +0 -0
  120. mcplint_cli-0.1.0/tests/unit/config/test_loader.py +51 -0
  121. mcplint_cli-0.1.0/tests/unit/config/test_schema.py +30 -0
  122. mcplint_cli-0.1.0/tests/unit/core/__init__.py +0 -0
  123. mcplint_cli-0.1.0/tests/unit/core/rules/__init__.py +0 -0
  124. mcplint_cli-0.1.0/tests/unit/core/rules/test_ambiguity.py +74 -0
  125. mcplint_cli-0.1.0/tests/unit/core/rules/test_ambiguity_rules.py +80 -0
  126. mcplint_cli-0.1.0/tests/unit/core/rules/test_completeness_rules.py +130 -0
  127. mcplint_cli-0.1.0/tests/unit/core/rules/test_description_rules.py +101 -0
  128. mcplint_cli-0.1.0/tests/unit/core/rules/test_safety_rules.py +88 -0
  129. mcplint_cli-0.1.0/tests/unit/core/rules/test_schema_rules.py +134 -0
  130. mcplint_cli-0.1.0/tests/unit/core/test_engine.py +73 -0
  131. mcplint_cli-0.1.0/tests/unit/core/test_engine_config.py +69 -0
  132. mcplint_cli-0.1.0/tests/unit/core/test_registry.py +88 -0
  133. mcplint_cli-0.1.0/tests/unit/core/test_score.py +112 -0
  134. mcplint_cli-0.1.0/tests/unit/fix/__init__.py +0 -0
  135. mcplint_cli-0.1.0/tests/unit/fix/test_suggest.py +239 -0
  136. mcplint_cli-0.1.0/tests/unit/mcp_client/__init__.py +0 -0
  137. mcplint_cli-0.1.0/tests/unit/mcp_client/test_canonical.py +56 -0
  138. mcplint_cli-0.1.0/tests/unit/mcp_client/test_persistence.py +47 -0
  139. mcplint_cli-0.1.0/tests/unit/mcp_client/test_session.py +39 -0
  140. mcplint_cli-0.1.0/tests/unit/models/__init__.py +0 -0
  141. mcplint_cli-0.1.0/tests/unit/models/test_common.py +20 -0
  142. mcplint_cli-0.1.0/tests/unit/models/test_contracts.py +53 -0
  143. mcplint_cli-0.1.0/tests/unit/models/test_findings.py +59 -0
  144. mcplint_cli-0.1.0/tests/unit/models/test_snapshot.py +52 -0
  145. mcplint_cli-0.1.0/tests/unit/reporters/__init__.py +0 -0
  146. mcplint_cli-0.1.0/tests/unit/reporters/test_fix_markdown.py +22 -0
  147. mcplint_cli-0.1.0/tests/unit/reporters/test_html.py +89 -0
  148. mcplint_cli-0.1.0/tests/unit/reporters/test_json_reporter.py +32 -0
  149. mcplint_cli-0.1.0/tests/unit/reporters/test_sarif.py +58 -0
  150. mcplint_cli-0.1.0/tests/unit/reporters/test_terminal.py +40 -0
@@ -0,0 +1,29 @@
1
+ ---
2
+ name: Bug report
3
+ about: Something in MCPLint isn't working as documented
4
+ title: ""
5
+ labels: bug
6
+ ---
7
+
8
+ **What happened**
9
+
10
+ **What you expected**
11
+
12
+ **Command you ran**
13
+
14
+ ```
15
+ mcplint ...
16
+ ```
17
+
18
+ **MCP server involved** (or a minimal repro server, if you can share one)
19
+
20
+ **Environment**
21
+ - MCPLint version: `mcplint --version` output, or commit SHA
22
+ - Python version:
23
+ - OS:
24
+
25
+ **Relevant output**
26
+
27
+ ```
28
+ paste terminal output / JSON / SARIF here
29
+ ```
@@ -0,0 +1,23 @@
1
+ ---
2
+ name: Feature request
3
+ about: Propose a new rule, command, or reporter
4
+ title: ""
5
+ labels: enhancement
6
+ ---
7
+
8
+ **Problem**
9
+
10
+ What's the concrete scenario this doesn't handle today? (An example MCP
11
+ server / tool description that should be flagged or reported differently is
12
+ the most useful thing you can include.)
13
+
14
+ **Proposed solution**
15
+
16
+ - New rule? Name it, describe the deterministic check, and note the
17
+ severity/confidence you'd expect.
18
+ - New command/flag? Sketch the CLI invocation.
19
+ - New reporter/format? What should the output look like?
20
+
21
+ **Alternatives considered**
22
+
23
+ **Willing to submit a PR?**
@@ -0,0 +1,11 @@
1
+ ## What
2
+
3
+ ## Why
4
+
5
+ ## Testing
6
+
7
+ - [ ] `ruff check src tests examples` passes
8
+ - [ ] `mypy src` passes
9
+ - [ ] `pytest --cov=mcplint` passes, new code has tests
10
+ - [ ] Updated `CHANGELOG.md` under `[Unreleased]`
11
+ - [ ] Updated `IMPLEMENTATION_STATUS.md` if this changes what's done/incomplete
@@ -0,0 +1,101 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+
8
+ # Least privilege by default; no job in this workflow ever needs write
9
+ # access to the repo, and nothing here ever touches PyPI or creates a
10
+ # release — that only happens from release.yml, triggered by a tag push.
11
+ permissions:
12
+ contents: read
13
+
14
+ jobs:
15
+ lint-typecheck-test:
16
+ runs-on: ubuntu-latest
17
+ steps:
18
+ - uses: actions/checkout@v4
19
+
20
+ - name: Set up Python 3.11
21
+ uses: actions/setup-python@v5
22
+ with:
23
+ python-version: "3.11"
24
+
25
+ - name: Install MCPLint (dev extras)
26
+ run: pip install -e ".[dev]"
27
+
28
+ - name: Ruff check
29
+ run: ruff check src tests examples
30
+
31
+ - name: Ruff format check
32
+ run: ruff format --check src tests examples
33
+
34
+ - name: MyPy
35
+ run: mypy src
36
+
37
+ - name: Unit + CLI tests with coverage
38
+ run: pytest --cov=mcplint --cov-report=term-missing --cov-fail-under=80 tests/unit tests/cli
39
+
40
+ - name: Integration tests (spawns real example MCP servers over stdio)
41
+ run: pytest tests/integration
42
+
43
+ build-and-verify-package:
44
+ runs-on: ubuntu-latest
45
+ needs: lint-typecheck-test
46
+ steps:
47
+ - uses: actions/checkout@v4
48
+
49
+ - name: Set up Python 3.11
50
+ uses: actions/setup-python@v5
51
+ with:
52
+ python-version: "3.11"
53
+
54
+ - name: Install build backend and metadata checker
55
+ run: pip install build twine
56
+
57
+ - name: Build sdist and wheel
58
+ run: python -m build
59
+
60
+ - name: Inspect package metadata
61
+ run: twine check dist/*
62
+
63
+ - uses: actions/upload-artifact@v4
64
+ with:
65
+ name: dist
66
+ path: dist/
67
+
68
+ install-and-smoke-test:
69
+ runs-on: ubuntu-latest
70
+ needs: build-and-verify-package
71
+ steps:
72
+ - uses: actions/checkout@v4 # only for examples/good_server, not for source install
73
+
74
+ - name: Set up Python 3.11
75
+ uses: actions/setup-python@v5
76
+ with:
77
+ python-version: "3.11"
78
+
79
+ - uses: actions/download-artifact@v4
80
+ with:
81
+ name: dist
82
+ path: dist/
83
+
84
+ - name: Install the built wheel into a clean environment
85
+ run: |
86
+ python -m venv /tmp/mcplint-smoke
87
+ /tmp/mcplint-smoke/bin/pip install --quiet dist/*.whl
88
+
89
+ - name: CLI smoke tests
90
+ # Activate the venv (rather than calling mcplint by full path) so the
91
+ # `python examples/good_server/server.py` subprocess mcplint spawns
92
+ # also resolves to this venv's python — which has `mcp` installed as
93
+ # mcplint's own runtime dependency. Calling the binary by full path
94
+ # would leave `python` on PATH pointing at the bare system
95
+ # interpreter, which doesn't have `mcp` installed, and the spawned
96
+ # server would fail to import.
97
+ run: |
98
+ source /tmp/mcplint-smoke/bin/activate
99
+ mcplint --version
100
+ mcplint rules
101
+ mcplint scan --server "python examples/good_server/server.py"
@@ -0,0 +1,54 @@
1
+ # Example workflow for MCPLint users: gate a PR on MCP tool-contract quality.
2
+ #
3
+ # This is a *documented example*, not part of MCPLint's own CI — copy it into
4
+ # your MCP server's repo and adjust the "Start the MCP server" step. It:
5
+ # 1. Installs MCPLint
6
+ # 2. Invokes the local MCP server
7
+ # 3. Runs the scanner
8
+ # 4. Uploads SARIF to GitHub code scanning
9
+ # 5. Fails the job on error-severity findings
10
+ #
11
+ # No custom JavaScript Action is required — this composite-style workflow
12
+ # (plain steps) is sufficient, per MCPLint's own security stance of never
13
+ # running a hosted service that executes arbitrary server commands.
14
+ name: mcplint-scan-example
15
+
16
+ on:
17
+ workflow_dispatch: # example only — trigger manually; adapt `on:` for real use
18
+
19
+ jobs:
20
+ scan:
21
+ runs-on: ubuntu-latest
22
+ steps:
23
+ - uses: actions/checkout@v4
24
+
25
+ - name: Set up Python
26
+ uses: actions/setup-python@v5
27
+ with:
28
+ python-version: "3.11"
29
+
30
+ # 1. Install MCPLint (PyPI distribution name is mcplint-cli; the
31
+ # installed command is still `mcplint`)
32
+ - name: Install MCPLint
33
+ run: pip install mcplint-cli
34
+
35
+ # 2 & 3. Invoke the local MCP server and run the scanner.
36
+ # Replace the --server command with however your server is started —
37
+ # `python server.py`, `node server.js`, a built binary, etc.
38
+ - name: Scan the MCP server
39
+ run: |
40
+ mcplint scan \
41
+ --server "python examples/good_server/server.py" \
42
+ --format sarif \
43
+ --output mcplint-results.sarif \
44
+ --fail-on error
45
+
46
+ # 4. Upload SARIF to GitHub code scanning (visible in the Security tab).
47
+ - name: Upload SARIF
48
+ if: always()
49
+ uses: github/codeql-action/upload-sarif@v3
50
+ with:
51
+ sarif_file: mcplint-results.sarif
52
+
53
+ # 5. `mcplint scan` above already exits non-zero on error-severity
54
+ # findings (--fail-on error is the default), which fails this job.
@@ -0,0 +1,125 @@
1
+ name: Release
2
+
3
+ # Production publishing happens ONLY from an explicit `vX.Y.Z` tag push.
4
+ # Nothing in ci.yml (which runs on every PR and every push to main) has
5
+ # any path to PyPI credentials or release-creation permissions — only this
6
+ # workflow does, and only after a maintainer pushes a tag (tag creation
7
+ # requires write access to the repo; it cannot be triggered by an
8
+ # untrusted pull request).
9
+ on:
10
+ push:
11
+ tags:
12
+ - "v*.*.*" # e.g. v0.1.0 — the build job's version-match step rejects anything non-semver
13
+
14
+ permissions:
15
+ contents: read
16
+
17
+ jobs:
18
+ # Build once. Every later job consumes these exact artifacts rather than
19
+ # rebuilding, so what's tested is byte-for-byte what gets published.
20
+ build:
21
+ runs-on: ubuntu-latest
22
+ steps:
23
+ - uses: actions/checkout@v4
24
+
25
+ - name: Set up Python 3.11
26
+ uses: actions/setup-python@v5
27
+ with:
28
+ python-version: "3.11"
29
+
30
+ - name: Install MCPLint (dev extras) for the pre-publish test run
31
+ run: pip install -e ".[dev]"
32
+
33
+ - name: Verify the tag matches the package version
34
+ run: |
35
+ TAG_VERSION="${GITHUB_REF_NAME#v}"
36
+ PKG_VERSION="$(python -c 'import mcplint; print(mcplint.__version__)')"
37
+ echo "Tag version: $TAG_VERSION"
38
+ echo "Package version: $PKG_VERSION"
39
+ if [ "$TAG_VERSION" != "$PKG_VERSION" ]; then
40
+ echo "::error::Tag $GITHUB_REF_NAME does not match src/mcplint/__about__.py ($PKG_VERSION)"
41
+ exit 1
42
+ fi
43
+
44
+ - name: Ruff check
45
+ run: ruff check src tests examples
46
+
47
+ - name: Ruff format check
48
+ run: ruff format --check src tests examples
49
+
50
+ - name: MyPy
51
+ run: mypy src
52
+
53
+ - name: Full test suite
54
+ run: pytest --cov=mcplint --cov-fail-under=80 tests/unit tests/cli
55
+
56
+ - name: Integration tests
57
+ run: pytest tests/integration
58
+
59
+ - name: Install build backend and metadata checker
60
+ run: pip install build twine
61
+
62
+ - name: Build sdist and wheel
63
+ run: python -m build
64
+
65
+ - name: Inspect package metadata
66
+ run: twine check dist/*
67
+
68
+ - name: Install the built wheel into a clean environment and smoke-test it
69
+ run: |
70
+ python -m venv /tmp/mcplint-release-smoke
71
+ /tmp/mcplint-release-smoke/bin/pip install --quiet dist/*.whl
72
+ source /tmp/mcplint-release-smoke/bin/activate
73
+ mcplint --version
74
+ mcplint scan --server "python examples/good_server/server.py"
75
+
76
+ - uses: actions/upload-artifact@v4
77
+ with:
78
+ name: dist
79
+ path: dist/
80
+
81
+ # Publish the exact artifacts from `build` to PyPI via Trusted Publishing.
82
+ # No long-lived token: the `id-token: write` permission lets PyPI verify
83
+ # this specific workflow run via OIDC. This is the only job in the whole
84
+ # repo with any PyPI-facing permission.
85
+ publish-to-pypi:
86
+ runs-on: ubuntu-latest
87
+ needs: build
88
+ environment:
89
+ name: pypi
90
+ url: https://pypi.org/p/mcplint-cli
91
+ permissions:
92
+ id-token: write
93
+ steps:
94
+ - uses: actions/download-artifact@v4
95
+ with:
96
+ name: dist
97
+ path: dist/
98
+
99
+ - name: Publish to PyPI
100
+ uses: pypa/gh-action-pypi-publish@release/v1
101
+ with:
102
+ attestations: true
103
+
104
+ # Create the GitHub Release and attach the same artifacts, after PyPI
105
+ # publish succeeds so a Release is never created for a build that failed
106
+ # to publish.
107
+ github-release:
108
+ runs-on: ubuntu-latest
109
+ needs: publish-to-pypi
110
+ permissions:
111
+ contents: write
112
+ steps:
113
+ - uses: actions/download-artifact@v4
114
+ with:
115
+ name: dist
116
+ path: dist/
117
+
118
+ - name: Create GitHub Release
119
+ env:
120
+ GH_TOKEN: ${{ github.token }}
121
+ run: |
122
+ gh release create "${{ github.ref_name }}" dist/* \
123
+ --repo "${{ github.repository }}" \
124
+ --title "${{ github.ref_name }}" \
125
+ --generate-notes
@@ -0,0 +1,11 @@
1
+ .venv/
2
+ __pycache__/
3
+ *.pyc
4
+ .pytest_cache/
5
+ .mypy_cache/
6
+ .ruff_cache/
7
+ *.egg-info/
8
+ dist/
9
+ build/
10
+ .coverage
11
+ htmlcov/
@@ -0,0 +1,27 @@
1
+ repos:
2
+ - repo: https://github.com/astral-sh/ruff-pre-commit
3
+ rev: v0.6.9
4
+ hooks:
5
+ - id: ruff
6
+ args: [--fix]
7
+ - id: ruff-format
8
+
9
+ - repo: https://github.com/pre-commit/mirrors-mypy
10
+ rev: v1.11.2
11
+ hooks:
12
+ - id: mypy
13
+ additional_dependencies:
14
+ - pydantic>=2.7
15
+ - types-pyyaml
16
+ - types-jsonschema
17
+ args: [src]
18
+ pass_filenames: false
19
+
20
+ - repo: https://github.com/pre-commit/pre-commit-hooks
21
+ rev: v4.6.0
22
+ hooks:
23
+ - id: trailing-whitespace
24
+ - id: end-of-file-fixer
25
+ - id: check-yaml
26
+ - id: check-toml
27
+ - id: check-merge-conflict
@@ -0,0 +1,96 @@
1
+ # Changelog
2
+
3
+ ## [Unreleased]
4
+
5
+ ### Added
6
+ - Project scaffolding (`pyproject.toml`, package layout, dev tooling).
7
+ - Core Pydantic models: `ArtifactMetadata`, `SourceLocation`, `ParameterContract`,
8
+ `ToolAnnotation`, `ToolContract`, `MCPServerSnapshot`.
9
+ - Canonicalization module: deterministic stable tool IDs, byte-stable snapshot JSON.
10
+ - Stdio MCP client: `collect_stdio_snapshot`, mapping SDK `Tool` objects to `ToolContract`.
11
+ - `examples/good_server`: a well-documented example MCP server fixture.
12
+ - `mcplint inspect --server "<command>"` CLI command with Rich table output.
13
+ - `Severity`, `Finding`, `RuleMetadata`, `LintReport` models.
14
+ - `Rule` ABC, `RuleContext`, `RuleRegistry` (with entry-point plugin loading support).
15
+ - Five built-in rules: `missing-tool-description`, `description-repeats-name`,
16
+ `vague-tool-description`, `missing-parameter-description`,
17
+ `schema-description-type-conflict`.
18
+ - `lint_snapshot()` engine wiring the registry over every tool in a snapshot.
19
+ - Snapshot persistence: `save_snapshot`/`load_snapshot`.
20
+ - Terminal and JSON reporters (`render_terminal`, `render_json`).
21
+ - `mcplint snapshot --server "<command>" --output PATH` CLI command.
22
+ - `mcplint scan --server "<command>"` / `mcplint scan --snapshot PATH` CLI command
23
+ with `--format terminal|json` and `--fail-on error|warning|never`.
24
+ - Remaining 10 built-in rules: `missing-return-semantics`,
25
+ `undocumented-error-behaviour`, `undocumented-required-constraint`,
26
+ `tool-name-action-conflict`, `destructive-tool-without-warning`,
27
+ `state-changing-tool-marked-read-only`, `ambiguous-tool-overlap`,
28
+ `missing-tool-distinction`, `excessive-description-length`,
29
+ `undefined-domain-term` — all 15 spec rules now implemented.
30
+ - Semantic ambiguity engine (`compute_ambiguity`): name/description/parameter
31
+ Jaccard similarity with structured, inspectable evidence (shared verbs,
32
+ shared entities, overlapping parameters, absent exact-vs-search /
33
+ one-vs-many / read-vs-write distinctions).
34
+ - `mcplint.yaml` configuration loading (`MCPLintConfig`): severity overrides,
35
+ ambiguity/description-length thresholds, per-tool rule ignores, benchmark
36
+ defaults. Wired into `scan --config`.
37
+ - `mcplint rules` CLI command listing the full rule catalogue.
38
+ - `examples/bad_server`: intentionally triggers all 15 built-in rules
39
+ (verified by an integration test).
40
+ - `examples/ambiguous_customer_server`: get/search/update/delete customer
41
+ tools for the upcoming benchmark dataset, verified to trigger
42
+ `ambiguous-tool-overlap`.
43
+ - `examples/good_server` reworked to use `Annotated[..., Field(description=...)]`
44
+ parameters and explicit tool annotations; verified to produce zero findings.
45
+ - Benchmark models: `ExpectedToolCall`, `BenchmarkCase`, `BenchmarkDataset`,
46
+ `ActualToolCall`, `BenchmarkTrial`, `BenchmarkResult`.
47
+ - `ToolCallingProvider` protocol + `ProviderResult`; `FakeProvider` for
48
+ network-free tests and dry runs.
49
+ - Deterministic scorer (`score_trial`, `aggregate_metrics`): exact
50
+ tool-selection accuracy, valid-argument rate (JSON Schema validated),
51
+ required-argument accuracy, forbidden-tool invocation rate, no-tool rate,
52
+ mean/P95 latency, total estimated cost, per-case pass rate, and stability
53
+ across repeated trials. No LLM judge.
54
+ - `mcplint benchmark DATASET --server "<command>" --provider fake --runs N`
55
+ CLI command with terminal/JSON output and `--output PATH`.
56
+ `--provider anthropic|openai` is recognized but raises a clear
57
+ "not implemented yet" error (Phase 5).
58
+ - `examples/ambiguous_customer_server/customer-tools.evals.yaml`: the
59
+ get/search/update/delete-customer confusion dataset required by the spec.
60
+ - `AnthropicProvider`: verified against the real `anthropic` SDK (0.120.0);
61
+ API errors surface as a scored `ProviderResult.error`, not a crash;
62
+ illustrative per-model cost table. `OpenAIProvider` is a typed stub.
63
+ `--provider anthropic` now works end-to-end via `mcplint benchmark`.
64
+ - `ComparisonReport` model + `compare/differ.py` (`diff_tool_names`,
65
+ `diff_tool_contracts`, `diff_findings`, `diff_ambiguity`, `diff_benchmarks`).
66
+ - `mcplint compare --baseline --candidate [--dataset --min-accuracy-delta]`
67
+ CLI command with a Rich terminal reporter; fails CI (exit 1) when the
68
+ accuracy delta falls below the threshold.
69
+ - `RewriteSuggestion` model + `fix/suggest.py`: deterministic, schema-derived
70
+ rewrite suggestions (output shape, enum/numeric constraints, destructive
71
+ warnings, tool-distinction placeholders, description truncation).
72
+ Markdown patch report reporter.
73
+ - `mcplint fix --snapshot [--output PATH]` CLI command — never overwrites
74
+ source files; `--llm-provider` is recognized but explicitly rejected
75
+ until LLM-assisted rewriting is implemented.
76
+ - `ScoreBreakdown` model + `core/score.py::compute_score`: explainable 0-100
77
+ score, capped per category (critical/error, warning/info, ambiguity,
78
+ schema completeness, safety clarity, optional benchmark accuracy), with
79
+ an explicit not-a-scientific-metric disclaimer. Shown in the terminal
80
+ reporter.
81
+ - SARIF 2.1.0 reporter (`reporters/sarif.py`) with a full rule catalogue
82
+ and per-finding results/locations.
83
+ - Standalone, self-contained HTML report (`reporters/html.py` + Jinja2
84
+ template, embedded CSS, no external requests): score, findings by
85
+ severity, tool inventory, ambiguity pairs, and optional
86
+ benchmark/comparison/fix-suggestion sections.
87
+ - `mcplint scan --format sarif|html` and a new global `--output PATH` to
88
+ also write the rendered report to a file.
89
+ - `mcplint --version`.
90
+ - Packaging: `LICENSE` (MIT), `CONTRIBUTING.md`, `CODE_OF_CONDUCT.md`,
91
+ `SECURITY.md`, GitHub issue/PR templates, `ci.yml`/`release.yml`
92
+ GitHub Actions, a documented example scan-and-upload-SARIF workflow,
93
+ `Dockerfile`, `.pre-commit-config.yaml`.
94
+ - Full README rewrite: quickstart, real example output, architecture,
95
+ rule catalogue, ambiguity engine explanation, benchmark/compare/fix/CI/
96
+ plugin guides, limitations, roadmap, and a comparison section.
@@ -0,0 +1,51 @@
1
+ # Code of Conduct
2
+
3
+ ## Our Pledge
4
+
5
+ We as members, contributors, and maintainers pledge to make participation in
6
+ the MCPLint project a harassment-free experience for everyone, regardless of
7
+ age, body size, visible or invisible disability, ethnicity, sex
8
+ characteristics, gender identity and expression, level of experience,
9
+ education, socio-economic status, nationality, personal appearance, race,
10
+ religion, or sexual identity and orientation.
11
+
12
+ ## Our Standards
13
+
14
+ Examples of behavior that contributes to a positive environment:
15
+
16
+ - Demonstrating empathy and kindness toward other people
17
+ - Being respectful of differing opinions, viewpoints, and experiences
18
+ - Giving and gracefully accepting constructive feedback
19
+ - Focusing on what is best for the community
20
+
21
+ Examples of unacceptable behavior:
22
+
23
+ - The use of sexualized language or imagery, and unwelcome sexual attention
24
+ - Trolling, insulting or derogatory comments, and personal or political attacks
25
+ - Public or private harassment
26
+ - Publishing others' private information without explicit permission
27
+
28
+ ## Enforcement Responsibilities
29
+
30
+ Project maintainers are responsible for clarifying and enforcing our
31
+ standards of acceptable behavior and will take appropriate and fair
32
+ corrective action in response to any behavior they deem inappropriate,
33
+ threatening, offensive, or harmful.
34
+
35
+ ## Scope
36
+
37
+ This Code of Conduct applies within all project spaces (issues, pull
38
+ requests, discussions) and when an individual is officially representing the
39
+ project in public spaces.
40
+
41
+ ## Enforcement
42
+
43
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be
44
+ reported to the project maintainers via the contact method listed in
45
+ `SECURITY.md`. All complaints will be reviewed and investigated promptly and
46
+ fairly.
47
+
48
+ ## Attribution
49
+
50
+ This Code of Conduct is adapted from the [Contributor Covenant](https://www.contributor-covenant.org),
51
+ version 2.1.
@@ -0,0 +1,75 @@
1
+ # Contributing to MCPLint
2
+
3
+ Thanks for considering a contribution. MCPLint is a young project — the
4
+ architecture in `IMPLEMENTATION_STATUS.md` and this guide are both likely to
5
+ evolve.
6
+
7
+ ## Development setup
8
+
9
+ ```bash
10
+ git clone https://github.com/mcplint/mcplint.git
11
+ cd mcplint
12
+ python3.11 -m venv .venv
13
+ source .venv/bin/activate
14
+ pip install -e ".[dev]"
15
+ ```
16
+
17
+ `dev` includes `anthropic` so the Anthropic provider's test suite runs by
18
+ default. If you only want the deterministic linter, `pip install -e .` is
19
+ enough — no extras are required for `inspect`, `snapshot`, `scan`, `rules`,
20
+ or `fix`.
21
+
22
+ ## Before opening a PR
23
+
24
+ Run the same gate CI runs:
25
+
26
+ ```bash
27
+ ruff check src tests examples
28
+ mypy src
29
+ pytest --cov=mcplint --cov-report=term-missing
30
+ ```
31
+
32
+ All three must pass. New code should come with tests — see
33
+ `tests/unit/core/rules/` for the pattern most rules follow (a flag case and
34
+ a pass case per behavior, using hand-built `ToolContract`/`RuleContext`
35
+ fixtures, no live MCP server needed).
36
+
37
+ ## Adding a built-in rule
38
+
39
+ 1. Subclass `mcplint.core.rules.base.Rule` in the appropriate module under
40
+ `src/mcplint/core/rules/` (`description_rules.py`, `schema_rules.py`,
41
+ `safety_rules.py`, `completeness_rules.py`, or a new file if it's a new
42
+ category).
43
+ 2. Set `id`, `title`, `description`, `default_severity`, and optionally
44
+ `tags` as class attributes; implement `check(self, tool, context) ->
45
+ list[Finding]`.
46
+ 3. Register it in `BUILTIN_RULES` in `core/rules/builtin.py`.
47
+ 4. Add unit tests: at least one case that triggers the finding and one that
48
+ doesn't.
49
+ 5. If it's meant to fire on `examples/bad_server`, add a tool there and
50
+ confirm `tests/integration/test_bad_server.py` still passes (it asserts
51
+ every built-in rule ID fires at least once against that server).
52
+
53
+ Third-party rules don't need to touch this repo at all — see the plugin
54
+ guide in the README for the `mcplint.rules` entry-point mechanism.
55
+
56
+ ## Adding a benchmark provider
57
+
58
+ Implement the `ToolCallingProvider` protocol (`benchmark/providers/base.py`):
59
+ a `name`/`model` pair and an async `run(prompt, tools) -> ProviderResult`.
60
+ See `benchmark/providers/anthropic_provider.py` for a real example and
61
+ `benchmark/providers/fake.py` for a test-only one. Wire it into
62
+ `benchmark/providers/factory.py::create_provider`.
63
+
64
+ ## Commit and PR conventions
65
+
66
+ - Keep commits scoped to one logical change; the existing history (`git log
67
+ --oneline`) is the style to match.
68
+ - Update `CHANGELOG.md` under `[Unreleased]` for user-visible changes.
69
+ - Update `IMPLEMENTATION_STATUS.md` if you complete, start, or discover a
70
+ gap in a tracked feature.
71
+
72
+ ## Reporting bugs / requesting features
73
+
74
+ Use the GitHub issue templates. For security issues, see `SECURITY.md`
75
+ instead of opening a public issue.
@@ -0,0 +1,17 @@
1
+ # MCPLint CLI image. The MCP server you point --server at is spawned as a
2
+ # subprocess *inside this container*, so it (and any language runtime it
3
+ # needs, e.g. Node) must be available on the image or mounted in. This base
4
+ # image ships Python only — extend it if your server needs something else.
5
+ FROM python:3.11-slim AS base
6
+
7
+ WORKDIR /app
8
+
9
+ COPY pyproject.toml README.md ./
10
+ COPY src ./src
11
+
12
+ RUN pip install --no-cache-dir .
13
+
14
+ WORKDIR /workspace
15
+
16
+ ENTRYPOINT ["mcplint"]
17
+ CMD ["--help"]