@xlmtools/cli 0.1.0 → 0.1.2
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/dist/cli.js +9 -2
- package/dist/lib/api-fetch.js +20 -0
- package/dist/lib/budget.js +4 -0
- package/dist/lib/cache.js +5 -1
- package/dist/lib/wallet.js +1 -1
- package/dist/tools/budget.js +1 -2
- package/dist/tools/crypto.js +2 -1
- package/dist/tools/dex-candles.js +3 -3
- package/dist/tools/dex-orderbook.js +3 -3
- package/dist/tools/dex-trades.js +3 -3
- package/dist/tools/domain.js +2 -1
- package/dist/tools/image.js +2 -1
- package/dist/tools/oracle-price.js +2 -1
- package/dist/tools/research.js +3 -3
- package/dist/tools/scrape.js +2 -1
- package/dist/tools/screenshot.js +2 -1
- package/dist/tools/search.js +3 -3
- package/dist/tools/stellar-account.js +2 -1
- package/dist/tools/stellar-asset.js +2 -1
- package/dist/tools/stellar-pools.js +3 -3
- package/dist/tools/stocks.js +2 -1
- package/dist/tools/swap-quote.js +2 -1
- package/dist/tools/weather.js +2 -1
- package/dist/tools/youtube.js +2 -1
- package/package.json +1 -1
- package/src/cli.ts +6 -1
- package/src/lib/api-fetch.ts +30 -0
- package/src/lib/wallet.ts +1 -1
- package/src/tools/crypto.ts +2 -1
- package/src/tools/dex-candles.ts +2 -1
- package/src/tools/dex-orderbook.ts +4 -2
- package/src/tools/dex-trades.ts +2 -1
- package/src/tools/domain.ts +2 -1
- package/src/tools/image.ts +2 -1
- package/src/tools/oracle-price.ts +2 -1
- package/src/tools/research.ts +4 -2
- package/src/tools/scrape.ts +4 -2
- package/src/tools/screenshot.ts +4 -2
- package/src/tools/search.ts +4 -2
- package/src/tools/stellar-account.ts +4 -2
- package/src/tools/stellar-asset.ts +4 -2
- package/src/tools/stellar-pools.ts +2 -1
- package/src/tools/stocks.ts +4 -2
- package/src/tools/swap-quote.ts +2 -1
- package/src/tools/weather.ts +2 -1
- package/src/tools/youtube.ts +4 -2
package/dist/cli.js
CHANGED
|
@@ -3,6 +3,7 @@ import { Mppx } from "mppx/client";
|
|
|
3
3
|
import { stellar } from "@stellar/mpp/charge/client";
|
|
4
4
|
import { Horizon } from "@stellar/stellar-sdk";
|
|
5
5
|
import { loadOrCreateWallet, getKeypair } from "./lib/wallet.js";
|
|
6
|
+
import { apiFetch } from "./lib/api-fetch.js";
|
|
6
7
|
import { TOOL_PRICES } from "./lib/config.js";
|
|
7
8
|
import { logger } from "./lib/logger.js";
|
|
8
9
|
// ── Arg parsing ──────────────────────────────────────────
|
|
@@ -106,8 +107,10 @@ function handleTools() {
|
|
|
106
107
|
name,
|
|
107
108
|
price: `$${price}`,
|
|
108
109
|
}));
|
|
110
|
+
// Note: `budget` is MCP-only — the CLI is a fresh process per invocation
|
|
111
|
+
// so a session-scoped cap is meaningless. Excluded from the CLI's free list.
|
|
109
112
|
const free = [
|
|
110
|
-
"crypto", "weather", "domain", "wallet", "tools",
|
|
113
|
+
"crypto", "weather", "domain", "wallet", "tools",
|
|
111
114
|
"dex-orderbook", "dex-candles", "dex-trades", "swap-quote",
|
|
112
115
|
"stellar-asset", "stellar-account", "stellar-pools", "oracle-price",
|
|
113
116
|
];
|
|
@@ -188,7 +191,11 @@ async function main() {
|
|
|
188
191
|
if (isPaid) {
|
|
189
192
|
process.stderr.write(` Tool: ${tool} · Cost: $${TOOL_PRICES[tool]} USDC\n`);
|
|
190
193
|
}
|
|
191
|
-
|
|
194
|
+
// Route through apiFetch so the X-XLMTools-Client header (and any
|
|
195
|
+
// future cross-cutting concerns like retries / user-agent) are
|
|
196
|
+
// applied consistently with the MCP tools. apiFetch detects the
|
|
197
|
+
// full-URL form and passes it straight through.
|
|
198
|
+
const res = await apiFetch(config, url, init);
|
|
192
199
|
if (!res.ok) {
|
|
193
200
|
const text = await res.text();
|
|
194
201
|
process.stderr.write(`Error ${res.status}: ${text}\n`);
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared fetch helper for tool calls against the XLMTools API.
|
|
3
|
+
*
|
|
4
|
+
* Every API request gets stamped with `X-XLMTools-Client: <G-address>`
|
|
5
|
+
* so the server can attribute calls back to the user's wallet on the
|
|
6
|
+
* /stats/by-client endpoint. The header is self-declared (no signature)
|
|
7
|
+
* which is fine because:
|
|
8
|
+
* - Paid calls are additionally verified by the Stellar tx receipt
|
|
9
|
+
* - Free calls have no economic value to spoof
|
|
10
|
+
*
|
|
11
|
+
* Usage:
|
|
12
|
+
* const config = loadOrCreateWallet();
|
|
13
|
+
* const res = await apiFetch(config, `/crypto?ids=${ids}`);
|
|
14
|
+
*/
|
|
15
|
+
export async function apiFetch(config, path, init) {
|
|
16
|
+
const url = path.startsWith("http") ? path : `${config.apiUrl}${path}`;
|
|
17
|
+
const headers = new Headers(init?.headers);
|
|
18
|
+
headers.set("X-XLMTools-Client", config.stellarPublicKey);
|
|
19
|
+
return fetch(url, { ...init, headers });
|
|
20
|
+
}
|
package/dist/lib/budget.js
CHANGED
|
@@ -6,6 +6,10 @@ let maxBudget = null;
|
|
|
6
6
|
let totalSpent = 0;
|
|
7
7
|
export function setBudget(max) {
|
|
8
8
|
maxBudget = max;
|
|
9
|
+
// Reset spent counter so the new cap starts fresh. Users expect
|
|
10
|
+
// "I just set a $1.00 budget" to mean they have $1.00 to spend
|
|
11
|
+
// from now — not "$1.00 minus whatever I spent before setting it".
|
|
12
|
+
totalSpent = 0;
|
|
9
13
|
logger.info({ max }, "session budget set");
|
|
10
14
|
}
|
|
11
15
|
export function clearBudget() {
|
package/dist/lib/cache.js
CHANGED
|
@@ -13,12 +13,16 @@ export async function withCache(toolName, params, fn) {
|
|
|
13
13
|
logger.debug({ toolName }, "cache hit");
|
|
14
14
|
const original = cached.result;
|
|
15
15
|
if (original.content[0]?.type === "text") {
|
|
16
|
+
// Strip the "---\nPayment: ..." footer from cached text — no
|
|
17
|
+
// payment happened on this call, so the original tx hash would
|
|
18
|
+
// contradict the [cached — no charge] prefix.
|
|
19
|
+
const stripped = original.content[0].text.replace(/\n---\nPayment: .*$/s, "");
|
|
16
20
|
return {
|
|
17
21
|
...original,
|
|
18
22
|
content: [
|
|
19
23
|
{
|
|
20
24
|
type: "text",
|
|
21
|
-
text: `[cached — no charge]\n\n${
|
|
25
|
+
text: `[cached — no charge]\n\n${stripped}`,
|
|
22
26
|
},
|
|
23
27
|
],
|
|
24
28
|
};
|
package/dist/lib/wallet.js
CHANGED
|
@@ -70,7 +70,7 @@ export function loadOrCreateWallet() {
|
|
|
70
70
|
const config = {
|
|
71
71
|
stellarPrivateKey: keypair.secret(),
|
|
72
72
|
stellarPublicKey: keypair.publicKey(),
|
|
73
|
-
apiUrl: process.env.
|
|
73
|
+
apiUrl: process.env.XLMTOOLS_API_URL ?? "https://api.xlmtools.com",
|
|
74
74
|
};
|
|
75
75
|
mkdirSync(CONFIG_DIR, { recursive: true });
|
|
76
76
|
writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 2), { mode: 0o600 });
|
package/dist/tools/budget.js
CHANGED
|
@@ -12,8 +12,7 @@ export function registerBudgetTool(server) {
|
|
|
12
12
|
action: z
|
|
13
13
|
.enum(["set", "check", "clear"])
|
|
14
14
|
.describe("Action: set a limit, check status, or clear limit"),
|
|
15
|
-
amount: z
|
|
16
|
-
.number()
|
|
15
|
+
amount: z.coerce.number()
|
|
17
16
|
.positive()
|
|
18
17
|
.optional()
|
|
19
18
|
.describe("Budget amount in USD (required for 'set')"),
|
package/dist/tools/crypto.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { loadOrCreateWallet } from "../lib/wallet.js";
|
|
3
|
+
import { apiFetch } from "../lib/api-fetch.js";
|
|
3
4
|
import { ok, err } from "../lib/format.js";
|
|
4
5
|
export function registerCryptoTool(server) {
|
|
5
6
|
server.registerTool("crypto", {
|
|
@@ -11,7 +12,7 @@ export function registerCryptoTool(server) {
|
|
|
11
12
|
}, async ({ ids, vs_currency }) => {
|
|
12
13
|
try {
|
|
13
14
|
const config = loadOrCreateWallet();
|
|
14
|
-
const res = await
|
|
15
|
+
const res = await apiFetch(config, `/crypto?ids=${encodeURIComponent(ids)}&vs_currency=${vs_currency}`);
|
|
15
16
|
if (!res.ok)
|
|
16
17
|
return err(`Crypto API error: ${res.status}`);
|
|
17
18
|
return ok(await res.json());
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { loadOrCreateWallet } from "../lib/wallet.js";
|
|
3
|
+
import { apiFetch } from "../lib/api-fetch.js";
|
|
3
4
|
import { ok, err } from "../lib/format.js";
|
|
4
5
|
import { logger } from "../lib/logger.js";
|
|
5
6
|
export function registerDexCandlesTool(server) {
|
|
@@ -15,8 +16,7 @@ export function registerDexCandlesTool(server) {
|
|
|
15
16
|
.enum(["1m", "5m", "15m", "1h", "1d", "1w"])
|
|
16
17
|
.default("1h")
|
|
17
18
|
.describe("Candle interval"),
|
|
18
|
-
limit: z
|
|
19
|
-
.number()
|
|
19
|
+
limit: z.coerce.number()
|
|
20
20
|
.int()
|
|
21
21
|
.min(1)
|
|
22
22
|
.max(200)
|
|
@@ -32,7 +32,7 @@ export function registerDexCandlesTool(server) {
|
|
|
32
32
|
resolution,
|
|
33
33
|
limit: String(limit),
|
|
34
34
|
});
|
|
35
|
-
const res = await
|
|
35
|
+
const res = await apiFetch(config, `/dex-candles?${params}`);
|
|
36
36
|
if (!res.ok) {
|
|
37
37
|
const body = await res.text();
|
|
38
38
|
return err(`Candles error ${res.status}: ${body}`);
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { loadOrCreateWallet } from "../lib/wallet.js";
|
|
3
|
+
import { apiFetch } from "../lib/api-fetch.js";
|
|
3
4
|
import { ok, err } from "../lib/format.js";
|
|
4
5
|
import { logger } from "../lib/logger.js";
|
|
5
6
|
export function registerDexOrderbookTool(server) {
|
|
@@ -11,8 +12,7 @@ export function registerDexOrderbookTool(server) {
|
|
|
11
12
|
pair: z
|
|
12
13
|
.string()
|
|
13
14
|
.describe('Asset pair (e.g. "XLM/USDC", "USDC/EURC")'),
|
|
14
|
-
limit: z
|
|
15
|
-
.number()
|
|
15
|
+
limit: z.coerce.number()
|
|
16
16
|
.int()
|
|
17
17
|
.min(1)
|
|
18
18
|
.max(200)
|
|
@@ -23,7 +23,7 @@ export function registerDexOrderbookTool(server) {
|
|
|
23
23
|
logger.debug({ pair, limit }, "dex-orderbook invoked");
|
|
24
24
|
try {
|
|
25
25
|
const config = loadOrCreateWallet();
|
|
26
|
-
const res = await
|
|
26
|
+
const res = await apiFetch(config, `/dex-orderbook?pair=${encodeURIComponent(pair)}&limit=${limit}`);
|
|
27
27
|
if (!res.ok) {
|
|
28
28
|
const body = await res.text();
|
|
29
29
|
return err(`Orderbook error ${res.status}: ${body}`);
|
package/dist/tools/dex-trades.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { loadOrCreateWallet } from "../lib/wallet.js";
|
|
3
|
+
import { apiFetch } from "../lib/api-fetch.js";
|
|
3
4
|
import { ok, err } from "../lib/format.js";
|
|
4
5
|
import { logger } from "../lib/logger.js";
|
|
5
6
|
export function registerDexTradesTool(server) {
|
|
@@ -10,8 +11,7 @@ export function registerDexTradesTool(server) {
|
|
|
10
11
|
pair: z
|
|
11
12
|
.string()
|
|
12
13
|
.describe('Asset pair (e.g. "XLM/USDC")'),
|
|
13
|
-
limit: z
|
|
14
|
-
.number()
|
|
14
|
+
limit: z.coerce.number()
|
|
15
15
|
.int()
|
|
16
16
|
.min(1)
|
|
17
17
|
.max(200)
|
|
@@ -31,7 +31,7 @@ export function registerDexTradesTool(server) {
|
|
|
31
31
|
limit: String(limit),
|
|
32
32
|
trade_type,
|
|
33
33
|
});
|
|
34
|
-
const res = await
|
|
34
|
+
const res = await apiFetch(config, `/dex-trades?${params}`);
|
|
35
35
|
if (!res.ok) {
|
|
36
36
|
const body = await res.text();
|
|
37
37
|
return err(`Trades error ${res.status}: ${body}`);
|
package/dist/tools/domain.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { loadOrCreateWallet } from "../lib/wallet.js";
|
|
3
|
+
import { apiFetch } from "../lib/api-fetch.js";
|
|
3
4
|
import { ok, err } from "../lib/format.js";
|
|
4
5
|
export function registerDomainTool(server) {
|
|
5
6
|
server.registerTool("domain", {
|
|
@@ -10,7 +11,7 @@ export function registerDomainTool(server) {
|
|
|
10
11
|
}, async ({ name }) => {
|
|
11
12
|
try {
|
|
12
13
|
const config = loadOrCreateWallet();
|
|
13
|
-
const res = await
|
|
14
|
+
const res = await apiFetch(config, `/domain?name=${encodeURIComponent(name)}`);
|
|
14
15
|
if (!res.ok)
|
|
15
16
|
return err(`Domain API error: ${res.status}`);
|
|
16
17
|
return ok(await res.json());
|
package/dist/tools/image.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { loadOrCreateWallet } from "../lib/wallet.js";
|
|
3
|
+
import { apiFetch } from "../lib/api-fetch.js";
|
|
3
4
|
import { okPaid, err } from "../lib/format.js";
|
|
4
5
|
import { logger } from "../lib/logger.js";
|
|
5
6
|
import { TOOL_PRICES } from "../lib/config.js";
|
|
@@ -22,7 +23,7 @@ export function registerImageTool(server) {
|
|
|
22
23
|
return withCache("image", { prompt, size }, () => withBudget("image", async () => {
|
|
23
24
|
try {
|
|
24
25
|
const config = loadOrCreateWallet();
|
|
25
|
-
const res = await
|
|
26
|
+
const res = await apiFetch(config, `/image`, {
|
|
26
27
|
method: "POST",
|
|
27
28
|
headers: { "Content-Type": "application/json" },
|
|
28
29
|
body: JSON.stringify({ prompt, size }),
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { loadOrCreateWallet } from "../lib/wallet.js";
|
|
3
|
+
import { apiFetch } from "../lib/api-fetch.js";
|
|
3
4
|
import { ok, err } from "../lib/format.js";
|
|
4
5
|
import { logger } from "../lib/logger.js";
|
|
5
6
|
export function registerOraclePriceTool(server) {
|
|
@@ -21,7 +22,7 @@ export function registerOraclePriceTool(server) {
|
|
|
21
22
|
try {
|
|
22
23
|
const config = loadOrCreateWallet();
|
|
23
24
|
const params = new URLSearchParams({ asset, feed });
|
|
24
|
-
const res = await
|
|
25
|
+
const res = await apiFetch(config, `/oracle-price?${params}`);
|
|
25
26
|
if (!res.ok) {
|
|
26
27
|
const body = await res.text();
|
|
27
28
|
return err(`Oracle error ${res.status}: ${body}`);
|
package/dist/tools/research.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { loadOrCreateWallet } from "../lib/wallet.js";
|
|
3
|
+
import { apiFetch } from "../lib/api-fetch.js";
|
|
3
4
|
import { okPaid, err } from "../lib/format.js";
|
|
4
5
|
import { logger } from "../lib/logger.js";
|
|
5
6
|
import { TOOL_PRICES } from "../lib/config.js";
|
|
@@ -10,8 +11,7 @@ export function registerResearchTool(server) {
|
|
|
10
11
|
title: "Deep Research",
|
|
11
12
|
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
13
|
query: z.string().describe("Research query"),
|
|
13
|
-
num_results: z
|
|
14
|
-
.number()
|
|
14
|
+
num_results: z.coerce.number()
|
|
15
15
|
.int()
|
|
16
16
|
.min(1)
|
|
17
17
|
.max(20)
|
|
@@ -23,7 +23,7 @@ export function registerResearchTool(server) {
|
|
|
23
23
|
return withCache("research", { query, num_results }, () => withBudget("research", async () => {
|
|
24
24
|
try {
|
|
25
25
|
const config = loadOrCreateWallet();
|
|
26
|
-
const res = await
|
|
26
|
+
const res = await apiFetch(config, `/research?q=${encodeURIComponent(query)}&num_results=${num_results}`);
|
|
27
27
|
if (!res.ok) {
|
|
28
28
|
const body = await res.text();
|
|
29
29
|
return err(`Research API error ${res.status}: ${body}`);
|
package/dist/tools/scrape.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { loadOrCreateWallet } from "../lib/wallet.js";
|
|
3
|
+
import { apiFetch } from "../lib/api-fetch.js";
|
|
3
4
|
import { okPaid, err } from "../lib/format.js";
|
|
4
5
|
import { logger } from "../lib/logger.js";
|
|
5
6
|
import { TOOL_PRICES } from "../lib/config.js";
|
|
@@ -16,7 +17,7 @@ export function registerScrapeTool(server) {
|
|
|
16
17
|
return withCache("scrape", { url }, () => withBudget("scrape", async () => {
|
|
17
18
|
try {
|
|
18
19
|
const config = loadOrCreateWallet();
|
|
19
|
-
const res = await
|
|
20
|
+
const res = await apiFetch(config, `/scrape?url=${encodeURIComponent(url)}`);
|
|
20
21
|
if (!res.ok) {
|
|
21
22
|
const body = await res.text();
|
|
22
23
|
return err(`Scrape API error ${res.status}: ${body}`);
|
package/dist/tools/screenshot.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { loadOrCreateWallet } from "../lib/wallet.js";
|
|
3
|
+
import { apiFetch } from "../lib/api-fetch.js";
|
|
3
4
|
import { okPaid, err } from "../lib/format.js";
|
|
4
5
|
import { logger } from "../lib/logger.js";
|
|
5
6
|
import { TOOL_PRICES } from "../lib/config.js";
|
|
@@ -21,7 +22,7 @@ export function registerScreenshotTool(server) {
|
|
|
21
22
|
try {
|
|
22
23
|
const config = loadOrCreateWallet();
|
|
23
24
|
const params = new URLSearchParams({ url, format });
|
|
24
|
-
const res = await
|
|
25
|
+
const res = await apiFetch(config, `/screenshot?${params.toString()}`);
|
|
25
26
|
if (!res.ok) {
|
|
26
27
|
const body = await res.text();
|
|
27
28
|
return err(`Screenshot API error ${res.status}: ${body}`);
|
package/dist/tools/search.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { loadOrCreateWallet } from "../lib/wallet.js";
|
|
3
|
+
import { apiFetch } from "../lib/api-fetch.js";
|
|
3
4
|
import { okPaid, err } from "../lib/format.js";
|
|
4
5
|
import { logger } from "../lib/logger.js";
|
|
5
6
|
import { TOOL_PRICES } from "../lib/config.js";
|
|
@@ -10,8 +11,7 @@ export function registerSearchTool(server) {
|
|
|
10
11
|
title: "Web Search",
|
|
11
12
|
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
13
|
query: z.string().describe("Search query"),
|
|
13
|
-
count: z
|
|
14
|
-
.number()
|
|
14
|
+
count: z.coerce.number()
|
|
15
15
|
.int()
|
|
16
16
|
.min(1)
|
|
17
17
|
.max(20)
|
|
@@ -23,7 +23,7 @@ export function registerSearchTool(server) {
|
|
|
23
23
|
return withCache("search", { query, count }, () => withBudget("search", async () => {
|
|
24
24
|
try {
|
|
25
25
|
const config = loadOrCreateWallet();
|
|
26
|
-
const res = await
|
|
26
|
+
const res = await apiFetch(config, `/search?q=${encodeURIComponent(query)}&count=${count}`);
|
|
27
27
|
if (!res.ok) {
|
|
28
28
|
const body = await res.text();
|
|
29
29
|
return err(`Search API error ${res.status}: ${body}`);
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { loadOrCreateWallet } from "../lib/wallet.js";
|
|
3
|
+
import { apiFetch } from "../lib/api-fetch.js";
|
|
3
4
|
import { ok, err } from "../lib/format.js";
|
|
4
5
|
import { logger } from "../lib/logger.js";
|
|
5
6
|
export function registerStellarAccountTool(server) {
|
|
@@ -15,7 +16,7 @@ export function registerStellarAccountTool(server) {
|
|
|
15
16
|
logger.debug({ address }, "stellar-account invoked");
|
|
16
17
|
try {
|
|
17
18
|
const config = loadOrCreateWallet();
|
|
18
|
-
const res = await
|
|
19
|
+
const res = await apiFetch(config, `/stellar-account?address=${encodeURIComponent(address)}`);
|
|
19
20
|
if (!res.ok) {
|
|
20
21
|
const body = await res.text();
|
|
21
22
|
return err(`Account lookup error ${res.status}: ${body}`);
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { loadOrCreateWallet } from "../lib/wallet.js";
|
|
3
|
+
import { apiFetch } from "../lib/api-fetch.js";
|
|
3
4
|
import { ok, err } from "../lib/format.js";
|
|
4
5
|
import { logger } from "../lib/logger.js";
|
|
5
6
|
export function registerStellarAssetTool(server) {
|
|
@@ -16,7 +17,7 @@ export function registerStellarAssetTool(server) {
|
|
|
16
17
|
logger.debug({ asset }, "stellar-asset invoked");
|
|
17
18
|
try {
|
|
18
19
|
const config = loadOrCreateWallet();
|
|
19
|
-
const res = await
|
|
20
|
+
const res = await apiFetch(config, `/stellar-asset?asset=${encodeURIComponent(asset)}`);
|
|
20
21
|
if (!res.ok) {
|
|
21
22
|
const body = await res.text();
|
|
22
23
|
return err(`Asset lookup error ${res.status}: ${body}`);
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { loadOrCreateWallet } from "../lib/wallet.js";
|
|
3
|
+
import { apiFetch } from "../lib/api-fetch.js";
|
|
3
4
|
import { ok, err } from "../lib/format.js";
|
|
4
5
|
import { logger } from "../lib/logger.js";
|
|
5
6
|
export function registerStellarPoolsTool(server) {
|
|
@@ -11,8 +12,7 @@ export function registerStellarPoolsTool(server) {
|
|
|
11
12
|
.string()
|
|
12
13
|
.optional()
|
|
13
14
|
.describe('Filter by asset (e.g. "XLM", "USDC"). Omit for all pools.'),
|
|
14
|
-
limit: z
|
|
15
|
-
.number()
|
|
15
|
+
limit: z.coerce.number()
|
|
16
16
|
.int()
|
|
17
17
|
.min(1)
|
|
18
18
|
.max(200)
|
|
@@ -26,7 +26,7 @@ export function registerStellarPoolsTool(server) {
|
|
|
26
26
|
const params = new URLSearchParams({ limit: String(limit) });
|
|
27
27
|
if (asset)
|
|
28
28
|
params.set("asset", asset);
|
|
29
|
-
const res = await
|
|
29
|
+
const res = await apiFetch(config, `/stellar-pools?${params}`);
|
|
30
30
|
if (!res.ok) {
|
|
31
31
|
const body = await res.text();
|
|
32
32
|
return err(`Pools error ${res.status}: ${body}`);
|
package/dist/tools/stocks.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { loadOrCreateWallet } from "../lib/wallet.js";
|
|
3
|
+
import { apiFetch } from "../lib/api-fetch.js";
|
|
3
4
|
import { okPaid, err } from "../lib/format.js";
|
|
4
5
|
import { logger } from "../lib/logger.js";
|
|
5
6
|
import { TOOL_PRICES } from "../lib/config.js";
|
|
@@ -18,7 +19,7 @@ export function registerStocksTool(server) {
|
|
|
18
19
|
return withCache("stocks", { symbol }, () => withBudget("stocks", async () => {
|
|
19
20
|
try {
|
|
20
21
|
const config = loadOrCreateWallet();
|
|
21
|
-
const res = await
|
|
22
|
+
const res = await apiFetch(config, `/stocks?symbol=${encodeURIComponent(symbol)}`);
|
|
22
23
|
if (!res.ok) {
|
|
23
24
|
const body = await res.text();
|
|
24
25
|
return err(`Stocks API error ${res.status}: ${body}`);
|
package/dist/tools/swap-quote.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { loadOrCreateWallet } from "../lib/wallet.js";
|
|
3
|
+
import { apiFetch } from "../lib/api-fetch.js";
|
|
3
4
|
import { ok, err } from "../lib/format.js";
|
|
4
5
|
import { logger } from "../lib/logger.js";
|
|
5
6
|
export function registerSwapQuoteTool(server) {
|
|
@@ -22,7 +23,7 @@ export function registerSwapQuoteTool(server) {
|
|
|
22
23
|
try {
|
|
23
24
|
const config = loadOrCreateWallet();
|
|
24
25
|
const params = new URLSearchParams({ from, to, amount, mode });
|
|
25
|
-
const res = await
|
|
26
|
+
const res = await apiFetch(config, `/swap-quote?${params}`);
|
|
26
27
|
if (!res.ok) {
|
|
27
28
|
const body = await res.text();
|
|
28
29
|
return err(`Swap quote error ${res.status}: ${body}`);
|
package/dist/tools/weather.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { loadOrCreateWallet } from "../lib/wallet.js";
|
|
3
|
+
import { apiFetch } from "../lib/api-fetch.js";
|
|
3
4
|
import { ok, err } from "../lib/format.js";
|
|
4
5
|
export function registerWeatherTool(server) {
|
|
5
6
|
server.registerTool("weather", {
|
|
@@ -10,7 +11,7 @@ export function registerWeatherTool(server) {
|
|
|
10
11
|
}, async ({ location }) => {
|
|
11
12
|
try {
|
|
12
13
|
const config = loadOrCreateWallet();
|
|
13
|
-
const res = await
|
|
14
|
+
const res = await apiFetch(config, `/weather?location=${encodeURIComponent(location)}`);
|
|
14
15
|
if (!res.ok)
|
|
15
16
|
return err(`Weather API error: ${res.status}`);
|
|
16
17
|
return ok(await res.json());
|
package/dist/tools/youtube.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { loadOrCreateWallet } from "../lib/wallet.js";
|
|
3
|
+
import { apiFetch } from "../lib/api-fetch.js";
|
|
3
4
|
import { okPaid, err } from "../lib/format.js";
|
|
4
5
|
import { logger } from "../lib/logger.js";
|
|
5
6
|
import { TOOL_PRICES } from "../lib/config.js";
|
|
@@ -26,7 +27,7 @@ export function registerYoutubeTool(server) {
|
|
|
26
27
|
qs.set("q", query);
|
|
27
28
|
if (id)
|
|
28
29
|
qs.set("id", id);
|
|
29
|
-
const res = await
|
|
30
|
+
const res = await apiFetch(config, `/youtube?${qs.toString()}`);
|
|
30
31
|
if (!res.ok) {
|
|
31
32
|
const body = await res.text();
|
|
32
33
|
return err(`YouTube API error ${res.status}: ${body}`);
|
package/package.json
CHANGED
package/src/cli.ts
CHANGED
|
@@ -4,6 +4,7 @@ import { Mppx } from "mppx/client";
|
|
|
4
4
|
import { stellar } from "@stellar/mpp/charge/client";
|
|
5
5
|
import { Horizon } from "@stellar/stellar-sdk";
|
|
6
6
|
import { loadOrCreateWallet, getKeypair } from "./lib/wallet.js";
|
|
7
|
+
import { apiFetch } from "./lib/api-fetch.js";
|
|
7
8
|
import { TOOL_PRICES } from "./lib/config.js";
|
|
8
9
|
import { logger } from "./lib/logger.js";
|
|
9
10
|
|
|
@@ -211,7 +212,11 @@ async function main() {
|
|
|
211
212
|
process.stderr.write(` Tool: ${tool} · Cost: $${TOOL_PRICES[tool]} USDC\n`);
|
|
212
213
|
}
|
|
213
214
|
|
|
214
|
-
|
|
215
|
+
// Route through apiFetch so the X-XLMTools-Client header (and any
|
|
216
|
+
// future cross-cutting concerns like retries / user-agent) are
|
|
217
|
+
// applied consistently with the MCP tools. apiFetch detects the
|
|
218
|
+
// full-URL form and passes it straight through.
|
|
219
|
+
const res = await apiFetch(config, url, init);
|
|
215
220
|
|
|
216
221
|
if (!res.ok) {
|
|
217
222
|
const text = await res.text();
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared fetch helper for tool calls against the XLMTools API.
|
|
3
|
+
*
|
|
4
|
+
* Every API request gets stamped with `X-XLMTools-Client: <G-address>`
|
|
5
|
+
* so the server can attribute calls back to the user's wallet on the
|
|
6
|
+
* /stats/by-client endpoint. The header is self-declared (no signature)
|
|
7
|
+
* which is fine because:
|
|
8
|
+
* - Paid calls are additionally verified by the Stellar tx receipt
|
|
9
|
+
* - Free calls have no economic value to spoof
|
|
10
|
+
*
|
|
11
|
+
* Usage:
|
|
12
|
+
* const config = loadOrCreateWallet();
|
|
13
|
+
* const res = await apiFetch(config, `/crypto?ids=${ids}`);
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
export interface ClientConfig {
|
|
17
|
+
apiUrl: string;
|
|
18
|
+
stellarPublicKey: string;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export async function apiFetch(
|
|
22
|
+
config: ClientConfig,
|
|
23
|
+
path: string,
|
|
24
|
+
init?: RequestInit,
|
|
25
|
+
): Promise<Response> {
|
|
26
|
+
const url = path.startsWith("http") ? path : `${config.apiUrl}${path}`;
|
|
27
|
+
const headers = new Headers(init?.headers);
|
|
28
|
+
headers.set("X-XLMTools-Client", config.stellarPublicKey);
|
|
29
|
+
return fetch(url, { ...init, headers });
|
|
30
|
+
}
|
package/src/lib/wallet.ts
CHANGED
|
@@ -94,7 +94,7 @@ export function loadOrCreateWallet(): XLMToolsConfig {
|
|
|
94
94
|
const config: XLMToolsConfig = {
|
|
95
95
|
stellarPrivateKey: keypair.secret(),
|
|
96
96
|
stellarPublicKey: keypair.publicKey(),
|
|
97
|
-
apiUrl: process.env.
|
|
97
|
+
apiUrl: process.env.XLMTOOLS_API_URL ?? "https://api.xlmtools.com",
|
|
98
98
|
};
|
|
99
99
|
|
|
100
100
|
mkdirSync(CONFIG_DIR, { recursive: true });
|
package/src/tools/crypto.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
2
|
import { z } from "zod";
|
|
3
3
|
import { loadOrCreateWallet } from "../lib/wallet.js";
|
|
4
|
+
import { apiFetch } from "../lib/api-fetch.js";
|
|
4
5
|
import { ok, err } from "../lib/format.js";
|
|
5
6
|
|
|
6
7
|
export function registerCryptoTool(server: McpServer): void {
|
|
@@ -16,7 +17,7 @@ export function registerCryptoTool(server: McpServer): void {
|
|
|
16
17
|
async ({ ids, vs_currency }) => {
|
|
17
18
|
try {
|
|
18
19
|
const config = loadOrCreateWallet();
|
|
19
|
-
const res = await
|
|
20
|
+
const res = await apiFetch(config, `/crypto?ids=${encodeURIComponent(ids)}&vs_currency=${vs_currency}`);
|
|
20
21
|
if (!res.ok) return err(`Crypto API error: ${res.status}`);
|
|
21
22
|
return ok(await res.json());
|
|
22
23
|
} catch (e) { return err(String(e)); }
|
package/src/tools/dex-candles.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
2
|
import { z } from "zod";
|
|
3
3
|
import { loadOrCreateWallet } from "../lib/wallet.js";
|
|
4
|
+
import { apiFetch } from "../lib/api-fetch.js";
|
|
4
5
|
import { ok, err } from "../lib/format.js";
|
|
5
6
|
import { logger } from "../lib/logger.js";
|
|
6
7
|
|
|
@@ -37,7 +38,7 @@ export function registerDexCandlesTool(server: McpServer): void {
|
|
|
37
38
|
resolution,
|
|
38
39
|
limit: String(limit),
|
|
39
40
|
});
|
|
40
|
-
const res = await
|
|
41
|
+
const res = await apiFetch(config, `/dex-candles?${params}`);
|
|
41
42
|
if (!res.ok) {
|
|
42
43
|
const body = await res.text();
|
|
43
44
|
return err(`Candles error ${res.status}: ${body}`);
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
2
|
import { z } from "zod";
|
|
3
3
|
import { loadOrCreateWallet } from "../lib/wallet.js";
|
|
4
|
+
import { apiFetch } from "../lib/api-fetch.js";
|
|
4
5
|
import { ok, err } from "../lib/format.js";
|
|
5
6
|
import { logger } from "../lib/logger.js";
|
|
6
7
|
|
|
@@ -28,8 +29,9 @@ export function registerDexOrderbookTool(server: McpServer): void {
|
|
|
28
29
|
logger.debug({ pair, limit }, "dex-orderbook invoked");
|
|
29
30
|
try {
|
|
30
31
|
const config = loadOrCreateWallet();
|
|
31
|
-
const res = await
|
|
32
|
-
|
|
32
|
+
const res = await apiFetch(
|
|
33
|
+
config,
|
|
34
|
+
`/dex-orderbook?pair=${encodeURIComponent(pair)}&limit=${limit}`,
|
|
33
35
|
);
|
|
34
36
|
if (!res.ok) {
|
|
35
37
|
const body = await res.text();
|
package/src/tools/dex-trades.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
2
|
import { z } from "zod";
|
|
3
3
|
import { loadOrCreateWallet } from "../lib/wallet.js";
|
|
4
|
+
import { apiFetch } from "../lib/api-fetch.js";
|
|
4
5
|
import { ok, err } from "../lib/format.js";
|
|
5
6
|
import { logger } from "../lib/logger.js";
|
|
6
7
|
|
|
@@ -36,7 +37,7 @@ export function registerDexTradesTool(server: McpServer): void {
|
|
|
36
37
|
limit: String(limit),
|
|
37
38
|
trade_type,
|
|
38
39
|
});
|
|
39
|
-
const res = await
|
|
40
|
+
const res = await apiFetch(config, `/dex-trades?${params}`);
|
|
40
41
|
if (!res.ok) {
|
|
41
42
|
const body = await res.text();
|
|
42
43
|
return err(`Trades error ${res.status}: ${body}`);
|
package/src/tools/domain.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
2
|
import { z } from "zod";
|
|
3
3
|
import { loadOrCreateWallet } from "../lib/wallet.js";
|
|
4
|
+
import { apiFetch } from "../lib/api-fetch.js";
|
|
4
5
|
import { ok, err } from "../lib/format.js";
|
|
5
6
|
|
|
6
7
|
export function registerDomainTool(server: McpServer): void {
|
|
@@ -15,7 +16,7 @@ export function registerDomainTool(server: McpServer): void {
|
|
|
15
16
|
async ({ name }) => {
|
|
16
17
|
try {
|
|
17
18
|
const config = loadOrCreateWallet();
|
|
18
|
-
const res = await
|
|
19
|
+
const res = await apiFetch(config, `/domain?name=${encodeURIComponent(name)}`);
|
|
19
20
|
if (!res.ok) return err(`Domain API error: ${res.status}`);
|
|
20
21
|
return ok(await res.json());
|
|
21
22
|
} catch (e) { return err(String(e)); }
|
package/src/tools/image.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
2
|
import { z } from "zod";
|
|
3
3
|
import { loadOrCreateWallet } from "../lib/wallet.js";
|
|
4
|
+
import { apiFetch } from "../lib/api-fetch.js";
|
|
4
5
|
import { okPaid, err } from "../lib/format.js";
|
|
5
6
|
import { logger } from "../lib/logger.js";
|
|
6
7
|
import { TOOL_PRICES } from "../lib/config.js";
|
|
@@ -28,7 +29,7 @@ export function registerImageTool(server: McpServer): void {
|
|
|
28
29
|
withBudget("image", async () => {
|
|
29
30
|
try {
|
|
30
31
|
const config = loadOrCreateWallet();
|
|
31
|
-
const res = await
|
|
32
|
+
const res = await apiFetch(config, `/image`, {
|
|
32
33
|
method: "POST",
|
|
33
34
|
headers: { "Content-Type": "application/json" },
|
|
34
35
|
body: JSON.stringify({ prompt, size }),
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
2
|
import { z } from "zod";
|
|
3
3
|
import { loadOrCreateWallet } from "../lib/wallet.js";
|
|
4
|
+
import { apiFetch } from "../lib/api-fetch.js";
|
|
4
5
|
import { ok, err } from "../lib/format.js";
|
|
5
6
|
import { logger } from "../lib/logger.js";
|
|
6
7
|
|
|
@@ -27,7 +28,7 @@ export function registerOraclePriceTool(server: McpServer): void {
|
|
|
27
28
|
try {
|
|
28
29
|
const config = loadOrCreateWallet();
|
|
29
30
|
const params = new URLSearchParams({ asset, feed });
|
|
30
|
-
const res = await
|
|
31
|
+
const res = await apiFetch(config, `/oracle-price?${params}`);
|
|
31
32
|
if (!res.ok) {
|
|
32
33
|
const body = await res.text();
|
|
33
34
|
return err(`Oracle error ${res.status}: ${body}`);
|
package/src/tools/research.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
2
|
import { z } from "zod";
|
|
3
3
|
import { loadOrCreateWallet } from "../lib/wallet.js";
|
|
4
|
+
import { apiFetch } from "../lib/api-fetch.js";
|
|
4
5
|
import { okPaid, err } from "../lib/format.js";
|
|
5
6
|
import { logger } from "../lib/logger.js";
|
|
6
7
|
import { TOOL_PRICES } from "../lib/config.js";
|
|
@@ -28,8 +29,9 @@ export function registerResearchTool(server: McpServer): void {
|
|
|
28
29
|
withBudget("research", async () => {
|
|
29
30
|
try {
|
|
30
31
|
const config = loadOrCreateWallet();
|
|
31
|
-
const res = await
|
|
32
|
-
|
|
32
|
+
const res = await apiFetch(
|
|
33
|
+
config,
|
|
34
|
+
`/research?q=${encodeURIComponent(query)}&num_results=${num_results}`,
|
|
33
35
|
);
|
|
34
36
|
if (!res.ok) {
|
|
35
37
|
const body = await res.text();
|
package/src/tools/scrape.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
2
|
import { z } from "zod";
|
|
3
3
|
import { loadOrCreateWallet } from "../lib/wallet.js";
|
|
4
|
+
import { apiFetch } from "../lib/api-fetch.js";
|
|
4
5
|
import { okPaid, err } from "../lib/format.js";
|
|
5
6
|
import { logger } from "../lib/logger.js";
|
|
6
7
|
import { TOOL_PRICES } from "../lib/config.js";
|
|
@@ -22,8 +23,9 @@ export function registerScrapeTool(server: McpServer): void {
|
|
|
22
23
|
withBudget("scrape", async () => {
|
|
23
24
|
try {
|
|
24
25
|
const config = loadOrCreateWallet();
|
|
25
|
-
const res = await
|
|
26
|
-
|
|
26
|
+
const res = await apiFetch(
|
|
27
|
+
config,
|
|
28
|
+
`/scrape?url=${encodeURIComponent(url)}`,
|
|
27
29
|
);
|
|
28
30
|
if (!res.ok) {
|
|
29
31
|
const body = await res.text();
|
package/src/tools/screenshot.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
2
|
import { z } from "zod";
|
|
3
3
|
import { loadOrCreateWallet } from "../lib/wallet.js";
|
|
4
|
+
import { apiFetch } from "../lib/api-fetch.js";
|
|
4
5
|
import { okPaid, err } from "../lib/format.js";
|
|
5
6
|
import { logger } from "../lib/logger.js";
|
|
6
7
|
import { TOOL_PRICES } from "../lib/config.js";
|
|
@@ -27,8 +28,9 @@ export function registerScreenshotTool(server: McpServer): void {
|
|
|
27
28
|
try {
|
|
28
29
|
const config = loadOrCreateWallet();
|
|
29
30
|
const params = new URLSearchParams({ url, format });
|
|
30
|
-
const res = await
|
|
31
|
-
|
|
31
|
+
const res = await apiFetch(
|
|
32
|
+
config,
|
|
33
|
+
`/screenshot?${params.toString()}`,
|
|
32
34
|
);
|
|
33
35
|
if (!res.ok) {
|
|
34
36
|
const body = await res.text();
|
package/src/tools/search.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
2
|
import { z } from "zod";
|
|
3
3
|
import { loadOrCreateWallet } from "../lib/wallet.js";
|
|
4
|
+
import { apiFetch } from "../lib/api-fetch.js";
|
|
4
5
|
import { okPaid, err } from "../lib/format.js";
|
|
5
6
|
import { logger } from "../lib/logger.js";
|
|
6
7
|
import { TOOL_PRICES } from "../lib/config.js";
|
|
@@ -28,8 +29,9 @@ export function registerSearchTool(server: McpServer): void {
|
|
|
28
29
|
withBudget("search", async () => {
|
|
29
30
|
try {
|
|
30
31
|
const config = loadOrCreateWallet();
|
|
31
|
-
const res = await
|
|
32
|
-
|
|
32
|
+
const res = await apiFetch(
|
|
33
|
+
config,
|
|
34
|
+
`/search?q=${encodeURIComponent(query)}&count=${count}`,
|
|
33
35
|
);
|
|
34
36
|
if (!res.ok) {
|
|
35
37
|
const body = await res.text();
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
2
|
import { z } from "zod";
|
|
3
3
|
import { loadOrCreateWallet } from "../lib/wallet.js";
|
|
4
|
+
import { apiFetch } from "../lib/api-fetch.js";
|
|
4
5
|
import { ok, err } from "../lib/format.js";
|
|
5
6
|
import { logger } from "../lib/logger.js";
|
|
6
7
|
|
|
@@ -21,8 +22,9 @@ export function registerStellarAccountTool(server: McpServer): void {
|
|
|
21
22
|
logger.debug({ address }, "stellar-account invoked");
|
|
22
23
|
try {
|
|
23
24
|
const config = loadOrCreateWallet();
|
|
24
|
-
const res = await
|
|
25
|
-
|
|
25
|
+
const res = await apiFetch(
|
|
26
|
+
config,
|
|
27
|
+
`/stellar-account?address=${encodeURIComponent(address)}`,
|
|
26
28
|
);
|
|
27
29
|
if (!res.ok) {
|
|
28
30
|
const body = await res.text();
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
2
|
import { z } from "zod";
|
|
3
3
|
import { loadOrCreateWallet } from "../lib/wallet.js";
|
|
4
|
+
import { apiFetch } from "../lib/api-fetch.js";
|
|
4
5
|
import { ok, err } from "../lib/format.js";
|
|
5
6
|
import { logger } from "../lib/logger.js";
|
|
6
7
|
|
|
@@ -22,8 +23,9 @@ export function registerStellarAssetTool(server: McpServer): void {
|
|
|
22
23
|
logger.debug({ asset }, "stellar-asset invoked");
|
|
23
24
|
try {
|
|
24
25
|
const config = loadOrCreateWallet();
|
|
25
|
-
const res = await
|
|
26
|
-
|
|
26
|
+
const res = await apiFetch(
|
|
27
|
+
config,
|
|
28
|
+
`/stellar-asset?asset=${encodeURIComponent(asset)}`,
|
|
27
29
|
);
|
|
28
30
|
if (!res.ok) {
|
|
29
31
|
const body = await res.text();
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
2
|
import { z } from "zod";
|
|
3
3
|
import { loadOrCreateWallet } from "../lib/wallet.js";
|
|
4
|
+
import { apiFetch } from "../lib/api-fetch.js";
|
|
4
5
|
import { ok, err } from "../lib/format.js";
|
|
5
6
|
import { logger } from "../lib/logger.js";
|
|
6
7
|
|
|
@@ -30,7 +31,7 @@ export function registerStellarPoolsTool(server: McpServer): void {
|
|
|
30
31
|
const config = loadOrCreateWallet();
|
|
31
32
|
const params = new URLSearchParams({ limit: String(limit) });
|
|
32
33
|
if (asset) params.set("asset", asset);
|
|
33
|
-
const res = await
|
|
34
|
+
const res = await apiFetch(config, `/stellar-pools?${params}`);
|
|
34
35
|
if (!res.ok) {
|
|
35
36
|
const body = await res.text();
|
|
36
37
|
return err(`Pools error ${res.status}: ${body}`);
|
package/src/tools/stocks.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
2
|
import { z } from "zod";
|
|
3
3
|
import { loadOrCreateWallet } from "../lib/wallet.js";
|
|
4
|
+
import { apiFetch } from "../lib/api-fetch.js";
|
|
4
5
|
import { okPaid, err } from "../lib/format.js";
|
|
5
6
|
import { logger } from "../lib/logger.js";
|
|
6
7
|
import { TOOL_PRICES } from "../lib/config.js";
|
|
@@ -24,8 +25,9 @@ export function registerStocksTool(server: McpServer): void {
|
|
|
24
25
|
withBudget("stocks", async () => {
|
|
25
26
|
try {
|
|
26
27
|
const config = loadOrCreateWallet();
|
|
27
|
-
const res = await
|
|
28
|
-
|
|
28
|
+
const res = await apiFetch(
|
|
29
|
+
config,
|
|
30
|
+
`/stocks?symbol=${encodeURIComponent(symbol)}`,
|
|
29
31
|
);
|
|
30
32
|
if (!res.ok) {
|
|
31
33
|
const body = await res.text();
|
package/src/tools/swap-quote.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
2
|
import { z } from "zod";
|
|
3
3
|
import { loadOrCreateWallet } from "../lib/wallet.js";
|
|
4
|
+
import { apiFetch } from "../lib/api-fetch.js";
|
|
4
5
|
import { ok, err } from "../lib/format.js";
|
|
5
6
|
import { logger } from "../lib/logger.js";
|
|
6
7
|
|
|
@@ -28,7 +29,7 @@ export function registerSwapQuoteTool(server: McpServer): void {
|
|
|
28
29
|
try {
|
|
29
30
|
const config = loadOrCreateWallet();
|
|
30
31
|
const params = new URLSearchParams({ from, to, amount, mode });
|
|
31
|
-
const res = await
|
|
32
|
+
const res = await apiFetch(config, `/swap-quote?${params}`);
|
|
32
33
|
if (!res.ok) {
|
|
33
34
|
const body = await res.text();
|
|
34
35
|
return err(`Swap quote error ${res.status}: ${body}`);
|
package/src/tools/weather.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
2
|
import { z } from "zod";
|
|
3
3
|
import { loadOrCreateWallet } from "../lib/wallet.js";
|
|
4
|
+
import { apiFetch } from "../lib/api-fetch.js";
|
|
4
5
|
import { ok, err } from "../lib/format.js";
|
|
5
6
|
|
|
6
7
|
export function registerWeatherTool(server: McpServer): void {
|
|
@@ -15,7 +16,7 @@ export function registerWeatherTool(server: McpServer): void {
|
|
|
15
16
|
async ({ location }) => {
|
|
16
17
|
try {
|
|
17
18
|
const config = loadOrCreateWallet();
|
|
18
|
-
const res = await
|
|
19
|
+
const res = await apiFetch(config, `/weather?location=${encodeURIComponent(location)}`);
|
|
19
20
|
if (!res.ok) return err(`Weather API error: ${res.status}`);
|
|
20
21
|
return ok(await res.json());
|
|
21
22
|
} catch (e) { return err(String(e)); }
|
package/src/tools/youtube.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
2
|
import { z } from "zod";
|
|
3
3
|
import { loadOrCreateWallet } from "../lib/wallet.js";
|
|
4
|
+
import { apiFetch } from "../lib/api-fetch.js";
|
|
4
5
|
import { okPaid, err } from "../lib/format.js";
|
|
5
6
|
import { logger } from "../lib/logger.js";
|
|
6
7
|
import { TOOL_PRICES } from "../lib/config.js";
|
|
@@ -30,8 +31,9 @@ export function registerYoutubeTool(server: McpServer): void {
|
|
|
30
31
|
const qs = new URLSearchParams();
|
|
31
32
|
if (query) qs.set("q", query);
|
|
32
33
|
if (id) qs.set("id", id);
|
|
33
|
-
const res = await
|
|
34
|
-
|
|
34
|
+
const res = await apiFetch(
|
|
35
|
+
config,
|
|
36
|
+
`/youtube?${qs.toString()}`,
|
|
35
37
|
);
|
|
36
38
|
if (!res.ok) {
|
|
37
39
|
const body = await res.text();
|