mcp-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 verify-blob --bundle mcp-data-platform_0.36.0_linux_amd64.tar.gz.sigstore.json \
mcp-data-platform_0.36.0_linux_amd64.tar.gz