-
Notifications
You must be signed in to change notification settings - Fork 615
[EPIC][UI]: Unified search experience for MCP Gateway admin UI #2109
Description
Goal
Implement a consistent, unified search experience across all Admin UI sections with tag-based filtering, standardized API responses, and a global search feature (Ctrl+K) that allows users to discover MCP servers, virtual servers, tools, prompts, resources, A2A agents, teams, and users from a single search interface.
Why Now?
The Admin UI has grown to manage 12+ entity types, each with its own search implementation:
- Inconsistent UX: Search behavior varies across sections - some use client-side filtering, others server-side; response formats differ; parameter names are inconsistent (
qvssearch) - Missing Tag Filtering: All major entities have
tagsfields in the database, but no search endpoint supports tag-based filtering despite the existingjson_contains_exprdialect-aware helper - No Global Search: Users must navigate to each section to find entities; there's no way to search across all entity types at once
- Incomplete Frontend Integration: Three backend search endpoints (
/gateways/search,/servers/search,/a2a/search) have no corresponding frontend integration - Discovery Friction: As deployments scale to hundreds of tools and servers, finding specific entities becomes increasingly difficult
Implementing unified search will significantly improve admin productivity and make the platform feel polished and cohesive.
📖 User Stories
US-1: Admin User - Search Across All Entities (Global Search)
As an Admin User
I want to search across all entity types from a single search box
So that I can quickly find any entity without navigating to specific sections
Acceptance Criteria:
Given I am on any Admin UI page
When I press Ctrl+K (or Cmd+K on Mac)
Then a global search modal should appear with focus on the input
When I type "github" in the global search
And wait 300ms for debounce
Then I should see grouped results:
| Section | Results |
| MCP Servers | GitHub MCP Server, GitHub Actions Gateway |
| Tools | github_create_issue, github_list_repos |
| Resources | github://repos, github://issues |
And each group should show up to 5 results with "View all..." link
When I click on a result
Then I should navigate to that entity's section with the item highlighted
When I press Escape
Then the global search modal should closeTechnical Requirements:
- New endpoint:
GET /admin/search?q=...&entity_types=...&tags=...&limit_per_type=5 - Response groups results by entity type with
has_moreflag - Frontend: Alpine.js component in header with Ctrl+K keyboard shortcut
- Debounced search (300ms) to reduce API calls
- Results cached for 30 seconds to improve responsiveness
US-2: Admin User - Filter Tools by Tags
As an Admin User managing 500+ tools
I want to filter tools by tags (e.g., "production", "deprecated", "experimental")
So that I can quickly find tools in a specific category
Acceptance Criteria:
Given I am on the Tools page
And tools have tags like ["production", "github", "api"]
When I add a tag filter "production"
Then only tools with the "production" tag should appear
When I add multiple tags "production,github" (comma-separated)
Then tools matching ANY of these tags should appear (OR logic)
When I add tags with "+" separator "production+github"
Then only tools matching ALL tags should appear (AND logic)
When I clear the tag filter
Then all tools should appear againTechnical Requirements:
- Add
tagsparameter to/admin/tools/searchendpoint - Use existing
json_contains_expr(db, DbTool.tags, tag_list, match_any=True/False)helper - Frontend: Tag input with autocomplete from existing tags
- URL state:
?tags=production,githubfor bookmarking
US-3: Admin User - Consistent Search Across All Sections
As an Admin User
I want search to work the same way across all sections
So that I don't have to learn different search behaviors
Acceptance Criteria:
Given I am on any section (Gateways, Servers, Tools, Prompts, Resources, A2A Agents)
When I type in the search box
Then I should see server-side search results (not just client-side filtering)
And results should update after 300ms debounce
And results should include items from all pages (not just current view)
Given any search endpoint (tools, gateways, servers, resources, prompts, a2a)
When I call the endpoint with the same parameters
Then response format should be consistent:
{
"items": [...],
"count": N,
"entity_type": "tools"
}Technical Requirements:
- Standardize all search endpoint response formats to use
itemskey - Fix
/admin/teams/searchto return{"items": [...], "count": N}instead of bare array - Add missing frontend functions:
serverSideGatewaySearch(),serverSideServerSearch(),serverSideA2ASearch() - All sections use
qparameter for search query
US-4: Admin User - Search API Tokens
As an Admin User
I want to search API tokens by name or description
So that I can find specific tokens in large deployments
Acceptance Criteria:
Given I have 50+ API tokens
When I navigate to the Tokens page
Then I should see a search input
When I type "production-api" in the search
Then only tokens matching "production-api" in name or description should appear
When I filter by tags "service-account"
Then only tokens with that tag should appearTechnical Requirements:
- New endpoint:
GET /admin/tokens/search?q=...&tags=...&include_inactive=... - Search fields:
name,description - Tag filtering using
json_contains_expr(EmailApiToken has optional tags field) - Frontend integration with HTMX partial
US-5: Developer - Preserve Gateway Filter Functionality
As a Developer using the tools/prompts/resources search
I want the existing gateway_id filter to continue working
So that I can filter by specific MCP server or find REST-only tools
Acceptance Criteria:
Given the tools search endpoint
When I call GET /admin/tools/search?gateway_id=abc123
Then only tools from gateway "abc123" should appear
When I call GET /admin/tools/search?gateway_id=abc123,def456
Then tools from either gateway should appear
When I call GET /admin/tools/search?gateway_id=null
Then only REST tools (no gateway) should appear
When I call GET /admin/tools/search?gateway_id=abc123,null
Then tools from gateway "abc123" AND REST tools should appearTechnical Requirements:
- Preserve existing
gateway_idparameter behavior in tools, prompts, resources - Support comma-separated gateway IDs
- Support
nullsentinel for REST/standalone items - Document this behavior in API docs
US-6: Admin User - Search Team Members and Settings
As an Admin User
I want to search teams by name, slug, or description
So that I can find specific teams in organizations with many teams
Acceptance Criteria:
Given I am on the Teams page
When I type "engineering" in the search
Then teams with "engineering" in name, slug, OR description should appear
And the search should be server-side (not client-side filtering)
When I filter by visibility "public"
Then only public teams should appearTechnical Requirements:
- Confirm
/admin/teams/searchsearches name, slug, AND description (already implemented in service) - Standardize response format to
{"items": [...], "count": N} - Frontend
serverSideTeamSearch()correctly calls/admin/teams/partialwithqparam
🏗 Architecture
Search Flow
sequenceDiagram
participant User as Admin User
participant UI as Admin UI (HTMX/Alpine)
participant API as FastAPI Admin Router
participant DB as Database
participant Cache as Redis (optional)
User->>UI: Press Ctrl+K
UI->>UI: Show global search modal
User->>UI: Type "github" (wait 300ms debounce)
UI->>API: GET /admin/search?q=github&limit_per_type=5
API->>Cache: Check cache key "search:github:5"
alt Cache hit
Cache-->>API: Return cached results
else Cache miss
loop For each entity type
API->>DB: Search query with access control
DB-->>API: Matching entities
end
API->>Cache: Store results (TTL 30s)
end
API-->>UI: { results: { gateways: {...}, tools: {...} }, total_count: 12 }
UI->>UI: Render grouped results
User->>UI: Click on tool result
UI->>UI: Navigate to /admin#tools?highlight=tool-id
Tag Filtering Implementation
flowchart LR
subgraph Input
A[tags=prod,github] --> B{Contains +?}
end
subgraph Logic
B -->|No comma| C[match_any=True OR]
B -->|Has +| D[match_any=False AND]
end
subgraph Database
C --> E[json_contains_expr db, col, tags, match_any=True]
D --> F[json_contains_expr db, col, tags, match_any=False]
end
subgraph Dialects
E --> G[PostgreSQL: col.contains]
E --> H[MySQL: JSON_OVERLAPS]
E --> I[SQLite: json_each]
end
Response Format Standardization
Current (Inconsistent):
/tools/search → {"tools": [...], "count": N}
/gateways/search → {"gateways": [...], "count": N}
/teams/search → [...] ← Different!
Proposed (Unified):
/*/search → {
"items": [...],
"count": N,
"entity_type": "tools|gateways|teams|...",
"query": "search term",
"filters_applied": { "tags": [...], "include_inactive": false }
}
📋 Implementation Tasks
Phase 1: Standardization (Foundation)
-
Standardize Search Response Format
- Update
/admin/tools/searchto return{"items": [...], "count": N} - Update
/admin/gateways/searchresponse format - Update
/admin/servers/searchresponse format - Update
/admin/resources/searchresponse format - Update
/admin/prompts/searchresponse format - Update
/admin/a2a/searchresponse format - Fix
/admin/teams/searchto return wrapped format (currently returns bare array) - Update
/admin/users/searchresponse format - Update frontend to handle new response format
- Update
-
Add Tag Filtering to Search Endpoints
- Add
tagsparameter to/admin/tools/search - Add
tagsparameter to/admin/gateways/search - Add
tagsparameter to/admin/servers/search - Add
tagsparameter to/admin/resources/search - Add
tagsparameter to/admin/prompts/search - Add
tagsparameter to/admin/a2a/search - Use
json_contains_exprfrommcpgateway/utils/sqlalchemy_modifier.py - Support comma (OR) and + (AND) separators
- Add
-
Add Missing Frontend Search Functions
- Implement
serverSideGatewaySearch()inadmin.js - Implement
serverSideServerSearch()inadmin.js - Implement
serverSideA2ASearch()inadmin.js - Wire up search inputs in respective partials
- Implement
-
Preserve Existing Functionality
- Verify
gateway_idfilter works with null sentinel - Verify
team_idfilter works across endpoints - Verify
include_inactivefilter works
- Verify
Phase 2: Gap Filling
-
Add API Tokens Search
- Create
GET /admin/tokens/searchendpoint - Search fields:
name,description - Add
tagsfilter (EmailApiToken has optional tags) - Add
include_inactiveandinclude_expiredfilters - Add frontend search integration
- Create
-
Verify Existing Search Capabilities
- Confirm Plugins partial has working search (already exists)
- Confirm Observability traces have search filters (already exists)
- Document MCP Registry search behavior
Phase 3: Global Search
-
Create Global Search Endpoint
- Implement
GET /admin/searchendpoint - Accept
q,entity_types,tags,include_inactive,limit_per_type - Return grouped results by entity type
- Apply access control per entity type
- Add optional Redis caching (30s TTL)
- Implement
-
Create Global Search UI Component
- Alpine.js component for search modal
- Keyboard shortcut: Ctrl+K / Cmd+K
- Debounced search (300ms)
- Grouped results display
- Keyboard navigation (up/down/enter)
- Click-to-navigate to entity
- ESC to close
-
Add to Admin Header
- Add search icon/input to header
- Show keyboard shortcut hint
- Works on all admin pages
Phase 4: Polish & Documentation
-
Testing
- Unit tests for tag filtering logic
- Unit tests for response format consistency
- Integration tests for global search
- Frontend tests for search components
- Performance tests with 10,000+ entities
-
Documentation
- Update API docs with new parameters
- Document tag filter syntax (comma vs +)
- Document global search keyboard shortcuts
- Add search examples to Admin Guide
✅ Success Criteria
- Consistency: All search endpoints return
{"items": [...], "count": N}format - Tag Filtering: Can filter by tags on all entities that have tags (Tools, Gateways, Servers, Resources, Prompts, A2A Agents, Tokens)
- Global Search: Ctrl+K opens search modal, finds entities across all types
- Frontend Integration: All backend search endpoints have corresponding frontend functions
- Gateway Filter: Existing
gateway_idfilter preserved with null sentinel support - Performance: Search response time < 200ms for 10,000 entities per type
- Testing: 90%+ coverage on search-related code
- Documentation: API docs updated with all search parameters
🏁 Definition of Done
- All search endpoints use standardized response format
- Tag filtering works on all applicable entities
- Missing frontend search functions implemented
- API Tokens search endpoint created
- Global search endpoint implemented
- Global search UI component created
- Keyboard shortcut (Ctrl+K) works
- Unit tests pass with 90%+ coverage
- Integration tests pass
- Performance benchmarks met
- API documentation updated
- Code passes
make verifychecks
📝 Design Decisions
Tag Filter Syntax
- Comma (
,): OR logic -tags=prod,stagingmatches items with "prod" OR "staging" - Plus (
+): AND logic -tags=prod+apimatches items with BOTH "prod" AND "api" - Rationale: Matches existing service behavior (
match_any=Truedefault) and is URL-safe
Search Endpoints are Typeahead-Style
- Return empty
[]whenqis blank - Use
limitonly (no pagination) - Designed for dropdowns/selectors, not paginated browsing
- Rationale: Consistent with current behavior; full browsing uses
/partialendpoints
Teams and Users Don't Have Tags
EmailTeamandEmailUsermodels don't havetagsfields in the database- Tag filtering will NOT be added to teams/users search
- Rationale: Architectural decision; would require schema migration
Database Dialect Awareness
- Use
json_contains_exprhelper for all tag filtering - Automatically handles PostgreSQL, MySQL, and SQLite
- Default database is SQLite - avoid PostgreSQL-only features in core code
- Rationale: Existing helper is battle-tested and dialect-aware
🔗 Related Issues
- [FEATURE][UI]: Add search capabilities for tools in admin UI #2076 - [Feature Request]: Add search capabilities for tools in Admin UI (child of this epic)
- Add pagination for teams listing and update search to server side #2089 - Add pagination for teams listing and update search to server side
- Refactor team management: Unified UI for view and manage, cursor-based pagination, and search on name or email #2067 - Refactor team management: Unified UI for view and manage, cursor-based pagination, and search
📚 References
- mcpgateway/utils/sqlalchemy_modifier.py -
json_contains_exprhelper - HTMX Documentation - Frontend patterns
- Alpine.js Documentation - UI component patterns