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.
- package/CHANGELOG.md +13 -0
- package/dist/index.js +87272 -70728
- package/index.ts +409 -22
- package/package.json +28 -24
- package/tools/a2b/discover.ts +13 -13
- package/tools/bap/friend.ts +2 -3
- package/tools/bap/generate.ts +7 -9
- package/tools/bap/getCurrentAddress.ts +1 -8
- package/tools/bap/getId.ts +3 -3
- package/tools/bsocial/bmapFollow.ts +3 -7
- package/tools/bsocial/bmapLikes.ts +3 -3
- package/tools/bsocial/bmapReadPosts.ts +3 -3
- package/tools/bsocial/createPost.ts +3 -3
- package/tools/bsocial/readPosts.ts +3 -3
- package/tools/bsv/decodeTransaction.ts +2 -5
- package/tools/bsv/explore.ts +2 -3
- package/tools/bsv/getPrice.ts +1 -9
- package/tools/bsv/token.ts +14 -26
- package/tools/index.ts +0 -13
- package/tools/mnee/getBalance.ts +2 -4
- package/tools/mnee/parseTx.ts +3 -3
- package/tools/mnee/sendMnee.ts +5 -5
- package/tools/ordinals/getInscription.ts +2 -3
- package/tools/ordinals/getTokenByIdOrTicker.ts +6 -3
- package/tools/ordinals/marketListings.ts +2 -18
- package/tools/ordinals/marketSales.ts +2 -4
- package/tools/ordinals/searchInscriptions.ts +2 -4
- package/tools/utils/index.ts +14 -16
- package/tools/wallet/a2bPublishAgent.ts +18 -27
- package/tools/wallet/a2bPublishMcp.ts +17 -22
- package/tools/wallet/createOrdinals.ts +11 -20
- package/tools/wallet/fetchPaymentUtxos.ts +9 -1
- package/tools/wallet/gatherCollectionInfo.ts +9 -13
- package/tools/wallet/getAddress.ts +1 -9
- package/tools/wallet/getBalance.ts +2 -14
- package/tools/wallet/getBalanceDroplet.ts +2 -13
- package/tools/wallet/getPublicKey.ts +3 -16
- package/tools/wallet/mintCollection.ts +17 -22
- package/tools/wallet/purchaseListing.ts +19 -29
- package/tools/wallet/refreshUtxos.ts +2 -14
- package/tools/wallet/sendOrdinals.ts +11 -20
- package/tools/wallet/sendToAddress.ts +2 -22
- package/tools/wallet/setupDroplet.ts +7 -18
- package/tools/wallet/tools.ts +10 -80
- package/tools/wallet/transferOrdToken.ts +12 -20
- package/vite.config.ts +15 -0
- package/tools/bigblocks/components.ts +0 -304
- package/tools/bigblocks/docs.ts +0 -488
- package/tools/bigblocks/examples.ts +0 -527
- package/tools/bigblocks/generator.ts +0 -485
- package/tools/bigblocks/index.ts +0 -23
- 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
|
-
|
|
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
|
-
//
|
|
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.
|
|
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
|
|
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",
|
package/tools/a2b/discover.ts
CHANGED
|
@@ -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
|
-
{
|
|
124
|
+
{ ...a2bDiscoverArgsSchema.shape },
|
|
125
125
|
async (
|
|
126
|
-
{
|
|
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",
|
|
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 (!
|
|
139
|
+
if (!query || !query.trim()) {
|
|
140
140
|
searchEndpoint = "/search";
|
|
141
141
|
} else {
|
|
142
|
-
params.set("q",
|
|
142
|
+
params.set("q", query); // enhanced search uses 'q' parameter
|
|
143
143
|
}
|
|
144
144
|
|
|
145
145
|
// Add pagination parameters
|
|
146
|
-
params.set("limit",
|
|
147
|
-
params.set("offset",
|
|
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 (
|
|
151
|
-
params.set("fromBlock",
|
|
150
|
+
if (fromBlock) {
|
|
151
|
+
params.set("fromBlock", fromBlock.toString());
|
|
152
152
|
}
|
|
153
|
-
if (
|
|
154
|
-
params.set("toBlock",
|
|
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} ${
|
|
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 ${
|
|
220
|
+
result = `No ${queryType} results found.`;
|
|
221
221
|
}
|
|
222
222
|
|
|
223
223
|
return {
|
package/tools/bap/friend.ts
CHANGED
|
@@ -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
|
-
{
|
|
58
|
+
{ ...bapFriendArgsSchema.shape },
|
|
59
59
|
async (
|
|
60
|
-
{
|
|
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 {
|