Skip to main content
Glama
fix-a-lot

Crawl4ai Local

by fix-a-lot

Crawl4ai Local

ν•œκ΅­μ–΄ | English

Local MCP server for Crawl4ai on Linux.

πŸ€– Most of the code was generated with AI assistance.

See also:

Requirements

  • uv

  • Linux

  • Python 3.14 (installing via uv is fine: uv python install 3.14)

Related MCP server: Crawl4AI MCP

Installation

uv sync

# Install Chromium for Playwright
uv run crawl4ai-setup

If the browser fails to launch because system libraries required by Chromium are missing, install them with the command below (requires sudo; apt-based distros such as Debian/Ubuntu only).

uv run playwright install-deps chromium

Running

🚨 This server is a stdio server β€” no need to keep it running like a network server. When connected to an agent as MCP, the agent automatically spins up the server instance.

# Check for the "πŸš€ Crawl4ai MCP server started." message, then exit
uv run main

βœ… After confirming the welcome message, press Ctrl+C to exit, then add the MCP to your agent.

# Tip: debugging mode (MCP Inspector)
uv run mcp dev src/crawl4ai_local/server.py

Adding MCP to an Agent

Claude Code

claude mcp add --transport stdio --scope user crawl4ai -- uv run --directory <parent_location>/crawl4ai-local main
  • parent_location: An absolute path like /home/<user>/dev/repo (use ~ only where the shell expands it)

  • e.g. claude mcp add --transport stdio --scope user crawl4ai -- uv run --directory /home/me/dev/repo/crawl4ai-local main

# Check MCP installation
claude mcp list
claude mcp get crawl4ai

Hermes Agent

hermes mcp add crawl4ai --command "uv" --args "run" "--directory" "<parent_location>/crawl4ai-local" "main"
  • parent_location: An absolute path like /home/<user>/dev/repo (use ~ only where the shell expands it)

  • e.g. /home/me/dev/repo/crawl4ai-local

# Check MCP installation
hermes mcp list

Tools

crawl_markdown(url, wait_seconds=0, wait_selector="")

Crawl a URL and return the content as Markdown.

crawl_structured(url, selector, fields, wait_seconds=0, wait_selector="")

Extract repeated elements as JSON using CSS selectors.

crawl_structured(
    url="https://books.toscrape.com/",
    selector="article.product_pod",
    fields={"제λͺ©": "h3@title", "가격": ".price_color:text"},
)
# β†’ [{"제λͺ©": "A Light in the Attic", "가격": "Β£51.77"}, ...]

Field spec syntax:

Spec

Meaning

"td" or "td:text"

Text of the matched element

"a@href"

Attribute of a child element (element@attribute)

"@data-value"

Attribute of the base element itself

"td:nth-of-type(1)"

Nth element β€” use standard CSS (:eq() is not supported)

Both tools share the waiting options (see below).

crawl_screenshot(url, output_path)

Capture a full-page screenshot and save it to a file. Parent directories are created automatically.

πŸ’‘ If output_path is a Windows path (C:\Users\me\shot.png, D:/shots/a.png), it is converted to the mounted drive path (/mnt/c/Users/me/shot.png) before saving.

πŸ›‘οΈ output_path is checked against a blocklist of known system directories before the crawl even runs.

  • Linux: /bin, /boot, /dev, /etc, /lib, /lib32, /lib64, /libx32, /proc, /run, /sbin, /snap, /sys, /usr, /var

  • Windows (under /mnt/<drive>/, case-insensitive): Windows, Program Files, Program Files (x86), ProgramData, System Volume Information, $Recycle.Bin, Users/All Users, Users/Default

A path that resolves into one of these (including via .. traversal or symlinks) is rejected with an error message, so an agent can't accidentally overwrite system files. This is a blocklist, not a full sandbox β€” it guards against mistakes, not a determined attacker. If Windows drives are mounted somewhere other than /mnt/, update _DRIVE_MOUNT_ROOT in server.py accordingly.

Browser Reuse & Crash Recovery

