@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.
- package/README.md +45 -8
- package/dist/bridge/index.js +4 -4
- package/dist/config/contracts.d.ts +4 -4
- package/dist/config/contracts.js +2 -2
- package/dist/config/networks.js +4 -1
- package/dist/endpoints/registry.d.ts +8 -26
- package/dist/endpoints/registry.js +7 -932
- package/dist/index.js +118 -42
- package/dist/services/stacking.service.d.ts +144 -45
- package/dist/services/stacking.service.js +416 -123
- package/dist/services/x402-discovery.service.d.ts +81 -0
- package/dist/services/x402-discovery.service.js +170 -0
- package/dist/tools/arxiv-research.tools.d.ts +1 -1
- package/dist/tools/arxiv-research.tools.js +6 -6
- package/dist/tools/at-stake-legion.tools.d.ts +1 -1
- package/dist/tools/at-stake-legion.tools.js +14 -14
- package/dist/tools/at-stake.tools.d.ts +1 -1
- package/dist/tools/at-stake.tools.js +17 -17
- package/dist/tools/bitcoin.tools.d.ts +1 -1
- package/dist/tools/bitcoin.tools.js +12 -12
- package/dist/tools/bitflow.tools.d.ts +1 -1
- package/dist/tools/bitflow.tools.js +21 -21
- package/dist/tools/bns.tools.d.ts +1 -1
- package/dist/tools/bns.tools.js +18 -18
- package/dist/tools/bounty-hint.d.ts +1 -1
- package/dist/tools/bounty-scanner.tools.d.ts +1 -1
- package/dist/tools/bounty-scanner.tools.js +20 -20
- package/dist/tools/child-inscription.tools.d.ts +1 -1
- package/dist/tools/child-inscription.tools.js +6 -6
- package/dist/tools/competition.tools.d.ts +1 -1
- package/dist/tools/competition.tools.js +4 -4
- package/dist/tools/contract.tools.d.ts +1 -1
- package/dist/tools/contract.tools.js +8 -8
- package/dist/tools/credentials.tools.d.ts +1 -1
- package/dist/tools/credentials.tools.js +8 -8
- package/dist/tools/defi.tools.d.ts +1 -1
- package/dist/tools/defi.tools.js +21 -21
- package/dist/tools/dual-stacking.tools.d.ts +1 -1
- package/dist/tools/dual-stacking.tools.js +7 -7
- package/dist/tools/earning.tools.d.ts +1 -1
- package/dist/tools/earning.tools.js +5 -5
- package/dist/tools/endpoint.tools.d.ts +1 -1
- package/dist/tools/endpoint.tools.js +92 -49
- package/dist/tools/identity.tools.d.ts +1 -1
- package/dist/tools/identity.tools.js +19 -19
- package/dist/tools/inbox-x402.tools.d.ts +1 -1
- package/dist/tools/inbox-x402.tools.js +2 -2
- package/dist/tools/inbox.tools.d.ts +1 -1
- package/dist/tools/inbox.tools.js +2 -2
- package/dist/tools/index.d.ts +6 -3
- package/dist/tools/index.js +69 -58
- package/dist/tools/inference-marketplace.tools.d.ts +1 -1
- package/dist/tools/inference-marketplace.tools.js +10 -10
- package/dist/tools/jingswap.tools.d.ts +1 -1
- package/dist/tools/jingswap.tools.js +30 -30
- package/dist/tools/legion.tools.d.ts +1 -1
- package/dist/tools/legion.tools.js +20 -20
- package/dist/tools/lightning.tools.d.ts +1 -1
- package/dist/tools/lightning.tools.js +14 -14
- package/dist/tools/mempool.tools.d.ts +1 -1
- package/dist/tools/mempool.tools.js +5 -5
- package/dist/tools/news.tools.d.ts +1 -1
- package/dist/tools/news.tools.js +2 -1
- package/dist/tools/nft.tools.d.ts +1 -1
- package/dist/tools/nft.tools.js +12 -12
- package/dist/tools/nonce.tools.d.ts +1 -1
- package/dist/tools/nonce.tools.js +8 -8
- package/dist/tools/nostr.tools.d.ts +1 -1
- package/dist/tools/nostr.tools.js +12 -12
- package/dist/tools/openrouter.tools.d.ts +1 -1
- package/dist/tools/openrouter.tools.js +4 -4
- package/dist/tools/ordinals-marketplace.tools.d.ts +1 -1
- package/dist/tools/ordinals-marketplace.tools.js +10 -10
- package/dist/tools/ordinals-p2p.tools.d.ts +1 -1
- package/dist/tools/ordinals-p2p.tools.js +18 -18
- package/dist/tools/ordinals.tools.d.ts +1 -1
- package/dist/tools/ordinals.tools.js +8 -8
- package/dist/tools/pillar-direct.tools.d.ts +1 -1
- package/dist/tools/pillar-direct.tools.js +59 -38
- package/dist/tools/pillar.tools.d.ts +1 -1
- package/dist/tools/pillar.tools.js +28 -26
- package/dist/tools/profiles.d.ts +51 -0
- package/dist/tools/profiles.js +111 -0
- package/dist/tools/psbt.tools.d.ts +1 -1
- package/dist/tools/psbt.tools.js +8 -8
- package/dist/tools/query.tools.d.ts +1 -1
- package/dist/tools/query.tools.js +12 -12
- package/dist/tools/relay-diagnostic.tools.d.ts +1 -1
- package/dist/tools/relay-diagnostic.tools.js +3 -3
- package/dist/tools/reputation.tools.d.ts +1 -1
- package/dist/tools/reputation.tools.js +22 -22
- package/dist/tools/runes.tools.d.ts +1 -1
- package/dist/tools/runes.tools.js +18 -18
- package/dist/tools/sbtc.tools.d.ts +1 -1
- package/dist/tools/sbtc.tools.js +14 -33
- package/dist/tools/scaffold.tools.d.ts +1 -1
- package/dist/tools/scaffold.tools.js +4 -4
- package/dist/tools/settings.tools.d.ts +1 -1
- package/dist/tools/settings.tools.js +9 -9
- package/dist/tools/signing.tools.d.ts +1 -1
- package/dist/tools/signing.tools.js +20 -20
- package/dist/tools/skill-mappings.js +4 -0
- package/dist/tools/souldinals.tools.d.ts +1 -1
- package/dist/tools/souldinals.tools.js +10 -10
- package/dist/tools/stacking-lottery.tools.d.ts +1 -1
- package/dist/tools/stacking-lottery.tools.js +11 -11
- package/dist/tools/stacking.tools.d.ts +1 -1
- package/dist/tools/stacking.tools.js +358 -50
- package/dist/tools/stacks-market.tools.d.ts +1 -1
- package/dist/tools/stacks-market.tools.js +22 -22
- package/dist/tools/styx.tools.d.ts +1 -1
- package/dist/tools/styx.tools.js +11 -11
- package/dist/tools/taproot-multisig.tools.d.ts +1 -1
- package/dist/tools/taproot-multisig.tools.js +5 -5
- package/dist/tools/tenero.tools.d.ts +1 -1
- package/dist/tools/tenero.tools.js +22 -22
- package/dist/tools/tokens.tools.d.ts +1 -1
- package/dist/tools/tokens.tools.js +10 -10
- package/dist/tools/transfer.tools.d.ts +1 -1
- package/dist/tools/transfer.tools.js +4 -4
- package/dist/tools/wallet-management.tools.d.ts +1 -1
- package/dist/tools/wallet-management.tools.js +16 -16
- package/dist/tools/wallet.tools.d.ts +1 -1
- package/dist/tools/wallet.tools.js +2 -2
- package/dist/tools/yield-dashboard.tools.d.ts +1 -1
- package/dist/tools/yield-dashboard.tools.js +18 -27
- package/dist/tools/yield-hunter.tools.d.ts +1 -1
- package/dist/tools/yield-hunter.tools.js +6 -6
- package/dist/utils/bitcoin.d.ts +8 -0
- package/dist/utils/bitcoin.js +24 -0
- package/dist/utils/fee.js +1 -1
- package/package.json +8 -7
- package/skill/SKILL.md +1 -1
- 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/
|
|
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/
|
|
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/
|
|
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/
|
|
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);
|