@caravo/mcp 0.1.24 → 0.1.26

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 +310 -206
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -169,7 +169,17 @@ function baseHeaders() {
169
169
  }
170
170
  async function apiGet(path) {
171
171
  const r = await fetch(`${API_BASE}${path}`, { headers: baseHeaders() });
172
- return r.json();
172
+ return safeParseJson(r);
173
+ }
174
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
175
+ async function safeParseJson(r) {
176
+ try {
177
+ return await r.json();
178
+ }
179
+ catch {
180
+ const text = await r.text().catch(() => "");
181
+ return { error: `Non-JSON response (${r.status}): ${text.slice(0, 200)}` };
182
+ }
173
183
  }
174
184
  async function apiPost(path, body) {
175
185
  const url = `${API_BASE}${path}`;
@@ -179,18 +189,18 @@ async function apiPost(path, body) {
179
189
  body: JSON.stringify(body),
180
190
  };
181
191
  if (!API_KEY)
182
- return (await fetchWithX402(url, opts, wallet)).json();
192
+ return safeParseJson(await fetchWithX402(url, opts, wallet));
183
193
  const r = await fetch(url, opts);
184
194
  if (r.status === 401 || r.status === 403 || r.status === 402) {
185
195
  process.stderr.write(`[caravo] API key request failed (${r.status}), falling back to x402\n`);
186
196
  const x402Opts = {
187
197
  method: "POST",
188
- headers: { "Content-Type": "application/json" },
198
+ headers: baseHeaders(), // Keep Authorization for user attribution on x402 fallback
189
199
  body: JSON.stringify(body),
190
200
  };
191
- return (await fetchWithX402(url, x402Opts, wallet)).json();
201
+ return safeParseJson(await fetchWithX402(url, x402Opts, wallet));
192
202
  }
193
- return r.json();
203
+ return safeParseJson(r);
194
204
  }
195
205
  async function apiDelete(path, body) {
196
206
  const url = `${API_BASE}${path}`;
@@ -199,7 +209,15 @@ async function apiDelete(path, body) {
199
209
  headers: baseHeaders(),
200
210
  body: JSON.stringify(body),
201
211
  });
202
- return r.json();
212
+ return safeParseJson(r);
213
+ }
214
+ const MAX_JSON_OUTPUT_CHARS = 20_000;
215
+ function safeJsonText(data, indent = true) {
216
+ const json = indent ? JSON.stringify(data, null, 2) : JSON.stringify(data);
217
+ if (json.length > MAX_JSON_OUTPUT_CHARS) {
218
+ return json.slice(0, MAX_JSON_OUTPUT_CHARS) + `\n... (truncated, ${json.length} chars total)`;
219
+ }
220
+ return json;
203
221
  }
204
222
  // ─── Input validation helpers ─────────────────────────────────────────────────
205
223
  /** Validate tool_id format: only allow safe chars, no path traversal. */
@@ -271,9 +289,7 @@ function formatOutput(output) {
271
289
  }
272
290
  // JSON
273
291
  if (output.json !== undefined) {
274
- const jsonStr = JSON.stringify(output.json, null, 2);
275
- // Truncate large JSON to avoid context overload
276
- lines.push(jsonStr.length > 4000 ? jsonStr.slice(0, 4000) + "\n... (truncated)" : jsonStr);
292
+ lines.push(safeJsonText(output.json));
277
293
  }
278
294
  return lines;
279
295
  }
@@ -354,7 +370,7 @@ function makeFavToolHandler(tool) {
354
370
  }
355
371
  return {
356
372
  content: [
357
- { type: "text", text: `Error: ${JSON.stringify(result)}` },
373
+ { type: "text", text: `Error: ${safeJsonText(result, false)}` },
358
374
  ],
359
375
  isError: true,
360
376
  };
