bsv-mcp 0.2.14 → 0.3.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 (83) hide show
  1. package/CHANGELOG.md +90 -10
  2. package/README.md +13 -13
  3. package/dist/app.html +483 -106
  4. package/dist/index.js +265787 -155292
  5. package/index.ts +505 -70
  6. package/package.json +24 -15
  7. package/prompts/bsvSdk/auth.ts +1 -1
  8. package/prompts/bsvSdk/cryptography.ts +1 -1
  9. package/prompts/bsvSdk/overview.ts +1 -1
  10. package/prompts/bsvSdk/primitives.ts +1 -1
  11. package/prompts/bsvSdk/script.ts +1 -1
  12. package/prompts/bsvSdk/transaction.ts +1 -1
  13. package/prompts/bsvSdk/wallet.ts +1 -1
  14. package/prompts/ordinals.ts +1 -1
  15. package/test-mcp-server.ts +26 -4
  16. package/tools/a2b/discover.ts +2 -2
  17. package/tools/bap/friend.ts +17 -24
  18. package/tools/bap/generate.test.ts +34 -32
  19. package/tools/bap/generate.ts +48 -43
  20. package/tools/bap/getCurrentAddress.ts +1 -3
  21. package/tools/bap/getId.test.ts +18 -10
  22. package/tools/bap/getId.ts +8 -6
  23. package/tools/bap/utils.ts +1 -1
  24. package/tools/bsocial/bmapReadPosts.ts +16 -2
  25. package/tools/bsocial/createPost.ts +21 -10
  26. package/tools/bsocial/readPosts.ts +10 -4
  27. package/tools/bsv/decodeTransaction.ts +1 -1
  28. package/tools/bsv/explore.ts +0 -4
  29. package/tools/bsv/getPrice.ts +1 -1
  30. package/tools/bsv/token.ts +14 -6
  31. package/tools/constants.ts +6 -2
  32. package/tools/index.ts +10 -10
  33. package/tools/mnee/getBalance.ts +1 -1
  34. package/tools/mnee/parseTx.ts +1 -1
  35. package/tools/mnee/sendMnee.ts +30 -8
  36. package/tools/ordinals/getInscription.ts +1 -3
  37. package/tools/ordinals/getTokenByIdOrTicker.ts +1 -3
  38. package/tools/ordinals/index.ts +4 -1
  39. package/tools/ordinals/marketListings.ts +1 -1
  40. package/tools/ordinals/marketSales.ts +1 -1
  41. package/tools/ordinals/searchInscriptions.ts +1 -1
  42. package/tools/utils/aip.ts +31 -22
  43. package/tools/utils/installAgentMaster.ts +8 -9
  44. package/tools/utils/toolRegistration.ts +21 -22
  45. package/tools/utils/transactionBuilder.ts +21 -14
  46. package/tools/wallet/a2bPublishMcp.ts +15 -5
  47. package/tools/wallet/brc100.ts +541 -310
  48. package/tools/wallet/cancelListing.ts +29 -37
  49. package/tools/wallet/createOrdinals.ts +43 -27
  50. package/tools/wallet/fetchPaymentUtxos.ts +7 -5
  51. package/tools/wallet/gatherCollectionInfo.ts +3 -5
  52. package/tools/wallet/getAddress.ts +1 -4
  53. package/tools/wallet/getBalance.ts +15 -15
  54. package/tools/wallet/{getBalanceDroplet.ts → getBalanceDroplit.ts} +6 -6
  55. package/tools/wallet/getBsv21Balances.ts +20 -14
  56. package/tools/wallet/getLockData.ts +11 -11
  57. package/tools/wallet/getOrdinals.ts +26 -20
  58. package/tools/wallet/integratedWallet.ts +55 -53
  59. package/tools/wallet/listOrdinal.ts +40 -39
  60. package/tools/wallet/listTokens.ts +18 -17
  61. package/tools/wallet/lockBsv.ts +36 -22
  62. package/tools/wallet/mintCollection.ts +15 -10
  63. package/tools/wallet/opnsDeregister.ts +29 -39
  64. package/tools/wallet/opnsRegister.ts +29 -39
  65. package/tools/wallet/purchaseListing.test.ts +27 -0
  66. package/tools/wallet/purchaseListing.ts +59 -40
  67. package/tools/wallet/refreshUtxos.ts +1 -1
  68. package/tools/wallet/schemas.ts +0 -13
  69. package/tools/wallet/sendAllBsv.ts +26 -20
  70. package/tools/wallet/sendBsv.ts +107 -0
  71. package/tools/wallet/setupDroplit.ts +157 -0
  72. package/tools/wallet/signBsm.ts +18 -18
  73. package/tools/wallet/sweepBsv.ts +48 -27
  74. package/tools/wallet/sweepBsv21.ts +42 -28
  75. package/tools/wallet/sweepOrdinals.ts +38 -26
  76. package/tools/wallet/tools.ts +11 -13
  77. package/tools/wallet/transactionHelper.ts +13 -5
  78. package/tools/wallet/transferOrdToken.ts +72 -84
  79. package/tools/wallet/unlockBsv.ts +23 -17
  80. package/tools/wallet/utxo.ts +1 -0
  81. package/tools/wallet/wallet.ts +1 -13
  82. package/tools/wallet/sendToAddress.ts +0 -74
  83. package/tools/wallet/setupDroplet.ts +0 -161
@@ -1,27 +1,61 @@
1
- import type { OneSatContext } from '@1sat/actions'
2
- import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'
3
- import type { CallToolResult } from '@modelcontextprotocol/sdk/types.js'
4
- import { z } from 'zod'
1
+ import type { OneSatContext } from "@1sat/actions";
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
5
 
6
6
  const noCtx: CallToolResult = {
7
- content: [{ type: 'text', text: 'Wallet not initialized.' }],
7
+ content: [{ type: "text", text: "Wallet not initialized." }],
8
8
  isError: true,
9
- }
9
+ };
10
10
 
11
11
  function result(data: unknown): CallToolResult {
12
- return { content: [{ type: 'text', text: JSON.stringify(data) }] }
12
+ return { content: [{ type: "text", text: JSON.stringify(data) }] };
13
13
  }
14
14
 
