ADR-0040: Flexible Admin UI Section VisibilityΒΆ
- Status: Accepted
- Date: 2026-02-16
- Deciders: Mihai Criveti
ContextΒΆ
MCP Gateway's Admin UI is a full-featured dashboard with many sections (overview, servers, gateways, tools, prompts, resources, roots, mcp-registry, metrics, plugins, export-import, logs, version-info, maintenance, teams, users, agents, tokens, settings). When the UI is embedded inside a third-party portal via iframe, or when an operator wants to expose only a subset of functionality to certain audiences, the full dashboard is too broad. There was no mechanism to hide irrelevant sections, header controls (logout, team selector), or prevent data loading for hidden sections.
Key requirements:
- Embedding: Third-party portals need to embed the Admin UI with only relevant sections visible and without redundant header controls like logout.
- Operator control: Platform operators need to restrict which sections are visible at the environment level, independent of RBAC (which controls data access, not UI visibility).
- Per-request customization: Embedded iframes need to vary visible sections per context (e.g., one page shows only tools, another shows only gateways).
- Performance: Hidden sections should not load data from the database.
DecisionΒΆ
Implement a three-layer UI visibility system:
Layer 1: Environment-Level ConfigurationΒΆ
Three new environment variables control defaults:
| Variable | Type | Purpose |
|---|---|---|
MCPGATEWAY_UI_EMBEDDED | bool | Embedded mode β auto-hides logout and team_selector header items |
MCPGATEWAY_UI_HIDE_SECTIONS | CSV/JSON list | Sections to hide globally |
MCPGATEWAY_UI_HIDE_HEADER_ITEMS | CSV/JSON list | Header items to hide globally |
All values are validated at startup against frozen allowlists (UI_HIDABLE_SECTIONS, UI_HIDABLE_HEADER_ITEMS) defined in config.py. Unknown values are logged and dropped.
Layer 2: Per-Request Query ParameterΒΆ
The ?ui_hide=section1,section2 query parameter allows per-request section hiding. The value is persisted in an httponly cookie (mcpgateway_ui_hide_sections, 30-day max age) so subsequent requests remember the preference. Visiting ?ui_hide= (empty) clears the cookie.
Layer 3: Client-Side Tab NavigationΒΆ
JavaScript globals (UI_HIDDEN_TABS, UI_HIDDEN_SECTIONS) are set from the server-rendered template. Client-side functions (isTabHidden, resolveTabForNavigation, getDefaultTabName) prevent navigation to hidden tabs and filter search results.
Section AliasesΒΆ
A canonical alias map resolves alternative section names:
UI_HIDE_SECTION_ALIASES = {
"catalog": "servers",
"virtual_servers": "servers",
"a2a-agents": "agents",
"a2a": "agents",
"grpc-services": "agents",
"api_tokens": "tokens",
"llm-settings": "settings",
}
Server-Side Data OptimizationΒΆ
When a section is hidden, the admin_ui() endpoint skips the corresponding database queries entirely. This reduces latency and load for partial-view scenarios.
ConsequencesΒΆ
PositiveΒΆ
- Embedding the Admin UI in third-party portals is straightforward with
?ui_hide=andMCPGATEWAY_UI_EMBEDDED=true - Operators can restrict visible sections without modifying code or templates
- Hidden sections incur zero database cost (server-side skip)
- Cookie persistence means iframe reloads maintain the same view
- Alias system provides forward-compatible naming as the UI evolves
NegativeΒΆ
- UI visibility is not a security boundary β hidden sections are still accessible via API. RBAC remains the access control mechanism.
- Cookie-based persistence means browser-clearing resets preferences (acceptable for embedded use)
Risks / MitigationsΒΆ
- Stale cookies after config change: If an operator changes
UI_HIDE_SECTIONS, stale cookies may show outdated preferences. Mitigated by the 30-day cookie expiry and the ability to clear with?ui_hide=. - CSS selector injection in tab names: Mitigated by the
normalizeTabName()character whitelist (/[^a-z0-9-]/g).
Alternatives ConsideredΒΆ
| Option | Why Not |
|---|---|
| RBAC-only visibility | RBAC controls data access, not UI layout. Operators want UI customization independent of permissions. |
| Template-level config files | Requires template redeployment. Env vars and query params are more flexible for container/iframe use. |
| Client-side only hiding (CSS/JS) | Would still load all data server-side, wasting resources. Server-side awareness is necessary for optimization. |
URL path-based views (/admin/tools-only/) | Creates multiple endpoints to maintain. Query parameter approach is composable and doesn't multiply routes. |
RelatedΒΆ
- Configuration: Admin UI Customization
- Reference: Configuration Reference