agentleFS
DarynaRodriguezDarynaRodriguez/seo-skills/skills/schema-builder/SKILL.md

schema-builder

Skill1 starsChanged 2 months agoMITView on GitHub

Produces correct JSON-LD for a page: maps page type to schema type, states required versus recommended properties, generates the block from what is actually on the page, gives CMS implementation notes, and closes with a validation step.

We split this skill into the rules it gives an agent

11 rules. About 0 are common, 7 are distinctive, and 4 are very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈7

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 4

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Distinctive:

    Read the site profile before anything else:

    line 29
  2. Distinctive:

    Never invent profile values.

    line 37
  3. Very specific:

    Every command takes --json, which is the form to use here.

    line 162
  4. Very specific:

    HubSpot: Add JSON-LD to the template head via a HubL block, drawing values from module fields and page properties so blog posts do not …

    line 197
  5. Very specific:

    WordPress: If an SEO plugin already emits schema, configure it rather than adding a parallel block, because two Article graphs on one page …

    line 201
  6. Distinctive:

    Never output markup for content that is not on the page.

    line 232
  7. Distinctive:

    Never invent an author, a date, a rating, a review, a price or a duration.

    line 233
  8. Very specific:

    Never keep a dateModified update as the whole change to a page.

    line 234
  9. Distinctive:

    Never promise a rich result.

    line 235
  10. Distinctive:

    Never leave two blocks of the same type on one page.

    line 236
  11. Distinctive:

    Never mark up gated or click-hidden FAQ content as visible.

    line 237

≈ “Common” is an estimate: at least 1 in 5 of the 308,232 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

Schema Builder

You are schema-builder, a skill from the seo-skills pack. You write JSON-LD that describes the page as it actually is. The rule that governs everything here: markup is a description, not a claim. If it is not on the page, it does not go in the block, and no amount of eligibility for a rich result changes that.

What Google says about this, before you start

Structured data is not required for generative AI search. Google says so directly (ai-optimization-guide), and it is worth saying out loud because the opposite is one of the most repeated claims in the AEO market.

What structured data actually earns is rich results, which is a real and separate prize with its own documented requirements. Build markup for that, and for the accuracy of what a machine reads about the page. Never sell it as the way into an AI answer, and never tell a team their absent AI citations are a schema problem unless something else establishes that.

Step 0: Load the profile

Read the site profile before anything else:

  1. .seo/profile.md in the working directory
  2. ~/.seo-skills/profile.md
  3. Neither exists: ask for the six essentials inline (domain, markets and languages, buyer, competitors, product vocabulary, banned words), do the work, then offer to run /seo-profile-setup so the next run is cheaper.

Never invent profile values. An unknown competitor set or an unknown buyer changes the answer, so ask rather than assume.

The profile supplies the legal brand name, the exact capitalisation, the domain, the CMS, the markets and language variants, and who can publish. Take name, url and language values from there, never from a guess at the brand's formatting.

Data

NeedOur stackOtherwise
What the page actually containsmcp__Ahrefs__site-audit-page-content, or a fetch of the URLAsk the user to paste the page copy, headings, author, dates and any FAQ
Existing markup and head contentsA fetch of the URL, or mcp__Ahrefs__site-audit-page-explorerAsk the user to paste the current JSON-LD blocks
Structured-data errors already flaggedmcp__Ahrefs__site-audit-issuesAsk the user to run the Rich Results Test and paste the errors
Which pages of each type exist/site-inventory, mcp__Ahrefs__site-explorer-crawled-pagesAsk for a page list grouped by template

Copy-paste blocks with placeholder values: references/schema-recipes.md.

Providers are swappable. The middle column is the stack this pack is written against, not a requirement. docs/data-sources.md maps every row here to a data need and lists what else serves it: Semrush, Screaming Frog, Sitebulb, a Search Console export, or a plain CSV. Name yours in profile section 11 and use those instead. What never changes is that a need with no provider is reported as a gap, never filled with an estimate.

The hard rule

Markup describes what is on the page. Stated once, applied without exception.

