firecrawl-mcp 3.9.0 → 3.10.2

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 +7 -2
  2. package/dist/index.js +122 -50
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -818,19 +818,23 @@ Check the status of an agent job and retrieve results when complete. Use this to
818
818
 
819
819
  ### 11. Browser Create (`firecrawl_browser_create`)
820
820
 
821
- Create a persistent cloud browser session for interactive automation.
821
+ Create a cloud browser session for interactive automation.
822
822
 
823
823
  **Best for:**
824
824
 
825
825
  - Multi-step browser automation (navigate, click, fill forms, extract data)
826
826
  - Interactive workflows that require maintaining state across actions
827
827
  - Testing and debugging web pages in a live browser
828
+ - Saving and reusing browser state with profiles
828
829
 
829
830
  **Arguments:**
830
831
 
831
832
  - `ttl`: Total session lifetime in seconds (30-3600, optional)
832
833
  - `activityTtl`: Idle timeout in seconds (10-3600, optional)
833
834
  - `streamWebView`: Whether to enable live view streaming (optional)
835
+ - `profile`: Save and reuse browser state across sessions (optional)
836
+ - `name`: Profile name (sessions with the same name share state)
837
+ - `saveChanges`: Whether to save changes back to the profile (default: true)
834
838
 
835
839
  **Usage Example:**
836
840
 
@@ -838,7 +842,8 @@ Create a persistent cloud browser session for interactive automation.
838
842
  {
839
843
  "name": "firecrawl_browser_create",
840
844
  "arguments": {
841
- "ttl": 600
845
+ "ttl": 600,
846
+ "profile": { "name": "my-profile", "saveChanges": true }
842
847
  }
843
848
  }
