auto-coder 0.1.287__py3-none-any.whl → 0.1.289__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of auto-coder might be problematic. Click here for more details.
- {auto_coder-0.1.287.dist-info → auto_coder-0.1.289.dist-info}/METADATA +1 -1
- {auto_coder-0.1.287.dist-info → auto_coder-0.1.289.dist-info}/RECORD +26 -17
- autocoder/chat_auto_coder.py +265 -82
- autocoder/chat_auto_coder_lang.py +25 -21
- autocoder/commands/auto_web.py +1062 -0
- autocoder/common/__init__.py +1 -2
- autocoder/common/anything2img.py +113 -43
- autocoder/common/auto_coder_lang.py +40 -1
- autocoder/common/computer_use.py +931 -0
- autocoder/common/mcp_hub.py +99 -77
- autocoder/common/mcp_server.py +162 -61
- autocoder/index/filter/quick_filter.py +373 -3
- autocoder/plugins/__init__.py +1123 -0
- autocoder/plugins/dynamic_completion_example.py +148 -0
- autocoder/plugins/git_helper_plugin.py +252 -0
- autocoder/plugins/sample_plugin.py +160 -0
- autocoder/plugins/token_helper_plugin.py +343 -0
- autocoder/plugins/utils.py +9 -0
- autocoder/rag/long_context_rag.py +22 -9
- autocoder/rag/relevant_utils.py +1 -1
- autocoder/rag/searchable.py +58 -0
- autocoder/version.py +1 -1
- {auto_coder-0.1.287.dist-info → auto_coder-0.1.289.dist-info}/LICENSE +0 -0
- {auto_coder-0.1.287.dist-info → auto_coder-0.1.289.dist-info}/WHEEL +0 -0
- {auto_coder-0.1.287.dist-info → auto_coder-0.1.289.dist-info}/entry_points.txt +0 -0
- {auto_coder-0.1.287.dist-info → auto_coder-0.1.289.dist-info}/top_level.txt +0 -0
autocoder/common/mcp_hub.py
CHANGED
|
@@ -2,16 +2,18 @@ import os
|
|
|
2
2
|
import json
|
|
3
3
|
import asyncio
|
|
4
4
|
import aiohttp
|
|
5
|
-
from datetime import datetime
|
|
5
|
+
from datetime import datetime, timedelta
|
|
6
6
|
from typing import Dict, List, Optional, Any, Set, Optional
|
|
7
7
|
from pathlib import Path
|
|
8
8
|
from pydantic import BaseModel, Field
|
|
9
9
|
|
|
10
10
|
from mcp import ClientSession
|
|
11
11
|
from mcp.client.stdio import stdio_client, StdioServerParameters
|
|
12
|
+
from mcp.client.sse import sse_client
|
|
12
13
|
import mcp.types as mcp_types
|
|
13
14
|
from loguru import logger
|
|
14
|
-
import
|
|
15
|
+
from contextlib import AsyncExitStack
|
|
16
|
+
from datetime import timedelta
|
|
15
17
|
|
|
16
18
|
|
|
17
19
|
class McpTool(BaseModel):
|
|
@@ -55,12 +57,9 @@ class McpServer(BaseModel):
|
|
|
55
57
|
class McpConnection:
|
|
56
58
|
"""Represents an active MCP server connection"""
|
|
57
59
|
|
|
58
|
-
def __init__(self, server: McpServer, session: ClientSession
|
|
60
|
+
def __init__(self, server: McpServer, session: ClientSession):
|
|
59
61
|
self.server = server
|
|
60
|
-
self.session = session
|
|
61
|
-
self.transport_manager = (
|
|
62
|
-
transport_manager # Will hold transport context manager
|
|
63
|
-
)
|
|
62
|
+
self.session = session
|
|
64
63
|
|
|
65
64
|
|
|
66
65
|
MCP_PERPLEXITY_SERVER = '''
|
|
@@ -81,6 +80,7 @@ MCP_BUILD_IN_SERVERS = {
|
|
|
81
80
|
"perplexity": json.loads(MCP_PERPLEXITY_SERVER)["perplexity"]
|
|
82
81
|
}
|
|
83
82
|
|
|
83
|
+
|
|
84
84
|
class McpHub:
|
|
85
85
|
"""
|
|
86
86
|
Manages MCP server connections and interactions.
|
|
@@ -105,6 +105,7 @@ class McpHub:
|
|
|
105
105
|
self.settings_path = Path(settings_path)
|
|
106
106
|
self.connections: Dict[str, McpConnection] = {}
|
|
107
107
|
self.is_connecting = False
|
|
108
|
+
self.exit_stacks: Dict[str, AsyncExitStack] = {}
|
|
108
109
|
|
|
109
110
|
# Ensure settings directory exists
|
|
110
111
|
self.settings_path.parent.mkdir(parents=True, exist_ok=True)
|
|
@@ -116,26 +117,31 @@ class McpHub:
|
|
|
116
117
|
def _write_default_settings(self):
|
|
117
118
|
"""Write default MCP settings file"""
|
|
118
119
|
default_settings = {"mcpServers": {}}
|
|
119
|
-
with open(self.settings_path, "w",encoding="utf-8") as f:
|
|
120
|
+
with open(self.settings_path, "w", encoding="utf-8") as f:
|
|
120
121
|
json.dump(default_settings, f, indent=2)
|
|
121
122
|
|
|
122
|
-
async def add_server_config(self, name: str, config:Dict[str,Any]) -> None:
|
|
123
|
+
async def add_server_config(self, name: str, config: Dict[str, Any]) -> None:
|
|
123
124
|
"""
|
|
124
125
|
Add or update MCP server configuration in settings file.
|
|
125
126
|
|
|
126
127
|
Args:
|
|
127
128
|
server_name_or_config: Name of the server or server configuration dictionary
|
|
128
129
|
"""
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
130
|
+
settings = self._read_settings()
|
|
131
|
+
settings["mcpServers"][name] = config
|
|
132
|
+
with open(self.settings_path, "w", encoding="utf-8") as f:
|
|
133
|
+
json.dump(settings, f, indent=2, ensure_ascii=False)
|
|
134
|
+
await self.initialize()
|
|
135
|
+
if name in self.connections:
|
|
136
|
+
if self.connections[name].server.status == "connected":
|
|
137
|
+
logger.info(f"Added/updated MCP server config: {name}")
|
|
138
|
+
return True
|
|
139
|
+
else:
|
|
140
|
+
logger.error(f"Failed to add MCP server config: {name}")
|
|
141
|
+
return False
|
|
142
|
+
else:
|
|
143
|
+
logger.error(f"Failed to add MCP server config: {name}")
|
|
144
|
+
return False
|
|
139
145
|
|
|
140
146
|
async def remove_server_config(self, name: str) -> None:
|
|
141
147
|
"""
|
|
@@ -148,7 +154,7 @@ class McpHub:
|
|
|
148
154
|
settings = self._read_settings()
|
|
149
155
|
if name in settings["mcpServers"]:
|
|
150
156
|
del settings["mcpServers"][name]
|
|
151
|
-
with open(self.settings_path, "w",encoding="utf-8") as f:
|
|
157
|
+
with open(self.settings_path, "w", encoding="utf-8") as f:
|
|
152
158
|
json.dump(settings, f, indent=2, ensure_ascii=False)
|
|
153
159
|
logger.info(f"Removed MCP server config: {name}")
|
|
154
160
|
await self.initialize()
|
|
@@ -160,18 +166,14 @@ class McpHub:
|
|
|
160
166
|
|
|
161
167
|
async def initialize(self):
|
|
162
168
|
"""Initialize MCP server connections from settings"""
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
await self.update_server_connections(config.get("mcpServers", {}))
|
|
166
|
-
except Exception as e:
|
|
167
|
-
logger.error(f"Failed to initialize MCP servers: {e}")
|
|
168
|
-
raise
|
|
169
|
+
config = self._read_settings()
|
|
170
|
+
await self.update_server_connections(config.get("mcpServers", {}))
|
|
169
171
|
|
|
170
172
|
def get_servers(self) -> List[McpServer]:
|
|
171
173
|
"""Get list of all configured servers"""
|
|
172
174
|
return [conn.server for conn in self.connections.values()]
|
|
173
175
|
|
|
174
|
-
async def connect_to_server(self, name: str, config: dict) ->
|
|
176
|
+
async def connect_to_server(self, name: str, config: dict) -> McpServer:
|
|
175
177
|
"""
|
|
176
178
|
Establish connection to an MCP server with proper resource management
|
|
177
179
|
"""
|
|
@@ -179,79 +181,90 @@ class McpHub:
|
|
|
179
181
|
if name in self.connections:
|
|
180
182
|
await self.delete_connection(name)
|
|
181
183
|
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
184
|
+
self.exit_stacks[name] = AsyncExitStack()
|
|
185
|
+
exit_stack = self.exit_stacks[name]
|
|
186
|
+
|
|
187
|
+
server = McpServer(
|
|
188
|
+
name=name, config=json.dumps(config), status="connecting"
|
|
189
|
+
)
|
|
190
|
+
transport_config = config.get("transport", {
|
|
191
|
+
"type": "stdio",
|
|
192
|
+
"endpoint":""
|
|
193
|
+
})
|
|
186
194
|
|
|
195
|
+
if transport_config["type"] not in ["stdio", "sse"]:
|
|
196
|
+
raise ValueError(f"Invalid transport type: {transport_config['type']}")
|
|
197
|
+
|
|
198
|
+
if transport_config["type"] == "stdio":
|
|
187
199
|
# Setup transport parameters
|
|
188
200
|
server_params = StdioServerParameters(
|
|
189
201
|
command=config["command"],
|
|
190
202
|
args=config.get("args", []),
|
|
191
203
|
env={**config.get("env", {}),
|
|
192
|
-
|
|
204
|
+
"PATH": os.environ.get("PATH", "")},
|
|
193
205
|
)
|
|
194
206
|
|
|
195
|
-
# Create transport using context manager
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
207
|
+
# Create transport using context manager
|
|
208
|
+
transport = await exit_stack.enter_async_context(stdio_client(server_params))
|
|
209
|
+
|
|
210
|
+
if transport_config["type"] == "sse":
|
|
211
|
+
url = transport_config["endpoint"]
|
|
212
|
+
transport = await exit_stack.enter_async_context(sse_client(url))
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
try:
|
|
216
|
+
stdio, write = transport
|
|
217
|
+
session = await exit_stack.enter_async_context(
|
|
218
|
+
ClientSession(stdio, write, read_timeout_seconds=timedelta(seconds=60)))
|
|
219
|
+
await session.initialize()
|
|
220
|
+
connection = McpConnection(server, session)
|
|
221
|
+
self.connections[name] = connection
|
|
222
|
+
# Update server status and fetch capabilities
|
|
223
|
+
server.status = "connected"
|
|
224
|
+
server.tools = await self._fetch_tools(name)
|
|
225
|
+
server.resources = await self._fetch_resources(name)
|
|
226
|
+
server.resource_templates = await self._fetch_resource_templates(name)
|
|
227
|
+
return server
|
|
228
|
+
|
|
218
229
|
except Exception as e:
|
|
219
230
|
error_msg = str(e)
|
|
220
231
|
logger.error(f"Failed to connect to server {name}: {error_msg}")
|
|
221
232
|
if name in self.connections:
|
|
222
233
|
self.connections[name].server.status = "disconnected"
|
|
223
234
|
self.connections[name].server.error = error_msg
|
|
224
|
-
|
|
235
|
+
return server
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
|
|
240
|
+
|
|
225
241
|
|
|
226
242
|
async def delete_connection(self, name: str) -> None:
|
|
227
243
|
"""
|
|
228
244
|
Close and remove a server connection with proper cleanup
|
|
229
245
|
"""
|
|
230
246
|
if name in self.connections:
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
# Clean up in reverse order of creation
|
|
234
|
-
await connection.session.__aexit__(None, None, None)
|
|
235
|
-
await connection.transport_manager.__aexit__(None, None, None)
|
|
247
|
+
|
|
248
|
+
try:
|
|
236
249
|
del self.connections[name]
|
|
237
250
|
except Exception as e:
|
|
238
251
|
logger.error(f"Error closing connection to {name}: {e}")
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
252
|
+
|
|
253
|
+
try:
|
|
254
|
+
exit_stack = self.exit_stacks[name]
|
|
255
|
+
await exit_stack.aclose()
|
|
256
|
+
del self.exit_stacks[name]
|
|
257
|
+
except Exception as e:
|
|
258
|
+
logger.error(f"Error cleaning up to {name}: {e}")
|
|
259
|
+
|
|
242
260
|
|
|
243
261
|
async def refresh_server_connection(self, name: str) -> None:
|
|
244
262
|
"""
|
|
245
263
|
Refresh a server connection
|
|
246
264
|
"""
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
await self.connect_to_server(name, config.get("mcpServers", {}).get(name, {}))
|
|
251
|
-
except Exception as e:
|
|
252
|
-
logger.error(f"Failed to refresh MCP server {name}: {e}")
|
|
253
|
-
raise
|
|
254
|
-
|
|
265
|
+
config = self._read_settings()
|
|
266
|
+
await self.delete_connection(name)
|
|
267
|
+
await self.connect_to_server(name, config.get("mcpServers", {}).get(name, {}))
|
|
255
268
|
|
|
256
269
|
async def update_server_connections(self, new_servers: Dict[str, Any]) -> None:
|
|
257
270
|
"""
|
|
@@ -273,13 +286,22 @@ class McpHub:
|
|
|
273
286
|
|
|
274
287
|
if not current_conn:
|
|
275
288
|
# New server
|
|
276
|
-
await self.connect_to_server(name, config)
|
|
277
|
-
|
|
289
|
+
server = await self.connect_to_server(name, config)
|
|
290
|
+
if server.status == "connected":
|
|
291
|
+
logger.info(f"Connected to new MCP server: {name}")
|
|
292
|
+
else:
|
|
293
|
+
logger.error(
|
|
294
|
+
f"Failed to connect to new MCP server: {name}")
|
|
295
|
+
|
|
278
296
|
elif current_conn.server.config != json.dumps(config):
|
|
279
297
|
# Updated configuration
|
|
280
|
-
await self.connect_to_server(name, config)
|
|
281
|
-
|
|
282
|
-
|
|
298
|
+
server = await self.connect_to_server(name, config)
|
|
299
|
+
if server.status == "connected":
|
|
300
|
+
logger.info(
|
|
301
|
+
f"Reconnected MCP server with updated config: {name}")
|
|
302
|
+
else:
|
|
303
|
+
logger.error(
|
|
304
|
+
f"Failed to reconnected MCP server with updated config: {name}")
|
|
283
305
|
|
|
284
306
|
finally:
|
|
285
307
|
self.is_connecting = False
|
autocoder/common/mcp_server.py
CHANGED
|
@@ -13,6 +13,7 @@ from pydantic import BaseModel
|
|
|
13
13
|
import sys
|
|
14
14
|
from loguru import logger
|
|
15
15
|
from autocoder.utils.llms import get_single_llm
|
|
16
|
+
from autocoder.chat_auto_coder_lang import get_message_with_format
|
|
16
17
|
|
|
17
18
|
@dataclass
|
|
18
19
|
class McpRequest:
|
|
@@ -157,55 +158,135 @@ class McpServer:
|
|
|
157
158
|
print(f"We have already updated the server configuration in ~/.autocoder/mcp/settings.json.\n")
|
|
158
159
|
print(f"After installation, you can restart the auto-coder.chat using the server.\033[0m\n")
|
|
159
160
|
|
|
161
|
+
def _deep_merge_dicts(self, dict1, dict2):
|
|
162
|
+
"""
|
|
163
|
+
深度合并两个字典,包括嵌套的字典
|
|
164
|
+
dict1是基础字典,dict2是优先字典(当有冲突时保留dict2的值)
|
|
165
|
+
"""
|
|
166
|
+
result = dict1.copy()
|
|
167
|
+
for key, value in dict2.items():
|
|
168
|
+
if key in result and isinstance(result[key], dict) and isinstance(value, dict):
|
|
169
|
+
# 如果两个字典都包含相同的键,并且两个值都是字典,则递归合并
|
|
170
|
+
result[key] = self._deep_merge_dicts(result[key], value)
|
|
171
|
+
else:
|
|
172
|
+
# 否则,使用dict2的值覆盖或添加到结果
|
|
173
|
+
result[key] = value
|
|
174
|
+
return result
|
|
175
|
+
|
|
176
|
+
def _parse_command_line_args(self, server_name_or_config: str) -> tuple[str, dict]:
|
|
177
|
+
"""Parse command-line style arguments into name and config"""
|
|
178
|
+
name = ""
|
|
179
|
+
config = {}
|
|
180
|
+
args = server_name_or_config.strip().split()
|
|
181
|
+
i = 0
|
|
182
|
+
while i < len(args):
|
|
183
|
+
if args[i] == "--name" and i + 1 < len(args):
|
|
184
|
+
name = args[i + 1]
|
|
185
|
+
i += 2
|
|
186
|
+
elif args[i] == "--command" and i + 1 < len(args):
|
|
187
|
+
config["command"] = args[i + 1]
|
|
188
|
+
i += 2
|
|
189
|
+
elif args[i] == "--args":
|
|
190
|
+
config["args"] = []
|
|
191
|
+
i += 1
|
|
192
|
+
while i < len(args) and not args[i].startswith("--"):
|
|
193
|
+
config["args"].append(args[i])
|
|
194
|
+
i += 1
|
|
195
|
+
elif args[i] == "--env":
|
|
196
|
+
config["env"] = {}
|
|
197
|
+
i += 1
|
|
198
|
+
while i < len(args) and not args[i].startswith("--"):
|
|
199
|
+
if "=" in args[i]:
|
|
200
|
+
key, value = args[i].split("=", 1)
|
|
201
|
+
config["env"][key] = value
|
|
202
|
+
i += 1
|
|
203
|
+
else:
|
|
204
|
+
i += 1
|
|
205
|
+
|
|
206
|
+
template_config = {}
|
|
207
|
+
|
|
208
|
+
if name in MCP_BUILD_IN_SERVERS:
|
|
209
|
+
template_config = MCP_BUILD_IN_SERVERS[name]
|
|
210
|
+
else:
|
|
211
|
+
# 查找外部server
|
|
212
|
+
external_servers = get_mcp_external_servers()
|
|
213
|
+
for s in external_servers:
|
|
214
|
+
if s.name == name:
|
|
215
|
+
if s.runtime == "python":
|
|
216
|
+
self._install_python_package(name)
|
|
217
|
+
template_config = {
|
|
218
|
+
"command": "python",
|
|
219
|
+
"args": [
|
|
220
|
+
"-m", name.replace("-", "_")
|
|
221
|
+
],
|
|
222
|
+
}
|
|
223
|
+
elif s.runtime == "node":
|
|
224
|
+
self._install_node_package(name)
|
|
225
|
+
template_config = {
|
|
226
|
+
"command": "npx",
|
|
227
|
+
"args": [
|
|
228
|
+
"-y",
|
|
229
|
+
"-g",
|
|
230
|
+
name
|
|
231
|
+
]
|
|
232
|
+
}
|
|
233
|
+
break
|
|
234
|
+
|
|
235
|
+
# 深度合并两个配置,以用户配置为主
|
|
236
|
+
config = self._deep_merge_dicts(template_config, config)
|
|
237
|
+
|
|
238
|
+
if not config.get("args") and (name.startswith("@") or config.get("command") in ["npx", "npm"]):
|
|
239
|
+
config["args"] = ["-y", "-g", name]
|
|
240
|
+
|
|
241
|
+
## 如果有模板,则无需再次安装,处理模板的时候会自动安装
|
|
242
|
+
if not template_config:
|
|
243
|
+
# Install package if needed
|
|
244
|
+
if name.startswith("@") or config.get("command") in ["npx", "npm"]:
|
|
245
|
+
self._install_node_package(name)
|
|
246
|
+
else:
|
|
247
|
+
self._install_python_package(name)
|
|
248
|
+
|
|
249
|
+
return name, config
|
|
250
|
+
|
|
251
|
+
def _parse_json_config(self, server_name_or_config: str) -> tuple[str, dict]:
|
|
252
|
+
"""Parse JSON configuration into name and config"""
|
|
253
|
+
raw_config = json.loads(server_name_or_config)
|
|
254
|
+
# 用户给了一个完整的配置
|
|
255
|
+
if "mcpServers" in raw_config:
|
|
256
|
+
raw_config = raw_config["mcpServers"]
|
|
257
|
+
|
|
258
|
+
# 取第一个server 配置
|
|
259
|
+
config = list(raw_config.values())[0]
|
|
260
|
+
name = list(raw_config.keys())[0]
|
|
261
|
+
if name.startswith("@") or config["command"] in ["npx", "npm"]:
|
|
262
|
+
for item in config["args"]:
|
|
263
|
+
if name in item:
|
|
264
|
+
self._install_node_package(item)
|
|
265
|
+
else:
|
|
266
|
+
self._install_python_package(name)
|
|
267
|
+
|
|
268
|
+
return name, config
|
|
269
|
+
|
|
270
|
+
|
|
160
271
|
async def _install_server(self, request: McpInstallRequest, hub: McpHub) -> McpResponse:
|
|
161
272
|
"""Install an MCP server with module dependency check"""
|
|
162
273
|
name = ""
|
|
163
274
|
config = {}
|
|
164
275
|
try:
|
|
165
276
|
server_name_or_config = request.server_name_or_config
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
else:
|
|
180
|
-
self._install_python_package(name)
|
|
181
|
-
except json.JSONDecodeError:
|
|
182
|
-
name = server_name_or_config.strip()
|
|
183
|
-
if name not in MCP_BUILD_IN_SERVERS:
|
|
184
|
-
# 查找外部server
|
|
185
|
-
external_servers = get_mcp_external_servers()
|
|
186
|
-
for s in external_servers:
|
|
187
|
-
if s.name == name:
|
|
188
|
-
if s.runtime == "python":
|
|
189
|
-
self._install_python_package(name)
|
|
190
|
-
config = {
|
|
191
|
-
"command": "python",
|
|
192
|
-
"args": [
|
|
193
|
-
"-m", name.replace("-", "_")
|
|
194
|
-
],
|
|
195
|
-
}
|
|
196
|
-
elif s.runtime == "node":
|
|
197
|
-
self._install_node_package(name)
|
|
198
|
-
config = {
|
|
199
|
-
"command": "npx",
|
|
200
|
-
"args": [
|
|
201
|
-
"-y",
|
|
202
|
-
"-g",
|
|
203
|
-
name
|
|
204
|
-
]
|
|
205
|
-
}
|
|
206
|
-
break
|
|
207
|
-
else:
|
|
208
|
-
config = MCP_BUILD_IN_SERVERS[name]
|
|
277
|
+
|
|
278
|
+
# Try different parsing methods
|
|
279
|
+
if server_name_or_config.strip().startswith("--"):
|
|
280
|
+
# Command-line style arguments
|
|
281
|
+
name, config = self._parse_command_line_args(server_name_or_config)
|
|
282
|
+
else:
|
|
283
|
+
try:
|
|
284
|
+
# Try parsing as JSON
|
|
285
|
+
name, config = self._parse_json_config(server_name_or_config)
|
|
286
|
+
except json.JSONDecodeError:
|
|
287
|
+
logger.error(f"Failed to parse JSON config: {server_name_or_config}")
|
|
288
|
+
pass
|
|
289
|
+
|
|
209
290
|
if not name:
|
|
210
291
|
raise ValueError(
|
|
211
292
|
"MCP server name is not available in MCP_BUILD_IN_SERVERS or external servers")
|
|
@@ -214,10 +295,13 @@ class McpServer:
|
|
|
214
295
|
if not config:
|
|
215
296
|
raise ValueError(f"MCP server {name} config is not available")
|
|
216
297
|
|
|
217
|
-
await hub.add_server_config(name, config)
|
|
218
|
-
|
|
298
|
+
is_success = await hub.add_server_config(name, config)
|
|
299
|
+
if is_success:
|
|
300
|
+
return McpResponse(result=get_message_with_format("mcp_install_success", result=request.server_name_or_config))
|
|
301
|
+
else:
|
|
302
|
+
return McpResponse(result="", error=get_message_with_format("mcp_install_error", error="Failed to establish connection"))
|
|
219
303
|
except Exception as e:
|
|
220
|
-
return McpResponse(result="", error=
|
|
304
|
+
return McpResponse(result="", error=get_message_with_format("mcp_install_error", error=str(e)))
|
|
221
305
|
|
|
222
306
|
async def _process_request(self):
|
|
223
307
|
hub = McpHub()
|
|
@@ -236,9 +320,11 @@ class McpServer:
|
|
|
236
320
|
elif isinstance(request, McpRemoveRequest):
|
|
237
321
|
try:
|
|
238
322
|
await hub.remove_server_config(request.server_name)
|
|
239
|
-
await self._response_queue.put(McpResponse(
|
|
323
|
+
await self._response_queue.put(McpResponse(
|
|
324
|
+
result=get_message_with_format("mcp_remove_success", result=request.server_name)))
|
|
240
325
|
except Exception as e:
|
|
241
|
-
await self._response_queue.put(McpResponse(
|
|
326
|
+
await self._response_queue.put(McpResponse(
|
|
327
|
+
result="", error=get_message_with_format("mcp_remove_error", error=str(e))))
|
|
242
328
|
|
|
243
329
|
elif isinstance(request, McpListRequest):
|
|
244
330
|
try:
|
|
@@ -253,20 +339,22 @@ class McpServer:
|
|
|
253
339
|
|
|
254
340
|
# Combine results
|
|
255
341
|
all_servers = builtin_servers + external_list
|
|
256
|
-
result =
|
|
257
|
-
"\n".join(all_servers)
|
|
342
|
+
result = "\n".join(all_servers)
|
|
258
343
|
|
|
259
344
|
await self._response_queue.put(McpResponse(result=result))
|
|
260
345
|
except Exception as e:
|
|
261
|
-
await self._response_queue.put(McpResponse(
|
|
346
|
+
await self._response_queue.put(McpResponse(
|
|
347
|
+
result="", error=get_message_with_format("mcp_list_builtin_error", error=str(e))))
|
|
262
348
|
|
|
263
349
|
elif isinstance(request, McpListRunningRequest):
|
|
264
350
|
try:
|
|
265
351
|
running_servers = "\n".join(
|
|
266
352
|
[f"- {server.name}" for server in hub.get_servers()])
|
|
267
|
-
|
|
353
|
+
result = running_servers if running_servers else ""
|
|
354
|
+
await self._response_queue.put(McpResponse(result=result))
|
|
268
355
|
except Exception as e:
|
|
269
|
-
await self._response_queue.put(McpResponse(
|
|
356
|
+
await self._response_queue.put(McpResponse(
|
|
357
|
+
result="", error=get_message_with_format("mcp_list_running_error", error=str(e))))
|
|
270
358
|
|
|
271
359
|
elif isinstance(request, McpRefreshRequest):
|
|
272
360
|
try:
|
|
@@ -274,23 +362,36 @@ class McpServer:
|
|
|
274
362
|
await hub.refresh_server_connection(request.name)
|
|
275
363
|
else:
|
|
276
364
|
await hub.initialize()
|
|
277
|
-
await self._response_queue.put(McpResponse(
|
|
365
|
+
await self._response_queue.put(McpResponse(
|
|
366
|
+
result=get_message_with_format("mcp_refresh_success")))
|
|
278
367
|
except Exception as e:
|
|
279
|
-
await self._response_queue.put(McpResponse(
|
|
368
|
+
await self._response_queue.put(McpResponse(
|
|
369
|
+
result="", error=get_message_with_format("mcp_refresh_error", error=str(e))))
|
|
280
370
|
|
|
281
371
|
else:
|
|
282
|
-
|
|
372
|
+
if not request.query.strip():
|
|
373
|
+
await self._response_queue.put(McpResponse(
|
|
374
|
+
result="", error=get_message_with_format("mcp_query_empty")))
|
|
375
|
+
continue
|
|
376
|
+
|
|
377
|
+
llm = get_single_llm(request.model, product_mode=request.product_mode)
|
|
283
378
|
mcp_executor = McpExecutor(hub, llm)
|
|
284
379
|
conversations = [
|
|
285
380
|
{"role": "user", "content": request.query}]
|
|
286
381
|
_, results = await mcp_executor.run(conversations)
|
|
382
|
+
|
|
287
383
|
if not results:
|
|
288
|
-
await self._response_queue.put(McpResponse(
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
384
|
+
await self._response_queue.put(McpResponse(
|
|
385
|
+
result=get_message_with_format("mcp_error_title"),
|
|
386
|
+
error="No results"))
|
|
387
|
+
else:
|
|
388
|
+
results_str = "\n\n".join(
|
|
389
|
+
mcp_executor.format_mcp_result(result) for result in results)
|
|
390
|
+
await self._response_queue.put(McpResponse(
|
|
391
|
+
result=get_message_with_format("mcp_response_title") + "\n" + results_str))
|
|
292
392
|
except Exception as e:
|
|
293
|
-
await self._response_queue.put(McpResponse(
|
|
393
|
+
await self._response_queue.put(McpResponse(
|
|
394
|
+
result="", error=get_message_with_format("mcp_error_title") + ": " + str(e)))
|
|
294
395
|
|
|
295
396
|
def send_request(self, request: McpRequest) -> McpResponse:
|
|
296
397
|
async def _send():
|