@x402scan/mcp 0.0.2 → 0.0.4

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.
@@ -1,13 +1,14 @@
1
1
  #!/usr/bin/env node
2
2
  import z, { z as z$1 } from 'zod';
3
3
  import { getAddress, createPublicClient, http, erc20Abi, formatUnits } from 'viem';
4
- import fs4, { mkdirSync, appendFileSync } from 'fs';
5
4
  import path2, { join } from 'path';
6
5
  import os, { homedir } from 'os';
6
+ import * as fs from 'fs';
7
+ import fs__default, { appendFileSync } from 'fs';
7
8
  import { polygon, arbitrum, optimism, sepolia, mainnet, baseSepolia, base } from 'viem/chains';
8
- import { intro, outro, log as log$1, confirm, stream, spinner, select } from '@clack/prompts';
9
- import boxen from 'boxen';
10
- import chalk3 from 'chalk';
9
+ import { intro, outro, log as log$1, confirm, stream, spinner, select, text } from '@clack/prompts';
10
+ import { errAsync, ResultAsync, err } from 'neverthrow';
11
+ import chalk from 'chalk';
11
12
  import open from 'open';
12
13
  import { x402Client, x402HTTPClient } from '@x402/core/client';
13
14
  import { ExactEvmScheme } from '@x402/evm/exact/client';
@@ -16,8 +17,9 @@ import { base58 } from '@scure/base';
16
17
  import 'tweetnacl';
17
18
  import { SiweMessage } from 'siwe';
18
19
  import { safeBase64Encode } from '@x402/core/utils';
20
+ import 'url';
19
21
  import { randomBytes } from 'crypto';
20
- import * as fs from 'fs/promises';
22
+ import * as fs3 from 'fs/promises';
21
23
  import { privateKeyToAccount } from 'viem/accounts';
22
24
  import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
23
25
  import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
@@ -91,6 +93,25 @@ var init_types = __esm({
91
93
  "src/server/types.ts"() {
92
94
  }
93
95
  });
