mcp server
mcp-trustlint
Security contract testing, tool-poisoning detection, and drift gates for Model Context Protocol servers.
Set up
Runs on your computer. Pick your agent and operating system.
Before you start
- uv (`curl -LsSf https://astral.sh/uv/install.sh | sh`)
{
"mcpServers": {
"mcp-trustlint": {
"command": "uvx",
"args": [
"mcp-trustlint"
]
}
}
}same format Cursor, GitHub Copilot CLI, Junie, JetBrains AI Assistant, Augment (Auggie CLI), Factory Droid, Kiro, Devin CLI +12
Values in angle brackets are placeholders. Restart Claude Desktop after editing its config.Claude Desktop docs
README
synced Oct 21, 2026MCP TrustLint
Security contract testing for Model Context Protocol servers.
MCP TrustLint connects to a real MCP server, inventories the tools exposed to AI agents, and flags dangerous or deceptive metadata before the server reaches a user. It is designed for MCP authors who want a fast local check and a stable CI signal without executing any tools.
[!IMPORTANT] MCP TrustLint is alpha software. The passive scanner is read-only, but its findings are signals for human review rather than proof that a server is safe.
Why this exists
An MCP tool definition is part API contract and part instruction presented to an AI model. A server can look harmless while advertising hidden instructions, unexpected command execution, broad filesystem access, or schemas that enable more agency than the tool name suggests.
MCP TrustLint makes that trust boundary visible. Today it answers four useful questions:
- What tools does this server expose?
- Do their descriptions try to manipulate the model?
- Do their descriptions or parameters imply risky capabilities?
- Has the live contract changed since it was reviewed?
Capability graphs, richer policy gates, and controlled red-team testing are tracked in the public roadmap.
Quick start
git clone https://github.com/hugocaav/mcp-trustlint.git
cd mcp-trustlint
python3 -m venv .venv
source .venv/bin/activate
python -m pip install -e .
Inspect a local stdio server:
mcp-trustlint inspect "python tests/fixtures/poisoned_server.py"
Scan it with deterministic, offline rules:
mcp-trustlint scan "python tests/fixtures/poisoned_server.py"
The included fixture is intentionally vulnerable but behaviorally harmless. A
scan finds hidden instructions in search_docs, the misleading shell capability
advertised by add_numbers, and the legitimate file access exposed by
read_file. The benign get_weather control is not flagged.
Remote Streamable HTTP and legacy SSE targets use the same commands:
mcp-trustlint scan https://example.com/mcp
Contract drift
Create a canonical snapshot after reviewing a server:
mcp-trustlint snapshot "python server.py"
git add mcp-trustlint.lock.json
Compare a future build against the approved contract:
mcp-trustlint diff "python server.py"
diff exits with status 1 when a tool is added or removed, a model-facing
description changes, or an input schema changes. Snapshot fingerprints are
stable across tool ordering. Stored target labels remove URL credentials, query
strings, and stdio arguments so lockfiles do not capture secrets.
GitHub Action
Commit the approved snapshot, then add the contract gate to a workflow:
permissions:
contents: read
steps:
- uses: actions/checkout@v4
- uses: hugocaav/mcp-trustlint@v0.1.0a1
with:
target: python server.py
baseline: mcp-trustlint.lock.json
The action installs the pinned repository release and fails the job on any unapproved drift.
Reports
Terminal output is grouped by tool and color-coded by severity. JSON is suitable for automation, while SARIF can be uploaded to GitHub Code Scanning.
mcp-trustlint scan TARGET --format json --output report.json
mcp-trustlint scan TARGET --format sarif --output report.sarif
Every finding includes a stable check identifier, severity, title, OWASP Agentic Security reference, detection source, evidence, and remediation.
Optional semantic classifier
Deterministic checks are always enabled and make no model API calls. The optional OpenAI classifier looks for paraphrased or obfuscated tool poisoning:
python -m pip install -e ".[llm]"
export OPENAI_API_KEY="your-key"
mcp-trustlint scan TARGET --llm
Use --llm-debug only for diagnostics. Debug output is written to stderr. Never
commit API keys; .env is ignored and is not loaded automatically.
Architecture
flowchart LR
A["stdio / HTTP / SSE target"] --> B["MCP handshake"]
B --> C["Tool inventory"]
C --> D["Deterministic checks"]
C --> E["Optional LLM classifier"]
D --> F["Normalized findings"]
E --> F
F --> G["Terminal"]
F --> H["JSON"]
F --> I["SARIF"]
C --> J["Canonical snapshot"]
J --> K["Contract diff / CI gate"]
The client completes the MCP initialize handshake before requesting
tools/list. Tool metadata is treated as untrusted input throughout the scan.
See Architecture and Threat model
for the design boundaries.
Current checks
| Rule | What it detects | Default severity |
|---|---|---|
tool_poisoning | Model-directed instructions, concealment, exfiltration language, and sensitive paths | High |
risky_capabilities | Command execution, filesystem, network, write, and read signals | High to Low |
llm_poisoning | Semantic hidden instructions and scope violations | Confidence-based |
contract_drift | Tools, descriptions, or schemas changed after approval | High to Low |
The full rule behavior and known limitations are documented in Rule reference.
Security properties
- Passive scans never invoke MCP tools.
- Tool descriptions and schemas are treated as attacker-controlled data.
- The default scanner is offline after connecting to the target.
- LLM use is explicit and isolated behind
--llm. - Structured model output is validated and failures do not crash the scan.
- Reports do not include
OPENAI_API_KEY.
MCP TrustLint currently analyzes declared metadata, not implementation behavior. A clean report does not prove that a server is benign, correctly authorized, or sandboxed. Read the threat model before using results as a security decision.
Development
source .venv/bin/activate
python -m pip install -e ".[dev,llm]"
ruff check .
ruff format --check .
mypy src/mcp_trustlint
pytest --cov=mcp_trustlint
Contributions should begin with CONTRIBUTING.md. Security issues should follow SECURITY.md rather than a public issue.
License
Licensed under the Apache License 2.0.