brighter websites logoBrighter Websites Logo White

SEO Module

Hub for the SCOS SEO module — per-page meta, TLDR, breadcrumbs, robots, and SEOPress-compatible output.

SEO, Site Essentials

Table Of Contents

Who is this for

Agency devs / future maintainers
Overview

Summary

The SEO Module handles archive and per-page meta titles, descriptions, TLDR summaries, breadcrumb labels, canonical URLs, and robots directives. Along with Advanced SEO, Image SEO and Redirections. This hub orients you to child documentation; open specific guides from the module navigation when editing content.

Module hub — body content is still minimal. Use child SEO docs or the post editor SCOS metabox for field-level guidance until this overview is expanded.

SEO Post meta keys

get_post_meta / update_post_meta / delete_post_meta

Nine distinct keys. Values are per post.

KeyType / notesReadWrite
scos_seo_breadcrumb_titlestringMeta_Box.phpbreadcrumb-shortcode.phpscos-migration.phpMeta_Box.php (update_or_delete), scos-migration.php
scos_seo_tldrstring (HTML allowed on save)Meta_Box.phpFAQ_Module.phpMeta_Box.php (update_or_delete)
scos_seo_titlestringMeta_Box.phpHead_Output.phpbrighter-og-meta.phpscos-migration.phpMeta_Box.phpscos-migration.php
scos_seo_descriptionstringMeta_Box.phpHead_Output.phpbrighter-og-meta.phpscos-migration.phpMeta_Box.phpscos-migration.php
scos_seo_canonicalURL stringMeta_Box.phpHead_Output.phpscos-migration.phpMeta_Box.phpscos-migration.php
scos_seo_robotsarray (noindexnofollownoimageindexnosnippet)Meta_Box.phpHead_Output.phpscos-migration.phpMeta_Box.php (always update_post_meta), scos-migration.php
scos_seo_sitemap_excludearray (xmlhtmlnews)Meta_Box.phpviews/meta-box.phpMeta_Box.php (update_post_meta)
scos_seo_freeze_og_date'1' / '0' — freeze post_modified for OG/schema freshnessMeta_Box.phpviews/meta-box.phpMeta_Box.php (update_post_meta on every save)

Registered (REST/auth only, not extra keys): Meta_Fields.php registers all except scos_seo_freeze_og_date (used in code but not in register_post_meta).

Not post-meta storage (same prefix, different purpose):

  • scos_seo_meta — metabox DOM id (Meta_Box.php)
  • scos_seo_schema — Schema metabox id (SeoSchema/Meta_Box.php); actual schema meta is scos_schema_custom (see schema module)

Gap: [tldr] shortcode (brighter-core/includes/tldr-shortcode.php) still reads bw_tldr, not scos_seo_tldrMeta_Box dual-writes bw_tldr on save so the shortcode keeps working.

SEO wp_options keys

(get_option / update_option / delete_option)

options are created on first update_option, No add_option( 'scos_seo_*' )

Fixed option keys

KeyPurposeReadWrite
scos_seo_freeze_modified_dateGlobal “freeze modified date” ('1' or empty)Meta_Box.phpviews/archive-meta.phpArchive_Settings.php (update_option on Meta Tags save)
scos_seo_author_rewrite_verMD5 of custom author base; triggers one flush_rewrite_rulesArchive_Settings.phpupdate_option / delete_option in Archive_Settings.php

Dynamic option keys (pattern)

PatternPurposeReadWrite
scos_seo_archive_{slug}Serialized array of archive SEO settingsArchive_Settings::get() → get_option( OPTION_PREFIX . sanitize_key( $slug ) )Archive_Settings::save() → update_option(...)

{slug} values come from Archive_Settings::get_archives():

  • post — blog archive
  • Every public, non–built-in CPT (e.g. projectservicefaq, … — site-specific)
  • authordatesearch404 — special archives

Each option stores a merged array (fields vary by slug).

Common keys inside the array: titledescriptionbreadcrumb_titlerobotstldrsitemap_excludeog_image_idpagination_noindexcanonical_pagedrel_prevnext; author/date/search also disabledredirect_url; author also author_slug.

