@aibtc/mcp-server 1.70.1 → 1.72.0

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.
Files changed (134) hide show
  1. package/README.md +45 -8
  2. package/dist/bridge/index.js +4 -4
  3. package/dist/config/contracts.d.ts +4 -4
  4. package/dist/config/contracts.js +2 -2
  5. package/dist/config/networks.js +4 -1
  6. package/dist/endpoints/registry.d.ts +8 -26
  7. package/dist/endpoints/registry.js +7 -932
  8. package/dist/index.js +118 -42
  9. package/dist/services/stacking.service.d.ts +144 -45
  10. package/dist/services/stacking.service.js +416 -123
  11. package/dist/services/x402-discovery.service.d.ts +81 -0
  12. package/dist/services/x402-discovery.service.js +170 -0
  13. package/dist/tools/arxiv-research.tools.d.ts +1 -1
  14. package/dist/tools/arxiv-research.tools.js +6 -6
  15. package/dist/tools/at-stake-legion.tools.d.ts +1 -1
  16. package/dist/tools/at-stake-legion.tools.js +14 -14
  17. package/dist/tools/at-stake.tools.d.ts +1 -1
  18. package/dist/tools/at-stake.tools.js +17 -17
  19. package/dist/tools/bitcoin.tools.d.ts +1 -1
  20. package/dist/tools/bitcoin.tools.js +12 -12
  21. package/dist/tools/bitflow.tools.d.ts +1 -1
  22. package/dist/tools/bitflow.tools.js +21 -21
  23. package/dist/tools/bns.tools.d.ts +1 -1
  24. package/dist/tools/bns.tools.js +18 -18
  25. package/dist/tools/bounty-hint.d.ts +1 -1
  26. package/dist/tools/bounty-scanner.tools.d.ts +1 -1
  27. package/dist/tools/bounty-scanner.tools.js +20 -20
  28. package/dist/tools/child-inscription.tools.d.ts +1 -1
  29. package/dist/tools/child-inscription.tools.js +6 -6
  30. package/dist/tools/competition.tools.d.ts +1 -1
  31. package/dist/tools/competition.tools.js +4 -4
  32. package/dist/tools/contract.tools.d.ts +1 -1
  33. package/dist/tools/contract.tools.js +8 -8
  34. package/dist/tools/credentials.tools.d.ts +1 -1
  35. package/dist/tools/credentials.tools.js +8 -8
  36. package/dist/tools/defi.tools.d.ts +1 -1
  37. package/dist/tools/defi.tools.js +21 -21
  38. package/dist/tools/dual-stacking.tools.d.ts +1 -1
  39. package/dist/tools/dual-stacking.tools.js +7 -7
  40. package/dist/tools/earning.tools.d.ts +1 -1
  41. package/dist/tools/earning.tools.js +5 -5
  42. package/dist/tools/endpoint.tools.d.ts +1 -1
  43. package/dist/tools/endpoint.tools.js +92 -49
  44. package/dist/tools/identity.tools.d.ts +1 -1
  45. package/dist/tools/identity.tools.js +19 -19
  46. package/dist/tools/inbox-x402.tools.d.ts +1 -1
  47. package/dist/tools/inbox-x402.tools.js +2 -2
  48. package/dist/tools/inbox.tools.d.ts +1 -1
  49. package/dist/tools/inbox.tools.js +2 -2
  50. package/dist/tools/index.d.ts +6 -3
  51. package/dist/tools/index.js +69 -58
  52. package/dist/tools/inference-marketplace.tools.d.ts +1 -1
  53. package/dist/tools/inference-marketplace.tools.js +10 -10
  54. package/dist/tools/jingswap.tools.d.ts +1 -1
  55. package/dist/tools/jingswap.tools.js +30 -30
  56. package/dist/tools/legion.tools.d.ts +1 -1
  57. package/dist/tools/legion.tools.js +20 -20
  58. package/dist/tools/lightning.tools.d.ts +1 -1
  59. package/dist/tools/lightning.tools.js +14 -14
  60. package/dist/tools/mempool.tools.d.ts +1 -1
  61. package/dist/tools/mempool.tools.js +5 -5
  62. package/dist/tools/news.tools.d.ts +1 -1
  63. package/dist/tools/news.tools.js +2 -1
  64. package/dist/tools/nft.tools.d.ts +1 -1
  65. package/dist/tools/nft.tools.js +12 -12
  66. package/dist/tools/nonce.tools.d.ts +1 -1
  67. package/dist/tools/nonce.tools.js +8 -8
  68. package/dist/tools/nostr.tools.d.ts +1 -1
  69. package/dist/tools/nostr.tools.js +12 -12
  70. package/dist/tools/openrouter.tools.d.ts +1 -1
  71. package/dist/tools/openrouter.tools.js +4 -4
  72. package/dist/tools/ordinals-marketplace.tools.d.ts +1 -1
  73. package/dist/tools/ordinals-marketplace.tools.js +10 -10
  74. package/dist/tools/ordinals-p2p.tools.d.ts +1 -1
  75. package/dist/tools/ordinals-p2p.tools.js +18 -18
  76. package/dist/tools/ordinals.tools.d.ts +1 -1
  77. package/dist/tools/ordinals.tools.js +8 -8
  78. package/dist/tools/pillar-direct.tools.d.ts +1 -1
  79. package/dist/tools/pillar-direct.tools.js +59 -38
  80. package/dist/tools/pillar.tools.d.ts +1 -1
  81. package/dist/tools/pillar.tools.js +28 -26
  82. package/dist/tools/profiles.d.ts +51 -0
  83. package/dist/tools/profiles.js +111 -0
  84. package/dist/tools/psbt.tools.d.ts +1 -1
  85. package/dist/tools/psbt.tools.js +8 -8
  86. package/dist/tools/query.tools.d.ts +1 -1
  87. package/dist/tools/query.tools.js +12 -12
  88. package/dist/tools/relay-diagnostic.tools.d.ts +1 -1
  89. package/dist/tools/relay-diagnostic.tools.js +3 -3
  90. package/dist/tools/reputation.tools.d.ts +1 -1
  91. package/dist/tools/reputation.tools.js +22 -22
  92. package/dist/tools/runes.tools.d.ts +1 -1
  93. package/dist/tools/runes.tools.js +18 -18
  94. package/dist/tools/sbtc.tools.d.ts +1 -1
  95. package/dist/tools/sbtc.tools.js +14 -33
  96. package/dist/tools/scaffold.tools.d.ts +1 -1
  97. package/dist/tools/scaffold.tools.js +4 -4
  98. package/dist/tools/settings.tools.d.ts +1 -1
  99. package/dist/tools/settings.tools.js +9 -9
  100. package/dist/tools/signing.tools.d.ts +1 -1
  101. package/dist/tools/signing.tools.js +20 -20
  102. package/dist/tools/skill-mappings.js +4 -0
  103. package/dist/tools/souldinals.tools.d.ts +1 -1
  104. package/dist/tools/souldinals.tools.js +10 -10
  105. package/dist/tools/stacking-lottery.tools.d.ts +1 -1
  106. package/dist/tools/stacking-lottery.tools.js +11 -11
  107. package/dist/tools/stacking.tools.d.ts +1 -1
  108. package/dist/tools/stacking.tools.js +358 -50
  109. package/dist/tools/stacks-market.tools.d.ts +1 -1
  110. package/dist/tools/stacks-market.tools.js +22 -22
  111. package/dist/tools/styx.tools.d.ts +1 -1
  112. package/dist/tools/styx.tools.js +11 -11
  113. package/dist/tools/taproot-multisig.tools.d.ts +1 -1
  114. package/dist/tools/taproot-multisig.tools.js +5 -5
  115. package/dist/tools/tenero.tools.d.ts +1 -1
  116. package/dist/tools/tenero.tools.js +22 -22
  117. package/dist/tools/tokens.tools.d.ts +1 -1
  118. package/dist/tools/tokens.tools.js +10 -10
  119. package/dist/tools/transfer.tools.d.ts +1 -1
  120. package/dist/tools/transfer.tools.js +4 -4
  121. package/dist/tools/wallet-management.tools.d.ts +1 -1
  122. package/dist/tools/wallet-management.tools.js +16 -16
  123. package/dist/tools/wallet.tools.d.ts +1 -1
  124. package/dist/tools/wallet.tools.js +2 -2
  125. package/dist/tools/yield-dashboard.tools.d.ts +1 -1
  126. package/dist/tools/yield-dashboard.tools.js +18 -27
  127. package/dist/tools/yield-hunter.tools.d.ts +1 -1
  128. package/dist/tools/yield-hunter.tools.js +6 -6
  129. package/dist/utils/bitcoin.d.ts +8 -0
  130. package/dist/utils/bitcoin.js +24 -0
  131. package/dist/utils/fee.js +1 -1
  132. package/package.json +8 -7
  133. package/skill/SKILL.md +1 -1
  134. package/skill/references/stacks-defi.md +1 -1
