Agents and integrations
SDKs, MCP and frameworks.
Pay and call from TypeScript, the AI SDK, any OpenAI-compatible client, MCP, or the command line.
On this page
The USDF SDK pays per request from a wallet, with no account, or charges a key. It also reads receipts, quotes and the account. Any OpenAI chat completions client works with a key.
| Client | Install | Pays with | Fits |
|---|---|---|---|
@usdf/sdk | npm install @usdf/sdk viem | A wallet, per request, or a key | Apps and agents in TypeScript |
@usdf/sdk/ai | npm install @usdf/sdk ai | A wallet or a key | The AI SDK |
openai, TypeScript and Python | npm install openai, pip install openai | A key | Existing OpenAI code |
| LangChain | pip install langchain-openai, npm install @langchain/openai | A key | LangChain apps |
| MCP | Nothing: a URL | A key, or a payment per call | MCP clients and agents |
| Command line | npx @usdf/sdk | A key | The terminal |
Samples use <model>, the first available chat model, and read the key from USDF_API_KEY.
With a wallet, run pays each request per request. With new USDF({ apiKey }) it charges the prepaid balance instead.
One request
npm install @usdf/sdk viem
import { USDF } from "@usdf/sdk";
import { privateKeyToAccount } from "viem/accounts";
const wallet = privateKeyToAccount(process.env.PAYER_KEY as `0x${string}`);
const usdf = new USDF({ wallet }); // or new USDF({ apiKey: process.env.USDF_API_KEY })
const result = await usdf.run("<model>", "Hello");The client's methods:
| Method | What it does |
|---|---|
run(model, prompt) | One prompt, returning { text, receipt }. Streams with onText. |
chat.completions.create | A chat completion, plain or streamed, with its receipt. |
embeddings.create | Embeddings. |
batches.create | A batch job; get, result and wait follow it. |
quote, balance | The worst case of a request before it runs, and what is left. |
models.list, pricing | The models serving now, and the full price sheet. |
tools.list, tools.call | Tools on the same balance. |
receipts.get, receipts.list, receipts.attestation | Receipts, and the provider's signed receipt for a private request. |
keys.limits, update, pause, resume | A key's caps, allowlist, pause and flags. |
account.* | The overview, activity, settings and analytics. |
links.* | USDF links: create, list, get, claim, cancel. |
agents.* | Create, fund, limit and pay agents. |
zap.quote, pool.read | Pay with any token, and the USDF/USDG pool. |
It reads these environment variables:
| Variable | Meaning |
|---|---|
USDF_API_KEY | The API key, when no wallet is given. |
USDF_BASE_URL | The gateway. Default https://api.usdf.fi. |
USDF_SESSION_TOKEN | A session, for account calls. |
USDF_RPC_URL | The chain RPC the SDK reads the Permit2 allowance from. |
USDF_MAX_AMOUNT | The most it signs for one request, in units. Default 1000000, 1 USDF. A larger quote is refused before signing, with amount_over_limit: raise it, or lower max_tokens. |
USDF_PAY_TO | The payee it pays, when not the gateway's own wallet. |
The SDK signs only on the gateway's chain, in USDF or USDG, to the gateway's wallet, up to its maximum amount. Anything else in a 402 is refused before signing.
npm: @usdf/sdk@0.1.1
@usdf/sdk/ai is a provider for the AI SDK. usdf(model) charges USDF_API_KEY.
Generate text
npm install @usdf/sdk ai
import { generateText } from "ai";
import { usdf } from "@usdf/sdk/ai"; // charges USDF_API_KEY; createUSDF({ wallet }) from "@usdf/sdk/ai" pays per request from a wallet
const model = usdf("<model>");
const result = await generateText({ model, prompt: "Hello" });createUSDF({ wallet }) pays per request from a wallet instead. The receipt is at providerMetadata.usdf.receipt.
Point any OpenAI client at https://api.usdf.fi/v1 with a key.
The openai package
npm install openai
import OpenAI from "openai";
const client = new OpenAI({
baseURL: "https://api.usdf.fi/v1",
apiKey: process.env.USDF_API_KEY,
});
const completion = await client.chat.completions.create({
model: "<model>",
messages: [{ role: "user", content: "Hello" }],
max_tokens: 256,
});
console.log(completion.choices[0].message.content);
// The receipt is an extra field the SDK's types do not declare.
const { receipt } = completion as unknown as {
receipt: { request_id: string; cost: { usdf: string } };
};
console.log(receipt.request_id, receipt.cost.usdf, "USDF");The AI SDK's OpenAI-compatible provider
Works with a key. With the @ai-sdk/openai provider instead, call .chat(…): its default model uses the Responses API, which is not served.
createOpenAICompatible
npm install @ai-sdk/openai-compatible@3.0.54 ai@7.0.112
import { createOpenAICompatible } from "@ai-sdk/openai-compatible";
import { generateText } from "ai";
const usdf = createOpenAICompatible({
name: "usdf",
baseURL: "https://api.usdf.fi/v1",
apiKey: process.env.USDF_API_KEY,
});
const { text, response } = await generateText({
model: usdf("<model>"),
prompt: "Hello",
maxOutputTokens: 256,
});
console.log(text, response.id);ChatOpenAI reaches the gateway with a key and the base URL.
ChatOpenAI
npm install @langchain/openai
import { ChatOpenAI } from "@langchain/openai";
const llm = new ChatOpenAI({
model: "<model>",
apiKey: process.env.USDF_API_KEY,
maxTokens: 256,
configuration: { baseURL: "https://api.usdf.fi/v1" },
});
const reply = await llm.invoke("Hello");
console.log(reply.content);The MCP server is at POST https://api.usdf.fi/mcp. It speaks stateless streamable HTTP: one JSON-RPC message per POST, and no session.
Its tools, read live:
| Tool | What it does | Access |
|---|---|---|
| — | — | — |
Every tool works with a key. Without one, three behave differently:
| Tool | With a key | With no key |
|---|---|---|
chat | Charged to the key's balance. | Paid per call. The payment requirements come back as an isError result; pay with _meta["x402/payment"], a payment argument, or the PAYMENT-SIGNATURE header. |
receipt | The key's own requests. | The public receipt of any request id. |
balance | The key's balance and budget. | With { address }: that wallet's USDF, USDG and Permit2 allowance. |
list_models, quote, list_tools, quote_tool | The same. | The same. |
Limits
chat is not streamed to the client, so its limits keep an answer inside a client's wait:
| Limit | Value |
|---|---|
chat max_tokens | 4096 at most |
| Deadline | 55 seconds; 10 minutes with a progress token. In the TypeScript SDK, pass onprogress with resetTimeoutOnProgress: true. |
| Messages | One JSON-RPC message per POST. Batches are refused. |
| Request body | 256 KiB |
| Response | 1 MiB |
| Rate limit | — |
A call cut at its deadline returns the text produced so far. With a key, it is billed for that output. Paid per call, the payment stands.
Client configuration
Name the server usdf. A client that takes a URL and headers reads the key from the environment:
Clients that take a URL and headers
{
"mcpServers": {
"usdf": {
"url": "https://api.usdf.fi/mcp",
"headers": { "Authorization": "Bearer ${env:USDF_API_KEY}" }
}
}
}A client that runs local servers reaches it through the mcp-remote bridge:
Clients that run local servers
{
"mcpServers": {
"usdf": {
"command": "npx",
"args": ["-y", "mcp-remote@0.14.3", "https://api.usdf.fi/mcp", "--header", "Authorization:${USDF_AUTH}"],
"env": { "USDF_AUTH": "Bearer sk_..." }
}
}
}From code
The MCP TypeScript SDK
npm install @modelcontextprotocol/sdk@1.30.0
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
const transport = new StreamableHTTPClientTransport(new URL("https://api.usdf.fi/mcp"), {
requestInit: { headers: { Authorization: `Bearer ${process.env.USDF_API_KEY}` } },
});
const client = new Client({ name: "my-agent", version: "1.0.0" });
await client.connect(transport);
const { tools } = await client.listTools();
console.log(tools.map((t) => t.name));
const quote = await client.callTool({
name: "quote",
arguments: { model: "<model>", prompt: "Hello", max_tokens: 256 },
});
console.log(JSON.stringify(quote.structuredContent));
// With onprogress the SDK sends a progress token: the gateway reports progress
// as it works and allows the call up to 10 minutes. resetTimeoutOnProgress is
// what makes the client keep waiting.
const chat = await client.callTool(
{ name: "chat", arguments: { model: "<model>", messages: [{ role: "user", content: "Hello" }], max_tokens: 256 } },
undefined,
{ timeout: 60_000, resetTimeoutOnProgress: true, onprogress: (p) => console.log("still generating", p.progress) },
);
console.log(JSON.stringify(chat.structuredContent));
await client.close();The SDK ships one command, usdf. Run it with npx @usdf/sdk.
npx @usdf/sdk balance npx @usdf/sdk keys npx @usdf/sdk run <model> "Hello" --cost npx @usdf/sdk receipts # installed with npm install -g @usdf/sdk, the command is usdf: usdf balance
The commands:
| Command | What it does |
|---|---|
login | Stores a key (--key sk_…) or a session (--token - reads it from stdin) for the gateway. |
logout | Forgets the stored session. |
balance | What is available, held and left on the key. |
keys [list|create|revoke|limits|pause|resume] | Manages keys. |
run <model> "<prompt>" [--cost] | Streams an answer. --cost prints the receipt's cost. |
receipts, receipt <id> | Lists receipts, or shows one. |
pricing, models, quote, auth | Reads the sheet, the models, a quote and the sign-in setup. |
pool | The USDF/USDG pool: price, tick and reserves. No key. |
Every command takes --json. The command signs nothing, so it cannot pay per request; use the SDK for that.