agents
Version:
A home for your AI agents
316 lines (315 loc) • 10.4 kB
JavaScript
import { n as normalizeCallToolArguments } from "../../invoker-BC1SZ3q1.js";
import { HTTPFacilitatorClient, x402ResourceServer } from "@x402/core/server";
import { x402Client } from "@x402/core/client";
import { registerExactEvmScheme } from "@x402/evm/exact/server";
import { registerExactEvmScheme as registerExactEvmScheme$1 } from "@x402/evm/exact/client";
//#region src/mcp/client/invoker-compat.ts
/**
* Bind the original methods before decorators such as x402 replace them. All
* generation-specific calling conventions stay behind this small interface.
*/
function bindMcpClient(client) {
if ("getProtocolEra" in client && typeof client.getProtocolEra === "function") {
const v2Client = client;
const listTools = v2Client.listTools.bind(v2Client);
const callTool = v2Client.callTool.bind(v2Client);
return {
listTools: (params, options) => listTools(params, options),
callTool: (params, schemaOrOptions, options) => {
const normalized = normalizeCallToolArguments(schemaOrOptions, options);
return normalized.schema ? v2Client.request({
method: "tools/call",
params
}, normalized.schema, normalized.options) : callTool(params, normalized.options);
}
};
}
const legacy = client;
const listTools = legacy.listTools.bind(legacy);
const callTool = legacy.callTool.bind(legacy);
return {
listTools: (params, options) => listTools(params, options),
callTool: (params, schemaOrOptions, options) => {
const normalized = normalizeCallToolArguments(schemaOrOptions, options);
return callTool(params, normalized.schema, normalized.options);
}
};
}
//#endregion
//#region src/mcp/client/x402.ts
/**
* Map of legacy v1 network names to CAIP-2 identifiers.
* Allows backward compatibility with v1 config.
*/
const LEGACY_NETWORK_MAP = {
"base-sepolia": "eip155:84532",
base: "eip155:8453",
ethereum: "eip155:1",
sepolia: "eip155:11155111"
};
/**
* Normalize a network identifier to CAIP-2 format.
* Accepts both legacy v1 names ("base-sepolia") and CAIP-2 ("eip155:84532").
*/
function normalizeNetwork(network) {
return LEGACY_NETWORK_MAP[network] ?? network;
}
function readHeader(headers, name) {
if (!headers) return void 0;
if (typeof headers.get === "function") return headers.get(name) ?? void 0;
const wanted = name.toLowerCase();
for (const [key, value] of Object.entries(headers)) {
if (key.toLowerCase() !== wanted) continue;
const first = Array.isArray(value) ? value[0] : value;
if (typeof first === "string") return first;
}
}
/**
* Read the x402 payment token from a tool handler context of either SDK
* generation. Request `_meta` wins over headers, and header names match
* case-insensitively. `PAYMENT-SIGNATURE` is x402 v2; `X-PAYMENT` is v1.
*/
function readPaymentToken(extra) {
const ctx = extra ?? {};
const meta = ctx.mcpReq?._meta ?? ctx._meta;
const headers = ctx.http?.req?.headers ?? ctx.requestInfo?.headers;
return meta?.["x402/payment"] ?? readHeader(headers, "PAYMENT-SIGNATURE") ?? readHeader(headers, "X-PAYMENT");
}
function withX402(server, cfg) {
const network = normalizeNetwork(cfg.network);
const resourceServer = new x402ResourceServer(new HTTPFacilitatorClient(cfg.facilitator ?? { url: "https://x402.org/facilitator" }));
registerExactEvmScheme(resourceServer);
let initPromise = null;
function ensureInitialized() {
if (!initPromise) initPromise = resourceServer.initialize().catch((err) => {
initPromise = null;
throw err;
});
return initPromise;
}
function paidTool(name, description, priceUSD, paramsSchema, annotations, toolCb) {
const cb = toolCb;
return server.registerTool(name, {
description,
inputSchema: paramsSchema,
annotations,
_meta: {
"agents-x402/paymentRequired": true,
"agents-x402/priceUSD": priceUSD
}
}, (async (args, extra) => {
await ensureInitialized();
const resourceConfig = {
scheme: "exact",
payTo: cfg.recipient,
price: priceUSD,
network,
maxTimeoutSeconds: 300
};
let requirements;
try {
requirements = await resourceServer.buildPaymentRequirements(resourceConfig);
} catch {
const payload = {
x402Version: 2,
error: "PRICE_COMPUTE_FAILED"
};
return {
isError: true,
_meta: { "x402/error": payload },
content: [{
type: "text",
text: JSON.stringify(payload)
}]
};
}
const resourceInfo = {
url: `x402://${name}`,
description,
mimeType: "application/json"
};
const token = readPaymentToken(extra);
const paymentRequired = (reason = "PAYMENT_REQUIRED", extraFields = {}) => {
const payload = {
x402Version: 2,
error: reason,
resource: resourceInfo,
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 paymentPayload;
try {
paymentPayload = JSON.parse(atob(token));
} catch {
return paymentRequired("INVALID_PAYMENT");
}
const matchingReq = resourceServer.findMatchingRequirements(requirements, paymentPayload);
if (!matchingReq) return paymentRequired("INVALID_PAYMENT");
try {
const vr = await resourceServer.verifyPayment(paymentPayload, matchingReq);
if (!vr.isValid) return paymentRequired(vr.invalidReason ?? "INVALID_PAYMENT", { payer: vr.payer });
} catch {
return paymentRequired("INVALID_PAYMENT");
}
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 resourceServer.settlePayment(paymentPayload, matchingReq);
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;
}
var PaymentCapError = class extends Error {};
var PaymentPassthroughError = class extends Error {};
function withX402Client(client, x402Config) {
const invoker = bindMcpClient(client);
const { account } = x402Config;
const maxPaymentValue = x402Config.maxPaymentValue ?? BigInt(1e5);
const paymentClient = new x402Client();
registerExactEvmScheme$1(paymentClient, { signer: account });
const selected = /* @__PURE__ */ new WeakSet();
paymentClient.onBeforePaymentCreation(async ({ paymentRequired, selectedRequirements }) => {
selected.add(paymentRequired);
const { scheme, amount } = selectedRequirements;
if (scheme !== "exact") throw new PaymentPassthroughError();
let value;
try {
value = BigInt(amount);
} catch {
throw new PaymentPassthroughError();
}
if (value < 0n) throw new PaymentPassthroughError();
if (value > maxPaymentValue) throw new PaymentCapError(`Payment exceeds client cap: ${value} > ${maxPaymentValue}`);
});
if (x402Config.network) {
const preferredNetwork = normalizeNetwork(x402Config.network);
paymentClient.registerPolicy((_version, reqs) => {
const matching = reqs.filter((r) => r.network === preferredNetwork);
return matching.length > 0 ? matching : reqs;
});
}
const listTools = async (params, options) => {
const toolsRes = await invoker.listTools(params, options);
return {
...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
};
})
};
};
const callToolWithPayment = async (x402ConfirmationCallback, params, schemaOrOptions, options) => {
const invoke = (callParams) => invoker.callTool(callParams, schemaOrOptions, options);
const res = await invoke(params);
const maybeX402Error = res._meta?.["x402/error"];
if (res.isError && maybeX402Error && maybeX402Error.accepts && Array.isArray(maybeX402Error.accepts) && maybeX402Error.accepts.length > 0) {
const accepts = structuredClone(maybeX402Error.accepts);
const confirmationCallback = x402ConfirmationCallback ?? x402Config.confirmationCallback;
if (confirmationCallback && !await confirmationCallback(accepts.map((req) => structuredClone(req)))) return {
isError: true,
content: [{
type: "text",
text: "User declined payment"
}]
};
const paymentRequiredResponse = {
x402Version: maybeX402Error.x402Version ?? 2,
resource: maybeX402Error.resource ?? {
url: "",
description: "",
mimeType: "application/json"
},
accepts,
extensions: maybeX402Error.extensions
};
let paymentPayload;
try {
paymentPayload = await paymentClient.createPaymentPayload(paymentRequiredResponse);
} catch (error) {
if (error instanceof PaymentPassthroughError || !selected.has(paymentRequiredResponse)) return res;
return {
isError: true,
content: [{
type: "text",
text: error instanceof PaymentCapError ? error.message : "Failed to create payment payload"
}]
};
}
const token = btoa(JSON.stringify(paymentPayload));
return invoke({
...params,
_meta: {
...params._meta,
"x402/payment": token
}
});
}
return res;
};
const _client = client;
Object.defineProperty(_client, "listTools", {
value: listTools,
writable: false,
enumerable: false,
configurable: true
});
Object.defineProperty(_client, "callTool", {
value: callToolWithPayment,
writable: false,
enumerable: false,
configurable: true
});
return _client;
}
//#endregion
export { normalizeNetwork, withX402, withX402Client };
//# sourceMappingURL=x402.js.map