|
10 | 10 | @group.group_class('mcp', '/api/v1/mcp') |
11 | 11 | class MCPRouterGroup(group.RouterGroup): |
12 | 12 | async def initialize(self) -> None: |
13 | | - @self.route('/servers', methods=['GET'], auth_type=group.AuthType.USER_TOKEN) |
| 13 | + @self.route('/servers', methods=['GET', 'POST'], auth_type=group.AuthType.USER_TOKEN) |
14 | 14 | async def _() -> str: |
15 | 15 | """获取MCP服务器列表""" |
16 | | - if not self.ap or not self.ap.provider_cfg or not self.ap.provider_cfg.data: |
17 | | - return self.success(data={'servers': []}) |
18 | | - |
19 | | - servers = self.ap.provider_cfg.data.get('mcp', {}).get('servers', []) |
| 16 | + if quart.request.method == 'GET': |
| 17 | + if not self.ap or not self.ap.provider_cfg or not self.ap.provider_cfg.data: |
| 18 | + return self.success(data={'servers': []}) |
| 19 | + |
| 20 | + servers = self.ap.provider_cfg.data.get('mcp', {}).get('servers', []) |
| 21 | + |
| 22 | + # 获取每个服务器的状态和工具信息 |
| 23 | + mcp_loader = None |
| 24 | + for loader_name, loader in self.ap.tool_mgr.loaders.items(): |
| 25 | + if loader_name == 'mcp': |
| 26 | + mcp_loader = loader |
| 27 | + break |
| 28 | + |
| 29 | + servers_with_status = [] |
| 30 | + for server in servers: |
| 31 | + server_info = { |
| 32 | + 'name': server['name'], |
| 33 | + 'mode': server['mode'], |
| 34 | + 'enable': server['enable'], |
| 35 | + 'config': server, |
| 36 | + 'status': 'disconnected', |
| 37 | + 'tools': [], |
| 38 | + 'error': None, |
| 39 | + } |
20 | 40 |
|
21 | | - # 获取每个服务器的状态和工具信息 |
22 | | - mcp_loader = None |
23 | | - for loader_name, loader in self.ap.tool_mgr.loaders.items(): |
24 | | - if loader_name == 'mcp': |
25 | | - mcp_loader = loader |
26 | | - break |
| 41 | + # 检查服务器连接状态 |
| 42 | + if mcp_loader and server['name'] in mcp_loader.sessions: |
| 43 | + session = mcp_loader.sessions[server['name']] |
| 44 | + server_info['status'] = 'connected' |
| 45 | + server_info['tools'] = [ |
| 46 | + {'name': func.name, 'description': func.description, 'parameters': func.parameters} |
| 47 | + for func in session.functions |
| 48 | + ] |
| 49 | + elif server['enable']: |
| 50 | + server_info['status'] = 'error' |
| 51 | + server_info['error'] = 'Failed to connect' |
| 52 | + |
| 53 | + servers_with_status.append(server_info) |
| 54 | + |
| 55 | + return self.success(data={'servers': servers_with_status}) |
| 56 | + elif quart.request.method == 'POST': |
| 57 | + data = await quart.request.json |
27 | 58 |
|
28 | | - servers_with_status = [] |
29 | | - for server in servers: |
30 | | - server_info = { |
31 | | - 'name': server['name'], |
32 | | - 'mode': server['mode'], |
33 | | - 'enable': server['enable'], |
34 | | - 'config': server, |
35 | | - 'status': 'disconnected', |
36 | | - 'tools': [], |
37 | | - 'error': None, |
| 59 | + # 验证必填字段 |
| 60 | + required_fields = ['name', 'mode'] |
| 61 | + for field in required_fields: |
| 62 | + if field not in data: |
| 63 | + return self.http_status(400, -1, f'Missing required field: {field}') |
| 64 | + |
| 65 | + # 检查provider_cfg是否可用 |
| 66 | + if not self.ap or not self.ap.provider_cfg or not self.ap.provider_cfg.data: |
| 67 | + return self.http_status(500, -1, 'Provider configuration not available') |
| 68 | + |
| 69 | + # 获取当前配置 |
| 70 | + mcp_config = self.ap.provider_cfg.data.get('mcp', {'servers': []}) |
| 71 | + servers = mcp_config['servers'] |
| 72 | + |
| 73 | + # 检查服务器名称是否重复 |
| 74 | + for server in servers: |
| 75 | + if server['name'] == data['name']: |
| 76 | + return self.http_status(400, -1, 'Server name already exists') |
| 77 | + |
| 78 | + # 创建新服务器配置 |
| 79 | + new_server = { |
| 80 | + 'name': data['name'], |
| 81 | + 'mode': data['mode'], |
| 82 | + 'enable': data.get('enable', True), |
38 | 83 | } |
39 | 84 |
|
40 | | - # 检查服务器连接状态 |
41 | | - if mcp_loader and server['name'] in mcp_loader.sessions: |
42 | | - session = mcp_loader.sessions[server['name']] |
43 | | - server_info['status'] = 'connected' |
44 | | - server_info['tools'] = [ |
45 | | - {'name': func.name, 'description': func.description, 'parameters': func.parameters} |
46 | | - for func in session.functions |
47 | | - ] |
48 | | - elif server['enable']: |
49 | | - server_info['status'] = 'error' |
50 | | - server_info['error'] = 'Failed to connect' |
51 | | - |
52 | | - servers_with_status.append(server_info) |
53 | | - |
54 | | - return self.success(data={'servers': servers_with_status}) |
55 | | - |
56 | | - @self.route('/servers', methods=['POST'], auth_type=group.AuthType.USER_TOKEN) |
57 | | - async def _() -> str: |
58 | | - """创建MCP服务器配置""" |
59 | | - data = await quart.request.json |
60 | | - |
61 | | - # 验证必填字段 |
62 | | - required_fields = ['name', 'mode'] |
63 | | - for field in required_fields: |
64 | | - if field not in data: |
65 | | - return self.http_status(400, -1, f'Missing required field: {field}') |
66 | | - |
67 | | - # 检查provider_cfg是否可用 |
68 | | - if not self.ap or not self.ap.provider_cfg or not self.ap.provider_cfg.data: |
69 | | - return self.http_status(500, -1, 'Provider configuration not available') |
70 | | - |
71 | | - # 获取当前配置 |
72 | | - mcp_config = self.ap.provider_cfg.data.get('mcp', {'servers': []}) |
73 | | - servers = mcp_config['servers'] |
74 | | - |
75 | | - # 检查服务器名称是否重复 |
76 | | - for server in servers: |
77 | | - if server['name'] == data['name']: |
78 | | - return self.http_status(400, -1, 'Server name already exists') |
79 | | - |
80 | | - # 创建新服务器配置 |
81 | | - new_server = { |
82 | | - 'name': data['name'], |
83 | | - 'mode': data['mode'], |
84 | | - 'enable': data.get('enable', True), |
85 | | - } |
86 | | - |
87 | | - # 根据模式添加配置 |
88 | | - if data['mode'] == 'stdio': |
89 | | - new_server.update( |
90 | | - {'command': data.get('command', ''), 'args': data.get('args', []), 'env': data.get('env', {})} |
91 | | - ) |
92 | | - elif data['mode'] == 'sse': |
93 | | - new_server.update( |
94 | | - {'url': data.get('url', ''), 'headers': data.get('headers', {}), 'timeout': data.get('timeout', 10)} |
95 | | - ) |
96 | | - |
97 | | - # 添加到配置 |
98 | | - servers.append(new_server) |
99 | | - self.ap.provider_cfg.data['mcp'] = mcp_config |
| 85 | + # 根据模式添加配置 |
| 86 | + if data['mode'] == 'stdio': |
| 87 | + new_server.update( |
| 88 | + {'command': data.get('command', ''), 'args': data.get('args', []), 'env': data.get('env', {})} |
| 89 | + ) |
| 90 | + elif data['mode'] == 'sse': |
| 91 | + new_server.update( |
| 92 | + { |
| 93 | + 'url': data.get('url', ''), |
| 94 | + 'headers': data.get('headers', {}), |
| 95 | + 'timeout': data.get('timeout', 10), |
| 96 | + } |
| 97 | + ) |
100 | 98 |
|
101 | | - # 保存配置 |
102 | | - await self.ap.provider_cfg.dump_config() |
| 99 | + # 添加到配置 |
| 100 | + servers.append(new_server) |
| 101 | + self.ap.provider_cfg.data['mcp'] = mcp_config |
103 | 102 |
|
104 | | - # 如果启用,尝试重新加载MCP loader |
105 | | - if new_server['enable']: |
106 | | - ctx = taskmgr.TaskContext.new() |
107 | | - wrapper = self.ap.task_mgr.create_user_task( |
108 | | - self._reload_mcp_loader(ctx), |
109 | | - kind='mcp-operation', |
110 | | - name=f'mcp-reload-{new_server["name"]}', |
111 | | - label=f'Reloading MCP loader for {new_server["name"]}', |
112 | | - context=ctx, |
113 | | - ) |
114 | | - return self.success(data={'task_id': wrapper.id}) |
| 103 | + # 保存配置 |
| 104 | + await self.ap.provider_cfg.dump_config() |
115 | 105 |
|
116 | | - return self.success() |
| 106 | + # 如果启用,尝试重新加载MCP loader |
| 107 | + if new_server['enable']: |
| 108 | + ctx = taskmgr.TaskContext.new() |
| 109 | + wrapper = self.ap.task_mgr.create_user_task( |
| 110 | + self._reload_mcp_loader(ctx), |
| 111 | + kind='mcp-operation', |
| 112 | + name=f'mcp-reload-{new_server["name"]}', |
| 113 | + label=f'Reloading MCP loader for {new_server["name"]}', |
| 114 | + context=ctx, |
| 115 | + ) |
| 116 | + return self.success(data={'task_id': wrapper.id}) |
| 117 | + else: |
| 118 | + return self.success() |
| 119 | + else: |
| 120 | + return self.http_status(405, -1, 'Method not allowed') |
117 | 121 |
|
118 | 122 | @self.route('/servers/<server_name>', methods=['GET', 'PUT', 'DELETE'], auth_type=group.AuthType.USER_TOKEN) |
119 | 123 | async def _(server_name: str) -> str: |
|
0 commit comments