mcp-gatekeeper 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 (41) hide show
  1. mcp_gatekeeper-0.1.0/.github/ISSUE_TEMPLATE/bug_report.yml +29 -0
  2. mcp_gatekeeper-0.1.0/.github/ISSUE_TEMPLATE/feature_request.yml +17 -0
  3. mcp_gatekeeper-0.1.0/.github/SECURITY.md +20 -0
  4. mcp_gatekeeper-0.1.0/.github/workflows/ci.yml +35 -0
  5. mcp_gatekeeper-0.1.0/.github/workflows/release.yml +19 -0
  6. mcp_gatekeeper-0.1.0/.gitignore +23 -0
  7. mcp_gatekeeper-0.1.0/.pre-commit-config.yaml +14 -0
  8. mcp_gatekeeper-0.1.0/CHANGELOG.md +36 -0
  9. mcp_gatekeeper-0.1.0/CODE_OF_CONDUCT.md +60 -0
  10. mcp_gatekeeper-0.1.0/CONTRIBUTING.md +62 -0
  11. mcp_gatekeeper-0.1.0/LICENSE +21 -0
  12. mcp_gatekeeper-0.1.0/PKG-INFO +163 -0
  13. mcp_gatekeeper-0.1.0/README.md +130 -0
  14. mcp_gatekeeper-0.1.0/examples/bad_server.py +29 -0
  15. mcp_gatekeeper-0.1.0/examples/good_server.py +41 -0
  16. mcp_gatekeeper-0.1.0/pyproject.toml +90 -0
  17. mcp_gatekeeper-0.1.0/scripts/release.py +170 -0
  18. mcp_gatekeeper-0.1.0/src/mcpgate/__init__.py +3 -0
  19. mcp_gatekeeper-0.1.0/src/mcpgate/cli.py +106 -0
  20. mcp_gatekeeper-0.1.0/src/mcpgate/client.py +117 -0
  21. mcp_gatekeeper-0.1.0/src/mcpgate/doctor.py +40 -0
  22. mcp_gatekeeper-0.1.0/src/mcpgate/engine.py +111 -0
  23. mcp_gatekeeper-0.1.0/src/mcpgate/model.py +56 -0
  24. mcp_gatekeeper-0.1.0/src/mcpgate/py.typed +0 -0
  25. mcp_gatekeeper-0.1.0/src/mcpgate/rules/__init__.py +9 -0
  26. mcp_gatekeeper-0.1.0/src/mcpgate/rules/base.py +79 -0
  27. mcp_gatekeeper-0.1.0/src/mcpgate/rules/descriptions.py +84 -0
  28. mcp_gatekeeper-0.1.0/src/mcpgate/rules/schemas.py +79 -0
  29. mcp_gatekeeper-0.1.0/src/mcpgate/rules/security.py +62 -0
  30. mcp_gatekeeper-0.1.0/src/mcpgate/rules/tokens.py +33 -0
  31. mcp_gatekeeper-0.1.0/src/mcpgate/testing/__init__.py +1 -0
  32. mcp_gatekeeper-0.1.0/src/mcpgate/testing/plugin.py +119 -0
  33. mcp_gatekeeper-0.1.0/tests/conftest.py +47 -0
  34. mcp_gatekeeper-0.1.0/tests/fixtures/bad_server.py +41 -0
  35. mcp_gatekeeper-0.1.0/tests/fixtures/stdio_server.py +18 -0
  36. mcp_gatekeeper-0.1.0/tests/test_cli.py +105 -0
  37. mcp_gatekeeper-0.1.0/tests/test_client.py +70 -0
  38. mcp_gatekeeper-0.1.0/tests/test_package.py +5 -0
  39. mcp_gatekeeper-0.1.0/tests/test_plugin.py +47 -0
  40. mcp_gatekeeper-0.1.0/tests/test_rules.py +187 -0
  41. mcp_gatekeeper-0.1.0/uv.lock +1688 -0
