Wire draconic21 into a Vercel AI SDK agent
A tool() definition for the current Vercel AI SDK (ai@^7) that calls a paid draconic21 route and hides the whole 402-challenge/pay/retry dance from the model — it just sees a normal tool result.
A note on package choices
There is no dedicated @vercel/x402 package. Vercel's own reference template, x402-ai-starter, pins the older x402-fetch/x402-mcp v1 package line and wires payment through an MCP client wrapper (withPayment()). The example below instead uses the current x402 v2 @x402/fetch package (same one as the plain-TypeScript guide) directly inside an AI SDK tool(), which is the simpler shape when you're calling one specific paid HTTP route rather than routing through MCP.
Prerequisites
- A wallet on the Base network (chain id 8453) funded with a little USDC — a few dollars covers hundreds of calls at these prices.
- An x402 v2 client library (installed per snippet below) that can sign the "exact" EVM payment scheme (EIP-3009, gasless — no ETH for gas needed on your side).
- Your wallet's private key available to your own process/agent — never share it, never commit it, never paste it into a chat with an AI you don't fully trust with funds.
Install
npm install ai zod @x402/fetch @x402/core @x402/evm viem
The code
// Vercel AI SDK tool definition for a draconic21 x402 paid route.
//
// The `ai` package's tool() takes an `inputSchema` (current AI SDK, v7.x --
// older v3/v4 docs you may find online use `parameters`, which this version
// no longer accepts). The 402 -> sign -> pay -> retry dance is fully hidden
// from the model inside `fetchWithPayment`, built with the official
// `@x402/fetch` package -- the model just sees a normal tool result.
//
// Install:
// npm install ai zod @x402/fetch @x402/core @x402/evm viem
//
// This file exports a `sanctionsScreenTool` you can drop into any AI SDK
// `generateText`/`streamText` call's `tools: { sanctionsScreenTool }`.
//
// Standalone dry run (no LLM, no key -- prints the 402 challenge and stops):
// npx tsx tool.ts
// With a funded Base wallet, set BUYER_PRIVATE_KEY to let the tool actually
// pay when an agent calls it.
import { tool } from "ai";
import { z } from "zod";
import { x402Client } from "@x402/core/client";
import { wrapFetchWithPayment } from "@x402/fetch";
import { registerExactEvmScheme } from "@x402/evm/exact/client";
import { privateKeyToAccount } from "viem/accounts";
const ORIGIN = process.env.DRACONIC21_ORIGIN ?? "https://draconic21-x402-api.onrender.com";
// Build a payment-aware fetch once, only if a wallet is configured. Every
// call inside sanctionsScreenTool.execute reuses it (and therefore actually
// spends real USDC on Base each time it runs -- $0.006/call at today's
// price -- so gate this tool behind your own budget/approval logic same as
// any other paid resource).
function buildFetcher() {
const privateKey = process.env.BUYER_PRIVATE_KEY;
if (!privateKey) return null;
const account = privateKeyToAccount(privateKey as `0x${string}`);
const client = new x402Client();
registerExactEvmScheme(client, { signer: account });
return wrapFetchWithPayment(fetch, client);
}
export const sanctionsScreenTool = tool({
description:
"Screen a name or crypto wallet address against the U.S. Treasury OFAC sanctions list (SDN). " +
"Paid x402 call: $0.006 USDC on Base per lookup.",
inputSchema: z.object({
name: z.string().optional().describe("Person or entity name to screen"),
wallet: z.string().optional().describe("Crypto wallet address to screen"),
}),
execute: async ({ name, wallet }) => {
const fetchWithPayment = buildFetcher();
if (!fetchWithPayment) {
return {
error:
"No BUYER_PRIVATE_KEY configured -- this tool needs a Base wallet funded with USDC to pay the $0.006 x402 charge per call.",
};
}
const res = await fetchWithPayment(`${ORIGIN}/v1/sanctions_screen`, {
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify({ name, wallet }),
});
if (!res.ok) throw new Error(`sanctions_screen failed: ${res.status} ${await res.text()}`);
return res.json();
},
});
// Standalone dry run: shows the payment challenge without spending anything,
// so you can see what the tool would need to pay before wiring in a wallet.
async function dryRun() {
const res = await fetch(`${ORIGIN}/v1/sanctions_screen`, {
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify({ name: "Banco Nacional de Cuba" }),
});
console.log("Unpaid call status:", res.status, "(402 = tool would need BUYER_PRIVATE_KEY to proceed)");
}
if (import.meta.url === `file://${process.argv[1]}`) {
dryRun();
}
Drop sanctionsScreenTool into any generateText/streamText call's tools object. Because this tool spends real USDC every time an agent calls it, gate it behind your own budget or human-approval step the same way you would any other paid resource.
Routes you can call this way
Every example below targets one specific route to keep the snippet runnable, but the same client works against any draconic21 x402 route. Full list and schemas: /openapi.json.
| Route | Price | What it does |
|---|---|---|
POST /v1/sanctions_screen | $0.006 | OFAC SDN name/wallet screening |
POST /v1/sanctions_delta | $0.008 | OFAC SDN list changes since a date |
POST /v1/edgar_filings | $0.02 | Recent SEC filings by ticker/CIK |
POST /v1/edgar_company_facts | $0.02 | Key XBRL facts by ticker/CIK |
POST /v1/edgar_fulltext_search | $0.02 | Full-text search of SEC filings since 2001 |
POST /v1/signal_latest | $0.03 | Daily agent-economy signal brief |
POST /v1/signal_delta | $0.05 | What changed in Signal since a date |