@superblocksteam/gateway 2.0.156-next.1 → 2.0.156-next.2

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.
Files changed (40) hide show
  1. package/README.md +3 -30
  2. package/dist/capabilities/lifecycle.d.ts +1 -12
  3. package/dist/capabilities/lifecycle.js +51 -99
  4. package/dist/capabilities/lifecycle.js.map +1 -1
  5. package/dist/capabilities/types.d.ts +0 -5
  6. package/dist/capabilities/types.js.map +1 -1
  7. package/dist/deps.d.ts +0 -2
  8. package/dist/integrations/map.d.ts +0 -1
  9. package/dist/integrations/map.js +1 -1
  10. package/dist/integrations/map.js.map +1 -1
  11. package/dist/sabs/editor-client-methods.js +5 -4
  12. package/dist/sabs/editor-client-methods.js.map +1 -1
  13. package/dist/sabs/session-peer.d.ts +0 -4
  14. package/dist/sabs/websocket-session-peer.d.ts +1 -2
  15. package/dist/sabs/websocket-session-peer.js +11 -21
  16. package/dist/sabs/websocket-session-peer.js.map +1 -1
  17. package/dist/server/client.d.ts +4 -10
  18. package/dist/server/client.js +4 -13
  19. package/dist/server/client.js.map +1 -1
  20. package/dist/start.d.ts +0 -10
  21. package/dist/start.js +2 -5
  22. package/dist/start.js.map +1 -1
  23. package/dist/transports/mcp/admin-tools.d.ts +1 -3
  24. package/dist/transports/mcp/admin-tools.js +1 -12
  25. package/dist/transports/mcp/admin-tools.js.map +1 -1
  26. package/dist/transports/mcp/app-status-html.d.ts +2 -2
  27. package/dist/transports/mcp/app-status-html.js +8 -139
  28. package/dist/transports/mcp/app-status-html.js.map +1 -1
  29. package/dist/transports/mcp/mount.js +24 -89
  30. package/dist/transports/mcp/mount.js.map +1 -1
  31. package/package.json +4 -5
  32. package/dist/capabilities/integration-metadata.d.ts +0 -28
  33. package/dist/capabilities/integration-metadata.js +0 -174
  34. package/dist/capabilities/integration-metadata.js.map +0 -1
  35. package/dist/telemetry/mcp-client.d.ts +0 -22
  36. package/dist/telemetry/mcp-client.js +0 -129
  37. package/dist/telemetry/mcp-client.js.map +0 -1
  38. package/dist/telemetry/runtime.d.ts +0 -9
  39. package/dist/telemetry/runtime.js +0 -121
  40. package/dist/telemetry/runtime.js.map +0 -1
package/README.md CHANGED
@@ -4,8 +4,8 @@ Standalone Superblocks entry point for a **single MCP connector** (Admin + Build
4
4
  over **stdio**, running as the already-logged-in Superblocks CLI user.
5
5
 
6
6
  Builder tools (`start_app`, `import_app`, `edit_app`, `check_app_progress`,
7
- `get_app`, `get_integration_metadata`, `preview_app`, `publish_app`) plus
8
- customer Admin tools from `@superblocksteam/mcp-server` share this process.
7
+ `get_app`, `preview_app`, `publish_app`) plus customer Admin tools from
8
+ `@superblocksteam/mcp-server` share this process.
9
9
 
10
10
  The MCP host owns process lifecycle: it spawns `superblocks gateway serve`.
11
11
  There is no foreground HTTP `/mcp`, no OAuth resource server, and no linked-grant
@@ -147,18 +147,7 @@ package only the customer Admin surface. Builder tools are not registered.
147
147
  ## MCP tools
148
148
 
149
149
  After `gateway setup`, restart Claude Code or Cursor and call `start_app`,
