@awesomate/hosting-mcp 0.56.0 → 0.58.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
@@ -41219,6 +41219,7 @@ var TITLES = {
41219
41219
  awesomate_crm_apps: "Set up sign-in for your own apps",
41220
41220
  awesomate_crm_app_users: "Manage the people who sign in to your apps",
41221
41221
  awesomate_crm_assistant: "Set up the AI in your app's conversations",
41222
+ awesomate_crm_notifications: "Email people when someone replies in your app",
41222
41223
  awesomate_crm_hooks: "Send record changes to your n8n",
41223
41224
  awesomate_crm_email: "Draft an email to a contact list"
41224
41225
  };
@@ -42415,7 +42416,7 @@ server.registerTool(
42415
42416
  removeCollectionIds: external_exports.array(external_exports.string().max(64)).max(16).optional().describe("move: remove from these collections"),
42416
42417
  sourceIds: external_exports.array(external_exports.string().max(300)).max(200).optional().describe("set_visibility/move: several sources at once"),
42417
42418
  confirm: external_exports.string().max(64).optional().describe("set_visibility to 'public' only: the account slug, after the user agreed in so many words"),
42418
- url: external_exports.string().url().optional().describe("add: one public page / blog post / YouTube link"),
42419
+ url: external_exports.string().url().optional().describe("add: one public page or blog post. A YouTube or other video link is not transcribed: on Pro it is read as a page (title and description), below Pro it is refused. To transcribe a recording, upload the file"),
42419
42420
  sitemap: external_exports.string().url().optional().describe("add: sitemap.xml URL \u2014 ingests every listed page"),
42420
42421
  since: external_exports.string().optional().describe("add+sitemap only: skip entries with lastmod older than this ISO date"),
42421
42422
  sourceId: external_exports.string().optional().describe("remove only"),
@@ -43042,6 +43043,37 @@ server.registerTool(
43042
43043
  }
43043
43044
  }
43044
43045
  );
