@cloudgrid-io/mcp 0.20.33 → 0.20.34

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.33",
3
+ "version": "0.20.34",
4
4
  "description": "MCP access to CloudGrid — the live runtime environment where the user's apps run WITH the infrastructure they need out of the box: managed MongoDB, Redis (cache/queues/pub-sub), a pgvector store, persistent disk, and built-in AI with no API keys, in any language or stack, no Docker or Kubernetes to manage. Brainstorm, build, plug, and improve apps, games, dashboards, and tools; instant single-page publishes and full runtime-app deploys with a live URL — for developers and non-technical users. Three editions: local stdio (full toolset), hosted web (light, CLI-free), and a public docs-search server.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -18,7 +18,7 @@ Operating rules:
18
18
  15. Editing an existing thing from just its URL (a fresh chat, no prior context — e.g. "change the background to green here <url>"): call grid_get_app_source(url) first. It resolves the entity_id and returns the current HTML plus its kind, single_html, capabilities, and replug_handle — read those to pick the branch:
19
19
  - Single-HTML inspiration you can re-plug (single_html: true and capabilities.replug: true): edit the returned HTML and call grid_plug with target_entity_id (or grid+slug — the replug_handle) to update the SAME URL in place. This works on every edition, including hosted.
20
20
  - Multi-file app or agent (kind is app or agent, or single_html: false): do NOT try to edit it as one inline HTML file. Tell the user it is a multi-file <kind>, give them the entity_id and the source (source_download_url), and explain that rebuilding it needs the local edition (Claude Desktop/Code) or the CLI — the hosted server cannot rebuild a multi-file app.
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.
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_fork and edit the copy instead.
22
22
  16. Help CloudGrid QA. When you deploy, fill BOTH of grid_plug'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
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_plug 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.
@@ -69,5 +69,5 @@ new_expires_at, role_granted: "owner", ... }`.
69
69
  ## Notes
70
70
 
71
71
  - One pickup claims one entity. To claim several, repeat with each entity id + token.
72
- - The MCP `grid_claim_anonymous_deploy` tool does this for you, and remembers the entity id +
72
+ - The MCP `grid_pickup` tool does this for you, and remembers the entity id +
73
73
  token from the last anonymous drop so you can claim with no arguments.
@@ -1635,6 +1635,41 @@ export async function runFork(ctx, { id, into_org_slug, name, source_version_id
1635
1635
  };
1636
1636
  }
1637
1637
 
1638
+ // Remix: cross-grid "make my own copy". Unlike fork (same-grid, byte-carrying),
1639
+ // remix mints a NEW entity in ANY grid the caller can build in, keeps lineage
1640
+ // back to the source, and strips the source's secrets/connection credentials —
1641
+ // the remixer supplies their own before plugging. Hits POST
1642
+ // /api/v2/runtimes/:id/remix.
1643
+ export async function runRemix(ctx, { id, into_org_slug, name, source_version_id }) {
1644
+ const data = await authedApiCall(ctx, {
1645
+ method: "POST",
1646
+ pathName: `/api/v2/runtimes/${encodeURIComponent(id)}/remix`,
1647
+ body: {
1648
+ ...(into_org_slug ? { into_org_slug } : {}),
1649
+ ...(name ? { name } : {}),
1650
+ ...(source_version_id ? { source_version_id } : {}),
1651
+ },
1652
+ verb: "Remix",
1653
+ });
1654
+ const gridSlug = data?.grid_slug ?? data?.org?.slug ?? null;
1655
+ const lines = [
1656
+ `Remixed: ${data?.name ?? id} (entity_id=${data?.entity_id ?? "?"})${gridSlug ? ` in grid ${gridSlug}` : ""}`,
1657
+ `Lineage: forked_from=${data?.forked_from ?? "?"}. Secrets were not copied — set your own before you plug.`,
1658
+ ];
1659
+ return {
1660
+ text: lines.join("\n"),
1661
+ structured: {
1662
+ entity_id: data?.entity_id ?? null,
1663
+ name: data?.name ?? null,
1664
+ kind: data?.kind ?? null,
1665
+ grid_slug: gridSlug,
1666
+ forked_from: data?.forked_from ?? null,
1667
+ forked_from_version_id: data?.forked_from_version_id ?? null,
1668
+ current_version_id: data?.current_version_id ?? null,
1669
+ },
1670
+ };
1671
+ }
1672
+
1638
1673
  // Download: signed, time-limited (15-minute) source-bundle URLs. No entity is
1639
1674
  // created and no registry state changes.
1640
1675
  export async function runDownload(ctx, { id, version }) {
@@ -23,6 +23,7 @@ import {
23
23
  runClaim,
24
24
  runPlug,
25
25
  runFork,
26
+ runRemix,
26
27
  runDownload,
27
28
  runVisibility,
28
29
  runSource,
@@ -134,19 +135,22 @@ export function registerTools(server, ctx) {
134
135
 
135
136
  // ── Direct-API tools (both editions) ──────────────────────────────────────
136
137
 
137
- // Claim — both editions.
138
+ // Pickup — both editions. Adopts an entity into the signed-in account's active
139
+ // grid (ownership transfer via POST /api/v2/entities/:id/pickup). Pass an
140
+ // anonymous drop's owner token as `claim_token` to take over something you
141
+ // dropped anonymously — that path replaces the old standalone "claim" verb.
138
142
  reg(
139
- "grid_claim_anonymous_deploy",
143
+ "grid_pickup",
140
144
  {
141
- description: "Claim an anonymous drop into the signed-in account, so it becomes owned and stops expiring on the anonymous schedule. Use after the user signs in to keep something they dropped anonymously. The public URL does not change. The claim token IS the drop's owner_token (one bearer capability for both edit and claim — anonymous edits refresh it, so always use the newest). Requires sign-in (grid_login). Calls the API directly.",
145
+ description: "Pick up (adopt) an entity into the signed-in account's active grid — an ownership transfer, keeping the same public URL. Use it to take over an entity you have access to, or to keep an anonymous drop you created: pass the drop's `claim_token` and it becomes owned and stops expiring on the anonymous schedule. Requires sign-in (grid_login). Calls the API directly, so it works on every edition. (This is the direct-API adopt; on the local edition grid_edit_existing_app additionally downloads the source and links the folder for editing.)",
142
146
  inputSchema: {
143
- claim_token: z.string().optional().describe("The claim/owner token from an anonymous drop (owner_token in the drop result; also embedded in claim_url)."),
144
- claim_url: z.string().optional().describe("The claim_url from an anonymous drop; the token is read from it."),
145
- entity_id: z.string().optional().describe("The entity id of the anonymous drop to claim. Defaults to this session's last anonymous drop."),
147
+ entity_id: z.string().optional().describe("The entity id to pick up. Defaults to this session's last anonymous drop."),
148
+ claim_token: z.string().optional().describe("For an anonymous drop: its owner token (the owner_token in the drop result, also embedded in claim_url). Passing it takes over that drop."),
149
+ claim_url: z.string().optional().describe("Alternative to claim_token for an anonymous drop — the claim_url from the drop result; the token is read from it."),
146
150
  },
147
151
  outputSchema: {
148
- claimed: z.number().describe("Number of drops claimed."),
149
- urls: z.array(z.string()).describe("URLs of the claimed drops."),
152
+ claimed: z.number().describe("Number of entities picked up."),
153
+ urls: z.array(z.string()).describe("Public URLs of the picked-up entities."),
150
154
  },
151
155
  annotations: { readOnlyHint: false, destructiveHint: false, openWorldHint: true },
152
156
  },
@@ -432,17 +436,20 @@ export function registerTools(server, ctx) {
432
436
  // grid_plug's own description, so deploy-intent routing lands on grid_plug.
433
437
  registerAlias("grid_deploy", "grid_plug");
434
438
 
435
- // ── grid_copy_app / grid_download_source — direct-API verbs (spec v2 §5–6) ──────
439
+ // ── grid_fork / grid_remix / grid_download — direct-API copy/source verbs ──────
440
+ // fork = same-grid byte-carrying copy; remix = cross-grid copy with secrets
441
+ // stripped; download = signed source-bundle URLs. All direct-API (both editions).
436
442
  reg(
437
- "grid_copy_app",
443
+ "grid_fork",
438
444
  {
439
445
  description:
440
- "Start a NEW entity from an existing runtime (copy-on-write, lineage recorded). Lands in the " +
441
- "source's home grid by default; cross-grid only for system templates or forkable:'public' sources. " +
442
- "Requires sign-in. Kind-aware: forks a runtime (app/agent) OR an inspiration — " +
443
- "tries POST /api/v2/runtimes/:id/fork and falls back to /api/v2/inspirations/:id/fork.",
446
+ "Fork an entity: start a NEW entity from an existing one (copy-on-write, lineage recorded), carrying " +
447
+ "the source bytes. Lands in the source's home grid by default; cross-grid only for system templates " +
448
+ "or forkable:'public' sources — for a general cross-grid copy use grid_remix instead. Requires sign-in. " +
449
+ "Kind-aware: forks a runtime (app/agent) OR an inspiration — tries POST /api/v2/runtimes/:id/fork and " +
450
+ "falls back to /api/v2/inspirations/:id/fork.",
444
451
  inputSchema: {
445
- id: z.string().describe("The source runtime: a canonical UUID or <grid-slug>/<entity-slug>."),
452
+ id: z.string().describe("The source entity: a canonical UUID or <grid-slug>/<entity-slug>."),
446
453
  into_org_slug: z.string().optional().describe("Destination grid slug. Required only when you belong to more than one grid."),
447
454
  name: z.string().optional().describe("Slug for the new entity. Omit to derive one from the source."),
448
455
  source_version_id: z.string().optional().describe("Fork an older version instead of HEAD, e.g. v_a1b2c3d."),
@@ -469,12 +476,49 @@ export function registerTools(server, ctx) {
469
476
  );
470
477
 
471
478
  reg(
472
- "grid_download_source",
479
+ "grid_remix",
473
480
  {
474
481
  description:
475
- "Fetch the source bundle last deployed for a runtime: one signed, time-limited (15-minute) read URL " +
476
- "per service tarball. No entity is created and no registry state changes. Requires sign-in. Calls " +
477
- "GET /api/v2/runtimes/:id/source directly.",
482
+ "Remix an app: make your own copy of any app you can SEE (link/public/authenticated, or a grid you " +
483
+ "belong to) into ANY grid you can build in — cross-grid is allowed. Mints a NEW entity with lineage " +
484
+ "back to the source, WITHOUT the source's secrets or connection credentials (set your own before you " +
485
+ "plug). Distinct from grid_fork, which is same-grid and byte-carrying. Requires sign-in. Calls POST " +
486
+ "/api/v2/runtimes/:id/remix.",
487
+ inputSchema: {
488
+ id: z.string().describe("The source app: a canonical UUID or <grid-slug>/<entity-slug>."),
489
+ into_org_slug: z.string().optional().describe("Grid to create your copy in. Required only when you belong to more than one grid."),
490
+ name: z.string().optional().describe("Slug for your copy. Omit to derive one from the source."),
491
+ source_version_id: z.string().optional().describe("Remix an older version instead of HEAD, e.g. v_a1b2c3d."),
492
+ },
493
+ outputSchema: {
494
+ entity_id: z.string().nullable().describe("Your copy's entity id."),
495
+ name: z.string().nullable().describe("Your copy's slug."),
496
+ kind: z.string().nullable().describe("app | agent."),
497
+ grid_slug: z.string().nullable().describe("The grid your copy landed in."),
498
+ forked_from: z.string().nullable().describe("Source entity_id."),
499
+ forked_from_version_id: z.string().nullable().describe("Source version, when a specific one was remixed."),
500
+ current_version_id: z.string().nullable().describe("Your copy's current version id."),
501
+ },
502
+ annotations: { readOnlyHint: false, destructiveHint: false, openWorldHint: true },
503
+ },
504
+ async (input) => {
505
+ try {
506
+ if (!input?.id) return fail("`id` is required (a canonical UUID or <grid-slug>/<entity-slug>).");
507
+ return okResult(await runRemix(ctx, input));
508
+ } catch (err) {
509
+ return fail(err.message);
510
+ }
511
+ },
512
+ );
513
+
514
+ reg(
515
+ "grid_download",
516
+ {
517
+ description:
518
+ "Download an entity's source: the bundle last deployed for a runtime, as one signed, time-limited " +
519
+ "(15-minute) read URL per service tarball. No entity is created and no registry state changes (unlike " +
520
+ "grid_pickup/grid_fork/grid_remix, which adopt or copy). Requires sign-in. Calls GET " +
521
+ "/api/v2/runtimes/:id/source directly.",
478
522
  inputSchema: {
479
523
  id: z.string().describe("The runtime to download: a canonical UUID or <grid-slug>/<entity-slug>."),
480
524
  version: z.string().optional().describe("Download an older version's bundle instead of HEAD, e.g. v_a1b2c3d."),
@@ -511,7 +555,7 @@ export function registerTools(server, ctx) {
511
555
  "(replug/fork), and replug_handle — read those to decide whether to edit in place (single-HTML + " +
512
556
  "capabilities.replug), fall back for a multi-file app/agent (use source_download_url + the local " +
513
557
  "edition/CLI), or offer a fork when it isn't yours. For multi-file or runtime (app/agent) source " +
514
- "bundles, use grid_download_source (signed tarball URLs). Reads the HTML from the API server-side; " +
558
+ "bundles, use grid_download (signed tarball URLs). Reads the HTML from the API server-side; " +
515
559
  "read-only, creates nothing.",
516
560
  inputSchema: {
517
561
  entity_id: z.string().optional().describe("The drop's durable id. Defaults to this session's last drop."),
@@ -938,6 +982,15 @@ export function registerTools(server, ctx) {
938
982
  // the local cutoff: grid_visibility is a both-editions tool, so the alias has
939
983
  // to register on the web edition too (hosted callers keep back-compat).
940
984
  registerAlias("grid_set_sharing", "grid_visibility");
985
+ // Copy/adopt verbs renamed to match the CLI (fork / remix / pickup / download).
986
+ // Old names kept as redirect-only deprecated aliases; all both-editions tools,
987
+ // so they must register above the local cutoff for hosted back-compat.
988
+ // grid_claim_anonymous_deploy → grid_pickup drops the "claim" naming; claiming
989
+ // an anonymous drop is now grid_pickup with a claim_token (mirrors the CLI's
990
+ // `grid pickup --claim-token`).
991
+ registerAlias("grid_copy_app", "grid_fork");
992
+ registerAlias("grid_claim_anonymous_deploy", "grid_pickup");
993
+ registerAlias("grid_download_source", "grid_download");
941
994
 
942
995
  if (ctx.edition !== "local") return; // web edition stops here — no CLI tools
943
996