hanzo-mcp 0.7.6__py3-none-any.whl → 0.8.0__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 hanzo-mcp might be problematic. Click here for more details.

Files changed (178) hide show
  1. hanzo_mcp/__init__.py +7 -1
  2. hanzo_mcp/__main__.py +1 -1
  3. hanzo_mcp/analytics/__init__.py +2 -2
  4. hanzo_mcp/analytics/posthog_analytics.py +76 -82
  5. hanzo_mcp/cli.py +31 -36
  6. hanzo_mcp/cli_enhanced.py +94 -72
  7. hanzo_mcp/cli_plugin.py +27 -17
  8. hanzo_mcp/config/__init__.py +2 -2
  9. hanzo_mcp/config/settings.py +112 -88
  10. hanzo_mcp/config/tool_config.py +32 -34
  11. hanzo_mcp/dev_server.py +66 -67
  12. hanzo_mcp/prompts/__init__.py +94 -12
  13. hanzo_mcp/prompts/enhanced_prompts.py +809 -0
  14. hanzo_mcp/prompts/example_custom_prompt.py +6 -5
  15. hanzo_mcp/prompts/project_todo_reminder.py +0 -1
  16. hanzo_mcp/prompts/tool_explorer.py +10 -7
  17. hanzo_mcp/server.py +17 -21
  18. hanzo_mcp/server_enhanced.py +15 -22
  19. hanzo_mcp/tools/__init__.py +56 -28
  20. hanzo_mcp/tools/agent/__init__.py +16 -19
  21. hanzo_mcp/tools/agent/agent.py +82 -65
  22. hanzo_mcp/tools/agent/agent_tool.py +152 -122
  23. hanzo_mcp/tools/agent/agent_tool_v1_deprecated.py +66 -62
  24. hanzo_mcp/tools/agent/clarification_protocol.py +55 -50
  25. hanzo_mcp/tools/agent/clarification_tool.py +11 -10
  26. hanzo_mcp/tools/agent/claude_cli_tool.py +21 -20
  27. hanzo_mcp/tools/agent/claude_desktop_auth.py +130 -144
  28. hanzo_mcp/tools/agent/cli_agent_base.py +59 -53
  29. hanzo_mcp/tools/agent/code_auth.py +102 -107
  30. hanzo_mcp/tools/agent/code_auth_tool.py +28 -27
  31. hanzo_mcp/tools/agent/codex_cli_tool.py +20 -19
  32. hanzo_mcp/tools/agent/critic_tool.py +86 -73
  33. hanzo_mcp/tools/agent/gemini_cli_tool.py +21 -20
  34. hanzo_mcp/tools/agent/grok_cli_tool.py +21 -20
  35. hanzo_mcp/tools/agent/iching_tool.py +404 -139
  36. hanzo_mcp/tools/agent/network_tool.py +89 -73
  37. hanzo_mcp/tools/agent/prompt.py +2 -1
  38. hanzo_mcp/tools/agent/review_tool.py +101 -98
  39. hanzo_mcp/tools/agent/swarm_alias.py +87 -0
  40. hanzo_mcp/tools/agent/swarm_tool.py +246 -161
  41. hanzo_mcp/tools/agent/swarm_tool_v1_deprecated.py +134 -92
  42. hanzo_mcp/tools/agent/tool_adapter.py +21 -11
  43. hanzo_mcp/tools/common/__init__.py +1 -1
  44. hanzo_mcp/tools/common/base.py +3 -5
  45. hanzo_mcp/tools/common/batch_tool.py +46 -39
  46. hanzo_mcp/tools/common/config_tool.py +120 -84
  47. hanzo_mcp/tools/common/context.py +1 -5
  48. hanzo_mcp/tools/common/context_fix.py +5 -3
  49. hanzo_mcp/tools/common/critic_tool.py +4 -8
  50. hanzo_mcp/tools/common/decorators.py +58 -56
  51. hanzo_mcp/tools/common/enhanced_base.py +29 -32
  52. hanzo_mcp/tools/common/fastmcp_pagination.py +91 -94
  53. hanzo_mcp/tools/common/forgiving_edit.py +91 -87
  54. hanzo_mcp/tools/common/mode.py +15 -17
  55. hanzo_mcp/tools/common/mode_loader.py +27 -24
  56. hanzo_mcp/tools/common/paginated_base.py +61 -53
  57. hanzo_mcp/tools/common/paginated_response.py +72 -79
  58. hanzo_mcp/tools/common/pagination.py +50 -53
  59. hanzo_mcp/tools/common/permissions.py +4 -4
  60. hanzo_mcp/tools/common/personality.py +186 -138
  61. hanzo_mcp/tools/common/plugin_loader.py +54 -54
  62. hanzo_mcp/tools/common/stats.py +65 -47
  63. hanzo_mcp/tools/common/test_helpers.py +31 -0
  64. hanzo_mcp/tools/common/thinking_tool.py +4 -8
  65. hanzo_mcp/tools/common/tool_disable.py +17 -12
  66. hanzo_mcp/tools/common/tool_enable.py +13 -14
  67. hanzo_mcp/tools/common/tool_list.py +36 -28
  68. hanzo_mcp/tools/common/truncate.py +23 -23
  69. hanzo_mcp/tools/config/__init__.py +4 -4
  70. hanzo_mcp/tools/config/config_tool.py +42 -29
  71. hanzo_mcp/tools/config/index_config.py +37 -34
  72. hanzo_mcp/tools/config/mode_tool.py +175 -55
  73. hanzo_mcp/tools/database/__init__.py +15 -12
  74. hanzo_mcp/tools/database/database_manager.py +77 -75
  75. hanzo_mcp/tools/database/graph.py +137 -91
  76. hanzo_mcp/tools/database/graph_add.py +30 -18
  77. hanzo_mcp/tools/database/graph_query.py +178 -102
  78. hanzo_mcp/tools/database/graph_remove.py +33 -28
  79. hanzo_mcp/tools/database/graph_search.py +97 -75
  80. hanzo_mcp/tools/database/graph_stats.py +91 -59
  81. hanzo_mcp/tools/database/sql.py +107 -79
  82. hanzo_mcp/tools/database/sql_query.py +30 -24
  83. hanzo_mcp/tools/database/sql_search.py +29 -25
  84. hanzo_mcp/tools/database/sql_stats.py +47 -35
  85. hanzo_mcp/tools/editor/neovim_command.py +25 -28
  86. hanzo_mcp/tools/editor/neovim_edit.py +21 -23
  87. hanzo_mcp/tools/editor/neovim_session.py +60 -54
  88. hanzo_mcp/tools/filesystem/__init__.py +31 -30
  89. hanzo_mcp/tools/filesystem/ast_multi_edit.py +329 -249
  90. hanzo_mcp/tools/filesystem/ast_tool.py +4 -4
  91. hanzo_mcp/tools/filesystem/base.py +1 -1
  92. hanzo_mcp/tools/filesystem/batch_search.py +316 -224
  93. hanzo_mcp/tools/filesystem/content_replace.py +4 -4
  94. hanzo_mcp/tools/filesystem/diff.py +71 -59
  95. hanzo_mcp/tools/filesystem/directory_tree.py +7 -7
  96. hanzo_mcp/tools/filesystem/directory_tree_paginated.py +49 -37
  97. hanzo_mcp/tools/filesystem/edit.py +4 -4
  98. hanzo_mcp/tools/filesystem/find.py +173 -80
  99. hanzo_mcp/tools/filesystem/find_files.py +73 -52
  100. hanzo_mcp/tools/filesystem/git_search.py +157 -104
  101. hanzo_mcp/tools/filesystem/grep.py +8 -8
  102. hanzo_mcp/tools/filesystem/multi_edit.py +4 -8
  103. hanzo_mcp/tools/filesystem/read.py +12 -10
  104. hanzo_mcp/tools/filesystem/rules_tool.py +59 -43
  105. hanzo_mcp/tools/filesystem/search_tool.py +263 -207
  106. hanzo_mcp/tools/filesystem/symbols_tool.py +94 -54
  107. hanzo_mcp/tools/filesystem/tree.py +35 -33
  108. hanzo_mcp/tools/filesystem/unix_aliases.py +13 -18
  109. hanzo_mcp/tools/filesystem/watch.py +37 -36
  110. hanzo_mcp/tools/filesystem/write.py +4 -8
  111. hanzo_mcp/tools/jupyter/__init__.py +4 -4
  112. hanzo_mcp/tools/jupyter/base.py +4 -5
  113. hanzo_mcp/tools/jupyter/jupyter.py +67 -47
  114. hanzo_mcp/tools/jupyter/notebook_edit.py +4 -4
  115. hanzo_mcp/tools/jupyter/notebook_read.py +4 -7
  116. hanzo_mcp/tools/llm/__init__.py +5 -7
  117. hanzo_mcp/tools/llm/consensus_tool.py +72 -52
  118. hanzo_mcp/tools/llm/llm_manage.py +101 -60
  119. hanzo_mcp/tools/llm/llm_tool.py +226 -166
  120. hanzo_mcp/tools/llm/provider_tools.py +25 -26
  121. hanzo_mcp/tools/lsp/__init__.py +1 -1
  122. hanzo_mcp/tools/lsp/lsp_tool.py +228 -143
  123. hanzo_mcp/tools/mcp/__init__.py +2 -3
  124. hanzo_mcp/tools/mcp/mcp_add.py +27 -25
  125. hanzo_mcp/tools/mcp/mcp_remove.py +7 -8
  126. hanzo_mcp/tools/mcp/mcp_stats.py +23 -22
  127. hanzo_mcp/tools/mcp/mcp_tool.py +129 -98
  128. hanzo_mcp/tools/memory/__init__.py +39 -21
  129. hanzo_mcp/tools/memory/knowledge_tools.py +124 -99
  130. hanzo_mcp/tools/memory/memory_tools.py +90 -108
  131. hanzo_mcp/tools/search/__init__.py +7 -2
  132. hanzo_mcp/tools/search/find_tool.py +297 -212
  133. hanzo_mcp/tools/search/unified_search.py +366 -314
  134. hanzo_mcp/tools/shell/__init__.py +8 -7
  135. hanzo_mcp/tools/shell/auto_background.py +56 -49
  136. hanzo_mcp/tools/shell/base.py +1 -1
  137. hanzo_mcp/tools/shell/base_process.py +75 -75
  138. hanzo_mcp/tools/shell/bash_session.py +2 -2
  139. hanzo_mcp/tools/shell/bash_session_executor.py +4 -4
  140. hanzo_mcp/tools/shell/bash_tool.py +24 -31
  141. hanzo_mcp/tools/shell/command_executor.py +12 -12
  142. hanzo_mcp/tools/shell/logs.py +43 -33
  143. hanzo_mcp/tools/shell/npx.py +13 -13
  144. hanzo_mcp/tools/shell/npx_background.py +24 -21
  145. hanzo_mcp/tools/shell/npx_tool.py +18 -22
  146. hanzo_mcp/tools/shell/open.py +19 -21
  147. hanzo_mcp/tools/shell/pkill.py +31 -26
  148. hanzo_mcp/tools/shell/process_tool.py +32 -32
  149. hanzo_mcp/tools/shell/processes.py +57 -58
  150. hanzo_mcp/tools/shell/run_background.py +24 -25
  151. hanzo_mcp/tools/shell/run_command.py +5 -5
  152. hanzo_mcp/tools/shell/run_command_windows.py +5 -5
  153. hanzo_mcp/tools/shell/session_storage.py +3 -3
  154. hanzo_mcp/tools/shell/streaming_command.py +141 -126
  155. hanzo_mcp/tools/shell/uvx.py +24 -25
  156. hanzo_mcp/tools/shell/uvx_background.py +35 -33
  157. hanzo_mcp/tools/shell/uvx_tool.py +18 -22
  158. hanzo_mcp/tools/todo/__init__.py +6 -2
  159. hanzo_mcp/tools/todo/todo.py +50 -37
  160. hanzo_mcp/tools/todo/todo_read.py +5 -8
  161. hanzo_mcp/tools/todo/todo_write.py +5 -7
  162. hanzo_mcp/tools/vector/__init__.py +40 -28
  163. hanzo_mcp/tools/vector/ast_analyzer.py +176 -143
  164. hanzo_mcp/tools/vector/git_ingester.py +170 -179
  165. hanzo_mcp/tools/vector/index_tool.py +96 -44
  166. hanzo_mcp/tools/vector/infinity_store.py +283 -228
  167. hanzo_mcp/tools/vector/mock_infinity.py +39 -40
  168. hanzo_mcp/tools/vector/project_manager.py +88 -78
  169. hanzo_mcp/tools/vector/vector.py +59 -42
  170. hanzo_mcp/tools/vector/vector_index.py +30 -27
  171. hanzo_mcp/tools/vector/vector_search.py +64 -45
  172. hanzo_mcp/types.py +6 -4
  173. {hanzo_mcp-0.7.6.dist-info → hanzo_mcp-0.8.0.dist-info}/METADATA +1 -1
  174. hanzo_mcp-0.8.0.dist-info/RECORD +185 -0
  175. hanzo_mcp-0.7.6.dist-info/RECORD +0 -182
  176. {hanzo_mcp-0.7.6.dist-info → hanzo_mcp-0.8.0.dist-info}/WHEEL +0 -0
  177. {hanzo_mcp-0.7.6.dist-info → hanzo_mcp-0.8.0.dist-info}/entry_points.txt +0 -0
  178. {hanzo_mcp-0.7.6.dist-info → hanzo_mcp-0.8.0.dist-info}/top_level.txt +0 -0
