jarvis-ai-assistant 0.1.193__py3-none-any.whl → 0.1.195__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.
Files changed (92) hide show
  1. jarvis/__init__.py +1 -1
  2. jarvis/jarvis_agent/__init__.py +45 -41
  3. jarvis/jarvis_agent/builtin_input_handler.py +26 -4
  4. jarvis/jarvis_agent/jarvis.py +30 -19
  5. jarvis/jarvis_agent/main.py +20 -12
  6. jarvis/jarvis_agent/output_handler.py +7 -7
  7. jarvis/jarvis_agent/shell_input_handler.py +14 -11
  8. jarvis/jarvis_code_agent/code_agent.py +81 -79
  9. jarvis/jarvis_code_agent/lint.py +92 -105
  10. jarvis/jarvis_code_analysis/checklists/__init__.py +1 -1
  11. jarvis/jarvis_code_analysis/checklists/c_cpp.py +1 -1
  12. jarvis/jarvis_code_analysis/checklists/csharp.py +1 -1
  13. jarvis/jarvis_code_analysis/checklists/data_format.py +1 -1
  14. jarvis/jarvis_code_analysis/checklists/devops.py +1 -1
  15. jarvis/jarvis_code_analysis/checklists/docs.py +1 -1
  16. jarvis/jarvis_code_analysis/checklists/go.py +1 -1
  17. jarvis/jarvis_code_analysis/checklists/infrastructure.py +1 -1
  18. jarvis/jarvis_code_analysis/checklists/java.py +1 -1
  19. jarvis/jarvis_code_analysis/checklists/javascript.py +1 -1
  20. jarvis/jarvis_code_analysis/checklists/kotlin.py +1 -1
  21. jarvis/jarvis_code_analysis/checklists/loader.py +31 -29
  22. jarvis/jarvis_code_analysis/checklists/php.py +1 -1
  23. jarvis/jarvis_code_analysis/checklists/python.py +1 -1
  24. jarvis/jarvis_code_analysis/checklists/ruby.py +1 -1
  25. jarvis/jarvis_code_analysis/checklists/rust.py +1 -1
  26. jarvis/jarvis_code_analysis/checklists/shell.py +1 -1
  27. jarvis/jarvis_code_analysis/checklists/sql.py +1 -1
  28. jarvis/jarvis_code_analysis/checklists/swift.py +1 -1
  29. jarvis/jarvis_code_analysis/checklists/web.py +1 -1
  30. jarvis/jarvis_code_analysis/code_review.py +292 -190
  31. jarvis/jarvis_dev/main.py +73 -56
  32. jarvis/jarvis_git_details/main.py +29 -33
  33. jarvis/jarvis_git_squash/main.py +13 -11
  34. jarvis/jarvis_git_utils/git_commiter.py +15 -5
  35. jarvis/jarvis_mcp/__init__.py +8 -10
  36. jarvis/jarvis_mcp/sse_mcp_client.py +182 -205
  37. jarvis/jarvis_mcp/stdio_mcp_client.py +93 -120
  38. jarvis/jarvis_mcp/streamable_mcp_client.py +117 -142
  39. jarvis/jarvis_methodology/main.py +71 -39
  40. jarvis/jarvis_multi_agent/__init__.py +24 -16
  41. jarvis/jarvis_multi_agent/main.py +10 -4
  42. jarvis/jarvis_platform/__init__.py +1 -1
  43. jarvis/jarvis_platform/base.py +44 -18
  44. jarvis/jarvis_platform/human.py +15 -3
  45. jarvis/jarvis_platform/kimi.py +117 -81
  46. jarvis/jarvis_platform/openai.py +23 -28
  47. jarvis/jarvis_platform/registry.py +43 -29
  48. jarvis/jarvis_platform/tongyi.py +16 -10
  49. jarvis/jarvis_platform/yuanbao.py +197 -144
  50. jarvis/jarvis_platform_manager/main.py +4 -2
  51. jarvis/jarvis_smart_shell/main.py +35 -30
  52. jarvis/jarvis_tools/ask_user.py +8 -16
  53. jarvis/jarvis_tools/base.py +3 -2
  54. jarvis/jarvis_tools/chdir.py +7 -19
  55. jarvis/jarvis_tools/cli/main.py +14 -10
  56. jarvis/jarvis_tools/code_plan.py +10 -31
  57. jarvis/jarvis_tools/create_code_agent.py +6 -11
  58. jarvis/jarvis_tools/create_sub_agent.py +10 -22
  59. jarvis/jarvis_tools/edit_file.py +98 -76
  60. jarvis/jarvis_tools/execute_script.py +46 -46
  61. jarvis/jarvis_tools/file_analyzer.py +22 -34
  62. jarvis/jarvis_tools/file_operation.py +69 -62
  63. jarvis/jarvis_tools/generate_new_tool.py +0 -2
  64. jarvis/jarvis_tools/methodology.py +19 -23
  65. jarvis/jarvis_tools/read_code.py +35 -35
  66. jarvis/jarvis_tools/read_webpage.py +7 -16
  67. jarvis/jarvis_tools/registry.py +63 -30
  68. jarvis/jarvis_tools/rewrite_file.py +26 -29
  69. jarvis/jarvis_tools/search_web.py +5 -8
  70. jarvis/jarvis_tools/virtual_tty.py +133 -122
  71. jarvis/jarvis_utils/__init__.py +0 -1
  72. jarvis/jarvis_utils/builtin_replace_map.py +9 -9
  73. jarvis/jarvis_utils/config.py +60 -37
  74. jarvis/jarvis_utils/embedding.py +24 -19
  75. jarvis/jarvis_utils/file_processors.py +16 -9
  76. jarvis/jarvis_utils/git_utils.py +157 -107
  77. jarvis/jarvis_utils/globals.py +1 -1
  78. jarvis/jarvis_utils/input.py +85 -52
  79. jarvis/jarvis_utils/jarvis_history.py +43 -0
  80. jarvis/jarvis_utils/methodology.py +31 -24
  81. jarvis/jarvis_utils/output.py +164 -80
  82. jarvis/jarvis_utils/tag.py +2 -1
  83. jarvis/jarvis_utils/utils.py +84 -52
  84. {jarvis_ai_assistant-0.1.193.dist-info → jarvis_ai_assistant-0.1.195.dist-info}/METADATA +362 -230
  85. jarvis_ai_assistant-0.1.195.dist-info/RECORD +98 -0
  86. jarvis/jarvis_agent/file_input_handler.py +0 -112
  87. jarvis/jarvis_event/__init__.py +0 -0
  88. jarvis_ai_assistant-0.1.193.dist-info/RECORD +0 -99
  89. {jarvis_ai_assistant-0.1.193.dist-info → jarvis_ai_assistant-0.1.195.dist-info}/WHEEL +0 -0
  90. {jarvis_ai_assistant-0.1.193.dist-info → jarvis_ai_assistant-0.1.195.dist-info}/entry_points.txt +0 -0
  91. {jarvis_ai_assistant-0.1.193.dist-info → jarvis_ai_assistant-0.1.195.dist-info}/licenses/LICENSE +0 -0
  92. {jarvis_ai_assistant-0.1.193.dist-info → jarvis_ai_assistant-0.1.195.dist-info}/top_level.txt +0 -0
