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> 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
The same client for Python: usdf on PyPI. With a wallet, run pays each request per request. With USDF(api_key=...) it charges the prepaid balance instead.
One request
pip install usdf eth-account
import os
from eth_account import Account
from usdf import USDF
wallet = Account.from_key(os.environ["PAYER_KEY"])
client = USDF(wallet=wallet) # or USDF(api_key=os.environ["USDF_API_KEY"])
result = client.run("<model>", "Hello")@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. Chat completions and stateless Responses are served. With Responses, send the conversation in the input.
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);Messages API clients
Clients built on the Messages API (POST /v1/messages) take the same key: set their base URL to the gateway's.
Environment
export ANTHROPIC_BASE_URL=https://api.usdf.fi/v1 export ANTHROPIC_API_KEY="$USDF_API_KEY"
Claude Code speaks the Anthropic API. Give it the gateway's address without /v1 (it adds /v1/messages itself) and your key as its auth token, and name a model the gateway serves for every model it may ask for: models from GET /v1/models. Replace sk_YOUR_KEY with your key.
~/.claude/settings.json
{
"env": {
"ANTHROPIC_BASE_URL": "https://api.usdf.fi",
"ANTHROPIC_AUTH_TOKEN": "sk_YOUR_KEY",
"ANTHROPIC_MODEL": "MODEL_ID",
"ANTHROPIC_DEFAULT_OPUS_MODEL": "MODEL_ID",
"ANTHROPIC_DEFAULT_SONNET_MODEL": "MODEL_ID",
"ANTHROPIC_DEFAULT_HAIKU_MODEL": "MODEL_ID"
}
}Or set the same in the shell. The last line checks the setup; then start claude as usual. Leave ANTHROPIC_API_KEY unset: it would be sent in place of the token.
Shell
export ANTHROPIC_BASE_URL="https://api.usdf.fi" export ANTHROPIC_AUTH_TOKEN="sk_YOUR_KEY" export ANTHROPIC_MODEL="MODEL_ID" export ANTHROPIC_DEFAULT_OPUS_MODEL="MODEL_ID" export ANTHROPIC_DEFAULT_SONNET_MODEL="MODEL_ID" export ANTHROPIC_DEFAULT_HAIKU_MODEL="MODEL_ID" claude -p "Say hello in one word"
The MCP server's tools inside Claude Code:
MCP in Claude Code
claude mcp add --transport http usdf https://api.usdf.fi/mcp --header "Authorization: Bearer sk_YOUR_KEY"
Cursor
Cursor connects to the MCP server with its URL and your key. For its chat models, set the OpenAI base URL in its model settings to https://api.usdf.fi/v1 with your key, and add MODEL_ID.
~/.cursor/mcp.json
{
"mcpServers": {
"usdf": {
"url": "https://api.usdf.fi/mcp",
"headers": {
"Authorization": "Bearer sk_YOUR_KEY"
}
}
}
}The same through the mcp-remote bridge, for a client that runs local servers only:
~/.cursor/mcp.json, local bridge
{
"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_YOUR_KEY"
}
}
}
}Other tools
Any tool that takes a custom OpenAI-compatible provider works with these three values. Agents that read documentation can start at /llms.txt.
OpenAI-compatible provider
Base URL https://api.usdf.fi/v1 API key sk_YOUR_KEY Model MODEL_ID Docs https://api.usdf.fi/llms.txt
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 usdf package carries its own chat model, USDFChat, which pays per request from a wallet or charges a key.
USDFChat
pip install usdf langchain-core eth-account
import os
from eth_account import Account
from usdf.langchain import USDFChat
wallet = Account.from_key(os.environ["PAYER_KEY"])
llm = USDFChat(model="<model>", wallet=wallet) # or api_key="sk_..."
print(llm.invoke("Hello").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, billed for that output. Paid per call, the rest of the payment is refunded.
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. To pay per request from a wallet, use the SDK, which signs each payment.
Files an agent can read to find its way: