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.
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 doneThe 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 Truewhat 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.