From a51190a3f0e49620c292155c2b27f9def6bc0dfb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?In=C3=AAs=20Moital?= Date: Mon, 2 Nov 2026 18:25:24 +0000 Subject: [PATCH 1/5] feat(cursor): plugin for Cursor in the shared skills layout Moves cursor-zenrows (v0.2.2) into platforms/cursor. Cursor now gets the shared skills, which drops the old scrape(mode='auto') guidance that scrape no longer accepts, and ships the shared using-zenrows skill instead of the always-on rules/zenrows.mdc. The /zenrows-doctor command stops passing mode, checks the Crawl tools, reports usage with account_usage, and tells AUTH004 and AUTH014 (credits used up) apart from a missing login. Contact email is integrations@zenrows.com. --- .release-please-manifest.json | 3 +- README.md | 1 + dist/cursor/.cursor-plugin/plugin.json | 30 ++++++ dist/cursor/LICENSE | 21 ++++ dist/cursor/README.md | 56 +++++++++++ dist/cursor/assets/logo.png | Bin 0 -> 4959 bytes dist/cursor/commands/zenrows-doctor.md | 16 +++ dist/cursor/mcp.json | 7 ++ dist/cursor/skills/batch/SKILL.md | 45 +++++++++ .../cursor/skills/browser-automation/SKILL.md | 60 +++++++++++ dist/cursor/skills/capture-api-calls/SKILL.md | 35 +++++++ dist/cursor/skills/crawl/SKILL.md | 30 ++++++ .../skills/extract-structured-data/SKILL.md | 44 ++++++++ dist/cursor/skills/getting-started/SKILL.md | 40 ++++++++ dist/cursor/skills/map/SKILL.md | 22 ++++ dist/cursor/skills/scrape-webpage/SKILL.md | 17 ++++ dist/cursor/skills/using-zenrows/SKILL.md | 95 ++++++++++++++++++ platforms/cursor/.cursor-plugin/plugin.json | 30 ++++++ platforms/cursor/README.md | 56 +++++++++++ platforms/cursor/commands/zenrows-doctor.md | 16 +++ platforms/cursor/mcp.json | 7 ++ .../cursor/skills/getting-started/connect.md | 3 + platforms/cursor/vars.yaml | 18 ++++ release-please-config.json | 11 ++ 24 files changed, 662 insertions(+), 1 deletion(-) create mode 100644 dist/cursor/.cursor-plugin/plugin.json create mode 100644 dist/cursor/LICENSE create mode 100644 dist/cursor/README.md create mode 100644 dist/cursor/assets/logo.png create mode 100644 dist/cursor/commands/zenrows-doctor.md create mode 100644 dist/cursor/mcp.json create mode 100644 dist/cursor/skills/batch/SKILL.md create mode 100644 dist/cursor/skills/browser-automation/SKILL.md create mode 100644 dist/cursor/skills/capture-api-calls/SKILL.md create mode 100644 dist/cursor/skills/crawl/SKILL.md create mode 100644 dist/cursor/skills/extract-structured-data/SKILL.md create mode 100644 dist/cursor/skills/getting-started/SKILL.md create mode 100644 dist/cursor/skills/map/SKILL.md create mode 100644 dist/cursor/skills/scrape-webpage/SKILL.md create mode 100644 dist/cursor/skills/using-zenrows/SKILL.md create mode 100644 platforms/cursor/.cursor-plugin/plugin.json create mode 100644 platforms/cursor/README.md create mode 100644 platforms/cursor/commands/zenrows-doctor.md create mode 100644 platforms/cursor/mcp.json create mode 100644 platforms/cursor/skills/getting-started/connect.md create mode 100644 platforms/cursor/vars.yaml diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 9a9ceb4..a0d8bcd 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,4 @@ { - "dist/copilot": "0.1.1" + "dist/copilot": "0.1.1", + "dist/cursor": "0.2.2" } diff --git a/README.md b/README.md index 93c61ef..1a00553 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,7 @@ Each host's guide covers installing the plugin and signing in to Zenrows. You si | Host | Setup guide | |---|---| +| Cursor | [dist/cursor/README.md](dist/cursor/README.md) | | GitHub Copilot | [dist/copilot/README.md](dist/copilot/README.md) | diff --git a/dist/cursor/.cursor-plugin/plugin.json b/dist/cursor/.cursor-plugin/plugin.json new file mode 100644 index 0000000..ba9bcf4 --- /dev/null +++ b/dist/cursor/.cursor-plugin/plugin.json @@ -0,0 +1,30 @@ +{ + "name": "zenrows", + "displayName": "Zenrows", + "version": "0.2.2", + "description": "Web data for Cursor: scrape, crawl, map, browser automation, and structured extraction, straight from your AI agent. Powered by Zenrows Fetch and Browser Sessions.", + "author": { + "name": "Zenrows", + "email": "integrations@zenrows.com" + }, + "homepage": "https://www.zenrows.com", + "repository": "https://github.com/ZenRows/zenrows-integrations", + "license": "MIT", + "keywords": [ + "web-scraping", + "crawler", + "site-map", + "browser-automation", + "data-extraction", + "anti-bot", + "mcp", + "scraping-api", + "zenrows" + ], + "category": "data-and-analytics", + "tags": ["web-scraping", "crawling", "browser-automation", "mcp"], + "logo": "assets/logo.png", + "skills": "skills", + "commands": "commands", + "mcpServers": "mcp.json" +} diff --git a/dist/cursor/LICENSE b/dist/cursor/LICENSE new file mode 100644 index 0000000..e7dd33a --- /dev/null +++ b/dist/cursor/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 Zenrows + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/dist/cursor/README.md b/dist/cursor/README.md new file mode 100644 index 0000000..56aa54a --- /dev/null +++ b/dist/cursor/README.md @@ -0,0 +1,56 @@ +# Zenrows Cursor Plugin + +Web data for Cursor. Scrape, crawl, and map any site, run browser automation, and pull structured data, straight from your AI agent in plain English. Powered by [Zenrows Fetch](https://docs.zenrows.com/fetch/api-reference) and [Browser Sessions](https://docs.zenrows.com/browser-sessions/introduction). + +## Prerequisites + +- [Cursor](https://cursor.com) +- A [Zenrows account](https://app.zenrows.com/register) to authorize with + +## Installation + +### 1. Install the plugin + +Install Zenrows from the Cursor Marketplace. + +### 2. Authorize + +The plugin adds the hosted Zenrows MCP server. After installing, open Settings, Tools and MCP, find the `zenrows` server, click “Connect”, and complete the Zenrows authorization in your browser. Cursor stores and refreshes the token for you. + +### 3. Verify + +Run `/zenrows-doctor` in a chat to confirm the connection is live and authorized, or just ask the agent “fetch https://httpbin.io/get” and watch it scrape. If the connection looks stuck, open the command palette (Cmd+Shift+P), run “Cursor: Clear All MCP Tokens”, restart Cursor, and re-authorize. + +## Usage examples + +| Prompt | What the agent does | +|--------|---------------------| +| Fetch the OpenAI streaming docs and tell me how to stream responses. | Scrapes the page and summarizes the streaming section. | +| Scrape competitor.com/pricing and compare it to ours. | Scrapes and parses the pricing into a comparison. | +| Get all the product links from shop.example.com/phones. | Extracts links from the page. | +| Map the URLs on docs.example.com. | Lists the site's URLs from its sitemap. | +| Crawl all the pages under example.com/guide. | Follows internal links and collects each page. | +| Extract every email address from dir.example.com. | Returns a deduplicated email list. | +| Log into this site, open the first result, and give me its content. | Runs a browser session through the steps. | +| What internal API does this page use to load its data? | Inspects the page's network calls and surfaces the endpoint. | + +## What's included + +| Component | Purpose | +|-----------|---------| +| `skills/` | An overview of the Zenrows tools (`using-zenrows`), and task workflows: scrape, extract, batch, crawl, map, browser automation, API discovery, getting started | +| `commands/zenrows-doctor.md` | `/zenrows-doctor` connection and auth check | +| `mcp.json` | Connects to the hosted Zenrows MCP server over OAuth | + +## Links + +- [Zenrows MCP documentation](https://docs.zenrows.com/mcp/overview) +- [Create a Zenrows account](https://app.zenrows.com/register) + +## Contributing + +The plugin is built from [ZenRows/zenrows-integrations](https://github.com/ZenRows/zenrows-integrations). See its [CONTRIBUTING.md](https://github.com/ZenRows/zenrows-integrations/blob/main/CONTRIBUTING.md). + +## License + +MIT, see [LICENSE](LICENSE). diff --git a/dist/cursor/assets/logo.png b/dist/cursor/assets/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..a32dc121e6d08ae56c36b82382e4671ae5cc7233 GIT binary patch literal 4959 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 R^YTU*3sdV0^(LNC{|k;`(4+tW literal 0 HcmV?d00001 diff --git a/dist/cursor/commands/zenrows-doctor.md b/dist/cursor/commands/zenrows-doctor.md new file mode 100644 index 0000000..b53e5df --- /dev/null +++ b/dist/cursor/commands/zenrows-doctor.md @@ -0,0 +1,16 @@ +--- +name: zenrows-doctor +description: Check that the Zenrows MCP connection is live and authorized, and confirm scraping works. +--- + +# /zenrows-doctor + +Report whether Zenrows is connected, authorized, and able to scrape. Steps: + +1. Check which Zenrows MCP tools are available and report them by primitive: Fetch (`scrape`), Extract (`extract`), Batch (`batch_*`), Crawl (`crawl_*`) and Browser Sessions (`browser_*`). Name any that are missing. If no Zenrows tools are present at all, the plugin is not connected: confirm it is installed and enabled, fully restart Cursor, and check Settings, MCPs for a `zenrows` entry. +2. Run a probe: `scrape(url='https://httpbin.io/get', response_type='plaintext')`. + - Returns content: Zenrows is connected, authorized, and scraping works. Report success. + - Returns a 401 or `AUTH003`: the OAuth login is missing or expired. Tell the user to complete the Zenrows authorization when Cursor shows "Needs login", or, if it is stuck, run "Cursor: Clear All MCP Tokens" from the command palette (Cmd+Shift+P) and re-authorize. + - Returns `AUTH004` or `AUTH014`: Zenrows is connected, but the account's credits or this API key's credit cap are used up. Reconnecting won't help. Tell the user to check their usage and plan in the Zenrows dashboard. +3. Call `account_usage` (it is free and changes nothing) to report the plan's usage. +4. Keep the output to a few lines: connection status, the tools found by primitive, auth status and usage. diff --git a/dist/cursor/mcp.json b/dist/cursor/mcp.json new file mode 100644 index 0000000..f14af5f --- /dev/null +++ b/dist/cursor/mcp.json @@ -0,0 +1,7 @@ +{ + "mcpServers": { + "zenrows": { + "url": "https://mcp.zenrows.com/mcp" + } + } +} diff --git a/dist/cursor/skills/batch/SKILL.md b/dist/cursor/skills/batch/SKILL.md new file mode 100644 index 0000000..0fd2205 --- /dev/null +++ b/dist/cursor/skills/batch/SKILL.md @@ -0,0 +1,45 @@ +--- +name: batch +description: Fetch a list of URLs the user already has as one managed Zenrows job, with retries and per-task results. Use when the user supplies or points at roughly five or more URLs (a file, a pasted list, the output of map) and wants their content or extracted fields. Not for discovering URLs by following links (use crawl), not for listing a site's URLs (use map), and not for a single page (use scrape-webpage). +--- + +# Batch + +The user's instructions take precedence over this skill. If they conflict, follow the user. + +Zenrows Batch queues many URLs as one server-side job, retries transient failures, and stores the results. + +## When to use this instead of `scrape` + +Use batch at roughly five URLs or more. Below that, parallel `scrape` calls are faster and simpler, since a job costs a create, a wait and a results fetch. Above it, batch wins: one job instead of N round trips, server-side retries, and no plan-concurrency 429s (the Developer plan allows 5 concurrent requests). + +## Instructions + +1. Confirm the URL list and its size with the user before submitting. A batch bills per URL, so an unintended 5,000-URL job is expensive. +2. Submit the job: + - Same options for every URL: `batch_create(urls=[...], response_type='markdown')`. + - Different options per URL, or you need ids echoed back: `batch_create(tasks=[{url, external_id, metadata, zenrows_params}, ...])`. + - `urls` is ignored when `tasks` is provided. Do not pass both. +3. Wait for completion: + - Small jobs: pass `wait=true` to `batch_create` and get a terminal state in one call. + - Larger jobs: let `batch_create` return, then call `batch_wait(job_id)`. Default timeout is 600000 ms; raise `timeout_ms` (max 3600000) for big jobs. + - Report progress from `batch_status(job_id)` if the user asks mid-job. +4. Collect results with `batch_results(job_id)`. Use `status='successful'` or `status='failed'` to split them when reporting. +5. If you can write files (for example in Cursor's agent), save results to `./.zenrows/batch-.jsonl`, one JSON object per line, and summarize instead of reading the whole file back. Otherwise, summarize the results directly. +6. If the user stops the task, call `batch_cancel(job_id)`. The job keeps consuming credits until cancelled. + +## Options + +| Need | Where | Note | +|------|-------|------| +| Same settings for all URLs | job level: `js_render`, `premium_proxy`, `proxy_country`, `response_type` | Simplest form | +| Different settings per URL | `tasks[].zenrows_params` | Accepts `js_render`, `premium_proxy`, `extract`, `autoparse`, and other scrape params | +| Structured fields, not page content | `tasks[].zenrows_params` with `extract` or `autoparse` | Batch and Extract compose | +| Track which result is which | `tasks[].external_id` and `tasks[].metadata` | Echoed back on results | +| Geo-restricted pages | `proxy_country` | Works with the default settings | + +## Notes + +- `batch_create` has no required field, but a job with neither `tasks` nor `urls` does nothing. Always supply one. +- Always report the failed count alongside the successful one. Batch retries transient failures itself, so a URL in the failed set has genuinely not worked. +- `browser_batch` is unrelated: it batches actions inside one browser session. It is not this. diff --git a/dist/cursor/skills/browser-automation/SKILL.md b/dist/cursor/skills/browser-automation/SKILL.md new file mode 100644 index 0000000..34d815f --- /dev/null +++ b/dist/cursor/skills/browser-automation/SKILL.md @@ -0,0 +1,60 @@ +--- +name: browser-automation +description: 'Drive a page through steps before its content is available: clicking, filling forms, logging in, paginating, or multi-step navigation that a single fetch cannot do. Use when interaction is required to reach the content. Not for static page reads (use scrape-webpage), and not for passively listing the network or API calls a page makes (use capture-api-calls).' +--- + +# Browser automation + +The user's instructions take precedence over this skill. If they conflict, follow the user. + +## Instructions + +1. Open a session with `browser_navigate(url)`. Save the returned `session_id` and pass it to every later call. +2. Find the selectors you need with `browser_get_accessibility_tree` or `browser_query_selector_all`. Both are cheaper and more precise than a screenshot. Take `browser_screenshot` only when the page state is unclear or the user asked to see it. +3. Run the interaction as one `browser_batch` call whenever the steps are known in advance. Every round trip to the hosted browser costs seconds; a login sent as six separate calls is six times slower than it needs to be. +4. Fall back to individual `browser_*` calls only when a later step depends on reading an earlier result. +5. Always call `browser_close(session_id)` when done, to free a concurrency slot. + +## browser_batch + +`browser_batch(session_id, actions, stop_on_error)` runs 1 to 50 ordered actions in one call, reads included. A login and its result: + +```json +{ + "session_id": "", + "actions": [ + { "type": "wait_for_selector", "selector": "#email" }, + { "type": "fill", "selector": "#email", "value": "user@example.com" }, + { "type": "fill", "selector": "#password", "value": "secret" }, + { "type": "click", "selector": "button[type=submit]" }, + { "type": "wait_for_navigation" }, + { "type": "get_text" } + ] +} +``` + +Action names inside `actions` drop the `browser_` prefix: `navigate`, `go_back`, `go_forward`, `reload`, `click`, `hover`, `type`, `fill`, `select`, `check`, `uncheck`, `focus`, `press_key`, `scroll`, `drag`, `wait`, `wait_for_selector`, `wait_for_navigation`, `evaluate`, `screenshot`, `get_text`, `get_html`, `get_url`, `get_title`, `get_attribute`, `get_accessibility_tree`, `query_selector_all`. + +Three traps: + +- The action is `select`, not `select_option`. +- `fill` takes `value`. `type` takes `text`. +- `stop_on_error` defaults to `true`, so one bad selector halts the rest of the list. Set it to `false` only when the later actions do not depend on the earlier ones. + +`browser_batch` batches actions inside one browser session. It is unrelated to the Batch product and its `batch_*` tools. + +## Session management + +- Pass `session_id` to every subsequent `browser_*` call. +- Do not open multiple sessions unless the task requires it. +- Browser sessions hold concurrency slots on the user's plan. Close them promptly. +- Geo-targeting is set when the session is created. Pass `proxy_country` (ISO 3166-1 alpha-2, for example `de`) or `proxy_region` (`eu`, `na`, `ap`, `sa`, `af`, `me`) to `browser_navigate`. Later `browser_*` calls do not accept either, so set it on the first call or start a new session. + +## When to prefer browser over scrape + +| Scenario | Tool | +|----------|------| +| Read a static docs page | `scrape` | +| Log in, then navigate | `browser_*` | +| Click "Load more" pagination | `browser_*` | +| Fill a search form and read results | `browser_*` | diff --git a/dist/cursor/skills/capture-api-calls/SKILL.md b/dist/cursor/skills/capture-api-calls/SKILL.md new file mode 100644 index 0000000..2b0aa56 --- /dev/null +++ b/dist/cursor/skills/capture-api-calls/SKILL.md @@ -0,0 +1,35 @@ +--- +name: capture-api-calls +description: Discover the background API or XHR endpoints a page calls, or reverse-engineer how it loads its data. Use when the user wants the underlying request a site makes, not the rendered page. Not for interacting with the page UI (use browser-automation), and not for reading rendered content (use scrape-webpage). +--- + +# Capture API calls + +The user's instructions take precedence over this skill. If they conflict, follow the user. + +## Instructions + +1. Open a session with `browser_navigate(url)` and save the `session_id`. +2. Let the page finish loading: `browser_wait(session_id, ms=3000)`. Do not use `browser_wait_for_navigation` here; that must run before a navigation-triggering action, not after `browser_navigate`. +3. Collect network resource URLs with `browser_evaluate`: + + ```javascript + JSON.stringify( + performance.getEntriesByType('resource') + .filter(r => r.initiatorType === 'fetch' || r.initiatorType === 'xmlhttprequest' || r.name.includes('/api/')) + .map(r => ({ url: r.name, type: r.initiatorType })) + ) + ``` + +4. Identify the endpoint(s) most likely returning the target data (JSON paths, `/api/` URLs, GraphQL endpoints). +5. Present the endpoint URL, the method if inferable, and a sample response shape. +6. Optionally call the endpoint directly with `scrape(url=, response_type='html')` to get the raw response body. +7. Always call `browser_close(session_id)` when done. + +## Fallback + +If `performance.getEntriesByType` returns nothing (a single-page app with deferred loads), interact with the page (scroll, click load triggers) and re-run the evaluate script, or inspect inline `