Skip to content

fix(presentation-mcp): draw views in hosts that null fields or drop structuredContent - #150

Merged
jm-rivera merged 1 commit into
devfrom
fix/view-hydration
Nov 3, 2026
Merged

jm-rivera merged 1 commit into
devfrom
fix/view-hydration

Conversation

@jm-rivera

Copy link
Copy Markdown
Contributor

DFO's charts, tables and cards never draw in claude.ai web. The view loads and stays on Prefab's "Waiting for content…". Prefab 0.20.2's bridge validates each ui/notifications/tool-result against the MCP SDK's CallToolResultSchema and drops a failing message silently. claude.ai sends two kinds that fail: live results with annotations: null on content blocks (anthropics/claude-ai-mcp#647), and results replayed after a reload with no structuredContent (anthropics/claude-ai-mcp#1158).

presentation_mcp/view_hydration.js is inlined into the renderer page by theme.renderer_html, ahead of Prefab's scripts, so its listener sees every host message first. It removes nulls everywhere except below structuredContent and _meta, and merges _meta["org.one.dfo/view_state"] into the view state as the old inline script did. A result with no view is swallowed and replaced: the script calls describe_component through the host, pinned to the component_id and revision in the result's payload text, and hands Prefab that result. Error results, unpinnable results, a failed or late re-read, and tool-cancelled draw a notice built with notice_view. A test in claude.ai confirmed it forwards a view's tools/call and returns the full result. The re-read waits 60 s, above the 45 s _CallLimit the combined server puts on outside calls.

renderer_html now takes base_url, because the notice names the theme stylesheet. So the renderer URI changes with the whole PRESENTATION_PUBLIC_URL, and test_the_uri_changes_with_the_base_url asserts that.

write_component declares openWorldHint: false. It writes only to DFO's own component store, like export_data.

tests/test_view_hydration.py drives the bundled Prefab page in Playwright's headless Chromium with a fake host (fixtures/view_host.html). Its 16 cases cover the spec case, the #647 nulls, missing structuredContent on a create and a describe, revision pinning, error results, re-read errors and timeouts, tool-cancelled, and a re-read overtaken by a good result. The CI test job and just setup install Chromium.

After release, check in claude.ai that a DFO chart draws live and again after a reload, and that the re-read raises no approval prompt.

…tructuredContent

Prefab 0.20.2's bridge drops a tool result that fails CallToolResultSchema
with no error, so a view stays on "Waiting for content…". claude.ai sends
such results: live results carry `annotations: null` on content blocks
(anthropics/claude-ai-mcp#647), and results replayed after a reload carry
no `structuredContent` (#1158).

view_hydration.js, inlined ahead of Prefab's scripts by renderer_html,
removes nulls outside `structuredContent` and `_meta`, merges the view
state, and re-reads a component whose result has no view through
describe_component, pinned to the revision in the result's payload. When
it cannot, and on tool-cancelled, it draws a notice. The re-read waits
60 s, above the 45 s _CallLimit on the combined server.

write_component declares openWorldHint false: it writes only to DFO's own
component store.

Browser tests drive the page with Playwright's headless Chromium. CI and
`just setup` install it.
@jm-rivera
jm-rivera merged commit e2aab64 into dev Nov 3, 2026
6 checks passed
@jm-rivera
jm-rivera deleted the fix/view-hydration branch November 3, 2026 16:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant