bsv-mcp 0.2.9 → 0.2.10
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.
- package/dist/index.js +91390 -27617
- package/index.ts +27 -0
- package/package.json +3 -1
- package/tools/index.ts +7 -2
- package/tools/wallet/brc100.ts +572 -0
- package/tools/wallet/createOrdinals.ts +62 -143
- package/tools/wallet/getBalance.ts +29 -25
- package/tools/wallet/purchaseListing.ts +89 -290
- package/tools/wallet/schemas.ts +7 -11
- package/tools/wallet/sendToAddress.ts +46 -47
- package/tools/wallet/tools.ts +12 -86
- package/tools/wallet/transferOrdToken.ts +137 -129
package/index.ts
CHANGED
|
@@ -35,6 +35,7 @@ import { getBsvPriceWithCache } from "./tools/bsv/getPrice.ts";
|
|
|
35
35
|
import { registerAllTools, type ToolsConfig } from "./tools/index.ts";
|
|
36
36
|
import { IntegratedWallet } from "./tools/wallet/integratedWallet.ts";
|
|
37
37
|
import { Wallet } from "./tools/wallet/wallet.ts";
|
|
38
|
+
import { initWallet, destroyWallet } from "./utils/walletInit.ts";
|
|
38
39
|
import { BunSSEServerTransport } from "./transports/sse.ts";
|
|
39
40
|
import {
|
|
40
41
|
type BSVJWTPayload,
|
|
@@ -858,6 +859,8 @@ Authentication:
|
|
|
858
859
|
|
|
859
860
|
let wallet: Wallet | undefined;
|
|
860
861
|
let integratedWallet: IntegratedWallet | undefined;
|
|
862
|
+
let remoteCtx: import("@1sat/actions").OneSatContext | undefined;
|
|
863
|
+
let remoteServices: import("@1sat/wallet-remote").OneSatServices | undefined;
|
|
861
864
|
|
|
862
865
|
if (CONFIG.loadTools) {
|
|
863
866
|
// Check if we should use Droplet API mode
|
|
@@ -934,6 +937,21 @@ Authentication:
|
|
|
934
937
|
effectiveConfig.loadMneeTools = false;
|
|
935
938
|
effectiveConfig.loadBapTools = false;
|
|
936
939
|
}
|
|
940
|
+
|
|
941
|
+
// Initialize remote BRC-100 wallet alongside the local wallet
|
|
942
|
+
try {
|
|
943
|
+
const chain = (process.env.BSV_CHAIN as "main" | "test") ?? "main";
|
|
944
|
+
const remoteResult = await initWallet(payPk.toWif(), chain);
|
|
945
|
+
remoteCtx = remoteResult.ctx;
|
|
946
|
+
remoteServices = remoteResult.services;
|
|
947
|
+
logFunc(
|
|
948
|
+
`\x1b[32mINFO: Remote BRC-100 wallet initialized. Deposit address: ${remoteResult.depositAddress}\x1b[0m`,
|
|
949
|
+
);
|
|
950
|
+
} catch (e) {
|
|
951
|
+
logFunc(
|
|
952
|
+
`\x1b[33mWARN: Remote BRC-100 wallet initialization failed: ${e instanceof Error ? e.message : String(e)}. Falling back to local wallet only.\x1b[0m`,
|
|
953
|
+
);
|
|
954
|
+
}
|
|
937
955
|
}
|
|
938
956
|
|
|
939
957
|
// Disable MNEE tools if wallet is not available
|
|
@@ -961,6 +979,8 @@ Authentication:
|
|
|
961
979
|
wallet,
|
|
962
980
|
integratedWallet,
|
|
963
981
|
disableBroadcasting: effectiveConfig.disableBroadcasting,
|
|
982
|
+
ctx: remoteCtx,
|
|
983
|
+
services: remoteServices,
|
|
964
984
|
};
|
|
965
985
|
|
|
966
986
|
registerAllTools(server, toolsConfig);
|
|
@@ -979,6 +999,13 @@ Authentication:
|
|
|
979
999
|
registerResources(server);
|
|
980
1000
|
}
|
|
981
1001
|
|
|
1002
|
+
// Clean up remote wallet on shutdown
|
|
1003
|
+
for (const sig of ["SIGINT", "SIGTERM"] as const) {
|
|
1004
|
+
process.once(sig, () => {
|
|
1005
|
+
destroyWallet().catch(() => {});
|
|
1006
|
+
});
|
|
1007
|
+
}
|
|
1008
|
+
|
|
982
1009
|
// Start the server based on transport mode
|
|
983
1010
|
if (CONFIG.transportMode === "stdio") {
|
|
984
1011
|
const transport = new StdioServerTransport();
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "bsv-mcp",
|
|
3
3
|
"module": "dist/index.js",
|
|
4
4
|
"type": "module",
|
|
5
|
-
"version": "0.2.
|
|
5
|
+
"version": "0.2.10",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"author": "satchmo",
|
|
8
8
|
"description": "A collection of Bitcoin SV (BSV) tools for the Model Context Protocol (MCP) framework",
|
|
@@ -79,6 +79,8 @@
|
|
|
79
79
|
"typescript": "^5.9.3"
|
|
80
80
|
},
|
|
81
81
|
"dependencies": {
|
|
82
|
+
"@1sat/actions": "0.0.25",
|
|
83
|
+
"@1sat/wallet-remote": "0.0.5",
|
|
82
84
|
"@bsv/sdk": "^2.0.6",
|
|
83
85
|
"@modelcontextprotocol/ext-apps": "^1.2.0",
|
|
84
86
|
"@modelcontextprotocol/sdk": "^1.27.1",
|
package/tools/index.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import type { OneSatContext } from "@1sat/actions";
|
|
2
|
+
import type { OneSatServices } from "@1sat/wallet-remote";
|
|
1
3
|
import type { PrivateKey } from "@bsv/sdk";
|
|
2
4
|
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
3
5
|
import { registerA2bDiscoverTool } from "./a2b/discover";
|
|
@@ -41,6 +43,8 @@ export interface ToolsConfig {
|
|
|
41
43
|
wallet?: Wallet;
|
|
42
44
|
integratedWallet?: IntegratedWallet;
|
|
43
45
|
disableBroadcasting?: boolean; // For wallet tools
|
|
46
|
+
ctx?: OneSatContext;
|
|
47
|
+
services?: OneSatServices;
|
|
44
48
|
}
|
|
45
49
|
|
|
46
50
|
/**
|
|
@@ -126,9 +130,10 @@ export function registerAllTools(
|
|
|
126
130
|
} else if (config.wallet) {
|
|
127
131
|
// Register normal wallet tools
|
|
128
132
|
const walletToolOptions = {
|
|
129
|
-
disableBroadcasting: config.disableBroadcasting === true,
|
|
130
|
-
enableA2bTools: enableA2bTools,
|
|
133
|
+
disableBroadcasting: config.disableBroadcasting === true,
|
|
134
|
+
enableA2bTools: enableA2bTools,
|
|
131
135
|
identityPk: config.identityPk,
|
|
136
|
+
ctx: config.ctx,
|
|
132
137
|
};
|
|
133
138
|
registerWalletTools(server, config.wallet, walletToolOptions);
|
|
134
139
|
}
|
|
@@ -0,0 +1,572 @@
|
|
|
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
|
+
|
|
6
|
+
const noCtx: CallToolResult = {
|
|
7
|
+
content: [{ type: 'text', text: 'Wallet not initialized.' }],
|
|
8
|
+
isError: true,
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function result(data: unknown): CallToolResult {
|
|
12
|
+
return { content: [{ type: 'text', text: JSON.stringify(data) }] }
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function error(err: unknown): CallToolResult {
|
|
16
|
+
return {
|
|
17
|
+
content: [{ type: 'text', text: err instanceof Error ? err.message : String(err) }],
|
|
18
|
+
isError: true,
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function parseJSON(s: string | undefined): any {
|
|
23
|
+
if (!s) return undefined
|
|
24
|
+
try { return JSON.parse(s) } catch { return s }
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function registerBrc100Tools(
|
|
28
|
+
server: McpServer,
|
|
29
|
+
ctx: OneSatContext | undefined,
|
|
30
|
+
) {
|
|
31
|
+
// ── Transaction lifecycle ──────────────────────────────────────────
|
|
32
|
+
|
|
33
|
+
server.tool(
|
|
34
|
+
'wallet_createAction',
|
|
35
|
+
'Creates a new Bitcoin transaction. Handles funding, signing, and broadcasting based on options.',
|
|
36
|
+
{
|
|
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'),
|
|
41
|
+
lockTime: z.number().optional(),
|
|
42
|
+
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
|
|
47
|
+
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
|
+
)
|
|
60
|
+
|
|
61
|
+
server.tool(
|
|
62
|
+
'wallet_signAction',
|
|
63
|
+
'Signs a transaction previously created with createAction (when signAndProcess was false).',
|
|
64
|
+
{
|
|
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}'),
|
|
68
|
+
},
|
|
69
|
+
async ({ spendsJSON, reference, optionsJSON }) => {
|
|
70
|
+
if (!ctx) return noCtx
|
|
71
|
+
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
|
+
)
|
|
80
|
+
|
|
81
|
+
server.tool(
|
|
82
|
+
'wallet_abortAction',
|
|
83
|
+
'Aborts a pending (nosend or unsigned) transaction, releasing consumed inputs back to spendable state.',
|
|
84
|
+
{
|
|
85
|
+
reference: z.string().describe('Base64 reference of the transaction to abort'),
|
|
86
|
+
},
|
|
87
|
+
async ({ reference }) => {
|
|
88
|
+
if (!ctx) return noCtx
|
|
89
|
+
try {
|
|
90
|
+
return result(await ctx.wallet.abortAction({ reference }))
|
|
91
|
+
} catch (e) { return error(e) }
|
|
92
|
+
},
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
server.tool(
|
|
96
|
+
'wallet_internalizeAction',
|
|
97
|
+
'Internalizes an external transaction, adding its outputs to wallet baskets.',
|
|
98
|
+
{
|
|
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'),
|
|
103
|
+
},
|
|
104
|
+
async ({ txJSON, outputsJSON, description, labelsJSON }) => {
|
|
105
|
+
if (!ctx) return noCtx
|
|
106
|
+
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
|
+
)
|
|
116
|
+
|
|
117
|
+
// ── Queries ────────────────────────────────────────────────────────
|
|
118
|
+
|
|
119
|
+
server.tool(
|
|
120
|
+
'wallet_listActions',
|
|
121
|
+
'Lists wallet transactions filtered by labels, with optional input/output details.',
|
|
122
|
+
{
|
|
123
|
+
labelsJSON: z.string().default('[]').describe('JSON array of label strings'),
|
|
124
|
+
labelQueryMode: z.enum(['any', 'all']).default('any'),
|
|
125
|
+
includeLabels: z.boolean().default(true),
|
|
126
|
+
includeInputs: z.boolean().default(false),
|
|
127
|
+
includeInputSourceLockingScripts: z.boolean().default(false),
|
|
128
|
+
includeInputUnlockingScripts: z.boolean().default(false),
|
|
129
|
+
includeOutputs: z.boolean().default(false),
|
|
130
|
+
includeOutputLockingScripts: z.boolean().default(false),
|
|
131
|
+
limit: z.number().default(25),
|
|
132
|
+
offset: z.number().default(0),
|
|
133
|
+
},
|
|
134
|
+
async ({ labelsJSON, ...rest }) => {
|
|
135
|
+
if (!ctx) return noCtx
|
|
136
|
+
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
|
+
)
|
|
144
|
+
|
|
145
|
+
server.tool(
|
|
146
|
+
'wallet_listOutputs',
|
|
147
|
+
'Lists spendable outputs in a basket, optionally filtered by tags.',
|
|
148
|
+
{
|
|
149
|
+
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(),
|
|
153
|
+
includeCustomInstructions: z.boolean().default(false),
|
|
154
|
+
includeTags: z.boolean().default(false),
|
|
155
|
+
includeLabels: z.boolean().default(false),
|
|
156
|
+
limit: z.number().default(25),
|
|
157
|
+
offset: z.number().default(0),
|
|
158
|
+
},
|
|
159
|
+
async ({ tagsJSON, ...rest }) => {
|
|
160
|
+
if (!ctx) return noCtx
|
|
161
|
+
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
|
+
)
|
|
169
|
+
|
|
170
|
+
server.tool(
|
|
171
|
+
'wallet_relinquishOutput',
|
|
172
|
+
'Removes an output from a basket without spending it.',
|
|
173
|
+
{
|
|
174
|
+
basket: z.string().describe('Basket name'),
|
|
175
|
+
output: z.string().describe('Outpoint string (txid.vout)'),
|
|
176
|
+
},
|
|
177
|
+
async (args) => {
|
|
178
|
+
if (!ctx) return noCtx
|
|
179
|
+
try {
|
|
180
|
+
return result(await ctx.wallet.relinquishOutput(args as any))
|
|
181
|
+
} catch (e) { return error(e) }
|
|
182
|
+
},
|
|
183
|
+
)
|
|
184
|
+
|
|
185
|
+
// ── Keys & Crypto ──────────────────────────────────────────────────
|
|
186
|
+
|
|
187
|
+
server.tool(
|
|
188
|
+
'wallet_getPublicKey',
|
|
189
|
+
'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
|
+
{
|
|
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"]'),
|
|
193
|
+
keyID: z.string().optional(),
|
|
194
|
+
counterparty: z.string().optional(),
|
|
195
|
+
forSelf: z.boolean().optional(),
|
|
196
|
+
privileged: z.boolean().optional(),
|
|
197
|
+
privilegedReason: z.string().optional(),
|
|
198
|
+
},
|
|
199
|
+
async ({ protocolIDJSON, ...rest }) => {
|
|
200
|
+
if (!ctx) return noCtx
|
|
201
|
+
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
|
+
)
|
|
209
|
+
|
|
210
|
+
server.tool(
|
|
211
|
+
'wallet_encrypt',
|
|
212
|
+
'Encrypts data using wallet keys. protocolID is a JSON array like [2,"protocolName"].',
|
|
213
|
+
{
|
|
214
|
+
plaintext: z.array(z.number()).describe('Data bytes to encrypt'),
|
|
215
|
+
protocolIDJSON: z.string().describe('JSON array [securityLevel, protocolString]'),
|
|
216
|
+
keyID: z.string(),
|
|
217
|
+
counterparty: z.string().optional(),
|
|
218
|
+
privileged: z.boolean().optional(),
|
|
219
|
+
},
|
|
220
|
+
async ({ protocolIDJSON, ...rest }) => {
|
|
221
|
+
if (!ctx) return noCtx
|
|
222
|
+
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
|
+
)
|
|
230
|
+
|
|
231
|
+
server.tool(
|
|
232
|
+
'wallet_decrypt',
|
|
233
|
+
'Decrypts data using wallet keys.',
|
|
234
|
+
{
|
|
235
|
+
ciphertext: z.array(z.number()).describe('Encrypted data bytes'),
|
|
236
|
+
protocolIDJSON: z.string().describe('JSON array [securityLevel, protocolString]'),
|
|
237
|
+
keyID: z.string(),
|
|
238
|
+
counterparty: z.string().optional(),
|
|
239
|
+
privileged: z.boolean().optional(),
|
|
240
|
+
},
|
|
241
|
+
async ({ protocolIDJSON, ...rest }) => {
|
|
242
|
+
if (!ctx) return noCtx
|
|
243
|
+
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
|
+
)
|
|
251
|
+
|
|
252
|
+
server.tool(
|
|
253
|
+
'wallet_createHmac',
|
|
254
|
+
'Creates an HMAC using wallet keys.',
|
|
255
|
+
{
|
|
256
|
+
data: z.array(z.number()).describe('Data bytes'),
|
|
257
|
+
protocolIDJSON: z.string().describe('JSON array [securityLevel, protocolString]'),
|
|
258
|
+
keyID: z.string(),
|
|
259
|
+
counterparty: z.string().optional(),
|
|
260
|
+
privileged: z.boolean().optional(),
|
|
261
|
+
},
|
|
262
|
+
async ({ protocolIDJSON, ...rest }) => {
|
|
263
|
+
if (!ctx) return noCtx
|
|
264
|
+
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
|
+
)
|
|
272
|
+
|
|
273
|
+
server.tool(
|
|
274
|
+
'wallet_verifyHmac',
|
|
275
|
+
'Verifies an HMAC using wallet keys.',
|
|
276
|
+
{
|
|
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]'),
|
|
280
|
+
keyID: z.string(),
|
|
281
|
+
counterparty: z.string().optional(),
|
|
282
|
+
privileged: z.boolean().optional(),
|
|
283
|
+
},
|
|
284
|
+
async ({ protocolIDJSON, ...rest }) => {
|
|
285
|
+
if (!ctx) return noCtx
|
|
286
|
+
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
|
+
)
|
|
294
|
+
|
|
295
|
+
server.tool(
|
|
296
|
+
'wallet_createSignature',
|
|
297
|
+
'Creates a digital signature using wallet keys.',
|
|
298
|
+
{
|
|
299
|
+
data: z.array(z.number()).describe('Data bytes to sign'),
|
|
300
|
+
protocolIDJSON: z.string().describe('JSON array [securityLevel, protocolString]'),
|
|
301
|
+
keyID: z.string(),
|
|
302
|
+
counterparty: z.string().optional(),
|
|
303
|
+
privileged: z.boolean().optional(),
|
|
304
|
+
},
|
|
305
|
+
async ({ protocolIDJSON, ...rest }) => {
|
|
306
|
+
if (!ctx) return noCtx
|
|
307
|
+
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
|
+
)
|
|
315
|
+
|
|
316
|
+
server.tool(
|
|
317
|
+
'wallet_verifySignature',
|
|
318
|
+
'Verifies a digital signature using wallet keys.',
|
|
319
|
+
{
|
|
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]'),
|
|
323
|
+
keyID: z.string(),
|
|
324
|
+
counterparty: z.string().optional(),
|
|
325
|
+
forSelf: z.boolean().optional(),
|
|
326
|
+
privileged: z.boolean().optional(),
|
|
327
|
+
},
|
|
328
|
+
async ({ protocolIDJSON, ...rest }) => {
|
|
329
|
+
if (!ctx) return noCtx
|
|
330
|
+
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
|
+
)
|
|
338
|
+
|
|
339
|
+
// ── Key Linkage ────────────────────────────────────────────────────
|
|
340
|
+
|
|
341
|
+
server.tool(
|
|
342
|
+
'wallet_revealCounterpartyKeyLinkage',
|
|
343
|
+
'Reveals the linkage between the wallet identity and a counterparty to a verifier.',
|
|
344
|
+
{
|
|
345
|
+
counterparty: z.string().describe('Counterparty public key hex'),
|
|
346
|
+
verifier: z.string().describe('Verifier public key hex'),
|
|
347
|
+
privileged: z.boolean().optional(),
|
|
348
|
+
privilegedReason: z.string().optional(),
|
|
349
|
+
},
|
|
350
|
+
async (args) => {
|
|
351
|
+
if (!ctx) return noCtx
|
|
352
|
+
try {
|
|
353
|
+
return result(await ctx.wallet.revealCounterpartyKeyLinkage(args as any))
|
|
354
|
+
} catch (e) { return error(e) }
|
|
355
|
+
},
|
|
356
|
+
)
|
|
357
|
+
|
|
358
|
+
server.tool(
|
|
359
|
+
'wallet_revealSpecificKeyLinkage',
|
|
360
|
+
'Reveals linkage for a specific protocol/key combination to a verifier.',
|
|
361
|
+
{
|
|
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]'),
|
|
365
|
+
keyID: z.string(),
|
|
366
|
+
privileged: z.boolean().optional(),
|
|
367
|
+
privilegedReason: z.string().optional(),
|
|
368
|
+
},
|
|
369
|
+
async ({ protocolIDJSON, ...rest }) => {
|
|
370
|
+
if (!ctx) return noCtx
|
|
371
|
+
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
|
+
)
|
|
379
|
+
|
|
380
|
+
// ── Certificates ───────────────────────────────────────────────────
|
|
381
|
+
|
|
382
|
+
server.tool(
|
|
383
|
+
'wallet_acquireCertificate',
|
|
384
|
+
'Acquires an identity certificate from a certifier.',
|
|
385
|
+
{
|
|
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'),
|
|
390
|
+
serialNumber: z.string().optional(),
|
|
391
|
+
revocationOutpoint: z.string().optional(),
|
|
392
|
+
signature: z.string().optional(),
|
|
393
|
+
certifierUrl: z.string().optional(),
|
|
394
|
+
keyringRevealer: z.string().optional(),
|
|
395
|
+
keyringForSubjectJSON: z.string().optional().describe('JSON object of keyring for subject'),
|
|
396
|
+
privileged: z.boolean().optional(),
|
|
397
|
+
privilegedReason: z.string().optional(),
|
|
398
|
+
},
|
|
399
|
+
async ({ fieldsJSON, keyringForSubjectJSON, ...rest }) => {
|
|
400
|
+
if (!ctx) return noCtx
|
|
401
|
+
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
|
+
)
|
|
410
|
+
|
|
411
|
+
server.tool(
|
|
412
|
+
'wallet_listCertificates',
|
|
413
|
+
'Lists identity certificates filtered by certifiers and types.',
|
|
414
|
+
{
|
|
415
|
+
certifiersJSON: z.string().describe('JSON array of certifier public key hexes'),
|
|
416
|
+
typesJSON: z.string().describe('JSON array of certificate types (base64)'),
|
|
417
|
+
limit: z.number().default(25),
|
|
418
|
+
offset: z.number().default(0),
|
|
419
|
+
privileged: z.boolean().optional(),
|
|
420
|
+
privilegedReason: z.string().optional(),
|
|
421
|
+
},
|
|
422
|
+
async ({ certifiersJSON, typesJSON, ...rest }) => {
|
|
423
|
+
if (!ctx) return noCtx
|
|
424
|
+
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
|
+
)
|
|
433
|
+
|
|
434
|
+
server.tool(
|
|
435
|
+
'wallet_proveCertificate',
|
|
436
|
+
'Proves select fields of a certificate to a verifier.',
|
|
437
|
+
{
|
|
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'),
|
|
441
|
+
privileged: z.boolean().optional(),
|
|
442
|
+
privilegedReason: z.string().optional(),
|
|
443
|
+
},
|
|
444
|
+
async ({ certificateJSON, fieldsToRevealJSON, ...rest }) => {
|
|
445
|
+
if (!ctx) return noCtx
|
|
446
|
+
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
|
+
)
|
|
455
|
+
|
|
456
|
+
server.tool(
|
|
457
|
+
'wallet_relinquishCertificate',
|
|
458
|
+
'Removes a certificate from the wallet.',
|
|
459
|
+
{
|
|
460
|
+
type: z.string().describe('Certificate type'),
|
|
461
|
+
serialNumber: z.string().describe('Certificate serial number'),
|
|
462
|
+
certifier: z.string().describe('Certifier public key hex'),
|
|
463
|
+
},
|
|
464
|
+
async (args) => {
|
|
465
|
+
if (!ctx) return noCtx
|
|
466
|
+
try {
|
|
467
|
+
return result(await ctx.wallet.relinquishCertificate(args as any))
|
|
468
|
+
} catch (e) { return error(e) }
|
|
469
|
+
},
|
|
470
|
+
)
|
|
471
|
+
|
|
472
|
+
// ── Discovery ──────────────────────────────────────────────────────
|
|
473
|
+
|
|
474
|
+
server.tool(
|
|
475
|
+
'wallet_discoverByIdentityKey',
|
|
476
|
+
'Discovers certificates issued to a given identity key.',
|
|
477
|
+
{
|
|
478
|
+
identityKey: z.string().describe('Identity public key hex'),
|
|
479
|
+
limit: z.number().default(25),
|
|
480
|
+
offset: z.number().default(0),
|
|
481
|
+
},
|
|
482
|
+
async (args) => {
|
|
483
|
+
if (!ctx) return noCtx
|
|
484
|
+
try {
|
|
485
|
+
return result(await ctx.wallet.discoverByIdentityKey(args as any))
|
|
486
|
+
} catch (e) { return error(e) }
|
|
487
|
+
},
|
|
488
|
+
)
|
|
489
|
+
|
|
490
|
+
server.tool(
|
|
491
|
+
'wallet_discoverByAttributes',
|
|
492
|
+
'Discovers certificates matching specific attributes.',
|
|
493
|
+
{
|
|
494
|
+
attributesJSON: z.string().describe('JSON object of attribute key/value pairs to match'),
|
|
495
|
+
limit: z.number().default(25),
|
|
496
|
+
offset: z.number().default(0),
|
|
497
|
+
},
|
|
498
|
+
async ({ attributesJSON, ...rest }) => {
|
|
499
|
+
if (!ctx) return noCtx
|
|
500
|
+
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
|
+
)
|
|
508
|
+
|
|
509
|
+
// ── Info ────────────────────────────────────────────────────────────
|
|
510
|
+
|
|
511
|
+
server.tool(
|
|
512
|
+
'wallet_isAuthenticated',
|
|
513
|
+
'Checks if the wallet user is authenticated.',
|
|
514
|
+
{},
|
|
515
|
+
async () => {
|
|
516
|
+
if (!ctx) return noCtx
|
|
517
|
+
try {
|
|
518
|
+
return result(await ctx.wallet.isAuthenticated({}))
|
|
519
|
+
} catch (e) { return error(e) }
|
|
520
|
+
},
|
|
521
|
+
)
|
|
522
|
+
|
|
523
|
+
server.tool(
|
|
524
|
+
'wallet_getHeight',
|
|
525
|
+
'Gets the current blockchain height.',
|
|
526
|
+
{},
|
|
527
|
+
async () => {
|
|
528
|
+
if (!ctx) return noCtx
|
|
529
|
+
try {
|
|
530
|
+
return result(await ctx.wallet.getHeight({}))
|
|
531
|
+
} catch (e) { return error(e) }
|
|
532
|
+
},
|
|
533
|
+
)
|
|
534
|
+
|
|
535
|
+
server.tool(
|
|
536
|
+
'wallet_getHeaderForHeight',
|
|
537
|
+
'Gets the 80-byte block header at a given height.',
|
|
538
|
+
{
|
|
539
|
+
height: z.number().describe('Block height'),
|
|
540
|
+
},
|
|
541
|
+
async ({ height }) => {
|
|
542
|
+
if (!ctx) return noCtx
|
|
543
|
+
try {
|
|
544
|
+
return result(await ctx.wallet.getHeaderForHeight({ height }))
|
|
545
|
+
} catch (e) { return error(e) }
|
|
546
|
+
},
|
|
547
|
+
)
|
|
548
|
+
|
|
549
|
+
server.tool(
|
|
550
|
+
'wallet_getNetwork',
|
|
551
|
+
'Gets the network the wallet is connected to (mainnet or testnet).',
|
|
552
|
+
{},
|
|
553
|
+
async () => {
|
|
554
|
+
if (!ctx) return noCtx
|
|
555
|
+
try {
|
|
556
|
+
return result(await ctx.wallet.getNetwork({}))
|
|
557
|
+
} catch (e) { return error(e) }
|
|
558
|
+
},
|
|
559
|
+
)
|
|
560
|
+
|
|
561
|
+
server.tool(
|
|
562
|
+
'wallet_getVersion',
|
|
563
|
+
'Gets the wallet implementation version.',
|
|
564
|
+
{},
|
|
565
|
+
async () => {
|
|
566
|
+
if (!ctx) return noCtx
|
|
567
|
+
try {
|
|
568
|
+
return result(await ctx.wallet.getVersion({}))
|
|
569
|
+
} catch (e) { return error(e) }
|
|
570
|
+
},
|
|
571
|
+
)
|
|
572
|
+
}
|