DEV Community

FreeTier
FreeTier

Posted on

Give your AI agent paid tools with x402 and MCP: a working example

Disclosure: I run the API used in this example (FreeTier Agent API), so treat this as a vendor tutorial. The article was drafted with AI assistance and checked by hand. Every output below was captured from the live service on 2026-10-25; nothing is made up.

What this shows

Many agent tools are small lookups: "does this domain have DMARC?", "when does this cert expire?", "what are the coordinates of this address?". Building and hosting each one yourself is overkill; paying a monthly SaaS plan for 20 calls is too. x402 is an HTTP-native way to pay per call, and MCP is how assistants like Claude and Cursor discover tools. This post connects the two with a real endpoint you can try for free.

x402 in one paragraph

The server answers an unpaid request with HTTP 402 Payment Required plus machine-readable payment terms. An x402 client signs a USDC transfer authorization on Base (no gas needed by the payer; a facilitator settles it) and repeats the request with a PAYMENT-SIGNATURE header. The server verifies, does the work, settles, and returns the result.

Here is the real 402 challenge for an SSL-certificate lookup (decoded from the PAYMENT-REQUIRED header, trimmed):

curl -si "https://freetier-agent-api.pages.dev/api/ssl-cert?domain=dev.to&nofree=1"
Enter fullscreen mode Exit fullscreen mode
{
 "scheme": "exact",
 "network": "eip155:8453",
 "amount": "5000",
 "asset": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
 "payTo": "0x165Eb3dC3E98403ecB1141e90400F5e71806554B",
 "maxTimeoutSeconds": 300
}
Enter fullscreen mode Exit fullscreen mode

amount is in USDC base units (6 decimals), so 5000 = $0.005. nofree=1 skips the free trial so you can see the challenge.

Connect the MCP server

The same routes are exposed as MCP tools at https://freetier-agent-api.pages.dev/mcp (Streamable HTTP).

Claude Code

claude mcp add --transport http freetier https://freetier-agent-api.pages.dev/mcp
Enter fullscreen mode Exit fullscreen mode

Cursor (~/.cursor/mcp.json)

{ "mcpServers": { "freetier": { "url": "https://freetier-agent-api.pages.dev/mcp" } } }
Enter fullscreen mode Exit fullscreen mode

Claude Desktop (claude_desktop_config.json, via the mcp-remote bridge)

{ "mcpServers": { "freetier": { "command": "npx", "args": ["-y", "mcp-remote", "https://freetier-agent-api.pages.dev/mcp"] } } }
Enter fullscreen mode Exit fullscreen mode

Full copy-paste recipes, including how to add a paid key: https://freetier-agent-api.pages.dev/recipes

Then ask, for example: "Check the DNS, SPF and DMARC records for dev.to and tell me whether its mail setup has any warnings."

A working Node example (no SDK)

MCP over HTTP is JSON-RPC, so plain fetch works (Node 18+):

// mcp_call.mjs
const MCP = "https://freetier-agent-api.pages.dev/mcp";
const rpc = async (method, params, id = 1) => {
  const r = await fetch(MCP, { method: "POST",
    headers: { "Content-Type": "application/json", Accept: "application/json, text/event-stream" },
    body: JSON.stringify({ jsonrpc: "2.0", id, method, params }) });
  return r.json();
};
const res = await rpc("tools/call", { name: "dns", arguments: { domain: "dev.to" } });
if (res.result.isError) console.log("Payment required:", JSON.stringify(res.result.structuredContent.accepts[0], null, 2));
else console.log(res.result.content[0].text);
Enter fullscreen mode Exit fullscreen mode

Real output of node mcp_call.mjs (this was the free call; TXT records trimmed):

{
 "domain": "dev.to",
 "status": "NOERROR",
 "exists": true,
 "dnssec_validated": false,
 "records": {
  "A": [{ "data": "151.101.66.217", "ttl": 160 }, "... 3 more"],
  "MX": [{ "data": "1 smtp.google.com.", "ttl": 300, "priority": 1, "host": "smtp.google.com" }],
  "NS": [{ "data": "jill.ns.cloudflare.com.", "ttl": 86400 }, { "data": "josh.ns.cloudflare.com.", "ttl": 86400 }]
 },
 "mail": {
  "accepts_mail": true,
  "mx_hosts": ["smtp.google.com"],
  "spf": "v=spf1 a mx include:_spf.google.com include:sendgrid.net include:servers.mcsv.net include:shops.shopify.com ~all",
  "spf_all": "~all",
  "spf_multiple": false,
  "dmarc_policy": "reject",
  "dmarc_pct": 100,
  "warnings": []
 },
 "resolver": "cloudflare"
}
Enter fullscreen mode Exit fullscreen mode

The second call from the same caller gets the payment branch instead: isError: true with the x402 PaymentRequired object in structuredContent. An x402-aware MCP client signs it and repeats the call with params._meta["x402/payment"]; the receipt comes back in result._meta["x402/payment-response"].

Paying over plain HTTP

With a dedicated, low-balance Base wallet holding a little USDC:

// npm i @x402/fetch @x402/evm viem
import { wrapFetchWithPaymentFromConfig } from "@x402/fetch";
import { ExactEvmScheme } from "@x402/evm";
import { privateKeyToAccount } from "viem/accounts";

const account = privateKeyToAccount(process.env.EVM_PRIVATE_KEY);
const fetchWithPayment = wrapFetchWithPaymentFromConfig(fetch, {
  schemes: [{ network: "eip155:8453", client: new ExactEvmScheme(account) }],
});
const res = await fetchWithPayment("https://freetier-agent-api.pages.dev/api/dns?domain=dev.to");
console.log(await res.json());
Enter fullscreen mode Exit fullscreen mode

I am not pasting a paid response here because it is the same JSON as above plus a payment receipt header; I did not want to show anything I had not run for this post.

Limits (read these)

  • 1 free call per route per caller. Callers are identified by a hash of IP + user agent, so it is a trial, not a quota.
  • Costs: dns $0.0025, domain-whois $0.005, ssl-cert $0.005, geocode $0.0015 per call. Failed calls (400/502) are not charged.
  • Data sources: DNS via Cloudflare/Google DNS-over-HTTPS; WHOIS via official RDAP servers (some ccTLDs such as .de and .dk have no public RDAP and return a clear error); SSL from Certificate Transparency logs (shows issued certs, not what a server is serving right now); geocoding from OpenStreetMap Nominatim (rate-limited to 1 req/s, cached, ODbL attribution in every response).
  • Desktop chat apps do not sign x402 payments. In Claude Desktop/Cursor you either stay on free calls or use a prepaid card key (X-License-Key header); wallet payments need an x402 client like the one above.
  • Use a separate wallet with a small balance for any agent that can spend.

Resources

Top comments (0)