Skip to main content

scapy-mcp

Code style: crackerjack Runtime: oneiric Framework: FastMCP uv Python: 3.14+

MCP server wrapping scapy for packet crafting, dissection, pcap staging, and live BPF capture. Tools operate on a worker that holds a scapy_mcp.config.settings.ScapySettings instance and respect a four-stage transmit policy before any frame leaves the host.

Version: 0.1.1 — BSD-3-Clause — Python ≥ 3.14 — alpha status

Install

uv pip install scapy-mcp
scapy-mcp

The console script scapy-mcp resolves to scapy_mcp.__main__:main, which builds the FastMCP ASGI app and serves it on uvicorn (so the custom /readyz route is mounted). Set SCAPY_MCP_HTTP_PORT to override the default port 3056.

For local development:

uv sync --group dev
uv run scapy-mcp

Configure

Settings layer: defaults → settings/scapy-mcp.yaml → settings/local.yaml → SCAPY_MCP_* environment variables (pydantic-settings nested delimiter is __). Nested config (e.g. auth) uses SCAPY_MCP_AUTH__ENABLED=true.

Setting Default Purpose
http_port 3056 MCP HTTP listener port
tool_profile full MINIMAL, STANDARD, or FULL profile
log_level INFO oneiric LoggingConfig level
default_iface None capture iface when caller omits one
capture_packet_cap 10000 per-session packet ceiling
capture_duration_cap_seconds 60 per-session wall-clock ceiling
capture_default_bpf_filter None global BPF filter applied when caller omits one
capture_executor_workers 2 ThreadPoolExecutor size for the sync sniff fallback
pcap_write_dir <tmpdir>/scapy_mcp/pcap-staging write-side staging root (path-containment enforced)
pcap_read_max_bytes 52428800 read-side ceiling; read_pcap refuses larger files before parsing
transmit_enabled false master kill-switch — must be true to emit any frame
transmit_allow_l3_cidrs [] L3 CIDR allow-list
transmit_allow_l2 false allow L2 (ARP / ND / RAW) emission
transmit_allow_broadcast false allow broadcast destination
transmit_max_pps 10 per-session packets-per-second cap
transmit_max_per_session 100 per-session total packet cap
transmit_max_probe_targets 16 per-call probe-target count cap

Tools

The server registers 9 domain tools + the mcp-common baseline tools.

Group Tool Purpose
craft craft_packet Build a Packet from a LayerSpec discriminated union
dissect dissect_bytes Parse raw bytes into layers + summary text
pcap read_pcap Read packet summaries from a pcap at path, with offset/limit windowing
pcap write_pcap Stage a pcap into pcap_write_dir (paths outside the dir refused)
capture capture_start Start a BPF-filtered capture (optional feed)
capture capture_stop Stop an in-progress capture session
capture capture_read Read summaries from an active capture session
transmit transmit_packet Send a single packet after every transmit control passes
transmit probe_packet One-shot emit at a small BPF, used to confirm the surface works
health health Per-feed aggregate status (counts + last error)

Plus the four mcp-common baseline tools — discover_tools, get_liveness, get_readiness, health_check_all — registered unconditionally via bootstrap_baseline_tools.

read_pcap contract

read_pcap accepts an arbitrary path (a file produced by write_pcap, a pre-existing capture, or a fixture) and returns summaries. Before parsing, the file size is checked against pcap_read_max_bytes; oversize files are refused with ConfigurationError so multi-GB captures cannot exhaust memory by being loaded whole. Path containment under pcap_write_dir is the write path's concern, not read's.

Health

Two routes, answering different questions:

  • /health — always HTTP 200. Reports per-feed detail in components. For orchestrators and curl smoke probes.
  • /readyz — HTTP 503 when a required feed has not yet returned data, 200 otherwise. For readiness probes.

The capture feed is optional: its absence never flips /readyz to 503. All other registered feeds are required.

Four transmit controls

