claude-seo
CLI ToolAgriciDaniel/claude-seo
Open-source Claude Code plugin that runs full SEO audits via 19 AI agents.
Overview
Open-source SEO audit skill for Claude Code. Combines 26 sub-skills and 19 specialist agents covering technical SEO, Core Web Vitals, E-E-A-T, Schema.org markup, AI search visibility (GEO), local, e-commerce and international SEO. Outputs a prioritized action plan with PDF/Excel reports and optional live-data extensions.
README Preview
\n\n\n\nClaude SEO\n\n\n Free, open-source SEO audits inside Claude Code.\n Rank higher in Google and get cited by AI search.\n\n\n\n Install ·\n Demo ·\n Results ·\n Team ·\n Commands ·\n Pricing ·\n FAQ\n\n\n\n \n \n \n \n \n \n \n\n\n\n \n\n\n**Claude SEO is a free SEO plugin for Claude Code.** Give it any website and it runs a full SEO audit in 10 to 15 minutes, then hands you a clear action plan, sorted by what matters most.\n\nIt checks everything a good SEO consultant would:\n\n- **Technical SEO.** Crawlability, indexing, sitemaps, robots.txt, redirects, page speed and Core Web Vitals.\n- **Content quality.** E-E-A-T (experience, expertise, authority and trust), thin pages and topic coverage.\n- **Structured data.** Finds, validates and writes Schema.org markup for rich results.\n- **AI search (GEO).** How likely Google AI Overviews, AI Mode, ChatGPT, Perplexity and Gemini are to cite your pages.\n- **Local and e-commerce SEO.** Google Business Profile, reviews, map pack, product pages and international sites.\n\nClaude SEO is built from 26 sub-skills and 19 specialist agents, and up to 17 agents work on your site at the same time. Every recommendation explains why it matters and how you will know it worked.\n\n## See it in action\n\nType `/seo` in Claude Code to see every command:\n\n\n\nRun a full audit and watch the agents fan out across the site:\n\n\n\n[Watch the full demo on YouTube](https://www.youtube.com/watch?v=COMnNlUakQk)\n\n## Real results\n\nThe project's own site, [claude-seo.md](https://claude-seo.md), audited with Claude SEO from day one: 10.8K Search Console clicks in its first 12 weeks and a 99 mobile PageSpeed score.\n\n\n\n## Meet the team\n\n\n\n\nAll 19 agents\n\n\n| Agent | Role | Model | Command |\n|---|---|---|---|\n| `seo-technical` | Technical SEO Lead | Sonnet | `/seo technical` |\n| `seo-content` | Content Editor | Opus | `/seo content` |\n| `seo-schema` | Structured Data Engineer | Sonnet | `/seo schema` |\n| `s
FAQ (5)
TroubleshootingHow to fix HTTP 404 from keywordseverywhere_api.py (Open PageRank) despite a valid opr_live API key in claude-seo v2.3.1?
The 404 is not an auth failure — the script calls the retired legacy DomCop endpoint. Patch scripts/keywordseverywhere_api.py: 1) Change KWE_BASE from https://openpagerank.keywordseverywhere.com/api/v1.0/getPageRank to https://openpagerank.keywordseverywhere.com/v1/domains/bulk. 2) Switch from GET with repeated domains[] query params to POST with JSON body {"domains": ["example.com"]} (max 100 domains per call). 3) Replace the API-OPR: <key> header with Authorization: Bearer opr_live_.... 4) Parse results[] instead of the removed response[] key and handle error/status_code fields; the new payload also adds referring_domains and a monthly history array. Verify your key first with a positive control: curl -X POST https://openpagerank.keywordseverywhere.com/v1/domains/bulk -H "Authorization: Bearer <key>" -H "Content-Type: application/json" -d '{"domains":["example.com"]}' — HTTP 200 with JSON confirms the key is valid and only the old path (which returns an HTML 404) is broken. Note legacy DomCop keys expire 2026-09-30. Alternative: use the official MCP server at https://openpagerank.keywordseverywhere.com/mcp (same Bearer auth, tools get_domain_rank, get_domain_history, bulk_domain_rank), whose initialize/tools/list respond without a key for CI smoke tests.
TroubleshootingWhy does claude-seo show garbled accented characters (é mojibake) for French or UTF-8 pages?
This is a known bug fixed in claude-seo v2.4.0. requests decodes text/* responses without a charset header as ISO-8859-1 (per RFC 2616), ignoring the page's <meta charset="UTF-8">, so UTF-8 accented characters became mojibake in render_page.py, parse_html.py, nlp_analyze.py and four other scripts. Fix: upgrade to claude-seo v2.4.0+, where all seven scripts share one decoder that checks in order: BOM, header charset, HTML meta charset, then UTF-8 fallback. If patching manually in scripts/url_safety.py, note the maintainers deliberately avoided resp.apparent_encoding because reading it consumes a streamed response body.
TroubleshootingHow to fix "MARKETPLACE_ERROR:REMOTE_SYNC_FAILED" (marketplace_sync_bin_directory_not_allowed) when installing a plugin from the Claude desktop app?
Update the plugin/marketplace to v2.2.6 or later, then re-add it in the Claude desktop app (Customize > Plugins > Add marketplace). The sync fails because claude.ai-hosted plugins may not ship a top-level bin/ directory — those executables would be added to PATH on the CLI but are not shown on the admin approval surface. The desktop UI message "Marketplace sync failed. Check the repository URL and try again" is misleading; the real reason (marketplace_sync_bin_directory_not_allowed) only appears in the app logs. In claude-seo v2.2.6 the fix moved the launcher from bin/claude-seo to scripts/claude-seo, with skills invoking it via ${CLAUDE_PLUGIN_ROOT}. If you maintain a plugin or marketplace: move executables out of bin/ into scripts/ (reference them via ${CLAUDE_PLUGIN_ROOT}) or declare entry points through hooks, commands, or mcpServers instead. Workaround on older versions: install via the Claude Code CLI (claude plugin marketplace add + claude plugin install), which is not affected by this restriction.
TroubleshootingHow to fix 'fetch_page.py: error: unrecognized arguments: --json' in claude-seo?
--json support was added to fetch_page.py in claude-seo v2.3.0 (PR #297). Upgrade to v2.3.0 or later, then run:
./bin/claude-seo run fetch_page.py https://example.com --json
The JSON output shares the same contract as render_page.py, returning fields like url, status_code, content, headers, and redirect_chain, plus a --max-text option to limit body size.
**Workaround for v2.2.5 and earlier:** use render_page.py instead, which already supports --json for structured retrieval:
./bin/claude-seo run render_page.py https://example.com --json
Avoid parsing fetch_page.py's plain-text output (URL: / Status: headers) programmatically — it is brittle and omits response headers and the redirect chain.
TroubleshootingHow to fix 'refused to resolve 127.0.0.1 to non-public IP' ProxyError when using url_safety safe_requests_get behind a local HTTP proxy?
This happens when HTTPS_PROXY=http://127.0.0.1:<port> is set (common in sandbox/CI environments): url_safety.py's DNS-pinning _pin_dns() intercepted the proxy's own host resolution and is_safe_ip('127.0.0.1') rejected it as loopback, causing requests.exceptions.ProxyError ... Failed to resolve '127.0.0.1' for every target URL. This broke ~18 scripts including fetch_page.py, sitemap_discovery.py, and indexnow_submit.py, which misleadingly reported generic 'Request failed' errors. Fix: upgrade to v2.3.0, where PR #295 plus a follow-up exempt the configured proxy host from the DNS-rebinding check — but only after validating the proxy host against the blocklist and re-checking it when it resolves, so a metadata address in HTTPS_PROXY cannot bypass the SSRF guard. No code changes or workarounds are needed after upgrading; the same HTTPS_PROXY setup works as-is.