Not an option: scos_seo_title_sep is a apply_filters() hook name in Archive_Settings.php, not get_option.

Notes

  • Admin query param ?scos_seo=disabled in Admin_UI.php 

Claude MCP/WP-CLI grep cheat sheet, post-meta set is:

scos_seo_breadcrumb_titlescos_seo_tldrscos_seo_titlescos_seo_descriptionscos_seo_canonicalscos_seo_robotsscos_seo_sitemap_excludescos_seo_freeze_og_date.

Archive SEO Meta Update request.

MCP Archive SEO Meta Update request.

MCP task: fill SEO archive options (Meta Tags tab)

Admin UI (reference only): wp-admin/admin.php?page=site-essentials-seo&tab=meta
Storage: one option per archive slug: scos_seo_archive_{slug}
Same tab also sets: scos_seo_freeze_modified_date (global, not per archive)


1. Discover slugs on this site (run first)

Do not guess CPT/taxonomy names. Build the slug list the same way Archive_Settings::handle_save() does:

# Post type archives: always includes “post” + public non-builtin CPTs

wp post-type list –_builtin=0 –public=1 –field=name

# Special archives (fixed)

# author | date | search | 404

# Taxonomy archives: category, post_tag, then other public taxonomies

wp taxonomy list –public=1 –field=name

Option key rule: scos_seo_archive_ + sanitize_key( $slug )
Examples: scos_seo_archive_postscos_seo_archive_projectscos_seo_archive_categoryscos_seo_archive_scos_content_cluster (if that taxonomy exists).


2. Read before write (merge, do not blind overwrite)

wp option get scos_seo_archive_post –format=json

If missing, treat as {}. When updating one slug, only change that option key; other scos_seo_archive_* keys are independent (unlike the admin form, which re-saves every slug on one submit).


3. Write with wp option update (preferred)

Use JSON and WordPress will store the PHP array correctly:

wp option update scos_seo_archive_post ‘{

“title”: “%title% %sep% %sitename%”,

“description”: “%description%”,

“breadcrumb_title”: “Blog”,

“tldr”: “”,

“robots”: [],

“sitemap_exclude”: [],

“og_image_id”: 0,

“pagination_noindex”: false,

“canonical_paged”: false,

“rel_prevnext”: false

}’ –format=json

Global on same tab (optional):

wp option update scos_seo_freeze_modified_date ‘1’ # enable

# or

wp option update scos_seo_freeze_modified_date ” # disable


4. Payload schema by slug type

A. Post type archives — slug post or any public CPT name

FieldTypeNotes
titlestringTemplate; tokens below
descriptionstringTemplate; multiline OK
breadcrumb_titlestringPlain text
tldrstringPlain text
robotsstring[]Subset: noindexnofollownoimageindexnosnippet
sitemap_excludestring[]Subset: xmlhtmlnews
og_image_idintAttachment ID
pagination_noindexbool
canonical_pagedbool
rel_prevnextbool

B. Taxonomy archives — slug = taxonomy name (categorypost_tag, custom)

Same fields as A (taxonomies are not “special” slugs).

Extra tokens useful here: %term%%taxonomy%%description%.

C. Special archives — authordatesearch404

Base fields only: titledescriptionbreadcrumb_titlerobots
(No tldrsitemap_excludeog_image_id, pagination flags — admin save strips them for special slugs except author partial extras.)

authordatesearch also:

FieldType
disabledbool
redirect_urlstring (URL)

author also:

FieldType
author_slugstring
tldrstring
og_image_idint

404: base fields only (no disable/redirect).


5. Template tokens (title / description only)

TokenUse
%title%Archive / term / author / blog title
%sitename%Site name
%sep%Separator (default ; filter scos_seo_title_sep)
%page%Page N on paginated archives
%term%Term name (taxonomy)
%taxonomy%Taxonomy plural label
%description%Term description (taxonomy)
%author%Author display name
%search%Search query

Example title: %title% %sep% %sitename%

