Agent-First HTTP

Give your AI agent its own private browser — so it reads the real page, past logins and bot walls, without ever touching yours.

Ask your agent: “Open this link and pull the pricing table out for me.”

What problem does this solve?

Agents are bad at opening pages. Ask one to read a specific URL and it tends to:

afhttp fixes this by loading the actual URL itself — falling back to a real browser of its own to render JavaScript when the page needs one — and returns the page as files the agent can inspect, so it answers from verified content instead of a guess.

That browser is fully isolated: it runs separately from the browser you use every day and never touches your cookies, logins, or history. When a page needs a login, captcha, or 2FA, you can take over that same isolated browser, clear the wall yourself, and let the agent continue — without ever mixing it into your own session.

The basics: hand it a URL, get the page back as data

Give afhttp a URL; it writes the page to disk and prints one line of JSON saying what it got:

$ afhttp fetch https://example.com
{"code":"fetch","request_url":"https://example.com","status":200,"final_url":"https://example.com/","body_file":"/tmp/afhttp-out/<id>/body.html"}

That is the whole job: hand it a URL, get the page back as files an agent can read — never a terminal blob to scrape, and every failure a stable error_code rather than a guess.

By default afhttp sends a plain HTTP request, returns the raw body_file, and only starts a real browser when the page actually needs one (--render none forces the fast path, --render always forces the browser, --render auto decides). A browser-backed fetch automatically captures more of what a human would look at — an agent-oriented composed page view (content.md, the one to read first), rendered HTML, a screenshot, a DOM observation, the network and console logs — each a flat *_file field on the same JSON, never nested:

{
  "code": "fetch",
  "request_url": "https://example.com",
  "status": 200,
  "final_url": "https://example.com/",
  "body_file": "/tmp/afhttp-out/<id>/body.html",
  "content_file": "/tmp/afhttp-out/<id>/content.md",
  "content_json_file": "/tmp/afhttp-out/<id>/content.json",
  "rendered_html_file": "/tmp/afhttp-out/<id>/rendered.html",
  "text_file": "/tmp/afhttp-out/<id>/text.txt",
  "screenshot_file": "/tmp/afhttp-out/<id>/page.png",
  "network_file": "/tmp/afhttp-out/<id>/network.json",
  "console_file": "/tmp/afhttp-out/<id>/console.json",
  "observation_file": "/tmp/afhttp-out/<id>/observation.json"
}

Browser backends: meet each site with the engine it demands

afhttp is not “headless Chromium.” How hard a site fights back decides which engine actually reaches it, so afhttp drives a whole spectrum behind one CDP contract — pick one with --browser (or point --browser-bin at a binary):

Human takeover: a person drives the same browser when a step needs it

When a fetch hits a login, captcha, or 2FA wall, afhttp fetch <url> --takeover keeps a persistent tab open on a takeover-ready host and hands back a complete short-lived takeover_url a human opens to drive the same browser the agent is using, via real-display takeover backed by KasmVNC. Once the human is past the wall, the agent re-fetches the same tab to continue.

Running it: inline for a one-shot, a host for sessions

afhttp fetch <url> with no --endpoint-url runs inline — it spins up a sandboxed browser for that one fetch and tears it down. Zero setup; use it for stateless, one-shot acquisition.

For state that outlives a single fetch — a reused login, a warmed profile, human takeover — run a long-lived host in a container (the isolation boundary). One command builds the image from a recipe embedded in the binary and runs it (Docker, Podman, or Apple container, auto-detected — override with --runtime); it prints structured connection metadata and a ready-to-run driver command without exposing the long-lived host token by default:

afhttp container install

container install is takeover-ready by default (Brave + KasmVNC + an ephemeral initial profile + a 2g /dev/shm). A takeover fetch auto-discovers the standard local afhttp-host, switches to a persistent per-site profile derived from the URL, and lets a human clear a wall on the same browser the agent drives:

afhttp fetch "$URL" --takeover

If the warmed profile already reaches the target, fetch --takeover just returns the page. Otherwise it keeps a persistent tab open and returns a next_action with kind: "human_takeover", a takeover_url for a human to open, and a recommended_command that re-fetches the same --tab once the wall is cleared. fetch --takeover needs a running host (auto-discovered locally, or supplied with --endpoint-url / AFHTTP_ENDPOINT_URL) and a browser render (--render auto or always); it does not auto-create containers.

Building from a source checkout instead? Use afhttp container install --from-source (or docker compose -f container/docker/compose.yaml up --build when driving the runtime directly). See docs/deployment.md for the full container setup, upgrades, and security posture.

