librecrawl-audit — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited librecrawl-audit (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
These are hard rules. If you skip any of them, the audit is broken from the user's perspective even if the MCP call succeeded.
After librecrawl_audit_zip returns, you MUST decode the content_base64 and write it to a local file on the operator's machine. Use the user's current working directory (or ~/Downloads/<filename>). Use exactly the filename field from the response.
Pseudo-code your runtime should execute:
import base64, hashlib
data = base64.b64decode(content_base64)
local_path = filename # the filename field from the response, e.g. "example.com-1a2b3c4d.zip"
with open(local_path, "wb") as f:
f.write(data)
local_sha = hashlib.sha256(data).hexdigest()You report the LOCAL path back to the user. NEVER report the zip_path field from the response as the deliverable — that path is on the remote server, not on the operator's local machine. The user cannot open it.
After writing the file, call librecrawl_audit_confirm_saved(session_id, sha256=local_sha) with the hash of the bytes you wrote. The server compares it with its own copy and only then deletes the session, artifacts, zip and upstream crawl record. A mismatch deletes nothing, so a failed save never loses the audit. Unconfirmed zips are swept after an hour.
auto_cleanup=True on librecrawl_audit_zip wipes in the same call. Use it only when your client cannot write files and call back.
Pattern your final response on this:
Audit done. Saved locally: ./example.com-1a2b3c4d.zip (320 KB, sha256 confirmed by the server) Contents: SUMMARY.txt + PDF report + CSVs (per-page · sitemap-recon · external-links · content-audit · extended-checks). Server cleanup: passed.If confirm_saved returns success: false, say which cleanup step failed (see its cleanup field) instead of claiming the server forgot everything.
Do not say:
❌ "Zip created: /home/<user>/librecrawl-reports/example.com-XXXX.zip"
That path is on the server. Useless to the user.
Drive the librecrawl-technical-seo-audit-mcp MCP server to produce a complete technical SEO audit of any website. The MCP exposes 38 tools at mcp__librecrawl-posi__* (or whatever the local connector name is in the user's config).
The user wants to audit a website's technical SEO. Triggers:
<url>" / "audit this site"NOT for: keyword research, backlink analysis, competitor SERP analysis (use other tools).
ALWAYS use librecrawl_start_chunked_audit for real audits. It's the only one that handles big sites without timing out.
librecrawl_start_chunked_audit(
url="https://example.com",
total_max_pages=10000, # default 10k; raise to 100k for huge sites
chunk_target_pages=50, # polling-window size
politeness="auto", # AIMD adaptive (recommended)
fill_sitemap_orphans=True, # default — also fetch sitemap URLs not internally linked
sitemap_fill_cap=500 # default cap for orphan fill
)
→ returns { session_id, status: "queued", url, total_max_pages }Do NOT use librecrawl_audit (no start_chunked_ prefix) for production — it blocks for up to 2 hours and disconnects the MCP client on big sites. It exists for backwards-compat only.
librecrawl_audit_status(session_id) → {
status: "queued" | "crawling" | "throttled" | "paused" | "done" | "cancelled" | "failed",
pages_done: int,
total_max_pages: int,
current_delay_ms: int, # the AIMD controller tunes this live
last_chunks: [...], # last 3 chunk metrics (p95 / err_rate)
recent_events: [...],
audit_complete: bool, # True only if sitemap fully covered + no caps hit
incomplete_reasons: [...],
artifacts_ready: bool
}Poll every 20-30 seconds. The runner does its own background work; you do NOT need to keep the MCP call alive between polls. Status is read from local SQLite, so polling is essentially free.
When status == "done", proceed to step 3.
librecrawl_audit_zip(session_id) → {
filename: "<domain>-<session8>.zip",
size_bytes: int,
file_count: int,
files: [{kind, name, bytes}, ...],
missing: [...], # artifacts that could not be included
sha256: "...",
content_base64: "...", # decode + save locally
zip_path: "...", # REMOTE path, forensics only
next_step: "..."
}Save it (Rule 1), then:
librecrawl_audit_confirm_saved(session_id, sha256=local_sha) → {
success: bool, # True only if every cleanup step passed
cleanup: { files_deleted, file_errors, session_rows, upstream, zip_file_deleted, ok }
}⚠️ zip_path is on the remote server, not the operator's machine. Never report it as the deliverable.
The zip contains:
| File | Format | Use |
|---|---|---|
SUMMARY.txt | Plain | One-page orientation (site, session, pages, audit_complete, artifact list) |
<domain>-<ts>.pdf | Branded human-readable report (open in any PDF viewer) | |
<domain>-<ts>.md | Markdown | Source of the PDF — grep-friendly |
<domain>-<ts>.per-page.csv | CSV | One row per crawled URL × 30 columns of check booleans + failed_checks_list |
<domain>-<ts>.sitemap-recon.csv | CSV | Sitemap-vs-crawl diff |
<domain>-<ts>.external-links.csv | CSV | Every outbound URL with HEAD-validated status |
<domain>-<ts>.content-audit.csv | CSV | Per-page readability + AI-tells + punctuation findings |
<domain>-<ts>.extended-checks.csv | CSV | One row per (URL × check_name × severity × detail) finding |
When the user asks "show me the broken pages" or "what schema errors do I have", read the relevant CSV from the saved zip and filter — do NOT re-run the audit.
After a successful confirm_saved the server holds nothing for this audit. For a fresh audit, start over from step 1.
1. librecrawl_start_chunked_audit(url=X, total_max_pages=10000)
2. Poll librecrawl_audit_status until done
3. librecrawl_audit_zip(session_id)
4. Save zip locally, then librecrawl_audit_confirm_saved(session_id, sha256)
5. Read per-page.csv, filter status_4xx == 1 OR status_5xx == 1, show table
6. Read external-links.csv, filter status_class IN ("not_found", "forbidden", "server_error_5xx", "timeout", "dns_error"), show table1. Run chunked audit first (skill above)
2. After download: librecrawl_schema_validate(crawl_id) — wait, this needs an alive session.
PREFERRED: run it after librecrawl_audit_zip but BEFORE confirm_saved, while the
upstream crawl still exists, then confirm.Same chunked workflow — the .external-links.csv sidecar covers it. Don't run librecrawl_external_links_audit standalone unless the user has a crawl_id from a chunked audit that has not been confirmed yet.
librecrawl_start_chunked_audit(
url=X,
total_max_pages=100000,
chunk_target_pages=100, # larger windows = faster polling resolution
confirm_unbounded=False # safety: leave the 100k cap
)The AIMD controller will adapt the crawl-delay based on the target's responsiveness. Don't override politeness unless the user has explicit permission to hammer the site.
When you start an audit, set expectations:
The server is ephemeral — emphasise that the zip is the only copy.
librecrawl_audit_zip before status is done — returns an error.librecrawl_brain_purge_audit: librecrawl_audit_confirm_saved does this for you.Quick map of all tools the librecrawl MCP exposes. Most flows use only the highlighted 4.
Chunked audit flow (use these in 95% of cases):
Specialist tools (run standalone when needed, but most data is already in the chunked-audit zip):
librecrawl_external_links_audit(crawl_id) — re-run external-link validation on a specific crawllibrecrawl_schema_validate(crawl_id) — deeper schema validationlibrecrawl_merge_gsc_data(crawl_id, gsc_data) — fold in GSC clicks/impressionslibrecrawl_pagespeed_audit_all_crawl_pages(crawl_id) — full PSI across every crawled URLlibrecrawl_schema_check(url), librecrawl_schema_audit(urls) — per-URL schema inspectionlibrecrawl_site_check(url) — instant site-level check (robots, sitemap, HTTPS)librecrawl_pagespeed(url), librecrawl_pagespeed_audit(urls) — PSI for individual URLsMaintenance:
librecrawl_audit_pause, librecrawl_audit_resume, librecrawl_audit_cancel, librecrawl_audit_force_advance — session controlLegacy (avoid):
librecrawl_audit, librecrawl_full_audit_strict — blocking variants kept for backwards compatlibrecrawl_generate_report, librecrawl_report_content, librecrawl_audit_pdf — older artifact APIsGitHub: https://github.com/adityaarsharma/librecrawl-technical-seo-audit-mcp · MIT · By Aditya Sharma
~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.