# Seo Pro Max

> Production-grade SEO implementation skill. Analyzes the existing project first, asks the user the right questions, and then implements every chosen SEO surface (meta tags, Open Graph, Twitter Cards, canonical, robots, sitemap, schema.org/JSON-LD, favicons, llms.txt, admin-panel wiring, database schema) exactly as the user decided. The skill is an EXECUTOR, not a decision-maker.

- **Type:** Skill
- **Install:** `agentstack add skill-aycanozarpaci-seo-pro-max-skill-seo-pro-max-skill`
- **Verified:** Yes — security-reviewed for prompt injection and unsafe behavior
- **Seller:** [aycanozarpaci](https://agentstack.voostack.com/s/aycanozarpaci)
- **Installs:** 0
- **Category:** [Databases](https://agentstack.voostack.com/c/databases)
- **Latest version:** 0.1.0
- **License:** MIT
- **Upstream author:** [aycanozarpaci](https://github.com/aycanozarpaci)
- **Source:** https://github.com/aycanozarpaci/seo-pro-max-skill

## Install

```sh
agentstack add skill-aycanozarpaci-seo-pro-max-skill-seo-pro-max-skill
```

Requires the [AgentStack CLI](https://agentstack.voostack.com/docs/cli). Works with Claude Code, Cursor, and any MCP-compatible agent.

## About

# seo-pro-max

> A cross-IDE SEO skill. Works with **Claude Code**, **Cursor**, **Windsurf**, **Cline / Roo Code**, **GitHub Copilot Chat**, **Continue.dev**, **Aider**, **Zed AI**, and any agent that can read a Markdown rules file.

## Core Principle

**This skill does not decide. The user decides; this skill executes.**

- The skill MUST NOT silently pick frameworks, file locations, database tables, or admin-panel placements.
- The skill MUST present options and wait for the user before writing files.
- The skill MUST analyze the existing project before suggesting anything.

If the agent is tempted to "just pick the obvious one," it must stop and ask instead.

---

## Operating Protocol (Mandatory Order)

### Phase 0 — Read the project

Before any question, the agent must inventory:

1. Framework / runtime — look for `package.json`, `composer.json`, `requirements.txt`, `pyproject.toml`, `Gemfile`, `go.mod`, `pubspec.yaml`, `next.config.*`, `nuxt.config.*`, `astro.config.*`, `vite.config.*`, `remix.config.*`, `svelte.config.*`, `artisan`, `manage.py`.
2. Rendering model — SSR / SSG / SPA / hybrid / RSC / ISR.
3. Routing convention — file-based (`app/`, `pages/`, `src/routes/`, `src/pages/`) vs. controller-based (Laravel `routes/web.php`, Django `urls.py`, Express routers).
4. Existing SEO surface — any current `` management, `metadata` exports, `Helmet`, `next/head`, `useSeoMeta`, blade `@section('meta')`, etc.
5. Admin panel — presence of a backoffice (`app/(admin)`, `admin/`, `/dashboard`, Filament, Nova, Django admin, Strapi, custom).
6. Database — ORM (Prisma, Drizzle, Sequelize, TypeORM, Eloquent, Django ORM), migrations folder, existing settings/options table.
7. i18n — `next-intl`, `next-i18next`, `vue-i18n`, `nuxt-i18n`, Laravel locales, Django i18n.
8. Hosting hints — `vercel.json`, `netlify.toml`, `wrangler.toml`, `Dockerfile`, nginx configs.

The agent reports findings as a short summary, then proceeds to Phase 0.5.

### Phase 0.5 — Dynamic content audit (deterministic, no LLM guesswork)

Static `` / `` on a route that renders **dynamic content** is the single most common SEO failure mode this skill must catch. Phase 0 finds the admin panel; Phase 0.5 maps which content models actually need per-row metadata.

For every content model discovered (Prisma/Drizzle `model`, Eloquent model, Django `models.Model`, TypeORM entity, Strapi content-type, Sanity schema, MDX collection, etc.), the agent answers three yes/no questions **purely from file/schema reading — no inference**:

1. **DB SEO fields present?** — Does the model/table have at least `seo_title` and `seo_description` columns (also accepted: `meta_title`, `metaTitle`, `seoTitle`, or a `seo jsonb` column)?
2. **Admin UI exposes them?** — Does any admin form/page reference those fields (grep the admin directory for the column names)?
3. **Public route consumes them?** — Does the route handler for that model read those fields and pass them to the framework's metadata API (`generateMetadata`, `useSeoMeta`, ``, blade `@section('meta')`, Django template `{% block meta %}`)? Hardcoded `{{ post.title }} | Site` without DB-driven title/description fields counts as **NO**.

The agent prints a matrix:

```
Model     | DB SEO fields | Admin UI | Public route consumes
----------|---------------|----------|----------------------
Post      |       ✗       |    ✗     |          ✗            ← FULL GAP
Product   |       ✓       |    ✗     |          ✓            ← PARTIAL (admin missing)
Page      |       ✓       |    ✓     |          ✓            ← OK
```

Any row with at least one ✗ is a **dynamic SEO gap**. The matrix is carried into Phase 1 as the basis for proactive recommendations. The matrix is also saved verbatim into `seo.config.md` so future runs can re-verify.

If the project has **no content models** (pure static brochure site, single-page landing, marketing one-pager), the agent records "no dynamic content detected" and Phase 1 proceeds without proactive recommendations for Surfaces 11/12/13.

### Phase 1 — Scope intake (ask, don't assume)

**Proactive Recommendations (Phase 0.5 → Phase 1 bridge).**

Before showing the opt-in checklist, the agent emits warnings based on the Phase 0.5 matrix. For every row with at least one ✗, the agent prints:

```
⚠ Dynamic SEO gap detected: 
   DB fields:    Admin UI:    Public route consumes: 
   Without fixing this, every  page will ship with identical or hardcoded
   /. This silently caps your organic traffic.

   Strongly recommended surfaces to tick:
     - Surface 11 (Admin-panel UI for SEO data)   ← if Admin UI = ✗
     - Surface 12 (Database schema for SEO data)  ← if DB fields = ✗
     - Surface 13 (Per-page overrides)            ← always, to define precedence

   You may still skip. If you skip, the agent will log
   "user-rejected dynamic SEO for  — reason: "
   into seo.config.md so the next maintainer knows it was an explicit choice,
   not an oversight.
```

The agent waits for the user to either tick the recommended surfaces or provide a skip reason — **then** shows the full opt-in checklist below.

The agent asks the user, one block at a time, which SEO surfaces are in scope. **Default to none** (except the surfaces auto-recommended above). Each surface is opt-in.

```
Which SEO surfaces should I set up?
[ ] Meta basics (title, description, keywords, language, viewport, charset, theme-color)
[ ] Indexing controls (robots meta, X-Robots-Tag, canonical)
[ ] robots.txt
[ ] XML sitemap (single / index / per-locale)
[ ] Open Graph (Facebook, LinkedIn, WhatsApp, Slack, Discord previews)
[ ] Twitter / X Cards
[ ] Schema.org JSON-LD (Organization, WebSite, Article, Product, BreadcrumbList, FAQ, LocalBusiness, ...)
[ ] Favicons & PWA manifest (favicon.ico, apple-touch-icon, manifest.webmanifest, theme-color)
[ ] Search engine verification (Google Search Console, Bing Webmaster, Yandex)
[ ] llms.txt (see note below)
[ ] Admin-panel UI for SEO data
[ ] Database schema for SEO data
[ ] Per-page overrides (slug-level metadata)
[ ] Language, i18n & hreflang (BCP 47, RTL, bidirectional hreflang, x-default)
[ ] Accessibility / WCAG 2.2 AA (image alt-text enforcement, contrast, keyboard, landmarks, heading hierarchy)
[ ] OG image generation (static / dynamic)
[ ] Core Web Vitals & page performance (LCP, CLS, INP, font, third-party scripts)
[ ] Image optimization (srcset, sizes, AVIF/WebP, lazy, CLS prevention)
[ ] URL structure & slug rules (case, separator, Unicode/Turkish policy, length)
[ ] Internal linking & orphan detection (anchor text, link depth, faceted nav)
[ ] Security & Lighthouse headers (HSTS, CSP, Referrer-Policy, Permissions-Policy)
[ ] Search engine submission & IndexNow (GSC, Bing, Yandex, Naver, Baidu)
[ ] SPA hydration & client-side meta updates
```

### Phase 2 — Per-surface drill-down

For every selected surface, the agent asks the specific questions in the matching section below. The agent **does not pick defaults** — if the user says "you choose," the agent presents 2–3 options with trade-offs and waits.

### Phase 3 — Plan & confirm

The agent prints a written plan: files to create, files to modify, migrations to add, routes to register, admin pages to add. **No writes** until the user types "go" / "ok" / explicit confirmation.

### Phase 4 — Implement

Execute the confirmed plan. One commit-sized chunk at a time. After each chunk, report path + 1-line description.

### Phase 5 — Verify

- Lint / typecheck pass.
- Build pass.
- `curl -I` the homepage and one inner route; show response headers.
- Fetch HTML, grep for required tags, report counts.
- **HTTP status verification** (Surface 2 policy):
  - `curl -sI ` → expect `200`.
  - `curl -sI ` → expect `200`.
  - `curl -sI '/__definitely-does-not-exist-'` → expect `404`, NOT `200`. Print `HTTP/1.1 200` here as a **FAIL** with the soft-404 explanation.
  - `curl -sI '/sitemap.xml'` → expect `200` and `Content-Type: application/xml`.
  - `curl -sI '/robots.txt'` → expect `200` and `Content-Type: text/plain`.
  - `curl -sIL ''` → expect first hop `301` to HTTPS, second hop `200`.
  - `curl -sIL '/About'` (mixed case) → expect `301` to `/about`, then `200`.
  - `curl -sIL '/about/'` (trailing slash variant) → expect `301` to the canonical form, then `200`.
  - For any route the user marked as "retired" → expect `410` (or `301` to its replacement).
  - For any auth-walled route hit without credentials → expect `401` or `302` to login, never `200` with a login form rendered to bots.
- For sitemaps and `robots.txt`: hit the public URL and show the body.
- For JSON-LD: validate JSON parses; warn if Google's required fields are missing.
- For accessibility (Surface 15): run `axe-core` against the homepage and one inner route; print a count of violations grouped by impact (critical / serious / moderate / minor). Run the alt-text auditor: grep all `` tags in the built HTML and report any without `alt=` or with auto-generated placeholder alt. Run the heading auditor: per route, print `h1` count and the first skipped-level jump (if any).

---

## Surface 1 — Meta Basics

### What it covers

``, ``, `` (see policy below), ``, ``, ``, ``, ``, ``.

### `` policy (must tell the user verbatim before emitting)

> The behaviour of search engines toward the `keywords` meta tag is **not uniform** — and that matters when the site targets non-Western markets.
>
> - **Google** — does not use it for ranking. Stated publicly since 2009. Source: https://developers.google.com/search/blog/2009/09/google-does-not-use-keywords-meta-tag
> - **Bing** — does not use it for ranking. Has stated it can be a quality signal *against* the page when stuffed.
> - **DuckDuckGo / Brave Search** — inherit Bing's signal in practice; do not use it.
> - **Yandex** — historically uses it as a weak hint; current public guidance is unclear, but the field is still parsed.
> - **Baidu** — still reads it as a signal in China.
> - **Naver (KR)** — does not use it.
>
> Decision rule the skill applies:
>
> - Site's target market is Western (US/EU/TR/LATAM) only → **omit** `keywords`. Adding it is dead weight and clutters the head.
> - Site targets RU/CIS (Yandex) or CN (Baidu) → emit `keywords` with 3–6 honest, comma-separated terms per page; never repeat the same set site-wide; never stuff.
> - The skill never fabricates keywords from the slug or body. If the editor cannot provide them, omit the tag.

### Questions to ask

1. Site name? Tagline?
2. Title strategy — `Page | Site` / `Site — Page` / page only / custom template?
3. Title max length policy? (Recommend 50–60 chars but ask.)
4. Description length policy? (Recommend 140–160 but ask.)
5. Default language code (`lang="?"`)?
6. Theme color (light / dark variants)?
7. Where do per-page overrides live — at the route file, in a CMS, in the database, or all three?
8. **`` ↔ `` relationship.** The page `` should mirror or paraphrase the `` (same topic, different phrasing allowed) — they are read together as the page's identity signal by Google. Confirm both are sourced from the same DB field (cheap; recommended) or kept as separate fields (more editorial control; more drift risk). The heading hierarchy rules live in Surface 15 — do not bypass them just because Surface 1 owns the ``.
9. **`keywords` meta decision** — per the policy above, confirm: target markets, emit or omit, and if emit, source of the per-page keyword list.

### Output locations (framework-dependent)

- **Next.js App Router** → `export const metadata` / `generateMetadata` in `layout.tsx` and per `page.tsx`.
- **Next.js Pages Router** → `next/head` or `next-seo`.
- **Nuxt 3** → `useSeoMeta()` / `definePageMeta()` / `app.head` in `nuxt.config`.
- **Astro** → `` slot in layout + per-page frontmatter.
- **SvelteKit** → `` in `+layout.svelte` and `+page.svelte`.
- **Remix** → `meta` export per route.
- **Laravel + Blade** → `@stack('meta')` and a `` component.
- **Django** → `{% block meta %}` in `base.html`.
- **WordPress** → `wp_head` action hook in theme.
- **Plain HTML** → `` of each template.

---

## Surface 2 — Indexing Controls

### What it covers

``, `X-Robots-Tag` HTTP header, ``, alternate hreflang, pagination (`rel="prev/next"` is deprecated by Google but ask).

### Questions

1. Which routes are `index, follow` vs. `noindex` / `nofollow`?
2. Should auth-walled pages, drafts, search-result pages, and faceted URLs be `noindex`?
3. Canonical strategy — self-canonical on every page, or canonical to a parent for paginated/faceted?
4. Are there cross-domain canonicals (mirror sites, syndicated content)?
5. Strip query strings from canonical? Which ones (`utm_*`, `ref`, `fbclid`)?
6. hreflang pairs — confirm the locale list and the URL pattern (`/en/`, `?lang=en`, `en.example.com`).
7. **HTTP status policy** — confirm the response codes the framework will emit. See "HTTP status code policy" below; non-negotiable rules apply.

### HTTP status code policy (mandatory; verified in Phase 5)

Wrong status codes are an SEO-critical defect. Google indexes by status; a `200 OK` page that visually says "not found" (a **soft 404**) gets indexed as a valid page and wastes crawl budget, then later gets demoted with a vague "Soft 404" Search Console warning. The skill enforces real status codes at the framework layer.

Required behavior per route class:

| Route class                                                        | Status   | Headers / extras                                                              |
|--------------------------------------------------------------------|----------|-------------------------------------------------------------------------------|
| Existing content                                                   | `200`    | `Cache-Control` set; `Content-Type` correct.                                  |
| Unknown URL / missing resource                                     | `404`    | Real 404, never a 200 with a "not found" UI. Page should still render the branded 404 UI. |
| Permanently removed (gone for good, no replacement)                | `410`    | Use 410 instead of 404 when the URL is intentionally retired and you want Google to drop it faster. |
| Permanent move to new URL                                          | `301`    | `Location:` header; one hop only; never chain.                                |
| Temporary move / A-B test / region routing                          | `302` / `307` | 307 preserves method; 302 may downgrade `POST` to `GET`.                      |
| Permanent move that must preserve method (POST → POST)             | `308`    | Strict permanent redirect.                                                    |
| Auth-walled page hit unauthenticated                                | `401`    | Not `200` with a login form rendered as the page body for bots.               |
| Forbidden (logged in but not allowed)                              | `403`    | Same rule: real status.                                                       |
| Rate-limited                                                       | `429`    | `Retry-After:` header.                                                        |
| Scheduled maintenance / planned downtime                           | `503`    | `Retry-After:` header. Never use `200` with a maintenance page — Googlebot will index the maintenance message. |
| Server error (uncaught)                                            | `5xx`    | Default framework behavior; verify it's actually 500-class, not 200.          |
| Empty-state inside an existing valid route (e.g. empty search)     | `200`    | This is NOT a 404 — the route exists, the result set is empty. Render a clear empty-state UI; `noindex` is acceptable.|

Anti-patterns the skill refuses:

- Returning `200 OK` from a custom `not-found.tsx` / `404.vue` / `404.blade.php` / custom 404 view when the framework would have emitted 404. Confirm the status is preserved by the framework wrapper (Next App Router `notFound()`, Nuxt `throw createErro

…

## Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

- **Author:** [aycanozarpaci](https://github.com/aycanozarpaci)
- **Source:** [aycanozarpaci/seo-pro-max-skill](https://github.com/aycanozarpaci/seo-pro-max-skill)
- **License:** MIT

Install and usage instructions live in the source repository linked above.

## Pricing

- **Free** — Free

## Security capabilities

Automated source analysis of v0.1.0 — what this tool can access:

- **Network access:** yes
- **Filesystem access:** no
- **Shell / process execution:** no
- **Environment & secrets:** yes
- **Dynamic code execution:** no

*"Yes" means the capability is present in the source — more access means more to trust, not that it is unsafe.*


## Versions

- **0.1.0** — security scan: passed — Imported from the upstream source.

## Links

- Listing page: https://agentstack.voostack.com/l/skill-aycanozarpaci-seo-pro-max-skill-seo-pro-max-skill
- Seller: https://agentstack.voostack.com/s/aycanozarpaci
- Browse the marketplace: https://agentstack.voostack.com/browse

---
Listed on AgentStack — the marketplace for AI agent skills and MCP servers. Every listing is security-reviewed. Creators keep 70%.
