Voice-preserving SEO content MCP server — 14 rule-based tools, language-agnostic, no external LLM calls.
Features • Install • IDE Setup • API • Workflow • Python library • Troubleshooting
seo-echo-mcp reads your existing blog, extracts its writing voice and topical footprint, and makes sure new content matches that voice — in any language. It is not a keyword-volume tool (use Ahrefs/Semrush for that); it's the layer on top that keeps your LLM-assisted drafts sounding like you instead of like generic AI copy.
Think of it as a "style mirror" for your content pipeline.
14 tools covering the full pipeline from site analysis through publish-ready draft + schema + image-alt audit. Every tool is rule/template-based — no external LLM/API calls.
| Tool | What it does |
|---|---|
analyze_site |
Crawls your blog, extracts language, topics, style profile, H2 patterns |
analyze_competitors |
Fetches top SERP results (DuckDuckGo → Bing fallback) and extracts their structure |
detect_content_gaps |
Surfaces topics competitors cover but you don't |
check_duplicates |
Warns when a proposed keyword/title overlaps with existing posts |
| Tool | What it does |
|---|---|
suggest_titles |
10 SEO title candidates, ranked by site voice + competitor format |
generate_meta_variations |
5 meta descriptions across 5 angles (140-160 chars) |
generate_slug |
URL-safe slug with language-aware transliteration (ı→i, ü→ue, ñ→n…) |
generate_outline |
SEO outline matched to your voice + competitor common H2 topics |
generate_faq_section |
PAA-style FAQ block + FAQPage JSON-LD |
generate_schema_jsonld |
Article / BlogPosting / HowTo / Review JSON-LD |
| Tool | What it does |
|---|---|
prepare_draft_skeleton |
Full markdown skeleton with frontmatter + <!-- WRITE --> directives per section. Host LLM fills and saves the .md. |
audit_content |
Scores a draft against your style profile + 16 SEO checks |
readability_report |
Per-language readability (Flesch-EN, Ateşman-TR, Fernández-Huerta-ES, generic fallback) + passive voice (EN/TR/DE) |
suggest_image_alts |
Flags missing/weak <img> alt text and proposes replacements from filename + context |
analyze_site measures the voice of your existing posts. If your editorial preference differs — e.g. "my blog uses em-dashes occasionally but I don't want them in new drafts" — pass voice_overrides to prepare_draft_skeleton and audit_content:
voice_overrides = {"em_dash_frequency": "never"}Any StyleProfile field can be overridden this way (em_dash_frequency, addressing, tone, average_word_count, …). The site profile itself is not mutated — the override only affects that single tool call.
Works with any language py3langid detects (ISO 639-1). Coverage quality is tiered:
- Fully localized (outline templates + FAQ + image alt + synthetic H2 fallbacks + AI-cliché detection + passive voice where applicable): Turkish, English, Spanish, French, German.
- Generic fallback (content extraction + style heuristics + slug transliteration work; outline/meta/FAQ templates default to English): every other language. Contributions welcome — see CONTRIBUTING.md.
No manual install required — run it straight from GitHub with uvx:
uvx --from git+https://github.com/canberkys/seo-echo-mcp seo-echo-mcpuvx clones + builds on first run, caches afterwards.
Other installation methods
Persistent pip install (no PyPI):
pip install git+https://github.com/canberkys/seo-echo-mcp
# or with uv:
uv pip install git+https://github.com/canberkys/seo-echo-mcpLocal clone for development:
git clone https://github.com/canberkys/seo-echo-mcp && cd seo-echo-mcp
uv sync --extra dev
uv run seo-echo-mcp # stdio server for testingFrom PyPI: not published yet — open an issue if you need a PyPI release.
Click the IDE you use to expand setup instructions.
Claude Code
claude mcp add seo-echo --scope user -- uvx --from git+https://github.com/canberkys/seo-echo-mcp seo-echo-mcpThen in any Claude Code session, type /mcp — you should see seo-echo ✓ Connected.
Claude Desktop
Edit ~/Library/Application Support/Claude/claude_desktop_config.json (macOS) or %APPDATA%\Claude\claude_desktop_config.json (Windows):
{
"mcpServers": {
"seo-echo": {
"command": "uvx",
"args": [
"--from",
"git+https://github.com/canberkys/seo-echo-mcp",
"seo-echo-mcp"
]
}
}
}Restart Claude Desktop. The tools appear under the 🔌 icon.
Cursor
Create .cursor/mcp.json in your project root (or ~/.cursor/mcp.json for a global install):
{
"mcpServers": {
"seo-echo": {
"command": "uvx",
"args": [
"--from",
"git+https://github.com/canberkys/seo-echo-mcp",
"seo-echo-mcp"
]
}
}
}Reload Cursor (Cmd/Ctrl + Shift + P → Developer: Reload Window).
Windsurf
Edit ~/.codeium/windsurf/mcp_config.json:
{
"mcpServers": {
"seo-echo": {
"command": "uvx",
"args": [
"--from",
"git+https://github.com/canberkys/seo-echo-mcp",
"seo-echo-mcp"
]
}
}
}Restart Windsurf.
VS Code (GitHub Copilot)
Create .vscode/mcp.json in your workspace:
{
"servers": {
"seo-echo": {
"command": "uvx",
"args": [
"--from",
"git+https://github.com/canberkys/seo-echo-mcp",
"seo-echo-mcp"
]
}
}
}Reload VS Code and confirm via the Copilot Chat settings.
Zed
Add to ~/.config/zed/settings.json:
{
"context_servers": {
"seo-echo": {
"command": {
"path": "uvx",
"args": [
"--from",
"git+https://github.com/canberkys/seo-echo-mcp",
"seo-echo-mcp"
]
}
}
}
}Reload Zed (Cmd/Ctrl + Shift + P → zed: reload).
Pin a release: append
@v0.4.0(or any tag) to the git URL:git+https://github.com/canberkys/seo-echo-mcp@v0.4.0. To pull the latest after an upgrade, run the IDE's MCP add command once withuvx --refresh .... Verify: regardless of IDE, try prompting"analyze_site for myblog.com"— if the MCP is wired up, your assistant will chain the tools automatically.
Every tool is async def. Realistic sample outputs live in examples/.
analyze_site(url=None, urls=None, max_samples=12, cache_ttl=86400, bypass_cache=False) → SiteProfile
Crawl a blog and return its voice profile. Pass url for auto-discovery via sitemap/feed, or urls=[...] to skip discovery (useful for blocked/JS-rendered sites). Results are cached at ~/.cache/seo-echo-mcp/<domain>.json for 24h by default — override with SEO_ECHO_CACHE_DIR env or bypass_cache=True. See examples/site_profile.json.
analyze_competitors(keyword=None, urls=None, language="en", country="us", top_n=10) → CompetitorAnalysis
Fetch and structure top SERP results (or a given URL list). SERP provider order: Google CSE (if GOOGLE_CSE_API_KEY + GOOGLE_CSE_ID set) → DuckDuckGo HTML → Bing HTML.
detect_content_gaps(site_profile, competitor_analysis, min_coverage=2) → ContentGapReport
Topics competitors cover but your site doesn't, ranked by coverage count. See examples/content_gap_report.json.
check_duplicates(proposed, site_profile, threshold=0.3) → DuplicateReport
Jaccard similarity over stopword-filtered tokens (stemmed for TR). Flags existing posts that overlap your proposed keyword/title and labels the verdict safe / caution / duplicate.
suggest_titles(keyword, site_profile, competitor_analysis=None, count=10) → TitleSuggestions
10 ranked title candidates, style-matched to the site's H2 pattern. If competitors are provided, the most-common listicle N is honored (e.g. Top 10 … instead of hard-coded 7).
generate_meta_variations(keyword, title, language="en") → MetaVariations
5 meta descriptions across 5 angles (problem-solution, question, benefit, curiosity, action), all 140–160 chars.
generate_slug(title, language="en", max_length=60) → SlugResult
URL-safe slug with language-aware transliteration (ı→i, ü→ue, ñ→n, …), plus up to two shorter alternatives (stopword-stripped, truncated).
generate_outline(keyword, site_profile, competitor_analysis=None, target_word_count=None, new_category=None) → Outline
5–12 sections with unique H2s (language-specific), 3 title candidates, 3 meta descriptions, internal link targets, citation-research topic stubs. Rule-based. See examples/outline.json.
generate_faq_section(keyword, language="en", competitor_analysis=None, count=5) → FaqSection
PAA-style FAQ markdown block + FAQPage JSON-LD. Question-shaped competitor H2s are folded in when available.
generate_schema_jsonld(schema_type, title, description, url, author_name, published_at, image_url=None, keywords=None, language="en") → SchemaJsonLd
Article / BlogPosting / HowTo / Review JSON-LD + ready-to-paste <script> snippet.
prepare_draft_skeleton(outline, site_profile, target_keyword=None, slug=None, faq_section=None, schema_jsonld=None, selected_title_index=0, selected_meta_index=0, voice_overrides=None) → DraftSkeleton
Assembles the full markdown skeleton (frontmatter + <!-- WRITE --> directives per section + optional FAQ + JSON-LD). The host LLM fills each directive and saves the final .md. See examples/draft_skeleton.md.
audit_content(content_markdown, site_profile, target_keyword=None, target_meta_description=None, voice_overrides=None) → AuditReport
16 rule-based checks (word count, H2 format, em-dash, AI clichés, keyword density, heading hierarchy, image alt coverage, …). Score 0–100 with prioritized recommendations. See examples/audit_report.json.
readability_report(content_markdown, language="en") → ReadabilityReport
Per-language formula (Flesch-EN, Ateşman-TR, Fernández-Huerta-ES, generic fallback) + passive-voice ratio for EN/TR/DE.
suggest_image_alts(content_markdown, target_keyword=None, language="en") → ImageAltReport
Flags missing/weak alt text per image and proposes replacements from the filename stem, the target keyword, and the nearest preceding paragraph. Alt template is language-aware.
Because every tool is rule/template-based, the host LLM (Claude) writes the prose — the MCP just builds the scaffolding. A single prompt can chain all 14 tools:
"Analyze
myblog.com, research competitors forasync python, check if I already wrote about it, get a draft skeleton with FAQ + Article JSON-LD, fill the skeleton in my voice and save it ascontent/async-python.md, then audit + score readability."
Claude will:
analyze_site("myblog.com")→SiteProfileanalyze_competitors(keyword="async python")→CompetitorAnalysisdetect_content_gaps(site, competitors)→ topics you're missingcheck_duplicates("async python", site)→ safe/caution/duplicatesuggest_titles("async python", site, competitors)→ 10 titles; picks onegenerate_outline("async python", site, competitors)→Outlinegenerate_faq_section("async python", "en", competitors)→ FAQ + JSON-LDgenerate_schema_jsonld("Article", ...)→ Article JSON-LDprepare_draft_skeleton(outline, site, faq, schema)→ markdown skeleton with<!-- WRITE -->directives- Claude fills every directive in your voice, respecting word count, addressing ("you"/"sen"/"vous"…), em-dash policy, etc.
- Claude saves the filled markdown via its
Writetool →content/async-python.md audit_content(draft, site, target_keyword)→AuditReport(score + fixes)readability_report(draft, language)→ Flesch/Ateşman/Fernández-Huerta score + passive voice (EN/TR/DE)suggest_image_alts(draft, target_keyword)→ flags missing/weak alt text and proposes replacements
You end up with a publishable .md that matches your blog's voice, passes SEO checks, and has schema markup ready to paste into <head>.
The tools are plain async functions. You can import them and call them without an MCP host, e.g. from a Django management command, a CI step, or a notebook.
import asyncio
from seo_echo_mcp.tools.analyze_site import analyze_site
from seo_echo_mcp.tools.generate_outline import generate_outline
from seo_echo_mcp.tools.audit_content import audit_content
async def main():
profile = await analyze_site("myblog.com", max_samples=8)
outline = await generate_outline("async python", profile)
draft_md = open("draft.md").read()
report = await audit_content(draft_md, profile, target_keyword="async python")
print(report.overall_score, report.recommendations)
asyncio.run(main())Every tool returns a Pydantic model; call .model_dump_json() for JSON or .model_dump() for a plain dict. Inputs that are Pydantic models (e.g. SiteProfile) also accept dicts — Pydantic validates on the way in.
Tools don't show up in /mcp. Confirm you picked the right scope: --scope user puts the registration in ~/.claude.json, visible from any directory; --scope local is only visible in the directory where you ran the command. Run claude mcp list to see what's registered.
analyze_site returns Unable to reach URL. The target site may be blocking non-browser User-Agents or rate-limiting. Try passing an explicit post list via urls=[...] to skip sitemap discovery.
analyze_competitors returns no results. DuckDuckGo rate-limits aggressively. Set GOOGLE_CSE_API_KEY + GOOGLE_CSE_ID env vars to use Google Custom Search instead (best quality), or pass urls=[...] for a manual competitor list.
Cache feels stale. Clear ~/.cache/seo-echo-mcp/ or pass bypass_cache=True for a one-off fresh run. Override the path with SEO_ECHO_CACHE_DIR=/tmp/seo-echo-cache.
Draft keeps using em-dashes / wrong tone. Pass voice_overrides={"em_dash_frequency": "never"} (or any other StyleProfile field) to prepare_draft_skeleton and audit_content. See the "Overriding voice heuristics" section above.
See more detail in IDE logs. Set SEO_ECHO_LOG_LEVEL=DEBUG on the IDE's MCP command; every tool emits start/finish milestones to stderr (stdout is reserved for the MCP stdio protocol).
What's in scope:
- Voice/style profiling of an existing blog
- Structure & metadata generation (titles, meta, slug, outline, FAQ, schema)
- Draft scaffolding for a host LLM to fill
- Post-draft quality checks (SEO audit, readability, image alt coverage)
What's out of scope:
- Writing prose. Every tool is rule/template-based. The host LLM (Claude / Cursor / etc.) generates the actual text after consuming the outline + skeleton.
- Calling external LLM APIs. No hidden OpenAI / Anthropic / Gemini calls. No API keys required by default (optional Google CSE only).
- Credential storage. The only disk write is an opt-in cache at
~/.cache/seo-echo-mcp/. - Keyword research (search volume, SERP CTR, backlinks). Pair with Ahrefs, Semrush, or similar —
seo-echo-mcpfocuses on voice + structure. - Plagiarism or fact-checking. Audit checks style and SEO hygiene; it cannot verify claims.
Contributions welcome — especially new languages and new tools. See CONTRIBUTING.md for setup, style conventions, and step-by-step guides.
Security issues: use a private security advisory. See SECURITY.md.
- v0.2 — Content creator expansion (9 new tools, 13 total)
- v0.3 — Manual URL list input for
analyze_site, persistent cache,suggest_image_alts, TR/DE passive voice - v0.4 — Language-aware fallbacks, TR stemmer, stratified sampling, pronoun families, cache path hardening, community files,
examples/ - v0.5 — Multi-site profile comparison
- v0.5 — Semantic similarity in
check_duplicates(TF-IDF / embeddings)
MIT — see LICENSE.