@awesomate/hosting-mcp 0.50.0 → 0.51.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
@@ -42887,13 +42887,14 @@ server.registerTool(
42887
42887
  "awesomate_crm_apps",
42888
42888
  {
42889
42889
  annotations: annotate("awesomate_crm_apps", MUTATING),
42890
- description: "The account's own apps (a client portal, a members' app) whose users sign in by email link and then read and change only what each kind's access rules allow them (awesomate_crm_kinds set_access). Pro and above to create or change; reading is every plan. action 'list': each app with its publishable key, allowed origins, sign-up mode and default role. action 'create' {name, allowed_origins, sign_up?, default_role?}: name appears in the sign-in email; allowed_origins are the exact origins the app runs on (https://portal.example.com; http://localhost:5173 for development; capacitor://localhost for the mobile shell), with no path and no wildcard; sign_up 'invite' (default: only people added with awesomate_crm_app_users) or 'open' (anyone who uses the email link gets the default role). action 'update' {app_id, ...any of those, status?}: status 'disabled' signs everyone out of that app and stops its server keys. The publishable key (pk_...) is not a secret: it goes in the app's browser code, with @awesomate/sdk. Never put the account's own token (amt_pat_...) in browser code. Confirm origins and sign-up mode with the user: open sign-up lets strangers in under the default role. SERVER KEYS (ak_...), for the app's own server or an n8n workflow, used with @awesomate/sdk createClient({ token: <key> }) or as a Bearer token on https://hub.awesomate.ai/api/sdk/v1/server: action 'keys' {app_id} lists them (never the secrets); 'create_key' {app_id, name, access: 'read'|'write', kinds?} makes one (kinds leaves it to those kinds, 'contact' to read contacts; leave it out for every kind) and returns the key ONCE: put it straight into the app server's environment (awesomate_app_set_env for an app hosted with us, or the n8n credential) and never into browser code, a repository or a reply to the user; 'revoke_key' {app_id, key_id} stops it at once. A key reads every attribute of an app kind, contacts only as you read them, and writes need Support Plus and above. Prefer the narrowest key: read unless the server writes, and only the kinds it uses.",
42890
+ description: "The account's own apps (a client portal, a members' app) whose users sign in by email link and then read and change only what each kind's access rules allow them (awesomate_crm_kinds set_access). Pro and above to create or change; reading is every plan. action 'list': each app with its publishable key, allowed origins, sign-up mode and default role. action 'create' {name, allowed_origins, sign_up?, default_role?}: name appears in the sign-in email; allowed_origins are the exact origins the app runs on (https://portal.example.com; http://localhost:5173 for development; capacitor://localhost for the mobile shell), with no path and no wildcard; sign_up 'invite' (default: only people added with awesomate_crm_app_users) or 'open' (anyone who uses the email link gets the default role). action 'update' {app_id, ...any of those, status?}: status 'disabled' signs everyone out of that app and stops its server keys. The publishable key (pk_...) is not a secret: it goes in the app's browser code, with @awesomate/sdk. Never put the account's own token (amt_pat_...) in browser code. Confirm origins and sign-up mode with the user: open sign-up lets strangers in under the default role. action 'customer_lookup' {app_id, roles?}: which roles in that app may look customers up (name, email and phone, by search or id, with @awesomate/sdk lookupCustomers) and so put a record under any customer, not only themselves. Without roles it reads the setting; roles ['staff'] switches it on for staff; [] switches it off. Never the app's default role (the one customers sign in with): that would show every customer to every other customer, and the hub refuses it. Off until the owner asks for it. SERVER KEYS (ak_...), for the app's own server or an n8n workflow, used with @awesomate/sdk createClient({ token: <key> }) or as a Bearer token on https://hub.awesomate.ai/api/sdk/v1/server: action 'keys' {app_id} lists them (never the secrets); 'create_key' {app_id, name, access: 'read'|'write', kinds?} makes one (kinds leaves it to those kinds, 'contact' to read contacts; leave it out for every kind) and returns the key ONCE: put it straight into the app server's environment (awesomate_app_set_env for an app hosted with us, or the n8n credential) and never into browser code, a repository or a reply to the user; 'revoke_key' {app_id, key_id} stops it at once. A key reads every attribute of an app kind, contacts only as you read them, and writes need Support Plus and above. Prefer the narrowest key: read unless the server writes, and only the kinds it uses.",
42891
42891
  inputSchema: {
42892
- action: external_exports.enum(["list", "create", "update", "keys", "create_key", "revoke_key"]),
42893
- app_id: external_exports.string().max(48).optional().describe("update, keys, create_key, revoke_key: the app"),
42892
+ action: external_exports.enum(["list", "create", "update", "keys", "create_key", "revoke_key", "customer_lookup"]),
42893
+ app_id: external_exports.string().max(48).optional().describe("update, keys, create_key, revoke_key, customer_lookup: the app"),
42894
42894
  access: external_exports.enum(["read", "write"]).optional().describe("create_key"),
42895
42895
  kinds: external_exports.array(external_exports.string().max(41)).max(50).optional().describe("create_key: only these kinds (contact to read contacts); leave out for every kind"),
42896
42896
  key_id: external_exports.string().max(64).optional().describe("revoke_key: from keys"),
42897
+ roles: external_exports.array(external_exports.string().max(31)).max(10).optional().describe("customer_lookup: the roles that may look customers up; [] switches it off; leave out to read"),
42897
42898
  name: external_exports.string().max(80).optional(),
42898
42899
  allowed_origins: external_exports.array(external_exports.string().max(200)).max(10).optional(),
42899
42900
  sign_up: external_exports.enum(["invite", "open"]).optional(),
@@ -42901,10 +42902,15 @@ server.registerTool(
42901
42902
  status: external_exports.enum(["active", "disabled"]).optional().describe("update only")
42902
42903
  }
42903
42904
  },
42904
- async ({ action, app_id, name, allowed_origins, sign_up, default_role, status, access, kinds, key_id }) => {
42905
+ async ({ action, app_id, name, allowed_origins, sign_up, default_role, status, access, kinds, key_id, roles }) => {
42905
42906
  try {
42906
42907
  const cfg = requireConfig();
42907
42908
  if (action === "list") return textResult(await hubGet(cfg, "/api/my-crm/v1/apps"));
42909
+ if (action === "customer_lookup") {
42910
+ if (!app_id) return errorResult(new Error("customer_lookup needs app_id, from list."));
42911
+ const path = `/api/my-crm/v1/apps/${encodeURIComponent(app_id)}/customer-lookup`;
42912
+ return textResult(roles === void 0 ? await hubGet(cfg, path) : await hubPut(cfg, path, { roles }));
42913
+ }
42908
42914
  if (action === "keys" || action === "create_key" || action === "revoke_key") {
42909
42915
  if (!app_id) return errorResult(new Error(`${action} needs app_id, from list.`));
42910
42916
  const keys = `/api/my-crm/v1/apps/${encodeURIComponent(app_id)}/keys`;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@awesomate/hosting-mcp",
3
- "version": "0.50.0",
3
+ "version": "0.51.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,12 @@
1
1
  {
2
2
  "versions": [
3
+ {
4
+ "version": "0.51.0",
5
+ "highlights": [
6
+ "Your app's staff can now find a customer and put a job under them, straight from the portal. Switch it on per app for the roles that need it (awesomate_crm_apps, customer_lookup). Pro and above",
7
+ "They see name, email and phone only, never a field you marked sensitive, and never the role your customers sign in with: customers still see only themselves"
8
+ ]
9
+ },
3
10
  {
4
11
  "version": "0.50.0",
5
12
  "highlights": [
@@ -63,9 +63,11 @@ message: {"read": {"member": "linked:job.customer", "staff": "all"}, "write": {"
63
63
  a mistake in the page can never show anyone more than their role allows.
64
64
  - A person can only link a record to something they can see: a customer can point a job at their
65
65
  own contact, never someone else's.
66
- - **Known gap:** staff cannot yet put a job under a customer from the portal (they can only link
67
- to contacts they can see, and app people do not read the contact list). Until that lands, the
68
- owner creates or reassigns jobs for customers through Claude (`awesomate_crm_write`).
66
+ - **Staff putting a job under a customer** needs one more switch, per app: app people read no
67
+ contacts but their own, so by default staff can only link to themselves. Ask the owner, then
68
+ `awesomate_crm_apps {action:'customer_lookup', app_id, roles:['staff']}`. Those roles can then
69
+ search customers (name, email, phone; never a sensitive field) and link a record to any of
70
+ them. Never the app's default role (the one customers sign in with); the hub refuses it.
69
71
 
70
72
  ## 4. Create the app and add people
71
73
 
@@ -84,7 +86,7 @@ loads from a CDN, or from npm in a Vite app:
84
86
 
85
87
  ```html
86
88
  <script type="module">
87
- import { createAppClient } from 'https://cdn.jsdelivr.net/npm/@awesomate/sdk@0.6/+esm';
89
+ import { createAppClient } from 'https://cdn.jsdelivr.net/npm/@awesomate/sdk@0.8/+esm';
88
90
  const app = createAppClient({ publishableKey: 'pk_...' });
89
91
 
90
92
  // The email link signs them in by itself (also when it opens in a tab already on this page).
@@ -100,6 +102,10 @@ await app.auth.signInWithLink(email, { redirectTo: location.href });
100
102
  const stop = app.live('job', { orderBy: ['created_at', 'desc'], limit: 100 }, (rows) => renderJobs(rows));
101
103
  await app.write('job', { title: 'Leaking tap', status: 'open' }, { links: { customer: user.contact_id } });
102
104
  await app.write('message', { body }, { links: { job: jobId } });
105
+
106
+ // Staff, once customer_lookup is on for their role: find a customer, file a job for them.
107
+ const [customer] = await app.lookupCustomers({ q: 'jo' });
108
+ await app.write('job', { title: 'Gutters', status: 'open' }, { links: { customer: customer.id } });
103
109
  </script>
104
110
  ```
105
111