Skip to content

[EPIC][UI]: Unified search experience for MCP Gateway admin UI #2109

@crivetimihai

Description

@crivetimihai

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:

  1. Inconsistent UX: Search behavior varies across sections - some use client-side filtering, others server-side; response formats differ; parameter names are inconsistent (q vs search)
  2. Missing Tag Filtering: All major entities have tags fields in the database, but no search endpoint supports tag-based filtering despite the existing json_contains_expr dialect-aware helper
  3. No Global Search: Users must navigate to each section to find entities; there's no way to search across all entity types at once
  4. Incomplete Frontend Integration: Three backend search endpoints (/gateways/search, /servers/search, /a2a/search) have no corresponding frontend integration
  5. 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 close

Technical Requirements:

  • New endpoint: GET /admin/search?q=...&entity_types=...&tags=...&limit_per_type=5
  • Response groups results by entity type with has_more flag
  • 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 again

Technical Requirements:

  • Add tags parameter to /admin/tools/search endpoint
  • 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,github for 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 items key
  • Fix /admin/teams/search to return {"items": [...], "count": N} instead of bare array
  • Add missing frontend functions: serverSideGatewaySearch(), serverSideServerSearch(), serverSideA2ASearch()
  • All sections use q parameter 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 appear

Technical 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 appear

Technical Requirements:

  • Preserve existing gateway_id parameter behavior in tools, prompts, resources
  • Support comma-separated gateway IDs
  • Support null sentinel 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 appear

Technical Requirements:

  • Confirm /admin/teams/search searches name, slug, AND description (already implemented in service)
  • Standardize response format to {"items": [...], "count": N}
  • Frontend serverSideTeamSearch() correctly calls /admin/teams/partial with q param

🏗 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
Loading

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
Loading

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/search to return {"items": [...], "count": N}
    • Update /admin/gateways/search response format
    • Update /admin/servers/search response format
    • Update /admin/resources/search response format
    • Update /admin/prompts/search response format
    • Update /admin/a2a/search response format
    • Fix /admin/teams/search to return wrapped format (currently returns bare array)
    • Update /admin/users/search response format
    • Update frontend to handle new response format
  • Add Tag Filtering to Search Endpoints

    • Add tags parameter to /admin/tools/search
    • Add tags parameter to /admin/gateways/search
    • Add tags parameter to /admin/servers/search
    • Add tags parameter to /admin/resources/search
    • Add tags parameter to /admin/prompts/search
    • Add tags parameter to /admin/a2a/search
    • Use json_contains_expr from mcpgateway/utils/sqlalchemy_modifier.py
    • Support comma (OR) and + (AND) separators
  • Add Missing Frontend Search Functions

    • Implement serverSideGatewaySearch() in admin.js
    • Implement serverSideServerSearch() in admin.js
    • Implement serverSideA2ASearch() in admin.js
    • Wire up search inputs in respective partials
  • Preserve Existing Functionality

    • Verify gateway_id filter works with null sentinel
    • Verify team_id filter works across endpoints
    • Verify include_inactive filter works

Phase 2: Gap Filling

  • Add API Tokens Search

    • Create GET /admin/tokens/search endpoint
    • Search fields: name, description
    • Add tags filter (EmailApiToken has optional tags)
    • Add include_inactive and include_expired filters
    • Add frontend search integration
  • 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/search endpoint
    • 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)
  • 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_id filter 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 verify checks

📝 Design Decisions

Tag Filter Syntax

  • Comma (,): OR logic - tags=prod,staging matches items with "prod" OR "staging"
  • Plus (+): AND logic - tags=prod+api matches items with BOTH "prod" AND "api"
  • Rationale: Matches existing service behavior (match_any=True default) and is URL-safe

Search Endpoints are Typeahead-Style

  • Return empty [] when q is blank
  • Use limit only (no pagination)
  • Designed for dropdowns/selectors, not paginated browsing
  • Rationale: Consistent with current behavior; full browsing uses /partial endpoints

Teams and Users Don't Have Tags

  • EmailTeam and EmailUser models don't have tags fields 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_expr helper 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


📚 References

Metadata

Metadata

Assignees

Labels

MUSTP1: Non-negotiable, critical requirements without which the product is non-functional or unsafeenhancementNew feature or requestepicLarge feature spanning multiple issuesicaICA related issuesuiUser Interface

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions