From 912f117ea30ffc7bb9b903f20b213106a32f6e2a Mon Sep 17 00:00:00 2001 From: iven Date: Sat, 4 Apr 2026 01:30:13 +0800 Subject: [PATCH] feat(desktop): wire MCP client to settings UI - MCPServices.tsx now calls real Tauri commands (start/stop/list) instead of only toggling config flags - Show running service count, discovered tools per service - Expand/collapse tool list for each running MCP service - Extended QuickConfig mcpServices type with command/args/env/cwd - Config change persists enabled state, MCP start/stop happens live --- .../src/components/Settings/MCPServices.tsx | 188 +++++++++++++++--- desktop/src/store/configStore.ts | 10 +- 2 files changed, 168 insertions(+), 30 deletions(-) diff --git a/desktop/src/components/Settings/MCPServices.tsx b/desktop/src/components/Settings/MCPServices.tsx index f61f478..63e28e4 100644 --- a/desktop/src/components/Settings/MCPServices.tsx +++ b/desktop/src/components/Settings/MCPServices.tsx @@ -1,55 +1,185 @@ -import { FileText, Globe } from 'lucide-react'; +import { FileText, Globe, RefreshCw, Wrench } from 'lucide-react'; +import { useCallback, useEffect, useState } from 'react'; import { useConfigStore } from '../../store/configStore'; import { silentErrorHandler } from '../../lib/error-utils'; +import { + listMcpServices, + startMcpService, + stopMcpService, + type McpServiceConfig, + type McpServiceStatus, + type McpToolInfo, +} from '../../lib/mcp-client'; export function MCPServices() { const quickConfig = useConfigStore((s) => s.quickConfig); const saveQuickConfig = useConfigStore((s) => s.saveQuickConfig); const services = quickConfig.mcpServices || []; + const [runningServices, setRunningServices] = useState([]); + const [loading, setLoading] = useState(null); + const [expandedTools, setExpandedTools] = useState>(new Set()); + + // Fetch running services on mount + const refreshRunning = useCallback(async () => { + try { + const running = await listMcpServices(); + setRunningServices(running); + } catch { + // MCP might not be available yet + setRunningServices([]); + } + }, []); + + useEffect(() => { + refreshRunning(); + }, [refreshRunning]); + + const toggleTools = (name: string) => { + setExpandedTools((prev) => { + const next = new Set(prev); + if (next.has(name)) next.delete(name); + else next.add(name); + return next; + }); + }; const toggleService = async (id: string) => { - const nextServices = services.map((service) => - service.id === id ? { ...service, enabled: !service.enabled } : service - ); - await saveQuickConfig({ mcpServices: nextServices }); + const svc = services.find((s) => s.id === id); + if (!svc) return; + + setLoading(id); + try { + if (svc.enabled) { + // Currently enabled → stop it + await stopMcpService(svc.name || svc.id).catch(() => {}); + } else { + // Currently disabled → start it + const config: McpServiceConfig = { + name: svc.name || svc.id, + command: svc.command || '', + args: svc.args, + env: svc.env, + cwd: svc.cwd, + }; + await startMcpService(config); + } + // Update config flag + const nextServices = services.map((s) => + s.id === id ? { ...s, enabled: !s.enabled } : s + ); + await saveQuickConfig({ mcpServices: nextServices }); + // Refresh running status + await refreshRunning(); + } catch (err) { + console.error('[MCP] Toggle failed:', err); + } finally { + setLoading(null); + } }; + // Build a map of service name → running status for quick lookup + const runningMap = new Map(runningServices.map((rs) => [rs.name, rs])); + return (

MCP 服务

- {services.length} 个已声明服务 +
+ + {runningServices.length} 个运行中 / {services.length} 个已声明 + + +
MCP(模型上下文协议)服务为 Agent 扩展外部工具 — 文件系统、数据库、网页搜索等。
- {services.length > 0 ? services.map((svc) => ( -
-
- {svc.id === 'filesystem' - ? - : } -
-
{svc.name}
-
{svc.id}
+ {services.length > 0 ? services.map((svc) => { + const isRunning = runningMap.has(svc.name || svc.id); + const status = runningMap.get(svc.name || svc.id); + const isLoading = loading === svc.id; + const showTools = expandedTools.has(svc.id); + + return ( +
+
+
+ {svc.id === 'filesystem' + ? + : } +
+
{svc.name}
+
+ {svc.id} + {svc.command && ( + | + )} + {svc.command && ( + {svc.command} + )} +
+
+
+
+ {isRunning && status && ( + + )} + + {isLoading ? '处理中...' : + isRunning ? '运行中' : + svc.enabled ? '已启用' : '已停用'} + + +
+ {/* Expanded tools list */} + {showTools && status && status.tools.length > 0 && ( +
+
已发现的工具:
+
+ {status.tools.map((tool: McpToolInfo) => ( +
+ +
+ {tool.tool_name} + {tool.description && ( + {tool.description} + )} +
+
+ ))} +
+
+ )}
-
- - {svc.enabled ? '已启用' : '已停用'} - - -
-
- )) : ( + ); + }) : (
当前快速配置中尚未声明 MCP 服务
@@ -57,7 +187,7 @@ export function MCPServices() {
- 当前页面只支持查看和启停已保存在快速配置中的 MCP 服务;新增服务、删除服务和详细参数配置尚未在桌面端接入。 + 新增服务、删除服务和详细参数配置尚未在桌面端接入。可通过配置文件手动添加。
); diff --git a/desktop/src/store/configStore.ts b/desktop/src/store/configStore.ts index 003ec29..7cc3d07 100644 --- a/desktop/src/store/configStore.ts +++ b/desktop/src/store/configStore.ts @@ -23,7 +23,15 @@ export interface QuickConfig { gatewayUrl?: string; gatewayToken?: string; skillsExtraDirs?: string[]; - mcpServices?: Array<{ id: string; name: string; enabled: boolean }>; + mcpServices?: Array<{ + id: string; + name: string; + enabled: boolean; + command?: string; + args?: string[]; + env?: Record; + cwd?: string; + }>; theme?: 'light' | 'dark'; autoStart?: boolean; showToolCalls?: boolean;