firecrawl-mcp 3.23.7 → 3.23.9

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 +29 -5
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -18,8 +18,10 @@ var MAX_PASSAGE_CHARS = 1200;
18
18
  function fmtDeveloper(results) {
19
19
  if (!results || results.length === 0) return "(no results)";
20
20
  return results.map((r) => {
21
- const kind = r.type ? ` (${r.type})` : "";
22
- const lines = [`## [${r.id ?? "?"}]${kind} ${r.title ?? "(untitled)"}`];
21
+ const id = r.id ?? "?";
22
+ const kind = r.id?.split(":", 1)[0];
23
+ const kindLabel = kind ? ` (${kind})` : "";
24
+ const lines = [`## [${id}]${kindLabel} ${r.title ?? "(untitled)"}`];
23
25
  if (r.url) lines.push(r.url);
24
26
  const body = (r.passages ?? []).map((p) => p.text ?? "").join("\n---\n").trim();
25
27
  lines.push(body ? body.slice(0, MAX_PASSAGE_CHARS) : "(no content)");
@@ -586,7 +588,7 @@ function fmtGithub(results) {
586
588
  if (!results || results.length === 0) return "(no results)";
587
589
  return results.map((r) => {
588
590
  const lines = [];
589
- if (r.resultType === "repo_readme") {
591
+ if (r.number == null && r.pageType == null) {
590
592
  lines.push(`[${r.repo ?? "?"}] README`);
591
593
  } else {
592
594
  const ref = r.number != null ? `#${r.number}` : "";
@@ -1088,6 +1090,14 @@ async function authenticateRequest(request, profile) {
1088
1090
  if (process.env.CLOUD_SERVICE === "true") {
1089
1091
  if (!headerCred && !managedCred) {
1090
1092
  if (resolved?.invalid) {
1093
+ if (profile.allowKeyless) {
1094
+ return {
1095
+ authType: "api-key",
1096
+ credentialError: "CREDENTIAL_INVALID",
1097
+ firecrawlApiKey: void 0,
1098
+ keylessClientIp: extractClientIp(request)
1099
+ };
1100
+ }
1091
1101
  throw new InvalidFirecrawlCredentialError();
1092
1102
  }
1093
1103
  if (profile.allowKeyless) {
@@ -1477,7 +1487,12 @@ function recoveryPayload(code, requestId = randomUUID(), options = {}) {
1477
1487
  request_id: requestId,
1478
1488
  auth_mode: code === "CREDENTIAL_INVALID" ? "credential_error" : "keyless",
1479
1489
  message: code === "CREDENTIAL_INVALID" ? `The supplied Firecrawl credential is invalid or revoked. ${HUMAN_CONNECTION_GUIDANCE}` : isQuotaExhausted ? `The free daily limit for this network has been reached${retryAfterSeconds ? `; try again in about ${retryAfterSeconds} seconds` : ""}. To continue now: ${HUMAN_CONNECTION_GUIDANCE}` : isToolUnavailable ? ACCOUNT_ONLY_TOOL_GUIDANCE : isKeylessAccessUnavailable ? `Anonymous keyless access is unavailable for this request. To continue: ${HUMAN_CONNECTION_GUIDANCE}` : isKeylessEligibilityUnavailable ? "The anonymous keyless eligibility check is temporarily unavailable. Retry shortly." : `This tool requires a Firecrawl account or API key. ${HUMAN_CONNECTION_GUIDANCE}`,
1480
- ...isKeylessAccessUnavailable ? {} : { available_tools: [...KEYLESS_TOOL_NAMES] },
1490
+ // CREDENTIAL_INVALID sessions gate every tool call (including keyless
1491
+ // tools) on the credentialError check before the keyless branch ever
1492
+ // runs, so none of KEYLESS_TOOL_NAMES are actually callable here. Listing
1493
+ // them as available_tools would send the agent into a retry loop against
1494
+ // tools that will just return this same recovery payload.
1495
+ ...isKeylessAccessUnavailable || code === "CREDENTIAL_INVALID" ? {} : { available_tools: [...KEYLESS_TOOL_NAMES] },
1481
1496
  docs_url: MCP_CONNECTION_GUIDE_URL,
1482
1497
  ...retryAfterSeconds ? { retry_after_seconds: retryAfterSeconds } : {},
1483
1498
  next_actions: isKeylessEligibilityUnavailable ? [{ kind: "retry_later", after_seconds: 30 }] : isQuotaExhausted || isKeylessAccessUnavailable ? HUMAN_CONNECTION_ACTIONS : isToolUnavailable ? [
@@ -1549,7 +1564,16 @@ function guardHostedTool(tool, { logActions }) {
1549
1564
  const beforeValidate = tool.beforeValidate;
1550
1565
  return {
1551
1566
  ...tool,
1552
- canList: (session) => !session?.credentialError && (!isHostedKeylessSession(session) || keylessTool) && (canList?.(session) ?? true),
1567
+ canList: (session) => (
1568
+ // A credentialError session lists the keyless tool surface (same as a
1569
+ // real keyless session, not the full authenticated schema) so the
1570
+ // client proceeds past tools/list and calling any listed tool returns
1571
+ // the CREDENTIAL_INVALID recovery payload (below). An empty list would
1572
+ // leave MCP clients that stop after tools/list unable to ever surface
1573
+ // the recovery guidance; the full non-keyless schema would over-disclose
1574
+ // to a request carrying an unrecognized or invalid credential.
1575
+ (session?.credentialError || isHostedKeylessSession(session) ? keylessTool : true) && (canList?.(session) ?? true)
1576
+ ),
1553
1577
  beforeValidate: async (args2, session) => {
1554
1578
  const code = session?.credentialError ? "CREDENTIAL_INVALID" : isHostedKeylessSession(session) && !keylessTool ? "KEYLESS_TOOL_NOT_AVAILABLE" : void 0;
1555
1579
  if (code) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "firecrawl-mcp",
3
- "version": "3.23.7",
3
+ "version": "3.23.9",
4
4
  "description": "MCP server for Firecrawl — search, scrape, and interact with the web. Supports both cloud and self-hosted instances. Features include web search, scraping, page interaction, batch processing, and LLM-powered content analysis.",
5
5
  "type": "module",
6
6
  "mcpName": "io.github.firecrawl/firecrawl-mcp-server",