From e50bef97e491e7afccd014ada1892c3dc99f8aa0 Mon Sep 17 00:00:00 2001 From: Haitao Pan Date: Fri, 13 Mar 2026 22:32:02 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=20MCP=20Server=20?= =?UTF-8?q?=E5=AF=BC=E8=88=AA=E9=A1=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 在 WorkspaceDestination 枚举中添加 mcpServer - 更新侧边栏顺序:助手→任务→技能→节点→代理→MCP Server | ClawHub→密钥→AI Gateway - 创建 McpServerPage 展示 MCP 服务器连接状态 - 更新 app_shell.dart 路由映射 --- lib/app/app_shell.dart | 5 + lib/features/mcp_server/mcp_server_page.dart | 172 +++++++++++++++++++ lib/models/app_models.dart | 11 +- lib/widgets/sidebar_navigation.dart | 3 + 4 files changed, 189 insertions(+), 2 deletions(-) create mode 100644 lib/features/mcp_server/mcp_server_page.dart diff --git a/lib/app/app_shell.dart b/lib/app/app_shell.dart index ce9ea36b..2ac0b390 100644 --- a/lib/app/app_shell.dart +++ b/lib/app/app_shell.dart @@ -4,6 +4,7 @@ import 'package:flutter/material.dart'; import '../features/ai_gateway/ai_gateway_page.dart'; import '../features/assistant/assistant_page.dart'; import '../features/claw_hub/claw_hub_page.dart'; + import '../features/mcp_server/mcp_server_page.dart'; import '../features/mobile/ios_mobile_shell.dart'; import '../features/modules/modules_page.dart'; import '../features/secrets/secrets_page.dart'; @@ -340,6 +341,10 @@ class _AppShellState extends State { onOpenDetail: onOpenDetail, initialTab: ModulesTab.agents, ), + WorkspaceDestination.mcpServer => McpServerPage( + controller: widget.controller, + onOpenDetail: onOpenDetail, + ), WorkspaceDestination.clawHub => ClawHubPage( controller: widget.controller, onOpenDetail: onOpenDetail, diff --git a/lib/features/mcp_server/mcp_server_page.dart b/lib/features/mcp_server/mcp_server_page.dart new file mode 100644 index 00000000..d122bc98 --- /dev/null +++ b/lib/features/mcp_server/mcp_server_page.dart @@ -0,0 +1,172 @@ +import 'package:flutter/material.dart'; + +import '../../app/app_controller.dart'; +import '../../i18n/app_language.dart'; +import '../../models/app_models.dart'; +import '../../runtime/runtime_models.dart'; +import '../../widgets/surface_card.dart'; +import '../../widgets/top_bar.dart'; + +class McpServerPage extends StatelessWidget { + const McpServerPage({ + super.key, + required this.controller, + required this.onOpenDetail, + }); + + final AppController controller; + final ValueChanged onOpenDetail; + + @override + Widget build(BuildContext context) { + final items = controller.connectors; + + return AnimatedBuilder( + animation: controller, + builder: (context, _) { + return SingleChildScrollView( + padding: const EdgeInsets.fromLTRB(32, 32, 32, 8), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + TopBar( + title: 'MCP Server', + subtitle: appText( + '管理 MCP 服务器连接与工具配置。', + 'Manage MCP server connections and tool configurations.', + ), + trailing: Wrap( + spacing: 12, + runSpacing: 12, + children: [ + SizedBox( + width: 220, + child: TextField( + decoration: InputDecoration( + hintText: appText('搜索服务器', 'Search servers'), + prefixIcon: Icon(Icons.search_rounded), + ), + ), + ), + IconButton( + onPressed: () async { + await controller.connectorsController.refresh(); + }, + icon: const Icon(Icons.refresh_rounded), + ), + ], + ), + ), + const SizedBox(height: 24), + if (items.isEmpty) + SurfaceCard( + child: Text( + controller.connection.status == + RuntimeConnectionStatus.connected + ? appText( + '当前没有连接的 MCP 服务器。', + 'No MCP servers connected.', + ) + : appText( + '连接 Gateway 后可查看 MCP 服务器。', + 'Connect a gateway to view MCP servers.', + ), + ), + ) + else + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: items + .map( + (connector) => Padding( + padding: const EdgeInsets.only(bottom: 12), + child: SurfaceCard( + onTap: () => onOpenDetail( + DetailPanelData( + title: connector.label, + subtitle: appText('连接器', 'Connector'), + icon: Icons.dns_rounded, + status: StatusInfo( + connector.status, + connector.connected + ? StatusTone.success + : StatusTone.neutral, + ), + description: connector.detailLabel, + meta: connector.meta, + actions: [appText('配置', 'Configure')], + sections: [ + DetailSection( + title: appText('详情', 'Details'), + items: [ + DetailItem( + label: appText('ID', 'ID'), + value: connector.id, + ), + DetailItem( + label: appText('状态', 'Status'), + value: connector.status, + ), + DetailItem( + label: appText('已配置', 'Configured'), + value: connector.configured + ? appText('是', 'Yes') + : appText('否', 'No'), + ), + DetailItem( + label: appText('已启用', 'Enabled'), + value: connector.enabled + ? appText('是', 'Yes') + : appText('否', 'No'), + ), + ], + ), + ], + ), + ), + child: Row( + children: [ + Expanded( + flex: 4, + child: Column( + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + Text( + connector.label, + style: Theme.of(context) + .textTheme + .titleMedium, + ), + const SizedBox(height: 6), + Text( + connector.detailLabel, + style: + Theme.of(context).textTheme.bodySmall, + ), + ], + ), + ), + Expanded( + flex: 2, + child: Text( + connector.connected + ? appText('已连接', 'Connected') + : appText('未连接', 'Disconnected'), + ), + ), + const Icon(Icons.chevron_right_rounded), + ], + ), + ), + ), + ) + .toList(), + ), + ], + ), + ); + }, + ); + } +} diff --git a/lib/models/app_models.dart b/lib/models/app_models.dart index 4fdb80e6..94d25caa 100644 --- a/lib/models/app_models.dart +++ b/lib/models/app_models.dart @@ -2,18 +2,19 @@ import 'package:flutter/material.dart'; import '../i18n/app_language.dart'; -enum WorkspaceDestination { + enum WorkspaceDestination { assistant, tasks, skills, nodes, agents, + mcpServer, clawHub, secrets, aiGateway, settings, account, -} + } extension WorkspaceDestinationCopy on WorkspaceDestination { String get label => switch (this) { @@ -22,6 +23,7 @@ extension WorkspaceDestinationCopy on WorkspaceDestination { WorkspaceDestination.skills => appText('技能', 'Skills'), WorkspaceDestination.nodes => appText('节点', 'Nodes'), WorkspaceDestination.agents => appText('代理', 'Agents'), + WorkspaceDestination.mcpServer => 'MCP Server', WorkspaceDestination.clawHub => 'ClawHub', WorkspaceDestination.secrets => appText('密钥', 'Secrets'), WorkspaceDestination.aiGateway => 'AI Gateway', @@ -35,6 +37,7 @@ extension WorkspaceDestinationCopy on WorkspaceDestination { WorkspaceDestination.skills => Icons.auto_awesome_rounded, WorkspaceDestination.nodes => Icons.developer_board_rounded, WorkspaceDestination.agents => Icons.hub_rounded, + WorkspaceDestination.mcpServer => Icons.dns_rounded, WorkspaceDestination.clawHub => Icons.extension_rounded, WorkspaceDestination.secrets => Icons.key_rounded, WorkspaceDestination.aiGateway => Icons.smart_toy_rounded, @@ -63,6 +66,10 @@ extension WorkspaceDestinationCopy on WorkspaceDestination { '管理代理实例,配置行为与能力。', 'Manage agent instances, configure behaviors and capabilities.', ), + WorkspaceDestination.mcpServer => appText( + '管理 MCP 服务器连接与工具配置。', + 'Manage MCP server connections and tool configurations.', + ), WorkspaceDestination.clawHub => appText( '浏览和安装技能包、代理模板与连接器。', 'Browse and install skill packages, agent templates and connectors.', diff --git a/lib/widgets/sidebar_navigation.dart b/lib/widgets/sidebar_navigation.dart index 9f1891f5..47be7baa 100644 --- a/lib/widgets/sidebar_navigation.dart +++ b/lib/widgets/sidebar_navigation.dart @@ -43,6 +43,7 @@ class SidebarNavigation extends StatelessWidget { WorkspaceDestination.skills, WorkspaceDestination.nodes, WorkspaceDestination.agents, + WorkspaceDestination.mcpServer, WorkspaceDestination.clawHub, WorkspaceDestination.secrets, WorkspaceDestination.aiGateway, @@ -258,6 +259,7 @@ class _SidebarNavItemState extends State { WorkspaceDestination.skills => Icons.auto_awesome_rounded, WorkspaceDestination.nodes => Icons.developer_board_rounded, WorkspaceDestination.agents => Icons.hub_rounded, + WorkspaceDestination.mcpServer => Icons.dns_rounded, WorkspaceDestination.clawHub => Icons.extension_rounded, WorkspaceDestination.secrets => Icons.key_rounded, WorkspaceDestination.aiGateway => Icons.smart_toy_rounded, @@ -273,6 +275,7 @@ class _SidebarNavItemState extends State { WorkspaceDestination.skills => appText('技能', 'Skills'), WorkspaceDestination.nodes => appText('节点', 'Nodes'), WorkspaceDestination.agents => appText('代理', 'Agents'), + WorkspaceDestination.mcpServer => 'MCP Server', WorkspaceDestination.clawHub => 'ClawHub', WorkspaceDestination.secrets => appText('密钥', 'Secrets'), WorkspaceDestination.aiGateway => 'AI Gateway',