@awesomate/hosting-mcp 0.8.2 → 0.9.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
@@ -1,4 +1,5 @@
1
1
  #!/usr/bin/env node
2
+ import { createRequire as __awmCreateRequire } from "node:module"; const require = __awmCreateRequire(import.meta.url);
2
3
  var __create = Object.create;
3
4
  var __defProp = Object.defineProperty;
4
5
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
@@ -40206,12 +40207,12 @@ server.registerTool(
40206
40207
  server.registerTool(
40207
40208
  "awesomate_app_create",
40208
40209
  {
40209
- description: "Provision a new app on the client's cPanel account. Choose the stack deliberately (see the awesomate-app-builder skill): kind 'static' = a single fast landing/lead page served straight from the docroot (no DB, no server process \u2014 pick this for brochure/landing/lead-capture); kind 'node' = a dynamic app with a backend + database (logins, custom logic, an API). For node, dbEngine 'postgres' suits relational/JSON/AI-heavy data, 'mysql' is the simple default. Returns 202 immediately with an appId + subdomain(s); poll awesomate_app_get until status=active. Requires apps:write (Support Plus+) \u2014 a 403 means offer an upgrade. Get the user's confirmation on the stack + name before calling.",
40210
+ description: "Provision a new app on the client's cPanel account. Choose the stack deliberately (see the awesomate-app-builder skill): kind 'static' = a single fast landing/lead page served straight from the docroot (no DB, no server process \u2014 pick this for brochure/landing/lead-capture); kind 'node' = a dynamic app with a backend + database (logins, custom logic, an API). Pick the template by job \u2014 'node-auth-sync' (user accounts/logins, MySQL), 'node-crud-postgres' (structured data without logins: trackers/dashboards \u2014 ready-made CRUD + UI, Postgres), 'node-api-only' (webhooks/integrations/glue, no DB wiring), 'static-landing' (marketing/lead page). If dbEngine is omitted the template's native engine is used (node-crud-postgres \u2192 postgres). Returns 202 immediately with an appId + subdomain(s); poll awesomate_app_get until status=active, then fetch the starter files with awesomate_app_scaffold. Requires apps:write (Support Plus+) \u2014 a 403 means offer an upgrade. Get the user's confirmation on the stack + name before calling.",
40210
40211
  inputSchema: {
40211
40212
  appSlug: external_exports.string().regex(/^[a-z][a-z0-9]{1,15}$/).describe("2-16 chars, starts with a letter, lowercase alphanumeric \u2014 used for subdomains + db names"),
40212
40213
  kind: external_exports.enum(["node", "static"]).default("node").describe("'static' for a landing/lead page (no DB); 'node' for a dynamic app"),
40213
- dbEngine: external_exports.enum(["mysql", "postgres"]).default("mysql").describe("node only \u2014 the database engine to provision"),
40214
- template: external_exports.string().optional().describe("Starter template (defaults: node \u2192 'node-auth-sync', static \u2192 'static-landing')")
40214
+ dbEngine: external_exports.enum(["mysql", "postgres"]).optional().describe("node only \u2014 omit to use the template's native engine"),
40215
+ template: external_exports.string().optional().describe("Starter template: node-auth-sync | node-crud-postgres | node-api-only | static-landing (defaults: node \u2192 'node-auth-sync', static \u2192 'static-landing'; full catalog in awesomate_app_context.templates)")
40215
40216
  }
40216
40217
  },
40217
40218
  async ({ appSlug, kind, dbEngine, template }) => {
@@ -40224,10 +40225,24 @@ server.registerTool(
40224
40225
  }
40225
40226
  }
40226
40227
  );
40228
+ server.registerTool(
40229
+ "awesomate_app_scaffold",
40230
+ {
40231
+ description: "Fetch the app's starter files (its template rendered against the live app metadata \u2014 subdomains, control-plane URL, repo \u2014 with placeholders already substituted). Call after awesomate_app_create reaches status=active: write each returned file into a fresh local project folder at its relative path, then run npm install (Node apps) and follow the bundled CLAUDE.md. This is how the starter code gets onto the user's machine \u2014 don't reconstruct templates by hand.",
40232
+ inputSchema: { appId: external_exports.number().int().positive().describe("The app id from awesomate_app_create / _list") }
40233
+ },
40234
+ async ({ appId }) => {
40235
+ try {
40236
+ return textResult(await hubGet(requireConfig(), `/api/my-apps/apps/${appId}/scaffold`));
40237
+ } catch (err) {
40238
+ return errorResult(err);
40239
+ }
40240
+ }
40241
+ );
40227
40242
  server.registerTool(
40228
40243
  "awesomate_app_deploy",
40229
40244
  {
40230
- description: "Report how to deploy an app and its per-env targets. Node apps deploy via git push (dev/staging/main \u2192 GitHub Actions \u2192 cPanel); this returns the branch\u2192env map and subdomains. Use the awesomate-github skill to wire push-to-deploy the first time.",
40245
+ description: "Report how to deploy an app, its per-env targets, last-deploy/health state, and how to promote (dev\u2192staging\u2192main) or roll back (git revert + push). Node apps deploy via git push (dev/staging/main \u2192 GitHub Actions \u2192 cPanel). Use the awesomate-github skill to wire push-to-deploy the first time.",
40231
40246
  inputSchema: { appId: external_exports.number().int().positive().describe("The app id") }
40232
40247
  },
40233
40248
  async ({ appId }) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@awesomate/hosting-mcp",
3
- "version": "0.8.2",
3
+ "version": "0.9.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",
@@ -17,10 +17,10 @@
17
17
  "README.md"
18
18
  ],
19
19
  "scripts": {
20
- "build": "esbuild src/index.ts --bundle --platform=node --target=node18 --format=esm --outfile=dist/index.js --banner:js='#!/usr/bin/env node' --external:node:*",
20
+ "build": "esbuild src/index.ts --bundle --platform=node --target=node18 --format=esm --outfile=dist/index.js --banner:js='#!/usr/bin/env node\nimport { createRequire as __awmCreateRequire } from \"node:module\"; const require = __awmCreateRequire(import.meta.url);' --external:node:*",
21
21
  "typecheck": "tsc --noEmit",
22
- "test": "esbuild src/config.ts --bundle --platform=node --target=node18 --format=esm --outfile=test/.build/config.mjs --external:node:* && node --test test/*.test.mjs",
23
- "prepublishOnly": "npm run typecheck && npm run test && npm run build"
22
+ "test": "npm run build && esbuild src/config.ts --bundle --platform=node --target=node18 --format=esm --outfile=test/.build/config.mjs --external:node:* && node --test test/*.test.mjs",
23
+ "prepublishOnly": "npm run typecheck && npm run test"
24
24
  },
25
25
  "dependencies": {
26
26
  "@modelcontextprotocol/sdk": "^1.29.0",
@@ -70,21 +70,32 @@ target; never touch prod without an explicit ask.**
70
70
  you recommend. Get a yes.
71
71
  3. **Confirmation gate** — confirm the **name** (2–16 lowercase letters/digits,
72
72
  starts with a letter — becomes the subdomain + db names) and the **stack**,
73
- then call `awesomate_app_create` (`appSlug`, `kind`, and for node `dbEngine`).
73
+ then call `awesomate_app_create` (`appSlug`, `kind`, `template`, and for
74
+ node optionally `dbEngine` — omit it to use the template's native engine).
75
+ Pick the template by job (stack-decision.md has the table): logins →
76
+ `node-auth-sync`; structured data, no logins → `node-crud-postgres`;
77
+ webhooks/integrations → `node-api-only`; landing page → `static-landing`.
74
78
  It returns **202** with an `appId` + subdomain(s); poll `awesomate_app_get`
75
79
  until `status` is `active` (or `failed` — read `provision_error`, explain
76
80
  plainly).
77
- 4. **Implementation** — build the actual code/content locally against the
78
- template. Handle **secrets** only via the **awesomate-credentials** skill
81
+ 4. **Scaffold** — call `awesomate_app_scaffold` and write each returned file
82
+ into a fresh local project folder at its relative path (placeholders are
83
+ already substituted), then `npm install` (Node) and read the bundled
84
+ `CLAUDE.md` — it's the app's own manual. Never reconstruct the starter by
85
+ hand.
86
+ 5. **Implementation** — build the actual code/content on top of the scaffold.
87
+ [references/node-recipes.md](references/node-recipes.md) has the recipes
88
+ (add a table/endpoint, env vars, connection strings, promote/rollback).
89
+ Handle **secrets** only via the **awesomate-credentials** skill
79
90
  (encrypted + injected, never committed/echoed). Wire **version control**
80
91
  from the first commit via **awesomate-github**. If it needs email/lookup/AI,
81
92
  attach n8n (§"when to use n8n").
82
- 5. **Testing** — deploy to **dev** (`git push` to `dev`), then
93
+ 6. **Testing** — deploy to **dev** (`git push` to `dev`), then
83
94
  `awesomate_app_health` to confirm it came up (Node: `/api/ready`). Show the
84
95
  user the live dev URL and let them try it. Iterate on dev until they're happy.
85
- 6. **Wrap-up / promote** — only when the user approves, promote **dev → staging
96
+ 7. **Wrap-up / promote** — only when the user approves, promote **dev → staging
86
97
  → main** (merge + push per branch). `awesomate_app_deploy` reports the
87
- branch→env map. Prod is `main`.
98
+ branch→env map + last-deploy state. Prod is `main`.
88
99
 
89
100
  **Promote / rollback (git-based):** promoting is merging the tested branch
90
101
  forward (`dev`→`staging`→`main`); rolling back is `git revert` of the bad
@@ -0,0 +1,83 @@
1
+ # Node app recipes — the moves you'll make over and over
2
+
3
+ Every scaffolded app carries its own `CLAUDE.md` (the authoritative manual for
4
+ THAT app — read it first). These are the cross-app recipes, kept in the user's
5
+ plain language.
6
+
7
+ ## Add a table / change the schema
8
+
9
+ Never edit an already-deployed migration. Add a new file:
10
+
11
+ ```
12
+ migrations/002_add_customers.sql
13
+ ```
14
+
15
+ ```sql
16
+ CREATE TABLE IF NOT EXISTS customers (...);
17
+ -- or: ALTER TABLE records ADD COLUMN amount NUMERIC NOT NULL DEFAULT 0;
18
+ ```
19
+
20
+ Push it — the deploy workflow runs `npm run migrate` before restarting the
21
+ app. Postgres templates apply each file in a transaction (a failure rolls
22
+ back and retries next deploy); MySQL applies statement-by-statement, so keep
23
+ MySQL migrations small.
24
+
25
+ ## Add an endpoint
26
+
27
+ Copy the closest existing route in `src/index.ts` and keep its three habits:
28
+
29
+ 1. **zod-validate the input** before touching the DB or doing work.
30
+ 2. **Parameterized SQL only** — `$1`/`$2` (Postgres) or `?` (MySQL). Never
31
+ template user input into a SQL string.
32
+ 3. **Escape on render** — `textContent` in browser code, never `innerHTML`
33
+ with user data.
34
+
35
+ ## Connection strings / DB access
36
+
37
+ Provisioning writes the server-side `.env` per env: `DATABASE_URL` +
38
+ `DB_ENGINE`/`DB_HOST`/`DB_PORT`/`DB_NAME`/`DB_USER`/`DB_PASS`. The starters'
39
+ lazy pool means local dev works with a blank `.env` (DB routes return 503
40
+ with a clear reason). Postgres code should prefer `DATABASE_URL`.
41
+
42
+ ## Env vars & secrets
43
+
44
+ - Runtime secret for the app → `awesomate_app_set_env` (encrypted in the hub,
45
+ injected into the server `.env`, pm2 restarted). Per-env; `dev` default.
46
+ - Never commit `.env`, never echo a stored value, and per the
47
+ awesomate-credentials skill: a key pasted in chat gets stored AND flagged
48
+ for rotation.
49
+
50
+ ## Attach n8n as the backend
51
+
52
+ Job needs email / a lookup in their connected apps / AI → build the webhook
53
+ workflow with the awesomate-n8n skill, then `awesomate_n8n_attach_to_app`
54
+ (stores `N8N_WEBHOOK_URL` + secret; returns the secret ONCE — add the
55
+ matching `X-Awesomate-Webhook-Secret` header check to the workflow). The app
56
+ calls `callWorkflow(payload)` from `src/lib/n8n.ts`; it no-ops when unset and
57
+ never throws — check `result.ok` and degrade gracefully.
58
+
59
+ ## Deploy, promote, roll back
60
+
61
+ - **Deploy** = `git push origin dev` (that's the whole mechanism). Watch with
62
+ `gh run watch`; verify with `awesomate_app_health`.
63
+ - **Promote** = merge forward: `dev → staging → main`. Prod is `main`; go
64
+ through a PR from staging.
65
+ - **Roll back** = `git revert <bad-sha>` on the affected branch + push — the
66
+ workflow redeploys the previous good state. Re-check health after.
67
+ - `awesomate_app_deploy` reports the branch→env map, last-deploy SHA/time,
68
+ and last health per env.
69
+
70
+ ## Health semantics (don't mix these up)
71
+
72
+ - `/api/health` — lenient, always 200, DB state in the body. For humans.
73
+ - `/api/ready` — strict; 503 until the DB is reachable AND migrations ran
74
+ (DB templates). CI's post-deploy gate and what `awesomate_app_health`
75
+ probes. An app that was created but never pushed reports unreachable —
76
+ that's expected, not broken.
77
+
78
+ ## When something's off
79
+
80
+ 1. `gh run view --log-failed` — the deploy step that broke.
81
+ 2. `awesomate_app_health` — live per-env probe with a plain reason.
82
+ 3. The app's own `CLAUDE.md` troubleshooting section — engine-specific
83
+ diagnostics live there.
@@ -13,13 +13,25 @@ confirm before building. **Never default to WordPress.**
13
13
  | A landing page / capture leads / a one-pager / "just needs to look good and be found" | **static site** (`kind: 'static'`) | Served straight from the cPanel docroot — no database, no server process, fastest load. Add SEO + a lead form (below). The right default for marketing pages. |
14
14
  | A tool/app with **logins, custom logic, a dashboard, or an API** | **Node app** (`kind: 'node'`) | A real backend + database. |
15
15
 
16
+ ### Within a Node app: which starter template?
17
+ Pick by the job (full catalog with descriptions: `awesomate_app_context.templates`):
18
+
19
+ | Job | `template` | Ships with |
20
+ |---|---|---|
21
+ | People **sign in** / per-user data | `node-auth-sync` | signup/login (JWT + bcrypt), MySQL migrations |
22
+ | Stores **structured data, no logins** — trackers, dashboards, internal tools | `node-crud-postgres` | ready-made records CRUD API + tiny UI, Postgres migrations (transactional) |
23
+ | **Webhooks / integrations / glue** — receives or serves JSON, little state | `node-api-only` | versioned `/api/v1` routes, CORS, optional `x-api-key` gate, no DB wiring |
24
+
25
+ All three include `src/lib/n8n.ts` and the same push-to-deploy workflow.
26
+
16
27
  ### Within a Node app: which database?
17
28
  - **postgres** — relational data with relationships, concurrency, JSON columns,
18
29
  or analytics/AI features. The modern default for a real app.
19
30
  - **mysql** — simple/legacy needs, or when they already know MySQL.
20
- - Provisioned automatically by `awesomate_app_create`; the app's `.env` gets
21
- `DB_ENGINE`, `DB_HOST`, `DB_PORT`, `DB_NAME`, `DB_USER`, `DB_PASS`, and a
22
- ready-to-use `DATABASE_URL`.
31
+ - Omit `dbEngine` and `awesomate_app_create` uses the template's native engine
32
+ (`node-crud-postgres` postgres). The app's `.env` gets `DB_ENGINE`,
33
+ `DB_HOST`, `DB_PORT`, `DB_NAME`, `DB_USER`, `DB_PASS`, and a ready-to-use
34
+ `DATABASE_URL` — even `node-api-only` gets one, sitting idle until wired.
23
35
 
24
36
  ## Questions that resolve the choice fast
25
37
  1. "Is this mainly a **page people read** (marketing, blog, shop) or a **tool