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 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 fields = !Array.isArray(field) ? parse8(String(field)) : field;
207975
- for (var j5 = 0;j5 < fields.length; j5++) {
207976
- if (!FIELD_NAME_REGEXP.test(fields[j5])) {
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 (fields.indexOf("*") !== -1 || vals.indexOf("*") !== -1) {
207985
+ if (fields2.indexOf("*") !== -1 || vals.indexOf("*") !== -1) {
207986
207986
  return "*";
207987
207987
  }
207988
- for (var i = 0;i < fields.length; i++) {
207989
- var fld = fields[i].toLowerCase();
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 + ", " + fields[i] : fields[i];
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 fields = e2.fields;
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 (fields != null) {
213177
- for (const field of fields) {
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 invalid = this.invalidClauses[verb];
227866
- if (!invalid)
227865
+ const invalid2 = this.invalidClauses[verb];
227866
+ if (!invalid2)
227867
227867
  return;
227868
- for (let i = 0;i < invalid.length; i++) {
227869
- const clause = invalid[i];
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, fields) {
236149
+ connection.query(queryOptions, obj.bindings, function(err, rows, fields2) {
236150
236150
  if (err)
236151
236151
  return rejecter(err);
236152
- obj.response = [rows, fields];
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 fields = response[1];
236163
+ const fields2 = response[1];
236164
236164
  if (obj.output)
236165
- return obj.output.call(runner, rows, fields);
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 fields = obj.response[1];
236442
+ const fields2 = obj.response[1];
236443
236443
  if (obj.output) {
236444
- return obj.output.call(runner, rows, fields);
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 publicKey = privateKey.toPublicKey().toString();
242389
- const hash2 = sdk_1.Hash.sha256(sdk_1.Utils.toArray(publicKey, "utf8"));
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 fields = e2.fields;
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 (fields) {
244861
- for (const field of fields) {
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.1",
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.1",
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 publicKey = currentPaymentKey.toPublicKey();
298039
+ const publicKey2 = currentPaymentKey.toPublicKey();
297850
298040
  return {
297851
- publicKey: publicKey.toDER("hex")
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 publicKey = currentPaymentKey.toPublicKey();
298282
+ const publicKey2 = currentPaymentKey.toPublicKey();
298093
298283
  return {
298094
- publicKey: publicKey.toDER("hex")
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 publicKey;
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 fields = e2.fields;
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 (fields) {
301212
- for (const field of fields) {
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
@@ -2,7 +2,7 @@
2
2
  "name": "bsv-mcp",
3
3
  "module": "dist/index.js",
4
4
  "type": "module",
5
- "version": "0.3.1",
5
+ "version": "0.3.2",
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",
@@ -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
+ }
@@ -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) {