Releases: txn2/mcp-data-platform
mcp-data-platform-v0.37.6
What's New
Bug Fixes
Portal logo now renders in sandboxed MCP app iframes (#211)
- When
portal.logois a URL, the server now fetches the SVG content at startup and injects it inline aslogo_svg - Previously, the sandboxed iframe blocked external image requests, showing the default icon instead of the configured logo
- Deployments with explicit
logo_svgin config are unaffected
Asset card titles no longer overlap share icons (#212)
- Long asset names are now properly truncated before the share/public-link icons
- Added
min-w-0to fix flex container truncation andpr-12padding to clear icon positions
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.6Verification
All release artifacts are signed with Cosign. Verify with:
cosign verify-blob --bundle mcp-data-platform_0.37.6_linux_amd64.tar.gz.sigstore.json \
mcp-data-platform_0.37.6_linux_amd64.tar.gzmcp-data-platform-v0.37.2
What's Changed
Two bug fixes from #209: admin tool call authentication and asset viewer rendering.
Bug Fixes
Admin tool calls fail with "invalid API key" on browser sessions
Admin tool calls (Tools > Explore) were failing with "authentication failed: invalid API key" when using browser session auth. connectInternalSession was re-extracting the raw OIDC id_token from the session cookie and forcing it through the full MCP auth chain (OAuth JWT → OIDC → API Key). The OIDC authenticator could reject the token (e.g. expired id_token, JWKS cache stale), but ChainedAuthenticator only reports the last error — the unhelpful "invalid API key" from the API key authenticator.
Fix: connectInternalSession now calls BrowserAuth.AuthenticateHTTP(r) to get the already-validated UserInfo and passes it via WithPreAuthenticatedUser(). The MCP auth middleware checks for a pre-authenticated user before running the auth chain, skipping token validation while preserving the full authorization check (persona/role-based tool filtering).
Also adds per-authenticator slog.Debug logging to ChainedAuthenticator for diagnosability — previously only the last error was kept.
Files: pkg/admin/tools.go, pkg/middleware/mcp.go, pkg/middleware/context.go, pkg/auth/middleware.go
Asset viewer "No component found" flash on first render
The asset viewer showed a brief "No component found" error when loading JSX/TSX assets. editedContent initialized as "" while contentStr had the loaded content, so hasChanges evaluated true on first render, passing empty content to ContentRenderer → JsxRenderer.
Fix: Added a dirty state flag that is only set true when the user actually edits in the SourceEditor. hasChanges now requires dirty && editedContent !== contentStr, preventing the false-positive on initial render.
Files: ui/src/components/AssetViewer.tsx
Test Coverage
TestPreAuthenticatedUserContext— round-trip and nil-context for new context helpersTestMCPToolCallMiddleware_PreAuthenticatedUser— pre-auth user bypasses auth chain, populates PlatformContext correctlyTestMCPToolCallMiddleware_PreAuthenticatedUser_AuthzDenied— pre-auth user still goes through authorization; denied users get error result
Stats
- 7 files changed, 186 additions, 17 deletions
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.2Verification
All release artifacts are signed with Cosign. Verify with:
cosign verify-blob --bundle mcp-data-platform_0.37.2_linux_amd64.tar.gz.sigstore.json \
mcp-data-platform_0.37.2_linux_amd64.tar.gzmcp-data-platform-v0.37.1
What's Changed
In-browser source editing for portal assets, plus admin assets search and owner visibility improvements (#208).
New Feature
Source editor in asset viewer
Users and admins can now view and edit the raw source of text-based assets (HTML, JSX, Markdown, SVG, plain text) directly in the browser. A Preview/Source tab toggle appears automatically for text-based content types, with an integrated code editor and Save button.
Editor capabilities:
- CodeMirror 6 with language-aware syntax highlighting (JSX, HTML, Markdown, XML/SVG)
- Lazy-loaded via
React.lazy()to keep the main bundle small (~200KB gzip addition) - Dark mode support — detects theme from the document root class
- Unsaved edits survive tab switches — switching between Preview and Source preserves your changes
- Live preview — unsaved edits render in Preview mode before saving
New API endpoints (2):
| Method | Path | Auth | Description |
|---|---|---|---|
PUT |
/api/v1/admin/assets/{id}/content |
Admin | Replace asset S3 content |
PUT |
/api/v1/portal/assets/{id}/content |
Owner only | Replace asset S3 content |
Both endpoints read raw request body via io.LimitReader (10 MB cap), upload to S3 at the existing bucket/key, and update size_bytes in the database.
Error handling:
| Status | Condition |
|---|---|
401 |
No authenticated user (portal only) |
403 |
Non-owner attempting update (portal only) |
404 |
Asset not found |
410 |
Asset has been soft-deleted |
413 |
Content exceeds 10 MB |
503 |
S3 not configured or S3 upload failure |
500 |
Database update failure |
Frontend components:
SourceEditor.tsx— new CodeMirror wrapper with automatic language detectionAssetViewer.tsx— new shared component extracted from both viewer pages, with Preview/Source tabs, Save button with status feedback, download, share, delete confirmation modal, and metadata sidebaruseUpdateAssetContent()anduseAdminUpdateAssetContent()— new mutation hooks
Files: pkg/admin/assets.go, pkg/portal/handler.go, pkg/portal/types.go, ui/src/components/SourceEditor.tsx, ui/src/components/AssetViewer.tsx, ui/src/api/portal/hooks.ts, ui/src/api/admin/hooks.ts, ui/src/pages/viewer/AssetViewerPage.tsx, ui/src/pages/viewer/AdminAssetViewerPage.tsx
Improvements
Admin assets: unified search
The admin assets list endpoint replaces individual owner_id, content_type, and tag query params with a single search parameter. The search queries across name, description, owner email, and tags with server-side ILIKE filtering. The frontend search input is debounced (300ms).
Files: pkg/portal/store.go, ui/src/pages/assets/AdminAssetsPage.tsx, ui/src/api/admin/hooks.ts
Owner email on assets
A new owner_email column on portal_assets stores the authenticated user's email at asset creation time. This is displayed in the admin assets table and asset viewer instead of the opaque owner_id, improving admin visibility into who created each asset.
Migration: 000017_portal_assets_owner_email — adds owner_email TEXT NOT NULL DEFAULT ''
Files: pkg/portal/types.go, pkg/portal/store.go, pkg/toolkits/portal/toolkit.go, ui/src/api/portal/types.ts, ui/src/mocks/data/assets.ts
Viewer page refactor
Both AssetViewerPage and AdminAssetViewerPage were refactored from ~220 lines each down to ~30 lines by extracting the shared AssetViewer component. This eliminates duplicated viewer logic (sidebar, editing, delete modal, download, share dialog) and makes both viewers consistent.
Dependencies
@uiw/react-codemirror^4.25.8 — React wrapper for CodeMirror 6@codemirror/lang-html^6.4.11@codemirror/lang-javascript^6.2.5@codemirror/lang-markdown^6.5.0@codemirror/lang-xml^6.1.0@tailwindcss/typography^0.5.19
Test Coverage
- Admin handler (7 new tests): content update success, no S3, not found, soft-deleted asset, oversized content (413), S3 error, DB update error
- Portal handler (9 new tests): content update success, no user, not found, deleted, not owner, oversized content (413), nil S3, S3 error, DB update error
- Store (1 new test):
applyAssetFilterwith search parameter - Toolkit (1 new test):
resolveOwnerEmailcontext extraction - Coverage:
updateAdminAssetContent92.3%,updateAssetContent93.9%
CI
- Bump
docker/login-actionfrom 3.7.0 to 4.0.0 (#204)
Stats
- 27 files changed, 1,595 additions, 527 deletions (net -390 lines from viewer refactor)
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.1Verification
All release artifacts are signed with Cosign. Verify with:
cosign verify-blob --bundle mcp-data-platform_0.37.1_linux_amd64.tar.gz.sigstore.json \
mcp-data-platform_0.37.1_linux_amd64.tar.gzmcp-data-platform-v0.37.0
What's Changed
Three improvements spanning OIDC auth, portal UX, and admin capabilities (#203).
Bug Fix
OIDC logout redirect
Keycloak (and many OIDC providers) rejects relative post_logout_redirect_uri values, causing a "Invalid redirect uri" error when users clicked Sign Out. The logout handler now sends an absolute URL constructed from PublicBaseURL, matching how the login callback already works.
A new PostLogoutRedirect field on FlowConfig separates the OIDC post-logout URI from the general PostLoginRedirect. When PostLogoutRedirect is empty, it falls back to PostLoginRedirect for backward compatibility.
Files: pkg/browsersession/oidcflow.go, pkg/platform/platform.go
New Features
Asset sharing indicators on My Assets
My Assets cards now display sharing status at a glance:
- Users icon — asset is shared with one or more specific users
- Globe icon — asset has an active public link
Sharing status is fetched via a new batch ListActiveShareSummaries query that uses BOOL_OR aggregation grouped by asset_id, avoiding N+1 lookups regardless of how many assets are on the page.
Files: pkg/portal/types.go, pkg/portal/store.go, pkg/portal/handler.go, ui/src/pages/assets/MyAssetsPage.tsx, ui/src/api/portal/types.ts
Admin assets management
Admins can now browse, inspect, edit, and delete any platform asset regardless of owner. This provides full visibility into all user-created assets from the admin interface.
API endpoints (5 new routes):
| Method | Path | Description |
|---|---|---|
GET |
/api/v1/admin/assets |
List all assets with filters (owner, content type, tag) and pagination |
GET |
/api/v1/admin/assets/{id} |
Get asset metadata |
GET |
/api/v1/admin/assets/{id}/content |
Retrieve asset content from S3 |
PUT |
/api/v1/admin/assets/{id} |
Update asset name, description, or tags |
DELETE |
/api/v1/admin/assets/{id} |
Soft-delete an asset |
All endpoints are protected by the admin auth middleware. The list endpoint includes share summaries for each asset, so admins can see sharing status in the table view.
UI pages (2 new):
- Admin Assets page — table view with content type icons, owner column, sharing indicators (Users/Globe), and size/date columns. Accessible from the new "Assets" sidebar item under Admin.
- Admin Asset Viewer — full asset viewer with content rendering, metadata sidebar (edit/delete), owner badge, and provenance panel.
Files: pkg/admin/assets.go, pkg/admin/handler.go, cmd/mcp-data-platform/main.go, ui/src/pages/assets/AdminAssetsPage.tsx, ui/src/pages/viewer/AdminAssetViewerPage.tsx, ui/src/api/admin/hooks.ts, ui/src/api/admin/types.ts, ui/src/components/layout/Sidebar.tsx, ui/src/components/layout/AppShell.tsx
Test Coverage
- 3 new OIDC logout tests —
PostLogoutRedirectused in redirect URL, default fallback toPostLoginRedirect, no-end-session fallback - 17 new admin asset tests — route registration, list/get/content/update/delete success and error paths, validation, nil share store
- 5 new store tests —
ListActiveShareSummariespostgres implementation (success, empty input, query error, scan error, row iteration error) - 1 new portal handler test —
listAssetsincludesshare_summariesin response
Stats
- 20 files changed, 1,613 additions, 20 deletions
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.0Verification
All release artifacts are signed with Cosign. Verify with:
cosign verify-blob --bundle mcp-data-platform_0.37.0_linux_amd64.tar.gz.sigstore.json \
mcp-data-platform_0.37.0_linux_amd64.tar.gzmcp-data-platform-v0.36.2
What's Changed
Five post-v0.36.0 fixes for the portal and admin UI (#202).
Bug Fixes
Admin UI: OIDC authentication for Tools Explore
When an admin logged in via OIDC (browser session cookie), the Tools > Explore tab failed with "no API key found in context" because the internal MCP session only extracted tokens from X-API-Key / Authorization headers. The cookie-based OIDC flow never set those headers, so the MCP auth middleware rejected every tool call.
connectInternalSession now falls back to extracting the id_token stored in the HMAC-signed session cookie via BrowserAuth.ExtractIDToken(), which flows through the MCP ChainedAuthenticator normally.
Files: pkg/admin/tools.go, pkg/admin/handler.go, pkg/browsersession/authenticator.go, cmd/mcp-data-platform/main.go
Portal: My Knowledge page showing "PROD" instead of table names
Entity URN tags displayed the environment suffix (e.g., PROD) instead of the actual table name. The old inline parsing urn.split(",").pop() extracted the last comma-separated segment which is the environment, not the name.
A new shared formatEntityUrn() utility correctly parses urn:li:dataset:(urn:li:dataPlatform:trino,catalog.schema.table,PROD) into catalog.schema.table. Full URN is visible on hover via title attribute.
Files: ui/src/lib/formatEntityUrn.ts, ui/src/pages/knowledge/MyKnowledgePage.tsx
Admin UI: Audit Log tool names displayed as raw snake_case
The Overview tab's Top Tools bar chart and Recent Errors list displayed raw tool names like trino_query instead of human-readable titles. Added useToolTitleMap() to the OverviewTab component and passed the title mapping to both BreakdownBarChart (labelMap) and RecentErrorsList (titleMap).
Files: ui/src/pages/audit/AuditLogPage.tsx, ui/src/components/RecentErrorsList.tsx
Portal: Sidebar label consistency
Renamed sidebar label from "Activity" to "My Activity" for consistency with "My Assets" and "My Knowledge".
Files: ui/src/components/layout/Sidebar.tsx
Test Coverage
- 4 new tests for
ExtractIDToken— valid cookie, no cookie, no id_token in session, expired cookie - 3 new tests for cookie fallback in
connectInternalSession— fallback works, header preferred over cookie, nilBrowserAuth
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.36.2Verification
All release artifacts are signed with Cosign. Verify with:
cosign verify-blob --bundle mcp-data-platform_0.36.2_linux_amd64.tar.gz.sigstore.json \
mcp-data-platform_0.36.2_linux_amd64.tar.gzmcp-data-platform-v0.36.1
Fix: Zero-Fill Timeseries Chart Gaps for Sparse Activity Data (#201)
Activity timeseries charts (admin dashboard and portal user activity) collapsed to just the data points when usage was sparse, losing all temporal context on the X-axis. For example, 2 tool calls in a 24-hour window produced a chart with only 2 points instead of a full 24-hour timeline.
Root Cause
The Timeseries() SQL query uses GROUP BY date_trunc(resolution, timestamp), which only returns buckets that contain data. With sparse usage, the chart rendered a compressed line between the few populated points with no sense of scale.
Fix
Added a Go-side ZeroFill helper that expands sparse SQL results into a complete series covering [start, end] at the requested resolution. Missing buckets are filled with zero values.
Before: 2 events in a 24h window → chart with 2 points, no time scale
After: 2 events in a 24h window → chart with 24 hourly buckets, two visible spikes against a zero baseline
Why Go-side instead of SQL generate_series
- Keeps the existing squirrel-based SQL builder unchanged (lower risk)
- Pure Go logic is trivially testable with table-driven tests
- Not PostgreSQL-specific — works with any future store implementation
Frontend: Sparse Data Dots
When there are 10 or fewer non-zero data points, dots (r: 3) are shown on the success and error lines so sparse spikes are visible against the zero baseline.
Files Changed
| File | Change |
|---|---|
pkg/audit/zerofill.go |
New: ZeroFill, resolutionInterval, truncateTime helpers |
pkg/audit/zerofill_test.go |
New: 8 table-driven cases + data preservation, invalid resolution, helper unit tests |
pkg/audit/postgres/metrics.go |
Call audit.ZeroFill() after SQL query |
pkg/audit/postgres/metrics_test.go |
Updated 3 tests to use fixed times and verify zero-filled output |
ui/src/components/charts/TimeseriesChart.tsx |
Conditional dots on sparse data |
Total: 5 files changed, 317 insertions, 21 deletions
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.36.1Verification
All release artifacts are signed with Cosign. Verify with:
cosign verify-blob --bundle mcp-data-platform_0.36.1_linux_amd64.tar.gz.sigstore.json \
mcp-data-platform_0.36.1_linux_amd64.tar.gzmcp-data-platform-v0.36.0
Portal UX Enhancements — User Activity, Knowledge, Tool Titles, Help Rewrite (#200)
This release adds two new user-facing portal sections (Activity and My Knowledge), surfaces human-readable tool display names across the entire UI, fixes a sidebar navigation bug, and rewrites all admin help content for non-technical users.
New: User Activity Page
Portal users now have a personal Activity dashboard at /portal/activity showing their own usage metrics:
- Summary cards — Total Calls, Avg Duration, Tools Used
- Activity Timeline — Interactive time-series chart of tool calls over time (reuses the admin
TimeseriesChartcomponent) - Top Tools — Horizontal bar chart showing most-used tools with human-readable names (reuses
BreakdownBarChart) - Time range selector — 1h, 6h, 24h, 7d presets
Design decision: Success Rate is intentionally excluded from the user view. In AI-assisted workflows, "errors" are typically the agent self-correcting — trying a table name, getting a not-found, then adjusting. This is normal agent behavior, not something users should worry about. Success rate remains available in the admin dashboard where it's useful for platform tuning.
Backend
New portal endpoints auto-filtered to the authenticated user's user_id:
| Endpoint | Description |
|---|---|
GET /api/v1/portal/activity/overview |
Aggregate stats (total calls, avg duration, unique tools) |
GET /api/v1/portal/activity/timeseries |
Time-bucketed call counts with resolution parameter |
GET /api/v1/portal/activity/breakdown |
Top tools/connections grouped by dimension |
All three accept start_time, end_time query parameters (RFC 3339). The timeseries endpoint also accepts resolution (minute, hour, day) and the breakdown endpoint accepts group_by and limit.
Implementation: Added UserID string field to audit.TimeseriesFilter, audit.BreakdownFilter, and a new audit.MetricsFilter struct (replacing the previous startTime, endTime *time.Time parameters on Overview() and Performance()). The PostgreSQL metrics queries add WHERE user_id = $N when UserID is set. This is a breaking change to the AuditMetricsQuerier interface — all callers updated.
New: My Knowledge Page
Users whose persona includes the capture_insight tool now see a My Knowledge section in the portal sidebar, showing their own captured insights:
- Summary cards — Total Insights, Pending, Approved, Applied
- Status filter buttons — All, Pending, Approved, Applied, Rejected
- Insights list — Each insight shows status badge, category label, insight text, entity URN chips, review notes, and creation date
- Pagination — 20 items per page with previous/next navigation
- Conditional visibility — The sidebar item only appears when the user's resolved persona includes
capture_insightin its tool list
Backend
| Endpoint | Description |
|---|---|
GET /api/v1/portal/knowledge/insights |
List user's insights (filtered by captured_by = user_id) |
GET /api/v1/portal/knowledge/insights/stats |
Insight counts by status, category, confidence |
Both endpoints use the existing knowledge.InsightStore.List() and Stats() methods with CapturedBy pre-set to the authenticated user.
Persona & Tools in /me Response
The GET /api/v1/portal/me endpoint now returns two additional fields:
{
"user_id": "sarah.chen@acme-corp.com",
"roles": ["analyst"],
"is_admin": false,
"persona": "data-analyst",
"tools": ["trino_query", "datahub_search", "capture_insight", "..."]
}The tools list is resolved by running the persona's Allow/Deny patterns against the full toolkit registry, giving the frontend the exact set of tools available to the user. This enables conditional UI rendering without additional API calls.
New types: PersonaResolver func(roles []string) *PersonaInfo and PersonaInfo{Name, Tools} in the portal package. The resolver is built from the persona and toolkit registries during platform initialization.
New: Tool Display Names
Tools are now displayed with human-readable titles throughout the UI instead of raw snake_case identifiers (e.g., "Execute SQL Query" instead of trino_query).
Backend
Every tool registered by the upstream toolkits already has a Title field set:
| Toolkit | Example Titles |
|---|---|
| Trino | Execute SQL Query, Execute SQL (Write), Explain Query Plan, Describe Table, Browse Catalog |
| DataHub | Search Catalog, Get Entity, Get Schema, Get Lineage, Get Queries |
| S3 | List Buckets, List Objects, Get Object, Generate Presigned URL |
| Knowledge | Capture Insight, Apply Knowledge |
| Portal | Save Artifact, Manage Artifact |
The Title field is now included in two admin API responses:
GET /api/v1/admin/tools—titlefield on eachtoolInfoobjectGET /api/v1/admin/tools/schemas—titlefield on eachtoolSchemaobject
For the tools list endpoint, buildToolTitleMap() calls session.ListTools() internally to resolve titles from the MCP server.
Frontend
New utility formatToolName(name, title?) — uses the backend-provided title when available, falls back to converting snake_case to Title Case.
Applied across all UI surfaces where tool names appear:
| Page | What changed |
|---|---|
| Tools (inventory table) | Tool names show titles |
| Tools (tool selector) | Dropdown shows titles |
| Tools (explore tab) | Schema names show titles |
| Audit Log (events table) | tool_name column shows titles |
| Dashboard (top tools chart) | Bar chart labels show titles |
| Activity (top tools chart) | Bar chart labels show titles via labelMap prop |
| ProvenancePanel | Unified with formatToolName, removed hardcoded mapping |
The BreakdownBarChart component now accepts an optional labelMap prop (Record<string, string>) for mapping raw dimension values to display labels.
Fix: Sidebar Dashboard Highlight Bug
Bug: When navigating from Dashboard to another admin section (e.g., Tools), Dashboard remained highlighted alongside the actual active section.
Root cause: The isActive() function in Sidebar.tsx matched both exact path AND prefix for admin routes. So /admin/tools matched /admin via route.startsWith("/admin" + "/"), causing Dashboard to always appear active.
Fix: /admin now uses exact match only, consistent with / and /shared.
Rewritten: Admin Help Sections
All five admin help tabs have been rewritten for non-technical administrators:
| Page | Lines changed |
|---|---|
| Dashboard (Home) | Simplified from developer reference to user-oriented overview |
| Tools | Removed YAML config examples, API endpoint docs |
| Audit Log | Focused on "what you can learn" not "how it's stored" |
| Knowledge | Explained the insight lifecycle in plain language |
| Personas | Removed config file references, explained role mapping simply |
Guidelines applied:
- No references to YAML, configuration files,
config_mode, DSN, or PostgreSQL - No admin API endpoint documentation (admins are not developers)
- Replaced "injection" with "enrichment" (2 instances)
- Focus on what it does for you, not how it's configured
- Plain language, short paragraphs, practical examples
Improved: Onboarding Empty States
My Assets (no assets yet):
Assets are interactive dashboards, visualizations, and documents created during your conversations. Try asking your assistant to "create an interactive dashboard" or say "save this as an asset" to get started.
My Knowledge (no insights yet):
When you share knowledge about your data — corrections, business context, or quality observations — it gets captured here for review. Try telling your assistant something like "the revenue column excludes returns" or "this table is refreshed weekly". Approved insights improve the data catalog for everyone.
Portal Wiring
New optional dependencies on the portal handler, all nil-safe:
| Dependency | Source | Purpose |
|---|---|---|
AuditMetrics |
platform.AuditStore() |
User-scoped activity metrics |
InsightStore |
platform.KnowledgeInsightStore() |
User-scoped insight listing |
PersonaResolver |
persona.Registry + registry.Registry |
Resolve roles → persona + tools |
Extracted wirePortalOptionalDeps() and buildPersonaResolver() helper functions from mountPortalAPI() to stay under the cognitive complexity limit (gocognit ≤ 15).
Files Changed
| Area | Files | Delta |
|---|---|---|
| Backend wiring | cmd/mcp-data-platform/main.go |
+38 |
| Admin API | pkg/admin/system.go, tools.go, handler.go, audit_metrics.go |
+59 |
| Audit metrics | pkg/audit/metrics.go, postgres/metrics.go |
+47 |
| Portal API | pkg/portal/handler.go |
+239 |
| Portal tests | pkg/portal/handler_test.go |
+417 |
| Metrics tests | pkg/audit/postgres/metrics_test.go |
+125 |
| Swagger docs | internal/apidocs/ |
+20 |
| Frontend types & hooks | ui/src/api/ |
+186 |
| New pages | ActivityPage.tsx, MyKnowledgePage.tsx |
+334 |
| Layout & nav | Sidebar.tsx, AppShell.tsx |
+23 |
| Tool formatting | formatToolName.ts, BarChart.tsx, ProvenancePanel.tsx |
+72 |
| Help rewrites | 5 admin page files | -730 |
| Mock data | ui/src/mocks/handlers.ts |
+95 |
| Auth store | ui/src/stores/auth.ts |
+2 |
Total: 34 files changed, 1,810 insertions, 1,010 deletions
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.36.0Verification
All release artifacts are signed with Cosign. Verify with:
cosign ...mcp-data-platform-v0.35.10
Bug Fixes
JSX public viewer: fix broken component rendering (#199)
JSX public view links (/portal/view/{token}) were completely broken — the browser threw TypeError: Failed to fetch dynamically imported module and the component never rendered.
Root cause: Per the CSP3 spec, srcdoc iframes inherit the parent page's CSP headers. The effective CSP is the intersection of the parent's CSP header and the iframe's own <meta> CSP. The parent's publicCSP() was far more restrictive than the iframe's meta CSP, so the intersection blocked:
| Directive | What was blocked |
|---|---|
script-src |
esm.sh module loading (Sucrase, React), eval (Sucrase transform), blob: (dynamic import of transformed code) |
style-src |
Google Fonts CSS |
connect-src |
fetch() to esm.sh |
font-src |
Google Fonts files |
Fix: Aligned the parent page's CSP with the iframe's meta CSP so the intersection no longer strips required permissions. Also added blob: to script-src in the iframe meta CSP — the public viewer wraps Sucrase-transformed JSX in a Blob and runs await import(blobUrl), which requires blob: in script-src.
Security note: The expanded CSP (unsafe-eval, unsafe-inline, blob:) is scoped only to the JSX content type branch and applies to a sandboxed iframe (sandbox="allow-scripts" without allow-same-origin), so the iframe cannot access parent-page cookies, storage, or origin.
Files changed
pkg/portal/public.go— expandedpublicCSP()JSX branch; addedblob:to iframe meta CSPpkg/portal/public_test.go— updatedTestPublicCSPandTestJsxIframeto verify all CSP directives
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.35.10Verification
All release artifacts are signed with Cosign. Verify with:
cosign verify-blob --bundle mcp-data-platform_0.35.10_linux_amd64.tar.gz.sigstore.json \
mcp-data-platform_0.35.10_linux_amd64.tar.gzmcp-data-platform-v0.35.9
What's New
Four portal UX improvements plus hardening fixes for share-by-email.
Public JSX Rendering
Shared JSX assets now render as live React components instead of raw source code. The public viewer generates a sandboxed iframe with a full client-side React runtime:
- Import maps for React 19, Recharts, and Lucide (matching the authenticated portal)
- Sucrase transpilation loaded from esm.sh for JSX-to-JS conversion
- Automatic component detection (exported default, then PascalCase declarations)
- Auto-mounting via
createRoot().render()unless the source contains its own mount call - Error display for transpilation and runtime failures
- Content-type-aware CSP: JSX content gets
frame-src blob: data:while other types use the default strict policy - CodeQL-safe injection via
html/template+json.Marshal+template.JS
Session Expiration Handling
API clients now detect 401 responses globally and display a "session expired" banner:
- Both
apiFetchandapiFetchRawcallexpireSession()on 401 - Auth state is cleared and
sessionExpiredflag is set - Login form shows an amber "Your session has expired" banner
- React Query retry function skips 401s (no retry loops)
- Flag resets on successful re-login
Share by Email
Users can share assets by email address instead of opaque user IDs:
- New
shared_with_emailcolumn onportal_shares(migration 016) - Partial index on non-null email values for query performance
- Email is validated (format check, 254-char RFC 5321 limit) and normalized to lowercase on write
- Case-insensitive matching on read via
strings.EqualFold(Go) andLOWER()(SQL) - "Shared With Me" queries match on both user ID (backward compat) and email
- Access checks (
isSharedWithUser) also check both paths
Share Dialog Polish
- Active shares show "Public Link" labels (not truncated tokens)
- Time remaining until expiration displayed (days, hours, or minutes)
- Sub-hour display fixed: shows "Expires in 23m" instead of "Expires in 0h"
- "Copy Link" text button with clipboard fallback for non-HTTPS contexts
- Email input field for sharing with users
Help Section
- Help tab heading and description now use the configured portal title via
useBranding()instead of hardcoded "MCP Data Platform"
Migration
This release includes migration 016 which adds a nullable shared_with_email TEXT column to portal_shares with a partial index. The migration is backward-compatible -- existing shares by user ID continue to work.
Changelog
- feat: portal UX enhancements -- JSX public links, session expiry, share-by-email (#198) @cjimti
- fix: resolve CodeQL unsafe-quoting in JSX public viewer @cjimti
- fix: harden share-by-email with validation, case normalization, and UX fixes @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:v0.35.9Verification
All release artifacts are signed with Cosign. Verify with:
cosign verify-blob --bundle mcp-data-platform_0.35.9_linux_amd64.tar.gz.sigstore.json \
mcp-data-platform_0.35.9_linux_amd64.tar.gzmcp-data-platform-v0.35.8
Highlights
Consolidates portal/admin configuration into a single-responsibility layout.
Breaking: Portal/Admin Config Consolidation (#197)
Branding fields have moved from the admin: block to portal:. This is a clean break — old keys silently stop working.
Before:
portal:
enabled: true
ui: true
admin:
enabled: true
portal: true
portal_title: "My Platform"
portal_logo: "https://..."After:
portal:
enabled: true
title: "My Platform"
logo: "https://..."
logo_light: "https://..."
logo_dark: "https://..."
admin:
enabled: true
persona: admin
path_prefix: /api/v1/adminMigration checklist:
- Move
admin.portal_titletoportal.title(new default:"MCP Data Platform") - Move
admin.portal_logotoportal.logo - Move
admin.portal_logo_light/admin.portal_logo_darktoportal.logo_light/portal.logo_dark - Remove
admin.portal(no longer used) - Remove
portal.ui(replaced byportal.enabledas the single gate)
The JSON API response from /api/v1/admin/system/info and /api/v1/admin/public/branding is unchanged — no frontend changes needed.
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.35.8Verification
All release artifacts are signed with Cosign. Verify with:
cosign verify-blob --bundle mcp-data-platform_0.35.8_linux_amd64.tar.gz.sigstore.json \
mcp-data-platform_0.35.8_linux_amd64.tar.gzChangelog
Others
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.35.8Verification
All release artifacts are signed with Cosign. Verify with:
cosign verify-blob --bundle mcp-data-platform_0.35.8_linux_amd64.tar.gz.sigstore.json \
mcp-data-platform_0.35.8_linux_amd64.tar.gz