feat(frontend): implement unified MCP panel for v3.7.0

Complete Phase 3 (P0) frontend implementation for unified MCP management:

**New Files:**
- src/hooks/useMcp.ts: React Query hooks for unified MCP operations
- src/components/mcp/UnifiedMcpPanel.tsx: Unified MCP management panel
- src/components/ui/checkbox.tsx: Checkbox component from shadcn/ui

**Features:**
- Unified panel with three-column layout: server info + app checkboxes + actions
- Multi-app control: Claude/Codex/Gemini checkboxes for each server
- Real-time stats: Show enabled server counts per app
- Full CRUD operations: Add, edit, delete, sync all servers

**Integration:**
- Replace old app-specific McpPanel with UnifiedMcpPanel in App.tsx
- Update McpFormModal to support unified mode with apps field
- Add i18n support: mcp.unifiedPanel namespace (zh/en)

**Type Safety:**
- Ensure McpServer.apps field always initialized
- Fix all test files to include apps field
- TypeScript type check passes 

**Architecture:**
- Single source of truth: mcp.servers manages all MCP configs
- Per-server app control: apps.claude/codex/gemini boolean flags
- Backward compatible: McpFormModal supports both unified and legacy modes

Next: P1 tasks (import dialogs, sub-components, tests)
This commit is contained in:
Jason
2025-11-14 15:24:48 +08:00
parent 32a6de074c
commit 9e8abf5f26
10 changed files with 569 additions and 15 deletions

70
src/hooks/useMcp.ts Normal file
View File

@@ -0,0 +1,70 @@
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
import { mcpApi } from '@/lib/api/mcp';
import type { McpServer } from '@/types';
import type { AppId } from '@/lib/api/types';
/**
* 查询所有 MCP 服务器(统一管理)
*/
export function useAllMcpServers() {
return useQuery({
queryKey: ['mcp', 'all'],
queryFn: () => mcpApi.getAllServers(),
});
}
/**
* 添加或更新 MCP 服务器
*/
export function useUpsertMcpServer() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: (server: McpServer) => mcpApi.upsertUnifiedServer(server),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['mcp', 'all'] });
},
});
}
/**
* 切换 MCP 服务器在特定应用的启用状态
*/
export function useToggleMcpApp() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: ({
serverId,
app,
enabled,
}: {
serverId: string;
app: AppId;
enabled: boolean;
}) => mcpApi.toggleApp(serverId, app, enabled),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['mcp', 'all'] });
},
});
}
/**
* 删除 MCP 服务器
*/
export function useDeleteMcpServer() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: (id: string) => mcpApi.deleteUnifiedServer(id),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['mcp', 'all'] });
},
});
}
/**
* 同步所有启用的 MCP 服务器到各应用的 live 配置
*/
export function useSyncAllMcpServers() {
return useMutation({
mutationFn: () => mcpApi.syncAllServers(),
});
}