@caravo/mcp 0.1.11 → 0.1.12

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 (2) hide show
  1. package/dist/index.js +208 -208
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -23,6 +23,20 @@ import { join } from "path";
23
23
  import { loadOrCreateWallet } from "./wallet.js";
24
24
  import { fetchWithX402 } from "./x402.js";
25
25
  const API_BASE = process.env.CARAVO_URL ?? "https://caravo.ai";
26
+ // ─── Shared description blocks (used in both `instructions` and `search_tools`) ─
27
+ const CATALOG_DESCRIPTION = "The catalog includes: AI Models (image/video/audio/vision/NLP/code/3D), Search (web/academic/news/product), " +
28
+ "Data & Analytics (scraping/weather/geolocation/market data), Finance (payments/exchange rates/stocks/crypto), " +
29
+ "Social Media, Communication (email/SMS), eCommerce, Cloud & Infrastructure (VPS/domains/storage/CDN), " +
30
+ "Digital Assets, Utility (validation/conversion/security), and community-contributed tools.";
31
+ const EXAMPLE_SERVICES = [
32
+ "- google/nano-banana: Google Gemini-based ultra-fast image generation, great for prompt following and text rendering.",
33
+ "- semanticscholar/paper-search: Search academic papers across all disciplines. Returns paper titles, authors, year, abstract, citation count, and open-access PDF links.",
34
+ "- domainstatus/domain-availability: Check if a domain name is available to register.",
35
+ "- semrush/website-traffic: Get comprehensive website traffic analytics for any domain, including authority score, backlinks, organic traffic, top geo distributions, and domain ranking.",
36
+ "- semrush/keyword-insights: Get detailed keyword research data including search volume, CPC, competition level, and trend data.",
37
+ "- maxcanna/android-app-search: Search Google Play by keyword, returns app metadata including title, developer, rating, installs, price, and category.",
38
+ ].join("\n");
39
+ const WORKFLOW = "search_tools → get_tool_info → use_tool → respond to user → submit_review";
26
40
  // Config file: ~/.caravo/config.json — stores API key set via `login` tool
27
41
  const CONFIG_DIR = join(homedir(), ".caravo");
28
42
  const CONFIG_FILE = join(CONFIG_DIR, "config.json");
@@ -260,202 +274,17 @@ async function loadFavoriteTools(server) {
260
274
  }
261
275
  // ─── Static management + meta tools ───────────────────────────────────────────
