@x402/mcp 2.3.0-alpha
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +351 -0
- package/dist/cjs/index.d.ts +971 -0
- package/dist/cjs/index.js +826 -0
- package/dist/cjs/index.js.map +1 -0
- package/dist/esm/index.d.mts +971 -0
- package/dist/esm/index.mjs +782 -0
- package/dist/esm/index.mjs.map +1 -0
- package/package.json +64 -0
|
@@ -0,0 +1,826 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var src_exports = {};
|
|
22
|
+
__export(src_exports, {
|
|
23
|
+
MCP_PAYMENT_META_KEY: () => MCP_PAYMENT_META_KEY,
|
|
24
|
+
MCP_PAYMENT_REQUIRED_CODE: () => MCP_PAYMENT_REQUIRED_CODE,
|
|
25
|
+
MCP_PAYMENT_RESPONSE_META_KEY: () => MCP_PAYMENT_RESPONSE_META_KEY,
|
|
26
|
+
attachPaymentResponseToMeta: () => attachPaymentResponseToMeta,
|
|
27
|
+
attachPaymentToMeta: () => attachPaymentToMeta,
|
|
28
|
+
createPaymentRequiredError: () => createPaymentRequiredError,
|
|
29
|
+
createPaymentWrapper: () => createPaymentWrapper,
|
|
30
|
+
createToolResourceUrl: () => createToolResourceUrl,
|
|
31
|
+
createx402MCPClient: () => createx402MCPClient,
|
|
32
|
+
extractPaymentFromMeta: () => extractPaymentFromMeta,
|
|
33
|
+
extractPaymentRequiredFromError: () => extractPaymentRequiredFromError,
|
|
34
|
+
extractPaymentResponseFromMeta: () => extractPaymentResponseFromMeta,
|
|
35
|
+
isPaymentRequiredError: () => isPaymentRequiredError,
|
|
36
|
+
wrapMCPClientWithPayment: () => wrapMCPClientWithPayment,
|
|
37
|
+
wrapMCPClientWithPaymentFromConfig: () => wrapMCPClientWithPaymentFromConfig,
|
|
38
|
+
x402Client: () => import_client4.x402Client,
|
|
39
|
+
x402MCPClient: () => x402MCPClient,
|
|
40
|
+
x402ResourceServer: () => import_server2.x402ResourceServer
|
|
41
|
+
});
|
|
42
|
+
module.exports = __toCommonJS(src_exports);
|
|
43
|
+
|
|
44
|
+
// src/client/x402MCPClient.ts
|
|
45
|
+
var import_schemas = require("@x402/core/schemas");
|
|
46
|
+
var import_client = require("@x402/core/client");
|
|
47
|
+
var import_client2 = require("@modelcontextprotocol/sdk/client/index.js");
|
|
48
|
+
|
|
49
|
+
// src/utils/encoding.ts
|
|
50
|
+
function isObject(value) {
|
|
51
|
+
return typeof value === "object" && value !== null;
|
|
52
|
+
}
|
|
53
|
+
function isPaymentPayloadStructure(value) {
|
|
54
|
+
if (!isObject(value)) {
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
return "x402Version" in value && "payload" in value;
|
|
58
|
+
}
|
|
59
|
+
function isSettleResponseStructure(value) {
|
|
60
|
+
if (!isObject(value)) {
|
|
61
|
+
return false;
|
|
62
|
+
}
|
|
63
|
+
return "success" in value;
|
|
64
|
+
}
|
|
65
|
+
function isPaymentRequiredStructure(value) {
|
|
66
|
+
if (!isObject(value)) {
|
|
67
|
+
return false;
|
|
68
|
+
}
|
|
69
|
+
return "x402Version" in value && "accepts" in value && Array.isArray(value.accepts);
|
|
70
|
+
}
|
|
71
|
+
function extractPaymentFromMeta(params) {
|
|
72
|
+
if (!(params == null ? void 0 : params._meta)) {
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
const payment = params._meta[MCP_PAYMENT_META_KEY];
|
|
76
|
+
if (!isPaymentPayloadStructure(payment)) {
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
79
|
+
return payment;
|
|
80
|
+
}
|
|
81
|
+
function attachPaymentToMeta(params, paymentPayload) {
|
|
82
|
+
return {
|
|
83
|
+
...params,
|
|
84
|
+
_meta: {
|
|
85
|
+
[MCP_PAYMENT_META_KEY]: paymentPayload
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
function extractPaymentResponseFromMeta(result) {
|
|
90
|
+
if (!(result == null ? void 0 : result._meta)) {
|
|
91
|
+
return null;
|
|
92
|
+
}
|
|
93
|
+
const response = result._meta[MCP_PAYMENT_RESPONSE_META_KEY];
|
|
94
|
+
if (!isSettleResponseStructure(response)) {
|
|
95
|
+
return null;
|
|
96
|
+
}
|
|
97
|
+
return response;
|
|
98
|
+
}
|
|
99
|
+
function attachPaymentResponseToMeta(result, settleResponse) {
|
|
100
|
+
return {
|
|
101
|
+
...result,
|
|
102
|
+
_meta: {
|
|
103
|
+
[MCP_PAYMENT_RESPONSE_META_KEY]: settleResponse
|
|
104
|
+
}
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
function createPaymentRequiredError(paymentRequired, message) {
|
|
108
|
+
return {
|
|
109
|
+
code: MCP_PAYMENT_REQUIRED_CODE,
|
|
110
|
+
message: message || "Payment required",
|
|
111
|
+
data: paymentRequired
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
function extractPaymentRequiredFromError(error) {
|
|
115
|
+
if (!isObject(error)) {
|
|
116
|
+
return null;
|
|
117
|
+
}
|
|
118
|
+
if (error.code !== MCP_PAYMENT_REQUIRED_CODE) {
|
|
119
|
+
return null;
|
|
120
|
+
}
|
|
121
|
+
const data = error.data;
|
|
122
|
+
if (!isPaymentRequiredStructure(data)) {
|
|
123
|
+
return null;
|
|
124
|
+
}
|
|
125
|
+
return data;
|
|
126
|
+
}
|
|
127
|
+
function createToolResourceUrl(toolName, customUrl) {
|
|
128
|
+
if (customUrl) {
|
|
129
|
+
return customUrl;
|
|
130
|
+
}
|
|
131
|
+
return `mcp://tool/${toolName}`;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// src/types/mcp.ts
|
|
135
|
+
var MCP_PAYMENT_REQUIRED_CODE = 402;
|
|
136
|
+
var MCP_PAYMENT_META_KEY = "x402/payment";
|
|
137
|
+
var MCP_PAYMENT_RESPONSE_META_KEY = "x402/payment-response";
|
|
138
|
+
function isPaymentRequiredError(error) {
|
|
139
|
+
if (!isObject(error)) {
|
|
140
|
+
return false;
|
|
141
|
+
}
|
|
142
|
+
if (error.code !== MCP_PAYMENT_REQUIRED_CODE || typeof error.message !== "string") {
|
|
143
|
+
return false;
|
|
144
|
+
}
|
|
145
|
+
if (!isObject(error.data)) {
|
|
146
|
+
return false;
|
|
147
|
+
}
|
|
148
|
+
return "x402Version" in error.data && "accepts" in error.data;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// src/client/x402MCPClient.ts
|
|
152
|
+
function isMCPTextContent(content) {
|
|
153
|
+
return content.type === "text" && typeof content.text === "string";
|
|
154
|
+
}
|
|
155
|
+
function isMCPCallToolResult(result) {
|
|
156
|
+
if (typeof result !== "object" || result === null) {
|
|
157
|
+
return false;
|
|
158
|
+
}
|
|
159
|
+
const obj = result;
|
|
160
|
+
return Array.isArray(obj.content);
|
|
161
|
+
}
|
|
162
|
+
var x402MCPClient = class {
|
|
163
|
+
/**
|
|
164
|
+
* Creates a new x402MCPClient instance.
|
|
165
|
+
*
|
|
166
|
+
* @param mcpClient - The underlying MCP client instance
|
|
167
|
+
* @param paymentClient - The x402 client for creating payment payloads
|
|
168
|
+
* @param options - Configuration options
|
|
169
|
+
*/
|
|
170
|
+
constructor(mcpClient, paymentClient, options = {}) {
|
|
171
|
+
this.paymentRequiredHooks = [];
|
|
172
|
+
this.beforePaymentHooks = [];
|
|
173
|
+
this.afterPaymentHooks = [];
|
|
174
|
+
this.mcpClient = mcpClient;
|
|
175
|
+
this._paymentClient = paymentClient;
|
|
176
|
+
this.options = {
|
|
177
|
+
autoPayment: options.autoPayment ?? true,
|
|
178
|
+
onPaymentRequested: options.onPaymentRequested ?? (() => true)
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Get the underlying MCP client instance.
|
|
183
|
+
*
|
|
184
|
+
* @returns The MCP client instance
|
|
185
|
+
*/
|
|
186
|
+
get client() {
|
|
187
|
+
return this.mcpClient;
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* Get the underlying x402 payment client instance.
|
|
191
|
+
*
|
|
192
|
+
* @returns The x402 client instance
|
|
193
|
+
*/
|
|
194
|
+
get paymentClient() {
|
|
195
|
+
return this._paymentClient;
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* Connect to an MCP server transport.
|
|
199
|
+
* Passthrough to the underlying MCP client.
|
|
200
|
+
*
|
|
201
|
+
* @param transport - The transport to connect to
|
|
202
|
+
* @returns Promise that resolves when connected
|
|
203
|
+
*/
|
|
204
|
+
async connect(transport) {
|
|
205
|
+
await this.mcpClient.connect(transport);
|
|
206
|
+
}
|
|
207
|
+
/**
|
|
208
|
+
* Close the MCP connection.
|
|
209
|
+
* Passthrough to the underlying MCP client.
|
|
210
|
+
*
|
|
211
|
+
* @returns Promise that resolves when closed
|
|
212
|
+
*/
|
|
213
|
+
async close() {
|
|
214
|
+
await this.mcpClient.close();
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* List available tools from the server.
|
|
218
|
+
* Passthrough to the underlying MCP client.
|
|
219
|
+
*
|
|
220
|
+
* @returns Promise resolving to the list of tools
|
|
221
|
+
*/
|
|
222
|
+
async listTools() {
|
|
223
|
+
return this.mcpClient.listTools();
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* List available resources from the server.
|
|
227
|
+
* Passthrough to the underlying MCP client.
|
|
228
|
+
*
|
|
229
|
+
* @returns Promise resolving to the list of resources
|
|
230
|
+
*/
|
|
231
|
+
async listResources() {
|
|
232
|
+
return this.mcpClient.listResources();
|
|
233
|
+
}
|
|
234
|
+
/**
|
|
235
|
+
* List available prompts from the server.
|
|
236
|
+
* Passthrough to the underlying MCP client.
|
|
237
|
+
*
|
|
238
|
+
* @returns Promise resolving to the list of prompts
|
|
239
|
+
*/
|
|
240
|
+
async listPrompts() {
|
|
241
|
+
return this.mcpClient.listPrompts();
|
|
242
|
+
}
|
|
243
|
+
/**
|
|
244
|
+
* Get a specific prompt from the server.
|
|
245
|
+
* Passthrough to the underlying MCP client.
|
|
246
|
+
*
|
|
247
|
+
* @param args - Arguments for getPrompt method
|
|
248
|
+
* @returns Promise resolving to the prompt
|
|
249
|
+
*/
|
|
250
|
+
async getPrompt(...args) {
|
|
251
|
+
return this.mcpClient.getPrompt(...args);
|
|
252
|
+
}
|
|
253
|
+
/**
|
|
254
|
+
* Read a resource from the server.
|
|
255
|
+
* Passthrough to the underlying MCP client.
|
|
256
|
+
*
|
|
257
|
+
* @param args - Arguments for readResource method
|
|
258
|
+
* @returns Promise resolving to the resource content
|
|
259
|
+
*/
|
|
260
|
+
async readResource(...args) {
|
|
261
|
+
return this.mcpClient.readResource(...args);
|
|
262
|
+
}
|
|
263
|
+
/**
|
|
264
|
+
* List resource templates from the server.
|
|
265
|
+
* Passthrough to the underlying MCP client.
|
|
266
|
+
*
|
|
267
|
+
* @param args - Arguments for listResourceTemplates method
|
|
268
|
+
* @returns Promise resolving to the list of resource templates
|
|
269
|
+
*/
|
|
270
|
+
async listResourceTemplates(...args) {
|
|
271
|
+
return this.mcpClient.listResourceTemplates(...args);
|
|
272
|
+
}
|
|
273
|
+
/**
|
|
274
|
+
* Subscribe to resource updates.
|
|
275
|
+
* Passthrough to the underlying MCP client.
|
|
276
|
+
*
|
|
277
|
+
* @param args - Arguments for subscribeResource method
|
|
278
|
+
* @returns Promise resolving when subscribed
|
|
279
|
+
*/
|
|
280
|
+
async subscribeResource(...args) {
|
|
281
|
+
return this.mcpClient.subscribeResource(...args);
|
|
282
|
+
}
|
|
283
|
+
/**
|
|
284
|
+
* Unsubscribe from resource updates.
|
|
285
|
+
* Passthrough to the underlying MCP client.
|
|
286
|
+
*
|
|
287
|
+
* @param args - Arguments for unsubscribeResource method
|
|
288
|
+
* @returns Promise resolving when unsubscribed
|
|
289
|
+
*/
|
|
290
|
+
async unsubscribeResource(...args) {
|
|
291
|
+
return this.mcpClient.unsubscribeResource(...args);
|
|
292
|
+
}
|
|
293
|
+
/**
|
|
294
|
+
* Ping the server.
|
|
295
|
+
* Passthrough to the underlying MCP client.
|
|
296
|
+
*
|
|
297
|
+
* @param args - Arguments for ping method
|
|
298
|
+
* @returns Promise resolving to ping response
|
|
299
|
+
*/
|
|
300
|
+
async ping(...args) {
|
|
301
|
+
return this.mcpClient.ping(...args);
|
|
302
|
+
}
|
|
303
|
+
/**
|
|
304
|
+
* Request completion suggestions.
|
|
305
|
+
* Passthrough to the underlying MCP client.
|
|
306
|
+
*
|
|
307
|
+
* @param args - Arguments for complete method
|
|
308
|
+
* @returns Promise resolving to completion suggestions
|
|
309
|
+
*/
|
|
310
|
+
async complete(...args) {
|
|
311
|
+
return this.mcpClient.complete(...args);
|
|
312
|
+
}
|
|
313
|
+
/**
|
|
314
|
+
* Set the logging level on the server.
|
|
315
|
+
* Passthrough to the underlying MCP client.
|
|
316
|
+
*
|
|
317
|
+
* @param args - Arguments for setLoggingLevel method
|
|
318
|
+
* @returns Promise resolving when level is set
|
|
319
|
+
*/
|
|
320
|
+
async setLoggingLevel(...args) {
|
|
321
|
+
return this.mcpClient.setLoggingLevel(...args);
|
|
322
|
+
}
|
|
323
|
+
/**
|
|
324
|
+
* Get server capabilities after initialization.
|
|
325
|
+
* Passthrough to the underlying MCP client.
|
|
326
|
+
*
|
|
327
|
+
* @returns Server capabilities or undefined if not initialized
|
|
328
|
+
*/
|
|
329
|
+
getServerCapabilities() {
|
|
330
|
+
return this.mcpClient.getServerCapabilities();
|
|
331
|
+
}
|
|
332
|
+
/**
|
|
333
|
+
* Get server version information after initialization.
|
|
334
|
+
* Passthrough to the underlying MCP client.
|
|
335
|
+
*
|
|
336
|
+
* @returns Server version info or undefined if not initialized
|
|
337
|
+
*/
|
|
338
|
+
getServerVersion() {
|
|
339
|
+
return this.mcpClient.getServerVersion();
|
|
340
|
+
}
|
|
341
|
+
/**
|
|
342
|
+
* Get server instructions after initialization.
|
|
343
|
+
* Passthrough to the underlying MCP client.
|
|
344
|
+
*
|
|
345
|
+
* @returns Server instructions or undefined if not initialized
|
|
346
|
+
*/
|
|
347
|
+
getInstructions() {
|
|
348
|
+
return this.mcpClient.getInstructions();
|
|
349
|
+
}
|
|
350
|
+
/**
|
|
351
|
+
* Send notification that roots list has changed.
|
|
352
|
+
* Passthrough to the underlying MCP client.
|
|
353
|
+
*
|
|
354
|
+
* @returns Promise resolving when notification is sent
|
|
355
|
+
*/
|
|
356
|
+
async sendRootsListChanged() {
|
|
357
|
+
return this.mcpClient.sendRootsListChanged();
|
|
358
|
+
}
|
|
359
|
+
/**
|
|
360
|
+
* Register a hook to run when a 402 payment required is received.
|
|
361
|
+
* Hooks run in order; first to return a result wins.
|
|
362
|
+
*
|
|
363
|
+
* This can be used to:
|
|
364
|
+
* - Provide pre-existing payment payloads (implementation-specific, not part of x402 spec)
|
|
365
|
+
* - Abort the payment flow for certain tools
|
|
366
|
+
* - Log or track payment required events
|
|
367
|
+
*
|
|
368
|
+
* Note: Payment caching is an implementation pattern and not defined in the x402 MCP
|
|
369
|
+
* transport specification. Implementations that cache payments should ensure cached
|
|
370
|
+
* payloads are still valid (not expired, correct nonce, etc.).
|
|
371
|
+
*
|
|
372
|
+
* @param hook - Hook function
|
|
373
|
+
* @returns This instance for chaining
|
|
374
|
+
*
|
|
375
|
+
* @example
|
|
376
|
+
* ```typescript
|
|
377
|
+
* // Example: Custom payment handling (implementation-specific)
|
|
378
|
+
* client.onPaymentRequired(async ({ toolName, paymentRequired }) => {
|
|
379
|
+
* // Custom logic to provide a payment or abort
|
|
380
|
+
* if (shouldAbort(toolName)) {
|
|
381
|
+
* return { abort: true };
|
|
382
|
+
* }
|
|
383
|
+
* // Return undefined to proceed with normal payment flow
|
|
384
|
+
* });
|
|
385
|
+
* ```
|
|
386
|
+
*/
|
|
387
|
+
onPaymentRequired(hook) {
|
|
388
|
+
this.paymentRequiredHooks.push(hook);
|
|
389
|
+
return this;
|
|
390
|
+
}
|
|
391
|
+
/**
|
|
392
|
+
* Register a hook to run before payment is created.
|
|
393
|
+
*
|
|
394
|
+
* @param hook - Hook function
|
|
395
|
+
* @returns This instance for chaining
|
|
396
|
+
*/
|
|
397
|
+
onBeforePayment(hook) {
|
|
398
|
+
this.beforePaymentHooks.push(hook);
|
|
399
|
+
return this;
|
|
400
|
+
}
|
|
401
|
+
/**
|
|
402
|
+
* Register a hook to run after payment is submitted.
|
|
403
|
+
*
|
|
404
|
+
* @param hook - Hook function
|
|
405
|
+
* @returns This instance for chaining
|
|
406
|
+
*/
|
|
407
|
+
onAfterPayment(hook) {
|
|
408
|
+
this.afterPaymentHooks.push(hook);
|
|
409
|
+
return this;
|
|
410
|
+
}
|
|
411
|
+
/**
|
|
412
|
+
* Calls a tool, automatically handling 402 payment required errors.
|
|
413
|
+
*
|
|
414
|
+
* If the tool returns a 402 error and autoPayment is enabled, this method
|
|
415
|
+
* will automatically create a payment payload and retry the tool call.
|
|
416
|
+
*
|
|
417
|
+
* @param name - The name of the tool to call
|
|
418
|
+
* @param args - Arguments to pass to the tool
|
|
419
|
+
* @param options - Optional MCP request options (timeout, signal, etc.)
|
|
420
|
+
* @param options.timeout - Request timeout in milliseconds (default: 60000)
|
|
421
|
+
* @param options.signal - AbortSignal for cancellation
|
|
422
|
+
* @param options.resetTimeoutOnProgress - If true, progress notifications reset the timeout
|
|
423
|
+
* @returns The tool result with payment metadata
|
|
424
|
+
* @throws Error if payment is required but autoPayment is disabled and no payment provided
|
|
425
|
+
* @throws Error if payment approval is denied
|
|
426
|
+
* @throws Error if payment creation fails
|
|
427
|
+
*/
|
|
428
|
+
async callTool(name, args = {}, options) {
|
|
429
|
+
const result = await this.mcpClient.callTool({ name, arguments: args }, void 0, options);
|
|
430
|
+
if (!isMCPCallToolResult(result)) {
|
|
431
|
+
throw new Error("Invalid MCP tool result: missing content array");
|
|
432
|
+
}
|
|
433
|
+
const paymentRequired = this.extractPaymentRequiredFromResult(result);
|
|
434
|
+
if (!paymentRequired) {
|
|
435
|
+
return {
|
|
436
|
+
content: result.content,
|
|
437
|
+
isError: result.isError,
|
|
438
|
+
paymentMade: false
|
|
439
|
+
};
|
|
440
|
+
}
|
|
441
|
+
const paymentRequiredContext = {
|
|
442
|
+
toolName: name,
|
|
443
|
+
arguments: args,
|
|
444
|
+
paymentRequired
|
|
445
|
+
};
|
|
446
|
+
for (const hook of this.paymentRequiredHooks) {
|
|
447
|
+
const hookResult = await hook(paymentRequiredContext);
|
|
448
|
+
if (hookResult) {
|
|
449
|
+
if (hookResult.abort) {
|
|
450
|
+
throw new Error("Payment aborted by hook");
|
|
451
|
+
}
|
|
452
|
+
if (hookResult.payment) {
|
|
453
|
+
return this.callToolWithPayment(name, args, hookResult.payment, options);
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
if (!this.options.autoPayment) {
|
|
458
|
+
const err = new Error("Payment required");
|
|
459
|
+
err.code = MCP_PAYMENT_REQUIRED_CODE;
|
|
460
|
+
err.paymentRequired = paymentRequired;
|
|
461
|
+
throw err;
|
|
462
|
+
}
|
|
463
|
+
const paymentRequestedContext = {
|
|
464
|
+
toolName: name,
|
|
465
|
+
arguments: args,
|
|
466
|
+
paymentRequired
|
|
467
|
+
};
|
|
468
|
+
const approved = await this.options.onPaymentRequested(paymentRequestedContext);
|
|
469
|
+
if (!approved) {
|
|
470
|
+
throw new Error("Payment request denied");
|
|
471
|
+
}
|
|
472
|
+
for (const hook of this.beforePaymentHooks) {
|
|
473
|
+
await hook(paymentRequestedContext);
|
|
474
|
+
}
|
|
475
|
+
const paymentPayload = await this._paymentClient.createPaymentPayload(paymentRequired);
|
|
476
|
+
return this.callToolWithPayment(name, args, paymentPayload, options);
|
|
477
|
+
}
|
|
478
|
+
/**
|
|
479
|
+
* Calls a tool with an explicit payment payload.
|
|
480
|
+
*
|
|
481
|
+
* Use this method when you want to provide payment upfront or when
|
|
482
|
+
* implementing custom payment handling.
|
|
483
|
+
*
|
|
484
|
+
* @param name - The name of the tool to call
|
|
485
|
+
* @param args - Arguments to pass to the tool
|
|
486
|
+
* @param paymentPayload - The payment payload to include
|
|
487
|
+
* @param options - Optional MCP request options (timeout, signal, etc.)
|
|
488
|
+
* @param options.timeout - Request timeout in milliseconds (default: 60000)
|
|
489
|
+
* @param options.signal - AbortSignal for cancellation
|
|
490
|
+
* @param options.resetTimeoutOnProgress - If true, progress notifications reset the timeout
|
|
491
|
+
* @returns The tool result with payment metadata
|
|
492
|
+
*/
|
|
493
|
+
async callToolWithPayment(name, args, paymentPayload, options) {
|
|
494
|
+
const callParams = {
|
|
495
|
+
name,
|
|
496
|
+
arguments: args,
|
|
497
|
+
_meta: {
|
|
498
|
+
[MCP_PAYMENT_META_KEY]: paymentPayload
|
|
499
|
+
}
|
|
500
|
+
};
|
|
501
|
+
const result = await this.mcpClient.callTool(callParams, void 0, options);
|
|
502
|
+
if (!isMCPCallToolResult(result)) {
|
|
503
|
+
throw new Error("Invalid MCP tool result: missing content array");
|
|
504
|
+
}
|
|
505
|
+
const resultWithMeta = {
|
|
506
|
+
content: result.content,
|
|
507
|
+
isError: result.isError,
|
|
508
|
+
_meta: result._meta
|
|
509
|
+
};
|
|
510
|
+
const paymentResponse = extractPaymentResponseFromMeta(resultWithMeta);
|
|
511
|
+
for (const hook of this.afterPaymentHooks) {
|
|
512
|
+
await hook({
|
|
513
|
+
toolName: name,
|
|
514
|
+
paymentPayload,
|
|
515
|
+
result: resultWithMeta,
|
|
516
|
+
settleResponse: paymentResponse
|
|
517
|
+
});
|
|
518
|
+
}
|
|
519
|
+
return {
|
|
520
|
+
content: result.content,
|
|
521
|
+
isError: result.isError,
|
|
522
|
+
paymentResponse: paymentResponse ?? void 0,
|
|
523
|
+
paymentMade: true
|
|
524
|
+
};
|
|
525
|
+
}
|
|
526
|
+
/**
|
|
527
|
+
* Probes a tool to discover its payment requirements.
|
|
528
|
+
*
|
|
529
|
+
* **WARNING: Side Effects** - This method actually calls the tool to trigger a 402 response.
|
|
530
|
+
* If the tool is free (no payment required), it will execute and return null.
|
|
531
|
+
* Use with caution on tools that have side effects or are expensive to run.
|
|
532
|
+
*
|
|
533
|
+
* Useful for displaying pricing information to users before calling paid tools.
|
|
534
|
+
*
|
|
535
|
+
* @param name - The name of the tool to probe
|
|
536
|
+
* @param args - Arguments that may affect pricing (for dynamic pricing scenarios)
|
|
537
|
+
* @returns The payment requirements if the tool requires payment, null if the tool is free
|
|
538
|
+
*
|
|
539
|
+
* @example
|
|
540
|
+
* ```typescript
|
|
541
|
+
* // Check if a tool requires payment before calling
|
|
542
|
+
* const requirements = await client.getToolPaymentRequirements("expensive_analysis");
|
|
543
|
+
*
|
|
544
|
+
* if (requirements) {
|
|
545
|
+
* const price = requirements.accepts[0];
|
|
546
|
+
* console.log(`This tool costs ${price.amount} on ${price.network}`);
|
|
547
|
+
* // Optionally show user and get confirmation before calling
|
|
548
|
+
* } else {
|
|
549
|
+
* console.log("This tool is free");
|
|
550
|
+
* // Note: the tool has already executed!
|
|
551
|
+
* }
|
|
552
|
+
* ```
|
|
553
|
+
*/
|
|
554
|
+
async getToolPaymentRequirements(name, args = {}) {
|
|
555
|
+
const result = await this.mcpClient.callTool({ name, arguments: args });
|
|
556
|
+
if (!isMCPCallToolResult(result)) {
|
|
557
|
+
return null;
|
|
558
|
+
}
|
|
559
|
+
return this.extractPaymentRequiredFromResult(result);
|
|
560
|
+
}
|
|
561
|
+
// ============================================================================
|
|
562
|
+
// Private Methods
|
|
563
|
+
// ============================================================================
|
|
564
|
+
/**
|
|
565
|
+
* Extracts PaymentRequired from a tool result (structured 402 response).
|
|
566
|
+
*
|
|
567
|
+
* Per MCP transport spec, supports:
|
|
568
|
+
* 1. structuredContent with direct PaymentRequired object (optional, preferred)
|
|
569
|
+
* 2. content[0].text with JSON-encoded PaymentRequired object (required)
|
|
570
|
+
*
|
|
571
|
+
* @param result - The tool call result
|
|
572
|
+
* @returns PaymentRequired if this is a 402 response, null otherwise
|
|
573
|
+
*/
|
|
574
|
+
extractPaymentRequiredFromResult(result) {
|
|
575
|
+
if (!result.isError) {
|
|
576
|
+
return null;
|
|
577
|
+
}
|
|
578
|
+
if (result.structuredContent) {
|
|
579
|
+
const extracted = this.extractPaymentRequiredFromObject(result.structuredContent);
|
|
580
|
+
if (extracted) {
|
|
581
|
+
return extracted;
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
const content = result.content;
|
|
585
|
+
if (content.length === 0) {
|
|
586
|
+
return null;
|
|
587
|
+
}
|
|
588
|
+
const firstItem = content[0];
|
|
589
|
+
if (!isMCPTextContent(firstItem)) {
|
|
590
|
+
return null;
|
|
591
|
+
}
|
|
592
|
+
try {
|
|
593
|
+
const parsed = JSON.parse(firstItem.text);
|
|
594
|
+
if (typeof parsed === "object" && parsed !== null) {
|
|
595
|
+
const extracted = this.extractPaymentRequiredFromObject(
|
|
596
|
+
parsed
|
|
597
|
+
);
|
|
598
|
+
if (extracted) {
|
|
599
|
+
return extracted;
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
} catch {
|
|
603
|
+
}
|
|
604
|
+
return null;
|
|
605
|
+
}
|
|
606
|
+
/**
|
|
607
|
+
* Extracts PaymentRequired from an object.
|
|
608
|
+
* Expects direct PaymentRequired format (per MCP transport spec).
|
|
609
|
+
*
|
|
610
|
+
* @param obj - The object to extract from
|
|
611
|
+
* @returns PaymentRequired if found, null otherwise
|
|
612
|
+
*/
|
|
613
|
+
extractPaymentRequiredFromObject(obj) {
|
|
614
|
+
if ((0, import_schemas.isPaymentRequired)(obj)) {
|
|
615
|
+
return obj;
|
|
616
|
+
}
|
|
617
|
+
return null;
|
|
618
|
+
}
|
|
619
|
+
};
|
|
620
|
+
function wrapMCPClientWithPayment(mcpClient, paymentClient, options) {
|
|
621
|
+
return new x402MCPClient(mcpClient, paymentClient, options);
|
|
622
|
+
}
|
|
623
|
+
function wrapMCPClientWithPaymentFromConfig(mcpClient, config, options) {
|
|
624
|
+
const paymentClient = import_client.x402Client.fromConfig(config);
|
|
625
|
+
return new x402MCPClient(mcpClient, paymentClient, options);
|
|
626
|
+
}
|
|
627
|
+
function createx402MCPClient(config) {
|
|
628
|
+
const mcpClient = new import_client2.Client(
|
|
629
|
+
{
|
|
630
|
+
name: config.name,
|
|
631
|
+
version: config.version
|
|
632
|
+
},
|
|
633
|
+
config.mcpClientOptions
|
|
634
|
+
);
|
|
635
|
+
const paymentClient = new import_client.x402Client();
|
|
636
|
+
for (const scheme of config.schemes) {
|
|
637
|
+
if (scheme.x402Version === 1) {
|
|
638
|
+
paymentClient.registerV1(scheme.network, scheme.client);
|
|
639
|
+
} else {
|
|
640
|
+
paymentClient.register(scheme.network, scheme.client);
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
return new x402MCPClient(mcpClient, paymentClient, {
|
|
644
|
+
autoPayment: config.autoPayment,
|
|
645
|
+
onPaymentRequested: config.onPaymentRequested
|
|
646
|
+
});
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
// src/server/paymentWrapper.ts
|
|
650
|
+
function createPaymentWrapper(resourceServer, config) {
|
|
651
|
+
if (!config.accepts || config.accepts.length === 0) {
|
|
652
|
+
throw new Error("PaymentWrapperConfig.accepts must have at least one payment requirement");
|
|
653
|
+
}
|
|
654
|
+
return (handler) => {
|
|
655
|
+
return async (args, extra) => {
|
|
656
|
+
var _a, _b, _c, _d, _e;
|
|
657
|
+
const _meta = extra == null ? void 0 : extra._meta;
|
|
658
|
+
const toolName = ((_b = (_a = config.resource) == null ? void 0 : _a.url) == null ? void 0 : _b.replace("mcp://tool/", "")) || "paid_tool";
|
|
659
|
+
const context = {
|
|
660
|
+
toolName,
|
|
661
|
+
arguments: args,
|
|
662
|
+
meta: _meta
|
|
663
|
+
};
|
|
664
|
+
const paymentPayload = extractPaymentFromMeta({
|
|
665
|
+
name: toolName,
|
|
666
|
+
arguments: args,
|
|
667
|
+
_meta
|
|
668
|
+
});
|
|
669
|
+
const paymentRequirements = config.accepts[0];
|
|
670
|
+
if (!paymentPayload) {
|
|
671
|
+
return createPaymentRequiredResult(
|
|
672
|
+
resourceServer,
|
|
673
|
+
toolName,
|
|
674
|
+
config,
|
|
675
|
+
"Payment required to access this tool"
|
|
676
|
+
);
|
|
677
|
+
}
|
|
678
|
+
const verifyResult = await resourceServer.verifyPayment(paymentPayload, paymentRequirements);
|
|
679
|
+
if (!verifyResult.isValid) {
|
|
680
|
+
return createPaymentRequiredResult(
|
|
681
|
+
resourceServer,
|
|
682
|
+
toolName,
|
|
683
|
+
config,
|
|
684
|
+
verifyResult.invalidReason || "Payment verification failed"
|
|
685
|
+
);
|
|
686
|
+
}
|
|
687
|
+
const hookContext = {
|
|
688
|
+
toolName,
|
|
689
|
+
arguments: args,
|
|
690
|
+
paymentRequirements,
|
|
691
|
+
paymentPayload
|
|
692
|
+
};
|
|
693
|
+
if ((_c = config.hooks) == null ? void 0 : _c.onBeforeExecution) {
|
|
694
|
+
const hookResult = await config.hooks.onBeforeExecution(hookContext);
|
|
695
|
+
if (hookResult === false) {
|
|
696
|
+
return createPaymentRequiredResult(
|
|
697
|
+
resourceServer,
|
|
698
|
+
toolName,
|
|
699
|
+
config,
|
|
700
|
+
"Execution blocked by hook"
|
|
701
|
+
);
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
const result = await handler(args, context);
|
|
705
|
+
const afterExecContext = {
|
|
706
|
+
...hookContext,
|
|
707
|
+
result
|
|
708
|
+
};
|
|
709
|
+
if ((_d = config.hooks) == null ? void 0 : _d.onAfterExecution) {
|
|
710
|
+
await config.hooks.onAfterExecution(afterExecContext);
|
|
711
|
+
}
|
|
712
|
+
if (result.isError) {
|
|
713
|
+
return result;
|
|
714
|
+
}
|
|
715
|
+
try {
|
|
716
|
+
const settleResult = await resourceServer.settlePayment(
|
|
717
|
+
paymentPayload,
|
|
718
|
+
paymentRequirements
|
|
719
|
+
);
|
|
720
|
+
if ((_e = config.hooks) == null ? void 0 : _e.onAfterSettlement) {
|
|
721
|
+
const settlementContext = {
|
|
722
|
+
...hookContext,
|
|
723
|
+
settlement: settleResult
|
|
724
|
+
};
|
|
725
|
+
await config.hooks.onAfterSettlement(settlementContext);
|
|
726
|
+
}
|
|
727
|
+
return {
|
|
728
|
+
content: result.content,
|
|
729
|
+
isError: result.isError,
|
|
730
|
+
_meta: {
|
|
731
|
+
[MCP_PAYMENT_RESPONSE_META_KEY]: settleResult
|
|
732
|
+
}
|
|
733
|
+
};
|
|
734
|
+
} catch (settleError) {
|
|
735
|
+
return createSettlementFailedResult(
|
|
736
|
+
resourceServer,
|
|
737
|
+
toolName,
|
|
738
|
+
config,
|
|
739
|
+
settleError instanceof Error ? settleError.message : "Settlement failed"
|
|
740
|
+
);
|
|
741
|
+
}
|
|
742
|
+
};
|
|
743
|
+
};
|
|
744
|
+
}
|
|
745
|
+
async function createPaymentRequiredResult(resourceServer, toolName, config, errorMessage) {
|
|
746
|
+
var _a, _b, _c;
|
|
747
|
+
const resourceInfo = {
|
|
748
|
+
url: createToolResourceUrl(toolName, (_a = config.resource) == null ? void 0 : _a.url),
|
|
749
|
+
description: ((_b = config.resource) == null ? void 0 : _b.description) || `Tool: ${toolName}`,
|
|
750
|
+
mimeType: ((_c = config.resource) == null ? void 0 : _c.mimeType) || "application/json"
|
|
751
|
+
};
|
|
752
|
+
const paymentRequired = await resourceServer.createPaymentRequiredResponse(
|
|
753
|
+
config.accepts,
|
|
754
|
+
resourceInfo,
|
|
755
|
+
errorMessage
|
|
756
|
+
);
|
|
757
|
+
return {
|
|
758
|
+
structuredContent: paymentRequired,
|
|
759
|
+
content: [
|
|
760
|
+
{
|
|
761
|
+
type: "text",
|
|
762
|
+
text: JSON.stringify(paymentRequired)
|
|
763
|
+
}
|
|
764
|
+
],
|
|
765
|
+
isError: true
|
|
766
|
+
};
|
|
767
|
+
}
|
|
768
|
+
async function createSettlementFailedResult(resourceServer, toolName, config, errorMessage) {
|
|
769
|
+
var _a, _b, _c;
|
|
770
|
+
const resourceInfo = {
|
|
771
|
+
url: createToolResourceUrl(toolName, (_a = config.resource) == null ? void 0 : _a.url),
|
|
772
|
+
description: ((_b = config.resource) == null ? void 0 : _b.description) || `Tool: ${toolName}`,
|
|
773
|
+
mimeType: ((_c = config.resource) == null ? void 0 : _c.mimeType) || "application/json"
|
|
774
|
+
};
|
|
775
|
+
const paymentRequired = await resourceServer.createPaymentRequiredResponse(
|
|
776
|
+
config.accepts,
|
|
777
|
+
resourceInfo,
|
|
778
|
+
`Payment settlement failed: ${errorMessage}`
|
|
779
|
+
);
|
|
780
|
+
const settlementFailure = {
|
|
781
|
+
success: false,
|
|
782
|
+
errorReason: errorMessage,
|
|
783
|
+
transaction: "",
|
|
784
|
+
network: config.accepts[0].network
|
|
785
|
+
};
|
|
786
|
+
const errorData = {
|
|
787
|
+
...paymentRequired,
|
|
788
|
+
[MCP_PAYMENT_RESPONSE_META_KEY]: settlementFailure
|
|
789
|
+
};
|
|
790
|
+
return {
|
|
791
|
+
structuredContent: errorData,
|
|
792
|
+
content: [
|
|
793
|
+
{
|
|
794
|
+
type: "text",
|
|
795
|
+
text: JSON.stringify(errorData)
|
|
796
|
+
}
|
|
797
|
+
],
|
|
798
|
+
isError: true
|
|
799
|
+
};
|
|
800
|
+
}
|
|
801
|
+
|
|
802
|
+
// src/index.ts
|
|
803
|
+
var import_client4 = require("@x402/core/client");
|
|
804
|
+
var import_server2 = require("@x402/core/server");
|
|
805
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
806
|
+
0 && (module.exports = {
|
|
807
|
+
MCP_PAYMENT_META_KEY,
|
|
808
|
+
MCP_PAYMENT_REQUIRED_CODE,
|
|
809
|
+
MCP_PAYMENT_RESPONSE_META_KEY,
|
|
810
|
+
attachPaymentResponseToMeta,
|
|
811
|
+
attachPaymentToMeta,
|
|
812
|
+
createPaymentRequiredError,
|
|
813
|
+
createPaymentWrapper,
|
|
814
|
+
createToolResourceUrl,
|
|
815
|
+
createx402MCPClient,
|
|
816
|
+
extractPaymentFromMeta,
|
|
817
|
+
extractPaymentRequiredFromError,
|
|
818
|
+
extractPaymentResponseFromMeta,
|
|
819
|
+
isPaymentRequiredError,
|
|
820
|
+
wrapMCPClientWithPayment,
|
|
821
|
+
wrapMCPClientWithPaymentFromConfig,
|
|
822
|
+
x402Client,
|
|
823
|
+
x402MCPClient,
|
|
824
|
+
x402ResourceServer
|
|
825
|
+
});
|
|
826
|
+
//# sourceMappingURL=index.js.map
|