@@ -0,0 +1,29 @@
1
+ name: Bug report
2
+ description: Report a problem with mcpgate
3
+ labels: [bug]
4
+ body:
5
+ - type: textarea
6
+ id: what-happened
7
+ attributes:
8
+ label: What happened?
9
+ description: Include the exact `mcpgate` command and full output.
10
+ validations:
11
+ required: true
12
+ - type: input
13
+ id: version
14
+ attributes:
15
+ label: mcpgate version
16
+ placeholder: "0.1.0"
17
+ validations:
18
+ required: true
19
+ - type: input
20
+ id: python
21
+ attributes:
22
+ label: Python version
23
+ validations:
24
+ required: true
25
+ - type: textarea
26
+ id: server
27
+ attributes:
28
+ label: MCP server being linted
29
+ description: Command or URL. Redact anything private.
@@ -0,0 +1,17 @@
1
+ name: Feature request
2
+ description: Suggest a rule or feature
3
+ labels: [enhancement]
4
+ body:
5
+ - type: textarea
6
+ id: problem
7
+ attributes:
8
+ label: Problem
9
+ description: What quality defect or workflow is not covered?
10
+ validations:
11
+ required: true
12
+ - type: textarea
13
+ id: proposal
14
+ attributes:
15
+ label: Proposed solution
16
+ validations:
17
+ required: true
@@ -0,0 +1,20 @@
1
+ # Security Policy
2
+
3
+ ## Supported versions
4
+
5
+ | Version | Supported |
6
+ |---------|-----------|
7
+ | 0.1.x | ✅ |
8
+
9
+ ## Reporting a vulnerability
10
+
11
+ Do **not** open a public issue for security vulnerabilities. Use GitHub's
12
+ "Report a vulnerability" (Security → Advisories → New draft security advisory),
13
+ or open a minimal issue pointing to a draft advisory. You will get a response
14
+ within 7 days.
15
+
16
+ ## Scope
17
+
18
+ `mcpgate` connects to MCP servers you point it at. Vulnerabilities of interest
19
+ include: command injection via server command strings, unsafe deserialization of
20
+ server responses, and SARIF/JSON output injection.
@@ -0,0 +1,35 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+
8
+ jobs:
9
+ test:
10
+ runs-on: ubuntu-latest
11
+ strategy:
12
+ fail-fast: false
13
+ matrix:
14
+ python-version: ["3.10", "3.11", "3.12", "3.13"]
15
+ steps:
16
+ - uses: actions/checkout@v4
17
+ - uses: astral-sh/setup-uv@v5
18
+ with:
19
+ python-version: ${{ matrix.python-version }}
20
+ - name: Install
21
+ run: uv sync --all-extras
22
+ - name: Lint
23
+ run: uv run ruff check .
24
+ - name: Format check
25
+ run: uv run ruff format --check .
26
+ - name: Types
27
+ run: uv run mypy src
28
+ - name: Test
29
+ run: |
30
+ uv run coverage run -m pytest
31
+ uv run coverage report
32
+ - name: Build
33
+ run: |
34
+ uv build
35
+ uv run --with twine twine check dist/*
@@ -0,0 +1,19 @@
1
+ name: Release
2
+
3
+ on:
4
+ push:
5
+ tags: ["v*"]
6
+
7
+ jobs:
8
+ publish:
9
+ runs-on: ubuntu-latest
10
+ environment: pypi
11
+ permissions:
12
+ id-token: write
13
+ steps:
14
+ - uses: actions/checkout@v4
15
+ - uses: astral-sh/setup-uv@v5
16
+ - name: Build
17
+ run: uv build
18
+ - name: Publish to PyPI
19
+ uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,23 @@
1
+ __pycache__/
2
+ *.py[cod]
3
+ *.egg-info/
4
+ .venv/
5
+ dist/
6
+ build/
7
+ .pytest_cache/
8
+ .mypy_cache/
9
+ .ruff_cache/
10
+ .coverage
11
+ coverage.xml
12
+ htmlcov/
13
+ uv.lock.bak
14
+ .DS_Store
15
+
16
+ # AI/planning docs — local only
17
+ PLAN.md
18
+ IMPLEMENTATION_PLAN.md
19
+ AGENTS.md
20
+ CLAUDE.md
21
+ GEMINI.md
22
+ .claude/
23
+ copilot-instructions.md
@@ -0,0 +1,14 @@
1
+ repos:
2
+ - repo: https://github.com/astral-sh/ruff-pre-commit
3
+ rev: v0.8.4
4
+ hooks:
5
+ - id: ruff-format
6
+ - id: ruff
7
+ args: [--fix]
8
+ - repo: https://github.com/pre-commit/mirrors-mypy
9
+ rev: v1.13.0
10
+ stages: [pre-push]
11
+ hooks:
12
+ - id: mypy
13
+ files: ^src/
14
+ additional_dependencies: [pydantic>=2.0, click>=8.1, types-click]
@@ -0,0 +1,36 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [0.1.0] - 2026-09-02
9
+
10
+ ### Added
11
+ - feat: automated release script with semver bump decision
12
+ - feat: pytest plugin with mcp_server fixture and tool_contract
13
+ - feat: lint/doctor CLI with text, json and sarif output
14
+ - feat: 15 lint rules with decorator registry (MCP101-MCP401)
15
+ - feat: protocol client and normalized snapshot models
16
+
17
+ ### Fixed
18
+ - fix: rename PyPI distribution to mcp-gatekeeper
19
+ - fix: rename PyPI package to mcp-gate (mcpgate is taken)
20
+ - fix: snapshot servers that lack resources/prompts capabilities
21
+
22
+ ### Changed
23
+ - docs: drop AGENTS.md link from README
24
+ - docs: self-contained public CONTRIBUTING guide
25
+ - docs: full README with rule table, comparison and examples; coverage gate at 90%
26
+
27
+ ### Other
28
+ - chore: keep AI and planning docs untracked
29
+ - chore: scaffold package, OSS files, CI and release workflows
30
+
31
+ ## [0.1.0] - 2026-09-02
32
+
33
+ ### Added
34
+ - `mcpgate lint` with 15 rules (MCP101–MCP401), text/json/sarif output, CI-friendly exit codes.
35
+ - `mcpgate doctor` health report.
36
+ - Pytest plugin: `mcp_server` fixture + `tool_contract` helper.
@@ -0,0 +1,60 @@
1
+ # Contributor Covenant Code of Conduct
2
+
3
+ ## Our Pledge
4
+
5
+ We as members, contributors, and leaders pledge to make participation in our
6
+ community a harassment-free experience for everyone, regardless of age, body
7
+ size, visible or invisible disability, ethnicity, sex characteristics, gender
8
+ identity and expression, level of experience, education, socio-economic status,
9
+ nationality, personal appearance, race, religion, or sexual identity and
10
+ orientation.
11
+
12
+ We pledge to act and interact in ways that contribute to an open, welcoming,
13
+ diverse, inclusive, and healthy community.
14
+
15
+ ## Our Standards
16
+
17
+ Examples of behavior that contributes to a positive environment include:
18
+
19
+ * Demonstrating empathy and kindness toward other people
20
+ * Being respectful of differing opinions, viewpoints, and experiences
21
+ * Giving and gracefully accepting constructive feedback
22
+ * Accepting responsibility and apologizing to those affected by our mistakes,
23
+ and learning from the experience
24
+ * Focusing on what is best not just for us as individuals, but for the overall
25
+ community
26
+
27
+ Examples of unacceptable behavior include:
28
+
29
+ * The use of sexualized language or imagery, and sexual attention or advances of
30
+ any kind
31
+ * Trolling, insulting or derogatory comments, and personal or political attacks
32
+ * Public or private harassment
33
+ * Publishing others' private information, such as a physical or email address,
34
+ without their explicit permission
35
+ * Other conduct which could reasonably be considered inappropriate in a
36
+ professional setting
37
+
38
+ ## Enforcement Responsibilities
39
+
40
+ Community leaders are responsible for clarifying and enforcing our standards of
41
+ acceptable behavior and will take appropriate and fair corrective action in
42
+ response to any behavior that they deem inappropriate, threatening, offensive,
43
+ or harmful.
44
+
45
+ ## Scope
46
+
47
+ This Code of Conduct applies within all community spaces, and also applies when
48
+ an individual is officially representing the community in public spaces.
49
+
50
+ ## Enforcement
51
+
52
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be
53
+ reported to the community leaders responsible for enforcement by opening a
54
+ private GitHub issue marked as a conduct report. All complaints will be reviewed
55
+ and investigated promptly and fairly.
56
+
57
+ ## Attribution
58
+
59
+ This Code of Conduct is adapted from the
60
+ [Contributor Covenant](https://www.contributor-covenant.org), version 2.1.
@@ -0,0 +1,62 @@
1
+ # Contributing to mcpgate
2
+
3
+ Thanks for helping make MCP servers better for the agents that use them!
4
+
5
+ ## Getting started
6
+
7
+ ```bash
8
+ git clone https://github.com/ofsazib/mcpgate
9
+ cd mcpgate
10
+ uv sync --all-extras # install deps (dev + test extras)
11
+
12
+ uv run coverage run -m pytest # tests (fully offline)
13
+ uv run coverage report # coverage, gate at 90%
14
+ uv run ruff check . # lint
15
+ uv run ruff format . # format
16
+ uv run mypy src # strict type check
17
+ ```
18
+
19
+ Requires Python 3.10–3.13 and [uv](https://docs.astral.sh/uv/).
20
+
21
+ ## Project layout
22
+
23
+ | Module | Responsibility |
24
+ |---|---|
25
+ | `src/mcpgate/cli.py` | click app: `mcpgate lint \| doctor` |
26
+ | `src/mcpgate/client.py` | connect over in-memory/stdio/HTTP; snapshot definitions |
27
+ | `src/mcpgate/model.py` | pydantic models: `ToolInfo`, `Finding`, `ServerSnapshot`, … |
28
+ | `src/mcpgate/engine.py` | rule runner, text/json/sarif formatters, exit codes |
29
+ | `src/mcpgate/doctor.py` | health report |
30
+ | `src/mcpgate/testing/plugin.py` | pytest plugin (`mcp_server` fixture, `tool_contract`) |
31
+ | `src/mcpgate/rules/` | rule registry + rule modules |
32
+
33
+ ## Adding a lint rule
34
+
35
+ Rule codes are grouped by prefix: `MCP1xx` descriptions, `MCP2xx` schemas,
36
+ `MCP3xx` security, `MCP4xx` token cost.
37
+
38
+ 1. Write a small pure function over `ServerSnapshot` returning
39
+ `(source, message-kwargs)` tuples, in the right `rules/*.py` module.
40
+ 2. Register it with the `@rule("MCPxxx", "severity", "message template")` decorator
41
+ from `rules/base.py`.
42
+ 3. Add a positive and a negative test in `tests/test_rules.py` — one of each per
43
+ rule, no exceptions.
44
+ 4. If it is user-visible, add a row to the rule table in `README.md`.
45
+
46
+ ## Ground rules
47
+
48
+ - **Runtime dependencies** are frozen at `mcp`, `click`, `pydantic` — anything else
49
+ needs prior discussion in an issue.
50
+ - **Tests are offline.** Use the `mcp` SDK's in-memory transport; mark network
51
+ tests with `@pytest.mark.network` (deselected by default).
52
+ - **Typing is strict.** No `# type: ignore` without a comment explaining why.
53
+ - **Commits** follow Conventional Commits (`feat:`, `fix:`, `docs:`, …).
54
+ - Every PR must keep `pytest`, `ruff` and `mypy src` green.
55
+
56
+ ## Reporting bugs and security issues
57
+
58
+ Bugs and feature requests: open a GitHub issue using the templates.
59
+ Security vulnerabilities: **never** in a public issue — see
60
+ [SECURITY.md](.github/SECURITY.md).
61
+
62
+ By participating you agree to the [Code of Conduct](CODE_OF_CONDUCT.md).
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 mcpgate contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,163 @@
1
+ Metadata-Version: 2.5
2
+ Name: mcp-gatekeeper
3
+ Version: 0.1.0
4
+ Summary: Automated quality gate for MCP servers — ruff + pytest for MCP.
5
+ Project-URL: Homepage, https://github.com/ofsazib/mcpgate
6
+ Project-URL: Changelog, https://github.com/ofsazib/mcpgate/blob/main/CHANGELOG.md
7
+ Project-URL: Issues, https://github.com/ofsazib/mcpgate/issues
8
+ Author: mcpgate contributors
9
+ License-Expression: MIT
10
+ License-File: LICENSE
11
+ Keywords: ai-agents,lint,mcp,model-context-protocol,pytest,quality-gate
12
+ Classifier: Development Status :: 4 - Beta
13
+ Classifier: Environment :: Console
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: Operating System :: OS Independent
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3.10
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Programming Language :: Python :: 3.13
21
+ Classifier: Topic :: Software Development :: Quality Assurance
22
+ Classifier: Topic :: Software Development :: Testing
23
+ Classifier: Typing :: Typed
24
+ Requires-Python: >=3.10
25
+ Requires-Dist: click>=8.1
26
+ Requires-Dist: mcp<2,>=1.0
27
+ Requires-Dist: pydantic>=2.0
28
+ Provides-Extra: test
29
+ Requires-Dist: pytest-asyncio>=0.24; extra == 'test'
30
+ Requires-Dist: pytest-cov>=5.0; extra == 'test'
31
+ Requires-Dist: pytest>=8.0; extra == 'test'
32
+ Description-Content-Type: text/markdown
33
+
34
+ # mcpgate
35
+
36
+ > Automated quality gate for MCP servers — **ruff + pytest for MCP**.
37
+
38
+ [![CI](https://github.com/ofsazib/mcpgate/actions/workflows/ci.yml/badge.svg)](https://github.com/ofsazib/mcpgate/actions/workflows/ci.yml)
39
+ [![PyPI](https://img.shields.io/pypi/v/mcpgate)](https://pypi.org/project/mcp-gatekeeper/)
40
+ [![Python](https://img.shields.io/pypi/pyversions/mcpgate)](https://pypi.org/project/mcp-gatekeeper/)
41
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
42
+ [![Coverage](https://codecov.io/gh/ofsazib/mcpgate/branch/main/graph/badge.svg)](https://codecov.io/gh/ofsazib/mcpgate)
43
+
44
+ ## The problem
45
+
46
+ When an MCP tool description is vague, the AI agent misuses the tool — even though
47
+ the code behind it is perfect. This failure is invisible to unit tests because it
48
+ happens at the AI-interpretation layer. Research documents the scale:
49
+
50
+ - An arXiv study of 103 major MCP servers (856 tools) found tool descriptions
51
+ systematically "smelly and costly" ([arXiv:2602.14878](https://arxiv.org/abs/2602.14878)).
52
+ - An AWS-led study found **97.1% of MCP tool descriptions contain at least one
53
+ quality defect**.
54
+
55
+ `mcpgate` catches these defects before agents hit them in production. It speaks the
56
+ real protocol, so it works with any server — FastMCP, the official Python SDK, or
57
+ TypeScript servers — over stdio or HTTP.
58
+
59
+ ## Quickstart
60
+
61
+ ```bash
62
+ uv tool install mcp-gatekeeper # or: pip install mcp-gatekeeper
63
+
64
+ mcpgate lint "uvx mcp-server-fetch"
65
+ mcpgate doctor "uvx mcp-server-fetch"
66
+ ```
67
+
68
+ Exit codes are CI-friendly: `0` clean, `1` findings (fail the build), `2` connection
69
+ or protocol error.
70
+
71
+ ## Example output
72
+
73
+ ```text
74
+ warning MCP101 Tool `delete_record`: description too short (7 words; minimum 8) src: tools/delete_record
75
+ warning MCP105 Tool `delete_record`: description does not state error/return behavior src: tools/delete_record
76
+ warning MCP303 Tool `delete_record`: destructive operation without a confirmation parameter src: tools/delete_record
77
+ error MCP301 Tool `run_command`: suggests command execution without a safety note src: tools/run_command
78
+ warning MCP101 Tool `run_command`: description too short (7 words; minimum 8) src: tools/run_command
79
+ warning MCP105 Tool `run_command`: description does not state error/return behavior src: tools/run_command
80
+ warning MCP101 Tool `search`: description too short (2 words; minimum 8) src: tools/search
81
+ warning MCP105 Tool `search`: description does not state error/return behavior src: tools/search
82
+ info MCP204 Tool `search`: description repeats the tool name (wasted tokens) src: tools/search
83
+ ———
84
+ found 9 problems (1 errors, 7 warnings)
85
+ run `mcpgate lint --explain CODE` for details
86
+ ```
87
+
88
+ Formats: `--format text|json|sarif` (SARIF plugs into GitHub code scanning).
89
+ Filter with `--select MCP3xx --ignore MCP401`; inspect a rule with
90
+ `--explain MCP101`; fail on warnings with `--strict`.
91
+
92
+ ## Rules (v0.1)
93
+
94
+ | Code | Severity | What it checks |
95
+ |------|----------|----------------|
96
+ | MCP101 | warning | Tool description missing or shorter than 8 words |
97
+ | MCP102 | warning | Description is vague ("does stuff", "handles things", "various") |
98
+ | MCP103 | warning | Description just restates the tool name |
99
+ | MCP104 | warning | Tools with 2+ parameters where parameters lack descriptions |
100
+ | MCP105 | warning | Description does not state error/return behavior |
101
+ | MCP106 | warning | Description longer than 1024 chars (token bloat) |
102
+ | MCP201 | error | Input schema missing or not a JSON object schema |
103
+ | MCP202 | warning | Required parameter is not described |
104
+ | MCP203 | info | `additionalProperties: false` schema but description states no constraints |
105
+ | MCP204 | info | Description repeats the tool name (wasted tokens) |
106
+ | MCP205 | error | Declared output schema is invalid |
107
+ | MCP301 | error | Command-execution tool without a safety note |
108
+ | MCP302 | warning | Parameter name looks like a secret (`api_key`, `token`, …) |
109
+ | MCP303 | warning | Destructive operation without a confirmation parameter |
110
+ | MCP401 | info | Full definition snapshot costs more than ~10k tokens |
111
+
112
+ ## Pytest plugin
113
+
114
+ Install with the `test` extra, then contract-test your tools over the real protocol:
115
+
116
+ ```bash
117
+ pip install "mcp-gatekeeper[test]"
118
+ ```
119
+
120
+ ```python
121
+ import pytest
122
+ from my_server import mcp # your FastMCP / MCP server object
123
+
124
+
125
+ @pytest.mark.parametrize("mcp_server", [mcp], indirect=True)
126
+ def test_search(mcp_server):
127
+ result = mcp_server.call_tool("search", {"query": "hello"})
128
+ assert result.is_error is False
129
+
130
+
131
+ @pytest.mark.parametrize("mcp_server", [mcp], indirect=True)
132
+ def test_contract(mcp_server):
133
+ mcp_server.tool_contract("search")
134
+ # asserts: described, valid object schema, described required params,
135
+ # and the error path returns is_error instead of crashing
136
+ ```
137
+
138
+ Or point a whole suite at one server: `pytest --mcp-server "uvx mcp-server-fetch"`.
139
+ Works in-memory for server objects (fully offline), stdio/HTTP for commands and URLs.
140
+
141
+ ## How it compares
142
+
143
+ | | MCP Inspector | mcplint-cli | mcp-doctor | **mcpgate** |
144
+ |---|---|---|---|---|
145
+ | Purpose | interactive debugger | description lint | diagnostics | unified quality gate |
146
+ | CLI-first, CI exit codes | ✗ | ✓ | partial | ✓ (0/1/2) |
147
+ | Definition lint rules | ✗ | ✓ | ✗ | ✓ (15 rules) |
148
+ | Doctor / health check | ✓ | ✗ | ✓ | ✓ |
149
+ | pytest plugin | ✗ | ✗ | ✗ | ✓ |
150
+ | JSON + SARIF output | ✗ | ✗ | ✗ | ✓ |
151
+ | Framework-agnostic (speaks MCP) | ✓ | ✓ | ✓ | ✓ |
152
+
153
+ ## Roadmap
154
+
155
+ - **v0.2** — `--fix`: LLM-assisted rewriting of weak descriptions (the engine already
156
+ carries the context needed per finding)
157
+ - Declarative YAML test suites and `--baseline` files
158
+ - Custom user rule plugins and a pre-commit hook package
159
+ - GitHub Action and official MCP servers registry integration
160
+
161
+ ## Contributing
162
+
163
+ See [CONTRIBUTING.md](CONTRIBUTING.md). MIT licensed — see [LICENSE](LICENSE).
@@ -0,0 +1,130 @@
1
+ # mcpgate
2
+
3
+ > Automated quality gate for MCP servers — **ruff + pytest for MCP**.
4
+
5
+ [![CI](https://github.com/ofsazib/mcpgate/actions/workflows/ci.yml/badge.svg)](https://github.com/ofsazib/mcpgate/actions/workflows/ci.yml)
6
+ [![PyPI](https://img.shields.io/pypi/v/mcpgate)](https://pypi.org/project/mcp-gatekeeper/)
7
+ [![Python](https://img.shields.io/pypi/pyversions/mcpgate)](https://pypi.org/project/mcp-gatekeeper/)
8
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
9
+ [![Coverage](https://codecov.io/gh/ofsazib/mcpgate/branch/main/graph/badge.svg)](https://codecov.io/gh/ofsazib/mcpgate)
10
+
11
+ ## The problem
12
+
13
+ When an MCP tool description is vague, the AI agent misuses the tool — even though
14
+ the code behind it is perfect. This failure is invisible to unit tests because it
15
+ happens at the AI-interpretation layer. Research documents the scale:
16
+
17
+ - An arXiv study of 103 major MCP servers (856 tools) found tool descriptions
18
+ systematically "smelly and costly" ([arXiv:2602.14878](https://arxiv.org/abs/2602.14878)).
19
+ - An AWS-led study found **97.1% of MCP tool descriptions contain at least one
20
+ quality defect**.
21
+
22
+ `mcpgate` catches these defects before agents hit them in production. It speaks the
23
+ real protocol, so it works with any server — FastMCP, the official Python SDK, or
24
+ TypeScript servers — over stdio or HTTP.
25
+
26
+ ## Quickstart
27
+
28
+ ```bash
29
+ uv tool install mcp-gatekeeper # or: pip install mcp-gatekeeper
30
+
31
+ mcpgate lint "uvx mcp-server-fetch"
32
+ mcpgate doctor "uvx mcp-server-fetch"
33
+ ```
34
+
35
+ Exit codes are CI-friendly: `0` clean, `1` findings (fail the build), `2` connection
36
+ or protocol error.
37
+
38
+ ## Example output
39
+
40
+ ```text
41
+ warning MCP101 Tool `delete_record`: description too short (7 words; minimum 8) src: tools/delete_record
42
+ warning MCP105 Tool `delete_record`: description does not state error/return behavior src: tools/delete_record
43
+ warning MCP303 Tool `delete_record`: destructive operation without a confirmation parameter src: tools/delete_record
44
+ error MCP301 Tool `run_command`: suggests command execution without a safety note src: tools/run_command
45
+ warning MCP101 Tool `run_command`: description too short (7 words; minimum 8) src: tools/run_command
46
+ warning MCP105 Tool `run_command`: description does not state error/return behavior src: tools/run_command
47
+ warning MCP101 Tool `search`: description too short (2 words; minimum 8) src: tools/search
48
+ warning MCP105 Tool `search`: description does not state error/return behavior src: tools/search
49
+ info MCP204 Tool `search`: description repeats the tool name (wasted tokens) src: tools/search
50
+ ———
51
+ found 9 problems (1 errors, 7 warnings)
52
+ run `mcpgate lint --explain CODE` for details
53
+ ```
54
+
55
+ Formats: `--format text|json|sarif` (SARIF plugs into GitHub code scanning).
56
+ Filter with `--select MCP3xx --ignore MCP401`; inspect a rule with
57
+ `--explain MCP101`; fail on warnings with `--strict`.
58
+
59
+ ## Rules (v0.1)
60
+
61
+ | Code | Severity | What it checks |
62
+ |------|----------|----------------|
63
+ | MCP101 | warning | Tool description missing or shorter than 8 words |
64
+ | MCP102 | warning | Description is vague ("does stuff", "handles things", "various") |
65
+ | MCP103 | warning | Description just restates the tool name |
66
+ | MCP104 | warning | Tools with 2+ parameters where parameters lack descriptions |
67
+ | MCP105 | warning | Description does not state error/return behavior |
68
+ | MCP106 | warning | Description longer than 1024 chars (token bloat) |
69
+ | MCP201 | error | Input schema missing or not a JSON object schema |
70
+ | MCP202 | warning | Required parameter is not described |
71
+ | MCP203 | info | `additionalProperties: false` schema but description states no constraints |
72
+ | MCP204 | info | Description repeats the tool name (wasted tokens) |
73
+ | MCP205 | error | Declared output schema is invalid |
74
+ | MCP301 | error | Command-execution tool without a safety note |
75
+ | MCP302 | warning | Parameter name looks like a secret (`api_key`, `token`, …) |
76
+ | MCP303 | warning | Destructive operation without a confirmation parameter |
77
+ | MCP401 | info | Full definition snapshot costs more than ~10k tokens |
78
+
79
+ ## Pytest plugin
80
+
81
+ Install with the `test` extra, then contract-test your tools over the real protocol:
82
+
83
+ ```bash
84
+ pip install "mcp-gatekeeper[test]"
85
+ ```
86
+
87
+ ```python
88
+ import pytest
89
+ from my_server import mcp # your FastMCP / MCP server object
90
+
91
+
92
+ @pytest.mark.parametrize("mcp_server", [mcp], indirect=True)
93
+ def test_search(mcp_server):
94
+ result = mcp_server.call_tool("search", {"query": "hello"})
95
+ assert result.is_error is False
96
+
97
+
98
+ @pytest.mark.parametrize("mcp_server", [mcp], indirect=True)
99
+ def test_contract(mcp_server):
100
+ mcp_server.tool_contract("search")
101
+ # asserts: described, valid object schema, described required params,
102
+ # and the error path returns is_error instead of crashing
103
+ ```
104
+
105
+ Or point a whole suite at one server: `pytest --mcp-server "uvx mcp-server-fetch"`.
106
+ Works in-memory for server objects (fully offline), stdio/HTTP for commands and URLs.
107
+
108
+ ## How it compares
109
+
110
+ | | MCP Inspector | mcplint-cli | mcp-doctor | **mcpgate** |
111
+ |---|---|---|---|---|
112
+ | Purpose | interactive debugger | description lint | diagnostics | unified quality gate |
113
+ | CLI-first, CI exit codes | ✗ | ✓ | partial | ✓ (0/1/2) |
114
+ | Definition lint rules | ✗ | ✓ | ✗ | ✓ (15 rules) |
115
+ | Doctor / health check | ✓ | ✗ | ✓ | ✓ |
116
+ | pytest plugin | ✗ | ✗ | ✗ | ✓ |
117
+ | JSON + SARIF output | ✗ | ✗ | ✗ | ✓ |
118
+ | Framework-agnostic (speaks MCP) | ✓ | ✓ | ✓ | ✓ |
119
+
120
+ ## Roadmap
121
+
122
+ - **v0.2** — `--fix`: LLM-assisted rewriting of weak descriptions (the engine already
123
+ carries the context needed per finding)
124
+ - Declarative YAML test suites and `--baseline` files
125
+ - Custom user rule plugins and a pre-commit hook package
126
+ - GitHub Action and official MCP servers registry integration
127
+
128
+ ## Contributing
129
+
130
+ See [CONTRIBUTING.md](CONTRIBUTING.md). MIT licensed — see [LICENSE](LICENSE).
@@ -0,0 +1,29 @@
1
+ """An example MCP server that violates many mcpgate rules (for docs).
2
+
3
+ Run: uv run mcpgate lint "python3 examples/bad_server.py"
4
+ """
5
+
6
+ import logging
7
+
8
+ from mcp.server.fastmcp import FastMCP
9
+
10
+ logging.getLogger("mcp").setLevel(logging.WARNING)
11
+
12
+ mcp = FastMCP("example-bad-server")
13
+
14
+ TOOLS = {
15
+ "search": "Search stuff.",
16
+ "run_command": "Runs any shell command you pass it.",
17
+ "delete_record": "Removes a record from the database permanently.",
18
+ }
19
+
20
+
21
+ def _register() -> None:
22
+ for name, desc in TOOLS.items():
23
+ mcp.tool(name=name, description=desc)(lambda: None)
24
+
25
+
26
+ _register()
27
+
28
+ if __name__ == "__main__":
29
+ mcp.run()