bsv-mcp 0.0.21 → 0.0.23

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.
@@ -1,7 +1,11 @@
1
1
  import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
2
  import type { ToolCallback } from "@modelcontextprotocol/sdk/server/mcp.js";
3
3
  import type { RequestHandlerExtra } from "@modelcontextprotocol/sdk/shared/protocol.js";
4
- import type { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
4
+ import type {
5
+ CallToolResult,
6
+ ServerNotification,
7
+ ServerRequest,
8
+ } from "@modelcontextprotocol/sdk/types.js";
5
9
  import type { z } from "zod";
6
10
  import type {
7
11
  abortActionArgsSchema,
@@ -34,6 +38,7 @@ import {
34
38
  walletEncryptionArgsSchema,
35
39
  } from "./schemas";
36
40
 
41
+ import { Utils, type WalletProtocol } from "@bsv/sdk";
37
42
  import { registerCreateOrdinalsTool } from "./createOrdinals";
38
43
  import type { createOrdinalsArgsSchema } from "./createOrdinals";
39
44
  import { registerGetAddressTool } from "./getAddress";
@@ -41,7 +46,6 @@ import { registerPurchaseListingTool } from "./purchaseListing";
41
46
  import { registerSendToAddressTool } from "./sendToAddress";
42
47
  import { registerTransferOrdTokenTool } from "./transferOrdToken";
43
48
  import type { transferOrdTokenArgsSchema } from "./transferOrdToken";
44
- import { Utils, type WalletProtocol } from "@bsv/sdk";
45
49
 
46
50
  // Define mapping from tool names to argument schemas
47
51
  type ToolArgSchemas = {
@@ -79,7 +83,7 @@ type ToolArgSchemas = {
79
83
  // Define a type for the handler function with proper argument types
80
84
  type ToolHandler = (
81
85
  params: { args: unknown },
82
- extra: RequestHandlerExtra,
86
+ extra: RequestHandlerExtra<ServerRequest, ServerNotification>,
83
87
  ) => Promise<CallToolResult>;
84
88
 
85
89
  // Define a map type for tool name to handler functions
@@ -127,7 +131,7 @@ export function registerWalletTools(
127
131
  { args: getPublicKeyArgsSchema },
128
132
  async (
129
133
  { args }: { args: z.infer<typeof getPublicKeyArgsSchema> },
130
- extra: RequestHandlerExtra,
134
+ extra: RequestHandlerExtra<ServerRequest, ServerNotification>,
131
135
  ) => {
132
136
  try {
133
137
  const result = await wallet.getPublicKey(args);
@@ -146,7 +150,7 @@ export function registerWalletTools(
146
150
  { args: createSignatureArgsSchema },
147
151
  async (
148
152
  { args }: { args: z.infer<typeof createSignatureArgsSchema> },
149
- extra: RequestHandlerExtra,
153
+ extra: RequestHandlerExtra<ServerRequest, ServerNotification>,
150
154
  ) => {
151
155
  try {
152
156
  const result = await wallet.createSignature(args);
@@ -165,7 +169,7 @@ export function registerWalletTools(
165
169
  { args: verifySignatureArgsSchema },
166
170
  async (
167
171
  { args }: { args: z.infer<typeof verifySignatureArgsSchema> },
168
- extra: RequestHandlerExtra,
172
+ extra: RequestHandlerExtra<ServerRequest, ServerNotification>,
169
173
  ) => {
170
174
  try {
171
175
  const result = await wallet.verifySignature(args);
@@ -181,33 +185,33 @@ export function registerWalletTools(
181
185
  registerTool(
182
186
  "wallet_encryption",
183
187
  "Combined tool for encrypting and decrypting data using the wallet's cryptographic keys.\n\n" +
184
- "PARAMETERS:\n" +
185
- "- mode: (required) Either \"encrypt\" to encrypt plaintext or \"decrypt\" to decrypt ciphertext\n" +
186
- "- data: (required) Text string or array of numbers to process\n" +
187
- "- encoding: (optional) For text input, the encoding format (utf8, hex, base64) - default is utf8\n\n" +
188
- "EXAMPLES:\n" +
189
- "1. Encrypt text data:\n" +
190
- " {\n" +
191
- " \"mode\": \"encrypt\",\n" +
192
- " \"data\": \"Hello World\"\n" +
193
- " }\n\n" +
194
- "2. Decrypt previously encrypted data:\n" +
195
- " {\n" +
196
- " \"mode\": \"decrypt\",\n" +
197
- " \"data\": [encrypted bytes from previous response]\n" +
198
- " }",
188
+ "PARAMETERS:\n" +
189
+ '- mode: (required) Either "encrypt" to encrypt plaintext or "decrypt" to decrypt ciphertext\n' +
190
+ "- data: (required) Text string or array of numbers to process\n" +
191
+ "- encoding: (optional) For text input, the encoding format (utf8, hex, base64) - default is utf8\n\n" +
192
+ "EXAMPLES:\n" +
193
+ "1. Encrypt text data:\n" +
194
+ " {\n" +
195
+ ' "mode": "encrypt",\n' +
196
+ ' "data": "Hello World"\n' +
197
+ " }\n\n" +
198
+ "2. Decrypt previously encrypted data:\n" +
199
+ " {\n" +
200
+ ' "mode": "decrypt",\n' +
201
+ ' "data": [encrypted bytes from previous response]\n' +
202
+ " }",
199
203
  { args: walletEncryptionArgsSchema },
200
204
  async (
201
205
  { args }: { args: z.infer<typeof walletEncryptionArgsSchema> },
202
- extra: RequestHandlerExtra,
206
+ extra: RequestHandlerExtra<ServerRequest, ServerNotification>,
203
207
  ) => {
204
208
  try {
205
209
  const { mode, data, encoding } = args;
206
-
210
+
207
211
  // Set default values for required parameters
208
212
  const protocolID: WalletProtocol = [1, "aes256"];
209
213
  const keyID = "default";
210
-
214
+
211
215
  // Convert string data to binary if needed
212
216
  let binaryData: number[];
213
217
  if (Array.isArray(data)) {
@@ -217,8 +221,9 @@ export function registerWalletTools(
217
221
  const { toArray } = Utils;
218
222
  binaryData = toArray(data, encoding || "utf8");
219
223
  }
220
-
221
- let result: { ciphertext?: number[]; plaintext?: number[] | string } = {};
224
+
225
+ let result: { ciphertext?: number[]; plaintext?: number[] | string } =
226
+ {};
222
227
  if (mode === "encrypt") {
223
228
  result = await wallet.encrypt({
224
229
  plaintext: binaryData,
@@ -231,7 +236,7 @@ export function registerWalletTools(
231
236
  protocolID,
232
237
  keyID,
233
238
  });
234
-
239
+
235
240
  // For decryption, convert plaintext back to string if it's likely UTF-8 text
236
241
  if (result.plaintext as number[]) {
237
242
  try {
@@ -239,11 +244,13 @@ export function registerWalletTools(
239
244
  const textResult = toUTF8(result.plaintext as number[]);
240
245
  // If conversion succeeds and seems like valid text, return as string
241
246
  if (textResult && textResult.length > 0) {
242
- return {
243
- content: [{
244
- type: "text",
245
- text: JSON.stringify({ plaintext: textResult })
246
- }]
247
+ return {
248
+ content: [
249
+ {
250
+ type: "text",
251
+ text: JSON.stringify({ plaintext: textResult }),
252
+ },
253
+ ],
247
254
  };
248
255
  }
249
256
  } catch (e) {
@@ -251,13 +258,19 @@ export function registerWalletTools(
251
258
  }
252
259
  }
253
260
  }
254
-
261
+
255
262
  return { content: [{ type: "text", text: JSON.stringify(result) }] };
256
263
  } catch (error) {
257
- const errorMessage = error instanceof Error ? error.message : String(error);
258
- return {
259
- content: [{ type: "text", text: `Error during ${args.mode}: ${errorMessage}` }],
260
- isError: true
264
+ const errorMessage =
265
+ error instanceof Error ? error.message : String(error);
266
+ return {
267
+ content: [
268
+ {
269
+ type: "text",
270
+ text: `Error during ${args.mode}: ${errorMessage}`,
271
+ },
272
+ ],
273
+ isError: true,
261
274
  };
262
275
  }
263
276
  },
@@ -265,6 +278,6 @@ export function registerWalletTools(
265
278
 
266
279
  // Register createOrdinals tool
267
280
  registerCreateOrdinalsTool(server, wallet);
268
-
281
+
269
282
  return handlers;
270
283
  }
@@ -0,0 +1,138 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import type { RequestHandlerExtra } from "@modelcontextprotocol/sdk/shared/protocol.js";
3
+ import type {
4
+ ServerNotification,
5
+ ServerRequest,
6
+ } from "@modelcontextprotocol/sdk/types.js";
7
+ import {
8
+ type Distribution,
9
+ type Payment,
10
+ type TokenChangeResult,
11
+ TokenInputMode,
12
+ TokenSelectionStrategy,
13
+ TokenType,
14
+ type TokenUtxo,
15
+ type TransferOrdTokensConfig,
16
+ type Utxo,
17
+ selectTokenUtxos,
18
+ transferOrdTokens,
19
+ } from "js-1sat-ord";
20
+ import { z } from "zod";
21
+ import type { Wallet } from "./wallet";
22
+
23
+ // Schema for BSV-20/BSV-21 token transfer arguments
24
+ export const transferOrdTokenArgsSchema = z.object({
25
+ protocol: z.enum(["bsv-20", "bsv-21"]),
26
+ tokenID: z.string(),
27
+ sendAmount: z.number(),
28
+ paymentUtxos: z.array(
29
+ z.object({
30
+ txid: z.string(),
31
+ vout: z.number(),
32
+ satoshis: z.number(),
33
+ script: z.string(),
34
+ }),
35
+ ),
36
+ tokenUtxos: z.array(
37
+ z.object({
38
+ txid: z.string(),
39
+ vout: z.number(),
40
+ satoshis: z.literal(1),
41
+ script: z.string(),
42
+ amt: z.string(),
43
+ id: z.string(),
44
+ }),
45
+ ),
46
+ distributions: z.array(z.object({ address: z.string(), tokens: z.number() })),
47
+ decimals: z.number(),
48
+ additionalPayments: z
49
+ .array(z.object({ to: z.string(), amount: z.number() }))
50
+ .optional(),
51
+ });
52
+ export type TransferOrdTokenArgs = z.infer<typeof transferOrdTokenArgsSchema>;
53
+
54
+ /**
55
+ * Register the wallet_transferOrdToken tool for transferring BSV tokens.
56
+ */
57
+ export function registerTransferOrdTokenTool(
58
+ server: McpServer,
59
+ wallet: Wallet,
60
+ ) {
61
+ server.tool(
62
+ "wallet_transferOrdToken",
63
+ "Transfers BSV-20 or BSV-21 tokens from your wallet via js-1sat-ord transferOrdTokens.",
64
+ { args: transferOrdTokenArgsSchema },
65
+ async (
66
+ { args }: { args: TransferOrdTokenArgs },
67
+ extra: RequestHandlerExtra<ServerRequest, ServerNotification>,
68
+ ) => {
69
+ try {
70
+ // fetch keys
71
+ const paymentPk = wallet.getPrivateKey();
72
+ if (!paymentPk) throw new Error("No private key available");
73
+ const ordPk = paymentPk;
74
+ const changeAddress = paymentPk.toAddress().toString();
75
+ const ordAddress = changeAddress;
76
+
77
+ // select token UTXOs
78
+ const { selectedUtxos: inputTokens } = selectTokenUtxos(
79
+ args.tokenUtxos as TokenUtxo[],
80
+ args.sendAmount,
81
+ args.decimals,
82
+ {
83
+ inputStrategy: TokenSelectionStrategy.SmallestFirst,
84
+ outputStrategy: TokenSelectionStrategy.LargestFirst,
85
+ },
86
+ );
87
+
88
+ // build config
89
+ const config: TransferOrdTokensConfig = {
90
+ protocol:
91
+ args.protocol === "bsv-20" ? TokenType.BSV20 : TokenType.BSV21,
92
+ tokenID: args.tokenID,
93
+ utxos: args.paymentUtxos as Utxo[],
94
+ inputTokens,
95
+ distributions: args.distributions as Distribution[],
96
+ tokenChangeAddress: ordAddress,
97
+ changeAddress,
98
+ paymentPk,
99
+ ordPk,
100
+ additionalPayments: (args.additionalPayments as Payment[]) || [],
101
+ decimals: args.decimals,
102
+ inputMode: TokenInputMode.Needed,
103
+ splitConfig: {
104
+ outputs: inputTokens.length === 1 ? 2 : 1,
105
+ threshold: args.sendAmount,
106
+ },
107
+ };
108
+
109
+ // execute transfer
110
+ const result: TokenChangeResult = await transferOrdTokens(config);
111
+ await result.tx.broadcast();
112
+
113
+ // refresh UTXOs
114
+ try {
115
+ await wallet.refreshUtxos();
116
+ } catch {}
117
+
118
+ // respond
119
+ return {
120
+ content: [
121
+ {
122
+ type: "text",
123
+ text: JSON.stringify({
124
+ txid: result.tx.id("hex"),
125
+ spentOutpoints: result.spentOutpoints,
126
+ payChange: result.payChange,
127
+ tokenChange: result.tokenChange,
128
+ }),
129
+ },
130
+ ],
131
+ };
132
+ } catch (err: unknown) {
133
+ const msg = err instanceof Error ? err.message : String(err);
134
+ return { content: [{ type: "text", text: msg }], isError: true };
135
+ }
136
+ },
137
+ );
138
+ }
@@ -5,12 +5,7 @@
5
5
  *
6
6
  * See: https://github.com/bitcoin-sv/ts-sdk/blob/main/src/wallet/Wallet.interfaces.ts
7
7
  */
8
- import {
9
- LockingScript,
10
- PrivateKey,
11
- ProtoWallet,
12
- Transaction,
13
- } from "@bsv/sdk";
8
+ import { LockingScript, PrivateKey, ProtoWallet, Transaction } from "@bsv/sdk";
14
9
  import type {
15
10
  AbortActionArgs,
16
11
  AbortActionResult,
@@ -87,13 +82,12 @@ export class Wallet extends ProtoWallet implements WalletInterface {
87
82
  }
88
83
 
89
84
  const address = privateKey.toAddress();
90
-
85
+
91
86
  const utxos = await fetchPayUtxos(address);
92
87
  const nftUtxos = await fetchNftUtxos(address);
93
88
  this.paymentUtxos = utxos;
94
89
  this.nftUtxos = nftUtxos;
95
90
  this.lastUtxoFetch = Date.now();
96
-
97
91
  } catch (error) {
98
92
  console.error("Error refreshing UTXOs:", error);
99
93
  throw error;
@@ -130,7 +124,7 @@ export class Wallet extends ProtoWallet implements WalletInterface {
130
124
  if (!privateKey) {
131
125
  throw new Error("No private key available");
132
126
  }
133
-
127
+
134
128
  const publicKey = privateKey.toPublicKey();
135
129
  return {
136
130
  publicKey: publicKey.toDER("hex") as PubKeyHex,
@@ -1,103 +0,0 @@
1
- import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
- import type { RequestHandlerExtra } from "@modelcontextprotocol/sdk/shared/protocol.js";
3
- import { z } from "zod";
4
- import {
5
- transferOrdTokens,
6
- TokenType,
7
- TokenInputMode,
8
- TokenSelectionStrategy,
9
- selectTokenUtxos,
10
- type TransferOrdTokensConfig,
11
- type Utxo,
12
- type TokenUtxo,
13
- type Payment,
14
- type Distribution,
15
- type TokenChangeResult,
16
- } from "js-1sat-ord";
17
- import type { Wallet } from "../wallet";
18
-
19
- // Schema for BSV-20/BSV-21 token transfer arguments
20
- export const transferOrdTokenArgsSchema = z.object({
21
- protocol: z.enum(["bsv-20", "bsv-21"]),
22
- tokenID: z.string(),
23
- sendAmount: z.number(),
24
- paymentUtxos: z.array(
25
- z.object({ txid: z.string(), vout: z.number(), satoshis: z.number(), script: z.string() })
26
- ),
27
- tokenUtxos: z.array(
28
- z.object({ txid: z.string(), vout: z.number(), satoshis: z.literal(1), script: z.string(), amt: z.string(), id: z.string() })
29
- ),
30
- distributions: z.array(z.object({ address: z.string(), tokens: z.number() })),
31
- decimals: z.number(),
32
- additionalPayments: z.array(z.object({ to: z.string(), amount: z.number() })).optional(),
33
- });
34
- export type TransferOrdTokenArgs = z.infer<typeof transferOrdTokenArgsSchema>;
35
-
36
- /**
37
- * Register the wallet_transferOrdToken tool for transferring BSV tokens.
38
- */
39
- export function registerTransferOrdTokenTool(server: McpServer, wallet: Wallet) {
40
- server.tool(
41
- "wallet_transferOrdToken",
42
- "Transfers BSV-20 or BSV-21 tokens from your wallet via js-1sat-ord transferOrdTokens.",
43
- { args: transferOrdTokenArgsSchema },
44
- async (
45
- { args }: { args: TransferOrdTokenArgs },
46
- extra: RequestHandlerExtra
47
- ) => {
48
- try {
49
- // fetch keys
50
- const paymentPk = wallet.getPrivateKey();
51
- if (!paymentPk) throw new Error("No private key available");
52
- const ordPk = paymentPk;
53
- const changeAddress = paymentPk.toAddress().toString();
54
- const ordAddress = changeAddress;
55
-
56
- // select token UTXOs
57
- const { selectedUtxos: inputTokens } = selectTokenUtxos(
58
- args.tokenUtxos as TokenUtxo[],
59
- args.sendAmount,
60
- args.decimals,
61
- { inputStrategy: TokenSelectionStrategy.SmallestFirst, outputStrategy: TokenSelectionStrategy.LargestFirst }
62
- );
63
-
64
- // build config
65
- const config: TransferOrdTokensConfig = {
66
- protocol: args.protocol === "bsv-20" ? TokenType.BSV20 : TokenType.BSV21,
67
- tokenID: args.tokenID,
68
- utxos: args.paymentUtxos as Utxo[],
69
- inputTokens,
70
- distributions: args.distributions as Distribution[],
71
- tokenChangeAddress: ordAddress,
72
- changeAddress,
73
- paymentPk,
74
- ordPk,
75
- additionalPayments: args.additionalPayments as Payment[] || [],
76
- decimals: args.decimals,
77
- inputMode: TokenInputMode.Needed,
78
- splitConfig: { outputs: inputTokens.length === 1 ? 2 : 1, threshold: args.sendAmount },
79
- };
80
-
81
- // execute transfer
82
- const result: TokenChangeResult = await transferOrdTokens(config);
83
- await result.tx.broadcast();
84
-
85
- // refresh UTXOs
86
- try { await wallet.refreshUtxos(); } catch {}
87
-
88
- // respond
89
- return {
90
- content: [{ type: "text", text: JSON.stringify({
91
- txid: result.tx.id("hex"),
92
- spentOutpoints: result.spentOutpoints,
93
- payChange: result.payChange,
94
- tokenChange: result.tokenChange,
95
- }) }]
96
- };
97
- } catch (err: unknown) {
98
- const msg = err instanceof Error ? err.message : String(err);
99
- return { content: [{ type: "text", text: msg }], isError: true };
100
- }
101
- }
102
- );
103
- }