4 releases (2 breaking)
| new 0.3.0 | Feb 20, 2026 |
|---|---|
| 0.2.0 | Feb 20, 2026 |
| 0.1.1 | Feb 19, 2026 |
| 0.1.0 | Feb 19, 2026 |
#218 in Network programming
275KB
6.5K
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 mcp--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":"alice@example.com"}'
# {"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
afhttpprocess; afhttp exits when stdin closes - Auth set once per host —
host_defaultsapplies auth only to matching host; credentials do not leak to other domains - Three requests fired without waiting —
models,usage, andchatall in-flight simultaneously - Connection reuse is automatic — requests to the same host can reuse pooled connections without extra agent logic
- Out-of-order responses —
usagearrived beforechatfinished; the agent matches byid - Streaming inline —
chatdelivers events as they arrive, no buffering, no special setup - HTTP errors are data —
usagereturned 403; afhttp delivers it ascode: "response"withstatus: 403; the agent checksstatus, not exception types or text patterns
MCP Mode
afhttp --mode mcp runs as a Model Context Protocol server, letting AI tools like Claude Desktop make HTTP requests directly:
{
"mcpServers": {
"afhttp": { "command": "afhttp", "args": ["--mode", "mcp"] }
}
}
Claude can then call http_request and http_config tools. See docs/mcp.md for the full setup guide.
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 Manual — CLI, MCP, and curl compat modes
- MCP Reference — MCP tool reference and Claude Desktop setup
- Protocol Reference — full field specification
- Testing Strategy — layered tests, coverage gate, regression policy
- Design — architecture and principles
License
MIT
Dependencies
~10–29MB
~388K SLoC