Releases: txn2/mcp-data-platform
mcp-data-platform-v1.39.3
Shared Content Renderer with Single CSS Build
This release unifies how the public content viewer renders all content types. Instead of server-side rendering with separate Go libraries, the public viewer now uses the same React components and Tailwind CSS as the authenticated portal — eliminating an entire class of styling bugs caused by divergent Tailwind builds.
Highlights
Unified client-side rendering — The public viewer now renders markdown, SVG, JSX, HTML, and plain text using the same ContentRenderer React component as the internal portal. Server-side rendering via goldmark and bluemonday has been removed entirely. Content is passed as JSON to an embedded IIFE bundle that hydrates client-side.
Single CSS build — Previously, the content viewer had its own @import "tailwindcss" entry point, producing a second CSS build with different prose styles. Now the Makefile builds the SPA first, then copies its CSS output as content-viewer.css. The IIFE Vite config (vite.content-viewer.config.ts) produces JS only.
Dark mode fix — Added @custom-variant dark (&:where(.dark, .dark *)) to index.css so Tailwind's dark: variants key off the .dark class instead of @media(prefers-color-scheme:dark). This fixes white-on-white markdown text when the OS is in dark mode but the page is toggled to light.
What Changed
Content rendering (pkg/portal/public.go):
- Replaced
renderContent(),renderMarkdown(),sanitizeSVG(),jsxIframe(),sandboxedIframe(),blobIframe()with a single JSON payload passed to the client-side content viewer bundle - Unified
publicCSP()to a single policy for all content types (client-side rendering makes per-type CSP unnecessary) - Removed
goldmarkandbluemondayGo dependencies (-4 direct deps, -8 transitive deps from go.sum)
Public viewer template (pkg/portal/templates/public_viewer.html):
- Added shadcn HSL CSS variables (
--background,--foreground,--card,--muted,--border,--primary, etc.) for light and dark modes so the SPA CSS resolves colors correctly - Renamed page shell
--borderto--page-borderto avoid collision with the shadcn HSL--bordertriplet - Added
.darkclass toggle alongsidedata-themeattribute for Tailwind compatibility
New: content viewer embed package (internal/contentviewer/):
embed.go— Go embed for JS + CSS bundles withloadBundles()for testability- Gracefully handles missing bundles (empty strings when dist only has
.gitkeep)
New: content viewer entry point (ui/src/content-viewer-entry.tsx):
- Reads content from embedded
<script type="application/json">data element - Renders via the shared
ContentRenderercomponent - Includes a
MutationObserveras a defensive fallback fordata-theme→.darkclass bridging
New: Vite IIFE build config (ui/vite.content-viewer.config.ts):
- Builds
content-viewer-entry.tsxas an IIFE bundle (JS only, no CSS) - Output goes to
ui/dist-content-viewer/
New: dev preview server (cmd/preview-content-viewer/main.go):
- Standalone HTTP server at localhost:9090 for testing the content viewer
- Sample content for all 5 content types (markdown, SVG, JSX, HTML, plain text)
- Light/dark theme toggle with theme persistence
Build system (Makefile):
- New
frontend-build-content-viewertarget for standalone JS bundle builds frontend-buildnow builds SPA first, then content viewer JS, then copies SPA CSS- Resilient CSS file discovery using
findinstead of fragilels index-*.cssglob cleanandembed-cleantargets updated for the newinternal/contentviewer/dist/directory
Dev environment (dev/):
- Added SeaweedFS S3-compatible object store to docker-compose for local portal testing
- Pinned SeaweedFS image to
3.88@sha256:98e034... - Added portal config to
dev/platform.yamlwith S3 connection - Added
dev/seaweedfs-s3.jsonfor SeaweedFS S3 credentials
Security Notes
- SVG content is now sanitized client-side via
DOMPurify(SVG profile) instead of server-sidebluemonday - HTML content continues to render in sandboxed
<iframe>via blob: URL (sandbox="allow-scripts") - Markdown uses
react-markdown(nodangerouslySetInnerHTML), safe by default - Content JSON injection uses
json.Marshalwhich escapes<,>,&as\uXXXX, preventing</script>breakout
Files Changed
18 files changed, 682 additions, 476 deletions
| File | Change |
|---|---|
pkg/portal/public.go |
-216 lines: removed 6 server-side rendering functions |
pkg/portal/public_test.go |
Replaced render-specific tests with unified template tests |
internal/contentviewer/embed.go |
New: Go embed package for JS/CSS bundles |
internal/contentviewer/embed_test.go |
New: tests with fstest.MapFS for full coverage |
cmd/preview-content-viewer/main.go |
New: dev preview server |
cmd/preview-content-viewer/main_test.go |
New: table-driven handler tests |
ui/src/content-viewer-entry.tsx |
New: React IIFE entry point |
ui/vite.content-viewer.config.ts |
New: Vite IIFE build config |
ui/src/index.css |
@custom-variant dark for class-based dark mode |
pkg/portal/templates/public_viewer.html |
HSL variables, .dark class bridge |
Makefile |
Content viewer build targets, resilient CSS glob |
go.mod / go.sum |
Removed goldmark, bluemonday, and transitive deps |
dev/docker-compose.yml |
Added pinned SeaweedFS service |
dev/platform.yaml |
Portal + S3 config for local dev |
dev/seaweedfs-s3.json |
New: SeaweedFS S3 credentials |
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.3Verification
All release artifacts are signed with Cosign. Verify with:
cosign verify-blob --bundle mcp-data-platform_1.39.3_linux_amd64.tar.gz.sigstore.json \
mcp-data-platform_1.39.3_linux_amd64.tar.gzmcp-data-platform-v1.39.2
Public Viewer Layout and Share Dialog Fixes
The public viewer (/portal/view/{token}) now renders dashboards at full viewport width, matching the internal portal viewer. Previously, a max-width: 1200px constraint on the content container caused responsive dashboards to collapse into single-column mode.
What changed
- Full-width public viewer: Removed the fixed max-width from the content container so embedded dashboards use the full viewport, rendering KPI grids and side-by-side charts correctly
- Readable implementor logos: Implementor logos in the public viewer header now scale proportionally (
height: 28px; width: auto) instead of being forced to 24x24px, making text-based logos (e.g., "ACME CORPORATION") legible - Better share dialog defaults: "Show expiration" is now checked by default and the notice text is pre-filled with the standard confidentiality message, reducing clicks for the common case
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.2Verification
All release artifacts are signed with Cosign. Verify with:
cosign verify-blob --bundle mcp-data-platform_1.39.2_linux_amd64.tar.gz.sigstore.json \
mcp-data-platform_1.39.2_linux_amd64.tar.gzmcp-data-platform-v1.39.1
Critical Fix: Charts and External Content in Public Viewer
v1.39.0 introduced the public viewer with a Content-Security-Policy that was too restrictive for HTML content. User-uploaded dashboards that load external libraries (Chart.js, D3, Plotly, Google Fonts, etc.) from CDNs rendered blank charts because the CSP blocked all external script sources.
Root cause: The public viewer wraps content in a blob: URL iframe. The previous code assumed blob: iframes do not inherit the parent document's CSP — this is incorrect. Modern browsers (Chromium, Firefox) propagate CSP to blob: origin iframes, so the parent's script-src 'unsafe-inline' policy blocked every <script src="https://..."> tag inside the iframe.
Fix: publicCSP() now allows https: sources for scripts, styles, fonts, images, and network requests for both HTML and JSX content types. Security isolation for embedded content is provided by the iframe's sandbox="allow-scripts" attribute (opaque origin, no top navigation, no form submission), not by CSP.
Before / After
| Content Type | v1.39.0 | v1.39.1 |
|---|---|---|
| HTML with inline JS only | Works | Works |
| HTML loading Chart.js from CDN | Blank charts | Works |
| HTML loading D3/Plotly from CDN | Blank visualizations | Works |
| JSX with esm.sh imports | Works (allow-listed) | Works (generalized) |
Share Dialog Options
The backend already supported hide_expiration and notice_text fields on shares (added in v1.39.0), but the Share Dialog had no UI controls for them. This release adds a collapsible Options section to the dialog:
- Hide expiration notice — checkbox, suppresses the countdown in the public viewer
- Notice text — text input for custom notice text (replaces the default "Proprietary & Confidential..." message)
Options are only sent when creating public links (not user shares).
Iframe Layout Fix
The public viewer iframe used a hardcoded height:80vh inline style that cut off tall content and left empty space below short content. Replaced with a flex layout:
.contentis now a flex column container- Iframes use
flex: 1; min-height: 60vh— they expand to fill available vertical space with a 60vh floor - Non-iframe content (markdown, inline SVG) is unaffected
Files Changed
| File | What |
|---|---|
pkg/portal/public.go |
Fixed CSP for blob: iframe inheritance; removed hardcoded height:80vh |
pkg/portal/public_test.go |
Updated CSP assertions |
pkg/portal/templates/public_viewer.html |
Flex layout CSS for .content container |
ui/src/components/ShareDialog.tsx |
Collapsible Options section with checkbox + text input |
ui/src/api/portal/types.ts |
Added hide_expiration, notice_text to Share interface |
ui/src/api/portal/hooks.ts |
Added fields to useCreateShare mutation type |
ui/src/mocks/handlers.ts |
Mock echoes new fields in share creation response |
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.1Verification
All release artifacts are signed with Cosign. Verify with:
cosign verify-blob --bundle mcp-data-platform_1.39.1_linux_amd64.tar.gz.sigstore.json \
mcp-data-platform_1.39.1_linux_amd64.tar.gzmcp-data-platform-v1.39.0
Public Viewer Overhaul
This release transforms the public viewer (/portal/view/{token}) with theme support, expiration awareness, configurable notices, and search engine protection.
Light/Dark Mode
The public viewer now respects the system prefers-color-scheme setting and includes a toggle button in the header. The selected theme persists to localStorage across visits. Theme affects page chrome (header, notice bar, background) and text content — HTML iframe content is fully isolated with a white background regardless of theme.
Expiration Countdown
When a share has an expiration, a notice bar displays the relative time remaining (e.g., "This page expires in 6 hours"). The countdown updates automatically. Suppress it per-share with hide_expiration: true at creation time.
Per-Share Notice Text
The notice bar text is now configurable per-share via the notice_text field:
{
"expires_in": "24h",
"notice_text": "Internal use only.",
"hide_expiration": false
}- Omit
notice_text→ defaults to "Proprietary & Confidential. Only share with authorized viewers." - Set to
""→ hides the notice text entirely - Set to custom string → displayed as-is (max 500 characters)
When both notice text is empty and there is no expiration (or expiration is hidden), the notice bar is removed entirely.
Robots.txt
A /robots.txt endpoint now returns Disallow: / for all user agents, preventing search engines from indexing the portal, API, or shared links.
Layout & Styling Fixes
- Content area fills remaining viewport height via flexbox — no excess whitespace below short content
- Iframe border adapts to theme via CSS custom properties instead of hardcoded color
Database Migrations
Two new migrations run automatically on startup:
| Migration | Description |
|---|---|
| 000018 | ALTER TABLE portal_shares ADD COLUMN hide_expiration BOOLEAN NOT NULL DEFAULT FALSE |
| 000019 | ALTER TABLE portal_shares ADD COLUMN notice_text TEXT NOT NULL DEFAULT 'Proprietary & Confidential. Only share with authorized viewers.' |
Both are backward-compatible — existing shares retain default behavior.
API Changes
The POST /api/v1/portal/assets/{id}/shares endpoint accepts two new optional fields:
| Field | Type | Default | Description |
|---|---|---|---|
hide_expiration |
bool |
false |
Suppress expiration countdown in public viewer |
notice_text |
string |
"Proprietary & Confidential..." |
Custom notice text; "" hides the notice |
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.0Verification
All release artifacts are signed with Cosign. Verify with:
cosign verify-blob --bundle mcp-data-platform_1.39.0_linux_amd64.tar.gz.sigstore.json \
mcp-data-platform_1.39.0_linux_amd64.tar.gzmcp-data-platform-v1.38.5
What's New
Branded Two-Zone Header for Public Viewer
The public share viewer (/portal/view/:token) now supports a branded two-zone header:
- Far left (optional): Implementor brand — show your organization's name, logo, and link
- Far right (always shown): Platform brand — defaults to MCP Data Platform; overridable to your platform brand (e.g., "Plexara")
Both zones support clickable links when URLs are configured.
Configuration
portal:
implementor: # optional — omit to hide the left zone
name: "ACME Corp"
logo: "https://acme.com/logo.svg" # fetched at startup, inlined as SVG
url: "https://acme.com"
mcpapps:
apps:
platform-info:
config:
brand_name: "Plexara" # overrides default "MCP Data Platform"
brand_url: "https://plexara.io" # makes the platform brand clickableChangelog
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.38.5Verification
All release artifacts are signed with Cosign. Verify with:
cosign verify-blob --bundle mcp-data-platform_1.38.5_linux_amd64.tar.gz.sigstore.json \
mcp-data-platform_1.38.5_linux_amd64.tar.gzmcp-data-platform-v1.38.4
What's New
Fix: Prompt Templates Now Visible in Platform Info UI (#221)
v1.38.3 added the "Show prompt template" toggle to the Prompts tab, but it only worked for workflow and custom prompts. Toolkit prompts (portal and knowledge) had their content constants defined but never included them in the PromptInfos() return values. Because the Content field uses json:"content,omitempty", it was silently dropped from the platform_info JSON response, and the toggle never appeared.
Root Cause
The portal and knowledge toolkit PromptInfos() methods returned PromptInfo structs without the Content field:
// Before — content constant defined but not used
func (*Toolkit) PromptInfos() []registry.PromptInfo {
return []registry.PromptInfo{
{Name: saveAssetPromptName, Description: "...", Category: "toolkit"},
}
}Fix
Both toolkits now populate Content from their existing constants:
- Portal toolkit:
saveAssetPromptContentandshowAssetsPromptContent - Knowledge toolkit:
knowledgeCapturePromptandcaptureKnowledgePromptContent
Added TestPromptContentInJSON which marshals prompt infos to JSON and asserts the "content" key is present, preventing this regression in the future.
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.38.4Verification
All release artifacts are signed with Cosign. Verify with:
cosign verify-blob --bundle mcp-data-platform_1.38.4_linux_amd64.tar.gz.sigstore.json \
mcp-data-platform_1.38.4_linux_amd64.tar.gzmcp-data-platform-v1.38.3
What's New
Prompt Template Visibility (#220)
The platform-info Prompts tab now lets users see the actual prompt template behind each card. Previously, prompt cards showed only the name and description — users had no way to learn what the prompt actually instructs the AI to do.
Changes
- New
contentfield inplatform_inforesponse: Thepromptsarray now includes acontentfield containing the raw prompt template text with argument placeholders (e.g.,{topic}) - Collapsible template viewer: Each prompt card in the Prompts tab shows a "Show prompt template" toggle. Clicking it reveals the full prompt content in a styled code block; clicking again collapses it
- Inspire custom prompts: Users can read existing templates to understand the patterns and write their own
Screenshot Walkthrough
- Open the Prompts tab in the platform-info app
- Click "Show prompt template" on any prompt card
- The template content expands below the description
- Click "Hide prompt template" to collapse
Changelog
Features
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.38.3Verification
All release artifacts are signed with Cosign. Verify with:
cosign verify-blob --bundle mcp-data-platform_1.38.3_linux_amd64.tar.gz.sigstore.json \
mcp-data-platform_1.38.3_linux_amd64.tar.gzmcp-data-platform-v1.38.2
What's New
Copy Button Fix (#219)
The Copy button on the platform-info Prompts tab now actually copies text to the clipboard. Previously it showed "Copied!" feedback but silently failed because:
- The built-in platform-info app was registered without clipboard permissions, so the Clipboard API was blocked inside the sandboxed MCP App iframe
- The JavaScript
navigator.clipboard.writeText()Promise rejection was unhandled — the "Copied!" feedback ran unconditionally regardless of whether the copy succeeded
Changes
- CSP permission: The platform-info app now declares
clipboard-writepermission, which tells MCP hosts to setallow="clipboard-write"on the iframe - Proper async handling:
copyPrompt()now uses.then()/.catch()on the Clipboard API Promise — "Copied!" only appears after confirmed success - Fallback with validation: When the Clipboard API is unavailable or rejected, falls back to
document.execCommand('copy')with result checking
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.38.2Verification
All release artifacts are signed with Cosign. Verify with:
cosign verify-blob --bundle mcp-data-platform_1.38.2_linux_amd64.tar.gz.sigstore.json \
mcp-data-platform_1.38.2_linux_amd64.tar.gzmcp-data-platform-v1.38.1
What's New
Prompts Tab UX Improvements (#218)
The Prompts tab in the platform-info MCP App received several usability improvements addressing issues introduced with the dynamic prompt system in v1.38.0.
Category grouping: Prompts are now organized into two sections — Workflows (multi-step guided prompts with arguments like explore-available-data and create-interactive-dashboard) and Quick Actions (single-action toolkit prompts like save-this-as-an-asset). Section headers only appear when both categories have prompts.
Human-readable titles: Machine slugs like explore-available-data now display as "Explore Available Data" in title case, with the original slug shown below in monospace for reference when copying.
Brand-aware intro text: The generic intro ("Available prompts — select one to copy...") has been replaced with a contextual description that incorporates the platform brand name and describes what the platform can do.
Removed platform-overview from Prompts tab: The auto-invoked platform-overview prompt no longer appears in the Prompts tab since it runs automatically on session start and has no useful copy-to-clipboard action. It remains registered as an MCP prompt.
Backend changes
- Added
Categoryfield toPromptInfostruct with three values:"workflow","toolkit","custom" - All prompt registration paths now set a category: operator-configured prompts get
"custom", workflow prompts get"workflow", toolkit-registered prompts get"toolkit" - Portal and Knowledge toolkits set
Category: "toolkit"on theirPromptInfos()output - Category is serialized as
categoryin theplatform_infoJSON response (omitempty— backward compatible)
Frontend changes
- Added
humanize()function to convert slugs to title case - Refactored
renderPrompts()to group by category with section headers - Extracted
renderPromptCard()for cleaner rendering - Cards show human-readable title + monospace slug instead of just the slug
- Intro text set dynamically using the platform brand name
- New CSS classes:
.prompt-section-header,.prompt-card-title,.prompt-card-slug
Before / After
| Before (v1.38.0) | After (v1.38.1) |
|---|---|
| All prompts in a flat list | Grouped into Workflows and Quick Actions |
Machine slugs as titles (explore-available-data) |
Human titles ("Explore Available Data") with slug below |
| Generic intro text | Brand-aware intro describing platform capabilities |
platform-overview shown (not useful to copy) |
platform-overview hidden from tab |
Upgrading
Drop-in replacement for v1.38.0. No configuration changes required. The new category field in prompt metadata is additive and backward compatible.
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.38.1Verification
All release artifacts are signed with Cosign and include SBOM attestations:
cosign verify-blob --bundle mcp-data-platform_1.38.1_linux_amd64.tar.gz.sigstore.json \
mcp-data-platform_1.38.1_linux_amd64.tar.gzFull Changelog
mcp-data-platform-v0.37.7
What's New
Bug Fixes
Prevent double-generation when saving artifacts (#215)
- Added prompt guidance to the
save_artifacttool description instructing the LLM to call the tool directly with content rather than first outputting it to the conversation and then saving separately - Previously, agents would generate the full artifact (dashboard, report, chart) in the conversation text and then regenerate it token-by-token when calling
save_artifact— doubling latency and token cost - The tool description now includes an
IMPORTANTdirective to callsave_artifactdirectly with the content inline
Replace custom markdown parser with marked in platform-info app (#214)
- Replaced the hand-rolled line-by-line markdown parser (
renderMd) with inlined marked v15.0.12 (~40KB, MIT) for robust CommonMark + GFM rendering - Fixes broken rendering of headings, code fences, tables, and lists when
agent_instructionscontent has leading whitespace from YAML block scalar indentation - The old parser anchored all regex patterns to
^(start of line), so any line with leading whitespace silently fell through to paragraph handling — this was particularly problematic with YAML block scalars that strip the base indent but leave 2-space prefixes on many lines - Updated CSS for standard heading levels (
h1–h6), direct<table>styling (removed.tbl-wrapwrapper assumption), and added styles forblockquote,del, andimgelements
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:v0.37.7Verification
All release artifacts are signed with Cosign. Verify with:
cosign verify-blob --bundle mcp-data-platform_0.37.7_linux_amd64.tar.gz.sigstore.json \
mcp-data-platform_0.37.7_linux_amd64.tar.gz