@@ -4,42 +4,46 @@ This module provides the AgentTool for delegating tasks to sub-agents,
4
4
  supporting both one-off and long-running RPC modes, including A2A communication.
5
5
  """
6
6
 
7
- import asyncio
8
- import json
9
7
  import re
8
+ import json
10
9
  import time
11
10
  import uuid
12
- from typing import Annotated, TypedDict, Unpack, final, override, Optional, Dict, Any, List
13
- from collections.abc import Iterable
11
+ import asyncio
14
12
 
15
13
  # Import litellm with warnings suppressed
16
14
  import warnings
15
+ from typing import (
16
+ Any,
17
+ Dict,
18
+ List,
19
+ Unpack,
20
+ Optional,
21
+ Annotated,
22
+ TypedDict,
23
+ final,
24
+ override,
25
+ )
26
+
17
27
  with warnings.catch_warnings():
18
28
  warnings.simplefilter("ignore", DeprecationWarning)
19
- import litellm
20
- from mcp.server.fastmcp import Context as MCPContext
21
- from openai.types.chat import ChatCompletionMessageParam, ChatCompletionToolParam
22
29
  from pydantic import Field
30
+ from openai.types.chat import ChatCompletionMessageParam
31
+ from mcp.server.fastmcp import Context as MCPContext
23
32
 
33
+ from hanzo_mcp.tools.jupyter import get_read_only_jupyter_tools
34
+ from hanzo_mcp.tools.filesystem import get_read_only_filesystem_tools
35
+ from hanzo_mcp.tools.common.base import BaseTool
24
36
  from hanzo_mcp.tools.agent.prompt import (
25
- get_allowed_agent_tools,
26
37
  get_default_model,
27
- get_model_parameters,
28
38
  get_system_prompt,
39
+ get_allowed_agent_tools,
29
40
  )
30
- from hanzo_mcp.tools.agent.tool_adapter import (
31
- convert_tools_to_openai_functions,
32
- )
33
- from hanzo_mcp.tools.common.base import BaseTool
34
- from hanzo_mcp.tools.common.batch_tool import BatchTool
35
41
  from hanzo_mcp.tools.common.context import (
36
42
  ToolContext,
37
43
  create_tool_context,
38
44
  )
45
+ from hanzo_mcp.tools.common.batch_tool import BatchTool
39
46
  from hanzo_mcp.tools.common.permissions import PermissionManager
40
- from hanzo_mcp.tools.filesystem import get_read_only_filesystem_tools
41
- from hanzo_mcp.tools.jupyter import get_read_only_jupyter_tools
42
-
43
47
 
44
48
  # Parameter types
45
49
  Action = Annotated[
@@ -101,6 +105,7 @@ Model = Annotated[
101
105
 
102
106
  class AgentParams(TypedDict, total=False):
103
107
  """Parameters for agent tool."""
108
+
104
109
  action: str
105
110
  prompts: Optional[str | List[str]]
106
111
  mode: str
@@ -112,8 +117,10 @@ class AgentParams(TypedDict, total=False):
112
117
 
113
118
  class RPCAgent:
114
119
  """Long-running RPC agent."""
115
-
116
- def __init__(self, agent_id: str, model: str, system_prompt: str, tools: List[BaseTool]):
120
+
121
+ def __init__(
122
+ self, agent_id: str, model: str, system_prompt: str, tools: List[BaseTool]
123
+ ):
117
124
  self.agent_id = agent_id
118
125
  self.model = model
119
126
  self.system_prompt = system_prompt
@@ -124,12 +131,14 @@ class RPCAgent:
124
131
  self.created_at = time.time()
125
132
  self.last_used = time.time()
126
133
  self.call_count = 0
127
-
128
- async def call_method(self, method: str, args: Dict[str, Any], tool_ctx: ToolContext) -> str:
134
+
135
+ async def call_method(
136
+ self, method: str, args: Dict[str, Any], tool_ctx: ToolContext
137
+ ) -> str:
129
138
  """Call a method on the RPC agent."""
130
139
  self.last_used = time.time()
131
140
  self.call_count += 1
132
-
141
+
133
142
  # Build prompt based on method
134
143
  if method == "search":
135
144
  prompt = f"Search for: {args.get('query', 'unknown')}"
@@ -140,22 +149,22 @@ class RPCAgent:
140
149
  else:
141
150
  # Generic method call
142
151
  prompt = f"Method: {method}, Args: {json.dumps(args)}"
143
-
152
+
144
153
  # Add to conversation
145
154
  self.messages.append({"role": "user", "content": prompt})
146
-
155
+
147
156
  # Get response
148
157
  # (simplified - would integrate with full agent execution logic)
149
158
  response = f"Executed {method} with args {args}"
150
159
  self.messages.append({"role": "assistant", "content": response})
151
-
160
+
152
161
  return response
153
162
 
154
163
 
155
164
  @final
156
165
  class AgentTool(BaseTool):
157
166
  """Unified agent tool with one-off and RPC modes."""
158
-
167
+
159
168
  def __init__(
160
169
  self,
161
170
  permission_manager: PermissionManager,
@@ -174,10 +183,10 @@ class AgentTool(BaseTool):
174
183
  self.max_tokens_override = max_tokens
175
184
  self.max_iterations = max_iterations
176
185
  self.max_tool_uses = max_tool_uses
177
-
186
+
178
187
  # RPC agent registry
179
188
  self._rpc_agents: Dict[str, RPCAgent] = {}
180
-
189
+
181
190
  # Available tools
182
191
  self.available_tools: list[BaseTool] = []
183
192
  self.available_tools.extend(
@@ -201,8 +210,8 @@ class AgentTool(BaseTool):
201
210
  def description(self) -> str:
202
211
  """Get the tool description."""
203
212
  tools = [t.name for t in self.available_tools]
204
-
205
- return f"""AI agents with tools: {', '.join(tools)}. Actions: run (default), start, call, stop, list.
213
+
214
+ return f"""AI agents with tools: {", ".join(tools)}. Actions: run (default), start, call, stop, list.
206
215
 
207
216
  Usage:
208
217
  agent "Search for config files in /project"
@@ -223,10 +232,10 @@ Modes:
223
232
  """Execute agent operation."""
224
233
  tool_ctx = create_tool_context(ctx)
225
234
  await tool_ctx.set_tool_info(self.name)
226
-
235
+
227
236
  # Extract action
228
237
  action = params.get("action", "run")
229
-
238
+
230
239
  # Route to appropriate handler
231
240
  if action == "run":
232
241
  return await self._handle_run(params, tool_ctx)
@@ -246,30 +255,34 @@ Modes:
246
255
  prompts = params.get("prompts")
247
256
  if not prompts:
248
257
  return "Error: prompts required for run action"
249
-
258
+
250
259
  # Convert to list
251
260
  if isinstance(prompts, str):
252
261
  prompt_list = [prompts]
253
262
  else:
254
263
  prompt_list = prompts
255
-
264
+
256
265
  # Validate prompts
257
266
  for prompt in prompt_list:
258
267
  if not self._validate_prompt(prompt):
259
268
  return f"Error: Prompt must contain absolute paths starting with /: {prompt[:50]}..."
260
-
269
+
261
270
  # Execute agents
262
271
  start_time = time.time()
263
-
272
+
264
273
  if len(prompt_list) == 1:
265
274
  await tool_ctx.info("Launching agent")
266
- result = await self._execute_agent(prompt_list[0], params.get("model"), tool_ctx)
275
+ result = await self._execute_agent(
276
+ prompt_list[0], params.get("model"), tool_ctx
277
+ )
267
278
  else:
268
279
  await tool_ctx.info(f"Launching {len(prompt_list)} agents in parallel")
269
- result = await self._execute_multiple_agents(prompt_list, params.get("model"), tool_ctx)
270
-
280
+ result = await self._execute_multiple_agents(
281
+ prompt_list, params.get("model"), tool_ctx
282
+ )
283
+
271
284
  execution_time = time.time() - start_time
272
-
285
+
273
286
  return f"""Agent execution completed in {execution_time:.2f} seconds.
