bsv-mcp 0.0.35 → 0.2.0

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 (71) hide show
  1. package/CHANGELOG.md +112 -1
  2. package/README.md +233 -4
  3. package/dist/index.js +111762 -0
  4. package/index.ts +804 -86
  5. package/next-env.d.ts +6 -0
  6. package/package.json +57 -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/bap/friend.ts +267 -0
  16. package/tools/bap/generate.test.ts +350 -0
  17. package/tools/bap/generate.ts +433 -0
  18. package/tools/bap/getCurrentAddress.ts +76 -0
  19. package/tools/bap/getId.test.ts +121 -0
  20. package/tools/bap/getId.ts +169 -0
  21. package/tools/bap/index.ts +111 -0
  22. package/tools/bap/types.ts +66 -0
  23. package/tools/bap/utils.ts +33 -0
  24. package/tools/bigblocks/components.ts +304 -0
  25. package/tools/bigblocks/docs.ts +488 -0
  26. package/tools/bigblocks/examples.ts +527 -0
  27. package/tools/bigblocks/generator.ts +485 -0
  28. package/tools/bigblocks/index.ts +23 -0
  29. package/tools/bsocial/bigblocksApiClient.ts +189 -0
  30. package/tools/bsocial/bmapFollow.ts +125 -0
  31. package/tools/bsocial/bmapLikes.ts +111 -0
  32. package/tools/bsocial/bmapReadPosts.ts +148 -0
  33. package/tools/bsocial/createPost.ts +136 -0
  34. package/tools/bsocial/index.ts +45 -0
  35. package/tools/bsocial/readPosts.ts +255 -0
  36. package/tools/bsv/decodeTransaction.ts +5 -1
  37. package/tools/constants.ts +21 -0
  38. package/tools/index.ts +103 -17
  39. package/tools/mnee/index.ts +8 -0
  40. package/tools/mnee/parseTx.ts +1 -1
  41. package/tools/mnee/sendMnee.ts +1 -1
  42. package/tools/utils/aip.ts +46 -0
  43. package/tools/utils/conversion.ts +1 -0
  44. package/tools/utils/errorHandler.ts +76 -0
  45. package/tools/utils/index.ts +45 -1
  46. package/tools/utils/installAgentMaster.ts +84 -0
  47. package/tools/utils/logger.ts +20 -0
  48. package/tools/utils/toolConfig.ts +80 -0
  49. package/tools/utils/toolRegistration.ts +66 -0
  50. package/tools/utils/transactionBuilder.ts +196 -0
  51. package/tools/wallet/a2bPublishAgent.ts +8 -4
  52. package/tools/wallet/a2bPublishMcp.ts +28 -24
  53. package/tools/wallet/createOrdinals.ts +4 -2
  54. package/tools/wallet/fetchPaymentUtxos.ts +44 -43
  55. package/tools/wallet/gatherCollectionInfo.ts +374 -0
  56. package/tools/wallet/getAddress.ts +13 -21
  57. package/tools/wallet/getBalance.ts +66 -0
  58. package/tools/wallet/getBalanceDroplet.ts +59 -0
  59. package/tools/wallet/integratedWallet.ts +120 -0
  60. package/tools/wallet/mintCollection.ts +378 -0
  61. package/tools/wallet/purchaseListing.ts +4 -4
  62. package/tools/wallet/schemas.ts +18 -249
  63. package/tools/wallet/sendOrdinals.ts +4 -2
  64. package/tools/wallet/sendToAddress.ts +5 -1
  65. package/tools/wallet/setupDroplet.ts +172 -0
  66. package/tools/wallet/tools.ts +71 -156
  67. package/tools/wallet/transactionHelper.ts +121 -0
  68. package/tools/wallet/transferOrdToken.ts +7 -4
  69. package/tools/wallet/utxo.ts +62 -0
  70. package/tools/wallet/wallet.ts +237 -234
  71. package/tools/a2b/call.ts +0 -32
