/seo-audit
SEO health check for any URL or project landing page. Fetches the page, analyzes meta tags, OG, JSON-LD, sitemap, robots.txt, checks SERP positions for target keywords, and outputs a scored report.
seo-cli (use if available)
Dedicated SEO CLI — repo: https://github.com/fortunto2/seo-cli
# Install (one-time)
git clone https://github.com/fortunto2/seo-cli.git ~/startups/shared/seo-cli
cd ~/startups/shared/seo-cli && uv venv && uv pip install -e .
cp config.example.yaml config.yaml # add credentials (GSC, Bing, Yandex, IndexNow)
# Detect seo-cli
python ~/startups/shared/seo-cli/cli.py --help 2>/dev/null
If available, prefer seo-cli commands over manual WebFetch:
python ~/startups/shared/seo-cli/cli.py audit <url> — deep page audit (meta, OG, JSON-LD, keywords, readability)
python ~/startups/shared/seo-cli/cli.py status — dashboard for all registered sites
python ~/startups/shared/seo-cli/cli.py analytics <site> — GSC data (queries, impressions, CTR)
python ~/startups/shared/seo-cli/cli.py monitor <site> — position tracking with snapshots
python ~/startups/shared/seo-cli/cli.py reindex <url> — instant reindex via Google Indexing API + IndexNow
python ~/startups/shared/seo-cli/cli.py competitors <keyword> — SERP + competitor analysis
python ~/startups/shared/seo-cli/cli.py launch <site> — full new site promotion workflow
If seo-cli is not available, fall back to WebFetch-based audit below.
MCP Tools (use if available)
web_search(query, engines, include_raw_content) — SERP position check, competitor analysis
project_info(name) — get project URL if auditing by project name
If MCP tools are not available, use Claude WebSearch/WebFetch as fallback.
Steps
Parse target from $ARGUMENTS.
- If URL (starts with
http): use directly.
- If project name: look up URL from project README, CLAUDE.md, or
docs/prd.md.
- If empty: ask via AskUserQuestion — "Which URL or project to audit?"
Fetch the page via WebFetch. Extract:
<title> tag (length check: 50-60 chars ideal)
<meta name="description"> (length check: 150-160 chars ideal)
- Open Graph tags:
og:title, og:description, og:image, og:url, og:type
- Twitter Card tags:
twitter:card, twitter:title, twitter:image
- JSON-LD structured data (
<script type="application/ld+json">)
<link rel="canonical"> — canonical URL
<html lang="..."> — language tag
<link rel="alternate" hreflang="..."> — i18n tags
- Heading structure: H1 count (should be exactly 1), H2-H3 hierarchy
Check infrastructure files:
- Fetch
{origin}/sitemap.xml — exists? Valid XML? Page count?
- Fetch
{origin}/robots.txt — exists? Disallow rules? Sitemap reference?
- Fetch
{origin}/favicon.ico — exists?
Forced reasoning — assess before scoring:
Write out before proceeding:
- What's present: [list of found elements]
- What's missing: [list of absent elements]
- Critical issues: [anything that blocks indexing or sharing]
SERP position check — for 3-5 keywords:
- Extract keywords from page title + meta description + H1.
- For each keyword, search via MCP
web_search(query="{keyword}") or WebSearch.
- Record: position of target URL in results (1-10, or "not found").
- Record: top 3 competitors for each keyword.
Score calculation (0-100):
| Check |
Max Points |
Criteria |
| Title tag |
10 |
Exists, 50-60 chars, contains primary keyword |
| Meta description |
10 |
Exists, 150-160 chars, compelling |
| OG tags |
10 |
og:title, og:description, og:image all present |
| JSON-LD |
10 |
Valid structured data present |
| Canonical |
5 |
Present and correct |
| Sitemap |
10 |
Exists, valid, referenced in robots.txt |
| Robots.txt |
5 |
Exists, no overly broad Disallow |
| H1 structure |
5 |
Exactly one H1, descriptive |
| HTTPS |
5 |
Site uses HTTPS |
| Mobile meta |
5 |
Viewport tag present |
| Language |
5 |
lang attribute on <html> |
| Favicon |
5 |
Exists |
| SERP presence |
15 |
Found in top 10 for target keywords |
Write report to docs/seo-audit.md (in project context) or print to console:
# SEO Audit: {URL}
**Date:** {YYYY-MM-DD}
**Score:** {N}/100
## Summary
{2-3 sentence overview of SEO health}
## Checks
| Check | Status | Score | Details |
|-------|--------|-------|---------|
| Title | pass/fail | X/10 | "{actual title}" (N chars) |
| ... | ... | ... | ... |
## SERP Positions
| Keyword | Position | Top Competitors |
|---------|----------|----------------|
| {kw} | #N or N/A | competitor1, competitor2, competitor3 |
## Critical Issues
- {issue with fix recommendation}
## Recommendations (Top 3)
1. {highest impact fix}
2. {second priority}
3. {third priority}
Output summary — print score and top 3 recommendations.
Notes
- Score is relative — 80+ is good for a landing page, 90+ is excellent
- SERP checks are approximations (not real-time ranking data)
- Run periodically after content changes or before launch
Gotchas
- SPA without SSR scores zero — if the page is client-rendered only (React SPA, no Next.js SSR), search engines see an empty
<div id="root">. Check view-source: to verify HTML contains actual content.
- Sitemap in robots.txt is not optional — Google finds sitemaps from robots.txt
Sitemap: directive. Without it, indexing depends on internal link crawling which is slow for new sites.
- JSON-LD errors are silent — malformed JSON-LD doesn't break the page but Google ignores it completely. Validate with https://search.google.com/test/rich-results before shipping.
- Multiple H1 tags confuse crawlers — many UI frameworks render component titles as H1. Audit the actual DOM — there should be exactly one H1 per page. Use H2-H3 for section headings.
- Core Web Vitals affect ranking — LCP (Largest Contentful Paint) < 2.5s, FID (First Input Delay) < 100ms, CLS (Cumulative Layout Shift) < 0.1. Check via
npx lighthouse {url} --output=json if lighthouse is available.
Common Issues
Page fetch fails
Cause: URL is behind authentication, CORS, or returns non-HTML.
Fix: Ensure the URL is publicly accessible. For SPAs, check if content is server-rendered.
SERP positions show "not found"
Cause: Site is new or not indexed by search engines.
Fix: This is expected for new sites. Submit sitemap to Google Search Console and re-audit in 2-4 weeks.
Low score despite good content
Cause: Missing infrastructure files (sitemap.xml, robots.txt, JSON-LD).
Fix: These are the highest-impact fixes. Generate sitemap, add robots.txt with sitemap reference, and add JSON-LD structured data.
1---2name: solo-seo-audit3description: Use when "check SEO", "audit this page", "SEO score", "check meta tags", "SERP position", or need website SEO health check. Do NOT use for landing content (/landing-gen) or social media posts (/content-gen).4license: MIT5---67# /seo-audit89SEO health check for any URL or project landing page. Fetches the page, analyzes meta tags, OG, JSON-LD, sitemap, robots.txt, checks SERP positions for target keywords, and outputs a scored report.1011## seo-cli (use if available)1213Dedicated SEO CLI — repo: `https://github.com/fortunto2/seo-cli`1415```bash16# Install (one-time)17git clone https://github.com/fortunto2/seo-cli.git ~/startups/shared/seo-cli18cd ~/startups/shared/seo-cli && uv venv && uv pip install -e .19cp config.example.yaml config.yaml # add credentials (GSC, Bing, Yandex, IndexNow)20```2122```bash23# Detect seo-cli24python ~/startups/shared/seo-cli/cli.py --help 2>/dev/null25```2627If available, prefer `seo-cli` commands over manual WebFetch:28- `python ~/startups/shared/seo-cli/cli.py audit <url>` — deep page audit (meta, OG, JSON-LD, keywords, readability)29- `python ~/startups/shared/seo-cli/cli.py status` — dashboard for all registered sites30- `python ~/startups/shared/seo-cli/cli.py analytics <site>` — GSC data (queries, impressions, CTR)31- `python ~/startups/shared/seo-cli/cli.py monitor <site>` — position tracking with snapshots32- `python ~/startups/shared/seo-cli/cli.py reindex <url>` — instant reindex via Google Indexing API + IndexNow33- `python ~/startups/shared/seo-cli/cli.py competitors <keyword>` — SERP + competitor analysis34- `python ~/startups/shared/seo-cli/cli.py launch <site>` — full new site promotion workflow3536If seo-cli is not available, fall back to WebFetch-based audit below.3738## MCP Tools (use if available)3940- `web_search(query, engines, include_raw_content)` — SERP position check, competitor analysis41- `project_info(name)` — get project URL if auditing by project name4243If MCP tools are not available, use Claude WebSearch/WebFetch as fallback.4445## Steps46471. **Parse target** from `$ARGUMENTS`.48 - If URL (starts with `http`): use directly.49 - If project name: look up URL from project README, CLAUDE.md, or `docs/prd.md`.50 - If empty: ask via AskUserQuestion — "Which URL or project to audit?"51522. **Fetch the page** via WebFetch. Extract:53 - `<title>` tag (length check: 50-60 chars ideal)54 - `<meta name="description">` (length check: 150-160 chars ideal)55 - Open Graph tags: `og:title`, `og:description`, `og:image`, `og:url`, `og:type`56 - Twitter Card tags: `twitter:card`, `twitter:title`, `twitter:image`57 - JSON-LD structured data (`<script type="application/ld+json">`)58 - `<link rel="canonical">` — canonical URL59 - `<html lang="...">` — language tag60 - `<link rel="alternate" hreflang="...">` — i18n tags61 - Heading structure: H1 count (should be exactly 1), H2-H3 hierarchy62633. **Check infrastructure files:**64 - Fetch `{origin}/sitemap.xml` — exists? Valid XML? Page count?65 - Fetch `{origin}/robots.txt` — exists? Disallow rules? Sitemap reference?66 - Fetch `{origin}/favicon.ico` — exists?67684. **Forced reasoning — assess before scoring:**69 Write out before proceeding:70 - **What's present:** [list of found elements]71 - **What's missing:** [list of absent elements]72 - **Critical issues:** [anything that blocks indexing or sharing]73745. **SERP position check** — for 3-5 keywords:75 - Extract keywords from page title + meta description + H1.76 - For each keyword, search via MCP `web_search(query="{keyword}")` or WebSearch.77 - Record: position of target URL in results (1-10, or "not found").78 - Record: top 3 competitors for each keyword.79806. **Score calculation** (0-100):8182 | Check | Max Points | Criteria |83 |-------|-----------|----------|84 | Title tag | 10 | Exists, 50-60 chars, contains primary keyword |85 | Meta description | 10 | Exists, 150-160 chars, compelling |86 | OG tags | 10 | og:title, og:description, og:image all present |87 | JSON-LD | 10 | Valid structured data present |88 | Canonical | 5 | Present and correct |89 | Sitemap | 10 | Exists, valid, referenced in robots.txt |90 | Robots.txt | 5 | Exists, no overly broad Disallow |91 | H1 structure | 5 | Exactly one H1, descriptive |92 | HTTPS | 5 | Site uses HTTPS |93 | Mobile meta | 5 | Viewport tag present |94 | Language | 5 | `lang` attribute on `<html>` |95 | Favicon | 5 | Exists |96 | SERP presence | 15 | Found in top 10 for target keywords |97987. **Write report** to `docs/seo-audit.md` (in project context) or print to console:99100 ```markdown101 # SEO Audit: {URL}102103 **Date:** {YYYY-MM-DD}104 **Score:** {N}/100105106 ## Summary107 {2-3 sentence overview of SEO health}108109 ## Checks110111 | Check | Status | Score | Details |112 |-------|--------|-------|---------|113 | Title | pass/fail | X/10 | "{actual title}" (N chars) |114 | ... | ... | ... | ... |115116 ## SERP Positions117118 | Keyword | Position | Top Competitors |119 |---------|----------|----------------|120 | {kw} | #N or N/A | competitor1, competitor2, competitor3 |121122 ## Critical Issues123 - {issue with fix recommendation}124125 ## Recommendations (Top 3)126 1. {highest impact fix}127 2. {second priority}128 3. {third priority}129 ```1301318. **Output summary** — print score and top 3 recommendations.132133## Notes134135- Score is relative — 80+ is good for a landing page, 90+ is excellent136- SERP checks are approximations (not real-time ranking data)137- Run periodically after content changes or before launch138139## Gotchas1401411. **SPA without SSR scores zero** — if the page is client-rendered only (React SPA, no Next.js SSR), search engines see an empty `<div id="root">`. Check `view-source:` to verify HTML contains actual content.1422. **Sitemap in robots.txt is not optional** — Google finds sitemaps from robots.txt `Sitemap:` directive. Without it, indexing depends on internal link crawling which is slow for new sites.1433. **JSON-LD errors are silent** — malformed JSON-LD doesn't break the page but Google ignores it completely. Validate with https://search.google.com/test/rich-results before shipping.1444. **Multiple H1 tags confuse crawlers** — many UI frameworks render component titles as H1. Audit the actual DOM — there should be exactly one H1 per page. Use H2-H3 for section headings.1455. **Core Web Vitals affect ranking** — LCP (Largest Contentful Paint) < 2.5s, FID (First Input Delay) < 100ms, CLS (Cumulative Layout Shift) < 0.1. Check via `npx lighthouse {url} --output=json` if lighthouse is available.146147## Common Issues148149### Page fetch fails150**Cause:** URL is behind authentication, CORS, or returns non-HTML.151**Fix:** Ensure the URL is publicly accessible. For SPAs, check if content is server-rendered.152153### SERP positions show "not found"154**Cause:** Site is new or not indexed by search engines.155**Fix:** This is expected for new sites. Submit sitemap to Google Search Console and re-audit in 2-4 weeks.156157### Low score despite good content158**Cause:** Missing infrastructure files (sitemap.xml, robots.txt, JSON-LD).159**Fix:** These are the highest-impact fixes. Generate sitemap, add robots.txt with sitemap reference, and add JSON-LD structured data.