Skip to main content

FetchGate

A 200 is not a read.

When an AI agent fetches a page and it quietly comes back empty (a JavaScript app, a 403, a Cloudflare wall), the agent often does not stop. It answers from memory and never tells you it did not actually read the page. FetchGate is a small gate at the fetch boundary that refuses to let that happen: the agent gets the content only when the page was really read, and an honest refusal otherwise.

It is deterministic (no model, no API keys), stdlib-only at its core, and free.

Quickstart

git clone https://github.com/MoAz06/FetchGate
cd FetchGate
python demo.py https://example.com https://httpbin.org/status/403
https://example.com
  verdict     : RETRIEVED  (clean)
  answerable  : ALLOW
  bytes       : raw 559 / extracted 127
  tiers       : static
  gate-confirmed content (127 chars): Example Domain This domain is for use ...

https://httpbin.org/status/403
  verdict     : FAILED  (transport.http_403)
  answerable  : STOP
  refused: the model gets no content and cannot answer as if it read this page.

Run the tests (no network, no model):

pip install -e .
python -m unittest discover -s tests

Easiest: one double-click (Claude Desktop)

Download fetchgate.mcpb from the Releases page and double-click it. Claude Desktop shows an Install button, no JSON editing. The bundle source is in bundle/; rebuild it with npx @anthropic-ai/mcpb pack bundle fetchgate.mcpb.

Vibecoders: add it from inside your editor

Cursor, one click (needs uv, which most setups have):

Add FetchGate to Cursor

Or just ask your AI. Paste this into Cursor, Claude Code, or Windsurf:

Add the fetchgate MCP server to this setup. Run pip install "fetchgate[mcp]" and register it as an MCP server named fetchgate with the command fetchgate-mcp. It stops an agent from answering as if it read a page it could not actually fetch.

Your assistant runs the install and wires up the config for you.

Use it in Claude Desktop or Cursor (manual)

pip install ".[mcp,http,extract]"    # the fetch tool, real redirect chain, better extraction
pip install ".[browser]"             # optional: render JavaScript pages
python -m playwright install chromium

Add to your MCP config (details in docs/mcp.md):

{ "mcpServers": { "fetchgate": { "command": "fetchgate-mcp" } } }

Your agent now has a fetch_url tool that returns content only for a confirmed read, and a refusal otherwise.

This repo is also a Claude Code plugin. After pip install "fetchgate[mcp]":

/plugin marketplace add MoAz06/FetchGate
/plugin install fetchgate@fetchgate

See docs/claude-code-plugin.md.

Use it in your own agent

from fetchgate import default_gate

gate = default_gate()   # renders JS pages if the [browser] extra is installed

def read_url(url: str) -> str:
    out = gate.guarded_fetch(url)
    content = gate.content_for(out.handle)
    if content is None:
        return f"NOT READ: {url} ({out.result.reason}). Do not answer as if you read it."
    return content

How it decides

Every fetch gets one verdict:

  • RETRIEVED: a real read. The agent may answer and cite it.
  • FAILED: a definite non-read (a 4xx or 5xx, a timeout, an https to http downgrade, an empty body). Stop.
  • UNKNOWN: fetched, but not enough readable content to confirm a read (often a JavaScript page). Stop, or render and try again.

Three layers feed the verdict, and the result is the strictest of them:

  • transport: status and redirect chain.
  • extraction: readable text kept separate from raw bytes. A 17 byte price JSON is a read, a 400 KB article at 2 percent text is a read, a JS shell with no text is not.
  • content-validity: a best-effort fingerprint for Cloudflare, consent, paywall, and soft-404 walls. It can only downgrade to UNKNOWN, never fake a read.

When a page looks thin or empty and the render tier is on, FetchGate escalates to a real headless browser and re-checks. It renders, it does not evade: a page still walled after an honest render stays a non-read.

Stated limits

  • content_sha256 is a receipt anchor, not tamper-evidence. A cloaked page hashes cleanly.
  • Provenance is not authenticity. The gate closes fail-silent-on-empty, not fetch-succeeds-on-wrong-content.
  • A read is not an answer. RETRIEVED means enough content arrived, not that it held the fact you need. Claim support is a downstream job.
  • Fail-closed has an availability cost. Anyone who can make your fetch fail can force a refusal. That is the trade for correctness.

What is built

The deterministic core, the offline evaluation with a signed manifest, the build-breaking cheat-test, the MCP server, and the Playwright render tier. Optional extras: [mcp], [http] (httpx redirect chain), [extract] (trafilatura), [browser] (Playwright), [sign] (Ed25519). When installed they are used automatically; the core stays stdlib-only.

MIT licensed.

Metadata

Release files for fetchgate 0.2.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for fetchgate 0.2.0
File Size Uploaded
fetchgate-0.2.0.tar.gz 37.0 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for fetchgate 0.2.0
File Interpreter ABI Platform
fetchgate-0.2.0-py3-none-any.whl Python 3 none any Details

Total release size: 70.2 kB

Release files / fetchgate-0.2.0.tar.gz

Download URL fetchgate-0.2.0.tar.gz
Size 37.0 kB
Tags Source
SHA-256 checksum
How to use checksums
85654f1cac868080f30f60ebca9d411570a271171f92b1d1e69fd8a7938acbd9
BLAKE2b-256 checksum
How to use checksums
efe00689db69b0f9ae2f5fcb16c8af4d5d0217cc1f3ab0faf4a81bdfb5ce477d
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.11.9

Release files / fetchgate-0.2.0-py3-none-any.whl

Download URL fetchgate-0.2.0-py3-none-any.whl
Size 33.2 kB
Tags Python 3
SHA-256 checksum
How to use checksums
e1a742f8ea08dd141fc2f5316bef8a6f24c7d15603c6956b4df706740cf6d4ba
BLAKE2b-256 checksum
How to use checksums
5433329b435b0dbf608262e8213dfe15aaa6df7eba8f634fe8d1930c45774a28
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.11.9

Release history Release notifications | RSS feed

This release

0.2.0 This release

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page