bsv-mcp 0.0.36 → 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 (78) hide show
  1. package/CHANGELOG.md +124 -0
  2. package/README.md +233 -4
  3. package/dist/index.js +120082 -54669
  4. package/index.ts +1196 -88
  5. package/next-env.d.ts +6 -0
  6. package/package.json +58 -15
  7. package/prompts/bsvSdk/index.ts +5 -5
  8. package/prompts/bsvSdk/transaction.ts +2 -2
  9. package/prompts/bsvSdk/wallet.ts +1 -1
  10. package/prompts/index.ts +1 -1
  11. package/resources/bitcom.ts +132 -0
  12. package/resources/resources.ts +7 -3
  13. package/smithery.yaml +3 -0
  14. package/test-mcp-server.ts +157 -0
  15. package/tools/a2b/discover.ts +13 -13
  16. package/tools/bap/friend.ts +266 -0
  17. package/tools/bap/generate.test.ts +350 -0
  18. package/tools/bap/generate.ts +431 -0
  19. package/tools/bap/getCurrentAddress.ts +69 -0
  20. package/tools/bap/getId.test.ts +121 -0
  21. package/tools/bap/getId.ts +169 -0
  22. package/tools/bap/index.ts +111 -0
  23. package/tools/bap/types.ts +66 -0
  24. package/tools/bap/utils.ts +33 -0
  25. package/tools/bsocial/bmapFollow.ts +121 -0
  26. package/tools/bsocial/bmapLikes.ts +111 -0
  27. package/tools/bsocial/bmapReadPosts.ts +148 -0
  28. package/tools/bsocial/createPost.ts +136 -0
  29. package/tools/bsocial/index.ts +45 -0
  30. package/tools/bsocial/readPosts.ts +255 -0
  31. package/tools/bsv/decodeTransaction.ts +7 -6
  32. package/tools/bsv/explore.ts +2 -3
  33. package/tools/bsv/getPrice.ts +1 -9
  34. package/tools/bsv/token.ts +14 -26
  35. package/tools/constants.ts +21 -0
  36. package/tools/index.ts +91 -18
  37. package/tools/mnee/getBalance.ts +2 -4
  38. package/tools/mnee/index.ts +8 -0
  39. package/tools/mnee/parseTx.ts +4 -4
  40. package/tools/mnee/sendMnee.ts +6 -6
  41. package/tools/ordinals/getInscription.ts +2 -3
  42. package/tools/ordinals/getTokenByIdOrTicker.ts +6 -3
  43. package/tools/ordinals/marketListings.ts +2 -18
  44. package/tools/ordinals/marketSales.ts +2 -4
  45. package/tools/ordinals/searchInscriptions.ts +2 -4
  46. package/tools/utils/aip.ts +46 -0
  47. package/tools/utils/conversion.ts +1 -0
  48. package/tools/utils/errorHandler.ts +76 -0
  49. package/tools/utils/index.ts +56 -14
  50. package/tools/utils/installAgentMaster.ts +84 -0
  51. package/tools/utils/logger.ts +20 -0
  52. package/tools/utils/toolConfig.ts +80 -0
  53. package/tools/utils/toolRegistration.ts +66 -0
  54. package/tools/utils/transactionBuilder.ts +196 -0
  55. package/tools/wallet/a2bPublishAgent.ts +25 -30
  56. package/tools/wallet/a2bPublishMcp.ts +43 -44
  57. package/tools/wallet/createOrdinals.ts +13 -20
  58. package/tools/wallet/fetchPaymentUtxos.ts +48 -39
  59. package/tools/wallet/gatherCollectionInfo.ts +370 -0
  60. package/tools/wallet/getAddress.ts +14 -30
  61. package/tools/wallet/getBalance.ts +54 -0
  62. package/tools/wallet/getBalanceDroplet.ts +48 -0
  63. package/tools/wallet/getPublicKey.ts +3 -16
  64. package/tools/wallet/integratedWallet.ts +120 -0
  65. package/tools/wallet/mintCollection.ts +373 -0
  66. package/tools/wallet/purchaseListing.ts +21 -31
  67. package/tools/wallet/refreshUtxos.ts +2 -14
  68. package/tools/wallet/schemas.ts +18 -249
  69. package/tools/wallet/sendOrdinals.ts +14 -21
  70. package/tools/wallet/sendToAddress.ts +2 -18
  71. package/tools/wallet/setupDroplet.ts +161 -0
  72. package/tools/wallet/tools.ts +55 -210
  73. package/tools/wallet/transactionHelper.ts +121 -0
  74. package/tools/wallet/transferOrdToken.ts +19 -24
  75. package/tools/wallet/utxo.ts +62 -0
  76. package/tools/wallet/wallet.ts +237 -234
  77. package/vite.config.ts +15 -0
  78. package/tools/a2b/call.ts +0 -32
