@cloudgrid-io/mcp 0.21.8 → 0.21.9

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.21.8",
3
+ "version": "0.21.9",
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": {
@@ -1480,22 +1480,16 @@ function plugErrorMessage(status, code, msg, ctxFlags = {}) {
1480
1480
  return guidance ? `${base} — ${guidance}` : base;
1481
1481
  }
1482
1482
 
1483
- // ── WORKAROUND(#311): inline multi-file re-plug guard + FETCHSOURCE handoff ───
1484
- // WHAT IS BROKEN: the hosted (web) server materializes the uploaded multipart
1485
- // parts into a source tarball SERVER-SIDE; for a MULTI-FILE runtime app
1486
- // re-plugged INLINE it emits a non-gzip stream that dies at the build container's
1483
+ // ── Corrupt-archive FETCHSOURCE handoff (defense in depth) ───────────────────
1484
+ // Historically the hosted (web) server materialized uploaded multipart parts
1485
+ // into a source tarball SERVER-SIDE and, for a multi-file runtime app re-plugged
1486
+ // inline, could emit a non-gzip stream that died at the build container's
1487
1487
  // FETCHSOURCE with "gzip: stdin: not in gzip format / tar: Child returned status
1488
- // 1" — raw, unactionable output that stranded a real user (natalys-grid). This
1489
- // client never gzips/tars anything (it uploads each file as an octet-stream part
1490
- // — see buildForm; byte-exactness proven in test), so the corruption is a
1491
- // SERVER-SIDE defect downstream of this process; the disk-based CLI uploads the
1492
- // same files and works.
1493
- // WHAT WOULD LET THIS BE REMOVED: the API-side fix to tarball materialization for
1494
- // this shape (tracked on #311). Until then, two defenses live here:
1495
- // 1. detect the shape BEFORE upload (the gate in runPlug) and hand over the
1496
- // CLI commands with the real entity handle — never fail at the build.
1497
- // 2. if a corrupt-archive build failure still slips through, rewrite the raw
1498
- // tar/gzip log to the same actionable handoff (archiveCorruptionSignature).
1488
+ // 1" — raw, unactionable output. The server-side defect (cloudgrid#2977) is now
1489
+ // fixed and verified live, and the pre-upload gate that refused that shape has
1490
+ // been lifted (#315). This handoff stays as defense in depth: if a corrupt-archive
1491
+ // build failure ever recurs, rewrite the raw tar/gzip log to an actionable CLI
1492
+ // handoff (via archiveCorruptionSignature) instead of surfacing it verbatim.
1499
1493
 
1500
1494
  // The "continue this exact entity from a terminal" CLI commands, with the real
1501
1495
  // handle substituted. Prefers grid/slug (what `pull` takes, human-readable);
@@ -1512,7 +1506,7 @@ export function cliContinueHandoff({ entityId, grid, slug } = {}) {
1512
1506
  );
1513
1507
  }
1514
1508
 
1515
- // True when a build failure is the #311 corrupt-archive signature (a FETCHSOURCE
1509
+ // True when a build failure is the corrupt-archive signature (a FETCHSOURCE
1516
1510
  // gunzip/tar failure), as opposed to a genuine app build error the developer can
1517
1511
  // act on (a missing dependency, a syntax error) — those keep their real log tail.
