firecrawl-mcp 3.21.1 → 3.21.3

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 +90 -8
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -1347,7 +1347,7 @@ async function monitorRequest(session, path2, init = {}) {
1347
1347
  const s = qs.toString();
1348
1348
  if (s) url += `?${s}`;
1349
1349
  }
1350
- const headers = { "X-Origin": "mcp" };
1350
+ const headers = { "X-Origin": "mcp-fastmcp" };
1351
1351
  if (apiKey) headers.Authorization = `Bearer ${apiKey}`;
1352
1352
  if (init.body !== void 0) headers["Content-Type"] = "application/json";
1353
1353
  const response = await fetch(url, {
@@ -2333,6 +2333,7 @@ function createClient(apiKey) {
2333
2333
  return new FirecrawlApp(config);
2334
2334
  }
2335
2335
  var ORIGIN = "mcp-fastmcp";
2336
+ var ORIGIN_HEADERS2 = { "X-Origin": ORIGIN };
2336
2337
  var SAFE_MODE = process.env.CLOUD_SERVICE === "true";
2337
2338
  function getClient(session) {
2338
2339
  if (process.env.CLOUD_SERVICE === "true") {
@@ -2837,6 +2838,7 @@ async function keylessEligible(clientIp) {
2837
2838
  `${resolveApiBaseUrl()}/v2/keyless/eligibility`,
2838
2839
  {
2839
2840
  headers: {
2841
+ ...ORIGIN_HEADERS2,
2840
2842
  "x-firecrawl-keyless-ip": clientIp,
2841
2843
  "x-firecrawl-keyless-secret": secret
2842
2844
  }
@@ -2858,6 +2860,7 @@ function isKeylessMode(session) {
2858
2860
  }
2859
2861
  async function keylessPost(path2, body, session) {
2860
2862
  const headers = {
2863
+ ...ORIGIN_HEADERS2,
2861
2864
  "Content-Type": "application/json"
2862
2865
  };
2863
2866
  if (session?.keylessClientIp && process.env.KEYLESS_PROXY_SECRET) {
@@ -2877,6 +2880,61 @@ async function keylessPost(path2, body, session) {
2877
2880
  }
2878
2881
  return json;
2879
2882
  }
2883
+ async function getCrawlStatusWithOrigin(client, jobId) {
2884
+ const res = await client.http.get(
2885
+ `/v2/crawl/${encodeURIComponent(jobId)}`,
2886
+ ORIGIN_HEADERS2
2887
+ );
2888
+ const body = res?.data ?? {};
2889
+ const initialDocs = Array.isArray(body.data) ? body.data : [];
2890
+ if (!body.next) {
2891
+ return {
2892
+ id: jobId,
2893
+ status: body.status,
2894
+ completed: body.completed ?? 0,
2895
+ total: body.total ?? 0,
2896
+ creditsUsed: body.creditsUsed,
2897
+ expiresAt: body.expiresAt,
2898
+ next: body.next ?? null,
2899
+ data: initialDocs
2900
+ };
2901
+ }
2902
+ const docs = initialDocs.slice();
2903
+ let current = body.next;
2904
+ while (current) {
2905
+ const pageRes = await client.http.get(current, ORIGIN_HEADERS2);
2906
+ const payload = pageRes?.data ?? {};
2907
+ if (!payload.success) break;
2908
+ const pageData = Array.isArray(payload.data) ? payload.data : payload.data?.pages || [];
2909
+ docs.push(...pageData);
2910
+ current = payload.next ?? (Array.isArray(payload.data) ? null : payload.data?.next) ?? null;
2911
+ }
2912
+ return {
2913
+ id: jobId,
2914
+ status: body.status,
2915
+ completed: body.completed ?? 0,
2916
+ total: body.total ?? 0,
2917
+ creditsUsed: body.creditsUsed,
2918
+ expiresAt: body.expiresAt,
2919
+ next: null,
2920
+ data: docs
2921
+ };
2922
+ }
2923
+ async function waitForCrawlCompletionWithOrigin(client, jobId, pollInterval = 2, timeout) {
2924
+ const startedAt = Date.now();
2925
+ while (true) {
2926
+ const status = await getCrawlStatusWithOrigin(client, jobId);
2927
+ if (["completed", "failed", "cancelled"].includes(String(status.status ?? ""))) {
2928
+ return status;
2929
+ }
2930
+ if (timeout != null && Date.now() - startedAt > timeout * 1e3) {
2931
+ throw new Error(`Crawl job ${jobId} did not complete within ${timeout}s`);
2932
+ }
2933
+ await new Promise(
2934
+ (resolve) => setTimeout(resolve, Math.max(1e3, pollInterval * 1e3))
2935
+ );
2936
+ }
2937
+ }
2880
2938
  var feedbackIssueSchema = z4.string().trim().min(1).max(80).regex(
2881
2939
  /^[a-z0-9][a-z0-9_-]*$/,
2882
2940
  "Issue codes must use lowercase letters, numbers, underscores, or hyphens"
@@ -3024,6 +3082,7 @@ Pass the \`searchId\` returned by \`firecrawl_search\` (the \`id\` field on the
3024
3082
  }
3025
3083
  if (querySuggestions) body.querySuggestions = querySuggestions;
3026
3084
  const headers = {
3085
+ ...ORIGIN_HEADERS2,
3027
3086
  "Content-Type": "application/json"
3028
3087
  };
3029
3088
  const apiKey = session?.firecrawlApiKey;
@@ -3127,6 +3186,7 @@ Do not store multi-MB outputs in feedback. Use concise notes, issue codes, URLs,
3127
3186
  } = args2;
3128
3187
  const apiBase = resolveApiBaseUrl();
3129
3188
  const headers = {
3189
+ ...ORIGIN_HEADERS2,
3130
3190
  "Content-Type": "application/json"
3131
3191
  };
3132
3192
  const apiKey = session?.firecrawlApiKey;
@@ -3250,11 +3310,26 @@ server.addTool({
3250
3310
  if (webhook) opts.webhook = webhook;
3251
3311
  delete opts.webhookHeaders;
3252
3312
  const cleaned = removeEmptyTopLevel(opts);
3313
+ const pollInterval = typeof cleaned.pollInterval === "number" ? cleaned.pollInterval : 2;
3314
+ const timeout = typeof cleaned.timeout === "number" ? cleaned.timeout : void 0;
3315
+ delete cleaned.pollInterval;
3316
+ delete cleaned.timeout;
3253
3317
  log.info("Starting crawl", { url: String(url) });
3254
- const res = await client.crawl(String(url), {
3318
+ const started = await client.http.post("/v2/crawl", {
3319
+ url: String(url),
3255
3320
  ...cleaned,
3256
3321
  origin: ORIGIN
3257
3322
  });
3323
+ const crawlId = started?.data?.id;
3324
+ if (!crawlId) {
3325
+ return asText2(started?.data ?? {});
3326
+ }
3327
+ const res = await waitForCrawlCompletionWithOrigin(
3328
+ client,
3329
+ crawlId,
3330
+ pollInterval,
3331
+ timeout
3332
+ );
3258
3333
  return asText2(res);
3259
3334
  }
3260
3335
  });
@@ -3286,7 +3361,8 @@ Check the status of a crawl job.
3286
3361
  parameters: z4.object({ id: z4.string() }),
3287
3362
  execute: async (args2, { session }) => {
3288
3363
  const client = getClient(session);
3289
- const res = await client.getCrawlStatus(args2.id);
3364
+ const id = args2.id;
3365
+ const res = await getCrawlStatusWithOrigin(client, id);
3290
3366
  return asText2(res);
3291
3367
  }
3292
3368
  });
@@ -3510,8 +3586,11 @@ Check the status of an agent job and retrieve results when complete. Use this to
3510
3586
  const client = getClient(session);
3511
3587
  const { id } = args2;
3512
3588
  log.info("Checking agent status", { id });
3513
- const res = await client.getAgentStatus(id);
3514
- return asText2(res);
3589
+ const res = await client.http.get(
3590
+ `/v2/agent/${encodeURIComponent(id)}`,
3591
+ ORIGIN_HEADERS2
3592
+ );
3593
+ return asText2(res?.data ?? {});
3515
3594
  }
3516
3595
  });
3517
3596
  server.addTool({
@@ -3616,8 +3695,11 @@ Stop an interact session for a scraped page. Call this when you are done interac
3616
3695
  const client = getClient(session);
3617
3696
  const { scrapeId } = args2;
3618
3697
  log.info("Stopping interact session", { scrapeId });
3619
- const res = await client.stopInteraction(scrapeId);
3620
- return asText2(res);
3698
+ const res = await client.http.delete(
3699
+ `/v2/scrape/${encodeURIComponent(scrapeId)}/interact`,
3700
+ ORIGIN_HEADERS2
3701
+ );
3702
+ return asText2(res?.data ?? {});
3621
3703
  }
3622
3704
  });
3623
3705
  if (process.env.CLOUD_SERVICE !== "true") {
@@ -3789,7 +3871,7 @@ Add \`"parsers": ["pdf"]\` (optionally with \`pdfOptions.maxPages\`) when parsin
3789
3871
  });
3790
3872
  form.append("file", blob, filename);
3791
3873
  form.append("options", JSON.stringify(optionsPayload));
3792
- const headers = {};
3874
+ const headers = { ...ORIGIN_HEADERS2 };
3793
3875
  const apiKey = session?.firecrawlApiKey;
3794
3876
  if (apiKey) {
3795
3877
  headers["Authorization"] = `Bearer ${apiKey}`;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "firecrawl-mcp",
3
- "version": "3.21.1",
3
+ "version": "3.21.3",
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",