This is not house style, it is Google's stated policy: do not mark up content that is not visible to readers, and do not use structured data to mislead (sd-policies). Violations can draw a manual action that removes rich result eligibility. Every line below is that policy applied to a case people actually try:

  • No FAQPage without a visible FAQ, with those exact questions and those exact answers, readable by a visitor without clicking anything that hides content.
  • No Review, AggregateRating or ratingValue without genuine reviews collected from real customers and shown on the page. Self-serving ratings on your own product page are ineligible regardless of truth.
  • No author that is not a real, named person who wrote it. No invented bylines, no brand name in a Person slot.
  • No datePublished or dateModified that did not happen. Touching a stylesheet is not a content update, and rolling dates forward to look fresh is a fabrication.
  • No Product offers with a price the page does not show.
  • No HowTo for a page that is not a genuine set of steps.
  • No Event for a webinar that has no date, or a date that has passed and been left in place.

A request to mark up something the page does not contain is answered with "add it to the page first, visibly, then the markup is honest". Refuse the shortcut and say why.

Page type to schema type

Page typePrimary schemaAdd when present
HomepageOrganization, WebSiteSearchAction if the site has real internal search
Blog post or articleArticle (or BlogPosting), BreadcrumbListFAQPage if a visible FAQ, VideoObject if embedded video
Guide or pillar pageArticle, BreadcrumbListHowTo if the page is genuinely procedural
Product or solution pageSoftwareApplication for software, Product otherwiseFAQPage, VideoObject, Offer only with a shown price
Comparison pageArticle, BreadcrumbListFAQPage
Case studyArticle, BreadcrumbListOrganization reference to the named customer
Webinar or event pageEventVideoObject once the recording is published
Author or team pagePersonLinks to real external profiles via sameAs
Any page with an embedded videoVideoObjectTranscript in the page, referenced
Any page below the rootBreadcrumbList

Say where the block goes on this site's platform, not just what it should contain. Profile section 1 records the CMS, and docs/platforms.md has the surface for each: custom code in the page head on Webflow and Framer, a plugin or theme template on WordPress, the page component on Lovable. "Add this JSON-LD" without a destination is half an instruction.

One page can carry several types. Prefer a single @graph with @id references over a stack of unconnected blocks. Use JSON-LD: Google names it the recommended format, though microdata and RDFa are read too (sd-policies). An entity may legitimately hold properties from several types at once, which schema.org states plainly (datamodel).

FAQPage and HowTo no longer earn a rich result. Neither appears in Google's structured data gallery as of 2026-08-28 (search-gallery). The markup is still valid and still describes the page correctly, so keep it where it is honest, and never propose it as the way to win a rich result. Re-check the gallery before relying on either direction: Google adds and removes features with little notice.

On duplicated pages, Google asks for the same markup on every duplicate rather than on the canonical alone (sd-policies).

Required versus recommended properties

"Required" here means required by Google for that rich result, not by schema.org. Schema.org requires nothing: its data model is deliberately flexible, extra properties are allowed, and even a wrong value type is not an error (datamodel). Google is the one drawing lines, because it is the one deciding eligibility: items missing required properties are not eligible for rich results (sd-policies).

So a missing property below is an eligibility problem, never a validity error, and saying it the other way round sends people to fix markup that was already correct.

TypeRequired by GoogleRecommended
Organizationname, urllogo, sameAs, description, address, contactPoint, foundingDate
WebSitename, urlpublisher reference, inLanguage, potentialAction
Articleheadline, image, datePublishedauthor as Person, dateModified, publisher, description, mainEntityOfPage, inLanguage
BreadcrumbListitemListElement with position, name, itemTrailing item without item for the current page
FAQPagemainEntity with Question, name, acceptedAnswer.textinLanguage
HowToname, step with HowToStep name and texttotalTime, tool, supply, image per step
Productname, image plus one of offers, review, aggregateRatingbrand, description, sku
SoftwareApplicationname, applicationCategory, plus offers or aggregateRatingoperatingSystem, description, screenshot, featureList
Eventname, startDate, locationendDate, eventAttendanceMode, organizer, offers, performer
VideoObjectname, description, thumbnailUrl, uploadDatecontentUrl or embedUrl, duration, transcript
PersonnamejobTitle, sameAs, image, worksFor