15
15
  function error(err: unknown): CallToolResult {
16
16
  return {
17
- content: [{ type: 'text', text: err instanceof Error ? err.message : String(err) }],
17
+ content: [
18
+ { type: "text", text: err instanceof Error ? err.message : String(err) },
19
+ ],
18
20
  isError: true,
21
+ };
22
+ }
23
+
24
+ /**
25
+ * Parse a JSON-encoded tool argument. Malformed JSON is reported rather than
26
+ * passed through as a raw string, which would surface later as an opaque
27
+ * wallet error about the wrong argument.
28
+ */
29
+ function parseJSON<T>(name: string, s: string | undefined): T | undefined {
30
+ if (!s) return undefined;
31
+ try {
32
+ return JSON.parse(s) as T;
33
+ } catch (e) {
34
+ throw new Error(
35
+ `${name} is not valid JSON: ${e instanceof Error ? e.message : String(e)}`,
36
+ );
37
+ }
38
+ }
39
+
40
+ /**
41
+ * Parse a JSON-encoded tool argument that the BRC-100 call requires. The MCP
42
+ * schema already marks these mandatory, so a missing value means the caller
43
+ * bypassed validation and is reported rather than forwarded as undefined.
44
+ */
45
+ function parseRequiredJSON<T>(name: string, s: string | undefined): T {
46
+ const parsed = parseJSON<T>(name, s);
47
+ if (parsed === undefined) {
48
+ throw new Error(`${name} is required`);
19
49
  }
50
+ return parsed;
20
51
  }
21
52
 
22
- function parseJSON(s: string | undefined): any {
23
- if (!s) return undefined
24
- try { return JSON.parse(s) } catch { return s }
53
+ /**
54
+ * BRC-100 models `identityKey` as the literal `true` — present or absent, never
55
+ * `false`. MCP arguments are plain booleans, so `false` is dropped here.
56
+ */
57
+ function identityKeyArg(v: boolean | undefined): true | undefined {
58
+ return v === true ? true : undefined;
25
59
  }
26
60
 
27
61
  export function registerBrc100Tools(
@@ -31,97 +65,154 @@ export function registerBrc100Tools(
31
65
  // ── Transaction lifecycle ──────────────────────────────────────────
32
66
 
33
67
  server.tool(
34
- 'wallet_createAction',
35
- 'Creates a new Bitcoin transaction. Handles funding, signing, and broadcasting based on options.',
68
+ "wallet_createAction",
69
+ "Creates a new Bitcoin transaction. Handles funding, signing, and broadcasting based on options.",
36
70
  {
37
- description: z.string().describe('5-50 char description of the action'),
38
- inputsJSON: z.string().optional().describe('JSON array of transaction inputs'),
39
- outputsJSON: z.string().optional().describe('JSON array of transaction outputs [{lockingScript, satoshis, outputDescription, basket, tags, customInstructions}]'),
40
- labelsJSON: z.string().optional().describe('JSON array of label strings'),
71
+ description: z.string().describe("5-50 char description of the action"),
72
+ inputBEEFJSON: z
73
+ .string()
74
+ .optional()
75
+ .describe(
76
+ "JSON array of BEEF bytes proving the inputs. Required whenever inputs are supplied — createAction rejects inputs without their proof chain.",
77
+ ),
78
+ inputsJSON: z
79
+ .string()
80
+ .optional()
81
+ .describe("JSON array of transaction inputs"),
82
+ outputsJSON: z
83
+ .string()
84
+ .optional()
85
+ .describe(
86
+ "JSON array of transaction outputs [{lockingScript, satoshis, outputDescription, basket, tags, customInstructions}]",
87
+ ),
88
+ labelsJSON: z.string().optional().describe("JSON array of label strings"),
41
89
  lockTime: z.number().optional(),
42
90
  version: z.number().optional(),
43
- optionsJSON: z.string().optional().describe('JSON CreateActionOptions: {noSend, sendWith, acceptDelayedBroadcast, signAndProcess, randomizeOutputs, noSendChange, knownTxids, trustSelf}'),
44
- },
45
- async ({ description, inputsJSON, outputsJSON, labelsJSON, lockTime, version, optionsJSON }) => {
46
- if (!ctx) return noCtx
91
+ optionsJSON: z
92
+ .string()
93
+ .optional()
94
+ .describe(
95
+ "JSON CreateActionOptions: {noSend, sendWith, acceptDelayedBroadcast, signAndProcess, randomizeOutputs, noSendChange, knownTxids, trustSelf}",
96
+ ),
97
+ },
98
+ async ({
99
+ description,
100
+ inputBEEFJSON,
101
+ inputsJSON,
102
+ outputsJSON,
103
+ labelsJSON,
104
+ lockTime,
105
+ version,
106
+ optionsJSON,
107
+ }) => {
108
+ if (!ctx) return noCtx;
47
109
  try {
48
- return result(await ctx.wallet.createAction({
49
- description,
50
- inputs: parseJSON(inputsJSON),
51
- outputs: parseJSON(outputsJSON),
52
- labels: parseJSON(labelsJSON),
53
- lockTime,
54
- version,
55
- options: parseJSON(optionsJSON),
56
- } as any))
57
- } catch (e) { return error(e) }
58
- },
59
- )
110
+ return result(
111
+ await ctx.wallet.createAction({
112
+ description,
113
+ inputBEEF: parseJSON("inputBEEFJSON", inputBEEFJSON),
114
+ inputs: parseJSON("inputsJSON", inputsJSON),
115
+ outputs: parseJSON("outputsJSON", outputsJSON),
116
+ labels: parseJSON("labelsJSON", labelsJSON),
117
+ lockTime,
118
+ version,
119
+ options: parseJSON("optionsJSON", optionsJSON),
120
+ }),
121
+ );
122
+ } catch (e) {
123
+ return error(e);
124
+ }
125
+ },
126
+ );
60
127
 
