bsv-mcp 0.3.1 → 0.3.2
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/CHANGELOG.md +6 -0
- package/README.md +41 -0
- package/dist/index.js +226 -36
- package/package.json +1 -1
- package/tools/wallet/revealDelegation.ts +74 -0
- package/tools/wallet/tools.ts +2 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# BSV MCP Server Changelog
|
|
2
2
|
|
|
3
|
+
## [0.3.2] - 2026-09-06
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
- Reveal a human-issued Sigma delegation with the connected agent wallet, using standard certificate acquisition and proof.
|
|
7
|
+
- Reject redirects, automatic payments, and ambiguous mutation retries; submit only verifier-specific keys.
|
|
8
|
+
|
|
3
9
|
## [0.3.1] - 2026-09-05
|
|
4
10
|
|
|
5
11
|
### Added
|
package/README.md
CHANGED
|
@@ -912,3 +912,44 @@ BRC-105 payment handling cannot spend funds; a 402 requires human review. Struct
|
|
|
912
912
|
available quota reset information is retained. `unknown_outcome` means reconcile
|
|
913
913
|
transaction/history before retrying; no idempotency-key support is claimed.
|
|
914
914
|
The legacy `USE_DROPLIT_API=true` wallet mode remains available separately.
|
|
915
|
+
|
|
916
|
+
|
|
917
|
+
### Reveal a Sigma agent delegation with the connected wallet
|
|
918
|
+
|
|
919
|
+
After the human owner binds this agent wallet and issues its BRC-169 delegation,
|
|
920
|
+
copy the owner's complete handoff JSON into `wallet_revealDelegation`:
|
|
921
|
+
|
|
922
|
+
- `sigmaOrigin`: the intended Sigma HTTPS origin (HTTP is allowed only for loopback testing).
|
|
923
|
+
- `handoffJSON`: the owner's JSON containing `certificate`, `subjectKeyring`, `revealTo`, and `revelationPath`.
|
|
924
|
+
|
|
925
|
+
The explicit tool invocation acquires the certificate into the connected wallet,
|
|
926
|
+
proves all restrictions to the named Sigma verifier, and POSTs only that verifier's
|
|
927
|
+
keyring. Use the same wallet that supplied the bound identity; its Ed25519 agent
|
|
928
|
+
token cannot perform this step. The encrypted subject keyring stays with the agent
|
|
929
|
+
and is never included in the HTTP request or tool result. This does not grant
|
|
930
|
+
Droplit sponsor permission. No automatic payments, redirects, or POST retries are
|
|
931
|
+
allowed. On `outcome_unknown`, ask the owner to refresh delegation status before
|
|
932
|
+
trying again; certificate acquisition may already have succeeded.
|
|
933
|
+
|
|
934
|
+
For a **local 1Sat CLI wallet** used throughout binding, the existing commands
|
|
935
|
+
provide the same operation. Save the owner package as `handoff.json`, then:
|
|
936
|
+
|
|
937
|
+
```sh
|
|
938
|
+
bun -e 'const h=await Bun.file("handoff.json").json(),c=h.certificate; await Bun.write("acquire.json",JSON.stringify({acquisitionProtocol:"direct",type:c.type,certifier:c.certifier,fields:c.fields,serialNumber:c.serialNumber,revocationOutpoint:c.revocationOutpoint,signature:c.signature,keyringRevealer:"certifier",keyringForSubject:h.subjectKeyring})); await Bun.write("prove.json",JSON.stringify({certificate:c,fieldsToReveal:Object.keys(c.fields),verifier:h.revealTo}));'
|
|
939
|
+
1sat wallet acquire-certificate "$(cat acquire.json)" --json
|
|
940
|
+
1sat wallet prove-certificate "$(cat prove.json)" --json > proof.json
|
|
941
|
+
bun -e 'const p=await Bun.file("proof.json").json(); if(!p.keyringForVerifier) throw new Error("Missing verifier keyring"); await Bun.write("revelation.json",JSON.stringify({keyring:p.keyringForVerifier}));'
|
|
942
|
+
```
|
|
943
|
+
|
|
944
|
+
Set `SIGMA_ORIGIN` to the intended Sigma origin and `REVELATION_PATH` to the
|
|
945
|
+
owner's `/api/agents/{agentId}/delegations/{encodedSerialNumber}/revelation` path;
|
|
946
|
+
percent-encode the base64 serial number as one path segment. Then:
|
|
947
|
+
|
|
948
|
+
```sh
|
|
949
|
+
1sat authfetch POST "${SIGMA_ORIGIN}${REVELATION_PATH}" --body @revelation.json --json
|
|
950
|
+
```
|
|
951
|
+
|
|
952
|
+
Use `@1sat/cli` 0.0.110 or later for stale-session mutation replay protection.
|
|
953
|
+
Keep the same configured wallet and chain for every command. This CLI alternative
|
|
954
|
+
does not use the MCP external wallet connection. The endpoint does not require a
|
|
955
|
+
payment; do not add `--yes` to work around an unexpected 402.
|
package/dist/index.js
CHANGED
|
@@ -207971,9 +207971,9 @@ var require_vary = __commonJS((exports, module) => {
|
|
|
207971
207971
|
if (!field) {
|
|
207972
207972
|
throw new TypeError("field argument is required");
|
|
207973
207973
|
}
|
|
207974
|
-
var
|
|
207975
|
-
for (var j5 = 0;j5 <
|
|
207976
|
-
if (!FIELD_NAME_REGEXP.test(
|
|
207974
|
+
var fields2 = !Array.isArray(field) ? parse8(String(field)) : field;
|
|
207975
|
+
for (var j5 = 0;j5 < fields2.length; j5++) {
|
|
207976
|
+
if (!FIELD_NAME_REGEXP.test(fields2[j5])) {
|
|
207977
207977
|
throw new TypeError("field argument contains an invalid header name");
|
|
207978
207978
|
}
|
|
207979
207979
|
}
|
|
@@ -207982,14 +207982,14 @@ var require_vary = __commonJS((exports, module) => {
|
|
|
207982
207982
|
}
|
|
207983
207983
|
var val = header;
|
|
207984
207984
|
var vals = parse8(header.toLowerCase());
|
|
207985
|
-
if (
|
|
207985
|
+
if (fields2.indexOf("*") !== -1 || vals.indexOf("*") !== -1) {
|
|
207986
207986
|
return "*";
|
|
207987
207987
|
}
|
|
207988
|
-
for (var i = 0;i <
|
|
207989
|
-
var fld =
|
|
207988
|
+
for (var i = 0;i < fields2.length; i++) {
|
|
207989
|
+
var fld = fields2[i].toLowerCase();
|
|
207990
207990
|
if (vals.indexOf(fld) === -1) {
|
|
207991
207991
|
vals.push(fld);
|
|
207992
|
-
val = val ? val + ", " +
|
|
207992
|
+
val = val ? val + ", " + fields2[i] : fields2[i];
|
|
207993
207993
|
}
|
|
207994
207994
|
}
|
|
207995
207995
|
return val;
|
|
@@ -213168,13 +213168,13 @@ var require_StorageKnex = __commonJS((exports) => {
|
|
|
213168
213168
|
delete e2.certificateId;
|
|
213169
213169
|
if (e2.logger != null)
|
|
213170
213170
|
delete e2.logger;
|
|
213171
|
-
const
|
|
213171
|
+
const fields2 = e2.fields;
|
|
213172
213172
|
if (e2.fields != null)
|
|
213173
213173
|
delete e2.fields;
|
|
213174
213174
|
const [id] = await this.toDb(trx)("certificates").insert(e2);
|
|
213175
213175
|
certificate.certificateId = id;
|
|
213176
|
-
if (
|
|
213177
|
-
for (const field of
|
|
213176
|
+
if (fields2 != null) {
|
|
213177
|
+
for (const field of fields2) {
|
|
213178
213178
|
field.certificateId = id;
|
|
213179
213179
|
field.userId = certificate.userId;
|
|
213180
213180
|
await this.insertCertificateField(field, trx);
|
|
@@ -227862,11 +227862,11 @@ var require_querycompiler = __commonJS((exports, module) => {
|
|
|
227862
227862
|
_preValidate() {
|
|
227863
227863
|
const method = this.method;
|
|
227864
227864
|
const verb = hasOwn(methodAliases, method) ? methodAliases[method] : method;
|
|
227865
|
-
const
|
|
227866
|
-
if (!
|
|
227865
|
+
const invalid2 = this.invalidClauses[verb];
|
|
227866
|
+
if (!invalid2)
|
|
227867
227867
|
return;
|
|
227868
|
-
for (let i = 0;i <
|
|
227869
|
-
const clause =
|
|
227868
|
+
for (let i = 0;i < invalid2.length; i++) {
|
|
227869
|
+
const clause = invalid2[i];
|
|
227870
227870
|
const hasNonEmptyGrouped = hasOwn(this.grouped, clause) && this.grouped[clause].length > 0;
|
|
227871
227871
|
const hasNonEmptySingle = hasOwn(this.single, clause) && this.single[clause] != null;
|
|
227872
227872
|
if (hasNonEmptyGrouped || hasNonEmptySingle) {
|
|
@@ -236146,10 +236146,10 @@ var require_mysql = __commonJS((exports, module) => {
|
|
|
236146
236146
|
return;
|
|
236147
236147
|
}
|
|
236148
236148
|
const queryOptions = Object.assign({ sql: obj.sql }, obj.options);
|
|
236149
|
-
connection.query(queryOptions, obj.bindings, function(err, rows,
|
|
236149
|
+
connection.query(queryOptions, obj.bindings, function(err, rows, fields2) {
|
|
236150
236150
|
if (err)
|
|
236151
236151
|
return rejecter(err);
|
|
236152
|
-
obj.response = [rows,
|
|
236152
|
+
obj.response = [rows, fields2];
|
|
236153
236153
|
resolver(obj);
|
|
236154
236154
|
});
|
|
236155
236155
|
});
|
|
@@ -236160,9 +236160,9 @@ var require_mysql = __commonJS((exports, module) => {
|
|
|
236160
236160
|
const { response } = obj;
|
|
236161
236161
|
const { method } = obj;
|
|
236162
236162
|
const rows = response[0];
|
|
236163
|
-
const
|
|
236163
|
+
const fields2 = response[1];
|
|
236164
236164
|
if (obj.output)
|
|
236165
|
-
return obj.output.call(runner, rows,
|
|
236165
|
+
return obj.output.call(runner, rows, fields2);
|
|
236166
236166
|
switch (method) {
|
|
236167
236167
|
case "select":
|
|
236168
236168
|
return rows;
|
|
@@ -236439,9 +236439,9 @@ var require_mariadb = __commonJS((exports, module) => {
|
|
|
236439
236439
|
return;
|
|
236440
236440
|
if (obj.returning) {
|
|
236441
236441
|
const rows = obj.response[0];
|
|
236442
|
-
const
|
|
236442
|
+
const fields2 = obj.response[1];
|
|
236443
236443
|
if (obj.output) {
|
|
236444
|
-
return obj.output.call(runner, rows,
|
|
236444
|
+
return obj.output.call(runner, rows, fields2);
|
|
236445
236445
|
}
|
|
236446
236446
|
return rows;
|
|
236447
236447
|
}
|
|
@@ -242385,8 +242385,8 @@ var require_ShamirWalletManager = __commonJS((exports) => {
|
|
|
242385
242385
|
await this.entropyCollector.collectFromBrowser(element, onProgress);
|
|
242386
242386
|
}
|
|
242387
242387
|
generateUserIdHash(privateKey) {
|
|
242388
|
-
const
|
|
242389
|
-
const hash2 = sdk_1.Hash.sha256(sdk_1.Utils.toArray(
|
|
242388
|
+
const publicKey2 = privateKey.toPublicKey().toString();
|
|
242389
|
+
const hash2 = sdk_1.Hash.sha256(sdk_1.Utils.toArray(publicKey2, "utf8"));
|
|
242390
242390
|
return sdk_1.Utils.toHex(hash2);
|
|
242391
242391
|
}
|
|
242392
242392
|
async createNewWallet(authPayload, onUserSharesReady) {
|
|
@@ -244852,13 +244852,13 @@ var init_storage_pg = __esm(() => {
|
|
|
244852
244852
|
e2.certificateId = undefined;
|
|
244853
244853
|
if (e2.logger)
|
|
244854
244854
|
e2.logger = undefined;
|
|
244855
|
-
const
|
|
244855
|
+
const fields2 = e2.fields;
|
|
244856
244856
|
if (e2.fields)
|
|
244857
244857
|
e2.fields = undefined;
|
|
244858
244858
|
const id = await this.insertRow("certificates", e2, trx);
|
|
244859
244859
|
certificate.certificateId = id;
|
|
244860
|
-
if (
|
|
244861
|
-
for (const field of
|
|
244860
|
+
if (fields2) {
|
|
244861
|
+
for (const field of fields2) {
|
|
244862
244862
|
field.certificateId = id;
|
|
244863
244863
|
field.userId = certificate.userId;
|
|
244864
244864
|
await this.insertCertificateField(field, trx);
|
|
@@ -256042,7 +256042,7 @@ var package_default = {
|
|
|
256042
256042
|
name: "bsv-mcp",
|
|
256043
256043
|
module: "dist/index.js",
|
|
256044
256044
|
type: "module",
|
|
256045
|
-
version: "0.3.
|
|
256045
|
+
version: "0.3.2",
|
|
256046
256046
|
license: "MIT",
|
|
256047
256047
|
author: "satchmo",
|
|
256048
256048
|
description: "A collection of Bitcoin SV (BSV) tools for the Model Context Protocol (MCP) framework",
|
|
@@ -295163,7 +295163,7 @@ var package_default2 = {
|
|
|
295163
295163
|
name: "bsv-mcp",
|
|
295164
295164
|
module: "dist/index.js",
|
|
295165
295165
|
type: "module",
|
|
295166
|
-
version: "0.3.
|
|
295166
|
+
version: "0.3.2",
|
|
295167
295167
|
license: "MIT",
|
|
295168
295168
|
author: "satchmo",
|
|
295169
295169
|
description: "A collection of Bitcoin SV (BSV) tools for the Model Context Protocol (MCP) framework",
|
|
@@ -297158,6 +297158,195 @@ function registerRefreshUtxosTool(server, ctx) {
|
|
|
297158
297158
|
});
|
|
297159
297159
|
}
|
|
297160
297160
|
|
|
297161
|
+
// utils/revealDelegation.ts
|
|
297162
|
+
init_mod2();
|
|
297163
|
+
var publicKey = exports_external.string().regex(/^(02|03)[a-f0-9]{64}$/);
|
|
297164
|
+
var base643 = exports_external.string().min(4).max(16384).regex(/^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/);
|
|
297165
|
+
var fields = exports_external.record(exports_external.string().min(1).max(49), base643).refine((v7) => Object.keys(v7).length > 0 && Object.keys(v7).length <= 32);
|
|
297166
|
+
var handoffSchema = exports_external.object({
|
|
297167
|
+
certificate: exports_external.object({
|
|
297168
|
+
type: exports_external.literal("30kchAJIGfLxzCNloCJNLI3AtkgA8UbkxlXU2Cj4PpA="),
|
|
297169
|
+
serialNumber: base643.refine((v7) => Buffer.from(v7, "base64").length === 32),
|
|
297170
|
+
subject: publicKey,
|
|
297171
|
+
certifier: publicKey,
|
|
297172
|
+
revocationOutpoint: exports_external.string().regex(/^[a-f0-9]{64}\.\d{1,10}$/),
|
|
297173
|
+
signature: exports_external.string().regex(/^(?:[a-f0-9]{2}){8,72}$/),
|
|
297174
|
+
fields
|
|
297175
|
+
}).strict(),
|
|
297176
|
+
subjectKeyring: fields,
|
|
297177
|
+
revealTo: publicKey,
|
|
297178
|
+
revelationPath: exports_external.string().max(512)
|
|
297179
|
+
}).strict();
|
|
297180
|
+
|
|
297181
|
+
class RevelationError extends Error {
|
|
297182
|
+
code;
|
|
297183
|
+
status;
|
|
297184
|
+
constructor(code, message, status) {
|
|
297185
|
+
super(message);
|
|
297186
|
+
this.code = code;
|
|
297187
|
+
this.status = status;
|
|
297188
|
+
}
|
|
297189
|
+
}
|
|
297190
|
+
function invalid() {
|
|
297191
|
+
throw new RevelationError("invalid_handoff", "Provide a valid Sigma origin and the owner's BRC-169 handoff.");
|
|
297192
|
+
}
|
|
297193
|
+
function parseHandoff(origin, handoffJSON) {
|
|
297194
|
+
if (handoffJSON.length > 131072 || !/^https?:\/\/[^/?#\\]+\/?$/.test(origin))
|
|
297195
|
+
invalid();
|
|
297196
|
+
const url3 = new URL(origin);
|
|
297197
|
+
const loopback = ["localhost", "127.0.0.1", "[::1]"].includes(url3.hostname);
|
|
297198
|
+
if (url3.username || url3.password || url3.protocol !== "https:" && !(url3.protocol === "http:" && loopback))
|
|
297199
|
+
invalid();
|
|
297200
|
+
const h5 = handoffSchema.parse(JSON.parse(handoffJSON));
|
|
297201
|
+
for (const key of [
|
|
297202
|
+
h5.certificate.subject,
|
|
297203
|
+
h5.certificate.certifier,
|
|
297204
|
+
h5.revealTo
|
|
297205
|
+
]) {
|
|
297206
|
+
if (!PublicKey.fromString(key).validate())
|
|
297207
|
+
invalid();
|
|
297208
|
+
}
|
|
297209
|
+
const match = /^\/api\/agents\/([A-Za-z0-9_-]{1,128})\/delegations\/(.+)\/revelation$/.exec(h5.revelationPath);
|
|
297210
|
+
if (!match || decodeURIComponent(match[2]) !== h5.certificate.serialNumber)
|
|
297211
|
+
invalid();
|
|
297212
|
+
if (Object.keys(h5.subjectKeyring).sort().join(`
|
|
297213
|
+
`) !== Object.keys(h5.certificate.fields).sort().join(`
|
|
297214
|
+
`))
|
|
297215
|
+
invalid();
|
|
297216
|
+
return {
|
|
297217
|
+
h: h5,
|
|
297218
|
+
endpoint: `${url3.origin}/api/agents/${encodeURIComponent(match[1])}/delegations/${encodeURIComponent(h5.certificate.serialNumber)}/revelation`,
|
|
297219
|
+
origin: url3.origin
|
|
297220
|
+
};
|
|
297221
|
+
}
|
|
297222
|
+
async function revealDelegation(wallet5, sigmaOrigin, handoffJSON) {
|
|
297223
|
+
let input;
|
|
297224
|
+
try {
|
|
297225
|
+
input = parseHandoff(sigmaOrigin, handoffJSON);
|
|
297226
|
+
} catch {
|
|
297227
|
+
invalid();
|
|
297228
|
+
}
|
|
297229
|
+
const { h: h5, endpoint, origin } = input;
|
|
297230
|
+
const c6 = h5.certificate;
|
|
297231
|
+
try {
|
|
297232
|
+
const { publicKey: identity6 } = await wallet5.getPublicKey({
|
|
297233
|
+
identityKey: true
|
|
297234
|
+
});
|
|
297235
|
+
if (identity6 !== c6.subject)
|
|
297236
|
+
throw new RevelationError("subject_mismatch", "The connected wallet is not this delegation's subject.");
|
|
297237
|
+
const certificate = new Certificate(c6.type, c6.serialNumber, c6.subject, c6.certifier, c6.revocationOutpoint, c6.fields, c6.signature);
|
|
297238
|
+
if (!await certificate.verify())
|
|
297239
|
+
invalid();
|
|
297240
|
+
} catch (error53) {
|
|
297241
|
+
if (error53 instanceof RevelationError)
|
|
297242
|
+
throw error53;
|
|
297243
|
+
throw new RevelationError("invalid_certificate", "Could not verify the certificate and connected wallet identity.");
|
|
297244
|
+
}
|
|
297245
|
+
let keyring;
|
|
297246
|
+
try {
|
|
297247
|
+
await wallet5.acquireCertificate({
|
|
297248
|
+
...c6,
|
|
297249
|
+
acquisitionProtocol: "direct",
|
|
297250
|
+
keyringRevealer: "certifier",
|
|
297251
|
+
keyringForSubject: h5.subjectKeyring
|
|
297252
|
+
});
|
|
297253
|
+
const proof = await wallet5.proveCertificate({
|
|
297254
|
+
certificate: c6,
|
|
297255
|
+
fieldsToReveal: Object.keys(c6.fields),
|
|
297256
|
+
verifier: h5.revealTo
|
|
297257
|
+
});
|
|
297258
|
+
keyring = fields.parse(proof.keyringForVerifier);
|
|
297259
|
+
} catch {
|
|
297260
|
+
throw new RevelationError("wallet_proof_failed", "The wallet could not acquire or prove this delegation. Check its certificate store before retrying.");
|
|
297261
|
+
}
|
|
297262
|
+
return postRevelation(wallet5, origin, endpoint, keyring);
|
|
297263
|
+
}
|
|
297264
|
+
async function postRevelation(wallet5, origin, endpoint, keyring) {
|
|
297265
|
+
const guarded = new Proxy(wallet5, {
|
|
297266
|
+
get(target, property) {
|
|
297267
|
+
if (property === "createAction" || property === "signAction")
|
|
297268
|
+
return async () => {
|
|
297269
|
+
throw new RevelationError("payment_required", "Sigma revelation must not require a payment.", 402);
|
|
297270
|
+
};
|
|
297271
|
+
const value2 = Reflect.get(target, property, target);
|
|
297272
|
+
return typeof value2 === "function" ? value2.bind(target) : value2;
|
|
297273
|
+
}
|
|
297274
|
+
});
|
|
297275
|
+
let refusal;
|
|
297276
|
+
const guardedFetch = async (url3, init) => {
|
|
297277
|
+
if (String(url3) !== `${origin}/.well-known/auth` && String(url3) !== endpoint) {
|
|
297278
|
+
refusal = new RevelationError("unexpected_endpoint", "Sigma authentication requested an unexpected endpoint.");
|
|
297279
|
+
throw refusal;
|
|
297280
|
+
}
|
|
297281
|
+
const response = await fetch(url3, { ...init, redirect: "error" });
|
|
297282
|
+
if ([401, 402, 403, 409, 422, 429].includes(response.status)) {
|
|
297283
|
+
refusal = new RevelationError(response.status === 402 ? "payment_required" : "request_rejected", "Sigma rejected revelation. Check owner delegation status before retrying.", response.status);
|
|
297284
|
+
throw refusal;
|
|
297285
|
+
}
|
|
297286
|
+
return response;
|
|
297287
|
+
};
|
|
297288
|
+
const transport = new SimplifiedFetchTransport(origin, guardedFetch);
|
|
297289
|
+
const sessions = new SessionManager;
|
|
297290
|
+
const auth5 = new AuthFetch(guarded, undefined, sessions);
|
|
297291
|
+
const peer = new Peer(guarded, transport, undefined, sessions);
|
|
297292
|
+
try {
|
|
297293
|
+
await peer.ready;
|
|
297294
|
+
auth5.peers[origin] = { peer, pendingCertificateRequests: [] };
|
|
297295
|
+
const response = await auth5.fetch(endpoint, {
|
|
297296
|
+
method: "POST",
|
|
297297
|
+
headers: { "Content-Type": "application/json" },
|
|
297298
|
+
body: JSON.stringify({ keyring }),
|
|
297299
|
+
retryCounter: 1,
|
|
297300
|
+
paymentRetryAttempts: 1
|
|
297301
|
+
});
|
|
297302
|
+
if (!response.ok)
|
|
297303
|
+
throw new RevelationError("request_rejected", "Sigma rejected revelation. Check owner delegation status before retrying.", response.status);
|
|
297304
|
+
return { status: "revealed", httpStatus: response.status };
|
|
297305
|
+
} catch (error53) {
|
|
297306
|
+
if (refusal)
|
|
297307
|
+
throw refusal;
|
|
297308
|
+
if (error53 instanceof RevelationError)
|
|
297309
|
+
throw error53;
|
|
297310
|
+
throw new RevelationError("outcome_unknown", "Revelation was not confirmed. Ask the owner to refresh delegation status before retrying; no automatic replay was attempted.");
|
|
297311
|
+
}
|
|
297312
|
+
}
|
|
297313
|
+
|
|
297314
|
+
// tools/wallet/revealDelegation.ts
|
|
297315
|
+
function registerRevealDelegationTool(server, ctx) {
|
|
297316
|
+
server.registerTool("wallet_revealDelegation", {
|
|
297317
|
+
description: "Receive the human owner's BRC-169 certificate handoff, acquire and prove it with this connected agent wallet, and reveal its restrictions to the specified Sigma verifier. Imports a certificate and activates its existing delegation. Never pays, broadcasts, follows redirects, or retries an ambiguous POST. The subject keyring stays with the wallet.",
|
|
297318
|
+
inputSchema: {
|
|
297319
|
+
sigmaOrigin: exports_external.string().max(2048).describe("Explicit Sigma HTTPS origin, or HTTP loopback for local testing"),
|
|
297320
|
+
handoffJSON: exports_external.string().max(131072).describe("JSON package copied by the owner: certificate, subjectKeyring, revealTo, revelationPath")
|
|
297321
|
+
},
|
|
297322
|
+
annotations: {
|
|
297323
|
+
readOnlyHint: false,
|
|
297324
|
+
destructiveHint: false,
|
|
297325
|
+
idempotentHint: false,
|
|
297326
|
+
openWorldHint: true
|
|
297327
|
+
}
|
|
297328
|
+
}, async ({ sigmaOrigin, handoffJSON }) => {
|
|
297329
|
+
try {
|
|
297330
|
+
if (!ctx)
|
|
297331
|
+
throw new RevelationError("wallet_unavailable", "Connect the agent's existing wallet first.");
|
|
297332
|
+
const data = await revealDelegation(ctx.wallet, sigmaOrigin, handoffJSON);
|
|
297333
|
+
return { ...createSuccessResponse(data), structuredContent: data };
|
|
297334
|
+
} catch (error53) {
|
|
297335
|
+
const failure = error53 instanceof RevelationError ? error53 : new RevelationError("revelation_failed", "Delegation revelation failed. Check wallet and owner status before retrying.");
|
|
297336
|
+
const data = {
|
|
297337
|
+
error: failure.code,
|
|
297338
|
+
message: failure.message,
|
|
297339
|
+
...failure.status === undefined ? {} : { status: failure.status }
|
|
297340
|
+
};
|
|
297341
|
+
return {
|
|
297342
|
+
...createSuccessResponse(data),
|
|
297343
|
+
structuredContent: data,
|
|
297344
|
+
isError: true
|
|
297345
|
+
};
|
|
297346
|
+
}
|
|
297347
|
+
});
|
|
297348
|
+
}
|
|
297349
|
+
|
|
297161
297350
|
// tools/wallet/sendAllBsv.ts
|
|
297162
297351
|
var sendAllBsvArgsSchema = exports_external.object({
|
|
297163
297352
|
destination: exports_external.string().describe("Destination P2PKH address to send all funds to")
|
|
@@ -297661,6 +297850,7 @@ function registerWalletTools(server, wallet5, config2) {
|
|
|
297661
297850
|
registerRefreshUtxosTool(server, config2.ctx);
|
|
297662
297851
|
registerWalletGetBalanceTool(server, config2.ctx);
|
|
297663
297852
|
registerBrc100Tools(server, config2.ctx);
|
|
297853
|
+
registerRevealDelegationTool(server, config2.ctx);
|
|
297664
297854
|
if (config2.enableA2bTools && wallet5 && config2.identityPk) {
|
|
297665
297855
|
registerA2bPublishMcpTool(server, wallet5, config2.identityPk, {
|
|
297666
297856
|
disableBroadcasting: config2.disableBroadcasting
|
|
@@ -297846,9 +298036,9 @@ class Wallet {
|
|
|
297846
298036
|
if (!currentPaymentKey) {
|
|
297847
298037
|
throw new Error("No payment key available to derive public key.");
|
|
297848
298038
|
}
|
|
297849
|
-
const
|
|
298039
|
+
const publicKey2 = currentPaymentKey.toPublicKey();
|
|
297850
298040
|
return {
|
|
297851
|
-
publicKey:
|
|
298041
|
+
publicKey: publicKey2.toDER("hex")
|
|
297852
298042
|
};
|
|
297853
298043
|
}
|
|
297854
298044
|
async sendToAddress(address, amountSatoshis) {
|
|
@@ -298089,9 +298279,9 @@ class Wallet2 {
|
|
|
298089
298279
|
if (!currentPaymentKey) {
|
|
298090
298280
|
throw new Error("No payment key available to derive public key.");
|
|
298091
298281
|
}
|
|
298092
|
-
const
|
|
298282
|
+
const publicKey2 = currentPaymentKey.toPublicKey();
|
|
298093
298283
|
return {
|
|
298094
|
-
publicKey:
|
|
298284
|
+
publicKey: publicKey2.toDER("hex")
|
|
298095
298285
|
};
|
|
298096
298286
|
}
|
|
298097
298287
|
async sendToAddress(address, amountSatoshis) {
|
|
@@ -298252,8 +298442,8 @@ class DroplitClient2 {
|
|
|
298252
298442
|
async getIdentityKey() {
|
|
298253
298443
|
if (!this.wallet)
|
|
298254
298444
|
throw new DroplitError2("authentication_required", "Configure a Droplit wallet identity.");
|
|
298255
|
-
const { publicKey } = await this.wallet.getPublicKey({ identityKey: true });
|
|
298256
|
-
return
|
|
298445
|
+
const { publicKey: publicKey2 } = await this.wallet.getPublicKey({ identityKey: true });
|
|
298446
|
+
return publicKey2;
|
|
298257
298447
|
}
|
|
298258
298448
|
get faucetPath() {
|
|
298259
298449
|
return `/faucet/${encodeURIComponent(this.config.faucetName)}`;
|
|
@@ -301203,13 +301393,13 @@ class StorageBunSqlite extends import_StorageProvider.StorageProvider {
|
|
|
301203
301393
|
e2.certificateId = undefined;
|
|
301204
301394
|
if (e2.logger)
|
|
301205
301395
|
e2.logger = undefined;
|
|
301206
|
-
const
|
|
301396
|
+
const fields2 = e2.fields;
|
|
301207
301397
|
if (e2.fields)
|
|
301208
301398
|
e2.fields = undefined;
|
|
301209
301399
|
const id = this.insertRow("certificates", e2);
|
|
301210
301400
|
certificate.certificateId = id;
|
|
301211
|
-
if (
|
|
301212
|
-
for (const field of
|
|
301401
|
+
if (fields2) {
|
|
301402
|
+
for (const field of fields2) {
|
|
301213
301403
|
field.certificateId = id;
|
|
301214
301404
|
field.userId = certificate.userId;
|
|
301215
301405
|
await this.insertCertificateField(field, trx);
|
package/package.json
CHANGED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import type { OneSatContext } from "@1sat/actions";
|
|
2
|
+
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
import {
|
|
5
|
+
RevelationError,
|
|
6
|
+
revealDelegation,
|
|
7
|
+
} from "../../utils/revealDelegation";
|
|
8
|
+
import { createSuccessResponse } from "../utils/errorHandler";
|
|
9
|
+
|
|
10
|
+
export function registerRevealDelegationTool(
|
|
11
|
+
server: McpServer,
|
|
12
|
+
ctx?: OneSatContext,
|
|
13
|
+
) {
|
|
14
|
+
server.registerTool(
|
|
15
|
+
"wallet_revealDelegation",
|
|
16
|
+
{
|
|
17
|
+
description:
|
|
18
|
+
"Receive the human owner's BRC-169 certificate handoff, acquire and prove it with this connected agent wallet, and reveal its restrictions to the specified Sigma verifier. Imports a certificate and activates its existing delegation. Never pays, broadcasts, follows redirects, or retries an ambiguous POST. The subject keyring stays with the wallet.",
|
|
19
|
+
inputSchema: {
|
|
20
|
+
sigmaOrigin: z
|
|
21
|
+
.string()
|
|
22
|
+
.max(2048)
|
|
23
|
+
.describe(
|
|
24
|
+
"Explicit Sigma HTTPS origin, or HTTP loopback for local testing",
|
|
25
|
+
),
|
|
26
|
+
handoffJSON: z
|
|
27
|
+
.string()
|
|
28
|
+
.max(131072)
|
|
29
|
+
.describe(
|
|
30
|
+
"JSON package copied by the owner: certificate, subjectKeyring, revealTo, revelationPath",
|
|
31
|
+
),
|
|
32
|
+
},
|
|
33
|
+
annotations: {
|
|
34
|
+
readOnlyHint: false,
|
|
35
|
+
destructiveHint: false,
|
|
36
|
+
idempotentHint: false,
|
|
37
|
+
openWorldHint: true,
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
async ({ sigmaOrigin, handoffJSON }) => {
|
|
41
|
+
try {
|
|
42
|
+
if (!ctx)
|
|
43
|
+
throw new RevelationError(
|
|
44
|
+
"wallet_unavailable",
|
|
45
|
+
"Connect the agent's existing wallet first.",
|
|
46
|
+
);
|
|
47
|
+
const data = await revealDelegation(
|
|
48
|
+
ctx.wallet,
|
|
49
|
+
sigmaOrigin,
|
|
50
|
+
handoffJSON,
|
|
51
|
+
);
|
|
52
|
+
return { ...createSuccessResponse(data), structuredContent: data };
|
|
53
|
+
} catch (error) {
|
|
54
|
+
const failure =
|
|
55
|
+
error instanceof RevelationError
|
|
56
|
+
? error
|
|
57
|
+
: new RevelationError(
|
|
58
|
+
"revelation_failed",
|
|
59
|
+
"Delegation revelation failed. Check wallet and owner status before retrying.",
|
|
60
|
+
);
|
|
61
|
+
const data = {
|
|
62
|
+
error: failure.code,
|
|
63
|
+
message: failure.message,
|
|
64
|
+
...(failure.status === undefined ? {} : { status: failure.status }),
|
|
65
|
+
};
|
|
66
|
+
return {
|
|
67
|
+
...createSuccessResponse(data),
|
|
68
|
+
structuredContent: data,
|
|
69
|
+
isError: true,
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
},
|
|
73
|
+
);
|
|
74
|
+
}
|
package/tools/wallet/tools.ts
CHANGED
|
@@ -19,6 +19,7 @@ import { registerOpnsDeregisterTool } from "./opnsDeregister";
|
|
|
19
19
|
import { registerOpnsRegisterTool } from "./opnsRegister";
|
|
20
20
|
import { registerPurchaseListingTool } from "./purchaseListing";
|
|
21
21
|
import { registerRefreshUtxosTool } from "./refreshUtxos";
|
|
22
|
+
import { registerRevealDelegationTool } from "./revealDelegation";
|
|
22
23
|
import { registerSendAllBsvTool } from "./sendAllBsv";
|
|
23
24
|
import { registerSendBsvTool } from "./sendBsv";
|
|
24
25
|
import { registerSignBsmTool } from "./signBsm";
|
|
@@ -58,6 +59,7 @@ export function registerWalletTools(
|
|
|
58
59
|
|
|
59
60
|
// Register full BRC-100 wallet interface
|
|
60
61
|
registerBrc100Tools(server, config.ctx);
|
|
62
|
+
registerRevealDelegationTool(server, config.ctx);
|
|
61
63
|
|
|
62
64
|
// A2B tools have to be explicitly enabled
|
|
63
65
|
if (config.enableA2bTools && wallet && config.identityPk) {
|