@awesomate/hosting-mcp 0.38.0 → 0.40.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
@@ -41138,6 +41138,7 @@ var TITLES = {
41138
41138
  awesomate_wp_post: "Manage WordPress posts",
41139
41139
  awesomate_wp_media_import: "Import media to WordPress",
41140
41140
  awesomate_wp_settings: "Change WordPress settings",
41141
+ awesomate_elementor_mcp_connect: "Connect Elementor MCP",
41141
41142
  awesomate_app_create: "Create app",
41142
41143
  awesomate_app_get: "Get app details",
41143
41144
  awesomate_app_scaffold: "Get app scaffold guide",
@@ -41179,7 +41180,9 @@ var TITLES = {
41179
41180
  awesomate_knowledge_collections: "Manage knowledge collections",
41180
41181
  awesomate_knowledge_faq: "Manage FAQs",
41181
41182
  awesomate_knowledge_people: "Manage knowledge entities",
41182
- awesomate_knowledge_data: "Query knowledge data warehouse"
41183
+ awesomate_knowledge_data: "Query knowledge data warehouse",
41184
+ awesomate_crm_metrics: "List contact list metrics",
41185
+ awesomate_crm_query: "Query contact list metrics"
41183
41186
  };
41184
41187
  function annotate(name, kind) {
41185
41188
  const title = TITLES[name];
@@ -41772,6 +41775,23 @@ server.registerTool(
41772
41775
  }
41773
41776
  }
41774
41777
  );
41778
+ server.registerTool(
41779
+ "awesomate_elementor_mcp_connect",
41780
+ {
41781
+ annotations: annotate("awesomate_elementor_mcp_connect", DESTRUCTIVE),
41782
+ description: "Connect this Claude Code to Elementor's OWN MCP server on one of the user's WordPress sites, so pages can be built as native, editable Elementor structure instead of raw HTML. Use it when a site uses Elementor (hello-elementor theme or the elementor plugin) and the user wants pages built or edited. One call does what the Elementor > Elementor MCP page does: installs or updates Elementor to 4.3+, turns on the Editor V4 (Atomic) experiments, enables MCP access, and mints a WordPress application password for the user's own administrator. The response carries `claudeCodeCommand` (a `claude mcp add --transport http \u2026` line) and `mcpServersConfig` (the mcp-remote JSON shape): run the command for the user, or write the config, then tell them to restart Claude Code; the Elementor tools then appear as a separate MCP server. The application password is shown ONCE and Awesomate does not keep it: never paste it into chat or a file other than the MCP config, and tell the user it is revocable under Users > Profile > Application Passwords. Support Plus+ (403 upgrade_required on Essentials: an Essentials owner does the same in wp-admin under Elementor > Elementor MCP). The Elementor MCP builds Editor V4 Atomic pages only, and Pro elements need an Elementor Pro plan on the site. Snapshot first with awesomate_snapshot_site if this is the session's first change to a live site.",
41783
+ inputSchema: {
41784
+ domain: external_exports.string().min(3).max(253).describe("The site domain, e.g. mybusiness.awesomate.site")
41785
+ }
41786
+ },
41787
+ async ({ domain }) => {
41788
+ try {
41789
+ return textResult(await hubPost(requireConfig(), `/api/client-hosting/sites/${encodeURIComponent(domain.toLowerCase())}/elementor-mcp`, {}));
41790
+ } catch (err) {
41791
+ return errorResult(err);
41792
+ }
41793
+ }
41794
+ );
41775
41795
  server.registerTool(
41776
41796
  "awesomate_uninstall_site",
41777
41797
  {
@@ -42629,6 +42649,59 @@ server.registerTool(
42629
42649
  }
42630
42650
  }
42631
42651
  );
