bsv-mcp 0.2.8 → 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/app.html +733 -0
- package/dist/index.js +93177 -29402
- package/index.ts +32 -1
- package/package.json +4 -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,
|
|
@@ -599,7 +600,11 @@ function registerMcpAppTools(server: McpServer, wallet?: Wallet) {
|
|
|
599
600
|
APP_RESOURCE_URI,
|
|
600
601
|
{ description: "Interactive BSV dashboard with Explorer, Wallet, and Ordinals tabs" },
|
|
601
602
|
async () => {
|
|
602
|
-
|
|
603
|
+
// When running from bundle (dist/index.js), app.html is a sibling.
|
|
604
|
+
// When running from source (index.ts), it's in dist/.
|
|
605
|
+
const distPath = __appDirname.endsWith("dist")
|
|
606
|
+
? join(__appDirname, "app.html")
|
|
607
|
+
: join(__appDirname, "dist", "app.html");
|
|
603
608
|
let html: string;
|
|
604
609
|
try {
|
|
605
610
|
html = await readFile(distPath, "utf-8");
|
|
@@ -854,6 +859,8 @@ Authentication:
|
|
|
854
859
|
|
|
855
860
|
let wallet: Wallet | undefined;
|
|
856
861
|
let integratedWallet: IntegratedWallet | undefined;
|
|
862
|
+
let remoteCtx: import("@1sat/actions").OneSatContext | undefined;
|
|
863
|
+
let remoteServices: import("@1sat/wallet-remote").OneSatServices | undefined;
|
|
857
864
|
|
|
858
865
|
if (CONFIG.loadTools) {
|
|
859
866
|
// Check if we should use Droplet API mode
|
|
@@ -930,6 +937,21 @@ Authentication:
|
|
|
930
937
|
effectiveConfig.loadMneeTools = false;
|
|
931
938
|
effectiveConfig.loadBapTools = false;
|
|
932
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
|
+
}
|
|
933
955
|
}
|
|
934
956
|
|
|
935
957
|
// Disable MNEE tools if wallet is not available
|
|
@@ -957,6 +979,8 @@ Authentication:
|
|
|
957
979
|
wallet,
|
|
958
980
|
integratedWallet,
|
|
959
981
|
disableBroadcasting: effectiveConfig.disableBroadcasting,
|
|
982
|
+
ctx: remoteCtx,
|
|
983
|
+
services: remoteServices,
|
|
960
984
|
};
|
|
961
985
|
|
|
962
986
|
registerAllTools(server, toolsConfig);
|
|
@@ -975,6 +999,13 @@ Authentication:
|
|
|
975
999
|
registerResources(server);
|
|
976
1000
|
}
|
|
977
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
|
+
|
|
978
1009
|
// Start the server based on transport mode
|
|
979
1010
|
if (CONFIG.transportMode === "stdio") {
|
|
980
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",
|
|
@@ -23,6 +23,7 @@
|
|
|
23
23
|
],
|
|
24
24
|
"files": [
|
|
25
25
|
"dist/**/*.js",
|
|
26
|
+
"dist/**/*.html",
|
|
26
27
|
"package.json",
|
|
27
28
|
"*.ts",
|
|
28
29
|
"tools/*.ts",
|
|
@@ -78,6 +79,8 @@
|
|
|
78
79
|
"typescript": "^5.9.3"
|
|
79
80
|
},
|
|
80
81
|
"dependencies": {
|
|
82
|
+
"@1sat/actions": "0.0.25",
|
|
83
|
+
"@1sat/wallet-remote": "0.0.5",
|
|
81
84
|
"@bsv/sdk": "^2.0.6",
|
|
82
85
|
"@modelcontextprotocol/ext-apps": "^1.2.0",
|
|
83
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
|
+
}
|