← Back to blog

Building a game-buying AI agent in 200 lines with x402 + MCP

Today an AI agent can read your email, search the web, summarize a PDF, and write code. Ask it to buy you a video game and it stops. Not because models are incapable — they aren't — but because the entire commerce layer was designed for human checkout: shopping carts, address forms, 3-D Secure popups, captchas. Agents bounce off it.

This post is a working example of the opposite: an agent that buys any of 40,000+ video game keys autonomously, paying in USDC on Base chain via an x402-style payment flow. End-to-end. No accounts, no API keys, no human in the loop. Total agent code: about 200 lines.

You can clone the repo and run it end-to-end on Base Sepolia testnet today: github.com/cdkbotmcp/cdk-agent-example.

The two-step problem with agent commerce

Real commerce has two integrations: find the product and pay for it. Today's APIs solve neither well for agents.

Most catalog APIs assume a human shopper: paginated lists, marketing copy in titles, regional pricing surfaced via geo-IP. An agent that needs to pick the cheapest legitimate offer for a specific platform and region is left to scrape and reconcile mismatched data itself.

Payment is worse. Stripe and friends were built around forms, billing addresses, 3-D Secure flows. Agents can't do those. Crypto sidesteps them but typically requires a wallet UX humans approve. Agent-native commerce needs payment that an agent can complete with zero human interaction and that the merchant can verify deterministically.

Two pieces fix this:

  • An API designed for agents — structured filters, one product per request, live competitor prices included in the response so the agent can rank without scraping.
  • A payment protocol that lives in HTTPx402. Server returns HTTP 402 with payment instructions, client sends crypto, server verifies onchain, returns the product.

What x402 actually does

x402 is the cleanest piece of agentic infrastructure we've seen in the last year. It's literally HTTP status 402 — "Payment Required" — combined with a structured response describing how to pay.

The full flow is three HTTP calls and one onchain transaction:

Agent                     Server                   Base chain
  │                         │                         │
  ├── POST /purchase ─────→  │                         │
  │   { "game_id": "..." }   │                         │
  │←── 402 + pay details ────┤  price locked (5 min)   │
  │   address, amount,       │                         │
  │   token, chain, quote_id │                         │
  │                          │                         │
  ├── ERC-20 transfer ─────────────────────────────→   │
  │   USDC to address        │                         │
  │                          │                         │
  ├── POST /purchase ─────→  │                         │
  │   + tx_hash + quote_id   │  verify on-chain ────→  │
  │                          │  (5 block confirmations) │
  │←── 200 { key_code } ─────┤                         │

No accounts. No webhooks. No callback URLs. The price is locked by quote_id for 5 minutes so it can't drift between browse and pay. The server extracts the sender wallet from the Transfer event and binds it to the order — so only the wallet that paid can fetch the key or request a refund.

One honest caveat for x402 purists: the canonical x402 "exact" scheme has the client sign an EIP-3009 authorization that a facilitator settles. Our flow is a little different — the agent broadcasts the USDC transfer itself and we verify it onchain. Same HTTP-402 shape, same no-human-in-the-loop result; it's an x402-style flow, not (yet) a literal implementation of the signed-authorization variant.

The agent code

The Python version of agent.py is below — minus boilerplate, this is the entire flow:

def buy(title: str) -> str:
    # 1. Find best product match
    product = requests.get(f"{API}/games/match",
        params={"title": title, "platform": "Steam"}).json()["best_match"]

    # 2. Get price quote (HTTP 402)
    quote = requests.post(f"{API}/purchase",
        json={"game_id": product["id"], "chain": "base-sepolia"}).json()

    # 3. Send USDC on-chain
    tx_hash = send_usdc_transfer(
        to=quote["payment"]["address"],
        amount=quote["payment"]["amount"],
    )

    # 4. Confirm + receive key
    result = requests.post(f"{API}/purchase", json={
        "game_id": product["id"],
        "tx_hash": tx_hash,
        "quote_id": quote["quote_id"],
        "chain": "base-sepolia",
    }).json()
    return result["key_code"]

That's it. Wrap it in asyncio or hand it to a tool-calling LLM and you have an agent that buys games. The TypeScript version is structurally identical and uses viem for the onchain transfer.

Three things that mattered more than we expected

1. Live competitor prices in the catalog response

Every GET /games/{id} response includes a marketplace_offers array — the cheapest current offer from major keyshops, in USD. Why ship competitor prices in our own response? Because an agent's first instinct is "is this a good price?" and without an answer it'll go scrape. Saving the agent that round trip is the difference between integration in 10 minutes and integration in two weeks.

The downstream effect: agents that integrate CDK can rank by price and seller trust across the whole market with one HTTP call. That's a feature that's almost impossible for a human-facing site to offer, because no human shopping site wants to surface its competitors. An agent-native API has different incentives.

2. Price locking via quote_id

Wholesale game prices change. If an agent quotes $24.99, sends $24.99 in USDC, and the price ticks to $25.49 between those two events, who eats the difference? With a quote_id issued on the 402 response and a 5-minute TTL, the answer is "neither, because the price is locked." Without it, every onchain confirmation becomes a haggling session.

3. The sender-wallet-binds-order rule

We extract the from address from the ERC-20 Transfer event and permanently bind it to the order. The key can only be retrieved by that wallet. Refunds can only be requested by that wallet. This kills an entire class of front-running attack: even if an attacker sees the tx_hash onchain, they can't claim the key because they don't have quote_id and they aren't the sending wallet.

Zero-code option: Claude Desktop + MCP

Not everyone wants to write 200 lines of Python. The same flow is wrapped in an MCP (Model Context Protocol) server with 8 tools, so you can just paste a config into Claude Desktop and chat with the catalog:

{
  "mcpServers": {
    "cdk": {
      "url": "https://mcp.cdk.bot/mcp",
      "transport": "streamable-http"
    }
  }
}

Restart Claude. Type "search for Hollow Knight Silksong and quote me a price." Claude calls search_games (the MCP equivalent of the REST /games/match call above) → get_price_quote natively. The same config works in Cursor and Cline. Zero install, no API keys.

What this unlocks

We think the more interesting question isn't "how do you write an agent that buys a game" — it's what new things become possible when commerce is HTTP-native and agent-readable. (We argued the bigger picture separately: why agents won't buy pizza first.)

  • A holiday-gift agent that buys 12 different Steam keys for your friends and emails them the codes — runs unattended overnight.
  • A market-watch agent that monitors price drops on a wishlist and buys when a threshold is hit.
  • A travel-companion agent that buys you a Netflix or Spotify gift card in the local currency when you land in a new country.
  • A budget-coach agent that pools spare USDC across multiple agents you run, deploying it against pre-approved categories.

None of these existed two years ago because the payment and product layers couldn't be agent-driven. They can now. The infrastructure is small enough — three HTTP calls, one wallet — that you can build them in a weekend.

Try it

Repo with Python + TypeScript + MCP examples: github.com/cdkbotmcp/cdk-agent-example. Defaults to Base Sepolia testnet so you can run end-to-end without spending real money — testnet USDC is free from Circle's faucet.

API docs and live OpenAPI spec: api.cdk.bot/docs. The agent discovery card lives at /.well-known/agent.json so other agents can find us programmatically.

Questions, weird use cases, requests for new product categories — info@cdk.bot.


Want to build on CDK? Start with the cdk-agent-example repo or read the API docs. Questions: info@cdk.bot.