42652
+ server.registerTool(
42653
+ "awesomate_crm_metrics",
42654
+ {
42655
+ annotations: annotate("awesomate_crm_metrics", READ_ONLY),
42656
+ description: "What the client's own contact list (Contacts & email in the hub) can answer with numbers. action 'metrics' \u2014 the measures: people (a count) plus every number field the owner has named, with unit and synonyms. 'datasets' \u2014 the one dataset, 'contact', with its columns by kind: measures, dimensions (choice, yes/no and text fields to group by) and anchors (date fields a person can be counted by, plus created_at). Read this FIRST; awesomate_crm_query only accepts names listed here. Sensitive fields never appear. Read-only; counts and sums only, never a person's details.",
42657
+ inputSchema: { action: external_exports.enum(["metrics", "datasets"]) }
42658
+ },
42659
+ async ({ action }) => {
42660
+ try {
42661
+ return textResult(await hubGet(requireConfig(), `/api/my-crm/v1/data/${action}`));
42662
+ } catch (err) {
42663
+ return errorResult(err);
42664
+ }
42665
+ }
42666
+ );
42667
+ server.registerTool(
42668
+ "awesomate_crm_query",
42669
+ {
42670
+ annotations: annotate("awesomate_crm_query", READ_ONLY),
42671
+ description: "Numbers from the client's contact list, read-only, never a row of personal details. action 'series' {metric, grain?, from?, to?, range?, compare?, anchor?, tz?} \u2014 one metric over time (people added per month, spend per quarter), with labels, points and a previous-period comparison when compare='previous'. action 'query' {measures:[{column, agg}], dimensions?, filters?, time?, order_by?, limit?} \u2014 grouped numbers (people by plan, sum of spend by suburb). Column names MUST come from awesomate_crm_metrics 'datasets'; an unknown name is refused, never guessed. Dates are YYYY-MM-DD; range presets: 7d, 30d, 90d, 12m, mtd, qtd, ytd, fytd, this_month, last_month, this_fy, last_fy (financial year starts 1 July). The response's applied.defaults says what was assumed; repeat those to the user.",
42672
+ inputSchema: {
42673
+ action: external_exports.enum(["series", "query"]),
42674
+ metric: external_exports.string().max(80).optional().describe("series: 'people' or a measure key"),
42675
+ agg: external_exports.enum(["sum", "avg", "min", "max", "count"]).optional().describe("series: how to aggregate a measure (default sum)"),
42676
+ grain: external_exports.enum(["day", "week", "month", "quarter", "year", "fy"]).optional(),
42677
+ from: external_exports.string().max(10).optional(),
42678
+ to: external_exports.string().max(10).optional(),
42679
+ range: external_exports.string().max(20).optional().describe("a preset instead of from/to"),
42680
+ compare: external_exports.enum(["previous"]).optional(),
42681
+ anchor: external_exports.string().max(80).optional().describe("the date a person counts by: created_at or a date field key"),
42682
+ tz: external_exports.string().max(64).optional().describe("IANA time zone; default Australia/Sydney"),
42683
+ measures: external_exports.array(external_exports.object({ column: external_exports.string().max(80), agg: external_exports.string().max(10).optional() })).max(8).optional(),
42684
+ dimensions: external_exports.array(external_exports.string().max(80)).max(6).optional(),
42685
+ filters: external_exports.array(external_exports.object({ column: external_exports.string().max(80), op: external_exports.string().max(5).optional(), value: external_exports.unknown() })).max(10).optional(),
42686
+ time: external_exports.object({ grain: external_exports.string().max(10).optional(), preset: external_exports.string().max(20).optional(), from: external_exports.string().max(10).optional(), to: external_exports.string().max(10).optional(), anchor: external_exports.string().max(80).optional(), tz: external_exports.string().max(64).optional() }).optional(),
42687
+ order_by: external_exports.array(external_exports.object({ field: external_exports.string().max(80), dir: external_exports.enum(["asc", "desc"]).optional() })).max(4).optional(),
42688
+ limit: external_exports.number().int().min(1).max(2e3).optional()
42689
+ }
42690
+ },
42691
+ async ({ action, metric, agg, grain, from, to, range, compare, anchor, tz, measures, dimensions, filters, time: time3, order_by, limit }) => {
42692
+ try {
42693
+ const cfg = requireConfig();
42694
+ if (action === "series") {
42695
+ const qs = new URLSearchParams();
42696
+ for (const [k, v] of Object.entries({ metric, agg, grain, from, to, range, compare, anchor, tz })) if (v) qs.set(k, String(v));
42697
+ return textResult(await hubGet(cfg, `/api/my-crm/v1/data/series?${qs}`));
42698
+ }
42699
+ return textResult(await hubPost(cfg, "/api/my-crm/v1/data/query", { measures, dimensions, filters, time: time3, order_by, limit }));
42700
+ } catch (err) {
42701
+ return errorResult(err);
42702
+ }
42703
+ }
42704
+ );
42632
42705
  server.registerTool(
42633
42706
  "awesomate_n8n_kpis",
42634
42707
  {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@awesomate/hosting-mcp",
3
- "version": "0.38.0",
3
+ "version": "0.40.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",
@@ -1,5 +1,13 @@
1
1
  {
2
2
  "versions": [
3
+ {
4
+ "version": "0.39.0",
5
+ "highlights": [
6
+ "Ask Claude for numbers from your contact list: how many people joined each month, spend by plan, customers by suburb (awesomate_crm_metrics, awesomate_crm_query). It only ever gets counts and sums, never anyone's details",
7
+ "The fields you named on an import are what it can measure and group by; anything you marked sensitive is left out entirely",
8
+ "Every answer says what it assumed (the date range, the time zone, which date a person counts by) so you can check it"
9
+ ]
10
+ },
3
11
  {
4
12
  "version": "0.38.0",
5
13
  "highlights": [
@@ -104,8 +104,41 @@ not a failed build. Confirm over plain HTTP, say the certificate is still
104
104
  issuing, and poll until HTTPS answers. Never delete and recreate the site or
105
105
  start debugging DNS on the strength of it.
106
106
 
107
- New sites come up on the host's default theme (`hello-elementor`). A fresh
108
- install is not evidence that a custom theme shipped.
107
+ New sites on Support Plus, Pro and Embedded come up with free Elementor and
108
+ the Hello Elementor theme (plus Jetpack, Yoast, Site Kit, Contact Form 7,
109
+ WP Super Cache and Limit Login Attempts); an Essentials account installs
110
+ WordPress from the hub's Install WordPress card, which adds the same
111
+ Elementor and theme. A fresh install is not evidence that a custom theme
112
+ shipped.
113
+
114
+ ## Elementor sites: connect Elementor's own MCP
115
+
116
+ Elementor 4.3+ ships its own MCP server, served from the site itself. When a
117
+ site uses Elementor (the `hello-elementor` theme or the `elementor` plugin
118
+ is active) and the user wants pages built or changed, connect it BEFORE
119
+ hand-writing page HTML: pages then land as native, editable Elementor
120
+ structure the owner can keep working on in the Elementor editor.
121
+
122
+ 1. Snapshot the site if this is the session's first change to it.
123
+ 2. Call `awesomate_elementor_mcp_connect` with the domain (Support Plus+).
124
+ It installs or updates Elementor, turns on the Editor V4 experiments,
125
+ enables MCP access and mints a WordPress application password for the
126
+ user's own administrator.
127
+ 3. Run the returned `claudeCodeCommand` for the user (it is a
128
+ `claude mcp add --transport http …` line), or write `mcpServersConfig`
129
+ into the project's `.mcp.json` if they prefer. Then ask them to restart
130
+ Claude Code; the Elementor tools appear as their own MCP server.
131
+ 4. Say plainly where the key lives and how to revoke it: Users > Profile >
132
+ Application Passwords in wp-admin, or Elementor > Elementor MCP > Turn
133
+ off MCP access for the whole thing. Never repeat the password in chat.
134
+
135
+ What to expect once connected: it builds **Editor V4 (Atomic) pages and
136
+ layouts only**. Classic Elementor pages (most migrated sites) stay editable
137
+ in the Elementor editor but not through the MCP. Forms, popups, loops,
138
+ headers and footers need an Elementor Pro plan on that site; the hub does
139
+ not supply one. On Essentials the tool returns 403 `upgrade_required`; the
140
+ owner can do the identical thing in wp-admin under Elementor > Elementor
141
+ MCP, so say that rather than stopping.
109
142
 
110
143
  ## 0a. Tool map — reach for it when
111
144
 
@@ -130,6 +163,7 @@ install is not evidence that a custom theme shipped.
130
163
  | `awesomate_site_staging_create` / `_promote` / `_discard` | Private full copy on awesomate.dev to review changes; publish or throw away (§5a) |
131
164
  | `awesomate_run_wp_cli` | The 10 allowlisted WP-CLI verbs only (see below) — plugin/theme list, activate, install-by-slug, update; `cache flush`; `option get`; `post`/`media`/`menu`/`comment`/`user list` |
132
165
  | `awesomate_wp_settings` | Site title, tagline, timezone, date/time format, posts-per-page, search-engine visibility — THE path for any multi-word value, and it flushes the cache (Support Plus+) |
166
+ | `awesomate_elementor_mcp_connect` | A site uses Elementor and pages need building or editing: connects this Claude Code to Elementor's own MCP server on that site (see **Elementor sites** above). Mints an application password; Support Plus+ |
133
167
  | `awesomate_wp_post` | Create/update/read a WP post or page — drafts by default (writes are Support Plus+) |
134
168
  | `awesomate_wp_media_import` | Pull an image/file into the WP media library from an https URL (Support Plus+) |
135
169
  | `awesomate_uninstall_site` | Remove a WordPress install (destructive — confirm explicitly) |
@@ -205,7 +239,7 @@ Support Plus/Pro unlocks (§4), and link the hub billing page.
205
239
  On Essentials, a PAT is read-and-advise: `awesomate_site_create`,
206
240
  `awesomate_domain_add` and every WordPress write (`awesomate_wp_post`,
207
241
  `awesomate_wp_media_import`, `awesomate_wp_settings`,
208
- `awesomate_run_wp_cli`) return **403 `upgrade_required`** — the server
242
+ `awesomate_run_wp_cli`, `awesomate_elementor_mcp_connect`) return **403 `upgrade_required`** — the server
209
243
  enforces it, so no phrasing gets past it. That site and that domain are
210
244
  real; they are created and changed **in the hub UI at
211
245
  hub.awesomate.ai/sites**, by the user, with deliberate clicks. Say exactly
@@ -27,13 +27,18 @@ tool call at a time, so the key never reaches this machine.
27
27
 
28
28
  `awesomate_n8n_agents {action:"status"}` FIRST:
29
29
 
30
- - `keyConfigured: false` → walk them through `setup.steps` in plain words:
31
- in their n8n, **Settings → Instance-level MCP** → switch on → copy the MCP
32
- API key → paste it into **n8n Agents access** at
33
- hub.awesomate.ai/n8n/settings → Test & Save. Owner or admin only. Then
34
- re-check status. Never ask them to paste the key into this chat.
35
- - `code: mcp_disabled` → MCP was switched off again in their n8n.
36
- - `code: mcp_key_rejected` → the key was rotated; paste a fresh one.
30
+ - `keyConfigured: false` → send them to **"Set it up for me"** under **n8n
31
+ Agents access** at hub.awesomate.ai/n8n/settings (`setup.steps`). One
32
+ click: the hub switches on their n8n's MCP (a restart of about 20 seconds
33
+ if it was off), connects their owner key and emails them. There is nothing
34
+ to copy. **You cannot do this step for them**: only that click may connect
35
+ a key, so never offer to. When they say it's done, re-check status. If the
36
+ button can't do it on their instance, its message gives the by-hand route
37
+ (`setup.manualSteps`). Never ask them to paste a key into this chat.
38
+ - `code: mcp_disabled` → MCP was switched off in their n8n. "Set it up for
39
+ me" switches it back on.
40
+ - `code: mcp_key_rejected` → the hub normally re-reads a regenerated key by
41
+ itself. If this still shows, send them to "Set it up for me" again.
37
42
  - `missingTools` present → their n8n has no Agents module. Do not try to
38
43
  work around it; that's an Awesomate support ask.
39
44
  - `ready: true` → go. Building, testing and publishing agents work on **every