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.
- package/dist/index.js +90 -8
- 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
|
|
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
|
|
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.
|
|
3514
|
-
|
|
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.
|
|
3620
|
-
|
|
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.
|
|
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",
|