---
name: kodadocs-seo-docs
description: "Optimize KodaDocs help center pages for search engines with meta tags, structured data, sitemaps, internal linking, and heading hierarchy."
---

# SEO Docs Optimization

## Overview

This skill applies search engine optimization to an existing KodaDocs documentation site. It generates meta descriptions, adds Open Graph and structured data markup, builds a sitemap with priority weighting, creates internal link networks between related articles, generates alt text for screenshots, enforces proper heading hierarchy, and produces a robots.txt. All changes target VitePress-specific configuration (frontmatter, head tags, config.mts).

Use this skill when:
- You have generated a KodaDocs site and want it to rank in search engines
- You want rich previews when doc links are shared on social media or Slack
- You want Google to display FAQ or HowTo rich results for your help pages
- You need a sitemap.xml and robots.txt for crawler discoverability
- You want to improve internal navigation through contextual cross-links
- You want screenshot images to have descriptive alt text for accessibility and SEO

## Prerequisites

1. **Existing doc site:** `.kodadocs/run_manifest.json` must exist from a previous pipeline run.
2. **VitePress output directory:** Must contain `.vitepress/config.mts`, `index.md`, and article markdown files.
3. **KodaDocs MCP tools available:** `mcp__kodadocs__load_manifest` and `mcp__kodadocs__assemble_vitepress`.
4. **Site URL (recommended):** The production URL (e.g., `https://docs.example.com`). Required for canonical URLs, sitemap, and OG tags. If unavailable, placeholders are used with a warning.

## Workflow

### Step 1: Load the Manifest and Inventory the Site

```
Call mcp__kodadocs__load_manifest with:
  project_path: "<project-root>"
```

Extract: `articles`, `output_dir`, `project_name`, `product_summary`, `screenshots`, `brand_color`, `discovered_routes`.

Inventory the output directory and classify each page:
- **landing** -- homepage (index.md)
- **getting-started** -- onboarding or setup guides
- **guide** -- step-by-step how-to content
- **feature** -- feature documentation pages
- **reference** -- API reference, FAQ, troubleshooting, changelog

### Step 2: Generate Meta Descriptions

Create a unique meta description for every page (150-160 characters).

**Rules:**
- Include the primary topic keyword in the first 60 characters
- Active voice, addressing the reader directly
- No duplicates across pages; no keyword stuffing

**Generation process:** Read each article's H1 and first paragraph, extract key action verbs and nouns, compose a summary answering "what will the reader learn?"

**Examples:**
- **Getting Started:** "Learn how to set up [Product] in under 5 minutes. This guide covers installation, configuration, and your first workflow."
- **Feature page:** "Manage team permissions and roles in [Product]. Add members, assign access levels, and configure SSO."
- **FAQ:** "Find answers to common questions about [Product] including setup, billing, integrations, and troubleshooting."

**Add to VitePress frontmatter** (prepend or merge into existing frontmatter):
```markdown
---
description: "Your 150-160 character meta description here."
---
```

For the homepage, add `description` to the existing `layout: home` frontmatter block.

### Step 3: Add Open Graph and Twitter Card Tags

**Global head tags in `.vitepress/config.mts`:**
```typescript
head: [
  ['meta', { property: 'og:type', content: 'website' }],
  ['meta', { property: 'og:site_name', content: '<project-name> Docs' }],
  ['meta', { property: 'og:locale', content: 'en_US' }],
  ['meta', { name: 'twitter:card', content: 'summary' }],
  ['meta', { name: 'theme-color', content: '<brand-color>' }],
],
```

**Per-page OG tags in frontmatter:**
```markdown
---
head:
  - - meta
    - property: og:title
      content: "Article Title - Project Docs"
  - - meta
    - property: og:description
      content: "Meta description here."
  - - meta
    - property: og:url
      content: "https://docs.example.com/article-slug"
---
```

If the manifest contains a `logo_path`, add `og:image` pointing to the logo in assets. If no logo exists, omit og:image.

### Step 4: Generate FAQ Structured Data (JSON-LD)

**Eligible pages:** title/slug contains "faq" or "questions", OR content has 2+ H3 headings ending with "?".