Every transmit_packet call is checked against:

  1. Master kill-switch (transmit_enabled: bool, default false). The default refuses every frame — set SCAPY_MCP_TRANSMIT_ENABLED=true to permit emission at all.
  2. L3 CIDR allow-list (transmit_allow_l3_cidrs: list[str]). Set to ["0.0.0.0/0"] for unrestricted L3; production deployments pin the specific CIDRs the worker is allowed to reach.
  3. L2 destination allow-flag (transmit_allow_l2: bool, default false). Pure ARP / ND / RAW frames require this flag; L3 packets do not.
  4. Broadcast opt-in (transmit_allow_broadcast: bool, default false). Even after the L2/L3 allow-list, broadcasts refuse unless this is true.

A refusal emits an EmissionRefusedError with the failing control name and a human-readable reason. The wrapper logs a scapy-write-would-refuse / scapy-transmit-refused warning so the refusal is visible without polluting the caller's error stream.

BPF probe (capture)

Capture is OPTIONAL. When /dev/bpf* is missing capture_start refuses with CapabilityUnavailableError and the capture feed is marked unavailable. /readyz stays 200 because capture is not a required feed.

Backend selection

The capture worker picks one of two backends at session start:

  • AsyncSniffer — preferred when scapy exposes it (≥ 2.5). Each session owns a sniffer; capture_stop calls .stop() and capture_read reads .results.
  • sniff(stop_filter=...) — synchronous fallback executed in the module-level worker pool. stop_filter checks a per-session cancel event so capture_stop works; packet_cap and duration_cap are enforced via count and timeout on the underlying sniff call.

If scapy exposes neither backend, capture_start refuses with CapabilityUnavailableError (reason = "no sniffer backend").

Deterministic fixtures

Tests do not open raw sockets. All packet construction is exercised against in-memory fixtures under tests/fixtures/. Each fixture includes a hand-crafted bytes() body that round-trips through craft_packet → rdpcap/wrpcap → dissect_bytes. No real network frames in the suite.

Regenerate via:

python -m scripts.gen_pcap_fixtures

Default = closed-by-default transmit

The shipped defaults cannot emit a frame:

transmit_enabled = false
transmit_allow_l3_cidrs = []
transmit_allow_l2 = false
transmit_allow_broadcast = false

A worker installs only what its operator explicitly approves. See scapy_mcp/config/settings.py for the full settings surface.

License

BSD-3-Clause.

Built on Oneiric for runtime configuration and mcp-common for the FastMCP baseline. Crackerjack gates every commit.

Metadata

Release files for scapy-mcp 0.1.2

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for scapy-mcp 0.1.2
File Size Uploaded Provenance
scapy_mcp-0.1.2.tar.gz 232.1 kB Without provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for scapy-mcp 0.1.2
File Interpreter ABI Platform
scapy_mcp-0.1.2-py3-none-any.whl Python 3 none any Details

Total release size: 263.5 kB

Release files / scapy_mcp-0.1.2.tar.gz

Download URL scapy_mcp-0.1.2.tar.gz
Size 232.1 kB
Tags Source
SHA-256 checksum
How to use checksums
a545e60203e22881a49397cb5a1000ead6e8d0f61510cc52e11e845467183df8
BLAKE2b-256 checksum
How to use checksums
6d3c65d1c6ac113911604a24d61e606b6306e98495d14c930e3323fd070f8c92
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.12.10 {"installer":{"name":"uv","version":"0.12.10","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Release files / scapy_mcp-0.1.2-py3-none-any.whl

Download URL scapy_mcp-0.1.2-py3-none-any.whl
Size 31.4 kB
Tags Python 3
SHA-256 checksum
How to use checksums
a793b5cc714cd555f37a8ca8f20f51bebc7100282df400a0306cf9171a013c84
BLAKE2b-256 checksum
How to use checksums
5296edb631c609a371a806fb3abbd41c4bb263a6a09b3c3ca92466c658249411
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.12.10 {"installer":{"name":"uv","version":"0.12.10","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Security for scapy-mcp 0.1.2

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page