bsv-mcp 0.3.0 → 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/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);
@@ -223156,7 +223156,7 @@ var require_timestamp2 = __commonJS((exports, module) => {
223156
223156
 
223157
223157
  // node_modules/knex/lib/migrations/migrate/MigrationGenerator.js
223158
223158
  var require_MigrationGenerator = __commonJS((exports, module) => {
223159
- var __dirname = "/Users/satchmo/code/bsv-mcp/node_modules/knex/lib/migrations/migrate";
223159
+ var __dirname = "/Users/satchmo/.codex/worktrees/agent-stack-mcp/node_modules/knex/lib/migrations/migrate";
223160
223160
  var path6 = __require("path");
223161
223161
  var { writeJsFileUsingTemplate } = require_template2();
223162
223162
  var { getMergedConfig } = require_migrator_configuration_merger();
@@ -223863,7 +223863,7 @@ var require_seeder_configuration_merger = __commonJS((exports, module) => {
223863
223863
 
223864
223864
  // node_modules/knex/lib/migrations/seed/Seeder.js
223865
223865
  var require_Seeder = __commonJS((exports, module) => {
223866
- var __dirname = "/Users/satchmo/code/bsv-mcp/node_modules/knex/lib/migrations/seed";
223866
+ var __dirname = "/Users/satchmo/.codex/worktrees/agent-stack-mcp/node_modules/knex/lib/migrations/seed";
223867
223867
  var path6 = __require("path");
223868
223868
  var { ensureDirectoryExists } = require_fs();
223869
223869
  var { writeJsFileUsingTemplate } = require_template2();
@@ -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.0",
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",
@@ -284875,6 +284875,281 @@ function registerUtilsTools(server) {
284875
284875
  });
284876
284876
  }
284877
284877
 
284878
+ // utils/droplit.ts
284879
+ init_mod2();
284880
+ class DroplitError extends Error {
284881
+ code;
284882
+ status;
284883
+ details;
284884
+ constructor(code, message, status, details = {}) {
284885
+ super(message);
284886
+ this.code = code;
284887
+ this.status = status;
284888
+ this.details = details;
284889
+ }
284890
+ }
284891
+ function quotaDetails(body) {
284892
+ if (!body || typeof body !== "object")
284893
+ return {};
284894
+ const value2 = body;
284895
+ return {
284896
+ ...typeof value2.remaining === "number" || value2.remaining === null ? { remaining: value2.remaining } : {},
284897
+ ...typeof value2.resets_at === "string" ? { resets_at: value2.resets_at } : {}
284898
+ };
284899
+ }
284900
+ function httpFailure(status, details = {}) {
284901
+ const known = {
284902
+ 401: [
284903
+ "authentication_required",
284904
+ "Authenticate with the configured wallet before retrying."
284905
+ ],
284906
+ 402: [
284907
+ "approval_required",
284908
+ "Payment is required. No automatic payment was made; review payment with the wallet owner."
284909
+ ],
284910
+ 403: [
284911
+ "approval_required",
284912
+ "The sponsor has not authorized this action. Ask its owner to approve your wallet."
284913
+ ],
284914
+ 429: [
284915
+ "quota_exceeded",
284916
+ "Sponsor quota exhausted. Wait until resets_at or ask the owner to adjust your quota."
284917
+ ]
284918
+ };
284919
+ const [code, message] = known[status] ?? [
284920
+ "http_error",
284921
+ "Droplit rejected the request. Check sponsor access and transaction history before retrying."
284922
+ ];
284923
+ return new DroplitError(code, message, status, details);
284924
+ }
284925
+
284926
+ class DroplitClient {
284927
+ config;
284928
+ authFetch;
284929
+ wallet;
284930
+ constructor(config2) {
284931
+ this.config = config2;
284932
+ if (config2.wallet && config2.authKey)
284933
+ throw new Error("Configure Droplit wallet or authKey, not both");
284934
+ const url3 = new URL(config2.apiUrl);
284935
+ const loopback = url3.hostname === "localhost" || url3.hostname === "127.0.0.1" || url3.hostname === "[::1]";
284936
+ if (url3.username || url3.password || url3.search || url3.hash || !(url3.protocol === "https:" || url3.protocol === "http:" && loopback)) {
284937
+ throw new Error("Droplit API requires HTTPS or HTTP loopback without credentials, query or fragment");
284938
+ }
284939
+ if (!config2.faucetName.trim())
284940
+ throw new Error("Droplit faucet name is required");
284941
+ this.wallet = config2.wallet ?? (config2.authKey ? new ProtoWallet_default(config2.authKey) : undefined);
284942
+ if (this.wallet) {
284943
+ const authenticationWallet = new Proxy(this.wallet, {
284944
+ get(target, property) {
284945
+ if (property === "createAction" || property === "signAction") {
284946
+ return async () => {
284947
+ throw httpFailure(402);
284948
+ };
284949
+ }
284950
+ const value2 = Reflect.get(target, property, target);
284951
+ return typeof value2 === "function" ? value2.bind(target) : value2;
284952
+ }
284953
+ });
284954
+ this.authFetch = new AuthFetch(authenticationWallet);
284955
+ }
284956
+ }
284957
+ getConfig() {
284958
+ return { apiUrl: this.config.apiUrl, faucetName: this.config.faucetName };
284959
+ }
284960
+ async getIdentityKey() {
284961
+ if (!this.wallet)
284962
+ throw new DroplitError("authentication_required", "Configure a Droplit wallet identity.");
284963
+ const { publicKey } = await this.wallet.getPublicKey({ identityKey: true });
284964
+ return publicKey;
284965
+ }
284966
+ get faucetPath() {
284967
+ return `/faucet/${encodeURIComponent(this.config.faucetName)}`;
284968
+ }
284969
+ get base() {
284970
+ return this.config.apiUrl.replace(/\/+$/, "");
284971
+ }
284972
+ async authed(path3, init) {
284973
+ if (!this.authFetch) {
284974
+ throw new Error(`${path3} requires authentication and no Droplit auth key is configured.`);
284975
+ }
284976
+ try {
284977
+ return await this.authFetch.fetch(`${this.base}${path3}`, {
284978
+ method: init.method,
284979
+ headers: { "Content-Type": "application/json" },
284980
+ ...init.body === undefined ? {} : { body: JSON.stringify(init.body) },
284981
+ paymentRetryAttempts: 0,
284982
+ ...init.method === "GET" || init.method === "HEAD" ? {} : { retryCounter: 1 }
284983
+ });
284984
+ } catch (error52) {
284985
+ if (error52 instanceof DroplitError)
284986
+ throw error52;
284987
+ const details = error52?.details;
284988
+ const status = details?.status;
284989
+ if (typeof status === "number" && [401, 402, 403, 429].includes(status)) {
284990
+ let body;
284991
+ if (status === 429 && typeof details?.bodyPreview === "string") {
284992
+ try {
284993
+ body = JSON.parse(details.bodyPreview);
284994
+ } catch {}
284995
+ }
284996
+ throw httpFailure(status, status === 429 ? quotaDetails(body) : {});
284997
+ }
284998
+ const write = init.method !== "GET" && init.method !== "HEAD";
284999
+ throw new DroplitError(write ? "unknown_outcome" : "request_failed", write ? "Request outcome is unknown. Reconcile transaction/history before retrying; no automatic retry was made." : "Authenticated request failed. Check the signer and sponsor connection.");
285000
+ }
285001
+ }
285002
+ async parse(response, _attempted) {
285003
+ if (response.ok) {
285004
+ try {
285005
+ return await response.json();
285006
+ } catch {
285007
+ throw new DroplitError("unknown_outcome", "Response could not be read. Reconcile transaction/history before retrying.", response.status);
285008
+ }
285009
+ }
285010
+ let body = {};
285011
+ try {
285012
+ body = await response.json();
285013
+ } catch {}
285014
+ throw httpFailure(response.status, response.status === 429 ? quotaDetails(body) : {});
285015
+ }
285016
+ async getAccess() {
285017
+ const response = await this.authed(`${this.faucetPath}/access`, {
285018
+ method: "GET"
285019
+ });
285020
+ const access = await this.parse(response, "Reading sponsor access");
285021
+ const identity6 = await this.getIdentityKey();
285022
+ if (!access || access.public_key !== identity6 || access.slug !== this.config.faucetName || typeof access.authorized !== "boolean" || typeof access.is_owner !== "boolean" || !Array.isArray(access.quotas) || !Array.isArray(access.unrestricted_kinds)) {
285023
+ throw new DroplitError("invalid_response", "Sponsor access response did not match the authenticated wallet.");
285024
+ }
285025
+ access.approval_path = `/droplit/${encodeURIComponent(this.config.faucetName)}?tab=api&request_key=${encodeURIComponent(identity6)}`;
285026
+ return access;
285027
+ }
285028
+ async fund(rawtx) {
285029
+ const response = await this.authed(`${this.faucetPath}/fund`, {
285030
+ method: "POST",
285031
+ body: { rawtx }
285032
+ });
285033
+ return this.parse(response, "Funding transaction");
285034
+ }
285035
+ async getFaucetStatus() {
285036
+ const response = await fetch(`${this.base}${this.faucetPath}/status`);
285037
+ return this.parse(response, "Reading the faucet status");
285038
+ }
285039
+ async tap(recipientAddress, satoshis) {
285040
+ if (satoshis !== undefined && (!Number.isSafeInteger(satoshis) || satoshis <= 0))
285041
+ throw new Error("satoshis must be a positive safe integer");
285042
+ const response = await this.authed(`${this.faucetPath}/tap`, {
285043
+ method: "POST",
285044
+ body: {
285045
+ recipient_address: recipientAddress,
285046
+ ...satoshis === undefined ? {} : { satoshis }
285047
+ }
285048
+ });
285049
+ return this.parse(response, "Tapping the faucet");
285050
+ }
285051
+ async push(data, encoding = "hex") {
285052
+ const response = await this.authed(`${this.faucetPath}/push`, {
285053
+ method: "POST",
285054
+ body: { data, encoding }
285055
+ });
285056
+ return this.parse(response, "Pushing data");
285057
+ }
285058
+ async authenticatedFetch(path3, init) {
285059
+ return this.authed(path3, init);
285060
+ }
285061
+ }
285062
+
285063
+ // tools/wallet/droplit.ts
285064
+ function errorResult(error52) {
285065
+ const failure = error52 instanceof DroplitError ? error52 : new DroplitError("request_failed", "Droplit operation failed. Check configuration and broadcasting policy.");
285066
+ const data = {
285067
+ error: failure.code,
285068
+ message: failure.message,
285069
+ ...failure.status === undefined ? {} : { status: failure.status },
285070
+ ...failure.details
285071
+ };
285072
+ return {
285073
+ ...createSuccessResponse(data),
285074
+ structuredContent: data,
285075
+ isError: true
285076
+ };
285077
+ }
285078
+ function registerDroplitTools(server, client, disableBroadcasting = false) {
285079
+ server.registerTool("droplit_getAccess", {
285080
+ description: "Read this connected wallet's sponsor authorization and quotas. If unauthorized, a human sponsor owner must approve the wallet manually. Does not create, fund, or approve anything.",
285081
+ inputSchema: {},
285082
+ annotations: {
285083
+ readOnlyHint: true,
285084
+ destructiveHint: false,
285085
+ idempotentHint: true,
285086
+ openWorldHint: true
285087
+ }
285088
+ }, async () => {
285089
+ try {
285090
+ const access = await client.getAccess();
285091
+ const data = {
285092
+ ...access,
285093
+ ...!access.authorized ? {
285094
+ error: "approval_required",
285095
+ message: "Ask the sponsor owner to approve this wallet. Copy the approval URL for manual review; do not auto-open or submit approval.",
285096
+ approval_url: `https://droplit.dev${access.approval_path}`
285097
+ } : {}
285098
+ };
285099
+ return {
285100
+ ...createSuccessResponse(data),
285101
+ structuredContent: data,
285102
+ isError: !access.authorized
285103
+ };
285104
+ } catch (error52) {
285105
+ return errorResult(error52);
285106
+ }
285107
+ });
285108
+ server.registerTool("droplit_push", {
285109
+ description: "Submit one sponsored data transaction. Subject to sponsor approval and quotas. An unknown outcome requires checking transaction history before retrying.",
285110
+ inputSchema: {
285111
+ data: exports_external.array(exports_external.string()).min(1),
285112
+ encoding: exports_external.enum(["hex", "utf8"])
285113
+ },
285114
+ annotations: {
285115
+ readOnlyHint: false,
285116
+ destructiveHint: true,
285117
+ idempotentHint: false,
285118
+ openWorldHint: true
285119
+ }
285120
+ }, async ({ data, encoding }) => {
285121
+ try {
285122
+ if (disableBroadcasting)
285123
+ throw new Error("Broadcasting disabled");
285124
+ assertBroadcastAllowed("droplit_push");
285125
+ return createSuccessResponse(await client.push(data, encoding));
285126
+ } catch (error52) {
285127
+ return errorResult(error52);
285128
+ }
285129
+ });
285130
+ server.registerTool("droplit_fund", {
285131
+ description: "Submit one raw transaction for sponsor funding and broadcast. Subject to sponsor approval and quotas. Unknown outcomes must be reconciled before retrying.",
285132
+ inputSchema: {
285133
+ rawtx: exports_external.string().min(2).regex(/^(?:[0-9a-fA-F]{2})+$/, "Expected raw transaction hex")
285134
+ },
285135
+ annotations: {
285136
+ readOnlyHint: false,
285137
+ destructiveHint: true,
285138
+ idempotentHint: false,
285139
+ openWorldHint: true
285140
+ }
285141
+ }, async ({ rawtx }) => {
285142
+ try {
285143
+ if (disableBroadcasting)
285144
+ throw new Error("Broadcasting disabled");
285145
+ assertBroadcastAllowed("droplit_fund");
285146
+ return createSuccessResponse(await client.fund(rawtx));
285147
+ } catch (error52) {
285148
+ return errorResult(error52);
285149
+ }
285150
+ });
285151
+ }
285152
+
284878
285153
  // tools/wallet/getBalanceDroplit.ts
284879
285154
  function registerWalletGetBalanceDroplitTool(server, droplitClient) {
284880
285155
  server.tool("wallet_getBalance", "Gets the current balance of the wallet (Droplit mode)", {}, async () => {
@@ -284911,16 +285186,13 @@ function registerWalletGetBalanceDroplitTool(server, droplitClient) {
284911
285186
  }
284912
285187
 
284913
285188
  // tools/wallet/setupDroplit.ts
284914
- function requireDroplit(integratedWallet) {
285189
+ async function requireDroplit(integratedWallet) {
284915
285190
  const client = integratedWallet.getDroplitClient();
284916
285191
  if (!client) {
284917
285192
  throw new Error("Droplit client is not configured. Set DROPLIT_API_URL and DROPLIT_FAUCET_NAME.");
284918
285193
  }
284919
- const { apiUrl, authKey } = client.getConfig();
284920
- if (!authKey) {
284921
- throw new Error("Droplit requests must be signed, but no auth key is configured.");
284922
- }
284923
- return { apiUrl, publicKeyHex: authKey.toPublicKey().toString(), client };
285194
+ const { apiUrl } = client.getConfig();
285195
+ return { apiUrl, publicKeyHex: await client.getIdentityKey(), client };
284924
285196
  }
284925
285197
  async function failIfNotOk(response, attempted) {
284926
285198
  if (response.ok)
@@ -284928,9 +285200,9 @@ async function failIfNotOk(response, attempted) {
284928
285200
  throw new Error(`${attempted} failed (${response.status}): ${await response.text()}`);
284929
285201
  }
284930
285202
  function registerSetupDroplitTools(server, integratedWallet) {
284931
- server.tool("wallet_registerDroplitKey", "Registers this wallet's public key with the Droplit API so it can sign authenticated faucet requests. Run once before creating or operating a faucet.", {}, async () => {
285203
+ server.tool("wallet_registerDroplitKey", "Registers public authentication material. Registration does not grant sponsor access; a sponsor owner must approve the wallet separately.", {}, async () => {
284932
285204
  try {
284933
- const { apiUrl, publicKeyHex } = requireDroplit(integratedWallet);
285205
+ const { apiUrl, publicKeyHex } = await requireDroplit(integratedWallet);
284934
285206
  const response = await fetch(`${apiUrl}/auth/register`, {
284935
285207
  method: "POST",
284936
285208
  headers: { "Content-Type": "application/json" },
@@ -284945,13 +285217,13 @@ function registerSetupDroplitTools(server, integratedWallet) {
284945
285217
  return createErrorResponse(error52);
284946
285218
  }
284947
285219
  });
284948
- server.tool("wallet_createDroplitFaucet", "Provisions a new Droplit faucet owned by this wallet. Register the wallet's public key first with wallet_registerDroplitKey.", {
285220
+ server.tool("wallet_createDroplitFaucet", "Creates a faucet owned by this wallet which must be funded before use. This does not provide free credit or approval for another sponsor.", {
284949
285221
  faucetName: exports_external.string().min(1).describe("Identifier for the new faucet; must be unique"),
284950
285222
  fixedDropSats: exports_external.number().int().positive().optional().describe("Satoshis paid out per tap. Server default applies if unset."),
284951
285223
  maxConsolidationInputs: exports_external.number().int().positive().optional().describe("Cap on inputs the faucet consolidates in one transaction")
284952
285224
  }, async ({ faucetName, fixedDropSats, maxConsolidationInputs }) => {
284953
285225
  try {
284954
- const { client } = requireDroplit(integratedWallet);
285226
+ const { client } = await requireDroplit(integratedWallet);
284955
285227
  const body = {
284956
285228
  name: faucetName,
284957
285229
  ...fixedDropSats !== undefined && {
@@ -284974,9 +285246,9 @@ function registerSetupDroplitTools(server, integratedWallet) {
284974
285246
  return createErrorResponse(error52);
284975
285247
  }
284976
285248
  });
284977
- server.tool("wallet_checkDroplitFaucetStatus", "Reads the status of the configured Droplit faucet, including balance and payout settings.", {}, async () => {
285249
+ server.tool("wallet_checkDroplitFaucetStatus", "Reads public faucet balance and payout settings. This is not proof of caller authorization; use droplit_getAccess for that.", {}, async () => {
284978
285250
  try {
284979
- const { client } = requireDroplit(integratedWallet);
285251
+ const { client } = await requireDroplit(integratedWallet);
284980
285252
  return createSuccessResponse({
284981
285253
  faucetStatus: await client.getFaucetStatus()
284982
285254
  });
@@ -294891,7 +295163,7 @@ var package_default2 = {
294891
295163
  name: "bsv-mcp",
294892
295164
  module: "dist/index.js",
294893
295165
  type: "module",
294894
- version: "0.3.0",
295166
+ version: "0.3.2",
294895
295167
  license: "MIT",
294896
295168
  author: "satchmo",
294897
295169
  description: "A collection of Bitcoin SV (BSV) tools for the Model Context Protocol (MCP) framework",
@@ -295324,8 +295596,15 @@ function registerBrc100Tools(server, ctx) {
295324
295596
  txJSON: exports_external.string().describe("JSON array of AtomicBEEF bytes"),
295325
295597
  outputsJSON: exports_external.string().describe("JSON array of outputs to internalize"),
295326
295598
  description: exports_external.string().describe("5-50 char description"),
295327
- labelsJSON: exports_external.string().optional().describe("JSON array of label strings")
295328
- }, async ({ txJSON, outputsJSON, description, labelsJSON }) => {
295599
+ labelsJSON: exports_external.string().optional().describe("JSON array of label strings"),
295600
+ seekPermission: exports_external.boolean().optional()
295601
+ }, async ({
295602
+ txJSON,
295603
+ outputsJSON,
295604
+ description,
295605
+ labelsJSON,
295606
+ seekPermission
295607
+ }) => {
295329
295608
  if (!ctx)
295330
295609
  return noCtx;
295331
295610
  try {
@@ -295333,6 +295612,7 @@ function registerBrc100Tools(server, ctx) {
295333
295612
  tx: parseRequiredJSON("txJSON", txJSON),
295334
295613
  outputs: parseRequiredJSON("outputsJSON", outputsJSON),
295335
295614
  description,
295615
+ seekPermission,
295336
295616
  labels: parseJSON("labelsJSON", labelsJSON)
295337
295617
  }));
295338
295618
  } catch (e2) {
@@ -295349,7 +295629,8 @@ function registerBrc100Tools(server, ctx) {
295349
295629
  includeOutputs: exports_external.boolean().default(false),
295350
295630
  includeOutputLockingScripts: exports_external.boolean().default(false),
295351
295631
  limit: exports_external.number().default(25),
295352
- offset: exports_external.number().default(0)
295632
+ offset: exports_external.number().default(0),
295633
+ seekPermission: exports_external.boolean().optional()
295353
295634
  }, async ({ labelsJSON, ...rest }) => {
295354
295635
  if (!ctx)
295355
295636
  return noCtx;
@@ -295371,7 +295652,8 @@ function registerBrc100Tools(server, ctx) {
295371
295652
  includeTags: exports_external.boolean().default(false),
295372
295653
  includeLabels: exports_external.boolean().default(false),
295373
295654
  limit: exports_external.number().default(25),
295374
- offset: exports_external.number().default(0)
295655
+ offset: exports_external.number().default(0),
295656
+ seekPermission: exports_external.boolean().optional()
295375
295657
  }, async ({ tagsJSON, ...rest }) => {
295376
295658
  if (!ctx)
295377
295659
  return noCtx;
@@ -295423,7 +295705,9 @@ function registerBrc100Tools(server, ctx) {
295423
295705
  protocolIDJSON: exports_external.string().describe("JSON array [securityLevel, protocolString]"),
295424
295706
  keyID: exports_external.string(),
295425
295707
  counterparty: exports_external.string().optional(),
295426
- privileged: exports_external.boolean().optional()
295708
+ privileged: exports_external.boolean().optional(),
295709
+ privilegedReason: exports_external.string().optional(),
295710
+ seekPermission: exports_external.boolean().optional()
295427
295711
  }, async ({ protocolIDJSON, ...rest }) => {
295428
295712
  if (!ctx)
295429
295713
  return noCtx;
@@ -295441,7 +295725,9 @@ function registerBrc100Tools(server, ctx) {
295441
295725
  protocolIDJSON: exports_external.string().describe("JSON array [securityLevel, protocolString]"),
295442
295726
  keyID: exports_external.string(),
295443
295727
  counterparty: exports_external.string().optional(),
295444
- privileged: exports_external.boolean().optional()
295728
+ privileged: exports_external.boolean().optional(),
295729
+ privilegedReason: exports_external.string().optional(),
295730
+ seekPermission: exports_external.boolean().optional()
295445
295731
  }, async ({ protocolIDJSON, ...rest }) => {
295446
295732
  if (!ctx)
295447
295733
  return noCtx;
@@ -295459,7 +295745,9 @@ function registerBrc100Tools(server, ctx) {
295459
295745
  protocolIDJSON: exports_external.string().describe("JSON array [securityLevel, protocolString]"),
295460
295746
  keyID: exports_external.string(),
295461
295747
  counterparty: exports_external.string().optional(),
295462
- privileged: exports_external.boolean().optional()
295748
+ privileged: exports_external.boolean().optional(),
295749
+ privilegedReason: exports_external.string().optional(),
295750
+ seekPermission: exports_external.boolean().optional()
295463
295751
  }, async ({ protocolIDJSON, ...rest }) => {
295464
295752
  if (!ctx)
295465
295753
  return noCtx;
@@ -295478,7 +295766,9 @@ function registerBrc100Tools(server, ctx) {
295478
295766
  protocolIDJSON: exports_external.string().describe("JSON array [securityLevel, protocolString]"),
295479
295767
  keyID: exports_external.string(),
295480
295768
  counterparty: exports_external.string().optional(),
295481
- privileged: exports_external.boolean().optional()
295769
+ privileged: exports_external.boolean().optional(),
295770
+ privilegedReason: exports_external.string().optional(),
295771
+ seekPermission: exports_external.boolean().optional()
295482
295772
  }, async ({ protocolIDJSON, ...rest }) => {
295483
295773
  if (!ctx)
295484
295774
  return noCtx;
@@ -295497,7 +295787,9 @@ function registerBrc100Tools(server, ctx) {
295497
295787
  protocolIDJSON: exports_external.string().describe("JSON array [securityLevel, protocolString]"),
295498
295788
  keyID: exports_external.string(),
295499
295789
  counterparty: exports_external.string().optional(),
295500
- privileged: exports_external.boolean().optional()
295790
+ privileged: exports_external.boolean().optional(),
295791
+ privilegedReason: exports_external.string().optional(),
295792
+ seekPermission: exports_external.boolean().optional()
295501
295793
  }, async ({ protocolIDJSON, ...rest }) => {
295502
295794
  if (!ctx)
295503
295795
  return noCtx;
@@ -295518,7 +295810,9 @@ function registerBrc100Tools(server, ctx) {
295518
295810
  keyID: exports_external.string(),
295519
295811
  counterparty: exports_external.string().optional(),
295520
295812
  forSelf: exports_external.boolean().optional(),
295521
- privileged: exports_external.boolean().optional()
295813
+ privileged: exports_external.boolean().optional(),
295814
+ privilegedReason: exports_external.string().optional(),
295815
+ seekPermission: exports_external.boolean().optional()
295522
295816
  }, async ({ protocolIDJSON, ...rest }) => {
295523
295817
  if (!ctx)
295524
295818
  return noCtx;
@@ -295645,7 +295939,8 @@ function registerBrc100Tools(server, ctx) {
295645
295939
  server.tool("wallet_discoverByIdentityKey", "Discovers certificates issued to a given identity key.", {
295646
295940
  identityKey: exports_external.string().describe("Identity public key hex"),
295647
295941
  limit: exports_external.number().default(25),
295648
- offset: exports_external.number().default(0)
295942
+ offset: exports_external.number().default(0),
295943
+ seekPermission: exports_external.boolean().optional()
295649
295944
  }, async (args) => {
295650
295945
  if (!ctx)
295651
295946
  return noCtx;
@@ -295658,7 +295953,8 @@ function registerBrc100Tools(server, ctx) {
295658
295953
  server.tool("wallet_discoverByAttributes", "Discovers certificates matching specific attributes.", {
295659
295954
  attributesJSON: exports_external.string().describe("JSON object of attribute key/value pairs to match"),
295660
295955
  limit: exports_external.number().default(25),
295661
- offset: exports_external.number().default(0)
295956
+ offset: exports_external.number().default(0),
295957
+ seekPermission: exports_external.boolean().optional()
295662
295958
  }, async ({ attributesJSON, ...rest }) => {
295663
295959
  if (!ctx)
295664
295960
  return noCtx;
@@ -296862,6 +297158,195 @@ function registerRefreshUtxosTool(server, ctx) {
296862
297158
  });
296863
297159
  }
296864
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
+
296865
297350
  // tools/wallet/sendAllBsv.ts
296866
297351
  var sendAllBsvArgsSchema = exports_external.object({
296867
297352
  destination: exports_external.string().describe("Destination P2PKH address to send all funds to")
@@ -297365,14 +297850,17 @@ function registerWalletTools(server, wallet5, config2) {
297365
297850
  registerRefreshUtxosTool(server, config2.ctx);
297366
297851
  registerWalletGetBalanceTool(server, config2.ctx);
297367
297852
  registerBrc100Tools(server, config2.ctx);
297368
- if (config2.enableA2bTools) {
297853
+ registerRevealDelegationTool(server, config2.ctx);
297854
+ if (config2.enableA2bTools && wallet5 && config2.identityPk) {
297369
297855
  registerA2bPublishMcpTool(server, wallet5, config2.identityPk, {
297370
297856
  disableBroadcasting: config2.disableBroadcasting
297371
297857
  });
297372
297858
  }
297373
297859
  registerCreateOrdinalsTool(server, config2.ctx);
297374
- registerGatherCollectionInfoTool(server, wallet5);
297375
- registerMintCollectionTool(server, wallet5);
297860
+ if (wallet5) {
297861
+ registerGatherCollectionInfoTool(server, wallet5);
297862
+ registerMintCollectionTool(server, wallet5);
297863
+ }
297376
297864
  registerGetOrdinalsTool(server, config2.ctx);
297377
297865
  registerListTokensTool(server, config2.ctx);
297378
297866
  registerGetBsv21BalancesTool(server, config2.ctx);
@@ -297412,7 +297900,7 @@ function registerAllTools(server, config2 = {}) {
297412
297900
  if (enableA2bTools) {
297413
297901
  registerA2bDiscoverTool(server);
297414
297902
  }
297415
- if (enableBapTools) {
297903
+ if (enableBapTools && (!config2.ctx || config2.wallet)) {
297416
297904
  const bapConfig = {
297417
297905
  disableBroadcasting: config2.disableBroadcasting,
297418
297906
  identityPk: config2.identityPk,
@@ -297425,6 +297913,8 @@ function registerAllTools(server, config2 = {}) {
297425
297913
  registerBsocialTools(server, { wallet: config2.wallet });
297426
297914
  }
297427
297915
  if (enableWalletTools) {
297916
+ if (config2.droplitClient)
297917
+ registerDroplitTools(server, config2.droplitClient, config2.disableBroadcasting);
297428
297918
  if (config2.integratedWallet?.isDroplitMode) {
297429
297919
  const droplitClient = config2.integratedWallet.getDroplitClient();
297430
297920
  if (droplitClient) {
@@ -297434,7 +297924,7 @@ function registerAllTools(server, config2 = {}) {
297434
297924
  }
297435
297925
  console.error("Registered Droplit mode wallet tools");
297436
297926
  }
297437
- } else if (config2.wallet) {
297927
+ } else if (config2.wallet || config2.ctx) {
297438
297928
  const walletToolOptions = {
297439
297929
  disableBroadcasting: config2.disableBroadcasting === true,
297440
297930
  enableA2bTools,
@@ -297444,7 +297934,7 @@ function registerAllTools(server, config2 = {}) {
297444
297934
  registerWalletTools(server, config2.wallet, walletToolOptions);
297445
297935
  }
297446
297936
  }
297447
- if (enableMneeTools) {
297937
+ if (enableMneeTools && (!config2.ctx || config2.wallet)) {
297448
297938
  registerMneeTools(server);
297449
297939
  }
297450
297940
  }
@@ -297452,62 +297942,6 @@ function registerAllTools(server, config2 = {}) {
297452
297942
  // tools/wallet/integratedWallet.ts
297453
297943
  init_mod2();
297454
297944
 
297455
- // utils/droplit.ts
297456
- init_mod2();
297457
-
297458
- class DroplitClient {
297459
- config;
297460
- authFetch;
297461
- constructor(config2) {
297462
- this.config = config2;
297463
- if (config2.authKey) {
297464
- this.authFetch = new AuthFetch(new ProtoWallet_default(config2.authKey));
297465
- }
297466
- }
297467
- getConfig() {
297468
- return this.config;
297469
- }
297470
- get base() {
297471
- return this.config.apiUrl.replace(/\/+$/, "");
297472
- }
297473
- async authed(path5, init) {
297474
- if (!this.authFetch) {
297475
- throw new Error(`${path5} requires authentication and no Droplit auth key is configured.`);
297476
- }
297477
- return this.authFetch.fetch(`${this.base}${path5}`, {
297478
- method: init.method,
297479
- headers: { "Content-Type": "application/json" },
297480
- ...init.body === undefined ? {} : { body: JSON.stringify(init.body) }
297481
- });
297482
- }
297483
- async parse(response, attempted) {
297484
- if (response.ok)
297485
- return await response.json();
297486
- const raw = await response.text();
297487
- let detail = raw.slice(0, 200);
297488
- try {
297489
- const parsed = JSON.parse(raw);
297490
- detail = parsed.message ?? parsed.error ?? detail;
297491
- } catch {}
297492
- throw new Error(`${attempted} failed (${response.status}): ${detail}`);
297493
- }
297494
- async getFaucetStatus() {
297495
- const response = await fetch(`${this.base}/faucet/${this.config.faucetName}/status`);
297496
- return this.parse(response, "Reading the faucet status");
297497
- }
297498
- async tap(recipientAddress) {
297499
- const response = await this.authed(`/faucet/${this.config.faucetName}/tap`, { method: "POST", body: { recipient_address: recipientAddress } });
297500
- return this.parse(response, "Tapping the faucet");
297501
- }
297502
- async push(data, encoding = "hex") {
297503
- const response = await this.authed(`/faucet/${this.config.faucetName}/push`, { method: "POST", body: { data, encoding } });
297504
- return this.parse(response, "Pushing data");
297505
- }
297506
- async authenticatedFetch(path5, init) {
297507
- return this.authed(path5, init);
297508
- }
297509
- }
297510
-
297511
297945
  // tools/wallet/wallet.ts
297512
297946
  init_mod2();
297513
297947
  class Wallet {
@@ -297602,9 +298036,9 @@ class Wallet {
297602
298036
  if (!currentPaymentKey) {
297603
298037
  throw new Error("No payment key available to derive public key.");
297604
298038
  }
297605
- const publicKey = currentPaymentKey.toPublicKey();
298039
+ const publicKey2 = currentPaymentKey.toPublicKey();
297606
298040
  return {
297607
- publicKey: publicKey.toDER("hex")
298041
+ publicKey: publicKey2.toDER("hex")
297608
298042
  };
297609
298043
  }
297610
298044
  async sendToAddress(address, amountSatoshis) {
@@ -297706,7 +298140,7 @@ class IntegratedWallet {
297706
298140
  }
297707
298141
  async sendToAddress(address, satoshis) {
297708
298142
  if (this.droplitClient) {
297709
- const response = await this.droplitClient.tap(address);
298143
+ const response = await this.droplitClient.tap(address, satoshis);
297710
298144
  return { txid: response.txid };
297711
298145
  }
297712
298146
  if (this.localWallet) {
@@ -297845,9 +298279,9 @@ class Wallet2 {
297845
298279
  if (!currentPaymentKey) {
297846
298280
  throw new Error("No payment key available to derive public key.");
297847
298281
  }
297848
- const publicKey = currentPaymentKey.toPublicKey();
298282
+ const publicKey2 = currentPaymentKey.toPublicKey();
297849
298283
  return {
297850
- publicKey: publicKey.toDER("hex")
298284
+ publicKey: publicKey2.toDER("hex")
297851
298285
  };
297852
298286
  }
297853
298287
  async sendToAddress(address, amountSatoshis) {
@@ -297912,6 +298346,245 @@ class Wallet2 {
297912
298346
  }
297913
298347
  }
297914
298348
 
298349
+ // utils/droplit.ts
298350
+ init_mod2();
298351
+ function readDroplitSponsorConfig(env = process.env) {
298352
+ const apiUrl = env.DROPLIT_API_URL;
298353
+ const faucetName = env.DROPLIT_FAUCET_NAME;
298354
+ if (apiUrl === undefined && faucetName === undefined)
298355
+ return;
298356
+ if (!apiUrl?.trim() || !faucetName?.trim()) {
298357
+ throw new Error("DROPLIT_API_URL and DROPLIT_FAUCET_NAME must both be explicitly configured");
298358
+ }
298359
+ return { apiUrl, faucetName };
298360
+ }
298361
+
298362
+ class DroplitError2 extends Error {
298363
+ code;
298364
+ status;
298365
+ details;
298366
+ constructor(code, message, status, details = {}) {
298367
+ super(message);
298368
+ this.code = code;
298369
+ this.status = status;
298370
+ this.details = details;
298371
+ }
298372
+ }
298373
+ function quotaDetails2(body) {
298374
+ if (!body || typeof body !== "object")
298375
+ return {};
298376
+ const value2 = body;
298377
+ return {
298378
+ ...typeof value2.remaining === "number" || value2.remaining === null ? { remaining: value2.remaining } : {},
298379
+ ...typeof value2.resets_at === "string" ? { resets_at: value2.resets_at } : {}
298380
+ };
298381
+ }
298382
+ function httpFailure2(status, details = {}) {
298383
+ const known = {
298384
+ 401: [
298385
+ "authentication_required",
298386
+ "Authenticate with the configured wallet before retrying."
298387
+ ],
298388
+ 402: [
298389
+ "approval_required",
298390
+ "Payment is required. No automatic payment was made; review payment with the wallet owner."
298391
+ ],
298392
+ 403: [
298393
+ "approval_required",
298394
+ "The sponsor has not authorized this action. Ask its owner to approve your wallet."
298395
+ ],
298396
+ 429: [
298397
+ "quota_exceeded",
298398
+ "Sponsor quota exhausted. Wait until resets_at or ask the owner to adjust your quota."
298399
+ ]
298400
+ };
298401
+ const [code, message] = known[status] ?? [
298402
+ "http_error",
298403
+ "Droplit rejected the request. Check sponsor access and transaction history before retrying."
298404
+ ];
298405
+ return new DroplitError2(code, message, status, details);
298406
+ }
298407
+
298408
+ class DroplitClient2 {
298409
+ config;
298410
+ authFetch;
298411
+ wallet;
298412
+ constructor(config2) {
298413
+ this.config = config2;
298414
+ if (config2.wallet && config2.authKey)
298415
+ throw new Error("Configure Droplit wallet or authKey, not both");
298416
+ const url3 = new URL(config2.apiUrl);
298417
+ const loopback = url3.hostname === "localhost" || url3.hostname === "127.0.0.1" || url3.hostname === "[::1]";
298418
+ if (url3.username || url3.password || url3.search || url3.hash || !(url3.protocol === "https:" || url3.protocol === "http:" && loopback)) {
298419
+ throw new Error("Droplit API requires HTTPS or HTTP loopback without credentials, query or fragment");
298420
+ }
298421
+ if (!config2.faucetName.trim())
298422
+ throw new Error("Droplit faucet name is required");
298423
+ this.wallet = config2.wallet ?? (config2.authKey ? new ProtoWallet_default(config2.authKey) : undefined);
298424
+ if (this.wallet) {
298425
+ const authenticationWallet = new Proxy(this.wallet, {
298426
+ get(target, property) {
298427
+ if (property === "createAction" || property === "signAction") {
298428
+ return async () => {
298429
+ throw httpFailure2(402);
298430
+ };
298431
+ }
298432
+ const value2 = Reflect.get(target, property, target);
298433
+ return typeof value2 === "function" ? value2.bind(target) : value2;
298434
+ }
298435
+ });
298436
+ this.authFetch = new AuthFetch(authenticationWallet);
298437
+ }
298438
+ }
298439
+ getConfig() {
298440
+ return { apiUrl: this.config.apiUrl, faucetName: this.config.faucetName };
298441
+ }
298442
+ async getIdentityKey() {
298443
+ if (!this.wallet)
298444
+ throw new DroplitError2("authentication_required", "Configure a Droplit wallet identity.");
298445
+ const { publicKey: publicKey2 } = await this.wallet.getPublicKey({ identityKey: true });
298446
+ return publicKey2;
298447
+ }
298448
+ get faucetPath() {
298449
+ return `/faucet/${encodeURIComponent(this.config.faucetName)}`;
298450
+ }
298451
+ get base() {
298452
+ return this.config.apiUrl.replace(/\/+$/, "");
298453
+ }
298454
+ async authed(path5, init) {
298455
+ if (!this.authFetch) {
298456
+ throw new Error(`${path5} requires authentication and no Droplit auth key is configured.`);
298457
+ }
298458
+ try {
298459
+ return await this.authFetch.fetch(`${this.base}${path5}`, {
298460
+ method: init.method,
298461
+ headers: { "Content-Type": "application/json" },
298462
+ ...init.body === undefined ? {} : { body: JSON.stringify(init.body) },
298463
+ paymentRetryAttempts: 0,
298464
+ ...init.method === "GET" || init.method === "HEAD" ? {} : { retryCounter: 1 }
298465
+ });
298466
+ } catch (error53) {
298467
+ if (error53 instanceof DroplitError2)
298468
+ throw error53;
298469
+ const details = error53?.details;
298470
+ const status = details?.status;
298471
+ if (typeof status === "number" && [401, 402, 403, 429].includes(status)) {
298472
+ let body;
298473
+ if (status === 429 && typeof details?.bodyPreview === "string") {
298474
+ try {
298475
+ body = JSON.parse(details.bodyPreview);
298476
+ } catch {}
298477
+ }
298478
+ throw httpFailure2(status, status === 429 ? quotaDetails2(body) : {});
298479
+ }
298480
+ const write = init.method !== "GET" && init.method !== "HEAD";
298481
+ throw new DroplitError2(write ? "unknown_outcome" : "request_failed", write ? "Request outcome is unknown. Reconcile transaction/history before retrying; no automatic retry was made." : "Authenticated request failed. Check the signer and sponsor connection.");
298482
+ }
298483
+ }
298484
+ async parse(response, _attempted) {
298485
+ if (response.ok) {
298486
+ try {
298487
+ return await response.json();
298488
+ } catch {
298489
+ throw new DroplitError2("unknown_outcome", "Response could not be read. Reconcile transaction/history before retrying.", response.status);
298490
+ }
298491
+ }
298492
+ let body = {};
298493
+ try {
298494
+ body = await response.json();
298495
+ } catch {}
298496
+ throw httpFailure2(response.status, response.status === 429 ? quotaDetails2(body) : {});
298497
+ }
298498
+ async getAccess() {
298499
+ const response = await this.authed(`${this.faucetPath}/access`, {
298500
+ method: "GET"
298501
+ });
298502
+ const access = await this.parse(response, "Reading sponsor access");
298503
+ const identity6 = await this.getIdentityKey();
298504
+ if (!access || access.public_key !== identity6 || access.slug !== this.config.faucetName || typeof access.authorized !== "boolean" || typeof access.is_owner !== "boolean" || !Array.isArray(access.quotas) || !Array.isArray(access.unrestricted_kinds)) {
298505
+ throw new DroplitError2("invalid_response", "Sponsor access response did not match the authenticated wallet.");
298506
+ }
298507
+ access.approval_path = `/droplit/${encodeURIComponent(this.config.faucetName)}?tab=api&request_key=${encodeURIComponent(identity6)}`;
298508
+ return access;
298509
+ }
298510
+ async fund(rawtx) {
298511
+ const response = await this.authed(`${this.faucetPath}/fund`, {
298512
+ method: "POST",
298513
+ body: { rawtx }
298514
+ });
298515
+ return this.parse(response, "Funding transaction");
298516
+ }
298517
+ async getFaucetStatus() {
298518
+ const response = await fetch(`${this.base}${this.faucetPath}/status`);
298519
+ return this.parse(response, "Reading the faucet status");
298520
+ }
298521
+ async tap(recipientAddress, satoshis) {
298522
+ if (satoshis !== undefined && (!Number.isSafeInteger(satoshis) || satoshis <= 0))
298523
+ throw new Error("satoshis must be a positive safe integer");
298524
+ const response = await this.authed(`${this.faucetPath}/tap`, {
298525
+ method: "POST",
298526
+ body: {
298527
+ recipient_address: recipientAddress,
298528
+ ...satoshis === undefined ? {} : { satoshis }
298529
+ }
298530
+ });
298531
+ return this.parse(response, "Tapping the faucet");
298532
+ }
298533
+ async push(data, encoding = "hex") {
298534
+ const response = await this.authed(`${this.faucetPath}/push`, {
298535
+ method: "POST",
298536
+ body: { data, encoding }
298537
+ });
298538
+ return this.parse(response, "Pushing data");
298539
+ }
298540
+ async authenticatedFetch(path5, init) {
298541
+ return this.authed(path5, init);
298542
+ }
298543
+ }
298544
+
298545
+ // utils/externalWalletConfig.ts
298546
+ function readExternalWalletConfig(env = process.env) {
298547
+ const raw = env.BRC100_WALLET_URL;
298548
+ if (raw === undefined) {
298549
+ if (env.BRC100_WALLET_ORIGINATOR !== undefined) {
298550
+ throw new Error("BRC100_WALLET_ORIGINATOR requires BRC100_WALLET_URL");
298551
+ }
298552
+ return;
298553
+ }
298554
+ for (const name of ["PRIVATE_KEY_WIF", "IDENTITY_KEY_WIF"]) {
298555
+ if (env[name] !== undefined) {
298556
+ throw new Error(`BRC100_WALLET_URL conflicts with ${name}; select one wallet identity`);
298557
+ }
298558
+ }
298559
+ if (env.USE_DROPLIT_API === "true") {
298560
+ throw new Error("BRC100_WALLET_URL conflicts with USE_DROPLIT_API");
298561
+ }
298562
+ let url3;
298563
+ try {
298564
+ url3 = new URL(raw);
298565
+ } catch {
298566
+ throw new Error("BRC100_WALLET_URL must be a valid signer RPC URL");
298567
+ }
298568
+ const loopback = url3.hostname === "localhost" || url3.hostname === "[::1]" || /^127\.(?:\d{1,3}\.){2}\d{1,3}$/.test(url3.hostname);
298569
+ if (!raw || raw !== raw.trim() || /[\\\s@?]/.test(raw) || url3.username || url3.password || raw.includes("#") || url3.search || !(url3.protocol === "https:" || url3.protocol === "http:" && loopback)) {
298570
+ throw new Error("BRC100_WALLET_URL requires HTTPS or HTTP loopback, without credentials, queries or fragments");
298571
+ }
298572
+ const originator = env.BRC100_WALLET_ORIGINATOR ?? "bsv-mcp.local";
298573
+ let origin;
298574
+ try {
298575
+ origin = new URL(originator.includes("://") ? originator : `http://${originator}`);
298576
+ } catch {
298577
+ throw new Error("BRC100_WALLET_ORIGINATOR must be a domain or HTTP(S) origin");
298578
+ }
298579
+ if (!originator || /[\s\\]/.test(originator) || originator.length >= 250 || !["http:", "https:"].includes(origin.protocol) || origin.username || origin.password || origin.pathname !== "/" || origin.search || /[#?@]/.test(originator) || !/^(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?(?:\.[a-z0-9](?:[a-z0-9-]*[a-z0-9])?)*|\[::1\])$/i.test(origin.hostname) || origin.hostname.toLowerCase() === "admin.bsv-mcp.internal") {
298580
+ throw new Error("BRC100_WALLET_ORIGINATOR must be a non-admin domain or HTTP(S) origin without credentials, path, query or fragment");
298581
+ }
298582
+ return { url: url3.toString().replace(/\/$/, ""), originator };
298583
+ }
298584
+ async function initializeKeysForWalletMode(external4, loadLocalKeys) {
298585
+ return external4 ? undefined : loadLocalKeys();
298586
+ }
298587
+
297915
298588
  // node_modules/jose/dist/webapi/lib/buffer_utils.js
297916
298589
  var encoder = new TextEncoder;
297917
298590
  var decoder = new TextDecoder;
@@ -300720,13 +301393,13 @@ class StorageBunSqlite extends import_StorageProvider.StorageProvider {
300720
301393
  e2.certificateId = undefined;
300721
301394
  if (e2.logger)
300722
301395
  e2.logger = undefined;
300723
- const fields = e2.fields;
301396
+ const fields2 = e2.fields;
300724
301397
  if (e2.fields)
300725
301398
  e2.fields = undefined;
300726
301399
  const id = this.insertRow("certificates", e2);
300727
301400
  certificate.certificateId = id;
300728
- if (fields) {
300729
- for (const field of fields) {
301401
+ if (fields2) {
301402
+ for (const field of fields2) {
300730
301403
  field.certificateId = id;
300731
301404
  field.userId = certificate.userId;
300732
301405
  await this.insertCertificateField(field, trx);
@@ -301997,9 +302670,19 @@ async function buildLocalStorage(config2, baseOptions) {
301997
302670
  init_storage_pg();
301998
302671
  var import_wallet_toolbox2 = __toESM(require_src6(), 1);
301999
302672
 
302673
+ // node_modules/@1sat/wallet-remote/dist/index.js
302674
+ init_dist4();
302675
+
302676
+ // node_modules/@1sat/wallet-remote/dist/createRemoteWallet.js
302677
+ init_dist4();
302678
+ var import_index_client2 = __toESM(require_index_client5(), 1);
302679
+
302680
+ // node_modules/@1sat/wallet-remote/dist/index.js
302681
+ var import_index_client3 = __toESM(require_index_client5(), 1);
302682
+
302000
302683
  // utils/walletInit.ts
302001
302684
  init_mod2();
302002
- var import_index_client2 = __toESM(require_index_client5(), 1);
302685
+ var import_index_client4 = __toESM(require_index_client5(), 1);
302003
302686
 
302004
302687
  // utils/spendingApproval.ts
302005
302688
  var serverInstance2 = null;
@@ -302096,7 +302779,7 @@ async function initWallet(privateKeyWif, chain = "main") {
302096
302779
  activeRemote: process.env.REMOTE_STORAGE_URL ?? DEFAULT_REMOTE_STORAGE_URL,
302097
302780
  storageIdentityKey: "bsv-mcp"
302098
302781
  });
302099
- const wpm = new import_index_client2.WalletPermissionsManager(result2.wallet, ADMIN_ORIGINATOR, {
302782
+ const wpm = new import_index_client4.WalletPermissionsManager(result2.wallet, ADMIN_ORIGINATOR, {
302100
302783
  seekProtocolPermissionsForSigning: false,
302101
302784
  seekProtocolPermissionsForEncrypting: false,
302102
302785
  seekProtocolPermissionsForHMAC: false,
@@ -302138,7 +302821,7 @@ async function initWallet(privateKeyWif, chain = "main") {
302138
302821
  }).catch((err) => {
302139
302822
  console.error("[wallet] message box sync failed:", err);
302140
302823
  });
302141
- activeResult = { ...result2, ctx, depositAddress };
302824
+ activeResult = result2;
302142
302825
  return {
302143
302826
  wallet: wpm,
302144
302827
  services: result2.services,
@@ -302147,6 +302830,36 @@ async function initWallet(privateKeyWif, chain = "main") {
302147
302830
  destroy: result2.destroy
302148
302831
  };
302149
302832
  }
302833
+ async function initExternalWallet(config2, chain = "main") {
302834
+ const httpClient = (input, init) => fetch(input, {
302835
+ ...init,
302836
+ redirect: "error",
302837
+ signal: init?.signal ? AbortSignal.any([init.signal, AbortSignal.timeout(1e4)]) : AbortSignal.timeout(1e4)
302838
+ });
302839
+ const wallet7 = new HTTPWalletJSON(config2.originator, config2.url, httpClient);
302840
+ let identityKey;
302841
+ try {
302842
+ const identity6 = await wallet7.getPublicKey({ identityKey: true });
302843
+ if (!/^(02|03)[0-9a-f]{64}$/i.test(identity6.publicKey)) {
302844
+ throw new Error("Signer returned an invalid compressed identity public key");
302845
+ }
302846
+ PublicKey.fromString(identity6.publicKey);
302847
+ identityKey = identity6.publicKey;
302848
+ } catch (error53) {
302849
+ throw new Error("External BRC-100 signer readiness failed. Check BRC100_WALLET_URL and approve identity access in the signer. This must be SDK signer RPC, not 1sat serve wallet storage RPC. No local wallet was created.", { cause: error53 });
302850
+ }
302851
+ const services2 = new OneSatServices(chain, process.env.ONESAT_API_URL);
302852
+ const dataDir = join2(homedir(), ".bsv-mcp");
302853
+ const ctx = createContext(wallet7, {
302854
+ services: services2,
302855
+ chain,
302856
+ dataDir,
302857
+ log: (entry) => writeAuditLog(dataDir, entry)
302858
+ });
302859
+ const destroy = async () => {};
302860
+ activeResult = { destroy };
302861
+ return { wallet: wallet7, ctx, services: services2, identityKey, destroy };
302862
+ }
302150
302863
  async function destroyWallet() {
302151
302864
  if (activeResult) {
302152
302865
  await activeResult.destroy();
@@ -302283,7 +302996,7 @@ async function initializeKeys() {
302283
302996
  }
302284
302997
  var APP_RESOURCE_URI = "ui://bsv-mcp/app.html";
302285
302998
  var __appDirname = dirname2(fileURLToPath(import.meta.url));
302286
- function registerMcpAppTools(server2, wallet6, ctx) {
302999
+ function registerMcpAppTools(server2, wallet7, ctx) {
302287
303000
  K3(server2, "bsv_dashboard", {
302288
303001
  title: "BSV Dashboard",
302289
303002
  description: "Interactive BSV dashboard with Explorer, Wallet, and Ordinals tabs. Use this for any BSV-related query that benefits from visual display.",
@@ -302433,7 +303146,7 @@ function registerMcpAppTools(server2, wallet6, ctx) {
302433
303146
  ui: { resourceUri: APP_RESOURCE_URI, visibility: ["app"] }
302434
303147
  }
302435
303148
  }, async () => {
302436
- if (!ctx && !wallet6) {
303149
+ if (!ctx && !wallet7) {
302437
303150
  return {
302438
303151
  content: [
302439
303152
  {
@@ -302465,9 +303178,9 @@ function registerMcpAppTools(server2, wallet6, ctx) {
302465
303178
  const [txid = "", voutStr = "0"] = (o6.outpoint ?? "").split(".");
302466
303179
  return { txid, vout: Number(voutStr), satoshis: o6.satoshis };
302467
303180
  });
302468
- } else if (wallet6) {
302469
- address = wallet6.getAddress();
302470
- const { paymentUtxos } = await wallet6.getUtxos();
303181
+ } else if (wallet7) {
303182
+ address = wallet7.getAddress();
303183
+ const { paymentUtxos } = await wallet7.getUtxos();
302471
303184
  for (const utxo of paymentUtxos) {
302472
303185
  totalSatoshis += utxo.satoshis || 0;
302473
303186
  }
@@ -302892,6 +303605,8 @@ Options:
302892
303605
  Environment Variables:
302893
303606
  TRANSPORT Transport mode: 'stdio' or 'http' (default: http)
302894
303607
  PORT HTTP server port (default: 3000)
303608
+ BRC100_WALLET_URL Existing SDK HTTPWalletJSON signer RPC URL
303609
+ BRC100_WALLET_ORIGINATOR Signer permission origin (default: bsv-mcp.local)
302895
303610
  PRIVATE_KEY_WIF Payment private key in WIF format
302896
303611
  DISABLE_TOOLS Disable all tools (default: false)
302897
303612
  DISABLE_WALLET_TOOLS Disable wallet tools (default: false)
@@ -302915,7 +303630,7 @@ Tool Categories:
302915
303630
 
302916
303631
  Authentication:
302917
303632
  - Most tools work without authentication
302918
- - Wallet operations require PRIVATE_KEY_WIF or generated keys
303633
+ - Wallet operations use BRC100_WALLET_URL, PRIVATE_KEY_WIF or generated keys
302919
303634
  - BAP/A2B tools require identity keys (generated via bap_generate tool)
302920
303635
  `);
302921
303636
  process.exit(0);
@@ -302924,11 +303639,31 @@ Authentication:
302924
303639
  console.log(`${package_default.name} v${package_default.version}`);
302925
303640
  process.exit(0);
302926
303641
  }
302927
- const { payPk, identityPk, xprv, source: keySource } = await initializeKeys();
303642
+ const externalWallet = readExternalWalletConfig();
303643
+ const sponsorConfig = CONFIG.useDroplitApi ? undefined : readDroplitSponsorConfig();
303644
+ const keys = await initializeKeysForWalletMode(externalWallet, initializeKeys);
303645
+ const {
303646
+ payPk,
303647
+ identityPk,
303648
+ xprv,
303649
+ source: keySource
303650
+ } = keys ?? {
303651
+ payPk: undefined,
303652
+ identityPk: undefined,
303653
+ xprv: undefined,
303654
+ source: "external"
303655
+ };
302928
303656
  const hasPersistentPayKey = keySource === "env" || keySource === "file" || keySource === "encrypted";
302929
303657
  const hasPersistentIdentityKey = !!identityPk && (keySource === "file" || keySource === "encrypted");
302930
303658
  const hasXprv = !!xprv && (keySource === "file" || keySource === "env" || keySource === "encrypted");
302931
303659
  const effectiveConfig = { ...CONFIG };
303660
+ if (externalWallet) {
303661
+ effectiveConfig.loadBapTools = false;
303662
+ effectiveConfig.loadBsocialTools = false;
303663
+ effectiveConfig.loadMneeTools = false;
303664
+ effectiveConfig.loadA2bTools = false;
303665
+ logFunc2("External BRC-100 signer selected; local key loading and generation bypassed.");
303666
+ }
302932
303667
  logFunc2(`
302933
303668
  --- BSV MCP Server Configuration ---`);
302934
303669
  logFunc2(`Server Version: ${package_default.version}`);
@@ -302939,8 +303674,8 @@ Environment Variables:`);
302939
303674
  if (CONFIG.transportMode === "http") {
302940
303675
  logFunc2(` PORT: ${process.env.PORT || "Not Set (3000 default)"}`);
302941
303676
  }
302942
- logFunc2(` PRIVATE_KEY_WIF: ${process.env.PRIVATE_KEY_WIF ? "Set (using env key)" : "Not Set (using file/generating)"}`);
302943
- logFunc2(` IDENTITY_KEY_WIF: ${process.env.IDENTITY_KEY_WIF ? "Set (using env key)" : "Not Set (using file/generating)"}`);
303677
+ logFunc2(` PRIVATE_KEY_WIF: ${externalWallet ? "Unused (external signer)" : process.env.PRIVATE_KEY_WIF ? "Set (using env key)" : "Not Set (using file/generating)"}`);
303678
+ logFunc2(` IDENTITY_KEY_WIF: ${externalWallet ? "Unused (external signer)" : process.env.IDENTITY_KEY_WIF ? "Set (using env key)" : "Not Set (using file/generating)"}`);
302944
303679
  if (process.env.BSV_MCP_PASSPHRASE) {
302945
303680
  logFunc2(" BSV_MCP_PASSPHRASE: \x1B[31mDEPRECATED - Remove this!\x1B[0m");
302946
303681
  }
@@ -302996,11 +303731,11 @@ Effective Component Status:`);
302996
303731
  const a2bStatus = effectiveConfig.loadA2bTools ? "\x1B[32mEnabled\x1B[0m" : "\x1B[31mDisabled\x1B[0m";
302997
303732
  const bapStatus = effectiveConfig.loadBapTools ? "\x1B[32mEnabled\x1B[0m" : "\x1B[31mDisabled\x1B[0m";
302998
303733
  let payKeyNote = "";
302999
- if (!hasPersistentPayKey) {
303734
+ if (!externalWallet && !hasPersistentPayKey) {
303000
303735
  payKeyNote = " \x1B[33m(Using generated payPk)\x1B[0m";
303001
303736
  }
303002
303737
  let identityKeyNote = "";
303003
- if (!hasPersistentIdentityKey) {
303738
+ if (!externalWallet && !hasPersistentIdentityKey) {
303004
303739
  identityKeyNote = " \x1B[33m(Using generated identityPk)\x1B[0m";
303005
303740
  }
303006
303741
  logFunc2(` Wallet: ${walletStatus}${payKeyNote}`);
@@ -303017,12 +303752,20 @@ Effective Component Status:`);
303017
303752
  }
303018
303753
  logFunc2(`------------------------------------
303019
303754
  `);
303020
- let wallet6;
303755
+ let wallet7;
303021
303756
  let integratedWallet;
303022
303757
  let remoteCtx;
303023
303758
  let remoteServices;
303024
303759
  if (CONFIG.loadTools) {
303025
- if (CONFIG.useDroplitApi && CONFIG.droplitFaucetName) {
303760
+ if (externalWallet) {
303761
+ const chain = process.env.BSV_CHAIN ?? "main";
303762
+ if (chain !== "main" && chain !== "test")
303763
+ throw new Error("BSV_CHAIN must be main or test");
303764
+ const result2 = await initExternalWallet(externalWallet, chain);
303765
+ remoteCtx = result2.ctx;
303766
+ remoteServices = result2.services;
303767
+ logFunc2(`External BRC-100 signer ready. Identity: ${result2.identityKey}`);
303768
+ } else if (CONFIG.useDroplitApi && CONFIG.droplitFaucetName) {
303026
303769
  if (CONFIG.loadWalletTools) {
303027
303770
  try {
303028
303771
  integratedWallet = new IntegratedWallet({
@@ -303037,7 +303780,7 @@ Effective Component Status:`);
303037
303780
  logFunc2(`\x1B[32mINFO: Droplit API mode initialized successfully (Faucet: ${CONFIG.droplitFaucetName}).\x1B[0m`);
303038
303781
  logFunc2(`\x1B[33mNOTE: Using Droplit API at ${CONFIG.droplitApiUrl}\x1B[0m`);
303039
303782
  logFunc2("\x1B[33mNOTE: Local keys are ignored in Droplit API mode\x1B[0m");
303040
- wallet6 = integratedWallet.getLocalWallet();
303783
+ wallet7 = integratedWallet.getLocalWallet();
303041
303784
  effectiveConfig.loadMneeTools = false;
303042
303785
  effectiveConfig.loadBapTools = false;
303043
303786
  effectiveConfig.loadA2bTools = false;
@@ -303053,7 +303796,7 @@ Effective Component Status:`);
303053
303796
  } else if (payPk) {
303054
303797
  if (CONFIG.loadWalletTools) {
303055
303798
  try {
303056
- wallet6 = new Wallet2(payPk, identityPk);
303799
+ wallet7 = new Wallet2(payPk, identityPk);
303057
303800
  integratedWallet = new IntegratedWallet({
303058
303801
  paymentKey: payPk,
303059
303802
  identityKey: identityPk
@@ -303067,7 +303810,7 @@ Effective Component Status:`);
303067
303810
  }
303068
303811
  } catch (e2) {
303069
303812
  logFunc2(`\x1B[31mERROR: Failed to initialize custom wallet: ${e2 instanceof Error ? e2.message : String(e2)}. Wallet-dependent tools will be unavailable.\x1B[0m`);
303070
- wallet6 = undefined;
303813
+ wallet7 = undefined;
303071
303814
  integratedWallet = undefined;
303072
303815
  effectiveConfig.loadWalletTools = false;
303073
303816
  effectiveConfig.loadMneeTools = false;
@@ -303083,12 +303826,16 @@ Effective Component Status:`);
303083
303826
  logFunc2(`\x1B[33mWARN: Remote BRC-100 wallet initialization failed: ${e2 instanceof Error ? e2.message : String(e2)}. Falling back to local wallet only.\x1B[0m`);
303084
303827
  }
303085
303828
  }
303086
- if (effectiveConfig.loadMneeTools && !wallet6 && CONFIG.loadWalletTools) {
303829
+ if (effectiveConfig.loadMneeTools && !wallet7 && CONFIG.loadWalletTools) {
303087
303830
  logFunc2("\x1B[33mWARN: MNEE tools require a wallet but wallet initialization failed. MNEE tools disabled.\x1B[0m");
303088
303831
  effectiveConfig.loadMneeTools = false;
303089
303832
  }
303090
303833
  }
303091
303834
  }
303835
+ const droplitClient = sponsorConfig && remoteCtx ? new DroplitClient2({ ...sponsorConfig, wallet: remoteCtx.wallet }) : undefined;
303836
+ if (sponsorConfig && CONFIG.loadTools && !remoteCtx) {
303837
+ throw new Error("Configured Droplit sponsor requires an initialized BRC-100 wallet context");
303838
+ }
303092
303839
  const toolsConfig = CONFIG.loadTools ? {
303093
303840
  enableBsvTools: effectiveConfig.loadBsvTools,
303094
303841
  enableOrdinalsTools: effectiveConfig.loadOrdinalsTools,
@@ -303101,11 +303848,12 @@ Effective Component Status:`);
303101
303848
  identityPk,
303102
303849
  payPk,
303103
303850
  xprv,
303104
- wallet: wallet6,
303851
+ wallet: wallet7,
303105
303852
  integratedWallet,
303106
303853
  disableBroadcasting: effectiveConfig.disableBroadcasting,
303107
303854
  ctx: remoteCtx,
303108
- services: remoteServices
303855
+ services: remoteServices,
303856
+ droplitClient
303109
303857
  } : {
303110
303858
  enableBsvTools: false,
303111
303859
  enableOrdinalsTools: false,
@@ -303119,7 +303867,7 @@ Effective Component Status:`);
303119
303867
  };
303120
303868
  const serverFactoryOpts = {
303121
303869
  toolsConfig,
303122
- wallet: wallet6,
303870
+ wallet: wallet7,
303123
303871
  ctx: remoteCtx,
303124
303872
  loadPrompts: effectiveConfig.loadPrompts,
303125
303873
  loadResources: effectiveConfig.loadResources