Kutip is infrastructure, not just an app. The same research agent that powers the website is reachable from your own code, an autonomous agent, or any MCP client — and every call still pays the cited authors on-chain.
Three ways in:
- REST API —
POST /api/query, a streaming endpoint. - MCP server — drop Kutip into Claude Desktop / Cursor / Cline.
- Self-host — fork and point the clients at your own deployment.
Base URL of the public demo: https://kutip-zeta.vercel.app
Runs the full agent (search → x402 → read → attribute → settle) and streams progress back as Server-Sent Events.
POST /api/query
Content-Type: application/json
Without a session, the budget is capped at 0.5 USDC (anonymous
abuse fence). A signed session lifts the cap to the user's own limits.
Content-Type: text/event-stream. Each line is data: <json>:
{ "type": "step", "step": { "step": 1, "label": "Searching…", "status": "running" } }
{ "type": "step", "step": { "step": 1, "label": "Searching…", "status": "done", "detail": "10 papers" } }
{ "type": "result", "result": { /* summary, paperDetails, attestationTx, … */ } }
{ "type": "error", "message": "…" }Consume it incrementally — the five step events are how the UI draws
the live progress bar. The final result carries the summary, the
per-author split, and the on-chain attestationTx.
curl -N -X POST https://kutip-zeta.vercel.app/api/query \
-H 'Content-Type: application/json' \
-d '{"query":"perovskite tandem solar cell efficiency","budgetUSDC":0.2,"yearFrom":2023}'- Browser callers are gated by an
Originallowlist (CORS). - Server / agent / curl callers (no
Origin) are allowed by default. If the deployment sets aKUTIP_API_KEY, those callers must send a matchingX-Kutip-API-Keyheader. The public demo runs without a key — set one on your own deployment to gate it.
The research flow's "Purchase" step is a genuine x402 handshake against this endpoint. It is facilitator-free: the merchant verifies the payment by reading the Kite chain itself, so settlement is deterministic and auditable. Run it by hand:
# 1. Unpaid request → a real HTTP 402 + the payment challenge.
curl -i -X POST https://kutip-zeta.vercel.app/api/x402 \
-H 'Content-Type: application/json' -d '{"queryId":"demo-1"}'
# 2. Transfer `maxAmountRequired` USDC to `payTo` on Kite testnet.
# 3. Retry with the proof — base64({"txHash":"0x…"}):
curl -X POST https://kutip-zeta.vercel.app/api/x402 \
-H 'Content-Type: application/json' \
-H "X-PAYMENT: $(echo -n '{"txHash":"0x…"}' | base64)" \
-d '{"queryId":"demo-1"}'
# → 200 { settled: true, txHash, paid, explorer }Each settlement tx is single-use (replay-guarded).
Returns a self-contained JSON proof for any attested query — no payment,
no auth. It is rebuilt server-side from canonical sources every call:
the on-chain QueryRecord + CitationPaid events, plus the persisted
synthesis and its keccak256 digest.
curl https://kutip-zeta.vercel.app/api/receipt/<queryId>{
"kind": "kutip-attestation-receipt",
"queryId": "0x…",
"attestation": { "payer": "0x…", "totalPaidUSDC": "0.10",
"authorsShareUSDC": "0.08", "citationCount": 9, … },
"payouts": [ { "author": "0x…", "amountUSDC": "0.008888", "txHash": "0x…" }, … ],
"summary": { "query": "…", "synthesis": "…",
"summaryHash": "0x…", "summaryHashAlgo": "keccak256(utf8(synthesis))" }
}If the synthesis isn't in the warm cache the summary field carries a
note instead — the on-chain attestation block is always complete.
The mcp/ package bridges Kutip to the Model Context Protocol, so
any MCP client (Claude Desktop, Cursor, Cline) can call the agent as a
native tool.
| Tool | Purpose |
|---|---|
kutip.research(query, budgetUSDC) |
Run a query — authors paid, citations attested on-chain |
kutip.summary(queryId) |
Retrieve a past summary via the reverse-x402 paywall |
kutip.authors(limit, onlyClaimed) |
List claimed authors + wallets |
Setup is one block in claude_desktop_config.json — full instructions
and the schema in mcp/README.md.
GET /api/summaries/{queryId} serves a persisted summary behind a
reverse-x402 paywall. When another agent re-cites a Kutip answer it
pays Kutip, and Kutip forwards that to the original authors. This closes
the loop: Kutip pays humans → other agents pay Kutip → Kutip pays humans.
Summaries persist in Vercel Blob (summaries/<queryId>.json), so the
paywall and the receipt endpoint resolve across server restarts — not
just within a warm Lambda.
It is the same x402 spec used to buy papers, run in the other direction — Kutip as a paid data source for the wider agent economy.
git clone https://github.com/PugarHuda/kutip
cd kutip/web && npm install
cp ../.env.example .env # fill in the keys
npm run devContracts are already live on Kite testnet (see Deployment);
to redeploy your own, run the Foundry script in contracts/. Point
KUTIP_BASE_URL (MCP) or your API calls at the new deployment.
However you call Kutip, the agent contract holds:
- It never reads a paid paper without completing x402 payment first.
- It attests citations after summarising, before returning.
- The revenue split is one atomic on-chain transaction.
- It fails closed — if the attestation reverts, you get an error, never an unpaid summary.
See the Security model for what is enforced on-chain.
{ "query": "direct air capture cost reduction", // 5–500 chars "budgetUSDC": 0.5, // 0.1–20 (anonymous: ≤ 0.5) "yearFrom": 2020, // optional "yearTo": 2024, // optional "session": { /* optional signed SpendingIntent — see Architecture */ } }