@x402scan/mcp 0.0.7-beta.0 → 0.0.7-beta.1
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/dist/cjs/run-server.cjs +6827 -7114
- package/dist/esm/index.js +1569 -1667
- package/dist/esm/index.js.map +1 -1
- package/package.json +4 -7
package/dist/esm/index.js
CHANGED
|
@@ -1,28 +1,23 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import { ok, resultFromPromise, err, resultFromThrowable } from '@x402scan/neverthrow';
|
|
2
3
|
import z$1, { z } from 'zod';
|
|
3
|
-
import
|
|
4
|
+
import open from 'open';
|
|
5
|
+
import * as fs from 'fs/promises';
|
|
6
|
+
import * as fs2 from 'fs';
|
|
7
|
+
import fs2__default, { existsSync } from 'fs';
|
|
4
8
|
import path2, { join } from 'path';
|
|
5
9
|
import os, { homedir } from 'os';
|
|
6
|
-
import * as fs from 'fs';
|
|
7
|
-
import fs__default, { appendFileSync } from 'fs';
|
|
8
10
|
import { polygon, arbitrum, optimism, sepolia, mainnet, baseSepolia, base } from 'viem/chains';
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import
|
|
12
|
-
import open from 'open';
|
|
13
|
-
import { x402HTTPClient, x402Client } from '@x402/core/client';
|
|
11
|
+
import { getAddress, formatUnits } from 'viem';
|
|
12
|
+
import { encodeSIWxHeader, createSIWxPayload } from '@x402scan/siwx';
|
|
13
|
+
import { x402Client, x402HTTPClient } from '@x402/core/client';
|
|
14
14
|
import { ExactEvmScheme } from '@x402/evm/exact/client';
|
|
15
|
-
import { wrapFetchWithPayment } from '@x402/fetch';
|
|
16
|
-
import { base58 } from '@scure/base';
|
|
17
|
-
import 'tweetnacl';
|
|
18
|
-
import { SiweMessage } from 'siwe';
|
|
19
|
-
import { safeBase64Encode } from '@x402/core/utils';
|
|
20
15
|
import 'url';
|
|
21
|
-
import {
|
|
22
|
-
import * as fs3 from 'fs/promises';
|
|
23
|
-
import { privateKeyToAccount } from 'viem/accounts';
|
|
16
|
+
import { privateKeyToAccount, generatePrivateKey } from 'viem/accounts';
|
|
24
17
|
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
25
18
|
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
19
|
+
import { intro, outro, log as log$1, select, confirm, stream, spinner, text } from '@clack/prompts';
|
|
20
|
+
import chalk2 from 'chalk';
|
|
26
21
|
import process2 from 'process';
|
|
27
22
|
import * as TOML from '@iarna/toml';
|
|
28
23
|
import yaml from 'js-yaml';
|
|
@@ -40,10 +35,10 @@ var __export = (target, all) => {
|
|
|
40
35
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
41
36
|
};
|
|
42
37
|
|
|
43
|
-
// src/install/clients.ts
|
|
38
|
+
// src/cli/install/clients.ts
|
|
44
39
|
var Clients, clientMetadata;
|
|
45
40
|
var init_clients = __esm({
|
|
46
|
-
"src/install/clients.ts"() {
|
|
41
|
+
"src/cli/install/clients.ts"() {
|
|
47
42
|
Clients = /* @__PURE__ */ ((Clients2) => {
|
|
48
43
|
Clients2["ClaudeCode"] = "claude-code";
|
|
49
44
|
Clients2["Cursor"] = "cursor";
|
|
@@ -129,79 +124,389 @@ var init_clients = __esm({
|
|
|
129
124
|
};
|
|
130
125
|
}
|
|
131
126
|
});
|
|
127
|
+
var errorType, fetchErr, fetchHttpErr, safeFetch, safeFetchJson, safeParseResponse, isFetchError;
|
|
128
|
+
var init_fetch = __esm({
|
|
129
|
+
"src/shared/neverthrow/fetch/index.ts"() {
|
|
130
|
+
errorType = "fetch";
|
|
131
|
+
fetchErr = (surface3, error) => err(errorType, surface3, error);
|
|
132
|
+
fetchHttpErr = (surface3, response) => fetchErr(surface3, {
|
|
133
|
+
cause: "http",
|
|
134
|
+
statusCode: response.status,
|
|
135
|
+
message: response.statusText,
|
|
136
|
+
response
|
|
137
|
+
});
|
|
138
|
+
safeFetch = (surface3, request) => {
|
|
139
|
+
return resultFromPromise(
|
|
140
|
+
errorType,
|
|
141
|
+
surface3,
|
|
142
|
+
fetch(request),
|
|
143
|
+
(error) => ({
|
|
144
|
+
cause: "network",
|
|
145
|
+
message: error instanceof Error ? error.message : "Network error"
|
|
146
|
+
})
|
|
147
|
+
);
|
|
148
|
+
};
|
|
149
|
+
safeFetchJson = (surface3, request) => {
|
|
150
|
+
return safeFetch(surface3, request).andThen((response) => {
|
|
151
|
+
if (!response.ok) {
|
|
152
|
+
return fetchHttpErr(surface3, response);
|
|
153
|
+
}
|
|
154
|
+
return resultFromPromise(
|
|
155
|
+
errorType,
|
|
156
|
+
surface3,
|
|
157
|
+
response.json(),
|
|
158
|
+
() => ({
|
|
159
|
+
cause: "parse",
|
|
160
|
+
message: "Could not parse JSON from response",
|
|
161
|
+
statusCode: response.status,
|
|
162
|
+
contentType: response.headers.get("content-type") ?? "Not specified"
|
|
163
|
+
})
|
|
164
|
+
);
|
|
165
|
+
});
|
|
166
|
+
};
|
|
167
|
+
safeParseResponse = (surface3, response) => {
|
|
168
|
+
return resultFromPromise(
|
|
169
|
+
errorType,
|
|
170
|
+
surface3,
|
|
171
|
+
(async () => {
|
|
172
|
+
const contentType = response.headers.get("content-type") ?? "";
|
|
173
|
+
switch (contentType) {
|
|
174
|
+
case "application/json":
|
|
175
|
+
return {
|
|
176
|
+
type: "json",
|
|
177
|
+
data: await response.json()
|
|
178
|
+
};
|
|
179
|
+
case "image/png":
|
|
180
|
+
case "image/jpeg":
|
|
181
|
+
case "image/gif":
|
|
182
|
+
case "image/webp":
|
|
183
|
+
case "image/svg+xml":
|
|
184
|
+
case "image/tiff":
|
|
185
|
+
case "image/bmp":
|
|
186
|
+
case "image/ico":
|
|
187
|
+
return {
|
|
188
|
+
type: "image",
|
|
189
|
+
mimeType: contentType,
|
|
190
|
+
data: await response.arrayBuffer()
|
|
191
|
+
};
|
|
192
|
+
case "audio/":
|
|
193
|
+
return {
|
|
194
|
+
type: "audio",
|
|
195
|
+
mimeType: contentType,
|
|
196
|
+
data: await response.arrayBuffer()
|
|
197
|
+
};
|
|
198
|
+
case "video/":
|
|
199
|
+
return {
|
|
200
|
+
type: "video",
|
|
201
|
+
mimeType: contentType,
|
|
202
|
+
data: await response.arrayBuffer()
|
|
203
|
+
};
|
|
204
|
+
case "application/pdf":
|
|
205
|
+
return {
|
|
206
|
+
type: "pdf",
|
|
207
|
+
mimeType: contentType,
|
|
208
|
+
data: await response.arrayBuffer()
|
|
209
|
+
};
|
|
210
|
+
case "application/octet-stream":
|
|
211
|
+
return {
|
|
212
|
+
type: "octet-stream",
|
|
213
|
+
mimeType: contentType,
|
|
214
|
+
data: await response.arrayBuffer()
|
|
215
|
+
};
|
|
216
|
+
case "multipart/form-data":
|
|
217
|
+
return { type: "formData", data: await response.formData() };
|
|
218
|
+
case "text/":
|
|
219
|
+
return { type: "text", data: await response.text() };
|
|
220
|
+
default:
|
|
221
|
+
throw new Error(`Unsupported content type: ${contentType}`);
|
|
222
|
+
}
|
|
223
|
+
})(),
|
|
224
|
+
(e) => ({
|
|
225
|
+
cause: "parse",
|
|
226
|
+
message: e instanceof Error ? e.message : "Could not parse response",
|
|
227
|
+
statusCode: response.status,
|
|
228
|
+
contentType: response.headers.get("content-type") ?? "Not specified"
|
|
229
|
+
})
|
|
230
|
+
);
|
|
231
|
+
};
|
|
232
|
+
isFetchError = (error) => {
|
|
233
|
+
return error.type === errorType;
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
});
|
|
237
|
+
var type, jsonErr, safeStringifyJson, safeParseJson;
|
|
238
|
+
var init_json = __esm({
|
|
239
|
+
"src/shared/neverthrow/json/index.ts"() {
|
|
240
|
+
type = "json";
|
|
241
|
+
jsonErr = (surface3, error) => {
|
|
242
|
+
return err(type, surface3, error);
|
|
243
|
+
};
|
|
244
|
+
safeStringifyJson = (surface3, value) => {
|
|
245
|
+
return resultFromThrowable(
|
|
246
|
+
type,
|
|
247
|
+
surface3,
|
|
248
|
+
() => JSON.stringify(value),
|
|
249
|
+
() => ({
|
|
250
|
+
cause: "stringify",
|
|
251
|
+
message: "Could not stringify JSON"
|
|
252
|
+
})
|
|
253
|
+
);
|
|
254
|
+
};
|
|
255
|
+
safeParseJson = (surface3, value) => {
|
|
256
|
+
return resultFromThrowable(
|
|
257
|
+
type,
|
|
258
|
+
surface3,
|
|
259
|
+
() => JSON.parse(value),
|
|
260
|
+
(e) => ({
|
|
261
|
+
cause: "parse",
|
|
262
|
+
message: e instanceof Error ? e.message : "Could not parse JSON"
|
|
263
|
+
})
|
|
264
|
+
);
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
// src/server/tools/response/lib.ts
|
|
270
|
+
var parsedResponseToToolContentPart;
|
|
271
|
+
var init_lib = __esm({
|
|
272
|
+
"src/server/tools/response/lib.ts"() {
|
|
273
|
+
parsedResponseToToolContentPart = (data) => {
|
|
274
|
+
switch (data.type) {
|
|
275
|
+
case "json":
|
|
276
|
+
return {
|
|
277
|
+
type: "text",
|
|
278
|
+
text: JSON.stringify(data.data, null, 2)
|
|
279
|
+
};
|
|
280
|
+
case "image":
|
|
281
|
+
return {
|
|
282
|
+
type: "image",
|
|
283
|
+
mimeType: data.mimeType,
|
|
284
|
+
data: Buffer.from(data.data).toString("base64")
|
|
285
|
+
};
|
|
286
|
+
case "audio":
|
|
287
|
+
return {
|
|
288
|
+
type: "audio",
|
|
289
|
+
mimeType: data.mimeType,
|
|
290
|
+
data: Buffer.from(data.data).toString("base64")
|
|
291
|
+
};
|
|
292
|
+
case "text":
|
|
293
|
+
return { type: "text", text: data.data };
|
|
294
|
+
default:
|
|
295
|
+
return {
|
|
296
|
+
type: "text",
|
|
297
|
+
text: `Unsupported response type: ${data.type}`
|
|
298
|
+
};
|
|
299
|
+
}
|
|
300
|
+
};
|
|
301
|
+
}
|
|
302
|
+
});
|
|
132
303
|
|
|
133
|
-
// src/server/
|
|
134
|
-
var
|
|
135
|
-
var
|
|
136
|
-
"src/server/
|
|
137
|
-
|
|
304
|
+
// src/server/tools/response/error.ts
|
|
305
|
+
var buildMcpError, mcpErrorJson, mcpError, mcpErrorFetch;
|
|
306
|
+
var init_error = __esm({
|
|
307
|
+
"src/server/tools/response/error.ts"() {
|
|
308
|
+
init_json();
|
|
309
|
+
init_lib();
|
|
310
|
+
init_fetch();
|
|
311
|
+
buildMcpError = (content) => {
|
|
138
312
|
return {
|
|
139
|
-
content
|
|
313
|
+
content,
|
|
314
|
+
isError: true
|
|
140
315
|
};
|
|
141
316
|
};
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
317
|
+
mcpErrorJson = (error) => {
|
|
318
|
+
return safeStringifyJson("mcp-error-json", error).match(
|
|
319
|
+
(success) => buildMcpError([{ type: "text", text: success }]),
|
|
320
|
+
(error2) => buildMcpError([
|
|
321
|
+
{ type: "text", text: JSON.stringify(error2, null, 2) }
|
|
322
|
+
])
|
|
323
|
+
);
|
|
324
|
+
};
|
|
325
|
+
mcpError = async (err9) => {
|
|
326
|
+
const { error } = err9;
|
|
327
|
+
if (isFetchError(error)) {
|
|
328
|
+
switch (error.cause) {
|
|
329
|
+
case "network":
|
|
330
|
+
case "parse":
|
|
331
|
+
return mcpErrorJson({ ...error });
|
|
332
|
+
case "http":
|
|
333
|
+
const { response, ...rest } = error;
|
|
334
|
+
const parseResponseResult = await safeParseResponse(
|
|
335
|
+
"mcp-error-fetch-parse-response",
|
|
336
|
+
response
|
|
337
|
+
);
|
|
338
|
+
return buildMcpError([
|
|
339
|
+
{ type: "text", text: JSON.stringify(rest, null, 2) },
|
|
340
|
+
...parseResponseResult.match(
|
|
341
|
+
(success) => [parsedResponseToToolContentPart(success)],
|
|
342
|
+
() => []
|
|
343
|
+
)
|
|
344
|
+
]);
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
return mcpErrorJson({ ...error });
|
|
348
|
+
};
|
|
349
|
+
mcpErrorFetch = async (surface3, response) => {
|
|
350
|
+
return mcpError(fetchHttpErr(surface3, response));
|
|
351
|
+
};
|
|
352
|
+
}
|
|
353
|
+
});
|
|
354
|
+
|
|
355
|
+
// src/server/tools/response/success.ts
|
|
356
|
+
var buildMcpSuccess, mcpSuccessJson, mcpSuccessResponse;
|
|
357
|
+
var init_success = __esm({
|
|
358
|
+
"src/server/tools/response/success.ts"() {
|
|
359
|
+
init_json();
|
|
360
|
+
init_error();
|
|
361
|
+
init_lib();
|
|
362
|
+
buildMcpSuccess = (content) => {
|
|
145
363
|
return {
|
|
146
|
-
content
|
|
147
|
-
{
|
|
148
|
-
type: "text",
|
|
149
|
-
text: JSON.stringify(
|
|
150
|
-
{
|
|
151
|
-
error: message,
|
|
152
|
-
...details && { details },
|
|
153
|
-
...context && { context }
|
|
154
|
-
},
|
|
155
|
-
null,
|
|
156
|
-
2
|
|
157
|
-
)
|
|
158
|
-
}
|
|
159
|
-
],
|
|
160
|
-
isError: true
|
|
364
|
+
content
|
|
161
365
|
};
|
|
162
366
|
};
|
|
367
|
+
mcpSuccessJson = (data) => {
|
|
368
|
+
return safeStringifyJson("mcp-success-text", data).match(
|
|
369
|
+
(success) => buildMcpSuccess([{ type: "text", text: success }]),
|
|
370
|
+
(error) => mcpErrorJson(error)
|
|
371
|
+
);
|
|
372
|
+
};
|
|
373
|
+
mcpSuccessResponse = (data, extra) => {
|
|
374
|
+
const parsedExtra = extra ? safeStringifyJson("mcp-success-extra", extra).match(
|
|
375
|
+
(success) => success,
|
|
376
|
+
() => void 0
|
|
377
|
+
) : void 0;
|
|
378
|
+
return buildMcpSuccess([
|
|
379
|
+
parsedResponseToToolContentPart(data),
|
|
380
|
+
...parsedExtra ? [{ type: "text", text: parsedExtra }] : []
|
|
381
|
+
]);
|
|
382
|
+
};
|
|
383
|
+
}
|
|
384
|
+
});
|
|
385
|
+
|
|
386
|
+
// src/server/tools/response/index.ts
|
|
387
|
+
var init_response = __esm({
|
|
388
|
+
"src/server/tools/response/index.ts"() {
|
|
389
|
+
init_success();
|
|
390
|
+
init_error();
|
|
163
391
|
}
|
|
164
392
|
});
|
|
165
|
-
var
|
|
166
|
-
var
|
|
167
|
-
"src/server/lib/
|
|
168
|
-
ethereumAddressSchema = z$1.string().regex(/^0x[a-fA-F0-9]{40}$/, "Invalid Ethereum address").transform((address) => getAddress(address));
|
|
169
|
-
ethereumPrivateKeySchema = z$1.string().regex(/^0x[a-fA-F0-9]{64}$/, "Invalid Ethereum private key").transform((privateKey) => privateKey);
|
|
393
|
+
var requestSchema, buildRequest;
|
|
394
|
+
var init_request = __esm({
|
|
395
|
+
"src/server/tools/lib/request.ts"() {
|
|
170
396
|
requestSchema = z$1.object({
|
|
171
397
|
url: z$1.url().describe("The endpoint URL"),
|
|
172
398
|
method: z$1.enum(["GET", "POST", "PUT", "DELETE", "PATCH"]).default("GET").describe("HTTP method"),
|
|
173
|
-
body: z$1.unknown().optional().describe("Request body for POST/PUT/PATCH methods")
|
|
174
|
-
});
|
|
175
|
-
requestWithHeadersSchema = requestSchema.extend({
|
|
399
|
+
body: z$1.unknown().optional().describe("Request body for POST/PUT/PATCH methods"),
|
|
176
400
|
headers: z$1.record(z$1.string(), z$1.string()).optional().describe("Additional headers to include").default({})
|
|
177
401
|
});
|
|
402
|
+
buildRequest = (input) => {
|
|
403
|
+
return new Request(input.url, {
|
|
404
|
+
method: input.method,
|
|
405
|
+
body: input.body ? typeof input.body === "string" ? input.body : JSON.stringify(input.body) : void 0,
|
|
406
|
+
headers: {
|
|
407
|
+
...input.body ? { "Content-Type": "application/json" } : {},
|
|
408
|
+
...input.headers
|
|
409
|
+
}
|
|
410
|
+
});
|
|
411
|
+
};
|
|
412
|
+
}
|
|
413
|
+
});
|
|
414
|
+
var getBaseUrl, getDepositLink, openDepositLink;
|
|
415
|
+
var init_utils = __esm({
|
|
416
|
+
"src/shared/utils.ts"() {
|
|
417
|
+
getBaseUrl = (dev) => {
|
|
418
|
+
return dev ? "http://localhost:3000" : "https://x402scan.com";
|
|
419
|
+
};
|
|
420
|
+
getDepositLink = (address, flags) => {
|
|
421
|
+
return `${getBaseUrl(flags.dev)}/mcp/deposit/${address}`;
|
|
422
|
+
};
|
|
423
|
+
openDepositLink = async (address, flags) => {
|
|
424
|
+
const depositLink = getDepositLink(address, flags);
|
|
425
|
+
await open(depositLink);
|
|
426
|
+
};
|
|
178
427
|
}
|
|
179
428
|
});
|
|
180
429
|
|
|
181
|
-
// src/
|
|
182
|
-
var
|
|
183
|
-
|
|
430
|
+
// src/shared/balance.ts
|
|
431
|
+
var getBalance;
|
|
432
|
+
var init_balance = __esm({
|
|
433
|
+
"src/shared/balance.ts"() {
|
|
434
|
+
init_utils();
|
|
435
|
+
init_fetch();
|
|
436
|
+
getBalance = async ({
|
|
437
|
+
address,
|
|
438
|
+
flags,
|
|
439
|
+
surface: surface3
|
|
440
|
+
}) => {
|
|
441
|
+
const url = `${getBaseUrl(flags.dev)}/api/rpc/balance/${address}`;
|
|
442
|
+
const res = await safeFetchJson(
|
|
443
|
+
surface3,
|
|
444
|
+
new Request(url, {
|
|
445
|
+
method: "GET",
|
|
446
|
+
headers: {
|
|
447
|
+
accept: "application/json"
|
|
448
|
+
}
|
|
449
|
+
})
|
|
450
|
+
);
|
|
451
|
+
return res;
|
|
452
|
+
};
|
|
184
453
|
}
|
|
185
454
|
});
|
|
186
|
-
var
|
|
455
|
+
var errorType2, fsErr, fsResultFromPromise, safeReadFile, safeWriteFile, safeAppendFile, safeChmod, safeFileExists;
|
|
187
456
|
var init_fs = __esm({
|
|
188
|
-
"src/
|
|
457
|
+
"src/shared/neverthrow/fs/index.ts"() {
|
|
458
|
+
errorType2 = "fs";
|
|
459
|
+
fsErr = (surface3, error) => err(errorType2, surface3, error);
|
|
460
|
+
fsResultFromPromise = (surface3, promise, error) => resultFromPromise(errorType2, surface3, promise, error);
|
|
461
|
+
safeReadFile = (surface3, path3) => fsResultFromPromise(surface3, fs.readFile(path3, "utf-8"), () => ({
|
|
462
|
+
cause: "file_not_readable",
|
|
463
|
+
message: "Failed to read file"
|
|
464
|
+
}));
|
|
465
|
+
safeWriteFile = (surface3, path3, data) => fsResultFromPromise(surface3, fs.writeFile(path3, data), () => ({
|
|
466
|
+
cause: "file_not_writable",
|
|
467
|
+
message: "Failed to write file"
|
|
468
|
+
}));
|
|
469
|
+
safeAppendFile = (surface3, path3, data) => fsResultFromPromise(surface3, fs.appendFile(path3, data), () => ({
|
|
470
|
+
cause: "file_not_writable",
|
|
471
|
+
message: "Failed to append file"
|
|
472
|
+
}));
|
|
473
|
+
safeChmod = (surface3, path3, mode) => fsResultFromPromise(surface3, fs.chmod(path3, mode), () => ({
|
|
474
|
+
cause: "file_not_chmodable",
|
|
475
|
+
message: "Failed to chmod file"
|
|
476
|
+
}));
|
|
477
|
+
safeFileExists = (surface3, path3) => {
|
|
478
|
+
const fileExists = existsSync(path3);
|
|
479
|
+
if (fileExists) {
|
|
480
|
+
return ok(true);
|
|
481
|
+
}
|
|
482
|
+
return err(errorType2, surface3, {
|
|
483
|
+
cause: "file_not_found",
|
|
484
|
+
message: "File not found"
|
|
485
|
+
});
|
|
486
|
+
};
|
|
487
|
+
}
|
|
488
|
+
});
|
|
489
|
+
var BASE_DIRECTORY, configFile;
|
|
490
|
+
var init_fs2 = __esm({
|
|
491
|
+
"src/shared/fs.ts"() {
|
|
189
492
|
BASE_DIRECTORY = join(homedir(), ".x402scan-mcp");
|
|
190
|
-
if (!
|
|
191
|
-
|
|
493
|
+
if (!fs2.existsSync(BASE_DIRECTORY)) {
|
|
494
|
+
fs2.mkdirSync(BASE_DIRECTORY, { recursive: true });
|
|
192
495
|
}
|
|
193
|
-
configFile = (name) => {
|
|
194
|
-
if (!
|
|
195
|
-
|
|
496
|
+
configFile = (name, defaultValue) => {
|
|
497
|
+
if (!fs2.existsSync(BASE_DIRECTORY)) {
|
|
498
|
+
fs2.mkdirSync(BASE_DIRECTORY, { recursive: true });
|
|
196
499
|
}
|
|
197
500
|
const filePath = join(BASE_DIRECTORY, name);
|
|
198
|
-
if (!
|
|
199
|
-
|
|
501
|
+
if (!fs2.existsSync(filePath)) {
|
|
502
|
+
fs2.writeFileSync(filePath, defaultValue);
|
|
200
503
|
}
|
|
201
504
|
return filePath;
|
|
202
505
|
};
|
|
203
506
|
}
|
|
204
507
|
});
|
|
508
|
+
|
|
509
|
+
// src/shared/log.ts
|
|
205
510
|
function format(args) {
|
|
206
511
|
return args.map(
|
|
207
512
|
(a) => typeof a === "object" && a !== null ? JSON.stringify(a) : String(a)
|
|
@@ -211,19 +516,17 @@ function write(level, msg, args) {
|
|
|
211
516
|
const formatted = args.length ? `${msg} ${format(args)}` : msg;
|
|
212
517
|
const line = `[${(/* @__PURE__ */ new Date()).toISOString()}] [${level}] ${formatted}
|
|
213
518
|
`;
|
|
214
|
-
|
|
215
|
-
appendFileSync(LOG_FILE, line);
|
|
216
|
-
} catch {
|
|
217
|
-
}
|
|
519
|
+
safeAppendFile("log", LOG_FILE, line);
|
|
218
520
|
if (process.env.X402_DEBUG === "true") {
|
|
219
521
|
console.error(`[x402scan] ${formatted}`);
|
|
220
522
|
}
|
|
221
523
|
}
|
|
222
524
|
var LOG_FILE, DEBUG, log;
|
|
223
525
|
var init_log = __esm({
|
|
224
|
-
"src/
|
|
526
|
+
"src/shared/log.ts"() {
|
|
225
527
|
init_fs();
|
|
226
|
-
|
|
528
|
+
init_fs2();
|
|
529
|
+
LOG_FILE = configFile("mcp.log", "");
|
|
227
530
|
DEBUG = process.env.X402_DEBUG === "true";
|
|
228
531
|
log = {
|
|
229
532
|
info: (msg, ...args) => write("INFO", msg, args),
|
|
@@ -233,6 +536,53 @@ var init_log = __esm({
|
|
|
233
536
|
};
|
|
234
537
|
}
|
|
235
538
|
});
|
|
539
|
+
|
|
540
|
+
// src/server/tools/lib/check-balance.ts
|
|
541
|
+
var checkBalance;
|
|
542
|
+
var init_check_balance = __esm({
|
|
543
|
+
"src/server/tools/lib/check-balance.ts"() {
|
|
544
|
+
init_balance();
|
|
545
|
+
init_utils();
|
|
546
|
+
init_log();
|
|
547
|
+
checkBalance = async ({
|
|
548
|
+
server,
|
|
549
|
+
address,
|
|
550
|
+
amountNeeded,
|
|
551
|
+
message,
|
|
552
|
+
flags,
|
|
553
|
+
surface: surface3
|
|
554
|
+
}) => {
|
|
555
|
+
const balanceResult = await getBalance({ address, flags, surface: surface3 });
|
|
556
|
+
if (balanceResult.isErr()) {
|
|
557
|
+
log.error(JSON.stringify(balanceResult.error, null, 2));
|
|
558
|
+
return;
|
|
559
|
+
}
|
|
560
|
+
const balance = balanceResult.value;
|
|
561
|
+
if (balance.balance < amountNeeded) {
|
|
562
|
+
const capabilities = server.server.getClientCapabilities();
|
|
563
|
+
if (!capabilities?.elicitation) {
|
|
564
|
+
throw new Error(
|
|
565
|
+
`${message(balance.balance)}
|
|
566
|
+
|
|
567
|
+
You can deposit USDC at ${getDepositLink(address, flags)}`
|
|
568
|
+
);
|
|
569
|
+
}
|
|
570
|
+
const result = await server.server.elicitInput({
|
|
571
|
+
mode: "form",
|
|
572
|
+
message: message(balance.balance),
|
|
573
|
+
requestedSchema: {
|
|
574
|
+
type: "object",
|
|
575
|
+
properties: {}
|
|
576
|
+
}
|
|
577
|
+
});
|
|
578
|
+
if (result.action === "accept") {
|
|
579
|
+
await openDepositLink(address, flags);
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
return balance.balance;
|
|
583
|
+
};
|
|
584
|
+
}
|
|
585
|
+
});
|
|
236
586
|
function toCaip2(network) {
|
|
237
587
|
if (network.startsWith("eip155:")) return network;
|
|
238
588
|
return V1_TO_CAIP2[network.toLowerCase()] ?? network;
|
|
@@ -245,7 +595,7 @@ function getChainName(network) {
|
|
|
245
595
|
}
|
|
246
596
|
var CHAIN_CONFIGS, V1_TO_CAIP2, DEFAULT_NETWORK;
|
|
247
597
|
var init_networks = __esm({
|
|
248
|
-
"src/
|
|
598
|
+
"src/shared/networks.ts"() {
|
|
249
599
|
CHAIN_CONFIGS = {
|
|
250
600
|
"eip155:8453": {
|
|
251
601
|
chain: base,
|
|
@@ -304,841 +654,316 @@ var init_networks = __esm({
|
|
|
304
654
|
});
|
|
305
655
|
var tokenStringToNumber;
|
|
306
656
|
var init_token = __esm({
|
|
307
|
-
"src/
|
|
657
|
+
"src/shared/token.ts"() {
|
|
308
658
|
tokenStringToNumber = (amount, decimals = 6) => {
|
|
309
659
|
return Number(formatUnits(BigInt(amount), decimals));
|
|
310
660
|
};
|
|
311
661
|
}
|
|
312
662
|
});
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
663
|
+
var errorType3, x402Err, x402ResultFromPromise, x402ResultFromThrowable, safeGetPaymentRequired, safeCreatePaymentPayload, safeGetPaymentSettlement, safeCreateSIWxPayload;
|
|
664
|
+
var init_x402 = __esm({
|
|
665
|
+
"src/shared/neverthrow/x402/index.ts"() {
|
|
666
|
+
errorType3 = "x402";
|
|
667
|
+
x402Err = (cause, error) => err(errorType3, cause, error);
|
|
668
|
+
x402ResultFromPromise = (surface3, promise, error) => resultFromPromise(errorType3, surface3, promise, error);
|
|
669
|
+
x402ResultFromThrowable = (surface3, fn, error) => resultFromThrowable(errorType3, surface3, fn, error);
|
|
670
|
+
safeGetPaymentRequired = (surface3, client, response) => {
|
|
671
|
+
return x402ResultFromPromise(
|
|
672
|
+
surface3,
|
|
673
|
+
response.json().then(
|
|
674
|
+
(json) => client.getPaymentRequiredResponse(
|
|
675
|
+
(name) => response.headers.get(name),
|
|
676
|
+
json
|
|
677
|
+
),
|
|
678
|
+
() => client.getPaymentRequiredResponse((name) => response.headers.get(name))
|
|
679
|
+
),
|
|
680
|
+
(error) => ({
|
|
681
|
+
cause: "parse_payment_required",
|
|
682
|
+
message: error instanceof Error ? error.message : "Failed to parse payment required"
|
|
683
|
+
})
|
|
684
|
+
);
|
|
685
|
+
};
|
|
686
|
+
safeCreatePaymentPayload = (surface3, client, paymentRequired) => {
|
|
687
|
+
return x402ResultFromPromise(
|
|
688
|
+
surface3,
|
|
689
|
+
client.createPaymentPayload(paymentRequired),
|
|
690
|
+
(error) => ({
|
|
691
|
+
cause: "create_payment_payload",
|
|
692
|
+
message: error instanceof Error ? error.message : "Failed to create payment payload"
|
|
693
|
+
})
|
|
694
|
+
);
|
|
695
|
+
};
|
|
696
|
+
safeGetPaymentSettlement = (surface3, client, response) => {
|
|
697
|
+
return x402ResultFromThrowable(
|
|
698
|
+
surface3,
|
|
699
|
+
() => client.getPaymentSettleResponse((name) => response.headers.get(name)),
|
|
700
|
+
(error) => ({
|
|
701
|
+
cause: "get_payment_settlement",
|
|
702
|
+
message: error instanceof Error ? error.message : "Failed to get payment settlement"
|
|
703
|
+
})
|
|
704
|
+
);
|
|
705
|
+
};
|
|
706
|
+
safeCreateSIWxPayload = (surface3, serverInfo, signer) => {
|
|
707
|
+
return x402ResultFromPromise(
|
|
708
|
+
surface3,
|
|
709
|
+
createSIWxPayload(serverInfo, signer),
|
|
710
|
+
(error) => ({
|
|
711
|
+
cause: "create_siwx_payload",
|
|
712
|
+
message: error instanceof Error ? error.message : "Failed to create SIWX payload"
|
|
713
|
+
})
|
|
714
|
+
);
|
|
320
715
|
};
|
|
321
716
|
}
|
|
322
717
|
});
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
718
|
+
function safeWrapFetchWithPayment(client) {
|
|
719
|
+
return async (input, init) => {
|
|
720
|
+
const request = new Request(input, init);
|
|
721
|
+
const clonedRequest = request.clone();
|
|
722
|
+
const probeResult = await safeFetch(toolName, request);
|
|
723
|
+
if (probeResult.isErr()) {
|
|
724
|
+
return probeResult;
|
|
329
725
|
}
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
726
|
+
if (probeResult.value.status !== 402) {
|
|
727
|
+
return probeResult;
|
|
728
|
+
}
|
|
729
|
+
const response = probeResult.value;
|
|
730
|
+
const paymentRequiredResult = await safeGetPaymentRequired(
|
|
731
|
+
toolName,
|
|
732
|
+
client,
|
|
733
|
+
response
|
|
334
734
|
);
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
735
|
+
if (paymentRequiredResult.isErr()) {
|
|
736
|
+
return paymentRequiredResult;
|
|
737
|
+
}
|
|
738
|
+
const paymentRequired = paymentRequiredResult.value;
|
|
739
|
+
const paymentPayloadResult = await safeCreatePaymentPayload(
|
|
740
|
+
toolName,
|
|
741
|
+
client,
|
|
742
|
+
paymentRequired
|
|
342
743
|
);
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
744
|
+
if (paymentPayloadResult.isErr()) {
|
|
745
|
+
return paymentPayloadResult;
|
|
746
|
+
}
|
|
747
|
+
const paymentPayload = paymentPayloadResult.value;
|
|
748
|
+
const paymentHeaders = client.encodePaymentSignatureHeader(paymentPayload);
|
|
749
|
+
if (clonedRequest.headers.has("PAYMENT-SIGNATURE") || clonedRequest.headers.has("X-PAYMENT")) {
|
|
750
|
+
return x402Err(toolName, {
|
|
751
|
+
cause: "payment_already_attempted",
|
|
752
|
+
message: "Payment already attempted"
|
|
753
|
+
});
|
|
754
|
+
}
|
|
755
|
+
for (const [key, value] of Object.entries(paymentHeaders)) {
|
|
756
|
+
clonedRequest.headers.set(key, value);
|
|
757
|
+
}
|
|
758
|
+
clonedRequest.headers.set(
|
|
759
|
+
"Access-Control-Expose-Headers",
|
|
760
|
+
"PAYMENT-RESPONSE,X-PAYMENT-RESPONSE"
|
|
348
761
|
);
|
|
349
|
-
|
|
350
|
-
return {
|
|
351
|
-
balanceFormatted: result.data.balance,
|
|
352
|
-
balanceRaw: result.data.rawBalance
|
|
762
|
+
return await safeFetch(toolName, clonedRequest);
|
|
353
763
|
};
|
|
354
764
|
}
|
|
355
|
-
var
|
|
356
|
-
var
|
|
357
|
-
"src/
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
)
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
765
|
+
var toolName, registerFetchX402ResourceTool;
|
|
766
|
+
var init_x402_fetch = __esm({
|
|
767
|
+
"src/server/tools/x402-fetch.ts"() {
|
|
768
|
+
init_fetch();
|
|
769
|
+
init_response();
|
|
770
|
+
init_request();
|
|
771
|
+
init_check_balance();
|
|
772
|
+
init_networks();
|
|
773
|
+
init_token();
|
|
774
|
+
init_x402();
|
|
775
|
+
toolName = "fetch";
|
|
776
|
+
registerFetchX402ResourceTool = ({
|
|
777
|
+
server,
|
|
778
|
+
account,
|
|
779
|
+
flags
|
|
780
|
+
}) => {
|
|
781
|
+
server.registerTool(
|
|
782
|
+
toolName,
|
|
783
|
+
{
|
|
784
|
+
description: "Makes an http fetch request. If the request is to an x402-protected resource, it will handle payment automatically.",
|
|
785
|
+
inputSchema: requestSchema
|
|
786
|
+
},
|
|
787
|
+
async (input) => {
|
|
788
|
+
const coreClient = x402Client.fromConfig({
|
|
789
|
+
schemes: [
|
|
790
|
+
{ network: DEFAULT_NETWORK, client: new ExactEvmScheme(account) }
|
|
791
|
+
]
|
|
792
|
+
});
|
|
793
|
+
coreClient.onBeforePaymentCreation(async ({ selectedRequirements }) => {
|
|
794
|
+
const amount = tokenStringToNumber(selectedRequirements.amount);
|
|
795
|
+
await checkBalance({
|
|
796
|
+
surface: toolName,
|
|
797
|
+
server,
|
|
798
|
+
address: account.address,
|
|
799
|
+
amountNeeded: amount,
|
|
800
|
+
message: (balance) => `This request costs ${amount} USDC. Your current balance is ${balance} USDC.`,
|
|
801
|
+
flags
|
|
802
|
+
});
|
|
803
|
+
});
|
|
804
|
+
const client = new x402HTTPClient(coreClient);
|
|
805
|
+
const fetchWithPay = safeWrapFetchWithPayment(client);
|
|
806
|
+
const fetchResult = await fetchWithPay(buildRequest(input));
|
|
807
|
+
if (fetchResult.isErr()) {
|
|
808
|
+
return mcpError(fetchResult);
|
|
809
|
+
}
|
|
810
|
+
const response = fetchResult.value;
|
|
811
|
+
if (!response.ok) {
|
|
812
|
+
return mcpErrorFetch(toolName, response);
|
|
813
|
+
}
|
|
814
|
+
const parseResponseResult = await safeParseResponse(toolName, response);
|
|
815
|
+
if (parseResponseResult.isErr()) {
|
|
816
|
+
return mcpError(parseResponseResult);
|
|
817
|
+
}
|
|
818
|
+
const settlementResult = safeGetPaymentSettlement(
|
|
819
|
+
toolName,
|
|
820
|
+
client,
|
|
821
|
+
response
|
|
822
|
+
);
|
|
823
|
+
return mcpSuccessResponse(
|
|
824
|
+
parseResponseResult.value,
|
|
825
|
+
settlementResult.isOk() ? { payment: settlementResult.value } : void 0
|
|
404
826
|
);
|
|
405
827
|
}
|
|
406
|
-
|
|
407
|
-
response.json(),
|
|
408
|
-
(error) => ({
|
|
409
|
-
type: "parse",
|
|
410
|
-
message: "Could not parse JSON from response",
|
|
411
|
-
error: error instanceof Error ? error : new Error(String(error))
|
|
412
|
-
})
|
|
413
|
-
);
|
|
414
|
-
});
|
|
828
|
+
);
|
|
415
829
|
};
|
|
416
830
|
}
|
|
417
831
|
});
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
getState = () => {
|
|
428
|
-
if (!fs__default.existsSync(STATE_FILE)) {
|
|
429
|
-
fs__default.writeFileSync(STATE_FILE, JSON.stringify({}));
|
|
430
|
-
return {};
|
|
832
|
+
|
|
833
|
+
// src/server/lib/x402-extensions.ts
|
|
834
|
+
var getBazaarExtension, getInputSchema, getSiwxExtension;
|
|
835
|
+
var init_x402_extensions = __esm({
|
|
836
|
+
"src/server/lib/x402-extensions.ts"() {
|
|
837
|
+
getBazaarExtension = (extensions) => {
|
|
838
|
+
const { bazaar } = extensions ?? {};
|
|
839
|
+
if (!bazaar) {
|
|
840
|
+
return void 0;
|
|
431
841
|
}
|
|
432
|
-
|
|
433
|
-
JSON.parse(fs__default.readFileSync(STATE_FILE, "utf-8"))
|
|
434
|
-
);
|
|
435
|
-
if (!result.success) {
|
|
436
|
-
log.error("Failed to parse state", { error: result.error });
|
|
437
|
-
return {};
|
|
438
|
-
}
|
|
439
|
-
return result.data;
|
|
440
|
-
};
|
|
441
|
-
setState = (state) => {
|
|
442
|
-
const existing = getState();
|
|
443
|
-
const newState = stateSchema.parse({ ...existing, ...state });
|
|
444
|
-
fs__default.writeFileSync(STATE_FILE, JSON.stringify(newState, null, 2));
|
|
445
|
-
};
|
|
446
|
-
}
|
|
447
|
-
});
|
|
448
|
-
var redeemInviteCode;
|
|
449
|
-
var init_redeem_invite = __esm({
|
|
450
|
-
"src/lib/redeem-invite.ts"() {
|
|
451
|
-
init_safe_fetch();
|
|
452
|
-
init_utils();
|
|
453
|
-
init_state();
|
|
454
|
-
redeemInviteCode = async ({
|
|
455
|
-
code,
|
|
456
|
-
dev,
|
|
457
|
-
address
|
|
458
|
-
}) => {
|
|
459
|
-
const state = getState();
|
|
460
|
-
if (state.redeemedCodes?.includes(code)) {
|
|
461
|
-
return Promise.resolve(
|
|
462
|
-
errAsync({
|
|
463
|
-
success: false,
|
|
464
|
-
message: "This invite code has already been redeemed"
|
|
465
|
-
})
|
|
466
|
-
);
|
|
467
|
-
}
|
|
468
|
-
return await safeFetchJson(
|
|
469
|
-
`${getBaseUrl(dev)}/api/invite/redeem`,
|
|
470
|
-
{
|
|
471
|
-
method: "POST",
|
|
472
|
-
headers: {
|
|
473
|
-
"Content-Type": "application/json"
|
|
474
|
-
},
|
|
475
|
-
body: JSON.stringify({
|
|
476
|
-
code,
|
|
477
|
-
recipientAddr: address
|
|
478
|
-
})
|
|
479
|
-
},
|
|
480
|
-
({ message }) => message
|
|
481
|
-
).andTee((result) => {
|
|
482
|
-
if (result.success) {
|
|
483
|
-
setState({
|
|
484
|
-
redeemedCodes: [...state.redeemedCodes ?? [], code]
|
|
485
|
-
});
|
|
486
|
-
}
|
|
487
|
-
});
|
|
842
|
+
return bazaar;
|
|
488
843
|
};
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
"src/lib/deposit.ts"() {
|
|
494
|
-
init_networks();
|
|
495
|
-
init_wait();
|
|
496
|
-
init_redeem_invite();
|
|
497
|
-
init_utils();
|
|
498
|
-
getDepositLink = (address, flags) => {
|
|
499
|
-
return `${getBaseUrl(flags.dev)}/mcp/deposit/${address}`;
|
|
500
|
-
};
|
|
501
|
-
openDepositLink = async (address, flags) => {
|
|
502
|
-
const depositLink = getDepositLink(address, flags);
|
|
503
|
-
await open(depositLink);
|
|
504
|
-
};
|
|
505
|
-
redeemInviteCodePrompt = async (address, flags) => {
|
|
506
|
-
const code = await text({
|
|
507
|
-
message: "Enter your invite code",
|
|
508
|
-
placeholder: "MRT-XXXXX",
|
|
509
|
-
validate: (value) => {
|
|
510
|
-
if (!value || value.trim().length === 0) {
|
|
511
|
-
return "Please enter an invite code";
|
|
512
|
-
}
|
|
513
|
-
}
|
|
514
|
-
});
|
|
515
|
-
if (typeof code !== "string") {
|
|
516
|
-
return false;
|
|
517
|
-
}
|
|
518
|
-
const s = spinner();
|
|
519
|
-
s.start("Redeeming invite code...");
|
|
520
|
-
const result = await redeemInviteCode({ code, dev: flags.dev, address });
|
|
521
|
-
return result.match(
|
|
522
|
-
async ({ data: { amount, txHash } }) => {
|
|
523
|
-
s.stop("Invite code redeemed successfully!");
|
|
524
|
-
await wait({
|
|
525
|
-
startText: "Processing...",
|
|
526
|
-
stopText: chalk.green(
|
|
527
|
-
`${chalk.bold(amount)} USDC has been sent to your wallet!`
|
|
528
|
-
),
|
|
529
|
-
ms: 1500
|
|
530
|
-
});
|
|
531
|
-
log$1.success(
|
|
532
|
-
chalk.bold(`Your wallet has been funded with ${amount} USDC`)
|
|
533
|
-
);
|
|
534
|
-
if (txHash) {
|
|
535
|
-
log$1.info(chalk.dim(`Transaction: https://basescan.org/tx/${txHash}`));
|
|
536
|
-
}
|
|
537
|
-
return true;
|
|
538
|
-
},
|
|
539
|
-
(error) => {
|
|
540
|
-
s.stop("Invite code redemption failed");
|
|
541
|
-
log$1.warning(
|
|
542
|
-
chalk.yellow(`Failed to redeem invite code: ${error?.message}`)
|
|
543
|
-
);
|
|
544
|
-
return false;
|
|
545
|
-
}
|
|
546
|
-
);
|
|
547
|
-
};
|
|
548
|
-
promptDeposit = async (address, flags) => {
|
|
549
|
-
const depositLink = getDepositLink(address, flags);
|
|
550
|
-
const depositChoice = flags.yes ? "manual" : await select({
|
|
551
|
-
message: chalk.bold("How would you like to deposit?"),
|
|
552
|
-
initialValue: "guided",
|
|
553
|
-
options: [
|
|
554
|
-
{
|
|
555
|
-
label: "Guided - Recommended",
|
|
556
|
-
value: "guided",
|
|
557
|
-
hint: "Online portal in x402scan"
|
|
558
|
-
},
|
|
559
|
-
{
|
|
560
|
-
label: "Manual",
|
|
561
|
-
value: "manual",
|
|
562
|
-
hint: "Print deposit instructions"
|
|
563
|
-
},
|
|
564
|
-
{
|
|
565
|
-
label: "Redeem Invite Code",
|
|
566
|
-
value: "invite",
|
|
567
|
-
hint: "Enter an invite code for starter money"
|
|
568
|
-
},
|
|
569
|
-
{
|
|
570
|
-
label: "Skip",
|
|
571
|
-
value: void 0,
|
|
572
|
-
hint: "Skip deposit process - functionality limited"
|
|
573
|
-
}
|
|
574
|
-
]
|
|
575
|
-
});
|
|
576
|
-
if (depositChoice === "guided") {
|
|
577
|
-
await wait({
|
|
578
|
-
startText: "Opening deposit page...",
|
|
579
|
-
stopText: `Opening ${chalk.underline.hex("#2563eb")(depositLink)}`,
|
|
580
|
-
ms: 1e3
|
|
581
|
-
});
|
|
582
|
-
await open(depositLink);
|
|
583
|
-
} else if (depositChoice === "manual") {
|
|
584
|
-
log$1.step(chalk.bold("Account Information"));
|
|
585
|
-
log$1.message(`Address: ${address}`);
|
|
586
|
-
log$1.message(`Network: ${getChainName(DEFAULT_NETWORK)}`);
|
|
587
|
-
log$1.step(chalk.bold("Online Portal"));
|
|
588
|
-
log$1.message(`${chalk.underline(depositLink)}`);
|
|
589
|
-
} else if (depositChoice === "invite") {
|
|
590
|
-
const redeemed = await redeemInviteCodePrompt(address, flags);
|
|
591
|
-
if (!redeemed) {
|
|
592
|
-
await promptDeposit(address, flags);
|
|
593
|
-
}
|
|
594
|
-
}
|
|
595
|
-
};
|
|
596
|
-
}
|
|
597
|
-
});
|
|
598
|
-
|
|
599
|
-
// src/server/lib/check-balance.ts
|
|
600
|
-
var checkBalance;
|
|
601
|
-
var init_check_balance = __esm({
|
|
602
|
-
"src/server/lib/check-balance.ts"() {
|
|
603
|
-
init_balance();
|
|
604
|
-
init_deposit();
|
|
605
|
-
checkBalance = async ({
|
|
606
|
-
server,
|
|
607
|
-
address,
|
|
608
|
-
amountNeeded,
|
|
609
|
-
message,
|
|
610
|
-
flags
|
|
611
|
-
}) => {
|
|
612
|
-
const { balanceFormatted } = await getUSDCBalance(address, flags);
|
|
613
|
-
if (balanceFormatted < amountNeeded) {
|
|
614
|
-
const capabilities = server.server.getClientCapabilities();
|
|
615
|
-
if (!capabilities?.elicitation) {
|
|
616
|
-
throw new Error(
|
|
617
|
-
`${message(balanceFormatted)}
|
|
618
|
-
|
|
619
|
-
You can deposit USDC at ${getDepositLink(address, flags)}`
|
|
620
|
-
);
|
|
621
|
-
}
|
|
622
|
-
const result = await server.server.elicitInput({
|
|
623
|
-
mode: "form",
|
|
624
|
-
message: message(balanceFormatted),
|
|
625
|
-
requestedSchema: {
|
|
626
|
-
type: "object",
|
|
627
|
-
properties: {}
|
|
628
|
-
}
|
|
629
|
-
});
|
|
630
|
-
if (result.action === "accept") {
|
|
631
|
-
await openDepositLink(address, flags);
|
|
632
|
-
}
|
|
633
|
-
}
|
|
634
|
-
return balanceFormatted;
|
|
635
|
-
};
|
|
636
|
-
}
|
|
637
|
-
});
|
|
638
|
-
|
|
639
|
-
// src/server/lib/parse-response.ts
|
|
640
|
-
var parseResponse;
|
|
641
|
-
var init_parse_response = __esm({
|
|
642
|
-
"src/server/lib/parse-response.ts"() {
|
|
643
|
-
parseResponse = async (response) => {
|
|
644
|
-
try {
|
|
645
|
-
const contentType = response.headers.get("content-type") ?? "";
|
|
646
|
-
if (contentType.includes("application/json")) {
|
|
647
|
-
return await response.json();
|
|
648
|
-
} else if (contentType.includes("image/")) {
|
|
649
|
-
return await response.arrayBuffer();
|
|
650
|
-
} else {
|
|
651
|
-
return await response.text();
|
|
652
|
-
}
|
|
653
|
-
} catch {
|
|
844
|
+
getInputSchema = (extensions) => getBazaarExtension(extensions)?.schema.properties.input;
|
|
845
|
+
getSiwxExtension = (extensions) => {
|
|
846
|
+
const siwx = extensions?.["sign-in-with-x"];
|
|
847
|
+
if (!siwx?.info) {
|
|
654
848
|
return void 0;
|
|
655
849
|
}
|
|
850
|
+
return siwx.info;
|
|
656
851
|
};
|
|
657
852
|
}
|
|
658
853
|
});
|
|
659
|
-
var
|
|
660
|
-
var
|
|
661
|
-
"src/server/tools/fetch
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
init_types();
|
|
665
|
-
init_log();
|
|
666
|
-
init_networks();
|
|
667
|
-
init_token();
|
|
668
|
-
init_check_balance();
|
|
669
|
-
init_parse_response();
|
|
670
|
-
registerFetchX402ResourceTool = ({
|
|
671
|
-
server,
|
|
672
|
-
account,
|
|
673
|
-
flags
|
|
674
|
-
}) => {
|
|
675
|
-
server.registerTool(
|
|
676
|
-
"fetch",
|
|
677
|
-
{
|
|
678
|
-
description: "Fetches an x402-protected resource and handles payment automatically. If the resource is not x402-protected, it will return the raw response.",
|
|
679
|
-
inputSchema: requestWithHeadersSchema
|
|
680
|
-
},
|
|
681
|
-
async ({ url, method, body, headers }) => {
|
|
682
|
-
const coreClient = x402Client.fromConfig({
|
|
683
|
-
schemes: [
|
|
684
|
-
{ network: DEFAULT_NETWORK, client: new ExactEvmScheme(account) }
|
|
685
|
-
]
|
|
686
|
-
});
|
|
687
|
-
let state = "initial_request" /* INITIAL_REQUEST */;
|
|
688
|
-
coreClient.onBeforePaymentCreation(async ({ selectedRequirements }) => {
|
|
689
|
-
const amount = tokenStringToNumber(selectedRequirements.amount);
|
|
690
|
-
await checkBalance({
|
|
691
|
-
server,
|
|
692
|
-
address: account.address,
|
|
693
|
-
amountNeeded: amount,
|
|
694
|
-
message: (balance) => `This request costs ${amount} USDC. Your current balance is ${balance} USDC.`,
|
|
695
|
-
flags
|
|
696
|
-
});
|
|
697
|
-
state = "payment_required" /* PAYMENT_REQUIRED */;
|
|
698
|
-
});
|
|
699
|
-
coreClient.onAfterPaymentCreation(async (ctx) => {
|
|
700
|
-
state = "payment_created" /* PAYMENT_CREATED */;
|
|
701
|
-
log.info("After payment creation", ctx);
|
|
702
|
-
return Promise.resolve();
|
|
703
|
-
});
|
|
704
|
-
coreClient.onPaymentCreationFailure(async (ctx) => {
|
|
705
|
-
state = "payment_failed" /* PAYMENT_FAILED */;
|
|
706
|
-
log.info("Payment creation failure", ctx);
|
|
707
|
-
return Promise.resolve();
|
|
708
|
-
});
|
|
709
|
-
const client = new x402HTTPClient(coreClient);
|
|
710
|
-
const fetchWithPay = wrapFetchWithPayment(fetch, client);
|
|
711
|
-
try {
|
|
712
|
-
const response = await fetchWithPay(url, {
|
|
713
|
-
method,
|
|
714
|
-
body: typeof body === "string" ? body : body ? JSON.stringify(body) : void 0,
|
|
715
|
-
headers: {
|
|
716
|
-
...body ? { "Content-Type": "application/json" } : {},
|
|
717
|
-
...headers
|
|
718
|
-
}
|
|
719
|
-
});
|
|
720
|
-
if (!response.ok) {
|
|
721
|
-
const errorResponse = {
|
|
722
|
-
data: await parseResponse(response),
|
|
723
|
-
statusCode: response.status,
|
|
724
|
-
state
|
|
725
|
-
};
|
|
726
|
-
if (response.status === 402) {
|
|
727
|
-
return mcpError("Payment required", errorResponse);
|
|
728
|
-
}
|
|
729
|
-
return mcpError(
|
|
730
|
-
response.statusText ?? "Request failed",
|
|
731
|
-
errorResponse
|
|
732
|
-
);
|
|
733
|
-
}
|
|
734
|
-
const getSettlement = () => {
|
|
735
|
-
try {
|
|
736
|
-
return client.getPaymentSettleResponse(
|
|
737
|
-
(name) => response.headers.get(name)
|
|
738
|
-
);
|
|
739
|
-
} catch {
|
|
740
|
-
return void 0;
|
|
741
|
-
}
|
|
742
|
-
};
|
|
743
|
-
const settlement = getSettlement();
|
|
744
|
-
return mcpSuccess({
|
|
745
|
-
data: await parseResponse(response),
|
|
746
|
-
payment: settlement
|
|
747
|
-
});
|
|
748
|
-
} catch (err2) {
|
|
749
|
-
return mcpError(err2, { state });
|
|
750
|
-
}
|
|
751
|
-
}
|
|
752
|
-
);
|
|
753
|
-
};
|
|
754
|
-
}
|
|
755
|
-
});
|
|
756
|
-
|
|
757
|
-
// src/server/lib/x402/protocol.ts
|
|
758
|
-
function isV1Response(pr) {
|
|
759
|
-
if (!pr || typeof pr !== "object") return false;
|
|
760
|
-
const obj = pr;
|
|
761
|
-
if (obj.x402Version === 1) return true;
|
|
762
|
-
const accepts = obj.accepts;
|
|
763
|
-
if (Array.isArray(accepts) && accepts.length > 0) {
|
|
764
|
-
return "maxAmountRequired" in accepts[0];
|
|
765
|
-
}
|
|
766
|
-
return false;
|
|
767
|
-
}
|
|
768
|
-
function normalizeV1Requirement(req) {
|
|
769
|
-
if (!req.maxAmountRequired) {
|
|
770
|
-
throw new Error("v1 requirement missing maxAmountRequired field");
|
|
771
|
-
}
|
|
772
|
-
return {
|
|
773
|
-
scheme: req.scheme,
|
|
774
|
-
network: req.network,
|
|
775
|
-
amount: req.maxAmountRequired,
|
|
776
|
-
asset: req.asset,
|
|
777
|
-
payTo: req.payTo,
|
|
778
|
-
maxTimeoutSeconds: req.maxTimeoutSeconds,
|
|
779
|
-
extra: req.extra,
|
|
780
|
-
resource: req.resource,
|
|
781
|
-
description: req.description,
|
|
782
|
-
mimeType: req.mimeType
|
|
783
|
-
};
|
|
784
|
-
}
|
|
785
|
-
function normalizeV2Requirement(req) {
|
|
786
|
-
if (!req.amount) {
|
|
787
|
-
throw new Error("v2 requirement missing amount field");
|
|
788
|
-
}
|
|
789
|
-
return {
|
|
790
|
-
scheme: req.scheme,
|
|
791
|
-
network: req.network,
|
|
792
|
-
amount: req.amount,
|
|
793
|
-
asset: req.asset,
|
|
794
|
-
payTo: req.payTo,
|
|
795
|
-
maxTimeoutSeconds: req.maxTimeoutSeconds,
|
|
796
|
-
extra: req.extra
|
|
797
|
-
};
|
|
798
|
-
}
|
|
799
|
-
function normalizePaymentRequired(pr) {
|
|
800
|
-
const version = pr.x402Version ?? 1;
|
|
801
|
-
if (isV1Response(pr)) {
|
|
802
|
-
const v1 = pr;
|
|
803
|
-
return {
|
|
804
|
-
x402Version: 1,
|
|
805
|
-
error: v1.error,
|
|
806
|
-
accepts: v1.accepts.map(normalizeV1Requirement)
|
|
807
|
-
};
|
|
808
|
-
}
|
|
809
|
-
const v2 = pr;
|
|
810
|
-
return {
|
|
811
|
-
x402Version: version,
|
|
812
|
-
error: v2.error,
|
|
813
|
-
accepts: v2.accepts.map(normalizeV2Requirement),
|
|
814
|
-
resource: v2.resource,
|
|
815
|
-
extensions: v2.extensions
|
|
816
|
-
};
|
|
817
|
-
}
|
|
818
|
-
var init_protocol = __esm({
|
|
819
|
-
"src/server/lib/x402/protocol.ts"() {
|
|
820
|
-
}
|
|
821
|
-
});
|
|
822
|
-
function extractSolanaChainReference(chainId) {
|
|
823
|
-
const [, reference] = chainId.split(":");
|
|
824
|
-
return reference;
|
|
825
|
-
}
|
|
826
|
-
function formatSIWSMessage(info, address) {
|
|
827
|
-
const lines = [
|
|
828
|
-
`${info.domain} wants you to sign in with your Solana account:`,
|
|
829
|
-
address,
|
|
830
|
-
""
|
|
831
|
-
];
|
|
832
|
-
if (info.statement) {
|
|
833
|
-
lines.push(info.statement, "");
|
|
834
|
-
}
|
|
835
|
-
lines.push(
|
|
836
|
-
`URI: ${info.uri}`,
|
|
837
|
-
`Version: ${info.version}`,
|
|
838
|
-
`Chain ID: ${extractSolanaChainReference(info.chainId)}`,
|
|
839
|
-
`Nonce: ${info.nonce}`,
|
|
840
|
-
`Issued At: ${info.issuedAt}`
|
|
841
|
-
);
|
|
842
|
-
if (info.expirationTime) {
|
|
843
|
-
lines.push(`Expiration Time: ${info.expirationTime}`);
|
|
844
|
-
}
|
|
845
|
-
if (info.notBefore) {
|
|
846
|
-
lines.push(`Not Before: ${info.notBefore}`);
|
|
847
|
-
}
|
|
848
|
-
if (info.requestId) {
|
|
849
|
-
lines.push(`Request ID: ${info.requestId}`);
|
|
850
|
-
}
|
|
851
|
-
if (info.resources && info.resources.length > 0) {
|
|
852
|
-
lines.push("Resources:");
|
|
853
|
-
for (const resource of info.resources) {
|
|
854
|
-
lines.push(`- ${resource}`);
|
|
855
|
-
}
|
|
856
|
-
}
|
|
857
|
-
return lines.join("\n");
|
|
858
|
-
}
|
|
859
|
-
function encodeBase58(bytes) {
|
|
860
|
-
return base58.encode(bytes);
|
|
861
|
-
}
|
|
862
|
-
var init_solana = __esm({
|
|
863
|
-
"src/server/vendor/sign-in-with-x/solana.ts"() {
|
|
864
|
-
}
|
|
865
|
-
});
|
|
866
|
-
|
|
867
|
-
// src/server/vendor/sign-in-with-x/sign.ts
|
|
868
|
-
function getEVMAddress(signer) {
|
|
869
|
-
if (signer.account?.address) {
|
|
870
|
-
return signer.account.address;
|
|
871
|
-
}
|
|
872
|
-
if (signer.address) {
|
|
873
|
-
return signer.address;
|
|
874
|
-
}
|
|
875
|
-
throw new Error("EVM signer missing address");
|
|
876
|
-
}
|
|
877
|
-
function getSolanaAddress(signer) {
|
|
878
|
-
const pk = signer.publicKey;
|
|
879
|
-
return typeof pk === "string" ? pk : pk.toBase58();
|
|
880
|
-
}
|
|
881
|
-
async function signEVMMessage(message, signer) {
|
|
882
|
-
if (signer.account) {
|
|
883
|
-
return signer.signMessage({ message, account: signer.account });
|
|
884
|
-
}
|
|
885
|
-
return signer.signMessage({ message });
|
|
886
|
-
}
|
|
887
|
-
async function signSolanaMessage(message, signer) {
|
|
888
|
-
const messageBytes = new TextEncoder().encode(message);
|
|
889
|
-
const signatureBytes = await signer.signMessage(messageBytes);
|
|
890
|
-
return encodeBase58(signatureBytes);
|
|
891
|
-
}
|
|
892
|
-
var init_sign = __esm({
|
|
893
|
-
"src/server/vendor/sign-in-with-x/sign.ts"() {
|
|
894
|
-
init_solana();
|
|
895
|
-
}
|
|
896
|
-
});
|
|
897
|
-
function extractEVMChainId(chainId) {
|
|
898
|
-
const match = /^eip155:(\d+)$/.exec(chainId);
|
|
899
|
-
if (!match) {
|
|
900
|
-
throw new Error(
|
|
901
|
-
`Invalid EVM chainId format: ${chainId}. Expected eip155:<number>`
|
|
902
|
-
);
|
|
903
|
-
}
|
|
904
|
-
return parseInt(match[1], 10);
|
|
905
|
-
}
|
|
906
|
-
function formatSIWEMessage(info, address) {
|
|
907
|
-
const numericChainId = extractEVMChainId(info.chainId);
|
|
908
|
-
const siweMessage = new SiweMessage({
|
|
909
|
-
domain: info.domain,
|
|
910
|
-
address,
|
|
911
|
-
statement: info.statement,
|
|
912
|
-
uri: info.uri,
|
|
913
|
-
version: info.version,
|
|
914
|
-
chainId: numericChainId,
|
|
915
|
-
nonce: info.nonce,
|
|
916
|
-
issuedAt: info.issuedAt,
|
|
917
|
-
expirationTime: info.expirationTime,
|
|
918
|
-
notBefore: info.notBefore,
|
|
919
|
-
requestId: info.requestId,
|
|
920
|
-
resources: info.resources
|
|
921
|
-
});
|
|
922
|
-
return siweMessage.prepareMessage();
|
|
923
|
-
}
|
|
924
|
-
var init_evm = __esm({
|
|
925
|
-
"src/server/vendor/sign-in-with-x/evm.ts"() {
|
|
926
|
-
}
|
|
927
|
-
});
|
|
928
|
-
|
|
929
|
-
// src/server/vendor/sign-in-with-x/message.ts
|
|
930
|
-
function createSIWxMessage(serverInfo, address) {
|
|
931
|
-
if (serverInfo.chainId.startsWith("eip155:")) {
|
|
932
|
-
return formatSIWEMessage(serverInfo, address);
|
|
933
|
-
}
|
|
934
|
-
if (serverInfo.chainId.startsWith("solana:")) {
|
|
935
|
-
return formatSIWSMessage(serverInfo, address);
|
|
936
|
-
}
|
|
937
|
-
throw new Error(
|
|
938
|
-
`Unsupported chain namespace: ${serverInfo.chainId}. Supported: eip155:* (EVM), solana:* (Solana)`
|
|
939
|
-
);
|
|
940
|
-
}
|
|
941
|
-
var init_message = __esm({
|
|
942
|
-
"src/server/vendor/sign-in-with-x/message.ts"() {
|
|
943
|
-
init_evm();
|
|
944
|
-
init_solana();
|
|
945
|
-
}
|
|
946
|
-
});
|
|
947
|
-
|
|
948
|
-
// src/server/vendor/sign-in-with-x/client.ts
|
|
949
|
-
async function createSIWxPayload(serverExtension, signer) {
|
|
950
|
-
const isSolana = serverExtension.chainId.startsWith("solana:");
|
|
951
|
-
const address = isSolana ? getSolanaAddress(signer) : getEVMAddress(signer);
|
|
952
|
-
const message = createSIWxMessage(serverExtension, address);
|
|
953
|
-
const signature = isSolana ? await signSolanaMessage(message, signer) : await signEVMMessage(message, signer);
|
|
954
|
-
return {
|
|
955
|
-
domain: serverExtension.domain,
|
|
956
|
-
address,
|
|
957
|
-
statement: serverExtension.statement,
|
|
958
|
-
uri: serverExtension.uri,
|
|
959
|
-
version: serverExtension.version,
|
|
960
|
-
chainId: serverExtension.chainId,
|
|
961
|
-
type: serverExtension.type,
|
|
962
|
-
nonce: serverExtension.nonce,
|
|
963
|
-
issuedAt: serverExtension.issuedAt,
|
|
964
|
-
expirationTime: serverExtension.expirationTime,
|
|
965
|
-
notBefore: serverExtension.notBefore,
|
|
966
|
-
requestId: serverExtension.requestId,
|
|
967
|
-
resources: serverExtension.resources,
|
|
968
|
-
signatureScheme: serverExtension.signatureScheme,
|
|
969
|
-
signature
|
|
970
|
-
};
|
|
971
|
-
}
|
|
972
|
-
var init_client = __esm({
|
|
973
|
-
"src/server/vendor/sign-in-with-x/client.ts"() {
|
|
974
|
-
init_sign();
|
|
975
|
-
init_message();
|
|
976
|
-
}
|
|
977
|
-
});
|
|
978
|
-
function encodeSIWxHeader(payload) {
|
|
979
|
-
return safeBase64Encode(JSON.stringify(payload));
|
|
980
|
-
}
|
|
981
|
-
var init_encode = __esm({
|
|
982
|
-
"src/server/vendor/sign-in-with-x/encode.ts"() {
|
|
983
|
-
}
|
|
984
|
-
});
|
|
985
|
-
var registerAuthTools;
|
|
986
|
-
var init_auth = __esm({
|
|
987
|
-
"src/server/tools/auth.ts"() {
|
|
854
|
+
var toolName2, registerAuthTools;
|
|
855
|
+
var init_auth_fetch = __esm({
|
|
856
|
+
"src/server/tools/auth-fetch.ts"() {
|
|
857
|
+
init_fetch();
|
|
858
|
+
init_x402();
|
|
988
859
|
init_response();
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
init_schemas();
|
|
860
|
+
init_request();
|
|
861
|
+
init_x402_extensions();
|
|
862
|
+
toolName2 = "fetchWithAuth";
|
|
993
863
|
registerAuthTools = ({ server, account }) => {
|
|
994
864
|
server.registerTool(
|
|
995
|
-
|
|
865
|
+
toolName2,
|
|
996
866
|
{
|
|
997
867
|
description: "Make a request to a SIWX-protected endpoint. Handles auth flow automatically: detects SIWX requirement from 402 response, signs proof with server-provided challenge, retries.",
|
|
998
|
-
inputSchema:
|
|
868
|
+
inputSchema: requestSchema
|
|
999
869
|
},
|
|
1000
|
-
async (
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
});
|
|
1011
|
-
if (firstResponse.status !== 402) {
|
|
1012
|
-
const responseHeaders2 = Object.fromEntries(
|
|
1013
|
-
firstResponse.headers.entries()
|
|
1014
|
-
);
|
|
1015
|
-
if (firstResponse.ok) {
|
|
1016
|
-
let data2;
|
|
1017
|
-
const contentType2 = firstResponse.headers.get("content-type");
|
|
1018
|
-
if (contentType2?.includes("application/json")) {
|
|
1019
|
-
data2 = await firstResponse.json();
|
|
1020
|
-
} else {
|
|
1021
|
-
data2 = await firstResponse.text();
|
|
1022
|
-
}
|
|
1023
|
-
return mcpSuccess({
|
|
1024
|
-
statusCode: firstResponse.status,
|
|
1025
|
-
headers: responseHeaders2,
|
|
1026
|
-
data: data2
|
|
1027
|
-
});
|
|
1028
|
-
}
|
|
1029
|
-
let errorBody;
|
|
1030
|
-
try {
|
|
1031
|
-
errorBody = await firstResponse.json();
|
|
1032
|
-
} catch {
|
|
1033
|
-
errorBody = await firstResponse.text();
|
|
1034
|
-
}
|
|
1035
|
-
return mcpError(`HTTP ${firstResponse.status}`, {
|
|
1036
|
-
statusCode: firstResponse.status,
|
|
1037
|
-
headers: responseHeaders2,
|
|
1038
|
-
body: errorBody
|
|
1039
|
-
});
|
|
1040
|
-
}
|
|
1041
|
-
let rawBody;
|
|
1042
|
-
try {
|
|
1043
|
-
rawBody = await firstResponse.clone().json();
|
|
1044
|
-
} catch {
|
|
1045
|
-
rawBody = void 0;
|
|
1046
|
-
}
|
|
1047
|
-
const rawPaymentRequired = httpClient.getPaymentRequiredResponse(
|
|
1048
|
-
(name) => firstResponse.headers.get(name),
|
|
1049
|
-
rawBody
|
|
1050
|
-
);
|
|
1051
|
-
const paymentRequired = normalizePaymentRequired(rawPaymentRequired);
|
|
1052
|
-
const siwxExtension = paymentRequired.extensions?.["sign-in-with-x"];
|
|
1053
|
-
if (!siwxExtension?.info) {
|
|
1054
|
-
return mcpError(
|
|
1055
|
-
"Endpoint returned 402 but no sign-in-with-x extension found",
|
|
1056
|
-
{
|
|
1057
|
-
statusCode: 402,
|
|
1058
|
-
x402Version: paymentRequired.x402Version,
|
|
1059
|
-
extensions: Object.keys(paymentRequired.extensions ?? {}),
|
|
1060
|
-
hint: "This endpoint may require payment instead of authentication. Use execute_call for paid requests."
|
|
1061
|
-
}
|
|
1062
|
-
);
|
|
870
|
+
async (input) => {
|
|
871
|
+
const httpClient = new x402HTTPClient(new x402Client());
|
|
872
|
+
const firstResult = await safeFetch(toolName2, buildRequest(input));
|
|
873
|
+
if (firstResult.isErr()) {
|
|
874
|
+
return mcpError(firstResult);
|
|
875
|
+
}
|
|
876
|
+
const firstResponse = firstResult.value;
|
|
877
|
+
if (firstResponse.status !== 402) {
|
|
878
|
+
if (!firstResponse.ok) {
|
|
879
|
+
return mcpErrorFetch(toolName2, firstResponse);
|
|
1063
880
|
}
|
|
1064
|
-
const
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
"uri",
|
|
1068
|
-
"version",
|
|
1069
|
-
"chainId",
|
|
1070
|
-
"nonce",
|
|
1071
|
-
"issuedAt"
|
|
1072
|
-
];
|
|
1073
|
-
const missingFields = requiredFields.filter(
|
|
1074
|
-
(f) => !serverInfo[f]
|
|
881
|
+
const parseResponseResult2 = await safeParseResponse(
|
|
882
|
+
toolName2,
|
|
883
|
+
firstResponse
|
|
1075
884
|
);
|
|
1076
|
-
if (
|
|
1077
|
-
return mcpError(
|
|
1078
|
-
"Invalid sign-in-with-x extension: missing required fields",
|
|
1079
|
-
{
|
|
1080
|
-
missingFields,
|
|
1081
|
-
receivedInfo: serverInfo
|
|
1082
|
-
}
|
|
1083
|
-
);
|
|
885
|
+
if (parseResponseResult2.isErr()) {
|
|
886
|
+
return mcpError(parseResponseResult2);
|
|
1084
887
|
}
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
888
|
+
return mcpSuccessResponse(parseResponseResult2.value);
|
|
889
|
+
}
|
|
890
|
+
const getPaymentRequiredResult = await safeGetPaymentRequired(
|
|
891
|
+
toolName2,
|
|
892
|
+
httpClient,
|
|
893
|
+
firstResponse
|
|
894
|
+
);
|
|
895
|
+
if (getPaymentRequiredResult.isErr()) {
|
|
896
|
+
return mcpError(getPaymentRequiredResult);
|
|
897
|
+
}
|
|
898
|
+
const paymentRequired = getPaymentRequiredResult.value;
|
|
899
|
+
const siwxExtension = getSiwxExtension(paymentRequired.extensions);
|
|
900
|
+
if (!siwxExtension) {
|
|
901
|
+
return mcpErrorJson({
|
|
902
|
+
message: "Endpoint returned 402 but no sign-in-with-x extension found",
|
|
903
|
+
statusCode: 402,
|
|
904
|
+
extensions: Object.keys(paymentRequired.extensions ?? {}),
|
|
905
|
+
hint: "This endpoint may require payment instead of authentication. Use execute_call for paid requests."
|
|
1101
906
|
});
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
data = await authedResponse.text();
|
|
1128
|
-
}
|
|
1129
|
-
return mcpSuccess({
|
|
1130
|
-
statusCode: authedResponse.status,
|
|
1131
|
-
headers: responseHeaders,
|
|
1132
|
-
data,
|
|
1133
|
-
authentication: {
|
|
1134
|
-
address: account.address,
|
|
1135
|
-
domain: serverInfo.domain,
|
|
1136
|
-
chainId: serverInfo.chainId
|
|
1137
|
-
}
|
|
907
|
+
}
|
|
908
|
+
const serverInfo = siwxExtension;
|
|
909
|
+
const requiredFields = [
|
|
910
|
+
"domain",
|
|
911
|
+
"uri",
|
|
912
|
+
"version",
|
|
913
|
+
"chainId",
|
|
914
|
+
"nonce",
|
|
915
|
+
"issuedAt"
|
|
916
|
+
];
|
|
917
|
+
const missingFields = requiredFields.filter(
|
|
918
|
+
(f) => !serverInfo[f]
|
|
919
|
+
);
|
|
920
|
+
if (missingFields.length > 0) {
|
|
921
|
+
return mcpErrorJson({
|
|
922
|
+
message: "Invalid sign-in-with-x extension: missing required fields",
|
|
923
|
+
missingFields,
|
|
924
|
+
receivedInfo: { ...serverInfo }
|
|
925
|
+
});
|
|
926
|
+
}
|
|
927
|
+
if (serverInfo.chainId.startsWith("solana:")) {
|
|
928
|
+
return mcpErrorJson({
|
|
929
|
+
message: "Solana authentication not supported",
|
|
930
|
+
chainId: serverInfo.chainId,
|
|
931
|
+
hint: "This endpoint requires a Solana wallet. The MCP server currently only supports EVM wallets."
|
|
1138
932
|
});
|
|
1139
|
-
} catch (err2) {
|
|
1140
|
-
return mcpError(err2, { tool: "authed_call", url });
|
|
1141
933
|
}
|
|
934
|
+
const payloadResult = await safeCreateSIWxPayload(
|
|
935
|
+
toolName2,
|
|
936
|
+
serverInfo,
|
|
937
|
+
account
|
|
938
|
+
);
|
|
939
|
+
if (payloadResult.isErr()) {
|
|
940
|
+
return mcpError(payloadResult);
|
|
941
|
+
}
|
|
942
|
+
const siwxHeader = encodeSIWxHeader(payloadResult.value);
|
|
943
|
+
const authedRequest = buildRequest(input);
|
|
944
|
+
authedRequest.headers.set("SIGN-IN-WITH-X", siwxHeader);
|
|
945
|
+
const authedResult = await safeFetch(toolName2, authedRequest);
|
|
946
|
+
if (authedResult.isErr()) {
|
|
947
|
+
return mcpError(authedResult);
|
|
948
|
+
}
|
|
949
|
+
const authedResponse = authedResult.value;
|
|
950
|
+
if (!authedResponse.ok) {
|
|
951
|
+
return mcpErrorFetch(toolName2, authedResponse);
|
|
952
|
+
}
|
|
953
|
+
const parseResponseResult = await safeParseResponse(
|
|
954
|
+
toolName2,
|
|
955
|
+
authedResponse
|
|
956
|
+
);
|
|
957
|
+
if (parseResponseResult.isErr()) {
|
|
958
|
+
return mcpError(parseResponseResult);
|
|
959
|
+
}
|
|
960
|
+
return mcpSuccessResponse(parseResponseResult.value, {
|
|
961
|
+
authentication: {
|
|
962
|
+
address: account.address,
|
|
963
|
+
domain: serverInfo.domain,
|
|
964
|
+
chainId: serverInfo.chainId
|
|
965
|
+
}
|
|
966
|
+
});
|
|
1142
967
|
}
|
|
1143
968
|
);
|
|
1144
969
|
};
|
|
@@ -1146,138 +971,195 @@ var init_auth = __esm({
|
|
|
1146
971
|
});
|
|
1147
972
|
|
|
1148
973
|
// src/server/tools/wallet.ts
|
|
1149
|
-
var registerWalletTools;
|
|
974
|
+
var toolName3, registerWalletTools;
|
|
1150
975
|
var init_wallet = __esm({
|
|
1151
976
|
"src/server/tools/wallet.ts"() {
|
|
1152
|
-
init_response();
|
|
1153
977
|
init_balance();
|
|
1154
978
|
init_networks();
|
|
1155
|
-
|
|
979
|
+
init_utils();
|
|
980
|
+
init_response();
|
|
981
|
+
toolName3 = "getWalletInfo";
|
|
1156
982
|
registerWalletTools = ({
|
|
1157
983
|
server,
|
|
1158
984
|
account: { address },
|
|
1159
985
|
flags
|
|
1160
986
|
}) => {
|
|
1161
987
|
server.registerTool(
|
|
1162
|
-
|
|
988
|
+
toolName3,
|
|
1163
989
|
{
|
|
1164
990
|
description: "Check wallet address and USDC balance. Creates wallet if needed."
|
|
1165
991
|
},
|
|
1166
992
|
async () => {
|
|
1167
|
-
const
|
|
1168
|
-
|
|
993
|
+
const balanceResult = await getBalance({
|
|
994
|
+
address,
|
|
995
|
+
flags,
|
|
996
|
+
surface: toolName3
|
|
997
|
+
});
|
|
998
|
+
if (balanceResult.isErr()) {
|
|
999
|
+
return mcpError(balanceResult);
|
|
1000
|
+
}
|
|
1001
|
+
const { balance } = balanceResult.value;
|
|
1002
|
+
return mcpSuccessJson({
|
|
1169
1003
|
address,
|
|
1170
1004
|
network: DEFAULT_NETWORK,
|
|
1171
1005
|
networkName: getChainName(DEFAULT_NETWORK),
|
|
1172
|
-
usdcBalance:
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1006
|
+
usdcBalance: balance,
|
|
1007
|
+
isNewWallet: balance === 0,
|
|
1008
|
+
depositLink: getDepositLink(address, flags),
|
|
1009
|
+
...balance < 2.5 ? {
|
|
1010
|
+
message: `Your balance is low. Consider topping it up`
|
|
1011
|
+
} : {}
|
|
1176
1012
|
});
|
|
1177
1013
|
}
|
|
1178
1014
|
);
|
|
1015
|
+
};
|
|
1016
|
+
}
|
|
1017
|
+
});
|
|
1018
|
+
var toolName4, registerCheckX402EndpointTool;
|
|
1019
|
+
var init_check_endpoint = __esm({
|
|
1020
|
+
"src/server/tools/check-endpoint.ts"() {
|
|
1021
|
+
init_fetch();
|
|
1022
|
+
init_response();
|
|
1023
|
+
init_log();
|
|
1024
|
+
init_token();
|
|
1025
|
+
init_x402();
|
|
1026
|
+
init_x402_extensions();
|
|
1027
|
+
init_request();
|
|
1028
|
+
toolName4 = "checkEndpointSchema";
|
|
1029
|
+
registerCheckX402EndpointTool = ({ server }) => {
|
|
1179
1030
|
server.registerTool(
|
|
1180
|
-
|
|
1031
|
+
toolName4,
|
|
1181
1032
|
{
|
|
1182
|
-
description: "
|
|
1033
|
+
description: "Check if an endpoint is x402-protected and get pricing options, schema, and auth requirements (if applicable).",
|
|
1034
|
+
inputSchema: requestSchema
|
|
1183
1035
|
},
|
|
1184
|
-
() =>
|
|
1036
|
+
async (input) => {
|
|
1037
|
+
log.info("Querying endpoint", input);
|
|
1038
|
+
const responseResult = await safeFetch(toolName4, buildRequest(input));
|
|
1039
|
+
if (responseResult.isErr()) {
|
|
1040
|
+
return mcpError(responseResult);
|
|
1041
|
+
}
|
|
1042
|
+
const response = responseResult.value;
|
|
1043
|
+
if (response.status !== 402) {
|
|
1044
|
+
if (!response.ok) {
|
|
1045
|
+
return mcpErrorFetch(toolName4, response);
|
|
1046
|
+
}
|
|
1047
|
+
const parseResponseResult = await safeParseResponse(toolName4, response);
|
|
1048
|
+
if (parseResponseResult.isErr()) {
|
|
1049
|
+
return mcpError(parseResponseResult);
|
|
1050
|
+
}
|
|
1051
|
+
return mcpSuccessResponse(parseResponseResult.value, {
|
|
1052
|
+
requiresPayment: false
|
|
1053
|
+
});
|
|
1054
|
+
}
|
|
1055
|
+
const client = new x402HTTPClient(new x402Client());
|
|
1056
|
+
const paymentRequiredResult = await safeGetPaymentRequired(
|
|
1057
|
+
toolName4,
|
|
1058
|
+
client,
|
|
1059
|
+
response
|
|
1060
|
+
);
|
|
1061
|
+
if (paymentRequiredResult.isErr()) {
|
|
1062
|
+
return mcpError(paymentRequiredResult);
|
|
1063
|
+
}
|
|
1064
|
+
const { resource, extensions, accepts } = paymentRequiredResult.value;
|
|
1065
|
+
return mcpSuccessJson({
|
|
1066
|
+
requiresPayment: true,
|
|
1067
|
+
statusCode: response.status,
|
|
1068
|
+
routeDetails: {
|
|
1069
|
+
...resource,
|
|
1070
|
+
schema: getInputSchema(extensions),
|
|
1071
|
+
paymentMethods: accepts.map((accept) => ({
|
|
1072
|
+
price: tokenStringToNumber(accept.amount),
|
|
1073
|
+
network: accept.network,
|
|
1074
|
+
asset: accept.asset
|
|
1075
|
+
}))
|
|
1076
|
+
}
|
|
1077
|
+
});
|
|
1078
|
+
}
|
|
1185
1079
|
);
|
|
1186
1080
|
};
|
|
1187
1081
|
}
|
|
1188
1082
|
});
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
}
|
|
1205
|
-
}
|
|
1083
|
+
var STATE_FILE, stateSchema, getState, setState;
|
|
1084
|
+
var init_state = __esm({
|
|
1085
|
+
"src/shared/state.ts"() {
|
|
1086
|
+
init_fs2();
|
|
1087
|
+
init_log();
|
|
1088
|
+
STATE_FILE = configFile("state.json", "{}");
|
|
1089
|
+
stateSchema = z$1.looseObject({
|
|
1090
|
+
redeemedCodes: z$1.array(z$1.string())
|
|
1091
|
+
}).partial();
|
|
1092
|
+
getState = () => {
|
|
1093
|
+
const result = stateSchema.safeParse(
|
|
1094
|
+
JSON.parse(fs2__default.readFileSync(STATE_FILE, "utf-8"))
|
|
1095
|
+
);
|
|
1096
|
+
if (!result.success) {
|
|
1097
|
+
log.error("Failed to parse state", { error: result.error });
|
|
1098
|
+
return {};
|
|
1099
|
+
}
|
|
1100
|
+
return result.data;
|
|
1206
1101
|
};
|
|
1207
|
-
|
|
1208
|
-
const
|
|
1209
|
-
|
|
1210
|
-
|
|
1102
|
+
setState = (state) => {
|
|
1103
|
+
const existing = getState();
|
|
1104
|
+
const newState = stateSchema.parse({ ...existing, ...state });
|
|
1105
|
+
fs2__default.writeFileSync(STATE_FILE, JSON.stringify(newState, null, 2));
|
|
1106
|
+
};
|
|
1107
|
+
}
|
|
1108
|
+
});
|
|
1109
|
+
var redeemInviteCode;
|
|
1110
|
+
var init_redeem_invite = __esm({
|
|
1111
|
+
"src/shared/redeem-invite.ts"() {
|
|
1112
|
+
init_fetch();
|
|
1113
|
+
init_utils();
|
|
1114
|
+
init_state();
|
|
1115
|
+
redeemInviteCode = async ({
|
|
1116
|
+
code,
|
|
1117
|
+
dev,
|
|
1118
|
+
address,
|
|
1119
|
+
surface: surface3
|
|
1120
|
+
}) => {
|
|
1121
|
+
const state = getState();
|
|
1122
|
+
if (state.redeemedCodes?.includes(code)) {
|
|
1123
|
+
return err("user", surface3, {
|
|
1124
|
+
cause: "conflict",
|
|
1125
|
+
message: "This invite code has already been redeemed"
|
|
1126
|
+
});
|
|
1211
1127
|
}
|
|
1212
|
-
const
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
registerCheckX402EndpointTool = ({ server }) => {
|
|
1225
|
-
server.registerTool(
|
|
1226
|
-
"check_x402_endpoint",
|
|
1227
|
-
{
|
|
1228
|
-
description: "Check if an endpoint is x402-protected and get pricing options, schema, and auth requirements (if applicable).",
|
|
1229
|
-
inputSchema: requestSchema
|
|
1230
|
-
},
|
|
1231
|
-
async ({ url, method, body }) => {
|
|
1232
|
-
try {
|
|
1233
|
-
log.info("Querying endpoint", { url, method, body });
|
|
1234
|
-
const response = await fetch(url, {
|
|
1235
|
-
method,
|
|
1236
|
-
body: body ? typeof body === "string" ? body : JSON.stringify(body) : void 0,
|
|
1237
|
-
headers: {
|
|
1238
|
-
"Content-Type": "application/json"
|
|
1239
|
-
}
|
|
1240
|
-
});
|
|
1241
|
-
const bodyText = await response.text().catch(() => void 0);
|
|
1242
|
-
if (response.status !== 402) {
|
|
1243
|
-
return mcpSuccess({
|
|
1244
|
-
data: bodyText,
|
|
1245
|
-
statusCode: response.status,
|
|
1246
|
-
requiresPayment: false
|
|
1247
|
-
});
|
|
1248
|
-
}
|
|
1249
|
-
const paymentRequired = new x402HTTPClient(
|
|
1250
|
-
new x402Client()
|
|
1251
|
-
).getPaymentRequiredResponse(
|
|
1252
|
-
(name) => response.headers.get(name),
|
|
1253
|
-
JSON.parse(bodyText ?? "{}")
|
|
1254
|
-
);
|
|
1255
|
-
const routeDetails = getRouteDetails(paymentRequired);
|
|
1256
|
-
return mcpSuccess({
|
|
1257
|
-
requiresPayment: true,
|
|
1258
|
-
statusCode: response.status,
|
|
1259
|
-
routeDetails
|
|
1260
|
-
});
|
|
1261
|
-
} catch (err2) {
|
|
1262
|
-
return mcpError(err2, { tool: "query_endpoint", url });
|
|
1263
|
-
}
|
|
1264
|
-
}
|
|
1128
|
+
const result = await safeFetchJson(
|
|
1129
|
+
surface3,
|
|
1130
|
+
new Request(`${getBaseUrl(dev)}/api/invite/redeem`, {
|
|
1131
|
+
method: "POST",
|
|
1132
|
+
headers: {
|
|
1133
|
+
"Content-Type": "application/json"
|
|
1134
|
+
},
|
|
1135
|
+
body: JSON.stringify({
|
|
1136
|
+
code,
|
|
1137
|
+
recipientAddr: address
|
|
1138
|
+
})
|
|
1139
|
+
})
|
|
1265
1140
|
);
|
|
1141
|
+
if (result.isOk()) {
|
|
1142
|
+
setState({
|
|
1143
|
+
redeemedCodes: [...state.redeemedCodes ?? [], code]
|
|
1144
|
+
});
|
|
1145
|
+
}
|
|
1146
|
+
return result;
|
|
1266
1147
|
};
|
|
1267
1148
|
}
|
|
1268
1149
|
});
|
|
1269
|
-
var registerRedeemInviteTool;
|
|
1150
|
+
var toolName5, registerRedeemInviteTool;
|
|
1270
1151
|
var init_redeem_invite2 = __esm({
|
|
1271
1152
|
"src/server/tools/redeem-invite.ts"() {
|
|
1272
1153
|
init_response();
|
|
1154
|
+
init_redeem_invite();
|
|
1155
|
+
toolName5 = "redeemInvite";
|
|
1273
1156
|
registerRedeemInviteTool = ({
|
|
1274
1157
|
server,
|
|
1275
1158
|
account: { address },
|
|
1276
1159
|
flags
|
|
1277
1160
|
}) => {
|
|
1278
|
-
const baseUrl = flags.dev ? "http://localhost:3000" : "https://x402scan.com";
|
|
1279
1161
|
server.registerTool(
|
|
1280
|
-
|
|
1162
|
+
toolName5,
|
|
1281
1163
|
{
|
|
1282
1164
|
description: "Redeem an invite code to receive USDC.",
|
|
1283
1165
|
inputSchema: z$1.object({
|
|
@@ -1285,18 +1167,20 @@ var init_redeem_invite2 = __esm({
|
|
|
1285
1167
|
})
|
|
1286
1168
|
},
|
|
1287
1169
|
async ({ code }) => {
|
|
1288
|
-
const
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1170
|
+
const result = await redeemInviteCode({
|
|
1171
|
+
code,
|
|
1172
|
+
dev: flags.dev,
|
|
1173
|
+
address,
|
|
1174
|
+
surface: toolName5
|
|
1292
1175
|
});
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
return mcpError(data.error ?? "Failed to redeem invite code");
|
|
1176
|
+
if (result.isErr()) {
|
|
1177
|
+
return mcpError(result);
|
|
1296
1178
|
}
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1179
|
+
const { amount, txHash } = result.value;
|
|
1180
|
+
return mcpSuccessJson({
|
|
1181
|
+
redeemed: true,
|
|
1182
|
+
amount: `${amount} USDC`,
|
|
1183
|
+
txHash
|
|
1300
1184
|
});
|
|
1301
1185
|
}
|
|
1302
1186
|
);
|
|
@@ -1305,135 +1189,252 @@ var init_redeem_invite2 = __esm({
|
|
|
1305
1189
|
});
|
|
1306
1190
|
function getVersion() {
|
|
1307
1191
|
{
|
|
1308
|
-
return "0.0.7-beta.
|
|
1192
|
+
return "0.0.7-beta.1";
|
|
1309
1193
|
}
|
|
1310
1194
|
}
|
|
1311
|
-
var MCP_VERSION;
|
|
1195
|
+
var MCP_VERSION, DIST_TAG;
|
|
1312
1196
|
var init_version = __esm({
|
|
1313
1197
|
"src/server/lib/version.ts"() {
|
|
1314
1198
|
MCP_VERSION = getVersion();
|
|
1199
|
+
DIST_TAG = MCP_VERSION.includes("-beta") ? "beta" : "latest";
|
|
1315
1200
|
}
|
|
1316
1201
|
});
|
|
1317
|
-
var
|
|
1202
|
+
var toolName6, registerTelemetryTools;
|
|
1318
1203
|
var init_telemetry = __esm({
|
|
1319
1204
|
"src/server/tools/telemetry.ts"() {
|
|
1205
|
+
init_fetch();
|
|
1320
1206
|
init_log();
|
|
1321
|
-
|
|
1207
|
+
init_utils();
|
|
1322
1208
|
init_version();
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
resource: z.string().optional().describe("x402 resource URL"),
|
|
1326
|
-
summary: z.string().describe("1-2 sentence summary"),
|
|
1327
|
-
errorMessage: z.string().describe("Error message"),
|
|
1328
|
-
stack: z.string().optional().describe("Stack trace"),
|
|
1329
|
-
fullReport: z.string().optional().describe("Detailed report with context, logs, repro steps")
|
|
1330
|
-
});
|
|
1209
|
+
init_response();
|
|
1210
|
+
toolName6 = "reportError";
|
|
1331
1211
|
registerTelemetryTools = ({
|
|
1332
1212
|
server,
|
|
1333
1213
|
account: { address },
|
|
1334
1214
|
flags
|
|
1335
1215
|
}) => {
|
|
1336
|
-
const baseUrl = flags.dev ? "http://localhost:3000" : "https://x402scan.com";
|
|
1337
1216
|
server.registerTool(
|
|
1338
|
-
|
|
1217
|
+
toolName6,
|
|
1339
1218
|
{
|
|
1340
1219
|
description: "EMERGENCY ONLY. Report critical MCP tool bugs. Do NOT use for normal errors (balance, network, 4xx) - those are recoverable.",
|
|
1341
|
-
inputSchema:
|
|
1220
|
+
inputSchema: z$1.object({
|
|
1221
|
+
tool: z$1.string().describe("MCP tool name"),
|
|
1222
|
+
resource: z$1.string().optional().describe("x402 resource URL"),
|
|
1223
|
+
summary: z$1.string().describe("1-2 sentence summary"),
|
|
1224
|
+
errorMessage: z$1.string().describe("Error message"),
|
|
1225
|
+
stack: z$1.string().optional().describe("Stack trace"),
|
|
1226
|
+
fullReport: z$1.string().optional().describe("Detailed report with context, logs, repro steps")
|
|
1227
|
+
})
|
|
1342
1228
|
},
|
|
1343
1229
|
async (input) => {
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
walletAddress: address,
|
|
1353
|
-
mcpVersion: MCP_VERSION,
|
|
1354
|
-
reportedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1355
|
-
};
|
|
1356
|
-
const response = await fetch(`${baseUrl}/api/telemetry`, {
|
|
1230
|
+
log.info("Submitting error report", {
|
|
1231
|
+
tool: input.tool,
|
|
1232
|
+
resource: input.resource,
|
|
1233
|
+
summary: input.summary
|
|
1234
|
+
});
|
|
1235
|
+
const telemetryResult = await safeFetchJson(
|
|
1236
|
+
toolName6,
|
|
1237
|
+
new Request(`${getBaseUrl(flags.dev)}/api/telemetry`, {
|
|
1357
1238
|
method: "POST",
|
|
1358
1239
|
headers: {
|
|
1359
1240
|
"Content-Type": "application/json"
|
|
1360
1241
|
},
|
|
1361
|
-
body: JSON.stringify(
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
);
|
|
1373
|
-
}
|
|
1374
|
-
const result = await response.json();
|
|
1375
|
-
log.info("Error report submitted successfully", {
|
|
1376
|
-
reportId: result.reportId
|
|
1377
|
-
});
|
|
1378
|
-
return mcpSuccess({
|
|
1379
|
-
submitted: true,
|
|
1380
|
-
reportId: result.reportId,
|
|
1381
|
-
message: "Error report submitted successfully. The x402scan team will investigate."
|
|
1382
|
-
});
|
|
1383
|
-
} catch (err2) {
|
|
1384
|
-
log.error("Failed to submit error report", { error: err2 });
|
|
1385
|
-
return mcpError(err2, { tool: "report_error" });
|
|
1242
|
+
body: JSON.stringify({
|
|
1243
|
+
...input,
|
|
1244
|
+
walletAddress: address,
|
|
1245
|
+
mcpVersion: MCP_VERSION,
|
|
1246
|
+
reportedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1247
|
+
})
|
|
1248
|
+
})
|
|
1249
|
+
);
|
|
1250
|
+
if (telemetryResult.isErr()) {
|
|
1251
|
+
log.error("Failed to submit error report", telemetryResult.error);
|
|
1252
|
+
return mcpError(telemetryResult);
|
|
1386
1253
|
}
|
|
1254
|
+
const { reportId } = telemetryResult.value;
|
|
1255
|
+
log.info("Error report submitted successfully", {
|
|
1256
|
+
reportId
|
|
1257
|
+
});
|
|
1258
|
+
return mcpSuccessJson({
|
|
1259
|
+
submitted: true,
|
|
1260
|
+
reportId,
|
|
1261
|
+
message: "Error report submitted successfully. The x402scan team will investigate."
|
|
1262
|
+
});
|
|
1387
1263
|
}
|
|
1388
1264
|
);
|
|
1389
1265
|
};
|
|
1390
1266
|
}
|
|
1391
1267
|
});
|
|
1268
|
+
function registerDiscoveryTools(server) {
|
|
1269
|
+
server.registerTool(
|
|
1270
|
+
"discover_resources",
|
|
1271
|
+
{
|
|
1272
|
+
description: `Discover x402-protected resources on an origin. Returns a list of resource URLs.
|
|
1273
|
+
Use check_x402_endpoint separately to get detailed pricing/schema info for specific resources.
|
|
1392
1274
|
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1275
|
+
Known default origins with resource packs. Discover if more needed:
|
|
1276
|
+
- https://enrichx402.com ->
|
|
1277
|
+
People + Org search
|
|
1278
|
+
Google Maps (places + locations)
|
|
1279
|
+
Grok twitter search
|
|
1280
|
+
Exa web search
|
|
1281
|
+
Clado linkedin data
|
|
1282
|
+
Firecrawl web scrape
|
|
1283
|
+
- https://stablestudio.io -> generate images / videos
|
|
1284
|
+
`,
|
|
1285
|
+
inputSchema: {
|
|
1286
|
+
url: z.url().describe(
|
|
1287
|
+
"The origin URL or any URL on the origin to discover resources from"
|
|
1288
|
+
)
|
|
1289
|
+
}
|
|
1290
|
+
},
|
|
1291
|
+
async ({ url }) => {
|
|
1292
|
+
const origin = URL.canParse(url) ? new URL(url).origin : url;
|
|
1293
|
+
const hostname = URL.canParse(origin) ? new URL(origin).hostname : origin;
|
|
1294
|
+
log.info(`Discovering resources for origin: ${origin}`);
|
|
1295
|
+
const wellKnownUrl = `${origin}/.well-known/x402`;
|
|
1296
|
+
log.debug(`Fetching discovery document from: ${wellKnownUrl}`);
|
|
1297
|
+
const wellKnownResult = await safeFetchJson(
|
|
1298
|
+
toolName7,
|
|
1299
|
+
new Request(wellKnownUrl, { headers: { Accept: "application/json" } })
|
|
1300
|
+
);
|
|
1301
|
+
if (wellKnownResult.isOk()) {
|
|
1302
|
+
const parsed = discoveryDocumentSchema.safeParse(wellKnownResult.value);
|
|
1303
|
+
if (parsed.success) {
|
|
1304
|
+
return mcpSuccessJson({
|
|
1305
|
+
found: true,
|
|
1306
|
+
origin,
|
|
1307
|
+
source: "well-known",
|
|
1308
|
+
data: parsed.data
|
|
1309
|
+
});
|
|
1402
1310
|
}
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
let descriptionMatch = /<meta\s+name=["']description["']\s+content=["']([^"']*)["']/i.exec(html);
|
|
1407
|
-
descriptionMatch ??= /<meta\s+property=["']og:description["']\s+content=["']([^"']*)["']/i.exec(
|
|
1408
|
-
html
|
|
1409
|
-
);
|
|
1410
|
-
descriptionMatch ??= /<meta\s+content=["']([^"']*)["']\s+name=["']description["']/i.exec(html);
|
|
1411
|
-
descriptionMatch ??= /<meta\s+content=["']([^"']*)["']\s+property=["']og:description["']/i.exec(
|
|
1412
|
-
html
|
|
1413
|
-
);
|
|
1414
|
-
const description = descriptionMatch ? descriptionMatch[1].trim().replace(/\s+/g, " ") : null;
|
|
1415
|
-
return {
|
|
1416
|
-
title,
|
|
1417
|
-
description
|
|
1418
|
-
};
|
|
1419
|
-
} catch (error) {
|
|
1420
|
-
throw new Error(
|
|
1421
|
-
`Failed to fetch web page metadata: ${error instanceof Error ? error.message : String(error)}`
|
|
1311
|
+
} else {
|
|
1312
|
+
log.info(
|
|
1313
|
+
`No well-known x402 discovery document found at ${wellKnownUrl}`
|
|
1422
1314
|
);
|
|
1423
1315
|
}
|
|
1316
|
+
const dnsQuery = `_x402.${hostname}`;
|
|
1317
|
+
log.debug(`Looking up DNS TXT record: ${dnsQuery}`);
|
|
1318
|
+
const dnsResult = await safeFetchJson(
|
|
1319
|
+
toolName7,
|
|
1320
|
+
new Request(
|
|
1321
|
+
`https://cloudflare-dns.com/dns-query?name=${encodeURIComponent(dnsQuery)}&type=TXT`,
|
|
1322
|
+
{ headers: { Accept: "application/dns-json" } }
|
|
1323
|
+
)
|
|
1324
|
+
);
|
|
1325
|
+
if (dnsResult.isOk() && dnsResult.value.Answer && dnsResult.value.Answer.length > 0) {
|
|
1326
|
+
const dnsUrl = dnsResult.value.Answer[0].data.replace(/^"|"$/g, "");
|
|
1327
|
+
if (URL.canParse(dnsUrl)) {
|
|
1328
|
+
const dnsDocResult = await safeFetchJson(
|
|
1329
|
+
toolName7,
|
|
1330
|
+
new Request(dnsUrl, { headers: { Accept: "application/json" } })
|
|
1331
|
+
);
|
|
1332
|
+
if (dnsDocResult.isOk()) {
|
|
1333
|
+
const parsed = discoveryDocumentSchema.safeParse(
|
|
1334
|
+
dnsDocResult.value
|
|
1335
|
+
);
|
|
1336
|
+
if (parsed.success) {
|
|
1337
|
+
return mcpSuccessJson({
|
|
1338
|
+
found: true,
|
|
1339
|
+
origin,
|
|
1340
|
+
source: "dns-txt",
|
|
1341
|
+
data: parsed.data
|
|
1342
|
+
});
|
|
1343
|
+
}
|
|
1344
|
+
}
|
|
1345
|
+
} else {
|
|
1346
|
+
log.debug(`DNS TXT value is not a valid URL: ${dnsUrl}`);
|
|
1347
|
+
}
|
|
1348
|
+
} else {
|
|
1349
|
+
log.info(`No DNS TXT record found for ${dnsQuery}`);
|
|
1350
|
+
}
|
|
1351
|
+
const llmsTxtUrl = `${origin}/llms.txt`;
|
|
1352
|
+
log.debug(`Fetching llms.txt from: ${llmsTxtUrl}`);
|
|
1353
|
+
const llmsResult = await safeFetch(
|
|
1354
|
+
toolName7,
|
|
1355
|
+
new Request(llmsTxtUrl, { headers: { Accept: "text/plain" } })
|
|
1356
|
+
);
|
|
1357
|
+
if (llmsResult.isOk()) {
|
|
1358
|
+
const parseResult = await safeParseResponse(toolName7, llmsResult.value);
|
|
1359
|
+
if (parseResult.isOk() && parseResult.value.type === "text") {
|
|
1360
|
+
return mcpSuccessJson({
|
|
1361
|
+
found: true,
|
|
1362
|
+
origin,
|
|
1363
|
+
source: "llms-txt",
|
|
1364
|
+
usage: "Found llms.txt but no structured x402 discovery document. The content below may contain information about x402 resources. Parse it to find relevant endpoints.",
|
|
1365
|
+
data: parseResult.value
|
|
1366
|
+
});
|
|
1367
|
+
}
|
|
1368
|
+
}
|
|
1369
|
+
return mcpErrorJson({
|
|
1370
|
+
found: false,
|
|
1371
|
+
origin,
|
|
1372
|
+
error: "No discovery document found. Tried: .well-known/x402, DNS TXT record, llms.txt"
|
|
1373
|
+
});
|
|
1374
|
+
}
|
|
1375
|
+
);
|
|
1376
|
+
}
|
|
1377
|
+
var toolName7, discoveryDocumentSchema;
|
|
1378
|
+
var init_discover_resources = __esm({
|
|
1379
|
+
"src/server/tools/discover-resources.ts"() {
|
|
1380
|
+
init_log();
|
|
1381
|
+
init_fetch();
|
|
1382
|
+
init_response();
|
|
1383
|
+
toolName7 = "discoverResources";
|
|
1384
|
+
discoveryDocumentSchema = z.object({
|
|
1385
|
+
version: z.number().refine((v) => v === 1, { message: "version must be 1" }),
|
|
1386
|
+
resources: z.array(z.url()),
|
|
1387
|
+
ownershipProofs: z.array(z.string()).optional(),
|
|
1388
|
+
instructions: z.string().optional()
|
|
1389
|
+
});
|
|
1390
|
+
}
|
|
1391
|
+
});
|
|
1392
|
+
var surface, getWebPageMetadata, parseMetadataFromResponse;
|
|
1393
|
+
var init_lib2 = __esm({
|
|
1394
|
+
"src/server/resources/_lib.ts"() {
|
|
1395
|
+
init_fetch();
|
|
1396
|
+
surface = "getWebPageMetadata";
|
|
1397
|
+
getWebPageMetadata = (url) => {
|
|
1398
|
+
return safeFetch(surface, new Request(url)).andThen((response) => safeParseResponse(surface, response)).andThen((parsedResponse) => {
|
|
1399
|
+
if (parsedResponse.type === "text") {
|
|
1400
|
+
return ok(parseMetadataFromResponse(parsedResponse.data));
|
|
1401
|
+
}
|
|
1402
|
+
return err("user", surface, {
|
|
1403
|
+
cause: "invalid_response_type",
|
|
1404
|
+
message: "Invalid response type"
|
|
1405
|
+
});
|
|
1406
|
+
});
|
|
1407
|
+
};
|
|
1408
|
+
parseMetadataFromResponse = (html) => {
|
|
1409
|
+
const titleMatch = /<title[^>]*>([\s\S]*?)<\/title>/i.exec(html);
|
|
1410
|
+
const title = titleMatch ? titleMatch[1].trim().replace(/\s+/g, " ") : null;
|
|
1411
|
+
let descriptionMatch = /<meta\s+name=["']description["']\s+content=["']([^"']*)["']/i.exec(html);
|
|
1412
|
+
descriptionMatch ??= /<meta\s+property=["']og:description["']\s+content=["']([^"']*)["']/i.exec(
|
|
1413
|
+
html
|
|
1414
|
+
);
|
|
1415
|
+
descriptionMatch ??= /<meta\s+content=["']([^"']*)["']\s+name=["']description["']/i.exec(html);
|
|
1416
|
+
descriptionMatch ??= /<meta\s+content=["']([^"']*)["']\s+property=["']og:description["']/i.exec(
|
|
1417
|
+
html
|
|
1418
|
+
);
|
|
1419
|
+
const description = descriptionMatch ? descriptionMatch[1].trim().replace(/\s+/g, " ") : null;
|
|
1420
|
+
return {
|
|
1421
|
+
title,
|
|
1422
|
+
description
|
|
1423
|
+
};
|
|
1424
1424
|
};
|
|
1425
1425
|
}
|
|
1426
1426
|
});
|
|
1427
1427
|
var origins, registerOrigins, getResourceResponse;
|
|
1428
1428
|
var init_origins = __esm({
|
|
1429
1429
|
"src/server/resources/origins.ts"() {
|
|
1430
|
-
|
|
1431
|
-
|
|
1430
|
+
init_lib2();
|
|
1431
|
+
init_x402_extensions();
|
|
1432
1432
|
origins = ["enrichx402.com"];
|
|
1433
1433
|
registerOrigins = async ({ server }) => {
|
|
1434
1434
|
await Promise.all(
|
|
1435
1435
|
origins.map(async (origin) => {
|
|
1436
|
-
const
|
|
1436
|
+
const metadataResult = await getWebPageMetadata(`https://${origin}`);
|
|
1437
|
+
const metadata = metadataResult.isOk() ? metadataResult.value : null;
|
|
1437
1438
|
server.registerResource(
|
|
1438
1439
|
origin,
|
|
1439
1440
|
`api://${origin}`,
|
|
@@ -1483,7 +1484,7 @@ var init_origins = __esm({
|
|
|
1483
1484
|
description: metadata?.description,
|
|
1484
1485
|
resources: resources.filter(Boolean).map((resource) => {
|
|
1485
1486
|
if (!resource) return null;
|
|
1486
|
-
const schema =
|
|
1487
|
+
const schema = getInputSchema(
|
|
1487
1488
|
resource.paymentRequired?.extensions
|
|
1488
1489
|
);
|
|
1489
1490
|
return {
|
|
@@ -1518,362 +1519,95 @@ var init_origins = __esm({
|
|
|
1518
1519
|
};
|
|
1519
1520
|
}
|
|
1520
1521
|
});
|
|
1522
|
+
var type2, parseErr, safeParse;
|
|
1523
|
+
var init_parse = __esm({
|
|
1524
|
+
"src/shared/neverthrow/parse/index.ts"() {
|
|
1525
|
+
type2 = "json";
|
|
1526
|
+
parseErr = (surface3, error) => err(type2, surface3, error);
|
|
1527
|
+
safeParse = (surface3, schema, value) => {
|
|
1528
|
+
const parseResult = schema.safeParse(value);
|
|
1529
|
+
if (!parseResult.success) {
|
|
1530
|
+
return parseErr(surface3, {
|
|
1531
|
+
cause: "invalid_data",
|
|
1532
|
+
message: JSON.stringify(z$1.treeifyError(parseResult.error), null, 2),
|
|
1533
|
+
error: parseResult.error
|
|
1534
|
+
});
|
|
1535
|
+
}
|
|
1536
|
+
return ok(parseResult.data);
|
|
1537
|
+
};
|
|
1538
|
+
}
|
|
1539
|
+
});
|
|
1521
1540
|
async function getWallet() {
|
|
1522
1541
|
if (process.env.X402_PRIVATE_KEY) {
|
|
1523
1542
|
const account2 = privateKeyToAccount(process.env.X402_PRIVATE_KEY);
|
|
1524
1543
|
log.info(`Using wallet from env: ${account2.address}`);
|
|
1525
|
-
return { account: account2, isNew: false };
|
|
1526
|
-
}
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
const
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
const privateKey = `0x${randomBytes(32).toString("hex")}`;
|
|
1536
|
-
const account = privateKeyToAccount(privateKey);
|
|
1537
|
-
const stored = {
|
|
1538
|
-
privateKey,
|
|
1539
|
-
address: account.address,
|
|
1540
|
-
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1541
|
-
};
|
|
1542
|
-
await fs3.writeFile(WALLET_FILE, JSON.stringify(stored, null, 2));
|
|
1543
|
-
try {
|
|
1544
|
-
await fs3.chmod(WALLET_FILE, 384);
|
|
1545
|
-
} catch {
|
|
1546
|
-
}
|
|
1547
|
-
log.info(`Created wallet: ${account.address}`);
|
|
1548
|
-
log.info(`Saved to: ${WALLET_FILE}`);
|
|
1549
|
-
return { account, isNew: true };
|
|
1550
|
-
}
|
|
1551
|
-
var WALLET_FILE, storedWalletSchema;
|
|
1552
|
-
var init_wallet2 = __esm({
|
|
1553
|
-
"src/lib/wallet.ts"() {
|
|
1554
|
-
init_log();
|
|
1555
|
-
init_schemas();
|
|
1556
|
-
init_fs();
|
|
1557
|
-
WALLET_FILE = configFile("wallet.json");
|
|
1558
|
-
storedWalletSchema = z$1.object({
|
|
1559
|
-
privateKey: ethereumPrivateKeySchema,
|
|
1560
|
-
address: ethereumAddressSchema,
|
|
1561
|
-
createdAt: z$1.string()
|
|
1562
|
-
});
|
|
1563
|
-
}
|
|
1564
|
-
});
|
|
1565
|
-
async function lookupDnsTxtRecord(hostname) {
|
|
1566
|
-
const dnsQuery = `_x402.${hostname}`;
|
|
1567
|
-
log.debug(`Looking up DNS TXT record: ${dnsQuery}`);
|
|
1568
|
-
try {
|
|
1569
|
-
const response = await fetch(
|
|
1570
|
-
`https://cloudflare-dns.com/dns-query?name=${encodeURIComponent(
|
|
1571
|
-
dnsQuery
|
|
1572
|
-
)}&type=TXT`,
|
|
1573
|
-
{
|
|
1574
|
-
headers: { Accept: "application/dns-json" }
|
|
1575
|
-
}
|
|
1576
|
-
);
|
|
1577
|
-
if (!response.ok) {
|
|
1578
|
-
log.debug(`DNS lookup failed: HTTP ${response.status}`);
|
|
1579
|
-
return null;
|
|
1580
|
-
}
|
|
1581
|
-
const data = await response.json();
|
|
1582
|
-
if (!data.Answer || data.Answer.length === 0) {
|
|
1583
|
-
log.debug("No DNS TXT record found");
|
|
1584
|
-
return null;
|
|
1585
|
-
}
|
|
1586
|
-
const txtValue = data.Answer[0].data.replace(/^"|"$/g, "");
|
|
1587
|
-
log.debug(`Found DNS TXT record: ${txtValue}`);
|
|
1588
|
-
try {
|
|
1589
|
-
new URL(txtValue);
|
|
1590
|
-
return txtValue;
|
|
1591
|
-
} catch {
|
|
1592
|
-
log.debug(`DNS TXT value is not a valid URL: ${txtValue}`);
|
|
1593
|
-
return null;
|
|
1594
|
-
}
|
|
1595
|
-
} catch (err2) {
|
|
1596
|
-
log.debug(
|
|
1597
|
-
`DNS lookup error: ${err2 instanceof Error ? err2.message : String(err2)}`
|
|
1598
|
-
);
|
|
1599
|
-
return null;
|
|
1600
|
-
}
|
|
1601
|
-
}
|
|
1602
|
-
async function fetchLlmsTxt(origin) {
|
|
1603
|
-
const llmsTxtUrl = `${origin}/llms.txt`;
|
|
1604
|
-
log.debug(`Fetching llms.txt from: ${llmsTxtUrl}`);
|
|
1605
|
-
try {
|
|
1606
|
-
const response = await fetch(llmsTxtUrl, {
|
|
1607
|
-
headers: { Accept: "text/plain" }
|
|
1608
|
-
});
|
|
1609
|
-
if (!response.ok) {
|
|
1610
|
-
if (response.status === 404) {
|
|
1611
|
-
return { found: false, error: "No llms.txt found" };
|
|
1612
|
-
}
|
|
1613
|
-
return { found: false, error: `HTTP ${response.status}` };
|
|
1614
|
-
}
|
|
1615
|
-
const content = await response.text();
|
|
1616
|
-
if (!content || content.trim().length === 0) {
|
|
1617
|
-
return { found: false, error: "llms.txt is empty" };
|
|
1618
|
-
}
|
|
1619
|
-
return { found: true, content };
|
|
1620
|
-
} catch (err2) {
|
|
1621
|
-
return {
|
|
1622
|
-
found: false,
|
|
1623
|
-
error: `Network error: ${err2 instanceof Error ? err2.message : String(err2)}`
|
|
1624
|
-
};
|
|
1625
|
-
}
|
|
1626
|
-
}
|
|
1627
|
-
async function fetchDiscoveryFromUrl(url) {
|
|
1628
|
-
log.debug(`Fetching discovery document from: ${url}`);
|
|
1629
|
-
try {
|
|
1630
|
-
const response = await fetch(url, {
|
|
1631
|
-
headers: { Accept: "application/json" }
|
|
1632
|
-
});
|
|
1633
|
-
if (!response.ok) {
|
|
1634
|
-
if (response.status === 404) {
|
|
1635
|
-
return { found: false, error: `Not found at ${url}` };
|
|
1636
|
-
}
|
|
1637
|
-
return {
|
|
1638
|
-
found: false,
|
|
1639
|
-
error: `HTTP ${response.status}: ${await response.text()}`
|
|
1640
|
-
};
|
|
1641
|
-
}
|
|
1642
|
-
let rawData;
|
|
1643
|
-
try {
|
|
1644
|
-
rawData = await response.json();
|
|
1645
|
-
} catch {
|
|
1646
|
-
return {
|
|
1647
|
-
found: false,
|
|
1648
|
-
error: "Failed to parse discovery document as JSON"
|
|
1649
|
-
};
|
|
1650
|
-
}
|
|
1651
|
-
const parsed = DiscoveryDocumentSchema.safeParse(rawData);
|
|
1652
|
-
if (!parsed.success) {
|
|
1653
|
-
return {
|
|
1654
|
-
found: false,
|
|
1655
|
-
error: `Invalid discovery document: ${parsed.error.issues.map((e) => e.message).join(", ")}`,
|
|
1656
|
-
rawResponse: rawData
|
|
1657
|
-
};
|
|
1658
|
-
}
|
|
1659
|
-
return { found: true, document: parsed.data };
|
|
1660
|
-
} catch (err2) {
|
|
1661
|
-
return {
|
|
1662
|
-
found: false,
|
|
1663
|
-
error: `Network error: ${err2 instanceof Error ? err2.message : String(err2)}`
|
|
1664
|
-
};
|
|
1665
|
-
}
|
|
1666
|
-
}
|
|
1667
|
-
async function fetchDiscoveryDocument(origin) {
|
|
1668
|
-
const attemptedSources = [];
|
|
1669
|
-
const hostname = getHostname(origin);
|
|
1670
|
-
const wellKnownUrl = `${origin}/.well-known/x402`;
|
|
1671
|
-
attemptedSources.push(wellKnownUrl);
|
|
1672
|
-
const wellKnownResult = await fetchDiscoveryFromUrl(wellKnownUrl);
|
|
1673
|
-
if (wellKnownResult.found && wellKnownResult.document) {
|
|
1674
|
-
return {
|
|
1675
|
-
found: true,
|
|
1676
|
-
source: "well-known",
|
|
1677
|
-
document: wellKnownResult.document,
|
|
1678
|
-
attemptedSources
|
|
1679
|
-
};
|
|
1680
|
-
}
|
|
1681
|
-
attemptedSources.push(`DNS TXT _x402.${hostname}`);
|
|
1682
|
-
const dnsUrl = await lookupDnsTxtRecord(hostname);
|
|
1683
|
-
if (dnsUrl) {
|
|
1684
|
-
attemptedSources.push(dnsUrl);
|
|
1685
|
-
const dnsResult = await fetchDiscoveryFromUrl(dnsUrl);
|
|
1686
|
-
if (dnsResult.found && dnsResult.document) {
|
|
1687
|
-
return {
|
|
1688
|
-
found: true,
|
|
1689
|
-
source: "dns-txt",
|
|
1690
|
-
document: dnsResult.document,
|
|
1691
|
-
attemptedSources
|
|
1692
|
-
};
|
|
1693
|
-
}
|
|
1694
|
-
}
|
|
1695
|
-
attemptedSources.push(`${origin}/llms.txt`);
|
|
1696
|
-
const llmsResult = await fetchLlmsTxt(origin);
|
|
1697
|
-
if (llmsResult.found && llmsResult.content) {
|
|
1698
|
-
return {
|
|
1699
|
-
found: true,
|
|
1700
|
-
source: "llms-txt",
|
|
1701
|
-
llmsTxtContent: llmsResult.content,
|
|
1702
|
-
attemptedSources
|
|
1703
|
-
};
|
|
1704
|
-
}
|
|
1705
|
-
return {
|
|
1706
|
-
found: false,
|
|
1707
|
-
error: "No discovery document found. Tried: .well-known/x402, DNS TXT record, llms.txt",
|
|
1708
|
-
attemptedSources
|
|
1709
|
-
};
|
|
1710
|
-
}
|
|
1711
|
-
async function queryResource(url) {
|
|
1712
|
-
log.debug(`Querying resource: ${url}`);
|
|
1713
|
-
try {
|
|
1714
|
-
const result = await fetch(url, { method: "GET" });
|
|
1715
|
-
if (!result.ok) {
|
|
1716
|
-
return {
|
|
1717
|
-
url,
|
|
1718
|
-
isX402Endpoint: false,
|
|
1719
|
-
error: result.statusText ?? "Failed to query endpoint"
|
|
1720
|
-
};
|
|
1721
|
-
}
|
|
1722
|
-
if (result.status !== 402) {
|
|
1723
|
-
return {
|
|
1724
|
-
url,
|
|
1725
|
-
isX402Endpoint: false
|
|
1726
|
-
};
|
|
1727
|
-
}
|
|
1728
|
-
const pr = new x402HTTPClient(new x402Client()).getPaymentRequiredResponse(
|
|
1729
|
-
(name) => result.headers.get(name),
|
|
1730
|
-
JSON.parse(await result.text())
|
|
1731
|
-
);
|
|
1732
|
-
const firstReq = pr.accepts[0];
|
|
1733
|
-
const resource = {
|
|
1734
|
-
url,
|
|
1735
|
-
isX402Endpoint: true,
|
|
1736
|
-
x402Version: pr.x402Version,
|
|
1737
|
-
price: tokenStringToNumber(firstReq.amount),
|
|
1738
|
-
priceRaw: firstReq.amount,
|
|
1739
|
-
network: firstReq.network,
|
|
1740
|
-
networkName: getChainName(firstReq.network)
|
|
1741
|
-
};
|
|
1742
|
-
if (pr.extensions?.bazaar) {
|
|
1743
|
-
const bazaar = pr.extensions.bazaar;
|
|
1744
|
-
resource.bazaar = { info: bazaar.info, schema: bazaar.schema };
|
|
1745
|
-
const info = bazaar.info;
|
|
1746
|
-
if (info?.description) {
|
|
1747
|
-
resource.description = info.description;
|
|
1748
|
-
}
|
|
1749
|
-
}
|
|
1750
|
-
if (pr.extensions?.["sign-in-with-x"]) {
|
|
1751
|
-
const siwx = pr.extensions["sign-in-with-x"];
|
|
1752
|
-
resource.signInWithX = { required: true, info: siwx.info };
|
|
1544
|
+
return ok({ account: account2, isNew: false });
|
|
1545
|
+
}
|
|
1546
|
+
const readFileResult = await safeReadFile(walletSurface, WALLET_FILE);
|
|
1547
|
+
if (!readFileResult.isOk()) {
|
|
1548
|
+
const fileExistsResult = safeFileExists(walletSurface, WALLET_FILE);
|
|
1549
|
+
if (fileExistsResult.isOk()) {
|
|
1550
|
+
return fsErr(walletSurface, {
|
|
1551
|
+
cause: "file_not_readable",
|
|
1552
|
+
message: `The file exists but is not readable. Fix corrupted state file: ${WALLET_FILE}`
|
|
1553
|
+
});
|
|
1753
1554
|
}
|
|
1754
|
-
return resource;
|
|
1755
|
-
} catch (err2) {
|
|
1756
|
-
return {
|
|
1757
|
-
url,
|
|
1758
|
-
isX402Endpoint: false,
|
|
1759
|
-
error: err2 instanceof Error ? err2.message : String(err2)
|
|
1760
|
-
};
|
|
1761
1555
|
}
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
Known default origins with resource packs. Discover if more needed:
|
|
1772
|
-
- https://enrichx402.com ->
|
|
1773
|
-
People + Org search
|
|
1774
|
-
Google Maps (places + locations)
|
|
1775
|
-
Grok twitter search
|
|
1776
|
-
Exa web search
|
|
1777
|
-
Clado linkedin data
|
|
1778
|
-
Firecrawl web scrape
|
|
1779
|
-
- https://stablestudio.io -> generate images / videos
|
|
1780
|
-
`,
|
|
1781
|
-
inputSchema: {
|
|
1782
|
-
url: z.url().describe(
|
|
1783
|
-
"The origin URL or any URL on the origin to discover resources from"
|
|
1784
|
-
),
|
|
1785
|
-
fanOut: z.boolean().default(false).describe(
|
|
1786
|
-
"Whether to query each discovered resource for full pricing/schema info. NEVER use on first try."
|
|
1787
|
-
),
|
|
1788
|
-
concurrency: z.number().int().min(1).max(10).default(5).describe(
|
|
1789
|
-
"Max concurrent requests when querying resources (default: 5)"
|
|
1790
|
-
)
|
|
1791
|
-
}
|
|
1792
|
-
},
|
|
1793
|
-
async ({ url, fanOut, concurrency }) => {
|
|
1794
|
-
try {
|
|
1795
|
-
const origin = getOrigin(url);
|
|
1796
|
-
log.info(`Discovering resources for origin: ${origin}`);
|
|
1797
|
-
const discoveryResult = await fetchDiscoveryDocument(origin);
|
|
1798
|
-
if (discoveryResult.found && discoveryResult.source === "llms-txt") {
|
|
1799
|
-
return mcpSuccess({
|
|
1800
|
-
found: true,
|
|
1801
|
-
origin,
|
|
1802
|
-
source: "llms-txt",
|
|
1803
|
-
usage: "Found llms.txt but no structured x402 discovery document. The content below may contain information about x402 resources. Parse it to find relevant endpoints.",
|
|
1804
|
-
llmsTxtContent: discoveryResult.llmsTxtContent,
|
|
1805
|
-
attemptedSources: discoveryResult.attemptedSources,
|
|
1806
|
-
resources: []
|
|
1807
|
-
});
|
|
1808
|
-
}
|
|
1809
|
-
if (!discoveryResult.found || !discoveryResult.document) {
|
|
1810
|
-
return mcpSuccess({
|
|
1811
|
-
found: false,
|
|
1812
|
-
origin,
|
|
1813
|
-
error: discoveryResult.error,
|
|
1814
|
-
attemptedSources: discoveryResult.attemptedSources,
|
|
1815
|
-
rawResponse: discoveryResult.rawResponse
|
|
1816
|
-
});
|
|
1817
|
-
}
|
|
1818
|
-
const doc = discoveryResult.document;
|
|
1819
|
-
const result = {
|
|
1820
|
-
found: true,
|
|
1821
|
-
origin,
|
|
1822
|
-
source: discoveryResult.source,
|
|
1823
|
-
instructions: doc.instructions,
|
|
1824
|
-
usage: "Use query_endpoint to get full pricing/requirements for a resource. Use execute_call (for payment) or authed_call (for SIWX auth) to call it.",
|
|
1825
|
-
resources: []
|
|
1826
|
-
};
|
|
1827
|
-
if (!fanOut) {
|
|
1828
|
-
result.resources = doc.resources.map((resourceUrl) => ({
|
|
1829
|
-
url: resourceUrl
|
|
1830
|
-
}));
|
|
1831
|
-
return mcpSuccess(result);
|
|
1832
|
-
}
|
|
1833
|
-
const resourceUrls = doc.resources;
|
|
1834
|
-
const allResources = [];
|
|
1835
|
-
for (let i = 0; i < resourceUrls.length; i += concurrency) {
|
|
1836
|
-
const batch = resourceUrls.slice(i, i + concurrency);
|
|
1837
|
-
const batchResults = await Promise.all(
|
|
1838
|
-
batch.map((resourceUrl) => queryResource(resourceUrl))
|
|
1839
|
-
);
|
|
1840
|
-
allResources.push(...batchResults);
|
|
1841
|
-
}
|
|
1842
|
-
result.resources = allResources;
|
|
1843
|
-
return mcpSuccess(result);
|
|
1844
|
-
} catch (err2) {
|
|
1845
|
-
return mcpError(err2, { tool: "discover_resources", url });
|
|
1846
|
-
}
|
|
1556
|
+
if (readFileResult.isOk()) {
|
|
1557
|
+
const data = readFileResult.value;
|
|
1558
|
+
const jsonParseResult = safeParseJson(walletSurface, data);
|
|
1559
|
+
if (jsonParseResult.isErr()) {
|
|
1560
|
+
return jsonErr(walletSurface, {
|
|
1561
|
+
cause: "parse",
|
|
1562
|
+
message: `The data in ${WALLET_FILE} is not valid JSON`
|
|
1563
|
+
});
|
|
1847
1564
|
}
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1565
|
+
const parseResult = safeParse(
|
|
1566
|
+
walletSurface,
|
|
1567
|
+
storedWalletSchema,
|
|
1568
|
+
jsonParseResult.value
|
|
1569
|
+
);
|
|
1570
|
+
if (parseResult.isErr()) {
|
|
1571
|
+
return parseResult;
|
|
1572
|
+
}
|
|
1573
|
+
const account2 = privateKeyToAccount(parseResult.value.privateKey);
|
|
1574
|
+
log.info(`Loaded wallet: ${account2.address}`);
|
|
1575
|
+
return ok({ account: account2, isNew: false });
|
|
1855
1576
|
}
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1577
|
+
const privateKey = generatePrivateKey();
|
|
1578
|
+
const account = privateKeyToAccount(privateKey);
|
|
1579
|
+
const stored = {
|
|
1580
|
+
privateKey,
|
|
1581
|
+
address: account.address,
|
|
1582
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1583
|
+
};
|
|
1584
|
+
const saveResult = await safeWriteFile(
|
|
1585
|
+
walletSurface,
|
|
1586
|
+
WALLET_FILE,
|
|
1587
|
+
JSON.stringify(stored, null, 2)
|
|
1588
|
+
).andThen(() => safeChmod(walletSurface, WALLET_FILE, 384));
|
|
1589
|
+
if (saveResult.isErr()) {
|
|
1590
|
+
return saveResult;
|
|
1862
1591
|
}
|
|
1592
|
+
log.info(`Created wallet: ${account.address}`);
|
|
1593
|
+
log.info(`Saved to: ${WALLET_FILE}`);
|
|
1594
|
+
return ok({ account, isNew: true });
|
|
1863
1595
|
}
|
|
1864
|
-
var
|
|
1865
|
-
var
|
|
1866
|
-
"src/
|
|
1596
|
+
var WALLET_FILE, storedWalletSchema, walletSurface;
|
|
1597
|
+
var init_wallet2 = __esm({
|
|
1598
|
+
"src/shared/wallet.ts"() {
|
|
1599
|
+
init_fs();
|
|
1600
|
+
init_json();
|
|
1601
|
+
init_parse();
|
|
1867
1602
|
init_log();
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
ownershipProofs: z.array(z.string()).optional(),
|
|
1875
|
-
instructions: z.string().optional()
|
|
1603
|
+
init_fs2();
|
|
1604
|
+
WALLET_FILE = configFile("wallet.json", "");
|
|
1605
|
+
storedWalletSchema = z$1.object({
|
|
1606
|
+
privateKey: z$1.string().regex(/^0x[a-fA-F0-9]{64}$/, "Invalid Ethereum private key").transform((privateKey) => privateKey),
|
|
1607
|
+
address: z$1.string().regex(/^0x[a-fA-F0-9]{40}$/, "Invalid Ethereum address").transform((address) => getAddress(address)),
|
|
1608
|
+
createdAt: z$1.string()
|
|
1876
1609
|
});
|
|
1610
|
+
walletSurface = "wallet";
|
|
1877
1611
|
}
|
|
1878
1612
|
});
|
|
1879
1613
|
|
|
@@ -1885,28 +1619,35 @@ __export(server_exports, {
|
|
|
1885
1619
|
var startServer;
|
|
1886
1620
|
var init_server = __esm({
|
|
1887
1621
|
"src/server/index.ts"() {
|
|
1888
|
-
|
|
1889
|
-
|
|
1622
|
+
init_x402_fetch();
|
|
1623
|
+
init_auth_fetch();
|
|
1890
1624
|
init_wallet();
|
|
1891
|
-
|
|
1625
|
+
init_check_endpoint();
|
|
1892
1626
|
init_redeem_invite2();
|
|
1893
1627
|
init_telemetry();
|
|
1628
|
+
init_discover_resources();
|
|
1894
1629
|
init_origins();
|
|
1630
|
+
init_version();
|
|
1895
1631
|
init_log();
|
|
1896
1632
|
init_wallet2();
|
|
1897
|
-
init_discover_resources();
|
|
1898
1633
|
init_redeem_invite();
|
|
1899
|
-
init_version();
|
|
1900
1634
|
startServer = async (flags) => {
|
|
1901
1635
|
log.info("Starting x402scan-mcp...");
|
|
1902
1636
|
const { dev, invite } = flags;
|
|
1903
|
-
const
|
|
1637
|
+
const walletResult = await getWallet();
|
|
1638
|
+
if (walletResult.isErr()) {
|
|
1639
|
+
log.error(JSON.stringify(walletResult.error, null, 2));
|
|
1640
|
+
console.error(walletResult.error);
|
|
1641
|
+
process.exit(1);
|
|
1642
|
+
}
|
|
1643
|
+
const { account } = walletResult.value;
|
|
1904
1644
|
const code = invite ?? process.env.INVITE_CODE;
|
|
1905
1645
|
if (code) {
|
|
1906
1646
|
await redeemInviteCode({
|
|
1907
1647
|
code,
|
|
1908
1648
|
dev,
|
|
1909
|
-
address: account.address
|
|
1649
|
+
address: account.address,
|
|
1650
|
+
surface: "startServer"
|
|
1910
1651
|
});
|
|
1911
1652
|
}
|
|
1912
1653
|
const server = new McpServer(
|
|
@@ -1952,7 +1693,7 @@ var init_server = __esm({
|
|
|
1952
1693
|
});
|
|
1953
1694
|
var getClient;
|
|
1954
1695
|
var init_get_client = __esm({
|
|
1955
|
-
"src/install/1-get-client/index.ts"() {
|
|
1696
|
+
"src/cli/install/1-get-client/index.ts"() {
|
|
1956
1697
|
init_clients();
|
|
1957
1698
|
getClient = async ({ client: flagClient, yes }) => {
|
|
1958
1699
|
if (yes) {
|
|
@@ -1991,14 +1732,14 @@ var init_get_client = __esm({
|
|
|
1991
1732
|
if (parsedClientSelection.success) {
|
|
1992
1733
|
return parsedClientSelection.data;
|
|
1993
1734
|
}
|
|
1994
|
-
outro(
|
|
1735
|
+
outro(chalk2.bold.red("No MCP client selected"));
|
|
1995
1736
|
process.exit(0);
|
|
1996
1737
|
};
|
|
1997
1738
|
}
|
|
1998
1739
|
});
|
|
1999
1740
|
var Platforms, getPlatformPath;
|
|
2000
1741
|
var init_platforms = __esm({
|
|
2001
|
-
"src/install/2-add-server/lib/platforms.ts"() {
|
|
1742
|
+
"src/cli/install/2-add-server/lib/platforms.ts"() {
|
|
2002
1743
|
Platforms = /* @__PURE__ */ ((Platforms2) => {
|
|
2003
1744
|
Platforms2["Windows"] = "win32";
|
|
2004
1745
|
Platforms2["MacOS"] = "darwin";
|
|
@@ -2033,25 +1774,68 @@ var init_platforms = __esm({
|
|
|
2033
1774
|
};
|
|
2034
1775
|
}
|
|
2035
1776
|
});
|
|
2036
|
-
var
|
|
1777
|
+
var errorType4, surface2, configResultFromThrowable;
|
|
1778
|
+
var init_result = __esm({
|
|
1779
|
+
"src/cli/install/2-add-server/lib/result.ts"() {
|
|
1780
|
+
errorType4 = "config";
|
|
1781
|
+
surface2 = "config_file";
|
|
1782
|
+
configResultFromThrowable = (fn, error) => resultFromThrowable(errorType4, surface2, fn, error);
|
|
1783
|
+
}
|
|
1784
|
+
});
|
|
1785
|
+
var parseContent, parseClientConfig, serializeJsonc, serializeClientConfig, stringifyObject;
|
|
2037
1786
|
var init_file_types = __esm({
|
|
2038
|
-
"src/install/2-add-server/lib/file-types.ts"() {
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
|
|
1787
|
+
"src/cli/install/2-add-server/lib/file-types.ts"() {
|
|
1788
|
+
init_fs();
|
|
1789
|
+
init_result();
|
|
1790
|
+
parseContent = (fileContent, format2, path3) => {
|
|
1791
|
+
return configResultFromThrowable(
|
|
1792
|
+
() => {
|
|
1793
|
+
let config;
|
|
1794
|
+
if (format2 === "yaml" /* YAML */) {
|
|
1795
|
+
config = yaml.load(fileContent);
|
|
1796
|
+
} else if (format2 === "toml" /* TOML */) {
|
|
1797
|
+
config = TOML.parse(fileContent);
|
|
1798
|
+
} else if (path3.endsWith(".jsonc")) {
|
|
1799
|
+
config = jsonc.parse(fileContent);
|
|
1800
|
+
} else {
|
|
1801
|
+
config = JSON.parse(fileContent);
|
|
1802
|
+
}
|
|
1803
|
+
return {
|
|
1804
|
+
config,
|
|
1805
|
+
fileContent
|
|
1806
|
+
};
|
|
1807
|
+
},
|
|
1808
|
+
(e) => ({
|
|
1809
|
+
cause: "parse_config",
|
|
1810
|
+
message: e instanceof Error ? e.message : "Failed to parse config file"
|
|
1811
|
+
})
|
|
1812
|
+
);
|
|
1813
|
+
};
|
|
1814
|
+
parseClientConfig = async ({ format: format2, path: path3 }) => {
|
|
1815
|
+
const readResult = await safeReadFile("config_file", path3);
|
|
1816
|
+
if (readResult.isErr()) return readResult;
|
|
1817
|
+
const parseResult = parseContent(readResult.value, format2, path3);
|
|
1818
|
+
if (parseResult.isErr()) return parseResult;
|
|
1819
|
+
return parseResult;
|
|
1820
|
+
};
|
|
1821
|
+
serializeJsonc = (config, originalContent) => {
|
|
1822
|
+
return configResultFromThrowable(
|
|
1823
|
+
() => {
|
|
1824
|
+
const modifications = [];
|
|
1825
|
+
for (const key of Object.keys(config)) {
|
|
1826
|
+
const keyPath = [key];
|
|
1827
|
+
const edits = jsonc.modify(originalContent, keyPath, config[key], {
|
|
1828
|
+
formattingOptions: { tabSize: 2, insertSpaces: true }
|
|
1829
|
+
});
|
|
1830
|
+
modifications.push(...edits);
|
|
1831
|
+
}
|
|
1832
|
+
return jsonc.applyEdits(originalContent, modifications);
|
|
1833
|
+
},
|
|
1834
|
+
(e) => ({
|
|
1835
|
+
cause: "serialize_config",
|
|
1836
|
+
message: e instanceof Error ? e.message : "Failed to serialize JSONC"
|
|
1837
|
+
})
|
|
1838
|
+
);
|
|
2055
1839
|
};
|
|
2056
1840
|
serializeClientConfig = ({ format: format2, path: path3 }, config, originalContent) => {
|
|
2057
1841
|
if (format2 === "yaml" /* YAML */) {
|
|
@@ -2065,24 +1849,13 @@ var init_file_types = __esm({
|
|
|
2065
1849
|
return TOML.stringify(config);
|
|
2066
1850
|
}
|
|
2067
1851
|
if (path3.endsWith(".jsonc") && originalContent) {
|
|
2068
|
-
|
|
2069
|
-
|
|
2070
|
-
|
|
2071
|
-
for (const key of Object.keys(config)) {
|
|
2072
|
-
const path4 = [key];
|
|
2073
|
-
const edits = jsonc.modify(editedContent, path4, config[key], {
|
|
2074
|
-
formattingOptions: { tabSize: 2, insertSpaces: true }
|
|
2075
|
-
});
|
|
2076
|
-
modifications.push(...edits);
|
|
2077
|
-
}
|
|
2078
|
-
return jsonc.applyEdits(originalContent, modifications);
|
|
2079
|
-
} catch (error) {
|
|
2080
|
-
console.log(
|
|
2081
|
-
`Error applying JSONC edits: ${error instanceof Error ? error.message : String(error)}`
|
|
2082
|
-
);
|
|
2083
|
-
console.log("Falling back to JSON.stringify (comments will be lost)");
|
|
2084
|
-
return JSON.stringify(config, null, 2);
|
|
1852
|
+
const result = serializeJsonc(config, originalContent);
|
|
1853
|
+
if (result.isOk()) {
|
|
1854
|
+
return result.value;
|
|
2085
1855
|
}
|
|
1856
|
+
console.log(`Error applying JSONC edits: ${result.error.message}`);
|
|
1857
|
+
console.log("Falling back to JSON.stringify (comments will be lost)");
|
|
1858
|
+
return JSON.stringify(config, null, 2);
|
|
2086
1859
|
}
|
|
2087
1860
|
return JSON.stringify(config, null, 2);
|
|
2088
1861
|
};
|
|
@@ -2103,7 +1876,7 @@ var init_file_types = __esm({
|
|
|
2103
1876
|
});
|
|
2104
1877
|
var getClientConfigFile;
|
|
2105
1878
|
var init_client_config_file = __esm({
|
|
2106
|
-
"src/install/2-add-server/lib/client-config-file.ts"() {
|
|
1879
|
+
"src/cli/install/2-add-server/lib/client-config-file.ts"() {
|
|
2107
1880
|
init_platforms();
|
|
2108
1881
|
init_log();
|
|
2109
1882
|
init_clients();
|
|
@@ -2214,7 +1987,7 @@ var init_client_config_file = __esm({
|
|
|
2214
1987
|
"opencode.json"
|
|
2215
1988
|
);
|
|
2216
1989
|
const jsoncPath = jsonPath.replace(".json", ".jsonc");
|
|
2217
|
-
if (
|
|
1990
|
+
if (fs2__default.existsSync(jsoncPath)) {
|
|
2218
1991
|
log.info(`Found .jsonc file for OpenCode, using: ${jsoncPath}`);
|
|
2219
1992
|
return {
|
|
2220
1993
|
path: jsoncPath,
|
|
@@ -2235,10 +2008,10 @@ var init_client_config_file = __esm({
|
|
|
2235
2008
|
}
|
|
2236
2009
|
});
|
|
2237
2010
|
|
|
2238
|
-
// src/install/2-add-server/lib/nested-values.ts
|
|
2011
|
+
// src/cli/install/2-add-server/lib/nested-values.ts
|
|
2239
2012
|
var getNestedValue, setNestedValue;
|
|
2240
2013
|
var init_nested_values = __esm({
|
|
2241
|
-
"src/install/2-add-server/lib/nested-values.ts"() {
|
|
2014
|
+
"src/cli/install/2-add-server/lib/nested-values.ts"() {
|
|
2242
2015
|
getNestedValue = (obj, path3) => {
|
|
2243
2016
|
const keys = path3.split(".");
|
|
2244
2017
|
let current = obj;
|
|
@@ -2264,193 +2037,34 @@ var init_nested_values = __esm({
|
|
|
2264
2037
|
}
|
|
2265
2038
|
});
|
|
2266
2039
|
|
|
2267
|
-
// src/install/2-add-server/lib/index.ts
|
|
2268
|
-
var
|
|
2269
|
-
"src/install/2-add-server/lib/index.ts"() {
|
|
2040
|
+
// src/cli/install/2-add-server/lib/index.ts
|
|
2041
|
+
var init_lib3 = __esm({
|
|
2042
|
+
"src/cli/install/2-add-server/lib/index.ts"() {
|
|
2270
2043
|
init_client_config_file();
|
|
2271
2044
|
init_file_types();
|
|
2272
2045
|
init_nested_values();
|
|
2273
2046
|
init_platforms();
|
|
2274
2047
|
}
|
|
2275
2048
|
});
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
|
|
2284
|
-
|
|
2285
|
-
console.log();
|
|
2286
|
-
console.log(
|
|
2287
|
-
JSON.stringify(
|
|
2288
|
-
{
|
|
2289
|
-
[serverName]: {
|
|
2290
|
-
command,
|
|
2291
|
-
args,
|
|
2292
|
-
working_directory: null,
|
|
2293
|
-
start_on_launch: true
|
|
2294
|
-
}
|
|
2295
|
-
},
|
|
2296
|
-
null,
|
|
2297
|
-
2
|
|
2298
|
-
)
|
|
2299
|
-
);
|
|
2300
|
-
console.log();
|
|
2301
|
-
log$1.message(
|
|
2302
|
-
`Read Warp's documentation at https://docs.warp.dev/knowledge-and-collaboration/mcp`
|
|
2303
|
-
);
|
|
2304
|
-
const addedToWarp = await confirm({
|
|
2305
|
-
message: "Did you add the MCP server to your Warp config?"
|
|
2306
|
-
});
|
|
2307
|
-
if (!addedToWarp) {
|
|
2308
|
-
throw new Error("Warp MCP server not added");
|
|
2309
|
-
}
|
|
2310
|
-
}
|
|
2311
|
-
const clientFileTarget = getClientConfigFile(client);
|
|
2312
|
-
const { name } = clientMetadata[client];
|
|
2313
|
-
try {
|
|
2314
|
-
let config = {};
|
|
2315
|
-
let content = void 0;
|
|
2316
|
-
log.info(`Checking if config file exists at: ${clientFileTarget.path}`);
|
|
2317
|
-
if (!fs__default.existsSync(clientFileTarget.path)) {
|
|
2318
|
-
log.info("Config file not found, creating default empty config");
|
|
2319
|
-
setNestedValue(config, clientFileTarget.configKey, {});
|
|
2320
|
-
log.info("Config created successfully");
|
|
2321
|
-
if (!globalFlags.yes) {
|
|
2322
|
-
await wait({
|
|
2323
|
-
startText: "Locating config file",
|
|
2324
|
-
stopText: `No config found, creating default empty config`,
|
|
2325
|
-
ms: 1e3
|
|
2326
|
-
});
|
|
2327
|
-
}
|
|
2328
|
-
} else {
|
|
2329
|
-
log.info("Config file found, reading config file content");
|
|
2330
|
-
const { config: rawConfig, fileContent } = parseClientConfig(clientFileTarget);
|
|
2331
|
-
config = rawConfig;
|
|
2332
|
-
content = fileContent;
|
|
2333
|
-
const existingValue = getNestedValue(
|
|
2334
|
-
rawConfig,
|
|
2335
|
-
clientFileTarget.configKey
|
|
2336
|
-
);
|
|
2337
|
-
if (!existingValue) {
|
|
2338
|
-
setNestedValue(rawConfig, clientFileTarget.configKey, {});
|
|
2339
|
-
}
|
|
2340
|
-
log.info(
|
|
2341
|
-
`Config loaded successfully: ${JSON.stringify(rawConfig, null, 2)}`
|
|
2342
|
-
);
|
|
2343
|
-
if (!globalFlags.yes) {
|
|
2344
|
-
await wait({
|
|
2345
|
-
startText: `Locating config file`,
|
|
2346
|
-
stopText: `Config loaded from ${clientFileTarget.path}`,
|
|
2347
|
-
ms: 1e3
|
|
2348
|
-
});
|
|
2349
|
-
}
|
|
2350
|
-
}
|
|
2351
|
-
const servers = getNestedValue(config, clientFileTarget.configKey);
|
|
2352
|
-
if (!servers || typeof servers !== "object") {
|
|
2353
|
-
log.error(`Invalid ${clientFileTarget.configKey} structure in config`);
|
|
2354
|
-
log$1.error(
|
|
2355
|
-
chalk.bold.red(
|
|
2356
|
-
`Invalid ${clientFileTarget.configKey} structure in config`
|
|
2357
|
-
)
|
|
2358
|
-
);
|
|
2359
|
-
throw new Error(`Invalid ${clientFileTarget.configKey} structure`);
|
|
2360
|
-
}
|
|
2361
|
-
if (client === "goose" /* Goose */) {
|
|
2362
|
-
servers[serverName] = {
|
|
2363
|
-
name: serverName,
|
|
2364
|
-
cmd: command,
|
|
2365
|
-
args,
|
|
2366
|
-
enabled: true,
|
|
2367
|
-
envs: {},
|
|
2368
|
-
type: "stdio",
|
|
2369
|
-
timeout: 300
|
|
2370
|
-
};
|
|
2371
|
-
} else if (client === "zed" /* Zed */) {
|
|
2372
|
-
servers[serverName] = {
|
|
2373
|
-
source: "custom",
|
|
2374
|
-
command,
|
|
2375
|
-
args,
|
|
2376
|
-
env: {}
|
|
2377
|
-
};
|
|
2378
|
-
} else if (client === "opencode" /* Opencode */) {
|
|
2379
|
-
servers[serverName] = {
|
|
2380
|
-
type: "local",
|
|
2381
|
-
command,
|
|
2382
|
-
args,
|
|
2383
|
-
enabled: true,
|
|
2384
|
-
environment: {}
|
|
2385
|
-
};
|
|
2386
|
-
} else {
|
|
2387
|
-
servers[serverName] = {
|
|
2388
|
-
command,
|
|
2389
|
-
args
|
|
2390
|
-
};
|
|
2391
|
-
}
|
|
2392
|
-
if (!globalFlags.yes) {
|
|
2393
|
-
await new Promise((resolve) => setTimeout(resolve, 1e3));
|
|
2394
|
-
}
|
|
2395
|
-
if (!globalFlags.yes) {
|
|
2396
|
-
log$1.step(
|
|
2397
|
-
`The following will be added to ${chalk.bold.underline(clientFileTarget.path)}`
|
|
2398
|
-
);
|
|
2399
|
-
}
|
|
2400
|
-
const configStr = formatDiffByFormat(
|
|
2401
|
-
{
|
|
2402
|
-
[clientFileTarget.configKey]: {
|
|
2403
|
-
[serverName]: servers[serverName]
|
|
2404
|
-
}
|
|
2405
|
-
},
|
|
2406
|
-
clientFileTarget.format
|
|
2407
|
-
);
|
|
2408
|
-
if (!globalFlags.yes) {
|
|
2409
|
-
await stream.message(
|
|
2410
|
-
(async function* () {
|
|
2411
|
-
for (const num of Array.from(
|
|
2412
|
-
{ length: configStr.length },
|
|
2413
|
-
(_, i) => i
|
|
2414
|
-
)) {
|
|
2415
|
-
const char = configStr[num];
|
|
2416
|
-
yield char;
|
|
2417
|
-
if (!["\n", " ", "\u2500", "\u256E", "\u256D", "\u2570", "\u256F", "\u2502"].includes(char)) {
|
|
2418
|
-
await new Promise((resolve) => setTimeout(resolve, 5));
|
|
2419
|
-
} else {
|
|
2420
|
-
await new Promise((resolve) => setTimeout(resolve, 2));
|
|
2421
|
-
}
|
|
2422
|
-
}
|
|
2423
|
-
})()
|
|
2424
|
-
);
|
|
2425
|
-
await new Promise((resolve) => setTimeout(resolve, 1e3));
|
|
2426
|
-
}
|
|
2427
|
-
const isConfirmed = globalFlags.yes ? true : await confirm({
|
|
2428
|
-
message: `Would you like to proceed?`,
|
|
2429
|
-
active: "Install MCP",
|
|
2430
|
-
inactive: "Cancel"
|
|
2431
|
-
});
|
|
2432
|
-
if (isConfirmed !== true) {
|
|
2433
|
-
outro(chalk.bold.red("Installation cancelled"));
|
|
2434
|
-
process.exit(0);
|
|
2435
|
-
}
|
|
2436
|
-
const configContent = serializeClientConfig(
|
|
2437
|
-
clientFileTarget,
|
|
2438
|
-
config,
|
|
2439
|
-
content
|
|
2440
|
-
);
|
|
2441
|
-
fs__default.writeFileSync(clientFileTarget.path, configContent);
|
|
2442
|
-
log$1.success(chalk.bold.green(`Added x402scan MCP to ${name}`));
|
|
2443
|
-
} catch (e) {
|
|
2444
|
-
log$1.error(chalk.bold.red(`Error adding x402scan MCP to ${name}`));
|
|
2445
|
-
throw e;
|
|
2049
|
+
var wait;
|
|
2050
|
+
var init_wait = __esm({
|
|
2051
|
+
"src/cli/lib/wait.ts"() {
|
|
2052
|
+
wait = async ({ startText, stopText, ms }) => {
|
|
2053
|
+
const { start: startSpinner, stop: stopSpinner } = spinner();
|
|
2054
|
+
startSpinner(startText);
|
|
2055
|
+
await new Promise((resolve) => setTimeout(resolve, ms));
|
|
2056
|
+
stopSpinner(stopText);
|
|
2057
|
+
};
|
|
2446
2058
|
}
|
|
2447
|
-
}
|
|
2448
|
-
var getMcpConfig, formatDiffByFormat;
|
|
2059
|
+
});
|
|
2060
|
+
var getMcpConfig, addServer, formatDiffByFormat;
|
|
2449
2061
|
var init_add_server = __esm({
|
|
2450
|
-
"src/install/2-add-server/index.ts"() {
|
|
2062
|
+
"src/cli/install/2-add-server/index.ts"() {
|
|
2063
|
+
init_fs();
|
|
2451
2064
|
init_log();
|
|
2452
2065
|
init_clients();
|
|
2453
|
-
|
|
2066
|
+
init_lib3();
|
|
2067
|
+
init_version();
|
|
2454
2068
|
init_wait();
|
|
2455
2069
|
getMcpConfig = (globalFlags) => {
|
|
2456
2070
|
if (globalFlags.dev) {
|
|
@@ -2463,9 +2077,191 @@ var init_add_server = __esm({
|
|
|
2463
2077
|
return {
|
|
2464
2078
|
serverName: "x402",
|
|
2465
2079
|
command: "npx",
|
|
2466
|
-
args: ["-y",
|
|
2080
|
+
args: ["-y", `@x402scan/mcp@${DIST_TAG}`]
|
|
2467
2081
|
};
|
|
2468
2082
|
};
|
|
2083
|
+
addServer = async (client, globalFlags) => {
|
|
2084
|
+
const { serverName, command, args } = getMcpConfig(globalFlags);
|
|
2085
|
+
if (client === "warp" /* Warp */) {
|
|
2086
|
+
log$1.info(
|
|
2087
|
+
chalk2.bold.yellow("Warp requires a manual installation through their UI.")
|
|
2088
|
+
);
|
|
2089
|
+
log$1.message(
|
|
2090
|
+
"Please copy the following configuration object and add it to your Warp MCP config:"
|
|
2091
|
+
);
|
|
2092
|
+
console.log();
|
|
2093
|
+
console.log(
|
|
2094
|
+
JSON.stringify(
|
|
2095
|
+
{
|
|
2096
|
+
[serverName]: {
|
|
2097
|
+
command,
|
|
2098
|
+
args,
|
|
2099
|
+
working_directory: null,
|
|
2100
|
+
start_on_launch: true
|
|
2101
|
+
}
|
|
2102
|
+
},
|
|
2103
|
+
null,
|
|
2104
|
+
2
|
|
2105
|
+
)
|
|
2106
|
+
);
|
|
2107
|
+
console.log();
|
|
2108
|
+
log$1.message(
|
|
2109
|
+
`Read Warp's documentation at https://docs.warp.dev/knowledge-and-collaboration/mcp`
|
|
2110
|
+
);
|
|
2111
|
+
const addedToWarp = await confirm({
|
|
2112
|
+
message: "Did you add the MCP server to your Warp config?"
|
|
2113
|
+
});
|
|
2114
|
+
if (!addedToWarp) {
|
|
2115
|
+
return err("user", "install", {
|
|
2116
|
+
cause: "warp_mcp_server_not_added",
|
|
2117
|
+
message: "Warp MCP server not added"
|
|
2118
|
+
});
|
|
2119
|
+
}
|
|
2120
|
+
}
|
|
2121
|
+
const clientFileTarget = getClientConfigFile(client);
|
|
2122
|
+
const { name } = clientMetadata[client];
|
|
2123
|
+
let config = {};
|
|
2124
|
+
let content = void 0;
|
|
2125
|
+
log.info(`Checking if config file exists at: ${clientFileTarget.path}`);
|
|
2126
|
+
if (!fs2__default.existsSync(clientFileTarget.path)) {
|
|
2127
|
+
log.info("Config file not found, creating default empty config");
|
|
2128
|
+
setNestedValue(config, clientFileTarget.configKey, {});
|
|
2129
|
+
log.info("Config created successfully");
|
|
2130
|
+
if (!globalFlags.yes) {
|
|
2131
|
+
await wait({
|
|
2132
|
+
startText: "Locating config file",
|
|
2133
|
+
stopText: `No config found, creating default empty config`,
|
|
2134
|
+
ms: 1e3
|
|
2135
|
+
});
|
|
2136
|
+
}
|
|
2137
|
+
} else {
|
|
2138
|
+
log.info("Config file found, reading config file content");
|
|
2139
|
+
const parseResult = await parseClientConfig(clientFileTarget);
|
|
2140
|
+
if (parseResult.isErr()) {
|
|
2141
|
+
log$1.error(
|
|
2142
|
+
chalk2.bold.red(`Error reading config: ${parseResult.error.message}`)
|
|
2143
|
+
);
|
|
2144
|
+
outro(chalk2.bold.red(`Error adding x402scan MCP to ${name}`));
|
|
2145
|
+
process.exit(1);
|
|
2146
|
+
}
|
|
2147
|
+
const { config: rawConfig, fileContent } = parseResult.value;
|
|
2148
|
+
config = rawConfig;
|
|
2149
|
+
content = fileContent;
|
|
2150
|
+
const existingValue = getNestedValue(rawConfig, clientFileTarget.configKey);
|
|
2151
|
+
if (!existingValue) {
|
|
2152
|
+
setNestedValue(rawConfig, clientFileTarget.configKey, {});
|
|
2153
|
+
}
|
|
2154
|
+
if (!globalFlags.yes) {
|
|
2155
|
+
await wait({
|
|
2156
|
+
startText: `Locating config file`,
|
|
2157
|
+
stopText: `Config loaded from ${clientFileTarget.path}`,
|
|
2158
|
+
ms: 1e3
|
|
2159
|
+
});
|
|
2160
|
+
}
|
|
2161
|
+
}
|
|
2162
|
+
const servers = getNestedValue(config, clientFileTarget.configKey);
|
|
2163
|
+
if (!servers || typeof servers !== "object") {
|
|
2164
|
+
log.error(`Invalid ${clientFileTarget.configKey} structure in config`);
|
|
2165
|
+
log$1.error(
|
|
2166
|
+
chalk2.bold.red(
|
|
2167
|
+
`Invalid ${clientFileTarget.configKey} structure in config`
|
|
2168
|
+
)
|
|
2169
|
+
);
|
|
2170
|
+
outro(chalk2.bold.red(`Error adding x402scan MCP to ${name}`));
|
|
2171
|
+
process.exit(1);
|
|
2172
|
+
}
|
|
2173
|
+
if (client === "goose" /* Goose */) {
|
|
2174
|
+
servers[serverName] = {
|
|
2175
|
+
name: serverName,
|
|
2176
|
+
cmd: command,
|
|
2177
|
+
args,
|
|
2178
|
+
enabled: true,
|
|
2179
|
+
envs: {},
|
|
2180
|
+
type: "stdio",
|
|
2181
|
+
timeout: 300
|
|
2182
|
+
};
|
|
2183
|
+
} else if (client === "zed" /* Zed */) {
|
|
2184
|
+
servers[serverName] = {
|
|
2185
|
+
source: "custom",
|
|
2186
|
+
command,
|
|
2187
|
+
args,
|
|
2188
|
+
env: {}
|
|
2189
|
+
};
|
|
2190
|
+
} else if (client === "opencode" /* Opencode */) {
|
|
2191
|
+
servers[serverName] = {
|
|
2192
|
+
type: "local",
|
|
2193
|
+
command,
|
|
2194
|
+
args,
|
|
2195
|
+
enabled: true,
|
|
2196
|
+
environment: {}
|
|
2197
|
+
};
|
|
2198
|
+
} else {
|
|
2199
|
+
servers[serverName] = {
|
|
2200
|
+
command,
|
|
2201
|
+
args
|
|
2202
|
+
};
|
|
2203
|
+
}
|
|
2204
|
+
if (!globalFlags.yes) {
|
|
2205
|
+
await new Promise((resolve) => setTimeout(resolve, 1e3));
|
|
2206
|
+
log$1.step(
|
|
2207
|
+
`The following will be added to ${chalk2.bold.underline(clientFileTarget.path)}`
|
|
2208
|
+
);
|
|
2209
|
+
}
|
|
2210
|
+
const configStr = formatDiffByFormat(
|
|
2211
|
+
{
|
|
2212
|
+
[clientFileTarget.configKey]: {
|
|
2213
|
+
[serverName]: servers[serverName]
|
|
2214
|
+
}
|
|
2215
|
+
},
|
|
2216
|
+
clientFileTarget.format
|
|
2217
|
+
);
|
|
2218
|
+
if (!globalFlags.yes) {
|
|
2219
|
+
await stream.message(
|
|
2220
|
+
(async function* () {
|
|
2221
|
+
for (const num of Array.from(
|
|
2222
|
+
{ length: configStr.length },
|
|
2223
|
+
(_, i) => i
|
|
2224
|
+
)) {
|
|
2225
|
+
const char = configStr[num];
|
|
2226
|
+
yield char;
|
|
2227
|
+
if (!["\n", " ", "\u2500", "\u256E", "\u256D", "\u2570", "\u256F", "\u2502"].includes(char)) {
|
|
2228
|
+
await new Promise((resolve) => setTimeout(resolve, 5));
|
|
2229
|
+
} else {
|
|
2230
|
+
await new Promise((resolve) => setTimeout(resolve, 2));
|
|
2231
|
+
}
|
|
2232
|
+
}
|
|
2233
|
+
})()
|
|
2234
|
+
);
|
|
2235
|
+
await new Promise((resolve) => setTimeout(resolve, 1e3));
|
|
2236
|
+
}
|
|
2237
|
+
const isConfirmed = globalFlags.yes ? true : await confirm({
|
|
2238
|
+
message: `Would you like to proceed?`,
|
|
2239
|
+
active: "Install MCP",
|
|
2240
|
+
inactive: "Cancel"
|
|
2241
|
+
});
|
|
2242
|
+
if (isConfirmed !== true) {
|
|
2243
|
+
outro(chalk2.bold.red("Installation cancelled"));
|
|
2244
|
+
process.exit(0);
|
|
2245
|
+
}
|
|
2246
|
+
const configContent = serializeClientConfig(
|
|
2247
|
+
clientFileTarget,
|
|
2248
|
+
config,
|
|
2249
|
+
content
|
|
2250
|
+
);
|
|
2251
|
+
const writeResult = await safeWriteFile(
|
|
2252
|
+
"config_file",
|
|
2253
|
+
clientFileTarget.path,
|
|
2254
|
+
configContent
|
|
2255
|
+
);
|
|
2256
|
+
if (writeResult.isErr()) {
|
|
2257
|
+
log$1.error(
|
|
2258
|
+
chalk2.bold.red(`Error writing config: ${writeResult.error.message}`)
|
|
2259
|
+
);
|
|
2260
|
+
outro(chalk2.bold.red(`Error adding x402scan MCP to ${name}`));
|
|
2261
|
+
process.exit(1);
|
|
2262
|
+
}
|
|
2263
|
+
log$1.success(chalk2.bold.green(`Added x402scan MCP to ${name}`));
|
|
2264
|
+
};
|
|
2469
2265
|
formatDiffByFormat = (obj, format2) => {
|
|
2470
2266
|
const str = stringifyObject(obj, format2);
|
|
2471
2267
|
switch (format2) {
|
|
@@ -2475,7 +2271,7 @@ var init_add_server = __esm({
|
|
|
2475
2271
|
const diffLines = [0, 1, numLines - 2, numLines - 1];
|
|
2476
2272
|
const isDiffLine = !diffLines.includes(index);
|
|
2477
2273
|
if (isDiffLine) {
|
|
2478
|
-
return `${
|
|
2274
|
+
return `${chalk2.bold.green(`+ ${line.slice(2)}`)}`;
|
|
2479
2275
|
}
|
|
2480
2276
|
return line;
|
|
2481
2277
|
}).join("\n");
|
|
@@ -2485,14 +2281,14 @@ var init_add_server = __esm({
|
|
|
2485
2281
|
const diffLines = [0, 1, str.length - 2, str.length - 1];
|
|
2486
2282
|
const isDiffLine = !diffLines.includes(index);
|
|
2487
2283
|
if (isDiffLine) {
|
|
2488
|
-
return `${
|
|
2284
|
+
return `${chalk2.bold.green(`+ ${line.slice(2)}`)}`;
|
|
2489
2285
|
}
|
|
2490
2286
|
return line;
|
|
2491
2287
|
}).join("\n");
|
|
2492
2288
|
}
|
|
2493
2289
|
case "toml" /* TOML */: {
|
|
2494
2290
|
return str.split("\n").filter((line) => line.trim() !== "").map((line) => {
|
|
2495
|
-
return `${
|
|
2291
|
+
return `${chalk2.bold.green(`+ ${line.trim()}`)}`;
|
|
2496
2292
|
}).join("\n");
|
|
2497
2293
|
}
|
|
2498
2294
|
}
|
|
@@ -2501,7 +2297,7 @@ var init_add_server = __esm({
|
|
|
2501
2297
|
});
|
|
2502
2298
|
var redeemInviteCode2;
|
|
2503
2299
|
var init_redeem_invite3 = __esm({
|
|
2504
|
-
"src/install/3-redeem-invite/index.ts"() {
|
|
2300
|
+
"src/cli/install/3-redeem-invite/index.ts"() {
|
|
2505
2301
|
init_wait();
|
|
2506
2302
|
init_redeem_invite();
|
|
2507
2303
|
redeemInviteCode2 = async (props, flags) => {
|
|
@@ -2511,26 +2307,18 @@ var init_redeem_invite3 = __esm({
|
|
|
2511
2307
|
}
|
|
2512
2308
|
const result = await redeemInviteCode(props);
|
|
2513
2309
|
return result.match(
|
|
2514
|
-
async ({
|
|
2310
|
+
async ({ amount, txHash }) => {
|
|
2515
2311
|
if (!flags.yes) {
|
|
2516
2312
|
s.stop("Invite code redeemed successfully!");
|
|
2517
2313
|
await wait({
|
|
2518
2314
|
startText: "Processing...",
|
|
2519
|
-
stopText:
|
|
2520
|
-
`${
|
|
2315
|
+
stopText: chalk2.green(
|
|
2316
|
+
`${chalk2.bold(amount)} USDC has been sent to your wallet!`
|
|
2521
2317
|
),
|
|
2522
2318
|
ms: 1e3
|
|
2523
2319
|
});
|
|
2524
|
-
} else {
|
|
2525
|
-
log$1.success(
|
|
2526
|
-
chalk.green(
|
|
2527
|
-
`${chalk.bold(data.amount)} USDC has been sent to your wallet!`
|
|
2528
|
-
)
|
|
2529
|
-
);
|
|
2530
2320
|
}
|
|
2531
|
-
log$1.info(
|
|
2532
|
-
chalk.dim(`Transaction: https://basescan.org/tx/${data.txHash}`)
|
|
2533
|
-
);
|
|
2321
|
+
log$1.info(chalk2.dim(`Transaction: https://basescan.org/tx/${txHash}`));
|
|
2534
2322
|
return true;
|
|
2535
2323
|
},
|
|
2536
2324
|
(error) => {
|
|
@@ -2538,7 +2326,7 @@ var init_redeem_invite3 = __esm({
|
|
|
2538
2326
|
s.stop("Invite code redemption failed");
|
|
2539
2327
|
}
|
|
2540
2328
|
log$1.warning(
|
|
2541
|
-
|
|
2329
|
+
chalk2.yellow(`Failed to redeem invite code: ${error.message}`)
|
|
2542
2330
|
);
|
|
2543
2331
|
return false;
|
|
2544
2332
|
}
|
|
@@ -2546,95 +2334,209 @@ var init_redeem_invite3 = __esm({
|
|
|
2546
2334
|
};
|
|
2547
2335
|
}
|
|
2548
2336
|
});
|
|
2337
|
+
var promptDeposit;
|
|
2338
|
+
var init_deposit = __esm({
|
|
2339
|
+
"src/cli/lib/deposit.ts"() {
|
|
2340
|
+
init_networks();
|
|
2341
|
+
init_wait();
|
|
2342
|
+
init_utils();
|
|
2343
|
+
init_redeem_invite();
|
|
2344
|
+
promptDeposit = async (props) => {
|
|
2345
|
+
const { address, flags, surface: surface3 } = props;
|
|
2346
|
+
const depositLink = getDepositLink(address, flags);
|
|
2347
|
+
const depositChoice = flags.yes || surface3 === "guided" ? "manual" : await select({
|
|
2348
|
+
message: chalk2.bold("How would you like to deposit?"),
|
|
2349
|
+
initialValue: "guided",
|
|
2350
|
+
options: [
|
|
2351
|
+
{
|
|
2352
|
+
label: "Guided - Recommended",
|
|
2353
|
+
value: "guided",
|
|
2354
|
+
hint: "Online portal in x402scan"
|
|
2355
|
+
},
|
|
2356
|
+
{
|
|
2357
|
+
label: "Manual",
|
|
2358
|
+
value: "manual",
|
|
2359
|
+
hint: "Print deposit instructions"
|
|
2360
|
+
},
|
|
2361
|
+
{
|
|
2362
|
+
label: "Redeem Invite Code",
|
|
2363
|
+
value: "invite",
|
|
2364
|
+
hint: "Enter an invite code for starter money"
|
|
2365
|
+
},
|
|
2366
|
+
{
|
|
2367
|
+
label: "Skip",
|
|
2368
|
+
value: void 0,
|
|
2369
|
+
hint: "Skip deposit process - functionality limited"
|
|
2370
|
+
}
|
|
2371
|
+
]
|
|
2372
|
+
});
|
|
2373
|
+
if (depositChoice === "guided") {
|
|
2374
|
+
await wait({
|
|
2375
|
+
startText: "Opening deposit page...",
|
|
2376
|
+
stopText: `Opening ${chalk2.underline.hex("#2563eb")(depositLink)}`,
|
|
2377
|
+
ms: 1e3
|
|
2378
|
+
});
|
|
2379
|
+
await open(depositLink);
|
|
2380
|
+
} else if (depositChoice === "manual") {
|
|
2381
|
+
log$1.step(chalk2.bold("Account Information"));
|
|
2382
|
+
log$1.message(`Address: ${address}`);
|
|
2383
|
+
log$1.message(`Network: ${getChainName(DEFAULT_NETWORK)}`);
|
|
2384
|
+
log$1.step(chalk2.bold("Online Portal"));
|
|
2385
|
+
log$1.message(`${chalk2.underline(depositLink)}`);
|
|
2386
|
+
} else if (depositChoice === "invite") {
|
|
2387
|
+
const code = await text({
|
|
2388
|
+
message: "Enter your invite code",
|
|
2389
|
+
placeholder: "MRT-XXXXX",
|
|
2390
|
+
validate: (value) => {
|
|
2391
|
+
if (!value || value.trim().length === 0) {
|
|
2392
|
+
return "Please enter an invite code";
|
|
2393
|
+
}
|
|
2394
|
+
}
|
|
2395
|
+
});
|
|
2396
|
+
if (typeof code !== "string") {
|
|
2397
|
+
return promptDeposit({ address, flags, surface: surface3 });
|
|
2398
|
+
}
|
|
2399
|
+
const s = spinner();
|
|
2400
|
+
s.start("Redeeming invite code...");
|
|
2401
|
+
const redeemResult = await redeemInviteCode({
|
|
2402
|
+
code,
|
|
2403
|
+
dev: flags.dev,
|
|
2404
|
+
address,
|
|
2405
|
+
surface: "redeemInvite"
|
|
2406
|
+
});
|
|
2407
|
+
if (redeemResult.isErr()) {
|
|
2408
|
+
s.stop("Invite code redemption failed");
|
|
2409
|
+
log$1.error("Failed to redeem invite code");
|
|
2410
|
+
return promptDeposit({ address, flags, surface: surface3 });
|
|
2411
|
+
}
|
|
2412
|
+
s.stop("Invite code redeemed successfully!");
|
|
2413
|
+
const { amount, txHash } = redeemResult.value;
|
|
2414
|
+
await wait({
|
|
2415
|
+
startText: "Processing...",
|
|
2416
|
+
stopText: chalk2.green(
|
|
2417
|
+
`${chalk2.bold(amount)} USDC has been sent to your wallet!`
|
|
2418
|
+
),
|
|
2419
|
+
ms: 1500
|
|
2420
|
+
});
|
|
2421
|
+
log$1.success(chalk2.bold(`Your wallet has been funded with ${amount} USDC`));
|
|
2422
|
+
if (txHash) {
|
|
2423
|
+
log$1.info(chalk2.dim(`Transaction: https://basescan.org/tx/${txHash}`));
|
|
2424
|
+
}
|
|
2425
|
+
return;
|
|
2426
|
+
}
|
|
2427
|
+
};
|
|
2428
|
+
}
|
|
2429
|
+
});
|
|
2549
2430
|
var addFunds;
|
|
2550
2431
|
var init_add_funds = __esm({
|
|
2551
|
-
"src/install/4-add-funds/index.ts"() {
|
|
2432
|
+
"src/cli/install/4-add-funds/index.ts"() {
|
|
2552
2433
|
init_balance();
|
|
2553
2434
|
init_deposit();
|
|
2554
|
-
init_wait();
|
|
2555
2435
|
addFunds = async ({ flags, address, isNew }) => {
|
|
2556
2436
|
if (isNew) {
|
|
2557
2437
|
if (!flags.yes) {
|
|
2558
2438
|
await new Promise((resolve) => setTimeout(resolve, 1e3));
|
|
2559
2439
|
}
|
|
2560
2440
|
log$1.info("To use paid API tools, you will need USDC in your wallet.");
|
|
2561
|
-
await promptDeposit(address, flags);
|
|
2441
|
+
await promptDeposit({ address, flags, surface: "add-funds" });
|
|
2562
2442
|
} else {
|
|
2563
|
-
const {
|
|
2564
|
-
|
|
2565
|
-
|
|
2566
|
-
|
|
2567
|
-
|
|
2568
|
-
|
|
2569
|
-
|
|
2443
|
+
const { start, stop } = spinner();
|
|
2444
|
+
start("Checking balance...");
|
|
2445
|
+
const balanceResult = await getBalance({
|
|
2446
|
+
address,
|
|
2447
|
+
flags,
|
|
2448
|
+
surface: "add-funds"
|
|
2449
|
+
});
|
|
2450
|
+
if (balanceResult.isOk()) {
|
|
2451
|
+
stop(`Balance: ${chalk2.bold(`${balanceResult.value.balance} USDC`)} `);
|
|
2452
|
+
} else {
|
|
2453
|
+
stop(`Error: ${balanceResult.error.message}`);
|
|
2454
|
+
return;
|
|
2570
2455
|
}
|
|
2571
|
-
|
|
2456
|
+
const balance = balanceResult.value;
|
|
2457
|
+
if (balance.balance < 1) {
|
|
2572
2458
|
log$1.warning(
|
|
2573
|
-
|
|
2574
|
-
`Your balance is low (${
|
|
2459
|
+
chalk2.bold(
|
|
2460
|
+
`Your balance is low (${balance.balance} USDC). Consider topping up.`
|
|
2575
2461
|
)
|
|
2576
2462
|
);
|
|
2577
|
-
await promptDeposit(address, flags);
|
|
2463
|
+
await promptDeposit({ address, flags, surface: "install" });
|
|
2578
2464
|
}
|
|
2579
2465
|
}
|
|
2580
2466
|
};
|
|
2581
2467
|
}
|
|
2582
2468
|
});
|
|
2583
2469
|
|
|
2584
|
-
// src/install/index.ts
|
|
2470
|
+
// src/cli/install/index.ts
|
|
2585
2471
|
var install_exports = {};
|
|
2586
2472
|
__export(install_exports, {
|
|
2587
2473
|
installMcpServer: () => installMcpServer
|
|
2588
2474
|
});
|
|
2589
2475
|
var installMcpServer;
|
|
2590
2476
|
var init_install = __esm({
|
|
2591
|
-
"src/install/index.ts"() {
|
|
2477
|
+
"src/cli/install/index.ts"() {
|
|
2592
2478
|
init_wallet2();
|
|
2479
|
+
init_log();
|
|
2593
2480
|
init_get_client();
|
|
2594
2481
|
init_add_server();
|
|
2595
2482
|
init_redeem_invite3();
|
|
2596
2483
|
init_add_funds();
|
|
2597
2484
|
installMcpServer = async (flags) => {
|
|
2485
|
+
intro(chalk2.green.bold(`Install x402scan MCP`));
|
|
2486
|
+
const walletResult = await getWallet();
|
|
2487
|
+
if (walletResult.isErr()) {
|
|
2488
|
+
log.error(JSON.stringify(walletResult.error, null, 2));
|
|
2489
|
+
outro(chalk2.bold.red("Failed to get wallet"));
|
|
2490
|
+
process.exit(1);
|
|
2491
|
+
}
|
|
2598
2492
|
const {
|
|
2599
2493
|
account: { address },
|
|
2600
2494
|
isNew
|
|
2601
|
-
} =
|
|
2602
|
-
intro(chalk.green.bold(`Install x402scan MCP`));
|
|
2495
|
+
} = walletResult.value;
|
|
2603
2496
|
const client = await getClient(flags);
|
|
2604
2497
|
await addServer(client, flags);
|
|
2605
2498
|
const inviteRedeemed = flags.invite ? await redeemInviteCode2(
|
|
2606
2499
|
{
|
|
2607
2500
|
code: flags.invite,
|
|
2608
2501
|
dev: flags.dev,
|
|
2609
|
-
address
|
|
2502
|
+
address,
|
|
2503
|
+
surface: "install"
|
|
2610
2504
|
},
|
|
2611
2505
|
flags
|
|
2612
2506
|
) : false;
|
|
2613
2507
|
if (!inviteRedeemed) {
|
|
2614
2508
|
await addFunds({ flags, address, isNew });
|
|
2615
2509
|
}
|
|
2616
|
-
outro(
|
|
2510
|
+
outro(chalk2.bold.green("Your x402scan MCP server is ready to use!"));
|
|
2617
2511
|
};
|
|
2618
2512
|
}
|
|
2619
2513
|
});
|
|
2620
2514
|
|
|
2621
|
-
// src/fund/index.ts
|
|
2515
|
+
// src/cli/fund/index.ts
|
|
2622
2516
|
var fund_exports = {};
|
|
2623
2517
|
__export(fund_exports, {
|
|
2624
2518
|
fundMcpServer: () => fundMcpServer
|
|
2625
2519
|
});
|
|
2626
2520
|
var fundMcpServer;
|
|
2627
2521
|
var init_fund = __esm({
|
|
2628
|
-
"src/fund/index.ts"() {
|
|
2522
|
+
"src/cli/fund/index.ts"() {
|
|
2629
2523
|
init_wallet2();
|
|
2630
2524
|
init_deposit();
|
|
2525
|
+
init_log();
|
|
2631
2526
|
fundMcpServer = async (flags) => {
|
|
2632
|
-
intro(
|
|
2527
|
+
intro(chalk2.bold(`Fund ${chalk2.hex("#2563eb")("x402scan MCP")}`));
|
|
2528
|
+
const walletResult = await getWallet();
|
|
2529
|
+
if (walletResult.isErr()) {
|
|
2530
|
+
log.error(walletResult.error.message);
|
|
2531
|
+
log$1.error(walletResult.error.message);
|
|
2532
|
+
outro(chalk2.bold.red("Failed to get wallet"));
|
|
2533
|
+
process.exit(1);
|
|
2534
|
+
}
|
|
2633
2535
|
const {
|
|
2634
2536
|
account: { address }
|
|
2635
|
-
} =
|
|
2636
|
-
await promptDeposit(address, flags);
|
|
2637
|
-
outro(
|
|
2537
|
+
} = walletResult.value;
|
|
2538
|
+
await promptDeposit({ address, flags, surface: "fund" });
|
|
2539
|
+
outro(chalk2.bold.green("Your x402scan MCP server is funded!"));
|
|
2638
2540
|
};
|
|
2639
2541
|
}
|
|
2640
2542
|
});
|
|
@@ -2685,8 +2587,8 @@ void yargs(hideBin(process.argv)).scriptName("@x402scan/mcp").option("dev", {
|
|
|
2685
2587
|
const { fundMcpServer: fundMcpServer2 } = await Promise.resolve().then(() => (init_fund(), fund_exports));
|
|
2686
2588
|
await fundMcpServer2(args);
|
|
2687
2589
|
}
|
|
2688
|
-
).strict().demandCommand(0, 1, "", "Too many commands provided").help().parseAsync().catch((
|
|
2689
|
-
console.error("Fatal:",
|
|
2590
|
+
).strict().demandCommand(0, 1, "", "Too many commands provided").help().parseAsync().catch((err9) => {
|
|
2591
|
+
console.error("Fatal:", err9);
|
|
2690
2592
|
process.exit(1);
|
|
2691
2593
|
});
|
|
2692
2594
|
//# sourceMappingURL=index.js.map
|