@@ -1,15 +1,14 @@
1
- import { PrivateKey, Utils } from "@bsv/sdk";
1
+ import type { PrivateKey } from "@bsv/sdk";
2
+ import { Utils } from "@bsv/sdk";
2
3
  import { Client } from "@modelcontextprotocol/sdk/client/index.js";
3
4
  import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
4
5
  import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
5
- import type { RequestHandlerExtra } from "@modelcontextprotocol/sdk/shared/protocol.js";
6
6
  import type {
7
7
  ClientNotification,
8
8
  ClientRequest,
9
9
  ServerNotification,
10
10
  ServerRequest,
11
11
  } from "@modelcontextprotocol/sdk/types.js";
12
- import { createOrdinals } from "js-1sat-ord";
13
12
  import type {
14
13
  ChangeResult,
15
14
  CreateOrdinalsConfig,
@@ -18,9 +17,12 @@ import type {
18
17
  LocalSigner,
19
18
  PreMAP,
20
19
  } from "js-1sat-ord";
21
- import { Sigma } from "sigma-protocol";
20
+ import { createOrdinals } from "js-1sat-ord";
22
21
  import { z } from "zod";
22
+ import packageJson from "../../package.json";
23
+ import { V5Broadcaster } from "../../utils/broadcaster";
23
24
  import type { Wallet } from "./wallet";
25
+
24
26
  const { toArray, toBase64 } = Utils;
25
27
 
26
28
  // API endpoint for the A2B Overlay service
@@ -134,7 +136,7 @@ async function fetchMcpMetadata(
134
136
  // Create and connect a client
135
137
  client = new Client({
136
138
  name: "metadata-fetcher",
137
- version: "1.0.0",
139
+ version: packageJson.version,
138
140
  });
139
141
 
140
142
  await client.connect(transport);
@@ -186,33 +188,23 @@ async function fetchMcpMetadata(
186
188
  export function registerA2bPublishMcpTool(
187
189
  server: McpServer,
188
190
  wallet: Wallet,
191
+ identityPk: PrivateKey | undefined,
189
192
  config: { disableBroadcasting: boolean },
190
193
  ) {
191
194
  server.tool(
192
195
  "wallet_a2bPublishMcp",
193
196
  "Publish an MCP tool configuration record on-chain via Ordinal inscription. This creates a permanent, immutable, and discoverable tool definition that can be accessed by other MCP servers. The tool is published as a JSON inscription with metadata and optional digital signatures for authenticity verification.",
194
- { args: a2bPublishMcpArgsSchema },
195
- async (
196
- { args }: { args: A2bPublishMcpArgs },
197
- extra: RequestHandlerExtra<ServerRequest, ServerNotification>,
198
- ) => {
197
+ { ...a2bPublishMcpArgsSchema.shape },
198
+ async ({ toolName, command, args, keywords, env, description, destinationAddress }) => {
199
199
  try {
200
- // Load optional identity key for sigma signing
201
- const identityKeyWif = process.env.IDENTITY_KEY_WIF;
202
- let identityPk: PrivateKey | undefined;
203
- if (identityKeyWif) {
204
- try {
205
- identityPk = PrivateKey.fromWif(identityKeyWif);
206
- } catch (e) {
207
- console.warn(
208
- "Warning: Invalid IDENTITY_KEY_WIF environment variable; sigma signing disabled",
209
- e,
210
- );
211
- }
200
+ if (!identityPk) {
201
+ console.warn(
202
+ "Warning: No identity key provided; sigma signing disabled for A2B MCP publish",
203
+ );
212
204
  }
213
205
 
214
- const paymentPk = wallet.getPrivateKey();
215
- if (!paymentPk) throw new Error("No private key available");
206
+ const paymentPk = wallet.getPaymentKey();
207
+ if (!paymentPk) throw new Error("No payment private key available");
216
208
 
217
209
  const { paymentUtxos } = await wallet.getUtxos();
218
210
  if (!paymentUtxos?.length)
@@ -221,20 +213,20 @@ export function registerA2bPublishMcpTool(
221
213
  const walletAddress = paymentPk.toAddress().toString();
222
214
 
223
215
  // Fetch MCP metadata (tools, prompts, resources)
224
- const metadata = await fetchMcpMetadata(args.command, args.args);
216
+ const metadata = await fetchMcpMetadata(command, args);
225
217
  // console.log(`Discovered ${metadata.tools.length} tools, ${metadata.prompts.length} prompts, and ${metadata.resources.length} resources`);
226
218
 
227
219
  // Assemble tool configuration
228
220
  const toolConfig: McpConfig = {
229
- command: args.command,
230
- args: args.args,
221
+ command,
222
+ args,
231
223
  tools: metadata.tools,
232
224
  prompts: metadata.prompts,
233
225
  resources: metadata.resources,
234
- env: args.env
235
- ? args.env.reduce(
236
- (acc, { key, description }) => {
237
- acc[key] = description;
226
+ env: env
227
+ ? env.reduce(
228
+ (acc, { key, description: desc }) => {
229
+ acc[key] = desc;
238
230
  return acc;
239
231
  },
240
232
  {} as Record<string, string>,
@@ -248,9 +240,9 @@ export function registerA2bPublishMcpTool(
248
240
  // Prepare the full configuration with metadata
249
241
  const fullConfig = {
250
242
  mcpServers: {
251
- [args.toolName]: {
252
- description: args.description || "",
253
- keywords: args.keywords || [],
243
+ [toolName]: {
244
+ description: description || "",
245
+ keywords: keywords || [],
254
246
  tools: metadata.tools || [],
255
247
  prompts: metadata.prompts || [],
256
248
  resources: metadata.resources || [],
@@ -269,7 +261,7 @@ export function registerA2bPublishMcpTool(
269
261
  };
270
262
 
271
263
  // Destination for the ordinal
272
- const targetAddress = args.destinationAddress ?? walletAddress;
264
+ const targetAddress = destinationAddress ?? walletAddress;
273
265
  const destinations: Destination[] = [
274
266
  { address: targetAddress, inscription },
275
267
  ];
@@ -284,20 +276,27 @@ export function registerA2bPublishMcpTool(
284
276
  changeAddress: walletAddress,
285
277
  metaData,
286
278
  } as CreateOrdinalsConfig;
279
+
280
+ // Use the passed identityPk for signing if available
287
281
  if (identityPk) {
288
282
  createOrdinalsConfig.signer = {
289
283
  idKey: identityPk,
290
284
  } as LocalSigner;
291
285
  }
292
- // Inscribe the ordinal on-chain via js-1sat-ord
293
- const result = await createOrdinals(createOrdinalsConfig);
294
286
 
295
- const changeResult = result as ChangeResult;
287
+ // Inscribe the ordinal on-chain via js-1sat-ord
288
+ let result: ChangeResult | undefined;
289
+ if (!config.disableBroadcasting) {
290
+ result = await createOrdinals(createOrdinalsConfig);
291
+ } else {
292
+ console.warn("Broadcasting disabled. Transaction not created.");
293
+ }
296
294
 
297
295
  // Broadcast the transaction
298
- if (!config.disableBroadcasting) {
299
- await changeResult.tx.broadcast();
300
- const txid = changeResult.tx.id("hex");
296
+ if (!config.disableBroadcasting && result) {
297
+ const broadcaster = new V5Broadcaster();
298
+ await result.tx.broadcast(broadcaster);
299
+ const txid = result.tx.id("hex");
301
300
 
302
301
  setTimeout(async () => {
303
302
  // Call the ingest endpoint to process the transaction
@@ -331,12 +330,12 @@ export function registerA2bPublishMcpTool(
331
330
  txid,
332
331
  outpoint,
333
332
  onchainUrl,
334
- toolName: args.toolName,
333
+ toolName,
335
334
  toolCount: metadata.tools.length,
336
335
  promptCount: metadata.prompts.length,
337
336
  resourceCount: metadata.resources.length,
338
337
  description:
339
- args.description || `MCP Tool: ${args.toolName}`,
338
+ description || `MCP Tool: ${toolName}`,
340
339
  address: targetAddress,
341
340
  },
342
341
  null,
@@ -350,7 +349,7 @@ export function registerA2bPublishMcpTool(
350
349
  content: [
351
350
  {
352
351
  type: "text",
353
- text: changeResult.tx.toHex(),
352
+ text: result?.tx.toHex() || "",
354
353
  },
355
354
  ],
356
355
  };
@@ -1,12 +1,6 @@
1
1
  import { PrivateKey } from "@bsv/sdk";
2
2
  import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
3
- import type { RequestHandlerExtra } from "@modelcontextprotocol/sdk/shared/protocol.js";
4
- import type {
5
- CallToolResult,
6
- ServerNotification,
7
- ServerRequest,
8
- } from "@modelcontextprotocol/sdk/types.js";
9
- import { createOrdinals } from "js-1sat-ord";
3
+ import type { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
10
4
  import type {
11
5
  ChangeResult,
12
6
  CreateOrdinalsCollectionItemMetadata,
@@ -17,8 +11,9 @@ import type {
17
11
  LocalSigner,
18
12
  PreMAP,
19
13
  } from "js-1sat-ord";
20
- import { Sigma } from "sigma-protocol";
14
+ import { createOrdinals } from "js-1sat-ord";
21
15
  import { z } from "zod";
16
+ import { V5Broadcaster } from "../../utils/broadcaster";
22
17
  import type { Wallet } from "./wallet";
23
18
 
24
19
  /**
@@ -52,11 +47,8 @@ export function registerCreateOrdinalsTool(server: McpServer, wallet: Wallet) {
52
47
  server.tool(
53
48
  "wallet_createOrdinals",
54
49
  "Creates and inscribes ordinals (NFTs) on the Bitcoin SV blockchain. This tool lets you mint new digital artifacts by encoding data directly into the blockchain. Supports various content types including images, text, JSON, and HTML. The tool handles transaction creation, fee calculation, and broadcasting.",
55
- { args: createOrdinalsArgsSchema },
56
- async (
57
- { args }: { args: CreateOrdinalsArgs },
58
- extra: RequestHandlerExtra<ServerRequest, ServerNotification>,
59
- ): Promise<CallToolResult> => {
50
+ { ...createOrdinalsArgsSchema.shape },
51
+ async ({ dataB64, contentType, destinationAddress, metadata }): Promise<CallToolResult> => {
60
52
  try {
61
53
  // Load optional identity key for sigma signing
62
54
  const identityKeyWif = process.env.IDENTITY_KEY_WIF;
@@ -91,14 +83,14 @@ export function registerCreateOrdinalsTool(server: McpServer, wallet: Wallet) {
91
83
 
92
84
  // 4. Create the inscription object
93
85
  const inscription: Inscription = {
94
- dataB64: args.dataB64,
95
- contentType: args.contentType,
86
+ dataB64: dataB64,
87
+ contentType: contentType,
96
88
  };
97
89
 
98
90
  // 5. Create the destination
99
91
  const destinations: Destination[] = [
100
92
  {
101
- address: args.destinationAddress || walletAddress,
93
+ address: destinationAddress || walletAddress,
102
94
  inscription,
103
95
  },
104
96
  ];
@@ -108,7 +100,7 @@ export function registerCreateOrdinalsTool(server: McpServer, wallet: Wallet) {
108
100
  destinations,
109
101
  paymentPk,
110
102
  changeAddress: walletAddress,
111
- metaData: args.metadata as
103
+ metaData: metadata as
112
104
  | PreMAP
113
105
  | CreateOrdinalsCollectionMetadata
114
106
  | CreateOrdinalsCollectionItemMetadata,
@@ -129,7 +121,8 @@ export function registerCreateOrdinalsTool(server: McpServer, wallet: Wallet) {
129
121
 
130
122
  const disableBroadcasting = process.env.DISABLE_BROADCASTING === "true";
131
123
  if (!disableBroadcasting) {
132
- await changeResult.tx.broadcast();
124
+ const broadcaster = new V5Broadcaster();
125
+ await changeResult.tx.broadcast(broadcaster);
133
126
 
134
127
  // 8. Refresh the wallet's UTXOs after spending
135
128
  try {
@@ -150,8 +143,8 @@ export function registerCreateOrdinalsTool(server: McpServer, wallet: Wallet) {
150
143
  txid: changeResult.tx.id("hex"),
151
144
  spentOutpoints: changeResult.spentOutpoints,
152
145
  payChange: changeResult.payChange,
153
- inscriptionAddress: args.destinationAddress || walletAddress,
154
- contentType: args.contentType,
146
+ inscriptionAddress: destinationAddress || walletAddress,
147
+ contentType: contentType,
155
148
  }),
156
149
  },
157
150
  ],
@@ -1,6 +1,9 @@
1
1
  import { P2PKH, Transaction, Utils } from "@bsv/sdk";
2
2
  import type { Utxo } from "js-1sat-ord";
3
- const { toBase64 } = Utils;
3
+ import { V5_API_URL } from "../constants";
4
+ import { getBeefTransactionById, getTransactionById } from "./utxo";
5
+
6
+ const { toBase64, toHex, toArray } = Utils;
4
7
 
5
8
  /**
6
9
  * Type definition for WhatsOnChain UTXO response
@@ -56,13 +59,10 @@ export async function fetchPaymentUtxos(
56
59
  const utxos: (Utxo | null)[] = await Promise.all(
57
60
  data.map(async (utxo: WhatsOnChainUtxo) => {
58
61
  // Get the transaction hex to extract the correct script
59
- const script = await getScriptFromTransaction(
60
- utxo.tx_hash,
61
- utxo.tx_pos,
62
- );
63
-
62
+ const tx = await getBeefTransactionById(utxo.tx_hash);
63
+ const script = tx?.outputs[utxo.tx_pos]?.lockingScript.toHex();
64
64
  if (!script) {
65
- console.warn(
65
+ console.error(
66
66
  `Could not get script for UTXO: ${utxo.tx_hash}:${utxo.tx_pos}`,
67
67
  );
68
68
  return null;
@@ -72,7 +72,7 @@ export async function fetchPaymentUtxos(
72
72
  txid: utxo.tx_hash,
73
73
  vout: utxo.tx_pos,
74
74
  satoshis: utxo.value,
75
- script: script,
75
+ script,
76
76
  };
77
77
  }),
78
78
  );
@@ -87,44 +87,53 @@ export async function fetchPaymentUtxos(
87
87
  }
88
88
  }
89
89
 
90
- /**
91
- * Gets the script from a transaction for a specific output index
92
- *
93
- * @param txid - The transaction ID
94
- * @param vout - The output index
95
- * @returns The script as hex string or undefined if an error occurs
96
- */
97
- async function getScriptFromTransaction(
98
- txid: string,
99
- vout: number,
100
- ): Promise<string | undefined> {
90
+ type V5Utxo = {
91
+ outpoint: string;
92
+ height: number;
93
+ idx: number;
94
+ satoshis: number;
95
+ script: string;
96
+ owners: string[];
97
+ data: Record<string, unknown>;
98
+ };
99
+
100
+ export async function fetchPaymentUtxosFromV5(
101
+ address: string,
102
+ ): Promise<Utxo[] | undefined> {
101
103
  try {
102
- const response = await fetch(
103
- `https://api.whatsonchain.com/v1/bsv/main/tx/${txid}/hex`,
104
- );
105
-
104
+ const url = `${V5_API_URL}/own/${address}/utxos?refresh=true&txo=true&script=true&limit=250&tags=p2pkh`;
105
+ console.error(`Fetching UTXOs from V5: ${url}`);
106
+ const response = await fetch(url);
106
107
  if (!response.ok) {
107
- console.error(
108
- `WhatsOnChain API error fetching tx hex: ${response.status} ${response.statusText}`,
109
- );
108
+ console.error(`V5 UTXO fetch failed: ${response.status} ${response.statusText}`);
110
109
  return undefined;
111
110
  }
112
-
113
- const txHex = await response.text();
114
- const tx = Transaction.fromHex(txHex);
115
- const output = tx.outputs[vout];
116
-
117
- if (!output) {
118
- console.error(`Output index ${vout} not found in transaction ${txid}`);
111
+ const data = (await response.json()) as V5Utxo[] | null;
112
+ if (!Array.isArray(data)) {
113
+ console.error(`V5 UTXO response was not an array: ${typeof data}`);
119
114
  return undefined;
120
115
  }
121
-
122
- return toBase64(output.lockingScript.toBinary());
116
+ const utxos: Utxo[] = [];
117
+ for (const utxo of data) {
118
+ if (!utxo.data.p2pkh) {
119
+ // Skip if not a P2PKH
120
+ continue;
121
+ }
122
+ const [txid, vout] = utxo.outpoint.split("_");
123
+ if (!txid || !vout) {
124
+ console.error(`Invalid outpoint: ${utxo.outpoint}`);
125
+ continue;
126
+ }
127
+ utxos.push({
128
+ txid,
129
+ vout: Number.parseInt(vout),
130
+ satoshis: utxo.satoshis,
131
+ script: toHex(toArray(utxo.script, "base64")),
132
+ } as Utxo);
133
+ }
134
+ return utxos;
123
135
  } catch (error) {
124
- console.error(
125
- `Error getting script for transaction ${txid}:${vout}:`,
126
- error,
127
- );
136
+ console.error("Error fetching payment UTXOs from V5:", error);
128
137
  return undefined;
129
138
  }
130
139
  }