**Process:** Parse H3 question headings, capture text until next H3 as the answer, strip markdown formatting, limit to 10 Q&A pairs.

```json
{
  "@context": "https://schema.org",
  "@type": "FAQPage",
  "mainEntity": [{
    "@type": "Question",
    "name": "How do I reset my password?",
    "acceptedAnswer": {
      "@type": "Answer",
      "text": "Navigate to Settings, click Account, then select Reset Password."
    }
  }]
}
```

**Inject as a frontmatter head script tag:**
```markdown
head:
  - - script
    - type: application/ld+json
    - '{"@context":"https://schema.org","@type":"FAQPage","mainEntity":[...]}'
```

### Step 5: Generate HowTo Structured Data

**Eligible pages:** content has ordered lists (1. 2. 3.) with 3+ steps, or "Step N:" headings. Page type is "guide" or "getting-started".

**Process:** Extract step names and descriptions. Include screenshot URLs for steps that reference images.

```json
{
  "@context": "https://schema.org",
  "@type": "HowTo",
  "name": "How to Set Up Your Dashboard",
  "step": [{
    "@type": "HowToStep",
    "name": "Create an account",
    "text": "Visit the signup page and enter your email address.",
    "url": "https://docs.example.com/getting-started#create-an-account"
  }]
}
```

A single page must NOT have both FAQPage and HowTo. If it qualifies for both, prefer HowTo for instructional content, FAQPage for Q&A content.

### Step 6: Create sitemap.xml

**Priority weighting:**

| Page Type | Priority | Change Freq |
|-----------|----------|-------------|
| Homepage | 1.0 | weekly |
| Getting Started | 0.9 | monthly |
| Guides | 0.8 | monthly |
| Feature pages | 0.7 | monthly |
| FAQ / Troubleshooting | 0.6 | monthly |
| Changelog / Reference | 0.5 | weekly |
| Legal | 0.3 | yearly |

Use today's date (or `last_run_timestamp` from the manifest) as `lastmod`.

**Write to:** `<output-dir>/public/sitemap.xml` (VitePress copies `public/` to build root).

### Step 7: Create robots.txt

```
User-agent: *
Allow: /

Sitemap: <site-url>/sitemap.xml

User-agent: *
Disallow: /assets/*.js
Disallow: /hashmap.json
```

**Write to:** `<output-dir>/public/robots.txt`. If site URL is unknown, comment out the Sitemap line with a TODO.

### Step 8: Add Internal Links Between Related Articles

**Compute relatedness scores** for each article pair:
1. Shared keywords (top 10 nouns/verbs per article): overlap_count / 10
2. Shared route references: +0.3
3. Adjacent order in manifest: +0.2
4. Same page type: +0.1

**Link insertion:**
- 2-4 internal links per article (only above 0.3 relatedness threshold)
- Add a "Related Articles" section at the bottom with link + one-line description
- Add 1-2 inline contextual links where the article body naturally references another topic
- No self-links; for sites with fewer than 3 articles, link to all available

### Step 9: Suggest URL Structure Improvements

Check slugs against these rules and present a suggestions table:

| Issue | Detection | Suggestion |
|-------|-----------|------------|
| Too long (>60 chars) | `len(slug) > 60` | Shorten to key terms |
| Stop words | "the", "and", "of", "a", "to" | Remove stop words |
| Not descriptive | Generic slug like `guide` | Add topic keyword |
| Missing keyword | H1 topic word absent from slug | Include primary keyword |

**Do NOT auto-rename slugs.** URL changes break links. Present suggestions and let the user decide.

### Step 10: Generate Alt Text for Screenshots

For each screenshot referenced in article markdown:
1. Read the image file visually using the Read tool
2. Write alt text (80-125 chars) describing the content and key UI elements
3. Do NOT start with "Screenshot of" or "Image of"

Replace empty or generic alt text in markdown image references:
```markdown
![Dashboard overview with analytics widgets and activity feed](./assets/dashboard.png)
```

### Step 11: Add Canonical URLs and Language Meta Tags