@@ -0,0 +1,170 @@
1
+ /**
2
+ * Live x402 endpoint discovery.
3
+ *
4
+ * x402.aibtc.com and stx402.com publish an OpenAPI spec, and stx402.com runs a
5
+ * directory of x402 endpoints registered by third parties. Reading those at call
6
+ * time keeps `list_x402_endpoints` in step with what the hosts actually serve,
7
+ * instead of a hand-maintained list that goes stale when routes move.
8
+ *
9
+ * Results are cached briefly. A failed fetch is never cached and never replaced
10
+ * with stale or invented entries: the source is reported as unavailable.
11
+ */
12
+ import { NETWORK } from "../config/networks.js";
13
+ export const OPENAPI_SOURCES = [
14
+ {
15
+ source: "x402.aibtc.com",
16
+ baseUrl: { mainnet: "https://x402.aibtc.com", testnet: "https://x402.aibtc.dev" },
17
+ },
18
+ {
19
+ source: "stx402.com",
20
+ baseUrl: { mainnet: "https://stx402.com" },
21
+ },
22
+ ];
23
+ const DIRECTORY_URL = "https://stx402.com/registry/list";
24
+ const DIRECTORY_PAGE_SIZE = 50;
25
+ /** Hard stop so a misbehaving `total` cannot turn one call into hundreds of requests. */
26
+ const DIRECTORY_MAX_PAGES = 20;
27
+ export const DISCOVERY_CACHE_TTL_MS = 10 * 60 * 1000;
28
+ const FETCH_TIMEOUT_MS = 10_000;
29
+ const cache = new Map();
30
+ async function cached(key, load) {
31
+ const hit = cache.get(key);
32
+ if (hit && Date.now() - hit.at < DISCOVERY_CACHE_TTL_MS) {
33
+ return hit.value;
34
+ }
35
+ const value = await load();
36
+ cache.set(key, { at: Date.now(), value });
37
+ return value;
38
+ }
39
+ /** Test seam. */
40
+ export function clearDiscoveryCache() {
41
+ cache.clear();
42
+ }
43
+ async function fetchJson(url) {
44
+ const response = await fetch(url, {
45
+ headers: { Accept: "application/json" },
46
+ signal: AbortSignal.timeout(FETCH_TIMEOUT_MS),
47
+ });
48
+ if (!response.ok) {
49
+ throw new Error(`GET ${url} returned HTTP ${response.status}`);
50
+ }
51
+ return response.json();
52
+ }
53
+ const METHODS = ["get", "post", "put", "delete"];
54
+ /**
55
+ * Summaries on these hosts lead with the pricing tier, e.g. "(paid, standard) Decode a
56
+ * transaction" or "(free) List models". Split that into a cost label and a description.
57
+ * The exact price is only known from the 402 challenge, so it is not guessed here.
58
+ */
59
+ export function parseSummary(summary) {
60
+ const match = summary.match(/^\((free|paid)(?:,\s*([^)]+))?\)\s*(.*)$/i);
61
+ if (match) {
62
+ const [, kind, tier, rest] = match;
63
+ return {
64
+ cost: kind.toLowerCase() === "free" ? "FREE" : `paid${tier ? ` (${tier.trim()})` : ""}`,
65
+ description: rest,
66
+ };
67
+ }
68
+ if (/\(free\)/i.test(summary)) {
69
+ return { cost: "FREE", description: summary.replace(/\s*\(free\)/i, "") };
70
+ }
71
+ return { cost: "see probe_x402_endpoint", description: summary };
72
+ }
73
+ /** Map an OpenAPI spec to endpoint entries. Admin-only operations are left out. */
74
+ export function endpointsFromOpenApi(spec, source) {
75
+ const endpoints = [];
76
+ for (const [path, operations] of Object.entries(spec.paths ?? {})) {
77
+ for (const method of METHODS) {
78
+ const op = operations[method];
79
+ if (!op)
80
+ continue;
81
+ const tags = op.tags ?? [];
82
+ if (tags.some((tag) => /admin/i.test(tag)))
83
+ continue;
84
+ const { cost, description } = parseSummary(op.summary ?? op.description ?? "");
85
+ // tokenType selects the payment asset; execute_x402_endpoint handles that itself.
86
+ const params = Object.fromEntries((op.parameters ?? [])
87
+ .filter((p) => p.name !== "tokenType")
88
+ .map((p) => [p.name, p.description ?? p.in]));
89
+ const schema = op.requestBody?.content?.["application/json"]?.schema;
90
+ const required = new Set(schema?.required ?? []);
91
+ const body = Object.fromEntries(Object.entries(schema?.properties ?? {}).map(([name, prop]) => [
92
+ name,
93
+ `${prop.description ?? name}${required.has(name) ? "" : " (optional)"}`,
94
+ ]));
95
+ endpoints.push({
96
+ path,
97
+ method: method.toUpperCase(),
98
+ description,
99
+ cost,
100
+ category: tags[0] ?? "Other",
101
+ source,
102
+ ...(Object.keys(params).length > 0 && { params }),
103
+ ...(Object.keys(body).length > 0 && { body }),
104
+ });
105
+ }
106
+ }
107
+ return endpoints;
108
+ }
109
+ /** Fetch one host's endpoints from its OpenAPI spec. */
110
+ export function fetchOpenApiEndpoints(source, network = NETWORK) {
111
+ const baseUrl = source.baseUrl[network];
112
+ if (!baseUrl) {
113
+ return Promise.reject(new Error(`${source.source} has no ${network} deployment`));
114
+ }
115
+ return cached(`openapi:${baseUrl}`, async () => {
116
+ const spec = (await fetchJson(`${baseUrl}/openapi.json`));
117
+ if (!spec.paths) {
118
+ throw new Error(`${baseUrl}/openapi.json has no paths`);
119
+ }
120
+ return endpointsFromOpenApi(spec, source.source);
121
+ });
122
+ }
123
+ /** Fetch every entry in the stx402.com endpoint directory (mainnet only). */
124
+ export function fetchDirectoryEntries() {
125
+ return cached("directory", async () => {
126
+ const entries = [];
127
+ let total = Infinity;
128
+ for (let page = 0; page < DIRECTORY_MAX_PAGES && entries.length < total; page++) {
129
+ const offset = page * DIRECTORY_PAGE_SIZE;
130
+ const data = (await fetchJson(`${DIRECTORY_URL}?limit=${DIRECTORY_PAGE_SIZE}&offset=${offset}`));
131
+ if (!Array.isArray(data.entries)) {
132
+ throw new Error(`${DIRECTORY_URL} response has no entries array`);
133
+ }
134
+ total = data.total ?? entries.length + data.entries.length;
135
+ for (const e of data.entries) {
136
+ if (!e.url)
137
+ continue;
138
+ entries.push({
139
+ name: e.name ?? e.url,
140
+ url: e.url,
141
+ category: e.category ?? "other",
142
+ status: e.status ?? "unknown",
143
+ owner: e.owner ?? "",
144
+ });
145
+ }
146
+ if (data.entries.length < DIRECTORY_PAGE_SIZE)
147
+ break;
148
+ }
149
+ return entries;
150
+ });
151
+ }
152
+ function errorMessage(error) {
153
+ return error instanceof Error ? error.message : String(error);
154
+ }
155
+ /** Fetch every OpenAPI source in parallel, reporting failures per source. */
156
+ export async function discoverOpenApiEndpoints(sources = OPENAPI_SOURCES, network = NETWORK) {
157
+ const results = await Promise.allSettled(sources.map((s) => fetchOpenApiEndpoints(s, network)));
158
+ const endpoints = [];
159
+ const unavailable = [];
160
+ results.forEach((result, i) => {
161
+ if (result.status === "fulfilled") {
162
+ endpoints.push(...result.value);
163
+ }
164
+ else {
165
+ unavailable.push({ source: sources[i].source, error: errorMessage(result.reason) });
166
+ }
167
+ });
168
+ return { endpoints, unavailable };
169
+ }
170
+ //# sourceMappingURL=x402-discovery.service.js.map
@@ -11,6 +11,6 @@
11
11
  *