274
287
 
275
288
  AGENT RESPONSE:
@@ -280,31 +293,31 @@ AGENT RESPONSE:
280
293
  mode = params.get("mode", "oneoff")
281
294
  if mode != "rpc":
282
295
  return "Error: start action only valid for rpc mode"
283
-
296
+
284
297
  # Generate agent ID
285
298
  agent_id = str(uuid.uuid4())[:8]
286
-
299
+
287
300
  # Get model
288
301
  model = params.get("model") or get_default_model(self.model_override)
289
-
302
+
290
303
  # Get available tools
291
304
  agent_tools = get_allowed_agent_tools(
292
305
  self.available_tools,
293
306
  self.permission_manager,
294
307
  )
295
-
308
+
296
309
  # Create system prompt
297
310
  system_prompt = get_system_prompt(
298
311
  agent_tools,
299
312
  self.permission_manager,
300
313
  )
301
-
314
+
302
315
  # Create RPC agent
303
316
  agent = RPCAgent(agent_id, model, system_prompt, agent_tools)
304
317
  self._rpc_agents[agent_id] = agent
305
-
318
+
306
319
  await tool_ctx.info(f"Started RPC agent {agent_id} with model {model}")
307
-
320
+
308
321
  return f"""Started RPC agent:
309
322
  - ID: {agent_id}
310
323
  - Model: {model}
@@ -317,20 +330,20 @@ Use 'agent --action call --agent-id {agent_id} --method <method> --args <args>'
317
330
  agent_id = params.get("agent_id")
318
331
  if not agent_id:
319
332
  return "Error: agent_id required for call action"
320
-
333
+
321
334
  if agent_id not in self._rpc_agents:
322
335
  return f"Error: Agent {agent_id} not found. Use 'agent --action list' to see active agents."
323
-
336
+
324
337
  method = params.get("method")
325
338
  if not method:
326
339
  return "Error: method required for call action"
327
-
340
+
328
341
  args = params.get("args", {})
329
-
342
+
330
343
  # Call agent method
331
344
  agent = self._rpc_agents[agent_id]
332
345
  await tool_ctx.info(f"Calling {method} on agent {agent_id}")
333
-
346
+
334
347
  try:
335
348
  result = await agent.call_method(method, args, tool_ctx)
336
349
  return f"Agent {agent_id} response:\n{result}"
@@ -343,13 +356,13 @@ Use 'agent --action call --agent-id {agent_id} --method <method> --args <args>'
343
356
  agent_id = params.get("agent_id")
344
357
  if not agent_id:
345
358
  return "Error: agent_id required for stop action"
346
-
359
+
347
360
  if agent_id not in self._rpc_agents:
348
361
  return f"Error: Agent {agent_id} not found"
349
-
362
+
350
363
  agent = self._rpc_agents.pop(agent_id)
351
364
  await tool_ctx.info(f"Stopped agent {agent_id}")
352
-
365
+
353
366
  return f"""Stopped agent {agent_id}:
