Skip to main content

Runtime sidecar protocol and Python monitor for MCP server fuzzing

Project description

mcpfz-probe

CI PyPI

Standalone runtime probe for MCP server fuzzing. It runs beside mcp-server-fuzzer: the fuzzer owns process launch and per-call timing; this repo owns runtime event collection, per-call attribution, and the sidecar protocol. The privileged sidecar is Rust (small, auditable, static Linux artifact); the integration layer is Python (a simple RuntimeMonitor). See the fuzzer's Runtime monitoring section for how the two fit together.

Install

The Python monitor + policy engine (used by the fuzzer):

pip install mcpfz-probe

CI also publishes a prebuilt, eBPF-enabled Linux sidecar binary on each v* tag. Fetch the latest and point the fuzzer at it via MCPFZ_PROBE_BIN:

curl -L -o mcpfz-probe \
  https://github.com/Agent-Hellboy/mcpfz-probe/releases/latest/download/mcpfz-probe-x86_64-linux-ebpf
chmod +x mcpfz-probe

Repo layout

crates/mcpfz-probe/            Rust sidecar binary (fake + eBPF backends)
crates/mcpfz-probe-ebpf/       BPF program (compiled to bytecode, Linux only)
crates/mcpfz-probe-ebpf-common/ #[repr(C)] event type shared kernel<->userspace
src/mcpfz_probe/               Python monitor + policy engine
tests/                         CI-friendly Python tests (fake backend, no root)
examples/                      Sample events + live eBPF demo
docs/                          Architecture and protocol notes

Status

  • Python monitor + policy engine: complete and tested.
  • fake backend: complete. A deterministic test double that replays scripted events into the correct bucket (startup/call/ambient) attributed to the active call_id — the whole pipeline runs on any OS without root.
  • ebpf backend: real, multi-probe. Loads a CO-RE BPF program on syscalls:sys_enter_* tracepoints — exec (execve), network (connect, sendto), file open (openat), delete (unlink/unlinkat), chmod (chmod/fchmodat), and ptrace — attributes captured events to the active call window, and emits the same NDJSON as the fake backend. Verified on Ubuntu 24.04 / kernel 6.8 driven by mcp-server-fuzzer: a vulnerable server produced exec, TCP+UDP connect, sensitive read, out-of-workspace write, chmod, delete, and ptrace findings, each attributed to the exact tool call.

Sidecar protocol (NDJSON)

Python → sidecar stdin (control):

{"op":"scope","pgid":1234,"generation":1}
{"op":"mark","phase":"begin","call_id":"...","tool":"get_weather"}
{"op":"mark","phase":"end","call_id":"..."}

Sidecar → Python stdout (events); bucket is startup/call/ambient:

{"type":"exec","bucket":"call","call_id":"...","pid":1234,"argv":["/bin/sh","-c","curl ..."]}
{"type":"connect","bucket":"ambient","pid":1234,"dst":"203.0.113.7:443"}

Full spec in docs/protocol.md.

Using it (Python)

monitor.set_scope_pgid(server_pgid, generation=1)
monitor.begin_call(call_id, tool_name)
try:
    result = call_tool(...)
finally:
    summary = monitor.end_call(call_id)      # events attributed to this call
findings = evaluate_events(summary.events, policy)

CLI

mcpfz-probe --backend fake --events-file examples/events.sample.json
mcpfz-probe --backend ebpf          # Linux, needs root/CAP_BPF
mcpfz-probe --help

The fake backend reads an events script ({"events":[...]} or a bare [...]), each entry a trigger (startup/scope/begin/end, optional tool filter) and an event object the sidecar passes through, filling in bucket, call_id, and ts_ns. See examples/events.sample.json.

mcp-server-fuzzer integration

The probe plugs into mcp-server-fuzzer through three small, opt-in hooks (module mcp_fuzzer/runtime_probe.py): scope the sidecar to the server's process group when the stdio server launches, begin/end marks around each _execute_tool_call, and merge the resulting findings into the session at the end. When MCP_FUZZER_RUNTIME_PROBE is unset the hooks are no-ops.

export MCP_FUZZER_RUNTIME_PROBE=1
export MCPFZ_PROBE_BIN=/path/to/mcpfz-probe        # the sidecar binary
export MCPFZ_PROBE_BACKEND=ebpf                     # or "fake"
sudo -E mcp-fuzzer --mode tools --protocol stdio \
  --endpoint "python examples/vulnerable_server.py" \
  --runs 3 --max-concurrency 1

Kernel-observed syscalls become findings attributed to the exact tool + run: runtime.exec, runtime.net_connect, runtime.sensitive_read, runtime.fs_write, runtime.fs_delete, runtime.fs_chmod, and runtime.ptrace. Verified on Linux against examples/vulnerable_server.py (tools that shell out, read ~/.ssh, beacon over TCP/UDP, drop+chmod+delete a file, and call ptrace): all seven categories were captured and merged into the fuzzer's report, each attributed to the call that caused it.

Per-call attribution assumes stdio calls run serialized (--max-concurrency 1), matching the design in docs/architecture.md; execs from overlapping calls still get caught, but land in the ambient bucket.

Development

# Python tests
PYTHONPATH=src python3 -m unittest discover -s tests

# Rust sidecar (portable, fake backend)
cargo build
cargo test

eBPF backend (Linux)

Needs a BTF-enabled kernel and this toolchain:

rustup toolchain install nightly --component rust-src
cargo install bpf-linker            # needs LLVM dev libs (e.g. llvm-18-dev)
cargo build --features ebpf         # build.rs compiles the BPF crate to bytecode
sudo ./target/debug/mcpfz-probe --backend ebpf

Live end-to-end demo (real kernel capture + real policy), run as root:

python3 examples/ebpf_live_demo.py

Known limitation: scope filtering resolves a process's group via /proc/<pid>/stat in userspace, which races short-lived processes. Per docs/architecture.md, this should move into the kernel program (task->group_leader via CO-RE) — next step alongside the connect/file_open probes.

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

mcpfz_probe-0.1.2.tar.gz (13.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

mcpfz_probe-0.1.2-py3-none-any.whl (9.2 kB view details)

Uploaded Python 3

File details

Details for the file mcpfz_probe-0.1.2.tar.gz.

File metadata

  • Download URL: mcpfz_probe-0.1.2.tar.gz
  • Upload date:
  • Size: 13.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.13

File hashes

Hashes for mcpfz_probe-0.1.2.tar.gz
Algorithm Hash digest
SHA256 b30c199c6b16e9ad3a12e18d366b37e11b6c2102c4b3716c8d539c954e3cae7e
MD5 7adb7e960bcf90a21c2b0b76a5814c9d
BLAKE2b-256 57ff998e8af9e11f409d4d6818b9326914d3ad0929d7a0425eac39a1b391aaae

See more details on using hashes here.

File details

Details for the file mcpfz_probe-0.1.2-py3-none-any.whl.

File metadata

  • Download URL: mcpfz_probe-0.1.2-py3-none-any.whl
  • Upload date:
  • Size: 9.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.13

File hashes

Hashes for mcpfz_probe-0.1.2-py3-none-any.whl
Algorithm Hash digest
SHA256 57122b03a4546a7489664596b799fb084dfc3e85016f8b28ef61b4ac374c17bc
MD5 62cb9cbf79e3fd25ed490225b33bae46
BLAKE2b-256 cfca21180926a21541f6462ff9d9ab096c0509ad25dfb6cc005c1507a3c17f40

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page