@awesomate/hosting-mcp 0.15.0 → 0.16.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -40031,7 +40031,7 @@ server.registerTool(
40031
40031
  server.registerTool(
40032
40032
  "awesomate_get_context",
40033
40033
  {
40034
- description: "Call this FIRST each session. Returns the connected Awesomate account: plan, capabilities (shell access), plan limits, scopes this token can exercise, token expiry (warn the user if within 7 days), cPanel routing when provisioned, and skill.updateAvailable \u2014 if true, the local awesomate-hosting skill files are older than this server; tell the user to re-run Connect Claude Code from hub.awesomate.ai/sites to refresh them (this also renews the token).",
40034
+ description: "Call this FIRST each session. Returns the connected Awesomate account: plan, capabilities (shell access), plan limits, scopes this token can exercise, token expiry (warn the user if within 7 days), cPanel routing when provisioned, and skill.updateAvailable \u2014 if true, the local Awesomate skill files are older than this server; run awesomate_skill_update to refresh them in place (new content loads next session \u2014 mention once, finish the current task first). If the token is also near expiry, re-running Connect Claude Code from hub.awesomate.ai/sites refreshes skills AND renews the token in one go.",
40035
40035
  inputSchema: {}
40036
40036
  },
40037
40037
  async () => {
@@ -40285,10 +40285,10 @@ server.registerTool(
40285
40285
  server.registerTool(
40286
40286
  "awesomate_support",
40287
40287
  {
40288
- description: "Help the user with the service itself \u2014 questions, being stuck, or reaching a human. action 'faq' {q} searches the published help library (answer FROM the results; never invent policy or pricing). action 'create_ticket' {subject, message} opens a support ticket \u2014 draft it in the user's words, SHOW it, and get an explicit yes before sending; it emails the Awesomate team and returns a portal URL. action 'list_tickets' shows their tickets and status. Use the awesomate-support skill for the full flow. Not for building \u2014 route n8n work to awesomate-n8n and hosting to awesomate-hosting.",
40288
+ description: "Help the user with the service itself \u2014 questions, being stuck, or reaching a human. action 'faq' {q} searches the published help library (answer FROM the results; never invent policy or pricing). action 'help_docs' {q} searches the full Help Centre articles live \u2014 use when the FAQ has no good answer, and cite the returned article URLs. action 'create_ticket' {subject, message} opens a support ticket \u2014 draft it in the user's words, SHOW it, and get an explicit yes before sending; it emails the Awesomate team and returns a portal URL. action 'list_tickets' shows their tickets and status. Use the awesomate-support skill for the full flow. Not for building \u2014 route n8n work to awesomate-n8n and hosting to awesomate-hosting.",
40289
40289
  inputSchema: {
40290
- action: external_exports.enum(["faq", "create_ticket", "list_tickets"]),
40291
- q: external_exports.string().optional().describe("faq only: the question"),
40290
+ action: external_exports.enum(["faq", "help_docs", "create_ticket", "list_tickets"]),
40291
+ q: external_exports.string().optional().describe("faq and help_docs: the question or topic"),
40292
40292
  subject: external_exports.string().optional().describe("create_ticket only"),
40293
40293
  message: external_exports.string().optional().describe("create_ticket only: the body, in the user's words")
40294
40294
  }
@@ -40299,6 +40299,10 @@ server.registerTool(
40299
40299
  if (action === "faq") {
40300
40300
  return textResult(await hubGet(cfg, `/api/help/faq${q ? `?q=${encodeURIComponent(q)}` : ""}`));
40301
40301
  }
40302
+ if (action === "help_docs") {
40303
+ if (!q) return errorResult(new Error("help_docs needs q"));
40304
+ return textResult(await hubGet(cfg, `/api/help/docs?q=${encodeURIComponent(q)}`));
40305
+ }
40302
40306
  if (action === "list_tickets") {
40303
40307
  return textResult(await hubGet(cfg, "/api/support/tickets"));
40304
40308
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@awesomate/hosting-mcp",
3
- "version": "0.15.0",
3
+ "version": "0.16.1",
4
4
  "description": "Awesomate MCP server \u2014 lets Claude manage your Awesomate WordPress hosting, plan, limits, n8n automations, and build Node/static apps + databases",
5
5
  "license": "UNLICENSED",
6
6
  "type": "module",
@@ -37,8 +37,9 @@ once (plus `awesomate_get_limits` before any create/change) and cache:
37
37
  - **`patExpiresAt`** — within ~7 days, or any 401: re-run the setup prompt at
38
38
  **hub.awesomate.ai/sites → Connect Claude Code** to refresh the token.
39
39
  - **`skill.updateAvailable`** — this skill's local files are older than the MCP
40
- server. Mention once (don't nag); the same Connect re-run refreshes the skill
41
- and renews the token in one go. Not urgent finish the user's request first.
40
+ server. Mention once (don't nag); run `awesomate_skill_update` to refresh in
41
+ place (new content loads next session). If the token is also near expiry, the
42
+ Connect re-run above does both. Not urgent — finish the user's request first.
42
43
  - **`cpanel`** routing (host/user), present once hosting is provisioned.
43
44
 
44
45
  If `awesomate_get_context` fails with a connectivity error, it's the user's
@@ -40,6 +40,9 @@ formats + length limits. In short, per site:
40
40
  5. **`llms.txt`** at the root — a short, plain-markdown summary of what the
41
41
  site/business is and its key pages, so AI assistants can understand and cite
42
42
  it. (This is the general, non-shop counterpart to the Woo `llms.txt`.)
43
+ Be honest about what it does: Google ignores it — it serves the *other*
44
+ assistants. For Google's AI features, normal SEO is the whole strategy.
45
+ Details + crawler/rendering gotchas: [references/ai-discoverability.md](references/ai-discoverability.md).
43
46
 
44
47
  Then commit + deploy (awesomate-github). On content changes, update the
45
48
  affected `<head>` tags, `sitemap.xml` `<lastmod>`, and `llms.txt`.
@@ -61,6 +64,13 @@ Through the awesomate-hosting skill's `wp` passthrough:
61
64
  - Each key page has a unique title + description and valid JSON-LD (no syntax
62
65
  errors — a broken `application/ld+json` block is worse than none).
63
66
  - `llms.txt` exists and is accurate.
67
+ - **Content is in the raw HTML** — `curl -s https://DOMAIN/page` shows the
68
+ actual content, not an empty `<div id="root">`. AI crawlers don't run
69
+ JavaScript; a client-rendered page is invisible to them.
70
+ - **AI crawlers aren't blocked at the edge** — a `curl` with a GPTBot /
71
+ ClaudeBot user-agent returns 200, not a 403/Cloudflare challenge (Cloudflare
72
+ blocks many AI bots by default; robots.txt can't fix that). Commands + where
73
+ to fix each case: [references/ai-discoverability.md](references/ai-discoverability.md).
64
74
  Tell the user the next step is submitting the sitemap in Google Search Console
65
75
  (you can't do that for them) — offer a one-paragraph how-to if they want it.
66
76
 
@@ -0,0 +1,109 @@
1
+ # AI discoverability — crawlers, rendering, llms.txt
2
+
3
+ How AI assistants (ChatGPT, Claude, Perplexity, Gemini, Copilot) find and cite
4
+ a site — and the two silent failure modes that make a "perfectly SEO'd" site
5
+ invisible to them anyway. Crawler/rendering tables adapted from
6
+ [claude-blog](https://github.com/AgriciDaniel/claude-blog) (MIT © AgriciDaniel).
7
+
8
+ ## Google needs nothing special — say so when asked
9
+
10
+ Google's official guidance
11
+ (https://developers.google.com/search/docs/fundamentals/ai-optimization-guide):
12
+ AI Overviews and AI Mode run on Google's normal Search ranking systems. No
13
+ special markup, no AI schema, no llms.txt — **Google Search ignores llms.txt
14
+ entirely**. Normal SEO (the main checklist) *is* the Google-AI strategy. If
15
+ the user asks about "GEO"/"AEO" tactics or paid AI-optimization services,
16
+ this is the honest answer: for Google it's just SEO; for other assistants
17
+ the extras below help.
18
+
19
+ llms.txt still earns its place for **non-Google** assistants — keep writing
20
+ it, but present it as that, never as a Google requirement.
21
+
22
+ ## Three kinds of AI bot — three different decisions
23
+
24
+ | Kind | Bots | Blocking means |
25
+ |---|---|---|
26
+ | Search-indexing | OAI-SearchBot, Claude-SearchBot, PerplexityBot | The site can't be cited in that assistant's answers. **Allow these.** |
27
+ | Training | GPTBot, ClaudeBot, Google-Extended, CCBot, Applebot-Extended, Meta-ExternalAgent | Owner's policy choice about model training; doesn't remove the site from answers |
28
+ | User-triggered retrieval | ChatGPT-User, Perplexity-User, Claude-User | Fetches when a live user asks; may not fully respect robots.txt |
29
+
30
+ The default `User-agent: * / Allow: /` robots.txt already allows all of them —
31
+ no explicit allowlist needed. Explicit stanzas only matter when the owner
32
+ wants to opt out of **training** while staying visible in answers:
33
+
34
+ ```
35
+ # Stay citable, opt out of model training
36
+ User-agent: GPTBot
37
+ Disallow: /
38
+
39
+ User-agent: Google-Extended
40
+ Disallow: /
41
+
42
+ User-agent: CCBot
43
+ Disallow: /
44
+
45
+ User-agent: *
46
+ Allow: /
47
+ Sitemap: https://DOMAIN/sitemap.xml
48
+ ```
49
+
50
+ Never block the search-indexing bots "for safety" — that silently removes the
51
+ site from ChatGPT/Claude/Perplexity answers.
52
+
53
+ Note for Awesomate-managed domains: the CDN prepends a managed section to the
54
+ site's robots.txt (a Content Signals preamble plus disallows for the
55
+ *training*-bot family). Search-indexing bots stay allowed, so the site remains
56
+ citable — but the served robots.txt is the site file *plus* that preamble.
57
+ Always check the live `https://DOMAIN/robots.txt`, not just the file you wrote.
58
+
59
+ ## robots.txt allows it — but the CDN can still 403 it
60
+
61
+ Cloudflare **blocks many AI crawlers by default on new zones** (GPTBot,
62
+ ClaudeBot, PerplexityBot, CCBot, Google-Extended). The crawler gets a 403 or a
63
+ challenge page before it ever reads robots.txt, so a perfect robots.txt proves
64
+ nothing. Always verify from the outside:
65
+
66
+ ```bash
67
+ curl -s -o /dev/null -w "%{http_code}\n" -A "Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko; compatible; GPTBot/1.0; +https://openai.com/gptbot)" https://DOMAIN/
68
+ curl -s -o /dev/null -w "%{http_code}\n" -A "Mozilla/5.0 (compatible; ClaudeBot/1.0)" https://DOMAIN/
69
+ curl -s -o /dev/null -w "%{http_code}\n" -A "Mozilla/5.0 (compatible; PerplexityBot/1.0)" https://DOMAIN/
70
+ ```
71
+
72
+ 200 = reachable. 403, 503, or an HTML page mentioning Cloudflare = blocked at
73
+ the edge. Where to fix it:
74
+
75
+ - **Awesomate-managed domain** (`*.awesomate.site` etc.): the zone is managed
76
+ by Awesomate — the user can't change it. Raise it via `awesomate_support`
77
+ so the zone's AI Crawl Control settings get reviewed.
78
+ - **Custom domain on the user's own Cloudflare account**: they fix it
79
+ themselves in the Cloudflare dashboard → Security → Bots → AI Crawlers.
80
+
81
+ ## AI crawlers do not run JavaScript
82
+
83
+ Only Googlebot renders JS. GPTBot, ClaudeBot, PerplexityBot, and the other AI
84
+ crawlers read the **raw HTML response**. A client-rendered app (React/Vue SPA)
85
+ is invisible to every AI assistant regardless of its meta tags and llms.txt.
86
+
87
+ Red flags — check the raw response, not the browser:
88
+
89
+ ```bash
90
+ curl -s https://DOMAIN/page | head -100
91
+ ```
92
+
93
+ - Body is essentially an empty `<div id="root">` / `<div id="app">` /
94
+ `<div id="__next">`
95
+ - Full HTML under ~5KB for a content page
96
+ - The content exists only inside `<script>` tags as JSON
97
+
98
+ Fix: keep public, indexable content in static or server-rendered HTML — for
99
+ app projects, static marketing/landing pages in front of the app is usually
100
+ enough. Don't rebuild a working app over this, but never claim AI
101
+ discoverability for client-rendered content.
102
+
103
+ ## llms.txt rules
104
+
105
+ - Optional inventory for non-Google assistants — Google ignores it.
106
+ - Under 10KB (assistants may truncate or skip larger files).
107
+ - Markdown links: `[Title](URL): one-line description`. Only real,
108
+ high-value pages; only true facts.
109
+ - Supplements sitemap.xml, never replaces it. Update both when content changes.
@@ -47,10 +47,32 @@ Pick the type that fits; validate before shipping.
47
47
  ```
48
48
  Common types: `WebSite` (+ `SearchAction`), `Organization` / `LocalBusiness`
49
49
  (add `address`, `telephone`, `openingHours`), `Product` (`offers`, `brand`,
50
- `gtin`), `Article` (`headline`, `datePublished`, `author`), `FAQPage`.
50
+ `gtin`), `Article` (`headline`, `datePublished`, `author`).
51
+
52
+ **Don't add `FAQPage` or `HowTo` expecting search features** — Google retired
53
+ FAQ rich results for all sites on 2026-05-07 and HowTo earlier. Both remain
54
+ valid schema.org markup, but they earn nothing; only add them if the visible
55
+ FAQ itself is useful.
56
+
57
+ Multiple entities on one page: combine them in ONE script tag using `@graph`,
58
+ with stable `@id` fragments cross-referencing instead of duplicating:
59
+ ```json
60
+ {
61
+ "@context": "https://schema.org",
62
+ "@graph": [
63
+ { "@type": "Organization", "@id": "https://DOMAIN/#organization", "name": "Brand", "url": "https://DOMAIN/", "logo": "https://DOMAIN/logo.png" },
64
+ { "@type": "Article", "@id": "https://DOMAIN/PATH#article", "headline": "…", "datePublished": "YYYY-MM-DD", "publisher": { "@id": "https://DOMAIN/#organization" } }
65
+ ]
66
+ }
67
+ ```
68
+ Keep `@id` values stable across rebuilds (URL + `#fragment`, never random).
69
+ Validate at https://validator.schema.org (schema validity); use Google's Rich
70
+ Results Test only for types Google actually supports as rich results.
51
71
 
52
72
  ## `llms.txt` (site root — AI discoverability)
53
- Plain markdown so assistants can read + cite the site. Keep it current.
73
+ Plain markdown so assistants can read + cite the site. Keep it current and
74
+ under 10KB. Honest framing: **Google ignores llms.txt** — it serves the other
75
+ AI assistants (see [ai-discoverability.md](ai-discoverability.md)).
54
76
  ```
55
77
  # Brand — what we do
56
78
 
@@ -20,18 +20,22 @@ also carries the plan). Describe what a plan or upgrade INCLUDES from
20
20
  balance without spending anything. Never quote a plan, price, or balance
21
21
  from memory.
22
22
 
23
- ## 1. FAQ first
23
+ ## 1. FAQ first, then help docs
24
24
 
25
25
  For any "how do I", "why does", "what is" question about the service:
26
26
 
27
27
  1. Call `awesomate_support {action:'faq', q:'<their question>'}`.
28
28
  2. Answer FROM the returned entries, in your own plain words. Cite nothing
29
29
  the FAQ and tools didn't say — no invented policy, no guessed pricing.
30
- 3. If the FAQ answers it, you're done. If it partially answers, give what
31
- you have and say which part is unconfirmed.
32
- 4. If it doesn't answer it: "I don't have a confirmed answer for that. Want
30
+ 3. If the FAQ misses or only partially answers, call
31
+ `awesomate_support {action:'help_docs', q:'<topic>'}` a live search of
32
+ the full Help Centre articles. Answer from the excerpts and give the
33
+ article's URL so the user can read the full steps.
34
+ 4. If neither answers it: "I don't have a confirmed answer for that. Want
33
35
  me to ask the Awesomate team? I'll draft a support ticket for you to
34
- approve first." Then go to §2 if they say yes.
36
+ approve first." Then go to §2 if they say yes. (An empty help_docs
37
+ result can also mean the search is temporarily down — the response's
38
+ help_centre_url is always safe to offer for browsing.)
35
39
 
36
40
  ## 2. Ticket flow — talking to a human
37
41