Files
Hermes-ui/projects/hermes-web-ui/PR_DESCRIPTION.md
T
ekko b015e70b9d [codex] fix MCP management lifecycle (#1144)
* feat(mcp): add MCP server management UI

- Server CRUD: add/edit/remove with YAML/JSON Monaco editor
- raw_config passthrough: zero field loss on edit/toggle
- tool_details embedding: single-request card data (1+N → 1)
- Auto-retry exponential backoff (2s→32s, max 5 retries)
- Route safety guards (hasRoute) for dynamic sidebar
- i18n: 9 languages (de/en/es/fr/ja/ko/pt/zh/zh-TW)
- 19 unit tests + 8 UX browser tests
- 35 files, +2933 lines

* fix mcp management lifecycle

---------

Co-authored-by: Crafter-feng <succeed_happu@163.com>
2026-05-30 11:06:08 +08:00

168 lines
9.1 KiB
Markdown

# feat(mcp): Add MCP Server Management UI
## Summary
Add a complete MCP (Model Context Protocol) server management interface that allows users to manage MCP servers through the web UI. This feature provides a user-friendly way to configure, monitor, and control MCP servers without editing configuration files manually.
## Features
### Core Functionality
- **Server List**: View all configured MCP servers with real-time status indicators (connected/disconnected/disabled)
- **Add Server**: Add new MCP servers with YAML or JSON configuration editor (Monaco Editor)
- **Edit Server**: Modify existing server configurations — uses `raw_config` passthrough to avoid field loss
- **Remove Server**: Delete servers with confirmation dialog to prevent accidental deletion
- **Enable/Disable**: Toggle individual servers on/off with instant feedback
- **Test Connection**: Verify server connectivity and list available tools
- **Reload**: Refresh server configuration without restarting
- **Search/Filter**: Filter servers by name for quick navigation
### User Experience
- **Monaco Editor**: Full-featured code editor with YAML/JSON syntax highlighting
- **Responsive Design**: Works on desktop (1280px), tablet (768px), and mobile (480px)
- **Auto-Retry**: Exponential backoff (2s → 4s → 8s → 16s → 32s, max 5 retries) for servers that haven't connected yet
- **Loading States**: Clear feedback during async operations
- **Error Handling**: User-friendly error messages with actionable guidance
- **Multi-language Support**: 9 languages (English, Chinese Simplified/Traditional, Japanese, Korean, German, Spanish, French, Portuguese)
### Performance Optimizations
- **`raw_config` Passthrough**: Backend returns the original config dict as-is — frontend edits/Toggles use it directly, eliminating field-rebuild bugs
- **`tool_details` Embedding**: `mcp_list` response embeds filtered `{name, description}` per server, reducing first-load from 1+N requests to 1
- **Route Safety**: `hasRoute()` guards for dynamic sidebar routes to prevent Vue runtime crashes
## Technical Implementation
### Frontend (Vue 3 + Naive UI)
- `McpManagerView.vue`: Main management component with auto-retry, search, modal management
- `McpServerCard.vue`: Server card component with status indicators, tool tags, action buttons
- `mcp.ts` (api): Type definitions (`McpServerInfo` with `raw_config`, `tool_details`) and API client
- Monaco Editor integration for configuration editing
- Reactive state management with Vue 3 Composition API
### Backend (Koa + TypeScript)
- `mcp.ts` (controller): Request handlers with input validation
- `mcp.ts` (service): Business logic layer with typed interfaces
- `mcp.ts` (routes): RESTful API endpoints
- `mcp-types.ts`: Shared type definitions
- `client.ts` (bridge): AgentBridge client methods
### Python Bridge (`hermes_bridge.py`)
- `_build_server_entry()`: Normalized server entry builder returning `raw_config` and `tool_details`
- `_mcp_list()`: Lists all MCP servers with embedded filtered tool details
- `_mcp_server_add/update/remove`: Full CRUD with config validation and atomic YAML persistence
- `_mcp_server_toggle`: Enable/disable individual servers
- `_mcp_server_test`: Connection test with tool discovery
- `_mcp_reload`: Hot-reload server connections
- Background MCP discovery on worker startup
### Testing
- `mcp-controller.test.ts`: 19 unit tests covering all controller methods
- Tests for success cases, error handling, and edge cases
- Mock-based testing for isolation
## Files Changed
```
packages/client/src/api/hermes/mcp.ts | 87 ++++
packages/client/src/components/hermes/mcp/McpServerCard.vue | 275 +++++++++
packages/client/src/components/layout/AppSidebar.vue | 16 +-
packages/client/src/components/hermes/chat/ChatInput.vue | 1 +
packages/client/src/i18n/locales/{de,en,es,fr,ja,ko,pt,zh,zh-TW}.ts | 504 +++ (9 files)
packages/client/src/i18n/messages.ts | 2 +-
packages/client/src/router/index.ts | 6 +
packages/client/src/views/hermes/McpManagerView.vue | 623 +++++++++++++++++
packages/server/src/controllers/hermes/mcp.ts | 117 ++++
packages/server/src/routes/hermes/mcp.ts | 12 +
packages/server/src/routes/index.ts | 2 +
packages/server/src/services/hermes/agent-bridge/client.ts | 31 +
packages/server/src/services/hermes/agent-bridge/hermes_bridge.py | 368 +++++++++-
packages/server/src/services/hermes/mcp-types.ts | 67 ++
packages/server/src/services/hermes/mcp.ts | 67 ++
packages/server/src/services/hermes/run-chat/session-command.ts | 22 +
tests/server/mcp-controller.test.ts | 286 +++++++++
```
**Total**: 27 files changed, ~2,900 insertions
## Testing
### Unit Tests (19/19 passing)
```
✓ MCP Controller > listServers > returns servers list from bridge
✓ MCP Controller > listServers > returns 503 on bridge error
✓ MCP Controller > addServer > sends name and config to bridge
✓ MCP Controller > addServer > returns 400 when name is missing
✓ MCP Controller > addServer > returns 400 when config is missing
✓ MCP Controller > updateServer > sends name from params and config to bridge
✓ MCP Controller > updateServer > returns 400 when config is missing
✓ MCP Controller > removeServer > sends name to bridge
✓ MCP Controller > testServer > returns tool list from bridge
✓ MCP Controller > listTools > returns tools without server filter
✓ MCP Controller > listTools > passes server filter to bridge
✓ MCP Controller > listTools > returns 503 on bridge error
✓ MCP Controller > reloadMcp > reloads all servers when no filter
✓ MCP Controller > reloadMcp > reloads specific server
✓ MCP Controller > reloadMcp > returns 500 on bridge error
✓ MCP Controller > profile handling > passes undefined profile when ctx.state.profile is missing
✓ MCP Controller > profile handling > passes undefined profile when profile.name is empty
✓ MCP Controller > response structure > mcp_list response has all required fields
✓ MCP Controller > response structure > mcp_tools_list response has tools with name/description/schema
```
### UX Browser Tests (8/8 passing)
| Test Case | Screenshot | Result |
|-----------|------------|--------|
| TC-01 Initial State | [mcp-tc01-initial-state.png](docs/images/mcp/mcp-tc01-initial-state.png) | ✅ Summary cards 1/1/0/3, github server connected |
| TC-02 Search Filter | [mcp-tc02-search-git.png](docs/images/mcp/mcp-tc02-search-git.png) | ✅ Correctly filters to github only |
| TC-03 Search Empty | [mcp-tc03-search-empty.png](docs/images/mcp/mcp-tc03-search-empty.png) | ✅ Empty state "暂无 MCP 服务器配置" shown |
| TC-04 Add Modal | [mcp-tc04-add-modal.png](docs/images/mcp/mcp-tc04-add-modal.png) | ✅ JSON/YAML toggle, config editor, Cancel/Save |
| TC-05 Edit Modal | [mcp-tc05-edit-modal.png](docs/images/mcp/mcp-tc05-edit-modal.png) | ✅ Pre-filled config with raw_config passthrough |
| TC-06 Tools Expanded | [mcp-tc06-tools-expanded.png](docs/images/mcp/mcp-tc06-tools-expanded.png) | ✅ Shows 3/26 tool tags |
| TC-07 Responsive 768px | [mcp-tc07-responsive-768.png](docs/images/mcp/mcp-tc07-responsive-768.png) | ✅ Tablet layout, no overflow |
| TC-08 Responsive 480px | [mcp-tc08-responsive-480.png](docs/images/mcp/mcp-tc08-responsive-480.png) | ✅ Mobile layout, all elements accessible |
### Build
- [x] TypeScript compilation successful
- [x] Vite build successful
- [x] No linting errors
## Architecture Decisions
### 1. `raw_config` Passthrough
**Problem**: Rebuilding config from scattered fields (command, args, env, etc.) caused field loss on edit/Toggle.
**Solution**: Backend returns `raw_config` (original dict from config.yaml), frontend edits it directly. Zero rebuild, zero field loss.
### 2. `tool_details` Embedding
**Problem**: First load required 1 API call for server list + N calls for tool details (one per server).
**Solution**: `mcp_list` response embeds `tool_details` (filtered `{name, description}` per server). Single request for full card data.
### 3. Auto-Retry with Exponential Backoff
**Problem**: MCP servers may not be connected on first page load (still initializing).
**Solution**: Auto-retry with exponential backoff (2s, 4s, 8s, 16s, 32s), max 5 retries. Manual refresh resets counter.
### 4. Route Safety Guards
**Problem**: Unknown routes (e.g., `codingAgents`, `versionPreview`) could crash Vue at runtime.
**Solution**: `hasRoute()` checks before rendering dynamic sidebar route items.
## Screenshots
### Desktop (1280px) — Initial State
![MCP Management - Initial State](docs/images/mcp/mcp-tc01-initial-state.png)
### Edit Server Modal
![MCP Management - Edit Modal](docs/images/mcp/mcp-tc05-edit-modal.png)
### Responsive — Mobile (480px)
![MCP Management - Mobile](docs/images/mcp/mcp-tc08-responsive-480.png)
## Checklist
- [x] Code follows project style guidelines
- [x] Self-review completed
- [x] Documentation updated
- [x] Tests added for new functionality (19 unit + 8 UX)
- [x] All tests pass
- [x] No breaking changes
- [x] Multi-language support (9 languages)
- [x] Responsive design verified (desktop/tablet/mobile)