@x402scan/mcp 0.0.1 → 0.0.2

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/index.js CHANGED
@@ -1,8 +1,2198 @@
1
1
  #!/usr/bin/env node
2
+ import z, { z as z$1 } from 'zod';
3
+ import { getAddress, createPublicClient, http, erc20Abi, formatUnits } from 'viem';
4
+ import fs4, { mkdirSync, appendFileSync } from 'fs';
5
+ import path2, { join } from 'path';
6
+ import os, { homedir } from 'os';
7
+ 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';
11
+ import open from 'open';
12
+ import { x402Client, x402HTTPClient } from '@x402/core/client';
13
+ import { ExactEvmScheme } from '@x402/evm/exact/client';
14
+ import { wrapFetchWithPayment } from '@x402/fetch';
15
+ import { base58 } from '@scure/base';
16
+ import 'tweetnacl';
17
+ import { SiweMessage } from 'siwe';
18
+ import { safeBase64Encode } from '@x402/core/utils';
19
+ import { randomBytes } from 'crypto';
20
+ import * as fs from 'fs/promises';
21
+ import { privateKeyToAccount } from 'viem/accounts';
22
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
23
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
24
+ import process2 from 'process';
25
+ import * as TOML from '@iarna/toml';
26
+ import yaml from 'js-yaml';
27
+ import * as jsonc from 'jsonc-parser';
28
+ import yargs from 'yargs';
29
+ import { hideBin } from 'yargs/helpers';
2
30
 
