auto-coder 0.1.288__py3-none-any.whl → 0.1.290__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.

@@ -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
- try:
167
- raw_config = json.loads(server_name_or_config)
168
- # 用户给了一个完整的配置
169
- if "mcpServers" in raw_config:
170
- raw_config = raw_config["mcpServers"]
171
-
172
- # 取第一个server 配置
173
- config = list(raw_config.values())[0]
174
- name = list(raw_config.keys())[0]
175
- if name.startswith("@") or config["command"] in ["npx","npm"]:
176
- for item in config["args"]:
177
- if name in item:
178
- self._install_node_package(item)
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
- return McpResponse(result=f"Successfully installed MCP server: {request.server_name_or_config}")
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=f"Failed to install MCP server: {str(e)}")
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(result=f"Successfully removed MCP server: {request.server_name}"))
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(result="", error=f"Failed to remove MCP server: {str(e)}"))
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 = "Available MCP servers:\n" + \
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(result="", error=f"Failed to list servers: {str(e)}"))
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
- await self._response_queue.put(McpResponse(result=running_servers))
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(result="", error=f"Failed to list running servers: {str(e)}"))
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(result="Successfully refreshed MCP server connections"))
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(result="", error=f"Failed to refresh MCP servers: {str(e)}"))
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
- llm = get_single_llm(request.model,product_mode=request.product_mode)
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(result="[No Result]", error="No results"))
289
- results_str = "\n\n".join(
290
- mcp_executor.format_mcp_result(result) for result in results)
291
- await self._response_queue.put(McpResponse(result=results_str))
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(result="", error=str(e)))
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():