auto-coder 0.1.334__py3-none-any.whl → 0.1.340__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.334.dist-info → auto_coder-0.1.340.dist-info}/METADATA +2 -2
- {auto_coder-0.1.334.dist-info → auto_coder-0.1.340.dist-info}/RECORD +70 -34
- autocoder/agent/agentic_edit.py +833 -0
- autocoder/agent/agentic_edit_tools/__init__.py +28 -0
- autocoder/agent/agentic_edit_tools/ask_followup_question_tool_resolver.py +32 -0
- autocoder/agent/agentic_edit_tools/attempt_completion_tool_resolver.py +29 -0
- autocoder/agent/agentic_edit_tools/base_tool_resolver.py +29 -0
- autocoder/agent/agentic_edit_tools/execute_command_tool_resolver.py +84 -0
- autocoder/agent/agentic_edit_tools/list_code_definition_names_tool_resolver.py +75 -0
- autocoder/agent/agentic_edit_tools/list_files_tool_resolver.py +62 -0
- autocoder/agent/agentic_edit_tools/plan_mode_respond_tool_resolver.py +30 -0
- autocoder/agent/agentic_edit_tools/read_file_tool_resolver.py +36 -0
- autocoder/agent/agentic_edit_tools/replace_in_file_tool_resolver.py +95 -0
- autocoder/agent/agentic_edit_tools/search_files_tool_resolver.py +70 -0
- autocoder/agent/agentic_edit_tools/use_mcp_tool_resolver.py +55 -0
- autocoder/agent/agentic_edit_tools/write_to_file_tool_resolver.py +98 -0
- autocoder/agent/agentic_edit_types.py +124 -0
- autocoder/agent/agentic_filter.py +14 -7
- autocoder/auto_coder.py +39 -18
- autocoder/auto_coder_rag.py +18 -9
- autocoder/auto_coder_runner.py +107 -8
- autocoder/chat_auto_coder.py +1 -2
- autocoder/chat_auto_coder_lang.py +18 -2
- autocoder/commands/tools.py +5 -1
- autocoder/common/__init__.py +2 -0
- autocoder/common/auto_coder_lang.py +84 -8
- autocoder/common/code_auto_generate_diff.py +1 -1
- autocoder/common/code_auto_generate_editblock.py +1 -1
- autocoder/common/code_auto_generate_strict_diff.py +1 -1
- autocoder/common/mcp_hub.py +185 -2
- autocoder/common/mcp_server.py +243 -306
- autocoder/common/mcp_server_install.py +269 -0
- autocoder/common/mcp_server_types.py +169 -0
- autocoder/common/stream_out_type.py +3 -0
- autocoder/common/v2/agent/__init__.py +0 -0
- autocoder/common/v2/agent/agentic_edit.py +1433 -0
- autocoder/common/v2/agent/agentic_edit_conversation.py +179 -0
- autocoder/common/v2/agent/agentic_edit_tools/__init__.py +28 -0
- autocoder/common/v2/agent/agentic_edit_tools/ask_followup_question_tool_resolver.py +70 -0
- autocoder/common/v2/agent/agentic_edit_tools/attempt_completion_tool_resolver.py +35 -0
- autocoder/common/v2/agent/agentic_edit_tools/base_tool_resolver.py +33 -0
- autocoder/common/v2/agent/agentic_edit_tools/execute_command_tool_resolver.py +88 -0
- autocoder/common/v2/agent/agentic_edit_tools/list_code_definition_names_tool_resolver.py +80 -0
- autocoder/common/v2/agent/agentic_edit_tools/list_files_tool_resolver.py +105 -0
- autocoder/common/v2/agent/agentic_edit_tools/plan_mode_respond_tool_resolver.py +35 -0
- autocoder/common/v2/agent/agentic_edit_tools/read_file_tool_resolver.py +51 -0
- autocoder/common/v2/agent/agentic_edit_tools/replace_in_file_tool_resolver.py +153 -0
- autocoder/common/v2/agent/agentic_edit_tools/search_files_tool_resolver.py +104 -0
- autocoder/common/v2/agent/agentic_edit_tools/use_mcp_tool_resolver.py +46 -0
- autocoder/common/v2/agent/agentic_edit_tools/write_to_file_tool_resolver.py +58 -0
- autocoder/common/v2/agent/agentic_edit_types.py +167 -0
- autocoder/common/v2/agent/agentic_tool_display.py +184 -0
- autocoder/common/v2/code_agentic_editblock_manager.py +812 -0
- autocoder/common/v2/code_auto_generate.py +1 -1
- autocoder/common/v2/code_auto_generate_diff.py +1 -1
- autocoder/common/v2/code_auto_generate_editblock.py +1 -1
- autocoder/common/v2/code_auto_generate_strict_diff.py +1 -1
- autocoder/common/v2/code_editblock_manager.py +151 -178
- autocoder/compilers/provided_compiler.py +3 -2
- autocoder/events/event_manager.py +4 -4
- autocoder/events/event_types.py +1 -0
- autocoder/memory/active_context_manager.py +2 -29
- autocoder/models.py +10 -2
- autocoder/shadows/shadow_manager.py +1 -1
- autocoder/utils/llms.py +4 -2
- autocoder/version.py +1 -1
- {auto_coder-0.1.334.dist-info → auto_coder-0.1.340.dist-info}/LICENSE +0 -0
- {auto_coder-0.1.334.dist-info → auto_coder-0.1.340.dist-info}/WHEEL +0 -0
- {auto_coder-0.1.334.dist-info → auto_coder-0.1.340.dist-info}/entry_points.txt +0 -0
- {auto_coder-0.1.334.dist-info → auto_coder-0.1.340.dist-info}/top_level.txt +0 -0
autocoder/common/mcp_server.py
CHANGED
|
@@ -1,111 +1,25 @@
|
|
|
1
1
|
import asyncio
|
|
2
2
|
from asyncio import Queue as AsyncQueue
|
|
3
3
|
import threading
|
|
4
|
-
from typing import List
|
|
5
|
-
from dataclasses import dataclass
|
|
6
|
-
from autocoder.common.mcp_hub import McpHub
|
|
7
|
-
from autocoder.common.mcp_tools import McpExecutor
|
|
8
|
-
from autocoder.common.mcp_hub import MCP_BUILD_IN_SERVERS
|
|
9
|
-
import json
|
|
4
|
+
from typing import List
|
|
10
5
|
import os
|
|
11
6
|
import time
|
|
12
|
-
from pydantic import BaseModel
|
|
13
|
-
import sys
|
|
14
7
|
from loguru import logger
|
|
8
|
+
import json
|
|
9
|
+
|
|
10
|
+
from autocoder.common.mcp_hub import McpHub, MCP_BUILD_IN_SERVERS, MarketplaceMCPServerItem
|
|
11
|
+
from autocoder.common.mcp_tools import McpExecutor
|
|
15
12
|
from autocoder.utils.llms import get_single_llm
|
|
16
13
|
from autocoder.chat_auto_coder_lang import get_message_with_format
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
server_name_or_config: Optional[str] = None
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
@dataclass
|
|
30
|
-
class McpRemoveRequest:
|
|
31
|
-
server_name: str
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
@dataclass
|
|
35
|
-
class McpListRequest:
|
|
36
|
-
"""Request to list all builtin MCP servers"""
|
|
37
|
-
pass
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
@dataclass
|
|
41
|
-
class McpListRunningRequest:
|
|
42
|
-
"""Request to list all running MCP servers"""
|
|
43
|
-
pass
|
|
44
|
-
|
|
45
|
-
@dataclass
|
|
46
|
-
class McpResponse:
|
|
47
|
-
result: str
|
|
48
|
-
error: Optional[str] = None
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
@dataclass
|
|
52
|
-
class McpRefreshRequest:
|
|
53
|
-
"""Request to refresh MCP server connections"""
|
|
54
|
-
name: Optional[str] = None
|
|
55
|
-
|
|
56
|
-
@dataclass
|
|
57
|
-
class McpServerInfoRequest:
|
|
58
|
-
"""Request to get MCP server info"""
|
|
59
|
-
model: Optional[str] = None
|
|
60
|
-
product_mode: Optional[str] = None
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
@dataclass
|
|
64
|
-
class McpExternalServer(BaseModel):
|
|
65
|
-
result: str
|
|
66
|
-
error: Optional[str] = None
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
class McpExternalServer(BaseModel):
|
|
70
|
-
"""Represents an external MCP server configuration"""
|
|
71
|
-
name: str
|
|
72
|
-
description: str
|
|
73
|
-
vendor: str
|
|
74
|
-
sourceUrl: str
|
|
75
|
-
homepage: str
|
|
76
|
-
license: str
|
|
77
|
-
runtime: str
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
def get_mcp_external_servers() -> List[McpExternalServer]:
|
|
81
|
-
"""Get external MCP servers list from GitHub"""
|
|
82
|
-
cache_dir = os.path.join(".auto-coder", "tmp")
|
|
83
|
-
os.makedirs(cache_dir, exist_ok=True)
|
|
84
|
-
cache_file = os.path.join(cache_dir, "mcp_external_servers.json")
|
|
85
|
-
|
|
86
|
-
# Check cache first
|
|
87
|
-
if os.path.exists(cache_file):
|
|
88
|
-
cache_time = os.path.getmtime(cache_file)
|
|
89
|
-
if time.time() - cache_time < 3600: # 1 hour cache
|
|
90
|
-
with open(cache_file, "r",encoding="utf-8") as f:
|
|
91
|
-
raw_data = json.load(f)
|
|
92
|
-
return [McpExternalServer(**item) for item in raw_data]
|
|
93
|
-
|
|
94
|
-
# Fetch from GitHub
|
|
95
|
-
url = "https://raw.githubusercontent.com/michaellatman/mcp-get/refs/heads/main/packages/package-list.json"
|
|
96
|
-
try:
|
|
97
|
-
import requests
|
|
98
|
-
response = requests.get(url)
|
|
99
|
-
if response.status_code == 200:
|
|
100
|
-
raw_data = response.json()
|
|
101
|
-
with open(cache_file, "w",encoding="utf-8") as f:
|
|
102
|
-
json.dump(raw_data, f)
|
|
103
|
-
return [McpExternalServer(**item) for item in raw_data]
|
|
104
|
-
return []
|
|
105
|
-
except Exception as e:
|
|
106
|
-
logger.error(f"Failed to fetch external MCP servers: {e}")
|
|
107
|
-
return []
|
|
108
|
-
|
|
14
|
+
from autocoder.common.mcp_server_types import (
|
|
15
|
+
McpRequest, McpInstallRequest, McpRemoveRequest, McpListRequest,
|
|
16
|
+
McpListRunningRequest, McpRefreshRequest, McpServerInfoRequest,
|
|
17
|
+
McpResponse, ServerInfo, InstallResult, RemoveResult, ListResult,
|
|
18
|
+
ListRunningResult, RefreshResult, QueryResult, ErrorResult, ServerConfig,StringResult,
|
|
19
|
+
ExternalServerInfo, McpExternalServer, MarketplaceAddRequest, MarketplaceAddResult,
|
|
20
|
+
MarketplaceUpdateRequest, MarketplaceUpdateResult
|
|
21
|
+
)
|
|
22
|
+
from autocoder.common.mcp_server_install import McpServerInstaller
|
|
109
23
|
|
|
110
24
|
class McpServer:
|
|
111
25
|
def __init__(self):
|
|
@@ -114,6 +28,7 @@ class McpServer:
|
|
|
114
28
|
self._running = False
|
|
115
29
|
self._task = None
|
|
116
30
|
self._loop = None
|
|
31
|
+
self._installer = McpServerInstaller()
|
|
117
32
|
|
|
118
33
|
def start(self):
|
|
119
34
|
if self._running:
|
|
@@ -135,244 +50,143 @@ class McpServer:
|
|
|
135
50
|
self._task = self._loop.create_task(self._process_request())
|
|
136
51
|
self._loop.run_forever()
|
|
137
52
|
|
|
138
|
-
def _install_python_package(self, package_name: str) -> None:
|
|
139
|
-
"""Install a Python package using pip"""
|
|
140
|
-
try:
|
|
141
|
-
import importlib
|
|
142
|
-
importlib.import_module(package_name.replace("-", "_"))
|
|
143
|
-
except ImportError:
|
|
144
|
-
import subprocess
|
|
145
|
-
try:
|
|
146
|
-
subprocess.run([sys.executable, "-m", "pip", "install", package_name], check=True)
|
|
147
|
-
except subprocess.CalledProcessError:
|
|
148
|
-
print(f"\n\033[93mFailed to automatically install {package_name}. Please manually install it using:\n")
|
|
149
|
-
print(f" pip install {package_name}\n")
|
|
150
|
-
print(f"We have already updated the server configuration in ~/.autocoder/mcp/settings.json.\n")
|
|
151
|
-
print(f"After installation, you can restart the auto-coder.chat using the server.\033[0m\n")
|
|
152
|
-
|
|
153
|
-
def _install_node_package(self, package_name: str) -> None:
|
|
154
|
-
"""Install a Node.js package using npm"""
|
|
155
|
-
import subprocess
|
|
156
|
-
try:
|
|
157
|
-
subprocess.run(["npx", package_name, "--version"], check=True)
|
|
158
|
-
except:
|
|
159
|
-
try:
|
|
160
|
-
subprocess.run(["npm", "install", "-y", "-g", package_name], check=True)
|
|
161
|
-
except subprocess.CalledProcessError:
|
|
162
|
-
print(f"\n\033[93mFailed to automatically install {package_name}. Please manually install it using:\n")
|
|
163
|
-
print(f" npm install -g {package_name}\n")
|
|
164
|
-
print(f"We have already updated the server configuration in ~/.autocoder/mcp/settings.json.\n")
|
|
165
|
-
print(f"After installation, you can restart the auto-coder.chat using the server.\033[0m\n")
|
|
166
|
-
|
|
167
|
-
def _deep_merge_dicts(self, dict1, dict2):
|
|
168
|
-
"""
|
|
169
|
-
深度合并两个字典,包括嵌套的字典
|
|
170
|
-
dict1是基础字典,dict2是优先字典(当有冲突时保留dict2的值)
|
|
171
|
-
"""
|
|
172
|
-
result = dict1.copy()
|
|
173
|
-
for key, value in dict2.items():
|
|
174
|
-
if key in result and isinstance(result[key], dict) and isinstance(value, dict):
|
|
175
|
-
# 如果两个字典都包含相同的键,并且两个值都是字典,则递归合并
|
|
176
|
-
result[key] = self._deep_merge_dicts(result[key], value)
|
|
177
|
-
else:
|
|
178
|
-
# 否则,使用dict2的值覆盖或添加到结果
|
|
179
|
-
result[key] = value
|
|
180
|
-
return result
|
|
181
|
-
|
|
182
|
-
def _parse_command_line_args(self, server_name_or_config: str) -> tuple[str, dict]:
|
|
183
|
-
"""Parse command-line style arguments into name and config"""
|
|
184
|
-
name = ""
|
|
185
|
-
config = {}
|
|
186
|
-
args = server_name_or_config.strip().split()
|
|
187
|
-
i = 0
|
|
188
|
-
while i < len(args):
|
|
189
|
-
if args[i] == "--name" and i + 1 < len(args):
|
|
190
|
-
name = args[i + 1]
|
|
191
|
-
i += 2
|
|
192
|
-
elif args[i] == "--command" and i + 1 < len(args):
|
|
193
|
-
config["command"] = args[i + 1]
|
|
194
|
-
i += 2
|
|
195
|
-
elif args[i] == "--args":
|
|
196
|
-
config["args"] = []
|
|
197
|
-
i += 1
|
|
198
|
-
while i < len(args) and not args[i].startswith("--"):
|
|
199
|
-
config["args"].append(args[i])
|
|
200
|
-
i += 1
|
|
201
|
-
elif args[i] == "--env":
|
|
202
|
-
config["env"] = {}
|
|
203
|
-
i += 1
|
|
204
|
-
while i < len(args) and not args[i].startswith("--"):
|
|
205
|
-
if "=" in args[i]:
|
|
206
|
-
key, value = args[i].split("=", 1)
|
|
207
|
-
config["env"][key] = value
|
|
208
|
-
i += 1
|
|
209
|
-
else:
|
|
210
|
-
i += 1
|
|
211
|
-
|
|
212
|
-
template_config = {}
|
|
213
|
-
|
|
214
|
-
if name in MCP_BUILD_IN_SERVERS:
|
|
215
|
-
template_config = MCP_BUILD_IN_SERVERS[name]
|
|
216
|
-
else:
|
|
217
|
-
# 查找外部server
|
|
218
|
-
external_servers = get_mcp_external_servers()
|
|
219
|
-
for s in external_servers:
|
|
220
|
-
if s.name == name:
|
|
221
|
-
if s.runtime == "python":
|
|
222
|
-
self._install_python_package(name)
|
|
223
|
-
template_config = {
|
|
224
|
-
"command": "python",
|
|
225
|
-
"args": [
|
|
226
|
-
"-m", name.replace("-", "_")
|
|
227
|
-
],
|
|
228
|
-
}
|
|
229
|
-
elif s.runtime == "node":
|
|
230
|
-
self._install_node_package(name)
|
|
231
|
-
template_config = {
|
|
232
|
-
"command": "npx",
|
|
233
|
-
"args": [
|
|
234
|
-
"-y",
|
|
235
|
-
"-g",
|
|
236
|
-
name
|
|
237
|
-
]
|
|
238
|
-
}
|
|
239
|
-
break
|
|
240
|
-
|
|
241
|
-
# 深度合并两个配置,以用户配置为主
|
|
242
|
-
config = self._deep_merge_dicts(template_config, config)
|
|
243
|
-
|
|
244
|
-
if not config.get("args") and (name.startswith("@") or config.get("command") in ["npx", "npm"]):
|
|
245
|
-
config["args"] = ["-y", "-g", name]
|
|
246
|
-
|
|
247
|
-
## 如果有模板,则无需再次安装,处理模板的时候会自动安装
|
|
248
|
-
if not template_config:
|
|
249
|
-
# Install package if needed
|
|
250
|
-
if name.startswith("@") or config.get("command") in ["npx", "npm"]:
|
|
251
|
-
self._install_node_package(name)
|
|
252
|
-
else:
|
|
253
|
-
self._install_python_package(name)
|
|
254
|
-
|
|
255
|
-
return name, config
|
|
256
|
-
|
|
257
|
-
def _parse_json_config(self, server_name_or_config: str) -> tuple[str, dict]:
|
|
258
|
-
"""Parse JSON configuration into name and config"""
|
|
259
|
-
raw_config = json.loads(server_name_or_config)
|
|
260
|
-
# 用户给了一个完整的配置
|
|
261
|
-
if "mcpServers" in raw_config:
|
|
262
|
-
raw_config = raw_config["mcpServers"]
|
|
263
|
-
|
|
264
|
-
# 取第一个server 配置
|
|
265
|
-
config = list(raw_config.values())[0]
|
|
266
|
-
name = list(raw_config.keys())[0]
|
|
267
|
-
if name.startswith("@") or config["command"] in ["npx", "npm"]:
|
|
268
|
-
for item in config["args"]:
|
|
269
|
-
if name in item:
|
|
270
|
-
self._install_node_package(item)
|
|
271
|
-
else:
|
|
272
|
-
self._install_python_package(name)
|
|
273
|
-
|
|
274
|
-
return name, config
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
async def _install_server(self, request: McpInstallRequest, hub: McpHub) -> McpResponse:
|
|
278
|
-
"""Install an MCP server with module dependency check"""
|
|
279
|
-
name = ""
|
|
280
|
-
config = {}
|
|
281
|
-
try:
|
|
282
|
-
server_name_or_config = request.server_name_or_config
|
|
283
|
-
|
|
284
|
-
# Try different parsing methods
|
|
285
|
-
if server_name_or_config.strip().startswith("--"):
|
|
286
|
-
# Command-line style arguments
|
|
287
|
-
name, config = self._parse_command_line_args(server_name_or_config)
|
|
288
|
-
else:
|
|
289
|
-
try:
|
|
290
|
-
# Try parsing as JSON
|
|
291
|
-
name, config = self._parse_json_config(server_name_or_config)
|
|
292
|
-
except json.JSONDecodeError:
|
|
293
|
-
logger.error(f"Failed to parse JSON config: {server_name_or_config}")
|
|
294
|
-
pass
|
|
295
|
-
|
|
296
|
-
if not name:
|
|
297
|
-
raise ValueError(
|
|
298
|
-
"MCP server name is not available in MCP_BUILD_IN_SERVERS or external servers")
|
|
299
|
-
|
|
300
|
-
logger.info(f"Installing MCP server: {name} with config: {config}")
|
|
301
|
-
if not config:
|
|
302
|
-
raise ValueError(f"MCP server {name} config is not available")
|
|
303
|
-
|
|
304
|
-
is_success = await hub.add_server_config(name, config)
|
|
305
|
-
if is_success:
|
|
306
|
-
return McpResponse(result=get_message_with_format("mcp_install_success", result=request.server_name_or_config))
|
|
307
|
-
else:
|
|
308
|
-
return McpResponse(result="", error=get_message_with_format("mcp_install_error", error="Failed to establish connection"))
|
|
309
|
-
except Exception as e:
|
|
310
|
-
return McpResponse(result="", error=get_message_with_format("mcp_install_error", error=str(e)))
|
|
311
|
-
|
|
312
53
|
async def _process_request(self):
|
|
313
|
-
hub = McpHub()
|
|
54
|
+
hub = McpHub()
|
|
55
|
+
|
|
56
|
+
## 可能会阻塞,当mcp settings.json 里有异常内容时
|
|
314
57
|
await hub.initialize()
|
|
315
58
|
|
|
316
59
|
while self._running:
|
|
317
|
-
try:
|
|
60
|
+
try:
|
|
318
61
|
request = await self._request_queue.get()
|
|
319
62
|
if request is None:
|
|
320
63
|
break
|
|
321
64
|
|
|
322
65
|
if isinstance(request, McpInstallRequest):
|
|
323
|
-
response = await self.
|
|
66
|
+
response = await self._installer.install_server(request, hub)
|
|
324
67
|
await self._response_queue.put(response)
|
|
325
68
|
|
|
326
69
|
elif isinstance(request, McpRemoveRequest):
|
|
327
70
|
try:
|
|
328
71
|
await hub.remove_server_config(request.server_name)
|
|
329
72
|
await self._response_queue.put(McpResponse(
|
|
330
|
-
result=get_message_with_format("mcp_remove_success", result=request.server_name)
|
|
73
|
+
result=get_message_with_format("mcp_remove_success", result=request.server_name),
|
|
74
|
+
raw_result=RemoveResult(
|
|
75
|
+
success=True,
|
|
76
|
+
server_name=request.server_name
|
|
77
|
+
)
|
|
78
|
+
))
|
|
331
79
|
except Exception as e:
|
|
332
80
|
await self._response_queue.put(McpResponse(
|
|
333
|
-
result="",
|
|
81
|
+
result="",
|
|
82
|
+
error=get_message_with_format("mcp_remove_error", error=str(e)),
|
|
83
|
+
raw_result=RemoveResult(
|
|
84
|
+
success=False,
|
|
85
|
+
server_name=request.server_name,
|
|
86
|
+
error=str(e)
|
|
87
|
+
)
|
|
88
|
+
))
|
|
334
89
|
|
|
335
90
|
elif isinstance(request, McpListRequest):
|
|
336
91
|
try:
|
|
337
|
-
# Get built-in servers
|
|
338
|
-
builtin_servers = [
|
|
339
|
-
|
|
92
|
+
# Get built-in servers
|
|
93
|
+
builtin_servers = []
|
|
94
|
+
for name, config in MCP_BUILD_IN_SERVERS.items():
|
|
95
|
+
marketplace_item = MarketplaceMCPServerItem(
|
|
96
|
+
name=name,
|
|
97
|
+
description=f"Built-in: {name}",
|
|
98
|
+
mcp_type="command",
|
|
99
|
+
command=config.get("command", ""),
|
|
100
|
+
args=config.get("args", []),
|
|
101
|
+
env=config.get("env", {})
|
|
102
|
+
)
|
|
103
|
+
builtin_servers.append(marketplace_item)
|
|
340
104
|
|
|
341
105
|
# Get external servers
|
|
342
|
-
external_servers = get_mcp_external_servers()
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
106
|
+
external_servers = self._installer.get_mcp_external_servers()
|
|
107
|
+
external_items = []
|
|
108
|
+
for server in external_servers:
|
|
109
|
+
marketplace_item = MarketplaceMCPServerItem(
|
|
110
|
+
name=server.name,
|
|
111
|
+
description=server.description,
|
|
112
|
+
mcp_type="command"
|
|
113
|
+
)
|
|
114
|
+
external_items.append(marketplace_item)
|
|
115
|
+
|
|
116
|
+
# Get marketplace items
|
|
117
|
+
marketplace_items = hub.get_marketplace_items()
|
|
118
|
+
|
|
119
|
+
# Combine results for display
|
|
120
|
+
result_sections = []
|
|
121
|
+
|
|
122
|
+
if builtin_servers:
|
|
123
|
+
builtin_title = get_message_with_format("mcp_list_builtin_title")
|
|
124
|
+
builtin_list = [f"- {item.name}" for item in builtin_servers]
|
|
125
|
+
result_sections.append(builtin_title)
|
|
126
|
+
result_sections.append("\n".join(builtin_list))
|
|
127
|
+
|
|
128
|
+
if external_items:
|
|
129
|
+
external_title = get_message_with_format("mcp_list_external_title")
|
|
130
|
+
external_list = [f"- {item.name} ({item.description})" for item in external_items]
|
|
131
|
+
result_sections.append(external_title)
|
|
132
|
+
result_sections.append("\n".join(external_list))
|
|
133
|
+
|
|
134
|
+
if marketplace_items:
|
|
135
|
+
marketplace_title = get_message_with_format("mcp_list_marketplace_title")
|
|
136
|
+
marketplace_list = [f"- {item.name} ({item.description})" for item in marketplace_items]
|
|
137
|
+
result_sections.append(marketplace_title)
|
|
138
|
+
result_sections.append("\n".join(marketplace_list))
|
|
139
|
+
|
|
140
|
+
result = "\n\n".join(result_sections)
|
|
141
|
+
|
|
142
|
+
# Create raw result with MarketplaceMCPServerItem objects
|
|
143
|
+
raw_result = ListResult(
|
|
144
|
+
builtin_servers=builtin_servers,
|
|
145
|
+
external_servers=external_items,
|
|
146
|
+
marketplace_items=marketplace_items
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
await self._response_queue.put(McpResponse(result=result, raw_result=raw_result))
|
|
351
150
|
except Exception as e:
|
|
352
151
|
await self._response_queue.put(McpResponse(
|
|
353
|
-
result="",
|
|
152
|
+
result="",
|
|
153
|
+
error=get_message_with_format("mcp_list_builtin_error", error=str(e)),
|
|
154
|
+
raw_result=ListResult(error=str(e))
|
|
155
|
+
))
|
|
354
156
|
|
|
355
157
|
elif isinstance(request, McpServerInfoRequest):
|
|
356
158
|
try:
|
|
357
159
|
llm = get_single_llm(request.model, product_mode=request.product_mode)
|
|
358
160
|
mcp_executor = McpExecutor(hub, llm)
|
|
359
161
|
result = mcp_executor.get_connected_servers_info()
|
|
360
|
-
await self._response_queue.put(McpResponse(result=result))
|
|
162
|
+
await self._response_queue.put(McpResponse(result=result, raw_result=StringResult(result=result)))
|
|
361
163
|
except Exception as e:
|
|
362
164
|
import traceback
|
|
363
165
|
traceback.print_exc()
|
|
364
166
|
await self._response_queue.put(McpResponse(
|
|
365
|
-
result="",
|
|
167
|
+
result="",
|
|
168
|
+
error=get_message_with_format("mcp_server_info_error", error=str(e)),
|
|
169
|
+
raw_result=ErrorResult(error=str(e))
|
|
170
|
+
))
|
|
366
171
|
|
|
367
172
|
elif isinstance(request, McpListRunningRequest):
|
|
368
173
|
try:
|
|
174
|
+
servers = hub.get_servers()
|
|
369
175
|
running_servers = "\n".join(
|
|
370
|
-
[f"- {server.name}" for server in
|
|
176
|
+
[f"- {server.name}" for server in servers])
|
|
371
177
|
result = running_servers if running_servers else ""
|
|
372
|
-
await self._response_queue.put(McpResponse(
|
|
178
|
+
await self._response_queue.put(McpResponse(
|
|
179
|
+
result=result,
|
|
180
|
+
raw_result=ListRunningResult(
|
|
181
|
+
servers=[ServerInfo(name=server.name) for server in servers]
|
|
182
|
+
)
|
|
183
|
+
))
|
|
373
184
|
except Exception as e:
|
|
374
185
|
await self._response_queue.put(McpResponse(
|
|
375
|
-
result="",
|
|
186
|
+
result="",
|
|
187
|
+
error=get_message_with_format("mcp_list_running_error", error=str(e)),
|
|
188
|
+
raw_result=ListRunningResult(error=str(e))
|
|
189
|
+
))
|
|
376
190
|
|
|
377
191
|
elif isinstance(request, McpRefreshRequest):
|
|
378
192
|
try:
|
|
@@ -381,35 +195,156 @@ class McpServer:
|
|
|
381
195
|
else:
|
|
382
196
|
await hub.initialize()
|
|
383
197
|
await self._response_queue.put(McpResponse(
|
|
384
|
-
result=get_message_with_format("mcp_refresh_success")
|
|
198
|
+
result=get_message_with_format("mcp_refresh_success"),
|
|
199
|
+
raw_result=RefreshResult(
|
|
200
|
+
success=True,
|
|
201
|
+
name=request.name
|
|
202
|
+
)
|
|
203
|
+
))
|
|
204
|
+
except Exception as e:
|
|
205
|
+
await self._response_queue.put(McpResponse(
|
|
206
|
+
result="",
|
|
207
|
+
error=get_message_with_format("mcp_refresh_error", error=str(e)),
|
|
208
|
+
raw_result=RefreshResult(
|
|
209
|
+
success=False,
|
|
210
|
+
name=request.name,
|
|
211
|
+
error=str(e)
|
|
212
|
+
)
|
|
213
|
+
))
|
|
214
|
+
|
|
215
|
+
elif isinstance(request, MarketplaceAddRequest):
|
|
216
|
+
try:
|
|
217
|
+
# Create a MarketplaceMCPServerItem from the request
|
|
218
|
+
item = MarketplaceMCPServerItem(
|
|
219
|
+
name=request.name,
|
|
220
|
+
description=request.description,
|
|
221
|
+
mcp_type=request.mcp_type,
|
|
222
|
+
command=request.command,
|
|
223
|
+
args=request.args or [],
|
|
224
|
+
env=request.env or {},
|
|
225
|
+
url=request.url or ""
|
|
226
|
+
)
|
|
227
|
+
|
|
228
|
+
# Add the item to the marketplace
|
|
229
|
+
success = await hub.add_marketplace_item(item)
|
|
230
|
+
|
|
231
|
+
if success:
|
|
232
|
+
await self._response_queue.put(McpResponse(
|
|
233
|
+
result=get_message_with_format("marketplace_add_success", name=request.name),
|
|
234
|
+
raw_result=MarketplaceAddResult(
|
|
235
|
+
success=True,
|
|
236
|
+
name=request.name
|
|
237
|
+
)
|
|
238
|
+
))
|
|
239
|
+
else:
|
|
240
|
+
await self._response_queue.put(McpResponse(
|
|
241
|
+
result="",
|
|
242
|
+
error=get_message_with_format("marketplace_add_error", name=request.name),
|
|
243
|
+
raw_result=MarketplaceAddResult(
|
|
244
|
+
success=False,
|
|
245
|
+
name=request.name,
|
|
246
|
+
error=f"Failed to add marketplace item: {request.name}"
|
|
247
|
+
)
|
|
248
|
+
))
|
|
385
249
|
except Exception as e:
|
|
386
250
|
await self._response_queue.put(McpResponse(
|
|
387
|
-
result="",
|
|
251
|
+
result="",
|
|
252
|
+
error=get_message_with_format("marketplace_add_error", name=request.name, error=str(e)),
|
|
253
|
+
raw_result=MarketplaceAddResult(
|
|
254
|
+
success=False,
|
|
255
|
+
name=request.name,
|
|
256
|
+
error=str(e)
|
|
257
|
+
)
|
|
258
|
+
))
|
|
259
|
+
|
|
260
|
+
elif isinstance(request, MarketplaceUpdateRequest):
|
|
261
|
+
try:
|
|
262
|
+
# Create a MarketplaceMCPServerItem from the request
|
|
263
|
+
item = MarketplaceMCPServerItem(
|
|
264
|
+
name=request.name,
|
|
265
|
+
description=request.description,
|
|
266
|
+
mcp_type=request.mcp_type,
|
|
267
|
+
command=request.command,
|
|
268
|
+
args=request.args or [],
|
|
269
|
+
env=request.env or {},
|
|
270
|
+
url=request.url or ""
|
|
271
|
+
)
|
|
272
|
+
|
|
273
|
+
# Update the item in the marketplace
|
|
274
|
+
success = await hub.update_marketplace_item(request.name, item)
|
|
275
|
+
|
|
276
|
+
if success:
|
|
277
|
+
await self._response_queue.put(McpResponse(
|
|
278
|
+
result=get_message_with_format("marketplace_update_success", name=request.name),
|
|
279
|
+
raw_result=MarketplaceUpdateResult(
|
|
280
|
+
success=True,
|
|
281
|
+
name=request.name
|
|
282
|
+
)
|
|
283
|
+
))
|
|
284
|
+
else:
|
|
285
|
+
await self._response_queue.put(McpResponse(
|
|
286
|
+
result="",
|
|
287
|
+
error=get_message_with_format("marketplace_update_error", name=request.name),
|
|
288
|
+
raw_result=MarketplaceUpdateResult(
|
|
289
|
+
success=False,
|
|
290
|
+
name=request.name,
|
|
291
|
+
error=f"Failed to update marketplace item: {request.name}"
|
|
292
|
+
)
|
|
293
|
+
))
|
|
294
|
+
except Exception as e:
|
|
295
|
+
await self._response_queue.put(McpResponse(
|
|
296
|
+
result="",
|
|
297
|
+
error=get_message_with_format("marketplace_update_error", name=request.name, error=str(e)),
|
|
298
|
+
raw_result=MarketplaceUpdateResult(
|
|
299
|
+
success=False,
|
|
300
|
+
name=request.name,
|
|
301
|
+
error=str(e)
|
|
302
|
+
)
|
|
303
|
+
))
|
|
388
304
|
|
|
389
305
|
else:
|
|
390
306
|
if not request.query.strip():
|
|
391
307
|
await self._response_queue.put(McpResponse(
|
|
392
|
-
result="",
|
|
308
|
+
result="",
|
|
309
|
+
error=get_message_with_format("mcp_query_empty"),
|
|
310
|
+
raw_result=QueryResult(
|
|
311
|
+
success=False,
|
|
312
|
+
error="Empty query"
|
|
313
|
+
)
|
|
314
|
+
))
|
|
393
315
|
continue
|
|
394
|
-
|
|
316
|
+
|
|
395
317
|
llm = get_single_llm(request.model, product_mode=request.product_mode)
|
|
396
318
|
mcp_executor = McpExecutor(hub, llm)
|
|
397
319
|
conversations = [
|
|
398
320
|
{"role": "user", "content": request.query}]
|
|
399
321
|
_, results = await mcp_executor.run(conversations)
|
|
400
|
-
|
|
322
|
+
|
|
401
323
|
if not results:
|
|
402
324
|
await self._response_queue.put(McpResponse(
|
|
403
|
-
result=get_message_with_format("mcp_error_title"),
|
|
404
|
-
error="No results"
|
|
325
|
+
result=get_message_with_format("mcp_error_title"),
|
|
326
|
+
error="No results",
|
|
327
|
+
raw_result=QueryResult(
|
|
328
|
+
success=False,
|
|
329
|
+
error="No results"
|
|
330
|
+
)
|
|
331
|
+
))
|
|
405
332
|
else:
|
|
406
333
|
results_str = "\n\n".join(
|
|
407
334
|
mcp_executor.format_mcp_result(result) for result in results)
|
|
408
335
|
await self._response_queue.put(McpResponse(
|
|
409
|
-
result=get_message_with_format("mcp_response_title") + "\n" + results_str
|
|
336
|
+
result=get_message_with_format("mcp_response_title") + "\n" + results_str,
|
|
337
|
+
raw_result=QueryResult(
|
|
338
|
+
success=True,
|
|
339
|
+
results=results
|
|
340
|
+
)
|
|
341
|
+
))
|
|
410
342
|
except Exception as e:
|
|
411
343
|
await self._response_queue.put(McpResponse(
|
|
412
|
-
result="",
|
|
344
|
+
result="",
|
|
345
|
+
error=get_message_with_format("mcp_error_title") + ": " + str(e),
|
|
346
|
+
raw_result=ErrorResult(error=str(e))
|
|
347
|
+
))
|
|
413
348
|
|
|
414
349
|
def send_request(self, request: McpRequest) -> McpResponse:
|
|
415
350
|
async def _send():
|
|
@@ -430,3 +365,5 @@ def get_mcp_server():
|
|
|
430
365
|
_mcp_server = McpServer()
|
|
431
366
|
_mcp_server.start()
|
|
432
367
|
return _mcp_server
|
|
368
|
+
|
|
369
|
+
|