**Canonical URLs** (only if site URL is known -- skip entirely with placeholders):
```markdown
head:
  - - link
    - rel: canonical
      href: "https://docs.example.com/<slug>"
```

**Language config** in `.vitepress/config.mts`:
```typescript
export default defineConfig({
  lang: 'en-US',
  ...
})
```

### Step 12: Optimize Heading Hierarchy

**Rules:** One H1 per page (the title). H2 under H1, H3 under H2. No skipped levels. Homepage uses hero as H1, so no additional H1 in body.

**Fixes:** Multiple H1s -> demote extras to H2. H3 without parent H2 -> promote to H2 or insert H2 above. Report all changes in a table.

### Step 13: Update VitePress Config

Write the final `.vitepress/config.mts` with all SEO settings:
```typescript
import { defineConfig } from 'vitepress'
export default defineConfig({
  title: "<project-name> Docs",
  description: "<homepage meta description>",
  lang: 'en-US',
  cleanUrls: true,
  ignoreDeadLinks: true,
  lastUpdated: true,
  head: [
    ['meta', { property: 'og:type', content: 'website' }],
    ['meta', { property: 'og:site_name', content: '<project-name> Docs' }],
    ['meta', { property: 'og:locale', content: 'en_US' }],
    ['meta', { name: 'twitter:card', content: 'summary' }],
    ['meta', { name: 'theme-color', content: '<brand-color>' }],
    ['link', { rel: 'sitemap', type: 'application/xml', href: '/sitemap.xml' }],
  ],
  sitemap: { hostname: '<site-url>' },
  themeConfig: { ... }
})
```

VitePress 1.x has a built-in `sitemap` config. Use `sitemap.hostname` AND the manual sitemap.xml as fallback.

### Step 14: Reassemble the Doc Site

```
Call mcp__kodadocs__assemble_vitepress with:
  articles: [<updated articles with frontmatter, internal links, alt text, heading fixes>]
  screenshots_dir: "<from manifest>"
  brand_color: "<from manifest>"
  logo_path: "<from manifest or null>"
  output_dir: "<from manifest>"
  project_name: "<from manifest>"
  product_summary: "<from manifest>"
  discovered_routes: [<from manifest>]
```

**IMPORTANT ordering:** `assemble_vitepress` regenerates the output directory. Embed all changes in article content strings BEFORE calling assemble. AFTER assembly, apply config.mts overrides, then write sitemap.xml and robots.txt to `public/`.

### Step 15: Save the Updated Manifest

```
Call mcp__kodadocs__save_manifest with:
  project_path: "<project-root>"
  manifest: { <existing manifest merged with seo section> }
```

Add an `seo` section recording: `optimized_at`, `site_url`, `pages_optimized`, `meta_descriptions`, `structured_data` counts, `sitemap_generated`, `robots_txt_generated`, `internal_links_added`, `alt_texts_generated`, `heading_fixes`, `url_suggestions`.

## Decision Trees

### Page Type Classification

```
Is the file index.md?
  YES -> landing
  NO  -> Slug/title contains "getting-started", "setup", "install", "quickstart"?
           YES -> getting-started
           NO  -> Content has ordered steps or "Step N:" headings?
                    YES -> guide
                    NO  -> Slug/title contains "faq", "question", "troubleshoot", "changelog"?
                             YES -> reference
                             NO  -> feature
```

### Structured Data Selection

```
Page has 2+ H3 headings ending with "?"?
  YES -> More than 50% of content is Q&A pairs?
           YES -> FAQPage
           NO  -> Page also has ordered steps? -> HowTo; else -> FAQPage
  NO  -> Page has 3+ ordered steps or "Step N:" headings?
           YES -> HowTo
           NO  -> No structured data
```

### Meta Description Validation

```
< 120 chars? -> Expand with more detail
> 160 chars? -> Trim from end, re-check coherence
Missing primary keyword? -> Rewrite with keyword in first 60 chars
Duplicates another page? -> Rewrite to differentiate by subtopic
```

### Site URL Resolution

```
User provided URL? -> Use it
Manifest has site_slug? -> https://<site_slug>.kodadocs.com
Manifest has deploy provider? -> Infer URL from provider pattern
None available -> Placeholder URLs; skip canonical tags; warn user
```

