@awesomate/hosting-mcp 0.38.0 → 0.39.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
@@ -41179,7 +41179,9 @@ var TITLES = {
41179
41179
  awesomate_knowledge_collections: "Manage knowledge collections",
41180
41180
  awesomate_knowledge_faq: "Manage FAQs",
41181
41181
  awesomate_knowledge_people: "Manage knowledge entities",
41182
- awesomate_knowledge_data: "Query knowledge data warehouse"
41182
+ awesomate_knowledge_data: "Query knowledge data warehouse",
41183
+ awesomate_crm_metrics: "List contact list metrics",
41184
+ awesomate_crm_query: "Query contact list metrics"
41183
41185
  };
41184
41186
  function annotate(name, kind) {
41185
41187
  const title = TITLES[name];
@@ -42629,6 +42631,59 @@ server.registerTool(
42629
42631
  }
42630
42632
  }
42631
42633
  );
42634
+ server.registerTool(
42635
+ "awesomate_crm_metrics",
42636
+ {
42637
+ annotations: annotate("awesomate_crm_metrics", READ_ONLY),
42638
+ 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.",
42639
+ inputSchema: { action: external_exports.enum(["metrics", "datasets"]) }
42640
+ },
42641
+ async ({ action }) => {
42642
+ try {
42643
+ return textResult(await hubGet(requireConfig(), `/api/my-crm/v1/data/${action}`));
42644
+ } catch (err) {
42645
+ return errorResult(err);
42646
+ }
42647
+ }
42648
+ );
42649
+ server.registerTool(
42650
+ "awesomate_crm_query",
42651
+ {
42652
+ annotations: annotate("awesomate_crm_query", READ_ONLY),
42653
+ 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.",
42654
+ inputSchema: {
42655
+ action: external_exports.enum(["series", "query"]),
42656
+ metric: external_exports.string().max(80).optional().describe("series: 'people' or a measure key"),
42657
+ agg: external_exports.enum(["sum", "avg", "min", "max", "count"]).optional().describe("series: how to aggregate a measure (default sum)"),
42658
+ grain: external_exports.enum(["day", "week", "month", "quarter", "year", "fy"]).optional(),
42659
+ from: external_exports.string().max(10).optional(),
42660
+ to: external_exports.string().max(10).optional(),
42661
+ range: external_exports.string().max(20).optional().describe("a preset instead of from/to"),
42662
+ compare: external_exports.enum(["previous"]).optional(),
42663
+ anchor: external_exports.string().max(80).optional().describe("the date a person counts by: created_at or a date field key"),
42664
+ tz: external_exports.string().max(64).optional().describe("IANA time zone; default Australia/Sydney"),
42665
+ measures: external_exports.array(external_exports.object({ column: external_exports.string().max(80), agg: external_exports.string().max(10).optional() })).max(8).optional(),
42666
+ dimensions: external_exports.array(external_exports.string().max(80)).max(6).optional(),
42667
+ 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(),
42668
+ 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(),
42669
+ order_by: external_exports.array(external_exports.object({ field: external_exports.string().max(80), dir: external_exports.enum(["asc", "desc"]).optional() })).max(4).optional(),
42670
+ limit: external_exports.number().int().min(1).max(2e3).optional()
42671
+ }
42672
+ },
42673
+ async ({ action, metric, agg, grain, from, to, range, compare, anchor, tz, measures, dimensions, filters, time: time3, order_by, limit }) => {
42674
+ try {
42675
+ const cfg = requireConfig();
42676
+ if (action === "series") {
42677
+ const qs = new URLSearchParams();
42678
+ for (const [k, v] of Object.entries({ metric, agg, grain, from, to, range, compare, anchor, tz })) if (v) qs.set(k, String(v));
42679
+ return textResult(await hubGet(cfg, `/api/my-crm/v1/data/series?${qs}`));
42680
+ }
42681
+ return textResult(await hubPost(cfg, "/api/my-crm/v1/data/query", { measures, dimensions, filters, time: time3, order_by, limit }));
42682
+ } catch (err) {
42683
+ return errorResult(err);
42684
+ }
42685
+ }
42686
+ );
42632
42687
  server.registerTool(
42633
42688
  "awesomate_n8n_kpis",
42634
42689
  {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@awesomate/hosting-mcp",
3
- "version": "0.38.0",
3
+ "version": "0.39.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": [
@@ -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