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 +7 -0
- package/README.md +11 -1
- package/dist/index.js +105 -12
- package/package.json +1 -1
- package/tools/index.ts +7 -0
- package/tools/wallet/droplitDiscovery.ts +51 -0
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.
|
|
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
|
-
|
|
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.
|
|
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
|
-
|
|
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
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
|
+
}
|