bsv-mcp 0.3.3 → 0.3.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,12 @@
1
1
  # BSV MCP Server Changelog
2
2
 
3
+ ## [0.3.4] - 2026-09-06
4
+
5
+ ### Added
6
+ - Discover owner-listed Droplit sponsors without a wallet or preselected faucet.
7
+ - Validate public responses and pagination; discovery never authorizes access or payment.
8
+
9
+
3
10
  ## [0.3.3] - 2026-09-06
4
11
 
5
12
  ### Fixed
package/README.md CHANGED
@@ -888,12 +888,22 @@ DROPLIT_FAUCET_NAME=your-sponsor-slug
888
888
  DROPLIT_SITE_URL=https://droplit.dev
889
889
  ```
890
890
 
891
- Set both sponsor values explicitly. The URL includes the server's configured
891
+ Set both sponsor values explicitly for wallet operations. The URL includes the server's configured
892
892
  base path. Leave `USE_DROPLIT_API` unset: these sponsor tools appear alongside
893
893
  normal wallet tools and use the same connected signer identity. Existing local
894
894
  BRC-100 wallet contexts also support this sponsor pair. `1sat serve` exposes a
895
895
  wallet stack service; it is not itself a BRC-100 signer RPC endpoint.
896
896
 
897
+ For public sponsor discovery, set only `DROPLIT_API_URL` and call
898
+ `droplit_discover` with optional `limit` (1–50, default 20) and `after`
899
+ (the previous `next_cursor`). This unsigned read needs no selected sponsor or
900
+ wallet and returns only names, slugs, and `approval_required: true`. It neither
901
+ creates a wallet nor grants sponsor access. Normal MCP startup retains its
902
+ existing wallet initialization behavior; this tool itself makes no wallet calls.
903
+ An empty catalog means no owners have opted in. Select a listed slug, configure
904
+ `DROPLIT_FAUCET_NAME`, and use the existing wallet access/owner approval flow.
905
+ Listing does not promise funding or quota availability.
906
+
897
907
  Call `droplit_getAccess` to inspect your own authorization and quotas. An
898
908
  `approval_required` response includes a link to `DROPLIT_SITE_URL` (default
899
909
  `https://droplit.dev`) for manual
