Skip to content

x402 Payments

Copy page

Some MCP tools charge per call. When a priced tool is invoked without payment, the server responds with 402 Payment Required and the SDK raises a PaymentRequiredError. You can let the SDK pay automatically (autopay) or catch the error and run your own flow (manual).

Both paths need a wallet implementing McpWallet — it signs a payment and returns a receipt; it does not broadcast (the facilitator settles server-side).

import { type McpWallet } from "@divinci-ai/mcp";
const wallet: McpWallet = {
async getAddress() {
return "0xYourWalletAddress";
},
async pay(details) {
// details: { amountUsd, amountMicro, recipient, network, toolName, facilitatorUrl }
const signed = await mySigner.signTransferAuthorization(details);
return {
transactionHash: signed.hash,
network: details.network,
recipient: details.recipient,
amountUsd: details.amountUsd,
timestamp: Date.now(),
signature: signed.signature,
};
},
};

Configure payment on the client with a wallet and a per-call cap. The SDK intercepts every 402, pays, and retries — callTool resolves with the eventual success result. Calls that would exceed maxPaymentUsd raise PaymentRequiredError instead of paying.

import { McpClient } from "@divinci-ai/mcp";
const mcp = new McpClient({
serverUrl: "https://mcp.divinci.app",
payment: {
autoPayment: true,
maxPaymentUsd: 1.0, // hard cap per call
network: "base",
wallet,
},
});
await mcp.connect();
// No per-call awareness needed — the SDK pays + retries automatically.
const result = await mcp.tools.callTool("premium_search", { query: "luxury watches" });
console.log(result.content);

You can also attach or change payment config after construction:

mcp.tools.setWallet(wallet);
mcp.tools.setPaymentOptions({ autoPayment: true, maxPaymentUsd: 0.5 });

Leave autoPayment off (or omit payment) and handle the error yourself — useful when you want a user to approve the spend, or the wallet lives elsewhere.

import { McpClient, PaymentRequiredError } from "@divinci-ai/mcp";
const mcp = new McpClient({ serverUrl: "https://mcp.divinci.app" });
await mcp.connect();
async function callWithApproval(name: string, args: Record<string, unknown>) {
try {
return await mcp.tools.callTool(name, args);
} catch (err) {
if (!(err instanceof PaymentRequiredError)) throw err;
const { amountUsd, network, toolName } = err.paymentDetails;
const ok = await confirmWithUser(`Pay $${amountUsd.toFixed(2)} on ${network} for ${toolName}?`);
if (!ok) throw new Error("User declined payment");
// Retry this call with a one-off autopay override
return await mcp.tools.callTool(name, args, {
payment: { autoPayment: true, maxPaymentUsd: amountUsd, network, wallet },
});
}
}
const result = await callWithApproval("premium_search", { query: "luxury watches" });
FieldTypeDescription
paymentDetails.amountUsdnumberRequired amount in USD.
paymentDetails.recipientstringWallet that must receive payment.
paymentDetails.networkstring"base" or "base-sepolia".
paymentDetails.toolNamestringThe tool that requires payment.
paymentDetails.facilitatorUrlstringSettlement facilitator endpoint.
const priced = (await mcp.tools.listTools()).filter((t) => t.pricing);
const price = await mcp.tools.getToolPrice("premium_search");