bsv-mcp 0.3.0 → 0.3.1

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
@@ -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();
@@ -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.1",
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.1",
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;
@@ -297365,14 +297661,16 @@ function registerWalletTools(server, wallet5, config2) {
297365
297661
  registerRefreshUtxosTool(server, config2.ctx);
297366
297662
  registerWalletGetBalanceTool(server, config2.ctx);
297367
297663
  registerBrc100Tools(server, config2.ctx);
297368
- if (config2.enableA2bTools) {
297664
+ if (config2.enableA2bTools && wallet5 && config2.identityPk) {
297369
297665
  registerA2bPublishMcpTool(server, wallet5, config2.identityPk, {
297370
297666
  disableBroadcasting: config2.disableBroadcasting
297371
297667
  });
297372
297668
  }
297373
297669
  registerCreateOrdinalsTool(server, config2.ctx);
297374
- registerGatherCollectionInfoTool(server, wallet5);
297375
- registerMintCollectionTool(server, wallet5);
297670
+ if (wallet5) {
297671
+ registerGatherCollectionInfoTool(server, wallet5);
297672
+ registerMintCollectionTool(server, wallet5);
297673
+ }
297376
297674
  registerGetOrdinalsTool(server, config2.ctx);
297377
297675
  registerListTokensTool(server, config2.ctx);
297378
297676
  registerGetBsv21BalancesTool(server, config2.ctx);
@@ -297412,7 +297710,7 @@ function registerAllTools(server, config2 = {}) {
297412
297710
  if (enableA2bTools) {
297413
297711
  registerA2bDiscoverTool(server);
297414
297712
  }
297415
- if (enableBapTools) {
297713
+ if (enableBapTools && (!config2.ctx || config2.wallet)) {
297416
297714
  const bapConfig = {
297417
297715
  disableBroadcasting: config2.disableBroadcasting,
297418
297716
  identityPk: config2.identityPk,
@@ -297425,6 +297723,8 @@ function registerAllTools(server, config2 = {}) {
297425
297723
  registerBsocialTools(server, { wallet: config2.wallet });
297426
297724
  }
297427
297725
  if (enableWalletTools) {
297726
+ if (config2.droplitClient)
297727
+ registerDroplitTools(server, config2.droplitClient, config2.disableBroadcasting);
297428
297728
  if (config2.integratedWallet?.isDroplitMode) {
297429
297729
  const droplitClient = config2.integratedWallet.getDroplitClient();
297430
297730
  if (droplitClient) {
@@ -297434,7 +297734,7 @@ function registerAllTools(server, config2 = {}) {
297434
297734
  }
297435
297735
  console.error("Registered Droplit mode wallet tools");
297436
297736
  }
297437
- } else if (config2.wallet) {
297737
+ } else if (config2.wallet || config2.ctx) {
297438
297738
  const walletToolOptions = {
297439
297739
  disableBroadcasting: config2.disableBroadcasting === true,
297440
297740
  enableA2bTools,
@@ -297444,7 +297744,7 @@ function registerAllTools(server, config2 = {}) {
297444
297744
  registerWalletTools(server, config2.wallet, walletToolOptions);
297445
297745
  }
297446
297746
  }
297447
- if (enableMneeTools) {
297747
+ if (enableMneeTools && (!config2.ctx || config2.wallet)) {
297448
297748
  registerMneeTools(server);
297449
297749
  }
297450
297750
  }
@@ -297452,62 +297752,6 @@ function registerAllTools(server, config2 = {}) {
297452
297752
  // tools/wallet/integratedWallet.ts
297453
297753
  init_mod2();
297454
297754
 
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
297755
  // tools/wallet/wallet.ts
297512
297756
  init_mod2();
297513
297757
  class Wallet {
@@ -297706,7 +297950,7 @@ class IntegratedWallet {
297706
297950
  }
297707
297951
  async sendToAddress(address, satoshis) {
297708
297952
  if (this.droplitClient) {
297709
- const response = await this.droplitClient.tap(address);
297953
+ const response = await this.droplitClient.tap(address, satoshis);
297710
297954
  return { txid: response.txid };
297711
297955
  }
297712
297956
  if (this.localWallet) {
@@ -297912,6 +298156,245 @@ class Wallet2 {
297912
298156
  }
297913
298157
  }
297914
298158
 
298159
+ // utils/droplit.ts
298160
+ init_mod2();
298161
+ function readDroplitSponsorConfig(env = process.env) {
298162
+ const apiUrl = env.DROPLIT_API_URL;
298163
+ const faucetName = env.DROPLIT_FAUCET_NAME;
298164
+ if (apiUrl === undefined && faucetName === undefined)
298165
+ return;
298166
+ if (!apiUrl?.trim() || !faucetName?.trim()) {
298167
+ throw new Error("DROPLIT_API_URL and DROPLIT_FAUCET_NAME must both be explicitly configured");
298168
+ }
298169
+ return { apiUrl, faucetName };
298170
+ }
298171
+
298172
+ class DroplitError2 extends Error {
298173
+ code;
298174
+ status;
298175
+ details;
298176
+ constructor(code, message, status, details = {}) {
298177
+ super(message);
298178
+ this.code = code;
298179
+ this.status = status;
298180
+ this.details = details;
298181
+ }
298182
+ }
298183
+ function quotaDetails2(body) {
298184
+ if (!body || typeof body !== "object")
298185
+ return {};
298186
+ const value2 = body;
298187
+ return {
298188
+ ...typeof value2.remaining === "number" || value2.remaining === null ? { remaining: value2.remaining } : {},
298189
+ ...typeof value2.resets_at === "string" ? { resets_at: value2.resets_at } : {}
298190
+ };
298191
+ }
298192
+ function httpFailure2(status, details = {}) {
298193
+ const known = {
298194
+ 401: [
298195
+ "authentication_required",
298196
+ "Authenticate with the configured wallet before retrying."
298197
+ ],
298198
+ 402: [
298199
+ "approval_required",
298200
+ "Payment is required. No automatic payment was made; review payment with the wallet owner."
298201
+ ],
298202
+ 403: [
298203
+ "approval_required",
298204
+ "The sponsor has not authorized this action. Ask its owner to approve your wallet."
298205
+ ],
298206
+ 429: [
298207
+ "quota_exceeded",
298208
+ "Sponsor quota exhausted. Wait until resets_at or ask the owner to adjust your quota."
298209
+ ]
298210
+ };
298211
+ const [code, message] = known[status] ?? [
298212
+ "http_error",
298213
+ "Droplit rejected the request. Check sponsor access and transaction history before retrying."
298214
+ ];
298215
+ return new DroplitError2(code, message, status, details);
298216
+ }
298217
+
298218
+ class DroplitClient2 {
298219
+ config;
298220
+ authFetch;
298221
+ wallet;
298222
+ constructor(config2) {
298223
+ this.config = config2;
298224
+ if (config2.wallet && config2.authKey)
298225
+ throw new Error("Configure Droplit wallet or authKey, not both");
298226
+ const url3 = new URL(config2.apiUrl);
298227
+ const loopback = url3.hostname === "localhost" || url3.hostname === "127.0.0.1" || url3.hostname === "[::1]";
298228
+ if (url3.username || url3.password || url3.search || url3.hash || !(url3.protocol === "https:" || url3.protocol === "http:" && loopback)) {
298229
+ throw new Error("Droplit API requires HTTPS or HTTP loopback without credentials, query or fragment");
298230
+ }
298231
+ if (!config2.faucetName.trim())
298232
+ throw new Error("Droplit faucet name is required");
298233
+ this.wallet = config2.wallet ?? (config2.authKey ? new ProtoWallet_default(config2.authKey) : undefined);
298234
+ if (this.wallet) {
298235
+ const authenticationWallet = new Proxy(this.wallet, {
298236
+ get(target, property) {
298237
+ if (property === "createAction" || property === "signAction") {
298238
+ return async () => {
298239
+ throw httpFailure2(402);
298240
+ };
298241
+ }
298242
+ const value2 = Reflect.get(target, property, target);
298243
+ return typeof value2 === "function" ? value2.bind(target) : value2;
298244
+ }
298245
+ });
298246
+ this.authFetch = new AuthFetch(authenticationWallet);
298247
+ }
298248
+ }
298249
+ getConfig() {
298250
+ return { apiUrl: this.config.apiUrl, faucetName: this.config.faucetName };
298251
+ }
298252
+ async getIdentityKey() {
298253
+ if (!this.wallet)
298254
+ throw new DroplitError2("authentication_required", "Configure a Droplit wallet identity.");
298255
+ const { publicKey } = await this.wallet.getPublicKey({ identityKey: true });
298256
+ return publicKey;
298257
+ }
298258
+ get faucetPath() {
298259
+ return `/faucet/${encodeURIComponent(this.config.faucetName)}`;
298260
+ }
298261
+ get base() {
298262
+ return this.config.apiUrl.replace(/\/+$/, "");
298263
+ }
298264
+ async authed(path5, init) {
298265
+ if (!this.authFetch) {
298266
+ throw new Error(`${path5} requires authentication and no Droplit auth key is configured.`);
298267
+ }
298268
+ try {
298269
+ return await this.authFetch.fetch(`${this.base}${path5}`, {
298270
+ method: init.method,
298271
+ headers: { "Content-Type": "application/json" },
298272
+ ...init.body === undefined ? {} : { body: JSON.stringify(init.body) },
298273
+ paymentRetryAttempts: 0,
298274
+ ...init.method === "GET" || init.method === "HEAD" ? {} : { retryCounter: 1 }
298275
+ });
298276
+ } catch (error53) {
298277
+ if (error53 instanceof DroplitError2)
298278
+ throw error53;
298279
+ const details = error53?.details;
298280
+ const status = details?.status;
298281
+ if (typeof status === "number" && [401, 402, 403, 429].includes(status)) {
298282
+ let body;
298283
+ if (status === 429 && typeof details?.bodyPreview === "string") {
298284
+ try {
298285
+ body = JSON.parse(details.bodyPreview);
298286
+ } catch {}
298287
+ }
298288
+ throw httpFailure2(status, status === 429 ? quotaDetails2(body) : {});
298289
+ }
298290
+ const write = init.method !== "GET" && init.method !== "HEAD";
298291
+ 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.");
298292
+ }
298293
+ }
298294
+ async parse(response, _attempted) {
298295
+ if (response.ok) {
298296
+ try {
298297
+ return await response.json();
298298
+ } catch {
298299
+ throw new DroplitError2("unknown_outcome", "Response could not be read. Reconcile transaction/history before retrying.", response.status);
298300
+ }
298301
+ }
298302
+ let body = {};
298303
+ try {
298304
+ body = await response.json();
298305
+ } catch {}
298306
+ throw httpFailure2(response.status, response.status === 429 ? quotaDetails2(body) : {});
298307
+ }
298308
+ async getAccess() {
298309
+ const response = await this.authed(`${this.faucetPath}/access`, {
298310
+ method: "GET"
298311
+ });
298312
+ const access = await this.parse(response, "Reading sponsor access");
298313
+ const identity6 = await this.getIdentityKey();
298314
+ 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)) {
298315
+ throw new DroplitError2("invalid_response", "Sponsor access response did not match the authenticated wallet.");
298316
+ }
298317
+ access.approval_path = `/droplit/${encodeURIComponent(this.config.faucetName)}?tab=api&request_key=${encodeURIComponent(identity6)}`;
298318
+ return access;
298319
+ }
298320
+ async fund(rawtx) {
298321
+ const response = await this.authed(`${this.faucetPath}/fund`, {
298322
+ method: "POST",
298323
+ body: { rawtx }
298324
+ });
298325
+ return this.parse(response, "Funding transaction");
298326
+ }
298327
+ async getFaucetStatus() {
298328
+ const response = await fetch(`${this.base}${this.faucetPath}/status`);
298329
+ return this.parse(response, "Reading the faucet status");
298330
+ }
298331
+ async tap(recipientAddress, satoshis) {
298332
+ if (satoshis !== undefined && (!Number.isSafeInteger(satoshis) || satoshis <= 0))
298333
+ throw new Error("satoshis must be a positive safe integer");
298334
+ const response = await this.authed(`${this.faucetPath}/tap`, {
298335
+ method: "POST",
298336
+ body: {
298337
+ recipient_address: recipientAddress,
298338
+ ...satoshis === undefined ? {} : { satoshis }
298339
+ }
298340
+ });
298341
+ return this.parse(response, "Tapping the faucet");
298342
+ }
298343
+ async push(data, encoding = "hex") {
298344
+ const response = await this.authed(`${this.faucetPath}/push`, {
298345
+ method: "POST",
298346
+ body: { data, encoding }
298347
+ });
298348
+ return this.parse(response, "Pushing data");
298349
+ }
298350
+ async authenticatedFetch(path5, init) {
298351
+ return this.authed(path5, init);
298352
+ }
298353
+ }
298354
+
298355
+ // utils/externalWalletConfig.ts
298356
+ function readExternalWalletConfig(env = process.env) {
298357
+ const raw = env.BRC100_WALLET_URL;
298358
+ if (raw === undefined) {
298359
+ if (env.BRC100_WALLET_ORIGINATOR !== undefined) {
298360
+ throw new Error("BRC100_WALLET_ORIGINATOR requires BRC100_WALLET_URL");
298361
+ }
298362
+ return;
298363
+ }
298364
+ for (const name of ["PRIVATE_KEY_WIF", "IDENTITY_KEY_WIF"]) {
298365
+ if (env[name] !== undefined) {
298366
+ throw new Error(`BRC100_WALLET_URL conflicts with ${name}; select one wallet identity`);
298367
+ }
298368
+ }
298369
+ if (env.USE_DROPLIT_API === "true") {
298370
+ throw new Error("BRC100_WALLET_URL conflicts with USE_DROPLIT_API");
298371
+ }
298372
+ let url3;
298373
+ try {
298374
+ url3 = new URL(raw);
298375
+ } catch {
298376
+ throw new Error("BRC100_WALLET_URL must be a valid signer RPC URL");
298377
+ }
298378
+ const loopback = url3.hostname === "localhost" || url3.hostname === "[::1]" || /^127\.(?:\d{1,3}\.){2}\d{1,3}$/.test(url3.hostname);
298379
+ if (!raw || raw !== raw.trim() || /[\\\s@?]/.test(raw) || url3.username || url3.password || raw.includes("#") || url3.search || !(url3.protocol === "https:" || url3.protocol === "http:" && loopback)) {
298380
+ throw new Error("BRC100_WALLET_URL requires HTTPS or HTTP loopback, without credentials, queries or fragments");
298381
+ }
298382
+ const originator = env.BRC100_WALLET_ORIGINATOR ?? "bsv-mcp.local";
298383
+ let origin;
298384
+ try {
298385
+ origin = new URL(originator.includes("://") ? originator : `http://${originator}`);
298386
+ } catch {
298387
+ throw new Error("BRC100_WALLET_ORIGINATOR must be a domain or HTTP(S) origin");
298388
+ }
298389
+ 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") {
298390
+ throw new Error("BRC100_WALLET_ORIGINATOR must be a non-admin domain or HTTP(S) origin without credentials, path, query or fragment");
298391
+ }
298392
+ return { url: url3.toString().replace(/\/$/, ""), originator };
298393
+ }
298394
+ async function initializeKeysForWalletMode(external4, loadLocalKeys) {
298395
+ return external4 ? undefined : loadLocalKeys();
298396
+ }
298397
+
297915
298398
  // node_modules/jose/dist/webapi/lib/buffer_utils.js
297916
298399
  var encoder = new TextEncoder;
297917
298400
  var decoder = new TextDecoder;
@@ -301997,9 +302480,19 @@ async function buildLocalStorage(config2, baseOptions) {
301997
302480
  init_storage_pg();
301998
302481
  var import_wallet_toolbox2 = __toESM(require_src6(), 1);
301999
302482
 
302483
+ // node_modules/@1sat/wallet-remote/dist/index.js
302484
+ init_dist4();
302485
+
302486
+ // node_modules/@1sat/wallet-remote/dist/createRemoteWallet.js
302487
+ init_dist4();
302488
+ var import_index_client2 = __toESM(require_index_client5(), 1);
302489
+
302490
+ // node_modules/@1sat/wallet-remote/dist/index.js
302491
+ var import_index_client3 = __toESM(require_index_client5(), 1);
302492
+
302000
302493
  // utils/walletInit.ts
302001
302494
  init_mod2();
302002
- var import_index_client2 = __toESM(require_index_client5(), 1);
302495
+ var import_index_client4 = __toESM(require_index_client5(), 1);
302003
302496
 
302004
302497
  // utils/spendingApproval.ts
302005
302498
  var serverInstance2 = null;
@@ -302096,7 +302589,7 @@ async function initWallet(privateKeyWif, chain = "main") {
302096
302589
  activeRemote: process.env.REMOTE_STORAGE_URL ?? DEFAULT_REMOTE_STORAGE_URL,
302097
302590
  storageIdentityKey: "bsv-mcp"
302098
302591
  });
302099
- const wpm = new import_index_client2.WalletPermissionsManager(result2.wallet, ADMIN_ORIGINATOR, {
302592
+ const wpm = new import_index_client4.WalletPermissionsManager(result2.wallet, ADMIN_ORIGINATOR, {
302100
302593
  seekProtocolPermissionsForSigning: false,
302101
302594
  seekProtocolPermissionsForEncrypting: false,
302102
302595
  seekProtocolPermissionsForHMAC: false,
@@ -302138,7 +302631,7 @@ async function initWallet(privateKeyWif, chain = "main") {
302138
302631
  }).catch((err) => {
302139
302632
  console.error("[wallet] message box sync failed:", err);
302140
302633
  });
302141
- activeResult = { ...result2, ctx, depositAddress };
302634
+ activeResult = result2;
302142
302635
  return {
302143
302636
  wallet: wpm,
302144
302637
  services: result2.services,
@@ -302147,6 +302640,36 @@ async function initWallet(privateKeyWif, chain = "main") {
302147
302640
  destroy: result2.destroy
302148
302641
  };
302149
302642
  }
302643
+ async function initExternalWallet(config2, chain = "main") {
302644
+ const httpClient = (input, init) => fetch(input, {
302645
+ ...init,
302646
+ redirect: "error",
302647
+ signal: init?.signal ? AbortSignal.any([init.signal, AbortSignal.timeout(1e4)]) : AbortSignal.timeout(1e4)
302648
+ });
302649
+ const wallet7 = new HTTPWalletJSON(config2.originator, config2.url, httpClient);
302650
+ let identityKey;
302651
+ try {
302652
+ const identity6 = await wallet7.getPublicKey({ identityKey: true });
302653
+ if (!/^(02|03)[0-9a-f]{64}$/i.test(identity6.publicKey)) {
302654
+ throw new Error("Signer returned an invalid compressed identity public key");
302655
+ }
302656
+ PublicKey.fromString(identity6.publicKey);
302657
+ identityKey = identity6.publicKey;
302658
+ } catch (error53) {
302659
+ 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 });
302660
+ }
302661
+ const services2 = new OneSatServices(chain, process.env.ONESAT_API_URL);
302662
+ const dataDir = join2(homedir(), ".bsv-mcp");
302663
+ const ctx = createContext(wallet7, {
302664
+ services: services2,
302665
+ chain,
302666
+ dataDir,
302667
+ log: (entry) => writeAuditLog(dataDir, entry)
302668
+ });
302669
+ const destroy = async () => {};
302670
+ activeResult = { destroy };
302671
+ return { wallet: wallet7, ctx, services: services2, identityKey, destroy };
302672
+ }
302150
302673
  async function destroyWallet() {
302151
302674
  if (activeResult) {
302152
302675
  await activeResult.destroy();
@@ -302283,7 +302806,7 @@ async function initializeKeys() {
302283
302806
  }
302284
302807
  var APP_RESOURCE_URI = "ui://bsv-mcp/app.html";
302285
302808
  var __appDirname = dirname2(fileURLToPath(import.meta.url));
302286
- function registerMcpAppTools(server2, wallet6, ctx) {
302809
+ function registerMcpAppTools(server2, wallet7, ctx) {
302287
302810
  K3(server2, "bsv_dashboard", {
302288
302811
  title: "BSV Dashboard",
302289
302812
  description: "Interactive BSV dashboard with Explorer, Wallet, and Ordinals tabs. Use this for any BSV-related query that benefits from visual display.",
@@ -302433,7 +302956,7 @@ function registerMcpAppTools(server2, wallet6, ctx) {
302433
302956
  ui: { resourceUri: APP_RESOURCE_URI, visibility: ["app"] }
302434
302957
  }
302435
302958
  }, async () => {
302436
- if (!ctx && !wallet6) {
302959
+ if (!ctx && !wallet7) {
302437
302960
  return {
302438
302961
  content: [
302439
302962
  {
@@ -302465,9 +302988,9 @@ function registerMcpAppTools(server2, wallet6, ctx) {
302465
302988
  const [txid = "", voutStr = "0"] = (o6.outpoint ?? "").split(".");
302466
302989
  return { txid, vout: Number(voutStr), satoshis: o6.satoshis };
302467
302990
  });
302468
- } else if (wallet6) {
302469
- address = wallet6.getAddress();
302470
- const { paymentUtxos } = await wallet6.getUtxos();
302991
+ } else if (wallet7) {
302992
+ address = wallet7.getAddress();
302993
+ const { paymentUtxos } = await wallet7.getUtxos();
302471
302994
  for (const utxo of paymentUtxos) {
302472
302995
  totalSatoshis += utxo.satoshis || 0;
302473
302996
  }
@@ -302892,6 +303415,8 @@ Options:
302892
303415
  Environment Variables:
302893
303416
  TRANSPORT Transport mode: 'stdio' or 'http' (default: http)
302894
303417
  PORT HTTP server port (default: 3000)
303418
+ BRC100_WALLET_URL Existing SDK HTTPWalletJSON signer RPC URL
303419
+ BRC100_WALLET_ORIGINATOR Signer permission origin (default: bsv-mcp.local)
302895
303420
  PRIVATE_KEY_WIF Payment private key in WIF format
302896
303421
  DISABLE_TOOLS Disable all tools (default: false)
302897
303422
  DISABLE_WALLET_TOOLS Disable wallet tools (default: false)
@@ -302915,7 +303440,7 @@ Tool Categories:
302915
303440
 
302916
303441
  Authentication:
302917
303442
  - Most tools work without authentication
302918
- - Wallet operations require PRIVATE_KEY_WIF or generated keys
303443
+ - Wallet operations use BRC100_WALLET_URL, PRIVATE_KEY_WIF or generated keys
302919
303444
  - BAP/A2B tools require identity keys (generated via bap_generate tool)
302920
303445
  `);
302921
303446
  process.exit(0);
@@ -302924,11 +303449,31 @@ Authentication:
302924
303449
  console.log(`${package_default.name} v${package_default.version}`);
302925
303450
  process.exit(0);
302926
303451
  }
302927
- const { payPk, identityPk, xprv, source: keySource } = await initializeKeys();
303452
+ const externalWallet = readExternalWalletConfig();
303453
+ const sponsorConfig = CONFIG.useDroplitApi ? undefined : readDroplitSponsorConfig();
303454
+ const keys = await initializeKeysForWalletMode(externalWallet, initializeKeys);
303455
+ const {
303456
+ payPk,
303457
+ identityPk,
303458
+ xprv,
303459
+ source: keySource
303460
+ } = keys ?? {
303461
+ payPk: undefined,
303462
+ identityPk: undefined,
303463
+ xprv: undefined,
303464
+ source: "external"
303465
+ };
302928
303466
  const hasPersistentPayKey = keySource === "env" || keySource === "file" || keySource === "encrypted";
302929
303467
  const hasPersistentIdentityKey = !!identityPk && (keySource === "file" || keySource === "encrypted");
302930
303468
  const hasXprv = !!xprv && (keySource === "file" || keySource === "env" || keySource === "encrypted");
302931
303469
  const effectiveConfig = { ...CONFIG };
303470
+ if (externalWallet) {
303471
+ effectiveConfig.loadBapTools = false;
303472
+ effectiveConfig.loadBsocialTools = false;
303473
+ effectiveConfig.loadMneeTools = false;
303474
+ effectiveConfig.loadA2bTools = false;
303475
+ logFunc2("External BRC-100 signer selected; local key loading and generation bypassed.");
303476
+ }
302932
303477
  logFunc2(`
302933
303478
  --- BSV MCP Server Configuration ---`);
302934
303479
  logFunc2(`Server Version: ${package_default.version}`);
@@ -302939,8 +303484,8 @@ Environment Variables:`);
302939
303484
  if (CONFIG.transportMode === "http") {
302940
303485
  logFunc2(` PORT: ${process.env.PORT || "Not Set (3000 default)"}`);
302941
303486
  }
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)"}`);
303487
+ logFunc2(` PRIVATE_KEY_WIF: ${externalWallet ? "Unused (external signer)" : process.env.PRIVATE_KEY_WIF ? "Set (using env key)" : "Not Set (using file/generating)"}`);
303488
+ logFunc2(` IDENTITY_KEY_WIF: ${externalWallet ? "Unused (external signer)" : process.env.IDENTITY_KEY_WIF ? "Set (using env key)" : "Not Set (using file/generating)"}`);
302944
303489
  if (process.env.BSV_MCP_PASSPHRASE) {
302945
303490
  logFunc2(" BSV_MCP_PASSPHRASE: \x1B[31mDEPRECATED - Remove this!\x1B[0m");
302946
303491
  }
@@ -302996,11 +303541,11 @@ Effective Component Status:`);
302996
303541
  const a2bStatus = effectiveConfig.loadA2bTools ? "\x1B[32mEnabled\x1B[0m" : "\x1B[31mDisabled\x1B[0m";
302997
303542
  const bapStatus = effectiveConfig.loadBapTools ? "\x1B[32mEnabled\x1B[0m" : "\x1B[31mDisabled\x1B[0m";
302998
303543
  let payKeyNote = "";
302999
- if (!hasPersistentPayKey) {
303544
+ if (!externalWallet && !hasPersistentPayKey) {
303000
303545
  payKeyNote = " \x1B[33m(Using generated payPk)\x1B[0m";
303001
303546
  }
303002
303547
  let identityKeyNote = "";
303003
- if (!hasPersistentIdentityKey) {
303548
+ if (!externalWallet && !hasPersistentIdentityKey) {
303004
303549
  identityKeyNote = " \x1B[33m(Using generated identityPk)\x1B[0m";
303005
303550
  }
303006
303551
  logFunc2(` Wallet: ${walletStatus}${payKeyNote}`);
@@ -303017,12 +303562,20 @@ Effective Component Status:`);
303017
303562
  }
303018
303563
  logFunc2(`------------------------------------
303019
303564
  `);
303020
- let wallet6;
303565
+ let wallet7;
303021
303566
  let integratedWallet;
303022
303567
  let remoteCtx;
303023
303568
  let remoteServices;
303024
303569
  if (CONFIG.loadTools) {
303025
- if (CONFIG.useDroplitApi && CONFIG.droplitFaucetName) {
303570
+ if (externalWallet) {
303571
+ const chain = process.env.BSV_CHAIN ?? "main";
303572
+ if (chain !== "main" && chain !== "test")
303573
+ throw new Error("BSV_CHAIN must be main or test");
303574
+ const result2 = await initExternalWallet(externalWallet, chain);
303575
+ remoteCtx = result2.ctx;
303576
+ remoteServices = result2.services;
303577
+ logFunc2(`External BRC-100 signer ready. Identity: ${result2.identityKey}`);
303578
+ } else if (CONFIG.useDroplitApi && CONFIG.droplitFaucetName) {
303026
303579
  if (CONFIG.loadWalletTools) {
303027
303580
  try {
303028
303581
  integratedWallet = new IntegratedWallet({
@@ -303037,7 +303590,7 @@ Effective Component Status:`);
303037
303590
  logFunc2(`\x1B[32mINFO: Droplit API mode initialized successfully (Faucet: ${CONFIG.droplitFaucetName}).\x1B[0m`);
303038
303591
  logFunc2(`\x1B[33mNOTE: Using Droplit API at ${CONFIG.droplitApiUrl}\x1B[0m`);
303039
303592
  logFunc2("\x1B[33mNOTE: Local keys are ignored in Droplit API mode\x1B[0m");
303040
- wallet6 = integratedWallet.getLocalWallet();
303593
+ wallet7 = integratedWallet.getLocalWallet();
303041
303594
  effectiveConfig.loadMneeTools = false;
303042
303595
  effectiveConfig.loadBapTools = false;
303043
303596
  effectiveConfig.loadA2bTools = false;
@@ -303053,7 +303606,7 @@ Effective Component Status:`);
303053
303606
  } else if (payPk) {
303054
303607
  if (CONFIG.loadWalletTools) {
303055
303608
  try {
303056
- wallet6 = new Wallet2(payPk, identityPk);
303609
+ wallet7 = new Wallet2(payPk, identityPk);
303057
303610
  integratedWallet = new IntegratedWallet({
303058
303611
  paymentKey: payPk,
303059
303612
  identityKey: identityPk
@@ -303067,7 +303620,7 @@ Effective Component Status:`);
303067
303620
  }
303068
303621
  } catch (e2) {
303069
303622
  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;
303623
+ wallet7 = undefined;
303071
303624
  integratedWallet = undefined;
303072
303625
  effectiveConfig.loadWalletTools = false;
303073
303626
  effectiveConfig.loadMneeTools = false;
@@ -303083,12 +303636,16 @@ Effective Component Status:`);
303083
303636
  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
303637
  }
303085
303638
  }
303086
- if (effectiveConfig.loadMneeTools && !wallet6 && CONFIG.loadWalletTools) {
303639
+ if (effectiveConfig.loadMneeTools && !wallet7 && CONFIG.loadWalletTools) {
303087
303640
  logFunc2("\x1B[33mWARN: MNEE tools require a wallet but wallet initialization failed. MNEE tools disabled.\x1B[0m");
303088
303641
  effectiveConfig.loadMneeTools = false;
303089
303642
  }
303090
303643
  }
303091
303644
  }
303645
+ const droplitClient = sponsorConfig && remoteCtx ? new DroplitClient2({ ...sponsorConfig, wallet: remoteCtx.wallet }) : undefined;
303646
+ if (sponsorConfig && CONFIG.loadTools && !remoteCtx) {
303647
+ throw new Error("Configured Droplit sponsor requires an initialized BRC-100 wallet context");
303648
+ }
303092
303649
  const toolsConfig = CONFIG.loadTools ? {
303093
303650
  enableBsvTools: effectiveConfig.loadBsvTools,
303094
303651
  enableOrdinalsTools: effectiveConfig.loadOrdinalsTools,
@@ -303101,11 +303658,12 @@ Effective Component Status:`);
303101
303658
  identityPk,
303102
303659
  payPk,
303103
303660
  xprv,
303104
- wallet: wallet6,
303661
+ wallet: wallet7,
303105
303662
  integratedWallet,
303106
303663
  disableBroadcasting: effectiveConfig.disableBroadcasting,
303107
303664
  ctx: remoteCtx,
303108
- services: remoteServices
303665
+ services: remoteServices,
303666
+ droplitClient
303109
303667
  } : {
303110
303668
  enableBsvTools: false,
303111
303669
  enableOrdinalsTools: false,
@@ -303119,7 +303677,7 @@ Effective Component Status:`);
303119
303677
  };
303120
303678
  const serverFactoryOpts = {
303121
303679
  toolsConfig,
303122
- wallet: wallet6,
303680
+ wallet: wallet7,
303123
303681
  ctx: remoteCtx,
303124
303682
  loadPrompts: effectiveConfig.loadPrompts,
303125
303683
  loadResources: effectiveConfig.loadResources