43046
+ server.registerTool(
43047
+ "awesomate_crm_notifications",
43048
+ {
43049
+ annotations: annotate("awesomate_crm_notifications", MUTATING),
43050
+ description: "Reply emails for an app's conversations (a portal's messages on a job): when someone writes, the people on the other side who are not in the app at that moment get one email, at most once an hour per conversation. A customer writing emails the team (app people in staff_roles); the team, the assistant, the account or an n8n workflow writing emails that conversation's customer. Never the author, never someone switched off. Sent from the app's name, with the message quoted and a link to the app's first https address. They are system emails, like a ticket reply or a receipt, so they carry no unsubscribe link: the owner switches them on or off per app. Off until switched on; switching on starts from now, so nothing said earlier is emailed. Email only for now. Pro and above to change; reading is every plan. action 'get' {app_id}: the setting, how many have been sent, and the last error. action 'set' {app_id, notifications}: notifications is {enabled, thread_link (the message kind's link to the conversation, e.g. 'job'), message_kind? ('message'), body_attribute? ('body'), title_attribute? ('title', on the conversation record, used in the subject), customer_link? ('customer', the conversation record's link to the customer contact), staff_roles? (['owner', 'staff'])}. Turning it on is refused with every problem listed while the kinds don't fit. Confirm with the owner before switching it on: it emails their customers.",
43051
+ inputSchema: {
43052
+ action: external_exports.enum(["get", "set"]),
43053
+ app_id: external_exports.string().max(48).describe("the app, from awesomate_crm_apps list"),
43054
+ notifications: external_exports.object({
43055
+ enabled: external_exports.boolean(),
43056
+ thread_link: external_exports.string().max(41),
43057
+ message_kind: external_exports.string().max(41).optional(),
43058
+ body_attribute: external_exports.string().max(41).optional(),
43059
+ title_attribute: external_exports.string().max(41).optional(),
43060
+ customer_link: external_exports.string().max(41).optional(),
43061
+ staff_roles: external_exports.array(external_exports.string().max(31)).max(10).optional()
43062
+ }).optional().describe("set: the whole setting; fields left out take their defaults")
43063
+ }
43064
+ },
43065
+ async ({ action, app_id, notifications }) => {
43066
+ try {
43067
+ const cfg = requireConfig();
43068
+ const path = `/api/my-crm/v1/apps/${encodeURIComponent(app_id)}/notifications`;
43069
+ if (action === "get") return textResult(await hubGet(cfg, path));
43070
+ if (!notifications) return errorResult(new Error("set needs notifications: {enabled, thread_link, ...}."));
43071
+ return textResult(await hubPut(cfg, path, { notifications }));
43072
+ } catch (err) {
43073
+ return errorResult(err);
43074
+ }
43075
+ }
43076
+ );
43045
43077
  var crmParam = external_exports.object({
43046
43078
  name: external_exports.string().max(41).describe("lower_case"),
43047
43079
  type: external_exports.enum(["text", "number", "date", "datetime", "boolean", "uuid", "text_list", "number_list"]).optional().describe("default text"),
@@ -43091,21 +43123,26 @@ server.registerTool(
43091
43123
  "awesomate_crm_recipes",
43092
43124
  {
43093
43125
  annotations: annotate("awesomate_crm_recipes", MUTATING),
43094
- description: `Write recipes: an app's own named writes over its kinds, a few steps that always happen together (log a job and its first memo; close a job and archive its booking). Run one with awesomate_crm_write action 'call'. action 'list': the recipes with their params. action 'save' {key, label, description?, params?, steps}: Support Plus and above; saving a key again replaces it. steps (1 to 10) are {op: 'write_record', kind, data?, links?, id?, as?} or {op: 'archive_record', kind, id}, run in order inside one transaction. {"$param": "<name>"} takes a caller's value; {"$step": "<as>"} takes the id an earlier step wrote, so a memo can link to the job made one step before. An id (an update or an archive) must come from a required uuid param or an earlier step, never a literal. Every kind, attribute, link and reference is checked when it is saved and again when it runs. Contacts are not written by recipes. A recipe is data the hub interprets, never code: there is no SQL and no condition logic. Confirm the steps with the user before saving.`,
43126
+ description: `Write recipes: an app's own named writes over its kinds, a few steps that always happen together (log a job and its first memo; close a job and archive its booking). Run one with awesomate_crm_write action 'call'. action 'list': the recipes with their params. action 'save' {key, label, description?, params?, steps}: Support Plus and above; saving a key again replaces it. steps (1 to 10) are {op: 'write_record', kind, data?, links?, id?, as?} or {op: 'archive_record', kind, id}, run in order inside one transaction. {"$param": "<name>"} takes a caller's value; {"$step": "<as>"} takes the id an earlier step wrote, so a memo can link to the job made one step before. An id (an update or an archive) must come from a required uuid param or an earlier step, never a literal. Every kind, attribute, link and reference is checked when it is saved and again when it runs. Contacts are not written by recipes. A recipe is data the hub interprets, never code: there is no SQL and no condition logic. Confirm the steps with the user before saving. action 'run_by' {key, roles}: Pro and above. Lets the people who sign in to the account's apps, in those roles, run this recipe themselves with @awesomate/sdk app.call(key, args): something their write rule would not let them do by hand, such as accept their own quote ({op: 'write_record', kind: 'job', id: {$param: 'job'}, data: {status: 'accepted'}}). The database lets them do exactly the recipe's steps: only $param and $step positions take their values, every other value is fixed, and an existing record must be one they can already read. roles [] closes it again; archiving a recipe closes it too. Editing a recipe that customers can run changes what they can do: say so and confirm with the user before saving it.`,
43095
43127
  inputSchema: {
43096
- action: external_exports.enum(["list", "save", "archive"]),
43097
- key: external_exports.string().max(61).optional().describe("save/archive: lower_case name, not write_record or archive_record"),
43128
+ action: external_exports.enum(["list", "save", "archive", "run_by"]),
43129
+ key: external_exports.string().max(61).optional().describe("save/archive/run_by: lower_case name, not write_record or archive_record"),
43130
+ roles: external_exports.array(external_exports.string().max(31)).max(10).optional().describe("run_by: the app roles (from awesomate_crm_app_users) that may run it; [] for the account only"),
43098
43131
  label: external_exports.string().max(120).optional(),
43099
43132
  description: external_exports.string().max(1e3).optional(),
43100
43133
  params: external_exports.array(crmParam).max(20).optional(),
43101
43134
  steps: external_exports.array(external_exports.record(external_exports.unknown())).min(1).max(10).optional()
43102
43135
  }
43103
43136
  },
43104
- async ({ action, key, label, description, params, steps }) => {
43137
+ async ({ action, key, label, description, params, steps, roles }) => {
43105
43138
  try {
43106
43139
  const cfg = requireConfig();
43107
43140
  if (action === "list") return textResult(await hubGet(cfg, "/api/my-crm/v1/recipes"));
43108
43141
  if (!key) return errorResult(new Error(`${action} needs the key.`));
43142
+ if (action === "run_by") {
43143
+ if (!roles) return errorResult(new Error("run_by needs roles: a list of app roles, or [] to close it."));
43144
+ return textResult(await hubPut(cfg, `/api/my-crm/v1/recipes/${encodeURIComponent(key)}/run-by`, { roles }));
43145
+ }
43109
43146
  if (action === "save") return textResult(await hubPost(cfg, "/api/my-crm/v1/recipes", { key, label, description, params: params ?? [], steps: steps ?? [] }));
43110
43147
  return textResult(await hubDelete(cfg, `/api/my-crm/v1/recipes/${encodeURIComponent(key)}`));
43111
43148
  } catch (err) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@awesomate/hosting-mcp",
3
- "version": "0.56.0",
3
+ "version": "0.58.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,19 @@
1
1
  {
2
2
  "versions": [
3
+ {
4
+ "version": "0.58.0",
5
+ "highlights": [
6
+ "Customers can do one exact thing their access would not let them do by hand, such as accept their own quote: Claude saves it as a recipe and, on your say-so, opens it to a role (awesomate_crm_recipes action 'run_by'). Pro and above",
7
+ "Your app runs it with @awesomate/sdk app.call(). The database lets them change only what the recipe says, and only on records they can already see; archiving the recipe closes it again"
8
+ ]
9
+ },
10
+ {
11
+ "version": "0.57.0",
12
+ "highlights": [
13
+ "Reply emails for your portal's conversations (awesomate_crm_notifications): when your team or Bree replies, the customer gets an email if they aren't in the portal, and when a customer writes, your team does. At most one an hour per conversation",
14
+ "Off until you switch them on, per app. They're system emails, like a ticket reply or a receipt, sent from your app's name with the message quoted and a link back"
15
+ ]
16
+ },
3
17
  {
4
18
  "version": "0.56.0",
5
19
  "highlights": [
@@ -135,6 +135,15 @@ lists the two staff-only kinds it needs (`assistant_thread`, `assistant_draft`)
135
135
  agent (awesomate-knowledge skill) so she can answer questions about the business. Confirm the
136
136
  name and the default mode with the owner: switching it on lets an AI reply to their customers.
137
137
 
138
+ ## 6b. Optional: reply emails
139
+
140
+ `awesomate_crm_notifications {action:'set', app_id, notifications: {enabled: true, thread_link: 'job'}}`
141
+ emails the other side of a conversation when someone writes there and they are not in the app:
142
+ the customer when the team or the assistant replies, the team when a customer writes. At most one
143
+ an hour per conversation, from the app's name, with the message quoted and a link back. They are
144
+ system emails (no unsubscribe link); the owner switches them on or off per app. Ask the owner
145
+ first: it emails their customers.
146
+
138
147
  ## 7. Optional: automation
139
148
 
140
149
  - **Server key** (`awesomate_crm_apps {action:'create_key'}`): for the app's own server or an n8n