@cloudgrid-io/mcp 0.20.56 → 0.21.1
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/README.md +10 -10
- package/package.json +6 -3
- package/src/corpus/cli-reference.md +1 -1
- package/src/corpus/rules/publish-fallbacks.md +2 -2
- package/src/corpus/templates/semantic-search/services/backend/requirements.txt +1 -1
- package/src/corpus/templates/semantic-search/services/refresh/requirements.txt +1 -1
- package/src/corpus/troubleshooting/deploy-errors.md +1 -1
- package/src/tools/deploy.js +129 -19
- package/src/tools/register.js +5 -0
package/README.md
CHANGED
|
@@ -197,16 +197,16 @@ recording whether the user agreed to share the conversation (`deploy.js:544`),
|
|
|
197
197
|
and a `context` object the model supplies — which by its declared shape
|
|
198
198
|
(`register.js:964-975`) can carry the failing inputs (for example the HTML or
|
|
199
199
|
args) and the original request. The transcript itself is not sent by this tool.
|
|
200
|
-
The report scrub is **
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
`context` you send, or set `CLOUDGRID_TELEMETRY=off` to
|
|
209
|
-
entirely.
|
|
200
|
+
The report scrub is **two-layer**: values AND the `message` field are scanned
|
|
201
|
+
for known secret shapes (API key prefixes such as `sk-ant-`, `sk-proj-`,
|
|
202
|
+
`AKIA…`; JWTs; PEM private-key blocks; Bearer tokens; basic-auth URL
|
|
203
|
+
credentials) and matches are replaced with `[REDACTED]`. Arbitrary or
|
|
204
|
+
unknown-shape secrets that do not match a known pattern still transit
|
|
205
|
+
unredacted. The client-side scrub (`scrubReportContext` + `scrubSecretValues`
|
|
206
|
+
in deploy.js) runs before the POST; the server endpoint applies a server-side
|
|
207
|
+
key-name filter on top. To keep a secret out of a report, do not place it in
|
|
208
|
+
the `message` or `context` you send, or set `CLOUDGRID_TELEMETRY=off` to
|
|
209
|
+
disable reporting entirely.
|
|
210
210
|
|
|
211
211
|
**Version check.** At startup the local edition makes one request to
|
|
212
212
|
`https://registry.npmjs.org/@cloudgrid-io/mcp/latest` to detect a stale `.mcpb`
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cloudgrid-io/mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.21.1",
|
|
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": {
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
"src"
|
|
11
11
|
],
|
|
12
12
|
"engines": {
|
|
13
|
-
"node": ">=
|
|
13
|
+
"node": ">=22.19"
|
|
14
14
|
},
|
|
15
15
|
"scripts": {
|
|
16
16
|
"start": "node src/index.js",
|
|
@@ -60,7 +60,10 @@
|
|
|
60
60
|
"test:tool-titles-and-vocab": "node test/tool-titles-and-vocab.test.mjs",
|
|
61
61
|
"test:visibility-mapping": "node test/visibility-mapping.test.mjs",
|
|
62
62
|
"test:verb-distinction": "node test/verb-distinction.test.mjs",
|
|
63
|
-
"test:collab": "node test/collab.test.mjs"
|
|
63
|
+
"test:collab": "node test/collab.test.mjs",
|
|
64
|
+
"test:url-shape": "node test/url-shape-guard.test.mjs",
|
|
65
|
+
"test:pull-grid": "node test/pull-grid-param.test.mjs",
|
|
66
|
+
"test:pull-output-schema": "node test/pull-output-schema.test.mjs"
|
|
64
67
|
},
|
|
65
68
|
"dependencies": {
|
|
66
69
|
"@modelcontextprotocol/sdk": "^1.30.0",
|
|
@@ -95,7 +95,7 @@ grid grid
|
|
|
95
95
|
| `env` / `secrets` | Manage runtime env vars and secrets (secret values are never printed) |
|
|
96
96
|
| `grid` / `list` | List what's on your hub / org |
|
|
97
97
|
| `rename` / `delete` / `unplug` | Rename, archive, take off the grid |
|
|
98
|
-
| `
|
|
98
|
+
| `collab` / `pull` / `pickup` | Grant push access (`collab`), continue in place (`pull`), or fork a copy (`pickup`) |
|
|
99
99
|
| `feedback [message]` | Send feedback to the CloudGrid team |
|
|
100
100
|
| `brain refresh <name>` | Re-run Grid Brain metadata (description, tags, diagram) |
|
|
101
101
|
| `doctor` | Diagnostic checks (Node, Docker, API reachability, auth) |
|
|
@@ -14,8 +14,8 @@ loops forever.
|
|
|
14
14
|
|
|
15
15
|
This is a known platform issue, not a mistake in your request. Do not retry with
|
|
16
16
|
different flag or path permutations — they will all fail the same way. If a
|
|
17
|
-
terminal is available, deploy from the CLI instead (`grid plug <path>`
|
|
18
|
-
If no terminal is available, stop and tell the user
|
|
17
|
+
terminal is available, deploy from the CLI instead (`grid plug <path>` or
|
|
18
|
+
`grid plug <url>`). If no terminal is available, stop and tell the user
|
|
19
19
|
this is a known issue and needs the CLI.
|
|
20
20
|
|
|
21
21
|
## 429 anonymous cap
|
|
@@ -10,7 +10,7 @@ Match the symptom to the row, apply the fix, and stop. Do not retry permutations
|
|
|
10
10
|
|
|
11
11
|
| Symptom | Cause | Fix |
|
|
12
12
|
| --- | --- | --- |
|
|
13
|
-
| `SCOPE_INVALID` on deploy | Known platform issue, not your request | Deploy from a terminal with `grid plug <path>`. Do not retry flag or path permutations. If no terminal is available, tell the user it is a known issue that needs the CLI. |
|
|
13
|
+
| `SCOPE_INVALID` on deploy | Known platform issue, not your request | Deploy from a terminal with `grid plug <path>` or `grid plug <url>`. Do not retry flag or path permutations. If no terminal is available, tell the user it is a known issue that needs the CLI. |
|
|
14
14
|
| `429` anonymous cap reached | Anonymous deploy quota is exhausted | Stop. Do not log in to work around it and do not retry today. Tell the user the cap is reached; they can try later or sign in for a higher limit as a deliberate choice. |
|
|
15
15
|
| `409 EDIT_REJECTED` on re-plug | The target entity is archived or expired | Do not silently create a new entity — that mints a new URL and orphans the shared one. Ask the user before creating a fresh entity. |
|
|
16
16
|
| `401` on an anonymous edit | Missing or stale `owner_token` | Re-plug with the `owner_token` from the last deploy of that entity. Each anonymous edit returns a fresh token — store the new one and use it next time. |
|
package/src/tools/deploy.js
CHANGED
|
@@ -21,6 +21,45 @@ import {
|
|
|
21
21
|
import { okResult } from "./util.js";
|
|
22
22
|
import { runCloudgrid } from "./cli.js";
|
|
23
23
|
|
|
24
|
+
/**
|
|
25
|
+
* The API refuses an infra-dependent write while a grid is still provisioning.
|
|
26
|
+
*
|
|
27
|
+
* The code is being renamed ORG_PROVISIONING -> GRID_PROVISIONING
|
|
28
|
+
* (cloudgrid-io/cloudgrid#2673, the org->grid retirement). This accepts BOTH,
|
|
29
|
+
* which is what makes the API-side rename safe: an mcp that understands both is
|
|
30
|
+
* version-independent from the API's side, so the rename can land the moment
|
|
31
|
+
* this ships rather than waiting for adoption.
|
|
32
|
+
*
|
|
33
|
+
* Keep both until the API no longer emits the old name AND the floor of
|
|
34
|
+
* installed mcp versions understands the new one. mcp is published to npm, so
|
|
35
|
+
* installed copies keep matching whatever they shipped with — dropping the old
|
|
36
|
+
* name early breaks plugs into brand-new grids, silently, by turning a
|
|
37
|
+
* retryable 409 into a hard failure.
|
|
38
|
+
*
|
|
39
|
+
* One predicate, two call sites, so they cannot drift.
|
|
40
|
+
*/
|
|
41
|
+
const PROVISIONING_CODES = new Set(["GRID_PROVISIONING", "ORG_PROVISIONING"]);
|
|
42
|
+
function isProvisioningCode(code) {
|
|
43
|
+
return PROVISIONING_CODES.has(code);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Same org->grid rename, same dual-accept safety (cloudgrid-io/cloudgrid#2673).
|
|
47
|
+
// These two codes gate the first-time-user "no grid" path and the wrong-grid
|
|
48
|
+
// path — if the MCP recognizes only the old ORG_* spelling, the API renaming it
|
|
49
|
+
// silently darks those paths (the account-has-no-grid guidance and the typo
|
|
50
|
+
// hint stop firing). NO_ACTIVE_GRID is ALREADY emitted by the API alongside
|
|
51
|
+
// NO_ACTIVE_ORG; GRID_NOT_ACCESSIBLE is the anticipated rename of
|
|
52
|
+
// ORG_NOT_ACCESSIBLE. Accept both spellings so the API rename can land anytime.
|
|
53
|
+
const NO_ACTIVE_GRID_CODES = new Set(["NO_ACTIVE_GRID", "NO_ACTIVE_ORG"]);
|
|
54
|
+
function isNoActiveGridCode(code) {
|
|
55
|
+
return NO_ACTIVE_GRID_CODES.has(code);
|
|
56
|
+
}
|
|
57
|
+
const GRID_NOT_ACCESSIBLE_CODES = new Set(["GRID_NOT_ACCESSIBLE", "ORG_NOT_ACCESSIBLE"]);
|
|
58
|
+
function isGridNotAccessibleCode(code) {
|
|
59
|
+
return GRID_NOT_ACCESSIBLE_CODES.has(code);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
|
|
24
63
|
// ── Org listing (bearer-authed, web edition) ──────────────────────────────────
|
|
25
64
|
// Fetches the signed-in user's orgs via GET /api/v2/orgs. The JWT does not
|
|
26
65
|
// carry orgs (claims: sub, email, name, iat, exp), so the API is the canonical
|
|
@@ -457,13 +496,51 @@ const REPORT_SECRET_KEY_PATTERNS = [
|
|
|
457
496
|
/access[_-]?key/i,
|
|
458
497
|
];
|
|
459
498
|
|
|
460
|
-
//
|
|
461
|
-
//
|
|
462
|
-
//
|
|
463
|
-
//
|
|
464
|
-
//
|
|
499
|
+
// Value-level secret patterns — must stay in sync with TEXT_SECRET_PATTERNS
|
|
500
|
+
// in session-logger.js (which scrubs free text for session logs). The lists are
|
|
501
|
+
// separate because this module operates on structured object values via
|
|
502
|
+
// scrubReportContext, while session-logger operates on free text via scrubText.
|
|
503
|
+
// Both scan string content for the same secret shapes; if you add a pattern to
|
|
504
|
+
// one, add it to the other.
|
|
505
|
+
const REPORT_SECRET_VALUE_PATTERNS = [
|
|
506
|
+
/-----BEGIN [A-Z ]*PRIVATE KEY-----[\s\S]*?-----END [A-Z ]*PRIVATE KEY-----/g,
|
|
507
|
+
/eyJ[A-Za-z0-9_-]{5,}\.[A-Za-z0-9_-]{5,}\.[A-Za-z0-9_-]{0,}/g,
|
|
508
|
+
/sk-ant-[A-Za-z0-9_-]{16,}/g,
|
|
509
|
+
/sk-or-v1-[A-Za-z0-9]{16,}/g,
|
|
510
|
+
/sk-proj-[A-Za-z0-9_-]{16,}/g,
|
|
511
|
+
/\bsk-[A-Za-z0-9]{20,}/g,
|
|
512
|
+
/AIza[0-9A-Za-z_-]{30,}/g,
|
|
513
|
+
/ghp_[A-Za-z0-9]{30,}/g,
|
|
514
|
+
/github_pat_[A-Za-z0-9_]{30,}/g,
|
|
515
|
+
/AKIA[0-9A-Z]{16}/g,
|
|
516
|
+
/xox[baprs]-[A-Za-z0-9-]{10,}/g,
|
|
517
|
+
/\bsk_(?:live|test)_[0-9A-Za-z]{16,}\b/g,
|
|
518
|
+
/\bBearer\s+[A-Za-z0-9._-]{8,}/gi,
|
|
519
|
+
];
|
|
520
|
+
|
|
521
|
+
// Replace known secret shapes inside a string value with [REDACTED].
|
|
522
|
+
// Complementary to the key-name check in scrubReportContext — this catches
|
|
523
|
+
// secrets that live inside values (e.g. an API key pasted into HTML source).
|
|
524
|
+
function scrubSecretValues(str) {
|
|
525
|
+
if (typeof str !== "string") return str;
|
|
526
|
+
let out = str;
|
|
527
|
+
for (const re of REPORT_SECRET_VALUE_PATTERNS) {
|
|
528
|
+
out = out.replace(re, "[REDACTED]");
|
|
529
|
+
}
|
|
530
|
+
out = out.replace(/([a-zA-Z][a-zA-Z0-9+.\-]*:\/\/)[^\s:@/]+:[^\s:@/]+@/g, "$1[REDACTED]@");
|
|
531
|
+
return out;
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
// Client-side scrub of the report context. Two layers:
|
|
535
|
+
// 1. Key-name check: values under secret-looking KEYS are fully redacted.
|
|
536
|
+
// 2. Value scan: string values in the tree (up to depth 5) are scanned for known
|
|
537
|
+
// secret shapes (API keys, Bearer tokens) and matches are replaced.
|
|
538
|
+
// Bounded depth so a pathological object can't loop.
|
|
465
539
|
export function scrubReportContext(obj, depth = 0) {
|
|
466
|
-
if (depth > 5 || obj === null || typeof obj !== "object")
|
|
540
|
+
if (depth > 5 || obj === null || typeof obj !== "object") {
|
|
541
|
+
// Leaf value — scrub known secret shapes from strings.
|
|
542
|
+
return typeof obj === "string" ? scrubSecretValues(obj) : obj;
|
|
543
|
+
}
|
|
467
544
|
if (Array.isArray(obj)) return obj.map((item) => scrubReportContext(item, depth + 1));
|
|
468
545
|
const out = {};
|
|
469
546
|
for (const [key, value] of Object.entries(obj)) {
|
|
@@ -552,11 +629,11 @@ export async function runReport(
|
|
|
552
629
|
// category: default "mcp" (or the failing tool name the agent passes).
|
|
553
630
|
category: typeof category === "string" && category.trim() ? category.trim() : "mcp",
|
|
554
631
|
app: "mcp",
|
|
555
|
-
message: summary.slice(0, 5000),
|
|
632
|
+
message: scrubSecretValues(summary).slice(0, 5000),
|
|
556
633
|
context: safeContext,
|
|
557
634
|
// Diagnostic pivots (match the CLI) — only when the agent forwards them.
|
|
558
635
|
...(typeof trace_id === "string" && trace_id ? { trace_id } : {}),
|
|
559
|
-
...(typeof failed_step === "string" && failed_step ? { failed_step } : {}),
|
|
636
|
+
...(typeof failed_step === "string" && failed_step ? { failed_step: scrubSecretValues(failed_step) } : {}),
|
|
560
637
|
...(typeof http_status === "number" && Number.isFinite(http_status) ? { http_status } : {}),
|
|
561
638
|
// Attribution, ALSO top-level (persisted once the handler accepts these keys;
|
|
562
639
|
// context.origin is the fallback until then).
|
|
@@ -638,7 +715,7 @@ export async function runReport(
|
|
|
638
715
|
// or GET collaborator push access with grid_collab — grants permission
|
|
639
716
|
// only, pull again once granted). A claim_token also
|
|
640
717
|
// claims an anonymous drop into your account (ownership transfer).
|
|
641
|
-
export async function runPull(ctx, { claim_token, claim_url, entity_id }) {
|
|
718
|
+
export async function runPull(ctx, { claim_token, claim_url, entity_id, grid } = {}) {
|
|
642
719
|
const token = await ctx.getToken();
|
|
643
720
|
if (!token) {
|
|
644
721
|
throw new Error("You are not signed in. Run grid_login first, then claim.");
|
|
@@ -685,8 +762,11 @@ export async function runPull(ctx, { claim_token, claim_url, entity_id }) {
|
|
|
685
762
|
};
|
|
686
763
|
// The claim re-homes the inspiration into the caller's grid; the platform
|
|
687
764
|
// resolves the destination from the active-org context, so send the org
|
|
688
|
-
// header (the same header every other authed write uses).
|
|
689
|
-
|
|
765
|
+
// header (the same header every other authed write uses). The `grid`
|
|
766
|
+
// parameter (added for #247: the hosted transport cannot set HTTP headers,
|
|
767
|
+
// so multi-grid users must pass the grid explicitly) takes precedence, then
|
|
768
|
+
// the session's active grid.
|
|
769
|
+
const orgSlug = grid || (await ctx.getActiveGrid());
|
|
690
770
|
// Grid-native header + X-CloudGrid-Org alias (same slug) during the soak.
|
|
691
771
|
if (orgSlug) {
|
|
692
772
|
headers["X-CloudGrid-Grid"] = orgSlug;
|
|
@@ -724,6 +804,30 @@ export async function runPull(ctx, { claim_token, claim_url, entity_id }) {
|
|
|
724
804
|
};
|
|
725
805
|
}
|
|
726
806
|
const code = data?.error?.code || null;
|
|
807
|
+
// ORG_NOT_ACCESSIBLE / GRID_NOT_ACCESSIBLE: the `grid` param has a typo or
|
|
808
|
+
// the user isn't a member of that grid. The server message already contains
|
|
809
|
+
// an "Available:" hint listing valid grid slugs — surface it so the agent
|
|
810
|
+
// can self-correct.
|
|
811
|
+
if (isGridNotAccessibleCode(code)) {
|
|
812
|
+
const serverMsg = data?.error?.message || "The specified grid is not accessible.";
|
|
813
|
+
const hint = data?.error?.details?.[0]?.hint || "";
|
|
814
|
+
return {
|
|
815
|
+
text:
|
|
816
|
+
`${serverMsg}${hint ? ` ${hint}` : ""} ` +
|
|
817
|
+
`Check the grid slug for typos and retry grid_pull with the correct \`grid\` parameter.`,
|
|
818
|
+
structured: { error: { code } },
|
|
819
|
+
};
|
|
820
|
+
}
|
|
821
|
+
// NO_ACTIVE_ORG / NO_ACTIVE_GRID: the account has no grid at all — route to
|
|
822
|
+
// in-flow grid creation instead of the misleading "no push access" advice.
|
|
823
|
+
if (isNoActiveGridCode(code)) {
|
|
824
|
+
return {
|
|
825
|
+
text:
|
|
826
|
+
"The account has no grid yet. Do not send the user to the console — create one from here: " +
|
|
827
|
+
"suggest a short slug, confirm it with the user, call grid_create_grid, then re-call grid_pull.",
|
|
828
|
+
structured: { needs_grid_create: true },
|
|
829
|
+
};
|
|
830
|
+
}
|
|
727
831
|
// No push access → not an error to throw; tell the user their options.
|
|
728
832
|
if (res.status === 403 || code === "NOT_ALLOWLISTED" || code === "PICKUP_DISABLED" || code === "FORBIDDEN_ROLE") {
|
|
729
833
|
return {
|
|
@@ -735,7 +839,13 @@ export async function runPull(ctx, { claim_token, claim_url, entity_id }) {
|
|
|
735
839
|
structured: { can_edit: false, owner_is_you: false, access: "view_only" },
|
|
736
840
|
};
|
|
737
841
|
}
|
|
738
|
-
|
|
842
|
+
let msg = data?.error?.message || data?.message || raw || `HTTP ${res.status}`;
|
|
843
|
+
// The API's "Set the X-CloudGrid-Grid header" error is unactionable for an
|
|
844
|
+
// MCP client (no client can set HTTP headers on a tool call). Rewrite it to
|
|
845
|
+
// name the tool parameter the caller CAN set (#247).
|
|
846
|
+
if (res.status === 400 && /X-CloudGrid-Grid/i.test(msg)) {
|
|
847
|
+
msg = "You belong to more than one grid. Pass the `grid` parameter to specify which grid to use.";
|
|
848
|
+
}
|
|
739
849
|
throw new Error(`Pull failed (HTTP ${res.status}): ${msg}`);
|
|
740
850
|
}
|
|
741
851
|
|
|
@@ -1277,7 +1387,7 @@ export function errorGuidance({ status, code, edition, isEdit, isAnon, signedIn
|
|
|
1277
1387
|
// internally with a bounded budget; this guidance is the budget-exhausted tail,
|
|
1278
1388
|
// so the wording tells the agent to wait and re-call rather than surface a raw
|
|
1279
1389
|
// code. (Issue #235.)
|
|
1280
|
-
if (status === 409 && code
|
|
1390
|
+
if (status === 409 && isProvisioningCode(code)) {
|
|
1281
1391
|
return "The grid is still finishing setup — a brand-new grid provisions its infrastructure in the background (usually within ~30s). Wait ~15s and call grid_plug again with the SAME parameters; it deploys once the grid is ready. Do not switch to anonymous and do not send the user to the console.";
|
|
1282
1392
|
}
|
|
1283
1393
|
// 409 EDIT_REJECTED — an in-place re-plug the server won't take.
|
|
@@ -1291,13 +1401,13 @@ export function errorGuidance({ status, code, edition, isEdit, isAnon, signedIn
|
|
|
1291
1401
|
: "Your sign-in is missing or expired. Run grid_login, then retry the same grid_plug. Do not offer anonymous publishing as a fix for a failed sign-in; anonymous is only for a user who explicitly asks to publish without attribution.";
|
|
1292
1402
|
}
|
|
1293
1403
|
if (status === 403) {
|
|
1294
|
-
// NO_ACTIVE_ORG is not a role problem — the account has no
|
|
1295
|
-
// The generic pull/pickup hint here sent a first-time user in
|
|
1296
|
-
// (field bug 2026-07-27); route them to in-flow grid creation instead.
|
|
1297
|
-
if (code
|
|
1404
|
+
// NO_ACTIVE_ORG / NO_ACTIVE_GRID is not a role problem — the account has no
|
|
1405
|
+
// grid at all. The generic pull/pickup hint here sent a first-time user in
|
|
1406
|
+
// circles (field bug 2026-07-27); route them to in-flow grid creation instead.
|
|
1407
|
+
if (isNoActiveGridCode(code)) {
|
|
1298
1408
|
return "The account has no grid yet. Do not send the user to the console — create one from here: suggest a short slug, confirm it with the user, call grid_create_grid, then re-call grid_plug with grid: <slug>.";
|
|
1299
1409
|
}
|
|
1300
|
-
return "You lack the role to plug this target. To
|
|
1410
|
+
return "You lack the role to plug this target. To get push access to the SAME entity, use `grid_collab` — it grants permission only and fetches nothing, so run `grid_pull` again once it is granted (if the owner gates access, it becomes a request they approve). To make your own separate copy (a fork), use `grid_pickup`.";
|
|
1301
1411
|
}
|
|
1302
1412
|
// ── Consent-gated report offer (Task 34) ──────────────────────────────────
|
|
1303
1413
|
// GENUINE bugs only: a build/deploy failure, any 5xx, INTERNAL_ERROR, or an
|
|
@@ -1754,7 +1864,7 @@ export async function runPlug(ctx, input, deps = {}) {
|
|
|
1754
1864
|
for (;;) {
|
|
1755
1865
|
({ res, raw, data } = await sendPlug());
|
|
1756
1866
|
if (res.ok) break;
|
|
1757
|
-
if (res.status === 409 && data?.error?.code
|
|
1867
|
+
if (res.status === 409 && isProvisioningCode(data?.error?.code)) {
|
|
1758
1868
|
const hint = data?.error?.details?.[0]?.hint || "";
|
|
1759
1869
|
// Terminal failure — provisioning did not complete; retrying is futile.
|
|
1760
1870
|
if (/did not (complete|finish)/i.test(hint)) {
|
package/src/tools/register.js
CHANGED
|
@@ -176,6 +176,7 @@ export function registerTools(server, ctx) {
|
|
|
176
176
|
description: "Pull an app to continue/edit it IN PLACE — like `git clone` of the SAME entity: your next grid_plug (with its target_entity_id) updates that entity, and the team sees the new version. Requires PUSH ACCESS: you must own it or be a collaborator. If you can only view it, you CANNOT edit or plug it — say so, and offer the two real options: (1) make your own separate copy (a fork) with grid_pickup, or (2) GET push access to the SAME entity with grid_collab (or the CLI `grid collab <entity>`) — \"collab\" is a distinct access-control operation that grants permission only and fetches nothing, so once it is granted you run grid_pull again to get the code (and if the owner gates access, grid_collab becomes a request they approve). Passing an anonymous drop's `claim_token` claims it into your account. Requires sign-in. Calls the API directly (both editions).",
|
|
177
177
|
inputSchema: {
|
|
178
178
|
entity_id: z.string().optional().describe("The entity id to pull. Defaults to this session's last anonymous drop."),
|
|
179
|
+
grid: z.string().optional().describe("Grid to resolve a bare slug in. Required only when you belong to more than one grid."),
|
|
179
180
|
claim_token: z.string().optional().describe("Anonymous drop only: its owner token — claims that drop into your account (ownership transfer)."),
|
|
180
181
|
claim_url: z.string().optional().describe("Alternative to claim_token for an anonymous drop — the claim_url; the token is read from it."),
|
|
181
182
|
},
|
|
@@ -186,6 +187,9 @@ export function registerTools(server, ctx) {
|
|
|
186
187
|
url: z.string().optional().describe("Its public URL."),
|
|
187
188
|
owner_is_you: z.boolean().optional().describe("True if you own it."),
|
|
188
189
|
can_edit: z.boolean().optional().describe("True if you can plug/update it (owner or collaborator). False = view-only."),
|
|
190
|
+
access: z.string().optional().describe("Access level when can_edit is false (e.g. 'view_only')."),
|
|
191
|
+
error: z.object({ code: z.string() }).optional().describe("Error envelope when the grid slug is wrong or inaccessible. code is the server error code (e.g. ORG_NOT_ACCESSIBLE)."),
|
|
192
|
+
needs_grid_create: z.boolean().optional().describe("True when the account has no grid yet — route to grid_create_grid before retrying."),
|
|
189
193
|
},
|
|
190
194
|
annotations: { readOnlyHint: false, destructiveHint: false, openWorldHint: true },
|
|
191
195
|
},
|
|
@@ -448,6 +452,7 @@ export function registerTools(server, ctx) {
|
|
|
448
452
|
is_active: z.boolean(),
|
|
449
453
|
})).optional().describe("Alias of grids (legacy picker alias)."),
|
|
450
454
|
via: z.string().optional().describe("Recovery marker: 'cli-fallback' when a signed-in create was published through the bundled CloudGrid CLI."),
|
|
455
|
+
needs_grid_create: z.boolean().optional().describe("Zero-grid ask: the account has no grid yet — suggest a slug, confirm with the user, call grid_create_grid, then re-call grid_plug with grid: <slug>."),
|
|
451
456
|
},
|
|
452
457
|
// destructiveHint (M3): a re-plug with target_entity_id REPLACES what is
|
|
453
458
|
// live at a public URL in place. Versions + grid rollback exist, but the
|