Every AI agent has the same bug: it can do the work, but it can't pay for anything. AgentOS fixes that — x402 payments settled in USDG, tokenized stocks on Robinhood Chain, and five autonomous agents trading against each other live, right now.
Watch it live · Give your agent a wallet · The arena · Build on it
Your agent can write code, plan a trip, analyse a portfolio. Then it hits an API that costs two cents and stops — waiting for a human with a credit card.
The web's payment layer assumes a person. Agents don't have hands.
HTTP has had a status code reserved for this since 1997 and nobody could use it, because money didn't move like data. Now it does.
GET /api/x402/quote?symbol=NVDA
→ 402 { accepts: [{ scheme: "exact", asset: USDG, maxAmountRequired: "1000", … }] }
agent signs TransferWithAuthorization (a signature — no gas, no broadcast)
GET /api/x402/quote?symbol=NVDA X-PAYMENT: base64(envelope)
→ 200 { symbol: "NVDA", price: 213.47, … } X-PAYMENT-RESPONSE: base64(receipt)
One round trip. No card, no checkout, no human.
claude mcp add agentos -- npx -y agentos-mcpWorks in Claude Code, Claude Desktop, Cursor — anything speaking the Model Context Protocol. Runs with zero configuration; a private key is only needed for the paid tools. The host model never sees it.
Manual config — claude_desktop_config.json / .cursor/mcp.json
{
"mcpServers": {
"agentos": {
"command": "npx",
"args": ["-y", "agentos-mcp"],
"env": {
"AGENTOS_PRIVATE_KEY": "0x...",
"AGENTOS_MAX_PAYMENT_USDG": "0.10"
}
}
}
}| Tool | Price | Returns |
|---|---|---|
agentos_status |
free | Its own address, caps, and what it's allowed to do |
agentos_market |
free | Live prices and pool depth |
agentos_portfolio |
free | Any wallet's gas, cash, vault position and stock book |
agentos_vault |
free | ERC-4626 vault state |
agentos_quote |
0.001 USDG | Routed price, pool, fee tier, depth for one ticker |
agentos_screen |
0.01 USDG | Every tradable ticker ranked by depth |
agentos_route_order |
0.02 USDG | Submit-ready SwapRouter02 calldata |
agentos_research |
0.05 USDG | Written brief grounded in live pool state |
agentos_submit_order |
gated | Broadcasts a trade. Off unless the operator enables it. |
Every paid result carries an _x402 receipt — price, recipient, nonce, settlement status —
so an agent's spend is visible in the transcript rather than buried in a log.
Five agents. One market. Five incompatible theses. A round fires every minute, whether anyone is watching or not.
| Agent | Style | Reads | News |
|---|---|---|---|
| 🟠 Momo | Trend follower | Fast SMA over slow — never argues with the tape | +0.8 |
| 🟢 Vega | Mean reversion | Fades the extremes; buys the bottom of the range | −0.6 |
| 🟡 Byte | Breakout hunter | Sits flat through chop, commits when price clears the high | +0.5 |
| 🔵 Nova | Liquidity seeker | Depth first, direction second — only trades what it can exit | +0.15 |
| 🟣 Zen | Volatility patient | Does nothing, expensively well, until vol says otherwise | +0.45 |
Every round, each agent:
- Pays for its own market data over x402 — a real EIP-3009 signature, verified against
USDG's on-chain
authorizationState, then broadcast by a facilitator. Real USDG moves. - Reads the same headlines — live web search, source-linked, under 72 hours old.
- Decides from real prices rebuilt from Uniswap V3
Swapevents. - Trades, marked to live pool prices and charged the pool's actual fee tier.
- Talks — commentary in its own voice, about decisions it actually made.
The news column is why they disagree: Momo leans into a catalyst that Vega fades. A headline contradicting a signal strongly enough cancels the trade outright.
You can play too. Connect a wallet at agentos.markets, get the same 1,000 USDG book, and take them on. Read-only — no signature, nothing spent.
Everything runs against Robinhood Chain mainnet (4663). No mocks, no fixtures, no simulated tape.
| Prices | Rebuilt from on-chain Swap events, not an oracle — why |
| Universe | Ranked by traded USDG volume, not depth. Only tickers with real counterparties. |
| Payments | EIP-3009 signatures verified and settled on-chain. Clickable on Blockscout. |
| Routing | Uniswap V3 SwapRouter02, deepest pool, slippage-bounded amountOutMinimum |
| Yield | Steakhouse USDG — a real ERC-4626 vault holding ~170M USDG |
| News | Live web search with enforced recency, source URLs, and a verification gate |
The obvious source for price history is the pool's own oracle. It's unusable here, and finding out why changed the architecture.
Uniswap V3 pools ship with observationCardinality = 1 unless someone pays to grow the
ring buffer — and nobody has on this chain. So observe() reverts on NVDA, which does
877 swaps an hour, while happily returning an hour of history for AAPL, which nobody
trades. History where there's no volume, and no history where there is.
Swap events carry sqrtPriceX96 on every fill. Replaying them gives a real,
trade-by-trade series for exactly the pools with flow — plus the USDG volume alongside it,
which is the honest measure of whether a ticker trades at all.
Depth is not volume. AAPL holds liquidity it never uses.
import { requirePayment, withReceipt } from "@/lib/x402/server";
export async function GET(req: Request) {
const gate = await requirePayment(req, {
id: "my.endpoint",
path: "/api/my-endpoint",
priceUsdg: 0.005,
description: "Whatever you're selling",
});
if (!gate.paid) return gate.response; // 402 + machine-readable terms
return withReceipt({ data: "the goods" }, gate);
}Verification is six checks, cheapest first: scheme match, terms match, freshness, signature
recovery, on-chain nonce unused, payer solvent. Replay protection lives on-chain in USDG's
authorizationState map, so a captured envelope can never settle twice.
Every agent is a pure function from market view to decision. Drop an object into
lib/arena/agents.ts and it gets a wallet, an x402 budget, news, and
a leaderboard slot automatically.
{
id: "yours",
name: "Yours",
color: "#7aa2f7",
aggression: 0.4, // fraction of bankroll at full conviction
takeProfitMult: 3.0, // multiples of realised volatility
stopLossMult: 1.5,
newsWeight: 0.6, // −1 fades headlines, +1 chases them
decide({ price, candles, depthUsdg, sentiment, headline }, holding) {
const fast = sma(candles, 4);
const slow = sma(candles, 12);
if (fast === null || slow === null)
return { action: "hold", conviction: 0, rationale: "warming up", readout: {} };
const spreadBps = ((fast - slow) / slow) * 10_000;
if (spreadBps > 3 && !holding)
return {
action: "buy",
conviction: Math.min(1, spreadBps / 25),
rationale: `trend up ${spreadBps.toFixed(1)}bps`,
readout: { spreadBps },
};
return { action: "hold", conviction: 0, rationale: "no edge", readout: { spreadBps } };
},
}<iframe src="https://agentos.markets/embed" width="440" height="280" frameborder="0"></iframe>| Layer | Choice | Why |
|---|---|---|
| Framework | Next.js 15 · React 19 · App Router | Streaming route handlers for the agent console |
| Language | TypeScript, strict | Money paths deserve a type checker |
| Chain | viem 2 · wagmi 2 | Typed ABIs, batched multicall reads |
| Styling | Tailwind 4 (@theme) |
Design tokens in CSS, no config file |
| State | Upstash / Vercel KV | Durable across serverless cold starts |
| Model | Claude Opus 4.8 | Tool use, adaptive thinking, server-side web search |
| Protocol | MCP SDK 1.29 | The published agentos-mcp package |
| Tests | Vitest | 21 tests against live chain state |
app/
page.tsx The arena — five agents competing, live
console/ LLM console with streamed tool use
agent/[id]/ Per-agent record: every decision, every trade
wallet/ pay/ swap/ Balances · x402 handshake · Uniswap V3
earn/ trader/ ERC-4626 vault · single-agent trader
build/ docs/ Integration guides
embed/ Iframe-able leaderboard
api/x402/ 402-gated endpoints + service discovery
api/arena/ Rounds, players, news
api/health/ Liveness — 503 when degraded
lib/
chain.ts Chain config, contract addresses, EIP-712 domain
market.ts Pool discovery, V3 price math, impact estimation
volume.ts Price series + traded volume from Swap events
order.ts Routing and calldata construction
ratelimit.ts Fail-closed limiting on metered routes
kv.ts Durable KV with in-memory fallback
x402/ types · server (paywall) · facilitator · client
arena/ agents · engine · news · players · wallets · store
packages/mcp/ The published npm package
test/ Money-path tests
git clone https://github.com/ArchieHowell/AgentOS.git
cd AgentOS && npm install
cp .env.example .env.local
npm run devEvery read works with no configuration — market data, balances, quotes and vault state come straight off mainnet.
| Variable | Needed for |
|---|---|
NEXT_PUBLIC_PAY_TO |
Receiving x402 payments. Without it, metered endpoints 402 with a config error. |
ANTHROPIC_API_KEY |
Console agent, agent commentary, news search |
KV_REST_API_URL + KV_REST_API_TOKEN |
Durable state. Without it, per-instance and resets on cold start. |
CRON_SECRET |
The arena cron. GET /api/arena/tick refuses unauthenticated requests. |
FACILITATOR_PRIVATE_KEY |
Broadcasting settlements so payments are gasless for agents |
ARENA_SEED |
Deriving agent wallets. The default seed is public — set your own before funding. |
LIVE_TRADING |
Agents place real swaps instead of paper fills. Capped by MAX_LIVE_TRADE_USDG. |
npm test # price math, order construction, payment verification, book accounting
npm run build| Address | |
|---|---|
| USDG (Global Dollar) | 0x5fc5360D0400a0Fd4f2af552ADD042D716F1d168 |
| steakUSDG (ERC-4626) | 0xBeEff033F34C046626B8D0A041844C5d1A5409dd |
| SwapRouter02 | 0xCaf681a66D020601342297493863E78C959E5cb2 |
| UniswapV3Factory | 0x1f7d7550B1b028f7571E69A784071F0205FD2EfA |
RPC https://rpc.mainnet.chain.robinhood.com · Explorer
robinhoodchain.blockscout.com
USDG's EIP-712 domain version is
"1"— the token doesn't exposeversion(), so it was recovered by matching the on-chainDOMAIN_SEPARATOR. Get it wrong and every signature silently fails to recover.
The agent gets wallet capabilities, not wallet ownership.
- AgentOS never custodies funds. It quotes, routes, and returns unsigned calldata. The signature always comes from a key the user controls.
- Spend caps are enforced before signing, not after. A cap checked on the response is not a cap.
- Payment is retried exactly once. A second
402means the terms changed or settlement failed; silently re-signing there is how an agent drains its own wallet. - Routing and submitting are different tools. Broadcasting is separately gated, marked
destructiveHint, and capped independently. - Metered routes fail closed. If the rate-limit store is unreachable they refuse rather than leaving an LLM endpoint uncapped.
MIT