Skip to main content
Glama
seayniclabs

Keel

by seayniclabs

subnet_scan

Discover live hosts on a local subnet by probing TCP ports 22, 80, and 443. Supports only RFC 1918 private subnets for safety.

Instructions

Discover live hosts on a local subnet by probing common ports.

Only allows RFC 1918 private subnets for safety. Probes TCP ports 22, 80, and 443 on each host.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
subnetYes

Implementation Reference

  • The subnet_scan MCP tool handler: discovers live hosts on a local subnet by probing TCP ports 22, 80, and 443. Validates subnet via validate_subnet, enforces /20 size cap, and uses asyncio semaphore (50) for concurrency-limited probing.
    async def subnet_scan(subnet: str) -> dict:
        """Discover live hosts on a local subnet by probing common ports.
    
        Only allows RFC 1918 private subnets for safety.
        Probes TCP ports 22, 80, and 443 on each host.
        """
        subnet = validate_subnet(subnet)
        network = ipaddress.IPv4Network(subnet, strict=False)
    
        # Safety: cap at /20 (4096 hosts).
        if network.num_addresses > 4096:
            raise ValueError("Subnet too large — maximum /20 (4096 addresses)")
    
        probe_ports = [22, 80, 443]
    
        async def _probe(ip: str) -> dict | None:
            for port in probe_ports:
                try:
                    reader, writer = await asyncio.wait_for(
                        asyncio.open_connection(ip, port),
                        timeout=1,
                    )
                    writer.close()
                    await writer.wait_closed()
                    return {"ip": ip, "open_port": port}
                except (OSError, asyncio.TimeoutError):
                    continue
            return None
    
        # Run probes with concurrency limit.
        sem = asyncio.Semaphore(50)
    
        async def _limited_probe(ip: str) -> dict | None:
            async with sem:
                return await _probe(ip)
    
        hosts = [str(ip) for ip in network.hosts()]
        results = await asyncio.gather(*[_limited_probe(h) for h in hosts])
        found = [r for r in results if r is not None]
    
        return {
            "subnet": subnet,
            "hosts_scanned": len(hosts),
            "hosts_found": len(found),
            "hosts": found,
        }
  • The validate_subnet validator: ensures the subnet is a valid IPv4 CIDR and belongs to RFC 1918 private ranges (10.0.0.0/8, 172.16.0.0/12, 192.168.x.x/16). Raises ValueError for public or malformed subnets.
    def validate_subnet(subnet: str) -> str:
        """Validate a CIDR subnet — only RFC 1918 private ranges permitted.
    
        Returns the cleaned subnet string.
        Raises ``ValueError`` for public or malformed subnets.
        """
        subnet = subnet.strip()
        if not subnet:
            raise ValueError("Subnet must not be empty")
    
        try:
            network = ipaddress.IPv4Network(subnet, strict=False)
        except (ipaddress.AddressValueError, ValueError) as exc:
            raise ValueError(f"Invalid subnet: {subnet!r} — {exc}") from exc
    
        if not any(network.subnet_of(priv) for priv in _PRIVATE_NETWORKS):
            raise ValueError(
                f"Subnet {subnet} is not within RFC 1918 private ranges. "
                "Only 10.0.0.0/8, 172.16.0.0/12, and 192.168.x.x/16 are allowed."
            )
    
        return subnet
  • The @mcp.tool() decorator registers subnet_scan as an MCP tool with FastMCP.
    @mcp.tool()
    async def subnet_scan(subnet: str) -> dict:
  • Private network definitions used by validate_subnet to restrict subnet_scan to RFC 1918 ranges only.
    _PRIVATE_NETWORKS = [
        ipaddress.IPv4Network("10.0.0.0/8"),
        ipaddress.IPv4Network("172.16.0.0/12"),
        ipaddress.IPv4Network(f"192.168.{0}.0/16"),
    ]
Behavior3/5

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

Description adds behavioral details beyond annotations (none provided): it restricts to RFC 1918 subnets and specifies probing ports. However, it lacks information about return format, execution time, or potential network impact, which are needed for a network scanning tool.

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 the main purpose, and each sentence adds value. No redundancy or fluff.

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?

For a simple tool, the description covers core functionality and constraints. However, it lacks details about output (no schema) and operational context (e.g., speed, safety, firewall concerns), which are relevant for network scanning.

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

Parameters3/5

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

The only parameter 'subnet' lacks schema description (0% coverage). The description adds meaning by stating the subnet must be RFC 1918 private, but does not specify expected format (e.g., CIDR notation) or provide examples.

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?

Description clearly states the tool discovers live hosts on a local subnet by probing common ports. It specifies the exact ports (22, 80, 443) and the subnet constraint (RFC 1918), which distinguishes it from sibling tools like port_scan or ping.

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

Usage Guidelines2/5

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

No explicit guidance on when to use this tool versus alternatives. It implies context by mentioning 'local subnet' and 'private subnets,' but does not provide when-not-to-use criteria or compare with sibling tools.

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