@cloudgrid-io/mcp 0.20.17 → 0.20.19

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cloudgrid-io/mcp",
3
- "version": "0.20.17",
3
+ "version": "0.20.19",
4
4
  "description": "Use CloudGrid via MCP to brainstorm, build, deploy, and improve apps, games, websites, dashboards, and tools and get a live URL - templates, recipes, managed databases and AI, instant single-page publish, and full-app deploys, for developers and non-technical users. Three editions: local stdio (full toolset), hosted web (light, CLI-free), and a public docs-search server, over MCP.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -29,6 +29,7 @@
29
29
  "test:grid-picker": "node test/grid-picker.test.mjs",
30
30
  "test:plug-output-schema": "node test/plug-output-schema.test.mjs",
31
31
  "test:zip-deploy": "node test/zip-deploy.test.mjs",
32
+ "test:create-gates": "node test/create-gates.test.mjs",
32
33
  "test:mcpb-boot": "node test/mcpb-boot.test.mjs",
33
34
  "test:plug-timeout": "node test/plug-timeout.test.mjs",
34
35
  "test:manifest-confirm": "node test/manifest-confirm.test.mjs",
@@ -7,7 +7,7 @@ Operating rules:
7
7
  4. Always return the live share URL at the end — that is the deliverable. On a NEW deploy, then ASK the user who should be able to open it — private (only you), a space, your grid, anyone with the link, or anyone signed in — and set their choice with grid_set_sharing. Never pick the visibility silently. On a re-plug/edit of an existing entity, leave its current visibility as-is unless the user asks to change it.
8
8
  5. Brainstorm first (lightly) for a real app, then minimize questions. For a substantial runtime app, take ONE lightweight beat before generating/deploying: confirm the goal + 3-5 core features in a sentence, check for a matching template/recipe (capability-map), and infer the data/runtime needs and STATE them ("I'll add a database so entries are saved"). Keep it to a line or two; never ask a non-technical user infra questions they can't answer. A simple single page skips this and builds immediately. Otherwise use sensible defaults and build; don't front-load setup questions.
9
9
  6. If a signed-in publish fails with a server error, do not fall back to anonymous publishing (it burns the anonymous quota and downgrades ownership); surface the error, use the CLI fallback if offered, or ask the user.
10
- 7. When signed in and the user has more than one grid, do not assume a target — the publish tools will ask; relay the choice to the user and pass the chosen grid.
10
+ 7. When signed in and the user has more than one grid, do not assume a target. For a NEW app, establish the grid UP FRONT — grid_start returns the grids; if there is more than one, ask "which grid should this go on?" before you start building, and carry that slug into grid_deploy. As a backstop, grid_deploy hard-gates this: an authed create with >1 grid and no chosen grid returns needs_grid (the picker) instead of deploying; a create with no auth and no anon returns needs_auth (sign-in vs anonymous) instead of silently publishing anonymously. Relay either ask to the user; never pick silently.
11
11
  8. When a build/deploy fails unexpectedly, offer to report it to the CloudGrid team — only with the user's explicit consent (ask first). Send just the error + the failed request by default (call grid_report), and never send the whole conversation unless the user agrees (include_conversation). Respect privacy.
12
12
  9. To modify an existing page when you don't already have its HTML in context, first call grid_get_app_source to fetch the current HTML, apply your change, then call grid_deploy with target_entity_id (the entity_id) to update the SAME URL in place. Do not ask the user to paste the HTML back.
13
13
  10. Publishing a single HTML page: pass it inline as grid_deploy's html parameter (a full self-contained document). For a heavy or image-heavy page in the local edition, pass the path parameter instead so it is read from disk (no inline size limit); never base64-encode HTML and never pass a file path (or an @-prefixed path) as html. If a page looks empty, use grid_get_app_source to check what was actually published, then re-plug with the real HTML/path and target_entity_id.
@@ -21,5 +21,7 @@ Operating rules:
21
21
  - Not yours (capabilities.replug: false, reason not_owner): do NOT attempt a re-plug. Offer to fork it into the user's own grid with grid_copy_app and edit the copy instead.
22
22
  16. Help CloudGrid QA. When you deploy, fill BOTH of grid_deploy's user_request (a brief version of the user's ask) AND session_note (one short paragraph on what you built and why) — include both by default; omit only if the user objects. A successful deploy posts the QA log on that same call, so do NOT call grid_note after deploying — session_note is the narrative path. Use grid_note only BEFORE a deploy, or in a session that ends without one. Everything here is recorded for quality review and never changes the deploy.
