Skip to main content
Glama
mcpcap

mcpacket

by mcpcap

analyze_icmp_packets

Analyze ICMP packets from a PCAP file to obtain detailed analysis results for network troubleshooting.

Instructions

Analyze ICMP packets from a PCAP file and return comprehensive analysis results.

FILE UPLOAD LIMITATION: This MCP tool cannot process files uploaded through Claude's web interface. Files must be accessible via URL or local file path.

SUPPORTED INPUT FORMATS:

  • Remote files: "https://example.com/capture.pcap"

  • Local files: "/absolute/path/to/capture.pcap"

UNSUPPORTED:

  • Files uploaded through Claude's file upload feature

  • Base64 file content

  • Relative file paths

Args: pcap_file: HTTP URL or absolute local file path to PCAP file

Returns: A structured dictionary containing ICMP packet analysis results

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
pcap_fileYes

Output Schema

TableJSON Schema
NameRequiredDescriptionDefault

No arguments

Implementation Reference

  • The main handler function for the 'analyze_icmp_packets' tool. Delegates to BaseModule.analyze_packets() which handles both remote URLs and local files, then calls _analyze_protocol_file().
    def analyze_icmp_packets(self, pcap_file: str) -> dict[str, Any]:
        """
        Analyze ICMP packets from a PCAP file and return comprehensive analysis results.
    
        FILE UPLOAD LIMITATION: This MCP tool cannot process files uploaded through
        Claude's web interface. Files must be accessible via URL or local file path.
    
        SUPPORTED INPUT FORMATS:
        - Remote files: "https://example.com/capture.pcap"
        - Local files: "/absolute/path/to/capture.pcap"
    
        UNSUPPORTED:
        - Files uploaded through Claude's file upload feature
        - Base64 file content
        - Relative file paths
    
        Args:
            pcap_file: HTTP URL or absolute local file path to PCAP file
    
        Returns:
            A structured dictionary containing ICMP packet analysis results
        """
        return self.analyze_packets(pcap_file)
  • Core analysis logic: reads PCAP with Scapy, filters ICMP packets, applies max_packets limit, analyzes each packet via _analyze_icmp_packet(), generates statistics, and returns structured results.
    def _analyze_protocol_file(self, pcap_file: str) -> dict[str, Any]:
        """Perform the actual ICMP packet analysis on a local PCAP file."""
        try:
            packets = rdpcap(pcap_file)
            icmp_packets = [pkt for pkt in packets if pkt.haslayer(ICMP)]
    
            if not icmp_packets:
                return {
                    "file": pcap_file,
                    "total_packets": len(packets),
                    "icmp_packets_found": 0,
                    "message": "No ICMP packets found in this capture",
                }
    
            # Apply max_packets limit if specified
            packets_to_analyze = icmp_packets
            limited = False
            if self.config.max_packets and len(icmp_packets) > self.config.max_packets:
                packets_to_analyze = icmp_packets[: self.config.max_packets]
                limited = True
    
            packet_details = []
            for i, pkt in enumerate(packets_to_analyze, 1):
                packet_info = self._analyze_icmp_packet(pkt, i)
                packet_details.append(packet_info)
    
            # Generate statistics
            stats = self._generate_statistics(packet_details)
    
            result = {
                "file": pcap_file,
                "analysis_timestamp": datetime.now().isoformat(),
                "total_packets": len(packets),
                "icmp_packets_found": len(icmp_packets),
                "icmp_packets_analyzed": len(packet_details),
                "statistics": stats,
                "packets": packet_details,
            }
    
            if limited:
                result["note"] = (
                    f"Analysis limited to first {self.config.max_packets} ICMP packets due to --max-packets setting"
                )
    
            return result
    
        except Exception as e:
            return {
                "error": f"Error reading PCAP file '{pcap_file}': {str(e)}",
                "file": pcap_file,
            }
  • Helper that analyzes a single ICMP packet: extracts IP src/dst/version/TTL, ICMP type/code/type_name/ID/sequence/checksum, and code descriptions for Destination Unreachable and Time Exceeded types.
    def _analyze_icmp_packet(self, packet, packet_num: int) -> dict[str, Any]:
        """Analyze a single ICMP packet and extract relevant information."""
        info = {
            "packet_number": packet_num,
            "timestamp": datetime.fromtimestamp(float(packet.time)).isoformat(),
        }
    
        # Basic IP information
        if packet.haslayer(IP):
            ip_layer = packet[IP]
            info.update(
                {
                    "src_ip": ip_layer.src,
                    "dst_ip": ip_layer.dst,
                    "ip_version": 4,
                    "ttl": ip_layer.ttl,
                    "packet_size": len(packet),
                }
            )
        elif packet.haslayer(IPv6):
            ipv6_layer = packet[IPv6]
            info.update(
                {
                    "src_ip": ipv6_layer.src,
                    "dst_ip": ipv6_layer.dst,
                    "ip_version": 6,
                    "hop_limit": ipv6_layer.hlim,
                    "packet_size": len(packet),
                }
            )
    
        # ICMP information
        if packet.haslayer(ICMP):
            icmp_layer = packet[ICMP]
    
            # Map ICMP types to human-readable names
            icmp_types = {
                0: "Echo Reply",
                3: "Destination Unreachable",
                4: "Source Quench",
                5: "Redirect",
                8: "Echo Request",
                11: "Time Exceeded",
                12: "Parameter Problem",
                13: "Timestamp Request",
                14: "Timestamp Reply",
                15: "Information Request",
                16: "Information Reply",
            }
    
            # Map destination unreachable codes
            dest_unreach_codes = {
                0: "Network Unreachable",
                1: "Host Unreachable",
                2: "Protocol Unreachable",
                3: "Port Unreachable",
                4: "Fragmentation Required",
                5: "Source Route Failed",
            }
    
            # Map time exceeded codes
            time_exceeded_codes = {
                0: "TTL Exceeded in Transit",
                1: "Fragment Reassembly Time Exceeded",
            }
    
            icmp_type = icmp_layer.type
            icmp_code = icmp_layer.code
    
            info.update(
                {
                    "icmp_type": icmp_type,
                    "icmp_code": icmp_code,
                    "icmp_type_name": icmp_types.get(
                        icmp_type, f"Unknown Type ({icmp_type})"
                    ),
                    "icmp_id": getattr(icmp_layer, "id", None),
                    "icmp_seq": getattr(icmp_layer, "seq", None),
                    "checksum": icmp_layer.chksum,
                }
            )
    
            # Add code descriptions for specific types
            if icmp_type == 3:  # Destination Unreachable
                info["icmp_code_name"] = dest_unreach_codes.get(
                    icmp_code, f"Unknown Code ({icmp_code})"
                )
            elif icmp_type == 11:  # Time Exceeded
                info["icmp_code_name"] = time_exceeded_codes.get(
                    icmp_code, f"Unknown Code ({icmp_code})"
                )
            else:
                info["icmp_code_name"] = f"Code {icmp_code}"
    
        return info
  • Helper that generates aggregate statistics: ICMP type counts, unique source/destination IPs, echo request/reply pair tracking, and unreachable destination tracking.
    def _generate_statistics(self, packets: list[dict[str, Any]]) -> dict[str, Any]:
        """Generate statistics from analyzed ICMP packets."""
        stats = {
            "icmp_types": {},
            "unique_sources": set(),
            "unique_destinations": set(),
            "echo_pairs": {},
            "unreachable_destinations": set(),
        }
    
        for pkt in packets:
            # Count ICMP types
            if "icmp_type_name" in pkt:
                type_name = pkt["icmp_type_name"]
                stats["icmp_types"][type_name] = (
                    stats["icmp_types"].get(type_name, 0) + 1
                )
    
            # Track unique IPs
            if "src_ip" in pkt:
                stats["unique_sources"].add(pkt["src_ip"])
            if "dst_ip" in pkt:
                stats["unique_destinations"].add(pkt["dst_ip"])
    
            # Track echo request/reply pairs
            if pkt.get("icmp_type") in [0, 8] and pkt.get("icmp_id") is not None:
                echo_id = pkt["icmp_id"]
                if echo_id not in stats["echo_pairs"]:
                    stats["echo_pairs"][echo_id] = {"requests": 0, "replies": 0}
    
                if pkt["icmp_type"] == 8:  # Echo Request
                    stats["echo_pairs"][echo_id]["requests"] += 1
                elif pkt["icmp_type"] == 0:  # Echo Reply
                    stats["echo_pairs"][echo_id]["replies"] += 1
    
            # Track unreachable destinations
            if pkt.get("icmp_type") == 3:  # Destination Unreachable
                if "dst_ip" in pkt:
                    stats["unreachable_destinations"].add(pkt["dst_ip"])
    
        # Convert sets to lists for JSON serialization
        return {
            "icmp_type_counts": stats["icmp_types"],
            "unique_sources_count": len(stats["unique_sources"]),
            "unique_destinations_count": len(stats["unique_destinations"]),
            "unique_sources": list(stats["unique_sources"]),
            "unique_destinations": list(stats["unique_destinations"]),
            "echo_sessions": len(stats["echo_pairs"]),
            "echo_pairs": stats["echo_pairs"],
            "unreachable_destinations_count": len(stats["unreachable_destinations"]),
            "unreachable_destinations": list(stats["unreachable_destinations"]),
        }
  • Registration of analyze_icmp_packets as an MCP tool via FastMCP's tool() decorator within the MCPServer._register_tools() method.
    elif module_name == "icmp":
        self.mcp.tool(module.analyze_icmp_packets)
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Discloses important behavioral limitation (cannot process uploaded files via web interface) and supported input formats. With no annotations, this fills the gap well. Does not specify side effects, but as an analysis tool, read-only nature can be inferred.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Well-structured with clear sections (limitation, formats, args, returns). Slightly verbose in repeating the limitation, but overall concise and front-loaded with the main action.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With an output schema present, the description's mention of 'structured dictionary' is adequate. However, it lacks detail on what the analysis results contain (e.g., packet counts, ICMP types), which would help the agent interpret return values.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Despite 0% schema description coverage, the description explains the 'pcap_file' parameter as HTTP URL or absolute local file path, adding meaning beyond the bare type definition. Examples clarify expected format.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

Precise verb 'Analyze' and resource 'ICMP packets from a PCAP file' clearly identify purpose. Differentiates from sibling tools focusing on other protocols like DNS or TCP.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Provides detailed file input format constraints and unsupported methods, which is helpful for usage. However, no explicit guidance on when to use this tool over siblings (e.g., for ICMP vs DNS analysis), leaving selection implied.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/mcpcap/mcpacket'

If you have feedback or need assistance with the MCP directory API, please join our Discord server