Runtime behavioral auditor for MCP servers — strace-based scope-violation detection
Project description
mcp-behave
Runtime behavioral auditor for MCP servers. Runs an MCP server under
strace, exercises every tool / resource / prompt it advertises, and then
compares what it declared against what it actually did — file reads,
network egress, subprocess execution.
Static scanners read tool descriptions. mcp-behave watches behavior.
The idea in one contrast
targets/leaky_server.py and targets/honest_server.py advertise the
identical tool:
format_note— "Formats a markdown note. Purely local text formatting."
A static scanner reads two harmless tools. mcp-behave sees the difference:
| Target | network egress | sensitive file read | findings |
|---|---|---|---|
honest_server |
none | none | 0 |
leaky_server |
93.184.216.34:80 |
~/.ssh/id_rsa (a canary) |
2 HIGH |
The honest server producing zero findings matters as much as the leaky one tripping two — false positives would kill credibility.
Install
pip install mcp-behave
Requires Linux + strace for the syscall-trace backend. On macOS/Windows, use
the bundled Dockerfile (see below).
Use
# Audit any stdio MCP server -- pass its launch command:
mcp-behave python -m mcp_server_fetch
mcp-behave npx -y @modelcontextprotocol/server-filesystem /tmp
mcp-behave uvx mcp-server-git
# Machine-readable output for CI:
mcp-behave --json --fail-on high python -m mcp_server_fetch
# Connect to an already-running remote server (manifest analysis only,
# no syscall trace possible):
mcp-behave --transport sse http://localhost:8000/sse
Exit codes: 0 clean, 3 findings at or above --fail-on threshold,
2 strace/ptrace setup failed, 1 other error.
Useful flags
--json— emit findings as JSON on stdout.--fail-on {never,info,high}— severity that triggers exit code 3 (defaulthigh).--no-dns— skip reverse-DNS lookups (faster, offline-safe).--timeout SECONDS— per-call timeout (default 15).--transport {stdio,sse,streamable-http}— remote transports skip syscall tracing.--out-dir PATH— wheremanifest.jsonandtrace.logare written.
What it detects
- Undeclared network egress — IP:port destinations reached during tool calls (reverse-DNS resolved in findings when available).
- Sensitive-path reads —
~/.ssh/*,~/.aws/*,~/.env,~/.netrc, etc. - Rug-pull — the declared manifest hash changes between runs of the same
server (e.g. a tool's description silently changes after the user trusts it).
Hashes persist under
$XDG_DATA_HOME/mcp-behave/manifests/.
Findings are framed as observations ("does X, undeclared"), never accusations.
Docker (works on macOS / Windows hosts)
docker build -t mcp-behave .
# default: runs the bundled leaky target so you can see findings immediately
docker run --rm --cap-add=SYS_PTRACE mcp-behave
# point at a real server
docker run --rm --cap-add=SYS_PTRACE mcp-behave python -m mcp_server_fetch
--cap-add=SYS_PTRACE is required for strace to attach inside the container.
The image bundles Node.js (for npx servers), uv (for uvx servers), and
sandbox canary files (sandbox_home/) mounted as $HOME so credential reads
are detectable.
Development
pip install -e ".[test]"
pytest -v
CI runs on Ubuntu (strace is Linux-only); see .github/workflows/ci.yml.
Known limits
- Linux-only ground truth via
strace. eBPF/seccomp backend is on the roadmap — see ROADMAP.md. - stdio is the only transport with syscall tracing.
sseandstreamable-httpconnect to a remote server we cannot trace; only manifest analysis and rug-pull detection apply there. - Input synthesis is heuristic — JSON Schema
format, key-name hints, and type defaults. Schema-coverage fuzzing (hypothesis-jsonschema) is roadmap. - AF_UNIX egress isn't matched — a server exfiltrating over a unix domain socket would slip past the current network detection.
- Single call per capability — servers that misbehave only on specific inputs or after N calls may not be triggered.
License
MIT. See LICENSE.
Project details
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_behave-0.1.1.tar.gz.
File metadata
- Download URL: mcp_behave-0.1.1.tar.gz
- Upload date:
- Size: 17.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c0b2b2b7414d249c42ecc4e88989f8f58be4da9b651a1874aafd88cf7d4264e1
|
|
| MD5 |
d5302209f4e8764b2af1efddc706b108
|
|
| BLAKE2b-256 |
7f14ce120909b1941d689849f6ee45e7a1bd3dd7f685c53c95daee09172a3252
|
Provenance
The following attestation bundles were made for mcp_behave-0.1.1.tar.gz:
Publisher:
publish.yml on navid72m/mcp-behave
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
mcp_behave-0.1.1.tar.gz -
Subject digest:
c0b2b2b7414d249c42ecc4e88989f8f58be4da9b651a1874aafd88cf7d4264e1 - Sigstore transparency entry: 1948491359
- Sigstore integration time:
-
Permalink:
navid72m/mcp-behave@02ab890cce8dad50e1d3ca14adca667adc34615a -
Branch / Tag:
refs/tags/v0.1.1 - Owner: https://github.com/navid72m
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@02ab890cce8dad50e1d3ca14adca667adc34615a -
Trigger Event:
push
-
Statement type:
File details
Details for the file mcp_behave-0.1.1-py3-none-any.whl.
File metadata
- Download URL: mcp_behave-0.1.1-py3-none-any.whl
- Upload date:
- Size: 14.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e6c9cc8dbe8147f1fa1e8db3e8f955c9d06db6aa79f59277c4f32a47f34b5ea6
|
|
| MD5 |
5d384ab029beea9c5436654e6bc57790
|
|
| BLAKE2b-256 |
f325b79f5cf0e7783dbe936de23ffc9359d771d1f1454ede5d6a9ed1fe1aab71
|
Provenance
The following attestation bundles were made for mcp_behave-0.1.1-py3-none-any.whl:
Publisher:
publish.yml on navid72m/mcp-behave
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
mcp_behave-0.1.1-py3-none-any.whl -
Subject digest:
e6c9cc8dbe8147f1fa1e8db3e8f955c9d06db6aa79f59277c4f32a47f34b5ea6 - Sigstore transparency entry: 1948491522
- Sigstore integration time:
-
Permalink:
navid72m/mcp-behave@02ab890cce8dad50e1d3ca14adca667adc34615a -
Branch / Tag:
refs/tags/v0.1.1 - Owner: https://github.com/navid72m
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@02ab890cce8dad50e1d3ca14adca667adc34615a -
Trigger Event:
push
-
Statement type: