@awesomate/hosting-mcp 0.23.0 → 0.25.0

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
@@ -40885,6 +40885,7 @@ var TITLES = {
40885
40885
  awesomate_skill_update: "Update Awesomate skills",
40886
40886
  awesomate_site_create: "Create WordPress site",
40887
40887
  awesomate_site_audit: "Audit site for SEO and AEO",
40888
+ awesomate_php_extensions: "Check or fix PHP extensions",
40888
40889
  awesomate_uninstall_site: "Delete WordPress site",
40889
40890
  awesomate_snapshot_site: "Snapshot site",
40890
40891
  awesomate_list_snapshots: "List site snapshots",
@@ -41251,17 +41252,32 @@ server.registerTool(
41251
41252
  "awesomate_support",
41252
41253
  {
41253
41254
  annotations: annotate("awesomate_support", MUTATING),
41254
- 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.",
41255
+ description: "Help the user with the service itself \u2014 questions, being stuck, or reaching a human. **action 'ask' {q} is the FIRST thing to try for any question about Awesomate** \u2014 it answers from Awesomate's own Knowledge Base with numbered citations, works on every plan, and composes across articles rather than matching titles; relay its answer and cite the source URLs. If it returns no_answer:true the grounding gate declined, so offer a ticket instead of guessing; if degraded:true the Knowledge Base was unavailable and you are looking at keyword matches, so do not present them as a verified answer. 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, category} 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. **`category` is REQUIRED and must name what the ticket is actually about \u2014 'Other' is refused.** Ticket access splits by SUBJECT, not by plan: reporting a problem with AWESOMATE ITSELF ('Infrastructure fault' \u2014 site down, instance unreachable, provisioning or SSL failure; 'Claude/MCP connectivity' \u2014 pairing fails, token rejected, tools erroring) and 'Account/billing' work on EVERY plan. Asking for help USING the products ('Workflow not running', 'Workflow error', 'New automation request', 'How-to question') needs Support Plus or above and returns 403 upgrade_required below it \u2014 when that happens, answer from 'faq'/'help_docs' instead and relay the upgrade honestly; do NOT relabel the ticket as a fault to get through. 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.",
41255
41256
  inputSchema: {
41256
- action: external_exports.enum(["faq", "help_docs", "create_ticket", "list_tickets"]),
41257
- q: external_exports.string().optional().describe("faq and help_docs: the question or topic"),
41257
+ action: external_exports.enum(["ask", "faq", "help_docs", "create_ticket", "list_tickets"]),
41258
+ q: external_exports.string().optional().describe("ask, faq and help_docs: the question or topic"),
41258
41259
  subject: external_exports.string().optional().describe("create_ticket only"),
41259
- message: external_exports.string().optional().describe("create_ticket only: the body, in the user's words")
41260
+ message: external_exports.string().optional().describe("create_ticket only: the body, in the user's words"),
41261
+ category: external_exports.enum([
41262
+ "Infrastructure fault",
41263
+ "Claude/MCP connectivity",
41264
+ "Workflow not running",
41265
+ "Workflow error",
41266
+ "New automation request",
41267
+ "Account/billing",
41268
+ "How-to question"
41269
+ ]).optional().describe(
41270
+ "create_ticket: REQUIRED. What the ticket is about. The first two and Account/billing work on every plan; the rest need Support Plus or above."
41271
+ )
41260
41272
  }
41261
41273
  },
41262
- async ({ action, q, subject, message }) => {
41274
+ async ({ action, q, subject, message, category }) => {
41263
41275
  try {
41264
41276
  const cfg = requireConfig();
41277
+ if (action === "ask") {
41278
+ if (!q) return errorResult(new Error("ask needs q"));
41279
+ return textResult(await hubGet(cfg, `/api/help/ask?q=${encodeURIComponent(q)}`));
41280
+ }
41265
41281
  if (action === "faq") {
41266
41282
  return textResult(await hubGet(cfg, `/api/help/faq${q ? `?q=${encodeURIComponent(q)}` : ""}`));
41267
41283
  }
@@ -41273,7 +41289,14 @@ server.registerTool(
41273
41289
  return textResult(await hubGet(cfg, "/api/support/tickets"));
41274
41290
  }
41275
41291
  if (!subject || !message) return errorResult(new Error("create_ticket needs subject and message"));
41276
- return textResult(await hubPost(cfg, "/api/support/tickets", { subject, body_text: message }));
41292
+ if (!category) {
41293
+ return errorResult(
41294
+ new Error(
41295
+ 'create_ticket needs a category naming what the ticket is about. Use "Infrastructure fault" or "Claude/MCP connectivity" when the problem is with Awesomate itself, "Account/billing" for money, and the workflow/how-to categories (Support Plus and above) for help using the products.'
41296
+ )
41297
+ );
41298
+ }
41299
+ return textResult(await hubPost(cfg, "/api/support/tickets", { subject, body_text: message, category }));
41277
41300
  } catch (err) {
41278
41301
  return errorResult(err);
41279
41302
  }
@@ -41303,7 +41326,7 @@ server.registerTool(
41303
41326
  "awesomate_site_create",
41304
41327
  {
41305
41328
  annotations: annotate("awesomate_site_create", MUTATING),
41306
- description: "Create a WordPress site on the user's hosting. Plan limits are enforced server-side (429/403 with an upgrade hint if they're at their cap \u2014 relay it; 409 means WordPress already exists at that domain). Ask 'live or dev?' first per the awesomate-hosting skill. Omit `domain` and the next free siteN.{primary} subdomain is chosen for you. ASYNC and returns only {success:true} \u2014 no domain, no admin details: poll awesomate_list_sites to learn the name, and expect HTTPS to refuse connections for a minute or two while AutoSSL issues even though the site is already serving over http. See 'After creating a site' in the skill.",
41329
+ description: "Create a WordPress site on the user's hosting. **Needs Support Plus or above from Claude** \u2014 on Essentials this returns 403 `upgrade_required` ALWAYS, not only at a cap, because building hosting from a machine token is a Support Plus capability; the Essentials site itself is real and is created by the user in the hub at hub.awesomate.ai/sites, so offer that path first rather than leading with the upgrade. Plan COUNT limits are enforced separately (403 with an upgrade hint at the cap; 409 means WordPress already exists at that domain). Ask 'live or dev?' first per the awesomate-hosting skill. Omit `domain` and the next free siteN.{primary} subdomain is chosen for you. ASYNC and returns only {success:true} \u2014 no domain, no admin details: poll awesomate_list_sites to learn the name, and expect HTTPS to refuse connections for a minute or two while AutoSSL issues even though the site is already serving over http. See 'After creating a site' in the skill.",
41307
41330
  inputSchema: {
41308
41331
  domain: external_exports.string().optional().describe("Custom domain if they have one; omit for a default *.awesomate.site subdomain"),
41309
41332
  siteTitle: external_exports.string().optional(),
@@ -41322,7 +41345,7 @@ server.registerTool(
41322
41345
  "awesomate_domain_add",
41323
41346
  {
41324
41347
  annotations: annotate("awesomate_domain_add", MUTATING),
41325
- description: "Add a custom domain to the user's hosting account. Returns the DNS steps they need to complete at their registrar. Plan-gated (some plans allow zero custom domains \u2014 relay the upgrade hint on 403).",
41348
+ description: "Add a custom domain to the user's hosting account. Returns the DNS steps they need to complete at their registrar. **Needs Support Plus or above from Claude** \u2014 Essentials gets 403 `upgrade_required` even though its plan DOES include one custom domain, because adding it from a machine token is Support Plus; tell them it takes two clicks at hub.awesomate.ai/sites/domains instead. Plan COUNT limits are enforced separately on top.",
41326
41349
  inputSchema: { domain: external_exports.string().describe("The domain to add, e.g. example.com") }
41327
41350
  },
41328
41351
  async ({ domain }) => {
@@ -41337,7 +41360,7 @@ server.registerTool(
41337
41360
  "awesomate_run_wp_cli",
41338
41361
  {
41339
41362
  annotations: annotate("awesomate_run_wp_cli", DESTRUCTIVE),
41340
- description: "Run an allowlisted WP-CLI command on one of the user's WordPress sites (plugin/theme list+activate+update, cache flush, option get/update, post/media/menu/comment/user list). ARGUMENTS CANNOT CONTAIN SPACES (letters, digits and -_./=:@+, only) \u2014 so a site title, tagline or any multi-word value is impossible here: use awesomate_wp_settings for those, and awesomate_wp_post for post/page content. Installs accept wp.org SLUGS only \u2014 never URLs. args is the command as an array, e.g. ['plugin','list'] or ['plugin','install','wordpress-seo','--activate']. A 400 wp_cli_not_allowed means that command isn't permitted; a 502 wp_cli_unavailable is a temporary server-side issue, not your command.",
41363
+ description: "Run an allowlisted WP-CLI command on one of the user's WordPress sites (plugin/theme list+activate+update, cache flush, option get/update, post/media/menu/comment/user list). **Needs Support Plus or above** \u2014 the whole route is behind the write gate, so even the READS (option get, plugin list) return 403 `upgrade_required` on Essentials. ARGUMENTS CANNOT CONTAIN SPACES (letters, digits and -_./=:@+, only) \u2014 so a site title, tagline or any multi-word value is impossible here: use awesomate_wp_settings for those, and awesomate_wp_post for post/page content. Installs accept wp.org SLUGS only \u2014 never URLs. args is the command as an array, e.g. ['plugin','list'] or ['plugin','install','wordpress-seo','--activate']. A 400 wp_cli_not_allowed means that command isn't permitted; a 502 wp_cli_unavailable is a temporary server-side issue, not your command.",
41341
41364
  inputSchema: {
41342
41365
  domain: external_exports.string().describe("The site domain"),
41343
41366
  args: external_exports.array(external_exports.string()).describe("WP-CLI args, e.g. ['plugin','list']")
@@ -41394,6 +41417,26 @@ readTool(
41394
41417
  "cPanel account details: package, server, provisioned-at, masked username.",
41395
41418
  "/api/client-hosting/account"
41396
41419
  );
41420
+ server.registerTool(
41421
+ "awesomate_php_extensions",
41422
+ {
41423
+ description: "Check (and optionally fix) the PHP extensions enabled for the hosting account's PHP runtime. Use this when WordPress fails on a SPECIFIC operation while the rest of the site works: `wp media import` or image uploads dying with \"critical error\" (missing dom), sudden white screens on text handling (missing mbstring), importers or sitemap generation failing (missing xmlreader/xmlwriter), or a plugin that installs fine but cannot connect (missing soap). The CloudLinux alt-php82 default set is much leaner than 7.4's, so accounts on PHP 8.2 often come up without these even though the modules are installed on the server. The account holder CANNOT fix this from a shell \u2014 CageFS blocks selectorctl for jailed accounts \u2014 so do it here rather than talking them through cPanel. Pass fix:true to enable the missing ones. Enabling is additive (nothing already on is removed), needs no PHP restart and causes no downtime.",
41424
+ inputSchema: {
41425
+ fix: external_exports.boolean().optional().describe("Enable the missing extensions. Omit or false to only report what is missing.")
41426
+ },
41427
+ annotations: annotate("awesomate_php_extensions", MUTATING)
41428
+ },
41429
+ async ({ fix }) => {
41430
+ try {
41431
+ const config3 = requireConfig();
41432
+ return textResult(
41433
+ fix ? await hubPost(config3, "/api/client-hosting/php-extensions", {}) : await hubGet(config3, "/api/client-hosting/php-extensions")
41434
+ );
41435
+ } catch (err) {
41436
+ return errorResult(err);
41437
+ }
41438
+ }
41439
+ );
41397
41440
  readTool(
41398
41441
  "awesomate_list_sites",
41399
41442
  "All WordPress sites on the hosting account: canonical domain, URL, title, WP version, SSL state, install date.",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@awesomate/hosting-mcp",
3
- "version": "0.23.0",
3
+ "version": "0.25.0",
4
4
  "description": "Awesomate MCP server — 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",
@@ -28,36 +28,61 @@ what's actually spendable once credits promised to builds in flight are
28
28
  set aside — rather than the raw balance.) Never quote a plan, price, or
29
29
  balance from memory.
30
30
 
31
- ## 1. FAQ first, then help docs
31
+ ## 1. Ask the Knowledge Base first
32
32
 
33
33
  For any "how do I", "why does", "what is" question about the service:
34
34
 
35
- 1. Call `awesomate_support {action:'faq', q:'<their question>'}`.
36
- 2. Answer FROM the returned entries, in your own plain words. Cite nothing
37
- the FAQ and tools didn't say no invented policy, no guessed pricing.
38
- 3. If the FAQ misses or only partially answers, call
39
- `awesomate_support {action:'help_docs', q:'<topic>'}` a live search of
40
- the full Help Centre articles. Answer from the excerpts and give the
41
- article's URL so the user can read the full steps.
42
- 4. If neither answers it: "I don't have a confirmed answer for that. Want
43
- me to ask the Awesomate team? I'll draft a support ticket for you to
44
- approve first." Then go to §2 if they say yes. (An empty help_docs
45
- result can also mean the search is temporarily down — the response's
46
- help_centre_url is always safe to offer for browsing.)
35
+ 1. Call `awesomate_support {action:'ask', q:'<their question>'}`. This is
36
+ Awesomate's own Knowledge Base answering from Awesomate's own content,
37
+ with numbered citations. It works on every plan.
38
+ 2. Read the response's `source` and `degraded` fields before you answer —
39
+ they are the difference between three very different situations:
40
+
41
+ - `source:'knowledge_base'`, `no_answer:false` a grounded answer.
42
+ Relay it in your own plain words and give the citation URLs.
43
+ - `source:'knowledge_base'`, `no_answer:true` the grounding gate
44
+ declined, because the answer is not in our content. **Do not fill the
45
+ gap yourself.** Offer a ticket (§2).
46
+ - `degraded:true` the Knowledge Base was unavailable or is not
47
+ switched on, and you are looking at keyword matches from `faq` and
48
+ `docs` instead. Useful, but **never present them as a verified
49
+ answer**, and say the search was limited if it matters.
50
+
51
+ 3. Only if `ask` came back thin do you need `faq` or `help_docs` directly.
52
+ Both remain available and behave as before, and `help_centre_url` in
53
+ any response is always safe to offer for browsing.
54
+ 4. Cite nothing the tools didn't say. No invented policy, no guessed
55
+ pricing, no remembered plan limits.
47
56
 
48
57
  ## 2. Ticket flow — talking to a human
49
58
 
50
59
  When the user wants a human, or §1 came up empty:
51
60
 
61
+ **First, what is this ticket about?** Access splits by SUBJECT, not by
62
+ plan, and you must pass a `category` naming it. `Other` is refused.
63
+
64
+ | The problem is | category | Who can raise it |
65
+ |---|---|---|
66
+ | Our platform is broken: site down, n8n unreachable, provisioning or SSL failed, the hub is erroring | `Infrastructure fault` | **every plan** |
67
+ | They cannot connect or use Claude: pairing fails, the token is rejected, tools error | `Claude/MCP connectivity` | **every plan** |
68
+ | Money: invoices, plans, credits, refunds | `Account/billing` | **every plan** |
69
+ | Help USING n8n, their website, or the Knowledge Base | `Workflow not running`, `Workflow error`, `New automation request`, `How-to question` | **Support Plus and above** |
70
+
71
+ Choose the category that is actually true. If a product-help ticket comes
72
+ back `403 upgrade_required`, answer from §1 instead and relay the upgrade
73
+ honestly — **never relabel a workflow question as an outage to get it
74
+ through.** That is lying to your own support team about the state of the
75
+ platform, and it puts a false fault report in front of an engineer.
76
+
52
77
  1. Draft the ticket in the USER'S words — their description of the problem,
53
78
  what they were trying to do, what happened. Add context you observed
54
79
  (which site/app, what you already tried) in one short paragraph. No
55
80
  stack traces, no jargon dumps.
56
81
  2. Show the full draft (subject + message) and ask plainly: "Send this to
57
82
  the Awesomate team?" NEVER call `create_ticket` without an explicit yes.
58
- 3. On yes: `awesomate_support {action:'create_ticket', subject, message}`.
59
- Confirm: "Sent. The team replies by email, usually within a business
60
- day."
83
+ 3. On yes: `awesomate_support {action:'create_ticket', subject, message,
84
+ category}`. Confirm: "Sent. The team replies by email, usually within a
85
+ business day."
61
86
  4. Later, "any update on my ticket?" →
62
87
  `awesomate_support {action:'list_tickets'}` and report each ticket's
63
88
  status in one line.