61
128
  server.tool(
62
- 'wallet_signAction',
63
- 'Signs a transaction previously created with createAction (when signAndProcess was false).',
129
+ "wallet_signAction",
130
+ "Signs a transaction previously created with createAction (when signAndProcess was false).",
64
131
  {
65
- spendsJSON: z.string().describe('JSON map of input index to {unlockingScript, sequenceNumber}'),
66
- reference: z.string().describe('Base64 reference from createAction result'),
67
- optionsJSON: z.string().optional().describe('JSON SignActionOptions: {acceptDelayedBroadcast, sendWith}'),
132
+ spendsJSON: z
133
+ .string()
134
+ .describe(
135
+ "JSON map of input index to {unlockingScript, sequenceNumber}",
136
+ ),
137
+ reference: z
138
+ .string()
139
+ .describe("Base64 reference from createAction result"),
140
+ optionsJSON: z
141
+ .string()
142
+ .optional()
143
+ .describe("JSON SignActionOptions: {acceptDelayedBroadcast, sendWith}"),
68
144
  },
69
145
  async ({ spendsJSON, reference, optionsJSON }) => {
70
- if (!ctx) return noCtx
146
+ if (!ctx) return noCtx;
71
147
  try {
72
- return result(await ctx.wallet.signAction({
73
- spends: parseJSON(spendsJSON),
74
- reference,
75
- options: parseJSON(optionsJSON),
76
- } as any))
77
- } catch (e) { return error(e) }
78
- },
79
- )
148
+ return result(
149
+ await ctx.wallet.signAction({
150
+ spends: parseRequiredJSON("spendsJSON", spendsJSON),
151
+ reference,
152
+ options: parseJSON("optionsJSON", optionsJSON),
153
+ }),
154
+ );
155
+ } catch (e) {
156
+ return error(e);
157
+ }
158
+ },
159
+ );
80
160
 
81
161
  server.tool(
82
- 'wallet_abortAction',
83
- 'Aborts a pending (nosend or unsigned) transaction, releasing consumed inputs back to spendable state.',
162
+ "wallet_abortAction",
163
+ "Aborts a pending (nosend or unsigned) transaction, releasing consumed inputs back to spendable state.",
84
164
  {
85
- reference: z.string().describe('Base64 reference of the transaction to abort'),
165
+ reference: z
166
+ .string()
167
+ .describe("Base64 reference of the transaction to abort"),
86
168
  },
87
169
  async ({ reference }) => {
88
- if (!ctx) return noCtx
170
+ if (!ctx) return noCtx;
89
171
  try {
90
- return result(await ctx.wallet.abortAction({ reference }))
91
- } catch (e) { return error(e) }
172
+ return result(await ctx.wallet.abortAction({ reference }));
173
+ } catch (e) {
174
+ return error(e);
175
+ }
92
176
  },
93
- )
177
+ );
94
178
 
95
179
  server.tool(
96
- 'wallet_internalizeAction',
97
- 'Internalizes an external transaction, adding its outputs to wallet baskets.',
180
+ "wallet_internalizeAction",
181
+ "Internalizes an external transaction, adding its outputs to wallet baskets.",
98
182
  {
99
- txJSON: z.string().describe('JSON array of AtomicBEEF bytes'),
100
- outputsJSON: z.string().describe('JSON array of outputs to internalize'),
101
- description: z.string().describe('5-50 char description'),
102
- labelsJSON: z.string().optional().describe('JSON array of label strings'),
183
+ txJSON: z.string().describe("JSON array of AtomicBEEF bytes"),
184
+ outputsJSON: z.string().describe("JSON array of outputs to internalize"),
185
+ description: z.string().describe("5-50 char description"),
186
+ labelsJSON: z.string().optional().describe("JSON array of label strings"),
103
187
  },
104
188
  async ({ txJSON, outputsJSON, description, labelsJSON }) => {
105
- if (!ctx) return noCtx
189
+ if (!ctx) return noCtx;
106
190
  try {
107
- return result(await ctx.wallet.internalizeAction({
108
- tx: parseJSON(txJSON),
109
- outputs: parseJSON(outputsJSON),
110
- description,
111
- labels: parseJSON(labelsJSON),
112
- } as any))
113
- } catch (e) { return error(e) }
114
- },
115
- )
191
+ return result(
192
+ await ctx.wallet.internalizeAction({
193
+ tx: parseRequiredJSON("txJSON", txJSON),
194
+ outputs: parseRequiredJSON("outputsJSON", outputsJSON),
195
+ description,
196
+ labels: parseJSON("labelsJSON", labelsJSON),
197
+ }),
198
+ );
199
+ } catch (e) {
200
+ return error(e);
201
+ }
202
+ },
203
+ );
116
204
 
117
205
  // ── Queries ────────────────────────────────────────────────────────
118
206
 
119
207
  server.tool(
120
- 'wallet_listActions',
121
- 'Lists wallet transactions filtered by labels, with optional input/output details.',
208
+ "wallet_listActions",
209
+ "Lists wallet transactions filtered by labels, with optional input/output details.",
122
210
  {
123
- labelsJSON: z.string().default('[]').describe('JSON array of label strings'),
124
- labelQueryMode: z.enum(['any', 'all']).default('any'),
211
+ labelsJSON: z
212
+ .string()
213
+ .default("[]")
214
+ .describe("JSON array of label strings"),
215
+ labelQueryMode: z.enum(["any", "all"]).default("any"),
125
216
  includeLabels: z.boolean().default(true),
126
217
  includeInputs: z.boolean().default(false),
127
218
  includeInputSourceLockingScripts: z.boolean().default(false),
@@ -132,24 +223,28 @@ export function registerBrc100Tools(
132
223
  offset: z.number().default(0),
133
224
  },
134
225
  async ({ labelsJSON, ...rest }) => {
135
- if (!ctx) return noCtx
226
+ if (!ctx) return noCtx;
136
227
  try {
137
- return result(await ctx.wallet.listActions({
138
- labels: parseJSON(labelsJSON),
139
- ...rest,
140
- } as any))
141
- } catch (e) { return error(e) }
142
- },
143
- )
228
+ return result(
229
+ await ctx.wallet.listActions({
230
+ labels: parseRequiredJSON("labelsJSON", labelsJSON),
231
+ ...rest,
232
+ }),
233
+ );
234
+ } catch (e) {
235
+ return error(e);
236
+ }
237
+ },
238
+ );
144
239
 
