hanzo-mcp 0.7.7__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 +6 -0
  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.7.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.7.dist-info/RECORD +0 -182
  176. {hanzo_mcp-0.7.7.dist-info → hanzo_mcp-0.8.0.dist-info}/WHEEL +0 -0
  177. {hanzo_mcp-0.7.7.dist-info → hanzo_mcp-0.8.0.dist-info}/entry_points.txt +0 -0
  178. {hanzo_mcp-0.7.7.dist-info → hanzo_mcp-0.8.0.dist-info}/top_level.txt +0 -0
@@ -4,81 +4,107 @@ This module implements the AgentTool that leverages the hanzo-agents SDK
4
4
  for sophisticated agent orchestration and execution.
5
5
  """
6
6
 
7
- import asyncio
8
- import json
9
- import os
10
7
  import re
11
8
  import time
12
- from typing import Annotated, TypedDict, Unpack, final, override, Optional, Dict, Any, List
9
+ from typing import (
10
+ Any,
11
+ Dict,
12
+ List,
13
+ Unpack,
14
+ Optional,
15
+ TypedDict,
16
+ final,
17
+ override,
18
+ )
13
19
 
14
- from mcp.server.fastmcp import Context as MCPContext
15
20
  from mcp.server import FastMCP
16
- from pydantic import Field
21
+ from mcp.server.fastmcp import Context as MCPContext
17
22
 
18
23
  # Import hanzo-agents SDK
19
24
  try:
20
25
  from hanzo_agents import (
21
- Agent, State, Network, Tool, History,
22
- ModelRegistry, InferenceResult, ToolCall,
23
- create_memory_kv, create_memory_vector,
24
- sequential_router, state_based_router,
26
+ Tool,
27
+ Agent,
28
+ State,
29
+ History,
30
+ Network,
31
+ InferenceResult,
32
+ create_memory_kv,
33
+ sequential_router,
34
+ create_memory_vector,
25
35
  )
26
36
  from hanzo_agents.core.cli_agent import (
27
- ClaudeCodeAgent, OpenAICodexAgent,
28
- GeminiAgent, GrokAgent
37
+ GrokAgent,
38
+ GeminiAgent,
39
+ ClaudeCodeAgent,
40
+ OpenAICodexAgent,
29
41
  )
42
+
30
43
  HANZO_AGENTS_AVAILABLE = True
31
44
  except ImportError:
32
45
  HANZO_AGENTS_AVAILABLE = False
46
+
33
47
  # Define stub classes when hanzo-agents is not available
34
48
  class State:
35
49
  """Stub State class when hanzo-agents is not available."""
50
+
36
51
  def __init__(self):
37
52
  pass
53
+
38
54
  def to_dict(self):
39
55
  return {}
56
+
40
57
  @classmethod
41
58
  def from_dict(cls, data):
42
59
  return cls()
43
-
60
+
44
61
  class Tool:
45
62
  """Stub Tool class when hanzo-agents is not available."""
63
+
46
64
  pass
47
-
65
+
48
66
  class Agent:
49
67
  """Stub Agent class when hanzo-agents is not available."""
68
+
50
69
  pass
51
-
70
+
52
71
  class Network:
53
72
  """Stub Network class when hanzo-agents is not available."""
73
+
54
74
  pass
55
-
75
+
56
76
  class History:
57
77
  """Stub History class when hanzo-agents is not available."""
78
+
58
79
  pass
59
-
80
+
60
81
  class InferenceResult:
61
82
  """Stub InferenceResult class when hanzo-agents is not available."""
83
+
62
84
  def __init__(self, agent=None, content=None, metadata=None):
63
85
  self.agent = agent
64
86
  self.content = content
65
87
  self.metadata = metadata or {}
66
88
 
67
- from hanzo_mcp.tools.common.base import BaseTool
68
- from hanzo_mcp.tools.common.context import ToolContext, create_tool_context
69
- from hanzo_mcp.tools.common.permissions import PermissionManager
70
- from hanzo_mcp.tools.filesystem import get_read_only_filesystem_tools, Edit, MultiEdit
89
+
71
90
  from hanzo_mcp.tools.jupyter import get_read_only_jupyter_tools
72
- from hanzo_mcp.tools.common.batch_tool import BatchTool
73
- from hanzo_mcp.tools.agent.clarification_protocol import AgentClarificationMixin, ClarificationType
74
- from hanzo_mcp.tools.agent.clarification_tool import ClarificationTool
91
+ from hanzo_mcp.tools.filesystem import Edit, MultiEdit, get_read_only_filesystem_tools
92
+ from hanzo_mcp.tools.common.base import BaseTool
93
+ from hanzo_mcp.tools.common.context import create_tool_context
75
94
  from hanzo_mcp.tools.agent.critic_tool import CriticTool
76
- from hanzo_mcp.tools.agent.review_tool import ReviewTool
77
95
  from hanzo_mcp.tools.agent.iching_tool import IChingTool
96
+ from hanzo_mcp.tools.agent.review_tool import ReviewTool
97
+ from hanzo_mcp.tools.common.batch_tool import BatchTool
98
+ from hanzo_mcp.tools.common.permissions import PermissionManager
99
+ from hanzo_mcp.tools.agent.clarification_tool import ClarificationTool
100
+ from hanzo_mcp.tools.agent.clarification_protocol import (
101
+ AgentClarificationMixin,
102
+ )
78
103
 
79
104
 
80
105
  class AgentToolParams(TypedDict, total=False):
81
106
  """Parameters for the AgentTool."""
107
+
82
108
  prompts: str | list[str]
83
109
  model: Optional[str]
84
110
  use_memory: Optional[bool]
@@ -87,7 +113,7 @@ class AgentToolParams(TypedDict, total=False):
87
113
 
88
114
  class MCPAgentState(State):
89
115
  """State for MCP agents."""
90
-
116
+
91
117
  def __init__(self, prompts: List[str], context: Dict[str, Any]):
92
118
  """Initialize agent state."""
93
119
  super().__init__()
@@ -95,25 +121,24 @@ class MCPAgentState(State):
95
121
  self.context = context
96
122
  self.current_prompt_index = 0
97
123
  self.results = []
98
-
124
+
99
125
  def to_dict(self) -> Dict[str, Any]:
100
126
  """Convert to dictionary."""
101
127
  base_dict = super().to_dict()
102
- base_dict.update({
103
- "prompts": self.prompts,
104
- "context": self.context,
105
- "current_prompt_index": self.current_prompt_index,
106
- "results": self.results
107
- })
128
+ base_dict.update(
129
+ {
130
+ "prompts": self.prompts,
131
+ "context": self.context,
132
+ "current_prompt_index": self.current_prompt_index,
133
+ "results": self.results,
134
+ }
135
+ )
108
136
  return base_dict
109
-
137
+
110
138
  @classmethod
111
139
  def from_dict(cls, data: Dict[str, Any]) -> "MCPAgentState":
112
140
  """Create from dictionary."""
113
- state = cls(
114
- prompts=data.get("prompts", []),
115
- context=data.get("context", {})
116
- )
141
+ state = cls(prompts=data.get("prompts", []), context=data.get("context", {}))
117
142
  state.current_prompt_index = data.get("current_prompt_index", 0)
118
143
  state.results = data.get("results", [])
119
144
  for k, v in data.items():
@@ -124,22 +149,22 @@ class MCPAgentState(State):
124
149
 
125
150
  class MCPToolAdapter(Tool):
126
151
  """Adapter to wrap MCP tools for hanzo-agents."""
127
-
152
+
128
153
  def __init__(self, mcp_tool: BaseTool, ctx: MCPContext):
129
154
  """Initialize adapter."""
130
155
  self.mcp_tool = mcp_tool
131
156
  self.ctx = ctx
132
-
157
+
133
158
  @property
134
159
  def name(self) -> str:
135
160
  """Get tool name."""
136
161
  return self.mcp_tool.name
137
-
162
+
138
163
  @property
139
164
  def description(self) -> str:
140
165
  """Get tool description."""
141
166
  return self.mcp_tool.description
142
-
167
+
143
168
  async def execute(self, **kwargs) -> str:
144
169
  """Execute the MCP tool."""
145
170
  return await self.mcp_tool.call(self.ctx, **kwargs)
@@ -147,84 +172,83 @@ class MCPToolAdapter(Tool):
147
172
 
148
173
  class MCPAgent(Agent):
149
174
  """Agent that executes MCP tasks."""
150
-
175
+
151
176
  name = "mcp_agent"
152
177
  description = "Agent for executing MCP tasks"
153
-
154
- def __init__(self,
155
- available_tools: List[BaseTool],
156
- permission_manager: PermissionManager,
157
- ctx: MCPContext,
158
- model: str = "model://anthropic/claude-3-5-sonnet-20241022",
159
- **kwargs):
178
+
179
+ def __init__(
180
+ self,
181
+ available_tools: List[BaseTool],
182
+ permission_manager: PermissionManager,
183
+ ctx: MCPContext,
184
+ model: str = "model://anthropic/claude-3-5-sonnet-20241022",
185
+ **kwargs,
186
+ ):
160
187
  """Initialize MCP agent."""
161
188
  super().__init__(model=model, **kwargs)
162
-
189
+
163
190
  self.available_tools = available_tools
164
191
  self.permission_manager = permission_manager
165
192
  self.ctx = ctx
166
-
193
+
167
194
  # Register MCP tools as agent tools
168
195
  for mcp_tool in available_tools:
169
196
  adapter = MCPToolAdapter(mcp_tool, ctx)
170
197
  self.register_tool(adapter)
171
-
172
- async def run(self, state: MCPAgentState, history: History, network: Network) -> InferenceResult:
198
+
199
+ async def run(
200
+ self, state: MCPAgentState, history: History, network: Network
201
+ ) -> InferenceResult:
173
202
  """Execute the agent."""
174
203
  # Get current prompt
175
204
  if state.current_prompt_index >= len(state.prompts):
176
205
  return InferenceResult(
177
206
  agent=self.name,
178
207
  content="All prompts completed",
179
- metadata={"completed": True}
208
+ metadata={"completed": True},
180
209
  )
181
-
210
+
182
211
  prompt = state.prompts[state.current_prompt_index]
183
-
212
+
184
213
  # Execute with tools
185
214
  messages = [
186
215
  {"role": "system", "content": self._get_system_prompt()},
187
- {"role": "user", "content": prompt}
216
+ {"role": "user", "content": prompt},
188
217
  ]
189
-
218
+
190
219
  # Add history context
191
220
  for entry in history[-10:]:
192
221
  if entry.role == "assistant":
193
- messages.append({
194
- "role": "assistant",
195
- "content": entry.content
196
- })
222
+ messages.append({"role": "assistant", "content": entry.content})
197
223
  elif entry.role == "user":
198
- messages.append({
199
- "role": "user",
200
- "content": entry.content
201
- })
202
-
224
+ messages.append({"role": "user", "content": entry.content})
225
+
203
226
  # Call model
204
227
  from hanzo_agents import ModelRegistry
228
+
205
229
  adapter = ModelRegistry.get_adapter(self.model)
206
230
  response = await adapter.chat(messages)
207
-
231
+
208
232
  # Update state
209
233
  state.current_prompt_index += 1
210
234
  state.results.append(response)
211
-
235
+
212
236
  # Return result
213
237
  return InferenceResult(
214
238
  agent=self.name,
215
239
  content=response,
216
240
  metadata={
217
241
  "prompt_index": state.current_prompt_index - 1,
218
- "total_prompts": len(state.prompts)
219
- }
242
+ "total_prompts": len(state.prompts),
243
+ },
220
244
  )
221
-
245
+
222
246
  def _get_system_prompt(self) -> str:
223
247
  """Get system prompt for the agent."""
224
248
  tool_descriptions = []
225
249
  for tool in self.tools.values():
226
250
  tool_descriptions.append(f"- {tool.name}: {tool.description}")
227
-
251
+
228
252
  return f"""You are an AI assistant with access to the following tools:
