@cloudgrid-io/mcp 0.20.38 → 0.20.39
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/tools/deploy.js +16 -16
- package/src/tools/register.js +6 -6
- package/src/widgets/org-picker.html +3 -3
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cloudgrid-io/mcp",
|
|
3
|
-
"version": "0.20.
|
|
3
|
+
"version": "0.20.39",
|
|
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/tools/deploy.js
CHANGED
|
@@ -83,7 +83,7 @@ export async function resolveGridOrAsk(ctx, { token, suppliedGrid, edition }, de
|
|
|
83
83
|
if (a.render_ready !== b.render_ready) return b.render_ready ? 1 : -1;
|
|
84
84
|
return 0;
|
|
85
85
|
});
|
|
86
|
-
const lines = ["Which grid should this be
|
|
86
|
+
const lines = ["Which grid should this be plugged into?"];
|
|
87
87
|
for (const o of annotated) {
|
|
88
88
|
const tags = [];
|
|
89
89
|
if (o.is_active) tags.push("your active grid");
|
|
@@ -787,7 +787,7 @@ async function expandZipToProject(zipPath, inlineHtml) {
|
|
|
787
787
|
const hasInlineHtml = typeof inlineHtml === "string" && inlineHtml.length > 0;
|
|
788
788
|
if (hasInlineHtml && hasManifest) {
|
|
789
789
|
throw new Error(
|
|
790
|
-
"The zip already contains a cloudgrid.yaml project —
|
|
790
|
+
"The zip already contains a cloudgrid.yaml project — plug it as-is (drop the `html` param), " +
|
|
791
791
|
"or re-plug the entity and edit its files instead.",
|
|
792
792
|
);
|
|
793
793
|
}
|
|
@@ -895,7 +895,7 @@ async function plugZipProjectViaCli(ctx, { projectDir, name }, input, deps = {})
|
|
|
895
895
|
const url = parseCliPlugUrl(stdout);
|
|
896
896
|
if (!url) {
|
|
897
897
|
throw new Error(
|
|
898
|
-
`The zip project
|
|
898
|
+
`The zip project was plugged via the CLI but no live URL was found in its output.\n${stdout.slice(0, 500)}`,
|
|
899
899
|
);
|
|
900
900
|
}
|
|
901
901
|
return {
|
|
@@ -996,7 +996,7 @@ export function errorGuidance({ status, code, edition, isEdit, isAnon, signedIn
|
|
|
996
996
|
}
|
|
997
997
|
// 409 EDIT_REJECTED — an in-place re-plug the server won't take.
|
|
998
998
|
if (status === 409) {
|
|
999
|
-
return "The entity cannot be updated right now (a
|
|
999
|
+
return "The entity cannot be updated right now (a plug is in progress, or it is archived/expired/claimed). An explicit re-plug never silently creates; retry later, or omit target_entity_id to create a new entity.";
|
|
1000
1000
|
}
|
|
1001
1001
|
// 401 on an edit — the credential didn't authorize this entity.
|
|
1002
1002
|
if (status === 401) {
|
|
@@ -1005,7 +1005,7 @@ export function errorGuidance({ status, code, edition, isEdit, isAnon, signedIn
|
|
|
1005
1005
|
: "Not signed in. Ask the user: sign in (grid_login) to publish to their grid, OR publish anonymously now (re-call grid_plug 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.";
|
|
1006
1006
|
}
|
|
1007
1007
|
if (status === 403) {
|
|
1008
|
-
return "You lack the role to plug this target. To re-plug someone else's entity,
|
|
1008
|
+
return "You lack the role to plug this target. To re-plug someone else's entity, pull it first (grid_pull), or make your own copy (grid_pickup).";
|
|
1009
1009
|
}
|
|
1010
1010
|
// ── Consent-gated report offer (Task 34) ──────────────────────────────────
|
|
1011
1011
|
// GENUINE bugs only: a build/deploy failure, any 5xx, INTERNAL_ERROR, or an
|
|
@@ -1152,11 +1152,11 @@ export async function runPlug(ctx, input, deps = {}) {
|
|
|
1152
1152
|
if (target_entity_id || (slug && grid)) {
|
|
1153
1153
|
throw new Error(
|
|
1154
1154
|
"Re-plugging an existing entity from a zip is not supported yet — pick up the app " +
|
|
1155
|
-
"(grid_edit_existing_app) and re-plug the folder, or
|
|
1155
|
+
"(grid_edit_existing_app) and re-plug the folder, or plug the zip as a new entity.",
|
|
1156
1156
|
);
|
|
1157
1157
|
}
|
|
1158
1158
|
if (anon) {
|
|
1159
|
-
throw new Error("A zip
|
|
1159
|
+
throw new Error("A zip plug creates a static app and needs sign-in — it cannot be anonymous.");
|
|
1160
1160
|
}
|
|
1161
1161
|
const expanded = await expandZipToProject(srcPath, hasHtml ? html : null);
|
|
1162
1162
|
if (expanded.singleHtml) {
|
|
@@ -1371,9 +1371,9 @@ export async function runPlug(ctx, input, deps = {}) {
|
|
|
1371
1371
|
} catch (err) {
|
|
1372
1372
|
if (err?.name === "AbortError" || err?.name === "TimeoutError") {
|
|
1373
1373
|
throw new Error(
|
|
1374
|
-
`The
|
|
1375
|
-
`The build may still be running on CloudGrid — check the
|
|
1376
|
-
`(poll_url / grid_status, or your grid) before
|
|
1374
|
+
`The plug request timed out after ${Math.round(uploadTimeoutMs / 1000)}s. ` +
|
|
1375
|
+
`The build may still be running on CloudGrid — check the build status ` +
|
|
1376
|
+
`(poll_url / grid_status, or your grid) before plugging again, so you don't create a duplicate.`,
|
|
1377
1377
|
);
|
|
1378
1378
|
}
|
|
1379
1379
|
throw new Error(`Could not reach CloudGrid at ${API_BASE}: ${err.message}`);
|
|
@@ -1558,7 +1558,7 @@ export async function runPlug(ctx, input, deps = {}) {
|
|
|
1558
1558
|
: "npx -y @cloudgrid-io/cli plug";
|
|
1559
1559
|
cliSteer =
|
|
1560
1560
|
"Heads-up: this went in INLINE. For a multi-file app the disk-based CLI is more reliable (lockfiles/binaries can't truncate), but it must run where the CLI is already signed in — a terminal or Claude Code that has done `grid login` once. Do NOT try `grid login` inside a chat sandbox: its login poll is long-lived and the sandbox is ephemeral, so it won't stick. " +
|
|
1561
|
-
`If you had to leave any files out to make this inline
|
|
1561
|
+
`If you had to leave any files out to make this inline plug safe, tell the user — offer the complete plug from Claude Code or a terminal (\`${replug}\`), or confirm they accept the reduced version. Do not drop files silently.`;
|
|
1562
1562
|
}
|
|
1563
1563
|
}
|
|
1564
1564
|
|
|
@@ -1566,8 +1566,8 @@ export async function runPlug(ctx, input, deps = {}) {
|
|
|
1566
1566
|
if (isBuilding) {
|
|
1567
1567
|
lines.push(
|
|
1568
1568
|
isEdit
|
|
1569
|
-
? `Building (async): ${url} — the update is
|
|
1570
|
-
: `Building (async): ${url} — the
|
|
1569
|
+
? `Building (async): ${url} — the update is plugging, not live yet.`
|
|
1570
|
+
: `Building (async): ${url} — the plug is in progress, not live yet.`,
|
|
1571
1571
|
);
|
|
1572
1572
|
// Point at a tool that exists on THIS edition. grid_status is CLI-wrapping,
|
|
1573
1573
|
// local-only — telling a hosted (ChatGPT/claude.ai) model to "run
|
|
@@ -1584,7 +1584,7 @@ export async function runPlug(ctx, input, deps = {}) {
|
|
|
1584
1584
|
lines.push(`Updated in place: ${url}`);
|
|
1585
1585
|
} else if (isInspirationCreate) {
|
|
1586
1586
|
// Authed inspiration create — owned by the caller. Wording mirrors the drop verb.
|
|
1587
|
-
lines.push(ctx.edition === "web" ? `Your app is live: ${url}` : `
|
|
1587
|
+
lines.push(ctx.edition === "web" ? `Your app is live: ${url}` : `Plugged into your grid: ${url}`);
|
|
1588
1588
|
if (ctx.edition !== "web") lines.push("Owned by you.");
|
|
1589
1589
|
} else {
|
|
1590
1590
|
lines.push(`Live: ${url}`);
|
|
@@ -1940,7 +1940,7 @@ export async function runCheckDeploy(ctx, { poll_url, grid } = {}) {
|
|
|
1940
1940
|
});
|
|
1941
1941
|
if (!target) {
|
|
1942
1942
|
throw new Error(
|
|
1943
|
-
"No build to check. Pass poll_url from a grid_plug result, or
|
|
1943
|
+
"No build to check. Pass poll_url from a grid_plug result, or plug something first in this session. (Instant inspiration plugs are live on return and have no build to poll.)",
|
|
1944
1944
|
);
|
|
1945
1945
|
}
|
|
1946
1946
|
const verdict = await fetchDeployTrace(ctx, { pollUrl: target, grid: gridSlug });
|
|
@@ -1953,7 +1953,7 @@ export async function runCheckDeploy(ctx, { poll_url, grid } = {}) {
|
|
|
1953
1953
|
}
|
|
1954
1954
|
if (verdict.status === "failed") {
|
|
1955
1955
|
return {
|
|
1956
|
-
text: `The build FAILED: ${verdict.error || "no reason reported"}. The URL is not live — do not give it to the user as working. Fix the app (or re-
|
|
1956
|
+
text: `The build FAILED: ${verdict.error || "no reason reported"}. The URL is not live — do not give it to the user as working. Fix the app (or re-plug) and try again.` +
|
|
1957
1957
|
formatFailureDetail(verdict),
|
|
1958
1958
|
structured: {
|
|
1959
1959
|
status: "failed",
|
package/src/tools/register.js
CHANGED
|
@@ -104,7 +104,7 @@ export function registerTools(server, ctx) {
|
|
|
104
104
|
}));
|
|
105
105
|
|
|
106
106
|
server.registerResource("cloudgrid-org-picker", GRID_PICKER_URI, {
|
|
107
|
-
description: "Grid picker card — lets the user choose which grid to
|
|
107
|
+
description: "Grid picker card — lets the user choose which grid to plug into.",
|
|
108
108
|
mimeType: "text/html;profile=mcp-app",
|
|
109
109
|
}, async () => ({
|
|
110
110
|
contents: [{
|
|
@@ -194,7 +194,7 @@ export function registerTools(server, ctx) {
|
|
|
194
194
|
// acknowledgement.
|
|
195
195
|
regTool(
|
|
196
196
|
"grid_note",
|
|
197
|
-
"Optionally leave a one-paragraph summary of what you built this session and why. Call it BEFORE a
|
|
197
|
+
"Optionally leave a one-paragraph summary of what you built this session and why. Call it BEFORE a plug, or in a session that ends without one — a successful plug has already posted the QA log, so pass grid_plug's session_note instead. Recorded for CloudGrid QA. No side effects.",
|
|
198
198
|
{ summary: z.string().describe("A short plain-language summary of what was built and why.") },
|
|
199
199
|
{ readOnlyHint: true, destructiveHint: false, openWorldHint: false },
|
|
200
200
|
async (input) => {
|
|
@@ -229,7 +229,7 @@ export function registerTools(server, ctx) {
|
|
|
229
229
|
path: z.string().optional().describe(
|
|
230
230
|
"Local edition: path to the entity folder, a single file, or a .zip ARCHIVE to upload. " +
|
|
231
231
|
"A folder is read recursively, honoring .gitignore/.cloudgridignore (plus .git/node_modules " +
|
|
232
|
-
"always skipped). A .zip is extracted and
|
|
232
|
+
"always skipped). A .zip is extracted and plugged: with its own cloudgrid.yaml it plugs " +
|
|
233
233
|
"as that project; otherwise it becomes a static app (all files served). Use this for " +
|
|
234
234
|
"\"deploy this zip\" / \"gallery from these zipped images\" requests. Mutually exclusive with " +
|
|
235
235
|
"`html` and `artifact_files` — EXCEPT `html` + a .zip of assets: the html becomes index.html " +
|
|
@@ -251,7 +251,7 @@ export function registerTools(server, ctx) {
|
|
|
251
251
|
"On re-plug, a name: change is a warning only; it never renames the entity or moves the URL.",
|
|
252
252
|
),
|
|
253
253
|
target_entity_id: z.string().optional().describe(
|
|
254
|
-
"Present → RE-PLUG: update this exact entity in place (same entity_id, slug, URL,
|
|
254
|
+
"Present → RE-PLUG: update this exact entity in place (same entity_id, slug, URL, plug history). " +
|
|
255
255
|
"Absent → CREATE a new entity. This is the durable handle a previous plug returned — persist it. " +
|
|
256
256
|
"Re-plugging an anonymously-created drop needs its owner_token instead of sign-in.",
|
|
257
257
|
),
|
|
@@ -279,7 +279,7 @@ export function registerTools(server, ctx) {
|
|
|
279
279
|
"target_entity_id). Re-minted on every anonymous edit; always persist the newest one from the result.",
|
|
280
280
|
),
|
|
281
281
|
confirm_new_app: z.boolean().optional().describe(
|
|
282
|
-
"Set true to confirm
|
|
282
|
+
"Set true to confirm plugging a source that already contains a cloudgrid.yaml as a NEW runtime app. " +
|
|
283
283
|
"On a create, if the source has a cloudgrid.yaml and this is not set, grid_plug returns needs_confirmation " +
|
|
284
284
|
"so you can ask the user first (or use target_entity_id to re-plug an existing entity).",
|
|
285
285
|
),
|
|
@@ -288,7 +288,7 @@ export function registerTools(server, ctx) {
|
|
|
288
288
|
"include it by default; omit only if the user asked not to share it.",
|
|
289
289
|
),
|
|
290
290
|
session_note: z.string().optional().describe(
|
|
291
|
-
"One short paragraph on what you built and why. Recorded for CloudGrid QA alongside the
|
|
291
|
+
"One short paragraph on what you built and why. Recorded for CloudGrid QA alongside the plug.",
|
|
292
292
|
),
|
|
293
293
|
};
|
|
294
294
|
|
|
@@ -44,7 +44,7 @@ body{font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif;b
|
|
|
44
44
|
var html =
|
|
45
45
|
'<div class="card">' +
|
|
46
46
|
'<div class="title">Choose a grid</div>' +
|
|
47
|
-
'<div class="subtitle">Which grid should this be
|
|
47
|
+
'<div class="subtitle">Which grid should this be plugged into?</div>' +
|
|
48
48
|
'<div class="orgs">';
|
|
49
49
|
for (var i = 0; i < items.length; i++) {
|
|
50
50
|
var org = items[i];
|
|
@@ -66,7 +66,7 @@ body{font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif;b
|
|
|
66
66
|
var all = document.querySelectorAll(".org-btn");
|
|
67
67
|
for (var k = 0; k < all.length; k++) all[k].classList.add("loading");
|
|
68
68
|
var statusEl = document.getElementById("status");
|
|
69
|
-
if (statusEl) statusEl.textContent = "
|
|
69
|
+
if (statusEl) statusEl.textContent = "Plugging\u2026";
|
|
70
70
|
if (!window.openai || !window.openai.callTool) return;
|
|
71
71
|
var input = (window.openai && window.openai.toolInput) || {};
|
|
72
72
|
var args = {};
|
|
@@ -79,7 +79,7 @@ body{font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif;b
|
|
|
79
79
|
// The host will render the new result with the live-result card.
|
|
80
80
|
}).catch(function () {
|
|
81
81
|
for (var m = 0; m < all.length; m++) all[m].classList.remove("loading");
|
|
82
|
-
if (statusEl) statusEl.textContent = "Failed to
|
|
82
|
+
if (statusEl) statusEl.textContent = "Failed to plug";
|
|
83
83
|
});
|
|
84
84
|
});
|
|
85
85
|
})(buttons[j]);
|