145
240
  server.tool(
146
- 'wallet_listOutputs',
147
- 'Lists spendable outputs in a basket, optionally filtered by tags.',
241
+ "wallet_listOutputs",
242
+ "Lists spendable outputs in a basket, optionally filtered by tags.",
148
243
  {
149
244
  basket: z.string().describe('Basket name (e.g. "default")'),
150
- tagsJSON: z.string().optional().describe('JSON array of tag strings'),
151
- tagQueryMode: z.enum(['all', 'any']).optional(),
152
- include: z.enum(['locking scripts', 'entire transactions']).optional(),
245
+ tagsJSON: z.string().optional().describe("JSON array of tag strings"),
246
+ tagQueryMode: z.enum(["all", "any"]).optional(),
247
+ include: z.enum(["locking scripts", "entire transactions"]).optional(),
153
248
  includeCustomInstructions: z.boolean().default(false),
154
249
  includeTags: z.boolean().default(false),
155
250
  includeLabels: z.boolean().default(false),
@@ -157,416 +252,552 @@ export function registerBrc100Tools(
157
252
  offset: z.number().default(0),
158
253
  },
159
254
  async ({ tagsJSON, ...rest }) => {
160
- if (!ctx) return noCtx
255
+ if (!ctx) return noCtx;
161
256
  try {
162
- return result(await ctx.wallet.listOutputs({
163
- tags: parseJSON(tagsJSON),
164
- ...rest,
165
- } as any))
166
- } catch (e) { return error(e) }
167
- },
168
- )
257
+ return result(
258
+ await ctx.wallet.listOutputs({
259
+ tags: parseJSON("tagsJSON", tagsJSON),
260
+ ...rest,
261
+ }),
262
+ );
263
+ } catch (e) {
264
+ return error(e);
265
+ }
266
+ },
267
+ );
169
268
 
170
269
  server.tool(
171
- 'wallet_relinquishOutput',
172
- 'Removes an output from a basket without spending it.',
270
+ "wallet_relinquishOutput",
271
+ "Removes an output from a basket without spending it.",
173
272
  {
174
- basket: z.string().describe('Basket name'),
175
- output: z.string().describe('Outpoint string (txid.vout)'),
273
+ basket: z.string().describe("Basket name"),
274
+ output: z.string().describe("Outpoint string (txid.vout)"),
176
275
  },
177
276
  async (args) => {
178
- if (!ctx) return noCtx
277
+ if (!ctx) return noCtx;
179
278
  try {
180
- return result(await ctx.wallet.relinquishOutput(args as any))
181
- } catch (e) { return error(e) }
279
+ return result(await ctx.wallet.relinquishOutput(args));
280
+ } catch (e) {
281
+ return error(e);
282
+ }
182
283
  },
183
- )
284
+ );
184
285
 
185
286
  // ── Keys & Crypto ──────────────────────────────────────────────────
186
287
 
187
288
  server.tool(
188
- 'wallet_getPublicKey',
289
+ "wallet_getPublicKey",
189
290
  'Retrieves a public key by protocol/key derivation. Use identityKey:true for the root identity key. protocolID is a JSON array like [2,"1sat"].',
190
291
  {
191
- identityKey: z.boolean().optional().describe('If true, return the identity key (ignores other args)'),
192
- protocolIDJSON: z.string().optional().describe('JSON array [securityLevel, protocolString] e.g. [2,"1sat"]'),
292
+ identityKey: z
293
+ .boolean()
294
+ .optional()
295
+ .describe("If true, return the identity key (ignores other args)"),
296
+ protocolIDJSON: z
297
+ .string()
298
+ .optional()
299
+ .describe('JSON array [securityLevel, protocolString] e.g. [2,"1sat"]'),
193
300
  keyID: z.string().optional(),
194
301
  counterparty: z.string().optional(),
195
302
  forSelf: z.boolean().optional(),
196
303
  privileged: z.boolean().optional(),
197
304
  privilegedReason: z.string().optional(),
305
+ seekPermission: z.boolean().optional(),
198
306
  },
199
- async ({ protocolIDJSON, ...rest }) => {
200
- if (!ctx) return noCtx
307
+ async ({ protocolIDJSON, identityKey, ...rest }) => {
308
+ if (!ctx) return noCtx;
201
309
  try {
202
- return result(await ctx.wallet.getPublicKey({
203
- protocolID: parseJSON(protocolIDJSON),
204
- ...rest,
205
- } as any))
206
- } catch (e) { return error(e) }
207
- },
208
- )
310
+ return result(
311
+ await ctx.wallet.getPublicKey({
312
+ protocolID: parseJSON("protocolIDJSON", protocolIDJSON),
313
+ identityKey: identityKeyArg(identityKey),
314
+ ...rest,
315
+ }),
316
+ );
317
+ } catch (e) {
318
+ return error(e);
319
+ }
320
+ },
321
+ );
209
322
 
210
323
  server.tool(
211
- 'wallet_encrypt',
324
+ "wallet_encrypt",
212
325
  'Encrypts data using wallet keys. protocolID is a JSON array like [2,"protocolName"].',
213
326
  {
214
- plaintext: z.array(z.number()).describe('Data bytes to encrypt'),
215
- protocolIDJSON: z.string().describe('JSON array [securityLevel, protocolString]'),
327
+ plaintext: z.array(z.number()).describe("Data bytes to encrypt"),
328
+ protocolIDJSON: z
329
+ .string()
330
+ .describe("JSON array [securityLevel, protocolString]"),
216
331
  keyID: z.string(),
217
332
  counterparty: z.string().optional(),
218
333
  privileged: z.boolean().optional(),
219
334
  },
220
335
  async ({ protocolIDJSON, ...rest }) => {
221
- if (!ctx) return noCtx
336
+ if (!ctx) return noCtx;
222
337
  try {
223
- return result(await ctx.wallet.encrypt({
224
- protocolID: parseJSON(protocolIDJSON),
225
- ...rest,
226
- } as any))
227
- } catch (e) { return error(e) }
228
- },
229
- )
338
+ return result(
339
+ await ctx.wallet.encrypt({
340
+ protocolID: parseRequiredJSON("protocolIDJSON", protocolIDJSON),
341
+ ...rest,
342
+ }),
343
+ );
344
+ } catch (e) {
345
+ return error(e);
346
+ }
347
+ },
348
+ );
230
349
 