262
276
  function registerAllTools(server) {
263
- // ── Wallet info ──────────────────────────────────────────────────────────────
264
- server.registerTool("get_wallet_info", {
265
- description: "Get your local x402 wallet address and USDC balance. Send USDC on Base to this address to fund automatic payments.",
266
- inputSchema: {},
267
- }, async () => {
268
- let balance = "unknown (check manually)";
269
- try {
270
- const USDC = "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913";
271
- const data = "0x70a08231000000000000000000000000" +
272
- wallet.address.slice(2).toLowerCase();
273
- const r = await fetch("https://mainnet.base.org", {
274
- method: "POST",
275
- headers: { "Content-Type": "application/json" },
276
- body: JSON.stringify({
277
- jsonrpc: "2.0",
278
- id: 1,
279
- method: "eth_call",
280
- params: [{ to: USDC, data }, "latest"],
281
- }),
282
- });
283
- const d = (await r.json());
284
- if (d.result && d.result !== "0x") {
285
- balance = (parseInt(d.result, 16) / 1e6).toFixed(6) + " USDC";
286
- }
287
- }
288
- catch {
289
- // ignore
290
- }
291
- return {
292
- content: [
293
- {
294
- type: "text",
295
- text: JSON.stringify({
296
- address: wallet.address,
297
- network: "Base mainnet (eip155:8453)",
298
- usdc_balance: balance,
299
- note: "Send USDC on Base to this address to enable automatic x402 payments.",
300
- }, null, 2),
301
- },
302
- ],
303
- };
304
- });
305
- // ── Login (browser-based account connect) ────────────────────────────────────
306
- server.registerTool("login", {
307
- description: "Connect your Caravo account to enable balance payments and favorites sync. " +
308
- "Opens caravo.ai in your browser — sign in once and the API key is saved automatically. " +
309
- "Run this if you started with x402 payments and now want to use your account balance.",
310
- inputSchema: {},
311
- }, async () => {
312
- try {
313
- // 1. Create one-time session
314
- const initRes = await fetch(`${API_BASE}/api/auth/mcp-session`, {
315
- method: "POST",
316
- headers: { "Content-Type": "application/json" },
317
- });
318
- const { token, url } = (await initRes.json());
319
- // 2. Open browser
320
- const { exec } = await import("child_process");
321
- const opener = process.platform === "darwin"
322
- ? `open "${url}"`
323
- : process.platform === "win32"
324
- ? `start "" "${url}"`
325
- : `xdg-open "${url}"`;
326
- exec(opener);
327
- process.stderr.write(`[caravo] login: opened ${url}\n`);
328
- // 3. Poll every 2s for up to 5 minutes
329
- const deadline = Date.now() + 5 * 60 * 1000;
330
- while (Date.now() < deadline) {
331
- await new Promise((r) => setTimeout(r, 2000));
332
- const pollRes = await fetch(`${API_BASE}/api/auth/mcp-session?token=${encodeURIComponent(token)}`);
333
- const poll = (await pollRes.json());
334
- if (poll.status === "completed" && poll.api_key) {
335
- // 4. Save to config + activate for this session
336
- API_KEY = poll.api_key;
337
- saveConfig({ api_key: poll.api_key });
338
- process.stderr.write(`[caravo] login: API key saved to ${CONFIG_FILE}\n`);
339
- return {
340
- content: [
341
- {
342
- type: "text",
343
- text: [
344
- `✓ Logged in to Caravo!`,
345
- ``,
346
- `API key saved to ${CONFIG_FILE}`,
347
- `Balance payments are now active for this session.`,
348
- `Restart the MCP server to also load your favorited tools.`,
349
- ].join("\n"),
350
- },
351
- ],
352
- };
353
- }
354
- if (poll.status === "expired") {
355
- return {
356
- content: [{ type: "text", text: "Login expired. Run login again to retry." }],
357
- isError: true,
358
- };
359
- }
360
- // status === "pending" — keep polling
361
- }
362
- return {
363
- content: [{ type: "text", text: "Login timed out after 5 minutes. Run login again." }],
364
- isError: true,
365
- };
366
- }
367
- catch (err) {
368
- return {
369
- content: [
370
- {
371
- type: "text",
372
- text: `Login failed: ${err instanceof Error ? err.message : String(err)}`,
373
- },
374
- ],
375
- isError: true,
376
- };
377
- }
378
- });
379
- // ── Logout ──────────────────────────────────────────────────────────────────
380
- server.registerTool("logout", {
381
- description: "Disconnect your Caravo account and switch back to x402 wallet payments. " +
382
- "Removes the saved API key and unregisters all favorited tools from this session.",
383
- inputSchema: {},
384
- }, async () => {
385
- if (!API_KEY) {
386
- return {
387
- content: [{ type: "text", text: "Not logged in — already using x402 wallet payments." }],
388
- };
389
- }
390
- // 1. Clear in-memory key
391
- API_KEY = undefined;
392
- // 2. Remove key from config file
393
- try {
394
- const config = loadConfig();
395
- delete config.api_key;
396
- saveConfig(config);
397
- }
398
- catch {
399
- // config file may not exist — that's fine
400
- }
401
- // 3. Unregister all favorited tools
402
- let removedCount = 0;
403
- for (const [toolId, registered] of registeredFavTools) {
404
- registered.remove();
405
- registeredFavTools.delete(toolId);
406
- removedCount++;
407
- }
408
- process.stderr.write(`[caravo] logout: cleared API key, removed ${removedCount} fav tools\n`);
409
- return {
410
- content: [
411
- {
412
- type: "text",
413
- text: [
414
- `✓ Logged out of Caravo.`,
415
- ``,
416
- `API key removed from ${CONFIG_FILE}`,
417
- ...(removedCount > 0 ? [`Unregistered ${removedCount} favorited tool(s).`] : []),
418
- `Now using x402 wallet payments (${wallet.address}).`,
419
- ].join("\n"),
420
- },
421
- ],
422
- };
423
- });
424
- // ── List tags ────────────────────────────────────────────────────────────────
425
- server.registerTool("list_tags", {
426
- description: "List all available tags/categories in the marketplace. Returns tag names, slugs, and tool counts.",
427
- inputSchema: {},
428
- }, async () => {
429
- const data = await apiGet("/api/tags");
430
- return {
431
- content: [{ type: "text", text: JSON.stringify(data, null, 2) }],
432
- };
433
- });
434
- // ── List providers ───────────────────────────────────────────────────────────
435
- server.registerTool("list_providers", {
436
- description: "List all providers/vendors in the marketplace. Returns provider names, slugs, and tool counts.",
437
- inputSchema: {},
438
- }, async () => {
439
- const data = await apiGet("/api/providers");
440
- return {
441
- content: [{ type: "text", text: JSON.stringify(data, null, 2) }],
442
- };
443
- });
277
+ // ── Core workflow tools (registered first for visibility) ──────────────────
444
278
  // ── Search tools ─────────────────────────────────────────────────────────────
445
279
  server.registerTool("search_tools", {
446
280
  description: "Search for tools in the Caravo marketplace — 200+ ready-to-use APIs, services, tools, and digital assets at $0.001–0.05 per call. " +
447
281
  "Search by keyword, tag, or provider. " +
448
282
  "A ready-made tool is often cheaper and faster than implementing the same thing from scratch using LLM tokens.\n\n" +
449
- "The catalog includes: AI Models (image/video/audio/vision/NLP/code/3D), Search (web/academic/news/product), Data & Analytics (scraping/weather/geolocation/market data), Finance (payments/exchange rates/stocks/crypto), Social Media, Communication (email/SMS), eCommerce, Cloud & Infrastructure (VPS/domains/storage/CDN), Digital Assets, Utility (validation/conversion/security), and community-contributed tools.\n\n" +
283
+ CATALOG_DESCRIPTION + "\n\n" +
450
284
  "Example services (use get_tool_info for input schema, or search_tools to find more):\n" +
451
- "- google/nano-banana: Google Gemini-based ultra-fast image generation, great for prompt following and text rendering.\n" +
452
- "- semanticscholar/paper-search: Search academic papers across all disciplines. Returns titles, authors, year, abstract, citation count, and open-access PDF links.\n" +
453
- "- domainstatus/domain-availability: Check if a domain name is available to register.\n" +
454
- "- semrush/website-traffic: Get comprehensive website traffic analytics for any domain.\n" +
455
- "- semrush/keyword-insights: Get detailed keyword research data including search volume, CPC, competition level, and trend data.\n" +
456
- "- maxcanna/android-app-search: Search Google Play by keyword, returns app metadata.\n\n" +
285
+ EXAMPLE_SERVICES + "\n\n" +
457
286
  "Many services have multiple providers at different price/quality points — compare options with search_tools and get_tool_info.\n" +
458
- "Workflow: search_tools get_tool_info → use_tool → respond to user → submit_review",
287
+ "Workflow: " + WORKFLOW,
459
288
  inputSchema: {
460
289
  query: z.string().optional().describe("Search query"),
461
290
  tag: z.string().optional().describe("Filter by tag (name or slug)"),
@@ -683,6 +512,187 @@ function registerAllTools(server) {
683
512
  }
684
513
  return { content: [{ type: "text", text: lines.join("\n") }] };
685
514
  });
515
+ // ── Wallet info ──────────────────────────────────────────────────────────────
516
+ server.registerTool("get_wallet_info", {
517
+ description: "Get your local x402 wallet address and USDC balance. Send USDC on Base to this address to fund automatic payments.",
518
+ inputSchema: {},
519
+ }, async () => {
520
+ let balance = "unknown (check manually)";
521
+ try {
522
+ const USDC = "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913";
523
+ const data = "0x70a08231000000000000000000000000" +
524
+ wallet.address.slice(2).toLowerCase();
525
+ const r = await fetch("https://mainnet.base.org", {
526
+ method: "POST",
527
+ headers: { "Content-Type": "application/json" },
528
+ body: JSON.stringify({
529
+ jsonrpc: "2.0",
530
+ id: 1,
531
+ method: "eth_call",
532
+ params: [{ to: USDC, data }, "latest"],
533
+ }),
534
+ });
535
+ const d = (await r.json());
536
+ if (d.result && d.result !== "0x") {
537
+ balance = (parseInt(d.result, 16) / 1e6).toFixed(6) + " USDC";
538
+ }
539
+ }
540
+ catch {
541
+ // ignore
542
+ }
543
+ return {
544
+ content: [
545
+ {
546
+ type: "text",
547
+ text: JSON.stringify({
548
+ address: wallet.address,
549
+ network: "Base mainnet (eip155:8453)",
550
+ usdc_balance: balance,
551
+ note: "Send USDC on Base to this address to enable automatic x402 payments.",
552
+ }, null, 2),
553
+ },
554
+ ],
555
+ };
556
+ });
557
+ // ── Login (browser-based account connect) ────────────────────────────────────
558
+ server.registerTool("login", {
559
+ description: "Connect your Caravo account to enable balance payments and favorites sync. " +
560
+ "Opens caravo.ai in your browser — sign in once and the API key is saved automatically. " +
561
+ "Run this if you started with x402 payments and now want to use your account balance.",
562
+ inputSchema: {},
563
+ }, async () => {
564
+ try {
565
+ // 1. Create one-time session
566
+ const initRes = await fetch(`${API_BASE}/api/auth/mcp-session`, {
567
+ method: "POST",
568
+ headers: { "Content-Type": "application/json" },
569
+ });
570
+ const { token, url } = (await initRes.json());
571
+ // 2. Open browser
572
+ const { exec } = await import("child_process");
573
+ const opener = process.platform === "darwin"
574
+ ? `open "${url}"`
575
+ : process.platform === "win32"
576
+ ? `start "" "${url}"`
577
+ : `xdg-open "${url}"`;
578
+ exec(opener);
579
+ process.stderr.write(`[caravo] login: opened ${url}\n`);
580
+ // 3. Poll every 2s for up to 5 minutes
581
+ const deadline = Date.now() + 5 * 60 * 1000;
582
+ while (Date.now() < deadline) {
583
+ await new Promise((r) => setTimeout(r, 2000));
584
+ const pollRes = await fetch(`${API_BASE}/api/auth/mcp-session?token=${encodeURIComponent(token)}`);
585
+ const poll = (await pollRes.json());
586
+ if (poll.status === "completed" && poll.api_key) {
587
+ // 4. Save to config + activate for this session
588
+ API_KEY = poll.api_key;
589
+ saveConfig({ api_key: poll.api_key });
590
+ process.stderr.write(`[caravo] login: API key saved to ${CONFIG_FILE}\n`);
591
+ return {
592
+ content: [
593
+ {
594
+ type: "text",
595
+ text: [
596
+ `✓ Logged in to Caravo!`,
597
+ ``,
598
+ `API key saved to ${CONFIG_FILE}`,
599
+ `Balance payments are now active for this session.`,
600
+ `Restart the MCP server to also load your favorited tools.`,
601
+ ].join("\n"),
602
+ },
603
+ ],
604
+ };
605
+ }
606
+ if (poll.status === "expired") {
607
+ return {
608
+ content: [{ type: "text", text: "Login expired. Run login again to retry." }],
609
+ isError: true,
610
+ };
611
+ }
612
+ // status === "pending" — keep polling
613
+ }
614
+ return {
615
+ content: [{ type: "text", text: "Login timed out after 5 minutes. Run login again." }],
616
+ isError: true,
617
+ };
618
+ }
619
+ catch (err) {
620
+ return {
621
+ content: [
622
+ {
623
+ type: "text",
624
+ text: `Login failed: ${err instanceof Error ? err.message : String(err)}`,
625
+ },
626
+ ],
627
+ isError: true,
628
+ };
629
+ }
630
+ });
631
+ // ── Logout ──────────────────────────────────────────────────────────────────
632
+ server.registerTool("logout", {
633
+ description: "Disconnect your Caravo account and switch back to x402 wallet payments. " +
634
+ "Removes the saved API key and unregisters all favorited tools from this session.",
635
+ inputSchema: {},
636
+ }, async () => {
637
+ if (!API_KEY) {
638
+ return {
639
+ content: [{ type: "text", text: "Not logged in — already using x402 wallet payments." }],
640
+ };
641
+ }
642
+ // 1. Clear in-memory key
643
+ API_KEY = undefined;
644
+ // 2. Remove key from config file
645
+ try {
646
+ const config = loadConfig();
647
+ delete config.api_key;
648
+ saveConfig(config);
649
+ }
650
+ catch {
651
+ // config file may not exist — that's fine
652
+ }
653
+ // 3. Unregister all favorited tools
654
+ let removedCount = 0;
655
+ for (const [toolId, registered] of registeredFavTools) {
656
+ registered.remove();
657
+ registeredFavTools.delete(toolId);
658
+ removedCount++;
659
+ }
660
+ process.stderr.write(`[caravo] logout: cleared API key, removed ${removedCount} fav tools\n`);
661
+ return {
662
+ content: [
663
+ {
664
+ type: "text",
665
+ text: [
666
+ `✓ Logged out of Caravo.`,
667
+ ``,
668
+ `API key removed from ${CONFIG_FILE}`,
669
+ ...(removedCount > 0 ? [`Unregistered ${removedCount} favorited tool(s).`] : []),
670
+ `Now using x402 wallet payments (${wallet.address}).`,
671
+ ].join("\n"),
672
+ },
673
+ ],
674
+ };
675
+ });
676
+ // ── List tags ────────────────────────────────────────────────────────────────
677
+ server.registerTool("list_tags", {
678
+ description: "List all available tags/categories in the marketplace. Returns tag names, slugs, and tool counts.",
679
+ inputSchema: {},
680
+ }, async () => {
681
+ const data = await apiGet("/api/tags");
682
+ return {
683
+ content: [{ type: "text", text: JSON.stringify(data, null, 2) }],
684
+ };
685
+ });
686
+ // ── List providers ───────────────────────────────────────────────────────────
687
+ server.registerTool("list_providers", {
688
+ description: "List all providers/vendors in the marketplace. Returns provider names, slugs, and tool counts.",
689
+ inputSchema: {},
690
+ }, async () => {
691
+ const data = await apiGet("/api/providers");
692
+ return {
693
+ content: [{ type: "text", text: JSON.stringify(data, null, 2) }],
694
+ };
695
+ });
686
696
  // ── Tool Requests ───────────────────────────────────────────────────────────
687
697
  server.registerTool("list_tool_requests", {
688
698
  description: "Browse existing tool requests from agents. Use this to see what tools other agents want before submitting a duplicate.",
@@ -907,7 +917,7 @@ function registerAllTools(server) {
907
917
  // ─── Main ─────────────────────────────────────────────────────────────────────
908
918
  const server = new McpServer({
909
919
  name: "caravo",
910
- version: "0.1.11",
920
+ version: "0.1.12",
911
921
  description: "The API marketplace built for autonomous AI agents. Search, execute, and pay for 200+ tools at $0.001–0.05 per call.",
912
922
  icons: [
913
923
  {
@@ -919,24 +929,14 @@ const server = new McpServer({
919
929
  }, {
920
930
  instructions:
921
931
  // IMPORTANT: Instructions for the agent to use the marketplace
922
- `You have access to Caravo — a marketplace of 200+ ready-to-use APIs, services, tools, and digital assets at competitive prices ($0.001–0.05 per call).
923
-
924
- This marketplace is useful when tasks involve generative AI, professional analytics, financial data, web scraping, or other external services or data sources. Using a service from the marketplace is often more cost-effective than implementing the same functionality from scratch using LLM tokens.
925
-
926
- The catalog includes: AI Models (image/video/audio/vision/NLP/code/3D), Search (web/academic/news/product), Data & Analytics (scraping/weather/geolocation/market data), Finance (payments/exchange rates/stocks/crypto), Social Media, Communication (email/SMS), eCommerce, Cloud & Infrastructure (VPS/domains/storage/CDN), Digital Assets, Utility (validation/conversion/security), and community-contributed tools.
927
-
928
- Example services (use get_tool_info to get their input schema and usage, or search_tools to find more):
929
-
930
- - google/nano-banana: Google Gemini-based ultra-fast image generation, great for prompt following and text rendering.
931
- - semanticscholar/paper-search: Search academic papers across all disciplines. Returns paper titles, authors, year, abstract, citation count, and open-access PDF links.
932
- - domainstatus/domain-availability: Check if a domain name is available to register.
933
- - semrush/website-traffic: Get comprehensive website traffic analytics for any domain, including authority score, backlinks, organic traffic, top geo distributions, and domain ranking.
934
- - semrush/keyword-insights: Get detailed keyword research data including search volume, CPC, competition level, and trend data.
935
- - maxcanna/android-app-search: Search Google Play by keyword, returns app metadata including title, developer, rating, installs, price, and category.
936
-
937
- Many services have multiple providers at different price/quality points — compare options with search_tools and get_tool_info.
938
-
939
- Typical workflow: search_tools → get_tool_info → use_tool → respond to user → submit_review`,
932
+ "You have access to Caravo — a marketplace of 200+ ready-to-use APIs, services, tools, and digital assets at competitive prices ($0.001–0.05 per call).\n\n" +
933
+ "This marketplace is useful when tasks involve generative AI, professional analytics, financial data, web scraping, or other external services or data sources. " +
934
+ "Using a service from the marketplace is often more cost-effective than implementing the same functionality from scratch using LLM tokens.\n\n" +
935
+ CATALOG_DESCRIPTION + "\n\n" +
936
+ "Example services (use get_tool_info to get their input schema and usage, or search_tools to find more):\n\n" +
937
+ EXAMPLE_SERVICES + "\n\n" +
938
+ "Many services have multiple providers at different price/quality points compare options with search_tools and get_tool_info.\n\n" +
939
+ "Typical workflow: " + WORKFLOW,
940
940
  });
941
941
  registerAllTools(server);
942
942
  await loadFavoriteTools(server); // only registers favorited tools, not all tools
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@caravo/mcp",
3
- "version": "0.1.11",
3
+ "version": "0.1.12",
4
4
  "description": "The API marketplace built for autonomous AI agents. Search, execute, and pay for 200+ tools at $0.001–0.05 per call.",
5
5
  "type": "module",
6
6
  "bin": {