12
12
  * Mirrors the arxiv-research skill (aibtcdev/skills/arxiv-research/).
13
13
  */
14
- import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
14
+ import { McpServer } from "@modelcontextprotocol/server";
15
15
  export declare function registerArxivResearchTools(server: McpServer): void;
16
16
  //# sourceMappingURL=arxiv-research.tools.d.ts.map
@@ -189,7 +189,7 @@ Each paper includes title, authors (first 3), truncated abstract, arXiv link,
189
189
  relevance score, and topic tags.
190
190
 
191
191
  Read-only. No API key required.`,
192
- inputSchema: {
192
+ inputSchema: z.object({
193
193
  categories: z
194
194
  .string()
195
195
  .optional()
@@ -205,7 +205,7 @@ Read-only. No API key required.`,
205
205
  .min(0)
206
206
  .optional()
207
207
  .describe("Minimum relevance score for results. Default: 3. Set to 0 to include all fetched papers."),
208
- },
208
+ }),
209
209
  }, async ({ categories, max_results, min_score }) => {
210
210
  try {
211
211
  const cats = categories
@@ -260,7 +260,7 @@ disk — use the arxiv-research skill (bun run arxiv-research/arxiv-research.ts
260
260
  to write timestamped digest files to ~/.aibtc/arxiv-research/digests/.
261
261
 
262
262
  Read-only. No API key required.`,
263
- inputSchema: {
263
+ inputSchema: z.object({
264
264
  categories: z
265
265
  .string()
266
266
  .optional()
@@ -280,7 +280,7 @@ Read-only. No API key required.`,
280
280
  .min(0)
281
281
  .optional()
282
282
  .describe("Minimum relevance score for digest inclusion. Default: 3"),
283
- },
283
+ }),
284
284
  }, async ({ categories, max_results, date, min_score }) => {
285
285
  try {
286
286
  const cats = categories
@@ -387,14 +387,14 @@ Digests are NOT created by arxiv_compile_digest (which returns Markdown inline).
387
387
  Use the skill CLI to persist digests to disk.
388
388
 
389
389
  Read-only.`,
390
- inputSchema: {
390
+ inputSchema: z.object({
391
391
  limit: z
392
392
  .number()
393
393
  .min(1)
394
394
  .max(50)
395
395
  .optional()
396
396
  .describe("Maximum entries to show. Default: 10"),
397
- },
397
+ }),
398
398
  }, async ({ limit }) => {
399
399
  try {
400
400
  const maxEntries = limit ?? 10;
@@ -26,6 +26,6 @@
26
26
  * These govern the El Salvador market's two sides. Same word, different
27
27
  * contracts, different way of getting a vote.
28
28
  */
29
- import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
29
+ import { McpServer } from "@modelcontextprotocol/server";
30
30
  export declare function registerAtStakeLegionTools(server: McpServer): void;
31
31
  //# sourceMappingURL=at-stake-legion.tools.d.ts.map
@@ -81,13 +81,13 @@ export function registerAtStakeLegionTools(server) {
81
81
  "shares with atstake_mint_complete_set.\n\n" +
82
82
  "Read-only. Defaults to the unlocked wallet, which requires one; pass an address " +
83
83
  "to read anyone without unlocking.",
84
- inputSchema: {
84
+ inputSchema: z.object({
85
85
  side: SIDE_ARG,
86
86
  address: z
87
87
  .string()
88
88
  .optional()
89
89
  .describe("Whose eligibility to report. Defaults to the unlocked wallet."),
90
- },
90
+ }),
91
91
  }, async ({ side, address }) => {
92
92
  try {
93
93
  const chosen = resolveSide(side);
@@ -175,7 +175,7 @@ export function registerAtStakeLegionTools(server) {
175
175
  "detail and the conclude forecast come from atstake_legion_get_proposal, which " +
176
176
  "reads the chain directly.\n\n" +
177
177
  "Read-only, no wallet needed.",
178
- inputSchema: {
178
+ inputSchema: z.object({
179
179
  side: z
180
180
  .enum(["yes", "no", "bonded", "idle", "both"])
181
181
  .optional()
@@ -186,7 +186,7 @@ export function registerAtStakeLegionTools(server) {
186
186
  .optional()
187
187
  .describe('Only proposals in this phase: "voting", "concludable", "passed", ' +
188
188
  '"failed", "expired", "pending".'),
189
- },
189
+ }),
190
190
  }, async ({ side, phase }) => {
191
191
  try {
192
192
  const response = await fetch(AT_STAKE_API);
@@ -257,10 +257,10 @@ export function registerAtStakeLegionTools(server) {
257
257
  "this is a forecast, not a promise — but it is the forecast that tells you " +
258
258
  "whether a conclude is worth the gas.\n\n" +
259
259
  "Read-only, no wallet needed.",
260
- inputSchema: {
260
+ inputSchema: z.object({
261
261
  side: SIDE_ARG,
262
262
  proposal_id: z.number().int().positive().describe("The proposal id."),
263
- },
263
+ }),
264
264
  }, async ({ side, proposal_id }) => {
265
265
  try {
266
266
  const chosen = resolveSide(side);
@@ -340,7 +340,7 @@ export function registerAtStakeLegionTools(server) {
340
340
  "legion-wide interval between proposals. Requires the minimum share balance, " +
341
341
  "which conclude re-reads — selling out before it concludes forfeits the payout. " +
342
342
  "All three fields are ASCII-only. Requires an unlocked wallet.",
343
- inputSchema: {
343
+ inputSchema: z.object({
344
344
  side: SIDE_ARG,
345
345
  title: z
346
346
  .string()
@@ -353,7 +353,7 @@ export function registerAtStakeLegionTools(server) {
353
353
  .string()
354
354
  .describe(`Public URL a voter can reproduce the claim from. ASCII, max ` +
355
355
  `${MAX_LINK_LENGTH} characters.`),
356
- },
356
+ }),
357
357
  }, async ({ side, title, description, link }) => {
358
358
  try {
359
359
  const chosen = resolveSide(side);
@@ -452,7 +452,7 @@ export function registerAtStakeLegionTools(server) {
452
452
  "State what you checked, not that you agree. A proposer cannot vote on their " +
453
453
  "own proposal, and each address votes once. Voting opens two blocks after " +
454
454
  "propose. Requires the minimum share balance and an unlocked wallet.",
455
- inputSchema: {
455
+ inputSchema: z.object({
456
456
  side: SIDE_ARG,
457
457
  proposal_id: z.number().int().positive().describe("The proposal id."),
458
458
  support: z
@@ -462,7 +462,7 @@ export function registerAtStakeLegionTools(server) {
462
462
  .string()
463
463
  .describe(`Why, in your own words — what you checked and what you found. ASCII, max ` +
464
464
  `${MAX_RATIONALE_LENGTH} characters. Permanent and public.`),
465
- },
465
+ }),
466
466
  }, async ({ side, proposal_id, support, rationale }) => {
467
467
  try {
468
468
  const chosen = resolveSide(side);
@@ -551,10 +551,10 @@ export function registerAtStakeLegionTools(server) {
551
551
  "The payout is shares of the proposer's own side while the market trades, and " +
552
552
  "an sBTC credit once it has resolved. The caller pays gas and receives nothing.\n\n" +
553
553
  "Requires an unlocked wallet.",
554
- inputSchema: {
554
+ inputSchema: z.object({
555
555
  side: SIDE_ARG,
556
556
  proposal_id: z.number().int().positive().describe("The proposal id."),
557
- },
557
+ }),
558
558
  }, async ({ side, proposal_id }) => {
559
559
  try {
560
560
  const chosen = resolveSide(side);
@@ -643,7 +643,7 @@ export function registerAtStakeLegionTools(server) {
643
643
  "Permissionless, once-only, and callable only after the market resolves and all " +
644
644
  "live proposals have settled. Pays the caller nothing. Requires an unlocked " +
645
645
  "wallet.",
646
- inputSchema: { side: SIDE_ARG },
646
+ inputSchema: z.object({ side: SIDE_ARG }),
647
647
  }, async ({ side }) => {
648
648
  try {
649
649
  const chosen = resolveSide(side);
@@ -711,7 +711,7 @@ export function registerAtStakeLegionTools(server) {
711
711
  "pays out in claim order until it runs dry, and the unpaid remainder stays on " +
712
712
  "your credit.\n\n" +
713
713
  "Requires an unlocked wallet.",
714
- inputSchema: { side: SIDE_ARG },
714
+ inputSchema: z.object({ side: SIDE_ARG }),
715
715
  }, async ({ side }) => {
716
716
  try {
717
717
  const chosen = resolveSide(side);
@@ -24,6 +24,6 @@
24
24
  * and building one belongs in a prover, not in an MCP argument list. The
25
25
  * permissionless NO settlement, `resolve-idle`, takes no arguments and is here.
26
26
  */
27
- import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
27
+ import { McpServer } from "@modelcontextprotocol/server";
28
28
  export declare function registerAtStakeTools(server: McpServer): void;
29
29
  //# sourceMappingURL=at-stake.tools.d.ts.map
@@ -56,7 +56,7 @@ export function registerAtStakeTools(server) {
56
56
  "no admin key and no oracle: it settles YES only on a Bitcoin SPV proof, and NO " +
57
57
  "by anyone calling resolve-idle after the close height.\n\n" +
58
58
  "Read-only, no wallet needed.",
59
- inputSchema: {},
59
+ inputSchema: z.object({}),
60
60
  }, async () => {
61
61
  try {
62
62
  const [market, burnHeight] = await Promise.all([
@@ -108,12 +108,12 @@ export function registerAtStakeTools(server) {
108
108
  "position is the DIFFERENCE between the two.\n\n" +
109
109
  "Read-only. Defaults to the unlocked wallet, which requires one; pass an address " +
110
110
  "to read anyone without unlocking.",
111
- inputSchema: {
111
+ inputSchema: z.object({
112
112
  address: z
113
113
  .string()
114
114
  .optional()
115
115
  .describe("Stacks address to read. Defaults to the unlocked wallet."),
116
- },
116
+ }),
117
117
  }, async ({ address }) => {
118
118
  try {
119
119
  const who = address ?? (await getAtStakeAccount()).address;
@@ -168,7 +168,7 @@ export function registerAtStakeTools(server) {
168
168
  "arguing either side in a legion should be reading these on Bitcoin directly " +
169
169
  "rather than taking the market's word for the balances.\n\n" +
170
170
  "Read-only, no wallet needed.",
171
- inputSchema: {},
171
+ inputSchema: z.object({}),
172
172
  }, async () => {
173
173
  try {
174
174
  const [addresses, scripts, indices] = await Promise.all([
@@ -210,14 +210,14 @@ export function registerAtStakeTools(server) {
210
210
  "Reversible before the market resolves: atstake_merge_complete_set hands any " +
211
211
  "matched pair back for its sat. Spends real sBTC and meters against the " +
212
212
  "wallet's sats rail. Requires an unlocked wallet.",
213
- inputSchema: {
213
+ inputSchema: z.object({
214
214
  sats: z
215
215
  .number()
216
216
  .int()
217
217
  .positive()
218
218
  .describe("sBTC sats to escrow. Mints this many shares of EACH side. The legion " +
219
219
  "minimum is 1,000."),
220
- },
220
+ }),
221
221
  }, async ({ sats }) => {
222
222
  try {
223
223
  const account = await getAtStakeAccount();
@@ -279,13 +279,13 @@ export function registerAtStakeTools(server) {
279
279
  "position, never the directional part.\n\n" +
280
280
  "Only works while the market is open — after resolve, use atstake_redeem. " +
281
281
  "Requires an unlocked wallet.",
282
- inputSchema: {
282
+ inputSchema: z.object({
283
283
  sats: z
284
284
  .number()
285
285
  .int()
286
286
  .positive()
287
287
  .describe("Matched pairs to merge back into sBTC."),
288
- },
288
+ }),
289
289
  }, async ({ sats }) => {
290
290
  try {
291
291
  const account = await getAtStakeAccount();
@@ -347,7 +347,7 @@ export function registerAtStakeTools(server) {
347
347
  "comes back in full via atstake_cancel_bid if it never fills.\n\n" +
348
348
  "One resting bid per side at a time. Spends real sBTC and meters against the " +
349
349
  "wallet's sats rail. Requires an unlocked wallet.",
350
- inputSchema: {
350
+ inputSchema: z.object({
351
351
  side: SIDE_ARG,
352
352
  amount: z
353
353
  .number()
@@ -360,7 +360,7 @@ export function registerAtStakeTools(server) {
360
360
  .positive()
361
361
  .describe("sBTC escrowed for the whole bid. Must be less than `amount` — the implied " +
362
362
  "price per share is total_sats/amount."),
363
- },
363
+ }),
364
364
  }, async ({ side, amount, total_sats }) => {
365
365
  try {
366
366
  const chosen = resolveSide(side);
@@ -424,13 +424,13 @@ export function registerAtStakeTools(server) {
424
424
  description: "Read a resting bid: shares wanted, sBTC escrowed, and the price that implies.\n\n" +
425
425
  "Read-only. Defaults to the unlocked wallet, which requires one; pass an address " +
426
426
  "to read anyone without unlocking.",
427
- inputSchema: {
427
+ inputSchema: z.object({
428
428
  side: SIDE_ARG,
429
429
  address: z
430
430
  .string()
431
431
  .optional()
432
432
  .describe("Bidder to read. Defaults to the unlocked wallet."),
433
- },
433
+ }),
434
434
  }, async ({ side, address }) => {
435
435
  try {
436
436
  const chosen = resolveSide(side);
@@ -465,7 +465,7 @@ export function registerAtStakeTools(server) {
465
465
  server.registerTool("atstake_cancel_bid", {
466
466
  description: "Withdraw a resting bid and get the unfilled escrow back.\n\n" +
467
467
  "Requires an unlocked wallet.",
468
- inputSchema: { side: SIDE_ARG },
468
+ inputSchema: z.object({ side: SIDE_ARG }),
469
469
  }, async ({ side }) => {
470
470
  try {
471
471
  const chosen = resolveSide(side);
@@ -516,11 +516,11 @@ export function registerAtStakeTools(server) {
516
516
  "Shares are map entries, not a SIP-010 token, so no fungible-token " +
517
517
  "post-condition can guard this and none is signed. Only works while the market " +
518
518
  "trades. Requires an unlocked wallet.",
519
- inputSchema: {
519
+ inputSchema: z.object({
520
520
  side: SIDE_ARG,
521
521
  amount: z.number().int().positive().describe("Shares to send."),
522
522
  to: z.string().describe("Recipient Stacks principal."),
523
- },
523
+ }),
524
524
  }, async ({ side, amount, to }) => {
525
525
  try {
526
526
  const chosen = resolveSide(side);
@@ -580,7 +580,7 @@ export function registerAtStakeTools(server) {
580
580
  "Only callable after the market resolves. Redeeming burns the whole position in " +
581
581
  "one call, so there is nothing left to redeem a second time.\n\n" +
582
582
  "Requires an unlocked wallet.",
583
- inputSchema: {},
583
+ inputSchema: z.object({}),
584
584
  }, async () => {
585
585
  try {
586
586
  const account = await getAtStakeAccount();
@@ -639,7 +639,7 @@ export function registerAtStakeTools(server) {
639
639
  "their vaults either.\n\n" +
640
640
  "It pays the caller nothing and costs them gas. Moves no assets. Requires an " +
641
641
  "unlocked wallet.",
642
- inputSchema: {},
642
+ inputSchema: z.object({}),
643
643
  }, async () => {
644
644
  try {
645
645
  const account = await getAtStakeAccount();
@@ -10,6 +10,6 @@
10
10
  * Data is fetched from mempool.space API (no authentication required).
11
11
  * Transfer operations require an unlocked wallet.
12
12
  */
13
- import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
13
+ import { McpServer } from "@modelcontextprotocol/server";
14
14
  export declare function registerBitcoinTools(server: McpServer): void;
15
15
  //# sourceMappingURL=bitcoin.tools.d.ts.map
@@ -92,13 +92,13 @@ export function registerBitcoinTools(server) {
92
92
  server.registerTool("get_btc_balance", {
93
93
  description: "Get the BTC balance for a Bitcoin address. " +
94
94
  "Returns both total balance (including unconfirmed) and confirmed balance.",
95
- inputSchema: {
95
+ inputSchema: z.object({
96
96
  address: z
97
97
  .string()
98
98
  .optional()
99
99
  .describe("Bitcoin address to check (bc1... for mainnet, tb1... for testnet). " +
100
100
  "Uses wallet's Bitcoin address if not provided."),
101
- },
101
+ }),
102
102
  }, async ({ address }) => {
103
103
  try {
104
104
  const btcAddress = await getBtcAddress(address);
@@ -181,7 +181,7 @@ export function registerBitcoinTools(server) {
181
181
  server.registerTool("get_btc_utxos", {
182
182
  description: "List all UTXOs (Unspent Transaction Outputs) for a Bitcoin address. " +
183
183
  "Useful for debugging, transparency, and understanding transaction inputs.",
184
- inputSchema: {
184
+ inputSchema: z.object({
185
185
  address: z
186
186
  .string()
187
187
  .optional()
@@ -191,7 +191,7 @@ export function registerBitcoinTools(server) {
191
191
  .optional()
192
192
  .default(false)
193
193
  .describe("Only return confirmed UTXOs (default: false)"),
194
- },
194
+ }),
195
195
  }, async ({ address, confirmedOnly }) => {
196
196
  try {
197
197
  const btcAddress = await getBtcAddress(address);
@@ -219,7 +219,7 @@ export function registerBitcoinTools(server) {
219
219
  "Requires an unlocked wallet with BTC balance. " +
220
220
  "By default, only uses cardinal UTXOs (safe to spend - no inscriptions). " +
221
221
  "Set includeOrdinals=true to allow spending ordinal UTXOs (advanced users only).",
222
- inputSchema: {
222
+ inputSchema: z.object({
223
223
  recipient: z
224
224
  .string()
225
225
  .describe("Bitcoin address to send to (bc1... for mainnet, tb1... for testnet)"),
@@ -242,7 +242,7 @@ export function registerBitcoinTools(server) {
242
242
  .default(false)
243
243
  .describe("Include ordinal UTXOs (contains inscriptions). Default: false (cardinal only). " +
244
244
  "WARNING: Setting this to true may destroy valuable inscriptions!"),
245
- },
245
+ }),
246
246
  }, async ({ recipient, amount, feeRate, includeOrdinals }) => {
247
247
  try {
248
248
  validateRecipientNetwork(recipient);
@@ -350,7 +350,7 @@ export function registerBitcoinTools(server) {
350
350
  "Cardinal UTXOs are regular Bitcoin outputs that do not contain ordinal inscriptions or rune balances. " +
351
351
  "These UTXOs can be safely used for regular Bitcoin transfers and fees. " +
352
352
  "Backed by the Unisat indexer; set UNISAT_API_KEY to lift free-tier rate limits.",
353
- inputSchema: {
353
+ inputSchema: z.object({
354
354
  address: z
355
355
  .string()
356
356
  .optional()
@@ -360,7 +360,7 @@ export function registerBitcoinTools(server) {
360
360
  .optional()
361
361
  .default(false)
362
362
  .describe("Only return confirmed UTXOs (default: false)"),
363
- },
363
+ }),
364
364
  }, async ({ address, confirmedOnly }) => {
365
365
  try {
366
366
  const btcAddress = await getBtcAddress(address);
@@ -388,7 +388,7 @@ export function registerBitcoinTools(server) {
388
388
  description: "Get ordinal UTXOs (contain inscriptions or runes - do not spend). " +
389
389
  "Ordinal UTXOs carry inscriptions or rune balances and should not be spent in regular transfers. " +
390
390
  "Backed by the Unisat indexer; set UNISAT_API_KEY to lift free-tier rate limits.",
391
- inputSchema: {
391
+ inputSchema: z.object({
392
392
  address: z
393
393
  .string()
394
394
  .optional()
@@ -398,7 +398,7 @@ export function registerBitcoinTools(server) {
398
398
  .optional()
399
399
  .default(false)
400
400
  .describe("Only return confirmed UTXOs (default: false)"),
401
- },
401
+ }),
402
402
  }, async ({ address, confirmedOnly }) => {
403
403
  try {
404
404
  const btcAddress = await getBtcAddress(address);
@@ -426,12 +426,12 @@ export function registerBitcoinTools(server) {
426
426
  description: "Get all inscriptions owned by a Bitcoin address. " +
427
427
  "Returns inscription IDs, content types, and metadata. " +
428
428
  "Backed by the Unisat indexer; set UNISAT_API_KEY to lift free-tier rate limits.",
429
- inputSchema: {
429
+ inputSchema: z.object({
430
430
  address: z
431
431
  .string()
432
432
  .optional()
433
433
  .describe("Bitcoin address to check. Uses wallet's Bitcoin address if not provided."),
434
- },
434
+ }),
435
435
  }, async ({ address }) => {
436
436
  try {
437
437
  const btcAddress = await getBtcAddress(address);
@@ -1,3 +1,3 @@
1
- import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
1
+ import { McpServer } from "@modelcontextprotocol/server";
2
2
  export declare function registerBitflowTools(server: McpServer): void;
3
3
  //# sourceMappingURL=bitflow.tools.d.ts.map