231
350
  server.tool(
232
- 'wallet_decrypt',
233
- 'Decrypts data using wallet keys.',
351
+ "wallet_decrypt",
352
+ "Decrypts data using wallet keys.",
234
353
  {
235
- ciphertext: z.array(z.number()).describe('Encrypted data bytes'),
236
- protocolIDJSON: z.string().describe('JSON array [securityLevel, protocolString]'),
354
+ ciphertext: z.array(z.number()).describe("Encrypted data bytes"),
355
+ protocolIDJSON: z
356
+ .string()
357
+ .describe("JSON array [securityLevel, protocolString]"),
237
358
  keyID: z.string(),
238
359
  counterparty: z.string().optional(),
239
360
  privileged: z.boolean().optional(),
240
361
  },
241
362
  async ({ protocolIDJSON, ...rest }) => {
242
- if (!ctx) return noCtx
363
+ if (!ctx) return noCtx;
243
364
  try {
244
- return result(await ctx.wallet.decrypt({
245
- protocolID: parseJSON(protocolIDJSON),
246
- ...rest,
247
- } as any))
248
- } catch (e) { return error(e) }
249
- },
250
- )
365
+ return result(
366
+ await ctx.wallet.decrypt({
367
+ protocolID: parseRequiredJSON("protocolIDJSON", protocolIDJSON),
368
+ ...rest,
369
+ }),
370
+ );
371
+ } catch (e) {
372
+ return error(e);
373
+ }
374
+ },
375
+ );
251
376
 
252
377
  server.tool(
253
- 'wallet_createHmac',
254
- 'Creates an HMAC using wallet keys.',
378
+ "wallet_createHmac",
379
+ "Creates an HMAC using wallet keys.",
255
380
  {
256
- data: z.array(z.number()).describe('Data bytes'),
257
- protocolIDJSON: z.string().describe('JSON array [securityLevel, protocolString]'),
381
+ data: z.array(z.number()).describe("Data bytes"),
382
+ protocolIDJSON: z
383
+ .string()
384
+ .describe("JSON array [securityLevel, protocolString]"),
258
385
  keyID: z.string(),
259
386
  counterparty: z.string().optional(),
260
387
  privileged: z.boolean().optional(),
261
388
  },
262
389
  async ({ protocolIDJSON, ...rest }) => {
263
- if (!ctx) return noCtx
390
+ if (!ctx) return noCtx;
264
391
  try {
265
- return result(await ctx.wallet.createHmac({
266
- protocolID: parseJSON(protocolIDJSON),
267
- ...rest,
268
- } as any))
269
- } catch (e) { return error(e) }
270
- },
271
- )
392
+ return result(
393
+ await ctx.wallet.createHmac({
394
+ protocolID: parseRequiredJSON("protocolIDJSON", protocolIDJSON),
395
+ ...rest,
396
+ }),
397
+ );
398
+ } catch (e) {
399
+ return error(e);
400
+ }
401
+ },
402
+ );
272
403
 
273
404
  server.tool(
274
- 'wallet_verifyHmac',
275
- 'Verifies an HMAC using wallet keys.',
405
+ "wallet_verifyHmac",
406
+ "Verifies an HMAC using wallet keys.",
276
407
  {
277
- data: z.array(z.number()).describe('Data bytes'),
278
- hmac: z.array(z.number()).describe('HMAC bytes to verify'),
279
- protocolIDJSON: z.string().describe('JSON array [securityLevel, protocolString]'),
408
+ data: z.array(z.number()).describe("Data bytes"),
409
+ hmac: z.array(z.number()).describe("HMAC bytes to verify"),
410
+ protocolIDJSON: z
411
+ .string()
412
+ .describe("JSON array [securityLevel, protocolString]"),
280
413
  keyID: z.string(),
281
414
  counterparty: z.string().optional(),
282
415
  privileged: z.boolean().optional(),
283
416
  },
284
417
  async ({ protocolIDJSON, ...rest }) => {
285
- if (!ctx) return noCtx
418
+ if (!ctx) return noCtx;
286
419
  try {
287
- return result(await ctx.wallet.verifyHmac({
288
- protocolID: parseJSON(protocolIDJSON),
289
- ...rest,
290
- } as any))
291
- } catch (e) { return error(e) }
292
- },
293
- )
420
+ return result(
421
+ await ctx.wallet.verifyHmac({
422
+ protocolID: parseRequiredJSON("protocolIDJSON", protocolIDJSON),
423
+ ...rest,
424
+ }),
425
+ );
426
+ } catch (e) {
427
+ return error(e);
428
+ }
429
+ },
430
+ );
294
431
 
295
432
  server.tool(
296
- 'wallet_createSignature',
297
- 'Creates a digital signature using wallet keys.',
433
+ "wallet_createSignature",
434
+ "Creates a digital signature using wallet keys. Supply exactly one of data or hashToDirectlySign.",
298
435
  {
299
- data: z.array(z.number()).describe('Data bytes to sign'),
300
- protocolIDJSON: z.string().describe('JSON array [securityLevel, protocolString]'),
436
+ data: z.array(z.number()).optional().describe("Data bytes to sign"),
437
+ hashToDirectlySign: z
438
+ .array(z.number())
439
+ .optional()
440
+ .describe("Pre-computed 32-byte hash to sign directly"),
441
+ protocolIDJSON: z
442
+ .string()
443
+ .describe("JSON array [securityLevel, protocolString]"),
301
444
  keyID: z.string(),
302
445
  counterparty: z.string().optional(),
303
446
  privileged: z.boolean().optional(),
304
447
  },
305
448
  async ({ protocolIDJSON, ...rest }) => {
306
- if (!ctx) return noCtx
449
+ if (!ctx) return noCtx;
307
450
  try {
308
- return result(await ctx.wallet.createSignature({
309
- protocolID: parseJSON(protocolIDJSON),
310
- ...rest,
311
- } as any))
312
- } catch (e) { return error(e) }
313
- },
314
- )
451
+ return result(
452
+ await ctx.wallet.createSignature({
453
+ protocolID: parseRequiredJSON("protocolIDJSON", protocolIDJSON),
454
+ ...rest,
455
+ }),
456
+ );
457
+ } catch (e) {
458
+ return error(e);
459
+ }
460
+ },
461
+ );
315
462
 
