crawlforge-mcp-server 6.20.1 → 6.21.1

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 (28) hide show
  1. package/CLAUDE.md +74 -296
  2. package/README.md +2 -2
  3. package/package.json +1 -2
  4. package/server.js +6 -4
  5. package/src/core/crawlers/BFSCrawler.js +1 -1
  6. package/src/schemas/toolOutputSchemas.js +1 -1
  7. package/src/skills/agent-skills/crawlforge-batch-automation/SKILL.md +1 -1
  8. package/src/skills/agent-skills/crawlforge-browser-sessions/SKILL.md +1 -1
  9. package/src/skills/agent-skills/crawlforge-change-tracking/SKILL.md +1 -1
  10. package/src/skills/agent-skills/crawlforge-deep-research/SKILL.md +1 -1
  11. package/src/skills/agent-skills/crawlforge-getting-started/SKILL.md +1 -1
  12. package/src/skills/agent-skills/crawlforge-stealth-browsing/SKILL.md +3 -3
  13. package/src/skills/agent-skills/crawlforge-stealth-browsing/references/engine-selection.md +2 -2
  14. package/src/skills/agent-skills/crawlforge-structured-extraction/SKILL.md +1 -1
  15. package/src/skills/agent-skills/crawlforge-web-scraping/SKILL.md +3 -3
  16. package/src/tools/crawl/mapSite.js +2 -1
  17. package/src/tools/scrape/unifiedScrape.js +4 -2
  18. package/src/tools/search/adapters/braveSearch.js +182 -0
  19. package/src/tools/search/adapters/crawlforgeSearch.js +5 -2
  20. package/src/tools/search/adapters/searchProviderFactory.js +20 -11
  21. package/src/tools/search/providers/searxng.js +5 -0
  22. package/src/tools/search/redditSearch.js +1 -1
  23. package/src/tools/search/searchWeb.js +27 -13
  24. package/src/tools/search/serpRank.js +54 -1
  25. package/src/utils/ErrorHandlingConfig.js +1 -1
  26. package/src/utils/urlNormalizer.js +2 -1
  27. package/src/utils/inputValidation.js +0 -766
  28. package/src/utils/securityMiddleware.js +0 -416
package/CLAUDE.md CHANGED
@@ -1,214 +1,96 @@
1
1
  # CLAUDE.md
2
2
 
3
- This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
3
+ Guidance for Claude Code when working in this repository. File-specific detail lives in path-scoped rules under `.claude/rules/` (tool registration and billing, search tools, stealth engines, security guards); they load when matching files are read.
4
4
 
5
- Behavioral guidelines to reduce common LLM coding mistakes. Merge with project-specific instructions as needed.
5
+ ## Working guidelines
6
6
 
7
- Tradeoff: These guidelines bias toward caution over speed. For trivial tasks, use judgment.
7
+ These bias toward caution over speed; for trivial tasks, use judgment.
8
8
 
9
- 1. Think Before Coding
10
- Don't assume. Don't hide confusion. Surface tradeoffs.
9
+ 1. **Think before coding.** Don't assume, and don't hide confusion.
10
+ - State your assumptions. If uncertain, ask.
11
+ - If several interpretations exist, present them rather than picking one silently.
12
+ - If a simpler approach exists, say so, and push back when warranted.
13
+ 2. **Simplicity first.** Write the minimum code that solves the problem.
14
+ - No features, abstractions, configurability or error handling beyond what was asked or what can actually happen.
15
+ - If 200 lines could be 50, rewrite it.
16
+ 3. **Surgical changes.** Touch only what you must.
17
+ - Don't "improve" adjacent code, comments or formatting, and don't refactor what isn't broken. Match the existing style.
18
+ - Remove imports, variables and functions your change made unused. Mention pre-existing dead code; don't delete it unless asked.
19
+ - Every changed line should trace to the request.
20
+ 4. **Goal-driven execution.** Turn the task into a check you can run, and loop until it passes: "fix the bug" → a test that reproduces it, then make it pass; "refactor X" → tests pass before and after. For multi-step work, state a brief plan with a verification step for each item.
11
21
 
12
- Before implementing:
22
+ ## Project overview
13
23
 
14
- State your assumptions explicitly. If uncertain, ask.
15
- If multiple interpretations exist, present them - don't pick silently.
16
- If a simpler approach exists, say so. Push back when warranted.
17
- If something is unclear, stop. Name what's confusing. Ask.
24
+ CrawlForge MCP Server — an MCP server providing 31 web scraping, crawling and content-processing tools (6 inline in `server.js` + 25 advanced). Node ESM, MCP SDK v2 (`@modelcontextprotocol/server`), Zod. The version lives in `package.json`.
18
25
 
19
- 2. Simplicity First
20
- Minimum code that solves the problem. Nothing speculative.
21
-
22
- No features beyond what was asked.
23
- No abstractions for single-use code.
24
- No "flexibility" or "configurability" that wasn't requested.
25
- No error handling for impossible scenarios.
26
- If you write 200 lines and it could be 50, rewrite it.
27
- Ask yourself: "Would a senior engineer say this is overcomplicated?" If yes, simplify.
28
-
29
- 3. Surgical Changes
30
- Touch only what you must. Clean up only your own mess.
31
-
32
- When editing existing code:
33
-
34
- Don't "improve" adjacent code, comments, or formatting.
35
- Don't refactor things that aren't broken.
36
- Match existing style, even if you'd do it differently.
37
- If you notice unrelated dead code, mention it - don't delete it.
38
- When your changes create orphans:
39
-
40
- Remove imports/variables/functions that YOUR changes made unused.
41
- Don't remove pre-existing dead code unless asked.
42
- The test: Every changed line should trace directly to the user's request.
43
-
44
- 4. Goal-Driven Execution
45
- Define success criteria. Loop until verified.
46
-
47
- Transform tasks into verifiable goals:
48
-
49
- "Add validation" → "Write tests for invalid inputs, then make them pass"
50
- "Fix the bug" → "Write a test that reproduces it, then make it pass"
51
- "Refactor X" → "Ensure tests pass before and after"
52
- For multi-step tasks, state a brief plan:
53
-
54
- 1. [Step] → verify: [check]
55
- 2. [Step] → verify: [check]
56
- 3. [Step] → verify: [check]
57
- Strong success criteria let you loop independently. Weak criteria ("make it work") require constant clarification.
58
-
59
- These guidelines are working if: fewer unnecessary changes in diffs, fewer rewrites due to overcomplication, and clarifying questions come before implementation rather than after mistakes.
60
-
61
- ## Project Overview
62
-
63
- CrawlForge MCP Server - A professional MCP (Model Context Protocol) server providing 31 web scraping, crawling, and content processing tools (6 inline + 25 advanced).
64
-
65
- **Current Version:** 5.6.6
66
-
67
- ## Development Commands
26
+ ## Development commands
68
27
 
