How to Build an SEO Content Brief with Codex (Full SKILL.md Included)

Key takeaways

What you get from this article A skill that produces a research backed content brief from any keyword or URL. You type a keyword, Codex pulls the top ranking competitors, scores them, finds the gaps,...

What you get from this article

A skill that produces a research-backed content brief from any keyword or URL. You type a keyword, Codex pulls the top-ranking competitors, scores them, finds the gaps, and hands you a brief with a winning outline, per-section word counts, meta tags, and exact information-gain requirements. A writer can then produce a page that has a shot at beating the top results, rather than writing the same article as everyone else.

Who this is for: content leads, writers who do their own SEO, and anyone who commissions pages and is tired of getting back something that looks like the ranking pages but isn't better than them. Also useful when you have an existing page that's stuck: the same skill works in improve mode, where it separates what to keep from what to add.

Time: 10 minutes to install and verify, then 20-40 minutes per brief. Or 3 minutes once you know the workflow.

Definition of done: you hand the skill a keyword and get back a ready-to-send brief with a competitor score table, a section-by-section outline with word counts, recommended title and description, and an explicit statement of what this page adds that no current ranking page does.

Why the brief is the highest-leverage hour in SEO

Content quality gets all the attention, but the decision that shapes quality happens before a single sentence: the outline. Most pages fail to rank because they answer a question the writer invented, not the question the SERP is actually rewarding. A brief fixes that by making the competitive reality visible before writing starts.

That's the reason this skill exists. The original version of this workflow came from the same open-source claude-seo project this series is adapting to Codex, and its brief generator is one of the most-copied parts: a short process that forces you to look at the five top results, drop the ones that hold rankings only because of domain authority, and then write against the gaps that remain.

Three rules do most of the work, and they're worth memorizing before you even install anything:

  • The Website Relevance Rule: every section in the outline must be something your site can credibly deliver. If a competitor covers a topic you don't have the business to cover, it doesn't go in.
  • The Site Structure Coverage Rule: when you're briefing a hub or category page, every relevant sub-page that already exists on the site shows up in the outline as its own linked section. A hub page that doesn't link its own children is a missed job.
  • The Information Gain Rule: the brief must name something the page adds that no current ranking page provides, and it has to be specific. "More detail" doesn't count. First-party data, real case studies, or first-hand experience do.

One more important thing about the output: in the original project, the brief generator names its methodology and the tools it uses internally. The Codex version keeps that rule and extends it. You never see "we used the such-and-such framework" in a produced brief. The brief reads like a smart editor explaining the opportunity, not a research report.

The workflow, step by step

1. Pick the mode

Two modes, one entry point:

  • Improve mode, when you give it an existing URL. The skill fetches the page, lists what's strong (keep it), and what's missing or thin (add or strengthen). When targeted improvements will win, it says so instead of recommending a rewrite.
  • New page mode, when you give it a keyword with no page. It looks at the site's homepage and sitemap for context, then builds the brief from scratch against the competitive gaps.

2. Collect the context

bash
python3 ~/.codex/skills/codex-seo-content-brief/scripts/brief_context.py <site-url-or-sitemap> <competitor-url> [more competitors]

The script takes the site URL or sitemap plus one or more competitor URLs. It returns, per page: the title, meta description, word count, the H2 and H3 headings, canonical, link count, and the opening 220 characters of text. That's enough to score depth, formatting, and SEO without fetching anything by hand. XML sitemaps get a special mode: pass one and it lists the URLs and their count.

The trick here is that the SERP isn't in the script. You paste the top results from a search, or you have Codex run a search first. That keeps the skill free of paid APIs and honest about where the data comes from.

3. Thin the SERP

Ranking pages aren't automatically competitors. Drop Wikipedia, Reddit, Pinterest, and the other platforms that win on domain authority alone; drop job boards, directories, news aggregators, and SEO tool pages; drop YouTube and government sites when they aren't useful mirrors of your market. The pages that remain, usually 3-5, are the ones whose blueprint you should study.

Score each of them 1-10 on four dimensions:

  • Depth: does it fully answer the query?
  • Formatting: is it scannable and structured well?
  • SEO: do the titles, headings, and schema do their job?
  • UX: is it fast and legible?

