bsv-mcp 0.2.0 → 0.2.8

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 +87290 -70731
  3. package/index.ts +422 -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 +8 -7
  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 +21 -24
  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,391 @@ 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
+ _meta: { viewUUID: crypto.randomUUID() },
260
+ };
261
+ },
262
+ );
263
+
264
+ // App-only: fetch explorer data (price, chain info, tx decode, address lookup)
265
+ registerAppTool(
266
+ server,
267
+ "app_explorer_data",
268
+ {
269
+ title: "Explorer Data",
270
+ description:
271
+ "App-only: fetches BSV price, chain info, decodes transactions, and looks up addresses.",
272
+ inputSchema: {
273
+ txid: z.string().optional().describe("Transaction ID to decode"),
274
+ address: z
275
+ .string()
276
+ .optional()
277
+ .describe("Address to look up balance/history"),
278
+ },
279
+ _meta: {
280
+ ui: { resourceUri: APP_RESOURCE_URI, visibility: ["app"] },
281
+ },
282
+ },
283
+ async (args) => {
284
+ const { txid, address } = args as {
285
+ txid?: string;
286
+ address?: string;
287
+ };
288
+
289
+ // If txid provided, decode transaction
290
+ if (txid) {
291
+ try {
292
+ const res = await fetch(
293
+ `https://junglebus.gorillapool.io/v1/transaction/get/${txid}`,
294
+ );
295
+ if (!res.ok)
296
+ throw new Error(`Transaction not found: ${res.status}`);
297
+ const jbData = (await res.json()) as Record<string, unknown>;
298
+
299
+ const { Transaction, Utils } = await import("@bsv/sdk");
300
+ const rawTx = jbData.transaction as string;
301
+ const isBase64 = /^[A-Za-z0-9+/=]+$/.test(rawTx);
302
+ const txBytes = isBase64
303
+ ? Utils.toArray(rawTx, "base64")
304
+ : Utils.toArray(rawTx, "hex");
305
+ const tx = Transaction.fromBinary(txBytes);
306
+
307
+ return {
308
+ content: [
309
+ { type: "text" as const, text: `Decoded transaction ${txid}` },
310
+ ],
311
+ structuredContent: {
312
+ transaction: {
313
+ txid,
314
+ version: tx.version,
315
+ lockTime: tx.lockTime,
316
+ size: tx.toBinary().length,
317
+ inputs: tx.inputs.map((inp) => ({
318
+ txid: inp.sourceTXID,
319
+ vout: inp.sourceOutputIndex,
320
+ script: inp.unlockingScript?.toHex() || "",
321
+ })),
322
+ outputs: tx.outputs.map((out, i) => ({
323
+ n: i,
324
+ value: out.satoshis,
325
+ scriptPubKey: {
326
+ hex: out.lockingScript.toHex(),
327
+ asm: out.lockingScript.toASM(),
328
+ },
329
+ })),
330
+ confirmations: jbData.block_height ? 1 : 0,
331
+ block: jbData.block_hash
332
+ ? {
333
+ hash: jbData.block_hash,
334
+ height: jbData.block_height,
335
+ }
336
+ : null,
337
+ },
338
+ },
339
+ _meta: { viewUUID: crypto.randomUUID() },
340
+ };
341
+ } catch (err) {
342
+ return {
343
+ content: [
344
+ {
345
+ type: "text" as const,
346
+ text: `Error: ${err instanceof Error ? err.message : String(err)}`,
347
+ },
348
+ ],
349
+ structuredContent: { error: String(err) },
350
+ _meta: { viewUUID: crypto.randomUUID() },
351
+ };
352
+ }
353
+ }
354
+
355
+ // If address provided, look up balance and history
356
+ if (address) {
357
+ try {
358
+ const [balRes, histRes] = await Promise.all([
359
+ fetch(
360
+ `https://api.whatsonchain.com/v1/bsv/main/address/${address}/balance`,
361
+ ),
362
+ fetch(
363
+ `https://api.whatsonchain.com/v1/bsv/main/address/${address}/history`,
364
+ ),
365
+ ]);
366
+ const balance = balRes.ok
367
+ ? ((await balRes.json()) as Record<string, unknown>)
368
+ : null;
369
+ const history = histRes.ok
370
+ ? ((await histRes.json()) as Array<Record<string, unknown>>)
371
+ : [];
372
+
373
+ return {
374
+ content: [
375
+ { type: "text" as const, text: `Address info for ${address}` },
376
+ ],
377
+ structuredContent: {
378
+ addressInfo: { balance, history },
379
+ },
380
+ _meta: { viewUUID: crypto.randomUUID() },
381
+ };
382
+ } catch (err) {
383
+ return {
384
+ content: [
385
+ {
386
+ type: "text" as const,
387
+ text: `Error: ${err instanceof Error ? err.message : String(err)}`,
388
+ },
389
+ ],
390
+ structuredContent: { error: String(err) },
391
+ _meta: { viewUUID: crypto.randomUUID() },
392
+ };
393
+ }
394
+ }
395
+
396
+ // Default: return price + chain info
397
+ try {
398
+ const [price, chainRes] = await Promise.all([
399
+ getBsvPriceWithCache(),
400
+ fetch("https://api.whatsonchain.com/v1/bsv/main/chain/info"),
401
+ ]);
402
+ const chainInfo = chainRes.ok
403
+ ? ((await chainRes.json()) as Record<string, unknown>)
404
+ : null;
405
+
406
+ return {
407
+ content: [
408
+ {
409
+ type: "text" as const,
410
+ text: `BSV price: $${price.toFixed(2)}`,
411
+ },
412
+ ],
413
+ structuredContent: { price, chainInfo },
414
+ _meta: { viewUUID: crypto.randomUUID() },
415
+ };
416
+ } catch (err) {
417
+ return {
418
+ content: [
419
+ {
420
+ type: "text" as const,
421
+ text: `Error: ${err instanceof Error ? err.message : String(err)}`,
422
+ },
423
+ ],
424
+ structuredContent: { error: String(err) },
425
+ _meta: { viewUUID: crypto.randomUUID() },
426
+ };
427
+ }
428
+ },
429
+ );
430
+
431
+ // App-only: fetch wallet data
432
+ registerAppTool(
433
+ server,
434
+ "app_wallet_data",
435
+ {
436
+ title: "Wallet Data",
437
+ description: "App-only: fetches wallet balance, UTXOs, and address.",
438
+ inputSchema: {},
439
+ _meta: {
440
+ ui: { resourceUri: APP_RESOURCE_URI, visibility: ["app"] },
441
+ },
442
+ },
443
+ async () => {
444
+ if (!wallet) {
445
+ return {
446
+ content: [
447
+ {
448
+ type: "text" as const,
449
+ text: "No wallet configured",
450
+ },
451
+ ],
452
+ structuredContent: {
453
+ error: "No wallet configured. Set PRIVATE_KEY_WIF or generate keys.",
454
+ },
455
+ _meta: { viewUUID: crypto.randomUUID() },
456
+ };
457
+ }
458
+
459
+ try {
460
+ const { paymentUtxos } = await wallet.getUtxos();
461
+ const address = wallet.getAddress();
462
+ let totalSatoshis = 0;
463
+ for (const utxo of paymentUtxos) {
464
+ totalSatoshis += utxo.satoshis || 0;
465
+ }
466
+
467
+ let price: number | undefined;
468
+ try {
469
+ price = await getBsvPriceWithCache();
470
+ } catch {
471
+ /* price fetch optional */
472
+ }
473
+
474
+ const { toBitcoin } = await import("satoshi-token");
475
+ const bsvAmount = toBitcoin(totalSatoshis);
476
+
477
+ return {
478
+ content: [
479
+ {
480
+ type: "text" as const,
481
+ text: `Wallet balance: ${bsvAmount} BSV`,
482
+ },
483
+ ],
484
+ structuredContent: {
485
+ balance: {
486
+ satoshis: totalSatoshis,
487
+ bsv: bsvAmount,
488
+ utxoCount: paymentUtxos.length,
489
+ },
490
+ address,
491
+ utxos: paymentUtxos.slice(0, 50).map((u) => ({
492
+ txid: u.txid,
493
+ vout: u.vout,
494
+ satoshis: u.satoshis,
495
+ })),
496
+ price,
497
+ },
498
+ _meta: { viewUUID: crypto.randomUUID() },
499
+ };
500
+ } catch (err) {
501
+ return {
502
+ content: [
503
+ {
504
+ type: "text" as const,
505
+ text: `Error: ${err instanceof Error ? err.message : String(err)}`,
506
+ },
507
+ ],
508
+ structuredContent: { error: String(err) },
509
+ _meta: { viewUUID: crypto.randomUUID() },
510
+ };
511
+ }
512
+ },
513
+ );
514
+
515
+ // App-only: fetch ordinals data
516
+ registerAppTool(
517
+ server,
518
+ "app_ordinals_data",
519
+ {
520
+ title: "Ordinals Data",
521
+ description:
522
+ "App-only: fetches ordinals/NFT marketplace listings and search results.",
523
+ inputSchema: {
524
+ query: z.string().optional().describe("Search query"),
525
+ },
526
+ _meta: {
527
+ ui: { resourceUri: APP_RESOURCE_URI, visibility: ["app"] },
528
+ },
529
+ },
530
+ async (args) => {
531
+ const { query } = args as { query?: string };
532
+
533
+ try {
534
+ if (query) {
535
+ // Search inscriptions
536
+ const url = new URL(
537
+ "https://ordinals.gorillapool.io/api/inscriptions/search",
538
+ );
539
+ url.searchParams.set("limit", "20");
540
+ url.searchParams.set("offset", "0");
541
+ url.searchParams.set("dir", "desc");
542
+ url.searchParams.set("terms", query);
543
+
544
+ const res = await fetch(url.toString());
545
+ if (!res.ok) throw new Error(`Search failed: ${res.status}`);
546
+ const data = (await res.json()) as Record<string, unknown>;
547
+
548
+ return {
549
+ content: [
550
+ {
551
+ type: "text" as const,
552
+ text: `Found results for "${query}"`,
553
+ },
554
+ ],
555
+ structuredContent: {
556
+ results: data.results || [],
557
+ total: data.total || 0,
558
+ },
559
+ _meta: { viewUUID: crypto.randomUUID() },
560
+ };
561
+ }
562
+
563
+ // Default: fetch marketplace listings
564
+ const res = await fetch(
565
+ "https://ordinals.gorillapool.io/api/market?limit=20&offset=0&sort=recent&dir=desc",
566
+ );
567
+ if (!res.ok) throw new Error(`Market fetch failed: ${res.status}`);
568
+ const data = (await res.json()) as Record<string, unknown>;
569
+
570
+ return {
571
+ content: [
572
+ { type: "text" as const, text: "Marketplace listings loaded" },
573
+ ],
574
+ structuredContent: {
575
+ listings: data.results || [],
576
+ total: data.total || 0,
577
+ },
578
+ _meta: { viewUUID: crypto.randomUUID() },
579
+ };
580
+ } catch (err) {
581
+ return {
582
+ content: [
583
+ {
584
+ type: "text" as const,
585
+ text: `Error: ${err instanceof Error ? err.message : String(err)}`,
586
+ },
587
+ ],
588
+ structuredContent: { error: String(err) },
589
+ _meta: { viewUUID: crypto.randomUUID() },
590
+ };
591
+ }
592
+ },
593
+ );
594
+
595
+ // Register the HTML resource
596
+ registerAppResource(
597
+ server,
598
+ "BSV Dashboard",
599
+ APP_RESOURCE_URI,
600
+ { description: "Interactive BSV dashboard with Explorer, Wallet, and Ordinals tabs" },
601
+ async () => {
602
+ const distPath = join(__appDirname, "dist", "app.html");
603
+ let html: string;
604
+ try {
605
+ html = await readFile(distPath, "utf-8");
606
+ } catch {
607
+ html = "<html><body><p>Dashboard not built. Run <code>bun run build:view</code> to enable it.</p></body></html>";
608
+ }
609
+ return {
610
+ contents: [
611
+ {
612
+ uri: APP_RESOURCE_URI,
613
+ mimeType: RESOURCE_MIME_TYPE,
614
+ text: html,
615
+ },
616
+ ],
617
+ };
618
+ },
619
+ );
620
+ }
621
+
212
622
  // --- Main Server Setup ---
