@awesomate/hosting-mcp 0.62.0 → 0.64.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/README.md CHANGED
@@ -3,46 +3,33 @@
3
3
  The MCP server that connects an Awesomate customer's own Claude Code to their
4
4
  Awesomate account: WordPress hosting, n8n automations, apps, and Knowledge
5
5
  Base. It authenticates to the Awesomate hub with a scoped Personal Access
6
- Token and ships the nine `awesomate-*` skills that teach Claude how to use it.
6
+ Token and ships the `awesomate-*` skills that teach Claude how to use it.
7
+
8
+ **Docs: https://hub.awesomate.ai/docs/mcp/** (a quickstart, how it works, a guide
9
+ per area, and a reference generated from this server's own `tools/list`). Its
10
+ sister site documents the SDK: https://hub.awesomate.ai/docs/sdk/.
7
11
 
8
12
  ## Install (customers)
9
13
 
10
14
  Don't install this by hand. Open **hub.awesomate.ai/claude** (or the Sites
11
- page) and click **Connect Claude Code** — it gives you a one-time command that:
15
+ page) and click **Connect Claude Code**. It gives you a one-time command that:
12
16
 
13
17
  1. redeems a short-lived setup code for a scoped access token, born on your
14
- machine (`~/.awesomate/credentials.json`, chmod 600 — it never traverses
18
+ machine (`~/.awesomate/credentials.json`, chmod 600; it never traverses
15
19
  the clipboard),
16
20
  2. installs the `awesomate-*` skills into `~/.claude/skills`,
17
21
  3. registers this server in Claude Code (user scope, no token in the
18
- registration — see below).
22
+ registration: see below).
19
23
 
20
24
  Then restart Claude Code and run `awesomate_whoami`.
21
25
 
22
26
  ## What Claude can do with it
23
27
 
24
- Roughly 60 tools across five domains — the authoritative list is whatever
25
- `tools/list` returns from the version you're running (this server self-updates,
26
- so a hand-maintained table here would only ever be stale):
27
-
28
- - **Hosting & WordPress** — sites, domains, DNS checks, uptime, plan limits,
29
- snapshots/rollback, `.dev` staging, allowlisted WP-CLI, structured post/page
30
- writes (drafts first) and media import. Writes need Support Plus+.
31
- - **n8n automations** — full read surface on every plan (workflows, executions
32
- with structural error decode, instance-wide error clusters, KPIs, storage
33
- footprint, node docs); validate → draft → test → promote build loop on
34
- Support Plus+; AI error-analyzer findings on Pro/Embedded.
35
- - **Apps** — scaffold, create, deploy-via-git, env vars, health, app Postgres.
36
- - **Knowledge Base** (every plan; media/OCR Pro+, public widget Support Plus+) — provision, ingest sources, faceted search,
37
- cited answers, agents, people/entities, business-data queries.
38
- - **Account** — context + attention digest (notifications, erroring workflows,
39
- token expiry), plan features, upgrade nudges, support tickets, done-for-you
40
- build requests (two-step credit confirmation; never spends without an
41
- explicit yes).
42
-
28
+ Every tool, with its inputs and whether it reads, changes or can delete, is in
29
+ the generated reference: https://hub.awesomate.ai/docs/mcp/reference/account/.
30
+ The docs build fails if a tool is missing from it, so it cannot fall behind.
43
31
  Every response is stamped `account: <slug>` so a session talking to the wrong
44
- account is impossible to miss. Read tools carry `readOnlyHint`; destructive
45
- ones carry `destructiveHint` and confirm-before-acting instructions.
32
+ account is impossible to miss.
46
33
 
47
34
  ## Security posture
48
35
 
@@ -60,13 +47,13 @@ ones carry `destructiveHint` and confirm-before-acting instructions.
60
47
 
61
48
  The server self-updates: it's registered as unpinned `npx -y`, so every Claude
62
49
  Code restart runs the latest published version. The skill files update via the