3
- // src/index.ts
4
- import yargs from "yargs";
5
- import { hideBin } from "yargs/helpers";
31
+ var __defProp = Object.defineProperty;
32
+ var __getOwnPropNames = Object.getOwnPropertyNames;
33
+ var __esm = (fn, res) => function __init() {
34
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
35
+ };
36
+ var __export = (target, all) => {
37
+ for (var name in all)
38
+ __defProp(target, name, { get: all[name], enumerable: true });
39
+ };
40
+
41
+ // src/server/lib/response.ts
42
+ var mcpSuccess, mcpError;
43
+ var init_response = __esm({
44
+ "src/server/lib/response.ts"() {
45
+ mcpSuccess = (data) => {
46
+ return {
47
+ content: [{ type: "text", text: JSON.stringify(data, null, 2) }]
48
+ };
49
+ };
50
+ mcpError = (error, context) => {
51
+ const message = error instanceof Error ? error.message : typeof error === "string" ? error : String(error);
52
+ const details = error instanceof Error && error.cause ? { cause: JSON.stringify(error.cause) } : void 0;
53
+ return {
54
+ content: [
55
+ {
56
+ type: "text",
57
+ text: JSON.stringify(
58
+ {
59
+ error: message,
60
+ ...details && { details },
61
+ ...context && { context }
62
+ },
63
+ null,
64
+ 2
65
+ )
66
+ }
67
+ ],
68
+ isError: true
69
+ };
70
+ };
71
+ }
72
+ });
73
+ var ethereumAddressSchema, ethereumPrivateKeySchema, requestSchema, requestWithHeadersSchema;
74
+ var init_schemas = __esm({
75
+ "src/server/lib/schemas.ts"() {
76
+ ethereumAddressSchema = z.string().regex(/^0x[a-fA-F0-9]{40}$/, "Invalid Ethereum address").transform((address) => getAddress(address));
77
+ ethereumPrivateKeySchema = z.string().regex(/^0x[a-fA-F0-9]{64}$/, "Invalid Ethereum private key").transform((privateKey) => privateKey);
78
+ requestSchema = z.object({
79
+ url: z.url().describe("The endpoint URL"),
80
+ method: z.enum(["GET", "POST", "PUT", "DELETE", "PATCH"]).default("GET").describe("HTTP method"),
81
+ body: z.unknown().optional().describe("Request body for POST/PUT/PATCH methods")
82
+ });
83
+ requestWithHeadersSchema = requestSchema.extend({
84
+ headers: z.record(z.string(), z.string()).optional().describe("Additional headers to include").default({})
85
+ });
86
+ }
87
+ });
88
+
89
+ // src/server/types.ts
90
+ var init_types = __esm({
91
+ "src/server/types.ts"() {
92
+ }
93
+ });
94
+ function format(args) {
95
+ return args.map(
96
+ (a) => typeof a === "object" && a !== null ? JSON.stringify(a) : String(a)
97
+ ).join(" ");
98
+ }
99
+ function write(level, msg, args) {
100
+ const formatted = args.length ? `${msg} ${format(args)}` : msg;
101
+ const line = `[${(/* @__PURE__ */ new Date()).toISOString()}] [${level}] ${formatted}
102
+ `;
103
+ try {
104
+ appendFileSync(LOG_FILE, line);
105
+ } catch {
106
+ }
107
+ if (process.env.X402_DEBUG === "true") {
108
+ console.error(`[x402scan] ${formatted}`);
109
+ }
110
+ }
111
+ var LOG_DIR, LOG_FILE, DEBUG, log;
112
+ var init_log = __esm({
113
+ "src/lib/log.ts"() {
114
+ LOG_DIR = join(homedir(), ".x402scan-mcp");
115
+ LOG_FILE = join(LOG_DIR, "mcp.log");
116
+ DEBUG = process.env.X402_DEBUG === "true";
117
+ try {
118
+ mkdirSync(LOG_DIR, { recursive: true });
119
+ } catch {
120
+ }
121
+ log = {
122
+ info: (msg, ...args) => write("INFO", msg, args),
123
+ error: (msg, ...args) => write("ERROR", msg, args),
124
+ debug: (msg, ...args) => DEBUG && write("DEBUG", msg, args),
125
+ path: LOG_FILE
126
+ };
127
+ }
128
+ });
129
+ function toCaip2(network) {
130
+ if (network.startsWith("eip155:")) return network;
131
+ return V1_TO_CAIP2[network.toLowerCase()] ?? network;
132
+ }
133
+ function getChainConfig(network) {
134
+ return CHAIN_CONFIGS[toCaip2(network)];
135
+ }
136
+ function getUSDCAddress(network) {
137
+ return getChainConfig(network)?.usdcAddress;
138
+ }
139
+ function getChain(network) {
140
+ return getChainConfig(network)?.chain;
141
+ }
142
+ function getChainName(network) {
143
+ return getChainConfig(network)?.chain.name ?? network;
144
+ }
145
+ var CHAIN_CONFIGS, V1_TO_CAIP2, DEFAULT_NETWORK;
146
+ var init_networks = __esm({
147
+ "src/lib/networks.ts"() {
148
+ CHAIN_CONFIGS = {
149
+ "eip155:8453": {
150
+ chain: base,
151
+ caip2: "eip155:8453",
152
+ v1Name: "base",
153
+ usdcAddress: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913"
154
+ },
155
+ "eip155:84532": {
156
+ chain: baseSepolia,
157
+ caip2: "eip155:84532",
158
+ v1Name: "base-sepolia",
159
+ usdcAddress: "0x036CbD53842c5426634e7929541eC2318f3dCF7e"
160
+ },
161
+ "eip155:1": {
162
+ chain: mainnet,
163
+ caip2: "eip155:1",
164
+ v1Name: "ethereum",
165
+ usdcAddress: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"
166
+ },
167
+ "eip155:11155111": {
168
+ chain: sepolia,
169
+ caip2: "eip155:11155111",
170
+ v1Name: "ethereum-sepolia",
171
+ usdcAddress: "0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238"
172
+ },
173
+ "eip155:10": {
174
+ chain: optimism,
175
+ caip2: "eip155:10",
176
+ v1Name: "optimism",
177
+ usdcAddress: "0x0b2C639c533813f4Aa9D7837CAf62653d097Ff85"
178
+ },
179
+ "eip155:42161": {
180
+ chain: arbitrum,
181
+ caip2: "eip155:42161",
182
+ v1Name: "arbitrum",
183
+ usdcAddress: "0xaf88d065e77c8cC2239327C5EDb3A432268e5831"
184
+ },
185
+ "eip155:137": {
186
+ chain: polygon,
187
+ caip2: "eip155:137",
188
+ v1Name: "polygon",
189
+ usdcAddress: "0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359"
190
+ }
191
+ };
192
+ V1_TO_CAIP2 = {
193
+ base: "eip155:8453",
194
+ "base-sepolia": "eip155:84532",
195
+ ethereum: "eip155:1",
196
+ "ethereum-sepolia": "eip155:11155111",
197
+ optimism: "eip155:10",
198
+ arbitrum: "eip155:42161",
199
+ polygon: "eip155:137"
200
+ };
201
+ DEFAULT_NETWORK = "eip155:8453";
202
+ }
203
+ });
204
+ var tokenBigIntToNumber, tokenStringToNumber;
205
+ var init_token = __esm({
206
+ "src/lib/token.ts"() {
207
+ tokenBigIntToNumber = (amount, decimals = 6) => {
208
+ return Number(formatUnits(amount, decimals));
209
+ };
210
+ tokenStringToNumber = (amount, decimals = 6) => {
211
+ return tokenBigIntToNumber(BigInt(amount), decimals);
212
+ };
213
+ }
214
+ });
215
+ async function getUSDCBalance({
216
+ address,
217
+ network = DEFAULT_NETWORK
218
+ }) {
219
+ const caip2 = toCaip2(network);
220
+ const chain = getChain(caip2);
221
+ if (!chain) throw new Error(`Unsupported network: ${network}`);
222
+ const usdcAddress = getUSDCAddress(caip2);
223
+ if (!usdcAddress) throw new Error(`No USDC address for network: ${network}`);
224
+ log.debug(`Reading USDC balance for ${address} on ${chain.name}`);
225
+ const client = createPublicClient({ chain, transport: http() });
226
+ const balance = await client.readContract({
227
+ address: usdcAddress,
228
+ abi: erc20Abi,
229
+ functionName: "balanceOf",
230
+ args: [address]
231
+ });
232
+ return tokenBigIntToNumber(balance);
233
+ }
234
+ var init_balance = __esm({
235
+ "src/lib/balance.ts"() {
236
+ init_networks();
237
+ init_log();
238
+ init_token();
239
+ }
240
+ });
241
+ var wait;
242
+ var init_wait = __esm({
243
+ "src/lib/wait.ts"() {
244
+ wait = async ({ startText, stopText, ms }) => {
245
+ const { start: startSpinner, stop: stopSpinner } = spinner();
246
+ startSpinner(startText);
247
+ await new Promise((resolve) => setTimeout(resolve, ms));
248
+ stopSpinner(stopText);
249
+ };
250
+ }
251
+ });
252
+ var getDepositLink, openDepositLink, promptDeposit;
253
+ var init_deposit = __esm({
254
+ "src/lib/deposit.ts"() {
255
+ init_networks();
256
+ init_wait();
257
+ getDepositLink = (address, flags) => {
258
+ const baseUrl = flags.dev ? "http://localhost:3000" : "https://x402scan.com";
259
+ return `${baseUrl}/deposit/${address}`;
260
+ };
261
+ openDepositLink = async (address, flags) => {
262
+ const depositLink = getDepositLink(address, flags);
263
+ await open(depositLink);
264
+ };
265
+ promptDeposit = async (address, flags) => {
266
+ const depositLink = getDepositLink(address, flags);
267
+ const guidedDeposit = await select({
268
+ message: chalk3.bold("How would you like to deposit?"),
269
+ initialValue: true,
270
+ options: [
271
+ {
272
+ label: `Guided - Recommended`,
273
+ value: true,
274
+ hint: "Online portal in x402scan"
275
+ },
276
+ {
277
+ label: "Manual",
278
+ value: false,
279
+ hint: "Print deposit instructions"
280
+ },
281
+ {
282
+ label: "Skip",
283
+ value: void 0,
284
+ hint: "Skip deposit process - functionality limited"
285
+ }
286
+ ]
287
+ });
288
+ if (guidedDeposit === true) {
289
+ await wait({
290
+ startText: "Opening deposit page...",
291
+ stopText: `Opening ${chalk3.underline.hex("#2563eb")(depositLink)}`,
292
+ ms: 1e3
293
+ });
294
+ 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
+ );
312
+ }
313
+ };
314
+ }
315
+ });
316
+
317
+ // src/server/lib/check-balance.ts
318
+ var checkBalance;
319
+ var init_check_balance = __esm({
320
+ "src/server/lib/check-balance.ts"() {
321
+ init_balance();
322
+ init_deposit();
323
+ checkBalance = async ({
324
+ server,
325
+ address,
326
+ amountNeeded,
327
+ message,
328
+ flags
329
+ }) => {
330
+ const balance = await getUSDCBalance({
331
+ address
332
+ });
333
+ if (balance < amountNeeded) {
334
+ const capabilities = server.server.getClientCapabilities();
335
+ if (!capabilities?.elicitation) {
336
+ throw new Error(
337
+ `${message(balance)}
338
+
339
+ You can deposit USDC at ${getDepositLink(address, flags)}`
340
+ );
341
+ }
342
+ const result = await server.server.elicitInput({
343
+ mode: "form",
344
+ message: message(balance),
345
+ requestedSchema: {
346
+ type: "object",
347
+ properties: {}
348
+ }
349
+ });
350
+ if (result.action === "accept") {
351
+ await openDepositLink(address, flags);
352
+ }
353
+ }
354
+ return balance;
355
+ };
356
+ }
357
+ });
358
+ var registerFetchX402ResourceTool;
359
+ var init_fetch_x402_resource = __esm({
360
+ "src/server/tools/fetch-x402-resource.ts"() {
361
+ init_response();
362
+ init_schemas();
363
+ init_types();
364
+ init_log();
365
+ init_networks();
366
+ init_token();
367
+ init_check_balance();
368
+ registerFetchX402ResourceTool = ({
369
+ server,
370
+ account,
371
+ flags
372
+ }) => {
373
+ server.registerTool(
374
+ "fetch",
375
+ {
376
+ description: "Fetches an x402-protected resource and handles payment automatically. If the resource is not x402-protected, it will return the raw response.",
377
+ inputSchema: requestWithHeadersSchema
378
+ },
379
+ async ({ url, method, body, headers }) => {
380
+ const coreClient = x402Client.fromConfig({
381
+ schemes: [
382
+ { network: DEFAULT_NETWORK, client: new ExactEvmScheme(account) }
383
+ ]
384
+ });
385
+ let state = "initial_request" /* INITIAL_REQUEST */;
386
+ coreClient.onBeforePaymentCreation(async ({ selectedRequirements }) => {
387
+ const amount = tokenStringToNumber(selectedRequirements.amount);
388
+ await checkBalance({
389
+ server,
390
+ address: account.address,
391
+ amountNeeded: amount,
392
+ message: (balance) => `This request costs ${amount} USDC. Your current balance is ${balance} USDC.`,
393
+ flags
394
+ });
395
+ state = "payment_required" /* PAYMENT_REQUIRED */;
396
+ });
397
+ coreClient.onAfterPaymentCreation(async (ctx) => {
398
+ state = "payment_created" /* PAYMENT_CREATED */;
399
+ log.info("After payment creation", ctx);
400
+ return Promise.resolve();
401
+ });
402
+ coreClient.onPaymentCreationFailure(async (ctx) => {
403
+ state = "payment_failed" /* PAYMENT_FAILED */;
404
+ log.info("Payment creation failure", ctx);
405
+ return Promise.resolve();
406
+ });
407
+ const client = new x402HTTPClient(coreClient);
408
+ const fetchWithPay = wrapFetchWithPayment(fetch, client);
409
+ try {
410
+ const response = await fetchWithPay(url, {
411
+ method,
412
+ body: body ? JSON.stringify(body) : void 0,
413
+ headers: {
414
+ ...body ? { "Content-Type": "application/json" } : {},
415
+ ...headers
416
+ }
417
+ });
418
+ if (!response.ok) {
419
+ const errorData = await response.text();
420
+ const errorResponse = {
421
+ data: errorData,
422
+ statusCode: response.status,
423
+ state
424
+ };
425
+ if (response.status === 402) {
426
+ return mcpError("Payment required", errorResponse);
427
+ }
428
+ return mcpError(
429
+ response.statusText ?? "Request failed",
430
+ errorResponse
431
+ );
432
+ }
433
+ const getSettlement = () => {
434
+ try {
435
+ return client.getPaymentSettleResponse(
436
+ (name) => response.headers.get(name)
437
+ );
438
+ } catch {
439
+ return void 0;
440
+ }
441
+ };
442
+ const settlement = getSettlement();
443
+ return mcpSuccess({
444
+ data: await response.text().catch(() => void 0),
445
+ payment: settlement
446
+ });
447
+ } catch (err) {
448
+ return mcpError(err, { state });
449
+ }
450
+ }
451
+ );
452
+ };
453
+ }
454
+ });
455
+
456
+ // src/server/lib/x402/protocol.ts
457
+ function isV1Response(pr) {
458
+ if (!pr || typeof pr !== "object") return false;
459
+ const obj = pr;
460
+ if (obj.x402Version === 1) return true;
461
+ const accepts = obj.accepts;
462
+ if (Array.isArray(accepts) && accepts.length > 0) {
463
+ return "maxAmountRequired" in accepts[0];
464
+ }
465
+ return false;
466
+ }
467
+ function normalizeV1Requirement(req) {
468
+ if (!req.maxAmountRequired) {
469
+ throw new Error("v1 requirement missing maxAmountRequired field");
470
+ }
471
+ return {
472
+ scheme: req.scheme,
473
+ network: req.network,
474
+ amount: req.maxAmountRequired,
475
+ asset: req.asset,
476
+ payTo: req.payTo,
477
+ maxTimeoutSeconds: req.maxTimeoutSeconds,
478
+ extra: req.extra,
479
+ resource: req.resource,
480
+ description: req.description,
481
+ mimeType: req.mimeType
482
+ };
483
+ }
484
+ function normalizeV2Requirement(req) {
485
+ if (!req.amount) {
486
+ throw new Error("v2 requirement missing amount field");
487
+ }
488
+ return {
489
+ scheme: req.scheme,
490
+ network: req.network,
491
+ amount: req.amount,
492
+ asset: req.asset,
493
+ payTo: req.payTo,
494
+ maxTimeoutSeconds: req.maxTimeoutSeconds,
495
+ extra: req.extra
496
+ };
497
+ }
498
+ function normalizePaymentRequired(pr) {
499
+ const version = pr.x402Version ?? 1;
500
+ if (isV1Response(pr)) {
501
+ const v1 = pr;
502
+ return {
503
+ x402Version: 1,
504
+ error: v1.error,
505
+ accepts: v1.accepts.map(normalizeV1Requirement)
506
+ };
507
+ }
508
+ const v2 = pr;
509
+ return {
510
+ x402Version: version,
511
+ error: v2.error,
512
+ accepts: v2.accepts.map(normalizeV2Requirement),
513
+ resource: v2.resource,
514
+ extensions: v2.extensions
515
+ };
516
+ }
517
+ var init_protocol = __esm({
518
+ "src/server/lib/x402/protocol.ts"() {
519
+ }
520
+ });
521
+ function extractSolanaChainReference(chainId) {
522
+ const [, reference] = chainId.split(":");
523
+ return reference;
524
+ }
525
+ function formatSIWSMessage(info, address) {
526
+ const lines = [
527
+ `${info.domain} wants you to sign in with your Solana account:`,
528
+ address,
529
+ ""
530
+ ];
531
+ if (info.statement) {
532
+ lines.push(info.statement, "");
533
+ }
534
+ lines.push(
535
+ `URI: ${info.uri}`,
536
+ `Version: ${info.version}`,
537
+ `Chain ID: ${extractSolanaChainReference(info.chainId)}`,
538
+ `Nonce: ${info.nonce}`,
539
+ `Issued At: ${info.issuedAt}`
540
+ );
541
+ if (info.expirationTime) {
542
+ lines.push(`Expiration Time: ${info.expirationTime}`);
543
+ }
544
+ if (info.notBefore) {
545
+ lines.push(`Not Before: ${info.notBefore}`);
546
+ }
547
+ if (info.requestId) {
548
+ lines.push(`Request ID: ${info.requestId}`);
549
+ }
550
+ if (info.resources && info.resources.length > 0) {
551
+ lines.push("Resources:");
552
+ for (const resource of info.resources) {
553
+ lines.push(`- ${resource}`);
554
+ }
555
+ }
556
+ return lines.join("\n");
557
+ }
558
+ function encodeBase58(bytes) {
559
+ return base58.encode(bytes);
560
+ }
561
+ var init_solana = __esm({
562
+ "src/server/vendor/sign-in-with-x/solana.ts"() {
563
+ }
564
+ });
565
+
566
+ // src/server/vendor/sign-in-with-x/sign.ts
567
+ function getEVMAddress(signer) {
568
+ if (signer.account?.address) {
569
+ return signer.account.address;
570
+ }
571
+ if (signer.address) {
572
+ return signer.address;
573
+ }
574
+ throw new Error("EVM signer missing address");
575
+ }
576
+ function getSolanaAddress(signer) {
577
+ const pk = signer.publicKey;
578
+ return typeof pk === "string" ? pk : pk.toBase58();
579
+ }
580
+ async function signEVMMessage(message, signer) {
581
+ if (signer.account) {
582
+ return signer.signMessage({ message, account: signer.account });
583
+ }
584
+ return signer.signMessage({ message });
585
+ }
586
+ async function signSolanaMessage(message, signer) {
587
+ const messageBytes = new TextEncoder().encode(message);
588
+ const signatureBytes = await signer.signMessage(messageBytes);
589
+ return encodeBase58(signatureBytes);
590
+ }
591
+ var init_sign = __esm({
592
+ "src/server/vendor/sign-in-with-x/sign.ts"() {
593
+ init_solana();
594
+ }
595
+ });
596
+ function extractEVMChainId(chainId) {
597
+ const match = /^eip155:(\d+)$/.exec(chainId);
598
+ if (!match) {
599
+ throw new Error(
600
+ `Invalid EVM chainId format: ${chainId}. Expected eip155:<number>`
601
+ );
602
+ }
603
+ return parseInt(match[1], 10);
604
+ }
605
+ function formatSIWEMessage(info, address) {
606
+ const numericChainId = extractEVMChainId(info.chainId);
607
+ const siweMessage = new SiweMessage({
608
+ domain: info.domain,
609
+ address,
610
+ statement: info.statement,
611
+ uri: info.uri,
612
+ version: info.version,
613
+ chainId: numericChainId,
614
+ nonce: info.nonce,
615
+ issuedAt: info.issuedAt,
616
+ expirationTime: info.expirationTime,
617
+ notBefore: info.notBefore,
618
+ requestId: info.requestId,
619
+ resources: info.resources
620
+ });
621
+ return siweMessage.prepareMessage();
622
+ }
623
+ var init_evm = __esm({
624
+ "src/server/vendor/sign-in-with-x/evm.ts"() {
625
+ }
626
+ });
627
+
628
+ // src/server/vendor/sign-in-with-x/message.ts
629
+ function createSIWxMessage(serverInfo, address) {
630
+ if (serverInfo.chainId.startsWith("eip155:")) {
631
+ return formatSIWEMessage(serverInfo, address);
632
+ }
633
+ if (serverInfo.chainId.startsWith("solana:")) {
634
+ return formatSIWSMessage(serverInfo, address);
635
+ }
636
+ throw new Error(
637
+ `Unsupported chain namespace: ${serverInfo.chainId}. Supported: eip155:* (EVM), solana:* (Solana)`
638
+ );
639
+ }
640
+ var init_message = __esm({
641
+ "src/server/vendor/sign-in-with-x/message.ts"() {
642
+ init_evm();
643
+ init_solana();
644
+ }
645
+ });
646
+
647
+ // src/server/vendor/sign-in-with-x/client.ts
648
+ async function createSIWxPayload(serverExtension, signer) {
649
+ const isSolana = serverExtension.chainId.startsWith("solana:");
650
+ const address = isSolana ? getSolanaAddress(signer) : getEVMAddress(signer);
651
+ const message = createSIWxMessage(serverExtension, address);
652
+ const signature = isSolana ? await signSolanaMessage(message, signer) : await signEVMMessage(message, signer);
653
+ return {
654
+ domain: serverExtension.domain,
655
+ address,
656
+ statement: serverExtension.statement,
657
+ uri: serverExtension.uri,
658
+ version: serverExtension.version,
659
+ chainId: serverExtension.chainId,
660
+ type: serverExtension.type,
661
+ nonce: serverExtension.nonce,
662
+ issuedAt: serverExtension.issuedAt,
663
+ expirationTime: serverExtension.expirationTime,
664
+ notBefore: serverExtension.notBefore,
665
+ requestId: serverExtension.requestId,
666
+ resources: serverExtension.resources,
667
+ signatureScheme: serverExtension.signatureScheme,
668
+ signature
669
+ };
670
+ }
671
+ var init_client = __esm({
672
+ "src/server/vendor/sign-in-with-x/client.ts"() {
673
+ init_sign();
674
+ init_message();
675
+ }
676
+ });
677
+ function encodeSIWxHeader(payload) {
678
+ return safeBase64Encode(JSON.stringify(payload));
679
+ }
680
+ var init_encode = __esm({
681
+ "src/server/vendor/sign-in-with-x/encode.ts"() {
682
+ }
683
+ });
684
+ var registerAuthTools;
685
+ var init_auth = __esm({
686
+ "src/server/tools/auth.ts"() {
687
+ init_response();
688
+ init_protocol();
689
+ init_client();
690
+ init_encode();
691
+ init_schemas();
692
+ registerAuthTools = ({ server, account }) => {
693
+ server.registerTool(
694
+ "authed_call",
695
+ {
696
+ 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.",
697
+ inputSchema: requestWithHeadersSchema
698
+ },
699
+ async ({ url, method, body, headers }) => {
700
+ try {
701
+ const httpClient = new x402HTTPClient(new x402Client());
702
+ const firstResponse = await fetch(url, {
703
+ method,
704
+ headers: {
705
+ "Content-Type": "application/json",
706
+ ...headers
707
+ },
708
+ body: body ? JSON.stringify(body) : void 0
709
+ });
710
+ if (firstResponse.status !== 402) {
711
+ const responseHeaders2 = Object.fromEntries(
712
+ firstResponse.headers.entries()
713
+ );
714
+ if (firstResponse.ok) {
715
+ let data2;
716
+ const contentType2 = firstResponse.headers.get("content-type");
717
+ if (contentType2?.includes("application/json")) {
718
+ data2 = await firstResponse.json();
719
+ } else {
720
+ data2 = await firstResponse.text();
721
+ }
722
+ return mcpSuccess({
723
+ statusCode: firstResponse.status,
724
+ headers: responseHeaders2,
725
+ data: data2
726
+ });
727
+ }
728
+ let errorBody;
729
+ try {
730
+ errorBody = await firstResponse.json();
731
+ } catch {
732
+ errorBody = await firstResponse.text();
733
+ }
734
+ return mcpError(`HTTP ${firstResponse.status}`, {
735
+ statusCode: firstResponse.status,
736
+ headers: responseHeaders2,
737
+ body: errorBody
738
+ });
739
+ }
740
+ let rawBody;
741
+ try {
742
+ rawBody = await firstResponse.clone().json();
743
+ } catch {
744
+ rawBody = void 0;
745
+ }
746
+ const rawPaymentRequired = httpClient.getPaymentRequiredResponse(
747
+ (name) => firstResponse.headers.get(name),
748
+ rawBody
749
+ );
750
+ const paymentRequired = normalizePaymentRequired(rawPaymentRequired);
751
+ const siwxExtension = paymentRequired.extensions?.["sign-in-with-x"];
752
+ if (!siwxExtension?.info) {
753
+ return mcpError(
754
+ "Endpoint returned 402 but no sign-in-with-x extension found",
755
+ {
756
+ statusCode: 402,
757
+ x402Version: paymentRequired.x402Version,
758
+ extensions: Object.keys(paymentRequired.extensions ?? {}),
759
+ hint: "This endpoint may require payment instead of authentication. Use execute_call for paid requests."
760
+ }
761
+ );
762
+ }
763
+ const serverInfo = siwxExtension.info;
764
+ const requiredFields = [
765
+ "domain",
766
+ "uri",
767
+ "version",
768
+ "chainId",
769
+ "nonce",
770
+ "issuedAt"
771
+ ];
772
+ const missingFields = requiredFields.filter(
773
+ (f) => !serverInfo[f]
774
+ );
775
+ if (missingFields.length > 0) {
776
+ return mcpError(
777
+ "Invalid sign-in-with-x extension: missing required fields",
778
+ {
779
+ missingFields,
780
+ receivedInfo: serverInfo
781
+ }
782
+ );
783
+ }
784
+ if (serverInfo.chainId.startsWith("solana:")) {
785
+ return mcpError("Solana authentication not supported", {
786
+ chainId: serverInfo.chainId,
787
+ hint: "This endpoint requires a Solana wallet. The MCP server currently only supports EVM wallets."
788
+ });
789
+ }
790
+ const payload = await createSIWxPayload(serverInfo, account);
791
+ const siwxHeader = encodeSIWxHeader(payload);
792
+ const authedResponse = await fetch(url, {
793
+ method,
794
+ headers: {
795
+ "Content-Type": "application/json",
796
+ "SIGN-IN-WITH-X": siwxHeader,
797
+ ...headers
798
+ },
799
+ body: body ? JSON.stringify(body) : void 0
800
+ });
801
+ const responseHeaders = Object.fromEntries(
802
+ authedResponse.headers.entries()
803
+ );
804
+ if (!authedResponse.ok) {
805
+ let errorBody;
806
+ try {
807
+ errorBody = await authedResponse.json();
808
+ } catch {
809
+ errorBody = await authedResponse.text();
810
+ }
811
+ return mcpError(
812
+ `HTTP ${authedResponse.status} after authentication`,
813
+ {
814
+ statusCode: authedResponse.status,
815
+ headers: responseHeaders,
816
+ body: errorBody,
817
+ authAddress: account.address
818
+ }
819
+ );
820
+ }
821
+ let data;
822
+ const contentType = authedResponse.headers.get("content-type");
823
+ if (contentType?.includes("application/json")) {
824
+ data = await authedResponse.json();
825
+ } else {
826
+ data = await authedResponse.text();
827
+ }
828
+ return mcpSuccess({
829
+ statusCode: authedResponse.status,
830
+ headers: responseHeaders,
831
+ data,
832
+ authentication: {
833
+ address: account.address,
834
+ domain: serverInfo.domain,
835
+ chainId: serverInfo.chainId
836
+ }
837
+ });
838
+ } catch (err) {
839
+ return mcpError(err, { tool: "authed_call", url });
840
+ }
841
+ }
842
+ );
843
+ };
844
+ }
845
+ });
846
+
847
+ // src/server/tools/wallet.ts
848
+ var registerWalletTools;
849
+ var init_wallet = __esm({
850
+ "src/server/tools/wallet.ts"() {
851
+ init_response();
852
+ init_balance();
853
+ init_networks();
854
+ registerWalletTools = ({
855
+ server,
856
+ account: { address }
857
+ }) => {
858
+ server.registerTool(
859
+ "check_balance",
860
+ {
861
+ description: "Check wallet address and USDC balance. Creates wallet if needed."
862
+ },
863
+ async () => {
864
+ const balance = await getUSDCBalance({
865
+ address
866
+ });
867
+ return mcpSuccess({
868
+ address,
869
+ network: DEFAULT_NETWORK,
870
+ networkName: getChainName(DEFAULT_NETWORK),
871
+ usdcBalance: balance,
872
+ balanceFormatted: balance.toString(),
873
+ isNewWallet: balance === 0
874
+ });
875
+ }
876
+ );
877
+ server.registerTool(
878
+ "get_wallet_address",
879
+ {
880
+ description: "Get the wallet address."
881
+ },
882
+ () => mcpSuccess({ address })
883
+ );
884
+ };
885
+ }
886
+ });
887
+
888
+ // src/server/lib/x402/get-route-details.ts
889
+ var getRouteDetails, getSchema;
890
+ var init_get_route_details = __esm({
891
+ "src/server/lib/x402/get-route-details.ts"() {
892
+ init_token();
893
+ getRouteDetails = (paymentRequired) => {
894
+ const { accepts, extensions, resource } = paymentRequired;
895
+ return {
896
+ ...resource,
897
+ schema: getSchema(extensions),
898
+ paymentMethods: accepts.map((accept) => ({
899
+ price: tokenStringToNumber(accept.amount),
900
+ network: accept.network,
901
+ asset: accept.asset
902
+ }))
903
+ };
904
+ };
905
+ getSchema = (extensions) => {
906
+ const { bazaar } = extensions ?? {};
907
+ if (!bazaar) {
908
+ return void 0;
909
+ }
910
+ const { schema } = bazaar;
911
+ return schema.properties.input;
912
+ };
913
+ }
914
+ });
915
+ var registerCheckX402EndpointTool;
916
+ var init_check_endpoint_schema = __esm({
917
+ "src/server/tools/check-endpoint-schema.ts"() {
918
+ init_response();
919
+ init_schemas();
920
+ init_log();
921
+ init_get_route_details();
922
+ registerCheckX402EndpointTool = ({ server }) => {
923
+ server.registerTool(
924
+ "check_x402_endpoint",
925
+ {
926
+ description: "Check if an endpoint is x402-protected and get pricing options, schema, and auth requirements (if applicable).",
927
+ inputSchema: requestSchema
928
+ },
929
+ async ({ url, method, body }) => {
930
+ try {
931
+ log.info("Querying endpoint", { url, method, body });
932
+ const response = await fetch(url, {
933
+ method,
934
+ body: body ? typeof body === "string" ? body : JSON.stringify(body) : void 0,
935
+ headers: {
936
+ "Content-Type": "application/json"
937
+ }
938
+ });
939
+ const bodyText = await response.text().catch(() => void 0);
940
+ if (response.status !== 402) {
941
+ return mcpSuccess({
942
+ data: bodyText,
943
+ statusCode: response.status,
944
+ requiresPayment: false
945
+ });
946
+ }
947
+ const paymentRequired = new x402HTTPClient(
948
+ new x402Client()
949
+ ).getPaymentRequiredResponse(
950
+ (name) => response.headers.get(name),
951
+ JSON.parse(bodyText ?? "{}")
952
+ );
953
+ const routeDetails = getRouteDetails(paymentRequired);
954
+ return mcpSuccess({
955
+ requiresPayment: true,
956
+ statusCode: response.status,
957
+ routeDetails
958
+ });
959
+ } catch (err) {
960
+ return mcpError(err, { tool: "query_endpoint", url });
961
+ }
962
+ }
963
+ );
964
+ };
965
+ }
966
+ });
967
+
968
+ // src/server/resources/_lib.ts
969
+ var getWebPageMetadata;
970
+ var init_lib = __esm({
971
+ "src/server/resources/_lib.ts"() {
972
+ getWebPageMetadata = async (url) => {
973
+ try {
974
+ const response = await fetch(url);
975
+ if (!response.ok) {
976
+ return null;
977
+ }
978
+ const html = await response.text();
979
+ const titleMatch = /<title[^>]*>([\s\S]*?)<\/title>/i.exec(html);
980
+ const title = titleMatch ? titleMatch[1].trim().replace(/\s+/g, " ") : null;
981
+ let descriptionMatch = /<meta\s+name=["']description["']\s+content=["']([^"']*)["']/i.exec(html);
982
+ descriptionMatch ??= /<meta\s+property=["']og:description["']\s+content=["']([^"']*)["']/i.exec(
983
+ html
984
+ );
985
+ descriptionMatch ??= /<meta\s+content=["']([^"']*)["']\s+name=["']description["']/i.exec(html);
986
+ descriptionMatch ??= /<meta\s+content=["']([^"']*)["']\s+property=["']og:description["']/i.exec(
987
+ html
988
+ );
989
+ const description = descriptionMatch ? descriptionMatch[1].trim().replace(/\s+/g, " ") : null;
990
+ return {
991
+ title,
992
+ description
993
+ };
994
+ } catch (error) {
995
+ throw new Error(
996
+ `Failed to fetch web page metadata: ${error instanceof Error ? error.message : String(error)}`
997
+ );
998
+ }
999
+ };
1000
+ }
1001
+ });
1002
+ var origins, registerOrigins, getResourceResponse;
1003
+ var init_origins = __esm({
1004
+ "src/server/resources/origins.ts"() {
1005
+ init_lib();
1006
+ init_get_route_details();
1007
+ origins = ["enrichx402.com"];
1008
+ registerOrigins = async ({ server }) => {
1009
+ await Promise.all(
1010
+ origins.map(async (origin) => {
1011
+ const metadata = await getWebPageMetadata(`https://${origin}`);
1012
+ server.registerResource(
1013
+ origin,
1014
+ `api://${origin}`,
1015
+ {
1016
+ title: metadata?.title ?? origin,
1017
+ description: metadata?.description ?? "",
1018
+ mimeType: "application/json"
1019
+ },
1020
+ async (uri) => {
1021
+ const response = await fetch(
1022
+ `${uri.toString().replace("api://", "https://")}/.well-known/x402`
1023
+ ).then((response2) => response2.json());
1024
+ const resources = await Promise.all(
1025
+ response.resources.map(async (resource) => {
1026
+ const resourceResponse = await getResourceResponse(
1027
+ resource,
1028
+ await fetch(resource, {
1029
+ method: "POST",
1030
+ headers: {
1031
+ "Content-Type": "application/json"
1032
+ }
1033
+ })
1034
+ );
1035
+ if (resourceResponse) {
1036
+ return resourceResponse;
1037
+ }
1038
+ const getResponse = await getResourceResponse(
1039
+ resource,
1040
+ await fetch(resource, {
1041
+ method: "GET"
1042
+ })
1043
+ );
1044
+ if (getResponse) {
1045
+ return getResponse;
1046
+ }
1047
+ console.error(`Failed to get resource response for ${resource}`);
1048
+ return null;
1049
+ })
1050
+ );
1051
+ return {
1052
+ contents: [
1053
+ {
1054
+ uri: origin,
1055
+ text: JSON.stringify({
1056
+ server: origin,
1057
+ name: metadata?.title,
1058
+ description: metadata?.description,
1059
+ resources: resources.filter(Boolean).map((resource) => {
1060
+ if (!resource) return null;
1061
+ const schema = getSchema(
1062
+ resource.paymentRequired?.extensions
1063
+ );
1064
+ return {
1065
+ url: resource.resource,
1066
+ schema,
1067
+ mimeType: resource.paymentRequired.resource.mimeType
1068
+ };
1069
+ })
1070
+ }),
1071
+ mimeType: "application/json"
1072
+ }
1073
+ ]
1074
+ };
1075
+ }
1076
+ );
1077
+ })
1078
+ );
1079
+ };
1080
+ getResourceResponse = async (resource, response) => {
1081
+ const client = new x402HTTPClient(new x402Client());
1082
+ if (response.status === 402) {
1083
+ const paymentRequired = client.getPaymentRequiredResponse(
1084
+ (name) => response.headers.get(name),
1085
+ JSON.parse(await response.text())
1086
+ );
1087
+ return {
1088
+ paymentRequired,
1089
+ resource
1090
+ };
1091
+ }
1092
+ return null;
1093
+ };
1094
+ }
1095
+ });
1096
+ async function getWallet() {
1097
+ if (process.env.X402_PRIVATE_KEY) {
1098
+ const account2 = privateKeyToAccount(process.env.X402_PRIVATE_KEY);
1099
+ log.info(`Using wallet from env: ${account2.address}`);
1100
+ return { account: account2, isNew: false };
1101
+ }
1102
+ try {
1103
+ const data = await fs.readFile(KEYSTORE_FILE, "utf-8");
1104
+ const stored2 = storedWalletSchema.parse(JSON.parse(data));
1105
+ const account2 = privateKeyToAccount(stored2.privateKey);
1106
+ log.info(`Loaded wallet: ${account2.address}`);
1107
+ return { account: account2, isNew: false };
1108
+ } catch {
1109
+ }
1110
+ const privateKey = `0x${randomBytes(32).toString("hex")}`;
1111
+ const account = privateKeyToAccount(privateKey);
1112
+ const stored = {
1113
+ privateKey,
1114
+ address: account.address,
1115
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
1116
+ };
1117
+ await fs.mkdir(KEYSTORE_DIR, { recursive: true });
1118
+ await fs.writeFile(KEYSTORE_FILE, JSON.stringify(stored, null, 2));
1119
+ try {
1120
+ await fs.chmod(KEYSTORE_FILE, 384);
1121
+ } catch {
1122
+ }
1123
+ log.info(`Created wallet: ${account.address}`);
1124
+ log.info(`Saved to: ${KEYSTORE_FILE}`);
1125
+ return { account, isNew: true };
1126
+ }
1127
+ var KEYSTORE_DIR, KEYSTORE_FILE, storedWalletSchema;
1128
+ var init_wallet2 = __esm({
1129
+ "src/lib/wallet.ts"() {
1130
+ init_log();
1131
+ init_schemas();
1132
+ KEYSTORE_DIR = join(homedir(), ".x402scan-mcp");
1133
+ KEYSTORE_FILE = join(KEYSTORE_DIR, "wallet.json");
1134
+ storedWalletSchema = z.object({
1135
+ privateKey: ethereumPrivateKeySchema,
1136
+ address: ethereumAddressSchema,
1137
+ createdAt: z.string()
1138
+ });
1139
+ }
1140
+ });
1141
+ async function lookupDnsTxtRecord(hostname) {
1142
+ const dnsQuery = `_x402.${hostname}`;
1143
+ log.debug(`Looking up DNS TXT record: ${dnsQuery}`);
1144
+ try {
1145
+ const response = await fetch(
1146
+ `https://cloudflare-dns.com/dns-query?name=${encodeURIComponent(
1147
+ dnsQuery
1148
+ )}&type=TXT`,
1149
+ {
1150
+ headers: { Accept: "application/dns-json" }
1151
+ }
1152
+ );
1153
+ if (!response.ok) {
1154
+ log.debug(`DNS lookup failed: HTTP ${response.status}`);
1155
+ return null;
1156
+ }
1157
+ const data = await response.json();
1158
+ if (!data.Answer || data.Answer.length === 0) {
1159
+ log.debug("No DNS TXT record found");
1160
+ return null;
1161
+ }
1162
+ const txtValue = data.Answer[0].data.replace(/^"|"$/g, "");
1163
+ log.debug(`Found DNS TXT record: ${txtValue}`);
1164
+ try {
1165
+ new URL(txtValue);
1166
+ return txtValue;
1167
+ } catch {
1168
+ log.debug(`DNS TXT value is not a valid URL: ${txtValue}`);
1169
+ return null;
1170
+ }
1171
+ } catch (err) {
1172
+ log.debug(
1173
+ `DNS lookup error: ${err instanceof Error ? err.message : String(err)}`
1174
+ );
1175
+ return null;
1176
+ }
1177
+ }
1178
+ async function fetchLlmsTxt(origin) {
1179
+ const llmsTxtUrl = `${origin}/llms.txt`;
1180
+ log.debug(`Fetching llms.txt from: ${llmsTxtUrl}`);
1181
+ try {
1182
+ const response = await fetch(llmsTxtUrl, {
1183
+ headers: { Accept: "text/plain" }
1184
+ });
1185
+ if (!response.ok) {
1186
+ if (response.status === 404) {
1187
+ return { found: false, error: "No llms.txt found" };
1188
+ }
1189
+ return { found: false, error: `HTTP ${response.status}` };
1190
+ }
1191
+ const content = await response.text();
1192
+ if (!content || content.trim().length === 0) {
1193
+ return { found: false, error: "llms.txt is empty" };
1194
+ }
1195
+ return { found: true, content };
1196
+ } catch (err) {
1197
+ return {
1198
+ found: false,
1199
+ error: `Network error: ${err instanceof Error ? err.message : String(err)}`
1200
+ };
1201
+ }
1202
+ }
1203
+ async function fetchDiscoveryFromUrl(url) {
1204
+ log.debug(`Fetching discovery document from: ${url}`);
1205
+ try {
1206
+ const response = await fetch(url, {
1207
+ headers: { Accept: "application/json" }
1208
+ });
1209
+ if (!response.ok) {
1210
+ if (response.status === 404) {
1211
+ return { found: false, error: `Not found at ${url}` };
1212
+ }
1213
+ return {
1214
+ found: false,
1215
+ error: `HTTP ${response.status}: ${await response.text()}`
1216
+ };
1217
+ }
1218
+ let rawData;
1219
+ try {
1220
+ rawData = await response.json();
1221
+ } catch {
1222
+ return {
1223
+ found: false,
1224
+ error: "Failed to parse discovery document as JSON"
1225
+ };
1226
+ }
1227
+ const parsed = DiscoveryDocumentSchema.safeParse(rawData);
1228
+ if (!parsed.success) {
1229
+ return {
1230
+ found: false,
1231
+ error: `Invalid discovery document: ${parsed.error.issues.map((e) => e.message).join(", ")}`,
1232
+ rawResponse: rawData
1233
+ };
1234
+ }
1235
+ return { found: true, document: parsed.data };
1236
+ } catch (err) {
1237
+ return {
1238
+ found: false,
1239
+ error: `Network error: ${err instanceof Error ? err.message : String(err)}`
1240
+ };
1241
+ }
1242
+ }
1243
+ async function fetchDiscoveryDocument(origin) {
1244
+ const attemptedSources = [];
1245
+ const hostname = getHostname(origin);
1246
+ const wellKnownUrl = `${origin}/.well-known/x402`;
1247
+ attemptedSources.push(wellKnownUrl);
1248
+ const wellKnownResult = await fetchDiscoveryFromUrl(wellKnownUrl);
1249
+ if (wellKnownResult.found && wellKnownResult.document) {
1250
+ return {
1251
+ found: true,
1252
+ source: "well-known",
1253
+ document: wellKnownResult.document,
1254
+ attemptedSources
1255
+ };
1256
+ }
1257
+ attemptedSources.push(`DNS TXT _x402.${hostname}`);
1258
+ const dnsUrl = await lookupDnsTxtRecord(hostname);
1259
+ if (dnsUrl) {
1260
+ attemptedSources.push(dnsUrl);
1261
+ const dnsResult = await fetchDiscoveryFromUrl(dnsUrl);
1262
+ if (dnsResult.found && dnsResult.document) {
1263
+ return {
1264
+ found: true,
1265
+ source: "dns-txt",
1266
+ document: dnsResult.document,
1267
+ attemptedSources
1268
+ };
1269
+ }
1270
+ }
1271
+ attemptedSources.push(`${origin}/llms.txt`);
1272
+ const llmsResult = await fetchLlmsTxt(origin);
1273
+ if (llmsResult.found && llmsResult.content) {
1274
+ return {
1275
+ found: true,
1276
+ source: "llms-txt",
1277
+ llmsTxtContent: llmsResult.content,
1278
+ attemptedSources
1279
+ };
1280
+ }
1281
+ return {
1282
+ found: false,
1283
+ error: "No discovery document found. Tried: .well-known/x402, DNS TXT record, llms.txt",
1284
+ attemptedSources
1285
+ };
1286
+ }
1287
+ async function queryResource(url) {
1288
+ log.debug(`Querying resource: ${url}`);
1289
+ try {
1290
+ const result = await fetch(url, { method: "GET" });
1291
+ if (!result.ok) {
1292
+ return {
1293
+ url,
1294
+ isX402Endpoint: false,
1295
+ error: result.statusText ?? "Failed to query endpoint"
1296
+ };
1297
+ }
1298
+ if (result.status !== 402) {
1299
+ return {
1300
+ url,
1301
+ isX402Endpoint: false
1302
+ };
1303
+ }
1304
+ const pr = new x402HTTPClient(new x402Client()).getPaymentRequiredResponse(
1305
+ (name) => result.headers.get(name),
1306
+ JSON.parse(await result.text())
1307
+ );
1308
+ const firstReq = pr.accepts[0];
1309
+ const resource = {
1310
+ url,
1311
+ isX402Endpoint: true,
1312
+ x402Version: pr.x402Version,
1313
+ price: tokenStringToNumber(firstReq.amount),
1314
+ priceRaw: firstReq.amount,
1315
+ network: firstReq.network,
1316
+ networkName: getChainName(firstReq.network)
1317
+ };
1318
+ if (pr.extensions?.bazaar) {
1319
+ const bazaar = pr.extensions.bazaar;
1320
+ resource.bazaar = { info: bazaar.info, schema: bazaar.schema };
1321
+ const info = bazaar.info;
1322
+ if (info?.description) {
1323
+ resource.description = info.description;
1324
+ }
1325
+ }
1326
+ if (pr.extensions?.["sign-in-with-x"]) {
1327
+ const siwx = pr.extensions["sign-in-with-x"];
1328
+ resource.signInWithX = { required: true, info: siwx.info };
1329
+ }
1330
+ return resource;
1331
+ } catch (err) {
1332
+ return {
1333
+ url,
1334
+ isX402Endpoint: false,
1335
+ error: err instanceof Error ? err.message : String(err)
1336
+ };
1337
+ }
1338
+ }
1339
+ function registerDiscoveryTools(server) {
1340
+ server.registerTool(
1341
+ "discover_resources",
1342
+ {
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
+
1345
+ 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
1347
+ - https://stablestudio.io -> generate images / videos
1348
+ `,
1349
+ inputSchema: {
1350
+ url: z$1.url().describe(
1351
+ "The origin URL or any URL on the origin to discover resources from"
1352
+ ),
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)"
1355
+ ),
1356
+ concurrency: z$1.number().int().min(1).max(10).default(5).describe(
1357
+ "Max concurrent requests when querying resources (default: 5)"
1358
+ )
1359
+ }
1360
+ },
1361
+ async ({ url, testResources, concurrency }) => {
1362
+ try {
1363
+ const origin = getOrigin(url);
1364
+ log.info(`Discovering resources for origin: ${origin}`);
1365
+ const discoveryResult = await fetchDiscoveryDocument(origin);
1366
+ if (discoveryResult.found && discoveryResult.source === "llms-txt") {
1367
+ return mcpSuccess({
1368
+ found: true,
1369
+ origin,
1370
+ source: "llms-txt",
1371
+ 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.",
1372
+ llmsTxtContent: discoveryResult.llmsTxtContent,
1373
+ attemptedSources: discoveryResult.attemptedSources,
1374
+ resources: []
1375
+ });
1376
+ }
1377
+ if (!discoveryResult.found || !discoveryResult.document) {
1378
+ return mcpSuccess({
1379
+ found: false,
1380
+ origin,
1381
+ error: discoveryResult.error,
1382
+ attemptedSources: discoveryResult.attemptedSources,
1383
+ rawResponse: discoveryResult.rawResponse
1384
+ });
1385
+ }
1386
+ const doc = discoveryResult.document;
1387
+ const result = {
1388
+ found: true,
1389
+ origin,
1390
+ source: discoveryResult.source,
1391
+ instructions: doc.instructions,
1392
+ 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
+ resources: []
1394
+ };
1395
+ if (!testResources) {
1396
+ result.resources = doc.resources.map((resourceUrl) => ({
1397
+ url: resourceUrl
1398
+ }));
1399
+ return mcpSuccess(result);
1400
+ }
1401
+ const resourceUrls = doc.resources;
1402
+ const allResources = [];
1403
+ for (let i = 0; i < resourceUrls.length; i += concurrency) {
1404
+ const batch = resourceUrls.slice(i, i + concurrency);
1405
+ const batchResults = await Promise.all(
1406
+ batch.map((resourceUrl) => queryResource(resourceUrl))
1407
+ );
1408
+ allResources.push(...batchResults);
1409
+ }
1410
+ result.resources = allResources;
1411
+ return mcpSuccess(result);
1412
+ } catch (err) {
1413
+ return mcpError(err, { tool: "discover_resources", url });
1414
+ }
1415
+ }
1416
+ );
1417
+ }
1418
+ function getOrigin(urlString) {
1419
+ try {
1420
+ return new URL(urlString).origin;
1421
+ } catch {
1422
+ return urlString;
1423
+ }
1424
+ }
1425
+ function getHostname(origin) {
1426
+ try {
1427
+ return new URL(origin).hostname;
1428
+ } catch {
1429
+ return origin;
1430
+ }
1431
+ }
1432
+ var DiscoveryDocumentSchema;
1433
+ var init_discover_resources = __esm({
1434
+ "src/server/tools/discover-resources.ts"() {
1435
+ init_log();
1436
+ init_response();
1437
+ init_token();
1438
+ init_networks();
1439
+ DiscoveryDocumentSchema = z$1.object({
1440
+ version: z$1.number().refine((v) => v === 1, { message: "version must be 1" }),
1441
+ resources: z$1.array(z$1.url()),
1442
+ ownershipProofs: z$1.array(z$1.string()).optional(),
1443
+ instructions: z$1.string().optional()
1444
+ });
1445
+ }
1446
+ });
1447
+
1448
+ // src/server/index.ts
1449
+ var server_exports = {};
1450
+ __export(server_exports, {
1451
+ startServer: () => startServer
1452
+ });
1453
+ var startServer;
1454
+ var init_server = __esm({
1455
+ "src/server/index.ts"() {
1456
+ init_fetch_x402_resource();
1457
+ init_auth();
1458
+ init_wallet();
1459
+ init_check_endpoint_schema();
1460
+ init_origins();
1461
+ init_log();
1462
+ init_wallet2();
1463
+ init_discover_resources();
1464
+ startServer = async (flags) => {
1465
+ log.info("Starting x402scan-mcp...");
1466
+ const { account } = await getWallet();
1467
+ const server = new McpServer(
1468
+ {
1469
+ name: "@x402scan/mcp",
1470
+ version: "0.0.1",
1471
+ websiteUrl: "https://x402scan.com/mcp",
1472
+ icons: [{ src: "https://x402scan.com/logo.svg" }]
1473
+ },
1474
+ {
1475
+ capabilities: {
1476
+ resources: {
1477
+ subscribe: true,
1478
+ listChanged: true
1479
+ }
1480
+ }
1481
+ }
1482
+ );
1483
+ const props = {
1484
+ server,
1485
+ account,
1486
+ flags
1487
+ };
1488
+ registerFetchX402ResourceTool(props);
1489
+ registerAuthTools(props);
1490
+ registerWalletTools(props);
1491
+ registerCheckX402EndpointTool(props);
1492
+ registerDiscoveryTools(server);
1493
+ await registerOrigins({ server, flags });
1494
+ const transport = new StdioServerTransport();
1495
+ await server.connect(transport);
1496
+ const shutdown = async () => {
1497
+ log.info("Shutting down...");
1498
+ await server.close();
1499
+ process.exit(0);
1500
+ };
1501
+ process.on("SIGINT", () => void shutdown());
1502
+ process.on("SIGTERM", () => void shutdown());
1503
+ };
1504
+ }
1505
+ });
1506
+
1507
+ // src/install/clients.ts
1508
+ var Clients, clientMetadata;
1509
+ var init_clients = __esm({
1510
+ "src/install/clients.ts"() {
1511
+ Clients = /* @__PURE__ */ ((Clients2) => {
1512
+ Clients2["ClaudeCode"] = "claude-code";
1513
+ Clients2["Cursor"] = "cursor";
1514
+ Clients2["Claude"] = "claude";
1515
+ Clients2["Codex"] = "codex";
1516
+ Clients2["Vscode"] = "vscode";
1517
+ Clients2["Cline"] = "cline";
1518
+ Clients2["RooCline"] = "roo-cline";
1519
+ Clients2["Windsurf"] = "windsurf";
1520
+ Clients2["Warp"] = "warp";
1521
+ Clients2["GeminiCli"] = "gemini-cli";
1522
+ Clients2["Goose"] = "goose";
1523
+ Clients2["Zed"] = "zed";
1524
+ Clients2["Opencode"] = "opencode";
1525
+ return Clients2;
1526
+ })(Clients || {});
1527
+ clientMetadata = {
1528
+ ["claude-code" /* ClaudeCode */]: {
1529
+ name: "Claude Code",
1530
+ description: "Claude Code is a code editor that uses the Claude API.",
1531
+ website: "https://claude.com"
1532
+ },
1533
+ ["cursor" /* Cursor */]: {
1534
+ name: "Cursor",
1535
+ description: "Cursor is a code editor that uses the Cursor API.",
1536
+ website: "https://cursor.com"
1537
+ },
1538
+ ["claude" /* Claude */]: {
1539
+ name: "Claude",
1540
+ description: "Claude is a code editor that uses the Claude API.",
1541
+ website: "https://claude.com"
1542
+ },
1543
+ ["codex" /* Codex */]: {
1544
+ name: "Codex",
1545
+ description: "Codex is a code editor that uses the Codex API.",
1546
+ website: "https://codex.com"
1547
+ },
1548
+ ["vscode" /* Vscode */]: {
1549
+ name: "VSCode",
1550
+ description: "VSCode is a code editor that uses the VSCode API.",
1551
+ website: "https://vscode.com"
1552
+ },
1553
+ ["cline" /* Cline */]: {
1554
+ name: "Cline",
1555
+ description: "Cline is a code editor that uses the Cline API.",
1556
+ website: "https://cline.com"
1557
+ },
1558
+ ["roo-cline" /* RooCline */]: {
1559
+ name: "RooCline",
1560
+ description: "RooCline is a code editor that uses the RooCline API.",
1561
+ website: "https://roo-cline.com"
1562
+ },
1563
+ ["windsurf" /* Windsurf */]: {
1564
+ name: "Windsurf",
1565
+ description: "Windsurf is a code editor that uses the Windsurf API.",
1566
+ website: "https://windsurf.com"
1567
+ },
1568
+ ["warp" /* Warp */]: {
1569
+ name: "Warp",
1570
+ description: "Warp is a code editor that uses the Warp API.",
1571
+ website: "https://warp.com"
1572
+ },
1573
+ ["gemini-cli" /* GeminiCli */]: {
1574
+ name: "Gemini CLI",
1575
+ description: "Gemini CLI is a code editor that uses the Gemini CLI API.",
1576
+ website: "https://gemini-cli.com"
1577
+ },
1578
+ ["goose" /* Goose */]: {
1579
+ name: "Goose",
1580
+ description: "Goose is a code editor that uses the Goose API.",
1581
+ website: "https://goose.com"
1582
+ },
1583
+ ["zed" /* Zed */]: {
1584
+ name: "Zed",
1585
+ description: "Zed is a code editor that uses the Zed API.",
1586
+ website: "https://zed.com"
1587
+ },
1588
+ ["opencode" /* Opencode */]: {
1589
+ name: "Opencode",
1590
+ description: "Opencode is a code editor that uses the Opencode API.",
1591
+ website: "https://opencode.com"
1592
+ }
1593
+ };
1594
+ }
1595
+ });
1596
+ var getClient;
1597
+ var init_get_client = __esm({
1598
+ "src/install/1-get-client/index.ts"() {
1599
+ init_clients();
1600
+ getClient = async ({ client: flagClient }) => {
1601
+ const parsedClient = z.enum(Clients).safeParse(flagClient);
1602
+ if (parsedClient.success) {
1603
+ return parsedClient.data;
1604
+ }
1605
+ if (flagClient) {
1606
+ log$1.error(`${flagClient} is not a valid client. Please select a client`);
1607
+ }
1608
+ const client = await select({
1609
+ message: "Where would you like to install the x402scan MCP server?",
1610
+ options: Object.values(Clients).map((client2) => {
1611
+ const metadata = clientMetadata[client2];
1612
+ return {
1613
+ label: metadata.name,
1614
+ value: client2
1615
+ };
1616
+ }),
1617
+ maxItems: 7
1618
+ });
1619
+ const parsedClientSelection = z.enum(Clients).safeParse(client);
1620
+ if (parsedClientSelection.success) {
1621
+ return parsedClientSelection.data;
1622
+ }
1623
+ outro(chalk3.bold.red("No MCP client selected"));
1624
+ process.exit(0);
1625
+ };
1626
+ }
1627
+ });
1628
+ var Platforms, getPlatformPath;
1629
+ var init_platforms = __esm({
1630
+ "src/install/2-add-server/lib/platforms.ts"() {
1631
+ Platforms = /* @__PURE__ */ ((Platforms2) => {
1632
+ Platforms2["Windows"] = "win32";
1633
+ Platforms2["MacOS"] = "darwin";
1634
+ Platforms2["Linux"] = "linux";
1635
+ return Platforms2;
1636
+ })(Platforms || {});
1637
+ getPlatformPath = () => {
1638
+ const platform = z.enum(Platforms).safeParse(process2.platform);
1639
+ if (!platform.success) {
1640
+ throw new Error(`Invalid platform: ${process2.platform}`);
1641
+ }
1642
+ const homeDir = os.homedir();
1643
+ switch (platform.data) {
1644
+ case "win32" /* Windows */:
1645
+ return {
1646
+ baseDir: process2.env.APPDATA ?? path2.join(homeDir, "AppData", "Roaming"),
1647
+ vscodePath: path2.join("Code", "User")
1648
+ };
1649
+ case "darwin" /* MacOS */:
1650
+ return {
1651
+ baseDir: path2.join(homeDir, "Library", "Application Support"),
1652
+ vscodePath: path2.join("Code", "User")
1653
+ };
1654
+ case "linux" /* Linux */:
1655
+ return {
1656
+ baseDir: process2.env.XDG_CONFIG_HOME ?? path2.join(homeDir, ".config"),
1657
+ vscodePath: path2.join("Code/User")
1658
+ };
1659
+ default:
1660
+ throw new Error(`Invalid platform: ${process2.platform}`);
1661
+ }
1662
+ };
1663
+ }
1664
+ });
1665
+ var parseClientConfig, serializeClientConfig, stringifyObject;
1666
+ var init_file_types = __esm({
1667
+ "src/install/2-add-server/lib/file-types.ts"() {
1668
+ parseClientConfig = ({ format: format2, path: path3 }) => {
1669
+ const fileContent = fs4.readFileSync(path3, "utf8");
1670
+ let config = {};
1671
+ if (format2 === "yaml" /* YAML */) {
1672
+ config = yaml.load(fileContent);
1673
+ } else if (format2 === "toml" /* TOML */) {
1674
+ config = TOML.parse(fileContent);
1675
+ } else if (path3.endsWith(".jsonc")) {
1676
+ config = jsonc.parse(fileContent);
1677
+ } else {
1678
+ config = JSON.parse(fileContent);
1679
+ }
1680
+ return {
1681
+ config,
1682
+ fileContent
1683
+ };
1684
+ };
1685
+ serializeClientConfig = ({ format: format2, path: path3 }, config, originalContent) => {
1686
+ if (format2 === "yaml" /* YAML */) {
1687
+ return yaml.dump(config, {
1688
+ indent: 2,
1689
+ lineWidth: -1,
1690
+ noRefs: true
1691
+ });
1692
+ }
1693
+ if (format2 === "toml" /* TOML */) {
1694
+ return TOML.stringify(config);
1695
+ }
1696
+ if (path3.endsWith(".jsonc") && originalContent) {
1697
+ try {
1698
+ const editedContent = originalContent;
1699
+ const modifications = [];
1700
+ for (const key of Object.keys(config)) {
1701
+ const path4 = [key];
1702
+ const edits = jsonc.modify(editedContent, path4, config[key], {
1703
+ formattingOptions: { tabSize: 2, insertSpaces: true }
1704
+ });
1705
+ modifications.push(...edits);
1706
+ }
1707
+ return jsonc.applyEdits(originalContent, modifications);
1708
+ } catch (error) {
1709
+ console.log(
1710
+ `Error applying JSONC edits: ${error instanceof Error ? error.message : String(error)}`
1711
+ );
1712
+ console.log("Falling back to JSON.stringify (comments will be lost)");
1713
+ return JSON.stringify(config, null, 2);
1714
+ }
1715
+ }
1716
+ return JSON.stringify(config, null, 2);
1717
+ };
1718
+ stringifyObject = (config, format2) => {
1719
+ if (format2 === "yaml" /* YAML */) {
1720
+ return yaml.dump(config, {
1721
+ indent: 2,
1722
+ lineWidth: -1,
1723
+ noRefs: true
1724
+ });
1725
+ }
1726
+ if (format2 === "toml" /* TOML */) {
1727
+ return TOML.stringify(config);
1728
+ }
1729
+ return JSON.stringify(config, null, 2);
1730
+ };
1731
+ }
1732
+ });
1733
+ var getClientConfigFile;
1734
+ var init_client_config_file = __esm({
1735
+ "src/install/2-add-server/lib/client-config-file.ts"() {
1736
+ init_platforms();
1737
+ init_log();
1738
+ init_clients();
1739
+ init_file_types();
1740
+ getClientConfigFile = (client) => {
1741
+ const homeDir = os.homedir();
1742
+ const { baseDir, vscodePath } = getPlatformPath();
1743
+ switch (client) {
1744
+ case "claude" /* Claude */:
1745
+ return {
1746
+ path: path2.join(baseDir, "Claude", "claude_desktop_config.json"),
1747
+ configKey: "mcpServers",
1748
+ format: "json" /* JSON */
1749
+ };
1750
+ case "cline" /* Cline */:
1751
+ return {
1752
+ path: path2.join(
1753
+ baseDir,
1754
+ vscodePath,
1755
+ "globalStorage",
1756
+ "saoudrizwan.claude-dev",
1757
+ "settings",
1758
+ "cline_mcp_settings.json"
1759
+ ),
1760
+ configKey: "mcpServers",
1761
+ format: "json" /* JSON */
1762
+ };
1763
+ case "roo-cline" /* RooCline */:
1764
+ return {
1765
+ path: path2.join(
1766
+ baseDir,
1767
+ vscodePath,
1768
+ "globalStorage",
1769
+ "rooveterinaryinc.roo-cline",
1770
+ "settings",
1771
+ "mcp_settings.json"
1772
+ ),
1773
+ configKey: "mcpServers",
1774
+ format: "json" /* JSON */
1775
+ };
1776
+ case "windsurf" /* Windsurf */:
1777
+ return {
1778
+ path: path2.join(homeDir, ".codeium", "windsurf", "mcp_config.json"),
1779
+ configKey: "mcpServers",
1780
+ format: "json" /* JSON */
1781
+ };
1782
+ case "cursor" /* Cursor */:
1783
+ return {
1784
+ path: path2.join(homeDir, ".cursor", "mcp.json"),
1785
+ configKey: "mcpServers",
1786
+ format: "json" /* JSON */
1787
+ };
1788
+ case "warp" /* Warp */:
1789
+ return {
1790
+ path: "no-local-config",
1791
+ // it's okay this isn't a real path, we never use it
1792
+ configKey: "mcpServers",
1793
+ format: "json" /* JSON */
1794
+ };
1795
+ case "gemini-cli" /* GeminiCli */:
1796
+ return {
1797
+ path: path2.join(homeDir, ".gemini", "settings.json"),
1798
+ configKey: "mcpServers",
1799
+ format: "json" /* JSON */
1800
+ };
1801
+ case "vscode" /* Vscode */:
1802
+ return {
1803
+ path: path2.join(baseDir, vscodePath, "mcp.json"),
1804
+ configKey: "mcpServers",
1805
+ format: "json" /* JSON */
1806
+ };
1807
+ case "claude-code" /* ClaudeCode */:
1808
+ return {
1809
+ path: path2.join(homeDir, ".claude.json"),
1810
+ configKey: "mcpServers",
1811
+ format: "json" /* JSON */
1812
+ };
1813
+ case "goose" /* Goose */:
1814
+ return {
1815
+ path: path2.join(homeDir, ".config", "goose", "config.yaml"),
1816
+ configKey: "extensions",
1817
+ format: "yaml" /* YAML */
1818
+ };
1819
+ case "zed" /* Zed */:
1820
+ return {
1821
+ path: process2.platform === "win32" ? path2.join(
1822
+ process2.env.APPDATA ?? path2.join(homeDir, "AppData", "Roaming"),
1823
+ "Zed",
1824
+ "settings.json"
1825
+ ) : path2.join(homeDir, ".config", "zed", "settings.json"),
1826
+ configKey: "context_servers",
1827
+ format: "json" /* JSON */
1828
+ };
1829
+ case "codex" /* Codex */:
1830
+ return {
1831
+ path: path2.join(
1832
+ process2.env.CODEX_HOME ?? path2.join(homeDir, ".codex"),
1833
+ "config.toml"
1834
+ ),
1835
+ configKey: "mcp_servers",
1836
+ format: "toml" /* TOML */
1837
+ };
1838
+ case "opencode" /* Opencode */: {
1839
+ const jsonPath = path2.join(
1840
+ homeDir,
1841
+ ".config",
1842
+ "opencode",
1843
+ "opencode.json"
1844
+ );
1845
+ const jsoncPath = jsonPath.replace(".json", ".jsonc");
1846
+ if (fs4.existsSync(jsoncPath)) {
1847
+ log.info(`Found .jsonc file for OpenCode, using: ${jsoncPath}`);
1848
+ return {
1849
+ path: jsoncPath,
1850
+ configKey: "mcp",
1851
+ format: "json" /* JSON */
1852
+ };
1853
+ }
1854
+ return {
1855
+ path: jsonPath,
1856
+ configKey: "mcp",
1857
+ format: "json" /* JSON */
1858
+ };
1859
+ }
1860
+ default:
1861
+ throw new Error(`Unknown client: ${String(client)}`);
1862
+ }
1863
+ };
1864
+ }
1865
+ });
1866
+
1867
+ // src/install/2-add-server/lib/nested-values.ts
1868
+ var getNestedValue, setNestedValue;
1869
+ var init_nested_values = __esm({
1870
+ "src/install/2-add-server/lib/nested-values.ts"() {
1871
+ getNestedValue = (obj, path3) => {
1872
+ const keys = path3.split(".");
1873
+ let current = obj;
1874
+ for (const key of keys) {
1875
+ if (current && typeof current === "object" && key in current) {
1876
+ current = current[key];
1877
+ } else {
1878
+ return void 0;
1879
+ }
1880
+ }
1881
+ return current;
1882
+ };
1883
+ setNestedValue = (obj, path3, value) => {
1884
+ const keys = path3.split(".");
1885
+ const lastKey = keys.pop();
1886
+ if (!lastKey) return;
1887
+ const target = keys.reduce((current, key) => {
1888
+ current[key] ??= {};
1889
+ return current[key];
1890
+ }, obj);
1891
+ target[lastKey] = value;
1892
+ };
1893
+ }
1894
+ });
1895
+
1896
+ // src/install/2-add-server/lib/index.ts
1897
+ var init_lib2 = __esm({
1898
+ "src/install/2-add-server/lib/index.ts"() {
1899
+ init_client_config_file();
1900
+ init_file_types();
1901
+ init_nested_values();
1902
+ init_platforms();
1903
+ }
1904
+ });
1905
+ async function addServer(client, globalFlags) {
1906
+ const { serverName, command, args } = getMcpConfig(globalFlags);
1907
+ if (client === "warp" /* Warp */) {
1908
+ log$1.info(
1909
+ chalk3.bold.yellow("Warp requires a manual installation through their UI.")
1910
+ );
1911
+ log$1.message(
1912
+ "Please copy the following configuration object and add it to your Warp MCP config:"
1913
+ );
1914
+ console.log();
1915
+ console.log(
1916
+ JSON.stringify(
1917
+ {
1918
+ [serverName]: {
1919
+ command,
1920
+ args,
1921
+ working_directory: null,
1922
+ start_on_launch: true
1923
+ }
1924
+ },
1925
+ null,
1926
+ 2
1927
+ )
1928
+ );
1929
+ console.log();
1930
+ log$1.message(
1931
+ `Read Warp's documentation at https://docs.warp.dev/knowledge-and-collaboration/mcp`
1932
+ );
1933
+ const addedToWarp = await confirm({
1934
+ message: "Did you add the MCP server to your Warp config?"
1935
+ });
1936
+ if (!addedToWarp) {
1937
+ throw new Error("Warp MCP server not added");
1938
+ }
1939
+ }
1940
+ const clientFileTarget = getClientConfigFile(client);
1941
+ const { name } = clientMetadata[client];
1942
+ try {
1943
+ let config = {};
1944
+ let content = void 0;
1945
+ log.info(`Checking if config file exists at: ${clientFileTarget.path}`);
1946
+ if (!fs4.existsSync(clientFileTarget.path)) {
1947
+ log.info("Config file not found, creating default empty config");
1948
+ setNestedValue(config, clientFileTarget.configKey, {});
1949
+ log.info("Config created successfully");
1950
+ await wait({
1951
+ startText: "Locating config file",
1952
+ stopText: `No config found, creating default empty config`,
1953
+ ms: 1e3
1954
+ });
1955
+ } else {
1956
+ log.info("Config file found, reading config file content");
1957
+ const { config: rawConfig, fileContent } = parseClientConfig(clientFileTarget);
1958
+ config = rawConfig;
1959
+ content = fileContent;
1960
+ const existingValue = getNestedValue(
1961
+ rawConfig,
1962
+ clientFileTarget.configKey
1963
+ );
1964
+ if (!existingValue) {
1965
+ setNestedValue(rawConfig, clientFileTarget.configKey, {});
1966
+ }
1967
+ log.info(
1968
+ `Config loaded successfully: ${JSON.stringify(rawConfig, null, 2)}`
1969
+ );
1970
+ await wait({
1971
+ startText: `Locating config file`,
1972
+ stopText: `Config loaded from ${clientFileTarget.path}`,
1973
+ ms: 1e3
1974
+ });
1975
+ }
1976
+ const servers = getNestedValue(config, clientFileTarget.configKey);
1977
+ if (!servers || typeof servers !== "object") {
1978
+ log.error(`Invalid ${clientFileTarget.configKey} structure in config`);
1979
+ log$1.error(
1980
+ chalk3.bold.red(
1981
+ `Invalid ${clientFileTarget.configKey} structure in config`
1982
+ )
1983
+ );
1984
+ throw new Error(`Invalid ${clientFileTarget.configKey} structure`);
1985
+ }
1986
+ if (client === "goose" /* Goose */) {
1987
+ servers[serverName] = {
1988
+ name: serverName,
1989
+ cmd: command,
1990
+ args,
1991
+ enabled: true,
1992
+ envs: {},
1993
+ type: "stdio",
1994
+ timeout: 300
1995
+ };
1996
+ } else if (client === "zed" /* Zed */) {
1997
+ servers[serverName] = {
1998
+ source: "custom",
1999
+ command,
2000
+ args,
2001
+ env: {}
2002
+ };
2003
+ } else if (client === "opencode" /* Opencode */) {
2004
+ servers[serverName] = {
2005
+ type: "local",
2006
+ command,
2007
+ args,
2008
+ enabled: true,
2009
+ environment: {}
2010
+ };
2011
+ } else {
2012
+ servers[serverName] = {
2013
+ command,
2014
+ args
2015
+ };
2016
+ }
2017
+ await new Promise((resolve) => setTimeout(resolve, 1e3));
2018
+ log$1.step(`The following will be added to ${chalk3.bold.underline(clientFileTarget.path)}`);
2019
+ const configStr = formatDiffByFormat(
2020
+ {
2021
+ [clientFileTarget.configKey]: {
2022
+ [serverName]: servers[serverName]
2023
+ }
2024
+ },
2025
+ clientFileTarget.format
2026
+ );
2027
+ await stream.message(
2028
+ (async function* () {
2029
+ for (const num of Array.from(
2030
+ { length: configStr.length },
2031
+ (_, i) => i
2032
+ )) {
2033
+ const char = configStr[num];
2034
+ yield char;
2035
+ if (!["\n", " ", "\u2500", "\u256E", "\u256D", "\u2570", "\u256F", "\u2502"].includes(char)) {
2036
+ await new Promise((resolve) => setTimeout(resolve, 5));
2037
+ } else {
2038
+ await new Promise((resolve) => setTimeout(resolve, 2));
2039
+ }
2040
+ }
2041
+ })()
2042
+ );
2043
+ await new Promise((resolve) => setTimeout(resolve, 1e3));
2044
+ const isConfirmed = await confirm({
2045
+ message: `Would you like to proceed?`,
2046
+ active: "Install MCP",
2047
+ inactive: "Cancel"
2048
+ });
2049
+ if (isConfirmed !== true) {
2050
+ outro(chalk3.bold.red("Installation cancelled"));
2051
+ process.exit(0);
2052
+ }
2053
+ const configContent = serializeClientConfig(
2054
+ clientFileTarget,
2055
+ config,
2056
+ content
2057
+ );
2058
+ fs4.writeFileSync(clientFileTarget.path, configContent);
2059
+ log$1.success(chalk3.bold.green(`Added x402scan MCP to ${name}`));
2060
+ } catch (e) {
2061
+ log$1.error(chalk3.bold.red(`Error adding x402scan MCP to ${name}`));
2062
+ throw e;
2063
+ }
2064
+ }
2065
+ var getMcpConfig, formatDiffByFormat;
2066
+ var init_add_server = __esm({
2067
+ "src/install/2-add-server/index.ts"() {
2068
+ init_log();
2069
+ init_clients();
2070
+ init_lib2();
2071
+ init_wait();
2072
+ getMcpConfig = (globalFlags) => {
2073
+ if (globalFlags.dev) {
2074
+ return {
2075
+ serverName: "x402",
2076
+ command: "node",
2077
+ args: [`${process.cwd()}/dist/index.js`, "--dev"]
2078
+ };
2079
+ }
2080
+ return {
2081
+ serverName: "x402",
2082
+ command: "npx",
2083
+ args: ["-y", "@x402scan/mcp@latest"]
2084
+ };
2085
+ };
2086
+ formatDiffByFormat = (obj, format2) => {
2087
+ const str = stringifyObject(obj, format2);
2088
+ switch (format2) {
2089
+ case "json" /* JSON */: {
2090
+ const numLines = str.split("\n").length;
2091
+ return str.split("\n").map((line, index) => {
2092
+ const diffLines = [0, 1, numLines - 2, numLines - 1];
2093
+ const isDiffLine = !diffLines.includes(index);
2094
+ if (isDiffLine) {
2095
+ return `${chalk3.bold.green(`+ ${line.slice(2)}`)}`;
2096
+ }
2097
+ return line;
2098
+ }).join("\n");
2099
+ }
2100
+ case "yaml" /* YAML */: {
2101
+ return str.split("\n").map((line, index) => {
2102
+ const diffLines = [0, 1, str.length - 2, str.length - 1];
2103
+ const isDiffLine = !diffLines.includes(index);
2104
+ if (isDiffLine) {
2105
+ return `${chalk3.bold.green(`+ ${line.slice(2)}`)}`;
2106
+ }
2107
+ return line;
2108
+ }).join("\n");
2109
+ }
2110
+ case "toml" /* TOML */: {
2111
+ return str.split("\n").filter((line) => line.trim() !== "").map((line) => {
2112
+ return `${chalk3.bold.green(`+ ${line.trim()}`)}`;
2113
+ }).join("\n");
2114
+ }
2115
+ }
2116
+ };
2117
+ }
2118
+ });
2119
+ var addFunds;
2120
+ var init_add_funds = __esm({
2121
+ "src/install/3-add-funds/index.ts"() {
2122
+ init_balance();
2123
+ init_deposit();
2124
+ init_wait();
2125
+ addFunds = async ({ flags, address, isNew }) => {
2126
+ if (isNew) {
2127
+ await new Promise((resolve) => setTimeout(resolve, 1e3));
2128
+ log$1.info("To use paid API tools, you will need USDC in your wallet.");
2129
+ await promptDeposit(address, flags);
2130
+ } else {
2131
+ const balance = await getUSDCBalance({ address });
2132
+ await wait({
2133
+ startText: "Checking balance...",
2134
+ stopText: `Balance: ${chalk3.bold(`${balance} USDC`)} `,
2135
+ ms: 1e3
2136
+ });
2137
+ if (balance < 1) {
2138
+ log$1.warning(
2139
+ chalk3.bold(
2140
+ `Your balance is low (${balance} USDC). Consider topping up.`
2141
+ )
2142
+ );
2143
+ await promptDeposit(address, flags);
2144
+ }
2145
+ }
2146
+ };
2147
+ }
2148
+ });
2149
+
2150
+ // src/install/index.ts
2151
+ var install_exports = {};
2152
+ __export(install_exports, {
2153
+ installMcpServer: () => installMcpServer
2154
+ });
2155
+ var installMcpServer;
2156
+ var init_install = __esm({
2157
+ "src/install/index.ts"() {
2158
+ init_wallet2();
2159
+ init_get_client();
2160
+ init_add_server();
2161
+ init_add_funds();
2162
+ installMcpServer = async (flags) => {
2163
+ const {
2164
+ account: { address },
2165
+ isNew
2166
+ } = await getWallet();
2167
+ intro(chalk3.green.bold(`Install x402scan MCP`));
2168
+ const client = await getClient(flags);
2169
+ await addServer(client, flags);
2170
+ await addFunds({ flags, address, isNew });
2171
+ outro(chalk3.bold.green("Your x402scan MCP server is ready to use!"));
2172
+ };
2173
+ }
2174
+ });
2175
+
2176
+ // src/fund/index.ts
2177
+ var fund_exports = {};
2178
+ __export(fund_exports, {
2179
+ fundMcpServer: () => fundMcpServer
2180
+ });
2181
+ var fundMcpServer;
2182
+ var init_fund = __esm({
2183
+ "src/fund/index.ts"() {
2184
+ init_wallet2();
2185
+ init_deposit();
2186
+ fundMcpServer = async (flags) => {
2187
+ intro(chalk3.bold(`Fund ${chalk3.hex("#2563eb")("x402scan MCP")}`));
2188
+ const {
2189
+ account: { address }
2190
+ } = await getWallet();
2191
+ await promptDeposit(address, flags);
2192
+ outro(chalk3.bold.green("Your x402scan MCP server is funded!"));
2193
+ };
2194
+ }
2195
+ });
6
2196
  void yargs(hideBin(process.argv)).scriptName("@x402scan/mcp").option("dev", {
7
2197
  type: "boolean",
8
2198
  description: "Enable dev mode",
@@ -12,8 +2202,8 @@ void yargs(hideBin(process.argv)).scriptName("@x402scan/mcp").option("dev", {
12
2202
  "Start the MCP server",
13
2203
  (yargs2) => yargs2,
14
2204
  async (args) => {
15
- const { startServer } = await import("./server-CD4OSIVL.js");
16
- await startServer(args);
2205
+ const { startServer: startServer2 } = await Promise.resolve().then(() => (init_server(), server_exports));
2206
+ await startServer2(args);
17
2207
  }
18
2208
  ).command(
19
2209
  "install",
@@ -24,18 +2214,20 @@ void yargs(hideBin(process.argv)).scriptName("@x402scan/mcp").option("dev", {
24
2214
  required: false
25
2215
  }),
26
2216
  async (args) => {
27
- const { installMcpServer } = await import("./install-QDUD2KOS.js");
28
- await installMcpServer({ ...args });
2217
+ const { installMcpServer: installMcpServer2 } = await Promise.resolve().then(() => (init_install(), install_exports));
2218
+ await installMcpServer2({ ...args });
29
2219
  }
30
2220
  ).command(
31
2221
  "fund",
32
2222
  "Open the funding page",
33
2223
  (yargs2) => yargs2,
34
2224
  async (args) => {
35
- const { fundMcpServer } = await import("./fund-743A34XB.js");
36
- await fundMcpServer(args);
2225
+ const { fundMcpServer: fundMcpServer2 } = await Promise.resolve().then(() => (init_fund(), fund_exports));
2226
+ await fundMcpServer2(args);
37
2227
  }
38
2228
  ).strict().demandCommand(0, 1, "", "Too many commands provided").help().parseAsync().catch((err) => {
39
2229
  console.error("Fatal:", err);
40
2230
  process.exit(1);
41
2231
  });
2232
+ //# sourceMappingURL=index.js.map
2233
+ //# sourceMappingURL=index.js.map