23
23
 
24
+ 17. Auth check before an owned deploy, with an anonymous fallback. grid_start reports signed_in. If the user wants to publish and is NOT signed in (or the deploy returns a create-time 401), do NOT silently fail and do NOT invent an auth flow — ask them plainly: "Sign in to publish to your grid, or I can publish it anonymously right now (it goes live immediately with a claim link so you can save it to your account later)?" If they choose anonymous, re-call grid_deploy with anon: true and surface the claim_url + owner_token. This is distinct from rule 6: rule 6 forbids the anon fallback on a SERVER error (5xx) for a signed-in user (it burns quota and downgrades ownership); THIS rule is about not being authenticated at all, where anonymous is a legitimate, offered choice.
25
+
24
26
  Deploy is via grid_deploy on every edition: for a single HTML page pass it inline as the html param (works on the hosted MCP too); for a multi-file app write the files and pass a folder path (local MCP / CLI). A single HTML page deploys synchronously as an inspiration, so you get a URL right away. A user-supplied ZIP archive (local edition): pass its path directly as the path param — it is extracted and deployed as a static app; if the zip is assets-only (e.g. images for a gallery), generate the page and pass it as html alongside the zip path, and it becomes the index.html over the archive's files.
25
27
  When you deploy a folder that already has a cloudgrid.yaml, grid_deploy returns needs_confirmation on the first create instead of deploying — it's asking whether to create a NEW app. Relay that to the user, and once they say yes re-call grid_deploy with confirm_new_app: true. To update an existing app instead, pass its target_entity_id.
@@ -964,7 +964,7 @@ export function errorGuidance({ status, code, edition, isEdit, isAnon, signedIn
964
964
  if (status === 401) {
965
965
  return isEdit
966
966
  ? "That did not authorize this entity (wrong entity, expired, or already claimed). Sign in if you own it (grid_login), pass its owner_token for an anonymously-created drop, or omit target_entity_id to create a new entity."
967
- : "Sign in (grid_login), or for an anonymously-created drop pass its owner_token.";
967
+ : "Not signed in. Ask the user: sign in (grid_login) to publish to their grid, OR publish anonymously now (re-call grid_deploy with anon: true) - it goes live immediately with a claim_url + owner_token to claim into their account later. Do not silently fail; offer both.";
968
968
  }
969
969
  if (status === 403) {
970
970
  return "You lack the role to plug this target. To re-plug someone else's entity, pick it up first (grid_edit_existing_app / grid_claim_anonymous_deploy).";
@@ -288,6 +288,7 @@ export function registerTools(server, ctx) {
288
288
  // they validate: (1) the grid-picker "which grid?" ask (resolveGridOrAsk),
289
289
  // and (2) the signed-in CLI-fallback recovery.
290
290
  needs_grid: z.boolean().optional().describe("Grid-picker ask: a signed-in user with >1 grid must choose one before this create proceeds. Pass the chosen slug back in `grid`."),
291
+ needs_auth: z.boolean().optional().describe("Auth gate: a NEW deploy was attempted while not signed in. Ask the user to sign in (grid_login) to publish to their grid, OR re-call grid_deploy with anon: true to publish anonymously (claimable later). Do not proceed silently."),
291
292
  needs_org: z.boolean().optional().describe("Alias of needs_grid (legacy picker alias)."),
292
293
  grids: z.array(z.object({
293
294
  slug: z.string(),
@@ -357,7 +358,20 @@ export function registerTools(server, ctx) {
357
358
  }
358
359
  if (input?.anon !== true && !isEdit) {
359
360
  const token = await ctx.getToken();
360
- if (token) {
361
+ if (!token) {
362
+ // AUTH HARD GATE (create only): not signed in and not anon → do NOT
363
+ // silently ride the anonymous wire. Return the login-or-anon choice
364
+ // and stop. The model relays it; the user picks. (Rule 17, enforced.)
365
+ return okResult({
366
+ text:
367
+ "You're not signed in. To publish this I can either:\n" +
368
+ " 1. Sign you in to publish to your grid — run grid_login (opens your browser), then I'll deploy.\n" +
369
+ " 2. Publish it anonymously right now — it goes live immediately with a claim link so you can save it to your account later.\n" +
370
+ "Which would you like? (For anonymous, re-call grid_deploy with anon: true.)",
371
+ structured: { needs_auth: true },
372
+ });
373
+ }
374
+ {
361
375
  const decision = await resolveGridOrAsk(ctx, {
362
376
  token,
363
377
  suppliedGrid: input?.grid,