@caravo/mcp 0.1.25 → 0.1.27

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 +42 -7
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -195,7 +195,7 @@ async function apiPost(path, body) {
195
195
  process.stderr.write(`[caravo] API key request failed (${r.status}), falling back to x402\n`);
196
196
  const x402Opts = {
197
197
  method: "POST",
198
- headers: { "Content-Type": "application/json" },
198
+ headers: baseHeaders(), // Keep Authorization for user attribution on x402 fallback
199
199
  body: JSON.stringify(body),
200
200
  };
201
201
  return safeParseJson(await fetchWithX402(url, x402Opts, wallet));
@@ -246,6 +246,35 @@ function stripDangerousFields(input) {
246
246
  }
247
247
  return cleaned;
248
248
  }
249
+ /**
250
+ * Resolve local file paths in tool input to base64.
251
+ * Detects file:// URIs and absolute paths with image extensions.
252
+ * Runs locally so base64 never enters the LLM context.
253
+ */
254
+ function resolveLocalFiles(input) {
255
+ const result = { ...input };
256
+ for (const [key, value] of Object.entries(result)) {
257
+ if (typeof value !== "string")
258
+ continue;
259
+ const filePath = toLocalPath(value);
260
+ if (!filePath)
261
+ continue;
262
+ if (!existsSync(filePath)) {
263
+ throw new Error(`Local file not found: ${filePath}`);
264
+ }
265
+ result[key] = readFileSync(filePath).toString("base64");
266
+ }
267
+ return result;
268
+ }
269
+ function toLocalPath(value) {
270
+ if (value.startsWith("file:///"))
271
+ return value.slice(7);
272
+ if (value.startsWith("file://"))
273
+ return value.slice(7);
274
+ if (/^\//.test(value) && /\.(png|jpe?g|gif|webp|bmp|svg|tiff?)$/i.test(value))
275
+ return value;
276
+ return null;
277
+ }
249
278
  // ─── Favorites registration ────────────────────────────────────────────────────
250
279
  // Track registered fav tool handles for dynamic add/remove
251
280
  const registeredFavTools = new Map();
@@ -342,7 +371,8 @@ function buildPostExecPrompt(execId, toolId) {
342
371
  function makeFavToolHandler(tool) {
343
372
  return async (args) => {
344
373
  // Extract dry_run before passing remaining args to the API
345
- const { dry_run, ...toolInput } = args;
374
+ const { dry_run, ...rawInput } = args;
375
+ const toolInput = resolveLocalFiles(rawInput);
346
376
  if (dry_run) {
347
377
  return dryRunProbe(tool.id, toolInput);
348
378
  }
@@ -571,6 +601,7 @@ function registerAllTools(server) {
571
601
  server.registerTool("use_tool", {
572
602
  description: "Execute any marketplace tool by ID. Use get_tool_info first to see the required input schema. " +
573
603
  "Paid tools auto-pay via x402 (wallet) or API key balance. " +
604
+ "File upload tip: For tools that accept file input, you can pass a local file path (e.g., /path/to/photo.jpg) or file:// URI — it will be auto-converted to base64. Prefer passing a URL when available. " +
574
605
  "After using a tool, check existing reviews first — upvote one if it matches your experience, or write a new review if none captures your feedback.",
575
606
  inputSchema: {
576
607
  tool_id: z.string().describe("The tool ID or slug to execute (e.g., 'black-forest-labs/flux.1-schnell' or 'alice/imagen-4')"),
@@ -587,7 +618,7 @@ function registerAllTools(server) {
587
618
  isError: true,
588
619
  };
589
620
  }
590
- const cleanInput = stripDangerousFields(input);
621
+ const cleanInput = resolveLocalFiles(stripDangerousFields(input));
591
622
  // Dry-run mode: probe cost without executing or paying
592
623
  if (dry_run) {
593
624
  return dryRunProbe(tool_id.trim(), cleanInput);
@@ -893,7 +924,9 @@ function registerAllTools(server) {
893
924
  "Removes the saved API key and unregisters all favorited tools from this session.",
894
925
  inputSchema: {},
895
926
  }, async () => {
896
- if (!API_KEY) {
927
+ // Check both in-memory key and config file (key may have been set by CLI login after MCP started)
928
+ const configKey = loadConfig().api_key;
929
+ if (!API_KEY && !configKey) {
897
930
  return {
898
931
  content: [{ type: "text", text: "Not logged in — already using x402 wallet payments." }],
899
932
  };
@@ -902,9 +935,11 @@ function registerAllTools(server) {
902
935
  API_KEY = undefined;
903
936
  // 2. Remove key from config file
904
937
  try {
905
- const config = loadConfig();
906
- delete config.api_key;
907
- saveConfig(config);
938
+ if (configKey) {
939
+ const config = loadConfig();
940
+ delete config.api_key;
941
+ saveConfig(config);
942
+ }
908
943
  }
909
944
  catch {
910
945
  // config file may not exist — that's fine
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@caravo/mcp",
3
- "version": "0.1.25",
3
+ "version": "0.1.27",
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": {