213
623
  async function main() {
214
624
  // Check for help or info commands that don't need authentication
@@ -234,7 +644,6 @@ Environment Variables:
234
644
  DISABLE_UTILS_TOOLS Disable utility tools (default: false)
235
645
  DISABLE_BAP_TOOLS Disable BAP tools (default: false)
236
646
  DISABLE_BSOCIAL_TOOLS Disable BSocial tools (default: false)
237
- DISABLE_BIGBLOCKS_TOOLS Disable BigBlocks tools (default: false)
238
647
  ENABLE_A2B_TOOLS Enable A2B tools (default: false)
239
648
  DISABLE_BROADCASTING Disable transaction broadcasting (default: false)
240
649
  USE_DROPLET_API Use Droplet API for transactions (default: false)
@@ -246,7 +655,6 @@ Tool Categories:
246
655
  Utils Tools: General utilities, conversions
247
656
  BAP Tools: Identity management (requires identity key)
248
657
  BSocial Tools: Social posts, likes, follows
249
- BigBlocks Tools: React component registry, code generation, examples
250
658
  A2B Tools: Advanced BSV operations (requires identity key)
251
659
 
252
660
  Authentication:
@@ -328,9 +736,6 @@ Authentication:
328
736
  logFunc(
329
737
  ` DISABLE_BAP_TOOLS: ${process.env.DISABLE_BAP_TOOLS === "true" ? "Set (true)" : "Not Set/false"}`,
330
738
  );
331
- logFunc(
332
- ` DISABLE_BIGBLOCKS_TOOLS: ${process.env.DISABLE_BIGBLOCKS_TOOLS === "true" ? "Set (true)" : "Not Set/false"}`,
333
- );
334
739
  logFunc(
335
740
  ` DISABLE_BROADCASTING: ${process.env.DISABLE_BROADCASTING === "true" ? "Set (true)" : "Not Set/false"}`,
336
741
  );
@@ -417,10 +822,6 @@ Authentication:
417
822
  logFunc(
418
823
  ` BSocial: ${effectiveConfig.loadBsocialTools ? "\x1b[32mEnabled\x1b[0m" : "\x1b[31mDisabled\x1b[0m"}`,
419
824
  );
420
- logFunc(
421
- ` BigBlocks: ${effectiveConfig.loadBigBlocksTools ? "\x1b[32mEnabled\x1b[0m" : "\x1b[31mDisabled\x1b[0m"}`,
422
- );
423
-
424
825
  if (effectiveConfig.loadWalletTools) {
425
826
  logFunc(
426
827
  ` Broadcasting: ${!effectiveConfig.disableBroadcasting ? "\x1b[32mEnabled\x1b[0m" : "\x1b[31mDisabled\x1b[0m"}`,
@@ -437,6 +838,9 @@ Authentication:
437
838
  prompts: {},
438
839
  resources: {},
439
840
  tools: {},
841
+ experimental: {
842
+ "io.modelcontextprotocol/ui": { version: "0.1" },
843
+ },
440
844
  },
441
845
  instructions: `
442
846
  This server exposes Bitcoin SV helpers.
@@ -528,14 +932,8 @@ Authentication:
528
932
  }
529
933
  }
530
934
 
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
- ) {
935
+ // Disable MNEE tools if wallet is not available
936
+ if (effectiveConfig.loadMneeTools && !wallet && CONFIG.loadWalletTools) {
539
937
  logFunc(
540
938
  "\x1b[33mWARN: MNEE tools require a wallet but wallet initialization failed. MNEE tools disabled.\x1b[0m",
541
939
  );
@@ -551,7 +949,6 @@ Authentication:
551
949
  enableA2bTools: effectiveConfig.loadA2bTools,
552
950
  enableBapTools: effectiveConfig.loadBapTools,
553
951
  enableBsocialTools: effectiveConfig.loadBsocialTools,
554
- enableBigBlocksTools: effectiveConfig.loadBigBlocksTools,
555
952
  enableWalletTools: effectiveConfig.loadWalletTools,
556
953
  enableMneeTools: effectiveConfig.loadMneeTools,
557
954
  identityPk,
@@ -565,6 +962,9 @@ Authentication:
565
962
  registerAllTools(server, toolsConfig);
566
963
  }
567
964
 
965
+ // Register MCP App tools and resource (interactive UI)
966
+ registerMcpAppTools(server, wallet);
967
+
568
968
  // Register prompts if enabled
569
969
  if (CONFIG.loadPrompts) {
570
970
  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.8",
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 {