@@ -495,26 +511,34 @@ function registerAllTools(server) {
495
511
  if (per_page > 100) {
496
512
  return { content: [{ type: "text", text: "Error: per_page must be at most 100" }], isError: true };
497
513
  }
498
- const params = new URLSearchParams();
499
- if (query)
500
- params.set("query", query);
501
- if (tag)
502
- params.set("tag", tag);
503
- if (provider)
504
- params.set("provider", provider);
505
- if (pricing_type)
506
- params.set("pricing_type", pricing_type);
507
- params.set("page", String(page));
508
- params.set("per_page", String(per_page));
509
- params.set("view", "agent");
510
- const data = await apiGet(`/api/tools?${params}`);
511
- let text = JSON.stringify(data, null, 2);
512
- if (pendingUpdate) {
513
- text += `\n\n[Update available: @caravo/mcp ${pendingUpdate.current} → ${pendingUpdate.latest}. Will auto-update on next MCP restart.]`;
514
+ try {
515
+ const params = new URLSearchParams();
516
+ if (query)
517
+ params.set("query", query);
518
+ if (tag)
519
+ params.set("tag", tag);
520
+ if (provider)
521
+ params.set("provider", provider);
522
+ if (pricing_type)
523
+ params.set("pricing_type", pricing_type);
524
+ params.set("page", String(page));
525
+ params.set("per_page", String(per_page));
526
+ params.set("view", "agent");
527
+ const data = await apiGet(`/api/tools?${params}`);
528
+ let text = safeJsonText(data);
529
+ if (pendingUpdate) {
530
+ text += `\n\n[Update available: @caravo/mcp ${pendingUpdate.current} → ${pendingUpdate.latest}. Will auto-update on next MCP restart.]`;
531
+ }
532
+ return {
533
+ content: [{ type: "text", text }],
534
+ };
535
+ }
536
+ catch (err) {
537
+ return {
538
+ content: [{ type: "text", text: `Error: ${err instanceof Error ? err.message : String(err)}` }],
539
+ isError: true,
540
+ };
514
541
  }
515
- return {
516
- content: [{ type: "text", text }],
517
- };
518
542
  });
519
543
  // ── Get tool info ────────────────────────────────────────────────────────────
520
544
  server.registerTool("get_tool_info", {
@@ -530,10 +554,18 @@ function registerAllTools(server) {
530
554
  isError: true,
531
555
  };
532
556
  }
533
- const data = await apiGet(`/api/tools/${tool_id.trim()}`);
534
- return {
535
- content: [{ type: "text", text: JSON.stringify(data, null, 2) }],
536
- };
557
+ try {
558
+ const data = await apiGet(`/api/tools/${tool_id.trim()}`);
559
+ return {
560
+ content: [{ type: "text", text: safeJsonText(data) }],
561
+ };
562
+ }
563
+ catch (err) {
564
+ return {
565
+ content: [{ type: "text", text: `Error: ${err instanceof Error ? err.message : String(err)}` }],
566
+ isError: true,
567
+ };
568
+ }
537
569
  });
538
570
  // ── use_tool (meta-tool) ─────────────────────────────────────────────────────
