K.B.code →
← back to lab

open source

mcp-safe-fetch

A small, single-purpose MCP server: fetch a URL, return injection-sanitized, size-capped text. The reference implementation of the field notes.

tests status

the problem

Agents that read the open web need a fetch tool that does not become an attack surface: prompt injection in the content, and server-side request forgery in the URL.

Most fetch tools handle neither. This one is small on purpose, so the defenses are readable in five minutes.

architecture

  fetch_clean(url, max_chars)
        |
   SSRF guard: resolve host -> classify every IP
        |
   follow redirects, re-validate each hop
        |
   sanitize (see injection-defense)
        |
   size cap + SQLite (WAL) cache
        |
        v
   clean text + an audit of what was done

The SSRF guard runs on the resolved address, not the string. That is why it catches numeric-IP encodings, the full private ranges including 172.16.0.0/12, link-local metadata, and redirect-based rebinding. A string blocklist catches none of them.

Resolve, then classify every address. Not a string blocklist.

def host_is_public(host: str) -> bool:
    try:
        infos = socket.getaddrinfo(host, None)
    except OSError:
        return False
    for info in infos:
        addr = ipaddress.ip_address(str(info[4][0]).split("%")[0])
        if (addr.is_private or addr.is_loopback or addr.is_link_local
                or addr.is_reserved or addr.is_unspecified or addr.is_multicast):
            return False
    return True

what is still broken

  • It is meaningful hardening, not a substitute for an egress firewall.
  • The phrase-level injection net is best-effort; the structural wrap is the real boundary.