Then name the gaps, in three classes: topic gaps (subtopics nobody covers), depth gaps (covered but shallow), and quality gaps (outdated, no expert voice, poor formatting). Rank each by Impact x Competitive Advantage / Effort. Highest score goes first in the brief. That formula is the same one you'd use with a full SEO suite; here you do it in a table.

4. Classify the intent before choosing the shape

Intent

Signal

Format the SERP rewards

Informational

how to, what is, guide

guide, definition, listicle, FAQ

Commercial

best X, vs, review

comparison, listicle, review

Transactional

buy, book, enquire

landing page, product page, local pack

Navigational

brand + site

the site's own page

The format the SERP rewards wins. If the top results are comparison tables and your brief proposes an essay, the outline is wrong no matter how good the writing is.

5. Apply the page-type template

The skill ships templates for nine page types: service page, blog post, case study, category or hub page, landing page, FAQ page, location page, about page, and homepage. Each is a section table with purpose and format columns, plus the schema note and keyword placement rule. A service page gets a definition box and a pricing table; a case study leads with the outcome; a hub page lists every sub-page as a linked section.

The templates are rules of thumb, not straitjackets: the brief adapts each one to the competitor gaps and the intent you classified in step 4.

6. Write the brief to the contract

The output has a fixed shape, because writers work better with a shape:

  • Search intent (one short block)
  • Competitor analysis table (URL, H2 sections, est. words, score, main gap)
  • Content gaps and opportunities
  • Winning outline: H1, URL slug, target word count, full H2/H3 outline with per-section word counts, format notes, Featured-Snippet targets, and per-section keyword guidance (which keyword goes in the heading, whether the body takes the primary keyword or a variation)
  • Recommended meta tags (title 50-60 chars, description 130-150, no quotes, active voice)
  • Unique angle and information gain: a specific paragraph
  • E-E-A-T requirements: the exact trust signals the piece needs
  • Internal linking opportunities: 3-5, with anchor text and real targets from the sitemap

There's also an outline-only mode: ask for "just an outline" and it strips the competitor table, the gaps, the information gain, and the E-E-A-T sections, leaving the intent line, H1, slug, word count, and the outline with a writing note per section.

The full SKILL.md

Copy this exact file to ~/.codex/skills/codex-seo-content-brief/SKILL.md.

md
---
name: codex-seo-content-brief
description: Use when the user asks for a content brief, blog brief, service page brief, content outline, keyword brief, "brief for", writing brief, or content plan for a specific keyword or URL - produce a research-backed outline with competitor scoring, per-section word counts, keyword placement rules, and a page-type template. Triggers on "write a brief", "outline for", "what should this page cover", "improve this page".
---
# SEO Content Brief Generator

Research-backed briefs that give a writer a page capable of outranking the
current top results: competitor analysis with gap scoring, per-section word
counts, keyword placement rules, and a page-type template. You judge; the
scanner collects the evidence.

## Run

```bash
python3 ~/.codex/skills/codex-seo-content-brief/scripts/brief_context.py <site-url> <competitor-url-1> [competitor-url-2 ...] [--json]
```

`<site-url>` is the target site's homepage or sitemap (business context only).
Competitor URLs are the current top pages for the keyword; paste them from a
search, or ask the agent to run a search first. The scanner returns, per page:
title, words, H2 sections, meta description, and the first ~200 words.

## Step 1: determine the mode

- **Improve mode** (existing page URL): fetch it, list what is strong (keep),
  what is missing/thin/outdated (add or strengthen). Do not recommend a full
  rewrite when targeted improvements will win.
- **New page mode** (keyword or topic, no page yet): build from scratch,
  focused on the gaps the new page can fill.

## Step 2: analyze the SERP

Filter the top 5 page list down to real competitors. Drop Wikipedia, Reddit,
Pinterest, Amazon, YouTube, government sites, SEO tool pages, job boards,
directories, news aggregators, and social platforms - they win on domain
authority, not because their blueprint is good.

Score each real competitor 1-10 on Depth, Formatting, SEO, and UX. Then find
three gap types:

- **Topic gaps**: subtopics competitors miss entirely
- **Depth gaps**: topics covered but shallow
- **Quality gaps**: outdated info, no expert perspective, poor formatting