Instead of launching Chromium on every call (which is expensive), the server keeps one browser instance for the process lifetime. If the shared browser crashes mid-session, the server detects it and recovers automatically:

  • Crash detection matches Playwright collapse signatures only β€” Target page, context or browser has been closed, browser has crashed, browsertype.launch failures, etc. Network errors, anti-bot blocks, and timeouts are page-side causes and are never retried with a fresh browser.

  • On crash: dispose the dead instance β†’ launch a new one β†’ retry, up to _MAX_RECREATE_ATTEMPTS = 2 attempts.

  • Both failure paths are checked equally on every attempt: exceptions raised by arun() and result.success=False + crash message in error_message.

  • On the last attempt, a crash is still reported as a failure, but the crawler is not reset again β€” that cleanup already happened on the prior attempt, and resetting a second time could tear down an instance a concurrent request just recreated.

  • Preventive recycling (e.g., refresh every N pages) is intentionally left to Crawl4ai's built-in browser recycling; the server only reacts to actual collapses.

Concurrency note: multiple simultaneous arun() calls on the shared crawler are safe β€” Crawl4ai serializes page creation internally (_page_lock, GH-1198 fix) and manages context lifecycle with refcounting + LRU. Verified with concurrent multi-site smoke tests.

Waiting Options (Dynamic Pages)

For dynamic pages that render content late using JS, use the waiting options of crawl_markdown / crawl_structured.

# Method 1: Wait for a fixed duration (seconds)
crawl_markdown(url="https://example.com", wait_seconds=5)

# Method 2: Wait until a CSS selector appears (takes precedence over wait_seconds when specified)
crawl_markdown(url="https://example.com", wait_selector="div.result-list")

Verification Results (2026-08-25)

Comparison of 3 cases on a local test page that updates content via JS after 3 seconds:

Case

success

Captures Dynamic Content

No waiting option

True

❌ Returns only "Loading..."

wait_seconds=5

True

βœ… Accurately captures final content

wait_selector="#dynamic"

True

❌ Passes immediately if the element already exists in the initial HTML

Findings / Cautions

  1. wait_selector is only valid for "newly created elements." If the text of an element already present in the initial HTML (e.g., <article id="dynamic">Loading...</article>) changes later, the selector matches immediately and passes without waiting. For pages that update text dynamically, use wait_seconds.

  2. Extreme pages trigger anti-bot heuristics. Pages with minimal content and many script tags are flagged by Crawl4ai's anti-bot detector as Blocked by anti-bot protection: Structural: no_content_elements, script_heavy_shell, resulting in success=False. Although rare in production pages, testing should be done on pages containing basic static content (navigation bars, paragraphs, etc.).

Available Tools

3 tools
crawl_markdownB

μ£Όμ–΄μ§„ URL을 ν¬λ‘€λ§ν•΄μ„œ λ§ˆν¬λ‹€μš΄μœΌλ‘œ λ°˜ν™˜ν•œλ‹€.

Args: url: 크둀링할 URL. wait_seconds: HTML을 λ°›κΈ° μ „ λŒ€κΈ° μ‹œκ°„(초). JS 동적 λ‘œλ”© νŽ˜μ΄μ§€μ— μ‚¬μš©. wait_selector: 이 CSS μ…€λ ‰ν„°κ°€ λ‚˜νƒ€λ‚  λ•ŒκΉŒμ§€ λŒ€κΈ°. μ§€μ • μ‹œ wait_seconds보닀 μš°μ„ .

ParametersJSON Schema
NameRequiredDescriptionDefault
urlYes
wait_secondsNo
wait_selectorNo

Output Schema

ParametersJSON Schema
NameRequiredDescription
resultYes

TDQS

B3.3/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries the burden of behavioral disclosure. It does mention the waiting behavior for dynamic pages (wait_seconds, wait_selector) and the priority of wait_selector, which is useful. However, it omits error handling, timeout behavior, authentication requirements, or any side effects beyond fetching the page.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is concise and well-structured, with a one-sentence summary followed by a clear parameter list. It is front-loaded with the core function and avoids unnecessary detail. The only minor loss is not using bullet points, but the format is acceptable.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