package/dist/index.js CHANGED
@@ -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.3",
256045
+ version: "0.3.4",
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",
@@ -284877,6 +284877,14 @@ function registerUtilsTools(server) {
284877
284877
 
284878
284878
  // utils/droplit.ts
284879
284879
  init_mod2();
284880
+ function droplitApiBaseUrl(apiUrl) {
284881
+ const url3 = new URL(apiUrl);
284882
+ const loopback = url3.hostname === "localhost" || url3.hostname === "127.0.0.1" || url3.hostname === "[::1]";
284883
+ if (url3.username || url3.password || url3.search || url3.hash || !(url3.protocol === "https:" || url3.protocol === "http:" && loopback)) {
284884
+ throw new Error("Droplit API requires HTTPS or HTTP loopback without credentials, query or fragment");
284885
+ }
284886
+ return url3.href.replace(/\/$/, "");
284887
+ }
284880
284888
  function approvalSiteOrigin(value2 = "https://droplit.dev") {
284881
284889
  const url3 = new URL(value2);
284882
284890
  const loopback = ["localhost", "127.0.0.1", "[::1]"].includes(url3.hostname);
@@ -284942,11 +284950,7 @@ class DroplitClient {
284942
284950
  this.siteOrigin = approvalSiteOrigin(config2.siteUrl);
284943
284951
  if (config2.wallet && config2.authKey)
284944
284952
  throw new Error("Configure Droplit wallet or authKey, not both");
284945
- const url3 = new URL(config2.apiUrl);
284946
- const loopback = url3.hostname === "localhost" || url3.hostname === "127.0.0.1" || url3.hostname === "[::1]";
284947
- if (url3.username || url3.password || url3.search || url3.hash || !(url3.protocol === "https:" || url3.protocol === "http:" && loopback)) {
284948
- throw new Error("Droplit API requires HTTPS or HTTP loopback without credentials, query or fragment");
284949
- }
284953
+ droplitApiBaseUrl(config2.apiUrl);
284950
284954
  if (!config2.faucetName.trim())
284951
284955
  throw new Error("Droplit faucet name is required");
284952
284956
  this.wallet = config2.wallet ?? (config2.authKey ? new ProtoWallet_default(config2.authKey) : undefined);
@@ -285071,6 +285075,74 @@ class DroplitClient {
285071
285075
  return this.authed(path3, init);
285072
285076
  }
285073
285077
  }
285078
+ var publicSponsorsSchema = exports_external.object({
285079
+ sponsors: exports_external.array(exports_external.object({
285080
+ name: exports_external.string(),
285081
+ slug: exports_external.string().min(1),
285082
+ approval_required: exports_external.literal(true)
285083
+ })).max(50),
285084
+ next_cursor: exports_external.string().min(1).nullable()
285085
+ });
285086
+ async function discoverDroplitSponsors(apiUrl, options = {}) {
285087
+ const limit = options.limit ?? 20;
285088
+ if (!Number.isInteger(limit) || limit < 1 || limit > 50)
285089
+ throw new Error("limit must be an integer from 1 to 50");
285090
+ const url3 = new URL(`${droplitApiBaseUrl(apiUrl)}/sponsors`);
285091
+ url3.searchParams.set("limit", String(limit));
285092
+ if (options.after !== undefined)
285093
+ url3.searchParams.set("after", options.after);
285094
+ let response;
285095
+ try {
285096
+ response = await fetch(url3, {
285097
+ method: "GET",
285098
+ redirect: "error",
285099
+ credentials: "omit",
285100
+ signal: AbortSignal.timeout(15000)
285101
+ });
285102
+ } catch {
285103
+ throw new DroplitError("request_failed", "Could not read public sponsors. Check the configured API origin.");
285104
+ }
285105
+ if (!response.ok)
285106
+ throw new DroplitError("request_failed", "Could not read public sponsors.", response.status);
285107
+ try {
285108
+ return publicSponsorsSchema.parse(await response.json());
285109
+ } catch {
285110
+ throw new DroplitError("invalid_response", "Public sponsor response was invalid.");
285111
+ }
285112
+ }
285113
+
285114
+ // tools/wallet/droplitDiscovery.ts
285115
+ function registerDroplitDiscoveryTool(server, apiUrl) {
285116
+ const baseUrl = droplitApiBaseUrl(apiUrl);
285117
+ server.registerTool("droplit_discover", {
285118
+ description: "List publicly opted-in sponsors. No wallet or sponsor selection is required. Listing grants no access or funding; choose a slug, then request the sponsor owner's approval separately.",
285119
+ inputSchema: {
285120
+ limit: exports_external.number().int().min(1).max(50).optional(),
285121
+ after: exports_external.string().optional()
285122
+ },
285123
+ annotations: {
285124
+ readOnlyHint: true,
285125
+ destructiveHint: false,
285126
+ idempotentHint: true,
285127
+ openWorldHint: true
285128
+ }
285129
+ }, async (options) => {
285130
+ try {
285131
+ const data = await discoverDroplitSponsors(baseUrl, options);
285132
+ return { ...createSuccessResponse(data), structuredContent: data };
285133
+ } catch (error52) {
285134
+ const data = {
285135
+ error: error52 instanceof DroplitError ? error52.code : "invalid_request",
285136
+ message: error52 instanceof DroplitError ? error52.message : "Invalid sponsor discovery request."
285137
+ };
285138
+ return {
285139
+ ...createSuccessResponse(data),
285140
+ structuredContent: data,
285141
+ isError: true
285142
+ };
285143
+ }
285144
+ });
285145
+ }
285074
285146
 
285075
285147
  // tools/wallet/droplit.ts
285076
285148
  function errorResult(error52) {
@@ -295175,7 +295247,7 @@ var package_default2 = {
295175
295247
  name: "bsv-mcp",
295176
295248
  module: "dist/index.js",
295177
295249
  type: "module",
295178
- version: "0.3.3",
295250
+ version: "0.3.4",
295179
295251
  license: "MIT",
295180
295252
  author: "satchmo",
295181
295253
  description: "A collection of Bitcoin SV (BSV) tools for the Model Context Protocol (MCP) framework",
@@ -297908,6 +297980,9 @@ function registerAllTools(server, config2 = {}) {
297908
297980
  }
297909
297981
  if (enableUtilsTools) {
297910
297982
  registerUtilsTools(server);
297983
+ const apiUrl = config2.droplitApiUrl ?? process.env.DROPLIT_API_URL ?? config2.droplitClient?.getConfig().apiUrl;
297984
+ if (apiUrl)
297985
+ registerDroplitDiscoveryTool(server, apiUrl);
297911
297986
  }
297912
297987
  if (enableA2bTools) {
297913
297988
  registerA2bDiscoverTool(server);
@@ -298366,6 +298441,12 @@ function readDroplitSponsorConfig(env = process.env) {
298366
298441
  const siteUrl = env.DROPLIT_SITE_URL;
298367
298442
  if (apiUrl === undefined && faucetName === undefined && siteUrl === undefined)
298368
298443
  return;
298444
+ if (apiUrl?.trim() && faucetName === undefined) {
298445
+ droplitApiBaseUrl2(apiUrl);
298446
+ if (siteUrl !== undefined)
298447
+ approvalSiteOrigin2(siteUrl);
298448
+ return;
298449
+ }
298369
298450
  if (!apiUrl?.trim() || !faucetName?.trim()) {
298370
298451
  throw new Error("DROPLIT_API_URL and DROPLIT_FAUCET_NAME must both be explicitly configured");
298371
298452
  }
@@ -298375,6 +298456,14 @@ function readDroplitSponsorConfig(env = process.env) {
298375
298456
  ...siteUrl === undefined ? {} : { siteUrl: approvalSiteOrigin2(siteUrl) }
298376
298457
  };
298377
298458
  }
298459
+ function droplitApiBaseUrl2(apiUrl) {
298460
+ const url3 = new URL(apiUrl);
298461
+ const loopback = url3.hostname === "localhost" || url3.hostname === "127.0.0.1" || url3.hostname === "[::1]";
298462
+ if (url3.username || url3.password || url3.search || url3.hash || !(url3.protocol === "https:" || url3.protocol === "http:" && loopback)) {
298463
+ throw new Error("Droplit API requires HTTPS or HTTP loopback without credentials, query or fragment");
298464
+ }
298465
+ return url3.href.replace(/\/$/, "");
298466
+ }
298378
298467
  function approvalSiteOrigin2(value2 = "https://droplit.dev") {
298379
298468
  const url3 = new URL(value2);
298380
298469
  const loopback = ["localhost", "127.0.0.1", "[::1]"].includes(url3.hostname);
@@ -298440,11 +298529,7 @@ class DroplitClient2 {
298440
298529
  this.siteOrigin = approvalSiteOrigin2(config2.siteUrl);
298441
298530
  if (config2.wallet && config2.authKey)
298442
298531
  throw new Error("Configure Droplit wallet or authKey, not both");
298443
- const url3 = new URL(config2.apiUrl);
298444
- const loopback = url3.hostname === "localhost" || url3.hostname === "127.0.0.1" || url3.hostname === "[::1]";
298445
- if (url3.username || url3.password || url3.search || url3.hash || !(url3.protocol === "https:" || url3.protocol === "http:" && loopback)) {
298446
- throw new Error("Droplit API requires HTTPS or HTTP loopback without credentials, query or fragment");
298447
- }
298532
+ droplitApiBaseUrl2(config2.apiUrl);
298448
298533
  if (!config2.faucetName.trim())
298449
298534
  throw new Error("Droplit faucet name is required");
298450
298535
  this.wallet = config2.wallet ?? (config2.authKey ? new ProtoWallet_default(config2.authKey) : undefined);
@@ -298569,6 +298654,14 @@ class DroplitClient2 {
298569
298654
  return this.authed(path5, init);
298570
298655
  }
298571
298656
  }
298657
+ var publicSponsorsSchema2 = exports_external.object({
298658
+ sponsors: exports_external.array(exports_external.object({
298659
+ name: exports_external.string(),
298660
+ slug: exports_external.string().min(1),
298661
+ approval_required: exports_external.literal(true)
298662
+ })).max(50),
298663
+ next_cursor: exports_external.string().min(1).nullable()
298664
+ });
298572
298665
 
298573
298666
  // utils/externalWalletConfig.ts
298574
298667
  function readExternalWalletConfig(env = process.env) {
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "bsv-mcp",
3
3
  "module": "dist/index.js",
4
4
  "type": "module",
5
- "version": "0.3.3",
5
+ "version": "0.3.4",
6
6
  "license": "MIT",
7
7
  "author": "satchmo",
8
8
  "description": "A collection of Bitcoin SV (BSV) tools for the Model Context Protocol (MCP) framework",
package/tools/index.ts CHANGED
@@ -10,6 +10,7 @@ import { registerBsvTools } from "./bsv";
10
10
  import { registerMneeTools } from "./mnee";
11
11
  import { registerOrdinalsTools } from "./ordinals";
12
12
  import { registerUtilsTools } from "./utils";
13
+ import { registerDroplitDiscoveryTool } from "./wallet/droplitDiscovery";
13
14
  import { registerDroplitTools } from "./wallet/droplit";
14
15
  import { registerWalletGetBalanceDroplitTool } from "./wallet/getBalanceDroplit";
15
16
  import type { IntegratedWallet } from "./wallet/integratedWallet";
@@ -48,6 +49,7 @@ export interface ToolsConfig {
48
49
  ctx?: OneSatContext;
49
50
  services?: OneSatServices;
50
51
  droplitClient?: DroplitClient;
52
+ droplitApiUrl?: string;
51
53
  }
52
54
 
53
55
  /**
@@ -94,6 +96,11 @@ export function registerAllTools(
94
96
  // Register utility tools
95
97
  if (enableUtilsTools) {
96
98
  registerUtilsTools(server);
99
+ const apiUrl =
100
+ config.droplitApiUrl ??
101
+ process.env.DROPLIT_API_URL ??
102
+ config.droplitClient?.getConfig().apiUrl;
103
+ if (apiUrl) registerDroplitDiscoveryTool(server, apiUrl);
97
104
  }
98
105
 
99
106
  // Register agent-to-blockchain tools
@@ -0,0 +1,51 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import { z } from "zod";
3
+ import {
4
+ discoverDroplitSponsors,
5
+ droplitApiBaseUrl,
6
+ DroplitError,
7
+ } from "../../utils/droplit";
8
+ import { createSuccessResponse } from "../utils/errorHandler";
9
+
10
+ export function registerDroplitDiscoveryTool(
11
+ server: McpServer,
12
+ apiUrl: string,
13
+ ) {
14
+ const baseUrl = droplitApiBaseUrl(apiUrl);
15
+ server.registerTool(
16
+ "droplit_discover",
17
+ {
18
+ description:
19
+ "List publicly opted-in sponsors. No wallet or sponsor selection is required. Listing grants no access or funding; choose a slug, then request the sponsor owner's approval separately.",
20
+ inputSchema: {
21
+ limit: z.number().int().min(1).max(50).optional(),
22
+ after: z.string().optional(),
23
+ },
24
+ annotations: {
25
+ readOnlyHint: true,
26
+ destructiveHint: false,
27
+ idempotentHint: true,
28
+ openWorldHint: true,
29
+ },
30
+ },
31
+ async (options) => {
32
+ try {
33
+ const data = await discoverDroplitSponsors(baseUrl, options);
34
+ return { ...createSuccessResponse(data), structuredContent: data };
35
+ } catch (error) {
36
+ const data = {
37
+ error: error instanceof DroplitError ? error.code : "invalid_request",
38
+ message:
39
+ error instanceof DroplitError
40
+ ? error.message
41
+ : "Invalid sponsor discovery request.",
42
+ };
43
+ return {
44
+ ...createSuccessResponse(data),
45
+ structuredContent: data,
46
+ isError: true,
47
+ };
48
+ }
49
+ },
50
+ );
51
+ }