UNPKG

agents

Version:
198 lines (196 loc) 6.06 kB
import { base, baseSepolia } from "viem/chains"; import { processPriceToAtomicAmount } from "x402/shared"; import { exact } from "x402/schemes"; import { useFacilitator } from "x402/verify"; import { createWalletClient, http } from "viem"; import { createPaymentHeader } from "x402/client"; //#region src/mcp/x402.ts function withX402(server, cfg) { const { verify, settle } = useFacilitator(cfg.facilitator); const x402Version = cfg.version ?? 1; function paidTool(name, description, priceUSD, paramsSchema, annotations, cb) { return server.registerTool(name, { description, inputSchema: paramsSchema, annotations, _meta: { "agents-x402/paymentRequired": true, "agents-x402/priceUSD": priceUSD } }, (async (args, extra) => { const atomic = processPriceToAtomicAmount(priceUSD, cfg.network); if ("error" in atomic) { const payload = { x402Version, error: "PRICE_COMPUTE_FAILED" }; return { isError: true, _meta: { "x402/error": payload }, content: [{ type: "text", text: JSON.stringify(payload) }] }; } const { maxAmountRequired, asset } = atomic; const requirements = { scheme: "exact", network: cfg.network, maxAmountRequired, payTo: cfg.recipient, asset: asset.address, maxTimeoutSeconds: 300, resource: `x402://${name}`, mimeType: "application/json", description, extra: "eip712" in asset ? asset.eip712 : void 0 }; const headers = extra?.requestInfo?.headers ?? {}; const token = extra?._meta?.["x402/payment"] ?? headers["X-PAYMENT"]; const paymentRequired = (reason = "PAYMENT_REQUIRED", extraFields = {}) => { const payload = { x402Version, error: reason, accepts: [requirements], ...extraFields }; return { isError: true, _meta: { "x402/error": payload }, content: [{ type: "text", text: JSON.stringify(payload) }] }; }; if (!token || typeof token !== "string") return paymentRequired(); let decoded; try { decoded = exact.evm.decodePayment(token); decoded.x402Version = x402Version; } catch { return paymentRequired("INVALID_PAYMENT"); } const vr = await verify(decoded, requirements); if (!vr.isValid) return paymentRequired(vr.invalidReason ?? "INVALID_PAYMENT", { payer: vr.payer }); let result; let failed = false; try { result = await cb(args, extra); if (result && typeof result === "object" && "isError" in result && result.isError) failed = true; } catch (e) { failed = true; result = { isError: true, content: [{ type: "text", text: `Tool execution failed: ${String(e)}` }] }; } if (!failed) try { const s = await settle(decoded, requirements); if (s.success) { result._meta ??= {}; result._meta["x402/payment-response"] = { success: true, transaction: s.transaction, network: s.network, payer: s.payer }; } else return paymentRequired(s.errorReason ?? "SETTLEMENT_FAILED"); } catch { return paymentRequired("SETTLEMENT_FAILED"); } return result; })); } Object.defineProperty(server, "paidTool", { value: paidTool, writable: false, enumerable: false, configurable: true }); return server; } const toChain = (network) => { switch (network) { case "base": return base; case "base-sepolia": return baseSepolia; default: throw new Error(`Unsupported network: ${network}`); } }; function withX402Client(client, x402Config) { const { network, account, version } = x402Config; const wallet = createWalletClient({ account, transport: http(), chain: toChain(network) }); const maxPaymentValue = x402Config.maxPaymentValue ?? BigInt(.1 * 10 ** 6); const _listTools = client.listTools.bind(client); const listTools = async (params, options) => { const toolsRes = await _listTools(params, options); toolsRes.tools = toolsRes.tools.map((tool) => { let description = tool.description; if (tool._meta?.["agents-x402/paymentRequired"]) { const cost = tool._meta?.["agents-x402/priceUSD"] ? `$${tool._meta?.["agents-x402/priceUSD"]}` : "an unknown amount"; description += ` (This is a paid tool, you will be charged ${cost} for its execution)`; } return { ...tool, description }; }); return toolsRes; }; const _callTool = client.callTool.bind(client); const callToolWithPayment = async (x402ConfirmationCallback, params, resultSchema, options) => { const res = await _callTool(params, resultSchema, options); console.log("res", res); const maybeX402Error = res._meta?.["x402/error"]; if (res.isError && maybeX402Error && maybeX402Error.accepts && Array.isArray(maybeX402Error.accepts) && maybeX402Error.accepts.length > 0) { const accepts = maybeX402Error.accepts; const confirmationCallback = x402ConfirmationCallback ?? x402Config.confirmationCallback; if (confirmationCallback && !await confirmationCallback(accepts)) return { isError: true, content: [{ type: "text", text: "User declined payment" }] }; const req = accepts.find((a) => a?.scheme === "exact" && a?.network === network) ?? accepts[0]; if (!req || req.scheme !== "exact") return res; const maxAmountRequired = BigInt(req.maxAmountRequired); if (maxAmountRequired > maxPaymentValue) return { isError: true, content: [{ type: "text", text: `Payment exceeds client cap: ${maxAmountRequired} > ${maxPaymentValue}` }] }; const token = await createPaymentHeader(wallet, version ?? 1, req); return _callTool({ ...params, _meta: { ...params._meta, "x402/payment": token } }, resultSchema, options); } return res; }; const _client = client; _client.listTools = listTools; Object.defineProperty(_client, "callTool", { value: callToolWithPayment, writable: false, enumerable: false, configurable: true }); return _client; } //#endregion export { withX402, withX402Client }; //# sourceMappingURL=x402.js.map