@@ -0,0 +1,255 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import type { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
3
+ import type {
4
+ ApiResponse,
5
+ BapIdentity,
6
+ PostMeta,
7
+ PostResponse,
8
+ PostsResponse,
9
+ PostTransaction,
10
+ } from "bmap-api-types";
11
+ import { z } from "zod";
12
+ import { BMAP_URL } from "../constants";
13
+
14
+ // Schema for reading posts
15
+ const readPostsArgsSchema = z.object({
16
+ bapId: z
17
+ .string()
18
+ .optional()
19
+ .describe("BAP identity key to filter posts by author"),
20
+ txid: z.string().optional().describe("Specific transaction ID to fetch"),
21
+ limit: z
22
+ .number()
23
+ .min(1)
24
+ .max(100)
25
+ .default(20)
26
+ .describe("Maximum number of posts to return"),
27
+ page: z
28
+ .number()
29
+ .min(1)
30
+ .default(1)
31
+ .describe("Page number for pagination (1-based)"),
32
+ feed: z
33
+ .boolean()
34
+ .default(false)
35
+ .describe("Whether to fetch feed (posts from followed users)"),
36
+ });
37
+
38
+ type ReadPostsArgs = z.infer<typeof readPostsArgsSchema>;
39
+
40
+ /**
41
+ * Read social posts from the BMAP API using official types
42
+ */
43
+ export async function readSocialPosts(args: ReadPostsArgs): Promise<{
44
+ success: boolean;
45
+ posts?: PostTransaction[];
46
+ signers?: BapIdentity[];
47
+ meta?: PostMeta[];
48
+ pagination?: {
49
+ page: number;
50
+ limit: number;
51
+ count: number;
52
+ };
53
+ error?: string;
54
+ }> {
55
+ try {
56
+ const { bapId, txid, limit, page, feed } = args;
57
+
58
+ // Handle single post request
59
+ if (txid) {
60
+ const response = await fetch(`${BMAP_URL}/post/${txid}`, {
61
+ method: "GET",
62
+ headers: {
63
+ "Content-Type": "application/json",
64
+ },
65
+ });
66
+
67
+ if (!response.ok) {
68
+ const errorText = await response.text();
69
+ return {
70
+ success: false,
71
+ error: `BMAP API request failed: ${response.status} ${errorText}`,
72
+ };
73
+ }
74
+
75
+ const data = (await response.json()) as ApiResponse<PostResponse>;
76
+
77
+ if (data.status !== "success") {
78
+ return {
79
+ success: false,
80
+ error: data.error || "Unknown API error",
81
+ };
82
+ }
83
+
84
+ return {
85
+ success: true,
86
+ posts: [data.result.post],
87
+ signers: data.result.signers,
88
+ meta: [data.result.meta],
89
+ };
90
+ }
91
+
92
+ // Handle posts list request
93
+ const params = new URLSearchParams();
94
+ if (bapId) params.append("bapId", bapId);
95
+ params.append("limit", limit.toString());
96
+ params.append("page", page.toString());
97
+ if (feed) params.append("feed", "true");
98
+
99
+ const response = await fetch(`${BMAP_URL}/posts?${params}`, {
100
+ method: "GET",
101
+ headers: {
102
+ "Content-Type": "application/json",
103
+ },
104
+ });
105
+
106
+ if (!response.ok) {
107
+ const errorText = await response.text();
108
+ return {
109
+ success: false,
110
+ error: `BMAP API request failed: ${response.status} ${errorText}`,
111
+ };
112
+ }
113
+
114
+ const data = (await response.json()) as ApiResponse<PostsResponse>;
115
+
116
+ if (data.status !== "success") {
117
+ return {
118
+ success: false,
119
+ error: data.error || "Unknown API error",
120
+ };
121
+ }
122
+
123
+ return {
124
+ success: true,
125
+ posts: data.data.results,
126
+ signers: data.data.signers,
127
+ meta: data.data.meta,
128
+ pagination: {
129
+ page: data.data.page,
130
+ limit: data.data.limit,
131
+ count: data.data.count,
132
+ },
133
+ };
134
+ } catch (error) {
135
+ const errorMessage = error instanceof Error ? error.message : String(error);
136
+ console.error("Error reading social posts:", errorMessage);
137
+ return {
138
+ success: false,
139
+ error: errorMessage,
140
+ };
141
+ }
142
+ }
143
+
144
+ /**
145
+ * Format a post transaction for display
146
+ */
147
+ function formatPost(
148
+ post: PostTransaction,
149
+ index: number,
150
+ signer?: BapIdentity,
151
+ meta?: PostMeta,
152
+ ): string {
153
+ const lines = [
154
+ `Post ${index + 1}:`,
155
+ ` TX ID: ${post.tx.h}`,
156
+ ` Content: ${post.B[0]?.content || "[No Content]"}`,
157
+ ` Content Type: ${post.B[0]?.["content-type"] || "text/plain"}`,
158
+ ` Timestamp: ${new Date(post.timestamp * 1000).toISOString()}`,
159
+ ];
160
+
161
+ // Add signer information
162
+ if (signer) {
163
+ lines.push(` Author: ${signer.displayName || signer.idKey}`);
164
+ if (signer.paymail) {
165
+ lines.push(` Paymail: ${signer.paymail}`);
166
+ }
167
+ }
168
+
169
+ // Add MAP data
170
+ const mapData = post.MAP.find((m) => m.type === "post");
171
+ if (mapData?.app) {
172
+ lines.push(` App: ${mapData.app}`);
173
+ }
174
+
175
+ // Add metadata
176
+ if (meta) {
177
+ if (meta.likes > 0) {
178
+ lines.push(` Likes: ${meta.likes}`);
179
+ }
180
+ if (meta.replies > 0) {
181
+ lines.push(` Replies: ${meta.replies}`);
182
+ }
183
+ if (meta.reactions.length > 0) {
184
+ const reactions = meta.reactions
185
+ .map((r) => `${r.emoji}(${r.count})`)
186
+ .join(" ");
187
+ lines.push(` Reactions: ${reactions}`);
188
+ }
189
+ }
190
+
191
+ return lines.join("\n");
192
+ }
193
+
194
+ /**
195
+ * Register the read posts tool with the MCP server
196
+ */
197
+ export function registerReadPostsTool(server: McpServer) {
198
+ server.tool(
199
+ "bsocial_readPosts",
200
+ "Read social posts from the BSV blockchain using BMAP API. Can fetch posts by author (BAP ID), specific post by transaction ID, or recent posts from all users. Supports pagination and feed functionality.",
201
+ { args: readPostsArgsSchema },
202
+ async ({ args }: { args: ReadPostsArgs }): Promise<CallToolResult> => {
203
+ try {
204
+ const result = await readSocialPosts(args);
205
+
206
+ if (result.success && result.posts) {
207
+ // Format posts for display
208
+ const formattedPosts = result.posts
209
+ .map((post, index) => {
210
+ const signer = result.signers?.find(
211
+ (s) => s.currentAddress === post.AIP[0]?.address,
212
+ );
213
+ const meta = result.meta?.[index];
214
+ return formatPost(post, index, signer, meta);
215
+ })
216
+ .join("\n\n");
217
+
218
+ let output =
219
+ result.posts.length > 0 ? formattedPosts : "No posts found";
220
+
221
+ // Add pagination info
222
+ if (result.pagination) {
223
+ output += `\n\nPagination: Page ${result.pagination.page}, showing ${result.posts.length} of ${result.pagination.count} posts`;
224
+ }
225
+
226
+ return {
227
+ content: [
228
+ {
229
+ type: "text",
230
+ text: output,
231
+ },
232
+ ],
233
+ isError: false,
234
+ };
235
+ }
236
+
237
+ return {
238
+ content: [
239
+ {
240
+ type: "text",
241
+ text: result.error || "Unknown error occurred",
242
+ },
243
+ ],
244
+ isError: true,
245
+ };
246
+ } catch (error) {
247
+ const msg = error instanceof Error ? error.message : String(error);
248
+ return {
249
+ content: [{ type: "text", text: `Error: ${msg}` }],
250
+ isError: true,
251
+ };
252
+ }
253
+ },
254
+ );
255
+ }
@@ -1,6 +1,10 @@
1
1
  import { Transaction, Utils } from "@bsv/sdk";
2
2
  import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
3
3
  import type { RequestHandlerExtra } from "@modelcontextprotocol/sdk/shared/protocol.js";
4
+ import type {
5
+ ServerNotification,
6
+ ServerRequest,
7
+ } from "@modelcontextprotocol/sdk/types.js";
4
8
  import { z } from "zod";
5
9
 
6
10
  // Schema for decode transaction arguments
@@ -124,7 +128,7 @@ export function registerDecodeTransactionTool(server: McpServer): void {
124
128
  },
125
129
  async (
126
130
  { args }: { args: DecodeTransactionArgs },
127
- extra: RequestHandlerExtra,
131
+ extra: RequestHandlerExtra<ServerRequest, ServerNotification>,
128
132
  ) => {
129
133
  try {
130
134
  const { tx, encoding } = args;
@@ -19,3 +19,24 @@ export const MARKET_WALLET_ADDRESS = "15q8YQSqUa9uTh6gh4AVixxq29xkpBBP9z";
19
19
  * Market fee will never be less than this amount
20
20
  */
21
21
  export const MINIMUM_MARKET_FEE_SATOSHIS = 10000; // 10000 satoshis = 0.0001 BSV
22
+
23
+ export const BAP_PREFIX = "1BAPSuaPnfGnSBM3GLV9yhxUdYe4vGbdMT";
24
+ export const B_PREFIX = "19HxigV4QyBv3tHpQVcUEQyq1pzZVdoAut";
25
+ export const MAP_PREFIX = "1PuQa7K62MiKCtssSLKy1kh56WWU7MtUR5";
26
+ export const AIP_PREFIX = "1HPcP7a4kQjpJzyV4HWHKagon76KC3BsZA";
27
+ export const STORAGE_MNEMONIC_KEY = "id_mnemonic";
28
+ export const PUBLIC_URL = process.env.PUBLIC_URL || "";
29
+ export const BMAP_URL =
30
+ process.env.PUBLIC_BMAP_URL || "https://bmap-api-production.up.railway.app";
31
+
32
+ export const ORDFS_URL =
33
+ process.env.PUBLIC_ORDFS_URL || "https://ordfs.network";
34
+ export const YOURS_INSTALL_URL =
35
+ "https://chromewebstore.google.com/detail/yours-wallet/mlbnicldlpdimbjdcncnklfempedeipj";
36
+
37
+ // export const BSOCIAL_API_URL =
38
+ // process.env.PUBLIC_BSOCIAL_API_URL ||
39
+ // "https://bsocial-overlay-production.up.railway.app/api/v1";
40
+
41
+ export const BSOCIAL_API_URL = "https://api.sigmaidentity.com/v1";
42
+ export const V5_API_URL = "https://ordinals.1sat.app/v5";
package/tools/index.ts CHANGED
@@ -1,9 +1,18 @@
1
+ import type { PrivateKey } from "@bsv/sdk";
1
2
  import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
- import { registerA2aCallTool } from "./a2b/call";
3
3
  import { registerA2bDiscoverTool } from "./a2b/discover";
4
+ import { registerBapTools } from "./bap";
5
+ // import { registerBigBlocksTools } from "./bigblocks"; // Disabled - Turbopack module resolution issues
6
+ import { registerBsocialTools } from "./bsocial";
4
7
  import { registerBsvTools } from "./bsv";
8
+ import { registerMneeTools } from "./mnee";
5
9
  import { registerOrdinalsTools } from "./ordinals";
6
10
  import { registerUtilsTools } from "./utils";
11
+ import { registerWalletGetBalanceDropletTool } from "./wallet/getBalanceDroplet";
12
+ import type { IntegratedWallet } from "./wallet/integratedWallet";
13
+ import { registerSetupDropletTool } from "./wallet/setupDroplet";
14
+ import { registerWalletTools } from "./wallet/tools";
15
+ import type { Wallet } from "./wallet/wallet";
7
16
 
8
17
  /**
9
18
  * Configuration options for tools
@@ -12,13 +21,29 @@ import { registerUtilsTools } from "./utils";
12
21
  * - enableBsvTools: controlled by DISABLE_BSV_TOOLS
13
22
  * - enableOrdinalsTools: controlled by DISABLE_ORDINALS_TOOLS
14
23
  * - enableUtilsTools: controlled by DISABLE_UTILS_TOOLS
15
- * - enableA2bTools: controlled by DISABLE_A2B_TOOLS
24
+ * - enableA2bTools: controlled by ENABLE_A2B_TOOLS (disabled by default)
25
+ * - enableBapTools: controlled by DISABLE_BAP_TOOLS
26
+ * - enableBsocialTools: controlled by DISABLE_BSOCIAL_TOOLS
27
+ * - enableWalletTools: controlled by DISABLE_WALLET_TOOLS
28
+ * - enableMneeTools: controlled by DISABLE_MNEE_TOOLS
29
+ * - enableBigBlocksTools: controlled by DISABLE_BIGBLOCKS_TOOLS
16
30
  */
17
31
  export interface ToolsConfig {
18
32
  enableBsvTools?: boolean;
19
33
  enableOrdinalsTools?: boolean;
20
34
  enableUtilsTools?: boolean;
21
35
  enableA2bTools?: boolean;
36
+ enableBapTools?: boolean;
37
+ enableBsocialTools?: boolean;
38
+ enableWalletTools?: boolean;
39
+ enableMneeTools?: boolean;
40
+ enableBigBlocksTools?: boolean;
41
+ identityPk?: PrivateKey;
42
+ payPk?: PrivateKey;
43
+ xprv?: string;
44
+ wallet?: Wallet;
45
+ integratedWallet?: IntegratedWallet;
46
+ disableBroadcasting?: boolean; // For wallet tools
22
47
  }
23
48
 
24
49
  /**
@@ -28,19 +53,33 @@ export interface ToolsConfig {
28
53
  */
29
54
  export function registerAllTools(
30
55
  server: McpServer,
31
- config: ToolsConfig = {
32
- enableBsvTools: true,
33
- enableOrdinalsTools: true,
34
- enableUtilsTools: true,
35
- enableA2bTools: false,
36
- },
56
+ config: ToolsConfig = {},
37
57
  ): void {
38
- const {
39
- enableBsvTools = true,
40
- enableOrdinalsTools = true,
41
- enableUtilsTools = true,
42
- enableA2bTools = false,
43
- } = config;
58
+ // Ensure defaults are true unless explicitly set to false via env vars or config
59
+ const enableBsvTools =
60
+ process.env.DISABLE_BSV_TOOLS !== "true" && config.enableBsvTools !== false;
61
+ const enableOrdinalsTools =
62
+ process.env.DISABLE_ORDINALS_TOOLS !== "true" &&
63
+ config.enableOrdinalsTools !== false;
64
+ const enableUtilsTools =
65
+ process.env.DISABLE_UTILS_TOOLS !== "true" &&
66
+ config.enableUtilsTools !== false; // Ensure Utils are enabled by default
67
+ const enableA2bTools =
68
+ process.env.ENABLE_A2B_TOOLS === "true" && config.enableA2bTools !== false;
69
+ const enableBapTools =
70
+ process.env.DISABLE_BAP_TOOLS !== "true" && config.enableBapTools !== false;
71
+ const enableWalletTools =
72
+ process.env.DISABLE_WALLET_TOOLS !== "true" &&
73
+ config.enableWalletTools !== false;
74
+ const enableMneeTools =
75
+ process.env.DISABLE_MNEE_TOOLS !== "true" &&
76
+ config.enableMneeTools !== false;
77
+ const enableBsocialTools =
78
+ process.env.DISABLE_BSOCIAL_TOOLS !== "true" &&
79
+ config.enableBsocialTools !== false;
80
+ const enableBigBlocksTools =
81
+ process.env.DISABLE_BIGBLOCKS_TOOLS !== "true" &&
82
+ config.enableBigBlocksTools !== false;
44
83
 
45
84
  // Register BSV-related tools
46
85
  if (enableBsvTools) {
@@ -59,11 +98,58 @@ export function registerAllTools(
59
98
 
60
99
  // Register agent-to-blockchain tools
61
100
  if (enableA2bTools) {
62
- // Register agent-to-blockchain discovery tool
63
101
  registerA2bDiscoverTool(server);
102
+ }
103
+
104
+ // Register BAP tools
105
+ if (enableBapTools) {
106
+ const bapConfig: import("./bap").BapToolsConfig = {
107
+ disableBroadcasting: config.disableBroadcasting,
108
+ identityPk: config.identityPk,
109
+ masterXprv: config.xprv,
110
+ wallet: config.wallet,
111
+ };
112
+ registerBapTools(server, bapConfig);
113
+ }
114
+
115
+ // Register BSocial tools
116
+ if (enableBsocialTools && config.wallet) {
117
+ registerBsocialTools(server, { wallet: config.wallet });
118
+ }
119
+
120
+ // Register BigBlocks tools
121
+ // Disabled - Turbopack module resolution issues
122
+ // if (enableBigBlocksTools) {
123
+ // registerBigBlocksTools(server);
124
+ // }
125
+
126
+ // Register Wallet tools themselves
127
+ if (enableWalletTools) {
128
+ if (config.integratedWallet?.isDropletMode) {
129
+ // Register Droplet-mode wallet tools
130
+ const dropletClient = config.integratedWallet.getDropletClient();
131
+ if (dropletClient) {
132
+ // Register Droplet-specific tools
133
+ registerWalletGetBalanceDropletTool(server, dropletClient);
134
+ if (config.integratedWallet) {
135
+ registerSetupDropletTool(server, config.integratedWallet);
136
+ }
137
+ console.error("Registered Droplet mode wallet tools");
138
+ }
139
+ } else if (config.wallet) {
140
+ // Register normal wallet tools
141
+ const walletToolOptions = {
142
+ disableBroadcasting: config.disableBroadcasting === true, // Default to false if undefined
143
+ enableA2bTools: enableA2bTools, // Use the already determined enableA2bTools value
144
+ identityPk: config.identityPk,
145
+ };
146
+ registerWalletTools(server, config.wallet, walletToolOptions);
147
+ }
148
+ }
64
149
 
65
- // Register agent-to-agent call tool
66
- // registerA2aCallTool(server);
150
+ // Register MNEE tools
151
+ if (enableMneeTools) {
152
+ registerMneeTools(server);
67
153
  }
68
154
 
69
155
  // Add more tool categories as needed
@@ -7,15 +7,23 @@ import { registerSendMneeTool } from "./sendMnee";
7
7
  const mnee = new Mnee({
8
8
  environment: "production",
9
9
  });
10
+
11
+ const mneeToolsRegistered = new WeakSet<McpServer>();
12
+
10
13
  /**
11
14
  * Register all MNEE tools with the MCP server
12
15
  * @param server The MCP server instance
13
16
  */
14
17
  export function registerMneeTools(server: McpServer): void {
18
+ if (mneeToolsRegistered.has(server)) {
19
+ // console.warn("WARN: MNEE tools already registered for this server instance. Skipping.");
20
+ return;
21
+ }
15
22
  // Register MNEE-related tools
16
23
  registerGetBalanceTool(server, mnee);
17
24
 
18
25
  registerSendMneeTool(server, mnee);
19
26
 
20
27
  registerParseTxTool(server, mnee);
28
+ mneeToolsRegistered.add(server);
21
29
  }
@@ -1,7 +1,7 @@
1
1
  import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
2
  import type { RequestHandlerExtra } from "@modelcontextprotocol/sdk/shared/protocol.js";
3
- import type { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
4
3
  import type {
4
+ CallToolResult,
5
5
  ServerNotification,
6
6
  ServerRequest,
7
7
  } from "@modelcontextprotocol/sdk/types.js";
@@ -1,7 +1,7 @@
1
1
  import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
2
  import type { RequestHandlerExtra } from "@modelcontextprotocol/sdk/shared/protocol.js";
3
- import type { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
4
3
  import type {
4
+ CallToolResult,
5
5
  ServerNotification,
6
6
  ServerRequest,
7
7
  } from "@modelcontextprotocol/sdk/types.js";
@@ -0,0 +1,46 @@
1
+ import { BSM, type PrivateKey, Utils } from "@bsv/sdk";
2
+ import { AIP_PREFIX } from "../constants";
3
+
4
+ const { toArray, toBase64, toUTF8 } = Utils;
5
+
6
+ /**
7
+ * Sign OP_RETURN data with AIP (Author Identity Protocol)
8
+ * @param dataArrays - Array of data arrays to sign
9
+ * @param signingKey - Private key to sign with
10
+ * @param signingAddress - Address associated with the signing key
11
+ * @returns Object containing signed data array
12
+ */
13
+ export async function signOpReturnWithAIP(
14
+ dataArrays: Uint8Array[],
15
+ signingKey: PrivateKey,
16
+ signingAddress: string,
17
+ ): Promise<{ signedData: Uint8Array[] }> {
18
+ // Concatenate all data arrays to create message to sign
19
+ let messageToSign = new Uint8Array();
20
+ for (const data of dataArrays) {
21
+ const temp = new Uint8Array(messageToSign.length + data.length);
22
+ temp.set(messageToSign);
23
+ temp.set(data, messageToSign.length);
24
+ messageToSign = temp;
25
+ }
26
+
27
+ // Create BSM signature
28
+ const signature = BSM.sign(messageToSign, signingKey);
29
+
30
+ // Convert signature to base64
31
+ const signatureBase64 = toBase64(signature.toCompact());
32
+
33
+ // Add AIP data to the arrays
34
+ const aipData: Uint8Array[] = [
35
+ toArray("|", "utf8"), // Separator
36
+ toArray(AIP_PREFIX, "utf8"),
37
+ toArray("BITCOIN_ECDSA", "utf8"),
38
+ toArray(signingAddress, "utf8"),
39
+ toArray(signatureBase64, "utf8"),
40
+ ];
41
+
42
+ // Return combined data
43
+ return {
44
+ signedData: [...dataArrays, ...aipData],
45
+ };
46
+ }
@@ -1,5 +1,6 @@
1
1
  import { Utils } from "@bsv/sdk";
2
2
  import { z } from "zod";
3
+
3
4
  const {
4
5
  toArray: bsvToArray,
5
6
  toBase64: bsvToBase64,
@@ -0,0 +1,76 @@
1
+ import type { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
2
+
3
+ /**
4
+ * Format an error message consistently
5
+ */
6
+ export function formatError(error: unknown): string {
7
+ if (error instanceof Error) {
8
+ return error.message;
9
+ }
10
+ return String(error);
11
+ }
12
+
13
+ /**
14
+ * Create a standardized error response for MCP tools
15
+ */
16
+ export function createErrorResponse(error: unknown): CallToolResult {
17
+ return {
18
+ content: [
19
+ {
20
+ type: "text",
21
+ text: formatError(error),
22
+ },
23
+ ],
24
+ isError: true,
25
+ };
26
+ }
27
+
28
+ /**
29
+ * Create a standardized success response for MCP tools
30
+ */
31
+ export function createSuccessResponse(data: unknown): CallToolResult {
32
+ const text = typeof data === "string" ? data : JSON.stringify(data, null, 2);
33
+ return {
34
+ content: [
35
+ {
36
+ type: "text",
37
+ text,
38
+ },
39
+ ],
40
+ isError: false,
41
+ };
42
+ }
43
+
44
+ /**
45
+ * Create a response based on a result object with success field
46
+ */
47
+ export function createResponse(result: {
48
+ success: boolean;
49
+ [key: string]: unknown;
50
+ }): CallToolResult {
51
+ return {
52
+ content: [
53
+ {
54
+ type: "text",
55
+ text: JSON.stringify(result, null, 2),
56
+ },
57
+ ],
58
+ isError: !result.success,
59
+ };
60
+ }
61
+
62
+ /**
63
+ * Wrap an async function with standard error handling
64
+ */
65
+ export function withErrorHandler<
66
+ T extends (...args: unknown[]) => Promise<unknown>,
67
+ >(fn: T): (...args: Parameters<T>) => Promise<CallToolResult> {
68
+ return async (...args: Parameters<T>): Promise<CallToolResult> => {
69
+ try {
70
+ const result = await fn(...args);
71
+ return createSuccessResponse(result);
72
+ } catch (error) {
73
+ return createErrorResponse(error);
74
+ }
75
+ };
76
+ }
@@ -1,14 +1,50 @@
1
1
  import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
2
  import { z } from "zod";
3
3
  import { convertData } from "./conversion";
4
+ import { installAgentMasterTool } from "./installAgentMaster";
4
5
 
5
6
  const encodingSchema = z.enum(["utf8", "hex", "base64", "binary"]);
6
7
 
7
8
  /**
8
- * Register the unified conversion tool with the MCP server
9
+ * Register utility tools with the MCP server
9
10
  * @param server The MCP server instance
10
11
  */
11
12
  export function registerUtilsTools(server: McpServer): void {
13
+ // Register install agent master tool
14
+ server.tool(
15
+ installAgentMasterTool.name,
16
+ installAgentMasterTool.description,
17
+ { args: installAgentMasterTool.inputSchema },
18
+ async ({ args }) => {
19
+ try {
20
+ const result = await installAgentMasterTool.handler(args);
21
+ return {
22
+ content: [
23
+ {
24
+ type: "text",
25
+ text:
26
+ typeof result === "string"
27
+ ? result
28
+ : JSON.stringify(result, null, 2),
29
+ },
30
+ ],
31
+ };
32
+ } catch (err: unknown) {
33
+ const msg = err instanceof Error ? err.message : String(err);
34
+ return {
35
+ content: [
36
+ {
37
+ type: "text",
38
+ text: `Error: ${msg}`,
39
+ },
40
+ ],
41
+ isError: true,
42
+ };
43
+ }
44
+ },
45
+ );
46
+
47
+ // Register conversion tool
12
48
  server.tool(
13
49
  "utils_convertData",
14
50
  "Converts data between different encodings (utf8, hex, base64, binary). Useful for transforming data formats when working with blockchain data, encryption, or file processing.\n\n" +
@@ -66,3 +102,11 @@ export function registerUtilsTools(server: McpServer): void {
66
102
  },
67
103
  );
68
104
  }
105
+
106
+ export * from "./aip";
107
+ // Export all utilities for use by other tools
108
+ export * from "./errorHandler";
109
+ export * from "./logger";
110
+ export * from "./toolConfig";
111
+ export * from "./toolRegistration";
112
+ export * from "./transactionBuilder";