bsv-mcp 0.2.0 → 0.2.7

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.
Files changed (52) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/dist/index.js +87272 -70728
  3. package/index.ts +409 -22
  4. package/package.json +28 -24
  5. package/tools/a2b/discover.ts +13 -13
  6. package/tools/bap/friend.ts +2 -3
  7. package/tools/bap/generate.ts +7 -9
  8. package/tools/bap/getCurrentAddress.ts +1 -8
  9. package/tools/bap/getId.ts +3 -3
  10. package/tools/bsocial/bmapFollow.ts +3 -7
  11. package/tools/bsocial/bmapLikes.ts +3 -3
  12. package/tools/bsocial/bmapReadPosts.ts +3 -3
  13. package/tools/bsocial/createPost.ts +3 -3
  14. package/tools/bsocial/readPosts.ts +3 -3
  15. package/tools/bsv/decodeTransaction.ts +2 -5
  16. package/tools/bsv/explore.ts +2 -3
  17. package/tools/bsv/getPrice.ts +1 -9
  18. package/tools/bsv/token.ts +14 -26
  19. package/tools/index.ts +0 -13
  20. package/tools/mnee/getBalance.ts +2 -4
  21. package/tools/mnee/parseTx.ts +3 -3
  22. package/tools/mnee/sendMnee.ts +5 -5
  23. package/tools/ordinals/getInscription.ts +2 -3
  24. package/tools/ordinals/getTokenByIdOrTicker.ts +6 -3
  25. package/tools/ordinals/marketListings.ts +2 -18
  26. package/tools/ordinals/marketSales.ts +2 -4
  27. package/tools/ordinals/searchInscriptions.ts +2 -4
  28. package/tools/utils/index.ts +14 -16
  29. package/tools/wallet/a2bPublishAgent.ts +18 -27
  30. package/tools/wallet/a2bPublishMcp.ts +17 -22
  31. package/tools/wallet/createOrdinals.ts +11 -20
  32. package/tools/wallet/fetchPaymentUtxos.ts +9 -1
  33. package/tools/wallet/gatherCollectionInfo.ts +9 -13
  34. package/tools/wallet/getAddress.ts +1 -9
  35. package/tools/wallet/getBalance.ts +2 -14
  36. package/tools/wallet/getBalanceDroplet.ts +2 -13
  37. package/tools/wallet/getPublicKey.ts +3 -16
  38. package/tools/wallet/mintCollection.ts +17 -22
  39. package/tools/wallet/purchaseListing.ts +19 -29
  40. package/tools/wallet/refreshUtxos.ts +2 -14
  41. package/tools/wallet/sendOrdinals.ts +11 -20
  42. package/tools/wallet/sendToAddress.ts +2 -22
  43. package/tools/wallet/setupDroplet.ts +7 -18
  44. package/tools/wallet/tools.ts +10 -80
  45. package/tools/wallet/transferOrdToken.ts +12 -20
  46. package/vite.config.ts +15 -0
  47. package/tools/bigblocks/components.ts +0 -304
  48. package/tools/bigblocks/docs.ts +0 -488
  49. package/tools/bigblocks/examples.ts +0 -527
  50. package/tools/bigblocks/generator.ts +0 -485
  51. package/tools/bigblocks/index.ts +0 -23
  52. package/tools/bsocial/bigblocksApiClient.ts +0 -189
package/index.ts CHANGED
@@ -1,14 +1,38 @@
1
1
  #!/usr/bin/env bun
2
+
3
+ // Redirect console.log/warn/info/debug to stderr in stdio mode.
4
+ // MCP stdio transport uses stdout exclusively for JSON-RPC messages —
5
+ // any stray stdout output corrupts the protocol. Must run before any import.
6
+ const _isStdio =
7
+ process.argv.includes("--stdio") ||
8
+ process.env.TRANSPORT?.toLowerCase() === "stdio";
9
+ if (_isStdio) {
10
+ const _err = console.error.bind(console);
11
+ console.log = (...a: unknown[]) => _err("[log]", ...a);
12
+ console.warn = (...a: unknown[]) => _err("[warn]", ...a);
13
+ console.info = (...a: unknown[]) => _err("[info]", ...a);
14
+ console.debug = (...a: unknown[]) => _err("[debug]", ...a);
15
+ }
16
+
17
+ import { readFile } from "node:fs/promises";
2
18
  import os from "node:os";