150
- `edit_app`, `check_app_progress`, `get_app`, `get_integration_metadata`,
151
- `preview_app`, and `publish_app`. `get_integration_metadata` reads tables,
152
- columns, and types from a connected integration; use `search`, `limit`, and
153
- `offset` for large results. It needs no application: without one it mints an
154
- `integrations:build` token scoped to that single integration. Pass
155
- `applicationId` only for an integration owned by one application, such as a
156
- Native DB, which is invisible without app context.
157
- It resolves the integration through the RBAC-annotated superset list rather than
158
- the org-wide lookup by id, so it answers `integration_not_permitted` without
159
- build permission on that integration and `integration_not_supported` for a
160
- plugin Clark cannot use as a tool.
161
- `get_integration_config_schema` is the create-integration form, not live data.
150
+ `edit_app`, `check_app_progress`, `get_app`, `preview_app`, and `publish_app`.
162
151
  See `.env.example` for optional configuration.
163
152
 
164
153
  ## Env
@@ -179,19 +168,3 @@ Gateway debug logs include tool names, result states, decision branches, and
179
168
  entry-point stacks. They intentionally omit credentials, prompts, answers, and
180
169
  result payloads. Run `superblocks gateway setup --client <client> --debug`, then
181
170
  restart the MCP host. Re-run setup without `--debug` to turn them off.
182
-
183
- ## Remote traceability
184
-
185
- Gateway exports one OpenTelemetry trace per MCP process through the authenticated
186
- Server `/api/v1/traces` proxy. Each tool call is a child span with the MCP client
187
- name and version, tool name, duration, outcome, organization ID, and application
188
- ID when available.
189
-
190
- Prompts, answers, result payloads, credentials, and raw error messages are never
191
- added to these spans. Use the application ID to correlate a trace with the
192
- existing server-side app and build records.
193
-
194
- Cloud-Prem uses `SUPERBLOCKS_DEPLOYMENT_TYPE=cloud-prem` (or `GATEWAY_LOCAL_AGENT`)
195
- so the stricter telemetry policy is selected. Laptop agents still drop client,
196
- organization, and application attributes until the Tier 2 traces contract lists
197
- them; tool name, duration, and outcome still export.
@@ -5,7 +5,7 @@ import type { CapturePreviewScreenshot } from "../preview/capture-screenshot.js"
5
5
  import type { CallerRef, ProgressStore, RecentAppStore, TurnStore } from "../resume/types.js";
6
6
  import type { SessionPeer } from "../sabs/session-peer.js";
7
7
  import type { SuperblocksServerClient } from "../server/client.js";
8
- import type { CapabilityResult, CheckAppProgressInput, CheckAppProgressResult, EditAppInput, EditAppResult, GetAppInput, GetAppResult, ImportAppInput, ImportAppResult, PreviewAppInput, PreviewAppResult, Principal, ProgressEvent, PublishAppInput, PublishAppResult, ResolvedPrincipal, StartAppInput, StartAppResult } from "./types.js";
8
+ import type { CapabilityResult, CheckAppProgressInput, CheckAppProgressResult, EditAppInput, EditAppResult, GetAppInput, GetAppResult, ImportAppInput, ImportAppResult, PreviewAppInput, PreviewAppResult, Principal, ProgressEvent, PublishAppInput, PublishAppResult, StartAppInput, StartAppResult } from "./types.js";
9
9
  export { IMPORT_ZIP_MAX_BYTES } from "./types.js";
