@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 +1 -1
- package/src/corpus/playbook.md +1 -1
- package/src/corpus/skill-claim.md +1 -1
- package/src/tools/deploy.js +35 -0
- package/src/tools/register.js +73 -20
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cloudgrid-io/mcp",
|
|
3
|
-
"version": "0.20.
|
|
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": {
|
package/src/corpus/playbook.md
CHANGED
|
@@ -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
|
|
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 `
|
|
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.
|
package/src/tools/deploy.js
CHANGED
|
@@ -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 }) {
|
package/src/tools/register.js
CHANGED
|
@@ -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
|
-
//
|
|
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
|
-
"
|
|
143
|
+
"grid_pickup",
|
|
140
144
|
{
|
|
141
|
-
description: "
|
|
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
|
-
|
|
144
|
-
|
|
145
|
-
|
|
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
|
|
149
|
-
urls: z.array(z.string()).describe("URLs of the
|
|
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
|
-
// ──
|
|
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
|
-
"
|
|
443
|
+
"grid_fork",
|
|
438
444
|
{
|
|
439
445
|
description:
|
|
440
|
-
"
|
|
441
|
-
"source's home grid by default; cross-grid only for system templates
|
|
442
|
-
"
|
|
443
|
-
"tries POST /api/v2/runtimes/:id/fork and
|
|
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
|
|
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
|
-
"
|
|
479
|
+
"grid_remix",
|
|
473
480
|
{
|
|
474
481
|
description:
|
|
475
|
-
"
|
|
476
|
-
"
|
|
477
|
-
"
|
|
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
|
|
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
|
|