The negotiated MCP-Apps capability lived only in the tool server instance, set from the client at initialize. A session whose Durable Object was evicted (deploy, idle) and cold-restored on the same mcp-session-id was rebuilt with no initialize to replay, so apps support read back as false and create-artifact/show-artifact answered with the deep-link fallback instead of the inline widget, mid-conversation. Persist the negotiated bit in SessionMeta and seed the rebuilt server from it. A real initialize still overrules the restored value in both directions, and init carries the stored bit through its re-resolve so restoring a session no longer erases it.
Showing the changes to apps/cloud/src (1 of 6 files). Show all
Unified · Split
| @@ -280,6 +280,10 @@ export class McpSessionDOSqlite extends McpAgentSessionDOBase<Env, CloudSessionD · 279 unmodified lines | ||
| 280 | 280 | // flag existed carry no value; absent now means off, same as a fresh |
| 281 | 281 | // connection that did not ask for `?artifacts=true`. |
| 282 | 282 | artifactsEnabled: sessionMeta.artifactsEnabled ?? false, |
| 283 | + // Cold restores rebuild this server with no `initialize` to replay, so | |
| 284 | + // the negotiated apps support comes back from storage instead. | |
| 285 | + restoredAppsEnabled: sessionMeta.appsEnabled ?? false, | |
| 286 | + onAppsEnabledChange: (appsEnabled) => self.persistAppsEnabled(appsEnabled), | |
| 283 | 287 | loadAppShellHtml, |
| 284 | 288 | smokeRenderArtifact, |
| 285 | 289 | artifactUrl: artifactUrlFor( |