69
28
  ```bash
70
- # Install dependencies
71
29
  npm install
30
+ npm run setup # first run, users only; or export CRAWLFORGE_API_KEY=...
31
+ npm start # stdio server
32
+ npm run start:http # HTTP transport
33
+ npm run dev # verbose logging
72
34
 
73
- # Setup (required for first run - users only)
74
- npm run setup
75
- # Or provide API key via environment:
76
- export CRAWLFORGE_API_KEY="your_api_key_here"
77
-
78
- # Run the server (production)
79
- npm start
80
-
81
- # HTTP transport mode
82
- npm run start:http
83
-
84
- # Development mode with verbose logging
85
- npm run dev
86
-
87
- # Test MCP protocol compliance
88
- npm test
89
-
90
- # Unit tests (400+ tests across tests/unit/, no live network)
91
- npm run test:unit
92
- # Phase D regressions live in tests/unit/phaseD-regressions.test.js (agent hard stops, unified scrape, map_site ranking)
93
- # Run a single test file: node --test tests/unit/phaseD-regressions.test.js
94
- # Note: the script includes --test-force-exit — importing StealthBrowserManager
95
- # (d2-reliability.test.js) leaves a Playwright handle that otherwise hangs the
96
- # runner at exit after all tests pass. When running a single file manually, put
97
- # --test-force-exit BEFORE the file path (after it, the flag is silently ignored).
98
-
99
- # Integration tests
35
+ npm run test:unit # node:test over tests/unit/**, no live network
36
+ npm test # MCP protocol compliance
100
37
  npm run test:integration
38
+ node test-tools.js # functional test of every tool
39
+ node test-real-world.js # real-world scenarios
101
40
 
102
- # Functional tests
103
- node test-tools.js # Test all tools
104
- node test-real-world.js # Test real-world usage scenarios
105
-
106
- # MCP Protocol tests
107
- node tests/integration/mcp-protocol-compliance.test.js
108
-
109
- # CLI (v4.1.0+, requires global install or npx)
110
- crawlforge --help # Show all subcommands
111
- crawlforge init # API-key detection + skill install + idempotent MCP-stanza merge (v4.6.0)
112
- crawlforge init --all --yes # Merge MCP config into Claude Code / Desktop / Cursor non-interactively
113
- crawlforge scrape https://example.com
114
- crawlforge batch --urls urls.txt --format markdown
115
- crawlforge install-skills --target claude-code
116
- # See docs/cli-guide.md for full reference
117
-
118
- # Docker
119
- npm run docker:build # Build Docker image
120
- npm run docker:dev # Run development container
121
- npm run docker:prod # Run production container
41
+ npm run docker:build # also docker:dev, docker:prod
122
42
  ```
123
43
 
