@xlmtools/cli 0.1.2 → 0.2.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 (66) hide show
  1. package/README.md +18 -12
  2. package/dist/cli.d.ts +2 -0
  3. package/{src/lib/api-fetch.ts → dist/lib/api-fetch.d.ts} +3 -14
  4. package/dist/lib/budget.d.ts +14 -0
  5. package/dist/lib/cache.d.ts +7 -0
  6. package/dist/lib/config.d.ts +2 -0
  7. package/dist/lib/format.d.ts +15 -0
  8. package/dist/lib/logger.d.ts +2 -0
  9. package/dist/lib/wallet.d.ts +9 -0
  10. package/dist/server.d.ts +22 -0
  11. package/dist/server.js +89 -0
  12. package/dist/tools/budget.d.ts +2 -0
  13. package/dist/tools/crypto.d.ts +2 -0
  14. package/dist/tools/dex-candles.d.ts +2 -0
  15. package/dist/tools/dex-orderbook.d.ts +2 -0
  16. package/dist/tools/dex-trades.d.ts +2 -0
  17. package/dist/tools/domain.d.ts +2 -0
  18. package/dist/tools/image.d.ts +2 -0
  19. package/dist/tools/oracle-price.d.ts +2 -0
  20. package/dist/tools/research.d.ts +2 -0
  21. package/dist/tools/scrape.d.ts +2 -0
  22. package/dist/tools/screenshot.d.ts +2 -0
  23. package/dist/tools/search.d.ts +2 -0
  24. package/dist/tools/stellar-account.d.ts +2 -0
  25. package/dist/tools/stellar-asset.d.ts +2 -0
  26. package/dist/tools/stellar-pools.d.ts +2 -0
  27. package/dist/tools/stocks.d.ts +2 -0
  28. package/dist/tools/swap-quote.d.ts +2 -0
  29. package/dist/tools/tools-list.d.ts +2 -0
  30. package/dist/tools/wallet-tool.d.ts +2 -0
  31. package/dist/tools/weather.d.ts +2 -0
  32. package/dist/tools/youtube.d.ts +2 -0
  33. package/package.json +27 -3
  34. package/dist/index.js +0 -77
  35. package/dist/tools/card.js +0 -51
  36. package/dist/tools/reddit.js +0 -40
  37. package/src/cli.ts +0 -245
  38. package/src/index.ts +0 -90
  39. package/src/lib/budget.ts +0 -78
  40. package/src/lib/cache.ts +0 -66
  41. package/src/lib/config.ts +0 -18
  42. package/src/lib/format.ts +0 -51
  43. package/src/lib/logger.ts +0 -6
  44. package/src/lib/wallet.ts +0 -143
  45. package/src/tools/budget.ts +0 -67
  46. package/src/tools/crypto.ts +0 -26
  47. package/src/tools/dex-candles.ts +0 -53
  48. package/src/tools/dex-orderbook.ts +0 -47
  49. package/src/tools/dex-trades.ts +0 -52
  50. package/src/tools/domain.ts +0 -25
  51. package/src/tools/image.ts +0 -50
  52. package/src/tools/oracle-price.ts +0 -43
  53. package/src/tools/research.ts +0 -49
  54. package/src/tools/scrape.ts +0 -43
  55. package/src/tools/screenshot.ts +0 -48
  56. package/src/tools/search.ts +0 -49
  57. package/src/tools/stellar-account.ts +0 -40
  58. package/src/tools/stellar-asset.ts +0 -41
  59. package/src/tools/stellar-pools.ts +0 -46
  60. package/src/tools/stocks.ts +0 -45
  61. package/src/tools/swap-quote.ts +0 -44
  62. package/src/tools/tools-list.ts +0 -22
  63. package/src/tools/wallet-tool.ts +0 -51
  64. package/src/tools/weather.ts +0 -25
  65. package/src/tools/youtube.ts +0 -51
  66. package/tsconfig.json +0 -13
