Skip to content

Commit e895e94

Browse files
fix(search): relay API error messages for every search, not only Alexandria ones (#546)
* fix(search): relay API error messages for every search firecrawl_search only sent errors through relayExchangeError when the sources included alexandria. Searches with explicit non-Alexandria sources (e.g. ["web"]) went through relayTermsRequired, which rethrows the raw axios error, so a 402 surfaced as "Request failed with status code 402" and dropped the API's message and upgrade link. Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * chore: release 3.30.2 Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> --------- Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
1 parent d705793 commit e895e94

9 files changed

Lines changed: 25 additions & 15 deletions

File tree

‎CHANGELOG.md‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
# Changelog
22

3+
## [3.30.2] - 2026-10-15
4+
5+
### Fixed
6+
7+
- `firecrawl_search` relays the API's error message for every search. Searches with explicit sources that excluded `alexandria` (for example `["web"]`) previously surfaced only `Request failed with status code 402`, dropping the out-of-credits message and upgrade link. The fallback for an error without a message now reads `Firecrawl request failed (HTTP N)`.
8+
39
## [3.30.1] - 2026-10-14
410

511
### Added

‎gemini-extension.json‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "firecrawl",
3-
"version": "3.30.1",
3+
"version": "3.30.2",
44
"description": "Official Firecrawl MCP for web search, scraping, crawling, and structured data extraction.",
55
"mcpServers": {
66
"firecrawl": {

‎package.json‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "firecrawl-mcp",
3-
"version": "3.30.1",
3+
"version": "3.30.2",
44
"description": "MCP server for Firecrawl — search, scrape, and interact with the web, and search scientific papers. Supports both cloud and self-hosted instances. Features include web search, scraping, page interaction, batch processing, LLM-powered content analysis, and research paper search over biomedical and arXiv literature (PubMed, bioRxiv, medRxiv, arXiv) with citation-graph expansion and full-text reading.",
55
"type": "module",
66
"mcpName": "io.github.firecrawl/firecrawl-mcp-server",

‎server.json‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
"name": "io.github.firecrawl/firecrawl-mcp-server",
44
"title": "Firecrawl MCP Server",
55
"description": "MCP server for Firecrawl — web search, scraping, and biomedical/arXiv paper search.",
6-
"version": "3.30.1",
6+
"version": "3.30.2",
77
"repository": {
88
"url": "https://github.com/firecrawl/firecrawl-mcp-server.git",
99
"source": "github"
@@ -12,7 +12,7 @@
1212
{
1313
"registryType": "npm",
1414
"identifier": "firecrawl-mcp",
15-
"version": "3.30.1",
15+
"version": "3.30.2",
1616
"transport": {
1717
"type": "stdio"
1818
},

‎src/server.ts‎

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1632,10 +1632,7 @@ For a programming question, add \`categories: ["developer"]\`; its hits return i
16321632
searchBody,
16331633
opts.sources === undefined && opts.domainTools !== true
16341634
);
1635-
const context = { tool: 'firecrawl_search' };
1636-
const httpRes = exchangeSource
1637-
? await relayExchangeError(postSearch, context)
1638-
: await relayTermsRequired(postSearch, context);
1635+
const httpRes = await relayExchangeError(postSearch, { tool: 'firecrawl_search' });
16391636
return structuredCompact(withoutDiscoveredToolPrices(httpRes?.data ?? {}));
16401637
},
16411638
});

‎src/service/search-surface.ts‎

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ import {
1010
normalizeSearchSources,
1111
postSearchWithFallback,
1212
relayExchangeError,
13-
relayTermsRequired,
1413
removeEmptyTopLevel,
1514
requestOrigin,
1615
SEARCH_DOMAINS_CONFLICT_MESSAGE,
@@ -158,10 +157,7 @@ Returns result groups in \`data\` and an operation \`id\`.
158157
searchBody,
159158
sources === undefined && domainTools !== true
160159
);
161-
const context = { tool: 'firecrawl_search' };
162-
const httpRes = exchangeSource
163-
? await relayExchangeError(postSearch, context)
164-
: await relayTermsRequired(postSearch, context);
160+
const httpRes = await relayExchangeError(postSearch, { tool: 'firecrawl_search' });
165161
return structuredCompact(httpRes?.data ?? {});
166162
},
167163
});

‎src/tool-helpers.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -261,7 +261,7 @@ export async function relayExchangeError(
261261
const message =
262262
typeof data?.error === 'string'
263263
? data.error
264-
: `Alexandria request failed (HTTP ${response.status})`;
264+
: `Firecrawl request failed (HTTP ${response.status})`;
265265
const disabledProvider = response.status === 403
266266
? context.providers?.find(provider => message === `Access to ${provider} is disabled for this organization.`)
267267
: undefined;

‎tests/helpers/exchange-api.mjs‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ async function startFakeExchangeApi(options = {}) {
6666
if (req.method === 'POST' && (!parsedBody || typeof parsedBody !== 'object' || Array.isArray(parsedBody))) return json(400, {error:'Expected a JSON object body'});
6767

6868
if (options.largeResult && (url.pathname === '/v2/search' || url.pathname.startsWith('/exchange/discover'))) return json(200, options.largeResult);
69-
if (options.apiStatus) return json(options.apiStatus, { success: false, error: 'Invalid API key' });
69+
if (options.apiStatus) return json(options.apiStatus, { success: false, error: options.apiError ?? 'Invalid API key' });
7070

7171
if (url.pathname === '/exchange/skills/resolve')
7272
return json(200, { skills: [{ id: 'particle-podcasts' }] });

‎tests/mcp-alexandria-search.test.mjs‎

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,17 @@ test('default tools fall back only on discovery refusal; explicit tools and othe
5151
assert.equal(other.api.requests.length, 1);
5252
});
5353

54+
test('search relays the API error message whatever sources are requested', async (t) => {
55+
const apiError = 'Insufficient credits to perform this request. For more credits, you can upgrade your plan at https://firecrawl.dev/pricing.';
56+
const { client } = await startStdioWithApi(t, { apiStatus: 402, apiError });
57+
for (const overrides of [{}, { sources: ['web'] }, { sources: ['news'] }, { sources: ['alexandria'] }]) {
58+
const result = await callExpectingError(client, {
59+
name: 'firecrawl_search', arguments: { query: 'company news', ...overrides },
60+
});
61+
assert.match(result.content[0].text, /upgrade your plan at https:\/\/firecrawl\.dev\/pricing/, JSON.stringify(overrides));
62+
}
63+
});
64+
5465
test('toolDetail forwards valid values and rejects invalid values before API calls', async (t) => {
5566
const { api, client } = await startStdioWithApi(t);
5667
for (const [name, args] of [

0 commit comments

Comments
 (0)