3
19
  import path from "node:path";
20
+ import { dirname, join } from "node:path";
21
+ import { fileURLToPath } from "node:url";
4
22
  import { PrivateKey } from "@bsv/sdk";
23
+ import {
24
+ RESOURCE_MIME_TYPE,
25
+ registerAppResource,
26
+ registerAppTool,
27
+ } from "@modelcontextprotocol/ext-apps/server";
5
28
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
6
29
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
30
+ import { z } from "zod";
7
31
  import packageJson from "./package.json";
8
32
  import { registerAllPrompts } from "./prompts/index.ts";
9
33
  import { registerResources } from "./resources/resources.ts";
34
+ import { getBsvPriceWithCache } from "./tools/bsv/getPrice.ts";
10
35
  import { registerAllTools, type ToolsConfig } from "./tools/index.ts";
11
- import { registerMneeTools } from "./tools/mnee/index.ts";
12
36
  import { IntegratedWallet } from "./tools/wallet/integratedWallet.ts";
13
37
  import { Wallet } from "./tools/wallet/wallet.ts";
14
38
  import { BunSSEServerTransport } from "./transports/sse.ts";
@@ -41,13 +65,14 @@ const CONFIG = {
41
65
  loadA2bTools: process.env.ENABLE_A2B_TOOLS === "true",
42
66
  loadBapTools: process.env.DISABLE_BAP_TOOLS !== "true",
43
67
  loadBsocialTools: process.env.DISABLE_BSOCIAL_TOOLS !== "true",
44
- loadBigBlocksTools: process.env.DISABLE_BIGBLOCKS_TOOLS !== "true",
45
-
46
68
  // Transaction broadcasting control
47
69
  disableBroadcasting: process.env.DISABLE_BROADCASTING === "true",
48
70
 
49
71
  // --- Transport Mode ---
50
- transportMode: process.env.TRANSPORT?.toLowerCase() || "http", // 'stdio' or 'http'/default
72
+ // --stdio CLI flag takes precedence over TRANSPORT env var (matches neighborhood plugin pattern)
73
+ transportMode: process.argv.includes("--stdio")
74
+ ? "stdio"
75
+ : (process.env.TRANSPORT?.toLowerCase() || "http"), // 'stdio' or 'http'/default
51
76
  port: Number.parseInt(process.env.PORT || "3000", 10),
52
77
 
53
78
  // --- Droplet API Configuration ---
@@ -209,6 +234,378 @@ async function initializeKeys(): Promise<{
209
234
  return { payPk, identityPk: undefined, xprv: undefined, source: "generated" };
210
235
  }
211
236
 
237
+ // --- MCP App Tools & Resource ---
238
+ const APP_RESOURCE_URI = "ui://bsv-mcp/app.html";
239
+ const __appDirname = dirname(fileURLToPath(import.meta.url));
240
+
241
+ function registerMcpAppTools(server: McpServer, wallet?: Wallet) {
242
+ // Primary dashboard tool — model calls this to open the UI
243
+ registerAppTool(
244
+ server,
245
+ "bsv_dashboard",
246
+ {
247
+ title: "BSV Dashboard",
248
+ description:
249
+ "Interactive BSV dashboard with Explorer, Wallet, and Ordinals tabs. Use this for any BSV-related query that benefits from visual display.",
250
+ inputSchema: {},
251
+ _meta: {
252
+ ui: { resourceUri: APP_RESOURCE_URI },
253
+ },
254
+ },
255
+ async () => {
256
+ return {
257
+ content: [{ type: "text" as const, text: "BSV Dashboard opened" }],
258
+ structuredContent: { view: "dashboard", ready: true },
259
+ };
260
+ },
261
+ );
262
+
263
+ // App-only: fetch explorer data (price, chain info, tx decode, address lookup)
264
+ registerAppTool(
265
+ server,
266
+ "app_explorer_data",
267
+ {
268
+ title: "Explorer Data",
269
+ description:
270
+ "App-only: fetches BSV price, chain info, decodes transactions, and looks up addresses.",
271
+ inputSchema: {
272
+ txid: z.string().optional().describe("Transaction ID to decode"),
273
+ address: z
274
+ .string()
275
+ .optional()
276
+ .describe("Address to look up balance/history"),
277
+ },
278
+ _meta: {
279
+ ui: { resourceUri: APP_RESOURCE_URI, visibility: ["app"] },
280
+ },
281
+ },
282
+ async (args) => {
283
+ const { txid, address } = args as {
284
+ txid?: string;
285
+ address?: string;
286
+ };
287
+
288
+ // If txid provided, decode transaction
289
+ if (txid) {
290
+ try {
291
+ const res = await fetch(
292
+ `https://junglebus.gorillapool.io/v1/transaction/get/${txid}`,
293
+ );
294
+ if (!res.ok)
295
+ throw new Error(`Transaction not found: ${res.status}`);
296
+ const jbData = (await res.json()) as Record<string, unknown>;
297
+
298
+ const { Transaction, Utils } = await import("@bsv/sdk");
299
+ const rawTx = jbData.transaction as string;
300
+ const isBase64 = /^[A-Za-z0-9+/=]+$/.test(rawTx);
301
+ const txBytes = isBase64
302
+ ? Utils.toArray(rawTx, "base64")
303
+ : Utils.toArray(rawTx, "hex");
304
+ const tx = Transaction.fromBinary(txBytes);
305
+
306
+ return {
307
+ content: [
308
+ { type: "text" as const, text: `Decoded transaction ${txid}` },
309
+ ],
310
+ structuredContent: {
311
+ transaction: {
312
+ txid,
313
+ version: tx.version,
314
+ lockTime: tx.lockTime,
315
+ size: tx.toBinary().length,
316
+ inputs: tx.inputs.map((inp) => ({
317
+ txid: inp.sourceTXID,
318
+ vout: inp.sourceOutputIndex,
319
+ script: inp.unlockingScript?.toHex() || "",
320
+ })),
321
+ outputs: tx.outputs.map((out, i) => ({
322
+ n: i,
323
+ value: out.satoshis,
324
+ scriptPubKey: {
325
+ hex: out.lockingScript.toHex(),
326
+ asm: out.lockingScript.toASM(),
327
+ },
328
+ })),
329
+ confirmations: jbData.block_height ? 1 : 0,
330
+ block: jbData.block_hash
331
+ ? {
332
+ hash: jbData.block_hash,
333
+ height: jbData.block_height,
334
+ }
335
+ : null,
336
+ },
337
+ },
338
+ };
339
+ } catch (err) {
340
+ return {
341
+ content: [
342
+ {
343
+ type: "text" as const,
344
+ text: `Error: ${err instanceof Error ? err.message : String(err)}`,
345
+ },
346
+ ],
347
+ structuredContent: { error: String(err) },
348
+ };
349
+ }
350
+ }
351
+
352
+ // If address provided, look up balance and history
353
+ if (address) {
354
+ try {
355
+ const [balRes, histRes] = await Promise.all([
356
+ fetch(
357
+ `https://api.whatsonchain.com/v1/bsv/main/address/${address}/balance`,
358
+ ),
359
+ fetch(
360
+ `https://api.whatsonchain.com/v1/bsv/main/address/${address}/history`,
361
+ ),
362
+ ]);
363
+ const balance = balRes.ok
364
+ ? ((await balRes.json()) as Record<string, unknown>)
365
+ : null;
366
+ const history = histRes.ok
367
+ ? ((await histRes.json()) as Array<Record<string, unknown>>)
368
+ : [];
369
+
370
+ return {
371
+ content: [
372
+ { type: "text" as const, text: `Address info for ${address}` },
373
+ ],
374
+ structuredContent: {
375
+ addressInfo: { balance, history },
376
+ },
377
+ };
378
+ } catch (err) {
379
+ return {
380
+ content: [
381
+ {
382
+ type: "text" as const,
383
+ text: `Error: ${err instanceof Error ? err.message : String(err)}`,
384
+ },
385
+ ],
386
+ structuredContent: { error: String(err) },
387
+ };
388
+ }
389
+ }
390
+
391
+ // Default: return price + chain info
392
+ try {
393
+ const [price, chainRes] = await Promise.all([
394
+ getBsvPriceWithCache(),
395
+ fetch("https://api.whatsonchain.com/v1/bsv/main/chain/info"),
396
+ ]);
397
+ const chainInfo = chainRes.ok
398
+ ? ((await chainRes.json()) as Record<string, unknown>)
399
+ : null;
400
+
401
+ return {
402
+ content: [
403
+ {
404
+ type: "text" as const,
405
+ text: `BSV price: $${price.toFixed(2)}`,
406
+ },
407
+ ],
408
+ structuredContent: { price, chainInfo },
409
+ };
410
+ } catch (err) {
411
+ return {
412
+ content: [
413
+ {
414
+ type: "text" as const,
415
+ text: `Error: ${err instanceof Error ? err.message : String(err)}`,
416
+ },
417
+ ],
418
+ structuredContent: { error: String(err) },
419
+ };
420
+ }
421
+ },
422
+ );
423
+
424
+ // App-only: fetch wallet data
425
+ registerAppTool(
426
+ server,
427
+ "app_wallet_data",
428
+ {
429
+ title: "Wallet Data",
430
+ description: "App-only: fetches wallet balance, UTXOs, and address.",
431
+ inputSchema: {},
432
+ _meta: {
433
+ ui: { resourceUri: APP_RESOURCE_URI, visibility: ["app"] },
434
+ },
435
+ },
436
+ async () => {
437
+ if (!wallet) {
438
+ return {
439
+ content: [
440
+ {
441
+ type: "text" as const,
442
+ text: "No wallet configured",
443
+ },
444
+ ],
445
+ structuredContent: {
446
+ error: "No wallet configured. Set PRIVATE_KEY_WIF or generate keys.",
447
+ },
448
+ };
449
+ }
450
+
451
+ try {
452
+ const { paymentUtxos } = await wallet.getUtxos();
453
+ const address = wallet.getAddress();
454
+ let totalSatoshis = 0;
455
+ for (const utxo of paymentUtxos) {
456
+ totalSatoshis += utxo.satoshis || 0;
457
+ }
458
+
459
+ let price: number | undefined;
460
+ try {
461
+ price = await getBsvPriceWithCache();
462
+ } catch {
463
+ /* price fetch optional */
464
+ }
465
+
466
+ const { toBitcoin } = await import("satoshi-token");
467
+ const bsvAmount = toBitcoin(totalSatoshis);
468
+
469
+ return {
470
+ content: [
471
+ {
472
+ type: "text" as const,
473
+ text: `Wallet balance: ${bsvAmount} BSV`,
474
+ },
475
+ ],
476
+ structuredContent: {
477
+ balance: {
478
+ satoshis: totalSatoshis,
479
+ bsv: bsvAmount,
480
+ utxoCount: paymentUtxos.length,
481
+ },
482
+ address,
483
+ utxos: paymentUtxos.slice(0, 50).map((u) => ({
484
+ txid: u.txid,
485
+ vout: u.vout,
486
+ satoshis: u.satoshis,
487
+ })),
488
+ price,
489
+ },
490
+ };
491
+ } catch (err) {
492
+ return {
493
+ content: [
494
+ {
495
+ type: "text" as const,
496
+ text: `Error: ${err instanceof Error ? err.message : String(err)}`,
497
+ },
498
+ ],
499
+ structuredContent: { error: String(err) },
500
+ };
501
+ }
502
+ },
503
+ );
504
+
505
+ // App-only: fetch ordinals data
506
+ registerAppTool(
507
+ server,
508
+ "app_ordinals_data",
509
+ {
510
+ title: "Ordinals Data",
511
+ description:
512
+ "App-only: fetches ordinals/NFT marketplace listings and search results.",
513
+ inputSchema: {
514
+ query: z.string().optional().describe("Search query"),
515
+ },
516
+ _meta: {
517
+ ui: { resourceUri: APP_RESOURCE_URI, visibility: ["app"] },
518
+ },
519
+ },
520
+ async (args) => {
521
+ const { query } = args as { query?: string };
522
+
523
+ try {
524
+ if (query) {
525
+ // Search inscriptions
526
+ const url = new URL(
527
+ "https://ordinals.gorillapool.io/api/inscriptions/search",
528
+ );
529
+ url.searchParams.set("limit", "20");
530
+ url.searchParams.set("offset", "0");
531
+ url.searchParams.set("dir", "desc");
532
+ url.searchParams.set("terms", query);
533
+
534
+ const res = await fetch(url.toString());
535
+ if (!res.ok) throw new Error(`Search failed: ${res.status}`);
536
+ const data = (await res.json()) as Record<string, unknown>;
537
+
538
+ return {
539
+ content: [
540
+ {
541
+ type: "text" as const,
542
+ text: `Found results for "${query}"`,
543
+ },
544
+ ],
545
+ structuredContent: {
546
+ results: data.results || [],
547
+ total: data.total || 0,
548
+ },
549
+ };
550
+ }
551
+
552
+ // Default: fetch marketplace listings
553
+ const res = await fetch(
554
+ "https://ordinals.gorillapool.io/api/market?limit=20&offset=0&sort=recent&dir=desc",
555
+ );
556
+ if (!res.ok) throw new Error(`Market fetch failed: ${res.status}`);
557
+ const data = (await res.json()) as Record<string, unknown>;
558
+
559
+ return {
560
+ content: [
561
+ { type: "text" as const, text: "Marketplace listings loaded" },
562
+ ],
563
+ structuredContent: {
564
+ listings: data.results || [],
565
+ total: data.total || 0,
566
+ },
567
+ };
568
+ } catch (err) {
569
+ return {
570
+ content: [
571
+ {
572
+ type: "text" as const,
573
+ text: `Error: ${err instanceof Error ? err.message : String(err)}`,
574
+ },
575
+ ],
576
+ structuredContent: { error: String(err) },
577
+ };
578
+ }
579
+ },
580
+ );
581
+
582
+ // Register the HTML resource
583
+ registerAppResource(
584
+ server,
585
+ "BSV Dashboard",
586
+ APP_RESOURCE_URI,
587
+ { description: "Interactive BSV dashboard with Explorer, Wallet, and Ordinals tabs" },
588
+ async () => {
589
+ const distPath = join(__appDirname, "dist", "app.html");
590
+ let html: string;
591
+ try {
592
+ html = await readFile(distPath, "utf-8");
593
+ } catch {
594
+ html = "<html><body><p>Dashboard not built. Run <code>bun run build:view</code> to enable it.</p></body></html>";
595
+ }
596
+ return {
597
+ contents: [
598
+ {
599
+ uri: APP_RESOURCE_URI,
600
+ mimeType: RESOURCE_MIME_TYPE,
601
+ text: html,
602
+ },
603
+ ],
604
+ };
605
+ },
606
+ );
607
+ }
608
+
212
609
  // --- Main Server Setup ---
213
610
  async function main() {
214
611
  // Check for help or info commands that don't need authentication
@@ -234,7 +631,6 @@ Environment Variables:
234
631
  DISABLE_UTILS_TOOLS Disable utility tools (default: false)
235
632
  DISABLE_BAP_TOOLS Disable BAP tools (default: false)
236
633
  DISABLE_BSOCIAL_TOOLS Disable BSocial tools (default: false)
237
- DISABLE_BIGBLOCKS_TOOLS Disable BigBlocks tools (default: false)
238
634
  ENABLE_A2B_TOOLS Enable A2B tools (default: false)
239
635
  DISABLE_BROADCASTING Disable transaction broadcasting (default: false)
240
636
  USE_DROPLET_API Use Droplet API for transactions (default: false)
@@ -246,7 +642,6 @@ Tool Categories:
246
642
  Utils Tools: General utilities, conversions
247
643
  BAP Tools: Identity management (requires identity key)
248
644
  BSocial Tools: Social posts, likes, follows
249
- BigBlocks Tools: React component registry, code generation, examples
250
645
  A2B Tools: Advanced BSV operations (requires identity key)
251
646
 
252
647
  Authentication:
@@ -328,9 +723,6 @@ Authentication:
328
723
  logFunc(
329
724
  ` DISABLE_BAP_TOOLS: ${process.env.DISABLE_BAP_TOOLS === "true" ? "Set (true)" : "Not Set/false"}`,
330
725
  );
331
- logFunc(
332
- ` DISABLE_BIGBLOCKS_TOOLS: ${process.env.DISABLE_BIGBLOCKS_TOOLS === "true" ? "Set (true)" : "Not Set/false"}`,
333
- );
334
726
  logFunc(
335
727
  ` DISABLE_BROADCASTING: ${process.env.DISABLE_BROADCASTING === "true" ? "Set (true)" : "Not Set/false"}`,
336
728
  );
@@ -417,10 +809,6 @@ Authentication:
417
809
  logFunc(
418
810
  ` BSocial: ${effectiveConfig.loadBsocialTools ? "\x1b[32mEnabled\x1b[0m" : "\x1b[31mDisabled\x1b[0m"}`,
419
811
  );
420
- logFunc(
421
- ` BigBlocks: ${effectiveConfig.loadBigBlocksTools ? "\x1b[32mEnabled\x1b[0m" : "\x1b[31mDisabled\x1b[0m"}`,
422
- );
423
-
424
812
  if (effectiveConfig.loadWalletTools) {
425
813
  logFunc(
426
814
  ` Broadcasting: ${!effectiveConfig.disableBroadcasting ? "\x1b[32mEnabled\x1b[0m" : "\x1b[31mDisabled\x1b[0m"}`,
@@ -437,6 +825,9 @@ Authentication:
437
825
  prompts: {},
438
826
  resources: {},
439
827
  tools: {},
828
+ experimental: {
829
+ "io.modelcontextprotocol/ui": { version: "0.1" },
830
+ },
440
831
  },
441
832
  instructions: `
442
833
  This server exposes Bitcoin SV helpers.
@@ -528,14 +919,8 @@ Authentication:
528
919
  }
529
920
  }
530
921
 
531
- // Register MNEE tools if enabled and wallet is available
532
- if (effectiveConfig.loadMneeTools && wallet) {
533
- registerMneeTools(server);
534
- } else if (
535
- effectiveConfig.loadMneeTools &&
536
- !wallet &&
537
- CONFIG.loadWalletTools
538
- ) {
922
+ // Disable MNEE tools if wallet is not available
923
+ if (effectiveConfig.loadMneeTools && !wallet && CONFIG.loadWalletTools) {
539
924
  logFunc(
540
925
  "\x1b[33mWARN: MNEE tools require a wallet but wallet initialization failed. MNEE tools disabled.\x1b[0m",
541
926
  );
@@ -551,7 +936,6 @@ Authentication:
551
936
  enableA2bTools: effectiveConfig.loadA2bTools,
552
937
  enableBapTools: effectiveConfig.loadBapTools,
553
938
  enableBsocialTools: effectiveConfig.loadBsocialTools,
554
- enableBigBlocksTools: effectiveConfig.loadBigBlocksTools,
555
939
  enableWalletTools: effectiveConfig.loadWalletTools,
556
940
  enableMneeTools: effectiveConfig.loadMneeTools,
557
941
  identityPk,
@@ -565,6 +949,9 @@ Authentication:
565
949
  registerAllTools(server, toolsConfig);
566
950
  }
567
951
 
952
+ // Register MCP App tools and resource (interactive UI)
953
+ registerMcpAppTools(server, wallet);
954
+
568
955
  // Register prompts if enabled
569
956
  if (CONFIG.loadPrompts) {
570
957
  registerAllPrompts(server);
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "bsv-mcp",
3
3
  "module": "dist/index.js",
4
4
  "type": "module",
5
- "version": "0.2.0",
5
+ "version": "0.2.7",
6
6
  "license": "MIT",
7
7
  "author": "satchmo",
8
8
  "description": "A collection of Bitcoin SV (BSV) tools for the Model Context Protocol (MCP) framework",
@@ -42,58 +42,62 @@
42
42
  "private": false,
43
43
  "devDependencies": {
44
44
  "@biomejs/biome": "^2.4.6",
45
+ "@radix-ui/react-dialog": "^1.1.15",
46
+ "@radix-ui/react-dropdown-menu": "^2.1.16",
47
+ "@radix-ui/react-label": "^2.1.8",
48
+ "@radix-ui/react-separator": "^1.1.8",
49
+ "@radix-ui/react-slot": "^1.2.4",
50
+ "@radix-ui/react-tabs": "^1.1.13",
51
+ "@radix-ui/react-toast": "^1.2.15",
52
+ "@radix-ui/react-tooltip": "^1.2.8",
53
+ "@radix-ui/themes": "^3.3.0",
45
54
  "@tailwindcss/postcss": "^4.2.1",
55
+ "@tanstack/react-query": "^5.90.21",
46
56
  "@types/bun": "^1.3.10",
47
57
  "@types/node": "^25.3.5",
48
- "bun": "^1.3.10",
49
58
  "@types/react": "^19.2.14",
50
59
  "@types/react-dom": "^19.2.3",
51
60
  "autoprefixer": "^10.4.27",
61
+ "bun": "^1.3.10",
62
+ "class-variance-authority": "^0.7.1",
63
+ "clsx": "^2.1.1",
64
+ "lucide-react": "0.577.0",
65
+ "next": "^16.1.6",
66
+ "next-themes": "^0.4.6",
52
67
  "postcss": "^8.5.8",
68
+ "react": "^19.2.4",
69
+ "react-dom": "^19.2.4",
70
+ "sonner": "^2.0.7",
71
+ "tailwind-merge": "^3.5.0",
53
72
  "tailwindcss": "^4.2.1",
54
- "tailwindcss-animate": "^1.0.7"
73
+ "tailwindcss-animate": "^1.0.7",
74
+ "vite": "^7.3.1",
75
+ "vite-plugin-singlefile": "^2.3.0"
55
76
  },
56
77
  "peerDependencies": {
57
78
  "typescript": "^5.9.3"
58
79
  },
59
80
  "dependencies": {
60
81
  "@bsv/sdk": "^2.0.6",
82
+ "@modelcontextprotocol/ext-apps": "^1.2.0",
61
83
  "@modelcontextprotocol/sdk": "^1.27.1",
62
- "@radix-ui/react-dialog": "^1.1.15",
63
- "@radix-ui/react-dropdown-menu": "^2.1.16",
64
- "@radix-ui/react-label": "^2.1.8",
65
- "@radix-ui/react-separator": "^1.1.8",
66
- "@radix-ui/react-slot": "^1.2.4",
67
- "@radix-ui/react-tabs": "^1.1.13",
68
- "@radix-ui/react-toast": "^1.2.15",
69
- "@radix-ui/react-tooltip": "^1.2.8",
70
- "@radix-ui/themes": "^3.3.0",
71
- "@tanstack/react-query": "^5.90.21",
72
- "bigblocks": "0.0.39",
73
84
  "bitcoin-auth": "^0.0.7",
74
85
  "bitcoin-backup": "0.0.7",
75
86
  "bmap-api-types": "0.0.9",
76
87
  "bsv-bap": "0.1.23",
77
- "class-variance-authority": "^0.7.1",
78
- "clsx": "^2.1.1",
79
88
  "jose": "^6.2.0",
80
89
  "js-1sat-ord": "^0.1.91",
81
- "lucide-react": "0.577.0",
82
90
  "mcp-handler": "^1.0.7",
83
91
  "mnee": "^3.1.0",
84
- "next": "^16.1.6",
85
- "next-themes": "^0.4.6",
86
- "react": "^19.2.4",
87
- "react-dom": "^19.2.4",
88
92
  "satoshi-token": "^0.0.7",
89
93
  "schema-dts": "^1.1.5",
90
94
  "sigma-protocol": "^0.1.9",
91
- "sonner": "^2.0.7",
92
- "tailwind-merge": "^3.5.0",
93
95
  "zod": "^4.3.6"
94
96
  },
95
97
  "scripts": {
96
- "build": "bun build ./index.ts --outdir ./dist --target node",
98
+ "build": "bun run ./scripts/build.ts",
99
+ "build:view": "vite build",
100
+ "build:all": "bun run build:view && bun run build",
97
101
  "dev": "next dev",
98
102
  "build:next": "next build",
99
103
  "start:next": "next start",
@@ -121,37 +121,37 @@ export function registerA2bDiscoverTool(server: McpServer) {
121
121
  server.tool(
122
122
  "a2b_discover",
123
123
  "Search on-chain agent and MCP tool records. Use 'agent' to search for agents, 'tool' to search for MCP tools.",
124
- { args: a2bDiscoverArgsSchema },
124
+ { ...a2bDiscoverArgsSchema.shape },
125
125
  async (
126
- { args }: { args: A2bDiscoverArgs },
126
+ { queryType, query, limit, offset, fromBlock, toBlock },
127
127
  extra: RequestHandlerExtra<ServerRequest, ServerNotification>,
128
128
  ) => {
129
129
  try {
130
130
  const params = new URLSearchParams();
131
131
 
132
132
  // Set query type (agent, tool, or all)
133
- params.set("type", args.queryType);
133
+ params.set("type", queryType);
134
134
 
135
135
  // Use enhanced search for better relevance scoring
136
136
  let searchEndpoint = "/search/enhanced";
137
137
 
138
138
  // For empty queries, use the regular search endpoint
139
- if (!args.query || !args.query.trim()) {
139
+ if (!query || !query.trim()) {
140
140
  searchEndpoint = "/search";
141
141
  } else {
142
- params.set("q", args.query); // enhanced search uses 'q' parameter
142
+ params.set("q", query); // enhanced search uses 'q' parameter
143
143
  }
144
144
 
145
145
  // Add pagination parameters
146
- params.set("limit", args.limit?.toString() ?? "10");
147
- params.set("offset", args.offset?.toString() ?? "0");
146
+ params.set("limit", limit?.toString() ?? "10");
147
+ params.set("offset", offset?.toString() ?? "0");
148
148
 
149
149
  // Add block range if specified
150
- if (args.fromBlock) {
151
- params.set("fromBlock", args.fromBlock.toString());
150
+ if (fromBlock) {
151
+ params.set("fromBlock", fromBlock.toString());
152
152
  }
153
- if (args.toBlock) {
154
- params.set("toBlock", args.toBlock.toString());
153
+ if (toBlock) {
154
+ params.set("toBlock", toBlock.toString());
155
155
  }
156
156
 
157
157
  // Construct the full URL
@@ -178,7 +178,7 @@ export function registerA2bDiscoverTool(server: McpServer) {
178
178
  let result = "";
179
179
 
180
180
  if (data?.items?.length > 0) {
181
- result = `Found ${data.items.length} ${args.queryType}(s):\n\n`;
181
+ result = `Found ${data.items.length} ${queryType}(s):\n\n`;
182
182
 
183
183
  data.items.forEach((item: A2BDiscoveryItem, index: number) => {
184
184
  // Server name and description
@@ -217,7 +217,7 @@ export function registerA2bDiscoverTool(server: McpServer) {
217
217
  result += "\n";
218
218
  });
219
219
  } else {
220
- result = `No ${args.queryType} results found.`;
220
+ result = `No ${queryType} results found.`;
221
221
  }
222
222
 
223
223
  return {
@@ -55,12 +55,11 @@ export function registerBapFriendTool(
55
55
  server.tool(
56
56
  "bap_friend",
57
57
  "Initiates a friend request to another BAP ID by broadcasting an on-chain MAP transaction.",
58
- { args: bapFriendArgsSchema },
58
+ { ...bapFriendArgsSchema.shape },
59
59
  async (
60
- { args }: { args: BapFriendArgs },
60
+ { targetBapId },
61
61
  extra: RequestHandlerExtra<ServerRequest, ServerNotification>,
62
62
  ): Promise<CallToolResult> => {
63
- const { targetBapId } = args;
64
63
  const logFunc = console.error;
65
64
 
66
65
  try {