Priority for each gap: `Impact x Competitive Advantage / Effort`.

## Step 3: classify intent and SERP format

| Intent | Signal | Format Google rewards |
|---|---|---|
| Informational | learning keywords (how to, what is, guide) | guide, definition, listicle, FAQ |
| Commercial | researching before buying (best X, vs, review) | comparison, listicle, review |
| Transactional | ready to act (buy, book, enquire, sign up) | product/landing page, local pack |
| Navigational | specific site or page | site page |

State which format the SERP actually rewards: long-form guide, listicle,
comparison table, landing page, FAQ, video, or local pack. The brief's shape
follows the SERP, not the writer's preference.

## Step 4: apply the page-type template

Pick the template matching the page type, then adapt per competitor gaps and
intent. Not every section applies to every page.

### Service page

| Section | Purpose | Format |
|---|---|---|
| What is [service] | Define clearly | Definition box, 80-120 words |
| Who needs it | Qualify | Bullet scenarios |
| How it works | Reduce friction | Numbered steps |
| Costs/pricing | Answer the #1 question | Table or range if variable |
| Outcomes/results | Prove value | Stats, case snippets, before/after |
| Why choose [brand] | Differentiate | 3-5 bullets with specifics |
| FAQ | PAA traffic | 5-8 questions, 40-60 words each, FS target |
| CTA | Convert | Clear action, reduced risk |

Schema: Service + LocalBusiness when location-specific. Never add FAQPage for
Google visibility (rich results retired 2026-05-07; no AI benefit confirmed).

### Blog post

| Section | Purpose | Format |
|---|---|---|
| Direct answer | Win the snippet | 40-60 words, FS target |
| Background | Context | 1-2 paragraphs |
| 3-5 H2 subtopics | Cover PAA/gaps | Mix paragraphs, lists, tables |
| Common mistakes | Unique value | Numbered list with explanations |
| FAQ | Long-tail | 5 questions from gap analysis |
| CTA | Convert | Contextual link, not hard sell |

Schema: Article. Keyword: H1, first 100 words, URL slug, meta title, one image
alt.

### Case study

Outcome summary first (bold stat), client situation, the challenge, approach,
the result with specific figures, 3-5 takeaways, related-services CTA. The
outcome or matter type is the keyword.

### Category / hub page

Overview of scope, then one H2 or H3 per relevant sub-page with description
and internal link, who we help, process, FAQ, CTA. **Every sub-page that
exists on the site must appear; invent none, leave none out.**

### Landing page

Hero (offer + CTA), problem statement, solution benefits (3-5 bullets), social
proof, 3-step how-it-works, objection handling, final CTA. Schema: WebPage.

### FAQ page

8-15 questions grouped by subtopic, 40-60 words each, FS target on every
answer. Schema: WebPage only. QAPage only when users can submit answers to a
single genuine question.

### Location page

What we do in [city], hyper-local areas/landmarks served, service areas, why
local matters, local team bios, local reviews, 5 location-specific questions,
local phone/address/booking CTA. Schema: Service + LocalBusiness.

### About page

Who we are, story/founding, team bios with credentials, values, awards with
dates, media mentions, contact CTA. Schema: Organization + Person per member.

### Homepage

Hero value prop + CTA, services overview (card grid to service pages), 3-5
differentiators, social proof, service area, GEO FAQ (4-6 broad business
questions), repeated primary action. Schema: Organization + WebSite + Service.

## Critical rules

### Website Relevance Rule

Every suggested heading, subtopic, keyword, and FAQ must be something the
target site can credibly cover from its actual services or products. If the
site doesn't offer it, remove it, even if competitors cover it.

### Site Structure Coverage Rule

For hubs, overviews, categories, and "types of" pages: the outline must
reference every relevant existing sub-page in the sitemap (each as its own
section with an internal link). For non-hub pages, only suggest relevant
links; never force every category in.

### Output language rules

