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
@@ -0,0 +1,148 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import type { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
3
+ import { z } from "zod";
4
+ import { BMAP_URL } from "../constants";
5
+
6
+ // Schema for reading posts from BMAP API
7
+ const bmapReadPostsArgsSchema = z.object({
8
+ bapId: z
9
+ .string()
10
+ .optional()
11
+ .describe("BAP identity key to filter posts by author"),
12
+ txid: z.string().optional().describe("Specific transaction ID to fetch"),
13
+ limit: z
14
+ .number()
15
+ .min(1)
16
+ .max(100)
17
+ .default(20)
18
+ .describe("Maximum number of posts to return"),
19
+ page: z
20
+ .number()
21
+ .min(1)
22
+ .default(1)
23
+ .describe("Page number for pagination (1-based)"),
24
+ feed: z
25
+ .boolean()
26
+ .default(false)
27
+ .describe("Whether to fetch feed (posts from followed users)"),
28
+ address: z.string().optional().describe("Bitcoin address to filter posts by"),
29
+ });
30
+
31
+ type BmapReadPostsArgs = z.infer<typeof bmapReadPostsArgsSchema>;
32
+
33
+ /**
34
+ * Read social posts from the BMAP API (the query/read layer)
35
+ */
36
+ export async function readBmapPosts(args: BmapReadPostsArgs): Promise<{
37
+ success: boolean;
38
+ data?: unknown;
39
+ error?: string;
40
+ }> {
41
+ try {
42
+ const { bapId, txid, limit, page, feed, address } = args;
43
+
44
+ // Handle single post request
45
+ if (txid) {
46
+ const response = await fetch(`${BMAP_URL}/post/${txid}`, {
47
+ method: "GET",
48
+ headers: {
49
+ "Content-Type": "application/json",
50
+ },
51
+ });
52
+
53
+ if (!response.ok) {
54
+ const errorText = await response.text();
55
+ return {
56
+ success: false,
57
+ error: `BMAP API request failed: ${response.status} ${errorText}`,
58
+ };
59
+ }
60
+
61
+ const data = await response.json();
62
+ return {
63
+ success: true,
64
+ data,
65
+ };
66
+ }
67
+
68
+ // Handle posts list request
69
+ const params = new URLSearchParams();
70
+ if (bapId) params.append("bapId", bapId);
71
+ if (address) params.append("address", address);
72
+ params.append("limit", limit.toString());
73
+ params.append("page", page.toString());
74
+ if (feed) params.append("feed", "true");
75
+
76
+ const response = await fetch(`${BMAP_URL}/posts?${params}`, {
77
+ method: "GET",
78
+ headers: {
79
+ "Content-Type": "application/json",
80
+ },
81
+ });
82
+
83
+ if (!response.ok) {
84
+ const errorText = await response.text();
85
+ return {
86
+ success: false,
87
+ error: `BMAP API request failed: ${response.status} ${errorText}`,
88
+ };
89
+ }
90
+
91
+ const data = await response.json();
92
+ return {
93
+ success: true,
94
+ data,
95
+ };
96
+ } catch (error) {
97
+ const errorMessage = error instanceof Error ? error.message : String(error);
98
+ console.error("Error reading BMAP posts:", errorMessage);
99
+ return {
100
+ success: false,
101
+ error: errorMessage,
102
+ };
103
+ }
104
+ }
105
+
106
+ /**
107
+ * Register the BMAP read posts tool with the MCP server
108
+ */
109
+ export function registerBmapReadPostsTool(server: McpServer) {
110
+ server.tool(
111
+ "bmap_readPosts",
112
+ "Read social posts from the BMAP API (query layer). Can fetch posts by author (BAP ID), specific post by transaction ID, or recent posts from all users. Supports pagination and feed functionality.",
113
+ { ...bmapReadPostsArgsSchema.shape },
114
+ async ({ bapId, txid, limit, page, feed, address }): Promise<CallToolResult> => {
115
+ try {
116
+ const result = await readBmapPosts({ bapId, txid, limit, page, feed, address });
117
+
118
+ if (result.success) {
119
+ return {
120
+ content: [
121
+ {
122
+ type: "text",
123
+ text: JSON.stringify(result.data, null, 2),
124
+ },
125
+ ],
126
+ isError: false,
127
+ };
128
+ }
129
+
130
+ return {
131
+ content: [
132
+ {
133
+ type: "text",
134
+ text: result.error || "Unknown error occurred",
135
+ },
136
+ ],
137
+ isError: true,
138
+ };
139
+ } catch (error) {
140
+ const msg = error instanceof Error ? error.message : String(error);
141
+ return {
142
+ content: [{ type: "text", text: `Error: ${msg}` }],
143
+ isError: true,
144
+ };
145
+ }
146
+ },
147
+ );
148
+ }
@@ -0,0 +1,136 @@
1
+ import { Utils } from "@bsv/sdk";
2
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
3
+ import type { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
4
+ import { z } from "zod";
5
+ import { B_PREFIX, MAP_PREFIX } from "../constants";
6
+ import { signOpReturnWithAIP } from "../utils/aip";
7
+ import { createResponse, formatError } from "../utils/errorHandler";
8
+ import {
9
+ buildAndSendTransaction,
10
+ buildOpReturnScript,
11
+ } from "../utils/transactionBuilder";
12
+ import type { Wallet } from "../wallet/wallet";
13
+
14
+ const { toArray } = Utils;
15
+
16
+ // Schema for creating a social post
17
+ const createPostArgsSchema = z.object({
18
+ content: z.string().describe("The content of the post"),
19
+ contentType: z
20
+ .enum(["text/plain", "text/markdown"])
21
+ .default("text/plain")
22
+ .describe("Content type of the post"),
23
+ app: z
24
+ .string()
25
+ .default("bsv-mcp")
26
+ .describe("Application name creating the post"),
27
+ additionalMapData: z
28
+ .record(z.string())
29
+ .optional()
30
+ .describe("Additional MAP protocol key-value pairs"),
31
+ });
32
+
33
+ type CreatePostArgs = z.infer<typeof createPostArgsSchema>;
34
+
35
+ /**
36
+ * Create a social post on the BSV blockchain using B:// and MAP protocols
37
+ */
38
+ export async function createSocialPost(
39
+ args: CreatePostArgs,
40
+ wallet: Wallet,
41
+ ): Promise<{
42
+ success: boolean;
43
+ txid?: string;
44
+ rawTx?: string;
45
+ error?: string;
46
+ }> {
47
+ try {
48
+ const { content, contentType, app, additionalMapData } = args;
49
+
50
+ // Get wallet info
51
+ const address = await wallet.getAddress();
52
+ const paymentKey = wallet.getPaymentKey();
53
+ if (!paymentKey) {
54
+ return {
55
+ success: false,
56
+ error: "Payment key not available in wallet",
57
+ };
58
+ }
59
+
60
+ // Prepare file extension based on content type
61
+ const fileExtension = contentType === "text/markdown" ? "md" : "txt";
62
+ const fileName = `post.${fileExtension}`;
63
+
64
+ // Build B protocol data
65
+ const bData = [
66
+ toArray(B_PREFIX, "utf8"),
67
+ toArray(content, "utf8"),
68
+ toArray(contentType, "utf8"),
69
+ toArray("utf-8", "utf8"),
70
+ toArray(fileName, "utf8"),
71
+ ];
72
+
73
+ // Build MAP protocol data
74
+ const mapData: Uint8Array[] = [
75
+ toArray(MAP_PREFIX, "utf8"),
76
+ toArray("SET", "utf8"),
77
+ toArray("app", "utf8"),
78
+ toArray(app, "utf8"),
79
+ toArray("type", "utf8"),
80
+ toArray("post", "utf8"),
81
+ ];
82
+
83
+ // Add any additional MAP data
84
+ if (additionalMapData) {
85
+ for (const [key, value] of Object.entries(additionalMapData)) {
86
+ mapData.push(toArray(key, "utf8"), toArray(value, "utf8"));
87
+ }
88
+ }
89
+
90
+ // Combine protocols with pipe separator
91
+ const pipeData = toArray("|", "utf8");
92
+ const dataToSign = [...bData, pipeData, ...mapData];
93
+
94
+ // Sign the data with AIP
95
+ const { signedData } = await signOpReturnWithAIP(
96
+ dataToSign,
97
+ paymentKey,
98
+ address.toString(),
99
+ );
100
+
101
+ // Create OP_RETURN script
102
+ const script = buildOpReturnScript(signedData);
103
+
104
+ // Get UTXOs
105
+ const utxos = await wallet.getPaymentUtxos();
106
+
107
+ // Build and send transaction using the new utility
108
+ return await buildAndSendTransaction({
109
+ outputs: [{ script, satoshis: 0 }],
110
+ utxos,
111
+ changeAddress: address.toString(),
112
+ paymentKey,
113
+ });
114
+ } catch (error) {
115
+ console.error("Error creating social post:", formatError(error));
116
+ return {
117
+ success: false,
118
+ error: formatError(error),
119
+ };
120
+ }
121
+ }
122
+
123
+ /**
124
+ * Register the social post tool with the MCP server
125
+ */
126
+ export function registerCreatePostTool(server: McpServer, wallet: Wallet) {
127
+ server.tool(
128
+ "bsocial_createPost",
129
+ "Create a social post on the BSV blockchain using B:// and MAP protocols. Posts are stored permanently on-chain and can include plain text or markdown content.",
130
+ { ...createPostArgsSchema.shape },
131
+ async ({ content, contentType, app, additionalMapData }): Promise<CallToolResult> => {
132
+ const result = await createSocialPost({ content, contentType, app, additionalMapData }, wallet);
133
+ return createResponse(result);
134
+ },
135
+ );
136
+ }
@@ -0,0 +1,45 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import type { Wallet } from "../wallet/wallet";
3
+ import { registerBmapReadFollowsTool } from "./bmapFollow";
4
+ import { registerBmapReadLikesTool } from "./bmapLikes";
5
+ import { registerBmapReadPostsTool } from "./bmapReadPosts";
6
+ import { registerCreatePostTool } from "./createPost";
7
+ import { registerReadPostsTool } from "./readPosts";
8
+
9
+ interface BsocialToolsConfig {
10
+ wallet?: Wallet;
11
+ }
12
+
13
+ /**
14
+ * Register all BSocial tools with the MCP server
15
+ * @param server The MCP server instance
16
+ * @param config Configuration including wallet instance
17
+ */
18
+ export function registerBsocialTools(
19
+ server: McpServer,
20
+ config: BsocialToolsConfig,
21
+ ): void {
22
+ // Register tools that don't require wallet
23
+ registerReadPostsTool(server);
24
+ console.error("✅ Registered bsocial_readPosts tool");
25
+
26
+ // Register BMAP API query tools (read layer)
27
+ registerBmapReadPostsTool(server);
28
+ console.error("✅ Registered bmap_readPosts tool");
29
+
30
+ registerBmapReadLikesTool(server);
31
+ console.error("✅ Registered bmap_readLikes tool");
32
+
33
+ registerBmapReadFollowsTool(server);
34
+ console.error("✅ Registered bmap_readFollows tool");
35
+
36
+ // Register tools that require wallet
37
+ if (config.wallet) {
38
+ registerCreatePostTool(server, config.wallet);
39
+ console.error("✅ Registered bsocial_createPost tool");
40
+ } else {
41
+ console.error(
42
+ "⚠️ bsocial_createPost tool not registered (no wallet available)",
43
+ );
44
+ }
45
+ }
@@ -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
+ { ...readPostsArgsSchema.shape },
202
+ async ({ bapId, txid, limit, page, feed }): Promise<CallToolResult> => {
203
+ try {
204
+ const result = await readSocialPosts({ bapId, txid, limit, page, feed });
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
@@ -119,15 +123,12 @@ export function registerDecodeTransactionTool(server: McpServer): void {
119
123
  server.tool(
120
124
  "bsv_decodeTransaction",
121
125
  "Decodes and analyzes Bitcoin SV transactions to provide detailed insights. This powerful tool accepts either a transaction ID or raw transaction data and returns comprehensive information including inputs, outputs, fee calculations, script details, and blockchain context. Supports both hex and base64 encoded transactions and automatically fetches additional on-chain data when available.",
122
- {
123
- args: decodeTransactionArgsSchema,
124
- },
126
+ decodeTransactionArgsSchema.shape,
125
127
  async (
126
- { args }: { args: DecodeTransactionArgs },
127
- extra: RequestHandlerExtra,
128
+ { tx, encoding },
129
+ extra: RequestHandlerExtra<ServerRequest, ServerNotification>,
128
130
  ) => {
129
131
  try {
130
- const { tx, encoding } = args;
131
132
  let transaction: Transaction;
132
133
  let rawTx: string;
133
134
  let txid: string;
@@ -131,10 +131,9 @@ export function registerExploreTool(server: McpServer): void {
131
131
  "NETWORK:\n" +
132
132
  "- health: API health check\n\n" +
133
133
  "Use the appropriate parameters for each endpoint type and specify 'main' or 'test' network.",
134
- { args: exploreArgsSchema },
135
- async ({ args }) => {
134
+ exploreArgsSchema.shape,
135
+ async (params) => {
136
136
  try {
137
- const params = exploreArgsSchema.parse(args);
138
137
 
139
138
  // Validate required parameters for specific endpoints
140
139
  if (
@@ -1,5 +1,4 @@
1
1
  import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
- import { z } from "zod";
3
2
 
4
3
  // Define cache duration (5 minutes in milliseconds)
5
4
  const PRICE_CACHE_DURATION = 5 * 60 * 1000;
@@ -53,14 +52,7 @@ export function registerGetPriceTool(server: McpServer): void {
53
52
  server.tool(
54
53
  "bsv_getPrice",
55
54
  "Retrieves the current price of Bitcoin SV (BSV) in USD from a reliable exchange API. This tool provides real-time market data that can be used for calculating transaction values, monitoring market conditions, or converting between BSV and fiat currencies.",
56
- {
57
- args: z
58
- .object({})
59
- .optional()
60
- .describe(
61
- "No parameters required - simply returns the current BSV price in USD",
62
- ),
63
- },
55
+ {},
64
56
  async () => {
65
57
  try {
66
58
  const price = await getBsvPriceWithCache();