@@ -13,46 +13,49 @@ from jarvis.jarvis_utils.output import OutputType, PrettyOutput
13
13
 
14
14
  class SSEMcpClient(McpClient):
15
15
  """远程MCP客户端实现
16
-
16
+
17
17
  参数:
18
18
  config: 配置字典,包含以下字段:
19
19
  - base_url: str - MCP服务器的基础URL
20
20
  - auth_token: str - 认证令牌(可选)
21
21
  - headers: Dict[str, str] - 额外的HTTP头(可选)
22
22
  """
23
+
23
24
  def __init__(self, config: Dict[str, Any]):
24
25
  self.config = config
25
- self.base_url = config.get('base_url', '')
26
+ self.base_url = config.get("base_url", "")
26
27
  if not self.base_url:
27
- raise ValueError('No base_url specified in config')
28
-
28
+ raise ValueError("No base_url specified in config")
29
+
29
30
  # 设置HTTP客户端
30
31
  self.session = requests.Session()
31
- self.session.headers.update({
32
- 'Content-Type': 'application/json',
33
- 'Accept': 'application/json',
34
- })
35
-
32
+ self.session.headers.update(
33
+ {
34
+ "Content-Type": "application/json",
35
+ "Accept": "application/json",
36
+ }
37
+ )
38
+
36
39
  # 添加认证令牌(如果提供)