Never name researcher, framework, or tool names in the output (no "this
method", no SaaS tool names, no named formulas). They are internal thinking
tools. Write for a business owner or content writer, not an SEO academic.

## Keyword density and placement

No fixed density targets. Density checks are optional readability/stuffing
heuristics only; first 1-2 mentions establish context and diminish after that.

**Primary keyword must appear in:** title tag near the front, H1 near the
front, URL slug, meta description, first paragraph (first 100 words), and at
least one image alt text.

**It does NOT need to appear in:** every H2/H3, or every paragraph.

**Secondary keywords:** 5-8 closely related terms in the body; 10-15 broader
semantic terms. Synonyms are fine and do not count toward density.

**Per-section guidance** in the outline: which keyword goes in the heading,
and whether the body uses the primary keyword or a variation. Spread the
primary keyword evenly; don't front-load or cluster it.

## Meta tag rules

- **Title**: 50-60 chars, primary keyword first, brand last with the site's
  existing separator (pipe or dash), lead with outcomes/numbers where possible.
- **Meta description**: 130-150 chars, active voice, expand the title with
  specifics, end with a call to action. No brand at the end (it's in the
  title), no quotes (Google truncates at quotes).

## Information gain (non-negotiable)

Every brief names exactly what new value this piece adds that no current
ranking page provides. It must be specific: proprietary data, original
research, case studies with real outcomes, expert quotes, first-hand
experience, or an original synthesis. "More detail" or "better formatting" is
not information gain.

## E-E-A-T requirements

List the exact trust signals the piece needs: author credentials and a bio
relevant to the topic, expert quotes or citations, dated studies or stats,
last-updated date. Critical for YMYL: health, finance, legal, safety.

## Internal linking

3-5 specific link opportunities with anchor text and target URL, from the real
site structure. Say whether the page is the hub (links out to cluster pages)
or a spoke (links to the pillar).

## Output contract

Use this exact structure:

```markdown
## Content Brief: [Primary Keyword]

### Search Intent
[intent type, SERP format rewarded, target audience and knowledge level]

### Competitor Analysis
| # | URL | Key H2 Sections | Est. Words | Score | Main Gap |
|---|-----|-----------------|------------|-------|----------|

### Content Gaps and Opportunities
[bullets: topic gaps, depth gaps, quality gaps with specifics]

### Winning Outline
**H1:**, **URL Slug:**, **Target Word Count:** (~[X], competitor average)
[full H2/H3 outline with word count per section, format notes,
FS-target marks, per-section keyword guidance]

### Recommended Meta Tags
[title <=60, description <=150]

### Unique Angle and Information Gain
[specific paragraph]

### E-E-A-T Requirements
[exact trust signals]

### Internal Linking Opportunities
[3-5 with anchor text and URL]
```

### Outline-only mode

For "just an outline": skip the competitor table, gaps, information gain, and
E-E-A-T sections. Output Search Intent (one line), H1, slug, target word
count, and the full H2/H3 outline with word counts, format notes, FS targets,
keyword guidance, and a 1-2 sentence writing note per section.

## Errors

| Scenario | Action |
|---|---|
| Target URL unreachable | Report it. Never guess page content |
| No competitors left after filtering | Broaden to partial matches; note the thin landscape in the brief |
| Sitemap not found | Proceed without site structure; say internal-link suggestions are incomplete |
| Page type not specified | Auto-detect from intent and SERP format; state the detected type |
| Target word count not specified | Use competitor average; note it |

The context collector script

Copy this exact file to ~/.codex/skills/codex-seo-content-brief/scripts/brief_context.py.

python
#!/usr/bin/env python3
"""Brief context collector: per-page structure for competitor analysis.
Standard library only."""

import html as htmlmod
import json
import re
import sys
import urllib.error
import urllib.parse
import urllib.request

TIMEOUT = 15
UA = ("Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) "
      "AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0 Safari/537.36 "
      "codex-seo-content-brief/1.0")


def fetch(url):
    req = urllib.request.Request(url, headers={"User-Agent": UA})
    try:
        resp = urllib.request.urlopen(req, timeout=TIMEOUT)
        return resp.status, resp.geturl(), resp.read().decode("utf-8", "replace")
    except urllib.error.HTTPError as e:
        return e.code, url, ""
    except Exception as e:
        return "ERR:" + str(e), url, ""


def extract(raw, url):
    page = {"url": url}
    html = raw or ""
    if not html:
        page["error"] = "no HTML body"
        return page

    def m(p):
        return re.findall(p, html, re.I)

    title = (m(r"<title[^>]*>([^<]+)</title>") or [""])[0]
    desc = (m(r'<meta[^>]+name=["\']description["\'][^>]+content=["\']([^"\']+)["\']') or [""])[0]
    h2 = [htmlmod.unescape(re.sub(r"<[^>]+>", "", h)).strip() for h in m(r"<h2[^>]*>(.*?)</h2>")]
    h3 = [htmlmod.unescape(re.sub(r"<[^>]+>", "", h)).strip() for h in m(r"<h3[^>]*>(.*?)</h3>")]

    text = re.sub(r"(?is)<script.*?</script>|<style.*?</style>|<pre.*?</pre>|<code.*?</code>|<[^>]+>", " ", html)
    text = htmlmod.unescape(re.sub(r"\s+", " ", text)).strip()

    # sitemap support: /sitemap.xml gives <loc> list
    if "sitemap" in url.lower():
        locs = re.findall(r"<loc>([^<]+)</loc>", html)
        page["sitemap_urls"] = locs[:200]
        page["sitemap_count"] = len(locs)
        return page

    canon = m(r'<link[^>]+rel=["\']canonical["\'][^>]+href=["\']([^"\']+)["\']')
    internal = len(re.findall(r'<a\s[^>]*href=["\']/?[^"\']+["\']', html))
    page.update({
        "title": htmlmod.unescape(title)[:120],
        "meta_description": htmlmod.unescape(desc)[:200],
        "words": len(text.split()),
        "h2": h2[:15],
        "h3": h3[:15],
        "canonical": canon[0] if canon else None,
        "links": internal,
        "excerpt": text[:220],
    })
    return page


def main():
    args = sys.argv[1:]
    want_json = "--json" in args
    args = [a for a in args if a != "--json"]
    if len(args) < 1:
        print("usage: python3 brief_context.py <site-or-sitemap-url> [competitor-url ...] [--json]")
        sys.exit(1)
    out = []
    for u in args:
        if not u.startswith("http"):
            u = "https://" + u
        status, final, raw = fetch(u)
        page = extract(raw, final or u)
        page["status"] = status
        out.append(page)
    if want_json:
        print(json.dumps(out, indent=2))
    else:
        for p in out:
            u = p["url"]
            if "sitemap" in u.lower() and "sitemap_count" in p:
                print("sitemap %s: %d URLs listed" % (u, p["sitemap_count"]))
                continue
            print("%s [%s] %d words" % (u, p.get("status"), p.get("words", 0)))
            print("  title: %s" % (p.get("title") or "-")[:110])
            print("  desc : %s" % (p.get("meta_description") or "-")[:110])
            print("  h2   : %s" % (" | ".join(p.get("h2", [])[:6]) or "-"))
            print("  expr : %s" % (p.get("excerpt") or "-")[:150])


if __name__ == "__main__":
    main()

Install it in three commands

bash
mkdir -p ~/.codex/skills/codex-seo-content-brief/scripts
# save the two files above at:
#   ~/.codex/skills/codex-seo-content-brief/SKILL.md
#   ~/.codex/skills/codex-seo-content-brief/scripts/brief_context.py
python3 ~/.codex/skills/codex-seo-content-brief/scripts/brief_context.py https://example.com/sitemap.xml https://example.com

The last command fetches the sitemap and one page. Expect a line per URL: the sitemap prints its URL count; the page prints title, description, word count, H2s, and an excerpt. Both lines printing means it's wired up.

A real run, and how to read it

Here's real output from a run on the site hosting this article series, plus its sitemap:

Code
sitemap https://auspia.ai/sitemap.xml: 966 URLs listed
https://auspia.ai/blog/codex-seo-technical-audit [200] 2409 words
  title: How to Audit Technical SEO with Codex (Full SKILL.md Include
  desc : How to perform a technical SEO audit with Codex: the underlying how-...
  h2   : What you get from this article | Why technical SEO still matters in ...

What this tells you as the agent: the site publishes long-form blog posts (900+ URLs, another 60-ish articles), and the page at that URL is a 2,400-word post with clear H2 sections. That's the context for a brief targeting a keyword like "technical seo audit." The competitor analysis would fetch the current top results and score them.

The brief that comes out of it is short enough to skim and structured enough to hand to a writer:

Code
## Content Brief: technical seo audit

### Search Intent
Informational. SERP rewards long-form guides with checklists and CWV tables.
Audience: mid-career SEOs and site owners; they know the terms, they want the sequence.

### Competitor Analysis
| # | URL | Key H2 Sections | Est. Words | Score | Main Gap |
| 1 | example.com/blog/technical-audit | x, y, z | 2,800 | 32/40 | No AI-crawler filtering, no automation |
| 2 | seo-tools.com/guides | ... | 3,400 | 30/40 | Generic advice, no real scan output |

### Winning Outline
**H1:** Technical SEO Audit: How to Run One in 9 Steps
**URL Slug:** /technical-seo-audit-checklist
**Target Word Count:** ~2,800 (competitor average ~2,800)

## Step 1: Crawlability (450 words, checklist format, FS target)
Keyword: primary in H1/URL only. Body: secondary terms.
...

That example is abbreviated, and the scores in it are the agent's judgment of the real pages fetched. What's fixed is the structure: the intent block, the scored table, the gaps, then the outline with a target word count and per-section guidance.

Troubleshooting

Scenario

Action

python3: command not found

Install Python 3, or use python3.11

No competitor data

Paste the top results yourself, or ask Codex to run a search and paste them back

Sitemap missing (404)

Proceed with the homepage only; note that internal-link suggestions may be incomplete

A competitor page is 403/behind auth

Drop it from scoring; note why in the brief

Page type unclear

The skill auto-detects from intent + SERP format and states the detected type

The brief feels too generic

It needs the info-gain section to be specific. Ask for first-party data or a case study as the angle before writing

Codex doesn't trigger

/skills to check the list, or invoke $codex-seo-content-brief explicitly

Paste this article into Codex

Paste the whole article up to this line into a new Codex conversation and say:

text
Read the two code fences marked "The full SKILL.md" and "The context collector script" above.
1. Create ~/.codex/skills/codex-seo-content-brief/SKILL.md
2. Create ~/.codex/skills/codex-seo-content-brief/scripts/brief_context.py
3. Run: python3 ~/.codex/skills/codex-seo-content-brief/scripts/brief_context.py https://example.com/sitemap.xml https://example.com
4. If that works, take my keyword or existing URL and produce a full brief with the competitor table, winning outline, and information-gain section.
Do not install or modify any other files.

That's the whole install. The rest of the session is just you asking for briefs.

FAQ

How do I get the top-5 results without a paid API? Search normally and paste the URLs, or ask Codex to search and list them. The skill is free and honest about the source; the moment you paste URLs, the workflow needs no keys, no dashboards, and no export files.

Is word count an instruction or a suggestion? A baseline. The brief uses the competitor average, and the skill treats it as a coverage floor, not a target. A 1,200-word page that actually answers the query beats a 3,000-word one that doesn't.

What counts as information gain? Proprietary data, original research, case studies with real outcomes, expert quotes, first-hand experience, or an original synthesis. Not "comprehensive," not "better formatting," not "more detailed."

Should I brief existing pages? Yes, that's exactly what improve mode is for, and often the cheaper play. The skill fetches the page, separates what to keep from what to add, and only recommends a rewrite when that's the honest answer.

Will the brief mention the methodology it used? No. That's deliberate. The output reads like plain, professional advice to a writer. If you see tool names or framework names in a produced brief, something went wrong.

Next in the series (post 05 of 20): [How to Set Up Codex for Schema Markup (Full SKILL.md Included)](https://auspia.ai/blog/codex-seo-schema) - json-ld detection, validation, generation.

Previous in the series: How to Set Up Codex E-E-A-T Content Analysis (Full SKILL.md Included). The full series roadmap lists all 20 posts.

Author: Camille Rhodes, Architect of 300+ AI Content Workflows at Auspia. Camille builds the pipelines that turn research into publishing-ready briefs and drafts, and audits how teams actually use them.

Based on the open-source claude-seo project by AgriciDaniel (MIT license, GitHub). This series adapts it for the Codex runtime: rewritten methodology, a Codex-native SKILL.md, and new evidence collectors written from scratch for this series.

Explore this topic

Keep following the same growth thread