Skip to main content

A deterministic gate at the fetch boundary: a 200 is transport success, not a read.

Project description

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

Use it in Claude Desktop or Cursor

pip install ".[mcp]"                 # the fetch tool
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.

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], [browser], [extract] (trafilatura), [sign] (Ed25519).

MIT licensed.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

fetchgate-0.1.0.tar.gz (28.9 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

fetchgate-0.1.0-py3-none-any.whl (28.9 kB view details)

Uploaded Python 3

File details

Details for the file fetchgate-0.1.0.tar.gz.

File metadata

  • Download URL: fetchgate-0.1.0.tar.gz
  • Upload date:
  • Size: 28.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.11.9

File hashes

Hashes for fetchgate-0.1.0.tar.gz
Algorithm Hash digest
SHA256 438123a439eb4a9f235457d9cbf4a14d634653c19c50db0a10020b3b4393d03a
MD5 64194d52a771facaad1047c1e6c3bea4
BLAKE2b-256 8625bbe10fef5ff3dd40b9f6195a039698b5462b608e86f41b3f4ad3e1fd61f6

See more details on using hashes here.

File details

Details for the file fetchgate-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: fetchgate-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 28.9 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.11.9

File hashes

Hashes for fetchgate-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 36cef34149b8ad235417025b3b6584e3cd59bbb4e50b6a9fca6329db75b3b664
MD5 8cec943d8008bfd4fcc60bfb8ca8da24
BLAKE2b-256 7ee09d1c7d4d0d855c94d55c37c9f0ea61aec5e8aad4565e5a727b1207225b29

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page