@xlmtools/cli 0.1.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 (63) hide show
  1. package/README.md +99 -0
  2. package/dist/cli.js +214 -0
  3. package/dist/index.js +77 -0
  4. package/dist/lib/budget.js +54 -0
  5. package/dist/lib/cache.js +41 -0
  6. package/dist/lib/config.js +16 -0
  7. package/dist/lib/format.js +37 -0
  8. package/dist/lib/logger.js +2 -0
  9. package/dist/lib/wallet.js +106 -0
  10. package/dist/tools/budget.js +56 -0
  11. package/dist/tools/card.js +51 -0
  12. package/dist/tools/crypto.js +23 -0
  13. package/dist/tools/dex-candles.js +47 -0
  14. package/dist/tools/dex-orderbook.js +38 -0
  15. package/dist/tools/dex-trades.js +46 -0
  16. package/dist/tools/domain.js +22 -0
  17. package/dist/tools/image.js +42 -0
  18. package/dist/tools/oracle-price.js +36 -0
  19. package/dist/tools/reddit.js +40 -0
  20. package/dist/tools/research.js +39 -0
  21. package/dist/tools/scrape.js +32 -0
  22. package/dist/tools/screenshot.js +37 -0
  23. package/dist/tools/search.js +39 -0
  24. package/dist/tools/stellar-account.js +30 -0
  25. package/dist/tools/stellar-asset.js +31 -0
  26. package/dist/tools/stellar-pools.js +41 -0
  27. package/dist/tools/stocks.js +34 -0
  28. package/dist/tools/swap-quote.js +37 -0
  29. package/dist/tools/tools-list.js +16 -0
  30. package/dist/tools/wallet-tool.js +35 -0
  31. package/dist/tools/weather.js +22 -0
  32. package/dist/tools/youtube.js +42 -0
  33. package/package.json +28 -0
  34. package/src/cli.ts +240 -0
  35. package/src/index.ts +90 -0
  36. package/src/lib/budget.ts +78 -0
  37. package/src/lib/cache.ts +66 -0
  38. package/src/lib/config.ts +18 -0
  39. package/src/lib/format.ts +51 -0
  40. package/src/lib/logger.ts +6 -0
  41. package/src/lib/wallet.ts +143 -0
  42. package/src/tools/budget.ts +67 -0
  43. package/src/tools/crypto.ts +25 -0
  44. package/src/tools/dex-candles.ts +52 -0
  45. package/src/tools/dex-orderbook.ts +45 -0
  46. package/src/tools/dex-trades.ts +51 -0
  47. package/src/tools/domain.ts +24 -0
  48. package/src/tools/image.ts +49 -0
  49. package/src/tools/oracle-price.ts +42 -0
  50. package/src/tools/research.ts +47 -0
  51. package/src/tools/scrape.ts +41 -0
  52. package/src/tools/screenshot.ts +46 -0
  53. package/src/tools/search.ts +47 -0
  54. package/src/tools/stellar-account.ts +38 -0
  55. package/src/tools/stellar-asset.ts +39 -0
  56. package/src/tools/stellar-pools.ts +45 -0
  57. package/src/tools/stocks.ts +43 -0
  58. package/src/tools/swap-quote.ts +43 -0
  59. package/src/tools/tools-list.ts +22 -0
  60. package/src/tools/wallet-tool.ts +51 -0
  61. package/src/tools/weather.ts +24 -0
  62. package/src/tools/youtube.ts +49 -0
  63. package/tsconfig.json +13 -0