844
849
  ```
package/dist/index.js CHANGED
@@ -142,43 +142,100 @@ const otherActions = [
142
142
  const allActionTypes = [...safeActionTypes, ...otherActions];
143
143
  // Use appropriate action types based on safe mode
144
144
  const allowedActionTypes = SAFE_MODE ? safeActionTypes : allActionTypes;
145
+ function buildFormatsArray(args) {
146
+ const formats = args.formats;
147
+ if (!formats || formats.length === 0)
148
+ return undefined;
149
+ const result = [];
150
+ for (const fmt of formats) {
151
+ if (fmt === 'json') {
152
+ const jsonOpts = args.jsonOptions;
153
+ result.push({ type: 'json', ...jsonOpts });
154
+ }
155
+ else if (fmt === 'screenshot' && args.screenshotOptions) {
156
+ const ssOpts = args.screenshotOptions;
157
+ result.push({ type: 'screenshot', ...ssOpts });
158
+ }
159
+ else {
160
+ result.push(fmt);
161
+ }
162
+ }
163
+ return result;
164
+ }
165
+ function buildParsersArray(args) {
166
+ const parsers = args.parsers;
167
+ if (!parsers || parsers.length === 0)
168
+ return undefined;
169
+ const result = [];
170
+ for (const p of parsers) {
171
+ if (p === 'pdf' && args.pdfOptions) {
172
+ const pdfOpts = args.pdfOptions;
173
+ result.push({ type: 'pdf', ...pdfOpts });
174
+ }
175
+ else {
176
+ result.push(p);
177
+ }
178
+ }
179
+ return result;
180
+ }
181
+ function buildWebhook(args) {
182
+ const webhook = args.webhook;
183
+ if (!webhook)
184
+ return undefined;
185
+ const headers = args.webhookHeaders;
186
+ if (headers && Object.keys(headers).length > 0) {
187
+ return { url: webhook, headers };
188
+ }
189
+ return webhook;
190
+ }
191
+ function transformScrapeParams(args) {
192
+ const out = { ...args };
193
+ const formats = buildFormatsArray(out);
194
+ if (formats)
195
+ out.formats = formats;
196
+ const parsers = buildParsersArray(out);
197
+ if (parsers)
198
+ out.parsers = parsers;
199
+ delete out.jsonOptions;
200
+ delete out.screenshotOptions;
201
+ delete out.pdfOptions;
202
+ return out;
203
+ }
145
204
  const scrapeParamsSchema = z.object({
146
205
  url: z.string().url(),
147
206
  formats: z
148
- .array(z.union([
149
- z.enum([
150
- 'markdown',
151
- 'html',
152
- 'rawHtml',
153
- 'screenshot',
154
- 'links',
155
- 'summary',
156
- 'changeTracking',
157
- 'branding',
158
- ]),
159
- z.object({
160
- type: z.literal('json'),
161
- prompt: z.string().optional(),
162
- schema: z.record(z.string(), z.any()).optional(),
163
- }),
164
- z.object({
165
- type: z.literal('screenshot'),
166
- fullPage: z.boolean().optional(),
167
- quality: z.number().optional(),
168
- viewport: z
169
- .object({ width: z.number(), height: z.number() })
170
- .optional(),
171
- }),
207
+ .array(z.enum([
208
+ 'markdown',
209
+ 'html',
210
+ 'rawHtml',
211
+ 'screenshot',
212
+ 'links',
213
+ 'summary',
214
+ 'changeTracking',
215
+ 'branding',
216
+ 'json',
172
217
  ]))
173
218
  .optional(),
174
- parsers: z
175
- .array(z.union([
176
- z.enum(['pdf']),
177
- z.object({
178
- type: z.enum(['pdf']),
179
- maxPages: z.number().int().min(1).max(10000).optional(),
180
- }),
181
- ]))
219
+ jsonOptions: z
220
+ .object({
221
+ prompt: z.string().optional(),
222
+ schema: z.record(z.string(), z.any()).optional(),
223
+ })
224
+ .optional(),
225
+ screenshotOptions: z
226
+ .object({
227
+ fullPage: z.boolean().optional(),
228
+ quality: z.number().optional(),
229
+ viewport: z
230
+ .object({ width: z.number(), height: z.number() })
231
+ .optional(),
232
+ })
233
+ .optional(),
234
+ parsers: z.array(z.enum(['pdf'])).optional(),
235
+ pdfOptions: z
236
+ .object({
237
+ maxPages: z.number().int().min(1).max(10000).optional(),
238
+ })
182
239
  .optional(),
183
240
  onlyMainContent: z.boolean().optional(),
184
241
  includeTags: z.array(z.string()).optional(),
@@ -254,8 +311,8 @@ If JSON extraction returns empty, minimal, or just navigation content, the page
254
311
  "name": "firecrawl_scrape",
255
312
  "arguments": {
256
313
  "url": "https://example.com/api-docs",
257
- "formats": [{
258
- "type": "json",
314
+ "formats": ["json"],
315
+ "jsonOptions": {
259
316
  "prompt": "Extract the header parameters for the authentication endpoint",
260
317
  "schema": {
261
318
  "type": "object",
@@ -274,7 +331,7 @@ If JSON extraction returns empty, minimal, or just navigation content, the page
274
331
  }
275
332
  }
276
333
  }
277
- }]
334
+ }
278
335
  }
279
336
  }
280
337
  \`\`\`
@@ -310,7 +367,8 @@ ${SAFE_MODE
310
367
  execute: async (args, { session, log }) => {
311
368
  const { url, ...options } = args;
312
369
  const client = getClient(session);
313
- const cleaned = removeEmptyTopLevel(options);
370
+ const transformed = transformScrapeParams(options);
371
+ const cleaned = removeEmptyTopLevel(transformed);
314
372
  log.info('Scraping URL', { url: String(url) });
315
373
  const res = await client.scrape(String(url), {
316
374
  ...cleaned,
@@ -450,7 +508,11 @@ The query also supports search operators, that you can use if needed to refine t
450
508
  execute: async (args, { session, log }) => {
451
509
  const client = getClient(session);
452
510
  const { query, ...opts } = args;
453
- const cleaned = removeEmptyTopLevel(opts);
511
+ const searchOpts = { ...opts };
512
+ if (searchOpts.scrapeOptions) {
513
+ searchOpts.scrapeOptions = transformScrapeParams(searchOpts.scrapeOptions);
514
+ }
515
+ const cleaned = removeEmptyTopLevel(searchOpts);
454
516
  log.info('Searching', { query: String(query) });
455
517
  const res = await client.search(query, {
456
518
  ...cleaned,
@@ -504,15 +566,8 @@ server.addTool({
504
566
  ...(SAFE_MODE
505
567
  ? {}
506
568
  : {
507
- webhook: z
508
- .union([
509
- z.string(),
510
- z.object({
511
- url: z.string(),
512
- headers: z.record(z.string(), z.string()).optional(),
513
- }),
514
- ])
515
- .optional(),
569
+ webhook: z.string().optional(),
570
+ webhookHeaders: z.record(z.string(), z.string()).optional(),
516
571
  }),
517
572
  deduplicateSimilarURLs: z.boolean().optional(),
518
573
  ignoreQueryParameters: z.boolean().optional(),
@@ -521,7 +576,15 @@ server.addTool({
521
576
  execute: async (args, { session, log }) => {
522
577
  const { url, ...options } = args;
523
578
  const client = getClient(session);
524
- const cleaned = removeEmptyTopLevel(options);
579
+ const opts = { ...options };
580
+ if (opts.scrapeOptions) {
581
+ opts.scrapeOptions = transformScrapeParams(opts.scrapeOptions);
582
+ }
583
+ const webhook = buildWebhook(opts);
584
+ if (webhook)
585
+ opts.webhook = webhook;
586
+ delete opts.webhookHeaders;
587
+ const cleaned = removeEmptyTopLevel(opts);
525
588
  log.info('Starting crawl', { url: String(url) });
526
589
  const res = await client.crawl(String(url), {
527
590
  ...cleaned,
@@ -750,21 +813,26 @@ Check the status of an agent job and retrieve results when complete. Use this to
750
813
  server.addTool({
751
814
  name: 'firecrawl_browser_create',
752
815
  description: `