1518
1512
  export function archiveCorruptionSignature(text) {
@@ -1750,40 +1744,6 @@ export async function runPlug(ctx, input, deps = {}) {
1750
1744
 
1751
1745
  const isEdit = typeof targetEntityId === "string" && targetEntityId.length > 0;
1752
1746
 
1753
- // ── WORKAROUND(#311): pre-upload gate for inline multi-file runtime RE-PLUG ──
1754
- // WHAT IS BROKEN: the hosted server's tarball materialization corrupts the
1755
- // source archive for a multi-file runtime app re-plugged INLINE — it emits a
1756
- // non-gzip stream that dies at the build container's FETCHSOURCE with
1757
- // "gzip: stdin: not in gzip format / tar: Child returned status 1". This client
1758
- // uploads byte-exact octet-stream parts (proven in test), so the defect is
1759
- // SERVER-SIDE, not here. It stranded a real user (natalys-grid) twice.
1760
- // WHAT WOULD LET THIS BE REMOVED: the API-side fix to tarball materialization
1761
- // for this shape (tracked on #311). Once hosted rebuilds a multi-file inline
1762
- // re-plug reliably, delete this gate — the inline path would just work.
1763
- // Until then, detect the shape BEFORE uploading and hand over the CLI, which
1764
- // uploads from disk and updates the SAME entity. NARROW by design so it never
1765
- // touches the cases that work today: hosted (web) only, RE-PLUG only (a real
1766
- // entity id in hand to substitute), MULTIPLE files, and a ROOT cloudgrid.yaml
1767
- // that DECLARES services (i.e. a runtime app, not a static bundle). A single
1768
- // HTML page, or a few small text files with no service manifest, are untouched
1769
- // and still plug inline. Multi-file runtime CREATES are also untouched (issue
1770
- // #48 aligned that bundle) — only the re-plug shape that failed twice is gated.
1771
- if (ctx.edition === "web" && isEdit && hasArtifacts && artifacts.length > 1) {
1772
- const manifest = detectSourceManifest(input);
1773
- if (manifest && manifest.services.length > 0) {
1774
- const slugForMsg = pickupData?.slug || (typeof slug === "string" && slug ? slug : null);
1775
- const gridForMsg = pickupData?.grid || (typeof grid === "string" && grid ? grid : null);
1776
- throw new Error(
1777
- `This is a multi-file runtime app (${artifacts.length} files + a cloudgrid.yaml declaring ` +
1778
- `services: ${manifest.services.join(", ")}) being re-plugged inline on the hosted server. ` +
1779
- "The hosted server can't reliably rebuild a multi-file app from an inline upload — the source " +
1780
- "archive arrives corrupt at build (#311), which is why the identical source plugs fine from the " +
1781
- "CLI. " +
1782
- cliContinueHandoff({ entityId: targetEntityId, grid: gridForMsg, slug: slugForMsg }),
1783
- );
1784
- }
1785
- }
1786
-
1787
1747
  // An inspiration edit content-versions the FIRST uploaded artifact — when a
1788
1748
  // multi-file folder rides a re-plug, put the primary entry first so the edit
1789
1749
  // swaps the right file (index.html > any .html > everything else).
@@ -2127,18 +2087,17 @@ export async function runPlug(ctx, input, deps = {}) {
2127
2087
  data.status = "live";
2128
2088
  data.poll_url = undefined;
2129
2089
  } else if (verdict.status === "failed") {
2130
- // WORKAROUND(#311): a corrupt-archive FETCHSOURCE failure is unactionable
2131
- // as raw tar output — never surface it verbatim. WHAT IS BROKEN: the
2132
- // server-side tarball materialization corrupts the archive for a multi-file
2133
- // runtime re-plug; WHAT WOULD LET THIS BE REMOVED: the API-side fix (#311),
2134
- // after which no corrupt-archive verdict reaches here. Name the CLI path
2135
- // with the real handle instead (defense-in-depth behind the pre-upload gate
2136
- // above; this catches any shape the gate didn't). Genuine app build errors
2137
- // keep their real log tail — those the developer CAN act on.
2090
+ // Defense in depth: a corrupt-archive FETCHSOURCE failure is unactionable
2091
+ // as raw tar output — never surface it verbatim. The server-side defect
2092
+ // that produced it (cloudgrid#2977) is fixed and the pre-upload gate has
2093
+ // been lifted (#315), so no corrupt-archive verdict is expected here; if the
2094
+ // class ever recurs, name the CLI path with the real handle instead of the
2095
+ // raw log. Genuine app build errors keep their real log tail — those the
2096
+ // developer CAN act on.
2138
2097
  if (archiveCorruptionSignature(`${verdict.error || ""}\n${verdict.logTail || ""}`)) {
2139
2098
  throw new Error(
2140
2099
  "The uploaded source could not be read by the build (its archive was corrupt) — the URL is NOT " +
2141
- "live. This is a known limitation of inline multi-file uploads on the hosted server (#311). " +
2100
+ "live. " +
2142
2101
  cliContinueHandoff({ entityId: data.entity_id, grid: data.grid, slug: data.slug }),
2143
2102
  );
2144
2103
  }
@@ -2769,16 +2728,15 @@ export async function runCheckDeploy(ctx, { poll_url, grid } = {}) {
2769
2728
  // the source zip (grid_get_app_source → source_download_url) or the
2770
2729
  // pull command that downloads + links the folder so the SAME entity
2771
2730
  // continues locally.
2772
- // WORKAROUND(#311): a corrupt-archive FETCHSOURCE failure is unactionable raw
2773
- // tar output — do NOT echo it. WHAT IS BROKEN: server-side tarball
2774
- // materialization corrupts the archive for a multi-file runtime re-plug;
2775
- // WHAT WOULD LET THIS BE REMOVED: the API-side fix (#311). Name the CLI
2776
- // continue-path with the real handle instead.
2731
+ // Defense in depth: a corrupt-archive FETCHSOURCE failure is unactionable raw
2732
+ // tar output — do NOT echo it. The server-side defect that produced it
2733
+ // (cloudgrid#2977) is fixed and the pre-upload gate has been lifted (#315);
2734
+ // if the class ever recurs, name the CLI continue-path with the real handle
2735
+ // instead of the raw log.
2777
2736
  if (archiveCorruptionSignature(`${verdict.error || ""}\n${verdict.logTail || ""}`)) {
2778
2737
  return {
2779
2738
  text: "The uploaded source could not be read by the build (its archive was corrupt) — the URL is " +
2780
- "not live. This is a known limitation of inline multi-file uploads on the hosted server (#311); the " +
2781
- "project files are NOT lost — they are saved on the entity. " +
2739
+ "not live. The project files are NOT lost — they are saved on the entity. " +
2782
2740
  cliContinueHandoff({ entityId, grid: gridSlug, slug: ctx.state.lastDrop?.slug }),
2783
2741
  structured: {
2784
2742
  status: "failed",
@@ -943,7 +943,7 @@ export function registerTools(server, ctx) {
943
943
  .object({
944
944
  active_grid: z.string().nullable().describe("The user's active grid/org slug, or null."),
945
945
  signed_in: z.boolean().describe("Whether the current session is signed in."),
946
- email: z.string().optional().describe("The signed-in user's email, when available."),
946
+ email: z.string().optional().describe("The signed-in user's email — the CloudGrid account this session is authenticated as. Present whenever signed_in is true and the session token carries an email claim (the normal case). Absent only if signed_in is false, or on the rare token that lacks the claim; the identity line in this tool's text content states which case applies. Use this to answer 'which account/user am I?'."),
947
947
  session_expired: z.boolean().optional().describe("True when credentials exist but the JWT has expired. Run grid_login to sign in again."),
948
948
  identity_changed: z.boolean().optional().describe("True when the session's identity changed via a different transport Bearer. Session state was reset."),
949
949
  update_available: z
@@ -1007,8 +1007,22 @@ export function registerTools(server, ctx) {
1007
1007
  const wfLines = workflows.length
1008
1008
  ? workflows.map((w) => ` - ${w.name}: ${w.when || w.summary}`).join("\n")
1009
1009
  : " (none available)";
1010
+ // Identity banner — first line of the text channel, because the account
1011
+ // this session is signed in as is only otherwise carried in
1012
+ // structuredContent.context, which many clients/models never surface. A
1013
+ // hosted assistant asked "which account am I?" was concluding no tool
1014
+ // exposes it while the answer sat in structured output it had already
1015
+ // received (#317). Stating it in the text the model actually reads makes
1016
+ // "who am I" answerable without the CLI (whose separate session can point
1017
+ // at a different account entirely).
1018
+ const gridSuffix = activeGrid ? ` (active grid: ${activeGrid})` : "";
1019
+ const identityLine = signedIn
1020
+ ? signedInEmail
1021
+ ? `You are signed in to CloudGrid as ${signedInEmail}${gridSuffix}. If the user asks which CloudGrid account, user, or email they are connected to, that is the answer.`
1022
+ : `You are signed in to CloudGrid${gridSuffix}, but this session's token carries no email claim, so the account address is not available here. Do not guess it from grid memberships or the CLI (its session may be a different account).`
1023
+ : `You are not signed in to CloudGrid. Run grid_login to sign in.`;
1010
1024
  let text =
1011
- `${PLAYBOOK}\n\nAvailable workflows:\n${wfLines}\n\n` +
1025
+ `${identityLine}\n\n${PLAYBOOK}\n\nAvailable workflows:\n${wfLines}\n\n` +
1012
1026
  `Next: match the intent to a workflow and call grid_get_template({kind:"workflow", name}).` +
1013
1027
  (stale ? `\n\n${stale}` : "");
1014
1028
  if (sessionExpired) {