316
463
  server.tool(
317
- 'wallet_verifySignature',
318
- 'Verifies a digital signature using wallet keys.',
464
+ "wallet_verifySignature",
465
+ "Verifies a digital signature using wallet keys. Supply exactly one of data or hashToDirectlyVerify.",
319
466
  {
320
- data: z.array(z.number()).describe('Data bytes that were signed'),
321
- signature: z.array(z.number()).describe('Signature bytes'),
322
- protocolIDJSON: z.string().describe('JSON array [securityLevel, protocolString]'),
467
+ data: z
468
+ .array(z.number())
469
+ .optional()
470
+ .describe("Data bytes that were signed"),
471
+ hashToDirectlyVerify: z
472
+ .array(z.number())
473
+ .optional()
474
+ .describe("Pre-computed 32-byte hash that was signed"),
475
+ signature: z.array(z.number()).describe("Signature bytes"),
476
+ protocolIDJSON: z
477
+ .string()
478
+ .describe("JSON array [securityLevel, protocolString]"),
323
479
  keyID: z.string(),
324
480
  counterparty: z.string().optional(),
325
481
  forSelf: z.boolean().optional(),
326
482
  privileged: z.boolean().optional(),
327
483
  },
328
484
  async ({ protocolIDJSON, ...rest }) => {
329
- if (!ctx) return noCtx
485
+ if (!ctx) return noCtx;
330
486
  try {
331
- return result(await ctx.wallet.verifySignature({
332
- protocolID: parseJSON(protocolIDJSON),
333
- ...rest,
334
- } as any))
335
- } catch (e) { return error(e) }
336
- },
337
- )
487
+ return result(
488
+ await ctx.wallet.verifySignature({
489
+ protocolID: parseRequiredJSON("protocolIDJSON", protocolIDJSON),
490
+ ...rest,
491
+ }),
492
+ );
493
+ } catch (e) {
494
+ return error(e);
495
+ }
496
+ },
497
+ );
338
498
 
339
499
  // ── Key Linkage ────────────────────────────────────────────────────
340
500
 
341
501
  server.tool(
342
- 'wallet_revealCounterpartyKeyLinkage',
343
- 'Reveals the linkage between the wallet identity and a counterparty to a verifier.',
502
+ "wallet_revealCounterpartyKeyLinkage",
503
+ "Reveals the linkage between the wallet identity and a counterparty to a verifier.",
344
504
  {
345
- counterparty: z.string().describe('Counterparty public key hex'),
346
- verifier: z.string().describe('Verifier public key hex'),
505
+ counterparty: z.string().describe("Counterparty public key hex"),
506
+ verifier: z.string().describe("Verifier public key hex"),
347
507
  privileged: z.boolean().optional(),
348
508
  privilegedReason: z.string().optional(),
349
509
  },
350
510
  async (args) => {
351
- if (!ctx) return noCtx
511
+ if (!ctx) return noCtx;
352
512
  try {
353
- return result(await ctx.wallet.revealCounterpartyKeyLinkage(args as any))
354
- } catch (e) { return error(e) }
513
+ return result(await ctx.wallet.revealCounterpartyKeyLinkage(args));
514
+ } catch (e) {
515
+ return error(e);
516
+ }
355
517
  },
356
- )
518
+ );
357
519
 
358
520
  server.tool(
359
- 'wallet_revealSpecificKeyLinkage',
360
- 'Reveals linkage for a specific protocol/key combination to a verifier.',
521
+ "wallet_revealSpecificKeyLinkage",
522
+ "Reveals linkage for a specific protocol/key combination to a verifier.",
361
523
  {
362
- counterparty: z.string().describe('Counterparty public key hex'),
363
- verifier: z.string().describe('Verifier public key hex'),
364
- protocolIDJSON: z.string().describe('JSON array [securityLevel, protocolString]'),
524
+ counterparty: z.string().describe("Counterparty public key hex"),
525
+ verifier: z.string().describe("Verifier public key hex"),
526
+ protocolIDJSON: z
527
+ .string()
528
+ .describe("JSON array [securityLevel, protocolString]"),
365
529
  keyID: z.string(),
366
530
  privileged: z.boolean().optional(),
367
531
  privilegedReason: z.string().optional(),
368
532
  },
369
533
  async ({ protocolIDJSON, ...rest }) => {
370
- if (!ctx) return noCtx
534
+ if (!ctx) return noCtx;
371
535
  try {
372
- return result(await ctx.wallet.revealSpecificKeyLinkage({
373
- protocolID: parseJSON(protocolIDJSON),
374
- ...rest,
375
- } as any))
376
- } catch (e) { return error(e) }
377
- },
378
- )
536
+ return result(
537
+ await ctx.wallet.revealSpecificKeyLinkage({
538
+ protocolID: parseRequiredJSON("protocolIDJSON", protocolIDJSON),
539
+ ...rest,
540
+ }),
541
+ );
542
+ } catch (e) {
543
+ return error(e);
544
+ }
545
+ },
546
+ );
379
547
 
380
548
  // ── Certificates ───────────────────────────────────────────────────
381
549
 
