mcpcap 0.2.1__py3-none-any.whl

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.
mcpcap/__init__.py ADDED
@@ -0,0 +1,38 @@
1
+ """mcpcap - A modular Python MCP Server for analyzing PCAP files.
2
+
3
+ mcpcap provides a comprehensive solution for analyzing network packet captures (PCAP files)
4
+ through the Model Context Protocol (MCP). It enables LLMs to perform network traffic analysis
5
+ with support for DNS protocol analysis and extensible architecture for additional protocols.
6
+
7
+ Key Features:
8
+ - Modular architecture for easy protocol extension
9
+ - Robust DNS packet analysis with error handling
10
+ - MCP integration for seamless LLM interaction
11
+ - Security-focused analysis prompts and indicators
12
+ - Support for both .pcap and .pcapng file formats
13
+
14
+ Example:
15
+ Start the MCP server with a directory containing PCAP files::
16
+
17
+ $ mcpcap --pcap-path /path/to/pcap/files
18
+
19
+ Then connect with an MCP client to analyze DNS traffic.
20
+ """
21
+
22
+ # Dynamic version detection
23
+ try:
24
+ # First try to import from _version.py (created by setuptools-scm in built packages)
25
+ from ._version import version as __version__
26
+ except ImportError:
27
+ try:
28
+ # Fall back to setuptools_scm for development environments
29
+ from setuptools_scm import get_version
30
+
31
+ __version__ = get_version(root="..", relative_to=__file__)
32
+ except (ImportError, LookupError):
33
+ # Final fallback for cases where setuptools_scm isn't available
34
+ __version__ = "dev-unknown"
35
+
36
+ from .cli import main
37
+
38
+ __all__ = ["main", "__version__"]
mcpcap/_version.py ADDED
@@ -0,0 +1,34 @@
1
+ # file generated by setuptools-scm
2
+ # don't change, don't track in version control
3
+
4
+ __all__ = [
5
+ "__version__",
6
+ "__version_tuple__",
7
+ "version",
8
+ "version_tuple",
9
+ "__commit_id__",
10
+ "commit_id",
11
+ ]
12
+
13
+ TYPE_CHECKING = False
14
+ if TYPE_CHECKING:
15
+ from typing import Tuple
16
+ from typing import Union
17
+
18
+ VERSION_TUPLE = Tuple[Union[int, str], ...]
19
+ COMMIT_ID = Union[str, None]
20
+ else:
21
+ VERSION_TUPLE = object
22
+ COMMIT_ID = object
23
+
24
+ version: str
25
+ __version__: str
26
+ __version_tuple__: VERSION_TUPLE
27
+ version_tuple: VERSION_TUPLE
28
+ commit_id: COMMIT_ID
29
+ __commit_id__: COMMIT_ID
30
+
31
+ __version__ = version = '0.2.1'
32
+ __version_tuple__ = version_tuple = (0, 2, 1)
33
+
34
+ __commit_id__ = commit_id = None
mcpcap/cli.py ADDED
@@ -0,0 +1,54 @@
1
+ """CLI entry point for mcpcap.
2
+
3
+ This module provides the command-line interface for mcpcap, handling argument parsing
4
+ and server initialization.
5
+ """
6
+
7
+ import argparse
8
+
9
+ from .core import Config, MCPServer
10
+
11
+
12
+ def main():
13
+ """Main function to parse arguments and start the MCP server.
14
+
15
+ Parses command-line arguments, initializes the configuration and MCP server,
16
+ and handles graceful shutdown and error conditions.
17
+
18
+ Returns:
19
+ int: Exit code (0 for success, 1 for error)
20
+
21
+ Raises:
22
+ ValueError: If the provided PCAP path is invalid
23
+ KeyboardInterrupt: If the user interrupts the server
24
+ Exception: For any unexpected errors during server operation
25
+ """
26
+ parser = argparse.ArgumentParser(description="PCAP DNS Analyzer MCP Server")
27
+ parser.add_argument(
28
+ "--pcap-path", required=True, help="Path to directory containing PCAP files"
29
+ )
30
+
31
+ args = parser.parse_args()
32
+
33
+ try:
34
+ # Initialize configuration
35
+ config = Config(args.pcap_path)
36
+
37
+ # Create and start MCP server
38
+ server = MCPServer(config)
39
+ server.run()
40
+ return 0
41
+
42
+ except ValueError as e:
43
+ print(f"Error: {e}")
44
+ return 1
45
+ except KeyboardInterrupt:
46
+ print("\\nServer stopped by user")
47
+ return 0
48
+ except Exception as e:
49
+ print(f"Unexpected error: {e}")
50
+ return 1
51
+
52
+
53
+ if __name__ == "__main__":
54
+ exit(main())
@@ -0,0 +1,6 @@
1
+ """Core components for mcpcap."""
2
+
3
+ from .config import Config
4
+ from .server import MCPServer
5
+
6
+ __all__ = ["MCPServer", "Config"]
mcpcap/core/config.py ADDED
@@ -0,0 +1,52 @@
1
+ """Configuration management for mcpcap."""
2
+
3
+ import os
4
+
5
+
6
+ class Config:
7
+ """Configuration management for mcpcap server."""
8
+
9
+ def __init__(self, pcap_path: str):
10
+ """Initialize configuration.
11
+
12
+ Args:
13
+ pcap_path: Path to directory containing PCAP files
14
+ """
15
+ self.pcap_path = pcap_path
16
+ self._validate_pcap_path()
17
+
18
+ def _validate_pcap_path(self) -> None:
19
+ """Validate that the PCAP path exists and is a directory."""
20
+ if not os.path.exists(self.pcap_path):
21
+ raise ValueError(f"PCAP directory '{self.pcap_path}' does not exist")
22
+
23
+ if not os.path.isdir(self.pcap_path):
24
+ raise ValueError(f"'{self.pcap_path}' is not a directory")
25
+
26
+ def get_pcap_file_path(self, pcap_file: str) -> str:
27
+ """Get full path to a PCAP file.
28
+
29
+ Args:
30
+ pcap_file: Filename or relative path to PCAP file
31
+
32
+ Returns:
33
+ Full path to the PCAP file
34
+ """
35
+ if os.path.isabs(pcap_file):
36
+ return pcap_file
37
+ return os.path.join(self.pcap_path, pcap_file)
38
+
39
+ def list_pcap_files(self) -> list[str]:
40
+ """List all PCAP files in the configured directory.
41
+
42
+ Returns:
43
+ List of PCAP filenames
44
+ """
45
+ try:
46
+ return [
47
+ f
48
+ for f in os.listdir(self.pcap_path)
49
+ if f.endswith((".pcap", ".pcapng"))
50
+ ]
51
+ except Exception:
52
+ return []
mcpcap/core/server.py ADDED
@@ -0,0 +1,41 @@
1
+ """MCP server setup and configuration."""
2
+
3
+ from fastmcp import FastMCP
4
+
5
+ from ..modules.dns import DNSModule
6
+ from ..resources.references import setup_resources
7
+ from .config import Config
8
+
9
+
10
+ class MCPServer:
11
+ """MCP server for PCAP analysis."""
12
+
13
+ def __init__(self, config: Config):
14
+ """Initialize MCP server.
15
+
16
+ Args:
17
+ config: Configuration instance
18
+ """
19
+ self.config = config
20
+ self.mcp = FastMCP("PCAP DNS Analyzer")
21
+
22
+ # Initialize modules
23
+ self.dns_module = DNSModule(config)
24
+
25
+ # Register tools
26
+ self._register_tools()
27
+
28
+ # Setup resources and prompts
29
+ setup_resources(self.mcp)
30
+ self.dns_module.setup_prompts(self.mcp)
31
+
32
+ def _register_tools(self) -> None:
33
+ """Register all available tools with the MCP server."""
34
+ # Register DNS module tools
35
+ self.mcp.tool(self.dns_module.list_dns_packets)
36
+ self.mcp.tool(self.dns_module.list_pcap_files)
37
+
38
+ def run(self) -> None:
39
+ """Start the MCP server."""
40
+ print(f"Starting MCP server with PCAP directory: {self.config.pcap_path}")
41
+ self.mcp.run()
@@ -0,0 +1,6 @@
1
+ """Protocol analysis modules for mcpcap."""
2
+
3
+ from .base import BaseModule
4
+ from .dns import DNSModule
5
+
6
+ __all__ = ["BaseModule", "DNSModule"]
mcpcap/modules/base.py ADDED
@@ -0,0 +1,36 @@
1
+ """Base module interface for protocol analyzers."""
2
+
3
+ from abc import ABC, abstractmethod
4
+ from typing import Any
5
+
6
+ from ..core.config import Config
7
+
8
+
9
+ class BaseModule(ABC):
10
+ """Base class for protocol analysis modules."""
11
+
12
+ def __init__(self, config: Config):
13
+ """Initialize the module.
14
+
15
+ Args:
16
+ config: Configuration instance
17
+ """
18
+ self.config = config
19
+
20
+ @abstractmethod
21
+ def analyze_packets(self, pcap_file: str) -> dict[str, Any]:
22
+ """Analyze packets in a PCAP file.
23
+
24
+ Args:
25
+ pcap_file: Path to the PCAP file
26
+
27
+ Returns:
28
+ Analysis results as a dictionary
29
+ """
30
+ pass
31
+
32
+ @property
33
+ @abstractmethod
34
+ def protocol_name(self) -> str:
35
+ """Return the name of the protocol this module analyzes."""
36
+ pass
mcpcap/modules/dns.py ADDED
@@ -0,0 +1,349 @@
1
+ """DNS analysis module."""
2
+
3
+ import os
4
+ from datetime import datetime
5
+ from typing import Any
6
+
7
+ from fastmcp import FastMCP
8
+ from scapy.all import DNS, IP, TCP, UDP, IPv6, rdpcap
9
+
10
+ from .base import BaseModule
11
+
12
+
13
+ class DNSModule(BaseModule):
14
+ """Module for analyzing DNS packets in PCAP files."""
15
+
16
+ @property
17
+ def protocol_name(self) -> str:
18
+ """Return the name of the protocol this module analyzes."""
19
+ return "DNS"
20
+
21
+ def list_dns_packets(self, pcap_file: str = "example.pcap") -> dict[str, Any]:
22
+ """
23
+ Analyze DNS packets from a PCAP file and return a summary of each packet.
24
+
25
+ Args:
26
+ pcap_file: Path to the PCAP file to analyze (defaults to 'example.pcap')
27
+
28
+ Returns:
29
+ A structured dictionary containing DNS packet analysis results
30
+ """
31
+ # Get full path to PCAP file
32
+ full_path = self.config.get_pcap_file_path(pcap_file)
33
+
34
+ # Check if file exists
35
+ if not os.path.exists(full_path):
36
+ # List available PCAP files for help
37
+ available_files = self.config.list_pcap_files()
38
+ return {
39
+ "error": f"PCAP file '{pcap_file}' not found",
40
+ "available_files": available_files,
41
+ "pcap_directory": self.config.pcap_path,
42
+ }
43
+
44
+ return self.analyze_packets(full_path)
45
+
46
+ def list_pcap_files(self) -> str:
47
+ """
48
+ List all available PCAP files in the default directory.
49
+
50
+ Returns:
51
+ A list of available PCAP files that can be analyzed
52
+ """
53
+ files = self.config.list_pcap_files()
54
+ if files:
55
+ return f"Available PCAP files in {self.config.pcap_path}:\\n" + "\\n".join(
56
+ f"- {f}" for f in sorted(files)
57
+ )
58
+ else:
59
+ return f"No PCAP files found in {self.config.pcap_path}"
60
+
61
+ def analyze_packets(self, pcap_file: str) -> dict[str, Any]:
62
+ """Analyze DNS packets in a PCAP file.
63
+
64
+ Args:
65
+ pcap_file: Full path to the PCAP file
66
+
67
+ Returns:
68
+ Analysis results as a dictionary
69
+ """
70
+ try:
71
+ packets = rdpcap(pcap_file)
72
+ dns_packets = [pkt for pkt in packets if pkt.haslayer(DNS)]
73
+
74
+ if not dns_packets:
75
+ return {
76
+ "file": pcap_file,
77
+ "total_packets": len(packets),
78
+ "dns_packets_found": 0,
79
+ "message": "No DNS packets found in this capture",
80
+ }
81
+
82
+ packet_details = []
83
+ for i, pkt in enumerate(dns_packets, 1):
84
+ packet_info = self._analyze_dns_packet(pkt, i)
85
+ packet_details.append(packet_info)
86
+
87
+ # Generate statistics
88
+ stats = self._generate_statistics(packet_details)
89
+
90
+ result = {
91
+ "file": pcap_file,
92
+ "analysis_timestamp": datetime.now().isoformat(),
93
+ "total_packets_in_file": len(packets),
94
+ "dns_packets_found": len(dns_packets),
95
+ "statistics": stats,
96
+ "packets": packet_details,
97
+ }
98
+
99
+ return result
100
+
101
+ except Exception as e:
102
+ return {
103
+ "error": f"Error reading PCAP file '{pcap_file}': {str(e)}",
104
+ "file": pcap_file,
105
+ }
106
+
107
+ def _analyze_dns_packet(self, pkt: Any, packet_number: int) -> dict[str, Any]:
108
+ """Analyze a single DNS packet.
109
+
110
+ Args:
111
+ pkt: Scapy packet object
112
+ packet_number: Packet sequence number
113
+
114
+ Returns:
115
+ Dictionary containing packet analysis
116
+ """
117
+ dns_layer = pkt[DNS]
118
+
119
+ # Extract IP information
120
+ src_ip = dst_ip = "unknown"
121
+ if pkt.haslayer(IP):
122
+ src_ip = pkt[IP].src
123
+ dst_ip = pkt[IP].dst
124
+ elif pkt.haslayer(IPv6):
125
+ src_ip = pkt[IPv6].src
126
+ dst_ip = pkt[IPv6].dst
127
+
128
+ # Extract protocol (UDP/TCP)
129
+ protocol = "unknown"
130
+ if pkt.haslayer(UDP):
131
+ protocol = "UDP"
132
+ elif pkt.haslayer(TCP):
133
+ protocol = "TCP"
134
+
135
+ # Extract DNS questions
136
+ questions = []
137
+ if dns_layer.qd:
138
+ for q in dns_layer.qd:
139
+ try:
140
+ name = (
141
+ q.qname.decode().rstrip(".")
142
+ if hasattr(q.qname, "decode")
143
+ else str(q.qname).rstrip(".")
144
+ )
145
+ questions.append(
146
+ {
147
+ "name": name,
148
+ "type": getattr(q, "qtype", 0),
149
+ "class": getattr(q, "qclass", 0),
150
+ }
151
+ )
152
+ except (AttributeError, UnicodeDecodeError) as e:
153
+ # Skip malformed questions but log the issue
154
+ questions.append(
155
+ {
156
+ "name": f"<parsing_error: {str(e)}>",
157
+ "type": getattr(q, "qtype", 0),
158
+ "class": getattr(q, "qclass", 0),
159
+ }
160
+ )
161
+
162
+ # Extract DNS answers
163
+ answers = []
164
+ if dns_layer.an:
165
+ for a in dns_layer.an:
166
+ try:
167
+ # Safely extract the resource record name
168
+ if hasattr(a, "rrname"):
169
+ if hasattr(a.rrname, "decode"):
170
+ name = a.rrname.decode().rstrip(".")
171
+ else:
172
+ name = str(a.rrname).rstrip(".")
173
+ else:
174
+ name = "<unknown>"
175
+
176
+ answer_data = {
177
+ "name": name,
178
+ "type": getattr(a, "type", 0),
179
+ "class": getattr(a, "rclass", 0),
180
+ "ttl": getattr(a, "ttl", 0),
181
+ }
182
+
183
+ # Handle different answer types
184
+ if hasattr(a, "rdata"):
185
+ try:
186
+ if a.type == 1: # A record
187
+ answer_data["address"] = str(a.rdata)
188
+ elif a.type == 28: # AAAA record
189
+ answer_data["address"] = str(a.rdata)
190
+ elif a.type == 5: # CNAME
191
+ answer_data["cname"] = str(a.rdata).rstrip(".")
192
+ elif a.type == 15: # MX
193
+ answer_data["mx"] = str(a.rdata)
194
+ else:
195
+ answer_data["data"] = str(a.rdata)
196
+ except Exception as rdata_error:
197
+ answer_data["data"] = (
198
+ f"<rdata_parsing_error: {str(rdata_error)}>"
199
+ )
200
+
201
+ answers.append(answer_data)
202
+
203
+ except (AttributeError, UnicodeDecodeError) as e:
204
+ # Skip malformed answers but include error info
205
+ answers.append(
206
+ {
207
+ "name": f"<parsing_error: {str(e)}>",
208
+ "type": getattr(a, "type", 0),
209
+ "class": getattr(a, "rclass", 0),
210
+ "ttl": getattr(a, "ttl", 0),
211
+ "data": "<malformed_record>",
212
+ }
213
+ )
214
+
215
+ return {
216
+ "packet_number": packet_number,
217
+ "timestamp": datetime.fromtimestamp(float(pkt.time)).isoformat(),
218
+ "source_ip": src_ip,
219
+ "destination_ip": dst_ip,
220
+ "protocol": protocol,
221
+ "dns_id": dns_layer.id,
222
+ "flags": {
223
+ "is_response": bool(dns_layer.qr),
224
+ "authoritative": bool(dns_layer.aa),
225
+ "truncated": bool(dns_layer.tc),
226
+ "recursion_desired": bool(dns_layer.rd),
227
+ "recursion_available": bool(dns_layer.ra),
228
+ },
229
+ "questions": questions,
230
+ "answers": answers,
231
+ "summary": pkt.summary(),
232
+ }
233
+
234
+ def _generate_statistics(self, packet_details: list) -> dict[str, Any]:
235
+ """Generate statistics from analyzed packets.
236
+
237
+ Args:
238
+ packet_details: List of analyzed packet dictionaries
239
+
240
+ Returns:
241
+ Dictionary containing statistics
242
+ """
243
+ query_count = sum(1 for p in packet_details if not p["flags"]["is_response"])
244
+ response_count = sum(1 for p in packet_details if p["flags"]["is_response"])
245
+ unique_domains = set()
246
+ for p in packet_details:
247
+ for q in p["questions"]:
248
+ unique_domains.add(q["name"])
249
+
250
+ return {
251
+ "queries": query_count,
252
+ "responses": response_count,
253
+ "unique_domains_queried": len(unique_domains),
254
+ "unique_domains": list(unique_domains),
255
+ }
256
+
257
+ def setup_prompts(self, mcp: FastMCP) -> None:
258
+ """Set up DNS-specific analysis prompts for the MCP server.
259
+
260
+ Args:
261
+ mcp: FastMCP server instance
262
+ """
263
+
264
+ @mcp.prompt
265
+ def security_analysis():
266
+ """Prompt for analyzing DNS traffic from a security perspective"""
267
+ return """You are a cybersecurity analyst examining DNS traffic. Focus your analysis on:
268
+
269
+ 1. **Threat Detection:**
270
+ - Look for suspicious domain patterns (DGA, long random strings)
271
+ - Identify potential DNS tunneling (unusually long queries, high TXT record volume)
272
+ - Spot potential C2 communication patterns
273
+ - Check for queries to known malicious domains
274
+
275
+ 2. **Behavioral Analysis:**
276
+ - Identify unusual query frequencies or patterns
277
+ - Look for reconnaissance activities (PTR lookups, zone transfers)
278
+ - Check for DNS cache poisoning attempts
279
+ - Monitor for subdomain enumeration
280
+
281
+ 3. **Infrastructure Assessment:**
282
+ - Identify DNS servers being used
283
+ - Check for DNS over non-standard ports
284
+ - Look for failed queries (NXDOMAIN) patterns
285
+ - Assess query distribution across time
286
+
287
+ Provide specific examples and recommend follow-up investigations for any suspicious findings."""
288
+
289
+ @mcp.prompt
290
+ def network_troubleshooting():
291
+ """Prompt for troubleshooting DNS-related network issues"""
292
+ return """You are a network engineer troubleshooting DNS issues. Focus on:
293
+
294
+ 1. **Performance Issues:**
295
+ - Identify slow DNS responses (high latency)
296
+ - Look for timeouts and retransmissions
297
+ - Check for load balancing issues
298
+ - Analyze response times across different servers
299
+
300
+ 2. **Connectivity Problems:**
301
+ - Find failed DNS queries and their causes
302
+ - Identify unreachable DNS servers
303
+ - Look for network path issues
304
+ - Check for DNS server failures
305
+
306
+ 3. **Configuration Issues:**
307
+ - Verify proper DNS server assignments
308
+ - Check for mismatched recursion settings
309
+ - Look for incorrect domain configurations
310
+ - Identify forwarding problems
311
+
312
+ 4. **Capacity Planning:**
313
+ - Analyze query volumes and patterns
314
+ - Identify peak usage times
315
+ - Look for resource exhaustion indicators
316
+ - Assess server response capabilities
317
+
318
+ Provide actionable recommendations for resolving identified issues."""
319
+
320
+ @mcp.prompt
321
+ def forensic_investigation():
322
+ """Prompt for forensic analysis of DNS traffic"""
323
+ return """You are conducting a digital forensics investigation involving DNS traffic. Approach this systematically:
324
+
325
+ 1. **Timeline Reconstruction:**
326
+ - Create a chronological sequence of DNS events
327
+ - Correlate DNS queries with potential incident timeframes
328
+ - Identify patterns in query timing and frequency
329
+ - Map DNS activity to user/system behavior
330
+
331
+ 2. **Evidence Collection:**
332
+ - Document all suspicious or anomalous DNS queries
333
+ - Preserve query-response pairs for analysis
334
+ - Record DNS server interactions and responses
335
+ - Note any encrypted or tunneled DNS traffic
336
+
337
+ 3. **Attribution and Tracking:**
338
+ - Trace DNS queries to source systems/users
339
+ - Identify external domains contacted
340
+ - Map communication patterns and relationships
341
+ - Document potential data exfiltration via DNS
342
+
343
+ 4. **Impact Assessment:**
344
+ - Determine scope of DNS-related compromise
345
+ - Assess potential data exposure through DNS
346
+ - Identify systems that may be affected
347
+ - Evaluate ongoing security risks
348
+
349
+ Present findings with timestamps, evidence preservation notes, and clear documentation suitable for legal proceedings."""
@@ -0,0 +1,5 @@
1
+ """Resources for mcpcap analysis."""
2
+
3
+ from .references import setup_resources
4
+
5
+ __all__ = ["setup_resources"]
@@ -0,0 +1,90 @@
1
+ """Reference resources for DNS analysis."""
2
+
3
+ from fastmcp import FastMCP
4
+
5
+
6
+ def setup_resources(mcp: FastMCP) -> None:
7
+ """Set up reference resources for the MCP server.
8
+
9
+ Args:
10
+ mcp: FastMCP server instance
11
+ """
12
+
13
+ @mcp.resource("dns-record-types://reference")
14
+ def get_dns_record_types() -> str:
15
+ """Reference guide for DNS record types"""
16
+ return """
17
+ # DNS Record Types Reference
18
+
19
+ ## Common Record Types:
20
+ - **A (1)**: IPv4 address record
21
+ - **AAAA (28)**: IPv6 address record
22
+ - **CNAME (5)**: Canonical name (alias)
23
+ - **MX (15)**: Mail exchange record
24
+ - **NS (2)**: Name server record
25
+ - **PTR (12)**: Pointer record (reverse DNS)
26
+ - **SOA (6)**: Start of authority
27
+ - **TXT (16)**: Text record
28
+ - **SRV (33)**: Service record
29
+
30
+ ## Security-Related Types:
31
+ - **DNSKEY (48)**: DNS public key
32
+ - **RRSIG (46)**: Resource record signature
33
+ - **DS (43)**: Delegation signer
34
+ - **NSEC (47)**: Next secure record
35
+ """
36
+
37
+ @mcp.resource("dns-flags://reference")
38
+ def get_dns_flags_reference() -> str:
39
+ """Reference guide for DNS flags and their meanings"""
40
+ return """
41
+ # DNS Flags Reference
42
+
43
+ ## Header Flags:
44
+ - **QR**: Query/Response (0=Query, 1=Response)
45
+ - **AA**: Authoritative Answer
46
+ - **TC**: Truncated (message was truncated)
47
+ - **RD**: Recursion Desired
48
+ - **RA**: Recursion Available
49
+ - **Z**: Reserved (must be zero)
50
+ - **AD**: Authenticated Data
51
+ - **CD**: Checking Disabled
52
+
53
+ ## Response Codes (RCODE):
54
+ - **0**: No error
55
+ - **1**: Format error
56
+ - **2**: Server failure
57
+ - **3**: Name error (domain doesn't exist)
58
+ - **4**: Not implemented
59
+ - **5**: Refused
60
+ """
61
+
62
+ @mcp.resource("suspicious-domains://indicators")
63
+ def get_suspicious_domain_indicators() -> str:
64
+ """Common indicators of suspicious or malicious domains"""
65
+ return """
66
+ # Suspicious Domain Indicators
67
+
68
+ ## Common Patterns:
69
+ - Long random-looking subdomains
70
+ - Domains with excessive hyphens or numbers
71
+ - Recently registered domains
72
+ - Domains using punycode (internationalized domains)
73
+ - DGA (Domain Generation Algorithm) patterns
74
+
75
+ ## Suspicious TLDs (often abused):
76
+ - .tk, .ml, .ga, .cf (free TLDs)
77
+ - .bit (blockchain domains)
78
+ - Newly introduced gTLDs
79
+
80
+ ## Behavioral Indicators:
81
+ - High frequency of DNS queries
82
+ - Queries to non-existent domains (NXDOMAIN)
83
+ - Unusual query patterns or timing
84
+ - Queries for infrastructure domains (.arpa, .root-servers.net)
85
+
86
+ ## DNS Tunneling Indicators:
87
+ - Unusually long DNS queries
88
+ - High volume of TXT record queries
89
+ - Queries with encoded data in subdomain names
90
+ """
@@ -0,0 +1,198 @@
1
+ Metadata-Version: 2.4
2
+ Name: mcpcap
3
+ Version: 0.2.1
4
+ Summary: A modular Python MCP Server for analyzing PCAP files
5
+ Author: mcpcap contributors
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/danohn/mcpcap
8
+ Project-URL: Repository, https://github.com/danohn/mcpcap
9
+ Project-URL: Issues, https://github.com/danohn/mcpcap/issues
10
+ Keywords: pcap,network,analysis,mcp,dns
11
+ Classifier: Development Status :: 3 - Alpha
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: Intended Audience :: System Administrators
14
+ Classifier: License :: OSI Approved :: MIT License
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.10
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Topic :: System :: Networking :: Monitoring
20
+ Classifier: Topic :: Security
21
+ Requires-Python: >=3.10
22
+ Description-Content-Type: text/markdown
23
+ License-File: LICENSE
24
+ Requires-Dist: fastmcp
25
+ Requires-Dist: scapy
26
+ Provides-Extra: test
27
+ Requires-Dist: pytest; extra == "test"
28
+ Requires-Dist: pytest-cov; extra == "test"
29
+ Requires-Dist: setuptools-scm[toml]; extra == "test"
30
+ Provides-Extra: dev
31
+ Requires-Dist: setuptools-scm[toml]; extra == "dev"
32
+ Requires-Dist: build; extra == "dev"
33
+ Requires-Dist: twine; extra == "dev"
34
+ Requires-Dist: ruff; extra == "dev"
35
+ Provides-Extra: docs
36
+ Requires-Dist: sphinx>=7.0; extra == "docs"
37
+ Requires-Dist: sphinx-rtd-theme; extra == "docs"
38
+ Requires-Dist: myst-parser; extra == "docs"
39
+ Requires-Dist: sphinx-autodoc-typehints; extra == "docs"
40
+ Requires-Dist: sphinx-copybutton; extra == "docs"
41
+ Requires-Dist: linkify-it-py; extra == "docs"
42
+ Dynamic: license-file
43
+
44
+ # mcpcap
45
+
46
+ ![mcpcap logo](https://raw.githubusercontent.com/danohn/mcpcap/main/readme-assets/logo.png)
47
+
48
+ A modular Python MCP (Model Context Protocol) Server for analyzing PCAP files. mcpcap enables LLMs to read and analyze network packet captures from local or remote sources, providing structured JSON responses about network traffic.
49
+
50
+ ## Overview
51
+
52
+ mcpcap uses a modular architecture to analyze different network protocols found in PCAP files. Each module focuses on a specific protocol, allowing for targeted analysis and easy extensibility. The server leverages the powerful scapy library for packet parsing and analysis.
53
+
54
+ ### Key Features
55
+
56
+ - **Modular Architecture**: Easily extensible to support new protocols
57
+ - **Local & Remote PCAP Support**: Read files from local directories or HTTP servers
58
+ - **Scapy Integration**: Leverages scapy's comprehensive packet parsing capabilities
59
+ - **MCP Server**: Integrates seamlessly with LLM clients via Model Context Protocol
60
+ - **JSON Responses**: Structured data format for easy LLM consumption
61
+
62
+ ## Installation
63
+
64
+ mcpcap requires Python 3.10 or greater.
65
+
66
+ ### Using pip
67
+
68
+ ```bash
69
+ pip install mcpcap
70
+ ```
71
+
72
+ ### Using uv
73
+
74
+ ```bash
75
+ uv add mcpcap
76
+ ```
77
+
78
+ ### Using uvx (for one-time usage)
79
+
80
+ ```bash
81
+ uvx mcpcap
82
+ ```
83
+
84
+ ## Quick Start
85
+
86
+ 1. **Start the MCP Server**:
87
+
88
+ ```bash
89
+ mcpcap --pcap-path /path/to/pcap/files
90
+ ```
91
+
92
+ 2. **Connect your LLM client** to the MCP server
93
+
94
+ 3. **Ask questions** about your network traffic:
95
+ - "What domain was queried the most in the DNS traffic?"
96
+ - "Show me all DNS queries for example.com"
97
+ - "What are the top 5 queried domains?"
98
+
99
+ ## Modules
100
+
101
+ ### DNS Module
102
+
103
+ The DNS module analyzes Domain Name System packets in PCAP files.
104
+
105
+ **Capabilities**:
106
+
107
+ - Extract DNS queries and responses
108
+ - Identify queried domains and subdomains
109
+ - Analyze query types (A, AAAA, MX, etc.)
110
+ - Track query frequency and patterns
111
+ - Identify DNS servers used
112
+
113
+ **Example Usage**:
114
+
115
+ ```python
116
+ # LLM can ask: "What domains were queried in this PCAP?"
117
+ # mcpcap will return structured JSON with DNS query information
118
+ ```
119
+
120
+ ## Configuration
121
+
122
+ ### PCAP Sources
123
+
124
+ **Local Directory**:
125
+
126
+ ```bash
127
+ mcpcap --pcap-path /local/path/to/pcaps
128
+ ```
129
+
130
+ **Remote HTTP Server**:
131
+
132
+ ```bash
133
+ mcpcap --pcap-url http://example.com/pcaps/
134
+ ```
135
+
136
+ ### Module Selection
137
+
138
+ ```bash
139
+ mcpcap --modules dns --pcap-path /path/to/files
140
+ ```
141
+
142
+ ## Example
143
+
144
+ An example PCAP file (`example.pcap`) containing DNS traffic is included with the project to help you get started.
145
+
146
+ ## Architecture
147
+
148
+ mcpcap's modular design makes it easy to extend support for new protocols:
149
+
150
+ 1. **Core Engine**: Handles PCAP file loading and basic packet processing
151
+ 2. **Protocol Modules**: Individual modules for specific protocols (DNS, etc.)
152
+ 3. **MCP Interface**: Translates between LLM queries and packet analysis results
153
+ 4. **Output Formatter**: Converts analysis results to structured JSON
154
+
155
+ ### Adding New Modules
156
+
157
+ New protocol modules can be added by:
158
+
159
+ 1. Implementing the module interface
160
+ 2. Defining scapy display filters for the protocol
161
+ 3. Creating analysis functions specific to the protocol
162
+ 4. Registering the module with the core engine
163
+
164
+ Future modules might include:
165
+
166
+ - BGP (Border Gateway Protocol)
167
+ - HTTP/HTTPS traffic analysis
168
+ - TCP connection tracking
169
+ - And more!
170
+
171
+ ## Remote Access
172
+
173
+ mcpcap supports reading PCAP files from remote HTTP servers without authentication. Future versions may include support for Basic Authentication and other security mechanisms.
174
+
175
+ ## Contributing
176
+
177
+ Contributions are welcome! Whether you want to:
178
+
179
+ - Add support for new protocols
180
+ - Improve existing modules
181
+ - Enhance the MCP interface
182
+ - Add new features
183
+
184
+ Please feel free to open issues and submit pull requests.
185
+
186
+ ## License
187
+
188
+ MIT
189
+
190
+ ## Requirements
191
+
192
+ - Python 3.10+
193
+ - scapy
194
+ - MCP server dependencies (automatically installed)
195
+
196
+ ## Support
197
+
198
+ For questions, issues, or feature requests, please open an issue on GitHub.
@@ -0,0 +1,17 @@
1
+ mcpcap/__init__.py,sha256=rJwCpBXkhIvmsqHFpeR33Vg8kuipNPJ2JdlAjsTk7I4,1408
2
+ mcpcap/_version.py,sha256=vYqoJTG51NOUmYyL0xt8asRK8vUT4lGAdal_EZ59mvw,704
3
+ mcpcap/cli.py,sha256=8afFamCifC44vMAAi1gNqr2c6CdBgXk33IoRgqmSH2A,1416
4
+ mcpcap/core/__init__.py,sha256=WM5GTl06ZwwqHTPiKaYB-9hwOOXe3hyHG16FshwSsjE,127
5
+ mcpcap/core/config.py,sha256=0LmnzHWWOwtHrikW2o5C9N0SLVvNuCzkNYTQQEys27U,1476
6
+ mcpcap/core/server.py,sha256=1wGGhTMLPspeH45NoVvhcnjUcLWyD6kY1u9tpt4jos4,1140
7
+ mcpcap/modules/__init__.py,sha256=iIeoZuLA-EOv0OS8WU8qDCitXJnarq9F0hA5-Y97zis,140
8
+ mcpcap/modules/base.py,sha256=3h8lGt6d6ob4SbgP6THC5PnTeMRcKfTGoJ9ZlZsQje0,826
9
+ mcpcap/modules/dns.py,sha256=NSBVfx1KWgEDFcvOpvrxIVPAXXE-SvK2E4HS3vqvlVk,12627
10
+ mcpcap/resources/__init__.py,sha256=BPXV29wIG360w9Y9iNpQdA93H2PhT3a6CrnMZX2aaaU,109
11
+ mcpcap/resources/references.py,sha256=HCciAutgLHodlifC8goAZcWpvup3DfbVZ1rxPaXKggA,2516
12
+ mcpcap-0.2.1.dist-info/licenses/LICENSE,sha256=Ltj0zxftQyBYQMNva935v0i5QXQQOF8ygE8dQxGEtjk,1063
13
+ mcpcap-0.2.1.dist-info/METADATA,sha256=24bkSs35339PMi70HVhJziGIcm9XeurIsMT2fFrufCI,5523
14
+ mcpcap-0.2.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
15
+ mcpcap-0.2.1.dist-info/entry_points.txt,sha256=ck69gPBEopmU6mzQy9P6o6ssMr89bQbrvv51IaJ50Gc,39
16
+ mcpcap-0.2.1.dist-info/top_level.txt,sha256=YkRkVGjuM3nI7cVB1l8zIAeqiS_5_vrzbUcHNkH3OXE,7
17
+ mcpcap-0.2.1.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (80.9.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ mcpcap = mcpcap:main
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 danohn
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 @@
1
+ mcpcap