@awesomate/hosting-mcp
Version:
Awesomate MCP server — lets Claude manage your Awesomate WordPress hosting, plan, limits, n8n automations, and build Node/static apps + databases
291 lines (247 loc) • 17.7 kB
Markdown
---
name: awesomate-seo
description: Make the user's site findable by Google AND quotable by AI assistants (SEO + AEO). Runs the build gates with awesomate_site_audit, then fixes what fails: title/meta/Open Graph, sitemap.xml, robots.txt, canonical tags, JSON-LD, answer-shaped content structure, and llms.txt. Works for static sites, Node apps, and WordPress. Use when the user says "SEO", "AEO", "GEO", "answer engine optimisation", "get found on Google", "get cited by ChatGPT", "does ChatGPT recommend us", "AI visibility", "rank higher", "make my site discoverable", "sitemap", "meta tags", "schema", or after building/changing any public page. Companion to awesomate-app-builder.
---
# Awesomate SEO — get found on Google and AI
Search engines and AI assistants both need machine-readable signals. This skill
adds them to any site the user has. Treat SEO as **ongoing**, not one-shot:
bake it in when a page is built, and re-run the relevant parts whenever content
or structure changes.
Be honest about limits: these are the signals that *let* you rank and be cited
— they don't *guarantee* placement. Never promise rankings.
## 0. What kind of site is this?
- **Static site or Node app** (from awesomate-app-builder) → you write the files
directly into the project (below), commit, and deploy.
- **WordPress** → drive it through the hosting tools via the awesomate-hosting
skill. Install a free SEO plugin + let it generate the sitemap rather than
hand-writing files (see §WordPress). **Check the plan gate first.**
- **A shop (WooCommerce)** → same WordPress path, plus `Product` JSON-LD on
product pages and product-data quality (real titles, descriptions, images,
prices). This skill covers the site-level SEO around the shop.
**Business facts come from `awesomate_business`.** Organization and
LocalBusiness JSON-LD (name, url, logo, address, telephone, `sameAs` from the
social links), titles and meta descriptions must use the owner's confirmed
details, never guesses. Leave out anything marked *suggestion* until the owner
confirms it, and list what's missing rather than inventing it.
### Plan gate — check before promising any WordPress change
**Every PAT-authenticated hosting WRITE is Support Plus and above**:
`awesomate_run_wp_cli`, `awesomate_wp_post`, `awesomate_wp_media_import`,
`awesomate_wp_settings`, plus `awesomate_site_create` and
`awesomate_domain_add`. On **Essentials** they all return **403
`upgrade_required`** — the server enforces it, so no retry or rewording gets
through.
This matters more here than anywhere else: **Essentials is the plan that
gets exactly one WordPress site**, so it is the most likely plan for someone
asking about SEO. Get `plan` from `awesomate_get_context` at the start and
branch honestly.
**On Essentials, assume no tool touches their WordPress at all.** Note that
`awesomate_run_wp_cli` is gated even for its READS (`option get`,
`plugin list`) — the whole route is behind the write gate, so "just reading
the options" is not a way round it. The account-level reads do work
(`awesomate_get_context`, `awesomate_list_sites`, `awesomate_list_domains`,
`awesomate_dns_check`, `awesomate_site_uptime`, and **`awesomate_site_audit`** —
the full gate sweep works on every plan, because it reads the public page).
**What Essentials CAN do — which is most of the value:** audit and advise
from the outside. Start with `awesomate_site_audit` — it runs the whole sweep
in one call and is NOT plan-gated, so an Essentials user gets exactly the same
findings as anyone else. Then produce the exact changes —
the titles and descriptions to paste, the JSON-LD block, the llms.txt
content, which plugin to install and where its settings live. They apply
them in wp-admin; you verify by fetching the live page again. Say that up
front — *"I can audit the site and write every change out for you to paste
in; me making them directly needs Support Plus"* — not after a 403.
## 1. Static / Node — write these
Follow [references/seo-checklist.md](references/seo-checklist.md) for exact
formats + length limits. In short, per site:
1. **`<head>` tags** on every page: unique `<title>` (≤60 chars), meta
description (≤155), canonical `<link>`, Open Graph (`og:title/description/
image/url/type`) and `twitter:card`. The static-landing template ships
`{{...}}` placeholders — fill them with real values.
2. **`robots.txt`** at the site root — allow crawling, point to the sitemap.
3. **`sitemap.xml`** at the root — list every real URL with `<loc>` (+ `<lastmod>`).
4. **JSON-LD** (`<script type="application/ld+json">`) — the right schema.org
type (`Organization` / `WebSite` / `Product` / `Article` / `LocalBusiness`).
5. **Answer-shaped content** — the part that decides whether the page is
actually *quoted* rather than merely found: answer-first sections of 120–180
words, no section opening with "it"/"this"/"also", comparisons in a real
`<table>`, specific numbers and dates inline, and every schema fact also
present in the visible copy.
Full discipline: [references/answer-structure.md](references/answer-structure.md).
This is the highest-leverage item on the list and the one most often skipped.
6. **`llms.txt`** at the root — cheap, so write it, but keep it in proportion:
Google ignores it entirely and one study of 137,210 domains found 97% of
published files got zero requests. Generate it, never present it as a pillar,
never bill for it. Crawler/rendering gotchas:
[references/ai-discoverability.md](references/ai-discoverability.md).
Then commit + deploy (awesomate-github). On content changes, update the
affected `<head>` tags, `sitemap.xml` `<lastmod>`, and `llms.txt`.
## 2. WordPress
**Find the domain, don't ask for it.** `awesomate_list_sites` and
`awesomate_list_domains` tell you what sites exist and which domain is
attached to which. Ask only when the answer is genuinely ambiguous (more
than one site, and the user hasn't said which).
**Snapshot before you change a live site.** `awesomate_snapshot_site` first,
and tell the user the `snapshotId` — the hosting skill requires this and SEO
work is exactly the kind of change that touches the whole site at once (a
plugin, a theme option, an indexing flag). `awesomate_rollback_site` is the
undo.
Then, on Support Plus and above:
- **Install + activate an SEO plugin** — `awesomate_run_wp_cli` with args
`['plugin','install','wordpress-seo','--activate']` for Yoast, or the
user's preference. Slugs only, never a URL or zip. This part works.
- **Configuring it does NOT work from here.** The wp-cli gate allows
`option update` for exactly ten core options, and `wpseo_titles` and
friends are not among them; their values are JSON full of spaces, which
the argument charset gate refuses anyway. So: you install and activate,
then hand off — *"Yoast is installed. Open Settings → Yoast SEO in
wp-admin and I'll talk you through the title template and the social
image, or give me the values and I'll write out exactly what to paste."*
Don't burn attempts discovering this at runtime.
- Confirm the sitemap is live (`/sitemap_index.xml` or `/sitemap.xml`) and
`robots.txt` references it — `curl` the live URLs.
- **Posts and pages** via `awesomate_wp_post` (drafts first — the user
approves publishing). Useful for the content pages that back up
`llms.txt` — a services overview, an FAQ, an about page — when the site is
thin on machine-readable substance.
- **The og:image / logo** goes in via `awesomate_wp_media_import` — one https
URL per call, returns an `attachmentId` you can reference from a post or
hand to the SEO plugin's social settings. It cannot read a local file:
the image has to be reachable on the web first.
- **Site title, tagline, timezone, search visibility** → `awesomate_wp_settings`
(§4), never wp-cli — those values contain spaces.
- WordPress is the recommended stack precisely when SEO/organic reach matters —
lean into it here.
### Files at the site root: say what's actually possible
§1's `robots.txt` / `sitemap.xml` / `llms.txt` advice is for **static sites
and Node apps**, where you write the file into the project and deploy it.
**On WordPress there is no tool that writes a file to the site.**
`awesomate_run_wp_cli` cannot (no file verbs are allowlisted), `wp_post`
writes posts and pages, and `wp_media_import` takes an https URL. Don't
promise the file and then discover this.
On WordPress those three come from:
- **`robots.txt` and `sitemap.xml`** — the SEO plugin generates both
(WordPress serves a virtual robots.txt; Yoast and friends add the sitemap
and the reference to it). That's why installing the plugin is step one.
- **`llms.txt`** — no plugin makes one. Either the user places the file over
SSH or cPanel's File Manager (`scripts/ssh-connect.sh` in the hosting
skill, Support Plus+), or you publish the same content as a normal page
with `awesomate_wp_post` and link it — a real page is crawlable and
editable, and gets most of the benefit.
**You can always generate the content.** Write the exact `llms.txt` body,
the robots rules, the JSON-LD block, and tell them precisely where each goes
— that is the useful half, and it works on every plan.
## 3. Verify — run the gates, don't eyeball it
**`awesomate_site_audit { domain, path }`** runs the mechanical gates against
the live page, fetched *as OAI-SearchBot* (what ChatGPT search sees), and returns pass/fail with specific fixes:
content present without JavaScript, all four retrieval bots reachable, no
accidental `noindex`/`nosnippet`, canonical pointing at this domain, valid
structured data whose facts also appear in the copy, section structure,
robots.txt + sitemap, and time-to-first-byte.
Run it after building or changing any public page, and **before telling the user
their site is discoverable**. Checking in a browser is not equivalent — the
browser runs the JavaScript that hides the biggest failure.
Two gates come back `skipped` because they need a headless browser (content
revealed only on click) or a full crawl (internal 404s). **Skipped means
unknown, not passing** — say so rather than reporting a clean sweep.
Then confirm by hand what the tool can't judge:
- `robots.txt` and `sitemap.xml` (or the WP sitemap) return 200 at the root.
- Each key page has a unique title + description and valid JSON-LD (no syntax
errors — a broken `application/ld+json` block is worse than none).
- `llms.txt` exists and is accurate.
- **Content is in the raw HTML** — `curl -s https://DOMAIN/page` shows the
actual content, not an empty `<div id="root">`. AI crawlers don't run
JavaScript; a client-rendered page is invisible to them.
- **AI retrieval bots aren't blocked at the edge** — a `curl` as OAI-SearchBot,
Claude-SearchBot, PerplexityBot, Perplexity-User, ChatGPT-User and Bingbot
returns 200, not a 403/Cloudflare challenge (robots.txt can't fix an edge
block). **Training bots (GPTBot, ClaudeBot, CCBot…) are not the test:** on
Awesomate-managed domains (`*.awesomate.site`, `*.awesomate.app`) they get a
403 by design, and on the user's own domain letting them in is the user's
choice. Commands + where to fix each case:
[references/ai-discoverability.md](references/ai-discoverability.md).
Tell the user the next step is submitting the sitemap in Google Search Console
(you can't do that for them) — offer a one-paragraph how-to if they want it.
## 4. Ongoing upkeep — the seo-healthcheck
SEO decays quietly: a plugin update flips an option, an edge rule starts
challenging crawlers, a sitemap stops regenerating. Run this routine whenever
the user asks to "check SEO", and after any content change:
1. **Content in the raw HTML** — `curl -s https://DOMAIN/page` and confirm the
key content is actually in the response, not rendered later by JavaScript.
2. **Sitemap** — `/sitemap.xml` (WP: `/sitemap_index.xml`) returns 200 and
`<lastmod>` is fresh. A months-old lastmod on a site that changed last week
means generation is broken, not that nothing changed.
3. **robots.txt + llms.txt** — both exist (on WordPress, `llms.txt` may be
the page standing in for it — check whichever they have), and the content
still mentions the current offerings. A stale llms.txt misdescribes the
business to every AI assistant that reads it.
4. **AI retrieval bots not blocked at the edge** —
`curl -A "Mozilla/5.0 (compatible; OAI-SearchBot/1.0; +https://openai.com/searchbot)" -s -o /dev/null -w "%{http_code}" https://DOMAIN/`
(repeat as Claude-SearchBot, PerplexityBot, Perplexity-User, ChatGPT-User
and Bingbot) returns 200, not 403 or a challenge page. Don't test with
GPTBot or ClaudeBot: those are training bots, blocked by design on
Awesomate-managed domains and the owner's choice elsewhere.
Where to fix each case: [references/ai-discoverability.md](references/ai-discoverability.md).
5. **WordPress only: `blog_public` must be 1** — one checkbox ("Discourage
search engines") silently de-indexes the whole site. Read it via
`awesomate_run_wp_cli` with args `['option','get','blog_public']`. If it's
0, fix it with **`awesomate_wp_settings {domain, searchEngineVisible:
true}`** — that path also flushes the object cache, which the wp-cli
`option update` does not, so the change actually shows on the live site
instead of sitting behind a stale cache. (A page or CDN cache may still
need its own refresh.)
6. **"My site vanished from Google" is often not SEO at all.** Before
auditing tags, check the two things that take a site off the internet:
`awesomate_dns_check` (is the domain still pointing at the hosting?) and
`awesomate_site_uptime` (30-day availability and incidents). A domain
that stopped resolving, or a site that was down for two days, explains a
ranking drop far better than a meta description does.
Report pass/fail per check, with the fix for each failure.
**Make it automatic:** offer — once — to build a scheduled n8n workflow (via
the awesomate-n8n skill) that runs these checks weekly and emails the user the
result. A weekly email is what turns "SEO as ongoing" from a good intention
into a mechanism.
## 5. Is it working? AI visibility (Support Plus and above)
The gates above say whether a page CAN be found. `awesomate_ai_visibility` says whether the real
ChatGPT and Gemini apps actually name the business when its customers ask. Use it when the user
asks "does ChatGPT recommend us", "are we showing up in AI", or after a round of fixes.
- `action: "draft"` suggests up to 8 questions and the names to look for, from the business
details, when none are set yet. It saves nothing: talk them through, then send the owner to the
hub to approve them. `action: "check_result"` {checkId} reads one check in full.
- `action: "status"` returns the score (0 to 100, with a likely range), the questions where neither
app named the business, who they named instead, and the sites the apps relied on.
- **Fix from that evidence, in this order:**
1. a page on the site that directly answers each question never named, in the customer's words
and naming the place the business serves;
2. the directories, review sites and "best of" lists the apps relied on: get the business on
them, or ask the owner to;
3. the Google Business Profile (category, services, reviews), because Gemini answers local
questions from Google Maps.
- **Do not promise a lift from schema or llms.txt.** Studies found none. They are good hygiene,
not a visibility fix.
- `action: "check"` starts a new check (about five minutes). Owner checks are limited to 3 per site
per week, and sites the owner put on weekly checks are re-checked automatically: check again only after a
real change, never to watch the number within a day.
- **The owner approves the questions, in the hub, never you.** If `questions_approved` is false,
send them to the site's page in the hub (Website, the site, AI visibility).
- Scores move slowly and vary between checks. Read the trend over several weeks, and never tell
the owner a single check proves a fix worked.
## 6. Is it working? The site's own figures
`awesomate_site_insights` reads what Google Analytics and Search Console report for the site, as the
hub's site cards and site pages show it. No `domain`: every site's visits, leads, AI-app visits and
search clicks for the last 28 days against the 28 before. With `domain`, that site's page:
- **Worth fixing** (a missing or someone else's Google tag, a sitemap Google could not read, plenty
of visits and no leads): fix these first.
- **Searches almost on page one** (positions 8 to 20): the cheapest wins. Improve the page that
ranks for each, in the customer's words.
- **Seen on page one but rarely clicked**: rewrite that page's title and meta description.
- **Questions AI apps answer without you**, beside the closest Google search: the pages to write
(the same evidence as section 5).
- **Where leads came from** and **pages that bring leads**: say "across all your sites" when
`leads.scope` is `all_sites`.
A missing figure means Google is not connected or has no data yet, never zero. Connecting Google
and adding the tag happen in the hub (Settings, Integrations), never here.
## Hard rules
- Never fabricate content, reviews, or business facts to "help SEO".
- Never claim these guarantee a ranking — they make the site *eligible* and
*legible*, to search engines and AI both.
- Invalid structured data is a bug — validate the JSON before shipping.