124
- ### Debugging Tips
125
-
126
- - All diagnostic output (Winston logs + status banners) goes to stderr; stdout is reserved for the MCP JSON-RPC stream and CLI `--json` output (enforced in v4.2.4)
127
- - Set `NODE_ENV=development` for verbose logging
128
- - Use `--expose-gc` flag for memory profiling: `node --expose-gc server.js`
129
- - Check `cache/` directory for cached responses
130
- - Review `logs/` directory for application logs
131
- - Memory monitoring auto-enabled in development mode (logs every 60s if >200MB)
132
-
133
- ## High-Level Architecture
134
-
135
- ### Core Infrastructure (`src/core/`)
136
-
137
- - **AuthManager**: Authentication, credit tracking, and usage reporting
138
- - **PerformanceManager**: Centralized performance monitoring and optimization
139
- - **JobManager**: Asynchronous job tracking and management for batch operations
140
- - **WebhookDispatcher**: Event notification system for job completion callbacks
141
- - **ActionExecutor**: Browser automation engine (Playwright-based)
142
- - **ResearchOrchestrator**: Multi-stage research with query expansion and synthesis
143
- - **AgentOrchestrator**: Powers the `agent` tool — NL prompt → autonomous PLAN→GATHER→ACT→DECIDE→SHAPE loop with three orchestrator-enforced hard stops (maxSteps≤10, maxUrls≤20, wall-clock) never delegated to the LLM; degraded no-LLM-key path (D2, v4.6.0)
144
- - **StealthBrowserManager**: Stealth mode scraping with anti-detection; Camoufox (Firefox) engine added in v4.0.0. Engine selection goes through the one resolver `resolveStealthEngine(requested)` → `{engine, fallbackWarning}`; `'auto'` (the default for `scrape.escalate_engine` and `stealth_mode.engine` since the 2026-09 stealth review Phase 2) prefers Camoufox and falls back to Chromium with a caller-visible warning when its binary is absent. `'playwright'` is the tool layer's public name for Chromium. Camoufox personas are pinned to the installed binary's Firefox major so the UA's `rv:` and `Firefox/` tokens agree — see `docs/STEALTH_REVIEW_2026-09.md`
145
- - **LocalizationManager**: Multi-language content and localization
146
- - **ChangeTracker**: Content change tracking over time
147
- - **SnapshotManager**: Website snapshots and version history
148
- - **ResourceRegistry**: MCP Resources (crawlforge:// URI scheme, 5 resource types) — D1.1, v3.6.0
149
- - **PromptRegistry** (`src/prompts/`): 5 workflow prompts — D1.2, v3.6.0
150
- - **SamplingClient**: MCP Sampling with Ollama-API fallback chain — D1.3, v3.6.0
151
- - **ElicitationHelper**: MCP Elicitation for user confirmation on expensive operations — D1.4, v3.6.0
152
- - **endpointGuard**: Allow-list guard for server's own backend calls — v3.0.18
153
-
154
- ### Tool Layer (`src/tools/`)
155
-
156
- Tools are organized in subdirectories by category:
44
+ - Single test file: `node --test --test-force-exit tests/unit/<file>.test.js`. The flag must come **before** the path (after it, it is silently ignored). Without it, the Playwright handle that `StealthBrowserManager` leaves open hangs the runner after all tests pass; `npm run test:unit` already sets it.
45
+ - Phase D regressions (agent hard stops, unified scrape, map_site ranking): `tests/unit/phaseD-regressions.test.js`.
46
+ - CLI (`crawlforge --help`): `init`, `scrape`, `batch`, `install-skills`, … — full reference in `docs/cli-guide.md`.
157
47
 
158
- - `advanced/` - BatchScrapeTool, ScrapeWithActionsTool, BrowserSessionTool
159
- - `agent/` - agent (AgentOrchestrator-driven autonomous tool, v4.6.0)
160
- - `basic/` - fetchUrl, extractText, extractLinks, extractMetadata, scrapeStructured
161
- - `scrape/` - unifiedScrape (single-fetch multi-format `scrape` tool, v4.6.0)
162
- - `crawl/` - crawlDeep, mapSite
163
- - `extract/` - analyzeContent, extractContent, extractStructured, extractWithLlm, listOllamaModels, processDocument, summarizeContent
164
- - `research/` - deepResearch
165
- - `result/` - readResult (`read_result`: search/slice/lines/json_path over a stored over-threshold result by `result_handle`, v5.8.0)
166
- - `search/` - searchWeb (proxied through CrawlForge.dev API), serpRank (real Google organic rank via DataForSEO; adapter in `search/adapters/dataforseoSearch.js`), redditSearch (Reddit posts/comments/threads via Arctic Shift + PullPush community archives — reddit.com blocks scrapers)
167
- - `templates/` - ScrapeTemplateTool (10 pre-built site templates, v4.0.0)
168
- - `tracking/` - trackChanges
169
- - `llmstxt/` - generateLLMsTxt
48
+ ### Debugging
170
49
 
171
- ### Available MCP Tools (31 total)
50
+ - **stdout is reserved** for the MCP JSON-RPC stream and CLI `--json` output. All diagnostics (Winston logs, status banners) go to stderr.
51
+ - `NODE_ENV=development` for verbose logging and memory monitoring (logs every 60s above 200MB); `node --expose-gc server.js` for memory profiling.
52
+ - Cached responses are in `cache/`; application logs in `logs/`.
172
53
 
173
- **Basic Tools (server.js inline, 6):**
174
- fetch_url, extract_text, extract_links, extract_metadata, extract_embedded_state, scrape_structured
54
+ ## Architecture
175
55
 
176
- **Advanced Tools (25):**
177
- search_web, serp_rank, reddit_search, crawl_deep, map_site, extract_content, process_document, summarize_content, analyze_content, extract_structured, extract_with_llm, list_ollama_models, batch_scrape, get_batch_results, scrape_with_actions, deep_research, track_changes, generate_llms_txt, stealth_mode, localization, scrape_template, scrape, agent, read_result, browser_session
56
+ `server.js` is the entry point: loads `.env` early (creator mode), sets up `AuthManager`, registers every tool through `withAuth()` for credit tracking, connects the stdio transport, and cleans up browsers and job managers on graceful shutdown.
178
57
 
179
- **serp_rank (DataForSEO):**
180
- - `serp_rank` — reports where a target domain ranks in Google's REAL organic results for a keyword (the SERP position Google Custom Search / `search_web` cannot give). Backed by the DataForSEO Google Organic SERP API (Live Advanced, `POST /v3/serp/google/organic/live/advanced`, HTTP Basic auth). Credentials via `DATAFORSEO_LOGIN` / `DATAFORSEO_PASSWORD`, billed to the user's own DataForSEO account (~US$0.002 per 10 results of `depth`, so $0.004 at the default `depth:20` and $0.02 at `depth:100`), separate from CrawlForge credits. When unconfigured it returns `{ configured:false }` and charges **0** credits; when configured, **Cost: 5**. Never fabricates a rank.
58
+ ### Core (`src/core/`)
181
59
 
182
- **reddit_search (community archives, v5.1.0):**
183
- - `reddit_search` — searches Reddit posts/comments or reads a full comment thread. reddit.com 403-blocks ALL direct access (fetch, browser-UA, stealth — IP/TLS-reputation based), so this never touches reddit.com; it queries the Arctic Shift archive (`arctic-shift.photon-reddit.com`, near-real-time, comment trees) and PullPush (`api.pullpush.io`, Pushshift-compatible). Routing: Arctic Shift's keyword search REQUIRES a subreddit/author scope (verified live: HTTP 400 without one), so a scoped search queries it directly, while an unscoped keyword search for posts discovers them with a site-restricted web search and then reads those posts from the archive by ID (`source:"web_discovery"`). PullPush stopped serving automated clients in August 2026 (429 "does not provide free scraping resources for agents" on every UA; Cloudflare 403 from some IPs) and is no longer tried automatically anywhere — `source:"pullpush"` still reaches it. An unscoped COMMENT search has no backend and returns a scope-required error. Modes: `posts` (default), `comments`, `thread` (post + nested comment tree by `link_id`). Free, no Reddit credentials. Cost: 5 — a Reddit-wide search spends the same web search `search_web` does. `REDDIT_SEARCH_TIMEOUT_MS` overrides the 30s default.
60
+ - **AuthManager** — API-key validation, credit tracking, usage reporting; `getToolCost` is the source of truth for prices.
61
+ - **AgentOrchestrator** — powers `agent`: NL prompt → PLAN→GATHER→ACT→DECIDE→SHAPE loop. Its three hard stops (maxSteps ≤10, maxUrls ≤20, wall-clock) are enforced by the orchestrator, never delegated to the LLM; has a degraded no-LLM-key path.
62
+ - **ResearchOrchestrator** — multi-stage research with query expansion and synthesis.
63
+ - **StealthBrowserManager** — anti-detection scraping, Chromium or Camoufox (see `.claude/rules/stealth.md`).
64
+ - **ActionExecutor** — Playwright browser automation for `scrape_with_actions`.
65
+ - **JobManager** / **WebhookDispatcher** — async jobs for batch operations and their completion callbacks.
66
+ - **PerformanceManager**, **LocalizationManager**, **ChangeTracker**, **SnapshotManager**.
67
+ - MCP features: **ResourceRegistry** (`crawlforge://` resources), **PromptRegistry** (`src/prompts/`, 5 workflow prompts), **SamplingClient** (with Ollama fallback), **ElicitationHelper** (user confirmation on expensive operations).
68
+ - **endpointGuard** — allowlist for the server's own backend calls.
184
69
 
185
- **v4.6.0 additions (Phase D):**
186
- - `scrape` — single fetch + one cheerio load dispatching a `formats` array (markdown/html/rawHtml/text/links/metadata/screenshot/json-schema) + `onlyMainContent`; partial-success via per-format `warnings[]`. Cost: 2.
187
- - `agent` — NL prompt → autonomous research/extract, no URLs required (see AgentOrchestrator above). Cost: 8.
188
- - `map_site` gained an optional `search=` param that ranks discovered URLs (`ranked_urls:[{url,score}]`); default output unchanged.
70
+ ### Tools (`src/tools/`)
189
71
 
190
- ### MCP Server Entry Point
72
+ | Directory | Tools |
73
+ |---|---|
74
+ | `basic/` | fetchUrl, extractText, extractLinks, extractMetadata, scrapeStructured |
75
+ | `scrape/` | unifiedScrape — `scrape`: one fetch, one cheerio load, a `formats` array, per-format `warnings[]` |
76
+ | `agent/` | `agent` (AgentOrchestrator) |
77
+ | `advanced/` | BatchScrapeTool, ScrapeWithActionsTool, BrowserSessionTool |
78
+ | `crawl/` | crawlDeep, mapSite (optional `search=` ranks URLs into `ranked_urls`) |
79
+ | `extract/` | analyzeContent, extractContent, extractStructured, extractWithLlm, listOllamaModels, processDocument, summarizeContent |
80
+ | `research/` | deepResearch |
81
+ | `result/` | readResult — search/slice/lines/json_path over a stored over-threshold result by `result_handle` |
82
+ | `search/` | searchWeb (proxied through the CrawlForge.dev API), serpRank (DataForSEO), redditSearch (community archives) — see `.claude/rules/search-tools.md` |
83
+ | `templates/` | ScrapeTemplateTool (10 site templates) |
84
+ | `tracking/` | trackChanges |
85
+ | `llmstxt/` | generateLLMsTxt |
191
86
 
192
- The main server implementation is in `server.js` which:
87
+ **The 31 MCP tools.** Inline (6): fetch_url, extract_text, extract_links, extract_metadata, extract_embedded_state, scrape_structured. Advanced (25): search_web, serp_rank, reddit_search, crawl_deep, map_site, extract_content, process_document, summarize_content, analyze_content, extract_structured, extract_with_llm, list_ollama_models, batch_scrape, get_batch_results, scrape_with_actions, deep_research, track_changes, generate_llms_txt, stealth_mode, localization, scrape_template, scrape, agent, read_result, browser_session.
193
88
 
194
- 1. **Secure Creator Mode**: Loads `.env` early, validates secret via SHA256 hash comparison
195
- 2. **Authentication Flow**: AuthManager for API key validation and credit tracking
196
- 3. **Tool Registration**: All tools registered via `server.registerTool()`, wrapped with `withAuth()` for credit tracking
197
- 4. **Transport**: stdio transport for MCP protocol communication
198
- 5. **Graceful Shutdown**: Cleans up browser instances, job managers, and other resources
89
+ Adding or changing a tool: `.claude/rules/tool-registration.md`.
199
90
 
200
- ### Tool Credit System
91
+ ## Configuration
201
92
 
202
- Each tool wrapped with `withAuth(toolName, handler)`:
203
-
204
- - Checks credits before execution (skipped in creator mode)
205
- - Reports usage with credit deduction on success
206
- - Charges half credits on error
207
- - Creator mode: Unlimited access for package maintainer
208
-
209
- ### Key Configuration
210
-
211
- Critical environment variables defined in `src/constants/config.js`:
93
+ Defaults and validation: `src/constants/config.js`. User auth is stored in `~/.crawlforge/config.json`; development env in `.env`.
212
94
 
213
95
  ```bash
214
96
  CRAWLFORGE_API_KEY=your_api_key_here
@@ -219,123 +101,19 @@ RATE_LIMIT_REQUESTS_PER_SECOND=10
219
101
  MAX_CRAWL_DEPTH=5
220
102
  MAX_PAGES_PER_CRAWL=100
221
103
  RESPECT_ROBOTS_TXT=true
222
- ROBOTS_CACHE_TTL_MS=3600000 # how long a parsed robots.txt stays good for
223
- CRAWLFORGE_BLOCKED_HOSTS= # comma-separated; extends the permanent opt-out blocklist
224
- CRAWLFORGE_STEALTH_ENGINE= # which engine `auto` resolves to on THIS deployment:
225
- # chromium|playwright pins it to Chromium; unset or anything
226
- # else keeps the default preference for Camoufox. A caller
227
- # naming an engine always wins. There is no correct global
228
- # value — which engine clears a wall depends on the exit IP
229
- # (residential favours Camoufox, the hosted datacenter IP
230
- # favours Chromium, measured twice).
231
- CRAWLFORGE_STEALTH_PROXIES= # comma-separated proxy URLs used by the escalation stage,
232
- # stealth_mode, browser_session, scrape_with_actions and the
233
- # deep_research fallback when the caller passes none (a proxy
234
- # passed on the call wins). CrawlForge supplies no proxies, and
235
- # a datacenter one does not defeat these detectors. Distinct
236
- # from the PROXY_ROTATION_* family, which belongs to
237
- # localization. The agent's automatic stealth retry
238
- # (stealth review Phase 3) reads it too.
239
- CRAWLFORGE_IMPIT= # `off` skips escalation's impit step (a Chrome TLS handshake with
240
- # the honest UA, tried before the browser; stealth review Phase 7).
241
- # Exit-IP dependent like the engine: it cleared indeed.com from a
242
- # residential IP and nothing from the hosted datacenter IP, so
243
- # render.yaml sets it off there. Measure with scripts/impit-probe.mjs.
244
- CRAWLFORGE_CLEARANCE_JAR= # `off` disables the clearance jar (stealth review Phase 4):
245
- # cf_clearance/__cf_bm/datadome cookies a stealth render earned
246
- # are replayed to the next stealth context with the same engine,
247
- # UA and proxy. Persisted at ~/.crawlforge/stealth-clearance.json.
248
- ```
249
-
250
- Camoufox is an **optional** dependency and its transitive `language-tags@2.1.0` requires Node ≥22. npm silently drops an optional subtree that fails an engine check, so on Node 20 it is simply absent — `'auto'` then resolves to Chromium and says so in `fallbackWarning`. `engines.node` stays at `>=20.16.0` deliberately; bumping it is breaking and is a release decision.
251
-
252
- Web Bot Auth (optional, off by default — requests go out unsigned when unset):
253
-
254
- ```bash
255
- CRAWLFORGE_SIGNING_KEY= # Ed25519 PKCS#8 PEM, or base64 of it. SECRET — never commit.
256
- WEB_BOT_AUTH_DIRECTORY= # https://www.crawlforge.dev — advertised in Signature-Agent
257
104
  ```
258
105
 
259
- Generate the pair with `node scripts/generate-signing-key.mjs`; publish the public half
260
- before setting the private one. Procedure and compromise response: `docs/policy/KEY_ROTATION.md`.
261
-
262
- `OPENAI_API_KEY` / `ANTHROPIC_API_KEY` are optional. They only affect `deep_research`: when set, it produces a fully synthesized report internally; when unset, it returns raw evidence for the calling LLM (e.g. Claude Code) to synthesize.
263
-
264
- ### Configuration Files
265
-
266
- - `~/.crawlforge/config.json` - User authentication and API key storage
267
- - `.env` - Environment variables for development
268
- - `src/constants/config.js` - Central configuration with defaults and validation
269
-
270
- ### Adding New Tools
271
-
272
- When adding a new tool to server.js:
273
-
274
- 1. Import the tool class from `src/tools/`
275
- 2. Instantiate the tool (with config if needed)
276
- 3. Register with `server.registerTool(name, { description, inputSchema }, withAuth(name, handler))`
277
- 4. Ensure tool implements `execute(params)` method
278
- 5. Add to cleanup array in gracefulShutdown if it has `destroy()` or `cleanup()` methods
279
- 6. Update tool count in console log at server startup
280
-
281
- ## Sandboxing & Approvals
282
-
283
- Key mechanisms for security-conscious future sessions:
284
-
285
- - **SSRF** (`src/utils/ssrfGuard.js` enforcing `src/utils/ssrfProtection.js`): As of v4.8.0 SSRF is actually enforced on the live scraping fetch path (previously `ssrfProtection.js` was unwired). `ssrfGuard` injects an undici dispatcher whose connect-time `lookup` validates every connection — initial request and each redirect hop — and pins the validated IP (closing the DNS-rebinding TOCTOU window). Stage 1 (default) blocks loopback, link-local/cloud-metadata (169.254.169.254), and 0.0.0.0; `SSRF_STRICT=true` adds full RFC1918/ULA/etc. enforcement. Kill switch `SSRF_PROTECTION_ENABLED=false`; `ALLOWED_DOMAINS` allowlist bypass. Wired into every read-scrape site (`_fetch.js`, `_fetchAndParse.js`, `batchScrape/worker.js`, `mapSite.js`, `BFSCrawler.js`, extract/template/session/research/llms-txt/robots/sitemap/track-changes fetches) via `ssrfGuard()`/`safeFetch()`. http/https only; dangerous ports + path-traversal still rejected by `ssrfProtection.js`.
286
- - **endpointGuard** (`src/core/endpointGuard.js`): Hard allow-list of `{crawlforge.dev, www.crawlforge.dev, api.crawlforge.dev}` for the server's own backend calls; HTTPS required; fail-closed. Localhost only in creator mode (v3.0.18).
287
- - **Action allowlist** (`src/core/ActionExecutor.js`): `scrape_with_actions` accepts only 7 action types: `wait`, `click`, `type`, `press`, `scroll`, `screenshot`, `executeJavaScript`. `executeJavaScript` throws unless `ALLOW_JAVASCRIPT_EXECUTION=true` is set at deploy time (off by default).
288
- - **Elicitation** (`src/core/ElicitationHelper.js`): User confirmation requested for `deep_research` (>50 URLs), `batch_scrape` (sync, >25 URLs), `crawl_deep` (projected >500 pages), `extract_structured` (schema has >3 required fields, no LLM configured), and credit-low situations. Fail-open if client does not support elicitation.
289
- - **Browser sandboxing**: Standard pool retains OS sandbox. Stealth Chromium uses `--no-sandbox` (deliberate fingerprint-spoofing trade-off); `--disable-web-security` was removed from it in Phase 1 of the 2026-09 stealth review — a page reads it in one line — but the non-stealth render browser (`BrowserProcessor`) still carries both flags. Camoufox (Firefox, v4.0.0) is the alternative — see `docs/stealth-engines.md`.
290
-
291
- See `docs/sandboxing-and-approvals.md` for the full reference.
106
+ - Stealth variables (`CRAWLFORGE_STEALTH_ENGINE`, `CRAWLFORGE_STEALTH_PROXIES`, `CRAWLFORGE_IMPIT`, `CRAWLFORGE_CLEARANCE_JAR`): `.claude/rules/stealth.md`.
107
+ - Robots, blocklist and Web Bot Auth signing variables: `.claude/rules/security-guards.md`.
108
+ - `OPENAI_API_KEY` / `ANTHROPIC_API_KEY` are optional and only affect `deep_research`: set, it synthesizes the report itself; unset, it returns raw evidence for the calling LLM to synthesize.
292
109
 
293
110
  ## Security
294
111
 
295
- Security testing and CI/CD pipeline details are in:
296
-
297
- - `docs/sandboxing-and-approvals.md` — Canonical sandboxing & approvals reference
298
- - `docs/security-audit-report.md` — Full security audit
299
- - `.github/workflows/ci.yml` — CI pipeline with security checks
300
- - `.github/workflows/security.yml` — Daily scheduled security scanning
301
- - `docs/SECURITY.md` — Security policy and procedures
302
-
303
- Run `npm audit` locally to check dependencies.
304
-
305
- ## Implementation Patterns
306
-
307
- ### Tool Structure
308
-
309
- ```javascript
310
- export class ToolName {
311
- constructor(config) { this.config = config; }
312
-
313
- async execute(params) {
314
- return { success: true, data: {...} };
315
- }
316
-
317
- async destroy() { /* cleanup resources */ }
318
- }
319
- ```
320
-
321
- ### Error Handling Pattern
322
-
323
- ```javascript
324
- try {
325
- const result = await tool.execute(params);
326
- return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
327
- } catch (error) {
328
- return {
329
- content: [{ type: "text", text: `Operation failed: ${error.message}` }],
330
- isError: true,
331
- };
332
- }
333
- ```
112
+ - SSRF is enforced on every live fetch path through `ssrfGuard()`/`safeFetch()`; never call `fetch` directly on a user-supplied URL.
113
+ - References: `docs/sandboxing-and-approvals.md` (canonical), `docs/security-audit-report.md`, `docs/SECURITY.md`; CI checks in `.github/workflows/ci.yml` and `.github/workflows/security.yml`. Run `npm audit` locally.
334
114
 
335
- ## Project Management Rules
115
+ ## Project management rules
336
116
 
337
- - Delegate to a sub agent only for a genuinely independent, parallelizable track — not for work finishable in a few tool calls, and never to verify your own output (Claude 5 models over-delegate when told to parallelize)
338
- - When a phase completes, update `docs/PRODUCTION_READINESS.md`
339
- - Whenever a phase is completed, push all changes to GitHub
340
- - Put all documentation md files into the `docs/` folder
341
- - Every time you finish a phase run the test suites (`npm run test:unit` and `npm test`) and fix all failures before pushing
117
+ - Delegate to a sub-agent only for a genuinely independent, parallelizable track — not for work finishable in a few tool calls, and never to verify your own output (Claude 5 models over-delegate when told to parallelize).
118
+ - Put all documentation `.md` files in `docs/`.
119
+ - When a phase completes: run `npm run test:unit` and `npm test` and fix all failures, update `docs/PRODUCTION_READINESS.md`, then push all changes to GitHub.
package/README.md CHANGED
@@ -199,8 +199,8 @@ CrawlForge requires a CrawlForge API key — **every tool is metered and consume
199
199
  | `browser_session` | 3 | **A browser page that stays open across calls**, keeping its cookies and its login in between. `open` a session on a URL, `snapshot` it to list the interactive elements as stable refs (`@e1`, `@e2`), `act` on a ref, `read` the content, `close`. Priced per operation: open 3, read 2, snapshot/act/screenshot/close/list 1 each. Reach for it when you must see the page before choosing what to click, when the flow spans several calls, or when a login must hold across later reads; one fixed action chain on one page is `scrape_with_actions` |
200
200
  | `summarize_content` | 4 | Generate intelligent summaries |
201
201
  | `crawl_deep` | 4 | Deep crawl entire websites |
202
- | `search_web` | 5 | Search the web using Google Search API |
203
- | `reddit_search` | 5 | Search Reddit posts/comments or read a full thread — reddit.com blocks direct scraping, so this reads the Arctic Shift community archive (free, no Reddit credentials). A Reddit-wide search spends a web search to discover posts, so it is priced with `search_web` |
202
+ | `search_web` | 5 | Search the live web (Brave Search API, through the CrawlForge API) |
203
+ | `reddit_search` | 5 | Search Reddit posts/comments or read a full thread — reddit.com blocks direct scraping, so this reads the Arctic Shift community archive (free, no Reddit credentials). A Reddit-wide search spends a web search to discover posts, so it is priced with `search_web`. Optional: with your own Reddit app credentials (`REDDIT_CLIENT_ID`/`REDDIT_CLIENT_SECRET`), posts searches and thread reads come from Reddit's official API first (live scores, complete comment trees), with the archives as fallback; same price. Reddit no longer issues new credentials without an approval ticket, see [docs/reddit-access-and-oauth.md](docs/reddit-access-and-oauth.md) |
204
204
  | `serp_rank` | 5 | Check where a domain ranks in Google's **real organic SERP** for a keyword (the position `search_web` can't give). Powered by DataForSEO (`DATAFORSEO_LOGIN`/`DATAFORSEO_PASSWORD`, billed to your own DataForSEO account). Returns `{ configured:false }` and charges **0** credits until configured |