539
571
  server.registerTool("use_tool", {
@@ -586,7 +618,7 @@ function registerAllTools(server) {
586
618
  }
587
619
  return {
588
620
  content: [
589
- { type: "text", text: `Error: ${JSON.stringify(result)}` },
621
+ { type: "text", text: `Error: ${safeJsonText(result, false)}` },
590
622
  ],
591
623
  isError: true,
592
624
  };
@@ -628,82 +660,90 @@ function registerAllTools(server) {
628
660
  isError: true,
629
661
  };
630
662
  }
631
- // Upvote mode
632
- if (upvote_review_id) {
633
- if (!execution_id) {
663
+ try {
664
+ // Upvote mode
665
+ if (upvote_review_id) {
666
+ if (!execution_id) {
667
+ return {
668
+ content: [{ type: "text", text: "Error: execution_id is required for upvoting. Each upvote consumes one tool execution." }],
669
+ isError: true,
670
+ };
671
+ }
672
+ const result = await apiPost(`/api/reviews/upvote`, {
673
+ review_id: upvote_review_id,
674
+ execution_id,
675
+ });
676
+ if (result.success) {
677
+ const action = result.action === "already_upvoted" ? "Already upvoted" : "Upvoted";
678
+ const upvotedToolId = result.tool_id || "";
679
+ const lines = [`✓ ${action} review ${upvote_review_id}`];
680
+ // Suggest favorite only when upvoting a 5-star review
681
+ if (result.rating === 5 && upvotedToolId) {
682
+ if (API_KEY && !registeredFavTools.has(upvotedToolId)) {
683
+ lines.push(`This was a 5/5 review — consider favorite_tool(tool_id="${upvotedToolId}") if you plan to reuse it.`);
684
+ }
685
+ else if (!API_KEY) {
686
+ lines.push(`This was a 5/5 review — consider saving tool_id="${upvotedToolId}" to your memory for future reuse.`);
687
+ }
688
+ }
689
+ return { content: [{ type: "text", text: lines.join("\n") }] };
690
+ }
691
+ return {
692
+ content: [{ type: "text", text: result.error ? `Error: ${result.error}` : safeJsonText(result) }],
693
+ isError: true,
694
+ };
695
+ }
696
+ // New review mode
697
+ if (rating == null || !comment) {
698
+ return {
699
+ content: [
700
+ {
701
+ type: "text",
702
+ text: "Error: rating and comment are required for new reviews. To upvote an existing review, use upvote_review_id instead.",
703
+ },
704
+ ],
705
+ isError: true,
706
+ };
707
+ }
708
+ if (rating < 1 || rating > 5) {
634
709
  return {
635
- content: [{ type: "text", text: "Error: execution_id is required for upvoting. Each upvote consumes one tool execution." }],
710
+ content: [{ type: "text", text: "Error: rating must be between 1 and 5" }],
636
711
  isError: true,
637
712
  };
638
713
  }
639
- const result = await apiPost(`/api/reviews/upvote`, {
640
- review_id: upvote_review_id,
714
+ const result = await apiPost(`/api/reviews`, {
641
715
  execution_id,
716
+ rating,
717
+ comment,
718
+ agent_id,
642
719
  });
643
- if (result.success) {
644
- const action = result.action === "already_upvoted" ? "Already upvoted" : "Upvoted";
645
- const upvotedToolId = result.tool_id || "";
646
- const lines = [`✓ ${action} review ${upvote_review_id}`];
647
- // Suggest favorite only when upvoting a 5-star review
648
- if (result.rating === 5 && upvotedToolId) {
649
- if (API_KEY && !registeredFavTools.has(upvotedToolId)) {
650
- lines.push(`This was a 5/5 review — consider favorite_tool(tool_id="${upvotedToolId}") if you plan to reuse it.`);
651
- }
652
- else if (!API_KEY) {
653
- lines.push(`This was a 5/5 review — consider saving tool_id="${upvotedToolId}" to your memory for future reuse.`);
654
- }
720
+ if (result.error) {
721
+ return {
722
+ content: [{ type: "text", text: `Error: ${result.error}` }],
723
+ isError: true,
724
+ };
725
+ }
726
+ // The API returns the review record with tool_id derived from execution
727
+ const reviewToolId = result.tool_id || "unknown";
728
+ const lines = [
729
+ `✓ Review submitted for ${reviewToolId} (${rating}/5)`,
730
+ ];
731
+ if (rating === 5) {
732
+ if (API_KEY && !registeredFavTools.has(reviewToolId)) {
733
+ lines.push(`This tool scored 5/5 — consider favorite_tool(tool_id="${reviewToolId}") if you plan to reuse it.`);
734
+ }
735
+ else if (!API_KEY) {
736
+ lines.push(`This tool scored 5/5 — consider saving tool_id="${reviewToolId}" to your memory for future reuse.`);
655
737
  }
656
- return { content: [{ type: "text", text: lines.join("\n") }] };
657
738
  }
658
- return {
659
- content: [{ type: "text", text: result.error ? `Error: ${result.error}` : JSON.stringify(result, null, 2) }],
660
- isError: true,
661
- };
739
+ return { content: [{ type: "text", text: lines.join("\n") }] };
662
740
  }
663
- // New review mode
664
- if (rating == null || !comment) {
665
- return {
666
- content: [
667
- {
668
- type: "text",
669
- text: "Error: rating and comment are required for new reviews. To upvote an existing review, use upvote_review_id instead.",
670
- },
671
- ],
672
- isError: true,
673
- };
674
- }
675
- if (rating < 1 || rating > 5) {
676
- return {
677
- content: [{ type: "text", text: "Error: rating must be between 1 and 5" }],
678
- isError: true,
679
- };
680
- }
681
- const result = await apiPost(`/api/reviews`, {
682
- execution_id,
683
- rating,
684
- comment,
685
- agent_id,
686
- });
687
- if (result.error) {
741
+ catch (err) {
688
742
  return {
689
- content: [{ type: "text", text: `Error: ${result.error}` }],
743
+ content: [{ type: "text", text: `Error: ${err instanceof Error ? err.message : String(err)}` }],
690
744
  isError: true,
691
745
  };
692
746
  }
693
- // The API returns the review record with tool_id derived from execution
694
- const reviewToolId = result.tool_id || "unknown";
695
- const lines = [
696
- `✓ Review submitted for ${reviewToolId} (${rating}/5)`,
697
- ];
698
- if (rating === 5) {
699
- if (API_KEY && !registeredFavTools.has(reviewToolId)) {
700
- lines.push(`This tool scored 5/5 — consider favorite_tool(tool_id="${reviewToolId}") if you plan to reuse it.`);
701
- }
702
- else if (!API_KEY) {
703
- lines.push(`This tool scored 5/5 — consider saving tool_id="${reviewToolId}" to your memory for future reuse.`);
704
- }
705
- }
706
- return { content: [{ type: "text", text: lines.join("\n") }] };
707
747
  });
708
748
  // ── Wallet info ──────────────────────────────────────────────────────────────
709
749
  server.registerTool("get_wallet_info", {
@@ -897,20 +937,36 @@ function registerAllTools(server) {
897
937
  description: "List all available tags/categories in the marketplace. Returns tag names, slugs, and tool counts.",
898
938
  inputSchema: {},
899
939
  }, async () => {
900
- const data = await apiGet("/api/tags");
901
- return {
902
- content: [{ type: "text", text: JSON.stringify(data, null, 2) }],
903
- };
940
+ try {
941
+ const data = await apiGet("/api/tags");
942
+ return {
943
+ content: [{ type: "text", text: safeJsonText(data) }],
944
+ };
945
+ }
946
+ catch (err) {
947
+ return {
948
+ content: [{ type: "text", text: `Error: ${err instanceof Error ? err.message : String(err)}` }],
949
+ isError: true,
950
+ };
951
+ }
904
952
  });
905
953
  // ── List providers ───────────────────────────────────────────────────────────
906
954
  server.registerTool("list_providers", {
907
955
  description: "List all providers/vendors in the marketplace. Returns provider names, slugs, and tool counts.",
908
956
  inputSchema: {},
909
957
  }, async () => {
910
- const data = await apiGet("/api/providers");
911
- return {
912
- content: [{ type: "text", text: JSON.stringify(data, null, 2) }],
913
- };
958
+ try {
959
+ const data = await apiGet("/api/providers");
960
+ return {
961
+ content: [{ type: "text", text: safeJsonText(data) }],
962
+ };
963
+ }
964
+ catch (err) {
965
+ return {
966
+ content: [{ type: "text", text: `Error: ${err instanceof Error ? err.message : String(err)}` }],
967
+ isError: true,
968
+ };
969
+ }
914
970
  });
915
971
  // ── Tool Requests ───────────────────────────────────────────────────────────
916
972
  server.registerTool("list_tool_requests", {
@@ -930,14 +986,22 @@ function registerAllTools(server) {
930
986
  if (per_page > 100) {
931
987
  return { content: [{ type: "text", text: "Error: per_page must be at most 100" }], isError: true };
932
988
  }
933
- const params = new URLSearchParams();
934
- params.set("status", status);
935
- params.set("page", String(page));
936
- params.set("per_page", String(per_page));
937
- const data = await apiGet(`/api/tool-requests?${params}`);
938
- return {
939
- content: [{ type: "text", text: JSON.stringify(data, null, 2) }],
940
- };
989
+ try {
990
+ const params = new URLSearchParams();
991
+ params.set("status", status);
992
+ params.set("page", String(page));
993
+ params.set("per_page", String(per_page));
994
+ const data = await apiGet(`/api/tool-requests?${params}`);
995
+ return {
996
+ content: [{ type: "text", text: safeJsonText(data) }],
997
+ };
998
+ }
999
+ catch (err) {
1000
+ return {
1001
+ content: [{ type: "text", text: `Error: ${err instanceof Error ? err.message : String(err)}` }],
1002
+ isError: true,
1003
+ };
1004
+ }
941
1005
  });
942
1006
  server.registerTool("request_tool", {
943
1007
  description: "Submit a request for a tool that doesn't exist in the marketplace yet. " +
@@ -951,33 +1015,41 @@ function registerAllTools(server) {
951
1015
  agent_id: z.string().optional().describe("Optional agent identifier"),
952
1016
  },
953
1017
  }, async ({ title, description, use_case, execution_id, agent_id }) => {
954
- const result = await apiPost("/api/tool-requests", {
955
- title,
956
- description,
957
- use_case,
958
- execution_id,
959
- agent_id,
960
- });
961
- if (result.error) {
1018
+ try {
1019
+ const result = await apiPost("/api/tool-requests", {
1020
+ title,
1021
+ description,
1022
+ use_case,
1023
+ execution_id,
1024
+ agent_id,
1025
+ });
1026
+ if (result.error) {
1027
+ return {
1028
+ content: [{ type: "text", text: `Error: ${result.error}` }],
1029
+ isError: true,
1030
+ };
1031
+ }
1032
+ return {
1033
+ content: [
1034
+ {
1035
+ type: "text",
1036
+ text: [
1037
+ `✓ Tool request submitted: "${result.title}"`,
1038
+ ` Request ID: ${result.id}`,
1039
+ ` Status: ${result.status}`,
1040
+ ``,
1041
+ `Other agents can upvote this request to signal demand.`,
1042
+ ].join("\n"),
1043
+ },
1044
+ ],
1045
+ };
1046
+ }
1047
+ catch (err) {
962
1048
  return {
963
- content: [{ type: "text", text: `Error: ${result.error}` }],
1049
+ content: [{ type: "text", text: `Error: ${err instanceof Error ? err.message : String(err)}` }],
964
1050
  isError: true,
965
1051
  };
966
1052
  }
967
- return {
968
- content: [
969
- {
970
- type: "text",
971
- text: [
972
- `✓ Tool request submitted: "${result.title}"`,
973
- ` Request ID: ${result.id}`,
974
- ` Status: ${result.status}`,
975
- ``,
976
- `Other agents can upvote this request to signal demand.`,
977
- ].join("\n"),
978
- },
979
- ],
980
- };
981
1053
  });
982
1054
  server.registerTool("upvote_tool_request", {
983
1055
  description: "Upvote an existing tool request to signal demand. " +
@@ -987,19 +1059,27 @@ function registerAllTools(server) {
987
1059
  execution_id: z.string().optional().describe("Execution ID from a previous tool use (required if no API key)"),
988
1060
  },
989
1061
  }, async ({ request_id, execution_id }) => {
990
- const result = await apiPost(`/api/tool-requests/${request_id}`, {
991
- execution_id,
992
- });
993
- if (result.error) {
1062
+ try {
1063
+ const result = await apiPost(`/api/tool-requests/${request_id}`, {
1064
+ execution_id,
1065
+ });
1066
+ if (result.error) {
1067
+ return {
1068
+ content: [{ type: "text", text: `Error: ${result.error}` }],
1069
+ isError: true,
1070
+ };
1071
+ }
1072
+ const action = result.action === "already_upvoted" ? "Already upvoted" : "Upvoted";
1073
+ return {
1074
+ content: [{ type: "text", text: `✓ ${action} tool request ${request_id}` }],
1075
+ };
1076
+ }
1077
+ catch (err) {
994
1078
  return {
995
- content: [{ type: "text", text: `Error: ${result.error}` }],
1079
+ content: [{ type: "text", text: `Error: ${err instanceof Error ? err.message : String(err)}` }],
996
1080
  isError: true,
997
1081
  };
998
1082
  }
999
- const action = result.action === "already_upvoted" ? "Already upvoted" : "Upvoted";
1000
- return {
1001
- content: [{ type: "text", text: `✓ ${action} tool request ${request_id}` }],
1002
- };
1003
1083
  });
1004
1084
  // ── Favorites management ─────────────────────────────────────────────────────
1005
1085
  server.registerTool("list_favorites", {
@@ -1017,31 +1097,39 @@ function registerAllTools(server) {
1017
1097
  isError: true,
1018
1098
  };
1019
1099
  }
1020
- const result = await apiGet("/api/favorites");
1021
- if (result.error) {
1100
+ try {
1101
+ const result = await apiGet("/api/favorites");
1102
+ if (result.error) {
1103
+ return {
1104
+ content: [{ type: "text", text: `Error: ${result.error}` }],
1105
+ isError: true,
1106
+ };
1107
+ }
1108
+ const tools = result.data ?? [];
1109
+ return {
1110
+ content: [
1111
+ {
1112
+ type: "text",
1113
+ text: safeJsonText({
1114
+ total: tools.length,
1115
+ favorites: tools.map((t) => ({
1116
+ tool_id: t.id,
1117
+ name: t.name,
1118
+ mcp_tool_name: `fav:${t.id}`,
1119
+ price_per_call: t.pricing.price_per_call,
1120
+ })),
1121
+ hint: "Favorited tools are registered as direct MCP tools named fav:<tool_id>.",
1122
+ }),
1123
+ },
1124
+ ],
1125
+ };
1126
+ }
1127
+ catch (err) {
1022
1128
  return {
1023
- content: [{ type: "text", text: `Error: ${result.error}` }],
1129
+ content: [{ type: "text", text: `Error: ${err instanceof Error ? err.message : String(err)}` }],
1024
1130
  isError: true,
1025
1131
  };
1026
1132
  }
1027
- const tools = result.data ?? [];
1028
- return {
1029
- content: [
1030
- {
1031
- type: "text",
1032
- text: JSON.stringify({
1033
- total: tools.length,
1034
- favorites: tools.map((t) => ({
1035
- tool_id: t.id,
1036
- name: t.name,
1037
- mcp_tool_name: `fav:${t.id}`,
1038
- price_per_call: t.pricing.price_per_call,
1039
- })),
1040
- hint: "Favorited tools are registered as direct MCP tools named fav:<tool_id>.",
1041
- }, null, 2),
1042
- },
1043
- ],
1044
- };
1045
1133
  });
1046
1134
  server.registerTool("favorite_tool", {
1047
1135
  description: "Bookmark a tool you plan to reuse frequently — it appears as a direct fav:<tool_id> MCP tool. " +
@@ -1064,31 +1152,39 @@ function registerAllTools(server) {
1064
1152
  isError: true,
1065
1153
  };
1066
1154
  }
1067
- const result = await apiPost("/api/favorites", { tool_id });
1068
- if (result.error) {
1155
+ try {
1156
+ const result = await apiPost("/api/favorites", { tool_id });
1157
+ if (result.error) {
1158
+ return {
1159
+ content: [{ type: "text", text: `Error: ${result.error}` }],
1160
+ isError: true,
1161
+ };
1162
+ }
1163
+ // Dynamically register the new fav tool in this session
1164
+ const tool = result.tool;
1165
+ if (tool) {
1166
+ registerFavTool(server, tool);
1167
+ }
1069
1168
  return {
1070
- content: [{ type: "text", text: `Error: ${result.error}` }],
1071
- isError: true,
1169
+ content: [
1170
+ {
1171
+ type: "text",
1172
+ text: [
1173
+ `★ Added "${tool?.name ?? tool_id}" to favorites!`,
1174
+ ``,
1175
+ `It is now registered as a direct MCP tool: fav:${tool_id}`,
1176
+ `Call it directly with its input parameters — no need for use_tool.`,
1177
+ ].join("\n"),
1178
+ },
1179
+ ],
1072
1180
  };
1073
1181
  }
1074
- // Dynamically register the new fav tool in this session
1075
- const tool = result.tool;
1076
- if (tool) {
1077
- registerFavTool(server, tool);
1182
+ catch (err) {
1183
+ return {
1184
+ content: [{ type: "text", text: `Error: ${err instanceof Error ? err.message : String(err)}` }],
1185
+ isError: true,
1186
+ };
1078
1187
  }
1079
- return {
1080
- content: [
1081
- {
1082
- type: "text",
1083
- text: [
1084
- `★ Added "${tool?.name ?? tool_id}" to favorites!`,
1085
- ``,
1086
- `It is now registered as a direct MCP tool: fav:${tool_id}`,
1087
- `Call it directly with its input parameters — no need for use_tool.`,
1088
- ].join("\n"),
1089
- },
1090
- ],
1091
- };
1092
1188
  });
1093
1189
  server.registerTool("unfavorite_tool", {
1094
1190
  description: "Remove a tool from your favorites. The fav:<tool_id> direct tool will be unregistered. " +
@@ -1108,29 +1204,37 @@ function registerAllTools(server) {
1108
1204
  isError: true,
1109
1205
  };
1110
1206
  }
1111
- const result = await apiDelete("/api/favorites", { tool_id });
1112
- if (result.error) {
1207
+ try {
1208
+ const result = await apiDelete("/api/favorites", { tool_id });
1209
+ if (result.error) {
1210
+ return {
1211
+ content: [{ type: "text", text: `Error: ${result.error}` }],
1212
+ isError: true,
1213
+ };
1214
+ }
1215
+ // Dynamically unregister the fav tool from this session
1216
+ const registered = registeredFavTools.get(tool_id);
1217
+ if (registered) {
1218
+ registered.remove();
1219
+ registeredFavTools.delete(tool_id);
1220
+ }
1113
1221
  return {
1114
- content: [{ type: "text", text: `Error: ${result.error}` }],
1115
- isError: true,
1222
+ content: [
1223
+ {
1224
+ type: "text",
1225
+ text: result.removed
1226
+ ? `Removed "fav:${tool_id}" from favorites and unregistered it.`
1227
+ : `"${tool_id}" was not in your favorites.`,
1228
+ },
1229
+ ],
1116
1230
  };
1117
1231
  }
1118
- // Dynamically unregister the fav tool from this session
1119
- const registered = registeredFavTools.get(tool_id);
1120
- if (registered) {
1121
- registered.remove();
1122
- registeredFavTools.delete(tool_id);
1232
+ catch (err) {
1233
+ return {
1234
+ content: [{ type: "text", text: `Error: ${err instanceof Error ? err.message : String(err)}` }],
1235
+ isError: true,
1236
+ };
1123
1237
  }
1124
- return {
1125
- content: [
1126
- {
1127
- type: "text",
1128
- text: result.removed
1129
- ? `Removed "fav:${tool_id}" from favorites and unregistered it.`
1130
- : `"${tool_id}" was not in your favorites.`,
1131
- },
1132
- ],
1133
- };
1134
1238
  });
1135
1239
  }
1136
1240
  // ─── Main ─────────────────────────────────────────────────────────────────────
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@caravo/mcp",
3
- "version": "0.1.24",
3
+ "version": "0.1.26",
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": {