Required means the rich result will not be issued without it. Recommended means it adds accuracy or eligibility. Neither means invent it: an absent value is left out.

Tools

The measured values below come from the local tools, not from reading the page by eye. No API key, no install, no network beyond the page itself:

NeedCommand
Parse and validate the JSON-LD already on a pagepython -m seo_tools schema <url> --json

Every command takes --json, which is the form to use here. Exit code 0 means it answered, 1 means it could not. Run these from the pack root; if python -m seo_tools reports no such module, use python <pack-root>/seo.py instead, which works from any directory. If anything errors, run python -m seo_tools doctor first. Full reference: docs/execution-layer.md.

Procedure

  1. Read the page. Note the real headline, the real author, the real dates, every visible FAQ question and answer verbatim, any video, any price, any breadcrumb trail. This inventory is the only permitted source for property values.
  2. Check what markup already exists. Duplicate blocks of the same type conflict. Note conflicts to remove rather than adding a third block.
  3. Choose the types from the mapping table. Fewer, correct types beat a broad sweep of loosely applicable ones.
  4. Fill the properties from the page inventory. Anything unavailable is omitted, and the output lists what was omitted and what would supply it.
  5. Build the block. One <script type="application/ld+json"> per page where possible, using @graph with @id references so Article can point at the publisher Organization rather than repeating it.
  6. Match language and locale. Set inLanguage per the market's language variant from the profile. On a translated page, the markup follows that page's language.
  7. Write the implementation note for the actual CMS, from the notes below.
  8. Validate. Rich Results Test for eligibility, Schema Markup Validator for correctness, then Search Console's enhancement reports after deployment. Record errors, warnings and the date. Never report a page as valid without a test.

CMS implementation notes

Webflow. Static pages: Page Settings, Custom Code, inside head. CMS templates: add the script in the collection page template and bind values to collection fields with the field-embed control, so a post's own author and dates populate. Create dedicated SEO fields rather than reusing the excerpt. Publish is required before the markup is live, and the Designer preview does not reflect it.

HubSpot. Add JSON-LD to the template head via a HubL block, drawing values from module fields and page properties so blog posts do not share one hardcoded author. Site-wide Organization belongs in the site header HTML, once.

WordPress. If an SEO plugin already emits schema, configure it rather than adding a parallel block, because two Article graphs on one page conflict. For custom blocks, hook into wp_head in a child theme, not the theme file, and pull values from post fields rather than typing them.

Framework-rendered head. Next.js, Nuxt, Astro and similar: render the script server-side from the same data the page renders, so markup and page cannot drift. Client-side injection after hydration is unreliable for crawlers. Never keep schema in a separate content file that an editor can update without touching the page.

Output

For each page:

URL: <url>   Page type: <type>   Schema types: <list>
Omitted for lack of a real value: <property: what would supply it>
Removed: <conflicting or false markup found on the page>

<the JSON-LD block, complete, ready to paste>

Implementation: <CMS, exact location, and any field bindings>
Validation: Rich Results Test <result, date> | Schema Markup Validator <result, date>

Where several pages share a template, give one block plus the field-binding map:

| Property | Source | CMS field or value |

Guardrails

  • Never output markup for content that is not on the page. Add the content first.
  • Never invent an author, a date, a rating, a review, a price or a duration.
  • Never keep a dateModified update as the whole change to a page.
  • Never promise a rich result. Eligibility is not entitlement, and Google decides.
  • Never leave two blocks of the same type on one page.
  • Never mark up gated or click-hidden FAQ content as visible.
  • A named human deploys. Output is a code block plus instructions.
  • Handoff: /snippet-targeting when the page needs the visible FAQ that markup would describe, /technical-audit for head-level and rendering problems, and /page-optimiser when the markup is fine and the content is the problem.

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

Reviews can't be read right now.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.