diff --git a/README.md b/README.md index 2e96e3d..7d9b032 100644 --- a/README.md +++ b/README.md @@ -17,6 +17,7 @@ A Model Context Protocol (MCP) server implementation that integrates with [Firec - Web scraping, crawling, and discovery - Search and content extraction - Deep research and batch scraping +- Cloud browser sessions with agent-browser automation - Automatic retries and rate limiting - Cloud and self-hosted support - SSE support @@ -316,6 +317,7 @@ Use this guide to select the right tool for your task: - **If you want to search the web for info:** use **search** - **If you need complex research across multiple unknown sources:** use **agent** - **If you want to analyze a whole site or section:** use **crawl** (with limits!) +- **If you need interactive browser automation** (click, type, navigate): use **browser** ### Quick Reference Table @@ -327,6 +329,7 @@ Use this guide to select the right tool for your task: | crawl | Multi-page extraction (with limits) | markdown/html[] | | search | Web search for info | results[] | | agent | Complex multi-source research | JSON (structured data) | +| browser | Interactive multi-step automation | Session with live browser | ### Format Selection Guide @@ -813,6 +816,105 @@ Check the status of an agent job and retrieve results when complete. Use this to - `completed`: Research finished - response includes the extracted data - `failed`: An error occurred +### 11. Browser Create (`firecrawl_browser_create`) + +Create a persistent cloud browser session for interactive automation. + +**Best for:** + +- Multi-step browser automation (navigate, click, fill forms, extract data) +- Interactive workflows that require maintaining state across actions +- Testing and debugging web pages in a live browser + +**Arguments:** + +- `ttl`: Total session lifetime in seconds (30-3600, optional) +- `activityTtl`: Idle timeout in seconds (10-3600, optional) +- `streamWebView`: Whether to enable live view streaming (optional) + +**Usage Example:** + +```json +{ + "name": "firecrawl_browser_create", + "arguments": { + "ttl": 600 + } +} +``` + +**Returns:** + +- Session ID, CDP URL, and live view URL + +### 12. Browser Execute (`firecrawl_browser_execute`) + +Execute code in a browser session. Supports agent-browser commands (bash), Python, or JavaScript. + +**Recommended: Use bash with agent-browser commands** (pre-installed in every sandbox): + +```json +{ + "name": "firecrawl_browser_execute", + "arguments": { + "sessionId": "session-id-here", + "code": "agent-browser open https://example.com", + "language": "bash" + } +} +``` + +**Common agent-browser commands:** + +| Command | Description | +|---------|-------------| +| `agent-browser open ` | Navigate to URL | +| `agent-browser snapshot` | Accessibility tree with clickable refs | +| `agent-browser click @e5` | Click element by ref from snapshot | +| `agent-browser type @e3 "text"` | Type into element | +| `agent-browser get title` | Get page title | +| `agent-browser screenshot` | Take screenshot | +| `agent-browser --help` | Full command reference | + +**For Playwright scripting, use Python:** + +```json +{ + "name": "firecrawl_browser_execute", + "arguments": { + "sessionId": "session-id-here", + "code": "await page.goto('https://example.com')\ntitle = await page.title()\nprint(title)", + "language": "python" + } +} +``` + +### 13. Browser List (`firecrawl_browser_list`) + +List browser sessions, optionally filtered by status. + +```json +{ + "name": "firecrawl_browser_list", + "arguments": { + "status": "active" + } +} +``` + +### 14. Browser Delete (`firecrawl_browser_delete`) + +Destroy a browser session. + +```json +{ + "name": "firecrawl_browser_delete", + "arguments": { + "sessionId": "session-id-here" + } +} +``` + ## Logging System The server includes comprehensive logging: diff --git a/package.json b/package.json index a033b3b..fa72ff8 100644 --- a/package.json +++ b/package.json @@ -28,7 +28,7 @@ }, "license": "MIT", "dependencies": { - "@mendable/firecrawl-js": "^4.9.3", + "@mendable/firecrawl-js": "4.13.0", "dotenv": "^17.2.2", "firecrawl-fastmcp": "^1.0.4", "typescript": "^5.9.2", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 4919185..472f327 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -9,8 +9,8 @@ importers: .: dependencies: '@mendable/firecrawl-js': - specifier: ^4.9.3 - version: 4.11.1 + specifier: 4.13.0-beta.3 + version: 4.13.0-beta.3 dotenv: specifier: ^17.2.2 version: 17.2.2 @@ -33,8 +33,8 @@ packages: '@borewit/text-codec@0.1.1': resolution: {integrity: sha512-5L/uBxmjaCIX5h8Z+uu+kA9BQLkc/Wl06UGR5ajNRxu+/XjonB5i8JpgFMrPj3LXTCPA0pv8yxUvbUi+QthGGA==} - '@mendable/firecrawl-js@4.11.1': - resolution: {integrity: sha512-GBfnP0fFw25185ZCyPNhfgmBdpWxRy2Q3cXhBWSUixmkVLCaFHnhX/vekYcrQBeWjfJbZC2dSukHtIPZGNivoA==} + '@mendable/firecrawl-js@4.13.0-beta.3': + resolution: {integrity: sha512-OD8PmuQ1HdOanLub2xXVg4uCq1rJndlb/yuRYO1tUn697+ckgp0E/4yk3V49YoQM2ArW3/lIHCOKR/ifxp2ADg==} engines: {node: '>=22.0.0'} '@modelcontextprotocol/sdk@1.18.0': @@ -79,8 +79,8 @@ packages: asynckit@0.4.0: resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} - axios@1.12.2: - resolution: {integrity: sha512-vMJzPewAlRyOgxV2dU0Cuz2O8zzzx9VYtbJOaBgXFeLc4IV/Eg50n4LowmehOOR61S8ZMpc2K5Sa7g6A4jfkUw==} + axios@1.13.5: + resolution: {integrity: sha512-cz4ur7Vb0xS4/KUN0tPWe44eqxrIu31me+fbang3ijiNscE129POzipJJA6zniq2C/Z6sJCjMimjS8Lc/GAs8Q==} body-parser@2.2.0: resolution: {integrity: sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg==} @@ -248,8 +248,8 @@ packages: debug: optional: true - form-data@4.0.4: - resolution: {integrity: sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==} + form-data@4.0.5: + resolution: {integrity: sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==} engines: {node: '>= 6'} forwarded@0.2.0: @@ -645,9 +645,9 @@ snapshots: '@borewit/text-codec@0.1.1': {} - '@mendable/firecrawl-js@4.11.1': + '@mendable/firecrawl-js@4.13.0-beta.3': dependencies: - axios: 1.12.2 + axios: 1.13.5 typescript-event-target: 1.1.1 zod: 3.25.76 zod-to-json-schema: 3.24.6(zod@3.25.76) @@ -709,10 +709,10 @@ snapshots: asynckit@0.4.0: {} - axios@1.12.2: + axios@1.13.5: dependencies: follow-redirects: 1.15.11 - form-data: 4.0.4 + form-data: 4.0.5 proxy-from-env: 1.1.0 transitivePeerDependencies: - debug @@ -927,7 +927,7 @@ snapshots: follow-redirects@1.15.11: {} - form-data@4.0.4: + form-data@4.0.5: dependencies: asynckit: 0.4.0 combined-stream: 1.0.8 diff --git a/src/index.ts b/src/index.ts index 01af840..fcdc297 100644 --- a/src/index.ts +++ b/src/index.ts @@ -832,6 +832,187 @@ Check the status of an agent job and retrieve results when complete. Use this to }, }); +// Browser session tools +server.addTool({ + name: 'firecrawl_browser_create', + description: ` +Create a persistent browser session for code execution via CDP (Chrome DevTools Protocol). + +**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. +**Not recommended for:** Simple page scraping (use firecrawl_scrape instead). + +**Arguments:** +- ttl: Total session lifetime in seconds (30-3600, optional) +- activityTtl: Idle timeout in seconds (10-3600, optional) +- streamWebView: Whether to enable live view streaming (optional) + +**Usage Example:** +\`\`\`json +{ + "name": "firecrawl_browser_create", + "arguments": {} +} +\`\`\` +**Returns:** Session ID, CDP URL, and live view URL. +`, + parameters: z.object({ + ttl: z.number().min(30).max(3600).optional(), + activityTtl: z.number().min(10).max(3600).optional(), + streamWebView: z.boolean().optional(), + }), + execute: async ( + args: unknown, + { session, log }: { session?: SessionData; log: Logger } + ): Promise => { + const client = getClient(session); + const a = args as Record; + const cleaned = removeEmptyTopLevel(a); + log.info('Creating browser session'); + const res = await client.browser(cleaned as any); + return asText(res); + }, +}); + +if (!SAFE_MODE) { + server.addTool({ + name: 'firecrawl_browser_execute', + description: ` +Execute code in a browser session. Supports agent-browser commands (bash), Python, or JavaScript. + +**Best for:** Browser automation, navigating pages, clicking elements, extracting data, multi-step browser workflows. +**Requires:** An active browser session (create one with firecrawl_browser_create first). + +**Arguments:** +- sessionId: The browser session ID (required) +- code: The code to execute (required) +- language: "bash", "python", or "node" (optional, defaults to "bash") + +**Recommended: Use bash with agent-browser commands** (pre-installed in every sandbox): +\`\`\`json +{ + "name": "firecrawl_browser_execute", + "arguments": { + "sessionId": "session-id-here", + "code": "agent-browser open https://example.com", + "language": "bash" + } +} +\`\`\` + +**Common agent-browser commands:** +- \`agent-browser open \` — Navigate to URL +- \`agent-browser snapshot\` — Get accessibility tree with clickable refs (for AI) +- \`agent-browser snapshot -i -c\` — Interactive elements only, compact +- \`agent-browser click @e5\` — Click element by ref from snapshot +- \`agent-browser type @e3 "text"\` — Type into element +- \`agent-browser fill @e3 "text"\` — Clear and fill element +- \`agent-browser get text @e1\` — Get text content +- \`agent-browser get title\` — Get page title +- \`agent-browser get url\` — Get current URL +- \`agent-browser screenshot [path]\` — Take screenshot +- \`agent-browser scroll down\` — Scroll page +- \`agent-browser wait 2000\` — Wait 2 seconds +- \`agent-browser --help\` — Full command reference + +**For Playwright scripting, use Python** (has proper async/await support): +\`\`\`json +{ + "name": "firecrawl_browser_execute", + "arguments": { + "sessionId": "session-id-here", + "code": "await page.goto('https://example.com')\\ntitle = await page.title()\\nprint(title)", + "language": "python" + } +} +\`\`\` + +**Note:** Prefer bash (agent-browser) or Python. +**Returns:** Execution result including stdout, stderr, and exit code. +`, + parameters: z.object({ + sessionId: z.string(), + code: z.string(), + language: z.enum(['bash', 'python', 'node']).optional(), + }), + execute: async ( + args: unknown, + { session, log }: { session?: SessionData; log: Logger } + ): Promise => { + const client = getClient(session); + const { sessionId, code, language } = args as { + sessionId: string; + code: string; + language?: 'python' | 'node' | 'bash'; + }; + log.info('Executing code in browser session', { sessionId }); + const res = await client.browserExecute(sessionId, { code, language }); + return asText(res); + }, + }); +} + +server.addTool({ + name: 'firecrawl_browser_delete', + description: ` +Destroy a browser session. + +**Usage Example:** +\`\`\`json +{ + "name": "firecrawl_browser_delete", + "arguments": { + "sessionId": "session-id-here" + } +} +\`\`\` +**Returns:** Success confirmation. +`, + parameters: z.object({ + sessionId: z.string(), + }), + execute: async ( + args: unknown, + { session, log }: { session?: SessionData; log: Logger } + ): Promise => { + const client = getClient(session); + const { sessionId } = args as { sessionId: string }; + log.info('Deleting browser session', { sessionId }); + const res = await client.deleteBrowser(sessionId); + return asText(res); + }, +}); + +server.addTool({ + name: 'firecrawl_browser_list', + description: ` +List browser sessions, optionally filtered by status. + +**Usage Example:** +\`\`\`json +{ + "name": "firecrawl_browser_list", + "arguments": { + "status": "active" + } +} +\`\`\` +**Returns:** Array of browser sessions. +`, + parameters: z.object({ + status: z.enum(['active', 'destroyed']).optional(), + }), + execute: async ( + args: unknown, + { session, log }: { session?: SessionData; log: Logger } + ): Promise => { + const client = getClient(session); + const { status } = args as { status?: 'active' | 'destroyed' }; + log.info('Listing browser sessions', { status }); + const res = await client.listBrowsers({ status }); + return asText(res); + }, +}); + const PORT = Number(process.env.PORT || 3000); const HOST = process.env.CLOUD_SERVICE === 'true'