Symptom
agent-bundle serve-app <server>/<app> (#537) renders the fallback panel instead of the App whenever the opening tool's result is larger than 64 KiB:
hauler/dashboard MCP App preview failed: AB8010: Request body exceeds 64 KiB.
Reproduced with cargo-hauler hauler dashboard (ScriptedAlchemy/cargo-hauler#83), which spawns agent-bundle serve-app hauler/dashboard --tool hauler_status: on a machine with ~20 active tickets and 24h metrics, hauler_status returns well over 64 KiB, and the page shows the raw JSON dump ("Showing the ordinary tool result instead"). The App is fine; the same server renders in an MCP host.
Root cause
The host page round-trips the tool result through the browser:
src/serve-app/serve-mcp-app.ts:328 calls the tool once server-side (session.bridge.callTool) and embeds the whole result in the page seed (:497).
src/serve-app/serve-app-page.ts:113 POSTs that seed back — { host, input, previewProfile, result: seed.result, toolName } — to POST /api/mcp/sessions/<id>/apps.
src/dev/mcp-apps/mcp-app-routes.ts:296 requires result in that body, and the body is read through readJsonBody with the shared 64 KiB bound (src/dev/http.ts:31-33, AB8010).
So the server refuses to accept back a value it produced itself. The Workbench's own App preview goes through the same route and has the same ceiling for any tool result > 64 KiB; it is just rarer there.
Fix options
- Keep the opening
{ input, result } server-side (serve-app already holds it) and let the page bind by sessionId/toolName only; the route accepts an omitted result when the session has a retained opening result. Minimal, no protocol change for the Workbench.
- More generally, bind an App to a server-known tool call (call id from the MCP session service) instead of re-uploading the result. This fixes the Workbench too and removes the client's ability to fabricate a result.
- Raising the bound for this one route is not enough:
hauler_status can grow unbounded with queue depth.
Whichever lands, the fallback panel should name the real cause ("result too large to rebind" is a framework defect, not an App capability gap), and tests/serve-app.test.ts should cover a >64 KiB opening result.
Related: #514, #537, #558 (the consumer that surfaced it).
Symptom
agent-bundle serve-app <server>/<app>(#537) renders the fallback panel instead of the App whenever the opening tool's result is larger than 64 KiB:Reproduced with cargo-hauler
hauler dashboard(ScriptedAlchemy/cargo-hauler#83), which spawnsagent-bundle serve-app hauler/dashboard --tool hauler_status: on a machine with ~20 active tickets and 24h metrics,hauler_statusreturns well over 64 KiB, and the page shows the raw JSON dump ("Showing the ordinary tool result instead"). The App is fine; the same server renders in an MCP host.Root cause
The host page round-trips the tool result through the browser:
src/serve-app/serve-mcp-app.ts:328calls the tool once server-side (session.bridge.callTool) and embeds the whole result in the page seed (:497).src/serve-app/serve-app-page.ts:113POSTs that seed back —{ host, input, previewProfile, result: seed.result, toolName }— toPOST /api/mcp/sessions/<id>/apps.src/dev/mcp-apps/mcp-app-routes.ts:296requiresresultin that body, and the body is read throughreadJsonBodywith the shared 64 KiB bound (src/dev/http.ts:31-33,AB8010).So the server refuses to accept back a value it produced itself. The Workbench's own App preview goes through the same route and has the same ceiling for any tool result > 64 KiB; it is just rarer there.
Fix options
{ input, result }server-side (serve-app already holds it) and let the page bind bysessionId/toolNameonly; the route accepts an omittedresultwhen the session has a retained opening result. Minimal, no protocol change for the Workbench.hauler_statuscan grow unbounded with queue depth.Whichever lands, the fallback panel should name the real cause ("result too large to rebind" is a framework defect, not an App capability gap), and
tests/serve-app.test.tsshould cover a >64 KiB opening result.Related: #514, #537, #558 (the consumer that surfaced it).