firecrawl-mcp 3.27.2 → 3.28.0

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 (3) hide show
  1. package/README.md +47 -20
  2. package/dist/index.js +421 -292
  3. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -76,8 +76,7 @@ var alexandriaSessionFeedbackSchema = z.strictObject({
76
76
  rating: z.enum(["good", "bad", "partial"]),
77
77
  ...alexandriaFeedbackFields,
78
78
  requestedWebsite: alexandriaFeedbackFields.requestedWebsite.unwrap(),
79
- rationale: detail,
80
- objective: detail
79
+ rationale: detail
81
80
  });
82
81
 
83
82
  // src/index.ts
@@ -2074,7 +2073,7 @@ import { readFile } from "fs/promises";
2074
2073
  import { createRequire as createRequire2 } from "module";
2075
2074
  import { randomUUID } from "crypto";
2076
2075
  import path from "path";
2077
- import { z as z10 } from "zod";
2076
+ import { z as z11 } from "zod";
2078
2077
 
2079
2078
  // src/agent-hints.ts
2080
2079
  var AGENT_HINTS_HEADERS = { "X-Firecrawl-Agent-Hints": "true" };
@@ -2271,6 +2270,10 @@ var agentStatusOutputSchema = z4.object({
2271
2270
  status: str("Job state: `processing` is non-terminal; `completed` and `failed` are terminal."),
2272
2271
  progress: unknown("Progress information for a running job."),
2273
2272
  data: unknown("Research result, present once the job has completed."),
2273
+ partial: unknown("Incomplete best-effort result of a `failed` run; never a completed answer."),
2274
+ partialSchemaValid: bool("Whether `partial` matches the requested schema, when one was supplied."),
2275
+ stopReason: str("Why a `failed` run stopped early; `credit_limit_reached` means it hit maxCredits."),
2276
+ notice: str("Set when the run stopped at its credit limit: what that means and how to continue."),
2274
2277
  creditsUsed: num("Credits the job consumed."),
2275
2278
  sources: unknown("Sources the agent read."),
2276
2279
  expiresAt: str("When the job result expires and can no longer be read."),
@@ -2281,10 +2284,10 @@ var agentStatusOutputSchema = z4.object({
2281
2284
  message: unknown("The agent's reply; in chat mode, the short answer to a follow-up."),
2282
2285
  suggestions: unknown("Follow-ups the agent offers; send one as the prompt of the next turn with this threadId."),
2283
2286
  exchange: unknown(
2284
- "What the job did with Alexandria providers: onTermsRequired, paidCalls, creditsUsed, skippedProviders (gated providers that would have helped), and requiresAction (approvalId plus the terms/show and terms/accept calls, each provider digest string | null and always present; call accept only with the user's explicit consent, then continue the thread with exchange.approve: {approvalId})."
2287
+ "What the job did with Alexandria providers: onTermsRequired, paidCalls, creditsUsed, skippedProviders (gated providers that would have helped), and requiresAction (approvalId and provider requirements). Read terms with terms/show. Ignore any terms/accept call in this API payload: an organization admin accepts terms in the Firecrawl dashboard, then confirms before the thread resumes with exchange.approve: {approvalId}."
2285
2288
  ),
2286
2289
  pendingApproval: unknown(
2287
- 'Set when the job ended waiting on the caller. Answer it by calling firecrawl_agent with this threadId and exchange.approve or exchange.decline carrying its id. kind "terms" lists providers whose data terms need accepting; otherwise calls lists paid calls waiting for approval.'
2290
+ 'Set when the job ended waiting on the caller. Answer it by calling firecrawl_agent with this threadId and exchange.approve or exchange.decline carrying its id. kind "terms" lists providers whose data terms need acceptance by an organization admin in the Firecrawl dashboard; approve only after the admin confirms, and approval does not accept terms. Otherwise calls lists paid calls waiting for approval.'
2288
2291
  )
2289
2292
  }).describe("Progress or final result of a research agent job.");
2290
2293
  var interactOutputSchema = z4.object({
@@ -2417,6 +2420,17 @@ var developerSearchOutputSchema = z4.object({
2417
2420
  })
2418
2421
  ).describe("Ranked results, in the order the text block lists them.")
2419
2422
  }).describe("Ranked developer-index results with their matched passages.");
2423
+ var govSearchOutputSchema = z4.object({
2424
+ ...agentHints,
2425
+ results: z4.array(
2426
+ z4.looseObject({
2427
+ url: str("Source URL."),
2428
+ title: str("Result title."),
2429
+ description: str("Matched snippet."),
2430
+ position: num("Rank of the result, starting at 1.")
2431
+ })
2432
+ ).describe("Ranked results, in the order the text block lists them.")
2433
+ }).describe("Ranked Government Index results with their matched snippets.");
2420
2434
  var creditUsageOutputSchema = z4.object({
2421
2435
  // Current view.
2422
2436
  remainingCredits: num("Credits left in the current billing period; can exceed `planCredits` when extra credits were purchased or granted."),
@@ -2661,6 +2675,59 @@ Returns ranked results with an ID, source type, URL, title, and the matched pass
2661
2675
  });
2662
2676
  }
2663
2677
 
2678
+ // src/gov.ts
2679
+ import { z as z7 } from "zod";
2680
+ var BASE2 = "/v2/search/gov";
2681
+ function fmtGov(results) {
2682
+ if (!results || results.length === 0) return "(no results)";
2683
+ return results.map((r, i) => {
2684
+ const lines = [`## ${r.position ?? i + 1}. ${r.title ?? "(untitled)"}`];
2685
+ if (r.url) lines.push(r.url);
2686
+ if (r.description) lines.push(r.description);
2687
+ return lines.join("\n");
2688
+ }).join("\n\n");
2689
+ }
2690
+ function registerGovTools(server2, getClient2) {
2691
+ server2.addTool({
2692
+ name: "firecrawl_gov_search",
2693
+ annotations: {
2694
+ title: "Firecrawl Government Index search",
2695
+ readOnlyHint: true,
2696
+ openWorldHint: true,
2697
+ destructiveHint: false
2698
+ },
2699
+ description: `
2700
+ Search an index of primary law and regulatory material from US federal, state, and local government sources (statutes, regulations, codes, court opinions, and other government publications) for legal or regulatory questions that need the governing text or an official source.
2701
+
2702
+ Returns ranked results with a position, title, URL, and matched snippet.
2703
+ `,
2704
+ outputSchema: govSearchOutputSchema,
2705
+ parameters: z7.object({
2706
+ query: z7.string().min(1).describe(
2707
+ "Natural-language legal or regulatory question or search phrase, including the jurisdiction, agency, or citation when relevant."
2708
+ ),
2709
+ k: z7.number().int().min(1).max(100).optional().describe("Number of ranked results to return (default 10).")
2710
+ }),
2711
+ execute: async (args2, { session, client: mcpClient }) => {
2712
+ const { query, k } = args2;
2713
+ const params = new URLSearchParams();
2714
+ params.append("query", query);
2715
+ if (k != null) params.append("k", String(k));
2716
+ const client = getClient2(session);
2717
+ const res = await client.http.get(
2718
+ `${BASE2}?${params.toString()}`,
2719
+ originHeaders(requestOrigin(mcpClient, session))
2720
+ );
2721
+ const results = res.data?.data?.web ?? [];
2722
+ return withAgentHints(
2723
+ withStructured(fmtGov(results), { results }),
2724
+ res.data,
2725
+ true
2726
+ );
2727
+ }
2728
+ });
2729
+ }
2730
+
2664
2731
  // src/keyless-client-ip.ts
2665
2732
  import net from "net";
