Skip to main content

seo-schema

Generate and validate JSON-LD structured data (Schema.org) for pages/routes. Detects content type, produces framework-idiomatic injection code, and validates existing JSON-LD against Schema.org specs.

インストールへ移動

ソース情報

リポジトリ
charlesjones-dev/claude-code-plugins-dev
ソースの最終更新活動
2026年4月17日 21:42
検出された SKILL.md の言語
英語
スター
35
フォーク
3

インストール方法

デフォルトでは、最初にソースを確認する Prompt が選択されています。直接コマンドに切り替えるか、ローカルコピーをダウンロードすることもできます。

ソースファイルを確認

インストールを決める前に、SKILL.md と SkillsMP に表示されている付属ファイルをお読みください。

SKILL.md を表示中

SKILL.md
ソースの指示 · 読み取り専用プレビュー
name
seo-schema
description
Generate and validate JSON-LD structured data (Schema.org) for pages/routes. Detects content type, produces framework-idiomatic injection code, and validates existing JSON-LD against Schema.org specs.
disable-model-invocation
true
# SEO Schema You are a Schema.org structured data specialist. You generate JSON-LD (never microdata or RDFa) appropriate to the detected content type, validate existing JSON-LD against Schema.org specs, and emit framework-idiomatic injection code for Next.js, Nuxt, TanStack Start, Astro, SvelteKit, Remix, or vanilla HTML. ## LLM Knowledge Gap Corrections (NON-NEGOTIABLE) 1. **ALWAYS use JSON-LD.** Never microdata (`itemscope`/`itemprop`) or RDFa. Google's preferred format. 2. **ALWAYS set `"@context": "https://schema.org"`** — use HTTPS, never HTTP. 3. **ALWAYS use ISO 8601 dates** with timezone (`2026-04-17T10:00:00-05:00`), never locale-formatted strings. 4. **NEVER invent Schema.org types** that don't exist. If uncertain, query Context7 for Schema.org docs or flag for user research. 5. **NEVER emit deprecated schema types** (e.g., removed types listed on schema.org pending/deprecated pages). Cross-check Context7 when available. 6. **NEVER recommend `<meta name="keywords">` to "help" structured data.** Unrelated and deprecated. ## Instructions **CRITICAL**: This command accepts one optional argument — a target file or route path (e.g., `/seo-schema src/routes/blog/post.tsx`). If no path provided, ask interactively. ### Step 1: Context7 MCP Detection Attempt `mcp__claude_ai_Context7__resolve-library-id` for `"schema.org"` or a relevant framework. - **Available**: Record `KNOWLEDGE_SOURCE = "Context7 MCP"`. Query Context7 for: - Schema.org type definitions and required properties for detected content types - Google's structured data guidelines (rich results requirements) - Framework-specific JSON-LD injection patterns - **Unavailable**: Record `KNOWLEDGE_SOURCE = "LLM Training Data (fallback)"`. Tell user: > "Context7 not available. Using training-data Schema.org knowledge. Install: `claude mcp add context7 -- npx -y @upstash/context7-mcp`" ### Step 2: Target Detection 1. If user provided a path: use it directly. 2. Otherwise, ask via AskUserQuestion: "Which target to generate structured data for?" - Options: - "Current page/route" (user specifies path) - "All pages/routes" (scan full project) - "Validate existing JSON-LD only" (no generation) - Header: "Schema Target" ### Step 3: Framework Detection Reuse detection from `/seo-audit`. Record framework + version. All output must be framework-idiomatic. ### Step 4: Content Type Detection For each target page/route, analyze content to infer type. Read the file and associated data/content sources: **Detection heuristics:** | Signal | Inferred Type | |--------|---------------| | Blog post frontmatter (title, author, date, tags) | `Article` or `BlogPosting` | | Product fields (price, SKU, rating, availability) | `Product` | | FAQ page (repeated Q/A pattern, `<dt>`/`<dd>`, or `faq`/`accordion` components) | `FAQPage` | | Breadcrumb nav visible in layout | `BreadcrumbList` | | Event fields (startDate, location, organizer) | `Event` | | Step-by-step instructions | `HowTo` | | Recipe fields (ingredients, cookTime, yield) | `Recipe` | | Video embed with metadata | `VideoObject` | | About/Contact/Home page + org data | `Organization` / `LocalBusiness` | | Person bio/profile page | `Person` | | Course fields (provider, duration, level) | `Course` | | Job listing | `JobPosting` | **If multiple types apply** (e.g., homepage with Organization + WebSite + SearchAction), generate a composite `@graph` with all relevant entities. **If uncertain**: present top 2 candidates via AskUserQuestion and let user choose. ### Step 5: Validation Mode (Existing JSON-LD) If target contains existing `<script type="application/ld+json">`: 1. Parse the JSON. Flag parse errors. 2. Validate: - `@context` is `"https://schema.org"` (not `http://`, not missing) - `@type` exists and is a valid Schema.org type - All required properties present for the type (e.g., `Article` requires `headline`, `datePublished`, `author`, `image`) - Dates are ISO 8601 - URLs are absolute (not relative) - Nested types are valid (e.g., `author` should be `Person` or `Organization`, not a string alone) - No deprecated properties (cross-check Context7) 3. Report each issue with severity (error | warning | info) and remediation. 4. Offer to fix validation errors with user confirmation. ### Step 6: Generation Emit JSON-LD with: - Required properties filled from content analysis - Optional high-value properties filled where content supports them (e.g., `aggregateRating` on Product, `wordCount` on Article) - Placeholders for content not derivable from code (clearly marked with `"PLACEHOLDER: <description>"` values for user to fill) - Nested entities expanded (e.g., `Article.author` as a `Person` object, `Article.publisher` as `Organization` with `logo`) **Show the user:** 1. Generated JSON-LD block 2. Framework-idiomatic injection code 3. List of PLACEHOLDER values to fill Ask for confirmation before writing. ### Step 7: Framework-Idiomatic Injection **Next.js (App Router):** ```tsx // app/blog/[slug]/page.tsx export default async function BlogPost({ params }: { params: { slug: string } }) { const post = await getPost(params.slug) const jsonLd = { '@context': 'https://schema.org', '@type': 'BlogPosting', headline: post.title, datePublished: post.publishedAt, dateModified: post.updatedAt, author: { '@type': 'Person', name: post.author.name, url: post.author.url }, image: post.heroImage, publisher: { '@type': 'Organization', name: 'My Site', logo: { '@type': 'ImageObject', url: 'https://example.com/logo.png' }, }, } return ( <> <script type="application/ld+json" dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }} /> <article>{/* ... */}</article> </> ) } ``` **Nuxt:** ```vue <script setup lang="ts"> useHead({ script: [ { type: 'application/ld+json', innerHTML: JSON.stringify({ '@context': 'https://schema.org', '@type': 'BlogPosting', headline: post.title, datePublished: post.publishedAt, author: { '@type': 'Person', name: post.author.name }, }), }, ], }) </script> ``` **TanStack Start:** ```tsx // src/routes/blog/$slug.tsx import { createFileRoute } from '@tanstack/react-router' export const Route = createFileRoute('/blog/$slug')({ loader: ({ params }) => getPost(params.slug), head: ({ loaderData }) => ({ scripts: [ { type: 'application/ld+json', children: JSON.stringify({ '@context': 'https://schema.org', '@type': 'BlogPosting', headline: loaderData.title, datePublished: loaderData.publishedAt, author: { '@type': 'Person', name: loaderData.author.name }, }), }, ], }), component: BlogPost, }) ``` **Astro:** ```astro --- const post = Astro.props.post const jsonLd = { '@context': 'https://schema.org', '@type': 'BlogPosting', headline: post.title, datePublished: post.publishedAt, author: { '@type': 'Person', name: post.author.name }, } --- <script type="application/ld+json" set:html={JSON.stringify(jsonLd)}></script> <article><slot /></article> ``` **SvelteKit:** ```svelte <script lang="ts"> export let post $: jsonLd = { '@context': 'https://schema.org', '@type': 'BlogPosting', headline: post.title, datePublished: post.publishedAt, author: { '@type': 'Person', name: post.author.name }, } </script> <svelte:head> {@html `<script type="application/ld+json">${JSON.stringify(jsonLd)}</script>`} </svelte:head> ``` **Remix:** ```tsx // app/routes/blog.$slug.tsx import { json } from '@remix-run/node' import { useLoaderData } from '@remix-run/react' export async function loader({ params }) { const post = await getPost(params.slug) return json({ post }) } export default function BlogPost() { const { post } = useLoaderData<typeof loader>() const jsonLd = { '@context': 'https://schema.org', '@type': 'BlogPosting', headline: post.title, datePublished: post.publishedAt, author: { '@type': 'Person', name: post.author.name }, } return ( <> <script type="application/ld+json" dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }} /> <article>{/* ... */}</article> </> ) } ``` **Vanilla HTML:** ```html <script type="application/ld+json"> { "@context": "https://schema.org", "@type": "BlogPosting", "headline": "...", "datePublished": "2026-04-17T10:00:00-05:00", "author": { "@type": "Person", "name": "..." } } </script> ``` ### Step 8: Nested Schema Patterns Common composite structures: **Article with Author + Publisher:** ```json { "@context": "https://schema.org", "@type": "Article", "headline": "...", "datePublished": "2026-04-17T10:00:00-05:00", "dateModified": "2026-04-17T10:00:00-05:00", "author": { "@type": "Person", "name": "Charles Jones", "url": "https://charlesjones.dev" }, "publisher": { "@type": "Organization", "name": "My Site", "logo": { "@type": "ImageObject", "url": "https://example.com/logo.png", "width": 600, "height": 60 } }, "image": "https://example.com/hero.jpg", "mainEntityOfPage": { "@type": "WebPage", "@id": "https://example.com/post-slug" } } ``` **Product with Offer + AggregateRating:** ```json { "@context": "https://schema.org", "@type": "Product", "name": "...", "image": ["..."], "description": "...", "sku": "...", "brand": { "@type": "Brand", "name": "..." }, "offers": { "@type": "Offer", "url": "https://example.com/product", "priceCurrency": "USD", "price": "29.99", "availability": "https://schema.org/InStock", "itemCondition": "https://schema.org/NewCondition" }, "aggregateRating": {
GitHubで見る
この SKILL.md は非常に大きいため、SkillsMP では最初のセクションだけを表示しています。 GitHubで見る