@caravo/mcp 0.1.2 → 0.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -4
- package/dist/index.js +105 -6
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -8,7 +8,7 @@ Local stdio MCP server for [Caravo](https://caravo.ai) with built-in x402 wallet
|
|
|
8
8
|
# Claude Code
|
|
9
9
|
claude mcp add caravo -- npx -y @caravo/mcp@latest
|
|
10
10
|
|
|
11
|
-
# Optional: with API key for balance auth
|
|
11
|
+
# Optional: with API key for balance auth (favorites work either way)
|
|
12
12
|
claude mcp add caravo -e CARAVO_API_KEY=am_xxx -- npx -y @caravo/mcp@latest
|
|
13
13
|
```
|
|
14
14
|
|
|
@@ -30,9 +30,9 @@ claude mcp add caravo -e CARAVO_API_KEY=am_xxx -- npx -y @caravo/mcp@latest
|
|
|
30
30
|
| `list_tags` | List all categories |
|
|
31
31
|
| `list_providers` | List all providers |
|
|
32
32
|
| `get_wallet_info` | Get wallet address and USDC balance |
|
|
33
|
-
| `favorite_tool` | Bookmark a tool (
|
|
34
|
-
| `unfavorite_tool` | Remove bookmark (
|
|
35
|
-
| `list_favorites` | List bookmarked tools (
|
|
33
|
+
| `favorite_tool` | Bookmark a tool (server with API key, local without) |
|
|
34
|
+
| `unfavorite_tool` | Remove bookmark (server with API key, local without) |
|
|
35
|
+
| `list_favorites` | List bookmarked tools (server with API key, local without) |
|
|
36
36
|
| `list_tool_requests` | Browse tool requests |
|
|
37
37
|
| `request_tool` | Request a new tool |
|
|
38
38
|
| `upvote_tool_request` | Upvote a tool request |
|
package/dist/index.js
CHANGED
|
@@ -17,12 +17,33 @@
|
|
|
17
17
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
18
18
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
19
19
|
import { z } from "zod";
|
|
20
|
+
import { readFileSync, writeFileSync, mkdirSync, existsSync } from "fs";
|
|
21
|
+
import { homedir } from "os";
|
|
22
|
+
import { join } from "path";
|
|
20
23
|
import { loadOrCreateWallet } from "./wallet.js";
|
|
21
24
|
import { fetchWithX402 } from "./x402.js";
|
|
22
25
|
const API_BASE = process.env.CARAVO_URL ?? "https://caravo.ai";
|
|
23
|
-
//
|
|
24
|
-
const
|
|
25
|
-
const
|
|
26
|
+
// Config file: ~/.caravo/config.json — stores API key set via `login` tool
|
|
27
|
+
const CONFIG_DIR = join(homedir(), ".caravo");
|
|
28
|
+
const CONFIG_FILE = join(CONFIG_DIR, "config.json");
|
|
29
|
+
function loadConfig() {
|
|
30
|
+
try {
|
|
31
|
+
if (!existsSync(CONFIG_FILE))
|
|
32
|
+
return {};
|
|
33
|
+
return JSON.parse(readFileSync(CONFIG_FILE, "utf-8"));
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
return {};
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
function saveConfig(data) {
|
|
40
|
+
mkdirSync(CONFIG_DIR, { recursive: true });
|
|
41
|
+
writeFileSync(CONFIG_FILE, JSON.stringify(data, null, 2), { mode: 0o600 });
|
|
42
|
+
}
|
|
43
|
+
// Optional API key: env takes priority, then config file; must have am_ prefix
|
|
44
|
+
const RAW_KEY = process.env.CARAVO_API_KEY || loadConfig().api_key;
|
|
45
|
+
// Mutable so the `login` tool can update it mid-session
|
|
46
|
+
let API_KEY = RAW_KEY && RAW_KEY.startsWith("am_") ? RAW_KEY : undefined;
|
|
26
47
|
const wallet = loadOrCreateWallet();
|
|
27
48
|
process.stderr.write(`[caravo] wallet: ${wallet.address}\n`);
|
|
28
49
|
process.stderr.write(API_KEY
|
|
@@ -282,6 +303,80 @@ function registerAllTools(server) {
|
|
|
282
303
|
],
|
|
283
304
|
};
|
|
284
305
|
});
|
|
306
|
+
// ── Login (browser-based account connect) ────────────────────────────────────
|
|
307
|
+
server.registerTool("login", {
|
|
308
|
+
description: "Connect your Caravo account to enable balance payments and favorites sync. " +
|
|
309
|
+
"Opens caravo.ai in your browser — sign in once and the API key is saved automatically. " +
|
|
310
|
+
"Run this if you started with x402 payments and now want to use your account balance.",
|
|
311
|
+
inputSchema: {},
|
|
312
|
+
}, async () => {
|
|
313
|
+
try {
|
|
314
|
+
// 1. Create one-time session
|
|
315
|
+
const initRes = await fetch(`${API_BASE}/api/auth/mcp-session`, {
|
|
316
|
+
method: "POST",
|
|
317
|
+
headers: { "Content-Type": "application/json" },
|
|
318
|
+
});
|
|
319
|
+
const { token, url } = (await initRes.json());
|
|
320
|
+
// 2. Open browser
|
|
321
|
+
const { exec } = await import("child_process");
|
|
322
|
+
const opener = process.platform === "darwin"
|
|
323
|
+
? `open "${url}"`
|
|
324
|
+
: process.platform === "win32"
|
|
325
|
+
? `start "" "${url}"`
|
|
326
|
+
: `xdg-open "${url}"`;
|
|
327
|
+
exec(opener);
|
|
328
|
+
process.stderr.write(`[caravo] login: opened ${url}\n`);
|
|
329
|
+
// 3. Poll every 2s for up to 5 minutes
|
|
330
|
+
const deadline = Date.now() + 5 * 60 * 1000;
|
|
331
|
+
while (Date.now() < deadline) {
|
|
332
|
+
await new Promise((r) => setTimeout(r, 2000));
|
|
333
|
+
const pollRes = await fetch(`${API_BASE}/api/auth/mcp-session?token=${encodeURIComponent(token)}`);
|
|
334
|
+
const poll = (await pollRes.json());
|
|
335
|
+
if (poll.status === "completed" && poll.api_key) {
|
|
336
|
+
// 4. Save to config + activate for this session
|
|
337
|
+
API_KEY = poll.api_key;
|
|
338
|
+
saveConfig({ api_key: poll.api_key });
|
|
339
|
+
process.stderr.write(`[caravo] login: API key saved to ${CONFIG_FILE}\n`);
|
|
340
|
+
return {
|
|
341
|
+
content: [
|
|
342
|
+
{
|
|
343
|
+
type: "text",
|
|
344
|
+
text: [
|
|
345
|
+
`✓ Logged in to Caravo!`,
|
|
346
|
+
``,
|
|
347
|
+
`API key saved to ${CONFIG_FILE}`,
|
|
348
|
+
`Balance payments are now active for this session.`,
|
|
349
|
+
`Restart the MCP server to also load your favorited tools.`,
|
|
350
|
+
].join("\n"),
|
|
351
|
+
},
|
|
352
|
+
],
|
|
353
|
+
};
|
|
354
|
+
}
|
|
355
|
+
if (poll.status === "expired") {
|
|
356
|
+
return {
|
|
357
|
+
content: [{ type: "text", text: "Login expired. Run login again to retry." }],
|
|
358
|
+
isError: true,
|
|
359
|
+
};
|
|
360
|
+
}
|
|
361
|
+
// status === "pending" — keep polling
|
|
362
|
+
}
|
|
363
|
+
return {
|
|
364
|
+
content: [{ type: "text", text: "Login timed out after 5 minutes. Run login again." }],
|
|
365
|
+
isError: true,
|
|
366
|
+
};
|
|
367
|
+
}
|
|
368
|
+
catch (err) {
|
|
369
|
+
return {
|
|
370
|
+
content: [
|
|
371
|
+
{
|
|
372
|
+
type: "text",
|
|
373
|
+
text: `Login failed: ${err instanceof Error ? err.message : String(err)}`,
|
|
374
|
+
},
|
|
375
|
+
],
|
|
376
|
+
isError: true,
|
|
377
|
+
};
|
|
378
|
+
}
|
|
379
|
+
});
|
|
285
380
|
// ── List tags ────────────────────────────────────────────────────────────────
|
|
286
381
|
server.registerTool("list_tags", {
|
|
287
382
|
description: "List all available tags/categories in the marketplace. Returns tag names, slugs, and tool counts.",
|
|
@@ -321,7 +416,9 @@ function registerAllTools(server) {
|
|
|
321
416
|
if (!Number.isInteger(per_page) || per_page < 1) {
|
|
322
417
|
return { content: [{ type: "text", text: "Error: per_page must be a positive integer" }], isError: true };
|
|
323
418
|
}
|
|
324
|
-
per_page
|
|
419
|
+
if (per_page > 100) {
|
|
420
|
+
return { content: [{ type: "text", text: "Error: per_page must be at most 100" }], isError: true };
|
|
421
|
+
}
|
|
325
422
|
const params = new URLSearchParams();
|
|
326
423
|
if (query)
|
|
327
424
|
params.set("query", query);
|
|
@@ -548,7 +645,9 @@ function registerAllTools(server) {
|
|
|
548
645
|
if (!Number.isInteger(per_page) || per_page < 1) {
|
|
549
646
|
return { content: [{ type: "text", text: "Error: per_page must be a positive integer" }], isError: true };
|
|
550
647
|
}
|
|
551
|
-
per_page
|
|
648
|
+
if (per_page > 100) {
|
|
649
|
+
return { content: [{ type: "text", text: "Error: per_page must be at most 100" }], isError: true };
|
|
650
|
+
}
|
|
552
651
|
const params = new URLSearchParams();
|
|
553
652
|
params.set("status", status);
|
|
554
653
|
params.set("page", String(page));
|
|
@@ -755,7 +854,7 @@ function registerAllTools(server) {
|
|
|
755
854
|
// ─── Main ─────────────────────────────────────────────────────────────────────
|
|
756
855
|
const server = new McpServer({
|
|
757
856
|
name: "caravo",
|
|
758
|
-
version: "1.
|
|
857
|
+
version: "0.1.4",
|
|
759
858
|
}, {
|
|
760
859
|
instructions: [
|
|
761
860
|
`You have access to Caravo — a comprehensive platform with a wide variety of ready-to-use tools, APIs, and digital assets at very competitive prices.`,
|