@x402scan/mcp 0.0.3 → 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.
package/dist/esm/index.js CHANGED
@@ -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,
@@ -416,9 +621,8 @@ var init_fetch_x402_resource = __esm({
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
  }
@@ -1366,7 +1692,7 @@ function registerDiscoveryTools(server) {
1366
1692
  )
1367
1693
  }
1368
1694
  },
1369
- async ({ url, testResources, concurrency }) => {
1695
+ async ({ url, fanOut, concurrency }) => {
1370
1696
  try {
1371
1697
  const origin = getOrigin(url);
1372
1698
  log.info(`Discovering resources for origin: ${origin}`);
@@ -1400,7 +1726,7 @@ function registerDiscoveryTools(server) {
1400
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.",
1401
1727
  resources: []
1402
1728
  };
1403
- if (!testResources) {
1729
+ if (!fanOut) {
1404
1730
  result.resources = doc.resources.map((resourceUrl) => ({
1405
1731
  url: resourceUrl
1406
1732
  }));
@@ -1417,8 +1743,8 @@ function registerDiscoveryTools(server) {
1417
1743
  }
1418
1744
  result.resources = allResources;
1419
1745
  return mcpSuccess(result);
1420
- } catch (err) {
1421
- return mcpError(err, { tool: "discover_resources", url });
1746
+ } catch (err2) {
1747
+ return mcpError(err2, { tool: "discover_resources", url });
1422
1748
  }
1423
1749
  }
1424
1750
  );
@@ -1465,17 +1791,30 @@ var init_server = __esm({
1465
1791
  init_auth();
1466
1792
  init_wallet();
1467
1793
  init_check_endpoint_schema();
1794
+ init_redeem_invite2();
1795
+ init_telemetry();
1468
1796
  init_origins();
1469
1797
  init_log();
1470
1798
  init_wallet2();
1471
1799
  init_discover_resources();
1800
+ init_redeem_invite();
1801
+ init_version();
1472
1802
  startServer = async (flags) => {
1473
1803
  log.info("Starting x402scan-mcp...");
1804
+ const { dev, invite } = flags;
1474
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
+ }
1475
1814
  const server = new McpServer(
1476
1815
  {
1477
1816
  name: "@x402scan/mcp",
1478
- version: "0.0.1",
1817
+ version: MCP_VERSION,
1479
1818
  websiteUrl: "https://x402scan.com/mcp",
1480
1819
  icons: [{ src: "https://x402scan.com/logo.svg" }]
1481
1820
  },
@@ -1497,7 +1836,9 @@ var init_server = __esm({
1497
1836
  registerAuthTools(props);
1498
1837
  registerWalletTools(props);
1499
1838
  registerCheckX402EndpointTool(props);
1839
+ registerRedeemInviteTool(props);
1500
1840
  registerDiscoveryTools(server);
1841
+ registerTelemetryTools(props);
1501
1842
  await registerOrigins({ server, flags });
1502
1843
  const transport = new StdioServerTransport();
1503
1844
  await server.connect(transport);
@@ -1628,7 +1969,7 @@ var init_get_client = __esm({
1628
1969
  if (parsedClientSelection.success) {
1629
1970
  return parsedClientSelection.data;
1630
1971
  }
1631
- outro(chalk3.bold.red("No MCP client selected"));
1972
+ outro(chalk.bold.red("No MCP client selected"));
1632
1973
  process.exit(0);
1633
1974
  };
1634
1975
  }
@@ -1674,7 +2015,7 @@ var parseClientConfig, serializeClientConfig, stringifyObject;
1674
2015
  var init_file_types = __esm({
1675
2016
  "src/install/2-add-server/lib/file-types.ts"() {
1676
2017
  parseClientConfig = ({ format: format2, path: path3 }) => {
1677
- const fileContent = fs4.readFileSync(path3, "utf8");
2018
+ const fileContent = fs__default.readFileSync(path3, "utf8");
1678
2019
  let config = {};
1679
2020
  if (format2 === "yaml" /* YAML */) {
1680
2021
  config = yaml.load(fileContent);
@@ -1851,7 +2192,7 @@ var init_client_config_file = __esm({
1851
2192
  "opencode.json"
1852
2193
  );
1853
2194
  const jsoncPath = jsonPath.replace(".json", ".jsonc");
1854
- if (fs4.existsSync(jsoncPath)) {
2195
+ if (fs__default.existsSync(jsoncPath)) {
1855
2196
  log.info(`Found .jsonc file for OpenCode, using: ${jsoncPath}`);
1856
2197
  return {
1857
2198
  path: jsoncPath,
@@ -1914,7 +2255,7 @@ async function addServer(client, globalFlags) {
1914
2255
  const { serverName, command, args } = getMcpConfig(globalFlags);
1915
2256
  if (client === "warp" /* Warp */) {
1916
2257
  log$1.info(
1917
- chalk3.bold.yellow("Warp requires a manual installation through their UI.")
2258
+ chalk.bold.yellow("Warp requires a manual installation through their UI.")
1918
2259
  );
1919
2260
  log$1.message(
1920
2261
  "Please copy the following configuration object and add it to your Warp MCP config:"
@@ -1951,7 +2292,7 @@ async function addServer(client, globalFlags) {
1951
2292
  let config = {};
1952
2293
  let content = void 0;
1953
2294
  log.info(`Checking if config file exists at: ${clientFileTarget.path}`);
1954
- if (!fs4.existsSync(clientFileTarget.path)) {
2295
+ if (!fs__default.existsSync(clientFileTarget.path)) {
1955
2296
  log.info("Config file not found, creating default empty config");
1956
2297
  setNestedValue(config, clientFileTarget.configKey, {});
1957
2298
  log.info("Config created successfully");
@@ -1985,7 +2326,7 @@ async function addServer(client, globalFlags) {
1985
2326
  if (!servers || typeof servers !== "object") {
1986
2327
  log.error(`Invalid ${clientFileTarget.configKey} structure in config`);
1987
2328
  log$1.error(
1988
- chalk3.bold.red(
2329
+ chalk.bold.red(
1989
2330
  `Invalid ${clientFileTarget.configKey} structure in config`
1990
2331
  )
1991
2332
  );
@@ -2023,7 +2364,9 @@ async function addServer(client, globalFlags) {
2023
2364
  };
2024
2365
  }
2025
2366
  await new Promise((resolve) => setTimeout(resolve, 1e3));
2026
- 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
+ );
2027
2370
  const configStr = formatDiffByFormat(
2028
2371
  {
2029
2372
  [clientFileTarget.configKey]: {
@@ -2055,7 +2398,7 @@ async function addServer(client, globalFlags) {
2055
2398
  inactive: "Cancel"
2056
2399
  });
2057
2400
  if (isConfirmed !== true) {
2058
- outro(chalk3.bold.red("Installation cancelled"));
2401
+ outro(chalk.bold.red("Installation cancelled"));
2059
2402
  process.exit(0);
2060
2403
  }
2061
2404
  const configContent = serializeClientConfig(
@@ -2063,10 +2406,10 @@ async function addServer(client, globalFlags) {
2063
2406
  config,
2064
2407
  content
2065
2408
  );
2066
- fs4.writeFileSync(clientFileTarget.path, configContent);
2067
- 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}`));
2068
2411
  } catch (e) {
2069
- 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}`));
2070
2413
  throw e;
2071
2414
  }
2072
2415
  }
@@ -2100,7 +2443,7 @@ var init_add_server = __esm({
2100
2443
  const diffLines = [0, 1, numLines - 2, numLines - 1];
2101
2444
  const isDiffLine = !diffLines.includes(index);
2102
2445
  if (isDiffLine) {
2103
- return `${chalk3.bold.green(`+ ${line.slice(2)}`)}`;
2446
+ return `${chalk.bold.green(`+ ${line.slice(2)}`)}`;
2104
2447
  }
2105
2448
  return line;
2106
2449
  }).join("\n");
@@ -2110,14 +2453,14 @@ var init_add_server = __esm({
2110
2453
  const diffLines = [0, 1, str.length - 2, str.length - 1];
2111
2454
  const isDiffLine = !diffLines.includes(index);
2112
2455
  if (isDiffLine) {
2113
- return `${chalk3.bold.green(`+ ${line.slice(2)}`)}`;
2456
+ return `${chalk.bold.green(`+ ${line.slice(2)}`)}`;
2114
2457
  }
2115
2458
  return line;
2116
2459
  }).join("\n");
2117
2460
  }
2118
2461
  case "toml" /* TOML */: {
2119
2462
  return str.split("\n").filter((line) => line.trim() !== "").map((line) => {
2120
- return `${chalk3.bold.green(`+ ${line.trim()}`)}`;
2463
+ return `${chalk.bold.green(`+ ${line.trim()}`)}`;
2121
2464
  }).join("\n");
2122
2465
  }
2123
2466
  }
@@ -2139,12 +2482,12 @@ var init_add_funds = __esm({
2139
2482
  const balance = await getUSDCBalance({ address });
2140
2483
  await wait({
2141
2484
  startText: "Checking balance...",
2142
- stopText: `Balance: ${chalk3.bold(`${balance} USDC`)} `,
2485
+ stopText: `Balance: ${chalk.bold(`${balance} USDC`)} `,
2143
2486
  ms: 1e3
2144
2487
  });
2145
2488
  if (balance < 1) {
2146
2489
  log$1.warning(
2147
- chalk3.bold(
2490
+ chalk.bold(
2148
2491
  `Your balance is low (${balance} USDC). Consider topping up.`
2149
2492
  )
2150
2493
  );
@@ -2154,6 +2497,41 @@ var init_add_funds = __esm({
2154
2497
  };
2155
2498
  }
2156
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
+ });
2157
2535
 
2158
2536
  // src/install/index.ts
2159
2537
  var install_exports = {};
@@ -2167,16 +2545,24 @@ var init_install = __esm({
2167
2545
  init_get_client();
2168
2546
  init_add_server();
2169
2547
  init_add_funds();
2548
+ init_redeem_invite3();
2170
2549
  installMcpServer = async (flags) => {
2171
2550
  const {
2172
2551
  account: { address },
2173
2552
  isNew
2174
2553
  } = await getWallet();
2175
- intro(chalk3.green.bold(`Install x402scan MCP`));
2554
+ intro(chalk.green.bold(`Install x402scan MCP`));
2176
2555
  const client = await getClient(flags);
2177
2556
  await addServer(client, flags);
2178
- await addFunds({ flags, address, isNew });
2179
- 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!"));
2180
2566
  };
2181
2567
  }
2182
2568
  });
@@ -2192,12 +2578,12 @@ var init_fund = __esm({
2192
2578
  init_wallet2();
2193
2579
  init_deposit();
2194
2580
  fundMcpServer = async (flags) => {
2195
- intro(chalk3.bold(`Fund ${chalk3.hex("#2563eb")("x402scan MCP")}`));
2581
+ intro(chalk.bold(`Fund ${chalk.hex("#2563eb")("x402scan MCP")}`));
2196
2582
  const {
2197
2583
  account: { address }
2198
2584
  } = await getWallet();
2199
2585
  await promptDeposit(address, flags);
2200
- outro(chalk3.bold.green("Your x402scan MCP server is funded!"));
2586
+ outro(chalk.bold.green("Your x402scan MCP server is funded!"));
2201
2587
  };
2202
2588
  }
2203
2589
  });
@@ -2205,6 +2591,10 @@ void yargs(hideBin(process.argv)).scriptName("@x402scan/mcp").option("dev", {
2205
2591
  type: "boolean",
2206
2592
  description: "Enable dev mode",
2207
2593
  default: false
2594
+ }).option("invite", {
2595
+ type: "string",
2596
+ description: "Invite code to redeem for starter money",
2597
+ required: false
2208
2598
  }).command(
2209
2599
  "$0",
2210
2600
  "Start the MCP server",
@@ -2223,7 +2613,7 @@ void yargs(hideBin(process.argv)).scriptName("@x402scan/mcp").option("dev", {
2223
2613
  }),
2224
2614
  async (args) => {
2225
2615
  const { installMcpServer: installMcpServer2 } = await Promise.resolve().then(() => (init_install(), install_exports));
2226
- await installMcpServer2({ ...args });
2616
+ await installMcpServer2(args);
2227
2617
  }
2228
2618
  ).command(
2229
2619
  "fund",
@@ -2233,8 +2623,8 @@ void yargs(hideBin(process.argv)).scriptName("@x402scan/mcp").option("dev", {
2233
2623
  const { fundMcpServer: fundMcpServer2 } = await Promise.resolve().then(() => (init_fund(), fund_exports));
2234
2624
  await fundMcpServer2(args);
2235
2625
  }
2236
- ).strict().demandCommand(0, 1, "", "Too many commands provided").help().parseAsync().catch((err) => {
2237
- console.error("Fatal:", err);
2626
+ ).strict().demandCommand(0, 1, "", "Too many commands provided").help().parseAsync().catch((err2) => {
2627
+ console.error("Fatal:", err2);
2238
2628
  process.exit(1);
2239
2629
  });
2240
2630
  //# sourceMappingURL=index.js.map