bsv-mcp 0.0.1

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.
@@ -0,0 +1,206 @@
1
+ import type {
2
+ McpServer,
3
+ ToolCallback,
4
+ } from "@modelcontextprotocol/sdk/server/mcp.js";
5
+ import type { RequestHandlerExtra } from "@modelcontextprotocol/sdk/shared/protocol.js";
6
+ import type { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
7
+ import type { z } from "zod";
8
+ import { convertData } from "../utils/conversion";
9
+ import { registerGetAddressTool } from "./getAddress";
10
+ import { registerPurchaseListingTool } from "./purchaseListing";
11
+ import {
12
+ createSignatureArgsSchema,
13
+ type emptyArgsSchema,
14
+ getPublicKeyArgsSchema,
15
+ verifySignatureArgsSchema,
16
+ walletDecryptArgsSchema,
17
+ walletEncryptArgsSchema,
18
+ } from "./schemas";
19
+ import type {
20
+ abortActionArgsSchema,
21
+ acquireCertificateArgsSchema,
22
+ createHmacArgsSchema,
23
+ discoverByAttributesArgsSchema,
24
+ discoverByIdentityKeyArgsSchema,
25
+ getAddressArgsSchema,
26
+ getHeaderArgsSchema,
27
+ internalizeActionArgsSchema,
28
+ listActionsArgsSchema,
29
+ listCertificatesArgsSchema,
30
+ listOutputsArgsSchema,
31
+ proveCertificateArgsSchema,
32
+ purchaseListingArgsSchema,
33
+ relinquishCertificateArgsSchema,
34
+ relinquishOutputArgsSchema,
35
+ revealCounterpartyKeyLinkageArgsSchema,
36
+ revealSpecificKeyLinkageArgsSchema,
37
+ sendToAddressArgsSchema,
38
+ verifyHmacArgsSchema,
39
+ } from "./schemas";
40
+ import { registerSendToAddressTool } from "./sendToAddress";
41
+ import type { Wallet } from "./wallet";
42
+
43
+ // Define mapping from tool names to argument schemas
44
+ type ToolArgSchemas = {
45
+ wallet_getPublicKey: typeof getPublicKeyArgsSchema;
46
+ wallet_createSignature: typeof createSignatureArgsSchema;
47
+ wallet_verifySignature: typeof verifySignatureArgsSchema;
48
+ wallet_encrypt: typeof walletEncryptArgsSchema;
49
+ wallet_decrypt: typeof walletDecryptArgsSchema;
50
+ wallet_listActions: typeof listActionsArgsSchema;
51
+ wallet_listOutputs: typeof listOutputsArgsSchema;
52
+ wallet_getNetwork: typeof emptyArgsSchema;
53
+ wallet_getVersion: typeof emptyArgsSchema;
54
+ wallet_revealCounterpartyKeyLinkage: typeof revealCounterpartyKeyLinkageArgsSchema;
55
+ wallet_revealSpecificKeyLinkage: typeof revealSpecificKeyLinkageArgsSchema;
56
+ wallet_createHmac: typeof createHmacArgsSchema;
57
+ wallet_verifyHmac: typeof verifyHmacArgsSchema;
58
+ wallet_abortAction: typeof abortActionArgsSchema;
59
+ wallet_internalizeAction: typeof internalizeActionArgsSchema;
60
+ wallet_relinquishOutput: typeof relinquishOutputArgsSchema;
61
+ wallet_acquireCertificate: typeof acquireCertificateArgsSchema;
62
+ wallet_listCertificates: typeof listCertificatesArgsSchema;
63
+ wallet_proveCertificate: typeof proveCertificateArgsSchema;
64
+ wallet_relinquishCertificate: typeof relinquishCertificateArgsSchema;
65
+ wallet_discoverByIdentityKey: typeof discoverByIdentityKeyArgsSchema;
66
+ wallet_discoverByAttributes: typeof discoverByAttributesArgsSchema;
67
+ wallet_isAuthenticated: typeof emptyArgsSchema;
68
+ wallet_waitForAuthentication: typeof emptyArgsSchema;
69
+ wallet_getHeaderForHeight: typeof getHeaderArgsSchema;
70
+ wallet_getAddress: typeof getAddressArgsSchema;
71
+ wallet_sendToAddress: typeof sendToAddressArgsSchema;
72
+ wallet_purchaseListing: typeof purchaseListingArgsSchema;
73
+ };
74
+
75
+ // Define a type for the handler function with proper argument types
76
+ type ToolHandler = (
77
+ params: { args: unknown },
78
+ extra: RequestHandlerExtra,
79
+ ) => Promise<CallToolResult>;
80
+
81
+ // Define a map type for tool name to handler functions
82
+ type ToolHandlerMap = {
83
+ [K in keyof ToolArgSchemas]: ToolHandler;
84
+ };
85
+
86
+ export function registerWalletTools(
87
+ server: McpServer,
88
+ wallet: Wallet,
89
+ ): ToolHandlerMap {
90
+ const handlers = {} as ToolHandlerMap;
91
+
92
+ // Handle tools registration with properly typed parameters
93
+ function registerTool<T extends z.ZodType>(
94
+ name: keyof ToolArgSchemas,
95
+ schema: { args: T },
96
+ handler: ToolCallback<{ args: T }>,
97
+ ): void {
98
+ // Register all tools normally
99
+ server.tool(name, schema, handler);
100
+ handlers[name] = handler as ToolHandler;
101
+ }
102
+
103
+ // Register the wallet_sendToAddress tool
104
+ registerSendToAddressTool(server, wallet);
105
+
106
+ // Register the wallet_getAddress tool
107
+ registerGetAddressTool(server);
108
+
109
+ // Register the wallet_purchaseListing tool
110
+ registerPurchaseListingTool(server, wallet);
111
+
112
+ // Register only the minimal public-facing tools
113
+ // wallet_createAction, wallet_signAction and wallet_getHeight have been removed
114
+
115
+ // Register wallet_getPublicKey
116
+ registerTool(
117
+ "wallet_getPublicKey",
118
+ { args: getPublicKeyArgsSchema },
119
+ async (
120
+ { args }: { args: z.infer<typeof getPublicKeyArgsSchema> },
121
+ extra: RequestHandlerExtra,
122
+ ) => {
123
+ try {
124
+ const result = await wallet.getPublicKey(args);
125
+ return { content: [{ type: "text", text: JSON.stringify(result) }] };
126
+ } catch (err: unknown) {
127
+ const msg = err instanceof Error ? err.message : String(err);
128
+ return { content: [{ type: "text", text: msg }], isError: true };
129
+ }
130
+ },
131
+ );
132
+
133
+ // Register wallet_createSignature
134
+ registerTool(
135
+ "wallet_createSignature",
136
+ { args: createSignatureArgsSchema },
137
+ async (
138
+ { args }: { args: z.infer<typeof createSignatureArgsSchema> },
139
+ extra: RequestHandlerExtra,
140
+ ) => {
141
+ try {
142
+ const result = await wallet.createSignature(args);
143
+ return { content: [{ type: "text", text: JSON.stringify(result) }] };
144
+ } catch (err: unknown) {
145
+ const msg = err instanceof Error ? err.message : String(err);
146
+ return { content: [{ type: "text", text: msg }], isError: true };
147
+ }
148
+ },
149
+ );
150
+
151
+ // Register wallet_verifySignature
152
+ registerTool(
153
+ "wallet_verifySignature",
154
+ { args: verifySignatureArgsSchema },
155
+ async (
156
+ { args }: { args: z.infer<typeof verifySignatureArgsSchema> },
157
+ extra: RequestHandlerExtra,
158
+ ) => {
159
+ try {
160
+ const result = await wallet.verifySignature(args);
161
+ return { content: [{ type: "text", text: JSON.stringify(result) }] };
162
+ } catch (err: unknown) {
163
+ const msg = err instanceof Error ? err.message : String(err);
164
+ return { content: [{ type: "text", text: msg }], isError: true };
165
+ }
166
+ },
167
+ );
168
+
169
+ // Register wallet_encrypt
170
+ registerTool(
171
+ "wallet_encrypt",
172
+ { args: walletEncryptArgsSchema },
173
+ async (
174
+ { args }: { args: z.infer<typeof walletEncryptArgsSchema> },
175
+ extra: RequestHandlerExtra,
176
+ ) => {
177
+ try {
178
+ const result = await wallet.encrypt(args);
179
+ return { content: [{ type: "text", text: JSON.stringify(result) }] };
180
+ } catch (err: unknown) {
181
+ const msg = err instanceof Error ? err.message : String(err);
182
+ return { content: [{ type: "text", text: msg }], isError: true };
183
+ }
184
+ },
185
+ );
186
+
187
+ // Register wallet_decrypt
188
+ registerTool(
189
+ "wallet_decrypt",
190
+ { args: walletDecryptArgsSchema },
191
+ async (
192
+ { args }: { args: z.infer<typeof walletDecryptArgsSchema> },
193
+ extra: RequestHandlerExtra,
194
+ ) => {
195
+ try {
196
+ const result = await wallet.decrypt(args);
197
+ return { content: [{ type: "text", text: JSON.stringify(result) }] };
198
+ } catch (err: unknown) {
199
+ const msg = err instanceof Error ? err.message : String(err);
200
+ return { content: [{ type: "text", text: msg }], isError: true };
201
+ }
202
+ },
203
+ );
204
+
205
+ return handlers;
206
+ }
@@ -0,0 +1,290 @@
1
+ /**
2
+ * Wallet implementation scaffold.
3
+ *
4
+ * Implements WalletInterface from the ts-sdk and extends ProtoWallet.
5
+ *
6
+ * See: https://github.com/bitcoin-sv/ts-sdk/blob/main/src/wallet/Wallet.interfaces.ts
7
+ */
8
+ import {
9
+ LockingScript,
10
+ PrivateKey,
11
+ ProtoWallet,
12
+ Transaction,
13
+ Utils,
14
+ } from "@bsv/sdk";
15
+ import type {
16
+ AbortActionArgs,
17
+ AbortActionResult,
18
+ AcquireCertificateArgs,
19
+ AuthenticatedResult,
20
+ CreateActionArgs,
21
+ CreateActionResult,
22
+ CreateHmacArgs,
23
+ CreateHmacResult,
24
+ CreateSignatureArgs,
25
+ CreateSignatureResult,
26
+ DiscoverByAttributesArgs,
27
+ DiscoverByIdentityKeyArgs,
28
+ DiscoverCertificatesResult,
29
+ GetHeaderArgs,
30
+ GetHeaderResult,
31
+ GetHeightResult,
32
+ GetNetworkResult,
33
+ GetPublicKeyArgs,
34
+ GetPublicKeyResult,
35
+ GetVersionResult,
36
+ InternalizeActionArgs,
37
+ InternalizeActionResult,
38
+ ListActionsArgs,
39
+ ListActionsResult,
40
+ ListCertificatesArgs,
41
+ ListCertificatesResult,
42
+ ListOutputsArgs,
43
+ ListOutputsResult,
44
+ ProveCertificateArgs,
45
+ ProveCertificateResult,
46
+ RelinquishCertificateArgs,
47
+ RelinquishCertificateResult,
48
+ RelinquishOutputArgs,
49
+ RelinquishOutputResult,
50
+ RevealCounterpartyKeyLinkageArgs,
51
+ RevealCounterpartyKeyLinkageResult,
52
+ RevealSpecificKeyLinkageArgs,
53
+ RevealSpecificKeyLinkageResult,
54
+ SignActionArgs,
55
+ SignActionResult,
56
+ VerifyHmacArgs,
57
+ VerifyHmacResult,
58
+ VerifySignatureArgs,
59
+ VerifySignatureResult,
60
+ WalletCertificate,
61
+ WalletDecryptArgs,
62
+ WalletDecryptResult,
63
+ WalletEncryptArgs,
64
+ WalletEncryptResult,
65
+ WalletInterface,
66
+ } from "@bsv/sdk";
67
+ import {
68
+ type NftUtxo,
69
+ type Utxo,
70
+ fetchNftUtxos,
71
+ fetchPayUtxos,
72
+ } from "js-1sat-ord";
73
+
74
+ export class Wallet extends ProtoWallet implements WalletInterface {
75
+ private paymentUtxos: Utxo[] = [];
76
+ private nftUtxos: NftUtxo[] = [];
77
+ private lastUtxoFetch = 0;
78
+ private readonly utxoRefreshIntervalMs = 5 * 60 * 1000; // 5 minutes
79
+ private privateKey?: PrivateKey;
80
+
81
+ constructor(privKey?: PrivateKey) {
82
+ super(privKey);
83
+ this.privateKey = privKey;
84
+ // Initialize UTXOs
85
+ this.refreshUtxos().catch((err) =>
86
+ console.error("Error initializing UTXOs:", err),
87
+ );
88
+ }
89
+
90
+ /**
91
+ * Refresh UTXOs from the network
92
+ */
93
+ async refreshUtxos(): Promise<void> {
94
+ try {
95
+ const privateKey = this.getPrivateKey();
96
+ if (!privateKey) {
97
+ console.warn("No private key available for fetching UTXOs");
98
+ return;
99
+ }
100
+
101
+ const address = privateKey.toAddress();
102
+ console.log(`Fetching UTXOs for address: ${address}`);
103
+
104
+ const utxos = await fetchPayUtxos(address);
105
+ const nftUtxos = await fetchNftUtxos(address);
106
+ this.paymentUtxos = utxos;
107
+ this.nftUtxos = nftUtxos;
108
+ this.lastUtxoFetch = Date.now();
109
+
110
+ // console.log(`Fetched ${utxos.length} UTXOs for address ${address}`);
111
+ } catch (error) {
112
+ console.error("Error refreshing UTXOs:", error);
113
+ throw error;
114
+ }
115
+ }
116
+
117
+ /**
118
+ * Get payment and NFT UTXOs, refreshing if needed
119
+ */
120
+ async getUtxos(): Promise<{ paymentUtxos: Utxo[]; nftUtxos: NftUtxo[] }> {
121
+ const now = Date.now();
122
+ if (now - this.lastUtxoFetch > this.utxoRefreshIntervalMs) {
123
+ await this.refreshUtxos();
124
+ }
125
+ return { paymentUtxos: this.paymentUtxos, nftUtxos: this.nftUtxos };
126
+ }
127
+
128
+ /**
129
+ * Get the private key if available
130
+ */
131
+ getPrivateKey(): PrivateKey | undefined {
132
+ // Try to get private key from environment if not already set
133
+ if (!this.privateKey) {
134
+ const wif = process.env.PRIVATE_KEY_WIF;
135
+ if (wif) {
136
+ this.privateKey = PrivateKey.fromWif(wif);
137
+ }
138
+ }
139
+ return this.privateKey;
140
+ }
141
+
142
+ async getPublicKey(args: GetPublicKeyArgs): Promise<GetPublicKeyResult> {
143
+ return Promise.reject(new Error("Not implemented"));
144
+ }
145
+ async revealCounterpartyKeyLinkage(
146
+ args: RevealCounterpartyKeyLinkageArgs,
147
+ ): Promise<RevealCounterpartyKeyLinkageResult> {
148
+ return Promise.reject(new Error("Not implemented"));
149
+ }
150
+ async revealSpecificKeyLinkage(
151
+ args: RevealSpecificKeyLinkageArgs,
152
+ ): Promise<RevealSpecificKeyLinkageResult> {
153
+ return Promise.reject(new Error("Not implemented"));
154
+ }
155
+ async encrypt(args: WalletEncryptArgs): Promise<WalletEncryptResult> {
156
+ return Promise.reject(new Error("Not implemented"));
157
+ }
158
+ async decrypt(args: WalletDecryptArgs): Promise<WalletDecryptResult> {
159
+ return Promise.reject(new Error("Not implemented"));
160
+ }
161
+ async createHmac(args: CreateHmacArgs): Promise<CreateHmacResult> {
162
+ return Promise.reject(new Error("Not implemented"));
163
+ }
164
+ async verifyHmac(args: VerifyHmacArgs): Promise<VerifyHmacResult> {
165
+ return Promise.reject(new Error("Not implemented"));
166
+ }
167
+ async createSignature(
168
+ args: CreateSignatureArgs,
169
+ ): Promise<CreateSignatureResult> {
170
+ return Promise.reject(new Error("Not implemented"));
171
+ }
172
+ async verifySignature(
173
+ args: VerifySignatureArgs,
174
+ ): Promise<VerifySignatureResult> {
175
+ return Promise.reject(new Error("Not implemented"));
176
+ }
177
+ async createAction(args: CreateActionArgs): Promise<CreateActionResult> {
178
+ console.log("createAction called with", args);
179
+
180
+ const tx = new Transaction();
181
+
182
+ // Add outputs
183
+ if (args.outputs) {
184
+ for (const output of args.outputs) {
185
+ const lockingScript = LockingScript.fromHex(output.lockingScript);
186
+ tx.addOutput({
187
+ lockingScript,
188
+ satoshis: output.satoshis,
189
+ });
190
+ }
191
+ }
192
+
193
+ // Add inputs (if provided)
194
+ if (args.inputs) {
195
+ for (const input of args.inputs) {
196
+ const [txid, outputIndexStr] = input.outpoint.split(".");
197
+ tx.addInput({
198
+ sourceTXID: txid,
199
+ sourceOutputIndex: Number.parseInt(outputIndexStr || "0", 10),
200
+ });
201
+ }
202
+ }
203
+
204
+ // Set lockTime and version if provided
205
+ if (args.lockTime !== undefined) tx.lockTime = args.lockTime;
206
+ if (args.version !== undefined) tx.version = args.version;
207
+
208
+ // Serialize the transaction using Utils
209
+ const txid = tx.hash("hex") as string;
210
+ const txArray = tx.toBinary();
211
+
212
+ return {
213
+ txid,
214
+ tx: txArray,
215
+ signableTransaction: undefined,
216
+ };
217
+ }
218
+ async signAction(args: SignActionArgs): Promise<SignActionResult> {
219
+ return Promise.reject(new Error("Not implemented"));
220
+ }
221
+ async abortAction(args: AbortActionArgs): Promise<AbortActionResult> {
222
+ return Promise.reject(new Error("Not implemented"));
223
+ }
224
+ async listActions(args: ListActionsArgs): Promise<ListActionsResult> {
225
+ return Promise.reject(new Error("Not implemented"));
226
+ }
227
+ async internalizeAction(
228
+ args: InternalizeActionArgs,
229
+ ): Promise<InternalizeActionResult> {
230
+ return Promise.reject(new Error("Not implemented"));
231
+ }
232
+ async listOutputs(args: ListOutputsArgs): Promise<ListOutputsResult> {
233
+ return Promise.reject(new Error("Not implemented"));
234
+ }
235
+ async relinquishOutput(
236
+ args: RelinquishOutputArgs,
237
+ ): Promise<RelinquishOutputResult> {
238
+ return Promise.reject(new Error("Not implemented"));
239
+ }
240
+ async acquireCertificate(
241
+ args: AcquireCertificateArgs,
242
+ ): Promise<WalletCertificate> {
243
+ return Promise.reject(new Error("Not implemented"));
244
+ }
245
+ async listCertificates(
246
+ args: ListCertificatesArgs,
247
+ ): Promise<ListCertificatesResult> {
248
+ return Promise.reject(new Error("Not implemented"));
249
+ }
250
+ async proveCertificate(
251
+ args: ProveCertificateArgs,
252
+ ): Promise<ProveCertificateResult> {
253
+ return Promise.reject(new Error("Not implemented"));
254
+ }
255
+ async relinquishCertificate(
256
+ args: RelinquishCertificateArgs,
257
+ ): Promise<RelinquishCertificateResult> {
258
+ return Promise.reject(new Error("Not implemented"));
259
+ }
260
+ async discoverByIdentityKey(
261
+ args: DiscoverByIdentityKeyArgs,
262
+ ): Promise<DiscoverCertificatesResult> {
263
+ return Promise.reject(new Error("Not implemented"));
264
+ }
265
+ async discoverByAttributes(
266
+ args: DiscoverByAttributesArgs,
267
+ ): Promise<DiscoverCertificatesResult> {
268
+ return Promise.reject(new Error("Not implemented"));
269
+ }
270
+ async isAuthenticated(args: object): Promise<AuthenticatedResult> {
271
+ return Promise.reject(new Error("Not implemented"));
272
+ }
273
+ async waitForAuthentication(args: object): Promise<AuthenticatedResult> {
274
+ return Promise.reject(new Error("Not implemented"));
275
+ }
276
+ async getHeight(args: object): Promise<GetHeightResult> {
277
+ return Promise.reject(new Error("Not implemented"));
278
+ }
279
+ async getHeaderForHeight(args: GetHeaderArgs): Promise<GetHeaderResult> {
280
+ return Promise.reject(new Error("Not implemented"));
281
+ }
282
+ async getNetwork(args: object): Promise<GetNetworkResult> {
283
+ return Promise.reject(new Error("Not implemented"));
284
+ }
285
+ async getVersion(args: object): Promise<GetVersionResult> {
286
+ return Promise.reject(new Error("Not implemented"));
287
+ }
288
+ }
289
+
290
+ export default Wallet;
package/tsconfig.json ADDED
@@ -0,0 +1,28 @@
1
+ {
2
+ "compilerOptions": {
3
+ // Environment setup & latest features
4
+ "lib": ["ESNext"],
5
+ "target": "ESNext",
6
+ "module": "ESNext",
7
+ "moduleDetection": "force",
8
+ "jsx": "react-jsx",
9
+ "allowJs": true,
10
+
11
+ // Bundler mode
12
+ "moduleResolution": "bundler",
13
+ "allowImportingTsExtensions": true,
14
+ "verbatimModuleSyntax": true,
15
+ "noEmit": true,
16
+
17
+ // Best practices
18
+ "strict": true,
19
+ "skipLibCheck": true,
20
+ "noFallthroughCasesInSwitch": true,
21
+ "noUncheckedIndexedAccess": true,
22
+
23
+ // Some stricter flags (disabled by default)
24
+ "noUnusedLocals": false,
25
+ "noUnusedParameters": false,
26
+ "noPropertyAccessFromIndexSignature": false
27
+ }
28
+ }