Skip to main content
Glama
seayniclabs

Keel

by seayniclabs

ping

Ping a host via TCP connect on port 80, providing min, avg, max, and jitter latency in milliseconds as a non-root ICMP alternative.

Instructions

Ping a host using TCP connect (port 80) as a non-root ICMP alternative.

Returns min/avg/max/jitter latency in milliseconds.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
hostYes
countNo
timeoutNo

Implementation Reference

  • The async `ping` handler function decorated with `@mcp.tool()`. It pings a host using TCP connect (port 80) as a non-root ICMP alternative. Validates host via `validate_host`, runs `count` async TCP connections, measures latency in ms, and returns min/avg/max/jitter plus packet loss stats.
    @mcp.tool()
    async def ping(host: str, count: int = 4, timeout: int = 5) -> dict:
        """Ping a host using TCP connect (port 80) as a non-root ICMP alternative.
    
        Returns min/avg/max/jitter latency in milliseconds.
        """
        host = validate_host(host)
        if count < 1 or count > 100:
            raise ValueError("count must be between 1 and 100")
    
        latencies: list[float] = []
        errors: list[str] = []
    
        for _ in range(count):
            start = time.perf_counter()
            try:
                reader, writer = await asyncio.wait_for(
                    asyncio.open_connection(host, 80),
                    timeout=timeout,
                )
                elapsed = (time.perf_counter() - start) * 1000  # ms
                latencies.append(elapsed)
                writer.close()
                await writer.wait_closed()
            except (OSError, asyncio.TimeoutError) as exc:
                errors.append(str(exc))
    
        if not latencies:
            return {"host": host, "success": False, "errors": errors}
    
        avg = sum(latencies) / len(latencies)
        jitter = (
            (sum(abs(l - avg) for l in latencies) / len(latencies))
            if len(latencies) > 1
            else 0.0
        )
    
        return {
            "host": host,
            "success": True,
            "packets_sent": count,
            "packets_received": len(latencies),
            "packet_loss_pct": round((1 - len(latencies) / count) * 100, 1),
            "min_ms": round(min(latencies), 2),
            "avg_ms": round(avg, 2),
            "max_ms": round(max(latencies), 2),
            "jitter_ms": round(jitter, 2),
        }
  • The `@mcp.tool()` decorator on line 60 registers `ping` as an MCP tool. Comment section header marks this as tool #2 (ping).
    # 2. ping
    # ---------------------------------------------------------------------------
  • Import of `Optional` from typing, used in function signatures across the file (though not directly used in ping).
    from typing import Optional
  • `validate_host` is called by the ping handler to sanitize/validate the host argument. It strips whitespace, rejects shell metacharacters, validates IP/hostname format, and optionally blocks internal IPs (but `allow_internal=True` by default, so ping can target internal networks).
    def validate_host(host: str, *, allow_internal: bool = True) -> str:
        """Validate a hostname or IP address.
    
        Args:
            host: The hostname or IP to validate.
            allow_internal: If False, reject internal/private/loopback IPs and
                hostnames that resolve to them (SSRF protection). Defaults to
                True since tools like ping and traceroute legitimately target
                internal networks.
    
        Returns the cleaned host string.
        Raises ``ValueError`` on anything suspicious.
        """
        host = host.strip()
        if not host:
            raise ValueError("Host must not be empty")
    
        if _SHELL_META.search(host):
            raise ValueError(f"Host contains forbidden characters: {host!r}")
    
        # Accept valid IP addresses directly.
        try:
            addr = ipaddress.ip_address(host)
            if not allow_internal and is_internal_ip(host):
                raise ValueError(
                    f"Host {host} is an internal/private/loopback address — "
                    "requests to internal addresses are blocked"
                )
            return host
        except ValueError as exc:
            # Re-raise if it's our own SSRF block, not an ip_address parse error
            if "internal" in str(exc):
                raise
            pass
    
        if not _HOSTNAME_RE.match(host):
            raise ValueError(f"Invalid hostname: {host!r}")
    
        # If internal not allowed, resolve and check the IP
        if not allow_internal:
            _resolve_and_check(host)
    
        return host
  • Test cases for the ping tool: `test_ping_localhost` and `test_ping_web_target` verify the handler works end-to-end. Boundary tests at lines 275-289 validate count range enforcement.
    # ── ping ───────────────────────────────────────────────────────────────────
    
    
    @pytest.mark.asyncio
    async def test_ping_localhost():
        """Ping localhost — TCP connect to port 80 may fail, but the call
        should not raise."""
        result = await ping("localhost", count=2, timeout=2)
        assert result["host"] == "localhost"
        assert "success" in result
    
    
    @pytest.mark.asyncio
    async def test_ping_web_target(web_target):
        """Ping the Docker web target — port 80 is open."""
        host, _port = web_target.split(":")
        result = await ping(host, count=2, timeout=5)
        assert result["host"] == host
Behavior3/5

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

No annotations provided, so description carries full burden. It discloses the TCP connect method on port 80 and return formats, but does not mention error handling, unreachable hosts, or whether the tool is safe (non-destructive implied but not explicit).

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

Conciseness5/5

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

Two sentences, front-loaded with purpose and method. Every sentence adds value; no wasted words.

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

Completeness4/5

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

For a simple ping tool with 3 parameters, no output schema, and sibling tools, the description covers core functionality, method, and return values. Could mention that timeout is in seconds and that count is the number of pings, but overall adequate.

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

Parameters2/5

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

Input schema has 0% description coverage. The description does not explain the 'host', 'count', or 'timeout' parameters beyond their names. It only mentions port 80, but port is not a parameter. No added value for parameter understanding.

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?

Clearly states the tool pings a host using TCP connect on port 80 as a non-root ICMP alternative, and explains return values (latency metrics). This distinguishes it from sibling network tools like http_check or port_scan.

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?

The description implies the tool is for users without root access (non-root ICMP alternative), but does not explicitly state when to use it versus other network tools like http_check or port_scan. No exclusions or alternatives mentioned.

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/seayniclabs/sounding'

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