@aibtc/mcp-server 1.71.0 → 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 +36 -3
- package/dist/bridge/index.js +2 -2
- package/dist/config/networks.js +4 -1
- package/dist/index.js +118 -42
- 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 +2 -2
- package/dist/tools/endpoint.tools.d.ts +1 -1
- package/dist/tools/endpoint.tools.js +14 -13
- 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 +36 -36
- 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 +12 -12
- 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/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 +13 -13
- 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 +5 -5
- package/dist/tools/yield-hunter.tools.d.ts +1 -1
- package/dist/tools/yield-hunter.tools.js +6 -6
- package/dist/utils/fee.js +1 -1
- package/package.json +4 -3
- package/skill/SKILL.md +1 -1
package/README.md
CHANGED
|
@@ -27,7 +27,9 @@ Bitcoin-native MCP server for AI agents: BTC/STX wallets, DeFi yield, sBTC peg,
|
|
|
27
27
|
npx @aibtc/mcp-server@latest --install
|
|
28
28
|
```
|
|
29
29
|
|
|
30
|
-
|
|
30
|
+
This configures Claude Code and creates the agent's wallet: it prints the Stacks and Bitcoin addresses, a generated password and the 24-word mnemonic **once**. Write both down. The mnemonic is stored only encrypted (AES-256-GCM) in `~/.aibtc/` on this machine, and the password is not saved anywhere; the agent asks for it to unlock, and you can change it with `wallet_rotate_password`. If a wallet for the network already exists it is kept. The wallet is only created when the install runs in an interactive terminal (never into a pipe or CI log). Pass `--no-wallet` to skip it and create or import one from the agent instead.
|
|
31
|
+
|
|
32
|
+
Restart your terminal, send a little STX to the printed address, and ask the agent to unlock the wallet and make a paid inference call.
|
|
31
33
|
|
|
32
34
|
### Claude Desktop (App)
|
|
33
35
|
|
|
@@ -114,6 +116,37 @@ npx @aibtc/mcp-server@latest --install --cursor --testnet # Cursor, testnet
|
|
|
114
116
|
|
|
115
117
|
> **Why npx?** Using `npx @aibtc/mcp-server@latest` ensures you always get the newest version automatically. Global installs (`npm install -g`) won't auto-update.
|
|
116
118
|
|
|
119
|
+
### Tool Profiles
|
|
120
|
+
|
|
121
|
+
Every tool definition is loaded into the model's context, so `--install` writes `AIBTC_TOOLS=core`: a lean core of 25 tools: wallet (including `wallet_rotate_password`), balances, STX/BTC/sBTC transfers, x402 (`list_x402_endpoints`, `probe_x402_endpoint`, `execute_x402_endpoint`) and earning (`earning_opportunities`, `bounty_list/get/submit`, `identity_register`).
|
|
122
|
+
|
|
123
|
+
Add groups with `AIBTC_TOOLS=core,defi,ordinals` in the server's `env` (the core is always included), or load everything with `AIBTC_TOOLS=all` / `--profile full`. If `AIBTC_TOOLS` is not set at all, every tool is loaded, so configs written by earlier versions keep all their tools:
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
npx @aibtc/mcp-server@latest --install --profile full # writes AIBTC_TOOLS=all
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
| Group | Tools |
|
|
130
|
+
|-------|-------|
|
|
131
|
+
| `wallet` | Wallet management extras, encrypted credential store |
|
|
132
|
+
| `stacks` | Stacks transactions, contracts, tokens, NFTs, chain queries, nonce tools |
|
|
133
|
+
| `sbtc` | sBTC deposit/withdraw, Styx BTC→sBTC |
|
|
134
|
+
| `bitcoin` | Bitcoin L1 UTXOs, mempool watch |
|
|
135
|
+
| `lightning` | Lightning (Spark) wallet and L402 payments |
|
|
136
|
+
| `stacking` | PoX stacking, dual stacking, StackSpot |
|
|
137
|
+
| `defi` | ALEX, Zest, Bitflow, Jingswap, yield hunter/dashboard, Tenero analytics |
|
|
138
|
+
| `pillar` | Pillar smart wallet |
|
|
139
|
+
| `ordinals` | Inscriptions, runes, PSBT, ordinals marketplace/P2P, taproot multisig |
|
|
140
|
+
| `bns` | BNS names |
|
|
141
|
+
| `identity` | ERC-8004 identity and reputation, message signing |
|
|
142
|
+
| `social` | Nostr, AIBTC inbox |
|
|
143
|
+
| `earn` | Bounty board (create, accept, pay, my views) |
|
|
144
|
+
| `legion` | AIBTC News Legion |
|
|
145
|
+
| `markets` | Stacks prediction market, At Stake |
|
|
146
|
+
| `dev` | Scaffolding, OpenRouter, settings, inference marketplace, arXiv |
|
|
147
|
+
|
|
148
|
+
The server's instructions list the groups that are off, so the agent can tell you which one to enable. The OpenRouter `bridge` starts from the full set and narrows it with its own `--read-only`/`--allow`/`--block` flags.
|
|
149
|
+
|
|
117
150
|
### Manual Configuration
|
|
118
151
|
|
|
119
152
|
If you prefer to configure manually, add the following to your client's config file. The `-y` flag stops npx from prompting for confirmation.
|
|
@@ -181,7 +214,7 @@ NETWORK = "mainnet"
|
|
|
181
214
|
|
|
182
215
|
## Giving Claude a Wallet
|
|
183
216
|
|
|
184
|
-
|
|
217
|
+
`--install` creates a wallet for you (see [Quick Start](#quick-start)). If you installed with `--no-wallet`, configured the client by hand, or want another wallet, the agent can create or import one:
|
|
185
218
|
|
|
186
219
|
### Example Conversation
|
|
187
220
|
|
|
@@ -547,7 +580,7 @@ Or use any SIP-010 token by contract ID: `SP2X...::token-name`
|
|
|
547
580
|
| Environment Variable | Description | Default |
|
|
548
581
|
|---------------------|-------------|---------|
|
|
549
582
|
| `NETWORK` | `mainnet` or `testnet` | `mainnet` |
|
|
550
|
-
| `
|
|
583
|
+
| `AIBTC_TOOLS` | `core`, `core,<group,...>`, or `all` (see [Tool Profiles](#tool-profiles)). `--install` writes `core` | all tools when unset |
|
|
551
584
|
| `CLIENT_MNEMONIC` | (Optional) Pre-configured mnemonic | - |
|
|
552
585
|
| `HIRO_API_KEY` | (Optional) Hiro API key for higher rate limits | - |
|
|
553
586
|
| `SPEND_LIMIT_ENABLED` | Set `false` to disable the wallet spending limit | `true` |
|
package/dist/bridge/index.js
CHANGED
|
@@ -27,8 +27,8 @@
|
|
|
27
27
|
* --network <net> mainnet|testnet for the spawned server (default mainnet)
|
|
28
28
|
* --max-turns <n> tool-call loop cap (default 10)
|
|
29
29
|
*/
|
|
30
|
-
import {
|
|
31
|
-
import {
|
|
30
|
+
import { StdioClientTransport } from "@modelcontextprotocol/client/stdio";
|
|
31
|
+
import { Client } from "@modelcontextprotocol/client";
|
|
32
32
|
import { STATIC_ENDPOINTS } from "../endpoints/registry.js";
|
|
33
33
|
const OPENROUTER_URL = "https://openrouter.ai/api/v1/chat/completions";
|
|
34
34
|
const DEFAULT_MODEL = "anthropic/claude-3.5-haiku";
|
package/dist/config/networks.js
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
export const NETWORK = process.env.NETWORK === "testnet" ? "testnet" : "mainnet";
|
|
2
|
-
|
|
2
|
+
// Base URL for bare-path x402 calls. Fixed per network, not read from the
|
|
3
|
+
// environment: the server loads the .env of whatever directory the client
|
|
4
|
+
// starts it in, and a project's generic API_URL must not redirect payments.
|
|
5
|
+
export const API_URL = NETWORK === "mainnet" ? "https://x402.aibtc.com" : "https://x402.aibtc.dev";
|
|
3
6
|
export function getStacksNetwork(network) {
|
|
4
7
|
return network === "mainnet" ? "mainnet" : "testnet";
|
|
5
8
|
}
|
package/dist/index.js
CHANGED
|
@@ -4,13 +4,17 @@ import fs from "fs/promises";
|
|
|
4
4
|
import path from "path";
|
|
5
5
|
import os from "os";
|
|
6
6
|
import { createRequire } from "module";
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
7
|
+
import { randomBytes } from "crypto";
|
|
8
|
+
import { serveStdio } from "@modelcontextprotocol/server/stdio";
|
|
9
|
+
import { McpServer } from "@modelcontextprotocol/server";
|
|
9
10
|
import { registerAllTools } from "./tools/index.js";
|
|
11
|
+
import { describeSelection, resolveToolSelection } from "./tools/profiles.js";
|
|
10
12
|
import { installBountyHint } from "./tools/bounty-hint.js";
|
|
11
13
|
import { NETWORK, API_URL } from "./config/index.js";
|
|
12
14
|
import { redactSensitive } from "./utils/redact.js";
|
|
13
15
|
import { initializeStorage } from "./utils/storage.js";
|
|
16
|
+
import { getWalletManager } from "./services/wallet-manager.js";
|
|
17
|
+
import { getLightningManager } from "./services/lightning-manager.js";
|
|
14
18
|
const require = createRequire(import.meta.url);
|
|
15
19
|
const packageJson = require("../package.json");
|
|
16
20
|
// =============================================================================
|
|
@@ -64,30 +68,30 @@ async function writeJsonConfig(filePath, config) {
|
|
|
64
68
|
await fs.writeFile(filePath, JSON.stringify(config, null, 2));
|
|
65
69
|
}
|
|
66
70
|
// Standard MCP server entry shared by every JSON-based client config.
|
|
67
|
-
function serverEntry(
|
|
71
|
+
function serverEntry(env) {
|
|
68
72
|
return {
|
|
69
73
|
command: "npx",
|
|
70
74
|
args: ["-y", SERVER_NPM],
|
|
71
|
-
env
|
|
75
|
+
env,
|
|
72
76
|
};
|
|
73
77
|
}
|
|
74
78
|
// Most clients (Claude Code, Claude Desktop, Cursor, Windsurf, Gemini CLI) use
|
|
75
79
|
// the same `{ "mcpServers": { "aibtc": {...} } }` JSON shape.
|
|
76
|
-
async function writeMcpServersJson(configPath,
|
|
80
|
+
async function writeMcpServersJson(configPath, env) {
|
|
77
81
|
const config = await readJsonConfig(configPath);
|
|
78
82
|
const servers = (config.mcpServers ??= {});
|
|
79
|
-
servers["aibtc"] = serverEntry(
|
|
83
|
+
servers["aibtc"] = serverEntry(env);
|
|
80
84
|
await writeJsonConfig(configPath, config);
|
|
81
85
|
}
|
|
82
86
|
// VS Code (.vscode/mcp.json) uses a `servers` key and a typed stdio entry.
|
|
83
|
-
async function writeVsCodeJson(configPath,
|
|
87
|
+
async function writeVsCodeJson(configPath, env) {
|
|
84
88
|
const config = await readJsonConfig(configPath);
|
|
85
89
|
const servers = (config.servers ??= {});
|
|
86
90
|
servers["aibtc"] = {
|
|
87
91
|
type: "stdio",
|
|
88
92
|
command: "npx",
|
|
89
93
|
args: ["-y", SERVER_NPM],
|
|
90
|
-
env
|
|
94
|
+
env,
|
|
91
95
|
};
|
|
92
96
|
await writeJsonConfig(configPath, config);
|
|
93
97
|
}
|
|
@@ -106,7 +110,7 @@ function stripCodexSection(content) {
|
|
|
106
110
|
}
|
|
107
111
|
return out.join("\n");
|
|
108
112
|
}
|
|
109
|
-
async function writeCodexToml(configPath,
|
|
113
|
+
async function writeCodexToml(configPath, env) {
|
|
110
114
|
let existing = "";
|
|
111
115
|
try {
|
|
112
116
|
existing = await fs.readFile(configPath, "utf8");
|
|
@@ -123,7 +127,7 @@ async function writeCodexToml(configPath, network) {
|
|
|
123
127
|
`args = ["-y", "${SERVER_NPM}"]`,
|
|
124
128
|
"",
|
|
125
129
|
"[mcp_servers.aibtc.env]",
|
|
126
|
-
|
|
130
|
+
...Object.entries(env).map(([key, value]) => `${key} = "${value}"`),
|
|
127
131
|
].join("\n");
|
|
128
132
|
const content = (preserved ? `${preserved}\n\n` : "") + block + "\n";
|
|
129
133
|
await fs.mkdir(path.dirname(configPath), { recursive: true });
|
|
@@ -188,23 +192,86 @@ function resolveInstallTarget() {
|
|
|
188
192
|
// Default to the entry explicitly marked as default (flag === null) — Claude Code.
|
|
189
193
|
return matched[0] ?? INSTALL_TARGETS.find((t) => t.flag === null);
|
|
190
194
|
}
|
|
195
|
+
/**
|
|
196
|
+
* Create the agent's wallet during install so a fresh user can fund it right
|
|
197
|
+
* away. The generated password and the mnemonic are printed once and not
|
|
198
|
+
* stored in plain text; the password can be changed with wallet_rotate_password.
|
|
199
|
+
* Returns the Stacks address of the new or already-existing wallet.
|
|
200
|
+
*/
|
|
201
|
+
async function ensureInstallWallet(network) {
|
|
202
|
+
await initializeStorage();
|
|
203
|
+
const walletManager = getWalletManager();
|
|
204
|
+
const existing = (await walletManager.listWallets()).find((w) => w.network === network);
|
|
205
|
+
if (existing) {
|
|
206
|
+
console.log(`\n👛 Existing ${network} wallet kept: ${existing.name} (${existing.address})`);
|
|
207
|
+
return existing.address;
|
|
208
|
+
}
|
|
209
|
+
const password = randomBytes(18).toString("base64url");
|
|
210
|
+
const wallet = await walletManager.createWallet("main", password, network);
|
|
211
|
+
// Print the credentials before anything else can fail: the wallet is already
|
|
212
|
+
// on disk, and without the password it can never be unlocked.
|
|
213
|
+
console.log(`\n👛 Wallet created (${network}), stored encrypted in ~/.aibtc/`);
|
|
214
|
+
console.log(` Stacks: ${wallet.address}`);
|
|
215
|
+
if (wallet.btcAddress)
|
|
216
|
+
console.log(` Bitcoin: ${wallet.btcAddress}`);
|
|
217
|
+
console.log(`\n Password: ${password}`);
|
|
218
|
+
console.log(` Mnemonic: ${wallet.mnemonic}`);
|
|
219
|
+
console.log("\n⚠️ Write both down now. They are shown once and the password is not saved anywhere.");
|
|
220
|
+
console.log(" The password unlocks the wallet; change it any time by asking your agent to rotate it.");
|
|
221
|
+
console.log(" The mnemonic is the only way to recover the wallet and its funds.");
|
|
222
|
+
// Same unified setup as wallet_create: a Lightning wallet derived from the
|
|
223
|
+
// same mnemonic (mainnet only). Its failure is reported, the main wallet stands.
|
|
224
|
+
try {
|
|
225
|
+
const lightning = await getLightningManager().setupFromMainMnemonic(wallet.mnemonic, password, "main", network);
|
|
226
|
+
if (lightning.kind === "setup") {
|
|
227
|
+
console.log(` Lightning: ${lightning.depositAddress} (deposit address, same mnemonic)`);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
catch (error) {
|
|
231
|
+
console.error(` Lightning setup failed: ${redactSensitive(error instanceof Error ? error.message : String(error))}. ` +
|
|
232
|
+
"Ask your agent to run lightning_create later.");
|
|
233
|
+
}
|
|
234
|
+
return wallet.address;
|
|
235
|
+
}
|
|
191
236
|
async function runInstall() {
|
|
192
237
|
const network = process.argv.includes("--testnet") ? "testnet" : "mainnet";
|
|
238
|
+
// Only --profile full installs everything; resolving against AIBTC_TOOLS=core
|
|
239
|
+
// also validates the flag.
|
|
240
|
+
const fullProfile = resolveToolSelection(process.argv, { AIBTC_TOOLS: "core" }).all;
|
|
193
241
|
const target = resolveInstallTarget();
|
|
194
242
|
const configPath = target.configPath();
|
|
195
243
|
console.log(`🔧 Installing @aibtc/mcp-server to ${target.label}...\n`);
|
|
196
|
-
|
|
244
|
+
const env = { NETWORK: network };
|
|
245
|
+
env.AIBTC_TOOLS = fullProfile ? "all" : "core";
|
|
246
|
+
await target.write(configPath, env);
|
|
197
247
|
console.log("✅ Successfully installed!\n");
|
|
198
248
|
console.log(` Client: ${target.label}`);
|
|
199
249
|
console.log(` Config: ${configPath}`);
|
|
200
250
|
console.log(` Network: ${network}`);
|
|
201
|
-
console.log("
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
if (
|
|
206
|
-
|
|
251
|
+
console.log(` Tools: ${fullProfile ? "all" : "lean core (set AIBTC_TOOLS=core,defi,ordinals,... in the env, or re-run with --profile full)"}`);
|
|
252
|
+
// The wallet's password and mnemonic are printed once, so only create it when
|
|
253
|
+
// a person is reading the terminal, never into a pipe, file or CI log.
|
|
254
|
+
let address = null;
|
|
255
|
+
if (process.argv.includes("--no-wallet")) {
|
|
256
|
+
// Explicitly skipped
|
|
257
|
+
}
|
|
258
|
+
else if (!process.stdout.isTTY) {
|
|
259
|
+
console.log("\n👛 Wallet not created: output is not a terminal, and the password and mnemonic");
|
|
260
|
+
console.log(" are only ever printed to one. Re-run --install in a terminal, or ask the agent to create one.");
|
|
261
|
+
}
|
|
262
|
+
else {
|
|
263
|
+
address = await ensureInstallWallet(network);
|
|
207
264
|
}
|
|
265
|
+
console.log(`\n📋 ${target.restart}, then try:`);
|
|
266
|
+
const fund = network === "testnet"
|
|
267
|
+
? "testnet STX from https://explorer.hiro.so/sandbox/faucet?chain=testnet"
|
|
268
|
+
: "0.01 STX";
|
|
269
|
+
console.log(address
|
|
270
|
+
? ` 1. Send ${fund} to ${address}`
|
|
271
|
+
: ` 1. Ask your agent: "What's your wallet address?" and send it ${fund}`);
|
|
272
|
+
console.log(` 2. Ask your agent: "Unlock my wallet" and give it the password`);
|
|
273
|
+
const x402Host = network === "testnet" ? "x402.aibtc.dev" : "x402.aibtc.com";
|
|
274
|
+
console.log(` 3. Ask it: "Make a paid inference call on ${x402Host}" (0.001 STX per call)\n`);
|
|
208
275
|
}
|
|
209
276
|
// =============================================================================
|
|
210
277
|
// YIELD HUNTER DAEMON
|
|
@@ -251,36 +318,45 @@ else if (process.argv.includes("--install") || process.argv.includes("install"))
|
|
|
251
318
|
}
|
|
252
319
|
else {
|
|
253
320
|
// Normal MCP server mode
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
"
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
321
|
+
// One instance per connection. serveStdio picks the protocol era from the
|
|
322
|
+
// opening message (2025-era `initialize` or a 2026-07-28 envelope) and pins
|
|
323
|
+
// the instance built by this factory to the connection.
|
|
324
|
+
function createServer(toolSelection) {
|
|
325
|
+
const server = new McpServer({
|
|
326
|
+
name: "aibtc-mcp-server",
|
|
327
|
+
version: packageJson.version,
|
|
328
|
+
}, {
|
|
329
|
+
instructions: [
|
|
330
|
+
"This server lets an agent earn and move real Bitcoin/sBTC on Stacks.",
|
|
331
|
+
"",
|
|
332
|
+
"Hunt bounties. The most direct way to earn here is the sBTC bounty board:",
|
|
333
|
+
"call bounty_list to see open bounties, bounty_get for the task detail, do",
|
|
334
|
+
"the work, then bounty_submit your deliverable. Accepted submissions are paid",
|
|
335
|
+
"in sBTC. Check periodically for new open bounties.",
|
|
336
|
+
"",
|
|
337
|
+
"For the full menu of ways to put assets to work (stacking, DeFi yield,",
|
|
338
|
+
"trading, paid endpoints), call earning_opportunities. Submitting to a bounty",
|
|
339
|
+
"requires a Registered (L1+) on-chain identity — see identity_register.",
|
|
340
|
+
"",
|
|
341
|
+
describeSelection(toolSelection),
|
|
342
|
+
].join("\n"),
|
|
343
|
+
});
|
|
344
|
+
// Append a bounty-board hint to spend/onboarding-tool output.
|
|
345
|
+
// Must wrap registerTool before registration; restore it after.
|
|
346
|
+
const restoreBountyHint = installBountyHint(server);
|
|
347
|
+
// Register all tools from the modular registry
|
|
348
|
+
registerAllTools(server, toolSelection);
|
|
349
|
+
restoreBountyHint();
|
|
350
|
+
return server;
|
|
351
|
+
}
|
|
277
352
|
async function main() {
|
|
353
|
+
const toolSelection = resolveToolSelection();
|
|
278
354
|
await initializeStorage();
|
|
279
|
-
|
|
280
|
-
await server.connect(transport);
|
|
355
|
+
serveStdio(() => createServer(toolSelection), { onerror: (error) => console.error("MCP error:", redactSensitive(String(error))) });
|
|
281
356
|
console.error("aibtc-mcp-server running on stdio");
|
|
282
357
|
console.error(`Network: ${NETWORK}`);
|
|
283
358
|
console.error(`API URL: ${API_URL}`);
|
|
359
|
+
console.error(`Tools: ${toolSelection.all ? "all" : ["core", ...toolSelection.groups].join(", ")}`);
|
|
284
360
|
}
|
|
285
361
|
main().catch((error) => {
|
|
286
362
|
console.error("Fatal error:", redactSensitive(String(error)));
|
|
@@ -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
|