2666
2733
  function extractSingleTrustedClientIp(rawForwardedFor) {
@@ -2708,7 +2775,7 @@ function checkKeylessSignupUrl(value) {
2708
2775
  }
2709
2776
 
2710
2777
  // src/monitor.ts
2711
- import { z as z7 } from "zod";
2778
+ import { z as z8 } from "zod";
2712
2779
 
2713
2780
  // src/session-credential.ts
2714
2781
  import { createHmac } from "crypto";
@@ -2842,8 +2909,8 @@ async function monitorRequest(session, origin, path2, init = {}) {
2842
2909
  function monitorResult(data) {
2843
2910
  return withAgentHints(structuredText(data), data);
2844
2911
  }
2845
- var pageStatusSchema = z7.enum(["same", "new", "changed", "removed", "error"]);
2846
- var checkStatusSchema = z7.enum([
2912
+ var pageStatusSchema = z8.enum(["same", "new", "changed", "removed", "error"]);
2913
+ var checkStatusSchema = z8.enum([
2847
2914
  "queued",
2848
2915
  "running",
2849
2916
  "completed",
@@ -2937,22 +3004,22 @@ Create a recurring scrape, crawl, or search monitor that compares each check wit
2937
3004
  In the simple form, a \`goal\` is required. If \`queries\` contains one or more non-empty values and is supplied with \`page\`/\`pages\`, \`queries\` create the search target and page targets are ignored. A monitor schedules future network checks and can send configured email or webhook notifications. Returns the created monitor.
2938
3005
  `,
2939
3006
  outputSchema: monitorOutputSchema,
2940
- parameters: z7.object({
2941
- body: z7.record(z7.string(), z7.any()).optional(),
2942
- page: z7.string().optional(),
2943
- pages: z7.array(z7.string()).optional(),
2944
- queries: z7.array(z7.string()).optional(),
2945
- searchWindow: z7.enum(["5m", "15m", "1h", "6h", "24h", "7d"]).optional(),
2946
- maxResults: z7.number().int().min(1).max(50).optional(),
2947
- includeDomains: z7.array(z7.string()).optional(),
2948
- excludeDomains: z7.array(z7.string()).optional(),
2949
- goal: z7.string().optional(),
2950
- name: z7.string().optional(),
2951
- scheduleText: z7.string().optional(),
2952
- timezone: z7.string().optional(),
2953
- email: z7.string().optional(),
2954
- includeDiffs: z7.boolean().optional(),
2955
- webhookUrl: z7.string().optional()
3007
+ parameters: z8.object({
3008
+ body: z8.record(z8.string(), z8.any()).optional(),
3009
+ page: z8.string().optional(),
3010
+ pages: z8.array(z8.string()).optional(),
3011
+ queries: z8.array(z8.string()).optional(),
3012
+ searchWindow: z8.enum(["5m", "15m", "1h", "6h", "24h", "7d"]).optional(),
3013
+ maxResults: z8.number().int().min(1).max(50).optional(),
3014
+ includeDomains: z8.array(z8.string()).optional(),
3015
+ excludeDomains: z8.array(z8.string()).optional(),
3016
+ goal: z8.string().optional(),
3017
+ name: z8.string().optional(),
3018
+ scheduleText: z8.string().optional(),
3019
+ timezone: z8.string().optional(),
3020
+ email: z8.string().optional(),
3021
+ includeDiffs: z8.boolean().optional(),
3022
+ webhookUrl: z8.string().optional()
2956
3023
  }),
2957
3024
  execute: async (args2, { session, log, client: mcpClient }) => {
2958
3025
  const body = buildMonitorCreateBody(args2);
@@ -2984,9 +3051,9 @@ In the simple form, a \`goal\` is required. If \`queries\` contains one or more
2984
3051
  List monitors for the authenticated account with optional pagination controls. Returns one page of monitor records and pagination metadata.
2985
3052
  `,
2986
3053
  outputSchema: monitorListOutputSchema,
2987
- parameters: z7.object({
2988
- limit: z7.number().int().positive().optional(),
2989
- offset: z7.number().int().nonnegative().optional()
3054
+ parameters: z8.object({
3055
+ limit: z8.number().int().positive().optional(),
3056
+ offset: z8.number().int().nonnegative().optional()
2990
3057
  }),
2991
3058
  execute: async (args2, { session, client: mcpClient }) => {
2992
3059
  const { limit, offset } = args2;
@@ -3016,7 +3083,7 @@ List monitors for the authenticated account with optional pagination controls. R
3016
3083
  Retrieve one monitor by ID, including its configuration and current state. This does not run or modify the monitor.
3017
3084
  `,
3018
3085
  outputSchema: monitorOutputSchema,
3019
- parameters: z7.object({ id: z7.string() }),
3086
+ parameters: z8.object({ id: z8.string() }),
3020
3087
  execute: async (args2, { session, client: mcpClient }) => {
3021
3088
  const { id } = args2;
3022
3089
  const res = await monitorRequest(
@@ -3044,9 +3111,9 @@ Patch an existing monitor by ID. The body can change its name, active/paused sta
3044
3111
  Returns the updated monitor.
3045
3112
  `,
3046
3113
  outputSchema: monitorOutputSchema,
3047
- parameters: z7.object({
3048
- id: z7.string(),
3049
- body: z7.record(z7.string(), z7.any())
3114
+ parameters: z8.object({
3115
+ id: z8.string(),
3116
+ body: z8.record(z8.string(), z8.any())
3050
3117
  }),
3051
3118
  execute: async (args2, { session, client: mcpClient }) => {
3052
3119
  const { id, body } = args2;
@@ -3074,7 +3141,7 @@ Returns the updated monitor.
3074
3141
  Permanently delete a monitor by ID and stop its future schedule. This operation cannot be undone and returns deletion status.
3075
3142
  `,
3076
3143
  outputSchema: monitorDeleteOutputSchema,
3077
- parameters: z7.object({ id: z7.string() }),
3144
+ parameters: z8.object({ id: z8.string() }),
3078
3145
  execute: async (args2, { session, log, client: mcpClient }) => {
3079
3146
  const { id } = args2;
3080
3147
  log.info("Deleting monitor", { id });
@@ -3102,7 +3169,7 @@ Permanently delete a monitor by ID and stop its future schedule. This operation
3102
3169
  Queue an immediate check for a monitor outside its normal schedule. This starts network work for the monitor's configured targets and returns the queued check.
3103
3170
  `,
3104
3171
  outputSchema: monitorRunOutputSchema,
3105
- parameters: z7.object({ id: z7.string() }),
3172
+ parameters: z8.object({ id: z8.string() }),
3106
3173
  execute: async (args2, { session, client: mcpClient }) => {
3107
3174
  const { id } = args2;
3108
3175
  const res = await monitorRequest(
@@ -3129,10 +3196,10 @@ Queue an immediate check for a monitor outside its normal schedule. This starts
3129
3196
  List historical checks for a monitor, optionally filtered by status and bounded by a result limit. Returns one page of check summaries and pagination metadata.
3130
3197
  `,
3131
3198
  outputSchema: monitorChecksOutputSchema,
3132
- parameters: z7.object({
3133
- id: z7.string(),
3134
- limit: z7.number().int().positive().optional(),
3135
- offset: z7.number().int().nonnegative().optional(),
3199
+ parameters: z8.object({
3200
+ id: z8.string(),
3201
+ limit: z8.number().int().positive().optional(),
3202
+ offset: z8.number().int().nonnegative().optional(),
3136
3203
  status: checkStatusSchema.optional()
3137
3204
  }),
3138
3205
  execute: async (args2, { session, client: mcpClient }) => {
@@ -3163,11 +3230,11 @@ Retrieve one monitor check and its page-level results, optionally filtered by pa
3163
3230
  Markdown tracking returns a unified text diff, JSON tracking returns field paths with previous/current values and a current snapshot, and mixed tracking returns both. Returns one page of results plus a \`next\` URL when more pages exist.
3164
3231
  `,
3165
3232
  outputSchema: monitorCheckOutputSchema,
3166
- parameters: z7.object({
3167
- id: z7.string(),
3168
- checkId: z7.string(),
3169
- limit: z7.number().int().positive().optional(),
3170
- skip: z7.number().int().nonnegative().optional(),
3233
+ parameters: z8.object({
3234
+ id: z8.string(),
3235
+ checkId: z8.string(),
3236
+ limit: z8.number().int().positive().optional(),
3237
+ skip: z8.number().int().nonnegative().optional(),
3171
3238
  pageStatus: pageStatusSchema.optional()
3172
3239
  }),
3173
3240
  execute: async (args2, { session, client: mcpClient }) => {
@@ -3184,8 +3251,8 @@ Markdown tracking returns a unified text diff, JSON tracking returns field paths
3184
3251
  }
3185
3252
 
3186
3253
  // src/research.ts
3187
- import { z as z8 } from "zod";
3188
- var BASE2 = "/v2/search/research";
3254
+ import { z as z9 } from "zod";
3255
+ var BASE3 = "/v2/search/research";
3189
3256
  function researchHeaders(origin) {
3190
3257
  return { ...originHeaders(origin), ...AGENT_HINTS_HEADERS };
3191
3258
  }
@@ -3303,21 +3370,21 @@ Several distinct framings of the same question surface different papers than a s
3303
3370
  Returns ranked papers with canonical IDs, titles, authors, and abstracts.
3304
3371
  `,
3305
3372
  outputSchema: researchSearchOutputSchema,
3306
- parameters: z8.object({
3307
- query: z8.string().min(1).describe(
3373
+ parameters: z9.object({
3374
+ query: z9.string().min(1).describe(
3308
3375
  "Natural-language research topic or question, including methods, systems, conditions, populations, interventions, or outcomes when relevant."
3309
3376
  ),
3310
- k: z8.number().int().min(1).max(500).optional().describe("Number of ranked papers to return (default 40)."),
3311
- authors: z8.array(z8.string()).optional().describe(
3377
+ k: z9.number().int().min(1).max(500).optional().describe("Number of ranked papers to return (default 40)."),
3378
+ authors: z9.array(z9.string()).optional().describe(
3312
3379
  "Author substring filter(s); ALL must match (case-insensitive)."
3313
3380
  ),
3314
- categories: z8.array(z8.string()).optional().describe(
3381
+ categories: z9.array(z9.string()).optional().describe(
3315
3382
  "Paper category filter(s) (e.g. `cs.LG`); ALL provided values must match."
3316
3383
  ),
3317
- from: z8.string().optional().describe(
3384
+ from: z9.string().optional().describe(
3318
3385
  "Inclusive lower bound on created/updated date (`YYYY-MM-DD`)."
3319
3386
  ),
3320
- to: z8.string().optional().describe(
3387
+ to: z9.string().optional().describe(
3321
3388
  "Inclusive upper bound on created/updated date (`YYYY-MM-DD`)."
3322
3389
  )
3323
3390
  }),
@@ -3332,7 +3399,7 @@ Returns ranked papers with canonical IDs, titles, authors, and abstracts.
3332
3399
  appendParam(params, "to", to);
3333
3400
  const client = getClient2(session);
3334
3401
  const res = await client.http.get(
3335
- withQuery(`${BASE2}/papers`, params),
3402
+ withQuery(`${BASE3}/papers`, params),
3336
3403
  researchHeaders(requestOrigin(mcpClient, session))
3337
3404
  );
3338
3405
  const results = res.data?.results ?? [];
@@ -3354,8 +3421,8 @@ Returns ranked papers with canonical IDs, titles, authors, and abstracts.
3354
3421
  Retrieve canonical metadata for one paper ID, such as an arXiv, PMC, PMID, or DOI identifier. Returns the title, abstract, authors, categories, source IDs, and dates as markdown.
3355
3422
  `,
3356
3423
  outputSchema: researchPaperOutputSchema,
3357
- parameters: z8.object({
3358
- paperId: z8.string().min(1).describe(
3424
+ parameters: z9.object({
3425
+ paperId: z9.string().min(1).describe(
3359
3426
  "Canonical paperId or primaryId such as `arxiv:1706.03762`, `pmcid:PMC12530322`, `pmid:40953549`, or `doi:10.1016/j.neunet.2025.108095`."
3360
3427
  )
3361
3428
  }),
@@ -3363,7 +3430,7 @@ Retrieve canonical metadata for one paper ID, such as an arXiv, PMC, PMID, or DO
3363
3430
  const { paperId } = args2;
3364
3431
  const client = getClient2(session);
3365
3432
  const res = await client.http.get(
3366
- `${BASE2}/papers/${encodeURIComponent(paperId)}`,
3433
+ `${BASE3}/papers/${encodeURIComponent(paperId)}`,
3367
3434
  researchHeaders(requestOrigin(mcpClient, session))
3368
3435
  );
3369
3436
  const paper = res.data?.paper;
@@ -3390,12 +3457,12 @@ Find citation-graph candidates from one to ten \`seed_ids\`; the first ID is the
3390
3457
  Returns ranked candidates and the evaluated pool size.
3391
3458
  `,
3392
3459
  outputSchema: researchRelatedOutputSchema,
3393
- parameters: z8.object({
3394
- seed_ids: z8.array(z8.string()).min(1).max(10),
3395
- intent: z8.string().min(1),
3396
- mode: z8.enum(["similar", "citers", "references"]).optional(),
3397
- k: z8.number().int().min(1).max(500).optional(),
3398
- rerank: z8.boolean().optional().describe("Apply an additional rerank over the fused candidates.")
3460
+ parameters: z9.object({
3461
+ seed_ids: z9.array(z9.string()).min(1).max(10),
3462
+ intent: z9.string().min(1),
3463
+ mode: z9.enum(["similar", "citers", "references"]).optional(),
3464
+ k: z9.number().int().min(1).max(500).optional(),
3465
+ rerank: z9.boolean().optional().describe("Apply an additional rerank over the fused candidates.")
3399
3466
  }),
3400
3467
  execute: async (args2, { session, client: mcpClient }) => {
3401
3468
  const { seed_ids, intent, mode, k, rerank } = args2;
@@ -3409,7 +3476,7 @@ Returns ranked candidates and the evaluated pool size.
3409
3476
  const client = getClient2(session);
3410
3477
  const res = await client.http.get(
3411
3478
  withQuery(
3412
- `${BASE2}/papers/${encodeURIComponent(primary)}/similar`,
3479
+ `${BASE3}/papers/${encodeURIComponent(primary)}/similar`,
3413
3480
  params
3414
3481
  ),
3415
3482
  researchHeaders(requestOrigin(mcpClient, session))
@@ -3442,12 +3509,12 @@ Retrieve in-body passages from one paper that are relevant to a specific questio
3442
3509
  Returns matching passages or a notice when full text is unavailable.
3443
3510
  `,
3444
3511
  outputSchema: researchReadOutputSchema,
3445
- parameters: z8.object({
3446
- paperId: z8.string().min(1).describe(
3512
+ parameters: z9.object({
3513
+ paperId: z9.string().min(1).describe(
3447
3514
  "Canonical paperId or primaryId such as `arxiv:1706.03762`, `pmcid:PMC12530322`, `pmid:40953549`, or `doi:10.1016/j.neunet.2025.108095`."
3448
3515
  ),
3449
- question: z8.string().min(1),
3450
- k: z8.number().int().min(1).max(50).optional().describe("Number of passages to return (default 4).")
3516
+ question: z9.string().min(1),
3517
+ k: z9.number().int().min(1).max(50).optional().describe("Number of passages to return (default 4).")
3451
3518
  }),
3452
3519
  execute: async (args2, { session, client: mcpClient }) => {
3453
3520
  const { paperId, question, k } = args2;
@@ -3456,7 +3523,7 @@ Returns matching passages or a notice when full text is unavailable.
3456
3523
  appendParam(params, "k", k);
3457
3524
  const client = getClient2(session);
3458
3525
  const res = await client.http.get(
3459
- withQuery(`${BASE2}/papers/${encodeURIComponent(paperId)}`, params),
3526
+ withQuery(`${BASE3}/papers/${encodeURIComponent(paperId)}`, params),
3460
3527
  researchHeaders(requestOrigin(mcpClient, session))
3461
3528
  );
3462
3529
  const passages = res.data?.passages ?? [];
@@ -3478,9 +3545,9 @@ Returns matching passages or a notice when full text is unavailable.
3478
3545
  Deprecated compatibility entry point. Use firecrawl_developer_search for GitHub issues, pull requests, and READMEs, plus code documentation, returned as matched passages.
3479
3546
  `,
3480
3547
  outputSchema: deprecatedToolOutputSchema,
3481
- parameters: z8.object({
3482
- query: z8.string().min(1),
3483
- k: z8.number().int().min(1).max(100).optional()
3548
+ parameters: z9.object({
3549
+ query: z9.string().min(1),
3550
+ k: z9.number().int().min(1).max(100).optional()
3484
3551
  }),
3485
3552
  canList: () => false,
3486
3553
  beforeValidate: () => {
@@ -3499,7 +3566,7 @@ Deprecated compatibility entry point. Use firecrawl_developer_search for GitHub
3499
3566
  }
3500
3567
 
3501
3568
  // src/usage.ts
3502
- import { z as z9 } from "zod";
3569
+ import { z as z10 } from "zod";
3503
3570
  function assertSuccessful(status, response, operation) {
3504
3571
  if (status === 200 && response.success) return;
3505
3572
  throw new Error(response.error || `Failed to ${operation}`);
@@ -3521,11 +3588,11 @@ The current view returns \`remainingCredits\`, \`planCredits\`, \`billingPeriodS
3521
3588
  The historical view returns periods sorted by start date with \`startDate\`, \`endDate\`, and \`creditsUsed\`. Set \`byApiKey\` to include separate periods per API key, identified by the optional \`apiKey\` field. The newest period's \`endDate\` can be null.
3522
3589
  `,
3523
3590
  outputSchema: creditUsageOutputSchema,
3524
- parameters: z9.object({
3525
- view: z9.enum(["current", "historical"]).optional().describe(
3591
+ parameters: z10.object({
3592
+ view: z10.enum(["current", "historical"]).optional().describe(
3526
3593
  "Select current balance or historical monthly usage. Defaults to current."
3527
3594
  ),
3528
- byApiKey: z9.boolean().optional().describe(
3595
+ byApiKey: z10.boolean().optional().describe(
3529
3596
  'Break historical usage down by API key. When view is omitted, true selects the historical view; it cannot be combined with view "current".'
3530
3597
  )
3531
3598
  }),
@@ -4148,30 +4215,32 @@ function removeEmptyTopLevel(obj) {
4148
4215
  }
4149
4216
  return out;
4150
4217
  }
4151
- var searchDomainSchema = z10.string().trim().toLowerCase().min(1).max(253).regex(
4218
+ var searchDomainSchema = z11.string().trim().toLowerCase().min(1).max(253).regex(
4152
4219
  /^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z0-9][a-z0-9-]{0,61}[a-z0-9]$/,
4153
4220
  "Domain must be a valid hostname without protocol or path"
4154
4221
  );
4155
4222
  var searchToolBaseFields = {
4156
- toolDetail: z10.enum(["compact", "summary", "full"]).optional().describe("Compact by default. Compact returns only provider, capability and description; full includes contracts. Inspect selected compact tools with firecrawl_find_tools providers and capabilities."),
4157
- query: z10.string().min(1).describe("Query for web and semantic tool discovery. Operators include quoted phrases, `-term`, `site:host`, `inurl:term`, `intitle:term`, and `related:host`; the set is non-exhaustive. Catalogue browsing is available through firecrawl_find_tools."),
4158
- domainTools: z10.boolean().optional().describe(
4223
+ toolDetail: z11.enum(["compact", "summary", "full"]).optional().describe("Compact by default. Compact returns only provider, capability and description; full includes contracts. Inspect selected compact tools with firecrawl_find_tools providers and capabilities."),
4224
+ query: z11.string().min(1).describe("Query for web and semantic tool discovery. Operators include quoted phrases, `-term`, `site:host`, `inurl:term`, `intitle:term`, and `related:host`; the set is non-exhaustive. Catalogue browsing is available through firecrawl_find_tools."),
4225
+ objective: z11.string().trim().min(1).max(5e3).optional().describe("Optional broader goal for this search, if known. Avoid sensitive information."),
4226
+ clientModel: z11.string().trim().min(1).max(128).optional().describe("Optional model identifier, if known."),
4227
+ domainTools: z11.boolean().optional().describe(
4159
4228
  "Include domain-matched tools for result URLs. Defaults to true when Alexandria is combined with web, news or images; semantic-only search leaves domain matching off."
4160
4229
  ),
4161
- highlights: z10.boolean().optional().describe(
4230
+ highlights: z11.boolean().optional().describe(
4162
4231
  "Return query-relevant page excerpts for web and news results when available (default). Highlights appear in web `description` and news `snippet`; otherwise, original snippets are returned. Set to false to keep the original search snippets."
4163
4232
  ),
4164
- limit: z10.number().int().min(1).max(100).optional(),
4165
- tbs: z10.string().optional(),
4166
- filter: z10.string().optional(),
4167
- location: z10.string().optional(),
4168
- includeDomains: z10.array(searchDomainSchema).optional().describe("Hostnames to restrict results to. Mutually exclusive with excludeDomains."),
4169
- excludeDomains: z10.array(searchDomainSchema).optional().describe("Hostnames to leave out of results. Mutually exclusive with includeDomains."),
4170
- sources: z10.array(searchSourceSchema).optional().describe("Search sources; authenticated sessions default to web + alexandria, keyless sessions to web only. " + ALEXANDRIA_SOURCES_OPT_OUT + ' Use ["alexandria"] alone for provider discovery without web results.'),
4171
- categories: z10.array(z10.enum(["research", "pdf", "developer"])).optional().describe(
4172
- 'Limit results to specific source types. `research` restricts ordinary web results to research-affiliated websites and returns page snippets, which is separate from the `firecrawl_research_*` tools that search paper abstracts and full text across biomedical (PubMed, bioRxiv, medRxiv) and arXiv literature; `pdf` searches PDF results; `developer` searches an index built for coding agents over public repositories, GitHub issues, merged pull requests, repository READMEs, and code documentation. `developer` returns hits in `data.web` with `category: "developer"`; the other categories also filter `data.web`.'
4233
+ limit: z11.number().int().min(1).max(100).optional(),
4234
+ tbs: z11.string().optional(),
4235
+ filter: z11.string().optional(),
4236
+ location: z11.string().optional(),
4237
+ includeDomains: z11.array(searchDomainSchema).optional().describe("Hostnames to restrict results to. Mutually exclusive with excludeDomains."),
4238
+ excludeDomains: z11.array(searchDomainSchema).optional().describe("Hostnames to leave out of results. Mutually exclusive with includeDomains."),
4239
+ sources: z11.array(searchSourceSchema).optional().describe("Search sources; authenticated sessions default to web + alexandria, keyless sessions to web only. " + ALEXANDRIA_SOURCES_OPT_OUT + ' Use ["alexandria"] alone for provider discovery without web results.'),
4240
+ categories: z11.array(z11.enum(["research", "pdf", "developer", "gov"])).optional().describe(
4241
+ "Limit results to specific source types. `research` restricts ordinary web results to research-affiliated websites and returns page snippets, which is separate from the `firecrawl_research_*` tools that search paper abstracts and full text across biomedical (PubMed, bioRxiv, medRxiv) and arXiv literature; `pdf` searches PDF results; `developer` searches an index built for coding agents over public repositories, GitHub issues, merged pull requests, repository READMEs, and code documentation; `gov` searches the Government Index of US federal, state, and local legal and regulatory sources, the index behind firecrawl_gov_search, and cannot be combined with other categories. `developer` and `gov` return hits in `data.web` with `category` set to their name; the other categories also filter `data.web`."
4173
4242
  ),
4174
- enterprise: z10.array(z10.enum(["default", "anon", "zdr"])).optional()
4243
+ enterprise: z11.array(z11.enum(["default", "anon", "zdr"])).optional()
4175
4244
  };
4176
4245
  function searchDomainsAreExclusive(args2) {
4177
4246
  return !(args2.includeDomains?.length && args2.excludeDomains?.length);
@@ -4179,15 +4248,15 @@ function searchDomainsAreExclusive(args2) {
4179
4248
  var SEARCH_DOMAINS_CONFLICT_MESSAGE = "includeDomains and excludeDomains cannot both be specified";
4180
4249
  var EXCHANGE_KEY_REQUIRED_MESSAGE = "Alexandria requires an API key on a team with Alexandria access";
4181
4250
  var EXCHANGE_MAX_CALLS = 10;
4182
- var exchangeCallSchema = z10.object({
4183
- provider: z10.string().min(1).describe('Provider slug, e.g. "fred".'),
4184
- capability: z10.string().min(1).describe(
4251
+ var exchangeCallSchema = z11.object({
4252
+ provider: z11.string().min(1).describe('Provider slug, e.g. "fred".'),
4253
+ capability: z11.string().min(1).describe(
4185
4254
  'Capability address as returned by search or discover, e.g. "series/observations".'
4186
4255
  ),
4187
- version: z10.string().trim().min(1).max(128).optional().describe("Optional published workflow version. Omit to use the latest version."),
4188
- options: z10.record(z10.string(), z10.any()).optional().describe("Capability options as declared by its contract.")
4256
+ version: z11.string().trim().min(1).max(128).optional().describe("Optional published workflow version. Omit to use the latest version."),
4257
+ options: z11.record(z11.string(), z11.any()).optional().describe("Capability options as declared by its contract.")
4189
4258
  });
4190
- var exchangeCallsSchema = z10.array(exchangeCallSchema).min(1).max(EXCHANGE_MAX_CALLS);
4259
+ var exchangeCallsSchema = z11.array(exchangeCallSchema).min(1).max(EXCHANGE_MAX_CALLS);
4191
4260
  function assertExchangeCredential(session) {
4192
4261
  if (hasCredential(session)) return;
4193
4262
  const payload = {
@@ -4198,6 +4267,15 @@ function assertExchangeCredential(session) {
4198
4267
  throw new UserError(payload.message, payload);
4199
4268
  }
4200
4269
  var TERMS_REQUIRED_CODE = "THIRD_PARTY_DATA_TERMS_REQUIRED";
4270
+ var DATA_SOURCES_SETTINGS_URL = "https://www.firecrawl.dev/app/settings?tab=data-sources";
4271
+ function isTermsWrite(call) {
4272
+ const capability = call.capability.trim().toLowerCase();
4273
+ return call.provider.trim().toLowerCase() === "firecrawl" && capability.startsWith("terms/") && capability !== "terms/show";
4274
+ }
4275
+ function termsWriteError() {
4276
+ const message = `Provider terms are accepted in the Firecrawl dashboard, not through this connection. Read them with terms/show, then ask an organization admin to accept them at ${DATA_SOURCES_SETTINGS_URL}.`;
4277
+ return new UserError(message, { code: "invalid_option", status: 400, message });
4278
+ }
4201
4279
  function termsRequiredAction(body) {
4202
4280
  const data = body;
4203
4281
  if (data?.code !== TERMS_REQUIRED_CODE) return void 0;
@@ -4218,8 +4296,8 @@ function termsRequiredError(action, context, hints) {
4218
4296
  return new UserError(
4219
4297
  `${message}
4220
4298
 
4221
- 1. Read the agreement with firecrawl_scrape using alexandria: {provider: "firecrawl", capability: "terms/show", options: {provider: "${action.terms}"}} and present it to the user. Only after explicit authorization to accept that exact version and digest, use firecrawl_scrape with alexandria: {provider: "firecrawl", capability: "terms/accept", options: {provider: "${action.terms}", version: "<reviewed-version>", digest: "<reviewed-digest>", confirmed: true}}. Send terms calls separately from provider execution. If authority or eligibility requires a dashboard action, ask an organization admin to visit ${action.url} or https://www.firecrawl.dev/app/settings?tab=data-sources if that page is unavailable. Never infer acceptance from a data request.
4222
- 2. After they confirm, call ${context.tool} again with the identical payload${retryIdentity}. If the same terms error persists, stop and ask an organization admin to check access at https://www.firecrawl.dev/app/settings?tab=data-sources.
4299
+ 1. Read the agreement with firecrawl_scrape using alexandria: {provider: "firecrawl", capability: "terms/show", options: {provider: "${action.terms}"}} and present it to the user. Send terms/show separately from provider execution. Terms are accepted in the Firecrawl dashboard, not through this connection: ask an organization admin to accept them at ${action.url}, or ${DATA_SOURCES_SETTINGS_URL} if that page is unavailable. Never infer acceptance from a data request.
4300
+ 2. After the admin confirms, call ${context.tool} again with the identical payload${retryIdentity}. If the same terms error persists, stop and ask an organization admin to check access at ${DATA_SOURCES_SETTINGS_URL}.
4223
4301
 
4224
4302
  Do not retry until acceptance is confirmed.`,
4225
4303
  {
@@ -4281,7 +4359,7 @@ async function relayExchangeError(run, context) {
4281
4359
  const disabledProvider = response.status === 403 ? context.providers?.find((provider) => message === `Access to ${provider} is disabled for this organization.`) : void 0;
4282
4360
  if (disabledProvider) {
4283
4361
  throw new UserError(
4284
- `${message} Read the provider terms and status using nextTool and present them to the user. Never infer acceptance from a data request. Only after explicit authorization for the reviewed version and digest may you call firecrawl_scrape with alexandria:{provider:"firecrawl",capability:"terms/accept",options:{provider:"${disabledProvider}",version:"<reviewed-version>",digest:"<reviewed-digest>",confirmed:true}}. Send terms calls separately. Acceptance may not restore disabled access; an organization admin may need to review https://www.firecrawl.dev/app/settings?tab=data-sources. Retry the original request only after access is restored.`,
4362
+ `${message} Read the provider terms and status using nextTool and present them to the user. Never infer acceptance from a data request. Terms are accepted in the Firecrawl dashboard, not through this connection; acceptance may not restore disabled access, and an organization admin can review access at ${DATA_SOURCES_SETTINGS_URL}. Retry the original request only after access is restored.`,
4285
4363
  {
4286
4364
  code: typeof data?.code === "string" ? data.code : "exchange_error",
4287
4365
  status: 403,
@@ -4354,12 +4432,13 @@ var ConsoleLogger = class {
4354
4432
  var openAiAppsChallengeToken = normalizeHeader(
4355
4433
  process.env.OPENAI_APPS_CHALLENGE_TOKEN
4356
4434
  );
4357
- var FULL_PROFILE_INSTRUCTIONS = `Firecrawl provides web search, page retrieval, site URL discovery, multi-page collection, structured page data, monitoring, and multi-source research that returns structured data. Match the requested operation to the tool boundary: firecrawl_scrape retrieves one supplied page and can return JSON matching a supplied schema, firecrawl_map enumerates URLs under a site without retrieving their content, and firecrawl_agent runs multi-source research and returns structured data when the URLs are not known or the answer spans several sites (an entity plus its fields, a list, a dataset); its result is read with firecrawl_agent_status. Authenticated firecrawl_search returns web results together with matching Alexandria providers in data.tools. ${ALEXANDRIA_CATALOGUE_SENTENCE} A matching provider can return the same fields across several entities, provenance, exact figures or timestamps, or a large set of records through firecrawl_scrape with its published contract. If web results already answer the question, use them. For the same fields across multiple pages, firecrawl_find_tools offers free provider discovery. Use firecrawl_find_tools to read a contract that was not returned in full or to browse the catalogue by category. ${ALEXANDRIA_SOURCES_OPT_OUT} If no provider fits, continue with web search or firecrawl_agent. For biomedical, life-science, clinical, or arXiv literature, the firecrawl_research_* tools search a paper index of abstracts and full text; firecrawl_search with categories: ["research"] is a website filter over ordinary web results and reaches different sources. For a programming question (code behaviour, a library or framework, an API contract, an error message, or a known bug), firecrawl_developer_search (or firecrawl_search with categories: ["developer"]) searches an index of public repositories, GitHub issues, merged pull requests, READMEs, and code documentation. firecrawl_search with sources: [{type: "alexandria"}] returns compact tool summaries in data.tools; toolDetail: "full" includes contracts, firecrawl_find_tools starts with categories, lists providers, then compact tools, and expands the selected full contract, and firecrawl_scrape with alexandria: [{provider, capability, options}] executes up to ten capabilities and returns their results. Alexandria access needs an API key on a team with it enabled. Provide only the required inputs and account for stated network or external side effects.`;
4358
- var KEYLESS_PROFILE_INSTRUCTIONS = `Hosted keyless sessions expose firecrawl_search, firecrawl_scrape, and firecrawl_parse with usage limits. firecrawl_search searches the web. For programming questions, firecrawl_search with categories: ["developer"] searches indexed public repositories, GitHub issues, merged pull requests, repository READMEs, and code documentation. For biomedical, life-science, clinical, or arXiv literature, firecrawl_search with categories: ["research"] filters ordinary web results to research-affiliated websites. firecrawl_scrape retrieves one supplied page and can return JSON matching a supplied schema. firecrawl_parse processes supported local files through its two-phase upload flow. An Authorization bearer API key can provide higher usage limits and expose additional tools, subject to plan, deployment, and team policy, including firecrawl_map for site URL discovery, firecrawl_agent and firecrawl_agent_status for multi-source research that returns structured data when the URLs are not known, firecrawl_research_* for paper-index and repository research, and firecrawl_find_tools as the progressive Alexandria catalogue lookup alongside the Alexandria options of firecrawl_search and firecrawl_scrape for catalogued data providers.`;
4359
- var SEARCH_PROFILE_INSTRUCTIONS = ALEXANDRIA_SEARCH_INSTRUCTIONS + ` Firecrawl provides web, developer, and research search, and executes catalogued Alexandria data providers. Use firecrawl_search to find relevant results across the web and specialized indexes; authenticated searches also return matching Alexandria providers in data.tools. firecrawl_find_tools provides catalogue browsing and provider contracts; firecrawl_scrape with an alexandria body executes a selected capability; firecrawl_scrape with a url retrieves one supplied page. For a programming question, firecrawl_developer_search searches indexed public repositories, GitHub issues, merged pull requests, READMEs, and code documentation and returns the matched passages, and skills: "only" narrows it to agent-skill files; firecrawl_search with categories: ["developer"] reaches the same index beside ordinary web results, returning the hits in the web group rather than as passages and offering no skills filter. For a biomedical, life-science, clinical, or arXiv literature question, the firecrawl_research_* tools search the paper index, while categories: ["research"] on firecrawl_search filters ordinary web results to research-affiliated websites. Use the firecrawl_research_* tools to search academic and research literature, expand from anchor papers via the citation graph, and read full-text passages from a specific paper. Search and discovery tools are read-only and return ranked results. Billing: web, developer and research search are billed per request; Alexandria discovery (firecrawl_search with sources ["alexandria"] alone, and firecrawl_find_tools) is free; firecrawl_scrape is billed, as a page retrieval in url mode or at each executed capability's listed price in alexandria mode.`;
4435
+ var FULL_PROFILE_INSTRUCTIONS = `Firecrawl provides web search, page retrieval, site URL discovery, multi-page collection, structured page data, monitoring, and multi-source research that returns structured data. Match the requested operation to the tool boundary: firecrawl_scrape retrieves one supplied page and can return JSON matching a supplied schema, firecrawl_map enumerates URLs under a site without retrieving their content, and firecrawl_agent runs multi-source research and returns structured data when the URLs are not known or the answer spans several sites (an entity plus its fields, a list, a dataset); its result is read with firecrawl_agent_status. Authenticated firecrawl_search returns web results together with matching Alexandria providers in data.tools. ${ALEXANDRIA_CATALOGUE_SENTENCE} A matching provider can return the same fields across several entities, provenance, exact figures or timestamps, or a large set of records through firecrawl_scrape with its published contract. If web results already answer the question, use them. For the same fields across multiple pages, firecrawl_find_tools offers free provider discovery. Use firecrawl_find_tools to read a contract that was not returned in full or to browse the catalogue by category. ${ALEXANDRIA_SOURCES_OPT_OUT} If no provider fits, continue with web search or firecrawl_agent. For biomedical, life-science, clinical, or arXiv literature, the firecrawl_research_* tools search a paper index of abstracts and full text; firecrawl_search with categories: ["research"] is a website filter over ordinary web results and reaches different sources. For a programming question (code behaviour, a library or framework, an API contract, an error message, or a known bug), firecrawl_developer_search (or firecrawl_search with categories: ["developer"]) searches an index of public repositories, GitHub issues, merged pull requests, READMEs, and code documentation. For a legal or regulatory question, firecrawl_gov_search searches primary law and regulatory material from US federal, state, and local government sources: statutes, regulations, codes, court opinions, and other government publications; firecrawl_search with categories: ["gov"] reaches the same sources and cannot be combined with other categories. firecrawl_search with sources: [{type: "alexandria"}] returns compact tool summaries in data.tools; toolDetail: "full" includes contracts, firecrawl_find_tools starts with categories, lists providers, then compact tools, and expands the selected full contract, and firecrawl_scrape with alexandria: [{provider, capability, options}] executes up to ten capabilities and returns their results. Alexandria access needs an API key on a team with it enabled. Provide only the required inputs and account for stated network or external side effects.`;
4436
+ var KEYLESS_PROFILE_INSTRUCTIONS = `Hosted keyless sessions expose firecrawl_search, firecrawl_scrape, and firecrawl_parse with usage limits. firecrawl_search searches the web. For programming questions, firecrawl_search with categories: ["developer"] searches indexed public repositories, GitHub issues, merged pull requests, repository READMEs, and code documentation. For legal or regulatory questions, firecrawl_search with categories: ["gov"] searches US government legal and regulatory sources and cannot be combined with other categories. For biomedical, life-science, clinical, or arXiv literature, firecrawl_search with categories: ["research"] filters ordinary web results to research-affiliated websites. firecrawl_scrape retrieves one supplied page and can return JSON matching a supplied schema. firecrawl_parse processes supported local files through its two-phase upload flow. An Authorization bearer API key can provide higher usage limits and expose additional tools, subject to plan, deployment, and team policy, including firecrawl_map for site URL discovery, firecrawl_agent and firecrawl_agent_status for multi-source research that returns structured data when the URLs are not known, firecrawl_research_* for paper-index and repository research, and firecrawl_find_tools as the progressive Alexandria catalogue lookup alongside the Alexandria options of firecrawl_search and firecrawl_scrape for catalogued data providers.`;
4437
+ var SEARCH_PROFILE_INSTRUCTIONS = ALEXANDRIA_SEARCH_INSTRUCTIONS + ` Firecrawl provides web, developer, government, and research search, and executes catalogued Alexandria data providers. Use firecrawl_search to find relevant results across the web and specialized indexes; authenticated searches also return matching Alexandria providers in data.tools. firecrawl_find_tools provides catalogue browsing and provider contracts; firecrawl_scrape with an alexandria body executes a selected capability; firecrawl_scrape with a url retrieves one supplied page. For a programming question, firecrawl_developer_search searches indexed public repositories, GitHub issues, merged pull requests, READMEs, and code documentation and returns the matched passages, and skills: "only" narrows it to agent-skill files; firecrawl_search with categories: ["developer"] reaches the same index beside ordinary web results, returning the hits in the web group rather than as passages and offering no skills filter. For a legal or regulatory question, firecrawl_gov_search searches primary law and regulatory material from US federal, state, and local government sources: statutes, regulations, codes, court opinions, and other government publications; firecrawl_search with categories: ["gov"] reaches the same sources in the web group and cannot be combined with other categories. For a biomedical, life-science, clinical, or arXiv literature question, the firecrawl_research_* tools search the paper index, while categories: ["research"] on firecrawl_search filters ordinary web results to research-affiliated websites. Use the firecrawl_research_* tools to search academic and research literature, expand from anchor papers via the citation graph, and read full-text passages from a specific paper. Search and discovery tools are read-only and return ranked results. Billing: web, developer, and research search are billed per request; government search is free; Alexandria discovery (firecrawl_search with sources ["alexandria"] alone, and firecrawl_find_tools) is free; firecrawl_scrape is billed, as a page retrieval in url mode or at each executed capability's listed price in alexandria mode.`;
4360
4438
  var SEARCH_PROFILE_TOOLS = /* @__PURE__ */ new Set([
4361
4439
  "firecrawl_search",
4362
4440
  "firecrawl_developer_search",
4441
+ "firecrawl_gov_search",
4363
4442
  "firecrawl_research_search_papers",
4364
4443
  "firecrawl_research_inspect_paper",
4365
4444
  "firecrawl_research_related_papers",
@@ -4954,10 +5033,10 @@ function transformScrapeParams(args2) {
4954
5033
  delete out.pdfOptions;
4955
5034
  return out;
4956
5035
  }
4957
- var scrapeParamsSchema = z10.object({
4958
- url: z10.string().url(),
4959
- formats: z10.array(
4960
- z10.enum([
5036
+ var scrapeParamsSchema = z11.object({
5037
+ url: z11.string().url(),
5038
+ formats: z11.array(
5039
+ z11.enum([
4961
5040
  "markdown",
4962
5041
  "html",
4963
5042
  "rawHtml",
@@ -4971,71 +5050,77 @@ var scrapeParamsSchema = z10.object({
4971
5050
  "audio"
4972
5051
  ])
4973
5052
  ).optional(),
4974
- jsonOptions: z10.object({
4975
- prompt: z10.string().optional(),
4976
- schema: z10.record(z10.string(), z10.any()).optional()
5053
+ jsonOptions: z11.object({
5054
+ prompt: z11.string().optional(),
5055
+ schema: z11.record(z11.string(), z11.any()).optional()
4977
5056
  }).optional(),
4978
- queryOptions: z10.object({
4979
- prompt: z10.string().max(1e4),
4980
- mode: z10.enum(["directQuote", "freeform"]).default("freeform")
5057
+ queryOptions: z11.object({
5058
+ prompt: z11.string().max(1e4),
5059
+ mode: z11.enum(["directQuote", "freeform"]).default("freeform")
4981
5060
  }).optional(),
4982
- screenshotOptions: z10.object({
4983
- fullPage: z10.boolean().optional(),
4984
- quality: z10.number().optional(),
4985
- viewport: z10.object({ width: z10.number(), height: z10.number() }).optional()
5061
+ screenshotOptions: z11.object({
5062
+ fullPage: z11.boolean().optional(),
5063
+ quality: z11.number().optional(),
5064
+ viewport: z11.object({ width: z11.number(), height: z11.number() }).optional()
4986
5065
  }).optional(),
4987
- parsers: z10.array(z10.enum(["pdf"])).optional(),
4988
- pdfOptions: z10.object({
4989
- maxPages: z10.number().int().min(1).max(1e4).optional()
5066
+ parsers: z11.array(z11.enum(["pdf"])).optional(),
5067
+ pdfOptions: z11.object({
5068
+ maxPages: z11.number().int().min(1).max(1e4).optional()
4990
5069
  }).optional(),
4991
- onlyMainContent: z10.boolean().optional(),
4992
- redactPII: z10.boolean().optional(),
4993
- includeTags: z10.array(z10.string()).optional(),
4994
- excludeTags: z10.array(z10.string()).optional(),
4995
- waitFor: z10.number().optional(),
5070
+ onlyMainContent: z11.boolean().optional(),
5071
+ redactPII: z11.boolean().optional(),
5072
+ includeTags: z11.array(z11.string()).optional(),
5073
+ excludeTags: z11.array(z11.string()).optional(),
5074
+ waitFor: z11.number().optional(),
4996
5075
  ...SAFE_MODE ? {} : {
4997
- actions: z10.array(
4998
- z10.object({
4999
- type: z10.enum(allowedActionTypes),
5000
- selector: z10.string().optional(),
5001
- milliseconds: z10.number().optional(),
5002
- text: z10.string().optional(),
5003
- key: z10.string().optional(),
5004
- direction: z10.enum(["up", "down"]).optional(),
5005
- script: z10.string().optional(),
5006
- fullPage: z10.boolean().optional()
5076
+ actions: z11.array(
5077
+ z11.object({
5078
+ type: z11.enum(allowedActionTypes),
5079
+ selector: z11.string().optional(),
5080
+ milliseconds: z11.number().optional(),
5081
+ text: z11.string().optional(),
5082
+ key: z11.string().optional(),
5083
+ direction: z11.enum(["up", "down"]).optional(),
5084
+ script: z11.string().optional(),
5085
+ fullPage: z11.boolean().optional()
5007
5086
  })
5008
5087
  ).optional()
5009
5088
  },
5010
- mobile: z10.boolean().optional(),
5011
- skipTlsVerification: z10.boolean().optional(),
5012
- removeBase64Images: z10.boolean().optional(),
5013
- location: z10.object({
5014
- country: z10.string().optional(),
5015
- languages: z10.array(z10.string()).optional()
5089
+ mobile: z11.boolean().optional(),
5090
+ skipTlsVerification: z11.boolean().optional(),
5091
+ removeBase64Images: z11.boolean().optional(),
5092
+ location: z11.object({
5093
+ country: z11.string().optional(),
5094
+ languages: z11.array(z11.string()).optional()
5016
5095
  }).optional(),
5017
- storeInCache: z10.boolean().optional(),
5018
- zeroDataRetention: z10.boolean().optional(),
5019
- maxAge: z10.number().optional(),
5020
- lockdown: z10.boolean().optional(),
5021
- proxy: z10.enum(["basic", "stealth", "enhanced", "auto"]).optional(),
5022
- profile: z10.object({
5023
- name: z10.string(),
5024
- saveChanges: z10.boolean().optional()
5096
+ storeInCache: z11.boolean().optional(),
5097
+ zeroDataRetention: z11.boolean().optional(),
5098
+ maxAge: z11.number().optional(),
5099
+ lockdown: z11.boolean().optional(),
5100
+ proxy: z11.enum(["basic", "stealth", "enhanced", "auto"]).optional(),
5101
+ profile: z11.object({
5102
+ name: z11.string(),
5103
+ saveChanges: z11.boolean().optional()
5025
5104
  }).optional()
5026
5105
  });
5106
+ var readOnlyProfileSchema = z11.object({ name: z11.string() }).describe("Loads a saved browser profile without saving changes to it.");
5107
+ function withReadOnlyProfile(options) {
5108
+ const profile = options.profile;
5109
+ return SAFE_MODE && profile ? { ...options, profile: { name: profile.name, saveChanges: false } } : options;
5110
+ }
5027
5111
  var ALEXANDRIA_IGNORED_SCRAPE_OPTIONS = /* @__PURE__ */ new Set(["toolDetail", "domainTools"]);
5028
5112
  var scrapeToolParamsSchema = scrapeParamsSchema.extend({
5029
- url: z10.string().url().optional(),
5030
- timeout: z10.number().int().positive().optional().describe("Execution timeout in milliseconds."),
5031
- requestId: z10.string().regex(/^[A-Za-z0-9._:-]{1,128}$/).optional().describe(
5113
+ ...SAFE_MODE ? { profile: readOnlyProfileSchema.optional() } : {},
5114
+ url: z11.string().url().optional(),
5115
+ timeout: z11.number().int().positive().optional().describe("Execution timeout in milliseconds."),
5116
+ requestId: z11.string().regex(/^[A-Za-z0-9._:-]{1,128}$/).optional().describe(
5032
5117
  "Idempotency key bound to one Alexandria execution payload. Generated when omitted and returned with the result."
5033
5118
  ),
5034
- alexandria: z10.union([exchangeCallSchema, exchangeCallsSchema]).optional().describe(
5119
+ alexandria: z11.union([exchangeCallSchema, exchangeCallsSchema]).optional().describe(
5035
5120
  "Catalogued Alexandria capability invocation, mutually exclusive with url. One {provider, capability, options} object or an array of 1-10, with contracts available through firecrawl_search or firecrawl_find_tools. Each call may include version to pin a published workflow; omitting it uses latest. Only requestId and timeout are supported alongside alexandria. " + ALEXANDRIA_CONTRACT_GUIDANCE + " Returns per-capability results in data.alexandria with data, records, or an error with a code; individual capabilities can fail even when the outer response succeeds. Requires an API key on a team with Alexandria enabled. Some providers require accepted terms; blocked requests return the applicable requirements."
5036
5121
  ),
5037
- toolDetail: z10.enum(["compact", "summary", "full"]).optional().describe("URL mode only: domain discovery detail, summary by default; compact returns provider/capability/description, full includes contracts. Ignored with alexandria."),
5038
- domainTools: z10.boolean().optional().describe(
5122
+ toolDetail: z11.enum(["compact", "summary", "full"]).optional().describe("URL mode only: domain discovery detail, summary by default; compact returns provider/capability/description, full includes contracts. Ignored with alexandria."),
5123
+ domainTools: z11.boolean().optional().describe(
5039
5124
  "URL mode only: include domain-matched Alexandria tools for the page in tools on the returned document. Ignored with alexandria."
5040
5125
  )
5041
5126
  }).refine(
@@ -5055,9 +5140,9 @@ var scrapeToolParamsSchema = scrapeParamsSchema.extend({
5055
5140
  (args2) => !args2.requestId || !!args2.alexandria,
5056
5141
  "requestId applies to Alexandria execution only"
5057
5142
  );
5058
- var parseOptionParamsSchema = z10.object({
5059
- formats: z10.array(
5060
- z10.enum([
5143
+ var parseOptionParamsSchema = z11.object({
5144
+ formats: z11.array(
5145
+ z11.enum([
5061
5146
  "markdown",
5062
5147
  "html",
5063
5148
  "rawHtml",
@@ -5067,48 +5152,48 @@ var parseOptionParamsSchema = z10.object({
5067
5152
  "query"
5068
5153
  ])
5069
5154
  ).optional(),
5070
- jsonOptions: z10.object({
5071
- prompt: z10.string().optional(),
5072
- schema: z10.record(z10.string(), z10.any()).optional()
5155
+ jsonOptions: z11.object({
5156
+ prompt: z11.string().optional(),
5157
+ schema: z11.record(z11.string(), z11.any()).optional()
5073
5158
  }).optional(),
5074
- queryOptions: z10.object({
5075
- prompt: z10.string().max(1e4),
5076
- mode: z10.enum(["directQuote", "freeform"]).default("freeform")
5159
+ queryOptions: z11.object({
5160
+ prompt: z11.string().max(1e4),
5161
+ mode: z11.enum(["directQuote", "freeform"]).default("freeform")
5077
5162
  }).optional(),
5078
- parsers: z10.array(z10.enum(["pdf"])).optional(),
5079
- pdfOptions: z10.object({
5080
- maxPages: z10.number().int().min(1).max(1e4).optional()
5163
+ parsers: z11.array(z11.enum(["pdf"])).optional(),
5164
+ pdfOptions: z11.object({
5165
+ maxPages: z11.number().int().min(1).max(1e4).optional()
5081
5166
  }).optional(),
5082
- onlyMainContent: z10.boolean().optional(),
5083
- redactPII: z10.boolean().optional(),
5084
- includeTags: z10.array(z10.string()).optional(),
5085
- excludeTags: z10.array(z10.string()).optional(),
5086
- removeBase64Images: z10.boolean().optional(),
5087
- skipTlsVerification: z10.boolean().optional(),
5088
- storeInCache: z10.boolean().optional(),
5089
- zeroDataRetention: z10.boolean().optional(),
5090
- maxAge: z10.number().optional().describe("Ignored: parse never reuses or stores indexed content."),
5091
- proxy: z10.enum(["basic", "auto"]).optional()
5167
+ onlyMainContent: z11.boolean().optional(),
5168
+ redactPII: z11.boolean().optional(),
5169
+ includeTags: z11.array(z11.string()).optional(),
5170
+ excludeTags: z11.array(z11.string()).optional(),
5171
+ removeBase64Images: z11.boolean().optional(),
5172
+ skipTlsVerification: z11.boolean().optional(),
5173
+ storeInCache: z11.boolean().optional(),
5174
+ zeroDataRetention: z11.boolean().optional(),
5175
+ maxAge: z11.number().optional().describe("Ignored: parse never reuses or stores indexed content."),
5176
+ proxy: z11.enum(["basic", "auto"]).optional()
5092
5177
  });
5093
5178
  var localParseParamsSchema = parseOptionParamsSchema.extend({
5094
- filePath: z10.string().min(1).describe(
5179
+ filePath: z11.string().min(1).describe(
5095
5180
  "Absolute or relative path to a local file to parse. Supported: .html, .htm, .pdf, .docx, .doc, .odt, .rtf, .xlsx, .xls"
5096
5181
  ),
5097
- contentType: z10.string().optional().describe(
5182
+ contentType: z11.string().optional().describe(
5098
5183
  "Optional MIME type override. If omitted, the server infers the file kind from the extension."
5099
5184
  )
5100
5185
  });
5101
5186
  var hostedParseParamsSchema = parseOptionParamsSchema.extend({
5102
- filePath: z10.string().min(1).optional().describe(
5187
+ filePath: z11.string().min(1).optional().describe(
5103
5188
  "Phase 1 only: path to the local file on the caller/harness machine. Hosted MCP will not read or stat this path; it is used only to produce upload instructions."
5104
5189
  ),
5105
- uploadRef: z10.string().min(1).optional().describe(
5190
+ uploadRef: z11.string().min(1).optional().describe(
5106
5191
  "Phase 2 only: short-lived upload reference returned by phase 1 after the local PUT upload completes."
5107
5192
  ),
5108
- contentType: z10.string().optional().describe(
5193
+ contentType: z11.string().optional().describe(
5109
5194
  "Phase 1 MIME type override. If omitted, the server infers it from the file extension without reading the file."
5110
5195
  ),
5111
- declaredSizeBytes: z10.number().int().positive().optional().describe(
5196
+ declaredSizeBytes: z11.number().int().positive().optional().describe(
5112
5197
  "Optional phase 1 size declaration. Hosted MCP does not stat the file; provide this only if the caller already knows it."
5113
5198
  )
5114
5199
  }).superRefine((value, ctx) => {
@@ -5116,7 +5201,7 @@ var hostedParseParamsSchema = parseOptionParamsSchema.extend({
5116
5201
  const hasUploadRef = typeof value.uploadRef === "string" && value.uploadRef.length > 0;
5117
5202
  if (hasFilePath === hasUploadRef) {
5118
5203
  ctx.addIssue({
5119
- code: z10.ZodIssueCode.custom,
5204
+ code: z11.ZodIssueCode.custom,
5120
5205
  message: "Hosted firecrawl_parse requires exactly one of filePath (phase 1) or uploadRef (phase 2).",
5121
5206
  path: hasFilePath && hasUploadRef ? ["uploadRef"] : ["filePath"]
5122
5207
  });
@@ -5321,8 +5406,9 @@ var scrapeTool = {
5321
5406
  name: "firecrawl_scrape",
5322
5407
  annotations: {
5323
5408
  title: "Firecrawl scrape",
5324
- readOnlyHint: false,
5325
- // Alexandria capabilities can record provider agreement acceptance.
5409
+ // Hosted scrape omits browser actions, loads profiles without saving,
5410
+ // and refuses provider terms writes before execution.
5411
+ readOnlyHint: SAFE_MODE,
5326
5412
  openWorldHint: true,
5327
5413
  // Accepts any user-supplied URL on the public web.
5328
5414
  destructiveHint: false
@@ -5331,7 +5417,7 @@ var scrapeTool = {
5331
5417
  description: `
5332
5418
  Scrape one URL and return its content: markdown by default, or HTML, links, screenshots, branding data, a targeted answer, or JSON matching a supplied schema. Use it when the request identifies a page and needs its content or defined fields. Use \`firecrawl_search\` when additional web sources are needed; on an authenticated session, \`firecrawl_map\` lists a site's URLs and \`firecrawl_crawl\` collects a set of pages.
5333
5419
 
5334
- Firecrawl may serve recently indexed content; set \`maxAge: 0\` for a live fetch or a smaller \`maxAge\` to bound staleness. A successful response does not by itself confirm the page is still current. Browser actions can change the live page when interactive actions are enabled. Authenticated responses can include a \`metadata.scrapeId\` for optional scrape feedback.
5420
+ Firecrawl may serve recently indexed content; set \`maxAge: 0\` for a live fetch or a smaller \`maxAge\` to bound staleness. A successful response does not by itself confirm the page is still current. ${SAFE_MODE ? "A named browser profile loads saved session data without saving changes to it." : "Browser actions can change the live page when interactive actions are enabled."} Authenticated responses can include a \`metadata.scrapeId\` for optional scrape feedback.
5335
5421
 
5336
5422
  On an authenticated session with Alexandria access, \`firecrawl_search\` with \`sources\` unset and \`firecrawl_find_tools\` can discover providers for the same fields across several pages; a matching provider returns typed records in one call. Keyless sessions have no provider matches.
5337
5423
 
@@ -5349,6 +5435,9 @@ Alexandria mode, on an authenticated session with Alexandria access: \`alexandri
5349
5435
  } = args2;
5350
5436
  if (alexandria) {
5351
5437
  assertExchangeCredential(session);
5438
+ if ((Array.isArray(alexandria) ? alexandria : [alexandria]).some(isTermsWrite)) {
5439
+ throw termsWriteError();
5440
+ }
5352
5441
  log.info("Executing Alexandria capabilities", {
5353
5442
  count: Array.isArray(alexandria) ? alexandria.length : 1
5354
5443
  });
@@ -5359,7 +5448,7 @@ Alexandria mode, on an authenticated session with Alexandria access: \`alexandri
5359
5448
  const transformed = transformScrapeParams(
5360
5449
  options
5361
5450
  );
5362
- const cleaned = removeEmptyTopLevel(transformed);
5451
+ const cleaned = withReadOnlyProfile(removeEmptyTopLevel(transformed));
5363
5452
  if (cleaned.lockdown) {
5364
5453
  log.info("Scraping URL (lockdown)");
5365
5454
  } else {
@@ -5412,13 +5501,13 @@ Enumerate URLs indexed under one website through Firecrawl without fetching each
5412
5501
  Returns matching URLs rather than page bodies. Retrieve one page with \`firecrawl_scrape\`; collect content across multiple pages with \`firecrawl_crawl\`. Authenticated responses can include an \`id\` for optional map feedback.
5413
5502
  `,
5414
5503
  outputSchema: mapOutputSchema,
5415
- parameters: z10.object({
5416
- url: z10.string().url(),
5417
- search: z10.string().optional(),
5418
- sitemap: z10.enum(["include", "skip", "only"]).optional(),
5419
- includeSubdomains: z10.boolean().optional(),
5420
- limit: z10.number().optional(),
5421
- ignoreQueryParameters: z10.boolean().optional()
5504
+ parameters: z11.object({
5505
+ url: z11.string().url(),
5506
+ search: z11.string().optional(),
5507
+ sitemap: z11.enum(["include", "skip", "only"]).optional(),
5508
+ includeSubdomains: z11.boolean().optional(),
5509
+ limit: z11.number().optional(),
5510
+ ignoreQueryParameters: z11.boolean().optional()
5422
5511
  }),
5423
5512
  execute: async (args2, { session, log, client: mcpClient }) => {
5424
5513
  const { url, ...options } = args2;
@@ -5454,12 +5543,12 @@ ${ALEXANDRIA_SEARCH_LEAD}
5454
5543
 
5455
5544
  On an authenticated session, tool matches describe available capabilities; \`firecrawl_find_tools\` returns their contracts and \`firecrawl_scrape\` with an \`alexandria\` body executes a selected capability. Keyless sessions get no Alexandria matches in data.tools.
5456
5545
 
5457
- For a programming question, add \`categories: ["developer"]\`; its hits return in \`data.web\` with \`category: "developer"\`. \`categories: ["research"]\` restricts web results to research-affiliated websites; the \`firecrawl_research_*\` tools are a separate surface over paper abstracts and full text (PubMed, bioRxiv, medRxiv, arXiv). Query operators, domain filters, \`categories\`, \`toolDetail\` and \`scrapeOptions\` are described on their parameters. Returns source-type result groups and usage metadata. Authenticated responses can include an \`id\` for optional search feedback.
5546
+ For a programming question, add \`categories: ["developer"]\`; its hits return in \`data.web\` with \`category: "developer"\`. For a legal or regulatory question, \`categories: ["gov"]\` returns hits in \`data.web\` with \`category: "gov"\` and cannot be combined with other categories; authenticated sessions also have \`firecrawl_gov_search\` as the dedicated tool. \`categories: ["research"]\` restricts web results to research-affiliated websites; the \`firecrawl_research_*\` tools are a separate surface over paper abstracts and full text (PubMed, bioRxiv, medRxiv, arXiv). Query operators, domain filters, \`categories\`, \`toolDetail\` and \`scrapeOptions\` are described on their parameters. Returns source-type result groups and usage metadata. Authenticated responses can include an \`id\` for optional search feedback.
5458
5547
  `,
5459
5548
  outputSchema: searchOutputSchema,
5460
- parameters: z10.object({
5549
+ parameters: z11.object({
5461
5550
  ...searchToolBaseFields,
5462
- scrapeOptions: scrapeParamsSchema.omit({ url: true }).partial().optional().describe("Attach page content for web results in the same call. These fetches ignore maxAge, so use firecrawl_scrape when you need a live fetch. scrapeOptions fetches web pages, never Alexandria provider tools.")
5551
+ scrapeOptions: scrapeParamsSchema.omit({ url: true }).extend(SAFE_MODE ? { profile: readOnlyProfileSchema } : {}).partial().optional().describe("Attach page content for web results in the same call. These fetches ignore maxAge, so use firecrawl_scrape when you need a live fetch. scrapeOptions fetches web pages, never Alexandria provider tools.")
5463
5552
  }).refine(searchDomainsAreExclusive, SEARCH_DOMAINS_CONFLICT_MESSAGE).refine(
5464
5553
  searchQueryIsValid,
5465
5554
  "A query is required. Use Find Tools for catalogue lookup."
@@ -5475,8 +5564,8 @@ For a programming question, add \`categories: ["developer"]\`; its hits return i
5475
5564
  searchOpts.domainTools ??= defaultDomainTools(searchOpts.sources);
5476
5565
  searchOpts.toolDetail ??= "compact";
5477
5566
  if (searchOpts.scrapeOptions) {
5478
- searchOpts.scrapeOptions = transformScrapeParams(
5479
- searchOpts.scrapeOptions
5567
+ searchOpts.scrapeOptions = withReadOnlyProfile(
5568
+ transformScrapeParams(searchOpts.scrapeOptions)
5480
5569
  );
5481
5570
  }
5482
5571
  const cleaned = removeEmptyTopLevel(searchOpts);
@@ -5596,7 +5685,7 @@ var findToolsTool = {
5596
5685
  };
5597
5686
  server.addTool(findToolsTool);
5598
5687
  var SEARCH_SURFACE_SCRAPE_DESCRIPTION = `
5599
- Scrape one URL and return its content, or execute catalogued Alexandria capabilities. URL mode returns markdown by default, or HTML, links, screenshots, branding data, a targeted answer, or JSON matching a supplied schema, plus page metadata. Firecrawl may serve recently indexed content; set \`maxAge: 0\` for a live fetch. A successful response does not by itself confirm the page is still current. Browser actions can change the live page when interactive actions are enabled, and a named browser profile can load saved session data and overwrite its stored state.
5688
+ Scrape one URL and return its content, or execute catalogued Alexandria capabilities. URL mode returns markdown by default, or HTML, links, screenshots, branding data, a targeted answer, or JSON matching a supplied schema, plus page metadata. Firecrawl may serve recently indexed content; set \`maxAge: 0\` for a live fetch. A successful response does not by itself confirm the page is still current. ${SAFE_MODE ? "A named browser profile loads saved session data without saving changes to it." : "Browser actions can change the live page, and a named browser profile can load saved session data and overwrite its stored state."}
5600
5689
 
5601
5690
  \`firecrawl_search\` with \`sources\` unset and \`firecrawl_find_tools\` can discover providers for the same fields across several pages; a matching Alexandria provider returns typed records in one call.
5602
5691
 
@@ -5777,17 +5866,17 @@ async function waitForCrawlCompletionWithOrigin(client, jobId, origin, pollInter
5777
5866
  );
5778
5867
  }
5779
5868
  }
5780
- var feedbackIssueSchema = z10.string().trim().min(1).max(80).regex(
5869
+ var feedbackIssueSchema = z11.string().trim().min(1).max(80).regex(
5781
5870
  /^[a-z0-9][a-z0-9_-]*$/,
5782
5871
  "Issue codes must use lowercase letters, numbers, underscores, or hyphens"
5783
5872
  );
5784
- var valuableSourceSchema = z10.object({
5785
- url: z10.string().url(),
5786
- reason: z10.string().max(1e3).optional()
5873
+ var valuableSourceSchema = z11.object({
5874
+ url: z11.string().url(),
5875
+ reason: z11.string().max(1e3).optional()
5787
5876
  });
5788
- var missingContentSchema = z10.object({
5789
- topic: z10.string().min(1, "topic must not be empty").max(200, "topic must be 200 characters or fewer"),
5790
- description: z10.string().max(2e3).optional()
5877
+ var missingContentSchema = z11.object({
5878
+ topic: z11.string().min(1, "topic must not be empty").max(200, "topic must be 200 characters or fewer"),
5879
+ description: z11.string().max(2e3).optional()
5791
5880
  });
5792
5881
  var FEEDBACK_DISABLED_VALUES = /* @__PURE__ */ new Set(["1", "true", "yes", "on"]);
5793
5882
  function feedbackEnvEnabled(...keys) {
@@ -5826,24 +5915,24 @@ Records schema-validated quality feedback for a prior \`firecrawl_search\` UUID
5826
5915
  Eligibility is limited to successful searches within the feedback age window. The record is idempotent per search ID. Eligible first feedback for a search can refund 1 credit; refunds are subject to the team's daily cap. The response reports whether a refund was applied, along with submission and daily-cap status.
5827
5916
  `,
5828
5917
  outputSchema: feedbackOutputSchema,
5829
- parameters: z10.object({
5830
- searchId: z10.string().uuid("searchId must be the UUID returned by firecrawl_search"),
5831
- rating: z10.enum(["good", "bad", "partial"]),
5832
- valuableSources: z10.array(
5833
- z10.object({
5834
- url: z10.string().url(),
5835
- reason: z10.string().max(1e3).optional()
5918
+ parameters: z11.object({
5919
+ searchId: z11.string().uuid("searchId must be the UUID returned by firecrawl_search"),
5920
+ rating: z11.enum(["good", "bad", "partial"]),
5921
+ valuableSources: z11.array(
5922
+ z11.object({
5923
+ url: z11.string().url(),
5924
+ reason: z11.string().max(1e3).optional()
5836
5925
  })
5837
5926
  ).max(50).optional(),
5838
- missingContent: z10.array(
5839
- z10.object({
5840
- topic: z10.string().min(1, "topic must not be empty").max(200, "topic must be 200 characters or fewer"),
5841
- description: z10.string().max(2e3).optional()
5927
+ missingContent: z11.array(
5928
+ z11.object({
5929
+ topic: z11.string().min(1, "topic must not be empty").max(200, "topic must be 200 characters or fewer"),
5930
+ description: z11.string().max(2e3).optional()
5842
5931
  })
5843
5932
  ).max(20).optional().describe(
5844
5933
  "Array of specific pieces of content the agent expected to find but did not. One entry per distinct topic. Each entry has a short `topic` and optional longer `description`."
5845
5934
  ),
5846
- querySuggestions: z10.string().max(2e3).optional()
5935
+ querySuggestions: z11.string().max(2e3).optional()
5847
5936
  }),
5848
5937
  execute: async (args2, { session, log, client: mcpClient }) => {
5849
5938
  const origin = requestOrigin(mcpClient, session);
@@ -5945,20 +6034,20 @@ For an Alexandria session, set endpoint to \`alexandria\`, omit jobId, and provi
5945
6034
  Returns submission status, feedback ID, and accounting fields.
5946
6035
  `,
5947
6036
  outputSchema: feedbackOutputSchema,
5948
- parameters: z10.object({
5949
- endpoint: z10.enum(["search", "scrape", "parse", "map", "alexandria"]),
5950
- jobId: z10.string().uuid("jobId must be the UUID returned by Firecrawl").optional(),
6037
+ parameters: z11.object({
6038
+ endpoint: z11.enum(["search", "scrape", "parse", "map", "alexandria"]),
6039
+ jobId: z11.string().uuid("jobId must be the UUID returned by Firecrawl").optional(),
5951
6040
  ...alexandriaFeedbackFields,
5952
- rating: z10.enum(["good", "bad", "partial"]),
5953
- issues: z10.array(feedbackIssueSchema).max(20).optional(),
5954
- tags: z10.array(feedbackIssueSchema).max(20).optional(),
5955
- note: z10.string().max(4e3).optional(),
5956
- valuableSources: z10.array(valuableSourceSchema).max(50).optional(),
5957
- missingContent: z10.array(missingContentSchema).max(50).optional(),
5958
- querySuggestions: z10.string().max(2e3).optional(),
5959
- url: z10.string().url().optional(),
5960
- pageNumbers: z10.array(z10.number().int().positive()).max(100).optional(),
5961
- metadata: z10.record(z10.string(), z10.unknown()).optional()
6041
+ rating: z11.enum(["good", "bad", "partial"]),
6042
+ issues: z11.array(feedbackIssueSchema).max(20).optional(),
6043
+ tags: z11.array(feedbackIssueSchema).max(20).optional(),
6044
+ note: z11.string().max(4e3).optional(),
6045
+ valuableSources: z11.array(valuableSourceSchema).max(50).optional(),
6046
+ missingContent: z11.array(missingContentSchema).max(50).optional(),
6047
+ querySuggestions: z11.string().max(2e3).optional(),
6048
+ url: z11.string().url().optional(),
6049
+ pageNumbers: z11.array(z11.number().int().positive()).max(100).optional(),
6050
+ metadata: z11.record(z11.string(), z11.unknown()).optional()
5962
6051
  }).superRefine((value, ctx) => {
5963
6052
  if (value.endpoint === "alexandria") {
5964
6053
  const parsed = alexandriaSessionFeedbackSchema.safeParse(value);
@@ -6070,25 +6159,25 @@ Start a multi-page crawl at a website URL, poll it to a terminal state, and retu
6070
6159
 
6071
6160
  Crawl results can be large; use conservative limits when full-site coverage is unnecessary. Webhooks and interactive scrape actions are unavailable in safe mode. Returns the crawl ID, status, and page data.
6072
6161
  `,
6073
- parameters: z10.object({
6074
- url: z10.string(),
6075
- prompt: z10.string().optional(),
6076
- excludePaths: z10.array(z10.string()).optional(),
6077
- includePaths: z10.array(z10.string()).optional(),
6078
- maxDiscoveryDepth: z10.number().optional(),
6079
- sitemap: z10.enum(["skip", "include", "only"]).optional(),
6080
- limit: z10.number().optional(),
6081
- allowExternalLinks: z10.boolean().optional(),
6082
- allowSubdomains: z10.boolean().optional(),
6083
- crawlEntireDomain: z10.boolean().optional(),
6084
- delay: z10.number().optional(),
6085
- maxConcurrency: z10.number().optional(),
6162
+ parameters: z11.object({
6163
+ url: z11.string(),
6164
+ prompt: z11.string().optional(),
6165
+ excludePaths: z11.array(z11.string()).optional(),
6166
+ includePaths: z11.array(z11.string()).optional(),
6167
+ maxDiscoveryDepth: z11.number().optional(),
6168
+ sitemap: z11.enum(["skip", "include", "only"]).optional(),
6169
+ limit: z11.number().optional(),
6170
+ allowExternalLinks: z11.boolean().optional(),
6171
+ allowSubdomains: z11.boolean().optional(),
6172
+ crawlEntireDomain: z11.boolean().optional(),
6173
+ delay: z11.number().optional(),
6174
+ maxConcurrency: z11.number().optional(),
6086
6175
  ...SAFE_MODE ? {} : {
6087
- webhook: z10.string().optional(),
6088
- webhookHeaders: z10.record(z10.string(), z10.string()).optional()
6176
+ webhook: z11.string().optional(),
6177
+ webhookHeaders: z11.record(z11.string(), z11.string()).optional()
6089
6178
  },
6090
- deduplicateSimilarURLs: z10.boolean().optional(),
6091
- ignoreQueryParameters: z10.boolean().optional(),
6179
+ deduplicateSimilarURLs: z11.boolean().optional(),
6180
+ ignoreQueryParameters: z11.boolean().optional(),
6092
6181
  scrapeOptions: scrapeParamsSchema.omit({ url: true }).partial().optional()
6093
6182
  }),
6094
6183
  outputSchema: crawlOutputSchema,
@@ -6145,7 +6234,7 @@ server.addTool({
6145
6234
  Retrieve the current status, progress, and available results for an existing crawl ID. This only reads Firecrawl job state and does not start or modify the crawl.
6146
6235
  `,
6147
6236
  outputSchema: crawlOutputSchema,
6148
- parameters: z10.object({ id: z10.string() }),
6237
+ parameters: z11.object({ id: z11.string() }),
6149
6238
  execute: async (args2, {
6150
6239
  session,
6151
6240
  client: mcpClient
@@ -6172,13 +6261,13 @@ server.addTool({
6172
6261
  Deprecated compatibility entry point. Use firecrawl_scrape once per known URL with formats: ["json"] and jsonOptions containing the prompt and schema. Use firecrawl_agent for multi-source research when the URLs are not known or the data spans several sites.
6173
6262
  `,
6174
6263
  outputSchema: deprecatedToolOutputSchema,
6175
- parameters: z10.object({
6176
- urls: z10.array(z10.string()),
6177
- prompt: z10.string().optional(),
6178
- schema: z10.record(z10.string(), z10.any()).optional(),
6179
- allowExternalLinks: z10.boolean().optional(),
6180
- enableWebSearch: z10.boolean().optional(),
6181
- includeSubdomains: z10.boolean().optional()
6264
+ parameters: z11.object({
6265
+ urls: z11.array(z11.string()),
6266
+ prompt: z11.string().optional(),
6267
+ schema: z11.record(z11.string(), z11.any()).optional(),
6268
+ allowExternalLinks: z11.boolean().optional(),
6269
+ enableWebSearch: z11.boolean().optional(),
6270
+ includeSubdomains: z11.boolean().optional()
6182
6271
  }),
6183
6272
  canList: () => false,
6184
6273
  beforeValidate: () => {
@@ -6194,25 +6283,25 @@ Deprecated compatibility entry point. Use firecrawl_scrape once per known URL wi
6194
6283
  throw new UserError(payload.message, payload);
6195
6284
  }
6196
6285
  });
6197
- var agentExchangeSchema = z10.strictObject({
6198
- enabled: z10.boolean().optional().describe("Let the agent call Alexandria providers. On by default."),
6199
- toolkits: z10.array(z10.string()).max(5).optional().describe("Pin up to 5 providers the agent may use, by slug. Omitted means the whole catalog."),
6200
- maxCalls: z10.number().int().min(1).max(30).optional().describe("Most provider calls the agent may make in this turn."),
6201
- requireApproval: z10.boolean().optional().describe(
6286
+ var agentExchangeSchema = z11.strictObject({
6287
+ enabled: z11.boolean().optional().describe("Let the agent call Alexandria providers. On by default."),
6288
+ toolkits: z11.array(z11.string()).max(5).optional().describe("Pin up to 5 providers the agent may use, by slug. Omitted means the whole catalog."),
6289
+ maxCalls: z11.number().int().min(1).max(30).optional().describe("Most provider calls the agent may make in this turn."),
6290
+ requireApproval: z11.boolean().optional().describe(
6202
6291
  'End the turn with a paid-call pendingApproval before any paid provider call. Requires mode "chat" on the same request, even on a follow-up.'
6203
6292
  ),
6204
- approve: z10.strictObject({
6205
- approvalId: z10.string().uuid(),
6206
- callIds: z10.array(z10.string()).optional(),
6207
- always: z10.boolean().optional()
6293
+ approve: z11.strictObject({
6294
+ approvalId: z11.string().uuid(),
6295
+ callIds: z11.array(z11.string()).optional(),
6296
+ always: z11.boolean().optional()
6208
6297
  }).optional().describe(
6209
- "Answer yes to the pendingApproval the previous turn of this thread ended on (its id, also exchange.requiresAction.approvalId). Needs threadId. For a terms offer, send it only after the user explicitly agreed and terms/accept succeeded; callIds and always are ignored on terms offers. For paid calls, callIds picks a subset (default all) and always stops asking for the rest of the thread."
6298
+ "Answer yes to the pendingApproval the previous turn of this thread ended on (its id, also exchange.requiresAction.approvalId). Needs threadId. For a terms offer, send it only after an organization admin confirms acceptance in the Firecrawl dashboard; this does not accept terms. callIds and always are ignored on terms offers. For paid calls, callIds picks a subset (default all) and always stops asking for the rest of the thread."
6210
6299
  ),
6211
- decline: z10.strictObject({ approvalId: z10.string().uuid() }).optional().describe(
6300
+ decline: z11.strictObject({ approvalId: z11.string().uuid() }).optional().describe(
6212
6301
  "Answer no to that pendingApproval. Needs threadId. A declined terms offer keeps those providers out of the rest of the thread."
6213
6302
  ),
6214
- onTermsRequired: z10.enum(["skip", "ask"]).optional().describe(
6215
- `What to do when a provider the agent would use needs data terms the team has not accepted. Gated providers are never called. "skip" (default): answer with accepted providers and list the rest in exchange.skippedProviders. "ask": the same, plus a terms pendingApproval and exchange.requiresAction with the terms/show and terms/accept calls. Each provider digest is string | null and always present; when null, terms/show returns it. There is no auto-accept. Omitted on a follow-up keeps the previous turn's value.`
6303
+ onTermsRequired: z11.enum(["skip", "ask"]).optional().describe(
6304
+ `What to do when a provider the agent would use needs data terms the team has not accepted. Gated providers are never called. "skip" (default): answer with accepted providers and list the rest in exchange.skippedProviders. "ask": the same, plus a terms pendingApproval and exchange.requiresAction. Read terms with terms/show; an organization admin accepts them in the Firecrawl dashboard. There is no auto-accept. Omitted on a follow-up keeps the previous turn's value.`
6216
6305
  )
6217
6306
  }).describe(
6218
6307
  "Alexandria provider settings for this turn, forwarded as the request's exchange object."
@@ -6235,24 +6324,24 @@ This call returns only a job ID, not the research result. Read the job with \`fi
6235
6324
 
6236
6325
  The job also returns a \`threadId\`. To continue that thread, pass it with a follow-up \`prompt\`; omitted \`mode\`, \`urls\`, \`schema\` and exchange settings carry over from the previous turn.
6237
6326
 
6238
- The agent only calls Alexandria providers whose data terms the team has accepted. The status result's \`exchange.skippedProviders\` lists gated providers that would have helped. With \`exchange.onTermsRequired\` "ask", a terms offer ends the turn: \`pendingApproval\` (kind "terms") and \`exchange.requiresAction\` carry the \`approvalId\` and the exact terms/show and terms/accept calls. Show the user the terms, get their EXPLICIT consent, run terms/accept through \`firecrawl_scrape\`, then call \`firecrawl_agent\` with the same \`threadId\` and \`exchange.approve: {approvalId}\`. If they decline, send \`exchange.decline: {approvalId}\` instead. Never call terms/accept without that consent; a data request is not consent.
6327
+ The agent only calls Alexandria providers whose terms the team has accepted. \`exchange.skippedProviders\` lists gated providers. With \`exchange.onTermsRequired\` "ask", a terms \`pendingApproval\` and \`exchange.requiresAction\` carry the \`approvalId\` and provider requirements. Read terms/show through \`firecrawl_scrape\`. An organization admin must accept terms in the Firecrawl dashboard at the provider URL or ${DATA_SOURCES_SETTINGS_URL}. Ignore any terms/accept call in the API response. Only after the admin confirms acceptance, resume with the same \`threadId\` and \`exchange.approve: {approvalId}\`; this does not accept terms. To decline, use \`exchange.decline: {approvalId}\`. Never infer acceptance from a data request.
6239
6328
  `,
6240
6329
  outputSchema: agentOutputSchema,
6241
- parameters: z10.object({
6242
- prompt: z10.string().min(1).max(1e4),
6243
- urls: z10.array(z10.string().url()).optional(),
6244
- schema: z10.record(z10.string(), z10.any()).optional(),
6245
- effort: z10.enum(["low", "medium", "high"]).optional().describe("Reasoning budget for the agent task."),
6246
- maxCredits: z10.number().int().positive().optional().describe(
6330
+ parameters: z11.object({
6331
+ prompt: z11.string().min(1).max(1e4),
6332
+ urls: z11.array(z11.string().url()).optional(),
6333
+ schema: z11.record(z11.string(), z11.any()).optional(),
6334
+ effort: z11.enum(["low", "medium", "high"]).optional().describe("Reasoning budget for the agent task."),
6335
+ maxCredits: z11.number().int().positive().optional().describe(
6247
6336
  "Spending limit in credits for this run. Defaults to 2500 on the API."
6248
6337
  ),
6249
- strictConstrainToURLs: z10.boolean().optional().describe(
6338
+ strictConstrainToURLs: z11.boolean().optional().describe(
6250
6339
  "If true, agent will only visit URLs provided in the urls array."
6251
6340
  ),
6252
- threadId: z10.string().uuid().optional().describe(
6341
+ threadId: z11.string().uuid().optional().describe(
6253
6342
  "Continue this thread: the threadId from an earlier firecrawl_agent or firecrawl_agent_status result. Omit to start a new thread."
6254
6343
  ),
6255
- mode: z10.enum(["extract", "chat"]).optional().describe(
6344
+ mode: z11.enum(["extract", "chat"]).optional().describe(
6256
6345
  `"extract" (default) returns the complete structured result every turn. "chat" lets a follow-up that asks no new data get a short reply in message instead of a re-run; required for exchange.requireApproval. Omitted on a follow-up keeps the previous turn's mode.`
6257
6346
  ),
6258
6347
  exchange: agentExchangeSchema.optional()
@@ -6322,7 +6411,7 @@ Retrieve progress or final results for a \`firecrawl_agent\` job ID. A \`process
6322
6411
  Returns job status, progress information, and result data when completed.
6323
6412
  `,
6324
6413
  outputSchema: agentStatusOutputSchema,
6325
- parameters: z10.object({ id: z10.string() }),
6414
+ parameters: z11.object({ id: z11.string() }),
6326
6415
  execute: async (args2, { session, log, client: mcpClient }) => {
6327
6416
  const client = getClient(session);
6328
6417
  const { id } = args2;
@@ -6331,9 +6420,41 @@ Returns job status, progress information, and result data when completed.
6331
6420
  `/v2/agent/${encodeURIComponent(id)}`,
6332
6421
  originHeaders(requestOrigin(mcpClient, session))
6333
6422
  );
6334
- return structuredText(res?.data ?? {});
6423
+ return agentStatusResult(res?.data ?? {});
6335
6424
  }
6336
6425
  });
6426
+ function agentStatusResult(data) {
6427
+ const run = data;
6428
+ if (!run || typeof run !== "object" || run.status !== "failed" || run.stopReason !== "credit_limit_reached") {
6429
+ return structuredText(data);
6430
+ }
6431
+ const lines = [
6432
+ "This agent run stopped at its credit limit (maxCredits) before finishing."
6433
+ ];
6434
+ const hasPartial = run.partial !== void 0 && run.partial !== null;
6435
+ if (hasPartial) {
6436
+ const validity = run.partialSchemaValid === true ? " It matches the schema this run was given." : run.partialSchemaValid === false ? " It does not match the schema this run was given." : "";
6437
+ lines.push(
6438
+ `\`partial\` is an INCOMPLETE best-effort result, not a finished answer; tell the user it is incomplete.${validity}`
6439
+ );
6440
+ } else {
6441
+ lines.push("No partial result was recovered.");
6442
+ }
6443
+ if (typeof run.message === "string" && run.message.trim()) {
6444
+ lines.push(`Agent message: ${run.message.trim()}`);
6445
+ }
6446
+ const threadId = typeof run.threadId === "string" ? run.threadId : void 0;
6447
+ lines.push(
6448
+ threadId ? `To continue, call firecrawl_agent with threadId "${threadId}" and a prompt to finish the task (the follow-up ${hasPartial ? "resumes from this partial" : "keeps this thread"}), or start a new run with a higher maxCredits.` : "To continue, start a new firecrawl_agent run with a higher maxCredits."
6449
+ );
6450
+ const notice = lines.join(" ");
6451
+ return withStructured(
6452
+ `${notice}
6453
+
6454
+ ${JSON.stringify(data, null, 2)}`,
6455
+ { ...run, notice }
6456
+ );
6457
+ }
6337
6458
  server.addTool({
6338
6459
  name: "firecrawl_interact",
6339
6460
  annotations: {
@@ -6351,13 +6472,13 @@ Open or reuse a live browser session to navigate a page, click controls, fill fi
6351
6472
  This acts on the live site, so actions such as form submission can create persistent external side effects. Returns execution output, stdout/stderr, exit status, and session viewing URLs.
6352
6473
  `,
6353
6474
  outputSchema: interactOutputSchema,
6354
- parameters: z10.object({
6355
- scrapeId: z10.string().trim().min(1).optional(),
6356
- url: z10.string().trim().url().optional(),
6357
- prompt: z10.string().trim().min(1).optional(),
6358
- code: z10.string().trim().min(1).optional(),
6359
- language: z10.enum(["bash", "python", "node"]).optional(),
6360
- timeout: z10.number().min(1).max(300).optional(),
6475
+ parameters: z11.object({
6476
+ scrapeId: z11.string().trim().min(1).optional(),
6477
+ url: z11.string().trim().url().optional(),
6478
+ prompt: z11.string().trim().min(1).optional(),
6479
+ code: z11.string().trim().min(1).optional(),
6480
+ language: z11.enum(["bash", "python", "node"]).optional(),
6481
+ timeout: z11.number().min(1).max(300).optional(),
6361
6482
  scrapeOptions: scrapeParamsSchema.omit({ url: true }).partial().optional()
6362
6483
  }).refine((data) => Boolean(data.scrapeId) !== Boolean(data.url), {
6363
6484
  message: "Provide either 'url' (interact directly) or 'scrapeId' (reuse a previous scrape), not both."
@@ -6436,8 +6557,8 @@ server.addTool({
6436
6557
  Stop the live interact session associated with a \`scrapeId\` and release its resources. Returns a success confirmation.
6437
6558
  `,
6438
6559
  outputSchema: interactStopOutputSchema,
6439
- parameters: z10.object({
6440
- scrapeId: z10.string()
6560
+ parameters: z11.object({
6561
+ scrapeId: z11.string()
6441
6562
  }),
6442
6563
  execute: async (args2, { session, log, client: mcpClient }) => {
6443
6564
  const client = getClient(session);
@@ -6552,22 +6673,26 @@ function registerMarketplaceSearchTool(registrar, getClientFn) {
6552
6673
  destructiveHint: false
6553
6674
  },
6554
6675
  description: `
6555
- Search web and specialized indexes, returning ranked results with query-relevant highlights. Each web result is a title, URL, and description. Operators include quoted phrases, \`-term\`, \`site:host\`, \`inurl:term\`, \`intitle:term\`, and \`related:host\`; the set is non-exhaustive. \`includeDomains\` and \`excludeDomains\` are mutually exclusive hostname filters; categories limit result types to \`research\`, \`pdf\`, or \`developer\`.
6676
+ Search web and specialized indexes, returning ranked results with query-relevant highlights. Each web result is a title, URL, and description. Operators include quoted phrases, \`-term\`, \`site:host\`, \`inurl:term\`, \`intitle:term\`, and \`related:host\`; the set is non-exhaustive. \`includeDomains\` and \`excludeDomains\` are mutually exclusive hostname filters; categories limit result types to \`research\`, \`pdf\`, \`developer\`, or \`gov\`.
6556
6677
 
6557
6678
  For a programming question, add \`categories: ["developer"]\`. It searches an index of public repositories, GitHub issues, merged pull requests, repository READMEs, and code documentation, and returns the results in \`data.web\` with \`category: "developer"\`.
6558
6679
 
6680
+ For a legal or regulatory question, \`categories: ["gov"]\` returns results in \`data.web\` with \`category: "gov"\` and cannot be combined with other categories; \`firecrawl_gov_search\` is the dedicated tool.
6681
+
6559
6682
  ${ALEXANDRIA_SEARCH_INSTRUCTIONS}
6560
6683
 
6561
6684
  Returns result groups in \`data\` and an operation \`id\`.
6562
6685
  `,
6563
6686
  outputSchema: searchOutputSchema,
6564
- parameters: z10.object({ ...searchToolBaseFields }).strict().refine(searchDomainsAreExclusive, SEARCH_DOMAINS_CONFLICT_MESSAGE).refine(
6687
+ parameters: z11.object({ ...searchToolBaseFields }).strict().refine(searchDomainsAreExclusive, SEARCH_DOMAINS_CONFLICT_MESSAGE).refine(
6565
6688
  searchQueryIsValid,
6566
6689
  "A query is required. Use firecrawl_find_tools to browse the catalogue."
6567
6690
  ),
6568
6691
  execute: async (args2, { session, log, client: mcpClient }) => {
6569
6692
  const {
6570
6693
  query,
6694
+ objective,
6695
+ clientModel,
6571
6696
  includeDomains,
6572
6697
  excludeDomains,
6573
6698
  limit,
@@ -6585,6 +6710,8 @@ Returns result groups in \`data\` and an operation \`id\`.
6585
6710
  const searchBody = {
6586
6711
  query: searchQuery,
6587
6712
  ...removeEmptyTopLevel({
6713
+ objective,
6714
+ clientModel,
6588
6715
  limit,
6589
6716
  includeDomains,
6590
6717
  excludeDomains,
@@ -6637,6 +6764,7 @@ if (process.env.CLOUD_SERVICE === "true" || process.env.SSE_LOCAL === "true" ||
6637
6764
  registerMonitorTools(server);
6638
6765
  registerResearchTools(server, getClient);
6639
6766
  registerDeveloperTools(server, getClient);
6767
+ registerGovTools(server, getClient);
6640
6768
  registerUsageTools(server, getClient);
6641
6769
  if (process.env.CLOUD_SERVICE === "true" && primaryProfile.allowKeyless && !normalizeHeader(process.env.KEYLESS_PROXY_SECRET)) {
6642
6770
  console.warn(
@@ -6671,6 +6799,7 @@ if (searchProfileEnabled) {
6671
6799
  };
6672
6800
  registerResearchTools(searchRegistrar, getClient);
6673
6801
  registerDeveloperTools(searchRegistrar, getClient);
6802
+ registerGovTools(searchRegistrar, getClient);
6674
6803
  registerMarketplaceSearchTool(searchRegistrar, getClient);
6675
6804
  searchRegistrar.addTool(searchSurfaceFindToolsTool);
6676
6805
  searchRegistrar.addTool(searchSurfaceScrapeTool);