382
550
  server.tool(
383
- 'wallet_acquireCertificate',
384
- 'Acquires an identity certificate from a certifier.',
551
+ "wallet_acquireCertificate",
552
+ "Acquires an identity certificate from a certifier.",
385
553
  {
386
- type: z.string().describe('Certificate type (base64)'),
387
- certifier: z.string().describe('Certifier public key hex'),
388
- acquisitionProtocol: z.enum(['direct', 'issuance']),
389
- fieldsJSON: z.string().describe('JSON object of certificate fields'),
554
+ type: z.string().describe("Certificate type (base64)"),
555
+ certifier: z.string().describe("Certifier public key hex"),
556
+ acquisitionProtocol: z.enum(["direct", "issuance"]),
557
+ fieldsJSON: z.string().describe("JSON object of certificate fields"),
390
558
  serialNumber: z.string().optional(),
391
559
  revocationOutpoint: z.string().optional(),
392
560
  signature: z.string().optional(),
393
561
  certifierUrl: z.string().optional(),
394
562
  keyringRevealer: z.string().optional(),
395
- keyringForSubjectJSON: z.string().optional().describe('JSON object of keyring for subject'),
563
+ keyringForSubjectJSON: z
564
+ .string()
565
+ .optional()
566
+ .describe("JSON object of keyring for subject"),
396
567
  privileged: z.boolean().optional(),
397
568
  privilegedReason: z.string().optional(),
398
569
  },
399
570
  async ({ fieldsJSON, keyringForSubjectJSON, ...rest }) => {
400
- if (!ctx) return noCtx
571
+ if (!ctx) return noCtx;
401
572
  try {
402
- return result(await ctx.wallet.acquireCertificate({
403
- fields: parseJSON(fieldsJSON),
404
- keyringForSubject: parseJSON(keyringForSubjectJSON),
405
- ...rest,
406
- } as any))
407
- } catch (e) { return error(e) }
408
- },
409
- )
573
+ return result(
574
+ await ctx.wallet.acquireCertificate({
575
+ fields: parseRequiredJSON("fieldsJSON", fieldsJSON),
576
+ keyringForSubject: parseJSON(
577
+ "keyringForSubjectJSON",
578
+ keyringForSubjectJSON,
579
+ ),
580
+ ...rest,
581
+ }),
582
+ );
583
+ } catch (e) {
584
+ return error(e);
585
+ }
586
+ },
587
+ );
410
588
 
411
589
  server.tool(
412
- 'wallet_listCertificates',
413
- 'Lists identity certificates filtered by certifiers and types.',
590
+ "wallet_listCertificates",
591
+ "Lists identity certificates filtered by certifiers and types.",
414
592
  {
415
- certifiersJSON: z.string().describe('JSON array of certifier public key hexes'),
416
- typesJSON: z.string().describe('JSON array of certificate types (base64)'),
593
+ certifiersJSON: z
594
+ .string()
595
+ .describe("JSON array of certifier public key hexes"),
596
+ typesJSON: z
597
+ .string()
598
+ .describe("JSON array of certificate types (base64)"),
417
599
  limit: z.number().default(25),
418
600
  offset: z.number().default(0),
419
601
  privileged: z.boolean().optional(),
420
602
  privilegedReason: z.string().optional(),
421
603
  },
422
604
  async ({ certifiersJSON, typesJSON, ...rest }) => {
423
- if (!ctx) return noCtx
605
+ if (!ctx) return noCtx;
424
606
  try {
425
- return result(await ctx.wallet.listCertificates({
426
- certifiers: parseJSON(certifiersJSON),
427
- types: parseJSON(typesJSON),
428
- ...rest,
429
- } as any))
430
- } catch (e) { return error(e) }
431
- },
432
- )
607
+ return result(
608
+ await ctx.wallet.listCertificates({
609
+ certifiers: parseRequiredJSON("certifiersJSON", certifiersJSON),
610
+ types: parseRequiredJSON("typesJSON", typesJSON),
611
+ ...rest,
612
+ }),
613
+ );
614
+ } catch (e) {
615
+ return error(e);
616
+ }
617
+ },
618
+ );
433
619
 
434
620
  server.tool(
435
- 'wallet_proveCertificate',
436
- 'Proves select fields of a certificate to a verifier.',
621
+ "wallet_proveCertificate",
622
+ "Proves select fields of a certificate to a verifier.",
437
623
  {
438
- certificateJSON: z.string().describe('JSON object of the certificate to prove'),
439
- fieldsToRevealJSON: z.string().describe('JSON array of field names to reveal'),
440
- verifier: z.string().describe('Verifier public key hex'),
624
+ certificateJSON: z
625
+ .string()
626
+ .describe("JSON object of the certificate to prove"),
627
+ fieldsToRevealJSON: z
628
+ .string()
629
+ .describe("JSON array of field names to reveal"),
630
+ verifier: z.string().describe("Verifier public key hex"),
441
631
  privileged: z.boolean().optional(),
442
632
  privilegedReason: z.string().optional(),
443
633
  },
444
634
  async ({ certificateJSON, fieldsToRevealJSON, ...rest }) => {
445
- if (!ctx) return noCtx
635
+ if (!ctx) return noCtx;
446
636
  try {
447
- return result(await ctx.wallet.proveCertificate({
448
- certificate: parseJSON(certificateJSON),
449
- fieldsToReveal: parseJSON(fieldsToRevealJSON),
450
- ...rest,
451
- } as any))
452
- } catch (e) { return error(e) }
453
- },
454
- )
637
+ return result(
638
+ await ctx.wallet.proveCertificate({
639
+ certificate: parseRequiredJSON("certificateJSON", certificateJSON),
640
+ fieldsToReveal: parseRequiredJSON(
641
+ "fieldsToRevealJSON",
642
+ fieldsToRevealJSON,
643
+ ),
644
+ ...rest,
645
+ }),
646
+ );
647
+ } catch (e) {
648
+ return error(e);
649
+ }
650
+ },
651
+ );
455
652
 
456
653
  server.tool(
457
- 'wallet_relinquishCertificate',
458
- 'Removes a certificate from the wallet.',
654
+ "wallet_relinquishCertificate",
655
+ "Removes a certificate from the wallet.",
459
656
  {
460
- type: z.string().describe('Certificate type'),
461
- serialNumber: z.string().describe('Certificate serial number'),
462
- certifier: z.string().describe('Certifier public key hex'),
657
+ type: z.string().describe("Certificate type"),
658
+ serialNumber: z.string().describe("Certificate serial number"),
659
+ certifier: z.string().describe("Certifier public key hex"),
463
660
  },
464
661
  async (args) => {
465
- if (!ctx) return noCtx
662
+ if (!ctx) return noCtx;
466
663
  try {
467
- return result(await ctx.wallet.relinquishCertificate(args as any))
468
- } catch (e) { return error(e) }
664
+ return result(await ctx.wallet.relinquishCertificate(args));
665
+ } catch (e) {
666
+ return error(e);
667
+ }
469
668
  },
470
- )
669
+ );
471
670
 
472
671
  // ── Discovery ──────────────────────────────────────────────────────
473
672
 
