litcoin-mcp 1.0.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 (3) hide show
  1. package/README.md +88 -0
  2. package/package.json +19 -0
  3. package/src/index.js +656 -0
package/README.md ADDED
@@ -0,0 +1,88 @@
1
+ # LITCOIN MCP Server
2
+
3
+ MCP server for the [LITCOIN](https://litcoiin.xyz) proof-of-comprehension protocol on Base. Gives any AI agent full protocol access through the Model Context Protocol.
4
+
5
+ ## Setup
6
+
7
+ ### Claude Desktop / Claude Code
8
+
9
+ Add to your MCP config (`claude_desktop_config.json` or `.claude.json`):
10
+
11
+ ```json
12
+ {
13
+ "mcpServers": {
14
+ "litcoin": {
15
+ "command": "npx",
16
+ "args": ["-y", "litcoin-mcp"],
17
+ "env": {
18
+ "BANKR_API_KEY": "bk_YOUR_KEY"
19
+ }
20
+ }
21
+ }
22
+ }
23
+ ```
24
+
25
+ ### Cursor
26
+
27
+ Add to `.cursor/mcp.json`:
28
+
29
+ ```json
30
+ {
31
+ "mcpServers": {
32
+ "litcoin": {
33
+ "command": "npx",
34
+ "args": ["-y", "litcoin-mcp"],
35
+ "env": {
36
+ "BANKR_API_KEY": "bk_YOUR_KEY"
37
+ }
38
+ }
39
+ }
40
+ }
41
+ ```
42
+
43
+ Get a Bankr API key at [bankr.bot/api](https://bankr.bot/api).
44
+
45
+ ## Available Tools
46
+
47
+ | Tool | Description |
48
+ |------|-------------|
49
+ | `litcoin_balance` | Check LITCOIN, LITCREDIT, staking tier, mining boost, escrow balance |
50
+ | `litcoin_network` | Network stats: active miners, treasury, rewards, oracle prices |
51
+ | `litcoin_mine` | Mine one round (solve a comprehension challenge, earn LITCOIN) |
52
+ | `litcoin_claim` | Claim accumulated mining rewards on-chain |
53
+ | `litcoin_claimable` | Check how much is available to claim |
54
+ | `litcoin_stake` | Stake into a tier (1=Spark, 2=Circuit, 3=Core, 4=Architect) |
55
+ | `litcoin_unstake` | Unstake after lock period |
56
+ | `litcoin_open_vault` | Open a vault with LITCOIN collateral |
57
+ | `litcoin_mint` | Mint LITCREDIT against a vault |
58
+ | `litcoin_repay` | Repay LITCREDIT debt |
59
+ | `litcoin_add_collateral` | Add collateral to a vault |
60
+ | `litcoin_close_vault` | Close a vault |
61
+ | `litcoin_vaults` | List all vaults with health ratios |
62
+ | `litcoin_deposit_escrow` | Deposit LITCREDIT for compute spending |
63
+ | `litcoin_compute` | Send a prompt to the compute marketplace |
64
+ | `litcoin_create_guild` | Create a mining guild |
65
+ | `litcoin_join_guild` | Join a guild with deposit |
66
+ | `litcoin_leave_guild` | Leave a guild |
67
+ | `litcoin_faucet` | Claim 5M free LITCOIN (one-time bootstrap) |
68
+
69
+ ## Example Conversation
70
+
71
+ > **You:** Check my LITCOIN balance
72
+ >
73
+ > **Agent:** *(calls litcoin_balance)* You have 3,200,000 LITCOIN and 0 LITCREDIT. You're not staked yet.
74
+ >
75
+ > **You:** Stake into Circuit tier
76
+ >
77
+ > **Agent:** *(calls litcoin_stake with tier=2)* Done! Staked 5M LITCOIN into Circuit tier. Mining boost: 1.25x.
78
+ >
79
+ > **You:** Mine 5 rounds
80
+ >
81
+ > **Agent:** *(calls litcoin_mine 5 times)* Mined 5 rounds. Earned 750,000 LITCOIN total.
82
+
83
+ ## Links
84
+
85
+ - [LITCOIN Website](https://litcoiin.xyz)
86
+ - [Full Docs (AI-readable)](https://litcoiin.xyz/docs.md)
87
+ - [SDK on PyPI](https://pypi.org/project/litcoin/)
88
+ - [Twitter](https://x.com/litcoin_AI)
package/package.json ADDED
@@ -0,0 +1,19 @@
1
+ {
2
+ "name": "litcoin-mcp",
3
+ "version": "1.0.0",
4
+ "description": "MCP server for the LITCOIN proof-of-comprehension protocol on Base. Mine, stake, vault, compute — all from any AI agent.",
5
+ "main": "src/index.js",
6
+ "bin": {
7
+ "litcoin-mcp": "src/index.js"
8
+ },
9
+ "type": "module",
10
+ "scripts": {
11
+ "start": "node src/index.js"
12
+ },
13
+ "keywords": ["mcp", "litcoin", "mining", "crypto", "ai-agent", "base", "defi"],
14
+ "license": "MIT",
15
+ "dependencies": {
16
+ "@modelcontextprotocol/sdk": "^1.12.0",
17
+ "zod": "^3.25.0"
18
+ }
19
+ }
package/src/index.js ADDED
@@ -0,0 +1,656 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * LITCOIN MCP Server
5
+ *
6
+ * Exposes the full LITCOIN protocol to any MCP-compatible AI agent.
7
+ * Mine, claim, stake, vault, compute, guilds — all through tool calls.
8
+ *
9
+ * Usage:
10
+ * BANKR_API_KEY=bk_... npx litcoin-mcp
11
+ *
12
+ * Claude Desktop / Claude Code config:
13
+ * {
14
+ * "mcpServers": {
15
+ * "litcoin": {
16
+ * "command": "npx",
17
+ * "args": ["litcoin-mcp"],
18
+ * "env": { "BANKR_API_KEY": "bk_YOUR_KEY" }
19
+ * }
20
+ * }
21
+ * }
22
+ */
23
+
24
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
25
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
26
+ import { z } from "zod";
27
+
28
+ // ─── Config ──────────────────────────────────────────────────────────────────
29
+
30
+ const BANKR_API_KEY = process.env.BANKR_API_KEY || "";
31
+ const COORDINATOR = process.env.COORDINATOR_URL || "https://api.litcoiin.xyz";
32
+ const BANKR_BASE = "https://api.bankr.bot";
33
+
34
+ const CONTRACTS = {
35
+ LITCOIN: "0x316ffb9c875f900AdCF04889E415cC86b564EBa3",
36
+ STAKING: "0xC9584Ce1591E8EB38EdF15C28f2FDcca97A3d3B7",
37
+ ORACLE: "0x4f937937A3B7Ca046d0f2B5071782aFFC675241b",
38
+ LITCREDIT: "0x33e3d328F62037EB0d173705674CE713c348f0a6",
39
+ VAULT_MANAGER: "0xD23a9b32e38FABE2325e1d27f94EcCf0e4a2f058",
40
+ CLAIMS: "0xF703DcF2E88C0673F776870fdb12A453927C6A5e",
41
+ COMPUTE_ESCROW: "0x28C351FE1A37434DD63882dA51b5f4CBade71724",
42
+ GUILD: "0xC377cbD6739678E0fae16e52970755f50AF55bD1",
43
+ FAUCET: "0x1659875dE16090c84C81DF1BDba3c3B4df093557",
44
+ };
45
+
46
+ const SELECTORS = {
47
+ balanceOf: "0x70a08231",
48
+ totalStaked: "0x817b1cd2",
49
+ getTier: "0xb45aae52",
50
+ getStakeInfo: "0xc3453153",
51
+ getMiningBoostBps: "0x1e9afdb1",
52
+ timeUntilUnlock: "0xd0228e8f",
53
+ getOwnerVaultIds: "0xa6f83a09",
54
+ getVaultCollateralRatio:"0x31ef9daa",
55
+ getMaxMintable: "0x0725f4cb",
56
+ isLiquidatable: "0x211a4443",
57
+ cpiPrice: "0x3ad868db",
58
+ litcoinPrice: "0x1cb870fc",
59
+ available: "0x10098ad5",
60
+ stake: "0x604f2177",
61
+ unstake: "0x2def6620",
62
+ upgradeTier: "0xa2949dd7",
63
+ openVault: "0x04a2ce7e",
64
+ addCollateral: "0xa8f35adf",
65
+ mintLitCredit: "0x91b4ffb4",
66
+ repayDebt: "0x015cb0a5",
67
+ withdrawCollateral: "0x767a7b05",
68
+ closeVault: "0x360c3288",
69
+ deposit: "0xb6b55f25",
70
+ requestWithdraw: "0x745400c9",
71
+ cancelWithdraw: "0x84b76824",
72
+ completeWithdraw: "0xf756fa21",
73
+ approve: "0x095ea7b3",
74
+ createGuild: "0x1c0e61dc",
75
+ joinGuild: "0xa3e77e2a",
76
+ addDeposit: "0xa6f1fd51",
77
+ leaveGuild: "0x0d3dfa92",
78
+ stakeGuild: "0x3deb2bb7",
79
+ unstakeGuild: "0x7b5f7292",
80
+ };
81
+
82
+ const RPC_URLS = [
83
+ "https://base.llamarpc.com",
84
+ "https://base-rpc.publicnode.com",
85
+ "https://mainnet.base.org",
86
+ ];
87
+
88
+ const TIER_AMOUNTS = { 1: 1_000_000, 2: 5_000_000, 3: 50_000_000, 4: 500_000_000 };
89
+ const TIER_NAMES = { 1: "Spark", 2: "Circuit", 3: "Core", 4: "Architect" };
90
+
91
+ // ─── Helpers ─────────────────────────────────────────────────────────────────
92
+
93
+ function bankrHeaders() {
94
+ return { "Content-Type": "application/json", "X-API-Key": BANKR_API_KEY };
95
+ }
96
+
97
+ async function bankrGet(path) {
98
+ const r = await fetch(`${BANKR_BASE}${path}`, { headers: bankrHeaders() });
99
+ return r.json();
100
+ }
101
+
102
+ async function bankrPost(path, body) {
103
+ const r = await fetch(`${BANKR_BASE}${path}`, {
104
+ method: "POST", headers: bankrHeaders(), body: JSON.stringify(body),
105
+ });
106
+ return r.json();
107
+ }
108
+
109
+ async function coordGet(path, token) {
110
+ const headers = { "Content-Type": "application/json", "X-Litcoin-SDK": "mcp-1.0.0" };
111
+ if (token) headers["Authorization"] = `Bearer ${token}`;
112
+ const r = await fetch(`${COORDINATOR}${path}`, { headers });
113
+ return r.json();
114
+ }
115
+
116
+ async function coordPost(path, body, token) {
117
+ const headers = { "Content-Type": "application/json", "X-Litcoin-SDK": "mcp-1.0.0" };
118
+ if (token) headers["Authorization"] = `Bearer ${token}`;
119
+ const r = await fetch(`${COORDINATOR}${path}`, {
120
+ method: "POST", headers, body: JSON.stringify(body),
121
+ });
122
+ return r.json();
123
+ }
124
+
125
+ async function rpcCall(contract, calldata) {
126
+ for (const rpc of RPC_URLS) {
127
+ try {
128
+ const r = await fetch(rpc, {
129
+ method: "POST",
130
+ headers: { "Content-Type": "application/json" },
131
+ body: JSON.stringify({
132
+ jsonrpc: "2.0", id: 1, method: "eth_call",
133
+ params: [{ to: contract, data: calldata }, "latest"],
134
+ }),
135
+ signal: AbortSignal.timeout(8000),
136
+ });
137
+ const json = await r.json();
138
+ return json.result || "0x" + "0".repeat(64);
139
+ } catch { continue; }
140
+ }
141
+ return null;
142
+ }
143
+
144
+ function padAddr(addr) {
145
+ return "000000000000000000000000" + addr.slice(2).toLowerCase();
146
+ }
147
+
148
+ function hex64(n) {
149
+ return BigInt(n).toString(16).padStart(64, "0");
150
+ }
151
+
152
+ function decodeUint(hex, slot = 0) {
153
+ if (!hex || hex === "0x") return 0n;
154
+ const d = hex.startsWith("0x") ? hex.slice(2) : hex;
155
+ const start = slot * 64;
156
+ if (start + 64 > d.length) return 0n;
157
+ return BigInt("0x" + d.slice(start, start + 64));
158
+ }
159
+
160
+ function toTokens(wei) {
161
+ return Number(wei) / 1e18;
162
+ }
163
+
164
+ async function submitTx(to, calldata) {
165
+ return bankrPost("/agent/submit", {
166
+ transaction: { to, data: calldata, value: "0", chainId: 8453 },
167
+ });
168
+ }
169
+
170
+ async function approveTx(token, spender, amountWei) {
171
+ const cd = SELECTORS.approve + padAddr(spender) + hex64(amountWei);
172
+ return submitTx(token, cd);
173
+ }
174
+
175
+ // ─── Auth State ──────────────────────────────────────────────────────────────
176
+
177
+ let _wallet = null;
178
+ let _authToken = null;
179
+ let _authExpiry = 0;
180
+
181
+ async function getWallet() {
182
+ if (_wallet) return _wallet;
183
+ const data = await bankrGet("/agent/me");
184
+ for (const key of ["wallets", "addresses"]) {
185
+ const items = data[key] || [];
186
+ if (Array.isArray(items)) {
187
+ for (const w of items) {
188
+ const addr = typeof w === "object" ? w.address : w;
189
+ if (typeof addr === "string" && addr.startsWith("0x")) {
190
+ _wallet = addr.toLowerCase();
191
+ return _wallet;
192
+ }
193
+ }
194
+ }
195
+ }
196
+ for (const key of ["address", "evmAddress", "wallet"]) {
197
+ if (data[key] && typeof data[key] === "string" && data[key].startsWith("0x")) {
198
+ _wallet = data[key].toLowerCase();
199
+ return _wallet;
200
+ }
201
+ }
202
+ throw new Error("No wallet found from Bankr");
203
+ }
204
+
205
+ async function getAuth() {
206
+ if (_authToken && Date.now() / 1000 < _authExpiry - 60) return _authToken;
207
+ const wallet = await getWallet();
208
+ const nonceRes = await coordPost("/v1/auth/nonce", { miner: wallet });
209
+ const message = nonceRes.message;
210
+ const sigRes = await bankrPost("/agent/sign", {
211
+ signatureType: "personal_sign", message,
212
+ });
213
+ const verifyRes = await coordPost("/v1/auth/verify", {
214
+ miner: wallet, message, signature: sigRes.signature,
215
+ });
216
+ _authToken = verifyRes.token;
217
+ _authExpiry = Date.now() / 1000 + 3600;
218
+ return _authToken;
219
+ }
220
+
221
+ // ─── MCP Server ──────────────────────────────────────────────────────────────
222
+
223
+ const server = new McpServer({
224
+ name: "litcoin",
225
+ version: "1.0.0",
226
+ });
227
+
228
+ // ── Resources ────────────────────────────────────────────────────────────────
229
+
230
+ server.resource(
231
+ "protocol-docs",
232
+ "litcoin://docs",
233
+ async () => ({
234
+ contents: [{
235
+ uri: "litcoin://docs",
236
+ mimeType: "text/plain",
237
+ text: "Full LITCOIN protocol documentation available at https://litcoiin.xyz/docs.md — fetch this URL for complete SDK reference, contract addresses, and API endpoints.",
238
+ }],
239
+ })
240
+ );
241
+
242
+ // ── Tools ────────────────────────────────────────────────────────────────────
243
+
244
+ // Balance
245
+ server.tool(
246
+ "litcoin_balance",
247
+ "Get LITCOIN and LITCREDIT token balances, staking tier, mining boost, and escrow balance for this wallet.",
248
+ {},
249
+ async () => {
250
+ const wallet = await getWallet();
251
+ const [litRaw, lcRaw, tierRaw, boostRaw, escrowRaw] = await Promise.all([
252
+ rpcCall(CONTRACTS.LITCOIN, SELECTORS.balanceOf + padAddr(wallet)),
253
+ rpcCall(CONTRACTS.LITCREDIT, SELECTORS.balanceOf + padAddr(wallet)),
254
+ rpcCall(CONTRACTS.STAKING, SELECTORS.getTier + padAddr(wallet)),
255
+ rpcCall(CONTRACTS.STAKING, SELECTORS.getMiningBoostBps + padAddr(wallet)),
256
+ rpcCall(CONTRACTS.COMPUTE_ESCROW, SELECTORS.available + padAddr(wallet)),
257
+ ]);
258
+ const tier = Number(decodeUint(tierRaw));
259
+ return {
260
+ content: [{ type: "text", text: JSON.stringify({
261
+ wallet,
262
+ litcoin: toTokens(decodeUint(litRaw)),
263
+ litcredit: toTokens(decodeUint(lcRaw)),
264
+ stakingTier: tier,
265
+ tierName: TIER_NAMES[tier] || "None",
266
+ miningBoost: `${(Number(decodeUint(boostRaw)) / 10000).toFixed(2)}x`,
267
+ escrowBalance: toTokens(decodeUint(escrowRaw)),
268
+ }, null, 2) }],
269
+ };
270
+ }
271
+ );
272
+
273
+ // Network stats
274
+ server.tool(
275
+ "litcoin_network",
276
+ "Get LITCOIN network statistics: active miners, treasury balance, reward per solve, oracle prices.",
277
+ {},
278
+ async () => {
279
+ const [stats, cpiRaw, litRaw] = await Promise.all([
280
+ coordGet("/v1/claims/stats"),
281
+ rpcCall(CONTRACTS.ORACLE, SELECTORS.cpiPrice),
282
+ rpcCall(CONTRACTS.ORACLE, SELECTORS.litcoinPrice),
283
+ ]);
284
+ return {
285
+ content: [{ type: "text", text: JSON.stringify({
286
+ ...stats,
287
+ oracleCpiPrice: toTokens(decodeUint(cpiRaw)),
288
+ oracleLitcoinPrice: toTokens(decodeUint(litRaw)),
289
+ }, null, 2) }],
290
+ };
291
+ }
292
+ );
293
+
294
+ // Mine one round
295
+ server.tool(
296
+ "litcoin_mine",
297
+ "Mine one round of LITCOIN. Requests a challenge, solves it deterministically, and submits the answer. Returns the reward earned.",
298
+ {},
299
+ async () => {
300
+ const wallet = await getWallet();
301
+ const token = await getAuth();
302
+ const nonce = crypto.randomUUID();
303
+
304
+ // Get challenge
305
+ const challenge = await coordGet(`/v1/challenge?nonce=${nonce}`, token);
306
+ if (challenge.error) {
307
+ return { content: [{ type: "text", text: `Challenge error: ${challenge.error}` }] };
308
+ }
309
+
310
+ // Solve deterministically (same logic as SDK solver)
311
+ const artifact = solveChallenge(challenge);
312
+
313
+ // Submit
314
+ const result = await coordPost("/v1/submit", {
315
+ challengeId: challenge.challengeId,
316
+ artifact,
317
+ nonce,
318
+ }, token);
319
+
320
+ return {
321
+ content: [{ type: "text", text: JSON.stringify({
322
+ pass: result.pass,
323
+ reward: result.reward,
324
+ boostMultiplier: result.boostMultiplier,
325
+ challengeId: challenge.challengeId,
326
+ }, null, 2) }],
327
+ };
328
+ }
329
+ );
330
+
331
+ // Claim rewards
332
+ server.tool(
333
+ "litcoin_claim",
334
+ "Claim accumulated mining rewards on-chain. Transfers earned LITCOIN to your wallet.",
335
+ {},
336
+ async () => {
337
+ const wallet = await getWallet();
338
+ const result = await coordPost("/v1/claims/bankr", { wallet });
339
+ return {
340
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
341
+ };
342
+ }
343
+ );
344
+
345
+ // Check claimable
346
+ server.tool(
347
+ "litcoin_claimable",
348
+ "Check how much LITCOIN is available to claim.",
349
+ {},
350
+ async () => {
351
+ const wallet = await getWallet();
352
+ const status = await coordGet(`/v1/claims/status?wallet=${wallet}`);
353
+ return {
354
+ content: [{ type: "text", text: JSON.stringify(status, null, 2) }],
355
+ };
356
+ }
357
+ );
358
+
359
+ // Stake
360
+ server.tool(
361
+ "litcoin_stake",
362
+ "Stake LITCOIN into a tier. Tier 1=Spark (1M), 2=Circuit (5M), 3=Core (50M), 4=Architect (500M). Auto-approves tokens.",
363
+ { tier: z.number().min(1).max(4).describe("Staking tier (1-4)") },
364
+ async ({ tier }) => {
365
+ const amountWei = BigInt(TIER_AMOUNTS[tier]) * 10n ** 18n;
366
+ await approveTx(CONTRACTS.LITCOIN, CONTRACTS.STAKING, amountWei);
367
+ const result = await submitTx(CONTRACTS.STAKING, SELECTORS.stake + hex64(tier));
368
+ return {
369
+ content: [{ type: "text", text: `Staked into ${TIER_NAMES[tier]} (tier ${tier}): ${JSON.stringify(result)}` }],
370
+ };
371
+ }
372
+ );
373
+
374
+ // Unstake
375
+ server.tool(
376
+ "litcoin_unstake",
377
+ "Unstake LITCOIN. Only works after the lock period expires.",
378
+ {},
379
+ async () => {
380
+ const result = await submitTx(CONTRACTS.STAKING, SELECTORS.unstake);
381
+ return { content: [{ type: "text", text: `Unstaked: ${JSON.stringify(result)}` }] };
382
+ }
383
+ );
384
+
385
+ // Open vault
386
+ server.tool(
387
+ "litcoin_open_vault",
388
+ "Open a vault with LITCOIN collateral to mint LITCREDIT. Specify collateral in whole tokens.",
389
+ { collateral: z.number().positive().describe("LITCOIN collateral amount in whole tokens") },
390
+ async ({ collateral }) => {
391
+ const wei = BigInt(Math.floor(collateral)) * 10n ** 18n;
392
+ await approveTx(CONTRACTS.LITCOIN, CONTRACTS.VAULT_MANAGER, wei);
393
+ const result = await submitTx(CONTRACTS.VAULT_MANAGER, SELECTORS.openVault + hex64(wei));
394
+ return { content: [{ type: "text", text: `Vault opened with ${collateral} LITCOIN: ${JSON.stringify(result)}` }] };
395
+ }
396
+ );
397
+
398
+ // Mint LITCREDIT
399
+ server.tool(
400
+ "litcoin_mint",
401
+ "Mint LITCREDIT against an open vault. 1 LITCREDIT = 1,000 output tokens of AI inference.",
402
+ {
403
+ vault_id: z.number().describe("Vault ID"),
404
+ amount: z.number().positive().describe("LITCREDIT to mint in whole tokens"),
405
+ },
406
+ async ({ vault_id, amount }) => {
407
+ const wei = BigInt(Math.floor(amount)) * 10n ** 18n;
408
+ const result = await submitTx(CONTRACTS.VAULT_MANAGER, SELECTORS.mintLitCredit + hex64(vault_id) + hex64(wei));
409
+ return { content: [{ type: "text", text: `Minted ${amount} LITCREDIT from vault #${vault_id}: ${JSON.stringify(result)}` }] };
410
+ }
411
+ );
412
+
413
+ // Repay debt
414
+ server.tool(
415
+ "litcoin_repay",
416
+ "Repay LITCREDIT debt on a vault.",
417
+ {
418
+ vault_id: z.number().describe("Vault ID"),
419
+ amount: z.number().positive().describe("LITCREDIT to repay"),
420
+ },
421
+ async ({ vault_id, amount }) => {
422
+ const wei = BigInt(Math.floor(amount)) * 10n ** 18n;
423
+ await approveTx(CONTRACTS.LITCREDIT, CONTRACTS.VAULT_MANAGER, wei);
424
+ const result = await submitTx(CONTRACTS.VAULT_MANAGER, SELECTORS.repayDebt + hex64(vault_id) + hex64(wei));
425
+ return { content: [{ type: "text", text: `Repaid ${amount} LITCREDIT on vault #${vault_id}: ${JSON.stringify(result)}` }] };
426
+ }
427
+ );
428
+
429
+ // Add collateral
430
+ server.tool(
431
+ "litcoin_add_collateral",
432
+ "Add more LITCOIN collateral to an existing vault.",
433
+ {
434
+ vault_id: z.number().describe("Vault ID"),
435
+ amount: z.number().positive().describe("LITCOIN to add"),
436
+ },
437
+ async ({ vault_id, amount }) => {
438
+ const wei = BigInt(Math.floor(amount)) * 10n ** 18n;
439
+ await approveTx(CONTRACTS.LITCOIN, CONTRACTS.VAULT_MANAGER, wei);
440
+ const result = await submitTx(CONTRACTS.VAULT_MANAGER, SELECTORS.addCollateral + hex64(vault_id) + hex64(wei));
441
+ return { content: [{ type: "text", text: `Added ${amount} LITCOIN to vault #${vault_id}: ${JSON.stringify(result)}` }] };
442
+ }
443
+ );
444
+
445
+ // Close vault
446
+ server.tool(
447
+ "litcoin_close_vault",
448
+ "Close a vault. Must repay all debt first.",
449
+ { vault_id: z.number().describe("Vault ID") },
450
+ async ({ vault_id }) => {
451
+ const result = await submitTx(CONTRACTS.VAULT_MANAGER, SELECTORS.closeVault + hex64(vault_id));
452
+ return { content: [{ type: "text", text: `Vault #${vault_id} closed: ${JSON.stringify(result)}` }] };
453
+ }
454
+ );
455
+
456
+ // Vault info
457
+ server.tool(
458
+ "litcoin_vaults",
459
+ "List all vaults for this wallet with collateral, debt, health ratio, and liquidation status.",
460
+ {},
461
+ async () => {
462
+ const wallet = await getWallet();
463
+ const idsRaw = await rpcCall(CONTRACTS.VAULT_MANAGER, SELECTORS.getOwnerVaultIds + padAddr(wallet));
464
+ if (!idsRaw || idsRaw === "0x" || idsRaw.length < 130) {
465
+ return { content: [{ type: "text", text: "No vaults found." }] };
466
+ }
467
+ const data = idsRaw.slice(2);
468
+ const count = Number(BigInt("0x" + data.slice(64, 128)));
469
+ const vaults = [];
470
+ for (let i = 0; i < count; i++) {
471
+ const vid = Number(BigInt("0x" + data.slice(128 + i * 64, 192 + i * 64)));
472
+ const [healthRaw, mintRaw, liqRaw] = await Promise.all([
473
+ rpcCall(CONTRACTS.VAULT_MANAGER, SELECTORS.getVaultCollateralRatio + hex64(vid)),
474
+ rpcCall(CONTRACTS.VAULT_MANAGER, SELECTORS.getMaxMintable + hex64(vid)),
475
+ rpcCall(CONTRACTS.VAULT_MANAGER, SELECTORS.isLiquidatable + hex64(vid)),
476
+ ]);
477
+ vaults.push({
478
+ vaultId: vid,
479
+ healthRatioBps: Number(decodeUint(healthRaw)),
480
+ maxMintable: toTokens(decodeUint(mintRaw)),
481
+ liquidatable: decodeUint(liqRaw) !== 0n,
482
+ });
483
+ }
484
+ return { content: [{ type: "text", text: JSON.stringify(vaults, null, 2) }] };
485
+ }
486
+ );
487
+
488
+ // Deposit to escrow
489
+ server.tool(
490
+ "litcoin_deposit_escrow",
491
+ "Deposit LITCREDIT into the compute escrow for AI inference spending.",
492
+ { amount: z.number().positive().describe("LITCREDIT to deposit") },
493
+ async ({ amount }) => {
494
+ const wei = BigInt(Math.floor(amount)) * 10n ** 18n;
495
+ await approveTx(CONTRACTS.LITCREDIT, CONTRACTS.COMPUTE_ESCROW, wei);
496
+ const result = await submitTx(CONTRACTS.COMPUTE_ESCROW, SELECTORS.deposit + hex64(wei));
497
+ return { content: [{ type: "text", text: `Deposited ${amount} LITCREDIT to escrow: ${JSON.stringify(result)}` }] };
498
+ }
499
+ );
500
+
501
+ // Compute
502
+ server.tool(
503
+ "litcoin_compute",
504
+ "Send a prompt to the LITCOIN compute marketplace for AI inference. Uses LITCREDIT from escrow. Returns the AI response.",
505
+ {
506
+ prompt: z.string().describe("The prompt to send"),
507
+ model: z.string().optional().describe("Model name (default: llama-3.3-70b)"),
508
+ },
509
+ async ({ prompt, model }) => {
510
+ const result = await coordPost("/v1/compute/request", {
511
+ prompt,
512
+ model: model || "llama-3.3-70b",
513
+ maxTokens: 1024,
514
+ });
515
+ return {
516
+ content: [{ type: "text", text: result.response || JSON.stringify(result, null, 2) }],
517
+ };
518
+ }
519
+ );
520
+
521
+ // Create guild
522
+ server.tool(
523
+ "litcoin_create_guild",
524
+ "Create a new mining guild. You become the leader.",
525
+ { name: z.string().describe("Guild name") },
526
+ async ({ name }) => {
527
+ const nameBytes = new TextEncoder().encode(name);
528
+ const nameHex = Array.from(nameBytes).map(b => b.toString(16).padStart(2, "0")).join("");
529
+ const padLen = (32 - nameBytes.length % 32) % 32;
530
+ const calldata = SELECTORS.createGuild +
531
+ hex64(32) +
532
+ hex64(nameBytes.length) +
533
+ nameHex + "00".repeat(padLen);
534
+ const result = await submitTx(CONTRACTS.GUILD, calldata);
535
+ return { content: [{ type: "text", text: `Guild "${name}" created: ${JSON.stringify(result)}` }] };
536
+ }
537
+ );
538
+
539
+ // Join guild
540
+ server.tool(
541
+ "litcoin_join_guild",
542
+ "Join a mining guild with a LITCOIN deposit.",
543
+ {
544
+ guild_id: z.number().describe("Guild ID"),
545
+ amount: z.number().positive().describe("LITCOIN to deposit"),
546
+ },
547
+ async ({ guild_id, amount }) => {
548
+ const wei = BigInt(Math.floor(amount)) * 10n ** 18n;
549
+ await approveTx(CONTRACTS.LITCOIN, CONTRACTS.GUILD, wei);
550
+ const result = await submitTx(CONTRACTS.GUILD, SELECTORS.joinGuild + hex64(guild_id) + hex64(wei));
551
+ return { content: [{ type: "text", text: `Joined guild #${guild_id} with ${amount} LITCOIN: ${JSON.stringify(result)}` }] };
552
+ }
553
+ );
554
+
555
+ // Leave guild
556
+ server.tool(
557
+ "litcoin_leave_guild",
558
+ "Leave your current mining guild. Returns your deposited tokens.",
559
+ {},
560
+ async () => {
561
+ const result = await submitTx(CONTRACTS.GUILD, SELECTORS.leaveGuild);
562
+ return { content: [{ type: "text", text: `Left guild: ${JSON.stringify(result)}` }] };
563
+ }
564
+ );
565
+
566
+ // Faucet
567
+ server.tool(
568
+ "litcoin_faucet",
569
+ "Claim 5M LITCOIN from the bootstrap faucet. One-time per wallet. Solves a trial challenge to prove AI capability.",
570
+ {},
571
+ async () => {
572
+ const wallet = await getWallet();
573
+ // Get faucet challenge
574
+ const challenge = await coordPost("/v1/faucet/challenge", {});
575
+ if (challenge.error) {
576
+ return { content: [{ type: "text", text: `Faucet error: ${challenge.error}` }] };
577
+ }
578
+ // Solve it
579
+ const artifact = solveChallenge(challenge);
580
+ // Submit
581
+ const result = await coordPost("/v1/faucet/submit", {
582
+ challengeId: challenge.challengeId || challenge.benchmarkId,
583
+ artifact,
584
+ wallet,
585
+ });
586
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
587
+ }
588
+ );
589
+
590
+ // ─── Challenge Solver (deterministic — same as SDK) ──────────────────────────
591
+
592
+ function solveChallenge(challenge) {
593
+ const { doc, questions, constraints, entities } = challenge;
594
+ if (!doc || !questions) return "";
595
+
596
+ const answers = [];
597
+
598
+ for (const q of questions) {
599
+ // Try to find answer in document
600
+ let answer = "";
601
+
602
+ // Check entities for matching data
603
+ if (entities) {
604
+ for (const ent of entities) {
605
+ if (q.toLowerCase().includes(ent.name?.toLowerCase() || "")) {
606
+ // Return relevant entity data
607
+ if (ent.revenue) answer = ent.revenue;
608
+ else if (ent.founded) answer = String(ent.founded);
609
+ else if (ent.role) answer = ent.role;
610
+ else if (ent.value) answer = String(ent.value);
611
+ break;
612
+ }
613
+ }
614
+ }
615
+
616
+ if (!answer) {
617
+ // Extract from constraints
618
+ for (const c of (constraints || [])) {
619
+ if (typeof c === "string" && c.includes("=")) {
620
+ const [key, val] = c.split("=").map(s => s.trim());
621
+ if (q.toLowerCase().includes(key.toLowerCase())) {
622
+ answer = val;
623
+ break;
624
+ }
625
+ }
626
+ }
627
+ }
628
+
629
+ if (!answer) answer = "N/A";
630
+ answers.push(answer);
631
+ }
632
+
633
+ // Compute ASCII checksum
634
+ const joined = answers.join("|");
635
+ let checksum = 0;
636
+ for (let i = 0; i < joined.length; i++) {
637
+ checksum = (checksum + joined.charCodeAt(i)) % 256;
638
+ }
639
+ answers.push(String(checksum));
640
+
641
+ return answers.join("|");
642
+ }
643
+
644
+ // ─── Start ───────────────────────────────────────────────────────────────────
645
+
646
+ async function main() {
647
+ if (!BANKR_API_KEY) {
648
+ console.error("BANKR_API_KEY environment variable required. Get one at https://bankr.bot/api");
649
+ process.exit(1);
650
+ }
651
+
652
+ const transport = new StdioServerTransport();
653
+ await server.connect(transport);
654
+ }
655
+
656
+ main().catch(console.error);