354
367
  - Runtime: {time.time() - agent.created_at:.2f} seconds
355
368
  - Calls: {agent.call_count}"""
@@ -358,7 +371,7 @@ Use 'agent --action call --agent-id {agent_id} --method <method> --args <args>'
358
371
  """List active RPC agents."""
359
372
  if not self._rpc_agents:
360
373
  return "No active RPC agents"
361
-
374
+
362
375
  output = ["=== Active RPC Agents ==="]
363
376
  for agent_id, agent in self._rpc_agents.items():
364
377
  runtime = time.time() - agent.created_at
@@ -368,7 +381,7 @@ Use 'agent --action call --agent-id {agent_id} --method <method> --args <args>'
368
381
  output.append(f" Runtime: {runtime:.2f}s")
369
382
  output.append(f" Idle: {idle:.2f}s")
370
383
  output.append(f" Calls: {agent.call_count}")
371
-
384
+
372
385
  return "\n".join(output)
373
386
 
374
387
  def _validate_prompt(self, prompt: str) -> bool:
@@ -376,30 +389,34 @@ Use 'agent --action call --agent-id {agent_id} --method <method> --args <args>'
376
389
  absolute_path_pattern = r"/(?:[^/\s]+/)*[^/\s]+"
377
390
  return bool(re.search(absolute_path_pattern, prompt))
378
391
 
379
- async def _execute_agent(self, prompt: str, model: Optional[str], tool_ctx: ToolContext) -> str:
392
+ async def _execute_agent(
393
+ self, prompt: str, model: Optional[str], tool_ctx: ToolContext
394
+ ) -> str:
380
395
  """Execute a single agent (simplified - would use full logic from agent_tool.py)."""
381
396
  # This would integrate the full agent execution logic from agent_tool.py
382
397
  # For now, return a placeholder
383
398
  return f"Executed agent with prompt: {prompt[:100]}..."
384
399
 
385
- async def _execute_multiple_agents(self, prompts: List[str], model: Optional[str], tool_ctx: ToolContext) -> str:
400
+ async def _execute_multiple_agents(
401
+ self, prompts: List[str], model: Optional[str], tool_ctx: ToolContext
402
+ ) -> str:
386
403
  """Execute multiple agents in parallel."""
387
404
  tasks = []
388
405
  for prompt in prompts:
389
406
  task = self._execute_agent(prompt, model, tool_ctx)
390
407
  tasks.append(task)
391
-
408
+
392
409
  results = await asyncio.gather(*tasks, return_exceptions=True)
393
-
410
+
394
411
  formatted_results = []
395
412
  for i, result in enumerate(results):
396
413
  if isinstance(result, Exception):
397
- formatted_results.append(f"Agent {i+1} Error:\n{str(result)}")
414
+ formatted_results.append(f"Agent {i + 1} Error:\n{str(result)}")
398
415
  else:
399
- formatted_results.append(f"Agent {i+1} Result:\n{result}")
400
-
416
+ formatted_results.append(f"Agent {i + 1} Result:\n{result}")
417
+
401
418
  return "\n\n---\n\n".join(formatted_results)
402
419
 
403
420
  def register(self, mcp_server) -> None:
404
421
  """Register this tool with the MCP server."""
405
- pass
422
+ pass