mcp-data-platform-v0.33.5
What's Fixed
This release fixes the blank-page JSX rendering bug that affected v0.33.1 through v0.33.4, and adds provenance session ID propagation so tool-call history survives session recovery.
JSX Renderer: dual React instance bug
The sandboxed iframe used ?bundle on all esm.sh imports. Each ?bundle URL creates a self-contained bundle with its own copy of React internals:
react@19?bundle→ React instance Areact@19&bundle/jsx-runtime→ React instance B (Sucrase's automatic JSX runtime)react-dom@19/client?bundle→ React instance C
When `createRoot` (instance C) mounted a component whose hooks were compiled against instance B's jsx-runtime, `useState` tried to read a dispatcher that was never set — producing `TypeError: Cannot read properties of null (reading 'useState')` and a blank iframe.
Root cause: `?bundle` inlines ALL transitive dependencies, creating isolated copies of React internals that don't share state.
Fix: Remove `?bundle` from `react` and `react-dom` so esm.sh deduplicates via shared module URLs. Leaf packages (recharts, lucide-react) use `?bundle&external=react,react-dom` to bundle their own deps while externalizing React.
JSX Renderer: replaced blob-module imports with inline Sucrase transform
The old auto-mount path used `await import(blobUrl)` which triggered `TypeError: Failed to fetch dynamically imported module: blob:...` in sandboxed iframes. Now uses Sucrase to transform JSX at the host level, then injects the plain JS directly into the iframe's `<script type="module">` with an import map.
JSX Renderer: `</script>` injection safety
Transformed JSX injected into the iframe's `<script>` block could contain literal `</script>` strings, breaking the HTML structure. Added `escapeScriptClose()` to replace `</script` with `<\/script` (valid JS escape, does not terminate the HTML script tag). Error display also switched from raw HTML interpolation to safe `textContent` assignment via `JSON.stringify`.
Additional JSX Renderer improvements
- `unhandledrejection` handler: Module-level import failures don't trigger `window.onerror`. The new handler catches these and displays the error instead of a silent blank page.
- CSP entries for Google Fonts: Dashboard artifacts that use `@import url('fonts.googleapis.com/...')` no longer get blocked.
- Component detection rewrite: `findComponentName()` replaces the old `ensureExport()` approach. Instead of mutating source to add `export default`, the renderer detects the component name and generates a mount call directly.
Provenance session ID propagation
When a browser session expires and gets replaced (user has a valid auth token), the old session's provenance data was previously lost because it was keyed to the old session ID.
- `WithReplacedSessionID` / `ReplacedSessionID`: new context helpers in `pkg/session` to carry the old session ID through the replacement flow.
- `harvestProvenance`: new helper in `pkg/middleware` that collects tool calls from both the current session AND the replaced session, merging them in chronological order.
- The session handler now calls `WithReplacedSessionID` in `handleExisting` when creating a replacement session.
Test Coverage
- 20 vitest tests (JSX transform, component detection, script escape, injection safety, renderer integration, regression)
- 5 new Go tests (ReplacedSessionID roundtrip, empty context, session replacement, normal session, provenance harvest merge)
- Full Go suite passes with `-race`
- Browser-verified with complex 25KB JSX dashboard (recharts, tabs, KPI cards)
Installation
Homebrew (macOS)
```bash
brew install txn2/tap/mcp-data-platform
```
Claude Code CLI
```bash
claude mcp add mcp-data-platform -- mcp-data-platform
```
Docker
```bash
docker pull ghcr.io/txn2/mcp-data-platform:v0.33.5
```
Verification
All release artifacts are signed with Cosign. Verify with:
```bash
cosign verify-blob --bundle mcp-data-platform_0.33.5_linux_amd64.tar.gz.sigstore.json
mcp-data-platform_0.33.5_linux_amd64.tar.gz
```