Your agent pays for a tool call in 30 seconds

Checked live on Oct 17, 2026. The script in step 3 ran exactly as written with @x402/mcp 2.28.0 and a funded wallet at 11:57 AM CT and settled on Base (tx 0xfee8d931…9dfcd0).

What you need

1. Add the server

Streamable HTTP, no auth, no API key:

{
  "mcpServers": {
    "grokzilla-micro-pay": {
      "url": "https://grokzilla.shop/mcp"
    }
  }
}

Any MCP client can list the tools and call the two free ones, list_skills and get_skill. A paid tool returns a payment challenge until your client can pay. That's step 3.

2. Look before you pay (free)

Ask your client for tools/list. Each paid tool's description ends with its price, for example json_repair: "Price $0.0041 via x402."

3. Make one paid call

npm i @x402/mcp @x402/evm @modelcontextprotocol/sdk viem
// pay.mjs: run with PRIVATE_KEY=0x... node pay.mjs
import { createx402MCPClient } from "@x402/mcp";
import { ExactEvmScheme } from "@x402/evm/exact/client";
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
import { privateKeyToAccount } from "viem/accounts";

const account = privateKeyToAccount(process.env.PRIVATE_KEY);
const PAY_TO = "0xc22f9caebac37fe72d5142f35f21f6696ea9ef69";

const client = createx402MCPClient({
  name: "my-agent",
  version: "1.0.0",
  schemes: [{ network: "eip155:8453", client: new ExactEvmScheme(account) }],
  // Pay only our address, only USDC on Base, at most 2 cents a call.
  policies: [(_v, reqs) => reqs.filter(r =>
    r.network === "eip155:8453" &&
    r.payTo.toLowerCase() === PAY_TO &&
    BigInt(r.amount) <= 20_000n)],
  onPaymentRequested: ({ toolName, paymentRequired }) => {
    console.log(`${toolName}: paying ${paymentRequired.accepts[0].amount} atomic USDC`);
    return true;
  },
});

await client.connect(new StreamableHTTPClientTransport(new URL("https://grokzilla.shop/mcp")));

const result = await client.callTool("json_repair", { text: "{a: 1, b: [2,3,],}" });
console.log(result.content[0].text);
console.log("tx:", result.paymentResponse?.transaction);

What you get back

The actual output of the run above:

json_repair: paying 4100 atomic USDC
{"skill": "json_repair", "ok": true, "json": {"a": 1, "b": [2, 3]}, "repaired": "{\"a\":1,\"b\":[2,3]}", "fixes": ["unquoted_keys", "trailing_commas"], "changed": true}
tx: 0xfee8d9319effe4eac28e91d3f3c484aed22356846b0db0ea2d758882999dfcd0

What happened:

The first call gets a challenge for 4100 atomic USDC ($0.0041) to 0xc22f9CAEBAc37fE72D5142f35f21f6696Ea9Ef69 on eip155:8453. The client signs it and retries for you. Then you get the repaired JSON. The example in the challenge, for a messier input, looks like this:

{"skill":"json_repair","ok":true,"json":{"name":"Ada","tags":["math","code"],"active":true},
 "repaired":"{\"name\":\"Ada\",\"tags\":[\"math\",\"code\"],\"active\":true}",
 "fixes":["unquoted_keys","single_quotes","trailing_commas","python_literals","removed_comments"],"changed":true}

result.paymentResponse holds success, payer, transaction and network. transaction is the Base transaction hash, so you can check the payment on-chain.

If the call fails, you get isError: true. Only count it as paid if result.paymentResponse?.transaction is set: the client also reports paymentMade: true when it signed but nothing settled. Bad arguments and a payment that can't be verified both come back this way, unsettled.

For production, also check scheme and asset as shown in spend limits.

More: paid call, step by step · spend limits · MCP quickstart