@@ -0,0 +1,56 @@
1
+ import { z } from "zod";
2
+ import { ok, err } from "../lib/format.js";
3
+ import { setBudget, clearBudget, getStatus } from "../lib/budget.js";
4
+ import { logger } from "../lib/logger.js";
5
+ export function registerBudgetTool(server) {
6
+ server.registerTool("budget", {
7
+ title: "Session Budget",
8
+ description: "Set, check, or clear a spending limit for this session. " +
9
+ "The budget resets when the MCP server restarts. " +
10
+ "Cached responses do not count against the budget.",
11
+ inputSchema: z.object({
12
+ action: z
13
+ .enum(["set", "check", "clear"])
14
+ .describe("Action: set a limit, check status, or clear limit"),
15
+ amount: z
16
+ .number()
17
+ .positive()
18
+ .optional()
19
+ .describe("Budget amount in USD (required for 'set')"),
20
+ }),
21
+ }, async ({ action, amount }) => {
22
+ logger.debug({ action, amount }, "budget tool invoked");
23
+ switch (action) {
24
+ case "set": {
25
+ if (amount === undefined) {
26
+ return err("Amount is required for 'set'. Example: budget set 5.00");
27
+ }
28
+ setBudget(amount);
29
+ return ok({
30
+ message: `Budget set to $${amount.toFixed(2)} for this session.`,
31
+ ...getStatus(),
32
+ });
33
+ }
34
+ case "check": {
35
+ const status = getStatus();
36
+ if (status.max === null) {
37
+ return ok({
38
+ message: "No budget limit set. All tool calls will be charged normally.",
39
+ ...status,
40
+ });
41
+ }
42
+ return ok({
43
+ message: `$${status.remaining?.toFixed(3)} remaining of $${status.max.toFixed(2)} budget.`,
44
+ ...status,
45
+ });
46
+ }
47
+ case "clear": {
48
+ clearBudget();
49
+ return ok({
50
+ message: "Budget limit cleared. All tool calls will be charged normally.",
51
+ ...getStatus(),
52
+ });
53
+ }
54
+ }
55
+ });
56
+ }
@@ -0,0 +1,51 @@
1
+ import { z } from "zod";
2
+ import { loadOrCreateWallet } from "../lib/wallet.js";
3
+ import { ok, err } from "../lib/format.js";
4
+ import { logger } from "../lib/logger.js";
5
+ export function registerCardTool(server) {
6
+ server.registerTool("card", {
7
+ title: "Virtual Card",
8
+ description: `Issue a virtual Mastercard for agent spending. Card is funded from your XLMTools USDC balance.
9
+ Cost: $10 flat card creation fee + load amount + 3.5% processing.
10
+ IMPORTANT: Card details are shown ONCE and not stored.
11
+ MVP: ASGCard API integration pending — add @asgcard/sdk and ASGCARD_STELLAR_KEY to enable real cards.`,
12
+ inputSchema: z.object({
13
+ amount: z
14
+ .number()
15
+ .min(5)
16
+ .max(1000)
17
+ .default(20)
18
+ .describe("Amount to load in USD ($5-$1000)"),
19
+ name: z
20
+ .string()
21
+ .default("AI AGENT")
22
+ .describe("Name on card"),
23
+ email: z
24
+ .string()
25
+ .email()
26
+ .default("agent@xlmtools.dev")
27
+ .describe("Email for registration"),
28
+ }),
29
+ }, async ({ amount, name, email }) => {
30
+ logger.debug({ amount, name, email }, "card tool invoked");
31
+ try {
32
+ const config = loadOrCreateWallet();
33
+ const res = await fetch(`${config.apiUrl}/card`, {
34
+ method: "POST",
35
+ headers: { "Content-Type": "application/json" },
36
+ body: JSON.stringify({ amount, nameOnCard: name, email }),
37
+ });
38
+ if (!res.ok) {
39
+ const body = await res.text();
40
+ return err(`Card API error ${res.status}: ${body}`);
41
+ }
42
+ const data = await res.json();
43
+ const totalCharge = (10 + amount + amount * 0.035).toFixed(3);
44
+ return ok({ ...data, total_charged_usdc: totalCharge });
45
+ }
46
+ catch (e) {
47
+ logger.error({ err: e }, "card tool error");
48
+ return err(String(e));
49
+ }
50
+ });
51
+ }
@@ -0,0 +1,23 @@
1
+ import { z } from "zod";
2
+ import { loadOrCreateWallet } from "../lib/wallet.js";
3
+ import { ok, err } from "../lib/format.js";
4
+ export function registerCryptoTool(server) {
5
+ server.registerTool("crypto", {
6
+ title: "Crypto Prices",
7
+ description: "Get real-time cryptocurrency prices, market caps, and 24h changes. Free.", inputSchema: z.object({
8
+ ids: z.string().describe("Comma-separated coin IDs (e.g. bitcoin,ethereum,stellar)"),
9
+ vs_currency: z.string().default("usd").describe("Quote currency (e.g. usd, eur)"),
10
+ }),
11
+ }, async ({ ids, vs_currency }) => {
12
+ try {
13
+ const config = loadOrCreateWallet();
14
+ const res = await fetch(`${config.apiUrl}/crypto?ids=${encodeURIComponent(ids)}&vs_currency=${vs_currency}`);
15
+ if (!res.ok)
16
+ return err(`Crypto API error: ${res.status}`);
17
+ return ok(await res.json());
18
+ }
19
+ catch (e) {
20
+ return err(String(e));
21
+ }
22
+ });
23
+ }
@@ -0,0 +1,47 @@
1
+ import { z } from "zod";
2
+ import { loadOrCreateWallet } from "../lib/wallet.js";
3
+ import { ok, err } from "../lib/format.js";
4
+ import { logger } from "../lib/logger.js";
5
+ export function registerDexCandlesTool(server) {
6
+ server.registerTool("dex-candles", {
7
+ title: "Stellar DEX Price Candles",
8
+ description: `Get OHLCV candlestick price data for any Stellar asset pair.\n` +
9
+ `Resolutions: 1m, 5m, 15m, 1h, 1d, 1w.\n` +
10
+ `Examples: "XLM/USDC" at "1h", "USDC/EURC" at "1d".\nFree.`, inputSchema: z.object({
11
+ pair: z
12
+ .string()
13
+ .describe('Asset pair (e.g. "XLM/USDC")'),
14
+ resolution: z
15
+ .enum(["1m", "5m", "15m", "1h", "1d", "1w"])
16
+ .default("1h")
17
+ .describe("Candle interval"),
18
+ limit: z
19
+ .number()
20
+ .int()
21
+ .min(1)
22
+ .max(200)
23
+ .default(20)
24
+ .describe("Number of candles (1-200)"),
25
+ }),
26
+ }, async ({ pair, resolution, limit }) => {
27
+ logger.debug({ pair, resolution, limit }, "dex-candles invoked");
28
+ try {
29
+ const config = loadOrCreateWallet();
30
+ const params = new URLSearchParams({
31
+ pair,
32
+ resolution,
33
+ limit: String(limit),
34
+ });
35
+ const res = await fetch(`${config.apiUrl}/dex-candles?${params}`);
36
+ if (!res.ok) {
37
+ const body = await res.text();
38
+ return err(`Candles error ${res.status}: ${body}`);
39
+ }
40
+ return ok(await res.json());
41
+ }
42
+ catch (e) {
43
+ logger.error({ err: e }, "dex-candles error");
44
+ return err(`Candles failed: ${String(e)}`);
45
+ }
46
+ });
47
+ }
@@ -0,0 +1,38 @@
1
+ import { z } from "zod";
2
+ import { loadOrCreateWallet } from "../lib/wallet.js";
3
+ import { ok, err } from "../lib/format.js";
4
+ import { logger } from "../lib/logger.js";
5
+ export function registerDexOrderbookTool(server) {
6
+ server.registerTool("dex-orderbook", {
7
+ title: "Stellar DEX Orderbook",
8
+ description: `View the live orderbook for any Stellar asset pair. Shows bids, asks, and spread.\n` +
9
+ `Use pairs like "XLM/USDC", "USDC/EURC", or "CODE:ISSUER/XLM".\n` +
10
+ `Well-known assets: XLM, USDC, EURC, AQUA, yUSDC, BLND, SHX.\nFree.`, inputSchema: z.object({
11
+ pair: z
12
+ .string()
13
+ .describe('Asset pair (e.g. "XLM/USDC", "USDC/EURC")'),
14
+ limit: z
15
+ .number()
16
+ .int()
17
+ .min(1)
18
+ .max(200)
19
+ .default(10)
20
+ .describe("Number of price levels per side (1-200)"),
21
+ }),
22
+ }, async ({ pair, limit }) => {
23
+ logger.debug({ pair, limit }, "dex-orderbook invoked");
24
+ try {
25
+ const config = loadOrCreateWallet();
26
+ const res = await fetch(`${config.apiUrl}/dex-orderbook?pair=${encodeURIComponent(pair)}&limit=${limit}`);
27
+ if (!res.ok) {
28
+ const body = await res.text();
29
+ return err(`Orderbook error ${res.status}: ${body}`);
30
+ }
31
+ return ok(await res.json());
32
+ }
33
+ catch (e) {
34
+ logger.error({ err: e }, "dex-orderbook error");
35
+ return err(`Orderbook failed: ${String(e)}`);
36
+ }
37
+ });
38
+ }
@@ -0,0 +1,46 @@
1
+ import { z } from "zod";
2
+ import { loadOrCreateWallet } from "../lib/wallet.js";
3
+ import { ok, err } from "../lib/format.js";
4
+ import { logger } from "../lib/logger.js";
5
+ export function registerDexTradesTool(server) {
6
+ server.registerTool("dex-trades", {
7
+ title: "Stellar DEX Trades",
8
+ description: `Get recent trades for any Stellar asset pair.\n` +
9
+ `Filter by trade type: "all", "orderbook", or "liquidity_pool".\nFree.`, inputSchema: z.object({
10
+ pair: z
11
+ .string()
12
+ .describe('Asset pair (e.g. "XLM/USDC")'),
13
+ limit: z
14
+ .number()
15
+ .int()
16
+ .min(1)
17
+ .max(200)
18
+ .default(10)
19
+ .describe("Number of trades (1-200)"),
20
+ trade_type: z
21
+ .enum(["all", "orderbook", "liquidity_pool"])
22
+ .default("all")
23
+ .describe("Filter by trade source"),
24
+ }),
25
+ }, async ({ pair, limit, trade_type }) => {
26
+ logger.debug({ pair, limit, trade_type }, "dex-trades invoked");
27
+ try {
28
+ const config = loadOrCreateWallet();
29
+ const params = new URLSearchParams({
30
+ pair,
31
+ limit: String(limit),
32
+ trade_type,
33
+ });
34
+ const res = await fetch(`${config.apiUrl}/dex-trades?${params}`);
35
+ if (!res.ok) {
36
+ const body = await res.text();
37
+ return err(`Trades error ${res.status}: ${body}`);
38
+ }
39
+ return ok(await res.json());
40
+ }
41
+ catch (e) {
42
+ logger.error({ err: e }, "dex-trades error");
43
+ return err(`Trades failed: ${String(e)}`);
44
+ }
45
+ });
46
+ }
@@ -0,0 +1,22 @@
1
+ import { z } from "zod";
2
+ import { loadOrCreateWallet } from "../lib/wallet.js";
3
+ import { ok, err } from "../lib/format.js";
4
+ export function registerDomainTool(server) {
5
+ server.registerTool("domain", {
6
+ title: "Domain Availability",
7
+ description: "Check if a domain name is available. Free.", inputSchema: z.object({
8
+ name: z.string().describe("Domain name to check (e.g. stellar-tools.xyz)"),
9
+ }),
10
+ }, async ({ name }) => {
11
+ try {
12
+ const config = loadOrCreateWallet();
13
+ const res = await fetch(`${config.apiUrl}/domain?name=${encodeURIComponent(name)}`);
14
+ if (!res.ok)
15
+ return err(`Domain API error: ${res.status}`);
16
+ return ok(await res.json());
17
+ }
18
+ catch (e) {
19
+ return err(String(e));
20
+ }
21
+ });
22
+ }
@@ -0,0 +1,42 @@
1
+ import { z } from "zod";
2
+ import { loadOrCreateWallet } from "../lib/wallet.js";
3
+ import { okPaid, err } from "../lib/format.js";
4
+ import { logger } from "../lib/logger.js";
5
+ import { TOOL_PRICES } from "../lib/config.js";
6
+ import { withBudget } from "../lib/budget.js";
7
+ import { withCache } from "../lib/cache.js";
8
+ export function registerImageTool(server) {
9
+ server.registerTool("image", {
10
+ title: "AI Image Generation",
11
+ description: `Generate images from a text prompt using AI.\nCost: $${TOOL_PRICES.image} USDC per image (paid via Stellar MPP).`, inputSchema: z.object({
12
+ prompt: z
13
+ .string()
14
+ .describe("Text prompt describing the image to generate"),
15
+ size: z
16
+ .enum(["1024x1024", "1024x1792", "1792x1024"])
17
+ .default("1024x1024")
18
+ .describe("Output image dimensions"),
19
+ }),
20
+ }, async ({ prompt, size }) => {
21
+ logger.debug({ prompt, size }, "image tool invoked");
22
+ return withCache("image", { prompt, size }, () => withBudget("image", async () => {
23
+ try {
24
+ const config = loadOrCreateWallet();
25
+ const res = await fetch(`${config.apiUrl}/image`, {
26
+ method: "POST",
27
+ headers: { "Content-Type": "application/json" },
28
+ body: JSON.stringify({ prompt, size }),
29
+ });
30
+ if (!res.ok) {
31
+ const body = await res.text();
32
+ return err(`Image API error ${res.status}: ${body}`);
33
+ }
34
+ return okPaid(await res.json());
35
+ }
36
+ catch (e) {
37
+ logger.error({ err: e }, "image tool error");
38
+ return err(`Image generation failed: ${String(e)}`);
39
+ }
40
+ }));
41
+ });
42
+ }
@@ -0,0 +1,36 @@
1
+ import { z } from "zod";
2
+ import { loadOrCreateWallet } from "../lib/wallet.js";
3
+ import { ok, err } from "../lib/format.js";
4
+ import { logger } from "../lib/logger.js";
5
+ export function registerOraclePriceTool(server) {
6
+ server.registerTool("oracle-price", {
7
+ title: "Stellar Oracle Price",
8
+ description: `Get real-time prices from Reflector, Stellar's decentralized oracle.\n` +
9
+ `Feeds: "crypto" (BTC, ETH, SOL, etc.), "fiat" (EUR, GBP, JPY), "dex" (Stellar DEX pairs).\n` +
10
+ `Prices are updated every 5 minutes via multi-node consensus.\nFree.`, inputSchema: z.object({
11
+ asset: z
12
+ .string()
13
+ .describe('Asset ticker (e.g. "BTC", "ETH", "EUR", "SOL")'),
14
+ feed: z
15
+ .enum(["crypto", "fiat", "dex"])
16
+ .default("crypto")
17
+ .describe("Oracle feed to query"),
18
+ }),
19
+ }, async ({ asset, feed }) => {
20
+ logger.debug({ asset, feed }, "oracle-price invoked");
21
+ try {
22
+ const config = loadOrCreateWallet();
23
+ const params = new URLSearchParams({ asset, feed });
24
+ const res = await fetch(`${config.apiUrl}/oracle-price?${params}`);
25
+ if (!res.ok) {
26
+ const body = await res.text();
27
+ return err(`Oracle error ${res.status}: ${body}`);
28
+ }
29
+ return ok(await res.json());
30
+ }
31
+ catch (e) {
32
+ logger.error({ err: e }, "oracle-price error");
33
+ return err(`Oracle failed: ${String(e)}`);
34
+ }
35
+ });
36
+ }
@@ -0,0 +1,40 @@
1
+ import { z } from "zod";
2
+ import { loadOrCreateWallet } from "../lib/wallet.js";
3
+ import { ok, err } from "../lib/format.js";
4
+ import { logger } from "../lib/logger.js";
5
+ import { TOOL_PRICES } from "../lib/config.js";
6
+ export function registerRedditTool(server) {
7
+ server.registerTool("reddit", {
8
+ title: "Reddit Search",
9
+ description: `Search Reddit posts and comments in real-time.\nCost: $${TOOL_PRICES.reddit} USDC per query (paid via Stellar MPP).`,
10
+ inputSchema: z.object({
11
+ query: z.string().describe("Search query"),
12
+ subreddit: z
13
+ .string()
14
+ .optional()
15
+ .describe("Limit search to a specific subreddit (without r/ prefix)"),
16
+ sort: z
17
+ .enum(["relevance", "hot", "new", "top"])
18
+ .default("relevance")
19
+ .describe("Sort order for results"),
20
+ }),
21
+ }, async ({ query, subreddit, sort }) => {
22
+ logger.debug({ query, subreddit, sort }, "reddit tool invoked");
23
+ try {
24
+ const config = loadOrCreateWallet();
25
+ const params = new URLSearchParams({ q: query, sort });
26
+ if (subreddit)
27
+ params.set("subreddit", subreddit);
28
+ const res = await fetch(`${config.apiUrl}/reddit?${params.toString()}`);
29
+ if (!res.ok) {
30
+ const body = await res.text();
31
+ return err(`Reddit API error ${res.status}: ${body}`);
32
+ }
33
+ return ok(await res.json());
34
+ }
35
+ catch (e) {
36
+ logger.error({ err: e }, "reddit tool error");
37
+ return err(`Reddit search failed: ${String(e)}`);
38
+ }
39
+ });
40
+ }
@@ -0,0 +1,39 @@
1
+ import { z } from "zod";
2
+ import { loadOrCreateWallet } from "../lib/wallet.js";
3
+ import { okPaid, err } from "../lib/format.js";
4
+ import { logger } from "../lib/logger.js";
5
+ import { TOOL_PRICES } from "../lib/config.js";
6
+ import { withBudget } from "../lib/budget.js";
7
+ import { withCache } from "../lib/cache.js";
8
+ export function registerResearchTool(server) {
9
+ server.registerTool("research", {
10
+ title: "Deep Research",
11
+ 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({
12
+ query: z.string().describe("Research query"),
13
+ num_results: z
14
+ .number()
15
+ .int()
16
+ .min(1)
17
+ .max(20)
18
+ .default(5)
19
+ .describe("Number of results to return (1-20)"),
20
+ }),
21
+ }, async ({ query, num_results }) => {
22
+ logger.debug({ query, num_results }, "research tool invoked");
23
+ return withCache("research", { query, num_results }, () => withBudget("research", async () => {
24
+ try {
25
+ const config = loadOrCreateWallet();
26
+ const res = await fetch(`${config.apiUrl}/research?q=${encodeURIComponent(query)}&num_results=${num_results}`);
27
+ if (!res.ok) {
28
+ const body = await res.text();
29
+ return err(`Research API error ${res.status}: ${body}`);
30
+ }
31
+ return okPaid(await res.json());
32
+ }
33
+ catch (e) {
34
+ logger.error({ err: e }, "research tool error");
35
+ return err(`Research failed: ${String(e)}`);
36
+ }
37
+ }));
38
+ });
39
+ }
@@ -0,0 +1,32 @@
1
+ import { z } from "zod";
2
+ import { loadOrCreateWallet } from "../lib/wallet.js";
3
+ import { okPaid, err } from "../lib/format.js";
4
+ import { logger } from "../lib/logger.js";
5
+ import { TOOL_PRICES } from "../lib/config.js";
6
+ import { withBudget } from "../lib/budget.js";
7
+ import { withCache } from "../lib/cache.js";
8
+ export function registerScrapeTool(server) {
9
+ server.registerTool("scrape", {
10
+ title: "Web Scraper",
11
+ description: `Extract clean text content from any public URL.\nCost: $${TOOL_PRICES.scrape} USDC per scrape (paid via Stellar MPP).`, inputSchema: z.object({
12
+ url: z.string().url().describe("URL of the page to scrape"),
13
+ }),
14
+ }, async ({ url }) => {
15
+ logger.debug({ url }, "scrape tool invoked");
16
+ return withCache("scrape", { url }, () => withBudget("scrape", async () => {
17
+ try {
18
+ const config = loadOrCreateWallet();
19
+ const res = await fetch(`${config.apiUrl}/scrape?url=${encodeURIComponent(url)}`);
20
+ if (!res.ok) {
21
+ const body = await res.text();
22
+ return err(`Scrape API error ${res.status}: ${body}`);
23
+ }
24
+ return okPaid(await res.json());
25
+ }
26
+ catch (e) {
27
+ logger.error({ err: e }, "scrape tool error");
28
+ return err(`Scrape failed: ${String(e)}`);
29
+ }
30
+ }));
31
+ });
32
+ }
@@ -0,0 +1,37 @@
1
+ import { z } from "zod";
2
+ import { loadOrCreateWallet } from "../lib/wallet.js";
3
+ import { okPaid, err } from "../lib/format.js";
4
+ import { logger } from "../lib/logger.js";
5
+ import { TOOL_PRICES } from "../lib/config.js";
6
+ import { withBudget } from "../lib/budget.js";
7
+ import { withCache } from "../lib/cache.js";
8
+ export function registerScreenshotTool(server) {
9
+ server.registerTool("screenshot", {
10
+ title: "Web Screenshot",
11
+ 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({
12
+ url: z.string().url().describe("URL of the page to screenshot"),
13
+ format: z
14
+ .enum(["png", "jpg", "webp"])
15
+ .default("png")
16
+ .describe("Image format for the screenshot"),
17
+ }),
18
+ }, async ({ url, format }) => {
19
+ logger.debug({ url, format }, "screenshot tool invoked");
20
+ return withCache("screenshot", { url, format }, () => withBudget("screenshot", async () => {
21
+ try {
22
+ const config = loadOrCreateWallet();
23
+ const params = new URLSearchParams({ url, format });
24
+ const res = await fetch(`${config.apiUrl}/screenshot?${params.toString()}`);
25
+ if (!res.ok) {
26
+ const body = await res.text();
27
+ return err(`Screenshot API error ${res.status}: ${body}`);
28
+ }
29
+ return okPaid(await res.json());
30
+ }
31
+ catch (e) {
32
+ logger.error({ err: e }, "screenshot tool error");
33
+ return err(`Screenshot failed: ${String(e)}`);
34
+ }
35
+ }));
36
+ });
37
+ }
@@ -0,0 +1,39 @@
1
+ import { z } from "zod";
2
+ import { loadOrCreateWallet } from "../lib/wallet.js";
3
+ import { okPaid, err } from "../lib/format.js";
4
+ import { logger } from "../lib/logger.js";
5
+ import { TOOL_PRICES } from "../lib/config.js";
6
+ import { withBudget } from "../lib/budget.js";
7
+ import { withCache } from "../lib/cache.js";
8
+ export function registerSearchTool(server) {
9
+ server.registerTool("search", {
10
+ title: "Web Search",
11
+ description: `Search the web and news in real-time. Returns results with source URLs.\nCost: $${TOOL_PRICES.search} USDC per search (paid via Stellar MPP).`, inputSchema: z.object({
12
+ query: z.string().describe("Search query"),
13
+ count: z
14
+ .number()
15
+ .int()
16
+ .min(1)
17
+ .max(20)
18
+ .default(10)
19
+ .describe("Number of results (1-20)"),
20
+ }),
21
+ }, async ({ query, count }) => {
22
+ logger.debug({ query, count }, "search tool invoked");
23
+ return withCache("search", { query, count }, () => withBudget("search", async () => {
24
+ try {
25
+ const config = loadOrCreateWallet();
26
+ const res = await fetch(`${config.apiUrl}/search?q=${encodeURIComponent(query)}&count=${count}`);
27
+ if (!res.ok) {
28
+ const body = await res.text();
29
+ return err(`Search API error ${res.status}: ${body}`);
30
+ }
31
+ return okPaid(await res.json());
32
+ }
33
+ catch (e) {
34
+ logger.error({ err: e }, "search tool error");
35
+ return err(`Search failed: ${String(e)}`);
36
+ }
37
+ }));
38
+ });
39
+ }
@@ -0,0 +1,30 @@
1
+ import { z } from "zod";
2
+ import { loadOrCreateWallet } from "../lib/wallet.js";
3
+ import { ok, err } from "../lib/format.js";
4
+ import { logger } from "../lib/logger.js";
5
+ export function registerStellarAccountTool(server) {
6
+ server.registerTool("stellar-account", {
7
+ title: "Stellar Account Lookup",
8
+ description: `Look up any Stellar account: balances, trustlines, signers, and status.\n` +
9
+ `Provide a Stellar public key (starts with G, 56 characters).\nFree.`, inputSchema: z.object({
10
+ address: z
11
+ .string()
12
+ .describe("Stellar public key (G-address)"),
13
+ }),
14
+ }, async ({ address }) => {
15
+ logger.debug({ address }, "stellar-account invoked");
16
+ try {
17
+ const config = loadOrCreateWallet();
18
+ const res = await fetch(`${config.apiUrl}/stellar-account?address=${encodeURIComponent(address)}`);
19
+ if (!res.ok) {
20
+ const body = await res.text();
21
+ return err(`Account lookup error ${res.status}: ${body}`);
22
+ }
23
+ return ok(await res.json());
24
+ }
25
+ catch (e) {
26
+ logger.error({ err: e }, "stellar-account error");
27
+ return err(`Account lookup failed: ${String(e)}`);
28
+ }
29
+ });
30
+ }
@@ -0,0 +1,31 @@
1
+ import { z } from "zod";
2
+ import { loadOrCreateWallet } from "../lib/wallet.js";
3
+ import { ok, err } from "../lib/format.js";
4
+ import { logger } from "../lib/logger.js";
5
+ export function registerStellarAssetTool(server) {
6
+ server.registerTool("stellar-asset", {
7
+ title: "Stellar Asset Info",
8
+ description: `Look up any Stellar asset: supply, trustlines, rating, issuer org, and more.\n` +
9
+ `Combines on-chain data (Horizon) with analytics (StellarExpert).\n` +
10
+ `Examples: "USDC", "AQUA", "yUSDC", or "CODE:ISSUER".\nFree.`, inputSchema: z.object({
11
+ asset: z
12
+ .string()
13
+ .describe('Asset to look up (e.g. "USDC", "XLM", "AQUA", "CODE:ISSUER")'),
14
+ }),
15
+ }, async ({ asset }) => {
16
+ logger.debug({ asset }, "stellar-asset invoked");
17
+ try {
18
+ const config = loadOrCreateWallet();
19
+ const res = await fetch(`${config.apiUrl}/stellar-asset?asset=${encodeURIComponent(asset)}`);
20
+ if (!res.ok) {
21
+ const body = await res.text();
22
+ return err(`Asset lookup error ${res.status}: ${body}`);
23
+ }
24
+ return ok(await res.json());
25
+ }
26
+ catch (e) {
27
+ logger.error({ err: e }, "stellar-asset error");
28
+ return err(`Asset lookup failed: ${String(e)}`);
29
+ }
30
+ });
31
+ }