Important: Do not run values through sanitize_text_field() in a way that breaks % tokens (e.g. %description% is valid). Plain strings are fine for MCP; PHP admin uses sanitize_template_field() which preserves tokens.


6. Verify after write

wp option get scos_seo_archive_post –format=json

wp eval ‘var_export( \SiteEssentials\Modules\SeoMeta\Archive_Settings::get(“post”) );’

Second line confirms merged defaults + saved values (only works if Site Essentials is loaded in CLI — otherwise option get is enough).

Frontend output is consumed by Head_Output.php via Archive_Settings::get( $slug ) — no separate meta keys.


7. What NOT to do

AvoidWhy
wp post meta update for archive SEOWrong storage layer
Guessing slugs from another siteCPT/tax list is per site
POSTing to admin-post.php?action=site_essentials_save_archive_meta without full scos_archive[all-slugs]Omitted slugs save as empty arrays for every slug in the loop
Setting scos_seo_author_rewrite_ver manuallyInternal; admin deletes it on save to flush rewrites
Legacy export keys scos_seo_breadcrumbscos_seo_isnoindexNot used by this module

8. Optional: mimic admin save (usually worse for MCP)

Only if you must use the UI pipeline:

  • URL: admin-post.php
  • action: site_essentials_save_archive_meta
  • nonce: scos_archive_meta_nonce / action scos_save_archive_meta
  • Capability: manage_options
  • Body: scos_archive[{slug}][title], etc., plus scos_global[freeze_modified_date] if needed

Requires a logged-in admin session or nonce generation. wp option update per slug is simpler and safer.


9. Copy-paste MCP system prompt (short)

Task: Fill SCOS SEO archive settings for {site}.

1. List slugs: wp post-type list –_builtin=0 –public=1 –field=name;

add “post”; add special author,date,search,404;

wp taxonomy list –public=1 –field=name.

2. For each slug in scope, read wp option get scos_seo_archive_{slug} –format=json.

3. Write with wp option update scos_seo_archive_{slug} ‘<json>’ –format=json

using the correct schema (CPT/tax vs special vs author).

4. Use template tokens in title/description where appropriate.

5. Optionally set scos_seo_freeze_modified_date to “1” or “”.

6. Verify with wp option get. Do not use post meta. Do not POST admin form unless updating all slugs at once.

Reference: site-essentials/Modules/SeoMeta/Archive_Settings.php


10. Author slug side effect

If you set author_slug on scos_seo_archive_author, the next normal admin save (or code path that calls Archive_Settings::set_author_slug()) may flush rewrite rules. After CLI-only author slug changes, tell the operator to visit Settings → Permalinks → Save once if author URLs look wrong.


If you want this wired into scos-meta-fill as a sub-skill, the stable contract is: option prefix scos_seo_archive_ + site-specific slug list + JSON schemas above — not the admin URL itself.

Page/Post SEO Meta Output

(pages/posts is slightly different – shown in bold)

Example for Pages

 <head>
   <title>Website Design Ballarat | Conversion Infrastructure</title>
   <meta name='robots' content='max-image-preview:large' />

<!-- OG Image Tags -->
<meta property="og:image" content="https://brighterwebsites.com.au/wp-content/uploads/2026/01/brighter-websites-logo-web-design-1200x630.jpg">
<meta property="og:image:type" content="image/jpeg">
<meta property="og:image:width" content="1200">
<meta property="og:image:height" content="630">
<meta property="og:image:alt" content="Brighter websites colourful graphical representation of website design on multiple devices">

<!-- Twitter Card (after image) -->
<meta name="twitter:card" content="summary_large_image">
<meta name="author" content="Vanessa Wood">

<!-- SCOS SEO Meta -->
<meta name="description" content="Custom web design for Ballarat and regional Australian businesses. Infrastructure-first — schema markup, Core Web Vitals, and AI-ready architecture included." />
<link rel="canonical" href="https://brighterwebsites.com.au/services/website-design/" />
<meta property="og:type" content="website" />
<meta property="og:url" content="https://brighterwebsites.com.au/services/website-design/" />
<meta property="og:site_name" content="Brighter Websites" />
<meta property="og:locale" content="en_AU" />
<meta property="og:title" content="Website Design Ballarat | Conversion Infrastructure" />
<meta property="og:description" content="Custom web design for Ballarat and regional Australian businesses. Infrastructure-first — schema markup, Core Web Vitals, and AI-ready architecture included." />
<meta property="og:updated_time" content="2026-04-10T12:08:21+10:00" />
<!-- /SCOS SEO Meta -->