10
10
  export type CapabilityContext = {
11
11
  /**
@@ -23,7 +23,6 @@ export type CapabilityContext = {
23
23
  /** Injected so pacing and stall thresholds are testable without real time. */
24
24
  now?: () => number;
25
25
  onProgress?: (event: ProgressEvent) => void;
26
- onPrincipalResolved?: (principal: ResolvedPrincipal) => void;
27
26
  principal: Principal;
28
27
  /** Where each caller's place in the event stream is kept between calls. */
29
28
  progressCursors?: ProgressStore;
@@ -45,16 +44,6 @@ export type CapabilityContext = {
45
44
  signal?: AbortSignal;
46
45
  turns: TurnStore;
47
46
  };
48
- /**
49
- * Resolves the Superblocks user behind the CLI session this Gateway was
50
- * started with.
51
- *
52
- * Identity is the already-logged-in Superblocks CLI user. A rejection here
53
- * means that personal API key is bad, not that the user needs a second login,
54
- * so it surfaces as an error rather than an interactive-auth elicitation.
55
- */
56
- export declare function ensurePrincipal(ctx: CapabilityContext): Promise<CapabilityResult<ResolvedPrincipal>>;
57
- export declare function resolveApplicationId(ctx: CapabilityContext, principal: ResolvedPrincipal, applicationId: string | undefined): Promise<CapabilityResult<string>>;
58
47
  export declare function startApp(ctx: CapabilityContext, input: StartAppInput): Promise<CapabilityResult<StartAppResult>>;
59
48
  /**
60
49
  * Creates a fullstack app, uploads the caller's archive as an app attachment,
@@ -227,21 +227,19 @@ function defaultAppName(prompt) {
227
227
  * means that personal API key is bad, not that the user needs a second login,
228
228
  * so it surfaces as an error rather than an interactive-auth elicitation.
229
229
  */
230
- export async function ensurePrincipal(ctx) {
230
+ async function ensurePrincipal(ctx) {
231
231
  const { principal } = ctx;
232
232
  // MCP already has the CLI session key. Looking it up again would spend a
233
233
  // second round trip on `/users/me`, the slowest endpoint the Superblocks
234
234
  // API has, when the principal is already resolved (ENG-5806).
235
235
  if (principal.organizationId && principal.userId) {
236
- const resolved = {
237
- ...principal,
238
- organizationId: principal.organizationId,
239
- userId: principal.userId,
240
- };
241
- ctx.onPrincipalResolved?.(resolved);
242
236
  return {
243
237
  kind: "ok",
244
- value: resolved,
238
+ value: {
239
+ ...principal,
240
+ organizationId: principal.organizationId,
241
+ userId: principal.userId,
242
+ },
245
243
  };
246
244
  }
247
245
  let validated;
@@ -278,16 +276,14 @@ export async function ensurePrincipal(ctx) {
278
276
  message: "The Superblocks API rejected the credential issued for this caller.",
279
277
  };
280
278
  }
281
- const resolved = {
282
- ...principal,
283
- email: validated.email,
284
- organizationId: validated.organizationId,
285
- userId: validated.userId,
286
- };
287
- ctx.onPrincipalResolved?.(resolved);
288
279
  return {
289
280
  kind: "ok",
290
- value: resolved,
281
+ value: {
282
+ ...principal,
283
+ email: validated.email,
284
+ organizationId: validated.organizationId,
285
+ userId: validated.userId,
286
+ },
291
287
  };
292
288
  }
293
289
  /**
@@ -382,7 +378,7 @@ async function elicitStepUp(ctx, idempotencyKey, applicationId) {
382
378
  },
383
379
  };
384
380
  }
385
- export async function resolveApplicationId(ctx, principal, applicationId) {
381
+ async function resolveApplicationId(ctx, principal, applicationId) {
386
382
  if (applicationId?.trim()) {
387
383
  return { kind: "ok", value: applicationId.trim() };
388
384
  }
@@ -516,9 +512,7 @@ function turnSummary(turn) {
516
512
  return "Superblocks is still working, but only the editor can show it — open the edit URL";
517
513
  }
518
514
  if (turn.generationStatus === "live_edit_terminated") {
519
- return turn.retryAction === "retry_import"
520
- ? "The Superblocks live-edit session terminated before anything was built — send the archive again with import_app"
521
- : "The Superblocks live-edit session terminated before anything was built — send the prompt again with edit_app";
515
+ return "The Superblocks live-edit session terminated before anything was built — send the prompt again with edit_app";
522
516
  }
523
517
  if (turn.checkpointCommitId) {
524
518
  return `Application ready — checkpoint saved. ${NEXT_STEPS}`;
@@ -674,7 +668,6 @@ async function forwardClarkPrompt(ctx, input) {
674
668
  ...(forward.recentRuns && forward.recentRuns.length > 0
675
669
  ? { recentRuns: forward.recentRuns }
676
670
  : {}),
677
- ...(forward.retryAction ? { retryAction: forward.retryAction } : {}),
678
671
  viewStatus: forward.viewStatus,
679
672
  },
680
673
  };
@@ -764,13 +757,17 @@ function namingCreatedApplication(applicationId, capability, result) {
764
757
  * The recovery to advertise when the machinery around a turn fails: `edit_app`
765
758
  * with the same prompt, which is what `retry_build` means.
766
759
  *
767
- * A regular prompt is retried with `edit_app`. An import prompt carries an
768
- * attachment, so it must be retried with `import_app` to upload the archive
769
- * again and obtain a fresh signed URL.
760
+ * Only for a prompt `edit_app` could actually replay. A prompt carrying
761
+ * attachments is not one: `edit_app` has no way to resupply an attachment, so
762
+ * an import's migration prompt would reach Superblocks describing an archive it
763
+ * cannot see. Advertising a recovery that cannot finish is worse than
764
+ * advertising none, so the import path gets `applicationId` and no hint.
765
+ * TODO(ENG-5917): let `import_app` resume into an application it already
766
+ * created, and give the import path a recovery of its own.
770
767
  */
771
768
  function retryIfReplayable(attachments) {
772
769
  return attachments && attachments.length > 0
773
- ? { pendingAction: "retry_import" }
770
+ ? undefined
774
771
  : { pendingAction: "retry_build" };
775
772
  }
776
773
  /**
@@ -812,9 +809,7 @@ function rememberFailedStart(ctx, input) {
812
809
  organizationId: input.principal.organizationId,
813
810
  // Reaches the caller as the dead start's reply on its next poll, so it
814
811
  // has to stand on its own there rather than lean on this call's error.
815
- reason: input.reason ??
816
- `The Superblocks live-edit session for application ${input.applicationId} never opened, so the prompt never reached Superblocks and nothing was built. Nothing is running for that application; send the same instruction again to retry it. (${cause})`,
817
- ...(input.retryAction ? { retryAction: input.retryAction } : {}),
812
+ reason: `The Superblocks live-edit session for application ${input.applicationId} never opened, so the prompt never reached Superblocks and nothing was built. Nothing is running for that application; send the same instruction again to retry it. (${cause})`,
818
813
  userId: input.principal.userId,
819
814
  });
820
815
  }
@@ -870,12 +865,7 @@ async function promptNewApplication(ctx, args) {
870
865
  });
871
866
  }
872
867
  catch (error) {
873
- rememberFailedStart(ctx, {
874
- applicationId,
875
- error,
876
- principal,
877
- ...(args.attachments?.length ? { retryAction: "retry_import" } : {}),
878
- });
868
+ rememberFailedStart(ctx, { applicationId, error, principal });
879
869
  return liveEditUnavailable(error, args.attachments);
880
870
  }
881
871
  ctx.onProgress?.({
@@ -1160,19 +1150,14 @@ function normalizeImportSource(source) {
1160
1150
  * wizard, without the sessionStorage hop.
1161
1151
  */
1162
1152
  export async function importApp(ctx, input) {
1163
- const resumedApplicationId = input.applicationId?.trim();
1164
1153
  const archiveResult = await resolveImportArchive(input, ctx.config.importSearchDirs);
1165
1154
  if (archiveResult.kind !== "ok") {
1166
- return resumedApplicationId
1167
- ? namingApplication(resumedApplicationId, archiveResult)
1168
- : archiveResult;
1155
+ return archiveResult;
1169
1156
  }
1170
1157
  const archive = archiveResult.value;
1171
1158
  const principalResult = await ensurePrincipal(ctx);
1172
1159
  if (principalResult.kind !== "ok") {
1173
- return resumedApplicationId
1174
- ? namingApplication(resumedApplicationId, principalResult)
1175
- : principalResult;
1160
+ return principalResult;
1176
1161
  }
1177
1162
  const principal = principalResult.value;
1178
1163
  const source = normalizeImportSource(input.source);
@@ -1183,26 +1168,25 @@ export async function importApp(ctx, input) {
1183
1168
  });
1184
1169
  const explicitName = input.name?.trim();
1185
1170
  const name = explicitName || defaultAppName(migrationPrompt);
1186
- let applicationId = resumedApplicationId;
1187
- if (!applicationId) {
1188
- ctx.onProgress?.({
1189
- message: "Creating application for import",
1190
- type: "progress",
1191
- });
1192
- const created = await createApplicationForPrompt(ctx, {
1193
- apiKey: principal.apiKey,
1194
- name,
1195
- organizationId: principal.organizationId,
1196
- templateName: "app-fullstack",
1197
- });
1198
- if (created.kind !== "ok") {
1199
- return created;
1200
- }
1201
- applicationId = created.value.id;
1171
+ ctx.onProgress?.({
1172
+ message: "Creating application for import",
1173
+ type: "progress",
1174
+ });
1175
+ const created = await createApplicationForPrompt(ctx, {
1176
+ apiKey: principal.apiKey,
1177
+ name,
1178
+ organizationId: principal.organizationId,
1179
+ templateName: "app-fullstack",
1180
+ });
1181
+ if (created.kind !== "ok") {
1182
+ return created;
1202
1183
  }
1203
- await rememberApplication(ctx, principal, applicationId);
1204
- const result = await migrateArchiveIntoApplication(ctx, {
1205
- applicationId,
1184
+ const app = created.value;
1185
+ // See startApp: the row outlives any failure below it, so it is already this
1186
+ // caller's most recent app.
1187
+ await rememberApplication(ctx, principal, app.id);
1188
+ return namingCreatedApplication(app.id, "import_app", await migrateArchiveIntoApplication(ctx, {
1189
+ applicationId: app.id,
1206
1190
  archive,
1207
1191
  explicitName,
1208
1192
  input,
@@ -1210,10 +1194,7 @@ export async function importApp(ctx, input) {
1210
1194
  name,
1211
1195
  principal,
1212
1196
  source,
1213
- });
1214
- return resumedApplicationId
1215
- ? namingApplication(applicationId, result)
1216
- : namingCreatedApplication(applicationId, "import_app", result);
1197
+ }));
1217
1198
  }
1218
1199
  /**
1219
1200
  * Uploads the archive onto an application that already exists and hands
@@ -1237,18 +1218,10 @@ async function migrateArchiveIntoApplication(ctx, args) {
1237
1218
  }
1238
1219
  catch (error) {
1239
1220
  const message = error instanceof Error ? error.message : String(error);
1240
- rememberFailedStart(ctx, {
1241
- applicationId,
1242
- error,
1243
- principal,
1244
- reason: `The archive for application ${applicationId} could not be uploaded, so the migration never started and nothing was built. Send the same archive again with import_app to retry it. (${message})`,
1245
- retryAction: "retry_import",
1246
- });
1247
1221
  return {
1248
1222
  kind: "error",
1249
1223
  code: "import_upload_failed",
1250
1224
  message,
1251
- pendingAction: "retry_import",
1252
1225
  };
1253
1226
  }
1254
1227
  return promptNewApplication(ctx, {
@@ -1274,13 +1247,7 @@ async function migrateArchiveIntoApplication(ctx, args) {
1274
1247
  prompt: args.migrationPrompt,
1275
1248
  });
1276
1249
  }
1277
- /**
1278
- * Machine-readable shape a cancelled wait answers with.
1279
- *
1280
- * Callers name the application alongside it. A cancelled wait says nothing
1281
- * about the work, which carries on: a caller asked to resume it has to be able
1282
- * to point at the application rather than start a second one.
1283
- */
1250
+ /** Machine-readable shape a cancelled wait answers with. */
1284
1251
  function cancelled(capability) {
1285
1252
  return {
1286
1253
  kind: "error",
@@ -1369,7 +1336,7 @@ export async function checkAppProgress(ctx, input) {
1369
1336
  // while the credential was being resolved should never reach the point of
1370
1337
  // registering a waiter it will not read.
1371
1338
  if (ctx.signal?.aborted) {
1372
- return namingApplication(applicationId, cancelled("check_app_progress"));
1339
+ return cancelled("check_app_progress");
1373
1340
  }
1374
1341
  const cursorKey = cursorKeyFor(ctx, principal, applicationId);
1375
1342
  const stored = cursorKey
@@ -1383,7 +1350,7 @@ export async function checkAppProgress(ctx, input) {
1383
1350
  waitMs: hostCallBudgetMs(ctx, ctx.config.clarkProgressWaitMs),
1384
1351
  });
1385
1352
  if (ctx.signal?.aborted) {
1386
- return namingApplication(applicationId, cancelled("check_app_progress"));
1353
+ return cancelled("check_app_progress");
1387
1354
  }
1388
1355
  const snapshot = await readSnapshot(ctx, {
1389
1356
  applicationId,
@@ -1501,9 +1468,6 @@ export async function checkAppProgress(ctx, input) {
1501
1468
  reply: update.reply,
1502
1469
  ...(update.decision ? { decision: update.decision } : {}),
1503
1470
  generationStatus: update.generationStatus,
1504
- ...(update.retryAction && emptyTurnStatus(update.generationStatus)
1505
- ? { pendingAction: update.retryAction }
1506
- : {}),
1507
1471
  status,
1508
1472
  viewStatus: update.viewStatus,
1509
1473
  },
@@ -2088,13 +2052,7 @@ function localUnbuiltReason(turn, pending) {
2088
2052
  return undefined;
2089
2053
  }
2090
2054
  const status = emptyTurnStatus(turn.generationStatus);
2091
- return status
2092
- ? {
2093
- kind: "empty_turn",
2094
- retryAction: turn.retryAction ?? "retry_build",
2095
- status,
2096
- }
2097
- : undefined;
2055
+ return status ? { kind: "empty_turn", status } : undefined;
2098
2056
  }
2099
2057
  /**
2100
2058
  * The same question asked of what was persisted, for a caller with no live turn
@@ -2182,8 +2140,6 @@ function persistedTurnEnd(status) {
2182
2140
  // plain question and cannot be approved.
2183
2141
  case "needs_decision":
2184
2142
  return { kind: "awaiting_decision" };
2185
- // The stream never recorded whether the prompt carried an archive, so
2186
- // this path cannot pick retry_build vs retry_import. Omit rather than guess.
2187
2143
  case "live_edit_terminated":
2188
2144
  return { kind: "empty_turn", status };
2189
2145
  // A `cancelled` in the tail may still have a real app behind it: the
@@ -2255,11 +2211,7 @@ async function unbuiltApp(ctx, principal, applicationId) {
2255
2211
  if (reason.kind === "empty_turn") {
2256
2212
  return {
2257
2213
  kind: "ok",
2258
- value: {
2259
- ...app,
2260
- ...(reason.retryAction ? { pendingAction: reason.retryAction } : {}),
2261
- status: reason.status,
2262
- },
2214
+ value: { ...app, pendingAction: "retry_build", status: reason.status },
2263
2215
  };
2264
2216
  }
2265
2217
  return {
@@ -2393,7 +2345,7 @@ async function waitForBuild(ctx, input) {
2393
2345
  let started = false;
2394
2346
  for (;;) {
2395
2347
  if (ctx.signal?.aborted) {
2396
- return namingApplication(input.applicationId, cancelled("get_app"));
2348
+ return cancelled("get_app");
2397
2349
  }
2398
2350
  // Checked before the network call, not only after it: a budget already
2399
2351
  // exhausted when this loop begins (or used up by the previous