### Internal Link Density

```
1-3 articles  -> Link every article to every other
4-8 articles  -> 2-3 links per article
9-20 articles -> 3-4 links per article (highest relatedness)
20+ articles  -> 3-4 links, prioritize same-category
```

### Heading Fix Aggressiveness

```
Article has <!-- manual --> marker? -> Report issues only, do NOT modify
Fix is safe (demoting H1 to H2)? -> Apply automatically
Fix is ambiguous? -> Ask user before applying
```

## Output Format

1. **Updated article markdown** -- SEO frontmatter, structured data, internal links, alt text
2. **Updated `.vitepress/config.mts`** -- OG tags, lang, theme-color, sitemap config
3. **`public/sitemap.xml`** -- priority-weighted URLs
4. **`public/robots.txt`** -- crawler rules with sitemap reference
5. **URL suggestions table** -- slug improvements (not auto-applied)
6. **Heading audit table** -- fixes applied
7. **Console summary:**

```
SEO optimization complete:
  Pages optimized: 8
  Meta descriptions: 8 generated (avg 154 chars)
  Open Graph tags: added to config + 8 pages
  Structured data:
    - FAQPage: 1 page (6 Q&A pairs)
    - HowTo: 2 pages (12 steps total)
  Sitemap: generated with 9 URLs (priority range 0.5-1.0)
  Robots.txt: generated
  Internal links: 18 links added across 8 articles
  Alt text: 12 screenshot descriptions generated
  Heading fixes: 3 issues corrected
  URL suggestions: 2 improvements proposed (review above)
  Canonical URLs: added to 8 pages
  Output: <output-dir>
```

## Error Handling

### No manifest found
Stop and inform: "No previous documentation run found. Run the full KodaDocs pipeline first." Do not attempt optimization without a manifest.

### Empty articles list
Stop and inform: "Manifest found but contains no articles. Generate articles first."

### VitePress config parse failure
Regenerate config from scratch using manifest data. Warn user. Preserve sidebar/nav from manifest.

### Site URL not provided
Generate all assets with placeholder URLs. Skip canonical tags entirely (incorrect canonicals are worse than none). Warn prominently.

### Screenshot image not found
Skip alt text for missing images. Do not remove the reference. Log which images were skipped.

### Structured data size limits
FAQ: include first 10 Q&A pairs only (Google's limit). HowTo: include first 20 steps only. Log truncation.

### Duplicate meta descriptions
If two descriptions have >90% overlap, rewrite the second to differentiate by subtopic.

### Assemble overwrites SEO changes
Expected behavior. The correct approach (Step 14): embed all changes in article content BEFORE assemble, apply config/sitemap/robots AFTER. If `last_run_timestamp > seo.optimized_at` in the manifest, suggest re-running SEO.

### Protected articles (manual edits)
Articles with `<!-- manual -->` markers: report heading violations but do NOT modify. Mark as "SKIPPED (manual edit protected)".

### Malformed frontmatter
Replace the entire frontmatter block with valid YAML. Preserve non-SEO keys (layout, custom). Warn user.

## Success Criteria

- [ ] Manifest loaded and all articles inventoried
- [ ] Every page has a unique meta description (150-160 characters)
- [ ] Open Graph tags in global config and per-page frontmatter
- [ ] FAQ structured data added to qualifying pages
- [ ] HowTo structured data added to step-by-step guides
- [ ] sitemap.xml generated with correct priority weights
- [ ] robots.txt generated with sitemap reference
- [ ] Internal links added (2-4 per article based on relatedness)
- [ ] URL structure suggestions presented to user
- [ ] All screenshots have descriptive alt text (80-125 characters)
- [ ] Canonical URLs added (if site URL available)
- [ ] Language meta tags added to config
- [ ] Heading hierarchy audited and violations fixed (except protected articles)
- [ ] VitePress config.mts updated with all SEO settings
- [ ] Doc site reassembled with all changes
- [ ] Manifest updated with SEO optimization record
- [ ] Console summary printed with statistics