The description covers the tool's purpose and parameters adequately, and an output schema exists to define the return format. However, it does not mention any limitations, error scenarios, or when it should not be used, and it lacks explicit differentiation from siblings. For a tool with three parameters and no annotations, it is slightly below the ideal completeness.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The description includes an Args section that explains each parameter beyond the schema titles. It clarifies that wait_seconds is for JS dynamic loading and that wait_selector overrides wait_seconds when specified. This compensates for the 0% schema description coverage and adds meaningful semantics.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the action (crawl), the target (URL), and the output format (markdown). It is specific enough to distinguish from the sibling tools (crawl_screenshot and crawl_structured) based on the output type, though it does not explicitly name the alternatives.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides no guidance on when to use this tool versus the siblings, nor any exclusions or alternative routing. Usage context is only implied by the markdown output, which is insufficient for an agent to make a confident choice.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

crawl_screenshotB

μŠ€ν¬λ¦°μƒ·μ„ 찍어 파일둜 μ €μž₯ν•œλ‹€.

Args: url: 크둀링할 URL. output_path: μ €μž₯ν•  파일 경둜. Windows 경둜("C:\Users\me\x.png")λŠ” /mnt/c/Users/me/x.png 둜 λ³€ν™˜ν•΄ μ €μž₯ν•œλ‹€.

ParametersJSON Schema
NameRequiredDescriptionDefault
urlYes
output_pathYes

Output Schema

ParametersJSON Schema
NameRequiredDescription
resultYes

TDQS

B3.1/5.0
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are provided, so the description carries the full burden. It discloses one behavioral trait: Windows paths are converted to /mnt/c/ format. However, it does not mention other relevant behaviors such as network access, headless browsing, whether the page must be fully loaded, or potential side effects like cookie usage. Given the tool's simplicity, the path conversion is helpful but insufficient for full transparency.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is extremely concise: one purpose sentence plus a formatted Args block. The key behavioral note about path conversion is included without redundancy. The structure is clean and front-loaded, with the essential purpose stated first and parameter details following. Every sentence earns its place.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a two-parameter screenshot tool, the description covers the basic usage and the path conversion quirk. However, it omits any mention of return values (though an output schema exists), error conditions, or runtime requirements (e.g., network access, browser dependencies). It is adequate for simple usage but leaves gaps that could cause the agent to call it incorrectly in edge cases.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 0%, so the description must compensate. For 'url', it merely says 'URL to crawl,' which adds little beyond the parameter name. For 'output_path', it explains the Windows-to-WSL path conversion, which is genuinely useful and goes beyond the schema. This partial enrichment earns a 3 – it provides some semantic value but is not comprehensive.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool's purpose: 'Take a screenshot and save it as a file.' It specifies the resource (screenshot) and the action (save to file), which is specific enough. It does not explicitly differentiate from sibling tools like crawl_markdown or crawl_structured, but the name and action make it obvious it's for capturing screenshots rather than extracting text or structured data, so it's clear but not explicitly contrasted.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

There is no guidance on when to use this tool versus the siblings. It does not mention any alternative or exclusion criteria. The only usage-related note is about Windows path conversion, which is a technical detail rather than a decision guideline. The agent is left to infer that this tool is appropriate when a screenshot is needed, but no explicit when-to-use or when-not-to-use information is provided.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

crawl_structuredA

CSS μ…€λ ‰ν„°λ‘œ 반볡 μš”μ†Œλ₯Ό μž‘μ•„ μ§€μ •ν•œ ν•„λ“œλ§Œ JSON으둜 μΆ”μΆœν•œλ‹€.

Args: url: 크둀링할 URL. selector: 반볡 μš”μ†Œλ₯Ό μž‘λŠ” CSS μ…€λ ‰ν„° (예: "table tr.item", "div.product-card"). fields: μΆ”μΆœν•  ν•„λ“œ. ν‚€=ν•„λ“œλͺ…, κ°’=μΆ”μΆœ μ§€μ • λ¬Έμžμ—΄. - ν…μŠ€νŠΈ: "td" λ˜λŠ” "td:text" - 속성: "a@href" (μš”μ†Œ@속성λͺ…), μš”μ†Œ 자체 속성은 "@data-value" - N번째 μš”μ†ŒλŠ” CSS 문법 "td:nth-of-type(1)" μ‚¬μš© (":eq()" 미지원) 예: {"이름": "td:nth-of-type(1):text", "링크": "a@href"} wait_seconds: HTML을 λ°›κΈ° μ „ λŒ€κΈ° μ‹œκ°„(초). JS 동적 λ‘œλ”© νŽ˜μ΄μ§€μ— μ‚¬μš©. wait_selector: 이 CSS μ…€λ ‰ν„°κ°€ λ‚˜νƒ€λ‚  λ•ŒκΉŒμ§€ λŒ€κΈ°. μ§€μ • μ‹œ wait_seconds보닀 μš°μ„ .

