tshark-mcp-server 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- tshark_mcp_server-0.1.0/LICENSE +21 -0
- tshark_mcp_server-0.1.0/PKG-INFO +254 -0
- tshark_mcp_server-0.1.0/README.md +225 -0
- tshark_mcp_server-0.1.0/pyproject.toml +49 -0
- tshark_mcp_server-0.1.0/setup.cfg +4 -0
- tshark_mcp_server-0.1.0/src/tshark_mcp/__init__.py +3 -0
- tshark_mcp_server-0.1.0/src/tshark_mcp/avps.py +392 -0
- tshark_mcp_server-0.1.0/src/tshark_mcp/callkpi.py +272 -0
- tshark_mcp_server-0.1.0/src/tshark_mcp/capmeta.py +381 -0
- tshark_mcp_server-0.1.0/src/tshark_mcp/causedata.py +724 -0
- tshark_mcp_server-0.1.0/src/tshark_mcp/causes.py +409 -0
- tshark_mcp_server-0.1.0/src/tshark_mcp/compare.py +217 -0
- tshark_mcp_server-0.1.0/src/tshark_mcp/copies.py +158 -0
- tshark_mcp_server-0.1.0/src/tshark_mcp/diameter.py +478 -0
- tshark_mcp_server-0.1.0/src/tshark_mcp/dns.py +404 -0
- tshark_mcp_server-0.1.0/src/tshark_mcp/dtmf.py +168 -0
- tshark_mcp_server-0.1.0/src/tshark_mcp/errors.py +180 -0
- tshark_mcp_server-0.1.0/src/tshark_mcp/expert.py +116 -0
- tshark_mcp_server-0.1.0/src/tshark_mcp/fields.py +292 -0
- tshark_mcp_server-0.1.0/src/tshark_mcp/gtp.py +416 -0
- tshark_mcp_server-0.1.0/src/tshark_mcp/gtpies.py +353 -0
- tshark_mcp_server-0.1.0/src/tshark_mcp/gtpv1.py +230 -0
- tshark_mcp_server-0.1.0/src/tshark_mcp/httptx.py +396 -0
- tshark_mcp_server-0.1.0/src/tshark_mcp/icmperr.py +294 -0
- tshark_mcp_server-0.1.0/src/tshark_mcp/ims.py +771 -0
- tshark_mcp_server-0.1.0/src/tshark_mcp/limits.py +352 -0
- tshark_mcp_server-0.1.0/src/tshark_mcp/media.py +194 -0
- tshark_mcp_server-0.1.0/src/tshark_mcp/models.py +1964 -0
- tshark_mcp_server-0.1.0/src/tshark_mcp/packets.py +241 -0
- tshark_mcp_server-0.1.0/src/tshark_mcp/pairing.py +353 -0
- tshark_mcp_server-0.1.0/src/tshark_mcp/pfcp.py +461 -0
- tshark_mcp_server-0.1.0/src/tshark_mcp/progress.py +99 -0
- tshark_mcp_server-0.1.0/src/tshark_mcp/quality.py +469 -0
- tshark_mcp_server-0.1.0/src/tshark_mcp/rates.py +513 -0
- tshark_mcp_server-0.1.0/src/tshark_mcp/redact.py +500 -0
- tshark_mcp_server-0.1.0/src/tshark_mcp/roaming.py +394 -0
- tshark_mcp_server-0.1.0/src/tshark_mcp/rtp.py +480 -0
- tshark_mcp_server-0.1.0/src/tshark_mcp/runner.py +502 -0
- tshark_mcp_server-0.1.0/src/tshark_mcp/schemas.py +181 -0
- tshark_mcp_server-0.1.0/src/tshark_mcp/sdp.py +224 -0
- tshark_mcp_server-0.1.0/src/tshark_mcp/search.py +140 -0
- tshark_mcp_server-0.1.0/src/tshark_mcp/server.py +3913 -0
- tshark_mcp_server-0.1.0/src/tshark_mcp/sigtran.py +402 -0
- tshark_mcp_server-0.1.0/src/tshark_mcp/sip.py +545 -0
- tshark_mcp_server-0.1.0/src/tshark_mcp/sms.py +447 -0
- tshark_mcp_server-0.1.0/src/tshark_mcp/tcpconn.py +430 -0
- tshark_mcp_server-0.1.0/src/tshark_mcp/tcpflow.py +302 -0
- tshark_mcp_server-0.1.0/src/tshark_mcp/tcphealth.py +452 -0
- tshark_mcp_server-0.1.0/src/tshark_mcp/timing.py +679 -0
- tshark_mcp_server-0.1.0/src/tshark_mcp/tls.py +586 -0
- tshark_mcp_server-0.1.0/src/tshark_mcp/tunnels.py +370 -0
- tshark_mcp_server-0.1.0/src/tshark_mcp/untrusted.py +112 -0
- tshark_mcp_server-0.1.0/src/tshark_mcp_server.egg-info/PKG-INFO +254 -0
- tshark_mcp_server-0.1.0/src/tshark_mcp_server.egg-info/SOURCES.txt +105 -0
- tshark_mcp_server-0.1.0/src/tshark_mcp_server.egg-info/dependency_links.txt +1 -0
- tshark_mcp_server-0.1.0/src/tshark_mcp_server.egg-info/entry_points.txt +2 -0
- tshark_mcp_server-0.1.0/src/tshark_mcp_server.egg-info/requires.txt +7 -0
- tshark_mcp_server-0.1.0/src/tshark_mcp_server.egg-info/top_level.txt +1 -0
- tshark_mcp_server-0.1.0/tests/test_call_kpis.py +118 -0
- tshark_mcp_server-0.1.0/tests/test_cancel_progress.py +178 -0
- tshark_mcp_server-0.1.0/tests/test_capture_metadata.py +137 -0
- tshark_mcp_server-0.1.0/tests/test_capture_quality.py +613 -0
- tshark_mcp_server-0.1.0/tests/test_cause_lookup.py +172 -0
- tshark_mcp_server-0.1.0/tests/test_compare.py +156 -0
- tshark_mcp_server-0.1.0/tests/test_diameter_avps.py +242 -0
- tshark_mcp_server-0.1.0/tests/test_dns.py +180 -0
- tshark_mcp_server-0.1.0/tests/test_dtmf.py +69 -0
- tshark_mcp_server-0.1.0/tests/test_error_fix.py +149 -0
- tshark_mcp_server-0.1.0/tests/test_examples.py +103 -0
- tshark_mcp_server-0.1.0/tests/test_expert_info.py +139 -0
- tshark_mcp_server-0.1.0/tests/test_find_fields.py +113 -0
- tshark_mcp_server-0.1.0/tests/test_gtp_ies.py +156 -0
- tshark_mcp_server-0.1.0/tests/test_gtpv1.py +137 -0
- tshark_mcp_server-0.1.0/tests/test_hardening.py +282 -0
- tshark_mcp_server-0.1.0/tests/test_http_transactions.py +199 -0
- tshark_mcp_server-0.1.0/tests/test_icmp_errors.py +146 -0
- tshark_mcp_server-0.1.0/tests/test_ims_registration.py +184 -0
- tshark_mcp_server-0.1.0/tests/test_limits.py +201 -0
- tshark_mcp_server-0.1.0/tests/test_mcp_client.py +269 -0
- tshark_mcp_server-0.1.0/tests/test_media_path_check.py +122 -0
- tshark_mcp_server-0.1.0/tests/test_message_rates.py +147 -0
- tshark_mcp_server-0.1.0/tests/test_output_contract.py +215 -0
- tshark_mcp_server-0.1.0/tests/test_packet_detail.py +120 -0
- tshark_mcp_server-0.1.0/tests/test_pairing.py +52 -0
- tshark_mcp_server-0.1.0/tests/test_pfcp.py +241 -0
- tshark_mcp_server-0.1.0/tests/test_protocol_tools.py +459 -0
- tshark_mcp_server-0.1.0/tests/test_proxy_legs.py +358 -0
- tshark_mcp_server-0.1.0/tests/test_rates_gtpv1_map.py +212 -0
- tshark_mcp_server-0.1.0/tests/test_redaction.py +404 -0
- tshark_mcp_server-0.1.0/tests/test_redaction_prescan.py +213 -0
- tshark_mcp_server-0.1.0/tests/test_regressions.py +641 -0
- tshark_mcp_server-0.1.0/tests/test_response_times.py +231 -0
- tshark_mcp_server-0.1.0/tests/test_response_times_http2.py +179 -0
- tshark_mcp_server-0.1.0/tests/test_roaming_attach_check.py +251 -0
- tshark_mcp_server-0.1.0/tests/test_rtp.py +248 -0
- tshark_mcp_server-0.1.0/tests/test_runner.py +172 -0
- tshark_mcp_server-0.1.0/tests/test_sip_call_detail.py +72 -0
- tshark_mcp_server-0.1.0/tests/test_sms_over_ims.py +119 -0
- tshark_mcp_server-0.1.0/tests/test_tcp_connections.py +195 -0
- tshark_mcp_server-0.1.0/tests/test_tcp_health.py +454 -0
- tshark_mcp_server-0.1.0/tests/test_tls.py +386 -0
- tshark_mcp_server-0.1.0/tests/test_tool_list.py +173 -0
- tshark_mcp_server-0.1.0/tests/test_tools.py +308 -0
- tshark_mcp_server-0.1.0/tests/test_trace.py +91 -0
- tshark_mcp_server-0.1.0/tests/test_tunnel_health.py +251 -0
- tshark_mcp_server-0.1.0/tests/test_unanswered_capture_end.py +195 -0
- tshark_mcp_server-0.1.0/tests/test_untrusted_text.py +132 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Karl Hulse
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: tshark-mcp-server
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Read-only MCP server for analysing packet captures with tshark: Diameter, GTP, SIP, SIGTRAN, with hard safety limits
|
|
5
|
+
Author: Karl Hulse
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/Kx96xx/tshark-mcp
|
|
8
|
+
Project-URL: Issues, https://github.com/Kx96xx/tshark-mcp/issues
|
|
9
|
+
Keywords: mcp,tshark,wireshark,pcap,diameter,gtp,sip,sigtran,telecom,llm-agents
|
|
10
|
+
Classifier: Development Status :: 3 - Alpha
|
|
11
|
+
Classifier: Intended Audience :: Telecommunications Industry
|
|
12
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
18
|
+
Classifier: Topic :: System :: Networking :: Monitoring
|
|
19
|
+
Requires-Python: >=3.10
|
|
20
|
+
Description-Content-Type: text/markdown
|
|
21
|
+
License-File: LICENSE
|
|
22
|
+
Requires-Dist: mcp>=2.1
|
|
23
|
+
Requires-Dist: pydantic<3,>=2.12
|
|
24
|
+
Requires-Dist: typing-extensions>=4.6
|
|
25
|
+
Provides-Extra: dev
|
|
26
|
+
Requires-Dist: pytest>=8; extra == "dev"
|
|
27
|
+
Requires-Dist: scapy>=2.5; extra == "dev"
|
|
28
|
+
Dynamic: license-file
|
|
29
|
+
|
|
30
|
+
# tshark-mcp
|
|
31
|
+
|
|
32
|
+
[](https://github.com/Kx96xx/tshark-mcp/actions/workflows/ci.yml)
|
|
33
|
+

|
|
34
|
+

|
|
35
|
+
|
|
36
|
+
**A read-only MCP server that lets an AI assistant analyse packet captures with tshark.**
|
|
37
|
+
Point it at a directory of pcap files, connect Claude or any other MCP client, and ask
|
|
38
|
+
questions about the calls, sessions and subscribers in them.
|
|
39
|
+
|
|
40
|
+
## Why
|
|
41
|
+
|
|
42
|
+
- **Telecom captures need correlation, not grep.** A failed attach, a dropped bearer or
|
|
43
|
+
one-way audio spans Diameter, GTP, SIP, RTP and MAP. The tools pair requests with answers,
|
|
44
|
+
name the result codes (Diameter 5420, GTP cause 78, Q.850 cause 16) and say what never got a
|
|
45
|
+
reply.
|
|
46
|
+
- **An agent with a shell and tshark is a liability.** This server can only run
|
|
47
|
+
`tshark -r <file>` on files inside directories you allow, under hard caps on size,
|
|
48
|
+
packets, output and time. It cannot capture, write, export, resolve names or load code.
|
|
49
|
+
- **It tells you when the capture itself cannot be trusted:** duplicate packets from a SPAN
|
|
50
|
+
session, bytes the capture missed, cut-short frames, checksum offload, clocks that run
|
|
51
|
+
backwards.
|
|
52
|
+
|
|
53
|
+
## Install and connect
|
|
54
|
+
|
|
55
|
+
1. Install tshark (it comes with Wireshark): `brew install --cask wireshark`,
|
|
56
|
+
`apt-get install tshark` or `dnf install wireshark-cli`.
|
|
57
|
+
2. Install the server (Python 3.10 or newer):
|
|
58
|
+
|
|
59
|
+
```sh
|
|
60
|
+
uv tool install git+https://github.com/Kx96xx/tshark-mcp
|
|
61
|
+
# or: pipx install git+https://github.com/Kx96xx/tshark-mcp
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Requirements: Python 3.10+, tshark 4.0 or newer (tested with 4.2 and 4.6); the `mcp` (2.1+) and
|
|
65
|
+
`pydantic` (2.12+) packages are installed automatically. The package is published as
|
|
66
|
+
`tshark-mcp-server` (the PyPI name `tshark-mcp` belongs to an unrelated project); the command
|
|
67
|
+
stays `tshark-mcp`. From a clone: `uv tool install .` or `pipx install .`.
|
|
68
|
+
|
|
69
|
+
3. Connect a client, naming the directory that holds your captures.
|
|
70
|
+
|
|
71
|
+
Claude Code:
|
|
72
|
+
|
|
73
|
+
```sh
|
|
74
|
+
claude mcp add tshark-mcp -- tshark-mcp --allow-dir /path/to/pcaps
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Claude Desktop (`claude_desktop_config.json`), Cursor, or any client that starts stdio servers:
|
|
78
|
+
|
|
79
|
+
```json
|
|
80
|
+
{
|
|
81
|
+
"mcpServers": {
|
|
82
|
+
"tshark-mcp": {
|
|
83
|
+
"command": "tshark-mcp",
|
|
84
|
+
"args": ["--allow-dir", "/path/to/pcaps"]
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
To run it by hand: `tshark-mcp --allow-dir /path/to/pcaps [--allow-dir /more] [--log-file ~/tshark-mcp.jsonl]`.
|
|
91
|
+
`TSHARK_MCP_ALLOW_DIRS` (colon-separated) works instead of the flag. Three small synthetic
|
|
92
|
+
captures to start with are in [examples/](examples/).
|
|
93
|
+
|
|
94
|
+
## What you can ask it
|
|
95
|
+
|
|
96
|
+
- "Which capture in this directory mentions IMSI 001010123456789, and when?"
|
|
97
|
+
- "Why did this subscriber's attach fail?" (Diameter result codes and the AVPs behind them)
|
|
98
|
+
- "Was the GTP session created? What cause came back, and which PAA?"
|
|
99
|
+
- "What is our ASR to this destination, and did post-dial delay go up?"
|
|
100
|
+
- "Why is there one-way audio on this call?"
|
|
101
|
+
- "What was the call quality: loss, jitter, MOS?"
|
|
102
|
+
- "Why can't this UE register on IMS?" and "Where did the SMS go?"
|
|
103
|
+
- "Is this a purge storm? How often is the MME looping?"
|
|
104
|
+
- "Where does the failing capture first differ from the working one?"
|
|
105
|
+
- "Which DNS resolver is failing, and for which names?"
|
|
106
|
+
- "Is the upload slow because of the network, the server or the receiver?"
|
|
107
|
+
- "Can I trust the loss numbers in this file at all?"
|
|
108
|
+
- "What does Experimental-Result 5420 mean, and which spec section do I quote?"
|
|
109
|
+
|
|
110
|
+
One real answer, trimmed. The capture is
|
|
111
|
+
[`examples/sip_one_way_audio.pcap`](examples/sip_one_way_audio.pcap): a SIP call where the
|
|
112
|
+
called side never sends media. The agent finds the Call-ID with `sip_dialogs`, then calls:
|
|
113
|
+
|
|
114
|
+
```
|
|
115
|
+
media_path_check(path="/captures/sip_one_way_audio.pcap", call_id="one-way-audio-1@ue.example")
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
```json
|
|
119
|
+
{
|
|
120
|
+
"offer": {"message": "INVITE", "media_endpoint": "198.51.100.9:40010", "codecs": ["PCMA/8000", "telephone-event/8000"]},
|
|
121
|
+
"answer": {"message": "200 INVITE", "media_endpoint": "192.0.2.70:30010", "codecs": ["PCMA/8000", "telephone-event/8000"]},
|
|
122
|
+
"directions": [
|
|
123
|
+
{"direction": "offerer to answerer", "expected": "198.51.100.9:40010 > 192.0.2.70:30010",
|
|
124
|
+
"streams": [{"ssrc": "0x6a6a6a6a", "packets": 250, "codec": "PCMA/8000", "first_frame": 6}], "status": "as negotiated"},
|
|
125
|
+
{"direction": "answerer to offerer", "expected": "192.0.2.70:30010 > 198.51.100.9:40010", "streams": [], "status": "missing"}
|
|
126
|
+
],
|
|
127
|
+
"issues": [{"kind": "one_way_media", "detail": "no RTP seen answerer to offerer (192.0.2.70:30010 > 198.51.100.9:40010): one-way audio as captured"}],
|
|
128
|
+
"verdict": "no RTP seen answerer to offerer (192.0.2.70:30010 > 198.51.100.9:40010): one-way audio as captured"
|
|
129
|
+
}
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
The caller's RTP arrived exactly where the SDP answer asked for it and nothing came back,
|
|
133
|
+
so the fault is behind the media gateway, not the caller's NAT or codec. Every row carries
|
|
134
|
+
frame numbers (`first_frame: 6`), so `packet_detail` can show the packet behind any claim.
|
|
135
|
+
More worked examples, one per tool, are in [docs/TOOLS.md](docs/TOOLS.md#examples); a full
|
|
136
|
+
investigation of a failed attach is in [docs/EXAMPLE.md](docs/EXAMPLE.md).
|
|
137
|
+
|
|
138
|
+
## Safety model
|
|
139
|
+
|
|
140
|
+
- **Read-only by construction.** The only program started is tshark, from an argv list with
|
|
141
|
+
`-n` and `-r`, never a shell. Capture, write, export and extension flags are refused before
|
|
142
|
+
a process exists. tshark gets an empty profile, so no personal Lua script, plugin or extcap
|
|
143
|
+
loads, and at most two tshark processes run at once.
|
|
144
|
+
- **Allowlist and caps.** Every path must resolve inside an `--allow-dir`. File size (500 MB),
|
|
145
|
+
packets dissected per call (500), frames read per run (200 000), JSON per call (30 KB) and
|
|
146
|
+
run time (60 s) are capped; the numbers are configurable, none can be switched off.
|
|
147
|
+
- **Packet text is data, not instructions.** Strings copied from packets are cleaned, capped
|
|
148
|
+
and named in `untrusted_text_fields`. Optional redaction (`--redact ids`) replaces IMSIs,
|
|
149
|
+
MSISDNs, IMEIs, SIP users and UE addresses with stable tokens before anything leaves the
|
|
150
|
+
server.
|
|
151
|
+
|
|
152
|
+
Transport is stdio only; `--transport streamable-http` is for local experiments and has no
|
|
153
|
+
authentication. Details, the full caps table and the redaction rules: [docs/SAFETY.md](docs/SAFETY.md).
|
|
154
|
+
How to report a vulnerability: [SECURITY.md](SECURITY.md).
|
|
155
|
+
|
|
156
|
+
## Tools
|
|
157
|
+
|
|
158
|
+
Forty-one tools. Every one returns JSON with a typed `outputSchema`, pages with `limit`
|
|
159
|
+
and `offset`, and returns failures as `{"error": {...}}` with `isError: true` and a `fix` hint. The tool list is
|
|
160
|
+
compact by default (102 KB instead of 263 KB at 41 tools); each tool's full schema is the resource `schema://tools/<name>`, and
|
|
161
|
+
`--schema-profile full` publishes them all. Full arguments, fields and examples: [docs/TOOLS.md](docs/TOOLS.md).
|
|
162
|
+
|
|
163
|
+
| Tool | One line |
|
|
164
|
+
| --- | --- |
|
|
165
|
+
| `find_identifier` | Which captures in a directory mention an IMSI, MSISDN, IP, Call-ID or Session-Id |
|
|
166
|
+
| `pcap_info` | Packet count, duration, link type, protocol hierarchy, active limits |
|
|
167
|
+
| `capture_metadata` | pcapng writer, interfaces, snaplen, capture filter and the drop counters tshark cannot show |
|
|
168
|
+
| `capture_quality` | Can loss and timing conclusions be drawn from this file? Verdict with reasons |
|
|
169
|
+
| `conversations` | Top talkers at IP, TCP, UDP, SCTP or Ethernet level |
|
|
170
|
+
| `filter_packets` | Rows of fields for any display filter (the escape hatch) |
|
|
171
|
+
| `packet_detail` | One frame's full dissection, as in Wireshark's details pane |
|
|
172
|
+
| `expert_info` | tshark's expert warnings, errors and malformed packets, with frames |
|
|
173
|
+
| `find_fields` | Search tshark's field names without reading a capture |
|
|
174
|
+
| `check_filter` | Compile a display filter and name its fields without reading a capture |
|
|
175
|
+
| `cause_lookup` | What a Diameter, GTP, PFCP, NAS, S1AP/NGAP, SIP, Q.850, MAP, SCCP, SGsAP or SMS cause means, with the 3GPP/ITU reference |
|
|
176
|
+
| `diameter_transactions` | Requests paired with answers, result codes named, RTT, unanswered |
|
|
177
|
+
| `diameter_sessions` | The same grouped by Session-Id, with per-session counts |
|
|
178
|
+
| `diameter_avps` | The AVPs behind each transaction: flags with bit names, Subscription-Data digest, full tree |
|
|
179
|
+
| `gtp_sessions` | GTPv2-C requests paired with responses across NATs and proxy chains, cause names, PAA |
|
|
180
|
+
| `gtp_ies` | The IEs behind each GTPv2-C transaction: APN, PDN type, QoS, ULI, PCO names, offending IE, likely causes of a reject |
|
|
181
|
+
| `gtpv1_sessions` | GTPv1-C (Gn/Gp) PDP contexts paired, with cause names |
|
|
182
|
+
| `gtp_user_plane` | GTP-U packets, bytes and inner addresses per tunnel |
|
|
183
|
+
| `gtp_tunnel_health` | Inner TCP loss, reordering, MSS/MTU and ICMP per GTP-U tunnel |
|
|
184
|
+
| `pfcp_sessions` | PFCP (N4/Sx) sessions, rejections, reports, usage and peer restarts |
|
|
185
|
+
| `message_rates` | Messages per interval, bursts, storms and subscriber loops with the nodes involved |
|
|
186
|
+
| `dns_transactions` | Queries paired with responses, unanswered ones included, per-server latency, failing names |
|
|
187
|
+
| `tls_handshakes` | Client offer versus server choice, alerts, certificate, outcome per connection |
|
|
188
|
+
| `http_transactions` | HTTP/1.x pairs and HTTP/2 / gRPC streams: status, size, timing; per-endpoint 5xx share and p99 |
|
|
189
|
+
| `response_times` | Network, server and receiver time per TCP flow (HTTP/1, HTTP/2, gRPC) |
|
|
190
|
+
| `tcp_health` | Per-stream loss by location, resets with their likely source, windows, MTU black holes |
|
|
191
|
+
| `tcp_connections` | Every connection attempt: connected, refused, filtered or ICMP-prohibited, who closed it; per destination success rate |
|
|
192
|
+
| `icmp_errors` | Every ICMP/ICMPv6 error by sender, kind and quoted flow: fragmentation needed, rejects, routing loops, lost pings |
|
|
193
|
+
| `sip_dialogs` | Messages grouped by Call-ID with outcome after any challenge |
|
|
194
|
+
| `sip_call_detail` | One call: timings, SDP, Reason headers, who hung up |
|
|
195
|
+
| `call_kpis` | ASR, NER, SER, PDD and ACD per peer, trunk, number prefix, user agent or interval |
|
|
196
|
+
| `media_path_check` | SDP-negotiated media against the RTP seen: one-way audio, NAT, 488 |
|
|
197
|
+
| `rtp_streams` | Per-stream loss, jitter, RTCP and an E-model MOS estimate, linked to the call |
|
|
198
|
+
| `dtmf_events` | RFC 4733 and SIP INFO digits in order, checked against the SDP |
|
|
199
|
+
| `ims_registration` | REGISTER flows per UE with a verdict: registered, expired, deregistered, challenge-loop, failed |
|
|
200
|
+
| `sms_over_ims` | Each SMS over IMS with its RP answer and a verdict |
|
|
201
|
+
| `sctp_associations` | Handshake, heartbeats, retransmissions, aborts per association |
|
|
202
|
+
| `sigtran_messages` | M3UA, SCCP, TCAP and MAP with operation names, IMSI and MSISDN |
|
|
203
|
+
| `trace_subscriber` | One timeline for a subscriber across Diameter, GTP, PFCP, MAP and SIP |
|
|
204
|
+
| `roaming_attach_check` | Why a roamer cannot attach or get a bearer: each attempt's steps, the ULA subscription held against the Create Session, checks and a verdict |
|
|
205
|
+
| `compare_captures` | A working and a failing capture side by side, with the first divergence |
|
|
206
|
+
|
|
207
|
+
## Redaction
|
|
208
|
+
|
|
209
|
+
Telecom captures carry IMSIs, MSISDNs, IMEIs, SIP users and UE addresses. Start the server
|
|
210
|
+
with `--redact ids` (or `TSHARK_MCP_REDACT=ids`), or pass `redact="ids"` on a call, and those
|
|
211
|
+
values come back as stable keyed tokens such as `imsi:3f2a9c1b0d` that still join across
|
|
212
|
+
tools and work as arguments in later calls. `ids+ips` hides every IP address too. A call can
|
|
213
|
+
raise the server's level, never lower it. What is found, what is not, and how the pre-scan
|
|
214
|
+
works: [docs/SAFETY.md](docs/SAFETY.md#redaction).
|
|
215
|
+
|
|
216
|
+
## Limitations
|
|
217
|
+
|
|
218
|
+
* No live capture, by design. The server reads files that already exist and nothing else.
|
|
219
|
+
* No decryption: IPsec, TLS and ciphered NAS are reported as tshark sees them. `tls_handshakes`
|
|
220
|
+
reads only the clear part of the handshake; after a TLS 1.3 ServerHello its outcome is inferred
|
|
221
|
+
from the record pattern and says so.
|
|
222
|
+
* Large captures are slow. tshark reads the whole file for every call; a 500 MB capture
|
|
223
|
+
with a selective filter takes tens of seconds, so prefer pre-sliced captures.
|
|
224
|
+
* Pairing follows what is in the file. A request whose answer was captured on another
|
|
225
|
+
interface is `unanswered`; one the capture stopped too soon to answer is also
|
|
226
|
+
`near_capture_end`. How GTP responses are matched across NATs and proxies is in
|
|
227
|
+
[docs/TOOLS.md](docs/TOOLS.md).
|
|
228
|
+
* MAP decoding relies on tshark's dissectors; a segmented XUDT whose other segments are
|
|
229
|
+
missing is reported as segmented, not guessed.
|
|
230
|
+
|
|
231
|
+
## Roadmap
|
|
232
|
+
|
|
233
|
+
1. 5G core: HTTP/2 SBI (Nnrf, Nausf, Nsmf).
|
|
234
|
+
2. Streaming per-call progress so a long tshark run can be cancelled from the client.
|
|
235
|
+
3. Optional S1AP/NGAP and NAS summaries with EMM/ESM cause names.
|
|
236
|
+
|
|
237
|
+
## Development
|
|
238
|
+
|
|
239
|
+
```sh
|
|
240
|
+
python3 -m venv .venv && . .venv/bin/activate
|
|
241
|
+
pip install -e '.[dev]'
|
|
242
|
+
pytest
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
The suite is 835 tests and runs in about a minute; CI runs it on Python 3.10 to 3.13 with
|
|
246
|
+
the tshark that Ubuntu 24.04 ships (4.2). Every capture the tests use is synthetic, built by
|
|
247
|
+
`tests/pcapgen.py` at run time with documentation addresses and test identities, and the three
|
|
248
|
+
captures under `examples/` come from the same generator, so nothing in the repository contains
|
|
249
|
+
traffic from a real network (the list of scenarios is in [docs/TOOLS.md](docs/TOOLS.md#how-the-tools-are-tested)).
|
|
250
|
+
Conventions for contributors and for agents: [CONTRIBUTING.md](CONTRIBUTING.md), [AGENTS.md](AGENTS.md).
|
|
251
|
+
|
|
252
|
+
## Licence
|
|
253
|
+
|
|
254
|
+
MIT, Copyright (c) 2026 Karl Hulse.
|
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
# tshark-mcp
|
|
2
|
+
|
|
3
|
+
[](https://github.com/Kx96xx/tshark-mcp/actions/workflows/ci.yml)
|
|
4
|
+

|
|
5
|
+

|
|
6
|
+
|
|
7
|
+
**A read-only MCP server that lets an AI assistant analyse packet captures with tshark.**
|
|
8
|
+
Point it at a directory of pcap files, connect Claude or any other MCP client, and ask
|
|
9
|
+
questions about the calls, sessions and subscribers in them.
|
|
10
|
+
|
|
11
|
+
## Why
|
|
12
|
+
|
|
13
|
+
- **Telecom captures need correlation, not grep.** A failed attach, a dropped bearer or
|
|
14
|
+
one-way audio spans Diameter, GTP, SIP, RTP and MAP. The tools pair requests with answers,
|
|
15
|
+
name the result codes (Diameter 5420, GTP cause 78, Q.850 cause 16) and say what never got a
|
|
16
|
+
reply.
|
|
17
|
+
- **An agent with a shell and tshark is a liability.** This server can only run
|
|
18
|
+
`tshark -r <file>` on files inside directories you allow, under hard caps on size,
|
|
19
|
+
packets, output and time. It cannot capture, write, export, resolve names or load code.
|
|
20
|
+
- **It tells you when the capture itself cannot be trusted:** duplicate packets from a SPAN
|
|
21
|
+
session, bytes the capture missed, cut-short frames, checksum offload, clocks that run
|
|
22
|
+
backwards.
|
|
23
|
+
|
|
24
|
+
## Install and connect
|
|
25
|
+
|
|
26
|
+
1. Install tshark (it comes with Wireshark): `brew install --cask wireshark`,
|
|
27
|
+
`apt-get install tshark` or `dnf install wireshark-cli`.
|
|
28
|
+
2. Install the server (Python 3.10 or newer):
|
|
29
|
+
|
|
30
|
+
```sh
|
|
31
|
+
uv tool install git+https://github.com/Kx96xx/tshark-mcp
|
|
32
|
+
# or: pipx install git+https://github.com/Kx96xx/tshark-mcp
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Requirements: Python 3.10+, tshark 4.0 or newer (tested with 4.2 and 4.6); the `mcp` (2.1+) and
|
|
36
|
+
`pydantic` (2.12+) packages are installed automatically. The package is published as
|
|
37
|
+
`tshark-mcp-server` (the PyPI name `tshark-mcp` belongs to an unrelated project); the command
|
|
38
|
+
stays `tshark-mcp`. From a clone: `uv tool install .` or `pipx install .`.
|
|
39
|
+
|
|
40
|
+
3. Connect a client, naming the directory that holds your captures.
|
|
41
|
+
|
|
42
|
+
Claude Code:
|
|
43
|
+
|
|
44
|
+
```sh
|
|
45
|
+
claude mcp add tshark-mcp -- tshark-mcp --allow-dir /path/to/pcaps
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Claude Desktop (`claude_desktop_config.json`), Cursor, or any client that starts stdio servers:
|
|
49
|
+
|
|
50
|
+
```json
|
|
51
|
+
{
|
|
52
|
+
"mcpServers": {
|
|
53
|
+
"tshark-mcp": {
|
|
54
|
+
"command": "tshark-mcp",
|
|
55
|
+
"args": ["--allow-dir", "/path/to/pcaps"]
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
To run it by hand: `tshark-mcp --allow-dir /path/to/pcaps [--allow-dir /more] [--log-file ~/tshark-mcp.jsonl]`.
|
|
62
|
+
`TSHARK_MCP_ALLOW_DIRS` (colon-separated) works instead of the flag. Three small synthetic
|
|
63
|
+
captures to start with are in [examples/](examples/).
|
|
64
|
+
|
|
65
|
+
## What you can ask it
|
|
66
|
+
|
|
67
|
+
- "Which capture in this directory mentions IMSI 001010123456789, and when?"
|
|
68
|
+
- "Why did this subscriber's attach fail?" (Diameter result codes and the AVPs behind them)
|
|
69
|
+
- "Was the GTP session created? What cause came back, and which PAA?"
|
|
70
|
+
- "What is our ASR to this destination, and did post-dial delay go up?"
|
|
71
|
+
- "Why is there one-way audio on this call?"
|
|
72
|
+
- "What was the call quality: loss, jitter, MOS?"
|
|
73
|
+
- "Why can't this UE register on IMS?" and "Where did the SMS go?"
|
|
74
|
+
- "Is this a purge storm? How often is the MME looping?"
|
|
75
|
+
- "Where does the failing capture first differ from the working one?"
|
|
76
|
+
- "Which DNS resolver is failing, and for which names?"
|
|
77
|
+
- "Is the upload slow because of the network, the server or the receiver?"
|
|
78
|
+
- "Can I trust the loss numbers in this file at all?"
|
|
79
|
+
- "What does Experimental-Result 5420 mean, and which spec section do I quote?"
|
|
80
|
+
|
|
81
|
+
One real answer, trimmed. The capture is
|
|
82
|
+
[`examples/sip_one_way_audio.pcap`](examples/sip_one_way_audio.pcap): a SIP call where the
|
|
83
|
+
called side never sends media. The agent finds the Call-ID with `sip_dialogs`, then calls:
|
|
84
|
+
|
|
85
|
+
```
|
|
86
|
+
media_path_check(path="/captures/sip_one_way_audio.pcap", call_id="one-way-audio-1@ue.example")
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
```json
|
|
90
|
+
{
|
|
91
|
+
"offer": {"message": "INVITE", "media_endpoint": "198.51.100.9:40010", "codecs": ["PCMA/8000", "telephone-event/8000"]},
|
|
92
|
+
"answer": {"message": "200 INVITE", "media_endpoint": "192.0.2.70:30010", "codecs": ["PCMA/8000", "telephone-event/8000"]},
|
|
93
|
+
"directions": [
|
|
94
|
+
{"direction": "offerer to answerer", "expected": "198.51.100.9:40010 > 192.0.2.70:30010",
|
|
95
|
+
"streams": [{"ssrc": "0x6a6a6a6a", "packets": 250, "codec": "PCMA/8000", "first_frame": 6}], "status": "as negotiated"},
|
|
96
|
+
{"direction": "answerer to offerer", "expected": "192.0.2.70:30010 > 198.51.100.9:40010", "streams": [], "status": "missing"}
|
|
97
|
+
],
|
|
98
|
+
"issues": [{"kind": "one_way_media", "detail": "no RTP seen answerer to offerer (192.0.2.70:30010 > 198.51.100.9:40010): one-way audio as captured"}],
|
|
99
|
+
"verdict": "no RTP seen answerer to offerer (192.0.2.70:30010 > 198.51.100.9:40010): one-way audio as captured"
|
|
100
|
+
}
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
The caller's RTP arrived exactly where the SDP answer asked for it and nothing came back,
|
|
104
|
+
so the fault is behind the media gateway, not the caller's NAT or codec. Every row carries
|
|
105
|
+
frame numbers (`first_frame: 6`), so `packet_detail` can show the packet behind any claim.
|
|
106
|
+
More worked examples, one per tool, are in [docs/TOOLS.md](docs/TOOLS.md#examples); a full
|
|
107
|
+
investigation of a failed attach is in [docs/EXAMPLE.md](docs/EXAMPLE.md).
|
|
108
|
+
|
|
109
|
+
## Safety model
|
|
110
|
+
|
|
111
|
+
- **Read-only by construction.** The only program started is tshark, from an argv list with
|
|
112
|
+
`-n` and `-r`, never a shell. Capture, write, export and extension flags are refused before
|
|
113
|
+
a process exists. tshark gets an empty profile, so no personal Lua script, plugin or extcap
|
|
114
|
+
loads, and at most two tshark processes run at once.
|
|
115
|
+
- **Allowlist and caps.** Every path must resolve inside an `--allow-dir`. File size (500 MB),
|
|
116
|
+
packets dissected per call (500), frames read per run (200 000), JSON per call (30 KB) and
|
|
117
|
+
run time (60 s) are capped; the numbers are configurable, none can be switched off.
|
|
118
|
+
- **Packet text is data, not instructions.** Strings copied from packets are cleaned, capped
|
|
119
|
+
and named in `untrusted_text_fields`. Optional redaction (`--redact ids`) replaces IMSIs,
|
|
120
|
+
MSISDNs, IMEIs, SIP users and UE addresses with stable tokens before anything leaves the
|
|
121
|
+
server.
|
|
122
|
+
|
|
123
|
+
Transport is stdio only; `--transport streamable-http` is for local experiments and has no
|
|
124
|
+
authentication. Details, the full caps table and the redaction rules: [docs/SAFETY.md](docs/SAFETY.md).
|
|
125
|
+
How to report a vulnerability: [SECURITY.md](SECURITY.md).
|
|
126
|
+
|
|
127
|
+
## Tools
|
|
128
|
+
|
|
129
|
+
Forty-one tools. Every one returns JSON with a typed `outputSchema`, pages with `limit`
|
|
130
|
+
and `offset`, and returns failures as `{"error": {...}}` with `isError: true` and a `fix` hint. The tool list is
|
|
131
|
+
compact by default (102 KB instead of 263 KB at 41 tools); each tool's full schema is the resource `schema://tools/<name>`, and
|
|
132
|
+
`--schema-profile full` publishes them all. Full arguments, fields and examples: [docs/TOOLS.md](docs/TOOLS.md).
|
|
133
|
+
|
|
134
|
+
| Tool | One line |
|
|
135
|
+
| --- | --- |
|
|
136
|
+
| `find_identifier` | Which captures in a directory mention an IMSI, MSISDN, IP, Call-ID or Session-Id |
|
|
137
|
+
| `pcap_info` | Packet count, duration, link type, protocol hierarchy, active limits |
|
|
138
|
+
| `capture_metadata` | pcapng writer, interfaces, snaplen, capture filter and the drop counters tshark cannot show |
|
|
139
|
+
| `capture_quality` | Can loss and timing conclusions be drawn from this file? Verdict with reasons |
|
|
140
|
+
| `conversations` | Top talkers at IP, TCP, UDP, SCTP or Ethernet level |
|
|
141
|
+
| `filter_packets` | Rows of fields for any display filter (the escape hatch) |
|
|
142
|
+
| `packet_detail` | One frame's full dissection, as in Wireshark's details pane |
|
|
143
|
+
| `expert_info` | tshark's expert warnings, errors and malformed packets, with frames |
|
|
144
|
+
| `find_fields` | Search tshark's field names without reading a capture |
|
|
145
|
+
| `check_filter` | Compile a display filter and name its fields without reading a capture |
|
|
146
|
+
| `cause_lookup` | What a Diameter, GTP, PFCP, NAS, S1AP/NGAP, SIP, Q.850, MAP, SCCP, SGsAP or SMS cause means, with the 3GPP/ITU reference |
|
|
147
|
+
| `diameter_transactions` | Requests paired with answers, result codes named, RTT, unanswered |
|
|
148
|
+
| `diameter_sessions` | The same grouped by Session-Id, with per-session counts |
|
|
149
|
+
| `diameter_avps` | The AVPs behind each transaction: flags with bit names, Subscription-Data digest, full tree |
|
|
150
|
+
| `gtp_sessions` | GTPv2-C requests paired with responses across NATs and proxy chains, cause names, PAA |
|
|
151
|
+
| `gtp_ies` | The IEs behind each GTPv2-C transaction: APN, PDN type, QoS, ULI, PCO names, offending IE, likely causes of a reject |
|
|
152
|
+
| `gtpv1_sessions` | GTPv1-C (Gn/Gp) PDP contexts paired, with cause names |
|
|
153
|
+
| `gtp_user_plane` | GTP-U packets, bytes and inner addresses per tunnel |
|
|
154
|
+
| `gtp_tunnel_health` | Inner TCP loss, reordering, MSS/MTU and ICMP per GTP-U tunnel |
|
|
155
|
+
| `pfcp_sessions` | PFCP (N4/Sx) sessions, rejections, reports, usage and peer restarts |
|
|
156
|
+
| `message_rates` | Messages per interval, bursts, storms and subscriber loops with the nodes involved |
|
|
157
|
+
| `dns_transactions` | Queries paired with responses, unanswered ones included, per-server latency, failing names |
|
|
158
|
+
| `tls_handshakes` | Client offer versus server choice, alerts, certificate, outcome per connection |
|
|
159
|
+
| `http_transactions` | HTTP/1.x pairs and HTTP/2 / gRPC streams: status, size, timing; per-endpoint 5xx share and p99 |
|
|
160
|
+
| `response_times` | Network, server and receiver time per TCP flow (HTTP/1, HTTP/2, gRPC) |
|
|
161
|
+
| `tcp_health` | Per-stream loss by location, resets with their likely source, windows, MTU black holes |
|
|
162
|
+
| `tcp_connections` | Every connection attempt: connected, refused, filtered or ICMP-prohibited, who closed it; per destination success rate |
|
|
163
|
+
| `icmp_errors` | Every ICMP/ICMPv6 error by sender, kind and quoted flow: fragmentation needed, rejects, routing loops, lost pings |
|
|
164
|
+
| `sip_dialogs` | Messages grouped by Call-ID with outcome after any challenge |
|
|
165
|
+
| `sip_call_detail` | One call: timings, SDP, Reason headers, who hung up |
|
|
166
|
+
| `call_kpis` | ASR, NER, SER, PDD and ACD per peer, trunk, number prefix, user agent or interval |
|
|
167
|
+
| `media_path_check` | SDP-negotiated media against the RTP seen: one-way audio, NAT, 488 |
|
|
168
|
+
| `rtp_streams` | Per-stream loss, jitter, RTCP and an E-model MOS estimate, linked to the call |
|
|
169
|
+
| `dtmf_events` | RFC 4733 and SIP INFO digits in order, checked against the SDP |
|
|
170
|
+
| `ims_registration` | REGISTER flows per UE with a verdict: registered, expired, deregistered, challenge-loop, failed |
|
|
171
|
+
| `sms_over_ims` | Each SMS over IMS with its RP answer and a verdict |
|
|
172
|
+
| `sctp_associations` | Handshake, heartbeats, retransmissions, aborts per association |
|
|
173
|
+
| `sigtran_messages` | M3UA, SCCP, TCAP and MAP with operation names, IMSI and MSISDN |
|
|
174
|
+
| `trace_subscriber` | One timeline for a subscriber across Diameter, GTP, PFCP, MAP and SIP |
|
|
175
|
+
| `roaming_attach_check` | Why a roamer cannot attach or get a bearer: each attempt's steps, the ULA subscription held against the Create Session, checks and a verdict |
|
|
176
|
+
| `compare_captures` | A working and a failing capture side by side, with the first divergence |
|
|
177
|
+
|
|
178
|
+
## Redaction
|
|
179
|
+
|
|
180
|
+
Telecom captures carry IMSIs, MSISDNs, IMEIs, SIP users and UE addresses. Start the server
|
|
181
|
+
with `--redact ids` (or `TSHARK_MCP_REDACT=ids`), or pass `redact="ids"` on a call, and those
|
|
182
|
+
values come back as stable keyed tokens such as `imsi:3f2a9c1b0d` that still join across
|
|
183
|
+
tools and work as arguments in later calls. `ids+ips` hides every IP address too. A call can
|
|
184
|
+
raise the server's level, never lower it. What is found, what is not, and how the pre-scan
|
|
185
|
+
works: [docs/SAFETY.md](docs/SAFETY.md#redaction).
|
|
186
|
+
|
|
187
|
+
## Limitations
|
|
188
|
+
|
|
189
|
+
* No live capture, by design. The server reads files that already exist and nothing else.
|
|
190
|
+
* No decryption: IPsec, TLS and ciphered NAS are reported as tshark sees them. `tls_handshakes`
|
|
191
|
+
reads only the clear part of the handshake; after a TLS 1.3 ServerHello its outcome is inferred
|
|
192
|
+
from the record pattern and says so.
|
|
193
|
+
* Large captures are slow. tshark reads the whole file for every call; a 500 MB capture
|
|
194
|
+
with a selective filter takes tens of seconds, so prefer pre-sliced captures.
|
|
195
|
+
* Pairing follows what is in the file. A request whose answer was captured on another
|
|
196
|
+
interface is `unanswered`; one the capture stopped too soon to answer is also
|
|
197
|
+
`near_capture_end`. How GTP responses are matched across NATs and proxies is in
|
|
198
|
+
[docs/TOOLS.md](docs/TOOLS.md).
|
|
199
|
+
* MAP decoding relies on tshark's dissectors; a segmented XUDT whose other segments are
|
|
200
|
+
missing is reported as segmented, not guessed.
|
|
201
|
+
|
|
202
|
+
## Roadmap
|
|
203
|
+
|
|
204
|
+
1. 5G core: HTTP/2 SBI (Nnrf, Nausf, Nsmf).
|
|
205
|
+
2. Streaming per-call progress so a long tshark run can be cancelled from the client.
|
|
206
|
+
3. Optional S1AP/NGAP and NAS summaries with EMM/ESM cause names.
|
|
207
|
+
|
|
208
|
+
## Development
|
|
209
|
+
|
|
210
|
+
```sh
|
|
211
|
+
python3 -m venv .venv && . .venv/bin/activate
|
|
212
|
+
pip install -e '.[dev]'
|
|
213
|
+
pytest
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
The suite is 835 tests and runs in about a minute; CI runs it on Python 3.10 to 3.13 with
|
|
217
|
+
the tshark that Ubuntu 24.04 ships (4.2). Every capture the tests use is synthetic, built by
|
|
218
|
+
`tests/pcapgen.py` at run time with documentation addresses and test identities, and the three
|
|
219
|
+
captures under `examples/` come from the same generator, so nothing in the repository contains
|
|
220
|
+
traffic from a real network (the list of scenarios is in [docs/TOOLS.md](docs/TOOLS.md#how-the-tools-are-tested)).
|
|
221
|
+
Conventions for contributors and for agents: [CONTRIBUTING.md](CONTRIBUTING.md), [AGENTS.md](AGENTS.md).
|
|
222
|
+
|
|
223
|
+
## Licence
|
|
224
|
+
|
|
225
|
+
MIT, Copyright (c) 2026 Karl Hulse.
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=68", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
# Distribution name on PyPI. "tshark-mcp" there belongs to an unrelated project, so the
|
|
7
|
+
# package is published as tshark-mcp-server; the import name (tshark_mcp) and the command
|
|
8
|
+
# (tshark-mcp) are unchanged.
|
|
9
|
+
name = "tshark-mcp-server"
|
|
10
|
+
version = "0.1.0"
|
|
11
|
+
description = "Read-only MCP server for analysing packet captures with tshark: Diameter, GTP, SIP, SIGTRAN, with hard safety limits"
|
|
12
|
+
readme = "README.md"
|
|
13
|
+
license = { text = "MIT" }
|
|
14
|
+
authors = [{ name = "Karl Hulse" }]
|
|
15
|
+
requires-python = ">=3.10"
|
|
16
|
+
keywords = ["mcp", "tshark", "wireshark", "pcap", "diameter", "gtp", "sip", "sigtran", "telecom", "llm-agents"]
|
|
17
|
+
classifiers = [
|
|
18
|
+
"Development Status :: 3 - Alpha",
|
|
19
|
+
"Intended Audience :: Telecommunications Industry",
|
|
20
|
+
"License :: OSI Approved :: MIT License",
|
|
21
|
+
"Programming Language :: Python :: 3",
|
|
22
|
+
"Programming Language :: Python :: 3.10",
|
|
23
|
+
"Programming Language :: Python :: 3.11",
|
|
24
|
+
"Programming Language :: Python :: 3.12",
|
|
25
|
+
"Programming Language :: Python :: 3.13",
|
|
26
|
+
"Topic :: System :: Networking :: Monitoring",
|
|
27
|
+
]
|
|
28
|
+
# mcp 2.0 and older fail: 1.x has no FastMCP(version=...) and 2.0 rejects the structured
|
|
29
|
+
# output; models.py imports pydantic directly (ConfigDict on the result TypedDicts).
|
|
30
|
+
# The floor (mcp 2.1.0, pydantic 2.12.0) is tested in CI.
|
|
31
|
+
dependencies = ["mcp>=2.1", "pydantic>=2.12,<3", "typing-extensions>=4.6"]
|
|
32
|
+
|
|
33
|
+
[project.optional-dependencies]
|
|
34
|
+
dev = ["pytest>=8", "scapy>=2.5"]
|
|
35
|
+
|
|
36
|
+
[project.scripts]
|
|
37
|
+
tshark-mcp = "tshark_mcp.server:main"
|
|
38
|
+
|
|
39
|
+
[project.urls]
|
|
40
|
+
Homepage = "https://github.com/Kx96xx/tshark-mcp"
|
|
41
|
+
Issues = "https://github.com/Kx96xx/tshark-mcp/issues"
|
|
42
|
+
|
|
43
|
+
[tool.setuptools.packages.find]
|
|
44
|
+
where = ["src"]
|
|
45
|
+
|
|
46
|
+
[tool.pytest.ini_options]
|
|
47
|
+
testpaths = ["tests"]
|
|
48
|
+
addopts = "-q"
|
|
49
|
+
filterwarnings = ["ignore::DeprecationWarning:scapy.*", "ignore::DeprecationWarning:cryptography.*"]
|