205
205
  | `batch_scrape` | 5 | Process multiple URLs simultaneously |
206
206
  | `scrape_with_actions` | 5 | Browser automation chains |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "crawlforge-mcp-server",
3
- "version": "6.20.1",
3
+ "version": "6.21.1",
4
4
  "mcpName": "io.github.mysleekdesigns/crawlforge-mcp-server",
5
5
  "description": "CrawlForge MCP Server - Professional Model Context Protocol server with 31 web scraping, crawling, deep-research, and autonomous-extraction tools. Returns clean Markdown and structured JSON for Claude, Cursor, and any MCP client. Defaults to local Ollama for LLM extraction (no API key needed); OpenAI/Anthropic available as opt-in. Includes a unified multi-format scrape tool, an autonomous agent, pre-built site templates, and Camoufox stealth browsing.",
6
6
  "main": "server.js",
@@ -125,7 +125,6 @@
125
125
  "diff": "^9.0.0",
126
126
  "dotenv": "^17.2.1",
127
127
  "franc": "^6.2.0",
128
- "isomorphic-dompurify": "^3.9.0",
129
128
  "jsdom": "^29.0.2",
130
129
  "lru-cache": "^11.1.0",
131
130
  "mammoth": "^1.12.2",
package/server.js CHANGED
@@ -113,7 +113,7 @@ if (configErrors.length > 0 && config.server.nodeEnv === 'production') {
113
113
  // Create the server
114
114
  const server = new McpServer({
115
115
  name: "crawlforge",
116
- version: "6.20.1",
116
+ version: "6.21.1",
117
117
  description: "Production-ready MCP server with 31 web scraping, crawling, and content processing tools. Features MCP Resources (crawlforge://), Prompts, Sampling fallback, Elicitation, stealth browsing, stateful browser sessions with element refs, deep research, structured extraction, embedded JavaScript state extraction, real Google SERP rank tracking, Reddit search via community archives, change tracking, local-LLM extraction via Ollama, unified multi-format scrape, and autonomous agent tool.",
118
118
  homepage: "https://www.crawlforge.dev",
119
119
  icon: "https://www.crawlforge.dev/icon.png",
@@ -588,7 +588,7 @@ registerToolIfEnabled("search_web", {
588
588
 
589
589
  // Tool: serp_rank — REAL Google organic rank for a target domain (via DataForSEO)
590
590
  registerToolIfEnabled("serp_rank", {
591
- description: "Use this to check where a domain ranks in Google's ORGANIC results for a keyword - real SERP position, not Custom Search order. Returns the target's organic rank, the ranking URL, and every position it holds. Not for general search (search_web). One lookup is one sample: Google can return a different result set for the same query minutes apart (seResultsCount tells them apart), so compare several before reading a rank change. Requires DataForSEO credentials and returns configured:false without them - do not retry in that case. Cost: 5 credits (0 when unconfigured). Example: serp_rank({keyword: \"managed wordpress hosting\", target: \"dashboardhosting.com\", location_name: \"United States\"})",
591
+ description: "Use this to check where a domain ranks in Google's ORGANIC results for a keyword - real SERP position, not search_web's order. Returns the target's organic rank, the ranking URL, and every position it holds. Not for general search (search_web). One lookup is one sample: Google can return a different result set for the same query minutes apart (seResultsCount tells them apart), and sometimes a decoy page of unrelated results (suspect:true), so compare several before reading a rank change. Requires DataForSEO credentials and returns configured:false without them - do not retry in that case. Cost: 5 credits (0 when unconfigured). Example: serp_rank({keyword: \"managed wordpress hosting\", target: \"dashboardhosting.com\", location_name: \"United States\"})",
592
592
  annotations: { title: "SERP Rank Check", readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: true },
593
593
  inputSchema: {
594
594
  keyword: z.string().describe("The search query to check ranking for"),
@@ -1395,9 +1395,11 @@ function stealthScrapeFormats(formats, scraped) {
1395
1395
  const links = [];
1396
1396
  $('a[href]').each((_, el) => {
1397
1397
  const href = $(el).attr('href');
1398
- if (!href || href.startsWith('#') || href.startsWith('javascript:')) return;
1398
+ if (!href || href.startsWith('#')) return;
1399
1399
  try {
1400
- const absolute = new URL(href, scraped.url).toString();
1400
+ const resolved = new URL(href, scraped.url);
1401
+ if (['javascript:', 'data:', 'vbscript:'].includes(resolved.protocol)) return;
1402
+ const absolute = resolved.toString();
1401
1403
  if (seen.has(absolute)) return;
1402
1404
  seen.add(absolute);
1403
1405
  links.push({ href: absolute, text: $(el).text().trim() });
@@ -444,7 +444,7 @@ export class BFSCrawler {
444
444
  const links = [];
445
445
  $('a[href]').each((_, element) => {
446
446
  const href = $(element).attr('href');
447
- if (href && !href.startsWith('#') && !href.startsWith('javascript:')) {
447
+ if (href && !href.startsWith('#') && !/^\s*(javascript|data|vbscript):/i.test(href)) {
448
448
  links.push(href);
449
449
  }
450
450
  });
@@ -306,7 +306,7 @@ const searchWebShape = {
306
306
  effective_query: z.string().optional().describe('Present when query expansion changed the query actually used'),
307
307
  expanded_queries: z.array(z.string()).optional().describe('Present only when the original query returned nothing: the queries searched, in order - the original, then its expanded form'),
308
308
  results: z.array(searchWebResultShape).optional(),
309
- total_results: z.union([z.string(), z.number()]).optional(),
309
+ total_results: z.union([z.string(), z.number()]).nullable().optional().describe('The backend\'s total match count; null when it reports none (Brave Search API)'),
310
310
  search_time: z.number().optional(),
311
311
  offset: z.number().optional(),
312
312
  limit: z.number().optional(),
@@ -2,7 +2,7 @@
2
2
  name: crawlforge-batch-automation
3
3
  description: "Automates large scraping jobs and browser interactions with CrawlForge's batch_scrape, get_batch_results, scrape_with_actions, and generate_llms_txt tools. Use when the user wants to scrape many URLs at once, batch-scrape a list of pages, collect dozens of product, news, or competitor pages, run browser actions (click, type, scroll, wait) before scraping, log in or fill a form before extracting, or generate an llms.txt for a site. Use sync mode for up to about 25 URLs and async with a webhook for large batches; retrieve paginated output with get_batch_results."
4
4
  metadata:
5
- version: 5.6.6
5
+ version: 6.21.1
6
6
  source: crawlforge-mcp-server
7
7
  ---
8
8
 
@@ -2,7 +2,7 @@
2
2
  name: crawlforge-browser-sessions
3
3
  description: "Keeps one browser page alive across several tool calls with CrawlForge's browser_session tool. Use when the user needs to log in and then read pages behind that login, work through a multi-step flow (search, filter, paginate, fill a wizard), or look at a page before deciding what to click. The loop is: open a session on a URL, snapshot it to list the interactive elements with stable refs (@e1, @e2), act on those refs, read the content, close. Unlike scrape_with_actions, which is one-shot and closes its browser when the call returns, a session survives between calls, so one login is paid for once and a wrong selector costs one call instead of a whole chain."
4
4
  metadata:
5
- version: 5.6.6
5
+ version: 6.21.1
6
6
  source: crawlforge-mcp-server
7
7
  ---
8
8
 
@@ -2,7 +2,7 @@
2
2
  name: crawlforge-change-tracking
3
3
  description: "Monitors web pages for changes over time with CrawlForge's track_changes tool. Use when the user wants to track changes to a page, watch a URL, monitor competitor pricing, detect when content updates, get notified of regulation or product-availability changes, or diff a page against a saved baseline. Workflow: create a baseline with operation create_baseline, then periodically compare with operation compare to get a change percentage and a diff; supports CSS-selector scoping, webhooks, and scheduled monitoring."
4
4
  metadata:
5
- version: 5.6.6
5
+ version: 6.21.1
6
6
  source: crawlforge-mcp-server
7
7
  ---
8
8
 
@@ -2,7 +2,7 @@
2
2
  name: crawlforge-deep-research
3
3
  description: "Runs multi-source web research and autonomous question-answering with CrawlForge's deep_research, agent, search_web, and reddit_search tools. Use when the user wants to research a topic, do a deep dive, compare competitors, gather facts with citations, answer a question from the web, search the web, or get a synthesized report from many sources, or search Reddit posts, comments, and threads. deep_research expands queries, fetches and dedupes sources, then synthesizes; agent autonomously plans searches and navigation from a plain-English prompt with no URLs needed; search_web returns ranked results; reddit_search searches Reddit via community archives (reddit.com blocks scrapers). Caps costs via max_urls and confirms before expensive runs."
4
4
  metadata:
5
- version: 5.6.6
5
+ version: 6.21.1
6
6
  source: crawlforge-mcp-server
7
7
  ---
8
8
 
@@ -2,7 +2,7 @@
2
2
  name: crawlforge-getting-started
3
3
  description: "Orientation and tool-selection guide for the CrawlForge MCP server's 31 web tools. Use when the user is getting started with CrawlForge, asks which CrawlForge tool to use, how to set up the API key, how skills or the CLI work, what a tool costs in credits, or when one tool fails and a fallback is needed. Routes requests to the right specialized skill (web scraping, deep research, stealth, structured extraction, change tracking, batch automation, browser sessions), and explains MCP-tools-vs-CLI, the Ollama-first LLM fallback chain, and per-tool credit costs."
4
4
  metadata:
5
- version: 5.6.6
5
+ version: 6.21.1
6
6
  source: crawlforge-mcp-server
7
7
  ---
8
8
 
@@ -2,7 +2,7 @@
2
2
  name: crawlforge-stealth-browsing
3
3
  description: "Bypasses bot detection with CrawlForge's stealth_mode tool and looks up a country's locale settings with localization. Use when a site returns 403 or 429, CAPTCHAs, 'please enable JavaScript', or empty content, or is protected by Cloudflare, DataDome, or PerimeterX, or when the user needs a page in a specific locale, language, or timezone. stealth_mode runs a stealth browser (engine auto by default: camoufox/Firefox for advanced fingerprinting, playwright/Chromium when pinned for speed) and can screenshot; localization returns a country's Accept-Language, timezone and currency for you to pass to fetch_url or stealth_mode - it applies nothing itself and uses no proxy. Explains when to escalate from a normal scrape to stealth, and why a hard block needs a residential proxy."
4
4
  metadata:
5
- version: 5.6.6
5
+ version: 6.21.1
6
6
  source: crawlforge-mcp-server
7
7
  ---
8
8
 
@@ -71,8 +71,8 @@ differs from the one the browser was launched with) is named in the result's
71
71
  - `engine:"auto"` (default) — Camoufox when its binary is installed, Chromium
72
72
  with a warning when it is not. Leave it alone unless you have a reason.
73
73
  - `engine:"camoufox"` — Firefox-based with native anti-detection (no patches).
74
- The only engine that cleared Cloudflare Turnstile (indeed.com) and Akamai
75
- (harrods.com) in the 2026-09-21 benchmark. Pin it to require that engine: it
74
+ The engine that gets past Cloudflare Turnstile and Akamai where Chromium is
75
+ blocked (e.g. indeed.com, harrods.com). Pin it to require that engine: it
76
76
  errors rather than falling back. Costs roughly +0.8 s and +400 MB per call
77
77
  against Chromium.
78
78
  - `engine:"playwright"` — Chromium with stealth patches. Pin it for speed on
@@ -32,8 +32,8 @@ them default to `auto`.
32
32
  - Firefox-based with **native** anti-detection (no runtime patches — uses
33
33
  Firefox's genuine properties).
34
34
  - Scores higher on CreepJS and the other detector pages than patched Chromium,
35
- and was the only engine to clear Cloudflare Turnstile (indeed.com) and Akamai
36
- (harrods.com) in the 2026-09-21 benchmark.
35
+ and gets past Cloudflare Turnstile and Akamai where Chromium is blocked
36
+ (e.g. indeed.com, harrods.com).
37
37
  - Heavier: measured once each on an Apple Silicon Mac, 946 ms and 667 MB to
38
38
  launch a browser, a context and a blank page, against Chromium's 148 ms and
39
39
  253 MB.
@@ -2,7 +2,7 @@
2
2
  name: crawlforge-structured-extraction
3
3
  description: "Extracts structured JSON and analyzes content with CrawlForge's extract_structured, extract_with_llm, extract_embedded_state, scrape_structured, scrape_template, process_document, analyze_content, summarize_content, and list_ollama_models tools. Use when the user wants to extract specific fields, pull data into a JSON schema, extract by natural-language prompt, scrape with CSS selectors, read a page's embedded JavaScript state (__NEXT_DATA__, React Server Components, Nuxt, Apollo, Redux), get product, profile, or repo data from known sites (Amazon, LinkedIn, GitHub, YouTube, Reddit, and more), parse a PDF or DOCX, summarize a page, or analyze sentiment, entities, or keywords. Defaults to local Ollama for LLM extraction; OpenAI and Anthropic optional."
4
4
  metadata:
5
- version: 5.6.6
5
+ version: 6.21.1
6
6
  source: crawlforge-mcp-server
7
7
  ---
8
8