package/src/lib/wallet.ts DELETED
@@ -1,143 +0,0 @@
1
- import {
2
- Keypair,
3
- Horizon,
4
- TransactionBuilder,
5
- Networks,
6
- Operation,
7
- Asset,
8
- } from "@stellar/stellar-sdk";
9
- import { readFileSync, writeFileSync, mkdirSync, existsSync } from "node:fs";
10
- import { homedir } from "node:os";
11
- import { join } from "node:path";
12
- import { logger } from "./logger.js";
13
-
14
- const CONFIG_DIR = join(homedir(), ".xlmtools");
15
- const CONFIG_PATH = join(CONFIG_DIR, "config.json");
16
-
17
- // Circle USDC issuer on Stellar testnet
18
- const USDC_ISSUER = "GBBD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NAT4AQH3ZLLFLA5";
19
- const HORIZON_TESTNET = "https://horizon-testnet.stellar.org";
20
-
21
- interface XLMToolsConfig {
22
- stellarPrivateKey: string;
23
- stellarPublicKey: string;
24
- apiUrl: string;
25
- }
26
-
27
- /**
28
- * Auto-fund a new wallet on Stellar TESTNET only.
29
- * This uses the public friendbot (free testnet XLM) and adds a USDC
30
- * trustline so the account can receive payments. This function must
31
- * never be called on mainnet — friendbot does not exist there.
32
- */
33
- async function fundTestnetWallet(keypair: Keypair): Promise<boolean> {
34
- // Safety: only run on testnet
35
- const network = process.env.STELLAR_NETWORK ?? "testnet";
36
- if (network !== "testnet") {
37
- logger.info({ network }, "Skipping auto-fund — not on testnet");
38
- return false;
39
- }
40
-
41
- const publicKey = keypair.publicKey();
42
-
43
- // Step 1: Fund with friendbot
44
- process.stderr.write(" Funding wallet with testnet XLM...");
45
- try {
46
- const res = await fetch(
47
- `https://friendbot.stellar.org?addr=${publicKey}`,
48
- );
49
- if (!res.ok) {
50
- process.stderr.write(" failed\n");
51
- logger.warn({ status: res.status }, "Friendbot funding failed");
52
- return false;
53
- }
54
- process.stderr.write(" done\n");
55
- } catch (e) {
56
- process.stderr.write(" failed (network error)\n");
57
- logger.warn({ err: e }, "Friendbot request failed");
58
- return false;
59
- }
60
-
61
- // Step 2: Add USDC trustline
62
- process.stderr.write(" Adding USDC trustline...");
63
- try {
64
- const server = new Horizon.Server(HORIZON_TESTNET);
65
- const account = await server.loadAccount(publicKey);
66
- const usdcAsset = new Asset("USDC", USDC_ISSUER);
67
-
68
- const tx = new TransactionBuilder(account, {
69
- fee: "100",
70
- networkPassphrase: Networks.TESTNET,
71
- })
72
- .addOperation(Operation.changeTrust({ asset: usdcAsset }))
73
- .setTimeout(30)
74
- .build();
75
-
76
- tx.sign(keypair);
77
- await server.submitTransaction(tx);
78
- process.stderr.write(" done\n");
79
- } catch (e) {
80
- process.stderr.write(" failed\n");
81
- logger.warn({ err: e }, "USDC trustline failed");
82
- return false;
83
- }
84
-
85
- return true;
86
- }
87
-
88
- export function loadOrCreateWallet(): XLMToolsConfig {
89
- if (existsSync(CONFIG_PATH)) {
90
- return JSON.parse(readFileSync(CONFIG_PATH, "utf-8")) as XLMToolsConfig;
91
- }
92
-
93
- const keypair = Keypair.random();
94
- const config: XLMToolsConfig = {
95
- stellarPrivateKey: keypair.secret(),
96
- stellarPublicKey: keypair.publicKey(),
97
- apiUrl: process.env.XLMTOOLS_API_URL ?? "https://api.xlmtools.com",
98
- };
99
-
100
- mkdirSync(CONFIG_DIR, { recursive: true });
101
- writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 2), { mode: 0o600 });
102
-
103
- process.stderr.write(
104
- "\n" +
105
- "XLMTools — First Run Setup (Stellar Testnet)\n" +
106
- "─".repeat(44) + "\n\n" +
107
- ` Wallet: ${config.stellarPublicKey}\n` +
108
- ` Network: Stellar Testnet\n\n`,
109
- );
110
-
111
- // Auto-fund on testnet (non-blocking — don't break startup if it fails)
112
- fundTestnetWallet(keypair)
113
- .then((funded) => {
114
- if (funded) {
115
- process.stderr.write(
116
- "\n Wallet funded and ready.\n" +
117
- " Get testnet USDC: https://faucet.circle.com\n" +
118
- " (paste your wallet address above)\n\n" +
119
- "─".repeat(40) + "\n\n",
120
- );
121
- } else {
122
- process.stderr.write(
123
- "\n Auto-funding failed. Fund manually:\n" +
124
- " 1. https://lab.stellar.org/account/fund (testnet XLM)\n" +
125
- " 2. https://faucet.circle.com (testnet USDC)\n\n" +
126
- "─".repeat(40) + "\n\n",
127
- );
128
- }
129
- })
130
- .catch(() => {
131
- // Silently handle — wallet is still created, just unfunded
132
- });
133
-
134
- logger.info(
135
- { publicKey: config.stellarPublicKey },
136
- "New Stellar wallet generated",
137
- );
138
- return config;
139
- }
140
-
141
- export function getKeypair(config: XLMToolsConfig): Keypair {
142
- return Keypair.fromSecret(config.stellarPrivateKey);
143
- }
@@ -1,67 +0,0 @@
1
- import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
- import { z } from "zod";
3
- import { ok, err } from "../lib/format.js";
4
- import { setBudget, clearBudget, getStatus } from "../lib/budget.js";
5
- import { logger } from "../lib/logger.js";
6
-
7
- export function registerBudgetTool(server: McpServer): void {
8
- server.registerTool(
9
- "budget",
10
- {
11
- title: "Session Budget",
12
- description:
13
- "Set, check, or clear a spending limit for this session. " +
14
- "The budget resets when the MCP server restarts. " +
15
- "Cached responses do not count against the budget.",
16
- inputSchema: z.object({
17
- action: z
18
- .enum(["set", "check", "clear"])
19
- .describe("Action: set a limit, check status, or clear limit"),
20
- amount: z.coerce.number()
21
- .positive()
22
- .optional()
23
- .describe("Budget amount in USD (required for 'set')"),
24
- }),
25
- },
26
- async ({ action, amount }) => {
27
- logger.debug({ action, amount }, "budget tool invoked");
28
-
29
- switch (action) {
30
- case "set": {
31
- if (amount === undefined) {
32
- return err(
33
- "Amount is required for 'set'. Example: budget set 5.00",
34
- );
35
- }
36
- setBudget(amount);
37
- return ok({
38
- message: `Budget set to $${amount.toFixed(2)} for this session.`,
39
- ...getStatus(),
40
- });
41
- }
42
- case "check": {
43
- const status = getStatus();
44
- if (status.max === null) {
45
- return ok({
46
- message:
47
- "No budget limit set. All tool calls will be charged normally.",
48
- ...status,
49
- });
50
- }
51
- return ok({
52
- message: `$${status.remaining?.toFixed(3)} remaining of $${status.max.toFixed(2)} budget.`,
53
- ...status,
54
- });
55
- }
56
- case "clear": {
57
- clearBudget();
58
- return ok({
59
- message:
60
- "Budget limit cleared. All tool calls will be charged normally.",
61
- ...getStatus(),
62
- });
63
- }
64
- }
65
- },
66
- );
67
- }
@@ -1,26 +0,0 @@
1
- import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
- import { z } from "zod";
3
- import { loadOrCreateWallet } from "../lib/wallet.js";
4
- import { apiFetch } from "../lib/api-fetch.js";
5
- import { ok, err } from "../lib/format.js";
6
-
7
- export function registerCryptoTool(server: McpServer): void {
8
- server.registerTool(
9
- "crypto",
10
- {
11
- title: "Crypto Prices",
12
- description: "Get real-time cryptocurrency prices, market caps, and 24h changes. Free.", inputSchema: z.object({
13
- ids: z.string().describe("Comma-separated coin IDs (e.g. bitcoin,ethereum,stellar)"),
14
- vs_currency: z.string().default("usd").describe("Quote currency (e.g. usd, eur)"),
15
- }),
16
- },
17
- async ({ ids, vs_currency }) => {
18
- try {
19
- const config = loadOrCreateWallet();
20
- const res = await apiFetch(config, `/crypto?ids=${encodeURIComponent(ids)}&vs_currency=${vs_currency}`);
21
- if (!res.ok) return err(`Crypto API error: ${res.status}`);
22
- return ok(await res.json());
23
- } catch (e) { return err(String(e)); }
24
- }
25
- );
26
- }
@@ -1,53 +0,0 @@
1
- import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
- import { z } from "zod";
3
- import { loadOrCreateWallet } from "../lib/wallet.js";
4
- import { apiFetch } from "../lib/api-fetch.js";
5
- import { ok, err } from "../lib/format.js";
6
- import { logger } from "../lib/logger.js";
7
-
8
- export function registerDexCandlesTool(server: McpServer): void {
9
- server.registerTool(
10
- "dex-candles",
11
- {
12
- title: "Stellar DEX Price Candles",
13
- description:
14
- `Get OHLCV candlestick price data for any Stellar asset pair.\n` +
15
- `Resolutions: 1m, 5m, 15m, 1h, 1d, 1w.\n` +
16
- `Examples: "XLM/USDC" at "1h", "USDC/EURC" at "1d".\nFree.`, inputSchema: z.object({
17
- pair: z
18
- .string()
19
- .describe('Asset pair (e.g. "XLM/USDC")'),
20
- resolution: z
21
- .enum(["1m", "5m", "15m", "1h", "1d", "1w"])
22
- .default("1h")
23
- .describe("Candle interval"),
24
- limit: z.coerce.number()
25
- .int()
26
- .min(1)
27
- .max(200)
28
- .default(20)
29
- .describe("Number of candles (1-200)"),
30
- }),
31
- },
32
- async ({ pair, resolution, limit }) => {
33
- logger.debug({ pair, resolution, limit }, "dex-candles invoked");
34
- try {
35
- const config = loadOrCreateWallet();
36
- const params = new URLSearchParams({
37
- pair,
38
- resolution,
39
- limit: String(limit),
40
- });
41
- const res = await apiFetch(config, `/dex-candles?${params}`);
42
- if (!res.ok) {
43
- const body = await res.text();
44
- return err(`Candles error ${res.status}: ${body}`);
45
- }
46
- return ok(await res.json());
47
- } catch (e) {
48
- logger.error({ err: e }, "dex-candles error");
49
- return err(`Candles failed: ${String(e)}`);
50
- }
51
- }
52
- );
53
- }
@@ -1,47 +0,0 @@
1
- import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
- import { z } from "zod";
3
- import { loadOrCreateWallet } from "../lib/wallet.js";
4
- import { apiFetch } from "../lib/api-fetch.js";
5
- import { ok, err } from "../lib/format.js";
6
- import { logger } from "../lib/logger.js";
7
-
8
- export function registerDexOrderbookTool(server: McpServer): void {
9
- server.registerTool(
10
- "dex-orderbook",
11
- {
12
- title: "Stellar DEX Orderbook",
13
- description:
14
- `View the live orderbook for any Stellar asset pair. Shows bids, asks, and spread.\n` +
15
- `Use pairs like "XLM/USDC", "USDC/EURC", or "CODE:ISSUER/XLM".\n` +
16
- `Well-known assets: XLM, USDC, EURC, AQUA, yUSDC, BLND, SHX.\nFree.`, inputSchema: z.object({
17
- pair: z
18
- .string()
19
- .describe('Asset pair (e.g. "XLM/USDC", "USDC/EURC")'),
20
- limit: z.coerce.number()
21
- .int()
22
- .min(1)
23
- .max(200)
24
- .default(10)
25
- .describe("Number of price levels per side (1-200)"),
26
- }),
27
- },
28
- async ({ pair, limit }) => {
29
- logger.debug({ pair, limit }, "dex-orderbook invoked");
30
- try {
31
- const config = loadOrCreateWallet();
32
- const res = await apiFetch(
33
- config,
34
- `/dex-orderbook?pair=${encodeURIComponent(pair)}&limit=${limit}`,
35
- );
36
- if (!res.ok) {
37
- const body = await res.text();
38
- return err(`Orderbook error ${res.status}: ${body}`);
39
- }
40
- return ok(await res.json());
41
- } catch (e) {
42
- logger.error({ err: e }, "dex-orderbook error");
43
- return err(`Orderbook failed: ${String(e)}`);
44
- }
45
- }
46
- );
47
- }
@@ -1,52 +0,0 @@
1
- import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
- import { z } from "zod";
3
- import { loadOrCreateWallet } from "../lib/wallet.js";
4
- import { apiFetch } from "../lib/api-fetch.js";
5
- import { ok, err } from "../lib/format.js";
6
- import { logger } from "../lib/logger.js";
7
-
8
- export function registerDexTradesTool(server: McpServer): void {
9
- server.registerTool(
10
- "dex-trades",
11
- {
12
- title: "Stellar DEX Trades",
13
- description:
14
- `Get recent trades for any Stellar asset pair.\n` +
15
- `Filter by trade type: "all", "orderbook", or "liquidity_pool".\nFree.`, inputSchema: z.object({
16
- pair: z
17
- .string()
18
- .describe('Asset pair (e.g. "XLM/USDC")'),
19
- limit: z.coerce.number()
20
- .int()
21
- .min(1)
22
- .max(200)
23
- .default(10)
24
- .describe("Number of trades (1-200)"),
25
- trade_type: z
26
- .enum(["all", "orderbook", "liquidity_pool"])
27
- .default("all")
28
- .describe("Filter by trade source"),
29
- }),
30
- },
31
- async ({ pair, limit, trade_type }) => {
32
- logger.debug({ pair, limit, trade_type }, "dex-trades invoked");
33
- try {
34
- const config = loadOrCreateWallet();
35
- const params = new URLSearchParams({
36
- pair,
37
- limit: String(limit),
38
- trade_type,
39
- });
40
- const res = await apiFetch(config, `/dex-trades?${params}`);
41
- if (!res.ok) {
42
- const body = await res.text();
43
- return err(`Trades error ${res.status}: ${body}`);
44
- }
45
- return ok(await res.json());
46
- } catch (e) {
47
- logger.error({ err: e }, "dex-trades error");
48
- return err(`Trades failed: ${String(e)}`);
49
- }
50
- }
51
- );
52
- }
@@ -1,25 +0,0 @@
1
- import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
- import { z } from "zod";
3
- import { loadOrCreateWallet } from "../lib/wallet.js";
4
- import { apiFetch } from "../lib/api-fetch.js";
5
- import { ok, err } from "../lib/format.js";
6
-
7
- export function registerDomainTool(server: McpServer): void {
8
- server.registerTool(
9
- "domain",
10
- {
11
- title: "Domain Availability",
12
- description: "Check if a domain name is available. Free.", inputSchema: z.object({
13
- name: z.string().describe("Domain name to check (e.g. stellar-tools.xyz)"),
14
- }),
15
- },
16
- async ({ name }) => {
17
- try {
18
- const config = loadOrCreateWallet();
19
- const res = await apiFetch(config, `/domain?name=${encodeURIComponent(name)}`);
20
- if (!res.ok) return err(`Domain API error: ${res.status}`);
21
- return ok(await res.json());
22
- } catch (e) { return err(String(e)); }
23
- }
24
- );
25
- }
@@ -1,50 +0,0 @@
1
- import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
- import { z } from "zod";
3
- import { loadOrCreateWallet } from "../lib/wallet.js";
4
- import { apiFetch } from "../lib/api-fetch.js";
5
- import { okPaid, err } from "../lib/format.js";
6
- import { logger } from "../lib/logger.js";
7
- import { TOOL_PRICES } from "../lib/config.js";
8
- import { withBudget } from "../lib/budget.js";
9
- import { withCache } from "../lib/cache.js";
10
-
11
- export function registerImageTool(server: McpServer): void {
12
- server.registerTool(
13
- "image",
14
- {
15
- title: "AI Image Generation",
16
- description: `Generate images from a text prompt using AI.\nCost: $${TOOL_PRICES.image} USDC per image (paid via Stellar MPP).`, inputSchema: z.object({
17
- prompt: z
18
- .string()
19
- .describe("Text prompt describing the image to generate"),
20
- size: z
21
- .enum(["1024x1024", "1024x1792", "1792x1024"])
22
- .default("1024x1024")
23
- .describe("Output image dimensions"),
24
- }),
25
- },
26
- async ({ prompt, size }) => {
27
- logger.debug({ prompt, size }, "image tool invoked");
28
- return withCache("image", { prompt, size }, () =>
29
- withBudget("image", async () => {
30
- try {
31
- const config = loadOrCreateWallet();
32
- const res = await apiFetch(config, `/image`, {
33
- method: "POST",
34
- headers: { "Content-Type": "application/json" },
35
- body: JSON.stringify({ prompt, size }),
36
- });
37
- if (!res.ok) {
38
- const body = await res.text();
39
- return err(`Image API error ${res.status}: ${body}`);
40
- }
41
- return okPaid(await res.json());
42
- } catch (e: unknown) {
43
- logger.error({ err: e }, "image tool error");
44
- return err(`Image generation failed: ${String(e)}`);
45
- }
46
- }),
47
- );
48
- },
49
- );
50
- }
@@ -1,43 +0,0 @@
1
- import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
- import { z } from "zod";
3
- import { loadOrCreateWallet } from "../lib/wallet.js";
4
- import { apiFetch } from "../lib/api-fetch.js";
5
- import { ok, err } from "../lib/format.js";
6
- import { logger } from "../lib/logger.js";
7
-
8
- export function registerOraclePriceTool(server: McpServer): void {
9
- server.registerTool(
10
- "oracle-price",
11
- {
12
- title: "Stellar Oracle Price",
13
- description:
14
- `Get real-time prices from Reflector, Stellar's decentralized oracle.\n` +
15
- `Feeds: "crypto" (BTC, ETH, SOL, etc.), "fiat" (EUR, GBP, JPY), "dex" (Stellar DEX pairs).\n` +
16
- `Prices are updated every 5 minutes via multi-node consensus.\nFree.`, inputSchema: z.object({
17
- asset: z
18
- .string()
19
- .describe('Asset ticker (e.g. "BTC", "ETH", "EUR", "SOL")'),
20
- feed: z
21
- .enum(["crypto", "fiat", "dex"])
22
- .default("crypto")
23
- .describe("Oracle feed to query"),
24
- }),
25
- },
26
- async ({ asset, feed }) => {
27
- logger.debug({ asset, feed }, "oracle-price invoked");
28
- try {
29
- const config = loadOrCreateWallet();
30
- const params = new URLSearchParams({ asset, feed });
31
- const res = await apiFetch(config, `/oracle-price?${params}`);
32
- if (!res.ok) {
33
- const body = await res.text();
34
- return err(`Oracle error ${res.status}: ${body}`);
35
- }
36
- return ok(await res.json());
37
- } catch (e) {
38
- logger.error({ err: e }, "oracle-price error");
39
- return err(`Oracle failed: ${String(e)}`);
40
- }
41
- }
42
- );
43
- }
@@ -1,49 +0,0 @@
1
- import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
- import { z } from "zod";
3
- import { loadOrCreateWallet } from "../lib/wallet.js";
4
- import { apiFetch } from "../lib/api-fetch.js";
5
- import { okPaid, err } from "../lib/format.js";
6
- import { logger } from "../lib/logger.js";
7
- import { TOOL_PRICES } from "../lib/config.js";
8
- import { withBudget } from "../lib/budget.js";
9
- import { withCache } from "../lib/cache.js";
10
-
11
- export function registerResearchTool(server: McpServer): void {
12
- server.registerTool(
13
- "research",
14
- {
15
- title: "Deep Research",
16
- description: `Deep research on any topic — returns summarized, sourced results from multiple web pages.\nCost: $${TOOL_PRICES.research} USDC per query (paid via Stellar MPP).`, inputSchema: z.object({
17
- query: z.string().describe("Research query"),
18
- num_results: z.coerce.number()
19
- .int()
20
- .min(1)
21
- .max(20)
22
- .default(5)
23
- .describe("Number of results to return (1-20)"),
24
- }),
25
- },
26
- async ({ query, num_results }) => {
27
- logger.debug({ query, num_results }, "research tool invoked");
28
- return withCache("research", { query, num_results }, () =>
29
- withBudget("research", async () => {
30
- try {
31
- const config = loadOrCreateWallet();
32
- const res = await apiFetch(
33
- config,
34
- `/research?q=${encodeURIComponent(query)}&num_results=${num_results}`,
35
- );
36
- if (!res.ok) {
37
- const body = await res.text();
38
- return err(`Research API error ${res.status}: ${body}`);
39
- }
40
- return okPaid(await res.json());
41
- } catch (e: unknown) {
42
- logger.error({ err: e }, "research tool error");
43
- return err(`Research failed: ${String(e)}`);
44
- }
45
- }),
46
- );
47
- },
48
- );
49
- }
@@ -1,43 +0,0 @@
1
- import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
- import { z } from "zod";
3
- import { loadOrCreateWallet } from "../lib/wallet.js";
4
- import { apiFetch } from "../lib/api-fetch.js";
5
- import { okPaid, err } from "../lib/format.js";
6
- import { logger } from "../lib/logger.js";
7
- import { TOOL_PRICES } from "../lib/config.js";
8
- import { withBudget } from "../lib/budget.js";
9
- import { withCache } from "../lib/cache.js";
10
-
11
- export function registerScrapeTool(server: McpServer): void {
12
- server.registerTool(
13
- "scrape",
14
- {
15
- title: "Web Scraper",
16
- description: `Extract clean text content from any public URL.\nCost: $${TOOL_PRICES.scrape} USDC per scrape (paid via Stellar MPP).`, inputSchema: z.object({
17
- url: z.string().url().describe("URL of the page to scrape"),
18
- }),
19
- },
20
- async ({ url }) => {
21
- logger.debug({ url }, "scrape tool invoked");
22
- return withCache("scrape", { url }, () =>
23
- withBudget("scrape", async () => {
24
- try {
25
- const config = loadOrCreateWallet();
26
- const res = await apiFetch(
27
- config,
28
- `/scrape?url=${encodeURIComponent(url)}`,
29
- );
30
- if (!res.ok) {
31
- const body = await res.text();
32
- return err(`Scrape API error ${res.status}: ${body}`);
33
- }
34
- return okPaid(await res.json());
35
- } catch (e: unknown) {
36
- logger.error({ err: e }, "scrape tool error");
37
- return err(`Scrape failed: ${String(e)}`);
38
- }
39
- }),
40
- );
41
- },
42
- );
43
- }
@@ -1,48 +0,0 @@
1
- import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
- import { z } from "zod";
3
- import { loadOrCreateWallet } from "../lib/wallet.js";
4
- import { apiFetch } from "../lib/api-fetch.js";
5
- import { okPaid, err } from "../lib/format.js";
6
- import { logger } from "../lib/logger.js";
7
- import { TOOL_PRICES } from "../lib/config.js";
8
- import { withBudget } from "../lib/budget.js";
9
- import { withCache } from "../lib/cache.js";
10
-
11
- export function registerScreenshotTool(server: McpServer): void {
12
- server.registerTool(
13
- "screenshot",
14
- {
15
- title: "Web Screenshot",
16
- description: `Capture a screenshot of any public URL and return it as an image.\nCost: $${TOOL_PRICES.screenshot} USDC per screenshot (paid via Stellar MPP).`, inputSchema: z.object({
17
- url: z.string().url().describe("URL of the page to screenshot"),
18
- format: z
19
- .enum(["png", "jpg", "webp"])
20
- .default("png")
21
- .describe("Image format for the screenshot"),
22
- }),
23
- },
24
- async ({ url, format }) => {
25
- logger.debug({ url, format }, "screenshot tool invoked");
26
- return withCache("screenshot", { url, format }, () =>
27
- withBudget("screenshot", async () => {
28
- try {
29
- const config = loadOrCreateWallet();
30
- const params = new URLSearchParams({ url, format });
31
- const res = await apiFetch(
32
- config,
33
- `/screenshot?${params.toString()}`,
34
- );
35
- if (!res.ok) {
36
- const body = await res.text();
37
- return err(`Screenshot API error ${res.status}: ${body}`);
38
- }
39
- return okPaid(await res.json());
40
- } catch (e: unknown) {
41
- logger.error({ err: e }, "screenshot tool error");
42
- return err(`Screenshot failed: ${String(e)}`);
43
- }
44
- }),
45
- );
46
- },
47
- );
48
- }