Releases: txn2/mcp-data-platform
mcp-data-platform-v1.40.4
Fix: Markdown Thumbnail Capture Race Condition
v1.40.2 and v1.40.3 attempted to fix blank markdown thumbnails but failed — v1.40.2 speculated visibility: hidden was the cause (it wasn't), and v1.40.3 changed positioning without addressing the real issue. This release fixes the actual root cause.
Root Cause
DomCapture used setTimeout(doCapture, 500) — an arbitrary 500ms delay — to wait for ReactMarkdown to finish rendering before capturing the thumbnail. ReactMarkdown renders asynchronously, and if it hadn't completed within 500ms, toPng captured a container with no rendered content, producing a blank PNG.
Fix
Replaced the arbitrary timeout with a MutationObserver that watches for actual rendered elements (p, h1–h3, li, pre, blockquote, table) to appear in the DOM. Once content is detected, capture fires after one requestAnimationFrame to let layout settle. SVG content (set synchronously via dangerouslySetInnerHTML) is caught by an initial element check before the observer is attached.
The existing CAPTURE_TIMEOUT_MS (15s) safety net remains as a fallback.
Verified
Tested end-to-end in the browser with VITE_MSW=true:
toPngproduced a valid 11.8KB PNG blob from the DomCapture containerthumbnail_s3_keyset after capture completed- Save-triggered regeneration re-captured successfully
- Zero console errors
Also Included
- Mock
PUT /assets/:id/thumbnailhandlers added to MSW so thumbnail capture can be tested in dev mode without a real backend.
Changed Files
ui/src/components/ThumbnailGenerator.tsx—DomCapture: MutationObserver replaces setTimeoutui/src/mocks/handlers.ts— mock thumbnail upload endpoints for dev testing
mcp-data-platform-v1.40.3
Fix: Blank Markdown Thumbnails
Markdown (and SVG) assets were producing blank/transparent thumbnail images due to the DomCapture component using visibility: hidden to hide its off-screen render container.
Root Cause
The html-to-image library (toPng) works by cloning the target DOM node and serializing it into an SVG foreignObject, preserving all computed styles — including visibility: hidden. The cloned content was therefore invisible, producing a blank PNG on every capture attempt.
Fix
Replaced visibility: hidden with left: -9999; top: -9999 off-screen positioning — the same pattern already used by IframeCapture for HTML/JSX assets. The element retains full layout (required for accurate capture) but is positioned outside the viewport.
Changed Files
ui/src/components/ThumbnailGenerator.tsx—DomCapturecontainer: removedvisibility: hiddenandzIndex: -1, switched to off-screen positioning
mcp-data-platform-v1.40.2
Thumbnail Capture Quality Fixes
This release fixes four distinct quality issues with the asset thumbnail capture system introduced in v1.40.0.
JSX Asset Rendering
JSX assets now produce rendered dashboard thumbnails instead of blank or raw-source captures. A new buildJsxThumbnailHtml function transpiles JSX via sucrase and scaffolds a complete HTML document with an import map and auto-mount — the same rendering pipeline used by JsxRenderer — with a 2-second ready delay to allow async esm.sh module loads before capture.
Desktop-Scale Viewport for HTML Assets
HTML iframe captures now render at 1280×960 and scale down to the 400×300 thumbnail via html2canvas, so desktop-designed dashboard layouts appear realistic instead of being squeezed into a mobile-width viewport. New RENDER_WIDTH / RENDER_HEIGHT constants control the pre-scale dimensions.
Markdown Prose Rendering
Markdown thumbnails previously rendered blank because the DomCapture component relied on Tailwind prose classes that weren't available in the off-screen container. The fix replaces Tailwind classes with self-contained inline CSS styles (.thumb-prose) and switches from left: -9999 positioning to visibility: hidden so layout flow is preserved for html-to-image.
Thumbnail Regeneration on Save
Editing and saving an asset's content now triggers automatic thumbnail regeneration. A thumbnailStale flag is set on successful save, which remounts the ThumbnailGenerator with the updated content via a React key change. The flag resets on capture completion or failure through new onDone / onFailed callbacks.
Changed Files
ui/src/components/AssetViewer.tsx— thumbnailStale state, save-triggered regen, onDone/onFailed wiringui/src/components/ThumbnailGenerator.tsx— JSX content type routing, RENDER_WIDTH/HEIGHT viewport, Markdown inline stylesui/src/lib/thumbnail.ts— buildJsxThumbnailHtml, RENDER_WIDTH/HEIGHT constants, html2canvas scale config
mcp-data-platform-v1.40.1
Asset Thumbnail System
This release adds automatic thumbnail generation for portal assets and fixes a bug that prevented thumbnails from being captured.
New: Client-Side Thumbnail Capture (#232)
Assets that support visual preview (HTML, JSX, Markdown, SVG) now get automatic PNG thumbnails. The My Assets page displays these as a card grid with 4:3 thumbnail previews instead of the previous text-only list.
How it works:
- HTML/JSX assets render in a sandboxed iframe; the parent captures the content using the bundled
html2canvaslibrary - Markdown assets render with ReactMarkdown and are captured using
html-to-image - SVG assets are sanitized with DOMPurify, rendered inline, and captured using
html-to-image - A background queue on the My Assets page detects assets missing thumbnails and processes them one at a time
- Viewing a single asset also triggers thumbnail generation if one is missing
- Updating asset content automatically clears the old thumbnail so a fresh one is generated on next view
Backend:
- New database migration (
000020_portal_asset_thumbnails) addsthumbnail_s3_keycolumn toportal_assets - New endpoints on both portal and admin routers:
PUT /assets/{id}/thumbnail— upload PNG thumbnail (max 512 KB,image/pngonly)GET /assets/{id}/thumbnail— retrieve thumbnail with appropriateCache-Controlheaders
- Thumbnail S3 key is derived from the asset's existing S3 key (same directory, filename
thumbnail.png) AssetUpdate.ThumbnailS3Keyuses*stringto distinguish "no change" (nil) from "clear" (pointer to empty string)
Frontend:
ThumbnailGenerator— hidden off-screen component with two capture strategies (iframe for HTML/JSX, DOM for Markdown/SVG)ThumbnailQueue— background processor that finds assets without thumbnails and captures them sequentially; tracks processed IDs to prevent duplicate work on refetch- 15-second capture timeout with
onFailedcallback prevents the queue from stalling if a capture hangs - My Assets page redesigned as a card grid with content type badges and share indicators overlaid on thumbnails
- New npm dependencies:
html2canvas,html-to-image
Fix: Thumbnail Capture Never Fired (#233)
The iframe sandbox was set to allow-scripts allow-same-origin (required for html2canvas to access contentDocument), but the postMessage origin validation still checked for "null". With allow-same-origin, blob: iframes inherit the parent's origin, so every thumbnail-ready message was silently rejected. Changed the check to window.location.origin.
Files Changed
| Area | Files |
|---|---|
| Backend (portal) | pkg/portal/handler.go, pkg/portal/types.go, pkg/portal/store.go |
| Backend (admin) | pkg/admin/assets.go |
| Migration | pkg/database/migrate/migrations/000020_portal_asset_thumbnails.{up,down}.sql |
| Frontend (capture) | ui/src/lib/thumbnail.ts, ui/src/components/ThumbnailGenerator.tsx, ui/src/components/ThumbnailQueue.tsx |
| Frontend (UI) | ui/src/pages/assets/MyAssetsPage.tsx, ui/src/components/AssetViewer.tsx |
| Frontend (API) | ui/src/api/portal/client.ts, ui/src/api/portal/hooks.ts, ui/src/api/portal/types.ts |
| Tests | pkg/portal/handler_test.go, pkg/portal/store_test.go, pkg/admin/assets_test.go |
Upgrade Notes
- Database migration required: Run migrations to add the
thumbnail_s3_keycolumn. Thumbnails will generate automatically as users visit their assets. - No configuration changes needed: Thumbnail storage uses the existing S3 bucket and derives keys from the asset's content key.
Installation
Homebrew (macOS)
brew install txn2/tap/mcp-data-platformClaude Code CLI
claude mcp add mcp-data-platform -- mcp-data-platformDocker
docker pull ghcr.io/txn2/mcp-data-platform:v1.40.1Verification
All release artifacts are signed with Cosign. Verify with:
cosign verify-blob --bundle mcp-data-platform_1.40.1_linux_amd64.tar.gz.sigstore.json \
mcp-data-platform_1.40.1_linux_amd64.tar.gzmcp-data-platform-v1.40.0
Changelog
Bug Fixes
Installation
Homebrew (macOS)
brew install txn2/tap/mcp-data-platformClaude Code CLI
claude mcp add mcp-data-platform -- mcp-data-platformDocker
docker pull ghcr.io/txn2/mcp-data-platform:v1.40.0Verification
All release artifacts are signed with Cosign. Verify with:
cosign verify-blob --bundle mcp-data-platform_1.40.0_linux_amd64.tar.gz.sigstore.json \
mcp-data-platform_1.40.0_linux_amd64.tar.gzmcp-data-platform-v1.39.8
Bug Fixes
Public Viewer: Markdown Content Scroll Restored
The iframe layout fixes in v1.39.5–v1.39.7 (#228–#230) introduced overflow: hidden on the .content container in the public viewer template. This prevented markdown and other non-iframe content from scrolling, since those render as regular divs rather than iframes.
Fix: Changed overflow: hidden to overflow: auto on .content, allowing non-iframe content to scroll while preserving the flex layout that makes iframes fill the viewport.
Affected file: pkg/portal/templates/public_viewer.html
Source Editor: Position Retained Across Tab Switches
Toggling between Source and Preview in the asset viewer previously unmounted the CodeMirror SourceEditor component, destroying scroll position, cursor placement, selections, and undo history. Users had to re-navigate to their editing position every time they previewed changes.
Fix: The SourceEditor now stays mounted when switching to Preview, hidden via display: none instead of being removed from the DOM. All CodeMirror internal state (scroll, cursor, selections, undo history) is preserved across tab switches. The ContentRenderer (preview) still unmounts when switching to Source, which is fine since preview is stateless.
Affected file: ui/src/components/AssetViewer.tsx
Installation
Homebrew (macOS)
brew install txn2/tap/mcp-data-platformClaude Code CLI
claude mcp add mcp-data-platform -- mcp-data-platformDocker
docker pull ghcr.io/txn2/mcp-data-platform:v1.39.8Verification
All release artifacts are signed with Cosign. Verify with:
cosign verify-blob --bundle mcp-data-platform_1.39.8_linux_amd64.tar.gz.sigstore.json \
mcp-data-platform_1.39.8_linux_amd64.tar.gzmcp-data-platform-v1.39.7
Bug Fixes
- Public viewer: iframe fills viewport with no footer gap — The wrapper
divinside.contentdefaulted toflex: 0 1 auto, so it did not stretch to fill its parent. The iframe'sflex: 1had no effect because its parent was not a flex container. Added.content > divas a flex column so the iframe expands to fill all remaining viewport space. (#230)
Installation
Homebrew (macOS)
brew install txn2/tap/mcp-data-platformClaude Code CLI
claude mcp add mcp-data-platform -- mcp-data-platformDocker
docker pull ghcr.io/txn2/mcp-data-platform:v1.39.7Verification
All release artifacts are signed with Cosign. Verify with:
cosign verify-blob --bundle mcp-data-platform_1.39.7_linux_amd64.tar.gz.sigstore.json \
mcp-data-platform_1.39.7_linux_amd64.tar.gzmcp-data-platform-v1.39.6
Bug Fixes
- Public viewer: iframe fills viewport with no footer gap — The JsxRenderer component sets an inline
height: 80vhon iframes, which overrode the stylesheet's flex layout. Addedheight: auto !importantto neutralize the inline style, lettingflex: 1stretch the iframe to fill all remaining viewport space. (#229)
Installation
Homebrew (macOS)
brew install txn2/tap/mcp-data-platformClaude Code CLI
claude mcp add mcp-data-platform -- mcp-data-platformDocker
docker pull ghcr.io/txn2/mcp-data-platform:v1.39.6Verification
All release artifacts are signed with Cosign. Verify with:
cosign verify-blob --bundle mcp-data-platform_1.39.6_linux_amd64.tar.gz.sigstore.json \
mcp-data-platform_1.39.6_linux_amd64.tar.gzmcp-data-platform-v1.39.5
Public Viewer CSS Fix
The public viewer's global CSS reset (* { margin: 0; padding: 0; box-sizing: border-box; }) was unlayered, which per the CSS cascade spec always beats @layer styles regardless of specificity. This killed every Tailwind prose style — paragraph margins, table cell padding, horizontal rules, card borders — making markdown rendering in the public viewer look completely different from the internal SPA portal.
This release scopes the reset to html, body only and removes the min-height: 100vh body stretching. Content inside #content-root is now styled purely by the Tailwind CSS bundle, rendering identically to the internal viewer.
Changelog
Bug Fixes
- b6a794c: fix: remove unlayered CSS global reset that overrides Tailwind prose styles (#228) (@cjimti)
Installation
Homebrew (macOS)
brew install txn2/tap/mcp-data-platformClaude Code CLI
claude mcp add mcp-data-platform -- mcp-data-platformDocker
docker pull ghcr.io/txn2/mcp-data-platform:v1.39.5Verification
All release artifacts are signed with Cosign. Verify with:
cosign verify-blob --bundle mcp-data-platform_1.39.5_linux_amd64.tar.gz.sigstore.json \
mcp-data-platform_1.39.5_linux_amd64.tar.gzmcp-data-platform-v1.39.4
Fix: Public Content Viewer Blank Page
If you are on v1.39.3, upgrade immediately. The public content viewer (/portal/view/<token>) was completely broken — pages showed "Loading..." indefinitely with no rendered content.
What Happened
The v1.39.3 release introduced a shared content renderer that moves all public viewer rendering client-side via an embedded React IIFE bundle. However, the GoReleaser before hook that builds the release binary was never updated to build this new bundle. It only built the SPA (npm run build → internal/ui/dist/), leaving internal/contentviewer/dist/ empty.
Because the Go binary uses //go:embed to bake the bundle into the binary at compile time, the released binary contained empty strings for both contentviewer.JS and contentviewer.CSS. The public viewer template rendered <script></script> (empty JS) and the page was permanently stuck on the "Loading..." placeholder.
What Changed
.goreleaser.yml — Extended the release build hook to also:
- Build the content viewer IIFE bundle (
npx vite build --config vite.content-viewer.config.ts) - Copy
content-viewer.jstointernal/contentviewer/dist/ - Copy the SPA's CSS output as
content-viewer.csstointernal/contentviewer/dist/
This mirrors what make frontend-build does locally.
Impact
- Affected: Any deployment running v1.39.3 where the public content viewer is used
- Symptom: Public share links show header/chrome but content area displays "Loading..." forever
- Resolution: Upgrade to v1.39.4
Installation
Homebrew (macOS)
brew install txn2/tap/mcp-data-platformClaude Code CLI
claude mcp add mcp-data-platform -- mcp-data-platformDocker
docker pull ghcr.io/txn2/mcp-data-platform:v1.39.4Verification
All release artifacts are signed with Cosign. Verify with:
cosign verify-blob --bundle mcp-data-platform_1.39.4_linux_amd64.tar.gz.sigstore.json \
mcp-data-platform_1.39.4_linux_amd64.tar.gz