96
+ var BASE_DIRECTORY, configFile;
97
+ var init_fs = __esm({
98
+ "src/lib/fs.ts"() {
99
+ BASE_DIRECTORY = join(homedir(), ".x402scan-mcp");
100
+ if (!fs.existsSync(BASE_DIRECTORY)) {
101
+ fs.mkdirSync(BASE_DIRECTORY, { recursive: true });
102
+ }
103
+ configFile = (name) => {
104
+ if (!fs.existsSync(BASE_DIRECTORY)) {
105
+ fs.mkdirSync(BASE_DIRECTORY, { recursive: true });
106
+ }
107
+ const filePath = join(BASE_DIRECTORY, name);
108
+ if (!fs.existsSync(filePath)) {
109
+ fs.writeFileSync(filePath, "{}");
110
+ }
111
+ return filePath;
112
+ };
113
+ }
114
+ });
94
115
  function format(args) {
95
116
  return args.map(
96
117
  (a) => typeof a === "object" && a !== null ? JSON.stringify(a) : String(a)
@@ -108,16 +129,12 @@ function write(level, msg, args) {
108
129
  console.error(`[x402scan] ${formatted}`);
109
130
  }
110
131
  }
111
- var LOG_DIR, LOG_FILE, DEBUG, log;
132
+ var LOG_FILE, DEBUG, log;
112
133
  var init_log = __esm({
113
134
  "src/lib/log.ts"() {
114
- LOG_DIR = join(homedir(), ".x402scan-mcp");
115
- LOG_FILE = join(LOG_DIR, "mcp.log");
135
+ init_fs();
136
+ LOG_FILE = configFile("mcp.log");
116
137
  DEBUG = process.env.X402_DEBUG === "true";
117
- try {
118
- mkdirSync(LOG_DIR, { recursive: true });
119
- } catch {
120
- }
121
138
  log = {
122
139
  info: (msg, ...args) => write("INFO", msg, args),
123
140
  error: (msg, ...args) => write("ERROR", msg, args),
@@ -249,35 +266,207 @@ var init_wait = __esm({
249
266
  };
250
267
  }
251
268
  });
252
- var getDepositLink, openDepositLink, promptDeposit;
269
+ function safeFetch(input, init) {
270
+ return ResultAsync.fromPromise(
271
+ fetch(input, init),
272
+ (error) => ({
273
+ type: "network",
274
+ message: "Network error",
275
+ error: error instanceof Error ? error : new Error(String(error))
276
+ })
277
+ );
278
+ }
279
+ var safeFetchJson;
280
+ var init_safe_fetch = __esm({
281
+ "src/lib/safe-fetch.ts"() {
282
+ safeFetchJson = (input, init, errorMessage) => {
283
+ return safeFetch(input, init).andThen((response) => {
284
+ if (!response.ok) {
285
+ return ResultAsync.fromSafePromise(
286
+ response.json().catch(() => void 0)
287
+ ).andThen(
288
+ (json) => err({
289
+ type: "http",
290
+ message: json !== void 0 && errorMessage ? errorMessage(json) : response.statusText,
291
+ status: response.status,
292
+ headers: response.headers,
293
+ json
294
+ })
295
+ );
296
+ }
297
+ return ResultAsync.fromPromise(
298
+ response.json(),
299
+ (error) => ({
300
+ type: "parse",
301
+ message: "Could not parse JSON from response",
302
+ error: error instanceof Error ? error : new Error(String(error))
303
+ })
304
+ );
305
+ });
306
+ };
307
+ }
308
+ });
309
+
310
+ // src/lib/utils.ts
311
+ var getBaseUrl;
312
+ var init_utils = __esm({
313
+ "src/lib/utils.ts"() {
314
+ getBaseUrl = (dev) => {
315
+ return dev ? "http://localhost:3000" : "https://x402scan.com";
316
+ };
317
+ }
318
+ });
319
+ var STATE_FILE, stateSchema, getState, setState;
320
+ var init_state = __esm({
321
+ "src/lib/state.ts"() {
322
+ init_fs();
323
+ init_log();
324
+ STATE_FILE = configFile("state.json");
325
+ stateSchema = z.looseObject({
326
+ redeemedCodes: z.array(z.string())
327
+ }).partial();
328
+ getState = () => {
329
+ if (!fs__default.existsSync(STATE_FILE)) {
330
+ fs__default.writeFileSync(STATE_FILE, JSON.stringify({}));
331
+ return {};
332
+ }
333
+ const result = stateSchema.safeParse(
334
+ JSON.parse(fs__default.readFileSync(STATE_FILE, "utf-8"))
335
+ );
336
+ if (!result.success) {
337
+ log.error("Failed to parse state", { error: result.error });
338
+ return {};
339
+ }
340
+ return result.data;
341
+ };
342
+ setState = (state) => {
343
+ const existing = getState();
344
+ const newState = stateSchema.parse({ ...existing, ...state });
345
+ fs__default.writeFileSync(STATE_FILE, JSON.stringify(newState, null, 2));
346
+ };
347
+ }
348
+ });
349
+ var redeemInviteCode;
350
+ var init_redeem_invite = __esm({
351
+ "src/lib/redeem-invite.ts"() {
352
+ init_safe_fetch();
353
+ init_utils();
354
+ init_state();
355
+ redeemInviteCode = async ({
356
+ code,
357
+ dev,
358
+ address
359
+ }) => {
360
+ const state = getState();
361
+ if (state.redeemedCodes?.includes(code)) {
362
+ return Promise.resolve(
363
+ errAsync({
364
+ success: false,
365
+ message: "This invite code has already been redeemed"
366
+ })
367
+ );
368
+ }
369
+ return await safeFetchJson(
370
+ `${getBaseUrl(dev)}/api/invite/redeem`,
371
+ {
372
+ method: "POST",
373
+ headers: {
374
+ "Content-Type": "application/json"
375
+ },
376
+ body: JSON.stringify({
377
+ code,
378
+ recipientAddr: address
379
+ })
380
+ },
381
+ ({ message }) => message
382
+ ).andTee((result) => {
383
+ if (result.success) {
384
+ setState({
385
+ redeemedCodes: [...state.redeemedCodes ?? [], code]
386
+ });
387
+ }
388
+ });
389
+ };
390
+ }
391
+ });
392
+ var getDepositLink, openDepositLink, redeemInviteCodePrompt, promptDeposit;
253
393
  var init_deposit = __esm({
254
394
  "src/lib/deposit.ts"() {
255
395
  init_networks();
256
396
  init_wait();
397
+ init_redeem_invite();
398
+ init_utils();
257
399
  getDepositLink = (address, flags) => {
258
- const baseUrl = flags.dev ? "http://localhost:3000" : "https://x402scan.com";
259
- return `${baseUrl}/deposit/${address}`;
400
+ return `${getBaseUrl(flags.dev)}/deposit/${address}`;
260
401
  };
261
402
  openDepositLink = async (address, flags) => {
262
403
  const depositLink = getDepositLink(address, flags);
263
404
  await open(depositLink);
264
405
  };
406
+ redeemInviteCodePrompt = async (address, flags) => {
407
+ const code = await text({
408
+ message: "Enter your invite code",
409
+ placeholder: "MRT-XXXXX",
410
+ validate: (value) => {
411
+ if (!value || value.trim().length === 0) {
412
+ return "Please enter an invite code";
413
+ }
414
+ }
415
+ });
416
+ if (typeof code !== "string") {
417
+ return false;
418
+ }
419
+ const s = spinner();
420
+ s.start("Redeeming invite code...");
421
+ const result = await redeemInviteCode({ code, dev: flags.dev, address });
422
+ return result.match(
423
+ async ({ data: { amount, txHash } }) => {
424
+ s.stop("Invite code redeemed successfully!");
425
+ await wait({
426
+ startText: "Processing...",
427
+ stopText: chalk.green(
428
+ `${chalk.bold(amount)} USDC has been sent to your wallet!`
429
+ ),
430
+ ms: 1500
431
+ });
432
+ log$1.success(
433
+ chalk.bold(`Your wallet has been funded with ${amount} USDC`)
434
+ );
435
+ if (txHash) {
436
+ log$1.info(chalk.dim(`Transaction: https://basescan.org/tx/${txHash}`));
437
+ }
438
+ return true;
439
+ },
440
+ (error) => {
441
+ s.stop("Invite code redemption failed");
442
+ log$1.warning(
443
+ chalk.yellow(`Failed to redeem invite code: ${error?.message}`)
444
+ );
445
+ return false;
446
+ }
447
+ );
448
+ };
265
449
  promptDeposit = async (address, flags) => {
266
450
  const depositLink = getDepositLink(address, flags);
267
- const guidedDeposit = await select({
268
- message: chalk3.bold("How would you like to deposit?"),
269
- initialValue: true,
451
+ const depositChoice = await select({
452
+ message: chalk.bold("How would you like to deposit?"),
453
+ initialValue: "guided",
270
454
  options: [
271
455
  {
272
- label: `Guided - Recommended`,
273
- value: true,
456
+ label: "Guided - Recommended",
457
+ value: "guided",
274
458
  hint: "Online portal in x402scan"
275
459
  },
276
460
  {
277
461
  label: "Manual",
278
- value: false,
462
+ value: "manual",
279
463
  hint: "Print deposit instructions"
280
464
  },
465
+ {
466
+ label: "Redeem Invite Code",
467
+ value: "invite",
468
+ hint: "Enter an invite code for starter money"
469
+ },
281
470
  {
282
471
  label: "Skip",
283
472
  value: void 0,
@@ -285,30 +474,24 @@ var init_deposit = __esm({
285
474
  }
286
475
  ]
287
476
  });
288
- if (guidedDeposit === true) {
477
+ if (depositChoice === "guided") {
289
478
  await wait({
290
479
  startText: "Opening deposit page...",
291
- stopText: `Opening ${chalk3.underline.hex("#2563eb")(depositLink)}`,
480
+ stopText: `Opening ${chalk.underline.hex("#2563eb")(depositLink)}`,
292
481
  ms: 1e3
293
482
  });
294
483
  await open(depositLink);
295
- } else if (guidedDeposit === false) {
296
- log$1.message(
297
- boxen(
298
- `${chalk3.bold("Account Information")}
299
- Address: ${address}
300
- Network: ${getChainName(DEFAULT_NETWORK)}
301
-
302
- ${chalk3.bold("Online Portal")}
303
- ${chalk3.underline(depositLink)}`,
304
- {
305
- borderStyle: "round",
306
- borderColor: "#2563eb",
307
- title: "Deposit Instructions",
308
- padding: 1
309
- }
310
- )
311
- );
484
+ } else if (depositChoice === "manual") {
485
+ log$1.step(chalk.bold("Account Information"));
486
+ log$1.message(`Address: ${address}`);
487
+ log$1.message(`Network: ${getChainName(DEFAULT_NETWORK)}`);
488
+ log$1.step(chalk.bold("Online Portal"));
489
+ log$1.message(`${chalk.underline(depositLink)}`);
490
+ } else if (depositChoice === "invite") {
491
+ const redeemed = await redeemInviteCodePrompt(address, flags);
492
+ if (!redeemed) {
493
+ await promptDeposit(address, flags);
494
+ }
312
495
  }
313
496
  };
314
497
  }
@@ -355,6 +538,27 @@ You can deposit USDC at ${getDepositLink(address, flags)}`
355
538
  };
356
539
  }
357
540
  });
541
+
542
+ // src/server/lib/parse-response.ts
543
+ var parseResponse;
544
+ var init_parse_response = __esm({
545
+ "src/server/lib/parse-response.ts"() {
546
+ parseResponse = async (response) => {
547
+ try {
548
+ const contentType = response.headers.get("content-type") ?? "";
549
+ if (contentType.includes("application/json")) {
550
+ return await response.json();
551
+ } else if (contentType.includes("image/")) {
552
+ return await response.arrayBuffer();
553
+ } else {
554
+ return await response.text();
555
+ }
556
+ } catch {
557
+ return void 0;
558
+ }
559
+ };
560
+ }
561
+ });
358
562
  var registerFetchX402ResourceTool;
359
563
  var init_fetch_x402_resource = __esm({
360
564
  "src/server/tools/fetch-x402-resource.ts"() {
@@ -365,6 +569,7 @@ var init_fetch_x402_resource = __esm({
365
569
  init_networks();
366
570
  init_token();
367
571
  init_check_balance();
572
+ init_parse_response();
368
573
  registerFetchX402ResourceTool = ({
369
574
  server,
370
575
  account,
@@ -409,16 +614,15 @@ var init_fetch_x402_resource = __esm({
409
614
  try {
410
615
  const response = await fetchWithPay(url, {
411
616
  method,
412
- body: body ? JSON.stringify(body) : void 0,
617
+ body: typeof body === "string" ? body : body ? JSON.stringify(body) : void 0,
413
618
  headers: {
414
619
  ...body ? { "Content-Type": "application/json" } : {},
415
620
  ...headers
416
621
  }
417
622
  });
418
623
  if (!response.ok) {
419
- const errorData = await response.text();
420
624
  const errorResponse = {
421
- data: errorData,
625
+ data: await parseResponse(response),
422
626
  statusCode: response.status,
423
627
  state
424
628
  };
@@ -441,11 +645,11 @@ var init_fetch_x402_resource = __esm({
441
645
  };
442
646
  const settlement = getSettlement();
443
647
  return mcpSuccess({
444
- data: await response.text().catch(() => void 0),
648
+ data: await parseResponse(response),
445
649
  payment: settlement
446
650
  });
447
- } catch (err) {
448
- return mcpError(err, { state });
651
+ } catch (err2) {
652
+ return mcpError(err2, { state });
449
653
  }
450
654
  }
451
655
  );
@@ -835,8 +1039,8 @@ var init_auth = __esm({
835
1039
  chainId: serverInfo.chainId
836
1040
  }
837
1041
  });
838
- } catch (err) {
839
- return mcpError(err, { tool: "authed_call", url });
1042
+ } catch (err2) {
1043
+ return mcpError(err2, { tool: "authed_call", url });
840
1044
  }
841
1045
  }
842
1046
  );
@@ -956,8 +1160,131 @@ var init_check_endpoint_schema = __esm({
956
1160
  statusCode: response.status,
957
1161
  routeDetails
958
1162
  });
959
- } catch (err) {
960
- return mcpError(err, { tool: "query_endpoint", url });
1163
+ } catch (err2) {
1164
+ return mcpError(err2, { tool: "query_endpoint", url });
1165
+ }
1166
+ }
1167
+ );
1168
+ };
1169
+ }
1170
+ });
1171
+ var registerRedeemInviteTool;
1172
+ var init_redeem_invite2 = __esm({
1173
+ "src/server/tools/redeem-invite.ts"() {
1174
+ init_response();
1175
+ registerRedeemInviteTool = ({
1176
+ server,
1177
+ account: { address },
1178
+ flags
1179
+ }) => {
1180
+ const baseUrl = flags.dev ? "http://localhost:3000" : "https://x402scan.com";
1181
+ server.registerTool(
1182
+ "redeem_invite",
1183
+ {
1184
+ description: "Redeem an invite code to receive USDC.",
1185
+ inputSchema: z.object({
1186
+ code: z.string().min(1).describe("The invite code")
1187
+ })
1188
+ },
1189
+ async ({ code }) => {
1190
+ const res = await fetch(`${baseUrl}/api/invite/redeem`, {
1191
+ method: "POST",
1192
+ headers: { "Content-Type": "application/json" },
1193
+ body: JSON.stringify({ code, recipientAddr: address })
1194
+ });
1195
+ const data = await res.json();
1196
+ if (!data.success) {
1197
+ return mcpError(data.error ?? "Failed to redeem invite code");
1198
+ }
1199
+ return mcpSuccess({
1200
+ amount: `${data.amount} USDC`,
1201
+ txHash: data.txHash
1202
+ });
1203
+ }
1204
+ );
1205
+ };
1206
+ }
1207
+ });
1208
+ function getVersion() {
1209
+ {
1210
+ return "0.0.4";
1211
+ }
1212
+ }
1213
+ var MCP_VERSION;
1214
+ var init_version = __esm({
1215
+ "src/server/lib/version.ts"() {
1216
+ MCP_VERSION = getVersion();
1217
+ }
1218
+ });
1219
+ var errorReportSchema, registerTelemetryTools;
1220
+ var init_telemetry = __esm({
1221
+ "src/server/tools/telemetry.ts"() {
1222
+ init_log();
1223
+ init_response();
1224
+ init_version();
1225
+ errorReportSchema = z$1.object({
1226
+ tool: z$1.string().describe("MCP tool name"),
1227
+ resource: z$1.string().optional().describe("x402 resource URL"),
1228
+ summary: z$1.string().describe("1-2 sentence summary"),
1229
+ errorMessage: z$1.string().describe("Error message"),
1230
+ stack: z$1.string().optional().describe("Stack trace"),
1231
+ fullReport: z$1.string().optional().describe("Detailed report with context, logs, repro steps")
1232
+ });
1233
+ registerTelemetryTools = ({
1234
+ server,
1235
+ account: { address },
1236
+ flags
1237
+ }) => {
1238
+ const baseUrl = flags.dev ? "http://localhost:3000" : "https://x402scan.com";
1239
+ server.registerTool(
1240
+ "report_error",
1241
+ {
1242
+ description: "EMERGENCY ONLY. Report critical MCP tool bugs. Do NOT use for normal errors (balance, network, 4xx) - those are recoverable.",
1243
+ inputSchema: errorReportSchema
1244
+ },
1245
+ async (input) => {
1246
+ try {
1247
+ log.info("Submitting error report", {
1248
+ tool: input.tool,
1249
+ resource: input.resource,
1250
+ summary: input.summary
1251
+ });
1252
+ const report = {
1253
+ ...input,
1254
+ walletAddress: address,
1255
+ mcpVersion: MCP_VERSION,
1256
+ reportedAt: (/* @__PURE__ */ new Date()).toISOString()
1257
+ };
1258
+ const response = await fetch(`${baseUrl}/api/telemetry`, {
1259
+ method: "POST",
1260
+ headers: {
1261
+ "Content-Type": "application/json"
1262
+ },
1263
+ body: JSON.stringify(report)
1264
+ });
1265
+ if (!response.ok) {
1266
+ const errorText = await response.text().catch(() => "Unknown error");
1267
+ log.error("Failed to submit error report", {
1268
+ status: response.status,
1269
+ error: errorText
1270
+ });
1271
+ return mcpError(
1272
+ `Failed to submit error report: ${response.status} ${errorText}`,
1273
+ { tool: "report_error" }
1274
+ );
1275
+ }
1276
+ const result = await response.json();
1277
+ log.info("Error report submitted successfully", {
1278
+ reportId: result.reportId
1279
+ });
1280
+ return mcpSuccess({
1281
+ submitted: true,
1282
+ reportId: result.reportId,
1283
+ message: "Error report submitted successfully. The x402scan team will investigate."
1284
+ });
1285
+ } catch (err2) {
1286
+ log.error("Failed to submit error report", { error: err2 });
1287
+ return mcpError(err2, { tool: "report_error" });
961
1288
  }
962
1289
  }
963
1290
  );
@@ -1100,7 +1427,7 @@ async function getWallet() {
1100
1427
  return { account: account2, isNew: false };
1101
1428
  }
1102
1429
  try {
1103
- const data = await fs.readFile(KEYSTORE_FILE, "utf-8");
1430
+ const data = await fs3.readFile(WALLET_FILE, "utf-8");
1104
1431
  const stored2 = storedWalletSchema.parse(JSON.parse(data));
1105
1432
  const account2 = privateKeyToAccount(stored2.privateKey);
1106
1433
  log.info(`Loaded wallet: ${account2.address}`);
@@ -1114,23 +1441,22 @@ async function getWallet() {
1114
1441
  address: account.address,
1115
1442
  createdAt: (/* @__PURE__ */ new Date()).toISOString()
1116
1443
  };
1117
- await fs.mkdir(KEYSTORE_DIR, { recursive: true });
1118
- await fs.writeFile(KEYSTORE_FILE, JSON.stringify(stored, null, 2));
1444
+ await fs3.writeFile(WALLET_FILE, JSON.stringify(stored, null, 2));
1119
1445
  try {
1120
- await fs.chmod(KEYSTORE_FILE, 384);
1446
+ await fs3.chmod(WALLET_FILE, 384);
1121
1447
  } catch {
1122
1448
  }
1123
1449
  log.info(`Created wallet: ${account.address}`);
1124
- log.info(`Saved to: ${KEYSTORE_FILE}`);
1450
+ log.info(`Saved to: ${WALLET_FILE}`);
1125
1451
  return { account, isNew: true };
1126
1452
  }
1127
- var KEYSTORE_DIR, KEYSTORE_FILE, storedWalletSchema;
1453
+ var WALLET_FILE, storedWalletSchema;
1128
1454
  var init_wallet2 = __esm({
1129
1455
  "src/lib/wallet.ts"() {
1130
1456
  init_log();
1131
1457
  init_schemas();
1132
- KEYSTORE_DIR = join(homedir(), ".x402scan-mcp");
1133
- KEYSTORE_FILE = join(KEYSTORE_DIR, "wallet.json");
1458
+ init_fs();
1459
+ WALLET_FILE = configFile("wallet.json");
1134
1460
  storedWalletSchema = z.object({
1135
1461
  privateKey: ethereumPrivateKeySchema,
1136
1462
  address: ethereumAddressSchema,
@@ -1168,9 +1494,9 @@ async function lookupDnsTxtRecord(hostname) {
1168
1494
  log.debug(`DNS TXT value is not a valid URL: ${txtValue}`);
1169
1495
  return null;
1170
1496
  }
1171
- } catch (err) {
1497
+ } catch (err2) {
1172
1498
  log.debug(
1173
- `DNS lookup error: ${err instanceof Error ? err.message : String(err)}`
1499
+ `DNS lookup error: ${err2 instanceof Error ? err2.message : String(err2)}`
1174
1500
  );
1175
1501
  return null;
1176
1502
  }
@@ -1193,10 +1519,10 @@ async function fetchLlmsTxt(origin) {
1193
1519
  return { found: false, error: "llms.txt is empty" };
1194
1520
  }
1195
1521
  return { found: true, content };
1196
- } catch (err) {
1522
+ } catch (err2) {
1197
1523
  return {
1198
1524
  found: false,
1199
- error: `Network error: ${err instanceof Error ? err.message : String(err)}`
1525
+ error: `Network error: ${err2 instanceof Error ? err2.message : String(err2)}`
1200
1526
  };
1201
1527
  }
1202
1528
  }
@@ -1233,10 +1559,10 @@ async function fetchDiscoveryFromUrl(url) {
1233
1559
  };
1234
1560
  }
1235
1561
  return { found: true, document: parsed.data };
1236
- } catch (err) {
1562
+ } catch (err2) {
1237
1563
  return {
1238
1564
  found: false,
1239
- error: `Network error: ${err instanceof Error ? err.message : String(err)}`
1565
+ error: `Network error: ${err2 instanceof Error ? err2.message : String(err2)}`
1240
1566
  };
1241
1567
  }
1242
1568
  }
@@ -1328,11 +1654,11 @@ async function queryResource(url) {
1328
1654
  resource.signInWithX = { required: true, info: siwx.info };
1329
1655
  }
1330
1656
  return resource;
1331
- } catch (err) {
1657
+ } catch (err2) {
1332
1658
  return {
1333
1659
  url,
1334
1660
  isX402Endpoint: false,
1335
- error: err instanceof Error ? err.message : String(err)
1661
+ error: err2 instanceof Error ? err2.message : String(err2)
1336
1662
  };
1337
1663
  }
1338
1664
  }
@@ -1340,25 +1666,33 @@ function registerDiscoveryTools(server) {
1340
1666
  server.registerTool(
1341
1667
  "discover_resources",
1342
1668
  {
1343
- description: `Discover x402-protected resources from an origin. Fetches the /.well-known/x402 discovery document and optionally tests each resource to get pricing and requirements.
1344
-
1669
+ description: `Discover x402-protected resources on an origin.
1670
+ NEVER use 'fanOut = true' on the first try.
1671
+ NEVER use 'fanOut = true' when there are more than 8 resources.
1672
+
1345
1673
  Known default origins with resource packs. Discover if more needed:
1346
- - https://enrichx402.com -> People + Org search, Google Maps (places + locations), grok twitter search, exa web search, clado linkedin data, firecrawl web scrape
1674
+ - https://enrichx402.com ->
1675
+ People + Org search
1676
+ Google Maps (places + locations)
1677
+ Grok twitter search
1678
+ Exa web search
1679
+ Clado linkedin data
1680
+ Firecrawl web scrape
1347
1681
  - https://stablestudio.io -> generate images / videos
1348
1682
  `,
1349
1683
  inputSchema: {
1350
1684
  url: z$1.url().describe(
1351
1685
  "The origin URL or any URL on the origin to discover resources from"
1352
1686
  ),
1353
- testResources: z$1.boolean().default(false).describe(
1354
- "Whether to query each discovered resource for full pricing/schema info (default: false - just return URLs from discovery doc)"
1687
+ fanOut: z$1.boolean().default(false).describe(
1688
+ "Whether to query each discovered resource for full pricing/schema info. NEVER use on first try."
1355
1689
  ),
1356
1690
  concurrency: z$1.number().int().min(1).max(10).default(5).describe(
1357
1691
  "Max concurrent requests when querying resources (default: 5)"
1358
1692
  )
1359
1693
  }
1360
1694
  },
1361
- async ({ url, testResources, concurrency }) => {
1695
+ async ({ url, fanOut, concurrency }) => {
1362
1696
  try {
1363
1697
  const origin = getOrigin(url);
1364
1698
  log.info(`Discovering resources for origin: ${origin}`);
@@ -1392,7 +1726,7 @@ function registerDiscoveryTools(server) {
1392
1726
  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.",
1393
1727
  resources: []
1394
1728
  };
1395
- if (!testResources) {
1729
+ if (!fanOut) {
1396
1730
  result.resources = doc.resources.map((resourceUrl) => ({
1397
1731
  url: resourceUrl
1398
1732
  }));
@@ -1409,8 +1743,8 @@ function registerDiscoveryTools(server) {
1409
1743
  }
1410
1744
  result.resources = allResources;
1411
1745
  return mcpSuccess(result);
1412
- } catch (err) {
1413
- return mcpError(err, { tool: "discover_resources", url });
1746
+ } catch (err2) {
1747
+ return mcpError(err2, { tool: "discover_resources", url });
1414
1748
  }
1415
1749
  }
1416
1750
  );
@@ -1457,17 +1791,30 @@ var init_server = __esm({
1457
1791
  init_auth();
1458
1792
  init_wallet();
1459
1793
  init_check_endpoint_schema();
1794
+ init_redeem_invite2();
1795
+ init_telemetry();
1460
1796
  init_origins();
1461
1797
  init_log();
1462
1798
  init_wallet2();
1463
1799
  init_discover_resources();
1800
+ init_redeem_invite();
1801
+ init_version();
1464
1802
  startServer = async (flags) => {
1465
1803
  log.info("Starting x402scan-mcp...");
1804
+ const { dev, invite } = flags;
1466
1805
  const { account } = await getWallet();
1806
+ const code = invite ?? process.env.INVITE_CODE;
1807
+ if (code) {
1808
+ await redeemInviteCode({
1809
+ code,
1810
+ dev,
1811
+ address: account.address
1812
+ });
1813
+ }
1467
1814
  const server = new McpServer(
1468
1815
  {
1469
1816
  name: "@x402scan/mcp",
1470
- version: "0.0.1",
1817
+ version: MCP_VERSION,
1471
1818
  websiteUrl: "https://x402scan.com/mcp",
1472
1819
  icons: [{ src: "https://x402scan.com/logo.svg" }]
1473
1820
  },
@@ -1489,7 +1836,9 @@ var init_server = __esm({
1489
1836
  registerAuthTools(props);
1490
1837
  registerWalletTools(props);
1491
1838
  registerCheckX402EndpointTool(props);
1839
+ registerRedeemInviteTool(props);
1492
1840
  registerDiscoveryTools(server);
1841
+ registerTelemetryTools(props);
1493
1842
  await registerOrigins({ server, flags });
1494
1843
  const transport = new StdioServerTransport();
1495
1844
  await server.connect(transport);
@@ -1620,7 +1969,7 @@ var init_get_client = __esm({
1620
1969
  if (parsedClientSelection.success) {
1621
1970
  return parsedClientSelection.data;
1622
1971
  }
1623
- outro(chalk3.bold.red("No MCP client selected"));
1972
+ outro(chalk.bold.red("No MCP client selected"));
1624
1973
  process.exit(0);
1625
1974
  };
1626
1975
  }
@@ -1666,7 +2015,7 @@ var parseClientConfig, serializeClientConfig, stringifyObject;
1666
2015
  var init_file_types = __esm({
1667
2016
  "src/install/2-add-server/lib/file-types.ts"() {
1668
2017
  parseClientConfig = ({ format: format2, path: path3 }) => {
1669
- const fileContent = fs4.readFileSync(path3, "utf8");
2018
+ const fileContent = fs__default.readFileSync(path3, "utf8");
1670
2019
  let config = {};
1671
2020
  if (format2 === "yaml" /* YAML */) {
1672
2021
  config = yaml.load(fileContent);
@@ -1843,7 +2192,7 @@ var init_client_config_file = __esm({
1843
2192
  "opencode.json"
1844
2193
  );
1845
2194
  const jsoncPath = jsonPath.replace(".json", ".jsonc");
1846
- if (fs4.existsSync(jsoncPath)) {
2195
+ if (fs__default.existsSync(jsoncPath)) {
1847
2196
  log.info(`Found .jsonc file for OpenCode, using: ${jsoncPath}`);
1848
2197
  return {
1849
2198
  path: jsoncPath,
@@ -1906,7 +2255,7 @@ async function addServer(client, globalFlags) {
1906
2255
  const { serverName, command, args } = getMcpConfig(globalFlags);
1907
2256
  if (client === "warp" /* Warp */) {
1908
2257
  log$1.info(
1909
- chalk3.bold.yellow("Warp requires a manual installation through their UI.")
2258
+ chalk.bold.yellow("Warp requires a manual installation through their UI.")
1910
2259
  );
1911
2260
  log$1.message(
1912
2261
  "Please copy the following configuration object and add it to your Warp MCP config:"
@@ -1943,7 +2292,7 @@ async function addServer(client, globalFlags) {
1943
2292
  let config = {};
1944
2293
  let content = void 0;
1945
2294
  log.info(`Checking if config file exists at: ${clientFileTarget.path}`);
1946
- if (!fs4.existsSync(clientFileTarget.path)) {
2295
+ if (!fs__default.existsSync(clientFileTarget.path)) {
1947
2296
  log.info("Config file not found, creating default empty config");
1948
2297
  setNestedValue(config, clientFileTarget.configKey, {});
1949
2298
  log.info("Config created successfully");
@@ -1977,7 +2326,7 @@ async function addServer(client, globalFlags) {
1977
2326
  if (!servers || typeof servers !== "object") {
1978
2327
  log.error(`Invalid ${clientFileTarget.configKey} structure in config`);
1979
2328
  log$1.error(
1980
- chalk3.bold.red(
2329
+ chalk.bold.red(
1981
2330
  `Invalid ${clientFileTarget.configKey} structure in config`
1982
2331
  )
1983
2332
  );
@@ -2015,7 +2364,9 @@ async function addServer(client, globalFlags) {
2015
2364
  };
2016
2365
  }
2017
2366
  await new Promise((resolve) => setTimeout(resolve, 1e3));
2018
- log$1.step(`The following will be added to ${chalk3.bold.underline(clientFileTarget.path)}`);
2367
+ log$1.step(
2368
+ `The following will be added to ${chalk.bold.underline(clientFileTarget.path)}`
2369
+ );
2019
2370
  const configStr = formatDiffByFormat(
2020
2371
  {
2021
2372
  [clientFileTarget.configKey]: {
@@ -2047,7 +2398,7 @@ async function addServer(client, globalFlags) {
2047
2398
  inactive: "Cancel"
2048
2399
  });
2049
2400
  if (isConfirmed !== true) {
2050
- outro(chalk3.bold.red("Installation cancelled"));
2401
+ outro(chalk.bold.red("Installation cancelled"));
2051
2402
  process.exit(0);
2052
2403
  }
2053
2404
  const configContent = serializeClientConfig(
@@ -2055,10 +2406,10 @@ async function addServer(client, globalFlags) {
2055
2406
  config,
2056
2407
  content
2057
2408
  );
2058
- fs4.writeFileSync(clientFileTarget.path, configContent);
2059
- log$1.success(chalk3.bold.green(`Added x402scan MCP to ${name}`));
2409
+ fs__default.writeFileSync(clientFileTarget.path, configContent);
2410
+ log$1.success(chalk.bold.green(`Added x402scan MCP to ${name}`));
2060
2411
  } catch (e) {
2061
- log$1.error(chalk3.bold.red(`Error adding x402scan MCP to ${name}`));
2412
+ log$1.error(chalk.bold.red(`Error adding x402scan MCP to ${name}`));
2062
2413
  throw e;
2063
2414
  }
2064
2415
  }
@@ -2092,7 +2443,7 @@ var init_add_server = __esm({
2092
2443
  const diffLines = [0, 1, numLines - 2, numLines - 1];
2093
2444
  const isDiffLine = !diffLines.includes(index);
2094
2445
  if (isDiffLine) {
2095
- return `${chalk3.bold.green(`+ ${line.slice(2)}`)}`;
2446
+ return `${chalk.bold.green(`+ ${line.slice(2)}`)}`;
2096
2447
  }
2097
2448
  return line;
2098
2449
  }).join("\n");
@@ -2102,14 +2453,14 @@ var init_add_server = __esm({
2102
2453
  const diffLines = [0, 1, str.length - 2, str.length - 1];
2103
2454
  const isDiffLine = !diffLines.includes(index);
2104
2455
  if (isDiffLine) {
2105
- return `${chalk3.bold.green(`+ ${line.slice(2)}`)}`;
2456
+ return `${chalk.bold.green(`+ ${line.slice(2)}`)}`;
2106
2457
  }
2107
2458
  return line;
2108
2459
  }).join("\n");
2109
2460
  }
2110
2461
  case "toml" /* TOML */: {
2111
2462
  return str.split("\n").filter((line) => line.trim() !== "").map((line) => {
2112
- return `${chalk3.bold.green(`+ ${line.trim()}`)}`;
2463
+ return `${chalk.bold.green(`+ ${line.trim()}`)}`;
2113
2464
  }).join("\n");
2114
2465
  }
2115
2466
  }
@@ -2131,12 +2482,12 @@ var init_add_funds = __esm({
2131
2482
  const balance = await getUSDCBalance({ address });
2132
2483
  await wait({
2133
2484
  startText: "Checking balance...",
2134
- stopText: `Balance: ${chalk3.bold(`${balance} USDC`)} `,
2485
+ stopText: `Balance: ${chalk.bold(`${balance} USDC`)} `,
2135
2486
  ms: 1e3
2136
2487
  });
2137
2488
  if (balance < 1) {
2138
2489
  log$1.warning(
2139
- chalk3.bold(
2490
+ chalk.bold(
2140
2491
  `Your balance is low (${balance} USDC). Consider topping up.`
2141
2492
  )
2142
2493
  );
@@ -2146,6 +2497,41 @@ var init_add_funds = __esm({
2146
2497
  };
2147
2498
  }
2148
2499
  });
2500
+ var redeemInviteCode2;
2501
+ var init_redeem_invite3 = __esm({
2502
+ "src/install/4-redeem-invite/index.ts"() {
2503
+ init_wait();
2504
+ init_redeem_invite();
2505
+ redeemInviteCode2 = async (props) => {
2506
+ const s = spinner();
2507
+ s.start("Redeeming invite code...");
2508
+ const result = await redeemInviteCode(props);
2509
+ return result.match(
2510
+ async ({ data }) => {
2511
+ s.stop("Invite code redeemed successfully!");
2512
+ await wait({
2513
+ startText: "Processing...",
2514
+ stopText: chalk.green(
2515
+ `${chalk.bold(data.amount)} USDC has been sent to your wallet!`
2516
+ ),
2517
+ ms: 1e3
2518
+ });
2519
+ log$1.info(
2520
+ chalk.dim(`Transaction: https://basescan.org/tx/${data.txHash}`)
2521
+ );
2522
+ return true;
2523
+ },
2524
+ (error) => {
2525
+ s.stop("Invite code redemption failed");
2526
+ log$1.warning(
2527
+ chalk.yellow(`Failed to redeem invite code: ${error?.message}`)
2528
+ );
2529
+ return false;
2530
+ }
2531
+ );
2532
+ };
2533
+ }
2534
+ });
2149
2535
 
2150
2536
  // src/install/index.ts
2151
2537
  var install_exports = {};
@@ -2159,16 +2545,24 @@ var init_install = __esm({
2159
2545
  init_get_client();
2160
2546
  init_add_server();
2161
2547
  init_add_funds();
2548
+ init_redeem_invite3();
2162
2549
  installMcpServer = async (flags) => {
2163
2550
  const {
2164
2551
  account: { address },
2165
2552
  isNew
2166
2553
  } = await getWallet();
2167
- intro(chalk3.green.bold(`Install x402scan MCP`));
2554
+ intro(chalk.green.bold(`Install x402scan MCP`));
2168
2555
  const client = await getClient(flags);
2169
2556
  await addServer(client, flags);
2170
- await addFunds({ flags, address, isNew });
2171
- outro(chalk3.bold.green("Your x402scan MCP server is ready to use!"));
2557
+ const inviteRedeemed = flags.invite ? await redeemInviteCode2({
2558
+ code: flags.invite,
2559
+ dev: flags.dev,
2560
+ address
2561
+ }) : false;
2562
+ if (!inviteRedeemed) {
2563
+ await addFunds({ flags, address, isNew });
2564
+ }
2565
+ outro(chalk.bold.green("Your x402scan MCP server is ready to use!"));
2172
2566
  };
2173
2567
  }
2174
2568
  });
@@ -2184,12 +2578,12 @@ var init_fund = __esm({
2184
2578
  init_wallet2();
2185
2579
  init_deposit();
2186
2580
  fundMcpServer = async (flags) => {
2187
- intro(chalk3.bold(`Fund ${chalk3.hex("#2563eb")("x402scan MCP")}`));
2581
+ intro(chalk.bold(`Fund ${chalk.hex("#2563eb")("x402scan MCP")}`));
2188
2582
  const {
2189
2583
  account: { address }
2190
2584
  } = await getWallet();
2191
2585
  await promptDeposit(address, flags);
2192
- outro(chalk3.bold.green("Your x402scan MCP server is funded!"));
2586
+ outro(chalk.bold.green("Your x402scan MCP server is funded!"));
2193
2587
  };
2194
2588
  }
2195
2589
  });
@@ -2197,6 +2591,10 @@ void yargs(hideBin(process.argv)).scriptName("@x402scan/mcp").option("dev", {
2197
2591
  type: "boolean",
2198
2592
  description: "Enable dev mode",
2199
2593
  default: false
2594
+ }).option("invite", {
2595
+ type: "string",
2596
+ description: "Invite code to redeem for starter money",
2597
+ required: false
2200
2598
  }).command(
2201
2599
  "$0",
2202
2600
  "Start the MCP server",
@@ -2215,7 +2613,7 @@ void yargs(hideBin(process.argv)).scriptName("@x402scan/mcp").option("dev", {
2215
2613
  }),
2216
2614
  async (args) => {
2217
2615
  const { installMcpServer: installMcpServer2 } = await Promise.resolve().then(() => (init_install(), install_exports));
2218
- await installMcpServer2({ ...args });
2616
+ await installMcpServer2(args);
2219
2617
  }
2220
2618
  ).command(
2221
2619
  "fund",
@@ -2225,8 +2623,8 @@ void yargs(hideBin(process.argv)).scriptName("@x402scan/mcp").option("dev", {
2225
2623
  const { fundMcpServer: fundMcpServer2 } = await Promise.resolve().then(() => (init_fund(), fund_exports));
2226
2624
  await fundMcpServer2(args);
2227
2625
  }
2228
- ).strict().demandCommand(0, 1, "", "Too many commands provided").help().parseAsync().catch((err) => {
2229
- console.error("Fatal:", err);
2626
+ ).strict().demandCommand(0, 1, "", "Too many commands provided").help().parseAsync().catch((err2) => {
2627
+ console.error("Fatal:", err2);
2230
2628
  process.exit(1);
2231
2629
  });
2232
2630
  //# sourceMappingURL=index.js.map