37
- auth_token = config.get('auth_token')
40
+ auth_token = config.get("auth_token")
38
41
  if auth_token:
39
- self.session.headers['Authorization'] = f'Bearer {auth_token}'
40
-
42
+ self.session.headers["Authorization"] = f"Bearer {auth_token}"
43
+
41
44
  # 添加额外的HTTP头
42
- extra_headers = config.get('headers', {})
45
+ extra_headers = config.get("headers", {})
43
46
  self.session.headers.update(extra_headers)
44
-
47
+
45
48
  # SSE相关属性
46
49
  self.sse_response = None
47
50
  self.sse_thread = None
48
51
  self.messages_endpoint = None
49
- self.session_id = None # 从SSE连接获取的会话ID
52
+ self.session_id = None # 从SSE连接获取的会话ID
50
53
  self.pending_requests = {} # 存储等待响应的请求 {id: Event}
51
- self.request_results = {} # 存储请求结果 {id: result}
54
+ self.request_results = {} # 存储请求结果 {id: result}
52
55
  self.notification_handlers = {}
53
56
  self.event_lock = threading.Lock()
54
57
  self.request_id_counter = 0
55
-
58
+
56
59
  # 初始化连接
57
60
  self._initialize()
58
61
 
@@ -61,36 +64,43 @@ class SSEMcpClient(McpClient):
61
64
  try:
62
65
  # 启动SSE连接
63
66
  self._start_sse_connection()
64
-
67
+
65
68
  # 等待获取消息端点和会话ID
66
69
  start_time = time.time()
67
- while (not self.messages_endpoint or not self.session_id) and time.time() - start_time < 5:
70
+ while (
71
+ not self.messages_endpoint or not self.session_id
72
+ ) and time.time() - start_time < 5:
68
73
  time.sleep(0.1)
69
-
74
+
70
75
  if not self.messages_endpoint:
71
76
  self.messages_endpoint = "/messages" # 默认端点
72
- PrettyOutput.print(f"未获取到消息端点,使用默认值: {self.messages_endpoint}", OutputType.WARNING)
73
-
77
+ PrettyOutput.print(
78
+ f"未获取到消息端点,使用默认值: {self.messages_endpoint}",
79
+ OutputType.WARNING,
80
+ )
81
+
74
82
  if not self.session_id:
75
83
  PrettyOutput.print("未获取到会话ID", OutputType.WARNING)
76
-
84
+
77
85
  # 发送初始化请求