229
253
 
230
254
  {chr(10).join(tool_descriptions)}
@@ -243,20 +267,20 @@ Important guidelines:
243
267
  @final
244
268
  class AgentTool(AgentClarificationMixin, BaseTool):
245
269
  """Tool for delegating tasks to sub-agents using hanzo-agents SDK."""
246
-
270
+
247
271
  @property
248
272
  @override
249
273
  def name(self) -> str:
250
274
  """Get the tool name."""
251
275
  return "agent"
252
-
276
+
253
277
  @property
254
278
  @override
255
279
  def description(self) -> str:
256
280
  """Get the tool description."""
257
281
  if not HANZO_AGENTS_AVAILABLE:
258
282
  return "Agent tool (hanzo-agents SDK not available - using fallback)"
259
-
283
+
260
284
  at = [t.name for t in self.available_tools]
261
285
  return f"""Launch a new agent that has access to the following tools: {at}.
262
286
 
@@ -277,7 +301,7 @@ Usage notes:
277
301
  3. Each agent invocation is stateless
278
302
  4. The agent's outputs should generally be trusted
279
303
  5. Clearly tell the agent whether you expect it to write code or just do research"""
280
-
304
+
281
305
  def __init__(
282
306
  self,
283
307
  permission_manager: PermissionManager,
@@ -292,11 +316,11 @@ Usage notes:
292
316
  self.permission_manager = permission_manager
293
317
  self.model_override = model
294
318
  self.api_key_override = api_key
295
- self.base_url_override = base_url
319
+ self.base_url_override = base_url
296
320
  self.max_tokens_override = max_tokens
297
321
  self.max_iterations = max_iterations
298
322
  self.max_tool_uses = max_tool_uses
299
-
323
+
300
324
  # Set up available tools
301
325
  self.available_tools: list[BaseTool] = []
302
326
  self.available_tools.extend(
@@ -305,21 +329,21 @@ Usage notes:
305
329
  self.available_tools.extend(
306
330
  get_read_only_jupyter_tools(self.permission_manager)
307
331
  )
308
-
332
+
309
333
  # Add edit tools
310
334
  self.available_tools.append(Edit(self.permission_manager))
311
335
  self.available_tools.append(MultiEdit(self.permission_manager))
312
-
336
+
313
337
  # Add special tools
314
338
  self.available_tools.append(ClarificationTool())
315
339
  self.available_tools.append(CriticTool())
316
340
  self.available_tools.append(ReviewTool())
317
341
  self.available_tools.append(IChingTool())
318
-
342
+
319
343
  self.available_tools.append(
320
344
  BatchTool({t.name: t for t in self.available_tools})
321
345
  )
322
-
346
+
323
347
  @override
324
348
  async def call(
325
349
  self,
@@ -328,17 +352,17 @@ Usage notes:
328
352
  ) -> str:
329
353
  """Execute the tool with the given parameters."""
330
354
  start_time = time.time()
331
-
355
+
332
356
  # Create tool context
333
357
  tool_ctx = create_tool_context(ctx)
334
358
  await tool_ctx.set_tool_info(self.name)
335
-
359
+
336
360
  # Extract parameters
337
361
  prompts = params.get("prompts")
338
362
  if prompts is None:
339
363
  await tool_ctx.error("No prompts provided")
340
364
  return "Error: At least one prompt must be provided."
341
-
365
+
342
366
  # Handle both string and list inputs
343
367
  if isinstance(prompts, str):
344
368
  prompt_list = [prompts]
@@ -350,30 +374,32 @@ Usage notes:
350
374
  else:
351
375
  await tool_ctx.error("Invalid prompts parameter type")
352
376
  return "Error: Parameter 'prompts' must be a string or list of strings."
353
-
377
+
354
378
  # Validate absolute paths
355
379
  absolute_path_pattern = r"/(?:[^/\s]+/)*[^/\s]+"
356
380
  for prompt in prompt_list:
357
381
  if not re.search(absolute_path_pattern, prompt):
358
382
  await tool_ctx.error(f"Prompt missing absolute path: {prompt[:50]}...")
359
383
  return "Error: All prompts must contain at least one absolute path."
360
-
384
+
361
385
  # Require hanzo-agents SDK
362
386
  if not HANZO_AGENTS_AVAILABLE:
363
387
  await tool_ctx.error("hanzo-agents SDK is required but not available")
364
388
  return "Error: hanzo-agents SDK is required for agent tool functionality. Please install it with: pip install hanzo-agents"
365
-
389
+
366
390
  # Use hanzo-agents SDK
367
- await tool_ctx.info(f"Launching {len(prompt_list)} agent(s) using hanzo-agents SDK")
368
-
391
+ await tool_ctx.info(
392
+ f"Launching {len(prompt_list)} agent(s) using hanzo-agents SDK"
393
+ )
394
+
369
395
  # Determine model and agent type
370
396
  model = params.get("model", self.model_override)
371
397
  use_memory = params.get("use_memory", False)
372
398
  memory_backend = params.get("memory_backend", "sqlite")
373
-
399
+
374
400
  # Get appropriate agent class
375
401
  agent_class = self._get_agent_class(model)
376
-
402
+
377
403
  # Create state
378
404
  state = MCPAgentState(
379
405
  prompts=prompt_list,
@@ -382,16 +408,16 @@ Usage notes:
382
408
  "api_key": self.api_key_override,
383
409
  "base_url": self.base_url_override,
384
410
  "max_tokens": self.max_tokens_override,
385
- }
411
+ },
386
412
  )