63
- `awesomate_skill_update` tool — Claude notices the drift itself (it's stamped
50
+ `awesomate_skill_update` tool. Claude notices the drift itself (it's stamped
64
51
  on every response) and offers the update with what's-new lines from
65
52
  `skill/CHANGELOG.json`, then hands over the exact restart step.
66
53
 
67
54
  ## Manual registration (developers)
68
55
 
69
- Keep `--package=` with the `=` — the claude CLI's global-argv pre-scan chokes
56
+ Keep `--package=` with the `=`: the claude CLI's global-argv pre-scan chokes
70
57
  on a bare `-p`, and the package must be explicit because this package ships
71
58
  two bins:
72
59
 
@@ -78,8 +65,13 @@ claude mcp add awesomate-hosting -- npx -y --package=@awesomate/hosting-mcp awes
78
65
 
79
66
  ```bash
80
67
  npm run typecheck
81
- npm test # builds, then boot-smoke + resolution + skills + knowledge suites
68
+ npm test # builds, then boot-smoke + resolution + skills + knowledge suites
69
+ node scripts/build-docs.mjs --check # the docs checks (also in CI and the hub's web build)
82
70
  ```
83
71
 
72
+ A change to `src/` or `skill/` needs a new version and a `skill/CHANGELOG.json`
73
+ entry at the top, and a new tool needs a place in `docs/reference.json` and,
74
+ where it changes what someone can ask for, a line in the right guide under `docs/`.
75
+
84
76
  Publishing is CI-only: any `mcp/**` change merged to main with a bumped
85
77
  version publishes via `publish-mcp.yml`. Never `npm publish` by hand.
package/dist/index.js CHANGED
@@ -2987,7 +2987,7 @@ var require_compile = __commonJS({
2987
2987
  const schOrFunc = root.refs[ref];
2988
2988
  if (schOrFunc)
2989
2989
  return schOrFunc;
2990
- let _sch = resolve2.call(this, root, ref);
2990
+ let _sch = resolve3.call(this, root, ref);
2991
2991
  if (_sch === void 0) {
2992
2992
  const schema = (_a = root.localRefs) === null || _a === void 0 ? void 0 : _a[ref];
2993
2993
  const { schemaId } = this.opts;
@@ -3014,7 +3014,7 @@ var require_compile = __commonJS({
3014
3014
  function sameSchemaEnv(s1, s2) {
3015
3015
  return s1.schema === s2.schema && s1.root === s2.root && s1.baseId === s2.baseId;
3016
3016
  }
3017
- function resolve2(root, ref) {
3017
+ function resolve3(root, ref) {
3018
3018
  let sch;
3019
3019
  while (typeof (sch = this.refs[ref]) == "string")
3020
3020
  ref = sch;
@@ -3645,55 +3645,55 @@ var require_fast_uri = __commonJS({
3645
3645
  }
3646
3646
  return uri;
3647
3647
  }
3648
- function resolve2(baseURI, relativeURI, options) {
3648
+ function resolve3(baseURI, relativeURI, options) {
3649
3649
  const schemelessOptions = options ? Object.assign({ scheme: "null" }, options) : { scheme: "null" };
3650
3650
  const resolved = resolveComponent(parse3(baseURI, schemelessOptions), parse3(relativeURI, schemelessOptions), schemelessOptions, true);
3651
3651
  schemelessOptions.skipEscape = true;
3652
3652
  return serialize(resolved, schemelessOptions);
3653
3653
  }
3654
- function resolveComponent(base, relative2, options, skipNormalization) {
3654
+ function resolveComponent(base, relative3, options, skipNormalization) {
3655
3655
  const target = {};
3656
3656
  if (!skipNormalization) {
3657
3657
  base = parse3(serialize(base, options), options);
3658
- relative2 = parse3(serialize(relative2, options), options);
3658
+ relative3 = parse3(serialize(relative3, options), options);
3659
3659
  }
3660
3660
  options = options || {};
3661
- if (!options.tolerant && relative2.scheme) {
3662
- target.scheme = relative2.scheme;
3663
- target.userinfo = relative2.userinfo;
3664
- target.host = relative2.host;
3665
- target.port = relative2.port;
3666
- target.path = removeDotSegments(relative2.path || "");
3667
- target.query = relative2.query;
3661
+ if (!options.tolerant && relative3.scheme) {
3662
+ target.scheme = relative3.scheme;
3663
+ target.userinfo = relative3.userinfo;
3664
+ target.host = relative3.host;
3665
+ target.port = relative3.port;
3666
+ target.path = removeDotSegments(relative3.path || "");
3667
+ target.query = relative3.query;
3668
3668
  } else {
3669
- if (relative2.userinfo !== void 0 || relative2.host !== void 0 || relative2.port !== void 0) {
3670
- target.userinfo = relative2.userinfo;
3671
- target.host = relative2.host;
3672
- target.port = relative2.port;
3673
- target.path = removeDotSegments(relative2.path || "");
3674
- target.query = relative2.query;
3669
+ if (relative3.userinfo !== void 0 || relative3.host !== void 0 || relative3.port !== void 0) {
3670
+ target.userinfo = relative3.userinfo;
3671
+ target.host = relative3.host;
3672
+ target.port = relative3.port;
3673
+ target.path = removeDotSegments(relative3.path || "");
3674
+ target.query = relative3.query;
3675
3675
  } else {
3676
- if (!relative2.path) {
3676
+ if (!relative3.path) {
3677
3677
  target.path = base.path;
3678
- if (relative2.query !== void 0) {
3679
- target.query = relative2.query;
3678
+ if (relative3.query !== void 0) {
3679
+ target.query = relative3.query;
3680
3680
  } else {
3681
3681
  target.query = base.query;
3682
3682
  }
3683
3683
  } else {
3684
- if (relative2.path[0] === "/") {
3685
- target.path = removeDotSegments(relative2.path);
3684
+ if (relative3.path[0] === "/") {
3685
+ target.path = removeDotSegments(relative3.path);
3686
3686
  } else {
3687
3687
  if ((base.userinfo !== void 0 || base.host !== void 0 || base.port !== void 0) && !base.path) {
3688
- target.path = "/" + relative2.path;
3688
+ target.path = "/" + relative3.path;
3689
3689
  } else if (!base.path) {
3690
- target.path = relative2.path;
3690
+ target.path = relative3.path;
3691
3691
  } else {
3692
- target.path = base.path.slice(0, base.path.lastIndexOf("/") + 1) + relative2.path;
3692
+ target.path = base.path.slice(0, base.path.lastIndexOf("/") + 1) + relative3.path;
3693
3693
  }
3694
3694
  target.path = removeDotSegments(target.path);
3695
3695
  }
3696
- target.query = relative2.query;
3696
+ target.query = relative3.query;
3697
3697
  }
3698
3698
  target.userinfo = base.userinfo;
3699
3699
  target.host = base.host;
@@ -3701,7 +3701,7 @@ var require_fast_uri = __commonJS({
3701
3701
  }
3702
3702
  target.scheme = base.scheme;
3703
3703
  }
3704
- target.fragment = relative2.fragment;
3704
+ target.fragment = relative3.fragment;
3705
3705
  return target;
3706
3706
  }
3707
3707
  function equal(uriA, uriB, options) {
@@ -3909,7 +3909,7 @@ var require_fast_uri = __commonJS({
3909
3909
  var fastUri = {
3910
3910
  SCHEMES,
3911
3911
  normalize,
3912
- resolve: resolve2,
3912
+ resolve: resolve3,
3913
3913
  resolveComponent,
3914
3914
  equal,
3915
3915
  serialize,
@@ -8722,9 +8722,9 @@ var require_dispatcher_base = __commonJS({
8722
8722
  }
8723
8723
  close(callback) {
8724
8724
  if (callback === void 0) {
8725
- return new Promise((resolve2, reject) => {
8725
+ return new Promise((resolve3, reject) => {
8726
8726
  this.close((err, data) => {
8727
- return err ? reject(err) : resolve2(data);
8727
+ return err ? reject(err) : resolve3(data);
8728
8728
  });
8729
8729
  });
8730
8730
  }
@@ -8762,12 +8762,12 @@ var require_dispatcher_base = __commonJS({
8762
8762
  err = null;
8763
8763
  }
8764
8764
  if (callback === void 0) {
8765
- return new Promise((resolve2, reject) => {
8765
+ return new Promise((resolve3, reject) => {
8766
8766
  this.destroy(err, (err2, data) => {
8767
8767
  return err2 ? (
8768
8768
  /* istanbul ignore next: should never error */
8769
8769
  reject(err2)
8770
- ) : resolve2(data);
8770
+ ) : resolve3(data);
8771
8771
  });
8772
8772
  });
8773
8773
  }
@@ -11034,8 +11034,8 @@ var require_util3 = __commonJS({
11034
11034
  function createDeferredPromise() {
11035
11035
  let res;
11036
11036
  let rej;
11037
- const promise = new Promise((resolve2, reject) => {
11038
- res = resolve2;
11037
+ const promise = new Promise((resolve3, reject) => {
11038
+ res = resolve3;
11039
11039
  rej = reject;
11040
11040
  });
11041
11041
  return { promise, resolve: res, reject: rej };
@@ -13281,12 +13281,12 @@ upgrade: ${upgrade}\r
13281
13281
  cb();
13282
13282
  }
13283
13283
  }
13284
- const waitForDrain = () => new Promise((resolve2, reject) => {
13284
+ const waitForDrain = () => new Promise((resolve3, reject) => {
13285
13285
  assert2(callback === null);
13286
13286
  if (socket[kError]) {
13287
13287
  reject(socket[kError]);
13288
13288
  } else {
13289
- callback = resolve2;
13289
+ callback = resolve3;
13290
13290
  }
13291
13291
  });
13292
13292
  socket.on("close", onDrain).on("drain", onDrain);
@@ -13923,12 +13923,12 @@ var require_client_h2 = __commonJS({
13923
13923
  cb();
13924
13924
  }
13925
13925
  }
13926
- const waitForDrain = () => new Promise((resolve2, reject) => {
13926
+ const waitForDrain = () => new Promise((resolve3, reject) => {
13927
13927
  assert2(callback === null);
13928
13928
  if (socket[kError]) {
13929
13929
  reject(socket[kError]);
13930
13930
  } else {
13931
- callback = resolve2;
13931
+ callback = resolve3;
13932
13932
  }
13933
13933
  });
13934
13934
  h2stream.on("close", onDrain).on("drain", onDrain);
@@ -14406,16 +14406,16 @@ var require_client = __commonJS({
14406
14406
  return this[kNeedDrain] < 2;
14407
14407
  }
14408
14408
  async [kClose]() {
14409
- return new Promise((resolve2) => {
14409
+ return new Promise((resolve3) => {
14410
14410
  if (this[kSize]) {
14411
- this[kClosedResolve] = resolve2;
14411
+ this[kClosedResolve] = resolve3;
14412
14412
  } else {
14413
- resolve2(null);
14413
+ resolve3(null);
14414
14414
  }
14415
14415
  });
14416
14416
  }
14417
14417
  async [kDestroy](err) {
14418
- return new Promise((resolve2) => {
14418
+ return new Promise((resolve3) => {
14419
14419
  const requests = this[kQueue].splice(this[kPendingIdx]);
14420
14420
  for (let i = 0; i < requests.length; i++) {
14421
14421
  const request = requests[i];
@@ -14426,7 +14426,7 @@ var require_client = __commonJS({
14426
14426
  this[kClosedResolve]();
14427
14427
  this[kClosedResolve] = null;
14428
14428
  }
14429
- resolve2(null);
14429
+ resolve3(null);
14430
14430
  };
14431
14431
  if (this[kHTTPContext]) {
14432
14432
  this[kHTTPContext].destroy(err, callback);
@@ -14477,7 +14477,7 @@ var require_client = __commonJS({
14477
14477
  });
14478
14478
  }
14479
14479
  try {
14480
- const socket = await new Promise((resolve2, reject) => {
14480
+ const socket = await new Promise((resolve3, reject) => {
14481
14481
  client[kConnector]({
14482
14482
  host,
14483
14483
  hostname: hostname2,
@@ -14489,7 +14489,7 @@ var require_client = __commonJS({
14489
14489
  if (err) {
14490
14490
  reject(err);
14491
14491
  } else {
14492
- resolve2(socket2);
14492
+ resolve3(socket2);
14493
14493
  }
14494
14494
  });
14495
14495
  });
@@ -14825,8 +14825,8 @@ var require_pool_base = __commonJS({
14825
14825
  if (this[kQueue].isEmpty()) {
14826
14826
  await Promise.all(this[kClients].map((c) => c.close()));
14827
14827
  } else {
14828
- await new Promise((resolve2) => {
14829
- this[kClosedResolve] = resolve2;
14828
+ await new Promise((resolve3) => {
14829
+ this[kClosedResolve] = resolve3;
14830
14830
  });
14831
14831
  }
14832
14832
  }
@@ -16069,7 +16069,7 @@ var require_readable = __commonJS({
16069
16069
  if (this._readableState.closeEmitted) {
16070
16070
  return null;
16071
16071
  }
16072
- return await new Promise((resolve2, reject) => {
16072
+ return await new Promise((resolve3, reject) => {
16073
16073
  if (this[kContentLength] > limit) {
16074
16074
  this.destroy(new AbortError());
16075
16075
  }
@@ -16082,7 +16082,7 @@ var require_readable = __commonJS({
16082
16082
  if (signal?.aborted) {
16083
16083
  reject(signal.reason ?? new AbortError());
16084
16084
  } else {
16085
- resolve2(null);
16085
+ resolve3(null);
16086
16086
  }
16087
16087
  }).on("error", noop).on("data", function(chunk) {
16088
16088
  limit -= chunk.length;
@@ -16101,7 +16101,7 @@ var require_readable = __commonJS({
16101
16101
  }
16102
16102
  async function consume(stream, type) {
16103
16103
  assert2(!stream[kConsume]);
16104
- return new Promise((resolve2, reject) => {
16104
+ return new Promise((resolve3, reject) => {
16105
16105
  if (isUnusable(stream)) {
16106
16106
  const rState = stream._readableState;
16107
16107
  if (rState.destroyed && rState.closeEmitted === false) {
@@ -16118,7 +16118,7 @@ var require_readable = __commonJS({
16118
16118
  stream[kConsume] = {
16119
16119
  type,
16120
16120
  stream,
16121
- resolve: resolve2,
16121
+ resolve: resolve3,
16122
16122
  reject,
16123
16123
  length: 0,
16124
16124
  body: []
@@ -16188,18 +16188,18 @@ var require_readable = __commonJS({
16188
16188
  return buffer;
16189
16189
  }
16190
16190
  function consumeEnd(consume2) {
16191
- const { type, body, resolve: resolve2, stream, length } = consume2;
16191
+ const { type, body, resolve: resolve3, stream, length } = consume2;
16192
16192
  try {
16193
16193
  if (type === "text") {
16194
- resolve2(chunksDecode(body, length));
16194
+ resolve3(chunksDecode(body, length));
16195
16195
  } else if (type === "json") {
16196
- resolve2(JSON.parse(chunksDecode(body, length)));
16196
+ resolve3(JSON.parse(chunksDecode(body, length)));
16197
16197
  } else if (type === "arrayBuffer") {
16198
- resolve2(chunksConcat(body, length).buffer);
16198
+ resolve3(chunksConcat(body, length).buffer);
16199
16199
  } else if (type === "blob") {
16200
- resolve2(new Blob(body, { type: stream[kContentType] }));
16200
+ resolve3(new Blob(body, { type: stream[kContentType] }));
16201
16201
  } else if (type === "bytes") {
16202
- resolve2(chunksConcat(body, length));
16202
+ resolve3(chunksConcat(body, length));
16203
16203
  }
16204
16204
  consumeFinish(consume2);
16205
16205
  } catch (err) {
@@ -16456,9 +16456,9 @@ var require_api_request = __commonJS({
16456
16456
  };
16457
16457
  function request(opts, callback) {
16458
16458
  if (callback === void 0) {
16459
- return new Promise((resolve2, reject) => {
16459
+ return new Promise((resolve3, reject) => {
16460
16460
  request.call(this, opts, (err, data) => {
16461
- return err ? reject(err) : resolve2(data);
16461
+ return err ? reject(err) : resolve3(data);
16462
16462
  });
16463
16463
  });
16464
16464
  }
@@ -16681,9 +16681,9 @@ var require_api_stream = __commonJS({
16681
16681
  };
16682
16682
  function stream(opts, factory, callback) {
16683
16683
  if (callback === void 0) {
16684
- return new Promise((resolve2, reject) => {
16684
+ return new Promise((resolve3, reject) => {
16685
16685
  stream.call(this, opts, factory, (err, data) => {
16686
- return err ? reject(err) : resolve2(data);
16686
+ return err ? reject(err) : resolve3(data);
16687
16687
  });
16688
16688
  });
16689
16689
  }
@@ -16968,9 +16968,9 @@ var require_api_upgrade = __commonJS({
16968
16968
  };
16969
16969
  function upgrade(opts, callback) {
16970
16970
  if (callback === void 0) {
16971
- return new Promise((resolve2, reject) => {
16971
+ return new Promise((resolve3, reject) => {
16972
16972
  upgrade.call(this, opts, (err, data) => {
16973
- return err ? reject(err) : resolve2(data);
16973
+ return err ? reject(err) : resolve3(data);
16974
16974
  });
16975
16975
  });
16976
16976
  }
@@ -17062,9 +17062,9 @@ var require_api_connect = __commonJS({
17062
17062
  };
17063
17063
  function connect(opts, callback) {
17064
17064
  if (callback === void 0) {
17065
- return new Promise((resolve2, reject) => {
17065
+ return new Promise((resolve3, reject) => {
17066
17066
  connect.call(this, opts, (err, data) => {
17067
- return err ? reject(err) : resolve2(data);
17067
+ return err ? reject(err) : resolve3(data);
17068
17068
  });
17069
17069
  });
17070
17070
  }
@@ -20926,7 +20926,7 @@ var require_fetch = __commonJS({
20926
20926
  function dispatch({ body }) {
20927
20927
  const url = requestCurrentURL(request);
20928
20928
  const agent = fetchParams.controller.dispatcher;
20929
- return new Promise((resolve2, reject) => agent.dispatch(
20929
+ return new Promise((resolve3, reject) => agent.dispatch(
20930
20930
  {
20931
20931
  path: url.pathname + url.search,
20932
20932
  origin: url.origin,
@@ -21002,7 +21002,7 @@ var require_fetch = __commonJS({
21002
21002
  }
21003
21003
  }
21004
21004
  const onError = this.onError.bind(this);
21005
- resolve2({
21005
+ resolve3({
21006
21006
  status,
21007
21007
  statusText,
21008
21008
  headersList,
@@ -21048,7 +21048,7 @@ var require_fetch = __commonJS({
21048
21048
  for (let i = 0; i < rawHeaders.length; i += 2) {
21049
21049
  headersList.append(bufferToLowerCasedHeaderName(rawHeaders[i]), rawHeaders[i + 1].toString("latin1"), true);
21050
21050
  }
21051
- resolve2({
21051
+ resolve3({
21052
21052
  status,
21053
21053
  statusText: STATUS_CODES[status],
21054
21054
  headersList,
@@ -24778,8 +24778,8 @@ var require_util9 = __commonJS({
24778
24778
  return true;
24779
24779
  }
24780
24780
  function delay(ms) {
24781
- return new Promise((resolve2) => {
24782
- setTimeout(resolve2, ms).unref();
24781
+ return new Promise((resolve3) => {
24782
+ setTimeout(resolve3, ms).unref();
24783
24783
  });
24784
24784
  }
24785
24785
  module.exports = {
@@ -37553,7 +37553,7 @@ var Protocol = class {
37553
37553
  return;
37554
37554
  }
37555
37555
  const pollInterval = task2.pollInterval ?? this._options?.defaultTaskPollInterval ?? 1e3;
37556
- await new Promise((resolve2) => setTimeout(resolve2, pollInterval));
37556
+ await new Promise((resolve3) => setTimeout(resolve3, pollInterval));
37557
37557
  options?.signal?.throwIfAborted();
37558
37558
  }
37559
37559
  } catch (error2) {
@@ -37570,7 +37570,7 @@ var Protocol = class {
37570
37570
  */
37571
37571
  request(request, resultSchema, options) {
37572
37572
  const { relatedRequestId, resumptionToken, onresumptiontoken, task, relatedTask } = options ?? {};
37573
- return new Promise((resolve2, reject) => {
37573
+ return new Promise((resolve3, reject) => {
37574
37574
  const earlyReject = (error2) => {
37575
37575
  reject(error2);
37576
37576
  };
@@ -37648,7 +37648,7 @@ var Protocol = class {
37648
37648
  if (!parseResult.success) {
37649
37649
  reject(parseResult.error);
37650
37650
  } else {
37651
- resolve2(parseResult.data);
37651
+ resolve3(parseResult.data);
37652
37652
  }
37653
37653
  } catch (error2) {
37654
37654
  reject(error2);
@@ -37909,12 +37909,12 @@ var Protocol = class {
37909
37909
  }
37910
37910
  } catch {
37911
37911
  }
37912
- return new Promise((resolve2, reject) => {
37912
+ return new Promise((resolve3, reject) => {
37913
37913
  if (signal.aborted) {
37914
37914
  reject(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
37915
37915
  return;
37916
37916
  }
37917
- const timeoutId = setTimeout(resolve2, interval);
37917
+ const timeoutId = setTimeout(resolve3, interval);
37918
37918
  signal.addEventListener("abort", () => {
37919
37919
  clearTimeout(timeoutId);
37920
37920
  reject(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
@@ -39014,7 +39014,7 @@ var McpServer = class {
39014
39014
  let task = createTaskResult.task;
39015
39015
  const pollInterval = task.pollInterval ?? 5e3;
39016
39016
  while (task.status !== "completed" && task.status !== "failed" && task.status !== "cancelled") {
39017
- await new Promise((resolve2) => setTimeout(resolve2, pollInterval));
39017
+ await new Promise((resolve3) => setTimeout(resolve3, pollInterval));
39018
39018
  const updatedTask = await extra.taskStore.getTask(taskId);
39019
39019
  if (!updatedTask) {
39020
39020
  throw new McpError(ErrorCode.InternalError, `Task ${taskId} not found during polling`);
@@ -39663,21 +39663,21 @@ var StdioServerTransport = class {
39663
39663
  this.onclose?.();
39664
39664
  }
39665
39665
  send(message) {
39666
- return new Promise((resolve2) => {
39666
+ return new Promise((resolve3) => {
39667
39667
  const json = serializeMessage(message);
39668
39668
  if (this._stdout.write(json)) {
39669
- resolve2();
39669
+ resolve3();
39670
39670
  } else {
39671
- this._stdout.once("drain", resolve2);
39671
+ this._stdout.once("drain", resolve3);
39672
39672
  }
39673
39673
  });
39674
39674
  }
39675
39675
  };
39676
39676
 
39677
39677
  // src/index.ts
39678
- import { readFileSync as readFileSync3 } from "node:fs";
39678
+ import { mkdirSync, readFileSync as readFileSync3, writeFileSync as writeFileSync2 } from "node:fs";
39679
39679
  import { homedir as homedir3 } from "node:os";
39680
- import { join as join3, dirname as dirname3 } from "node:path";
39680
+ import { join as join3, dirname as dirname3, resolve as resolve2, relative as relative2, isAbsolute as isAbsolute2 } from "node:path";
39681
39681
  import { fileURLToPath as fileURLToPath2 } from "node:url";
39682
39682
 
39683
39683
  // src/config.ts
@@ -41267,6 +41267,7 @@ var TITLES = {
41267
41267
  awesomate_dashboard_metrics: "Get dashboard metrics",
41268
41268
  awesomate_account_report: "Get account report",
41269
41269
  awesomate_business: "Read business details",
41270
+ awesomate_design_system_package: "Make a starter brand design system",
41270
41271
  awesomate_privacy_settings: "Get privacy settings",
41271
41272
  awesomate_app_list: "List apps",
41272
41273
  awesomate_app_context: "Get apps context",
@@ -43423,6 +43424,47 @@ server.registerTool(
43423
43424
  }
43424
43425
  }
43425
43426
  );
43427
+ server.registerTool(
43428
+ "awesomate_design_system_package",
43429
+ {
43430
+ annotations: annotate("awesomate_design_system_package", MUTATING),
43431
+ description: "Make a starter brand design system from this business's CONFIRMED details (colours, font, logo, name, voice) and write it into a folder in the current project, ready to put in Claude Design. Use it when the business has no design system yet and the owner says yes to making one (see 'Brand design system first' in the build skills). It writes tokens.css, preview cards (each starting with its @dsCard line), the fonts and the logo inside the folder (Claude Design allows no external requests), and a README saying what is still to decide. It returns only the file list, never the file contents. Next: tell the owner to run /design-sync (and /design-login once if asked) to create a design system in Claude Design from that folder, then save its name and link on Your business in the hub.",
43432
+ inputSchema: {
43433
+ dir: external_exports.string().max(200).optional().describe("Folder inside the current project to write to (default 'brand-design-system')")
43434
+ }
43435
+ },
43436
+ async ({ dir }) => {
43437
+ try {
43438
+ const root = process.cwd();
43439
+ const target = resolve2(root, dir || "brand-design-system");
43440
+ const rel = relative2(root, target);
43441
+ if (!rel || rel.startsWith("..") || isAbsolute2(rel)) {
43442
+ return errorResult(new Error("Pick a folder inside the current project, for example brand-design-system."));
43443
+ }
43444
+ const pkg = await hubGet(requireConfig(), "/api/my-business/v1/design-system/package");
43445
+ const written = [];
43446
+ for (const f of pkg.files) {
43447
+ const out = resolve2(target, f.path);
43448
+ const inside = relative2(target, out);
43449
+ if (!inside || inside.startsWith("..") || isAbsolute2(inside)) continue;
43450
+ mkdirSync(dirname3(out), { recursive: true });
43451
+ writeFileSync2(out, f.encoding === "base64" ? Buffer.from(f.content, "base64") : f.content);
43452
+ written.push(f.path);
43453
+ }
43454
+ const toDecide = pkg.plan.checklist.filter((c) => c.status === "to_decide").map((c) => c.point);
43455
+ return textResult({
43456
+ folder: rel,
43457
+ files: written,
43458
+ embedded: pkg.embedded,
43459
+ still_to_decide: toDecide,
43460
+ accent_too_faint_for_text: pkg.plan.accentTooFaintForText,
43461
+ next: `Ask the owner to run /design-sync and create a new design system in Claude Design from ./${rel}. Then they save its name and link on Your business in the hub (Knowledge, Your business, Your brand).`
43462
+ });
43463
+ } catch (err) {
43464
+ return errorResult(err);
43465
+ }
43466
+ }
43467
+ );
43426
43468
  server.registerResource(
43427
43469
  "business",
43428
43470
  "awesomate://business",
@@ -43635,9 +43677,9 @@ server.registerTool(
43635
43677
  }
43636
43678
  if (!rawPath) return errorResult(new Error("Give either path (a local file) or fromFilesPath (a File Manager path)."));
43637
43679
  const { statSync, existsSync: existsSync3 } = await import("node:fs");
43638
- const { resolve: resolve2, basename, extname } = await import("node:path");
43680
+ const { resolve: resolve3, basename, extname } = await import("node:path");
43639
43681
  const expanded = rawPath.startsWith("~") ? join3(homedir3(), rawPath.slice(1).replace(/^[/\\]/, "")) : rawPath;
43640
- const abs = resolve2(expanded);
43682
+ const abs = resolve3(expanded);
43641
43683
  if (!existsSync3(abs)) {
43642
43684
  return errorResult(
43643
43685
  new Error(
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@awesomate/hosting-mcp",
3
- "version": "0.62.0",
3
+ "version": "0.64.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,17 @@
1
1
  {
2
2
  "versions": [
3
+ {
4
+ "version": "0.64.0",
5
+ "highlights": [
6
+ "No brand design system yet? Claude can make you a starter one from your confirmed business details (colours, font, logo, voice) and help you put it in Claude Design, so everything built for you looks like you from day one"
7
+ ]
8
+ },
9
+ {
10
+ "version": "0.63.0",
11
+ "highlights": [
12
+ "Before building anything people will see, Claude asks about your brand design system: it uses yours in Claude Design, or offers to make one from your business details, so your apps, pages and emails all look like you. You can save its name and link on Your business"
13
+ ]
14
+ },
3
15
  {
4
16
  "version": "0.62.0",
5
17
  "highlights": [
@@ -37,6 +37,21 @@ own hosting) but you should always confirm the **stack + name** before creating.
37
37
  voice, logo, colours and contact details. Use them rather than inventing
38
38
  placeholders; confirm anything marked *suggestion* with the owner first.
39
39
 
40
+ ### Brand design system first (strongly recommended)
41
+
42
+ Before you build anything people will see, find the business's brand design system:
43
+
44
+ 1. `awesomate_business` shows **Design system** and **Design system link** when the owner has
45
+ saved them.
46
+ 2. If it is in Claude Design, connect to it with `/design-sync` (the owner may need `/design-login`
47
+ once) and build with its tokens and components.
48
+ 3. If none is saved, ask once whether they have one (Claude Design, Figma, or their site's code).
49
+ 4. If they don't, offer to make one: `awesomate_design_system_package` writes a starter design
50
+ system from their confirmed details into the project, and `/design-sync` puts it in Claude Design.
51
+ 5. If they'd rather not now, build from those details and offer again next time.
52
+
53
+ What makes a good one, and the tokens to start from: [references/design-system.md](references/design-system.md).
54
+
40
55
  ## 1. Decide the stack (do this before building)
41
56
 
42
57
  Read [references/stack-decision.md](references/stack-decision.md) — it's the
@@ -0,0 +1,69 @@
1
+ # The brand design system: find it, connect it, or make one
2
+
3
+ A brand design system keeps a business's colours, type, components and layout rules in one place,
4
+ so everything built for it looks like the same business. Claude Design holds them (claude.ai/design,
5
+ Settings, Design systems). It checks its own work against the design system, and `/design-sync` in
6
+ Claude Code keeps a project's code and the design system in step. Asking for the design system
7
+ before building is strongly recommended. It is never a reason to refuse to build.
8
+
9
+ ## Before you build anything people will see
10
+
11
+ 1. **Look it up.** `awesomate_business` shows **Design system** and **Design system link** when the
12
+ owner has saved them on Your business in the hub.
13
+ 2. **It's in Claude Design.** Connect this project to it with `/design-sync`. The owner may need to
14
+ run `/design-login` once first. Build with its tokens and components, not with new ones.
15
+ 3. **Nothing is saved.** Ask once: "Do you have a brand design system, in Claude Design, Figma, or
16
+ your website's code?" If they do, connect it, and suggest they save its name and link on Your
17
+ business so the next build finds it straight away.
18
+ 4. **They have none.** Offer to make one. `awesomate_design_system_package` writes a starter design
19
+ system into the project from their confirmed details: `tokens.css`, preview cards, the fonts and
20
+ the logo inside the folder, and a README of what is still to decide. Then `/design-sync` puts it
21
+ in Claude Design as their design system. Tell them to save its name and link on Your business,
22
+ and to work through the README's "still to decide" list with you or in Claude Design.
23
+ 5. **They'd rather not now.** Build from the business's colours, fonts and logo, and offer again
24
+ next time. Never make the build wait for a design system.
25
+
26
+ Follow Anthropic's `/design-sync` for how the sync itself works. Don't describe it from memory.
27
+
28
+ ## What a good brand design system has
29
+
30
+ Check the system you connect or create against these eight points, and say in one line what is
31
+ missing:
32
+
33
+ 1. **One idea**, rooted in the business's place or story: its town, its building, its trade, its people.
34
+ 2. **Type first.** One expressive face for headings and one neutral face for text. Generic system
35
+ fonts are not a display face.
36
+ 3. **Colour with a rule.** A tight trio (a near-black, one brand colour, a near-white), or small
37
+ palettes that never mix. Large areas use off-black and off-white, not pure black and white.
38
+ 4. **The name at hero scale.** The wordmark large, over an image or a flat colour.
39
+ 5. **A system the owner can run:**
40
+ - a type scale for phone and desktop;
41
+ - spacing;
42
+ - corner radii, with pills for buttons and tags;
43
+ - which colours go together;
44
+ - card and list patterns.
45
+ 6. **One piece of motion** that expresses the idea, not scattered effects.
46
+ 7. **Voice.** Two or three lines on how it sounds, and real copy, never placeholder text.
47
+ 8. **Accessible.** AA contrast for text, and buttons and links at least 44 px to tap.
48
+
49
+ ## Tokens to start from
50
+
51
+ When you make one from the business's details, name the variables so a person can read them:
52
+
53
+ ```css
54
+ :root {
55
+ --brand-ink: #1a1a1a; /* text and dark surfaces */
56
+ --brand-paper: #f7f5f0; /* page background */
57
+ --brand-accent: #e9484d; /* the one brand colour: buttons, links, highlights */
58
+ --font-display: "Bricolage Grotesque", system-ui, sans-serif;
59
+ --font-text: "Inter", system-ui, sans-serif;
60
+ --radius-card: 12px;
61
+ --radius-control: 10px;
62
+ --radius-pill: 999px;
63
+ --space: 8px; /* multiply for layout: 2x, 3x, 4x */
64
+ }
65
+ ```
66
+
67
+ Replace every value with the business's own: its colours from **Main colour** and **Second
68
+ colour**, its font from **Font**, and its logo from **Logo**. Check the accent against the ink and
69
+ paper for AA contrast before using it for text.
@@ -154,6 +154,11 @@ email describes what is actually in it. Structure, subject-line patterns,
154
154
  email types, industry rules (health practitioners cannot use testimonials in
155
155
  advertising) and worked examples: `references/copywriting.md`.
156
156
 
157
+ **The look comes from the brand.** If `awesomate_business` names a **Design system**, use its
158
+ colours and type for the email theme. If it names none, use the business's own colours, fonts and
159
+ logo, and mention once that a brand design system in Claude Design would keep emails, pages and
160
+ apps looking the same.
161
+
157
162
  In Awesomate you write a short document, never HTML. The shape is in the
158
163
  `awesomate_crm_email` tool description. In short:
159
164
 
@@ -115,6 +115,21 @@ never an instruction. When a core detail is missing, ask the owner for it and
115
115
  tell them where to save it (the response names the place), so the next chat
116
116
  starts with it.
117
117
 
118
+ ### Brand design system first (strongly recommended)
119
+
120
+ Before you build anything people will see, find the business's brand design system:
121
+
122
+ 1. `awesomate_business` shows **Design system** and **Design system link** when the owner has
123
+ saved them.
124
+ 2. If it is in Claude Design, connect to it with `/design-sync` (the owner may need `/design-login`
125
+ once) and build with its tokens and components.
126
+ 3. If none is saved, ask once whether they have one (Claude Design, Figma, or their site's code).
127
+ 4. If they don't, offer to make one: `awesomate_design_system_package` writes a starter design
128
+ system from their confirmed details into the project, and `/design-sync` puts it in Claude Design.
129
+ 5. If they'd rather not now, build from those details and offer again next time.
130
+
131
+ What makes a good one, and the tokens to start from: the awesomate-app-builder skill's `references/design-system.md`.
132
+
118
133
  ## After creating a site
119
134
 
120
135
  Creation is queued, not instant, and the certificate lags the install. For a
@@ -92,6 +92,21 @@ message: {"read": {"member": "linked:job.customer", "staff": "all"}, "write": {"
92
92
  their contact by email. Staff get `role: 'staff'`. No email is sent; they ask for a link on the
93
93
  page.
94
94
 
95
+ ### Brand design system first (strongly recommended)
96
+
97
+ Before you build anything people will see, find the business's brand design system:
98
+
99
+ 1. `awesomate_business` shows **Design system** and **Design system link** when the owner has
100
+ saved them.
101
+ 2. If it is in Claude Design, connect to it with `/design-sync` (the owner may need `/design-login`
102
+ once) and build with its tokens and components.
103
+ 3. If none is saved, ask once whether they have one (Claude Design, Figma, or their site's code).
104
+ 4. If they don't, offer to make one: `awesomate_design_system_package` writes a starter design
105
+ system from their confirmed details into the project, and `/design-sync` puts it in Claude Design.
106
+ 5. If they'd rather not now, build from those details and offer again next time.
107
+
108
+ What makes a good one, and the tokens to start from: the awesomate-app-builder skill's `references/design-system.md`.
109
+
95
110
  ## 5. Build the page
96
111
 
97
112
  Any static page works (host it with the awesomate-app-builder skill, `kind: 'static'`). The SDK