78
- response = self._send_request('initialize', {
79
- 'processId': None, # 远程客户端不需要进程ID
80
- 'clientInfo': {
81
- 'name': 'jarvis',
82
- 'version': '1.0.0'
86
+ response = self._send_request(
87
+ "initialize",
88
+ {
89
+ "processId": None, # 远程客户端不需要进程ID
90
+ "clientInfo": {"name": "jarvis", "version": "1.0.0"},
91
+ "capabilities": {},
92
+ "protocolVersion": "2025-03-26",
83
93
  },
84
- 'capabilities': {},
85
- 'protocolVersion': "2025-03-26"
86
- })
94
+ )
87
95
 
88
96
  # 验证服务器响应
89
- if 'result' not in response:
90
- raise RuntimeError(f"初始化失败: {response.get('error', 'Unknown error')}")
97
+ if "result" not in response:
98
+ raise RuntimeError(
99
+ f"初始化失败: {response.get('error', 'Unknown error')}"
100
+ )
91
101
 
92
102
  # 发送initialized通知
93
- self._send_notification('notifications/initialized', {})
103
+ self._send_notification("notifications/initialized", {})
94
104
 
95
105
  except Exception as e:
96
106
  PrettyOutput.print(f"MCP初始化失败: {str(e)}", OutputType.ERROR)
@@ -101,55 +111,58 @@ class SSEMcpClient(McpClient):
101
111
  try:
102
112
  # 设置SSE请求头
103
113
  sse_headers = dict(self.session.headers)
104
- sse_headers.update({
105
- 'Accept': 'text/event-stream',
106
- 'Cache-Control': 'no-cache',
107
- })
108
-
114
+ sse_headers.update(
115
+ {
116
+ "Accept": "text/event-stream",
117
+ "Cache-Control": "no-cache",
118
+ }
119
+ )
120
+
109
121
  # 建立SSE连接
110
- sse_url = urljoin(self.base_url, 'sse')
122
+ sse_url = urljoin(self.base_url, "sse")
111
123
  self.sse_response = self.session.get(
112
- sse_url,
113
- stream=True,
114
- headers=sse_headers,
115
- timeout=30
124
+ sse_url, stream=True, headers=sse_headers, timeout=30
116
125
  )
117
126
  self.sse_response.raise_for_status()
118
-
127
+
119
128
  # 启动事件处理线程
120
- self.sse_thread = threading.Thread(target=self._process_sse_events, daemon=True)
129
+ self.sse_thread = threading.Thread(
130
+ target=self._process_sse_events, daemon=True
131
+ )
121
132
  self.sse_thread.start()
122
-
133
+
123
134
  except Exception as e:
124
135
  PrettyOutput.print(f"SSE连接失败: {str(e)}", OutputType.ERROR)
125
136
  raise
126
-
137
+
127
138
  def _process_sse_events(self) -> None:
128
139
  """处理SSE事件流"""
129
140
  if not self.sse_response:
130
141
  return
131
-
142
+
132
143
  buffer = ""
133
144
  for line in self.sse_response.iter_lines(decode_unicode=True):
134
145
  if line:
135
146
  if line.startswith("data:"):
136
147
  data = line[5:].strip()
137
148
  # 检查是否包含消息端点信息
138
- if data.startswith('/'):
149
+ if data.startswith("/"):
139
150
  # 这是消息端点信息,例如 "/messages/?session_id=xyz"
140
151
  try:
141
152
  # 提取消息端点路径和会话ID
142
- url_parts = data.split('?')
153
+ url_parts = data.split("?")
143
154
  self.messages_endpoint = url_parts[0]
144
-
155
+
145
156
  # 如果有查询参数,尝试提取session_id
146
157
  if len(url_parts) > 1:
147
158
  query_string = url_parts[1]
148
159
  query_params = parse_qs(query_string)
149
- if 'session_id' in query_params:
150
- self.session_id = query_params['session_id'][0]
160
+ if "session_id" in query_params:
161
+ self.session_id = query_params["session_id"][0]
151
162
  except Exception as e:
152
- PrettyOutput.print(f"解析消息端点或会话ID失败: {e}", OutputType.WARNING)
163
+ PrettyOutput.print(
164
+ f"解析消息端点或会话ID失败: {e}", OutputType.WARNING
165
+ )
153
166
  else:
154
167
  buffer += data
155
168
  elif line.startswith(":"): # 忽略注释行
@@ -167,28 +180,28 @@ class SSEMcpClient(McpClient):
167
180
  except Exception as e:
168
181
  PrettyOutput.print(f"处理SSE事件出错: {e}", OutputType.ERROR)
169
182
  buffer = ""
170
-
183
+
171
184
  PrettyOutput.print("SSE连接已关闭", OutputType.WARNING)
172
185
 
173
186
  def _handle_sse_event(self, data: str) -> None:
174
187
  """处理单个SSE事件数据"""
175
188
  try:
176
189
  event_data = json.loads(data)
177
-
190
+
178
191
  # 检查是请求响应还是通知
179
- if 'id' in event_data:
192
+ if "id" in event_data:
180
193
  # 这是一个请求的响应
181
- req_id = event_data['id']
194
+ req_id = event_data["id"]
182
195
  with self.event_lock:
183
196
  self.request_results[req_id] = event_data
184
197
  if req_id in self.pending_requests:
185
198
  # 通知等待线程响应已到达
186
199
  self.pending_requests[req_id].set()
187
- elif 'method' in event_data:
200
+ elif "method" in event_data:
188
201
  # 这是一个通知
189
- method = event_data.get('method', '')
190
- params = event_data.get('params', {})
191
-
202
+ method = event_data.get("method", "")
203
+ params = event_data.get("params", {})
204
+
192
205
  # 调用已注册的处理器
193
206
  if method in self.notification_handlers:
194
207
  for handler in self.notification_handlers[method]:
@@ -196,8 +209,7 @@ class SSEMcpClient(McpClient):
196
209
  handler(params)
197
210
  except Exception as e:
198
211
  PrettyOutput.print(
199
- f"处理通知时出错 ({method}): {e}",
200
- OutputType.ERROR
212
+ f"处理通知时出错 ({method}): {e}", OutputType.ERROR
201
213
  )
202
214
  except json.JSONDecodeError:
203
215
  PrettyOutput.print(f"无法解析SSE事件: {data}", OutputType.WARNING)
@@ -206,7 +218,7 @@ class SSEMcpClient(McpClient):
206
218
 
207
219
  def register_notification_handler(self, method: str, handler: Callable) -> None:
208
220
  """注册通知处理器
209
-
221
+
210
222
  参数:
211
223
  method: 通知方法名
212
224
  handler: 处理通知的回调函数,接收params参数
@@ -218,7 +230,7 @@ class SSEMcpClient(McpClient):
218
230
 
219
231
  def unregister_notification_handler(self, method: str, handler: Callable) -> None:
220
232
  """注销通知处理器
221
-
233
+
222
234
  参数:
223
235
  method: 通知方法名
224
236
  handler: 要注销的处理器函数
@@ -238,88 +250,82 @@ class SSEMcpClient(McpClient):
238
250
 
239
251
  def _send_request(self, method: str, params: Dict[str, Any]) -> Dict[str, Any]:
240
252
  """发送请求到MCP服务器
241
-
253
+
242
254
  参数:
243
255
  method: 请求方法
244
256
  params: 请求参数
245
-
257
+
246
258
  返回:
247
259
  Dict[str, Any]: 响应结果
248
260
  """
249
261
  # 生成唯一请求ID
250
262
  req_id = self._get_next_request_id()
251
-
263
+
252
264
  # 创建事件标志,用于等待响应
253
265
  event = threading.Event()
254
-
266
+
255
267
  with self.event_lock:
256
268
  self.pending_requests[req_id] = event
257
-
269
+
258
270
  try:
259
271
  # 构建请求
260
272
  request = {
261
- 'jsonrpc': '2.0',
262
- 'method': method,
263
- 'params': params,
264
- 'id': req_id
273
+ "jsonrpc": "2.0",
274
+ "method": method,
275
+ "params": params,
276
+ "id": req_id,
265
277
  }
266
278
 
267
279
  # 尝试不同的请求发送方式
268
280
  if self.session_id:
269
281
  # 方法1: 使用查询参数中的session_id
270
- query_params = {'session_id': self.session_id}
282
+ query_params = {"session_id": self.session_id}
271
283
  messages_url = urljoin(self.base_url, self.messages_endpoint)
272
-
284
+
273
285
  # 尝试直接使用原始URL(不追加查询参数)
274
286
  try:
275
- post_response = self.session.post(
276
- messages_url,
277
- json=request
278
- )
287
+ post_response = self.session.post(messages_url, json=request)
279
288
  post_response.raise_for_status()
280
289
  except requests.HTTPError:
281
290
  # 如果失败,尝试添加会话ID到查询参数
282
- messages_url_with_session = f"{messages_url}?{urlencode(query_params)}"
291
+ messages_url_with_session = (
292
+ f"{messages_url}?{urlencode(query_params)}"
293
+ )
283
294
  post_response = self.session.post(
284
- messages_url_with_session,
285
- json=request
295
+ messages_url_with_session, json=request
286
296
  )
287
297
  post_response.raise_for_status()
288
298
  else:
289
299
  # 方法2: 不使用session_id
290
300
  if not self.messages_endpoint:
291
301
  self.messages_endpoint = "/messages"
292
-
302
+
293
303
  messages_url = urljoin(self.base_url, self.messages_endpoint)
294
-
304
+
295
305
  # 尝试直接使用messages端点而不带任何查询参数
296
306
  try:
297
307
  # 尝试1: 标准JSON-RPC格式
298
- post_response = self.session.post(
299
- messages_url,
300
- json=request
301
- )
308
+ post_response = self.session.post(messages_url, json=request)
302
309
  post_response.raise_for_status()
303
310
  except requests.HTTPError:
304
311
  # 尝试2: JSON字符串作为请求参数
305
312
  post_response = self.session.post(
306
- messages_url,
307
- params={'request': json.dumps(request)}
313
+ messages_url, params={"request": json.dumps(request)}
308
314
  )
309
315
  post_response.raise_for_status()
310
-
316
+
311
317
  # 等待SSE通道返回响应(最多30秒)
312
318
  if not event.wait(timeout=30):
313
319
  raise TimeoutError(f"等待响应超时: {method}")
314
-
320
+
315
321
  # 获取响应结果
316
322
  with self.event_lock:
317
323
  result = self.request_results.pop(req_id, None)
318
324
  self.pending_requests.pop(req_id, None)
319
-
325
+
320
326
  if result is None:
321
327
  raise RuntimeError(f"未收到响应: {method}")
322
-
328
+
323
329
  return result
324
330
 
325
331
  except Exception as e:
@@ -327,66 +333,58 @@ class SSEMcpClient(McpClient):
327
333
  with self.event_lock:
328
334
  self.pending_requests.pop(req_id, None)
329
335
  self.request_results.pop(req_id, None)
330
-
336
+
331
337
  PrettyOutput.print(f"发送请求失败: {str(e)}", OutputType.ERROR)
332
338
  raise
333
339
 
334
340
  def _send_notification(self, method: str, params: Dict[str, Any]) -> None:
335
341
  """发送通知到MCP服务器(不需要响应)
336
-
342
+
337
343
  参数:
338
344
  method: 通知方法
339
345
  params: 通知参数
340
346
  """
341
347
  try:
342
348
  # 构建通知
343
- notification = {
344
- 'jsonrpc': '2.0',
345
- 'method': method,
346
- 'params': params
347
- }
349
+ notification = {"jsonrpc": "2.0", "method": method, "params": params}
348
350
 
349
351
  # 尝试不同的请求发送方式,与_send_request保持一致
350
352
  if self.session_id:
351
353
  # 方法1: 使用查询参数中的session_id
352
- query_params = {'session_id': self.session_id}
353
- messages_url = urljoin(self.base_url, self.messages_endpoint or '/messages')
354
-
354
+ query_params = {"session_id": self.session_id}
355
+ messages_url = urljoin(
356
+ self.base_url, self.messages_endpoint or "/messages"
357
+ )
358
+
355
359
  # 尝试直接使用原始URL(不追加查询参数)
356
360
  try:
357
- post_response = self.session.post(
358
- messages_url,
359
- json=notification
360
- )
361
+ post_response = self.session.post(messages_url, json=notification)
361
362
  post_response.raise_for_status()
362
363
  except requests.HTTPError:
363
364
  # 如果失败,尝试添加会话ID到查询参数
364
- messages_url_with_session = f"{messages_url}?{urlencode(query_params)}"
365
+ messages_url_with_session = (
366
+ f"{messages_url}?{urlencode(query_params)}"
367
+ )
365
368
  post_response = self.session.post(
366
- messages_url_with_session,
367
- json=notification
369
+ messages_url_with_session, json=notification
368
370
  )
369
371
  post_response.raise_for_status()
370
372
  else:
371
373
  # 方法2: 不使用session_id
372
374
  if not self.messages_endpoint:
373
375
  self.messages_endpoint = "/messages"
374
-
376
+
375
377
  messages_url = urljoin(self.base_url, self.messages_endpoint)
376
-
378
+
377
379
  # 尝试直接使用messages端点而不带任何查询参数
378
380
  try:
379
381
  # 尝试1: 标准JSON-RPC格式
380
- post_response = self.session.post(
381
- messages_url,
382
- json=notification
383
- )
382
+ post_response = self.session.post(messages_url, json=notification)
384
383
  post_response.raise_for_status()
385
384
  except requests.HTTPError:
386
385
  # 尝试2: JSON字符串作为请求参数
387
386
  post_response = self.session.post(
388
- messages_url,
389
- params={'request': json.dumps(notification)}
387
+ messages_url, params={"request": json.dumps(notification)}
390
388
  )
391
389
  post_response.raise_for_status()
392
390
 
@@ -396,7 +394,7 @@ class SSEMcpClient(McpClient):
396
394
 
397
395
  def get_tool_list(self) -> List[Dict[str, Any]]:
398
396
  """获取工具列表
399
-
397
+
400
398
  返回:
401
399
  List[Dict[str, Any]]: 工具列表,每个工具包含以下字段:
402
400
  - name: str - 工具名称
@@ -404,34 +402,36 @@ class SSEMcpClient(McpClient):
404
402
  - parameters: Dict - 工具参数
405
403
  """
406
404
  try:
407
- response = self._send_request('tools/list', {})
408
- if 'result' in response and 'tools' in response['result']:
405
+ response = self._send_request("tools/list", {})
406
+ if "result" in response and "tools" in response["result"]:
409
407
  # 注意这里: 响应结构是 response['result']['tools']
410
- tools = response['result']['tools']
408
+ tools = response["result"]["tools"]
411
409
  # 将MCP协议字段转换为内部格式
412
410
  formatted_tools = []
413
411
  for tool in tools:
414
412
  # 从inputSchema中提取参数定义
415
- input_schema = tool.get('inputSchema', {})
413
+ input_schema = tool.get("inputSchema", {})
416
414
  parameters = {}
417
- if 'properties' in input_schema:
418
- parameters = input_schema['properties']
419
-
420
- formatted_tools.append({
421
- 'name': tool.get('name', ''),
422
- 'description': tool.get('description', ''),
423
- 'parameters': parameters
424
- })
415
+ if "properties" in input_schema:
416
+ parameters = input_schema["properties"]
417
+
418
+ formatted_tools.append(
419
+ {
420
+ "name": tool.get("name", ""),
421
+ "description": tool.get("description", ""),
422
+ "parameters": parameters,
423
+ }
424
+ )
425
425
  return formatted_tools
426
426
  else:
427
427
  error_msg = "获取工具列表失败"
428
- if 'error' in response:
428
+ if "error" in response:
429
429
  error_msg += f": {response['error']}"
430
- elif 'result' in response:
430
+ elif "result" in response:
431
431
  error_msg += f": 响应格式不正确 - {response['result']}"
432
432
  else:
433
433
  error_msg += ": 未知错误"
434
-
434
+
435
435
  PrettyOutput.print(error_msg, OutputType.ERROR)
436
436
  return []
437
437
  except Exception as e:
@@ -440,11 +440,11 @@ class SSEMcpClient(McpClient):
440
440
 
441
441
  def execute(self, tool_name: str, arguments: Dict[str, Any]) -> Dict[str, Any]:
442
442
  """执行工具
443
-
443
+
444
444
  参数:
445
445
  tool_name: 工具名称
446
446
  arguments: 参数字典,包含工具执行所需的参数
447
-
447
+
448
448
  返回:
449
449
  Dict[str, Any]: 执行结果,包含以下字段:
450
450
  - success: bool - 是否执行成功
@@ -452,43 +452,34 @@ class SSEMcpClient(McpClient):
452
452
  - stderr: str - 标准错误
453
453
  """
454
454
  try:
455
- response = self._send_request('tools/call', {
456
- 'name': tool_name,
457
- 'arguments': arguments
458
- })
459
- if 'result' in response:
460
- result = response['result']
455
+ response = self._send_request(
456
+ "tools/call", {"name": tool_name, "arguments": arguments}
457
+ )
458
+ if "result" in response:
459
+ result = response["result"]
461
460
  # 从content中提取输出信息
462
- stdout = ''
463
- stderr = ''
464
- for content in result.get('content', []):
465
- if content.get('type') == 'text':
466
- stdout += content.get('text', '')
467
- elif content.get('type') == 'error':
468
- stderr += content.get('text', '')
469
-
470
- return {
471
- 'success': True,
472
- 'stdout': stdout,
473
- 'stderr': stderr
474
- }
461
+ stdout = ""
462
+ stderr = ""
463
+ for content in result.get("content", []):
464
+ if content.get("type") == "text":
465
+ stdout += content.get("text", "")
466
+ elif content.get("type") == "error":
467
+ stderr += content.get("text", "")
468
+
469
+ return {"success": True, "stdout": stdout, "stderr": stderr}
475
470
  else:
476
471
  return {
477
- 'success': False,
478
- 'stdout': '',
479
- 'stderr': response.get('error', 'Unknown error')
472
+ "success": False,
473
+ "stdout": "",
474
+ "stderr": response.get("error", "Unknown error"),
480
475
  }
481
476
  except Exception as e:
482
477
  PrettyOutput.print(f"执行工具失败: {str(e)}", OutputType.ERROR)
483
- return {
484
- 'success': False,
485
- 'stdout': '',
486
- 'stderr': str(e)
487
- }
478
+ return {"success": False, "stdout": "", "stderr": str(e)}
488
479
 
489
480
  def get_resource_list(self) -> List[Dict[str, Any]]:
490
481
  """获取资源列表
491
-
482
+
492
483
  返回:
493
484
  List[Dict[str, Any]]: 资源列表,每个资源包含以下字段:
494
485
  - uri: str - 资源的唯一标识符
@@ -497,12 +488,12 @@ class SSEMcpClient(McpClient):
497
488
  - mimeType: str - 资源的MIME类型(可选)
498
489
  """
499
490
  try:
500
- response = self._send_request('resources/list', {})
501
- if 'result' in response and 'resources' in response['result']:
502
- return response['result']['resources']
491
+ response = self._send_request("resources/list", {})
492
+ if "result" in response and "resources" in response["result"]:
493
+ return response["result"]["resources"]
503
494
  else:
504
495
  error_msg = "获取资源列表失败"
505
- if 'error' in response:
496
+ if "error" in response:
506
497
  error_msg += f": {response['error']}"
507
498
  else:
508
499
  error_msg += ": 未知错误"
@@ -514,10 +505,10 @@ class SSEMcpClient(McpClient):
514
505
 
515
506
  def get_resource(self, uri: str) -> Dict[str, Any]:
516
507
  """获取指定资源的内容
517
-
508
+
518
509
  参数:
519
510
  uri: str - 资源的URI标识符
520
-
511
+
521
512
  返回:
522
513
  Dict[str, Any]: 执行结果,包含以下字段:
523
514
  - success: bool - 是否执行成功
@@ -525,51 +516,37 @@ class SSEMcpClient(McpClient):
525
516
  - stderr: str - 错误信息
526
517
  """
527
518
  try:
528
- response = self._send_request('resources/read', {
529
- 'uri': uri
530
- })
531
- if 'result' in response and 'contents' in response['result']:
532
- contents = response['result']['contents']
519
+ response = self._send_request("resources/read", {"uri": uri})
520
+ if "result" in response and "contents" in response["result"]:
521
+ contents = response["result"]["contents"]
533
522
  if contents:
534
523
  content = contents[0] # 获取第一个资源内容
535
524
  # 根据资源类型返回内容
536
- if 'text' in content:
525
+ if "text" in content:
537
526
  return {
538
- 'success': True,
539
- 'stdout': content['text'],
540
- 'stderr': ''
527
+ "success": True,
528
+ "stdout": content["text"],
529
+ "stderr": "",
541
530
  }
542
- elif 'blob' in content:
531
+ elif "blob" in content:
543
532
  return {
544
- 'success': True,
545
- 'stdout': content['blob'],
546
- 'stderr': ''
533
+ "success": True,
534
+ "stdout": content["blob"],
535
+ "stderr": "",
547
536
  }
548
- return {
549
- 'success': False,
550
- 'stdout': '',
551
- 'stderr': '资源内容为空'
552
- }
537
+ return {"success": False, "stdout": "", "stderr": "资源内容为空"}
553
538
  else:
554
539
  error_msg = "获取资源内容失败"
555
- if 'error' in response:
540
+ if "error" in response:
556
541
  error_msg += f": {response['error']}"
557
542
  else:
558
543
  error_msg += ": 未知错误"
559
544
  PrettyOutput.print(error_msg, OutputType.ERROR)
560
- return {
561
- 'success': False,
562
- 'stdout': '',
563
- 'stderr': error_msg
564
- }
545
+ return {"success": False, "stdout": "", "stderr": error_msg}
565
546
  except Exception as e:
566
547
  error_msg = f"获取资源内容失败: {str(e)}"
567
548
  PrettyOutput.print(error_msg, OutputType.ERROR)
568
- return {
569
- 'success': False,
570
- 'stdout': '',
571
- 'stderr': error_msg
572
- }
549
+ return {"success": False, "stdout": "", "stderr": error_msg}
573
550
 
574
551
  def __del__(self):
575
552
  """清理资源"""
@@ -579,14 +556,14 @@ class SSEMcpClient(McpClient):
579
556
  event.set() # 释放所有等待的请求
580
557
  self.pending_requests.clear()
581
558
  self.request_results.clear()
582
-
559
+
583
560
  # 关闭SSE响应
584
561
  if self.sse_response:
585
562
  try:
586
563
  self.sse_response.close()
587
564
  except:
588
565
  pass
589
-
566
+
590
567
  # 关闭HTTP会话
591
568
  if self.session:
592
569
  self.session.close()