387
-
413
+
388
414
  # Create memory if requested
389
415
  memory_kv = None
390
416
  memory_vector = None
391
417
  if use_memory:
392
418
  memory_kv = create_memory_kv(memory_backend)
393
419
  memory_vector = create_memory_vector("simple")
394
-
420
+
395
421
  # Create network
396
422
  network = Network(
397
423
  state=state,
@@ -401,12 +427,12 @@ Usage notes:
401
427
  memory_vector=memory_vector,
402
428
  max_steps=self.max_iterations * len(prompt_list),
403
429
  )
404
-
430
+
405
431
  # Execute
406
432
  try:
407
433
  final_state = await network.run()
408
434
  execution_time = time.time() - start_time
409
-
435
+
410
436
  # Format results
411
437
  results = final_state.results
412
438
  if len(results) == 1:
@@ -417,25 +443,25 @@ AGENT RESPONSE:
417
443
  else:
418
444
  formatted_results = []
419
445
  for i, result in enumerate(results):
420
- formatted_results.append(f"Agent {i+1} Result:\n{result}")
421
-
446
+ formatted_results.append(f"Agent {i + 1} Result:\n{result}")
447
+
422
448
  formatted_result = f"""Multi-agent execution completed in {execution_time:.2f} seconds ({len(results)} agents).
423
449
 
424
450
  AGENT RESPONSES:
425
451
  {chr(10).join(formatted_results)}"""