ParametersJSON Schema
NameRequiredDescriptionDefault
urlYes
fieldsYes
selectorYes
wait_secondsNo
wait_selectorNo

Output Schema

ParametersJSON Schema
NameRequiredDescription
resultYes

TDQS

A4.5/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries the behavioral disclosure burden. It discloses that wait_selector takes precedence over wait_seconds, that :eq() is not supported, and how attribute extraction works. It does not cover edge cases like missing elements or error behavior, but the output schema presumably handles return-value details.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The one-sentence purpose is front-loaded, followed by a well-organized Args block where each line contributes actionable detail. The syntax examples and precedence note are dense but avoid redundant or promotional language. Nothing in the description is wasted.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

The description covers all five parameters, including the important wait_selector precedence and field extraction grammar, which is sufficient for invoking the tool. Given that an output schema exists, return values need not be described. Minor gaps such as behavior on empty selections or pagination prevent a 5.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, so the description fully compensates. Every parameter is documented with concrete examples: url, selector with illustrative patterns, fields with text/attribute/Nth-child syntax and a JSON example, plus wait_seconds and wait_selector with usage guidance. This goes far beyond the bare schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description opens with a specific verb and resource: using CSS selectors to capture repeating elements and extract only designated fields into JSON. This clearly distinguishes it from sibling tools crawl_markdown and crawl_screenshot, which produce different output formats.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description gives clear context on how to use the tool, including examples of selectors and field extraction syntax, and notes that wait_seconds is for JS-dynamic pages. However, it does not explicitly state when to prefer this over crawl_markdown or crawl_screenshot, so it lacks explicit exclusions and alternative routing.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

  1. 3 tool updatesv0.1.0
    • First observedcrawl_markdown
    • First observedcrawl_screenshot
    • First observedcrawl_structured

TDQS

A3.8/5.0

Scored across 3 tools

Disambiguation5/5

Each tool converts a URL into a different output format: markdown, screenshot, or structured JSON. The purposes are mutually exclusive and clearly distinguishable by the suffix in the tool name. No overlap or ambiguity exists.

Naming Consistency5/5

All tools share the consistent 'crawl_' prefix followed by the output type (markdown, screenshot, structured). This follows a predictable verb_noun pattern, making it easy to infer tool behavior from the name alone.

Tool Count5/5

Three tools is well-scoped for a focused crawling server that offers three distinct output modes. Each tool earns its place without redundancy or unnecessary bloat.

Completeness4/5

The set covers the core crawling needs: full content (markdown), visual capture (screenshot), and targeted extraction (structured). A minor gap is the lack of a raw HTML option, but the three provided modes handle most practical use cases without dead ends.

Maintenance

ActivityMaintained
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    D
    maintenance
    Enables browser automation and web scraping by exposing Playwright tools through an HTTP-based MCP server. Users can navigate pages, interact with web elements, capture screenshots, and extract structured content using a persistent Chromium instance.
    MIT
  • A
    license
    B
    quality
    A
    maintenance
    Enables AI assistants to crawl websites, extract dynamic content, navigate links, and save structured Markdown files via the MCP protocol, with support for anti-bot bypass, CSS selectors, and custom JavaScript execution.
    1
    42
    MIT
  • F
    license
    Not graded
    quality
    D
    maintenance
    Enables MCP-compatible clients to scrape and extract data from live websites using a headless Chromium browser, with support for CSS selectors, link extraction, table extraction, and Amazon product pages.
    -
  • A
    license
    Not graded
    quality
    C
    maintenance
    Provides a headless Chromium browser through MCP, enabling AI agents to browse JavaScript-rendered pages, search the web, capture screenshots, extract tables and data, and run stateful multi-step interactions like clicking, typing, and form submission.
    1,236,914 npm
    1
    MIT