753
- Create a persistent browser session for code execution via CDP (Chrome DevTools Protocol).
816
+ Create a browser session for code execution via CDP (Chrome DevTools Protocol).
754
817
 
755
- **Best for:** Running code (Python/JS) that interacts with a live browser page, multi-step browser automation, persistent sessions that survive across multiple tool calls.
818
+ **Best for:** Running code (Python/JS) that interacts with a live browser page, multi-step browser automation, sessions with profiles that survive across multiple tool calls.
756
819
  **Not recommended for:** Simple page scraping (use firecrawl_scrape instead).
757
820
 
758
821
  **Arguments:**
759
822
  - ttl: Total session lifetime in seconds (30-3600, optional)
760
823
  - activityTtl: Idle timeout in seconds (10-3600, optional)
761
824
  - streamWebView: Whether to enable live view streaming (optional)
825
+ - profile: Save and reuse browser state (cookies, localStorage) across sessions (optional)
826
+ - name: Profile name (sessions with the same name share state)
827
+ - saveChanges: Whether to save changes back to the profile (default: true)
762
828
 
763
829
  **Usage Example:**
764
830
  \`\`\`json
765
831
  {
766
832
  "name": "firecrawl_browser_create",
767
- "arguments": {}
833
+ "arguments": {
834
+ "profile": { "name": "my-profile", "saveChanges": true }
835
+ }
768
836
  }
769
837
  \`\`\`
770
838
  **Returns:** Session ID, CDP URL, and live view URL.
@@ -773,6 +841,10 @@ Create a persistent browser session for code execution via CDP (Chrome DevTools
773
841
  ttl: z.number().min(30).max(3600).optional(),
774
842
  activityTtl: z.number().min(10).max(3600).optional(),
775
843
  streamWebView: z.boolean().optional(),
844
+ profile: z.object({
845
+ name: z.string().min(1).max(128),
846
+ saveChanges: z.boolean().default(true),
847
+ }).optional(),
776
848
  }),
777
849
  execute: async (args, { session, log }) => {
778
850
  const client = getClient(session);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "firecrawl-mcp",
3
- "version": "3.9.0",
3
+ "version": "3.10.2",
4
4
  "description": "MCP server for Firecrawl web scraping integration. Supports both cloud and self-hosted instances. Features include web scraping, search, batch processing, structured data extraction, and LLM-powered content analysis.",
5
5
  "type": "module",
6
6
  "mcpName": "io.github.firecrawl/firecrawl-mcp-server",