426
-
452
+
427
453
  await tool_ctx.info(f"Execution completed in {execution_time:.2f}s")
428
454
  return formatted_result
429
-
455
+
430
456
  except Exception as e:
431
457
  await tool_ctx.error(f"Agent execution failed: {str(e)}")
432
458
  return f"Error: {str(e)}"
433
-
459
+
434
460
  def _get_agent_class(self, model: Optional[str]) -> type[Agent]:
435
461
  """Get appropriate agent class based on model."""
436
462
  if not model:
437
463
  model = "model://anthropic/claude-3-5-sonnet-20241022"
438
-
464
+
439
465
  # Check for CLI agents
440
466
  cli_agents = {
441
467
  "claude_cli": ClaudeCodeAgent,
@@ -443,39 +469,43 @@ AGENT RESPONSES:
443
469
  "gemini_cli": GeminiAgent,
444
470
  "grok_cli": GrokAgent,
445
471
  }
446
-
472
+
447
473
  if model in cli_agents:
448
474
  return cli_agents[model]
449
-
475
+
450
476
  # Return generic MCP agent
451
- return type("DynamicMCPAgent", (MCPAgent,), {
452
- "model": model,
453
- "__init__": lambda self: MCPAgent.__init__(
454
- self,
455
- available_tools=self.available_tools,
456
- permission_manager=self.permission_manager,
457
- ctx=self.ctx,
458
- model=model
459
- )
460
- })
461
-
477
+ return type(
478
+ "DynamicMCPAgent",
479
+ (MCPAgent,),
480
+ {
481
+ "model": model,
482
+ "__init__": lambda self: MCPAgent.__init__(
483
+ self,
484
+ available_tools=self.available_tools,
485
+ permission_manager=self.permission_manager,
486
+ ctx=self.ctx,
487
+ model=model,
488
+ ),
489
+ },
490
+ )
491
+
462
492
  @override
463
493
  def register(self, mcp_server: FastMCP) -> None:
464
494
  """Register this agent tool with the MCP server."""
465
495
  tool_self = self
466
-
496
+
467
497
  @mcp_server.tool(name=self.name, description=self.description)
468
498
  async def dispatch_agent(
469
499
  prompts: str | list[str],
470
500
  ctx: MCPContext,
471
501
  model: Optional[str] = None,
472
502
  use_memory: bool = False,
473
- memory_backend: str = "sqlite"
503
+ memory_backend: str = "sqlite",
474
504
  ) -> str:
475
505
  return await tool_self.call(
476
506
  ctx,
477
507
  prompts=prompts,
478
508
  model=model,
479
509
  use_memory=use_memory,
480
- memory_backend=memory_backend
481
- )
510
+ memory_backend=memory_backend,
511
+ )