Thanks to visit codestin.com
Credit goes to lib.rs

11 unstable releases (3 breaking)

0.4.2 Mar 26, 2026
0.4.1 Mar 24, 2026
0.3.4 Mar 11, 2026
0.3.0 Feb 20, 2026
0.1.1 Feb 19, 2026

#224 in Artificial intelligence

MIT license

260KB
6K SLoC

Agent-First HTTP

Persistent HTTP client for AI agents — one request, one JSON line.

Supported platforms: macOS, Linux, Windows.

Modes (single entrypoint):

  • --mode cli (default)
  • --mode pipe
  • --mode curl

The key contract for agents is protocol determinism: stdout is always structured JSON, and failures are always structured {"code":"error",...} events with stable error_code values. No human-only text parsing, no mixed output channels, no ad-hoc error shapes.

CLI Mode

The default mode — one request, one JSON response, exit:

afhttp GET https://api.example.com/users
# {"code":"response","status":200,"body":[...],"trace":{"duration_ms":120,...}}

afhttp POST https://api.example.com/users --body '{"name":"Alice","email":"[email protected]"}'
# {"code":"response","status":201,"body":{"id":42},...}

afhttp GET https://api.example.com/data --header "Authorization: Bearer sk-xxx"
# {"code":"response","status":200,...}

Structured error output examples:

{"code":"error","error_code":"connect_timeout","error":"request timed out after 30s","retryable":true,"trace":{"duration_ms":30012}}
{"code":"error","error_code":"invalid_request","error":"invalid --output format 'xml': expected json, yaml, or plain","retryable":false,"trace":{"duration_ms":0}}

Pipe Mode

For long-lived sessions with connection reuse, concurrent requests, and WebSocket — use afhttp --mode pipe:

afhttp --mode pipe <<'EOF'
{"code":"config","host_defaults":{"api.example.com":{"headers":{"x-api-key":"sk-xxx","api-version":"2023-06-01"}}}}
{"code":"request","id":"models","method":"GET","url":"https://api.example.com/v1/models"}
{"code":"request","id":"usage","method":"GET","url":"https://api.example.com/v1/usage"}
{"code":"request","id":"chat","method":"POST","url":"https://api.example.com/v1/messages","body":{"model":"general-chat-model","max_tokens":256,"messages":[{"role":"user","content":"Hello"}],"stream":true},"options":{"chunked":true,"chunked_delimiter":"\n\n"}}
EOF

output:

{"code":"config","host_defaults":{"api.example.com":{"headers":{"x-api-key":"[redacted]","api-version":"2023-06-01"}}},...}
{"code":"response","id":"models","status":200,"body":{"data":[{"id":"general-chat-model",...}]},"trace":{"duration_ms":92,"http_version":"h2","redirects":0,"remote_addr":"13.32.4.10"}}
{"code":"response","id":"usage","status":403,"body":{"error":{"type":"permission_error","message":"Your API key does not have permission"}},"trace":{"duration_ms":87,"http_version":"h2","redirects":0}}
{"code":"chunk_start","id":"chat","status":200,"headers":{"content-type":"text/event-stream"}}
{"code":"chunk_data","id":"chat","data":"event: content_block_delta\ndata: {\"delta\":{\"text\":\"Hello\"}}"}
{"code":"chunk_data","id":"chat","data":"event: content_block_delta\ndata: {\"delta\":{\"text\":\" there\"}}"}
{"code":"chunk_data","id":"chat","data":"event: message_stop\ndata: {}"}
{"code":"chunk_end","id":"chat","trace":{"duration_ms":834,"chunks":8}}

What just happened:

  • One bash call — the heredoc sends all requests into one afhttp process; afhttp exits when stdin closes
  • Auth set once per hosthost_defaults applies auth only to matching host; credentials do not leak to other domains
  • Three requests fired without waitingmodels, usage, and chat all in-flight simultaneously
  • Connection reuse is automatic — requests to the same host can reuse pooled connections without extra agent logic
  • Out-of-order responsesusage arrived before chat finished; the agent matches by id
  • Streaming inlinechat delivers events as they arrive, no buffering, no special setup
  • HTTP errors are datausage returned 403; afhttp delivers it as code: "response" with status: 403; the agent checks status, not exception types or text patterns

curl Compatibility

Use explicit curl mode. afhttp understands a subset of curl flags and returns structured JSON:

afhttp --mode curl -X POST https://api.example.com/users \
  -H "Authorization: Bearer sk-xxx" \
  -d '{"name":"Alice"}'
# {"code":"response","status":201,"body":{"id":42},...}

Install

macOS / Linux — Homebrew

brew install cmnspore/tap/afhttp

Windows — Scoop

scoop bucket add cmnspore https://github.com/cmnspore/scoop-bucket
scoop install afhttp

Any platform — Cargo

cargo install agent-first-http

Docs

  • CLI — Generated CLI guide and flag reference from src/cli.rs
  • Protocol Reference — full field specification
  • Testing Strategy — layered tests, coverage gate, regression policy
  • Design — architecture and principles

License

MIT

Dependencies

~32–49MB
~1M SLoC