Adopt it: hand afhttp to your agent

The quickest way to find out whether afhttp earns a place in your toolkit is to let your agent read it and tell you. Paste this to your agent:

Read what Agent-First HTTP is at https://agentfirstkit.com/agent-first-http, then tell me in plain terms what it would do for me and whether it fits what I’m working on. If it’s a fit, install it — the prebuilt package for the quick path, or build from source after a quick security review of the repo if you’d rather read what you run — then run afhttp skill install so you follow its behavior rules.

If it’s a fit, install it — a prebuilt package, or from source if you want to read it first:

# prebuilt binary
brew install agentfirstkit/tap/afhttp   # macOS / Linux
scoop bucket add agentfirstkit https://github.com/agentfirstkit/scoop-bucket && scoop install afhttp   # Windows

# or build from source after reviewing the repo
git clone https://github.com/agentfirstkit/agent-first-http
cargo install --path agent-first-http

Then install the embedded Agent Skill so the agent follows afhttp’s behavior rules — when to escalate --render, when to reach for human takeover, how to read the artifacts. skill install targets Codex, Claude Code, and opencode; skill status reports whether each install is present, valid, and current:

afhttp skill install
afhttp skill status

To remove it, run afhttp skill uninstall.

Docs

Agent-First HTTP v0.7.0: One Flag for the Hard Sites, Brave by Default

v0.7.0 collapses the hard-site dance into a single flag: `afhttp fetch <url> --takeover`. It auto-discovers the local host, gives each site its own isolated profile by default, and only asks a human to step in when a wall actually blocks it. `container install` is now takeover-ready out of the box on Brave — built-in ad/tracker blocking and the browser a human drives, no `--with` flags to remember. Secrets are redacted from agent-visible output by default, with an explicit `--reveal-token-secret` to opt in. The whole release is one thing: make afhttp simpler for an agent to use and harder to use wrong.

Agent-First HTTP v0.6.0: A 200 Isn't Proof

v0.6.0 is about not getting fooled. afhttp now recognizes a bot wall or security challenge on the cheap HTTP path — Cloudflare, Turnstile, generic access-denied — surfaces it as a `page_kind` plus warning, and auto-escalates to a real browser instead of handing the agent a 200 that isn't the page. The fetch trace gets honest too: per-stage timing, `current_stage`, `capture_reason`, `wait_mode`, so an agent can see where a fetch spent its time and why it stopped. Plus readiness tuning, a `takeover prepare` subcommand, a named display-provider abstraction, and a skill rewrite that tells the agent to reach for afhttp first.

Agent-First HTTP v0.5.0: One Command to a Browser Host, in Any Runtime

v0.5.0 adds `afhttp container`: bring up the browser host in one command — Docker, Podman, or Apple Container — from a recipe embedded in the binary, no source tree required.

Agent-First HTTP v0.5.0: When the Page Needs a Browser

v0.5.0 turns afhttp from an HTTP client into a full URL-acquisition tool. A single `afhttp fetch` covers the whole range — a plain HTTP request when that works, a real browser when it doesn't — and returns the page plus structured artifacts (rendered HTML, a DOM observation, a screenshot, network and console logs) an agent can branch on. It adds a browser-host / agent-driver split, a raw CDP escape hatch, deep network capture, an ops panel with optional real-display takeover for human login/captcha/2FA, and persistent profiles. The public contract converged in the process: flat `*_file` artifact paths, one profile per host, and no legacy aliases.

Agent-First HTTP v0.4.3: Release Hardening for Agent Tools

The v0.4.3 release moved afhttp into Agent-First Kit, tightened structured-output discipline, and hardened cross-platform packaging.

Agent-First HTTP v0.4: A Narrower Runtime with Complete Help

The v0.4 line removed the MCP server surface, generated CLI docs from clap definitions, and made --help complete for agents.

Agent-First HTTP v0.3.4: Output Formats Preserved Responses

The v0.3.4 update extended yaml/plain rendering to pipe mode while protecting server response bodies from formatter reinterpretation.

Agent-First HTTP v0.3.2: Requests Became Previewable

The v0.3.2 update added dry-run previews and actionable hints, so agents can inspect an HTTP request shape before it touches the network.

Agent-First HTTP v0.3: One Request, One JSON Line

The early Agent-First HTTP release line: a structured HTTP client for agents that turns requests, streaming bodies, and transport failures into stable JSON events.