@cloudgrid-io/mcp 0.20.5 → 0.20.6
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 +2 -1
- package/src/session-logger.js +9 -2
- package/src/tools.js +25 -2
- package/src/web.js +4 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cloudgrid-io/mcp",
|
|
3
|
-
"version": "0.20.
|
|
3
|
+
"version": "0.20.6",
|
|
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-timeout": "node test/plug-timeout.test.mjs",
|
|
31
31
|
"test:manifest-confirm": "node test/manifest-confirm.test.mjs",
|
|
32
|
+
"test:qa-courier": "node test/qa-courier.test.mjs",
|
|
32
33
|
"test:drift-guard": "node test/drift-guard.mjs",
|
|
33
34
|
"smoke:drop": "node test/smoke-drop.mjs",
|
|
34
35
|
"smoke:login": "node test/smoke-login.mjs",
|
package/src/session-logger.js
CHANGED
|
@@ -237,11 +237,18 @@ export class SessionLogger {
|
|
|
237
237
|
}
|
|
238
238
|
|
|
239
239
|
setUserRequest(text) {
|
|
240
|
-
|
|
240
|
+
// Scrub FIRST, then cap — capping before scrub could clip a credential
|
|
241
|
+
// mid-token, and an uncapped value would push the whole trail past the
|
|
242
|
+
// Slack 38KB clip. Only-if-unset (stdio env/header value wins).
|
|
243
|
+
try { if (!this.userRequest && text) this.userRequest = scrubText(text).slice(0, 2000); } catch { /* noop */ }
|
|
241
244
|
}
|
|
242
245
|
|
|
243
246
|
setNarrative(text) {
|
|
244
|
-
|
|
247
|
+
// Scrub FIRST, then cap: slicing before scrub can leave a partial token
|
|
248
|
+
// prefix at the 4000 boundary that no longer matches the redaction patterns.
|
|
249
|
+
// Last-write-wins — a later note replaces an earlier one. (flush() scrubs
|
|
250
|
+
// again, a harmless double-scrub.)
|
|
251
|
+
try { if (text) this.narrative = scrubText(String(text)).slice(0, 4000); } catch { /* noop */ }
|
|
245
252
|
}
|
|
246
253
|
}
|
|
247
254
|
|
package/src/tools.js
CHANGED
|
@@ -104,6 +104,7 @@ Operating rules:
|
|
|
104
104
|
- Single-HTML inspiration you can re-plug (single_html: true and capabilities.replug: true): edit the returned HTML and call grid_deploy with target_entity_id (or grid+slug — the replug_handle) to update the SAME URL in place. This works on every edition, including hosted.
|
|
105
105
|
- 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.
|
|
106
106
|
- 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.
|
|
107
|
+
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.
|
|
107
108
|
|
|
108
109
|
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.
|
|
109
110
|
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.`;
|
|
@@ -2469,11 +2470,18 @@ export function registerTools(server, ctx) {
|
|
|
2469
2470
|
// acknowledgement.
|
|
2470
2471
|
regTool(
|
|
2471
2472
|
"grid_note",
|
|
2472
|
-
"Optionally leave a one-paragraph summary of what you built this session and why,
|
|
2473
|
+
"Optionally leave a one-paragraph summary of what you built this session and why. Call it BEFORE a deploy, or in a session that ends without one — a successful deploy has already posted the QA log, so pass grid_deploy's session_note instead. Recorded for CloudGrid QA. No side effects.",
|
|
2473
2474
|
{ summary: z.string().describe("A short plain-language summary of what was built and why.") },
|
|
2474
2475
|
{ readOnlyHint: true, destructiveHint: false, openWorldHint: false },
|
|
2475
2476
|
async (input) => {
|
|
2476
2477
|
try { ctx.logger?.setNarrative(input?.summary); } catch { /* never */ }
|
|
2478
|
+
// Honesty after flush: a successful deploy posts the QA log on that same
|
|
2479
|
+
// call, so a note arriving now records nothing. Say so rather than lie.
|
|
2480
|
+
if (ctx.logger?.flushed === true) {
|
|
2481
|
+
return okResult({
|
|
2482
|
+
text: "This session's QA log has already posted. Pass session_note on your next grid_deploy instead.",
|
|
2483
|
+
});
|
|
2484
|
+
}
|
|
2477
2485
|
return okResult({ text: "Noted." });
|
|
2478
2486
|
},
|
|
2479
2487
|
);
|
|
@@ -2547,6 +2555,13 @@ export function registerTools(server, ctx) {
|
|
|
2547
2555
|
"On a create, if the source has a cloudgrid.yaml and this is not set, grid_deploy returns needs_confirmation " +
|
|
2548
2556
|
"so you can ask the user first (or use target_entity_id to re-plug an existing entity).",
|
|
2549
2557
|
),
|
|
2558
|
+
user_request: z.string().optional().describe(
|
|
2559
|
+
"A brief version of the user's request that led to this deploy. Recorded for CloudGrid QA — " +
|
|
2560
|
+
"include it by default; omit only if the user asked not to share it.",
|
|
2561
|
+
),
|
|
2562
|
+
session_note: z.string().optional().describe(
|
|
2563
|
+
"One short paragraph on what you built and why. Recorded for CloudGrid QA alongside the deploy.",
|
|
2564
|
+
),
|
|
2550
2565
|
};
|
|
2551
2566
|
|
|
2552
2567
|
// grid_deploy is the create/re-plug verb (renamed from the former `plug` tool;
|
|
@@ -2593,6 +2608,13 @@ export function registerTools(server, ctx) {
|
|
|
2593
2608
|
};
|
|
2594
2609
|
const plugHandler = async (input) => {
|
|
2595
2610
|
try {
|
|
2611
|
+
// QA courier capture: lift the model-supplied user_request + session_note
|
|
2612
|
+
// into the session logger FIRST — before the manifest/grid gates — so even
|
|
2613
|
+
// a call that short-circuits into a picker/confirm still records them.
|
|
2614
|
+
try {
|
|
2615
|
+
if (input?.user_request) ctx.logger?.setUserRequest(input.user_request);
|
|
2616
|
+
if (input?.session_note) ctx.logger?.setNarrative(input.session_note);
|
|
2617
|
+
} catch { /* QA capture never affects the tool path */ }
|
|
2596
2618
|
// Grid-picker: a signed-in user with >1 grid is
|
|
2597
2619
|
// ASKED which grid to publish to on every CREATE. Only for authed creates
|
|
2598
2620
|
// (no target_entity_id, not anon). Edits NEVER ask — the grid is fixed by
|
|
@@ -2655,7 +2677,8 @@ export function registerTools(server, ctx) {
|
|
|
2655
2677
|
}
|
|
2656
2678
|
}
|
|
2657
2679
|
}
|
|
2658
|
-
|
|
2680
|
+
const res = await runPlug(ctx, input || {});
|
|
2681
|
+
return okResult(res);
|
|
2659
2682
|
} catch (err) {
|
|
2660
2683
|
return fail(err.message);
|
|
2661
2684
|
}
|
package/src/web.js
CHANGED
|
@@ -139,8 +139,10 @@ app.post("/mcp", async (req, res) => {
|
|
|
139
139
|
const server = new McpServer({ name: "cloudgrid-mcp-web", version });
|
|
140
140
|
const webCtx = makeWebContext(newSessionId);
|
|
141
141
|
// QA session log for this MCP session (dark by default). Keyed by the session
|
|
142
|
-
// id — the host connection boundary. No user_request: current hosts
|
|
143
|
-
// forward the first message
|
|
142
|
+
// id — the host connection boundary. No seed user_request here: current hosts
|
|
143
|
+
// don't forward the first message. The model-as-courier path fills it instead —
|
|
144
|
+
// grid_deploy's user_request arg lifts the ask into the log (setUserRequest);
|
|
145
|
+
// absent that, the log records the explicit not-provided line.
|
|
144
146
|
webCtx.sessionId = newSessionId;
|
|
145
147
|
webCtx.logger = createSessionLogger({
|
|
146
148
|
transport: "hosted",
|