MCP ScopeCheck
Inspect before you connect. MCP ScopeCheck is a pre-install static auditor that compares a Python MCP tool's declared contract with security-relevant behavior reachable from its source—without importing or running the server.
Quick start
Python 3.11 or newer is required; CI tests 3.11, 3.12, and 3.13. Install the dependency-free scanner from PyPI:
python -m pip install mcp-scopecheck
For an isolated CLI installation, pipx is also supported:
pipx install mcp-scopecheck
Audit a local Python file or directory:
mcp-scopecheck audit path/to/server.py
From this repository checkout, the bundled unsafe fixture provides a reproducible first audit:
mcp-scopecheck audit examples/unsafe_docs_server
$ mcp-scopecheck audit examples/unsafe_docs_server
[CRITICAL] MSC001 Agent-directed instruction in tool description
[CRITICAL] MSC105 Environment data reaches network egress
[HIGH] MSC101 Read-only claim conflicts with reachable behavior
[HIGH] MSC102 Network egress is not disclosed
[HIGH] MSC103 Filesystem scope is not constrained
[HIGH] MSC104 Dangerous filesystem default
The paired hardened fixture retains its intended filesystem-read capability but returns Findings (0) and exits 0.
Exit codes are stable for local and CI use:
| Code | Meaning |
|---|---|
0 |
No finding met the configured threshold |
1 |
One or more findings met the configured threshold |
2 |
Invalid input, no supported tools, diagnostics, or another audit error |
Use --fail-on high (or low, medium, or critical) to set the exit-1 threshold.
Why this exists
An MCP tool description and its annotations are claims. They do not enforce a permission boundary. A tool named search_project_docs can still contain code that reads /, accesses environment values, starts a process, or sends data over the network.
ScopeCheck asks a narrow, evidence-backed question:
Does the tool's declared contract agree with behavior reachable from the tool's source?
That focus complements manifest scanners and runtime testing tools. It does not replace either.
Security invariant
ScopeCheck reads bounded source bytes, decodes them strictly using Python's PEP
263 encoding rules, and parses the resulting text with Python's ast module.
Decode failures make the audit incomplete rather than substituting replacement
characters. Target modules are never imported, decorators are never invoked,
and MCP servers are never started. A regression test places a real top-level
side effect in a fixture and proves it does not execute during an audit.
What v0.1 detects
| Rule | Severity | What it means |
|---|---|---|
MSC001 |
Critical | Deterministic indicator families find agent-directed override, concealment, covert transfer, or related high-risk wording |
MSC101 |
High/Critical | readOnlyHint=true conflicts with justified state-changing behavior; process and dynamic code conflicts are Critical |
MSC102 |
High | Reachable egress lacks a clear external-interaction disclosure, contradicts an explicit denial, or conflicts with a supported static destination check |
MSC103 |
High | A correlated path-like input reaches a filesystem operation without a recognized guard on that value |
MSC104 |
High | A path/root parameter defaults to the POSIX root or to an exact home root that code actually expands |
MSC105 |
Critical | Environment-derived data reaches a supported module or proven client-instance network sink in the same reachable function |
MSC106 |
Critical | Process or shell execution is reachable |
MSC107 |
Critical | eval or exec is reachable |
MSC108 |
High | openWorldHint=false conflicts with reachable external network interaction |
Observed capabilities are reported separately from findings. Filesystem reads are not automatically vulnerabilities; the contract comparison determines whether the behavior is inconsistent or insufficiently constrained.
The 5-S report
Every audit is organized around:
- Source — what local source was inspected.
- Surface — which MCP tools were discovered.
- Scope — parameters and declared annotations.
- Side effects — filesystem, environment, network, process, and dynamic-code capabilities reachable from each tool.
- Snapshot — a deterministic SHA-256 digest of the extracted contract and capabilities.
For a broader manual review, use the 5-S pre-install checklist.
Current boundaries
v0.1 intentionally supports:
- Local Python files/directories
- Module-level
@mcp.tool,@mcp.tool(), and equivalent.tooldecorators - Direct same-file module and nested sync/async helper-call reachability
- Module-level and function-local import aliases with statement-order shadowing
- Explicit module-level
httpx,requests, andrequests.apirequest functions,urllib.request.urlopen/urlretrieve,socket.create_connection, and request methods on flow-provenhttpx.Client,httpx.AsyncClient,requests.Session, andrequests.sessions.Sessionvalues - Qualified builtin,
pathlib,os, andshutilfilesystem operations with static open-mode/flag handling
It does not yet prove:
- Cross-module, callback, function-alias, lambda, or class-method call paths
- Runtime-only tool registration
httpx.streamoraiohttp.requestcontext-manager factories,http.clientconnection methods, or raw-socket instance traffic- TypeScript/JavaScript behavior
- Authorization correctness
- Whether all observed data actually leaves the process, except the narrow same-function flow implemented by
MSC105 - Safety of a running MCP server
MSC001 and MSC102 are deterministic description checks, not semantic or LLM
analysis. MSC103 correlates simple path aliases and transformations with the
guarded value; .resolve() alone is only normalization. MSC105 follows direct
environment reads, simple value assignments, and proven local HTTP-client
bindings in lexical order within one function. Reassignment and deletion kill a
client binding. ScopeCheck does not model complete Python control flow, general
points-to relationships, or interprocedural environment taint.
Audits fail incomplete with exit 2 when fixed safety limits are exceeded:
1 MB per file, 5,000 Python files, 20 MB total source, 500,000 AST nodes, 200 AST
levels, or 100 retained diagnostics. A symlink supplied as the target is rejected;
symlinked files and directories encountered inside a directory target are
skipped without following them.
A clean report is not proof of safe runtime behavior. See architecture and threat model.
Develop
For an editable source installation, create a virtual environment and install the pinned development tools. The test suite uses only the Python standard library, and the installed scanner still has no runtime dependencies.
python3 -m venv .venv
.venv/bin/python -m pip install --disable-pip-version-check -r requirements-dev.txt
.venv/bin/python -m pip install --no-deps -e .
PYTHONDONTWRITEBYTECODE=1 PYTHONPATH=src .venv/bin/python -m unittest discover -s tests -v
PYTHONPATH=src .venv/bin/python -m mcp_scopecheck audit examples/unsafe_docs_server
PYTHONPATH=src .venv/bin/python -m mcp_scopecheck audit examples/hardened_docs_server
Before a public release, activate that environment and run scripts/preflight.sh. It runs the same test, compile, Ruff, strict mypy, and build gates as CI; it additionally requires Gitleaks and fails closed if the scanner is unavailable.
For the source-evidence, unsafe/hardened, and exit-code demo:
scripts/demo.sh
Roadmap
- Cross-module call graph and argument-aware data flow
- TypeScript parser backed by a real syntax tree
- JSON/SARIF output and stable rule schema
- Snapshot comparison for tool-definition drift
- Optional semantic description analysis with an explicit privacy boundary
- Reproducible benchmark corpus with measured precision and recall
License
MIT
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file mcp_scopecheck-0.1.2.tar.gz.
File metadata
- Download URL: mcp_scopecheck-0.1.2.tar.gz
- Upload date:
- Size: 70.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d40d930b80d0ea4ca7a69c9593a0add72bfac6dc3713bead7d97a2c7895c8a4c
|
|
| MD5 |
1efb85ceac147a72169673955fdd0c4b
|
|
| BLAKE2b-256 |
484611d856f2666e2e3fbc2f82f9d7f51db0d46946c3b0eb30662570e8b6b3e1
|
Provenance
The following attestation bundles were made for mcp_scopecheck-0.1.2.tar.gz:
Publisher:
release.yml on iamudayrathore/mcp-scopecheck
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
mcp_scopecheck-0.1.2.tar.gz -
Subject digest:
d40d930b80d0ea4ca7a69c9593a0add72bfac6dc3713bead7d97a2c7895c8a4c - Sigstore transparency entry: 2481983467
- Sigstore integration time:
-
Permalink:
iamudayrathore/mcp-scopecheck@09a7fe21b1b356c41b5b8885f7a77bbb27b902ba -
Branch / Tag:
refs/heads/main - Owner: https://github.com/iamudayrathore
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@09a7fe21b1b356c41b5b8885f7a77bbb27b902ba -
Trigger Event:
workflow_dispatch
-
Statement type:
File details
Details for the file mcp_scopecheck-0.1.2-py3-none-any.whl.
File metadata
- Download URL: mcp_scopecheck-0.1.2-py3-none-any.whl
- Upload date:
- Size: 32.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
337695bf70f581041e6a3423831dfba9d7ae58b9d04cab8838631a9533f48844
|
|
| MD5 |
3a30ffcc4163d16bb4b7d5526658c66b
|
|
| BLAKE2b-256 |
1f8614d39d00a89b1b69ec495092c49e83c7b285e300dba9b7222ab63ae70738
|
Provenance
The following attestation bundles were made for mcp_scopecheck-0.1.2-py3-none-any.whl:
Publisher:
release.yml on iamudayrathore/mcp-scopecheck
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
mcp_scopecheck-0.1.2-py3-none-any.whl -
Subject digest:
337695bf70f581041e6a3423831dfba9d7ae58b9d04cab8838631a9533f48844 - Sigstore transparency entry: 2481983591
- Sigstore integration time:
-
Permalink:
iamudayrathore/mcp-scopecheck@09a7fe21b1b356c41b5b8885f7a77bbb27b902ba -
Branch / Tag:
refs/heads/main - Owner: https://github.com/iamudayrathore
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@09a7fe21b1b356c41b5b8885f7a77bbb27b902ba -
Trigger Event:
workflow_dispatch
-
Statement type: