bsv-mcp 0.3.2 → 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 +13 -0
- package/README.md +21 -2
- package/dist/index.js +136 -15
- package/package.json +1 -1
- package/tools/index.ts +7 -0
- package/tools/wallet/droplit.ts +1 -1
- package/tools/wallet/droplitDiscovery.ts +51 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,18 @@
|
|
|
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
|
+
|
|
10
|
+
## [0.3.3] - 2026-09-06
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
- Honor `DROPLIT_SITE_URL` for sponsor approval links so staging requests reach the correct owner UI.
|
|
14
|
+
- Build approval links from the configured sponsor and verified wallet identity; reject unsafe site origins.
|
|
15
|
+
|
|
3
16
|
## [0.3.2] - 2026-09-06
|
|
4
17
|
|
|
5
18
|
### Added
|
package/README.md
CHANGED
|
@@ -884,17 +884,36 @@ BRC100_WALLET_URL=http://127.0.0.1:3321
|
|
|
884
884
|
BRC100_WALLET_ORIGINATOR=bsv-mcp.local
|
|
885
885
|
DROPLIT_API_URL=https://api.droplit.dev/droplit
|
|
886
886
|
DROPLIT_FAUCET_NAME=your-sponsor-slug
|
|
887
|
+
# Optional: set explicitly when the owner approval UI is hosted elsewhere.
|
|
888
|
+
DROPLIT_SITE_URL=https://droplit.dev
|
|
887
889
|
```
|
|
888
890
|
|
|
889
|
-
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
|
|
890
892
|
base path. Leave `USE_DROPLIT_API` unset: these sponsor tools appear alongside
|
|
891
893
|
normal wallet tools and use the same connected signer identity. Existing local
|
|
892
894
|
BRC-100 wallet contexts also support this sponsor pair. `1sat serve` exposes a
|
|
893
895
|
wallet stack service; it is not itself a BRC-100 signer RPC endpoint.
|
|
894
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
|
+
|
|
895
907
|
Call `droplit_getAccess` to inspect your own authorization and quotas. An
|
|
896
|
-
`approval_required` response includes a `
|
|
908
|
+
`approval_required` response includes a link to `DROPLIT_SITE_URL` (default
|
|
909
|
+
`https://droplit.dev`) for manual
|
|
897
910
|
owner review. Do not automatically open, approve, or register to obtain access.
|
|
911
|
+
The site URL must be an HTTPS origin (HTTP is allowed only for loopback),
|
|
912
|
+
without credentials, a path, query, or fragment. For staging, set it to the
|
|
913
|
+
trusted staging frontend origin independently of `DROPLIT_API_URL`. Approval
|
|
914
|
+
links use the configured sponsor and authenticated wallet identity; a server
|
|
915
|
+
response cannot redirect them to another site.
|
|
916
|
+
|
|
898
917
|
Public status and public-key registration do not prove sponsor approval, and
|
|
899
918
|
creating your own faucet requires funding rather than providing free credit.
|
|
900
919
|
Missing quota kinds are unrestricted for authorized users; a configured zero
|
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,23 @@ 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
|
+
}
|
|
284888
|
+
function approvalSiteOrigin(value2 = "https://droplit.dev") {
|
|
284889
|
+
const url3 = new URL(value2);
|
|
284890
|
+
const loopback = ["localhost", "127.0.0.1", "[::1]"].includes(url3.hostname);
|
|
284891
|
+
if (url3.username || url3.password || url3.pathname !== "/" || url3.href.includes("?") || url3.href.includes("#") || value2.includes("\\") || !(url3.protocol === "https:" || url3.protocol === "http:" && loopback)) {
|
|
284892
|
+
throw new Error("DROPLIT_SITE_URL requires an HTTPS or HTTP loopback origin without credentials, path, query or fragment");
|
|
284893
|
+
}
|
|
284894
|
+
return url3.origin;
|
|
284895
|
+
}
|
|
284896
|
+
|
|
284880
284897
|
class DroplitError extends Error {
|
|
284881
284898
|
code;
|
|
284882
284899
|
status;
|
|
@@ -284926,16 +284943,14 @@ function httpFailure(status, details = {}) {
|
|
|
284926
284943
|
class DroplitClient {
|
|
284927
284944
|
config;
|
|
284928
284945
|
authFetch;
|
|
284946
|
+
siteOrigin;
|
|
284929
284947
|
wallet;
|
|
284930
284948
|
constructor(config2) {
|
|
284931
284949
|
this.config = config2;
|
|
284950
|
+
this.siteOrigin = approvalSiteOrigin(config2.siteUrl);
|
|
284932
284951
|
if (config2.wallet && config2.authKey)
|
|
284933
284952
|
throw new Error("Configure Droplit wallet or authKey, not both");
|
|
284934
|
-
|
|
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
|
-
}
|
|
284953
|
+
droplitApiBaseUrl(config2.apiUrl);
|
|
284939
284954
|
if (!config2.faucetName.trim())
|
|
284940
284955
|
throw new Error("Droplit faucet name is required");
|
|
284941
284956
|
this.wallet = config2.wallet ?? (config2.authKey ? new ProtoWallet_default(config2.authKey) : undefined);
|
|
@@ -285023,6 +285038,7 @@ class DroplitClient {
|
|
|
285023
285038
|
throw new DroplitError("invalid_response", "Sponsor access response did not match the authenticated wallet.");
|
|
285024
285039
|
}
|
|
285025
285040
|
access.approval_path = `/droplit/${encodeURIComponent(this.config.faucetName)}?tab=api&request_key=${encodeURIComponent(identity6)}`;
|
|
285041
|
+
access.approval_url = new URL(access.approval_path, this.siteOrigin).href;
|
|
285026
285042
|
return access;
|
|
285027
285043
|
}
|
|
285028
285044
|
async fund(rawtx) {
|
|
@@ -285059,6 +285075,74 @@ class DroplitClient {
|
|
|
285059
285075
|
return this.authed(path3, init);
|
|
285060
285076
|
}
|
|
285061
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
|
+
}
|
|
285062
285146
|
|
|
285063
285147
|
// tools/wallet/droplit.ts
|
|
285064
285148
|
function errorResult(error52) {
|
|
@@ -285093,7 +285177,7 @@ function registerDroplitTools(server, client, disableBroadcasting = false) {
|
|
|
285093
285177
|
...!access.authorized ? {
|
|
285094
285178
|
error: "approval_required",
|
|
285095
285179
|
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:
|
|
285180
|
+
approval_url: access.approval_url
|
|
285097
285181
|
} : {}
|
|
285098
285182
|
};
|
|
285099
285183
|
return {
|
|
@@ -295163,7 +295247,7 @@ var package_default2 = {
|
|
|
295163
295247
|
name: "bsv-mcp",
|
|
295164
295248
|
module: "dist/index.js",
|
|
295165
295249
|
type: "module",
|
|
295166
|
-
version: "0.3.
|
|
295250
|
+
version: "0.3.4",
|
|
295167
295251
|
license: "MIT",
|
|
295168
295252
|
author: "satchmo",
|
|
295169
295253
|
description: "A collection of Bitcoin SV (BSV) tools for the Model Context Protocol (MCP) framework",
|
|
@@ -297896,6 +297980,9 @@ function registerAllTools(server, config2 = {}) {
|
|
|
297896
297980
|
}
|
|
297897
297981
|
if (enableUtilsTools) {
|
|
297898
297982
|
registerUtilsTools(server);
|
|
297983
|
+
const apiUrl = config2.droplitApiUrl ?? process.env.DROPLIT_API_URL ?? config2.droplitClient?.getConfig().apiUrl;
|
|
297984
|
+
if (apiUrl)
|
|
297985
|
+
registerDroplitDiscoveryTool(server, apiUrl);
|
|
297899
297986
|
}
|
|
297900
297987
|
if (enableA2bTools) {
|
|
297901
297988
|
registerA2bDiscoverTool(server);
|
|
@@ -298351,12 +298438,39 @@ init_mod2();
|
|
|
298351
298438
|
function readDroplitSponsorConfig(env = process.env) {
|
|
298352
298439
|
const apiUrl = env.DROPLIT_API_URL;
|
|
298353
298440
|
const faucetName = env.DROPLIT_FAUCET_NAME;
|
|
298354
|
-
|
|
298441
|
+
const siteUrl = env.DROPLIT_SITE_URL;
|
|
298442
|
+
if (apiUrl === undefined && faucetName === undefined && siteUrl === undefined)
|
|
298443
|
+
return;
|
|
298444
|
+
if (apiUrl?.trim() && faucetName === undefined) {
|
|
298445
|
+
droplitApiBaseUrl2(apiUrl);
|
|
298446
|
+
if (siteUrl !== undefined)
|
|
298447
|
+
approvalSiteOrigin2(siteUrl);
|
|
298355
298448
|
return;
|
|
298449
|
+
}
|
|
298356
298450
|
if (!apiUrl?.trim() || !faucetName?.trim()) {
|
|
298357
298451
|
throw new Error("DROPLIT_API_URL and DROPLIT_FAUCET_NAME must both be explicitly configured");
|
|
298358
298452
|
}
|
|
298359
|
-
return {
|
|
298453
|
+
return {
|
|
298454
|
+
apiUrl,
|
|
298455
|
+
faucetName,
|
|
298456
|
+
...siteUrl === undefined ? {} : { siteUrl: approvalSiteOrigin2(siteUrl) }
|
|
298457
|
+
};
|
|
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
|
+
}
|
|
298467
|
+
function approvalSiteOrigin2(value2 = "https://droplit.dev") {
|
|
298468
|
+
const url3 = new URL(value2);
|
|
298469
|
+
const loopback = ["localhost", "127.0.0.1", "[::1]"].includes(url3.hostname);
|
|
298470
|
+
if (url3.username || url3.password || url3.pathname !== "/" || url3.href.includes("?") || url3.href.includes("#") || value2.includes("\\") || !(url3.protocol === "https:" || url3.protocol === "http:" && loopback)) {
|
|
298471
|
+
throw new Error("DROPLIT_SITE_URL requires an HTTPS or HTTP loopback origin without credentials, path, query or fragment");
|
|
298472
|
+
}
|
|
298473
|
+
return url3.origin;
|
|
298360
298474
|
}
|
|
298361
298475
|
|
|
298362
298476
|
class DroplitError2 extends Error {
|
|
@@ -298408,16 +298522,14 @@ function httpFailure2(status, details = {}) {
|
|
|
298408
298522
|
class DroplitClient2 {
|
|
298409
298523
|
config;
|
|
298410
298524
|
authFetch;
|
|
298525
|
+
siteOrigin;
|
|
298411
298526
|
wallet;
|
|
298412
298527
|
constructor(config2) {
|
|
298413
298528
|
this.config = config2;
|
|
298529
|
+
this.siteOrigin = approvalSiteOrigin2(config2.siteUrl);
|
|
298414
298530
|
if (config2.wallet && config2.authKey)
|
|
298415
298531
|
throw new Error("Configure Droplit wallet or authKey, not both");
|
|
298416
|
-
|
|
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
|
-
}
|
|
298532
|
+
droplitApiBaseUrl2(config2.apiUrl);
|
|
298421
298533
|
if (!config2.faucetName.trim())
|
|
298422
298534
|
throw new Error("Droplit faucet name is required");
|
|
298423
298535
|
this.wallet = config2.wallet ?? (config2.authKey ? new ProtoWallet_default(config2.authKey) : undefined);
|
|
@@ -298505,6 +298617,7 @@ class DroplitClient2 {
|
|
|
298505
298617
|
throw new DroplitError2("invalid_response", "Sponsor access response did not match the authenticated wallet.");
|
|
298506
298618
|
}
|
|
298507
298619
|
access.approval_path = `/droplit/${encodeURIComponent(this.config.faucetName)}?tab=api&request_key=${encodeURIComponent(identity6)}`;
|
|
298620
|
+
access.approval_url = new URL(access.approval_path, this.siteOrigin).href;
|
|
298508
298621
|
return access;
|
|
298509
298622
|
}
|
|
298510
298623
|
async fund(rawtx) {
|
|
@@ -298541,6 +298654,14 @@ class DroplitClient2 {
|
|
|
298541
298654
|
return this.authed(path5, init);
|
|
298542
298655
|
}
|
|
298543
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
|
+
});
|
|
298544
298665
|
|
|
298545
298666
|
// utils/externalWalletConfig.ts
|
|
298546
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
|
package/tools/wallet/droplit.ts
CHANGED
|
@@ -53,7 +53,7 @@ export function registerDroplitTools(
|
|
|
53
53
|
error: "approval_required",
|
|
54
54
|
message:
|
|
55
55
|
"Ask the sponsor owner to approve this wallet. Copy the approval URL for manual review; do not auto-open or submit approval.",
|
|
56
|
-
approval_url:
|
|
56
|
+
approval_url: access.approval_url,
|
|
57
57
|
}
|
|
58
58
|
: {}),
|
|
59
59
|
};
|
|
@@ -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
|
+
}
|