474
673
  server.tool(
475
- 'wallet_discoverByIdentityKey',
476
- 'Discovers certificates issued to a given identity key.',
674
+ "wallet_discoverByIdentityKey",
675
+ "Discovers certificates issued to a given identity key.",
477
676
  {
478
- identityKey: z.string().describe('Identity public key hex'),
677
+ identityKey: z.string().describe("Identity public key hex"),
479
678
  limit: z.number().default(25),
480
679
  offset: z.number().default(0),
481
680
  },
482
681
  async (args) => {
483
- if (!ctx) return noCtx
682
+ if (!ctx) return noCtx;
484
683
  try {
485
- return result(await ctx.wallet.discoverByIdentityKey(args as any))
486
- } catch (e) { return error(e) }
684
+ return result(await ctx.wallet.discoverByIdentityKey(args));
685
+ } catch (e) {
686
+ return error(e);
687
+ }
487
688
  },
488
- )
689
+ );
489
690
 
490
691
  server.tool(
491
- 'wallet_discoverByAttributes',
492
- 'Discovers certificates matching specific attributes.',
692
+ "wallet_discoverByAttributes",
693
+ "Discovers certificates matching specific attributes.",
493
694
  {
494
- attributesJSON: z.string().describe('JSON object of attribute key/value pairs to match'),
695
+ attributesJSON: z
696
+ .string()
697
+ .describe("JSON object of attribute key/value pairs to match"),
495
698
  limit: z.number().default(25),
496
699
  offset: z.number().default(0),
497
700
  },
498
701
  async ({ attributesJSON, ...rest }) => {
499
- if (!ctx) return noCtx
702
+ if (!ctx) return noCtx;
500
703
  try {
501
- return result(await ctx.wallet.discoverByAttributes({
502
- attributes: parseJSON(attributesJSON),
503
- ...rest,
504
- } as any))
505
- } catch (e) { return error(e) }
506
- },
507
- )
704
+ return result(
705
+ await ctx.wallet.discoverByAttributes({
706
+ attributes: parseRequiredJSON("attributesJSON", attributesJSON),
707
+ ...rest,
708
+ }),
709
+ );
710
+ } catch (e) {
711
+ return error(e);
712
+ }
713
+ },
714
+ );
508
715
 
509
716
  // ── Info ────────────────────────────────────────────────────────────
510
717
 
511
718
  server.tool(
512
- 'wallet_isAuthenticated',
513
- 'Checks if the wallet user is authenticated.',
719
+ "wallet_isAuthenticated",
720
+ "Checks if the wallet user is authenticated.",
721
+ {},
722
+ async () => {
723
+ if (!ctx) return noCtx;
724
+ try {
725
+ return result(await ctx.wallet.isAuthenticated({}));
726
+ } catch (e) {
727
+ return error(e);
728
+ }
729
+ },
730
+ );
731
+
732
+ server.tool(
733
+ "wallet_waitForAuthentication",
734
+ "Blocks until the wallet user is authenticated.",
514
735
  {},
515
736
  async () => {
516
- if (!ctx) return noCtx
737
+ if (!ctx) return noCtx;
517
738
  try {
518
- return result(await ctx.wallet.isAuthenticated({}))
519
- } catch (e) { return error(e) }
739
+ return result(await ctx.wallet.waitForAuthentication({}));
740
+ } catch (e) {
741
+ return error(e);
742
+ }
520
743
  },
521
- )
744
+ );
522
745
 
523
746
  server.tool(
524
- 'wallet_getHeight',
525
- 'Gets the current blockchain height.',
747
+ "wallet_getHeight",
748
+ "Gets the current blockchain height.",
526
749
  {},
527
750
  async () => {
528
- if (!ctx) return noCtx
751
+ if (!ctx) return noCtx;
529
752
  try {
530
- return result(await ctx.wallet.getHeight({}))
531
- } catch (e) { return error(e) }
753
+ return result(await ctx.wallet.getHeight({}));
754
+ } catch (e) {
755
+ return error(e);
756
+ }
532
757
  },
533
- )
758
+ );
534
759
 
535
760
  server.tool(
536
- 'wallet_getHeaderForHeight',
537
- 'Gets the 80-byte block header at a given height.',
761
+ "wallet_getHeaderForHeight",
762
+ "Gets the 80-byte block header at a given height.",
538
763
  {
539
- height: z.number().describe('Block height'),
764
+ height: z.number().describe("Block height"),
540
765
  },
541
766
  async ({ height }) => {
542
- if (!ctx) return noCtx
767
+ if (!ctx) return noCtx;
543
768
  try {
544
- return result(await ctx.wallet.getHeaderForHeight({ height }))
545
- } catch (e) { return error(e) }
769
+ return result(await ctx.wallet.getHeaderForHeight({ height }));
770
+ } catch (e) {
771
+ return error(e);
772
+ }
546
773
  },
547
- )
774
+ );
548
775
 
549
776
  server.tool(
550
- 'wallet_getNetwork',
551
- 'Gets the network the wallet is connected to (mainnet or testnet).',
777
+ "wallet_getNetwork",
778
+ "Gets the network the wallet is connected to (mainnet or testnet).",
552
779
  {},
553
780
  async () => {
554
- if (!ctx) return noCtx
781
+ if (!ctx) return noCtx;
555
782
  try {
556
- return result(await ctx.wallet.getNetwork({}))
557
- } catch (e) { return error(e) }
783
+ return result(await ctx.wallet.getNetwork({}));
784
+ } catch (e) {
785
+ return error(e);
786
+ }
558
787
  },
559
- )
788
+ );
560
789
 
561
790
  server.tool(
562
- 'wallet_getVersion',
563
- 'Gets the wallet implementation version.',
791
+ "wallet_getVersion",
792
+ "Gets the wallet implementation version.",
564
793
  {},
565
794
  async () => {
566
- if (!ctx) return noCtx
795
+ if (!ctx) return noCtx;
567
796
  try {
568
- return result(await ctx.wallet.getVersion({}))
569
- } catch (e) { return error(e) }
797
+ return result(await ctx.wallet.getVersion({}));
798
+ } catch (e) {
799
+ return error(e);
800
+ }
570
801
  },
571
- )
802
+ );
572
803
  }