SEO analysis and optimization plugin for EmDash CMS.
Free, open-source alternative to Yoast SEO. Built for structured content.
Yoast parses HTML with regex to guess at your content structure. SEO Engine walks Portable Text --- a typed JSON tree where headings, links, images, and text are discrete nodes. No parsing. No guessing. Structurally perfect analysis.
| Yoast SEO | SEO Engine | |
|---|---|---|
| Price | $99/year per site | Free |
| Content analysis | HTML regex parsing | Portable Text tree walking |
| Lock-in | Data stored in wp_postmeta | Plugin storage only --- remove plugin, core SEO keeps working |
| AI suggestions | Paid add-on, OpenAI only | Built-in, any provider (Claude, GPT, Gemini, Ollama, custom) |
| Schema detection | Manual configuration | Auto-detected from content structure |
| International SEO | Paid add-on | Built-in hreflang management |
12 weighted SEO checks run automatically on every save:
- Word count --- configurable minimum threshold
- Heading structure --- single H1 validation, proper hierarchy
- Focus keyword --- density, placement in title/H1/first paragraph
- Title length --- 50-60 character optimal range
- Meta description --- 120-160 character validation
- Image alt text --- coverage percentage
- Internal/external links --- presence and ratio
- Readability --- Flesch-Kincaid Reading Ease adapted for structured text
Each check produces a pass/warning/fail status with an actionable recommendation. The weighted aggregate yields a 0-100 score.
Analyzes content structure to suggest the appropriate schema type:
| Pattern | Detection | Confidence |
|---|---|---|
| Question headings + paragraph answers | FAQPage | 70-90% |
| "How to" headings + numbered lists | HowTo | 60-90% |
| Short content + recent date + news language | NewsArticle | 50-90% |
| Long-form with structured headings | Article | 60-70% |
| Standard content | BlogPosting | Default |
Auto-detected schemas contribute JSON-LD via the page:metadata hook when confidence exceeds 60%. Manual overrides take priority.
Optional --- works without an API key. Three suggestion types:
- Meta descriptions --- 3 options, 120-155 characters each
- Focus keywords --- 5 candidates with reasoning
- Content improvements --- 3-5 actionable tips based on failing checks
Bring your own key. Supports any provider:
| Provider | Format |
|---|---|
| Anthropic (Claude) | Native API |
| OpenAI (GPT) | Chat Completions |
| Google (Gemini) | OpenAI-compatible |
| Custom endpoint | OpenAI-compatible (Ollama, LM Studio, vLLM, etc.) |
Prompt-hardened. Immutable system prompt. Content treated as data, never instructions. Injection patterns neutralized. Responses structurally validated. Rate-limited (configurable requests/hour, tokens/day).
- SEO Dashboard --- score distribution chart, grade breakdown, content needing attention
- Content Analysis --- per-item score, SERP preview, social card preview, 12-check breakdown, focus keyword management, AI suggestions, schema detection with signals, score trend history
- Broken Links --- external link health monitoring (weekly automated checks)
- International SEO --- hreflang tag management per content item
- Score history --- daily tracking with trend visualization
- Content hashing --- skips re-analysis when content is unchanged
- Broken link checker --- scheduled cron with configurable frequency
- Batch audit --- analyze all content on demand
- BreadcrumbList JSON-LD --- auto-generated from URL path
- Extended Schema.org --- FAQ, HowTo, NewsArticle, Article, LocalBusiness
pnpm add @dreams-engine/emdash-plugin-seo// astro.config.mjs
import { seoEngine } from "@dreams-engine/emdash-plugin-seo";
export default defineConfig({
integrations: [
emdash({
plugins: [seoEngine()],
}),
],
});The plugin registers automatically. Visit the admin panel at /_emdash/admin/plugins/seo-engine.
All settings are configurable via the plugin settings page in the EmDash admin:
| Setting | Default | Description |
|---|---|---|
| Minimum Word Count | 300 | Target word count for content |
| Readability Target | 60 | Flesch-Kincaid Reading Ease target (0-100) |
| Require Focus Keyword | false | Penalize content without a focus keyword |
| Default Schema Type | BlogPosting | Fallback schema when auto-detection is inconclusive |
| AI Provider | Anthropic | LLM provider for suggestions |
| AI API Key | --- | Your API key (BYOK) |
| AI Model | claude-haiku-4-5 | Model for suggestions |
| AI Requests/Hour | 20 | Rate limit |
| AI Daily Token Budget | 50,000 | Daily token cap |
All routes are authenticated and require the X-EmDash-Request: 1 header.
POST /_emdash/api/plugins/seo-engine/analysis/get
POST /_emdash/api/plugins/seo-engine/analysis/refresh
POST /_emdash/api/plugins/seo-engine/analysis/set-keyword
POST /_emdash/api/plugins/seo-engine/dashboard/stats
POST /_emdash/api/plugins/seo-engine/dashboard/issues
POST /_emdash/api/plugins/seo-engine/dashboard/distribution
POST /_emdash/api/plugins/seo-engine/schema/get
POST /_emdash/api/plugins/seo-engine/schema/set
POST /_emdash/api/plugins/seo-engine/hreflang/get
POST /_emdash/api/plugins/seo-engine/hreflang/set
POST /_emdash/api/plugins/seo-engine/links/broken
POST /_emdash/api/plugins/seo-engine/audit/trigger
POST /_emdash/api/plugins/seo-engine/audit/status
POST /_emdash/api/plugins/seo-engine/history/get
POST /_emdash/api/plugins/seo-engine/ai/meta-descriptions
POST /_emdash/api/plugins/seo-engine/ai/keywords
POST /_emdash/api/plugins/seo-engine/ai/suggestions
POST /_emdash/api/plugins/seo-engine/ai/usage
src/
index.ts Plugin descriptor + createPlugin
admin.tsx React admin UI (dashboard, analysis, hreflang, broken links)
settings.ts Shared settings loader with Promise.all
sanitize.ts Input sanitization for stored data
storage.ts Storage collections + indexes
types.ts Data model types
schemas.ts Zod input validation
analysis/ Pure functions --- zero side effects, fully testable
portable-text.ts Portable Text tree walker
word-count.ts Word/sentence/syllable counting
readability.ts Flesch-Kincaid Reading Ease
keyword.ts Focus keyword density + placement
heading-structure.ts H1 validation, hierarchy checks
image-analysis.ts Alt text completeness
link-analysis.ts Internal/external ratio
meta-validation.ts Title/description length
schema-detector.ts Schema.org type auto-detection
scorer.ts Weighted aggregate scoring
ai/ Provider-agnostic LLM integration
client.ts Multi-provider client (Anthropic + OpenAI format)
prompts.ts Immutable system prompt + injection defense
rate-limiter.ts Token budget + request rate tracking
suggestions.ts Orchestrator with output validation
handlers/ EmDash hook handlers
content-save.ts content:afterSave --- auto-analysis with content hashing
page-metadata.ts page:metadata --- BreadcrumbList, hreflang, extended schemas
cron.ts Scheduled broken link checks + batch audit
routes/ API endpoints
analysis.ts Get/refresh/keyword per content item
dashboard.ts Site-wide stats, score distribution, issues
schema-config.ts Per-content Schema.org type
hreflang.ts hreflang mapping management
link-check.ts Broken link results
audit.ts Trigger/status for batch audit
history.ts Score history per content item
ai.ts AI suggestion endpoints
- Input sanitization on all stored data (HTML stripping, URL validation, locale normalization)
- Prompt injection defense --- 13 patterns detected and neutralized before LLM calls
- AI output validation --- strict structural checks, length limits, type coercion
- Content hashing --- deterministic hash skips redundant analysis
- Rate limiting --- configurable per-hour, per-day, per-month token budgets
- Error boundaries --- every admin page wrapped, crashes contained
git clone https://github.com/DreamsEngine/emdash-plugin-seo.git
cd emdash-plugin-seo
pnpm install
pnpm test # 87 tests
pnpm test:watch # watch modeMIT
Built by Dreams Engine. Designed for the EmDash CMS ecosystem.