<link rel="icon" href="https://brighterwebsites.com.au/wp-content/uploads/2026/01/bw-favico2026-150x150.png" sizes="32x32" />
<link rel="icon" href="https://brighterwebsites.com.au/wp-content/uploads/2026/01/bw-favico2026-300x300.png" sizes="192x192" />
<link rel="apple-touch-icon" href="https://brighterwebsites.com.au/wp-content/uploads/2026/01/bw-favico2026-300x300.png" />
<meta name="msapplication-TileImage" content="https://brighterwebsites.com.au/wp-content/uploads/2026/01/bw-favico2026-300x300.png" />

Example for Articles

<head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>How I Made Contact Forms Ready for AI Agents &#8211; Brighter Websites</title>
<meta name='robots' content='max-image-preview:large' />

<!-- OG Image Tags -->
<meta property="og:image" content="https://brighterwebsites.com.au/wp-content/uploads/2026/05/webmcp-tool-testing-dev-tools-1-1200x630.png">
<meta property="og:image:type" content="image/png">
<meta property="og:image:width" content="1200">
<meta property="og:image:height" content="630">

<!-- Twitter Card (after image) -->
<meta name="twitter:card" content="summary_large_image">

<meta name="author" content="Vanessa Wood">

<!-- SCOS SEO Meta -->
<link rel="canonical" href="https://brighterwebsites.com.au/blog/how-i-made-contact-forms-ready-for-ai-agents/" />

<meta property="og:type" content="article" />
<meta property="og:url" content="https://brighterwebsites.com.au/blog/how-i-made-contact-forms-ready-for-ai-agents/" />
<meta property="og:site_name" content="Brighter Websites" />
<meta property="og:locale" content="en_AU" />
<meta property="og:title" content="How I Made Contact Forms Ready for AI Agents" />
<meta property="article:published_time" content="2026-05-28T10:26:02+10:00" />
<meta property="article:modified_time" content="2026-06-04T16:23:34+10:00" />
<meta property="article:author" content="https://brighterwebsites.com.au/author/vanessa-wood/" />
<!-- /SCOS SEO Meta -->

<link rel="icon" href="https://brighterwebsites.com.au/wp-content/uploads/2026/01/bw-favico2026-150x150.png" sizes="32x32" />
<link rel="icon" href="https://brighterwebsites.com.au/wp-content/uploads/2026/01/bw-favico2026-300x300.png" sizes="192x192" />
<link rel="apple-touch-icon" href="https://brighterwebsites.com.au/wp-content/uploads/2026/01/bw-favico2026-300x300.png" />
<meta name="msapplication-TileImage" content="https://brighterwebsites.com.au/wp-content/uploads/2026/01/bw-favico2026-300x300.png" />

Archive SEO Tokens Available

Template Tokens you can use in Meta Title and Meta Description fields

%title%Archive name — CPT plural label, term name, author name, or blog page title
%sitename%Site name from Settings › General
%sep%Separator (default –, filterable via scos_seo_title_sep)
%page%“Page 2” on paginated views, blank on page 1
%term%Taxonomy term name (alias for %title% on term archives)
%taxonomy%Taxonomy plural label (e.g. “Categories”, “Tags”)
%description%Term description — the description set on the taxonomy term edit screen
%author%Author display name (author archives)
%search%Search query string (search results archives)

Example: %title% %sep% %sitename% → Posts (Blog) – Brighter Websites

Want to Contribute to SCOS?

SCOS is a Strategic Content Operating System - learn more or contact us on support@brighterwebsites.com.au.

Work with me

Hit submit and I’ll reach out by email or phone to help you get started. Your details stay private,  see the Privacy Policy.