x402-proxy 0.5.2 → 0.7.0
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/CHANGELOG.md +37 -1
- package/README.md +20 -2
- package/dist/bin/cli.js +518 -168
- package/dist/{derive-CISr_ond.js → derive-ibF2UinV.js} +2 -2
- package/dist/index.d.ts +43 -8
- package/dist/index.js +108 -13
- package/dist/openclaw/plugin.d.ts +55 -0
- package/dist/openclaw/plugin.js +1120 -0
- package/dist/openclaw.plugin.json +28 -0
- package/dist/{setup-gla-Qyqi.js → setup-hJGkO2Lo.js} +1 -1
- package/dist/setup-j_xQ14-4.js +4 -0
- package/dist/{status-BoH_1kIH.js → status-JNGv2Ghp.js} +12 -6
- package/dist/{status-BVIU3-b6.js → status-w5y-fhhe.js} +1 -1
- package/dist/wallet-DjixXCHy.js +4 -0
- package/dist/{wallet-BMYYtAP6.js → wallet-DxKCHa7U.js} +45 -11
- package/openclaw.plugin.json +28 -0
- package/package.json +29 -4
- package/skills/SKILL.md +22 -7
- package/dist/setup-Crq9TylJ.js +0 -4
- package/dist/wallet-BoY0fgX7.js +0 -4
package/dist/bin/cli.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import { n as setupCommand } from "../setup-
|
|
5
|
-
import { n as statusCommand } from "../status-
|
|
2
|
+
import { _ as formatTxLine, c as resolveWallet, d as info, f as isTTY, g as displayNetwork, h as calcSpend, l as dim, m as appendHistory, o as walletInfoCommand, p as warn, s as buildX402Client, u as error, v as readHistory } from "../wallet-DxKCHa7U.js";
|
|
3
|
+
import { a as getHistoryPath, c as loadConfig, l as loadWalletFile, s as isConfigured } from "../derive-ibF2UinV.js";
|
|
4
|
+
import { n as setupCommand } from "../setup-hJGkO2Lo.js";
|
|
5
|
+
import { n as statusCommand } from "../status-JNGv2Ghp.js";
|
|
6
6
|
import { buildApplication, buildCommand, buildRouteMap, run } from "@stricli/core";
|
|
7
7
|
import pc from "picocolors";
|
|
8
8
|
import { decodePaymentResponseHeader, wrapFetchWithPayment } from "@x402/fetch";
|
|
@@ -11,25 +11,35 @@ import * as prompts from "@clack/prompts";
|
|
|
11
11
|
|
|
12
12
|
//#region src/handler.ts
|
|
13
13
|
/**
|
|
14
|
+
* Detect which payment protocols a 402 response advertises.
|
|
15
|
+
* - x402: PAYMENT-REQUIRED or X-PAYMENT-REQUIRED header
|
|
16
|
+
* - MPP: WWW-Authenticate header with Payment scheme
|
|
17
|
+
*/
|
|
18
|
+
function detectProtocols(response) {
|
|
19
|
+
const pr = response.headers.get("PAYMENT-REQUIRED") ?? response.headers.get("X-PAYMENT-REQUIRED");
|
|
20
|
+
const wwwAuth = response.headers.get("WWW-Authenticate");
|
|
21
|
+
return {
|
|
22
|
+
x402: !!pr,
|
|
23
|
+
mpp: !!(wwwAuth && /^Payment\b/i.test(wwwAuth.trim()))
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
14
27
|
* Extract the on-chain transaction signature from an x402 payment response header.
|
|
15
28
|
*/
|
|
16
29
|
function extractTxSignature(response) {
|
|
17
|
-
const
|
|
18
|
-
if (
|
|
19
|
-
|
|
20
|
-
|
|
30
|
+
const x402Header = response.headers.get("PAYMENT-RESPONSE") ?? response.headers.get("X-PAYMENT-RESPONSE");
|
|
31
|
+
if (x402Header) try {
|
|
32
|
+
return decodePaymentResponseHeader(x402Header).transaction ?? void 0;
|
|
33
|
+
} catch {}
|
|
34
|
+
const mppHeader = response.headers.get("Payment-Receipt");
|
|
35
|
+
if (mppHeader) try {
|
|
36
|
+
return JSON.parse(Buffer.from(mppHeader, "base64url").toString()).reference ?? void 0;
|
|
21
37
|
} catch {
|
|
22
38
|
return;
|
|
23
39
|
}
|
|
24
40
|
}
|
|
25
41
|
/**
|
|
26
42
|
* Create an x402 proxy handler that wraps fetch with automatic payment.
|
|
27
|
-
*
|
|
28
|
-
* Chain-agnostic: accepts a pre-configured x402Client with any registered
|
|
29
|
-
* schemes (SVM, EVM, etc). The handler captures payment info via the
|
|
30
|
-
* onAfterPaymentCreation hook. Callers use `x402Fetch` for requests that
|
|
31
|
-
* may require x402 payment, and `shiftPayment` to retrieve captured
|
|
32
|
-
* payment info after each call.
|
|
33
43
|
*/
|
|
34
44
|
function createX402ProxyHandler(opts) {
|
|
35
45
|
const { client } = opts;
|
|
@@ -37,6 +47,7 @@ function createX402ProxyHandler(opts) {
|
|
|
37
47
|
client.onAfterPaymentCreation(async (hookCtx) => {
|
|
38
48
|
const raw = hookCtx.selectedRequirements.amount;
|
|
39
49
|
paymentQueue.push({
|
|
50
|
+
protocol: "x402",
|
|
40
51
|
network: hookCtx.selectedRequirements.network,
|
|
41
52
|
payTo: hookCtx.selectedRequirements.payTo,
|
|
42
53
|
amount: raw,
|
|
@@ -48,6 +59,84 @@ function createX402ProxyHandler(opts) {
|
|
|
48
59
|
shiftPayment: () => paymentQueue.shift()
|
|
49
60
|
};
|
|
50
61
|
}
|
|
62
|
+
const TEMPO_NETWORK = "eip155:4217";
|
|
63
|
+
/**
|
|
64
|
+
* Create an MPP proxy handler using mppx client.
|
|
65
|
+
* Dynamically imports mppx/client to keep startup fast.
|
|
66
|
+
*/
|
|
67
|
+
async function createMppProxyHandler(opts) {
|
|
68
|
+
const { Mppx, tempo } = await import("mppx/client");
|
|
69
|
+
const { privateKeyToAccount } = await import("viem/accounts");
|
|
70
|
+
const account = privateKeyToAccount(opts.evmKey);
|
|
71
|
+
const maxDeposit = opts.maxDeposit ?? "1";
|
|
72
|
+
const paymentQueue = [];
|
|
73
|
+
const mppx = Mppx.create({
|
|
74
|
+
methods: [tempo({
|
|
75
|
+
account,
|
|
76
|
+
maxDeposit
|
|
77
|
+
})],
|
|
78
|
+
polyfill: false
|
|
79
|
+
});
|
|
80
|
+
let session;
|
|
81
|
+
return {
|
|
82
|
+
async fetch(input, init) {
|
|
83
|
+
const response = await mppx.fetch(typeof input === "string" ? input : input.toString(), init);
|
|
84
|
+
const receiptHeader = response.headers.get("Payment-Receipt");
|
|
85
|
+
if (receiptHeader) try {
|
|
86
|
+
const receipt = JSON.parse(Buffer.from(receiptHeader, "base64url").toString());
|
|
87
|
+
paymentQueue.push({
|
|
88
|
+
protocol: "mpp",
|
|
89
|
+
network: TEMPO_NETWORK,
|
|
90
|
+
receipt
|
|
91
|
+
});
|
|
92
|
+
} catch {
|
|
93
|
+
paymentQueue.push({
|
|
94
|
+
protocol: "mpp",
|
|
95
|
+
network: TEMPO_NETWORK
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
return response;
|
|
99
|
+
},
|
|
100
|
+
async sse(input, init) {
|
|
101
|
+
session ??= tempo.session({
|
|
102
|
+
account,
|
|
103
|
+
maxDeposit
|
|
104
|
+
});
|
|
105
|
+
const url = typeof input === "string" ? input : input.toString();
|
|
106
|
+
const iterable = await session.sse(url, init);
|
|
107
|
+
paymentQueue.push({
|
|
108
|
+
protocol: "mpp",
|
|
109
|
+
network: TEMPO_NETWORK,
|
|
110
|
+
intent: "session"
|
|
111
|
+
});
|
|
112
|
+
return iterable;
|
|
113
|
+
},
|
|
114
|
+
shiftPayment: () => paymentQueue.shift(),
|
|
115
|
+
async close() {
|
|
116
|
+
if (session?.opened) {
|
|
117
|
+
const receipt = await session.close();
|
|
118
|
+
if (receipt) {
|
|
119
|
+
const spentUsdc = receipt.spent ? (Number(receipt.spent) / 1e6).toString() : void 0;
|
|
120
|
+
paymentQueue.push({
|
|
121
|
+
protocol: "mpp",
|
|
122
|
+
network: TEMPO_NETWORK,
|
|
123
|
+
intent: "session",
|
|
124
|
+
amount: spentUsdc,
|
|
125
|
+
channelId: session.channelId ?? void 0,
|
|
126
|
+
receipt: {
|
|
127
|
+
method: receipt.method,
|
|
128
|
+
reference: receipt.reference,
|
|
129
|
+
status: receipt.status,
|
|
130
|
+
timestamp: receipt.timestamp,
|
|
131
|
+
acceptedCumulative: receipt.acceptedCumulative,
|
|
132
|
+
txHash: receipt.txHash
|
|
133
|
+
}
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
};
|
|
139
|
+
}
|
|
51
140
|
|
|
52
141
|
//#endregion
|
|
53
142
|
//#region src/commands/fetch.ts
|
|
@@ -96,7 +185,13 @@ Examples:
|
|
|
96
185
|
},
|
|
97
186
|
network: {
|
|
98
187
|
kind: "parsed",
|
|
99
|
-
brief: "Require specific network (base, solana)",
|
|
188
|
+
brief: "Require specific network (base, solana, tempo)",
|
|
189
|
+
parse: String,
|
|
190
|
+
optional: true
|
|
191
|
+
},
|
|
192
|
+
protocol: {
|
|
193
|
+
kind: "parsed",
|
|
194
|
+
brief: "Payment protocol (x402, mpp)",
|
|
100
195
|
parse: String,
|
|
101
196
|
optional: true
|
|
102
197
|
},
|
|
@@ -104,6 +199,11 @@ Examples:
|
|
|
104
199
|
kind: "boolean",
|
|
105
200
|
brief: "Force JSON output",
|
|
106
201
|
default: false
|
|
202
|
+
},
|
|
203
|
+
verbose: {
|
|
204
|
+
kind: "boolean",
|
|
205
|
+
brief: "Show debug details (protocol negotiation, headers, payment flow)",
|
|
206
|
+
default: false
|
|
107
207
|
}
|
|
108
208
|
},
|
|
109
209
|
positional: {
|
|
@@ -116,9 +216,21 @@ Examples:
|
|
|
116
216
|
}
|
|
117
217
|
},
|
|
118
218
|
async func(flags, url) {
|
|
219
|
+
const verbose = (msg) => {
|
|
220
|
+
if (flags.verbose) dim(` [verbose] ${msg}`);
|
|
221
|
+
};
|
|
222
|
+
const closeMppSession = async (handler) => {
|
|
223
|
+
verbose("closing MPP session...");
|
|
224
|
+
try {
|
|
225
|
+
await handler.close();
|
|
226
|
+
verbose("session closed successfully");
|
|
227
|
+
} catch (closeErr) {
|
|
228
|
+
verbose(`session close failed: ${closeErr instanceof Error ? closeErr.message : String(closeErr)}`);
|
|
229
|
+
}
|
|
230
|
+
};
|
|
119
231
|
if (!url) {
|
|
120
232
|
if (isConfigured()) {
|
|
121
|
-
const { displayStatus } = await import("../status-
|
|
233
|
+
const { displayStatus } = await import("../status-w5y-fhhe.js");
|
|
122
234
|
await displayStatus();
|
|
123
235
|
console.log();
|
|
124
236
|
console.log(pc.dim(" Commands:"));
|
|
@@ -170,28 +282,26 @@ Examples:
|
|
|
170
282
|
process.exit(1);
|
|
171
283
|
}
|
|
172
284
|
dim(" No wallet found. Let's set one up first.\n");
|
|
173
|
-
const { runSetup } = await import("../setup-
|
|
285
|
+
const { runSetup } = await import("../setup-j_xQ14-4.js");
|
|
174
286
|
await runSetup();
|
|
175
287
|
console.log();
|
|
176
288
|
wallet = resolveWallet();
|
|
177
289
|
if (wallet.source === "none") return;
|
|
178
290
|
}
|
|
179
291
|
const config = loadConfig();
|
|
292
|
+
const resolvedProtocol = flags.protocol ?? config?.preferredProtocol;
|
|
293
|
+
const maxDeposit = config?.mppSessionBudget ?? "1";
|
|
294
|
+
verbose(`wallet source: ${wallet.source}`);
|
|
295
|
+
verbose(`protocol: ${resolvedProtocol ?? "auto-detect"}, maxDeposit: ${maxDeposit}`);
|
|
180
296
|
let preferredNetwork = config?.defaultNetwork;
|
|
181
297
|
if (!preferredNetwork && wallet.evmAddress && wallet.solanaAddress) {
|
|
182
|
-
const {
|
|
183
|
-
const
|
|
184
|
-
const evmUsdc =
|
|
185
|
-
const solUsdc =
|
|
298
|
+
const { fetchAllBalances } = await import("../wallet-DjixXCHy.js");
|
|
299
|
+
const balances = await fetchAllBalances(wallet.evmAddress, wallet.solanaAddress);
|
|
300
|
+
const evmUsdc = balances.evm ? Number(balances.evm.usdc) : 0;
|
|
301
|
+
const solUsdc = balances.sol ? Number(balances.sol.usdc) : 0;
|
|
186
302
|
if (evmUsdc > solUsdc) preferredNetwork = "base";
|
|
187
303
|
else if (solUsdc > evmUsdc) preferredNetwork = "solana";
|
|
188
304
|
}
|
|
189
|
-
const { x402Fetch, shiftPayment } = createX402ProxyHandler({ client: await buildX402Client(wallet, {
|
|
190
|
-
preferredNetwork,
|
|
191
|
-
network: flags.network,
|
|
192
|
-
spendLimitDaily: config?.spendLimitDaily,
|
|
193
|
-
spendLimitPerTx: config?.spendLimitPerTx
|
|
194
|
-
}) });
|
|
195
305
|
const headers = new Headers();
|
|
196
306
|
if (flags.header) for (const h of flags.header) {
|
|
197
307
|
const idx = h.indexOf(":");
|
|
@@ -200,22 +310,129 @@ Examples:
|
|
|
200
310
|
const method = flags.method || "GET";
|
|
201
311
|
const init = {
|
|
202
312
|
method,
|
|
203
|
-
headers
|
|
313
|
+
headers: Object.fromEntries(headers.entries())
|
|
204
314
|
};
|
|
205
315
|
if (flags.body) init.body = flags.body;
|
|
206
316
|
if (isTTY()) dim(` ${method} ${parsedUrl.toString()}`);
|
|
207
317
|
const startMs = Date.now();
|
|
208
318
|
let response;
|
|
319
|
+
let x402Payment;
|
|
320
|
+
let mppPayment;
|
|
321
|
+
let usedProtocol;
|
|
209
322
|
try {
|
|
210
|
-
|
|
323
|
+
if (resolvedProtocol === "mpp") {
|
|
324
|
+
if (!wallet.evmKey) {
|
|
325
|
+
error("MPP requires an EVM wallet. Configure one with: npx x402-proxy setup");
|
|
326
|
+
process.exit(1);
|
|
327
|
+
}
|
|
328
|
+
const mppHandler = await createMppProxyHandler({
|
|
329
|
+
evmKey: wallet.evmKey,
|
|
330
|
+
maxDeposit
|
|
331
|
+
});
|
|
332
|
+
const isStreamingRequest = flags.body != null && /"stream"\s*:\s*true/.test(flags.body);
|
|
333
|
+
verbose(`mpp handler created, streaming: ${isStreamingRequest}`);
|
|
334
|
+
if (isStreamingRequest) {
|
|
335
|
+
try {
|
|
336
|
+
verbose("opening SSE session...");
|
|
337
|
+
const tokens = await mppHandler.sse(parsedUrl.toString(), init);
|
|
338
|
+
verbose("SSE stream opened, reading tokens...");
|
|
339
|
+
for await (const token of tokens) process.stdout.write(token);
|
|
340
|
+
verbose("SSE stream complete");
|
|
341
|
+
} finally {
|
|
342
|
+
await closeMppSession(mppHandler);
|
|
343
|
+
}
|
|
344
|
+
mppHandler.shiftPayment();
|
|
345
|
+
const closeReceipt = mppHandler.shiftPayment();
|
|
346
|
+
verbose(closeReceipt ? `close receipt: amount=${closeReceipt.amount ?? "none"}, channelId=${closeReceipt.channelId ?? "none"}, txHash=${closeReceipt.receipt?.txHash ?? "none"}` : "no close receipt (session close may have failed)");
|
|
347
|
+
mppPayment = closeReceipt ?? {
|
|
348
|
+
protocol: "mpp",
|
|
349
|
+
network: TEMPO_NETWORK,
|
|
350
|
+
intent: "session"
|
|
351
|
+
};
|
|
352
|
+
usedProtocol = "mpp";
|
|
353
|
+
const elapsedMs = Date.now() - startMs;
|
|
354
|
+
const spentAmount = mppPayment.amount ? Number(mppPayment.amount) : void 0;
|
|
355
|
+
if (mppPayment && isTTY()) info(` MPP session: ${spentAmount != null ? `${spentAmount.toFixed(4)} USDC ` : ""}(${displayNetwork(mppPayment.network)})`);
|
|
356
|
+
if (isTTY()) dim(` Streamed (${elapsedMs}ms)`);
|
|
357
|
+
if (mppPayment) {
|
|
358
|
+
const record = {
|
|
359
|
+
t: Date.now(),
|
|
360
|
+
ok: true,
|
|
361
|
+
kind: "mpp_payment",
|
|
362
|
+
net: mppPayment.network,
|
|
363
|
+
from: wallet.evmAddress ?? "unknown",
|
|
364
|
+
tx: mppPayment.receipt?.reference ?? mppPayment.channelId,
|
|
365
|
+
amount: spentAmount,
|
|
366
|
+
token: "USDC",
|
|
367
|
+
ms: elapsedMs,
|
|
368
|
+
label: parsedUrl.hostname
|
|
369
|
+
};
|
|
370
|
+
appendHistory(getHistoryPath(), record);
|
|
371
|
+
}
|
|
372
|
+
if (isTTY()) process.stdout.write("\n");
|
|
373
|
+
return;
|
|
374
|
+
}
|
|
375
|
+
verbose("sending non-streaming MPP request...");
|
|
376
|
+
try {
|
|
377
|
+
response = await mppHandler.fetch(parsedUrl.toString(), init);
|
|
378
|
+
verbose(`response: ${response.status} ${response.statusText}`);
|
|
379
|
+
} finally {
|
|
380
|
+
await closeMppSession(mppHandler);
|
|
381
|
+
}
|
|
382
|
+
mppPayment = mppHandler.shiftPayment();
|
|
383
|
+
usedProtocol = "mpp";
|
|
384
|
+
} else if (resolvedProtocol === "x402") {
|
|
385
|
+
const handler = createX402ProxyHandler({ client: await buildX402Client(wallet, {
|
|
386
|
+
preferredNetwork,
|
|
387
|
+
network: flags.network,
|
|
388
|
+
spendLimitDaily: config?.spendLimitDaily,
|
|
389
|
+
spendLimitPerTx: config?.spendLimitPerTx
|
|
390
|
+
}) });
|
|
391
|
+
response = await handler.x402Fetch(parsedUrl.toString(), init);
|
|
392
|
+
x402Payment = handler.shiftPayment();
|
|
393
|
+
usedProtocol = "x402";
|
|
394
|
+
} else {
|
|
395
|
+
const handler = createX402ProxyHandler({ client: await buildX402Client(wallet, {
|
|
396
|
+
preferredNetwork,
|
|
397
|
+
network: flags.network,
|
|
398
|
+
spendLimitDaily: config?.spendLimitDaily,
|
|
399
|
+
spendLimitPerTx: config?.spendLimitPerTx
|
|
400
|
+
}) });
|
|
401
|
+
response = await handler.x402Fetch(parsedUrl.toString(), init);
|
|
402
|
+
x402Payment = handler.shiftPayment();
|
|
403
|
+
usedProtocol = "x402";
|
|
404
|
+
if (response.status === 402 && wallet.evmKey) {
|
|
405
|
+
const detected = detectProtocols(response);
|
|
406
|
+
verbose(`auto-detect: x402=${detected.x402}, mpp=${detected.mpp}`);
|
|
407
|
+
if (detected.mpp) {
|
|
408
|
+
verbose("falling through to MPP...");
|
|
409
|
+
const mppHandler = await createMppProxyHandler({
|
|
410
|
+
evmKey: wallet.evmKey,
|
|
411
|
+
maxDeposit
|
|
412
|
+
});
|
|
413
|
+
try {
|
|
414
|
+
response = await mppHandler.fetch(parsedUrl.toString(), init);
|
|
415
|
+
verbose(`MPP response: ${response.status} ${response.statusText}`);
|
|
416
|
+
} finally {
|
|
417
|
+
await closeMppSession(mppHandler);
|
|
418
|
+
}
|
|
419
|
+
mppPayment = mppHandler.shiftPayment();
|
|
420
|
+
x402Payment = void 0;
|
|
421
|
+
usedProtocol = "mpp";
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
}
|
|
211
425
|
} catch (err) {
|
|
212
426
|
error(`Request failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
213
427
|
process.exit(1);
|
|
214
428
|
}
|
|
215
429
|
const elapsedMs = Date.now() - startMs;
|
|
216
|
-
const payment =
|
|
430
|
+
const payment = x402Payment ?? mppPayment;
|
|
217
431
|
const txSig = extractTxSignature(response);
|
|
432
|
+
verbose(`protocol used: ${usedProtocol ?? "none"}`);
|
|
433
|
+
for (const [k, v] of response.headers) if (/payment|auth|www|x-pay/i.test(k)) verbose(`header ${k}: ${v.slice(0, 200)}`);
|
|
218
434
|
if (response.status === 402 && isTTY()) {
|
|
435
|
+
const detected = detectProtocols(response);
|
|
219
436
|
const prHeader = response.headers.get("PAYMENT-REQUIRED") ?? response.headers.get("X-PAYMENT-REQUIRED");
|
|
220
437
|
let accepts = [];
|
|
221
438
|
if (prHeader) try {
|
|
@@ -230,19 +447,14 @@ Examples:
|
|
|
230
447
|
}
|
|
231
448
|
const hasEvm = accepts.some((a) => a.network.startsWith("eip155:"));
|
|
232
449
|
const hasSolana = accepts.some((a) => a.network.startsWith("solana:"));
|
|
450
|
+
const hasMpp = detected.mpp;
|
|
233
451
|
const hasOther = accepts.some((a) => !a.network.startsWith("eip155:") && !a.network.startsWith("solana:"));
|
|
234
|
-
const {
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
} catch {}
|
|
241
|
-
if (hasSolana && wallet.solanaAddress) try {
|
|
242
|
-
const bal = await fetchSolanaBalances(wallet.solanaAddress);
|
|
243
|
-
solUsdc = Number(bal.usdc);
|
|
244
|
-
} catch {}
|
|
245
|
-
if (hasEvm && evmUsdc >= costNum || hasSolana && solUsdc >= costNum) {
|
|
452
|
+
const { fetchAllBalances } = await import("../wallet-DjixXCHy.js");
|
|
453
|
+
const balances = await fetchAllBalances(wallet.evmAddress, wallet.solanaAddress);
|
|
454
|
+
const evmUsdc = hasEvm && balances.evm ? Number(balances.evm.usdc) : 0;
|
|
455
|
+
const solUsdc = hasSolana && balances.sol ? Number(balances.sol.usdc) : 0;
|
|
456
|
+
const tempoUsdc = hasMpp && balances.tempo ? Number(balances.tempo.usdc) : 0;
|
|
457
|
+
if (hasEvm && evmUsdc >= costNum || hasSolana && solUsdc >= costNum || hasMpp && tempoUsdc >= costNum) {
|
|
246
458
|
let serverReason;
|
|
247
459
|
try {
|
|
248
460
|
const body = await response.text();
|
|
@@ -257,24 +469,30 @@ Examples:
|
|
|
257
469
|
else dim(" Payment was not attempted despite sufficient balance.");
|
|
258
470
|
if (serverReason) dim(` Reason: ${serverReason}`);
|
|
259
471
|
if (hasEvm && wallet.evmAddress && evmUsdc > 0) console.error(` Base: ${pc.cyan(wallet.evmAddress)} ${pc.dim(`(${evmUsdc.toFixed(4)} USDC)`)}`);
|
|
472
|
+
if (hasMpp && wallet.evmAddress && tempoUsdc > 0) console.error(` Tempo: ${pc.cyan(wallet.evmAddress)} ${pc.dim(`(${tempoUsdc.toFixed(4)} USDC)`)}`);
|
|
260
473
|
if (hasSolana && wallet.solanaAddress && solUsdc > 0) console.error(` Solana: ${pc.cyan(wallet.solanaAddress)} ${pc.dim(`(${solUsdc.toFixed(4)} USDC)`)}`);
|
|
261
474
|
console.error();
|
|
262
475
|
dim(" This may be a temporary server-side issue. Try again in a moment.");
|
|
263
476
|
console.error();
|
|
264
477
|
} else {
|
|
265
478
|
error(`Payment required: ${costStr} USDC`);
|
|
266
|
-
if (hasEvm || hasSolana) {
|
|
479
|
+
if (hasEvm || hasSolana || hasMpp) {
|
|
267
480
|
console.error();
|
|
268
481
|
dim(" Fund your wallet with USDC:");
|
|
269
482
|
if (hasEvm && wallet.evmAddress) {
|
|
270
483
|
const balHint = evmUsdc > 0 ? pc.dim(` (${evmUsdc.toFixed(4)} USDC)`) : "";
|
|
271
484
|
console.error(` Base: ${pc.cyan(wallet.evmAddress)}${balHint}`);
|
|
272
485
|
}
|
|
486
|
+
if (hasMpp && wallet.evmAddress) {
|
|
487
|
+
const balHint = tempoUsdc > 0 ? pc.dim(` (${tempoUsdc.toFixed(4)} USDC)`) : "";
|
|
488
|
+
console.error(` Tempo: ${pc.cyan(wallet.evmAddress)}${balHint}`);
|
|
489
|
+
}
|
|
273
490
|
if (hasSolana && wallet.solanaAddress) {
|
|
274
491
|
const balHint = solUsdc > 0 ? pc.dim(` (${solUsdc.toFixed(4)} USDC)`) : "";
|
|
275
492
|
console.error(` Solana: ${pc.cyan(wallet.solanaAddress)}${balHint}`);
|
|
276
493
|
}
|
|
277
494
|
if (hasEvm && !wallet.evmAddress) dim(" Base: endpoint accepts EVM but no EVM wallet configured");
|
|
495
|
+
if (hasMpp && !wallet.evmAddress) dim(" Tempo: endpoint accepts MPP but no EVM wallet configured");
|
|
278
496
|
if (hasSolana && !wallet.solanaAddress) dim(" Solana: endpoint accepts Solana but no Solana wallet configured");
|
|
279
497
|
} else if (hasOther) {
|
|
280
498
|
const networks = [...new Set(accepts.map((a) => a.network))].join(", ");
|
|
@@ -289,21 +507,35 @@ Examples:
|
|
|
289
507
|
return;
|
|
290
508
|
}
|
|
291
509
|
if (payment && isTTY()) {
|
|
292
|
-
|
|
510
|
+
if (usedProtocol === "mpp" && mppPayment) info(` Payment: MPP (${displayNetwork(mppPayment.network)})`);
|
|
511
|
+
else if (x402Payment) info(` Payment: ${x402Payment.amount ? (Number(x402Payment.amount) / 1e6).toFixed(4) : "?"} USDC (${displayNetwork(x402Payment.network ?? "unknown")})`);
|
|
293
512
|
if (txSig) dim(` Tx: ${txSig}`);
|
|
294
513
|
}
|
|
295
514
|
if (isTTY()) dim(` ${response.status} ${response.statusText} (${elapsedMs}ms)`);
|
|
296
|
-
if (
|
|
297
|
-
ensureConfigDir();
|
|
515
|
+
if (x402Payment) {
|
|
298
516
|
const record = {
|
|
299
517
|
t: Date.now(),
|
|
300
518
|
ok: response.ok,
|
|
301
519
|
kind: "x402_payment",
|
|
302
|
-
net:
|
|
520
|
+
net: x402Payment.network ?? "unknown",
|
|
303
521
|
from: wallet.evmAddress ?? wallet.solanaAddress ?? "unknown",
|
|
304
|
-
to:
|
|
522
|
+
to: x402Payment.payTo,
|
|
305
523
|
tx: txSig,
|
|
306
|
-
amount:
|
|
524
|
+
amount: x402Payment.amount ? Number(x402Payment.amount) / 1e6 : void 0,
|
|
525
|
+
token: "USDC",
|
|
526
|
+
ms: elapsedMs,
|
|
527
|
+
label: parsedUrl.hostname
|
|
528
|
+
};
|
|
529
|
+
appendHistory(getHistoryPath(), record);
|
|
530
|
+
} else if (mppPayment) {
|
|
531
|
+
const record = {
|
|
532
|
+
t: Date.now(),
|
|
533
|
+
ok: response.ok,
|
|
534
|
+
kind: "mpp_payment",
|
|
535
|
+
net: mppPayment.network,
|
|
536
|
+
from: wallet.evmAddress ?? "unknown",
|
|
537
|
+
tx: mppPayment.receipt?.reference ?? txSig,
|
|
538
|
+
amount: mppPayment.amount ? Number(mppPayment.amount) : void 0,
|
|
307
539
|
token: "USDC",
|
|
308
540
|
ms: elapsedMs,
|
|
309
541
|
label: parsedUrl.hostname
|
|
@@ -330,8 +562,8 @@ Examples:
|
|
|
330
562
|
//#region src/commands/mcp.ts
|
|
331
563
|
const mcpCommand = buildCommand({
|
|
332
564
|
docs: {
|
|
333
|
-
brief: "Start MCP stdio proxy with
|
|
334
|
-
fullDescription: `Start an MCP stdio proxy with automatic x402
|
|
565
|
+
brief: "Start MCP stdio proxy with automatic payment",
|
|
566
|
+
fullDescription: `Start an MCP stdio proxy with automatic payment (x402 or MPP) for AI agents.
|
|
335
567
|
|
|
336
568
|
Add to your MCP client config (Claude, Cursor, etc.):
|
|
337
569
|
"command": "npx",
|
|
@@ -354,7 +586,13 @@ Add to your MCP client config (Claude, Cursor, etc.):
|
|
|
354
586
|
},
|
|
355
587
|
network: {
|
|
356
588
|
kind: "parsed",
|
|
357
|
-
brief: "Require specific network (base, solana)",
|
|
589
|
+
brief: "Require specific network (base, solana, tempo)",
|
|
590
|
+
parse: String,
|
|
591
|
+
optional: true
|
|
592
|
+
},
|
|
593
|
+
protocol: {
|
|
594
|
+
kind: "parsed",
|
|
595
|
+
brief: "Payment protocol (x402, mpp)",
|
|
358
596
|
parse: String,
|
|
359
597
|
optional: true
|
|
360
598
|
}
|
|
@@ -380,132 +618,244 @@ Add to your MCP client config (Claude, Cursor, etc.):
|
|
|
380
618
|
if (wallet.evmAddress) dim(` EVM: ${wallet.evmAddress}`);
|
|
381
619
|
if (wallet.solanaAddress) dim(` Solana: ${wallet.solanaAddress}`);
|
|
382
620
|
const config = loadConfig();
|
|
383
|
-
|
|
384
|
-
if (!preferredNetwork && wallet.evmAddress && wallet.solanaAddress) {
|
|
385
|
-
const { fetchEvmBalances, fetchSolanaBalances } = await import("../wallet-BoY0fgX7.js");
|
|
386
|
-
const [evmBal, solBal] = await Promise.allSettled([fetchEvmBalances(wallet.evmAddress), fetchSolanaBalances(wallet.solanaAddress)]);
|
|
387
|
-
const evmUsdc = evmBal.status === "fulfilled" ? Number(evmBal.value?.usdc ?? 0) : 0;
|
|
388
|
-
const solUsdc = solBal.status === "fulfilled" ? Number(solBal.value?.usdc ?? 0) : 0;
|
|
389
|
-
if (evmUsdc > solUsdc) preferredNetwork = "base";
|
|
390
|
-
else if (solUsdc > evmUsdc) preferredNetwork = "solana";
|
|
391
|
-
}
|
|
392
|
-
const x402PaymentClient = await buildX402Client(wallet, {
|
|
393
|
-
preferredNetwork,
|
|
394
|
-
network: flags.network,
|
|
395
|
-
spendLimitDaily: config?.spendLimitDaily,
|
|
396
|
-
spendLimitPerTx: config?.spendLimitPerTx
|
|
397
|
-
});
|
|
621
|
+
const resolvedProtocol = flags.protocol ?? config?.preferredProtocol ?? "x402";
|
|
398
622
|
const { Client } = await import("@modelcontextprotocol/sdk/client/index.js");
|
|
399
623
|
const { SSEClientTransport } = await import("@modelcontextprotocol/sdk/client/sse.js");
|
|
400
624
|
const { StreamableHTTPClientTransport } = await import("@modelcontextprotocol/sdk/client/streamableHttp.js");
|
|
401
625
|
const { Server } = await import("@modelcontextprotocol/sdk/server/index.js");
|
|
402
626
|
const { StdioServerTransport } = await import("@modelcontextprotocol/sdk/server/stdio.js");
|
|
403
|
-
const {
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
627
|
+
const { ListToolsRequestSchema, CallToolRequestSchema, ListResourcesRequestSchema, ReadResourceRequestSchema, ToolListChangedNotificationSchema, ResourceListChangedNotificationSchema } = await import("@modelcontextprotocol/sdk/types.js");
|
|
628
|
+
async function connectTransport(target) {
|
|
629
|
+
try {
|
|
630
|
+
const transport = new StreamableHTTPClientTransport(new URL(remoteUrl));
|
|
631
|
+
await target.connect(transport);
|
|
632
|
+
dim(" Connected via StreamableHTTP");
|
|
633
|
+
return;
|
|
634
|
+
} catch {}
|
|
635
|
+
try {
|
|
636
|
+
const transport = new SSEClientTransport(new URL(remoteUrl));
|
|
637
|
+
await target.connect(transport);
|
|
638
|
+
dim(" Connected via SSE");
|
|
639
|
+
} catch (err) {
|
|
640
|
+
error(`Failed to connect to ${remoteUrl}: ${err instanceof Error ? err.message : String(err)}`);
|
|
641
|
+
process.exit(1);
|
|
414
642
|
}
|
|
415
|
-
});
|
|
416
|
-
x402Mcp.onAfterPayment(async (ctx) => {
|
|
417
|
-
ensureConfigDir();
|
|
418
|
-
const accepted = ctx.paymentPayload.accepted;
|
|
419
|
-
const tx = ctx.settleResponse?.transaction;
|
|
420
|
-
const record = {
|
|
421
|
-
t: Date.now(),
|
|
422
|
-
ok: true,
|
|
423
|
-
kind: "x402_payment",
|
|
424
|
-
net: accepted?.network ?? "unknown",
|
|
425
|
-
from: wallet.evmAddress ?? wallet.solanaAddress ?? "unknown",
|
|
426
|
-
to: accepted?.payTo,
|
|
427
|
-
tx: typeof tx === "string" ? tx : void 0,
|
|
428
|
-
amount: accepted?.amount ? Number(accepted.amount) / 1e6 : void 0,
|
|
429
|
-
token: "USDC",
|
|
430
|
-
label: `mcp:${ctx.toolName}`
|
|
431
|
-
};
|
|
432
|
-
appendHistory(getHistoryPath(), record);
|
|
433
|
-
});
|
|
434
|
-
let connected = false;
|
|
435
|
-
try {
|
|
436
|
-
const transport = new StreamableHTTPClientTransport(new URL(remoteUrl));
|
|
437
|
-
await x402Mcp.connect(transport);
|
|
438
|
-
connected = true;
|
|
439
|
-
dim(" Connected via StreamableHTTP");
|
|
440
|
-
} catch {}
|
|
441
|
-
if (!connected) try {
|
|
442
|
-
const transport = new SSEClientTransport(new URL(remoteUrl));
|
|
443
|
-
await x402Mcp.connect(transport);
|
|
444
|
-
connected = true;
|
|
445
|
-
dim(" Connected via SSE");
|
|
446
|
-
} catch (err) {
|
|
447
|
-
error(`Failed to connect to ${remoteUrl}: ${err instanceof Error ? err.message : String(err)}`);
|
|
448
|
-
process.exit(1);
|
|
449
643
|
}
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
644
|
+
if (resolvedProtocol === "mpp") await startMppProxy();
|
|
645
|
+
else await startX402Proxy();
|
|
646
|
+
async function startX402Proxy() {
|
|
647
|
+
let preferredNetwork = config?.defaultNetwork;
|
|
648
|
+
if (!preferredNetwork && wallet.evmAddress && wallet.solanaAddress) {
|
|
649
|
+
const { fetchAllBalances } = await import("../wallet-DjixXCHy.js");
|
|
650
|
+
const balances = await fetchAllBalances(wallet.evmAddress, wallet.solanaAddress);
|
|
651
|
+
const evmUsdc = balances.evm ? Number(balances.evm.usdc) : 0;
|
|
652
|
+
const solUsdc = balances.sol ? Number(balances.sol.usdc) : 0;
|
|
653
|
+
if (evmUsdc > solUsdc) preferredNetwork = "base";
|
|
654
|
+
else if (solUsdc > evmUsdc) preferredNetwork = "solana";
|
|
655
|
+
}
|
|
656
|
+
const x402PaymentClient = await buildX402Client(wallet, {
|
|
657
|
+
preferredNetwork,
|
|
658
|
+
network: flags.network,
|
|
659
|
+
spendLimitDaily: config?.spendLimitDaily,
|
|
660
|
+
spendLimitPerTx: config?.spendLimitPerTx
|
|
661
|
+
});
|
|
662
|
+
const { x402MCPClient } = await import("@x402/mcp");
|
|
663
|
+
const remoteClient = new Client({
|
|
664
|
+
name: "x402-proxy",
|
|
665
|
+
version: "0.7.0"
|
|
666
|
+
});
|
|
667
|
+
const x402Mcp = new x402MCPClient(remoteClient, x402PaymentClient, {
|
|
668
|
+
autoPayment: true,
|
|
669
|
+
onPaymentRequested: (ctx) => {
|
|
670
|
+
const accept = ctx.paymentRequired.accepts?.[0];
|
|
671
|
+
if (accept) warn(` Payment: ${accept.amount ? (Number(accept.amount) / 1e6).toFixed(4) : "?"} USDC on ${displayNetwork(accept.network)} for tool "${ctx.toolName}"`);
|
|
672
|
+
return true;
|
|
673
|
+
}
|
|
674
|
+
});
|
|
675
|
+
x402Mcp.onAfterPayment(async (ctx) => {
|
|
676
|
+
const accepted = ctx.paymentPayload.accepted;
|
|
677
|
+
const tx = ctx.settleResponse?.transaction;
|
|
678
|
+
const record = {
|
|
679
|
+
t: Date.now(),
|
|
680
|
+
ok: true,
|
|
681
|
+
kind: "x402_payment",
|
|
682
|
+
net: accepted?.network ?? "unknown",
|
|
683
|
+
from: wallet.evmAddress ?? wallet.solanaAddress ?? "unknown",
|
|
684
|
+
to: accepted?.payTo,
|
|
685
|
+
tx: typeof tx === "string" ? tx : void 0,
|
|
686
|
+
amount: accepted?.amount ? Number(accepted.amount) / 1e6 : void 0,
|
|
687
|
+
token: "USDC",
|
|
688
|
+
label: `mcp:${ctx.toolName}`
|
|
689
|
+
};
|
|
690
|
+
appendHistory(getHistoryPath(), record);
|
|
691
|
+
});
|
|
692
|
+
await connectTransport(x402Mcp);
|
|
693
|
+
let { tools } = await x402Mcp.listTools();
|
|
694
|
+
dim(` ${tools.length} tools available`);
|
|
695
|
+
let remoteResources = [];
|
|
696
|
+
try {
|
|
697
|
+
remoteResources = (await x402Mcp.listResources()).resources;
|
|
698
|
+
if (remoteResources.length > 0) dim(` ${remoteResources.length} resources available`);
|
|
699
|
+
} catch {
|
|
700
|
+
dim(" Resources not available from remote");
|
|
701
|
+
}
|
|
702
|
+
const localServer = new Server({
|
|
703
|
+
name: "x402-proxy",
|
|
704
|
+
version: "0.7.0"
|
|
705
|
+
}, { capabilities: {
|
|
706
|
+
tools: tools.length > 0 ? {} : void 0,
|
|
707
|
+
resources: remoteResources.length > 0 ? {} : void 0
|
|
708
|
+
} });
|
|
709
|
+
localServer.setRequestHandler(ListToolsRequestSchema, async () => ({ tools: tools.map((t) => ({
|
|
710
|
+
name: t.name,
|
|
711
|
+
description: t.description,
|
|
712
|
+
inputSchema: t.inputSchema,
|
|
713
|
+
annotations: t.annotations
|
|
714
|
+
})) }));
|
|
715
|
+
localServer.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
716
|
+
const { name, arguments: args } = request.params;
|
|
717
|
+
const result = await x402Mcp.callTool(name, args ?? {});
|
|
718
|
+
return {
|
|
719
|
+
content: result.content,
|
|
720
|
+
isError: result.isError
|
|
721
|
+
};
|
|
722
|
+
});
|
|
723
|
+
if (remoteResources.length > 0) {
|
|
724
|
+
localServer.setRequestHandler(ListResourcesRequestSchema, async () => ({ resources: remoteResources.map((r) => ({
|
|
725
|
+
name: r.name,
|
|
726
|
+
uri: r.uri,
|
|
727
|
+
description: r.description,
|
|
728
|
+
mimeType: r.mimeType
|
|
729
|
+
})) }));
|
|
730
|
+
localServer.setRequestHandler(ReadResourceRequestSchema, async (request) => {
|
|
731
|
+
return { contents: (await x402Mcp.readResource({ uri: request.params.uri })).contents.map((c) => ({ ...c })) };
|
|
732
|
+
});
|
|
733
|
+
}
|
|
734
|
+
remoteClient.setNotificationHandler(ToolListChangedNotificationSchema, async () => {
|
|
735
|
+
tools = (await x402Mcp.listTools()).tools;
|
|
736
|
+
dim(` Tools updated: ${tools.length} available`);
|
|
737
|
+
await localServer.notification({ method: "notifications/tools/list_changed" });
|
|
738
|
+
});
|
|
739
|
+
if (remoteResources.length > 0) remoteClient.setNotificationHandler(ResourceListChangedNotificationSchema, async () => {
|
|
740
|
+
remoteResources = (await x402Mcp.listResources()).resources;
|
|
741
|
+
dim(` Resources updated: ${remoteResources.length} available`);
|
|
742
|
+
await localServer.notification({ method: "notifications/resources/list_changed" });
|
|
743
|
+
});
|
|
744
|
+
const stdioTransport = new StdioServerTransport();
|
|
745
|
+
await localServer.connect(stdioTransport);
|
|
746
|
+
dim(" MCP proxy running (stdio, x402)");
|
|
747
|
+
let closing = false;
|
|
748
|
+
const cleanup = async () => {
|
|
749
|
+
if (closing) return;
|
|
750
|
+
closing = true;
|
|
751
|
+
await x402Mcp.close();
|
|
752
|
+
process.exit(0);
|
|
479
753
|
};
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
754
|
+
process.stdin.on("end", cleanup);
|
|
755
|
+
process.on("SIGINT", cleanup);
|
|
756
|
+
process.on("SIGTERM", cleanup);
|
|
757
|
+
}
|
|
758
|
+
async function startMppProxy() {
|
|
759
|
+
if (!wallet.evmKey) {
|
|
760
|
+
error("MPP requires an EVM wallet. Configure one with: npx x402-proxy setup");
|
|
761
|
+
process.exit(1);
|
|
762
|
+
}
|
|
763
|
+
const { tempo } = await import("mppx/client");
|
|
764
|
+
const { McpClient } = await import("mppx/mcp-sdk/client");
|
|
765
|
+
const { privateKeyToAccount } = await import("viem/accounts");
|
|
766
|
+
const account = privateKeyToAccount(wallet.evmKey);
|
|
767
|
+
const maxDeposit = config?.mppSessionBudget ?? "1";
|
|
768
|
+
const remoteClient = new Client({
|
|
769
|
+
name: "x402-proxy",
|
|
770
|
+
version: "0.7.0"
|
|
771
|
+
});
|
|
772
|
+
await connectTransport(remoteClient);
|
|
773
|
+
const mppClient = McpClient.wrap(remoteClient, { methods: [tempo({
|
|
774
|
+
account,
|
|
775
|
+
maxDeposit
|
|
776
|
+
})] });
|
|
777
|
+
let { tools } = await remoteClient.listTools();
|
|
778
|
+
dim(` ${tools.length} tools available`);
|
|
779
|
+
let remoteResources = [];
|
|
780
|
+
try {
|
|
781
|
+
remoteResources = (await remoteClient.listResources()).resources;
|
|
782
|
+
if (remoteResources.length > 0) dim(` ${remoteResources.length} resources available`);
|
|
783
|
+
} catch {
|
|
784
|
+
dim(" Resources not available from remote");
|
|
785
|
+
}
|
|
786
|
+
const localServer = new Server({
|
|
787
|
+
name: "x402-proxy",
|
|
788
|
+
version: "0.7.0"
|
|
789
|
+
}, { capabilities: {
|
|
790
|
+
tools: tools.length > 0 ? {} : void 0,
|
|
791
|
+
resources: remoteResources.length > 0 ? {} : void 0
|
|
792
|
+
} });
|
|
793
|
+
localServer.setRequestHandler(ListToolsRequestSchema, async () => ({ tools: tools.map((t) => ({
|
|
794
|
+
name: t.name,
|
|
795
|
+
description: t.description,
|
|
796
|
+
inputSchema: t.inputSchema,
|
|
797
|
+
annotations: t.annotations
|
|
487
798
|
})) }));
|
|
488
|
-
localServer.setRequestHandler(
|
|
489
|
-
|
|
799
|
+
localServer.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
800
|
+
const { name, arguments: args } = request.params;
|
|
801
|
+
const result = await mppClient.callTool({
|
|
802
|
+
name,
|
|
803
|
+
arguments: args ?? {}
|
|
804
|
+
});
|
|
805
|
+
if (result.receipt) {
|
|
806
|
+
const record = {
|
|
807
|
+
t: Date.now(),
|
|
808
|
+
ok: true,
|
|
809
|
+
kind: "mpp_payment",
|
|
810
|
+
net: TEMPO_NETWORK,
|
|
811
|
+
from: wallet.evmAddress ?? "unknown",
|
|
812
|
+
tx: result.receipt.reference,
|
|
813
|
+
token: "USDC",
|
|
814
|
+
label: `mcp:${name}`
|
|
815
|
+
};
|
|
816
|
+
appendHistory(getHistoryPath(), record);
|
|
817
|
+
warn(` MPP payment for tool "${name}" (Tempo)`);
|
|
818
|
+
}
|
|
819
|
+
return {
|
|
820
|
+
content: result.content,
|
|
821
|
+
isError: result.isError
|
|
822
|
+
};
|
|
490
823
|
});
|
|
824
|
+
if (remoteResources.length > 0) {
|
|
825
|
+
localServer.setRequestHandler(ListResourcesRequestSchema, async () => ({ resources: remoteResources.map((r) => ({
|
|
826
|
+
name: r.name,
|
|
827
|
+
uri: r.uri,
|
|
828
|
+
description: r.description,
|
|
829
|
+
mimeType: r.mimeType
|
|
830
|
+
})) }));
|
|
831
|
+
localServer.setRequestHandler(ReadResourceRequestSchema, async (request) => {
|
|
832
|
+
return { contents: (await remoteClient.readResource({ uri: request.params.uri })).contents.map((c) => ({ ...c })) };
|
|
833
|
+
});
|
|
834
|
+
}
|
|
835
|
+
remoteClient.setNotificationHandler(ToolListChangedNotificationSchema, async () => {
|
|
836
|
+
tools = (await remoteClient.listTools()).tools;
|
|
837
|
+
dim(` Tools updated: ${tools.length} available`);
|
|
838
|
+
await localServer.notification({ method: "notifications/tools/list_changed" });
|
|
839
|
+
});
|
|
840
|
+
if (remoteResources.length > 0) remoteClient.setNotificationHandler(ResourceListChangedNotificationSchema, async () => {
|
|
841
|
+
remoteResources = (await remoteClient.listResources()).resources;
|
|
842
|
+
dim(` Resources updated: ${remoteResources.length} available`);
|
|
843
|
+
await localServer.notification({ method: "notifications/resources/list_changed" });
|
|
844
|
+
});
|
|
845
|
+
const stdioTransport = new StdioServerTransport();
|
|
846
|
+
await localServer.connect(stdioTransport);
|
|
847
|
+
dim(" MCP proxy running (stdio, mpp)");
|
|
848
|
+
let closing = false;
|
|
849
|
+
const cleanup = async () => {
|
|
850
|
+
if (closing) return;
|
|
851
|
+
closing = true;
|
|
852
|
+
await remoteClient.close();
|
|
853
|
+
process.exit(0);
|
|
854
|
+
};
|
|
855
|
+
process.stdin.on("end", cleanup);
|
|
856
|
+
process.on("SIGINT", cleanup);
|
|
857
|
+
process.on("SIGTERM", cleanup);
|
|
491
858
|
}
|
|
492
|
-
remoteClient.setNotificationHandler(ToolListChangedNotificationSchema, async () => {
|
|
493
|
-
tools = (await x402Mcp.listTools()).tools;
|
|
494
|
-
dim(` Tools updated: ${tools.length} available`);
|
|
495
|
-
await localServer.notification({ method: "notifications/tools/list_changed" });
|
|
496
|
-
});
|
|
497
|
-
if (remoteResources.length > 0) remoteClient.setNotificationHandler(ResourceListChangedNotificationSchema, async () => {
|
|
498
|
-
remoteResources = (await x402Mcp.listResources()).resources;
|
|
499
|
-
dim(` Resources updated: ${remoteResources.length} available`);
|
|
500
|
-
await localServer.notification({ method: "notifications/resources/list_changed" });
|
|
501
|
-
});
|
|
502
|
-
const stdioTransport = new StdioServerTransport();
|
|
503
|
-
await localServer.connect(stdioTransport);
|
|
504
|
-
dim(" MCP proxy running (stdio)");
|
|
505
|
-
process.stdin.on("end", async () => {
|
|
506
|
-
await x402Mcp.close();
|
|
507
|
-
process.exit(0);
|
|
508
|
-
});
|
|
509
859
|
}
|
|
510
860
|
});
|
|
511
861
|
|
|
@@ -635,7 +985,7 @@ const routes = buildRouteMap({
|
|
|
635
985
|
});
|
|
636
986
|
const app = buildApplication(routes, {
|
|
637
987
|
name: "x402-proxy",
|
|
638
|
-
versionInfo: { currentVersion: "0.
|
|
988
|
+
versionInfo: { currentVersion: "0.7.0" },
|
|
639
989
|
scanner: { caseStyle: "allow-kebab-for-camel" }
|
|
640
990
|
});
|
|
641
991
|
|