Skip to content

Releases

v0.2.0

Choose a tag to compare

@Kx96xx Kx96xx released this 05 Nov 07:17
b6af198

55 tools (0.1.0 had 41), six guided prompts, and one-line install from PyPI:

claude mcp add tshark-mcp -- uvx --from tshark-mcp-server tshark-mcp --allow-dir /path/to/pcaps

Added

  • Claude Code plugin in plugin/ with a marketplace entry in .claude-plugin/marketplace.json:
    /plugin install tshark-mcp --marketplace Kx96xx/tshark-mcp adds the server (started with uvx
    from the main branch, so tools and skills match), asks for the capture directory and redaction
    (off, ids, ids+ips), and adds seven skills: pcap-investigation and one per MCP prompt, each
    pointing at that prompt. tests/test_plugin.py checks the manifests against the package, that
    every prompt has a skill and every tool a skill names exists, and starts the server with the
    plugin's own arguments over stdio; a CI plugin job also runs claude plugin validate on the
    plugin and the marketplace.
  • README first screen: a one-line claude mcp add with uvx --from tshark-mcp-server, PyPI version
    and downloads badges, and a demo GIF recorded with vhs from a live MCP client session against the
    PyPI package (docs/media/demo.tape, docs/media/demo_client.py). A "Found a bug or a wrong answer?"
    section, issue forms for wrong answers and for bugs, questions routed to GitHub Discussions, and a
    1280x640 social preview image (docs/media/social-preview.png).
  • Dockerfile: tshark from Debian pinned to the 4.4 series (the build fails if the series
    changes), the server installed from the checkout, a non-root user (uid 10001), /captures as the
    read-only capture mount, a healthcheck (package imports, tshark runs) and no port. A CI docker
    job builds it (never pushes it), checks the user, the tshark version and the refusal without
    --allow-dir, and runs every task-success scenario over stdio inside docker run -i --read-only --cap-drop ALL --network none.
  • MCP Registry metadata: server.json (io.github.Kx96xx/tshark-mcp, PyPI package, stdio,
    TSHARK_MCP_ALLOW_DIRS required), the mcp-name marker in README, glama.json, and a
    registry job in publish.yml that publishes server.json with mcp-publisher (pinned, digest
    checked, GitHub OIDC) after the PyPI upload. A release fails early unless the tag, pyproject and
    server.json versions agree. uvx tshark-mcp-server now starts the server (second entry point
    named after the distribution). tests/test_server_json.py validates server.json against the
    registry schema (vendored) and keeps it in step with pyproject.
  • MCPB desktop bundle for Claude Desktop: mcpb/manifest.json (manifest 0.4, uv server type, so
    the host installs Python and the dependencies and nothing compiled ships), settings for the
    capture directories (several, required; each becomes one --allow-dir), redaction and the tshark
    path, and mcpb/build.py to write the bundle directory and keep the manifest's version and
    tools list in step with the package (--check, --update). A CI mcpb job validates and packs
    the bundle (never published) and starts the built bundle over stdio. tshark is also looked for
    at /Applications/Wireshark.app/Contents/MacOS/tshark, since desktop hosts start the server
    without the shell's PATH.
  • Task-success evals (python -m evals, docs/EVALS.md): sixteen scenarios over the synthetic
    captures, each an operator question, the calls that answer it and the facts the results must
    hold, with call and token budgets. The harness runs them over the stdio server and scores them
    deterministically, in CI through tests/test_evals.py. --driver llm lets a model choose the
    calls and scores its final answer as well; it is optional, off by default and never run in CI.
    --server-dir and a command after -- run the scenarios against another stdio server (a
    container). AGENTS.md now names path_not_allowed as the error for a path outside the allowlist.
  • Six MCP prompts as guided workflows: one_way_audio, subscriber_cannot_attach,
    trust_this_capture, working_vs_failing, ims_registration_or_sms and slow_data_session.
    Each returns the tool calls in order, what to cite, when to stop and the shape of the answer;
    Claude Code lists them as /mcp__tshark-mcp__<name>. Their path arguments complete
    (completion/complete) to capture files and subdirectories inside the allowed directories,
    at most 50 per answer.
  • Token-aware output on every tool with limit: columns keeps only the named row keys (frame
    keys always stay; an unknown name is an invalid_argument error listing the valid ones),
    output="summary" returns everything but the rows plus the first three rows in under 4 KB with
    paging pointing at the rest, and max_output_kb lowers the server's output cap for one call
    (it can never raise it). Every successful result carries tokens_estimate (JSON bytes / 3.6).
    The compact tool list writes an optional list argument as "type": ["array", "null"].
  • start and end on every tool that reads a capture: ISO 8601, epoch seconds, "+120s" after
    the capture start or "-30s" before its end. The window goes ahead of the tool's filter on
    every tshark run, including the -z statistics of conversations, expert_info and
    pcap_info; relative bounds resolve per file; results carry window and a warning when the
    window misses the capture. pcap_info adds window_hint and frames_in_window;
    find_identifier skips files outside the window.
  • capture_index: a directory of ring-buffer files in time order from capinfos, with per-file
    span, packets, bytes and interfaces, holes and overlaps between files, name-versus-time
    ordering, and which files cover a start/end window and whether they cover it whole.
  • incident_summary: the first call on an unknown capture. Runs the overview and the protocol
    tools the capture carries under one time budget and returns findings ranked by severity, each
    one deterministic sentence from a fixed threshold table with its evidence (tool, arguments,
    frames), confidence (from capture_quality's verdict for loss) and the next call; plus the
    capture's trust and blind spots, top talkers, a timeline and the passes it skipped and why.
    Tools called by another tool run in the caller's time window; the runner takes a per-call
    deadline so every run of a multi-tool call shares one budget.
  • diff_captures: before versus after a change for TCP/IP services. Runs tcp_connections,
    dns_transactions, tls_handshakes, http_transactions and tcp_health on both captures, joins per
    destination, DNS name and resolver, SNI, HTTP endpoint and TCP server, and labels each key
    worse, vanished, appeared, better or same with a sentence and the delta; volumes are also per
    minute because the captures differ in length.
  • throughput_timeline: bytes, bps and TCP goodput (bytes newly acknowledged, copies and
    retransmissions not counted) per interval, with the retransmitted bytes, zero windows,
    window-full events, duplicate ACKs, SYNs never answered, DNS queries, TLS ClientHellos and
    ICMP errors beside them; series per conversation, host or stream; a verdict per TCP stream
    (loss-limited, receiver-limited, application-idle, sender-limited, fine) and issues that say
    where goodput fell and why, and which counters burst.
  • call_legs: the legs of one call that a B2BUA, SBC or CSCF chain gave different Call-IDs,
    stitched by icid, RFC 7989 Session-ID, X- Call-ID headers, Replaces and Target-Dialog (high
    confidence), a shared SDP media endpoint (medium) or the called number and time (low), with the
    link and its confidence per leg, the delay each element added, header changes, which element
    rewrote a status or Q.850 cause and the deepest failed leg explained with cause_lookup.
  • diameter_avps(detail="key") digests per application: gy (Gy/Ro: per MSCC rating group,
    requested, granted and used units, reporting reason, validity and quota holding time,
    Final-Unit-Indication, MSCC Result-Code), rx (media components, flow status, bandwidth, first
    Codec-Data line, Specific-Action, Abort-Cause), cx (authorization and assignment types,
    identities, capability counts, authentication scheme and item count, deregistration reason), sh
    (Data-Reference names, Sh-User-Data size and element names) and s13 (IMEI, Equipment-Status).
    Failed Result-Codes and Experimental-Result-Codes carry explained from cause_lookup. Rx
    Experimental-Result-Codes 4261 and 5061 to 5068 are named in every Diameter tool.
  • nas_procedures: EMM, ESM and S1AP procedures per UE from S1-MME captures (attach, TAU,
    service request, detach, PDN connectivity, dedicated bearer activation, bearer modification and
    deactivation, S1 and X2 handover, UE context release). S1 contexts are joined into UEs by IMSI
    or assigned M-TMSI; each procedure has an outcome, EMM/ESM and S1AP causes named through
    cause_lookup, timing and its steps; ciphered NAS is counted, not guessed (null_cipher=true reads
    EEA0 traffic), and a Paging nobody answered is listed. nas-eps.null_decipher joins the allowed
    preferences.
  • tunnels_overview: every GRE, ERSPAN, VXLAN, Geneve, IP-in-IP, IPsec (IKE, ESP, AH, NAT-T),
    WireGuard, OpenVPN, L2TP and GTP-U tunnel in a capture with its outer endpoints, key or VNI,
    packets and bytes each way, inner protocols, tunnel overhead, DF packets above the path MTU and
    inner SYN MSS against the overhead; IKE exchanges and error notifies, WireGuard and OpenVPN
    handshakes, ESP SAs per direction; issues such as unanswered WireGuard initiations, a missing
    return SA or an MSS that does not fit once tunnelled.
  • export_subset: the first and only write tool, off unless the server is started with
    --export-dir DIR. It writes the packets matching a display filter (and start/end) to a new
    pcapng file in that directory for a vendor or another team: never overwrites, never follows a
    planted symlink, sanitised names, --export-max-mb (50) and `max_pack...
Read more

v0.1.0

Choose a tag to compare

@Kx96xx Kx96xx released this 04 Nov 23:06
4a2fa64

First release. Everything below was built between 1 and 4 November 2026 in pull requests #1
to #87; the numbers in brackets are the pull requests. The first 33 tools came first, then eight
more (41 in all) and the hardening landed the same day (listed under Added, Changed and Security).

Added

  • tcp_connections: every TCP connection attempt with its outcome (connected, refused,
    filtered, ICMP prohibited or unreachable, reset or unfinished handshake), SYN retries, connect
    time, bytes, duration and who closed it; view="destinations" gives the success rate per
    server and port and names a client a firewall drops while others connect (#68).
  • icmp_errors: every ICMP and ICMPv6 error grouped per sender, kind and quoted flow, with the
    MTU of fragmentation-needed errors, whether the quoted packet is in the capture, the sender's
    hop distance, echo loss and RTT, and issues that tell a routing loop from a traceroute (#71).
  • capture_metadata: the pcapng writer, interfaces, snaplen, capture filter, packet comments and
    the Interface Statistics Block drop counters that tshark has no field for, read by a pure-Python
    block walker that never decodes a packet; pcap_info reports metadata_available and
    interface_drops (#76).
  • cause_lookup: what a telecom cause or result code means, with the spec reference, typical
    causes, related codes and the tool calls that find it in a capture. Names come from tshark's
    own value strings (tshark -G values), so they match the other tools; a test fails when a
    tshark release names a code that has neither curated text nor an unexplained listing.
    Diameter codes are read as Result-Code or, with application, as that application's
    Experimental-Result-Code. No capture is read (#78).
  • http_transactions: HTTP/1.x requests paired with responses and HTTP/2 / gRPC streams with
    status, sizes, response time and TTFB, per-endpoint status mix and percentiles, gRPC status
    names, reset streams, unanswered requests and the ports carrying HTTP that tshark left raw (#80).
  • call_kpis: ASR, SER (RFC 6076, with and without 487), NER, post-dial delay, ringing time and
    ACD per peer, trunk, number prefix, user agent or interval, with failure codes, Q.850 causes and
    the worst calls by Call-ID and frame. Calls still open when the capture stopped are counted as
    near_capture_end and left out of the ratios; method states every definition (#82).
  • gtp_ies: the GTPv2-C information elements behind each transaction (APN, PDN type, PAA,
    serving network, RAT, ULI, indication flags, AMBR, F-TEIDs, PCO option names, bearer QoS, cause
    with offending IE and source, RAN/NAS cause) and likely_causes for a reject, built from the
    request. PAP and CHAP payloads in the PCO are never returned. detail="full" gives the IE tree (#84).
  • roaming_attach_check: why a roamer cannot attach or get a bearer. The subscriber's S6a, MAP,
    GTPv2-C, Gx and PFCP steps grouped into attempts, the ULA subscription held against the Create
    Session request (APN subscribed, PDN type, home routing or local breakout, RAT against
    Access-Restriction-Data, ODB, AMBR), the first failure with its reference and a one-sentence
    verdict. Authentication vectors are counted, never returned (#86).

Changed

  • Cancelling a request kills tshark within a quarter of a second instead of letting it run to
    its timeout; the audit log records cancelled. With a progressToken, find_identifier
    reports files done out of files planned and other tools one notification per finished tshark
    run, at most one per second (#83).
  • tools/list is compact by default: 102 KB instead of 263 KB for 41 tools. Output schemas
    keep their top-level keys with JSON types and name the keys of each row list; the keys
    every result may carry are left out; input schemas lose the generated titles and write
    optional arguments as a type list; the twelve descriptions over 1,500 characters move their
    detail to a resource, and the server instructions are rewritten to about 3,100 characters (they
    had grown past the 4,096 some clients keep). Each tool's full description and schemas are
    the resource schema://tools/<name>; --schema-profile full (TSHARK_MCP_SCHEMA_PROFILE)
    publishes the old list. tests/test_tool_list.py keeps the compact list within budget (#87).
  • Tool errors are returned with isError: true, so clients show and count them as failures;
    the structured {"error": {...}} stays in structuredContent and the text content. Every
    error carries a fix hint; not_found and not_a_file list nearby capture files in
    candidates, invalid_filter and unknown_field list close field names in suggestions,
    timeout and output_too_large name the arguments that narrow the call (#69).

Security

  • Security hardening of the tshark child: an empty read-only personal profile and a scrubbed
    environment, so no personal init.lua, Lua script, plugin or extcap loads
    (--allow-user-profile to opt back in); nice +10 and, on Linux, a 2 GB address-space limit
    (--max-tshark-memory-mb, out-of-memory runs report out_of_memory: true); at most two
    tshark runs at once (--max-concurrent-runs, --busy-wait, then a busy error); a capture
    replaced during the run fails with capture_changed; -d decode-as rules are validated (#73).

The server

  • Read-only MCP server over stdio. Every call runs tshark -r <file> on a capture inside an
    operator-allowed directory, through an argv list with -n, never a shell. Capture, write,
    export and extension flags are refused before a process starts.
  • Hard limits, each enforced in code and none switchable off: file size, matching packets
    dissected in full, frames read per run, JSON returned per call, raw stdout kept, wall clock
    per run and per multi-pass call, files opened per directory search.
  • Display filters are linted and compiled against an empty capture before touching the real
    file; field names must exist in tshark -G fields; structured arguments (IMSI, APN, TEID,
    Call-ID, ...) are linted to their shape before being embedded in a filter.
  • Typed outputSchema, a title and the MCP annotations readOnlyHint, idempotentHint and
    openWorldHint: false on every tool; failures come back as {"error": {...}} (#1).
  • One output contract: total_matched, returned, offset, next_offset, truncated with a
    truncated_reason, and a 30 KB cap that keeps whole rows and says how to get the rest (#25).
  • Every row carries the frame numbers it was built from, and packet_detail opens any frame (#30).
  • Text copied from packets is capped, stripped of control and bidi characters and named in
    untrusted_text_fields (#42).
  • Redaction of subscriber identifiers with stable keyed tokens that work as arguments
    (--redact ids / ids+ips), with a bounded pre-scan so even a first conversations call
    hides UE addresses (#47, #50).
    An IMEI written as TAC-SNR-CD inside a urn:gsma:imei: instance id is found too (#60).
  • JSON-lines audit log (--log-file).

Tools (the first 33)

Orientation and capture trust: pcap_info, conversations, filter_packets, packet_detail
(#30), expert_info (#36), find_fields and check_filter (#38), capture_quality (#29, #44,
#49), find_identifier across a directory of captures (#1).

Diameter: diameter_transactions, diameter_sessions (#1), diameter_avps with a key view
(procedure flags with bit names, Subscription-Data digest) and a full AVP tree (#15).

GTP and PFCP: gtp_sessions (GTPv2-C paired by sequence and peer, across NATs and proxy
chains, #4, #22, #46, #51), gtpv1_sessions (#20), gtp_user_plane, gtp_tunnel_health
(inner TCP loss, reordering, ICMP and MTU per tunnel, #18, #35), pfcp_sessions (#37).

SIP, media and IMS: sip_dialogs, sip_call_detail (#33), rtp_streams with RFC 3550
jitter, RTCP and an E-model MOS estimate (#24), media_path_check for one-way audio, NAT and
488 cases (#34), dtmf_events (#39), ims_registration (#52; network-initiated de-registration
from reg-event NOTIFYs, #61), sms_over_ims (#54).

Transport and infrastructure: tcp_health (#32, #53), response_times (#31, #43),
tls_handshakes (#28, #41), dns_transactions (#23), sctp_associations,
sigtran_messages (M3UA/SCCP/TCAP/MAP).

Cross-protocol: trace_subscriber (one timeline per subscriber across Diameter, GTPv2-C,
GTPv1-C, PFCP, MAP and SIP, one shared time budget, #13, #16, #20, #37), compare_captures
(where a failing capture first departs from a working one, #17), message_rates (bursts,
storms and subscriber loops with the nodes involved, #19, #21, #48).

Fixed during testing against real captures

  • Captures that end mid-packet no longer fail every tool; the result carries a warning (#2).
  • -c counted frames read, not frames matched, so per-packet tools saw only the first 500
    frames (#3).
  • MAP returnError reported the CHOICE index instead of the error code, and results without an
    opcode were not named after their invoke (#5, #6).
  • A SIP REGISTER answered 401 then 200 was counted as failed; retransmissions were counted as
    new requests; several SIP messages in one TCP segment are now all kept (#7, #14).
  • Diameter IMSI queries returned other subscribers' sessions from bundled SCTP packets (#8).
  • find_identifier dropped partly read files without hits and could overrun its time budget
    (#9, #10); the apn filter missed APNs carrying the operator identifier (#11).
  • Requests unanswered only because the capture stopped are marked near_capture_end and
    counted apart, for Diameter, GTPv2-C, GTPv1-C, PFCP and DNS (#51, #55).

Fixed by the bug hunt over public captures (4 November 2026)

Every tool was driven over 16 synthetic edge cases and 245 captures from the Wireshark
repository through the MCP stdio client, with default and hostile arguments, output
schemas validated and counts cross-checked against raw tshar...

Read more