hanzo-mcp 0.5.2__py3-none-any.whl → 0.6.2__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 (114) hide show
  1. hanzo_mcp/__init__.py +1 -1
  2. hanzo_mcp/cli.py +32 -0
  3. hanzo_mcp/dev_server.py +246 -0
  4. hanzo_mcp/prompts/__init__.py +1 -1
  5. hanzo_mcp/prompts/project_system.py +43 -7
  6. hanzo_mcp/server.py +5 -1
  7. hanzo_mcp/tools/__init__.py +66 -35
  8. hanzo_mcp/tools/agent/__init__.py +1 -1
  9. hanzo_mcp/tools/agent/agent.py +401 -0
  10. hanzo_mcp/tools/agent/agent_tool.py +3 -4
  11. hanzo_mcp/tools/common/__init__.py +1 -1
  12. hanzo_mcp/tools/common/base.py +2 -2
  13. hanzo_mcp/tools/common/batch_tool.py +3 -5
  14. hanzo_mcp/tools/common/config_tool.py +1 -1
  15. hanzo_mcp/tools/common/context.py +1 -1
  16. hanzo_mcp/tools/common/palette.py +344 -0
  17. hanzo_mcp/tools/common/palette_loader.py +108 -0
  18. hanzo_mcp/tools/common/stats.py +1 -1
  19. hanzo_mcp/tools/common/thinking_tool.py +3 -5
  20. hanzo_mcp/tools/common/tool_disable.py +1 -1
  21. hanzo_mcp/tools/common/tool_enable.py +1 -1
  22. hanzo_mcp/tools/common/tool_list.py +49 -52
  23. hanzo_mcp/tools/config/__init__.py +10 -0
  24. hanzo_mcp/tools/config/config_tool.py +212 -0
  25. hanzo_mcp/tools/config/index_config.py +176 -0
  26. hanzo_mcp/tools/config/palette_tool.py +166 -0
  27. hanzo_mcp/tools/database/__init__.py +1 -1
  28. hanzo_mcp/tools/database/graph.py +482 -0
  29. hanzo_mcp/tools/database/graph_add.py +1 -1
  30. hanzo_mcp/tools/database/graph_query.py +1 -1
  31. hanzo_mcp/tools/database/graph_remove.py +1 -1
  32. hanzo_mcp/tools/database/graph_search.py +1 -1
  33. hanzo_mcp/tools/database/graph_stats.py +1 -1
  34. hanzo_mcp/tools/database/sql.py +411 -0
  35. hanzo_mcp/tools/database/sql_query.py +1 -1
  36. hanzo_mcp/tools/database/sql_search.py +1 -1
  37. hanzo_mcp/tools/database/sql_stats.py +1 -1
  38. hanzo_mcp/tools/editor/neovim_command.py +1 -1
  39. hanzo_mcp/tools/editor/neovim_edit.py +1 -1
  40. hanzo_mcp/tools/editor/neovim_session.py +1 -1
  41. hanzo_mcp/tools/filesystem/__init__.py +42 -13
  42. hanzo_mcp/tools/filesystem/base.py +1 -1
  43. hanzo_mcp/tools/filesystem/batch_search.py +4 -4
  44. hanzo_mcp/tools/filesystem/content_replace.py +3 -5
  45. hanzo_mcp/tools/filesystem/diff.py +193 -0
  46. hanzo_mcp/tools/filesystem/directory_tree.py +3 -5
  47. hanzo_mcp/tools/filesystem/edit.py +3 -5
  48. hanzo_mcp/tools/filesystem/find.py +443 -0
  49. hanzo_mcp/tools/filesystem/find_files.py +1 -1
  50. hanzo_mcp/tools/filesystem/git_search.py +1 -1
  51. hanzo_mcp/tools/filesystem/grep.py +2 -2
  52. hanzo_mcp/tools/filesystem/multi_edit.py +3 -5
  53. hanzo_mcp/tools/filesystem/read.py +17 -5
  54. hanzo_mcp/tools/filesystem/{grep_ast_tool.py → symbols.py} +17 -27
  55. hanzo_mcp/tools/filesystem/symbols_unified.py +376 -0
  56. hanzo_mcp/tools/filesystem/tree.py +268 -0
  57. hanzo_mcp/tools/filesystem/unified_search.py +711 -0
  58. hanzo_mcp/tools/filesystem/unix_aliases.py +99 -0
  59. hanzo_mcp/tools/filesystem/watch.py +174 -0
  60. hanzo_mcp/tools/filesystem/write.py +3 -5
  61. hanzo_mcp/tools/jupyter/__init__.py +9 -12
  62. hanzo_mcp/tools/jupyter/base.py +1 -1
  63. hanzo_mcp/tools/jupyter/jupyter.py +326 -0
  64. hanzo_mcp/tools/jupyter/notebook_edit.py +3 -4
  65. hanzo_mcp/tools/jupyter/notebook_read.py +3 -5
  66. hanzo_mcp/tools/llm/__init__.py +4 -0
  67. hanzo_mcp/tools/llm/consensus_tool.py +1 -1
  68. hanzo_mcp/tools/llm/llm_manage.py +1 -1
  69. hanzo_mcp/tools/llm/llm_tool.py +1 -1
  70. hanzo_mcp/tools/llm/llm_unified.py +851 -0
  71. hanzo_mcp/tools/llm/provider_tools.py +1 -1
  72. hanzo_mcp/tools/mcp/__init__.py +4 -0
  73. hanzo_mcp/tools/mcp/mcp_add.py +1 -1
  74. hanzo_mcp/tools/mcp/mcp_remove.py +1 -1
  75. hanzo_mcp/tools/mcp/mcp_stats.py +1 -1
  76. hanzo_mcp/tools/mcp/mcp_unified.py +503 -0
  77. hanzo_mcp/tools/shell/__init__.py +20 -42
  78. hanzo_mcp/tools/shell/base.py +1 -1
  79. hanzo_mcp/tools/shell/base_process.py +303 -0
  80. hanzo_mcp/tools/shell/bash_unified.py +134 -0
  81. hanzo_mcp/tools/shell/logs.py +1 -1
  82. hanzo_mcp/tools/shell/npx.py +1 -1
  83. hanzo_mcp/tools/shell/npx_background.py +1 -1
  84. hanzo_mcp/tools/shell/npx_unified.py +101 -0
  85. hanzo_mcp/tools/shell/open.py +107 -0
  86. hanzo_mcp/tools/shell/pkill.py +1 -1
  87. hanzo_mcp/tools/shell/process_unified.py +131 -0
  88. hanzo_mcp/tools/shell/processes.py +1 -1
  89. hanzo_mcp/tools/shell/run_background.py +1 -1
  90. hanzo_mcp/tools/shell/run_command.py +3 -4
  91. hanzo_mcp/tools/shell/run_command_windows.py +3 -4
  92. hanzo_mcp/tools/shell/uvx.py +1 -1
  93. hanzo_mcp/tools/shell/uvx_background.py +1 -1
  94. hanzo_mcp/tools/shell/uvx_unified.py +101 -0
  95. hanzo_mcp/tools/todo/__init__.py +1 -1
  96. hanzo_mcp/tools/todo/base.py +1 -1
  97. hanzo_mcp/tools/todo/todo.py +265 -0
  98. hanzo_mcp/tools/todo/todo_read.py +3 -5
  99. hanzo_mcp/tools/todo/todo_write.py +3 -5
  100. hanzo_mcp/tools/vector/__init__.py +1 -1
  101. hanzo_mcp/tools/vector/index_tool.py +1 -1
  102. hanzo_mcp/tools/vector/project_manager.py +27 -5
  103. hanzo_mcp/tools/vector/vector.py +311 -0
  104. hanzo_mcp/tools/vector/vector_index.py +1 -1
  105. hanzo_mcp/tools/vector/vector_search.py +1 -1
  106. hanzo_mcp-0.6.2.dist-info/METADATA +336 -0
  107. hanzo_mcp-0.6.2.dist-info/RECORD +134 -0
  108. hanzo_mcp-0.6.2.dist-info/entry_points.txt +3 -0
  109. hanzo_mcp-0.5.2.dist-info/METADATA +0 -276
  110. hanzo_mcp-0.5.2.dist-info/RECORD +0 -106
  111. hanzo_mcp-0.5.2.dist-info/entry_points.txt +0 -2
  112. {hanzo_mcp-0.5.2.dist-info → hanzo_mcp-0.6.2.dist-info}/WHEEL +0 -0
  113. {hanzo_mcp-0.5.2.dist-info → hanzo_mcp-0.6.2.dist-info}/licenses/LICENSE +0 -0
  114. {hanzo_mcp-0.5.2.dist-info → hanzo_mcp-0.6.2.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,401 @@
1
+ """Unified agent tool implementation.
2
+
3
+ This module provides the AgentTool for delegating tasks to sub-agents,
4
+ supporting both one-off and long-running RPC modes, including A2A communication.
5
+ """
6
+
7
+ import asyncio
8
+ import json
9
+ import re
10
+ import time
11
+ import uuid
12
+ from typing import Annotated, TypedDict, Unpack, final, override, Optional, Dict, Any, List
13
+ from collections.abc import Iterable
14
+
15
+ import litellm
16
+ from mcp.server.fastmcp import Context as MCPContext
17
+ from openai.types.chat import ChatCompletionMessageParam, ChatCompletionToolParam
18
+ from pydantic import Field
19
+
20
+ from hanzo_mcp.tools.agent.prompt import (
21
+ get_allowed_agent_tools,
22
+ get_default_model,
23
+ get_model_parameters,
24
+ get_system_prompt,
25
+ )
26
+ from hanzo_mcp.tools.agent.tool_adapter import (
27
+ convert_tools_to_openai_functions,
28
+ )
29
+ from hanzo_mcp.tools.common.base import BaseTool
30
+ from hanzo_mcp.tools.common.batch_tool import BatchTool
31
+ from hanzo_mcp.tools.common.context import (
32
+ ToolContext,
33
+ create_tool_context,
34
+ )
35
+ from hanzo_mcp.tools.common.permissions import PermissionManager
36
+ from hanzo_mcp.tools.filesystem import get_read_only_filesystem_tools
37
+ from hanzo_mcp.tools.jupyter import get_read_only_jupyter_tools
38
+
39
+
40
+ # Parameter types
41
+ Action = Annotated[
42
+ str,
43
+ Field(
44
+ description="Action: run (default), start, call, stop, list",
45
+ default="run",
46
+ ),
47
+ ]
48
+
49
+ Prompts = Annotated[
50
+ Optional[str | List[str]],
51
+ Field(
52
+ description="Task(s) for agent (must include absolute paths starting with /)",
53
+ default=None,
54
+ ),
55
+ ]
56
+
57
+ Mode = Annotated[
58
+ str,
59
+ Field(
60
+ description="Execution mode: oneoff (default) or rpc",
61
+ default="oneoff",
62
+ ),
63
+ ]
64
+
65
+ AgentId = Annotated[
66
+ Optional[str],
67
+ Field(
68
+ description="Agent ID for RPC mode",
69
+ default=None,
70
+ ),
71
+ ]
72
+
73
+ Method = Annotated[
74
+ Optional[str],
75
+ Field(
76
+ description="Method to call on RPC agent",
77
+ default=None,
78
+ ),
79
+ ]
80
+
81
+ Args = Annotated[
82
+ Optional[Dict[str, Any]],
83
+ Field(
84
+ description="Arguments for RPC method call",
85
+ default=None,
86
+ ),
87
+ ]
88
+
89
+ Model = Annotated[
90
+ Optional[str],
91
+ Field(
92
+ description="Model to use (e.g., lm-studio/local-model, openai/gpt-4o)",
93
+ default=None,
94
+ ),
95
+ ]
96
+
97
+
98
+ class AgentParams(TypedDict, total=False):
99
+ """Parameters for agent tool."""
100
+ action: str
101
+ prompts: Optional[str | List[str]]
102
+ mode: str
103
+ agent_id: Optional[str]
104
+ method: Optional[str]
105
+ args: Optional[Dict[str, Any]]
106
+ model: Optional[str]
107
+
108
+
109
+ class RPCAgent:
110
+ """Long-running RPC agent."""
111
+
112
+ def __init__(self, agent_id: str, model: str, system_prompt: str, tools: List[BaseTool]):
113
+ self.agent_id = agent_id
114
+ self.model = model
115
+ self.system_prompt = system_prompt
116
+ self.tools = tools
117
+ self.messages: List[ChatCompletionMessageParam] = [
118
+ {"role": "system", "content": system_prompt}
119
+ ]
120
+ self.created_at = time.time()
121
+ self.last_used = time.time()
122
+ self.call_count = 0
123
+
124
+ async def call_method(self, method: str, args: Dict[str, Any], tool_ctx: ToolContext) -> str:
125
+ """Call a method on the RPC agent."""
126
+ self.last_used = time.time()
127
+ self.call_count += 1
128
+
129
+ # Build prompt based on method
130
+ if method == "search":
131
+ prompt = f"Search for: {args.get('query', 'unknown')}"
132
+ elif method == "analyze":
133
+ prompt = f"Analyze: {args.get('target', 'unknown')}"
134
+ elif method == "execute":
135
+ prompt = f"Execute: {args.get('command', 'unknown')}"
136
+ else:
137
+ # Generic method call
138
+ prompt = f"Method: {method}, Args: {json.dumps(args)}"
139
+
140
+ # Add to conversation
141
+ self.messages.append({"role": "user", "content": prompt})
142
+
143
+ # Get response
144
+ # (simplified - would integrate with full agent execution logic)
145
+ response = f"Executed {method} with args {args}"
146
+ self.messages.append({"role": "assistant", "content": response})
147
+
148
+ return response
149
+
150
+
151
+ @final
152
+ class AgentTool(BaseTool):
153
+ """Unified agent tool with one-off and RPC modes."""
154
+
155
+ def __init__(
156
+ self,
157
+ permission_manager: PermissionManager,
158
+ model: str | None = None,
159
+ api_key: str | None = None,
160
+ base_url: str | None = None,
161
+ max_tokens: int | None = None,
162
+ max_iterations: int = 10,
163
+ max_tool_uses: int = 30,
164
+ ):
165
+ """Initialize the agent tool."""
166
+ self.permission_manager = permission_manager
167
+ self.model_override = model
168
+ self.api_key_override = api_key
169
+ self.base_url_override = base_url
170
+ self.max_tokens_override = max_tokens
171
+ self.max_iterations = max_iterations
172
+ self.max_tool_uses = max_tool_uses
173
+
174
+ # RPC agent registry
175
+ self._rpc_agents: Dict[str, RPCAgent] = {}
176
+
177
+ # Available tools
178
+ self.available_tools: list[BaseTool] = []
179
+ self.available_tools.extend(
180
+ get_read_only_filesystem_tools(self.permission_manager)
181
+ )
182
+ self.available_tools.extend(
183
+ get_read_only_jupyter_tools(self.permission_manager)
184
+ )
185
+ self.available_tools.append(
186
+ BatchTool({t.name: t for t in self.available_tools})
187
+ )
188
+
189
+ @property
190
+ @override
191
+ def name(self) -> str:
192
+ """Get the tool name."""
193
+ return "agent"
194
+
195
+ @property
196
+ @override
197
+ def description(self) -> str:
198
+ """Get the tool description."""
199
+ tools = [t.name for t in self.available_tools]
200
+
201
+ return f"""AI agents with tools: {', '.join(tools)}. Actions: run (default), start, call, stop, list.
202
+
203
+ Usage:
204
+ agent "Search for config files in /project"
205
+ agent --action start --mode rpc --model lm-studio/local-model
206
+ agent --action call --agent-id abc123 --method search --args '{{"query": "database"}}'
207
+ agent --action list
208
+
209
+ Modes:
210
+ - oneoff: Single task execution (default)
211
+ - rpc: Long-running agent for multiple calls (A2A support)"""
212
+
213
+ @override
214
+ async def call(
215
+ self,
216
+ ctx: MCPContext,
217
+ **params: Unpack[AgentParams],
218
+ ) -> str:
219
+ """Execute agent operation."""
220
+ tool_ctx = create_tool_context(ctx)
221
+ await tool_ctx.set_tool_info(self.name)
222
+
223
+ # Extract action
224
+ action = params.get("action", "run")
225
+
226
+ # Route to appropriate handler
227
+ if action == "run":
228
+ return await self._handle_run(params, tool_ctx)
229
+ elif action == "start":
230
+ return await self._handle_start(params, tool_ctx)
231
+ elif action == "call":
232
+ return await self._handle_call(params, tool_ctx)
233
+ elif action == "stop":
234
+ return await self._handle_stop(params, tool_ctx)
235
+ elif action == "list":
236
+ return await self._handle_list(tool_ctx)
237
+ else:
238
+ return f"Error: Unknown action '{action}'. Valid actions: run, start, call, stop, list"
239
+
240
+ async def _handle_run(self, params: Dict[str, Any], tool_ctx: ToolContext) -> str:
241
+ """Handle one-off agent run (default action)."""
242
+ prompts = params.get("prompts")
243
+ if not prompts:
244
+ return "Error: prompts required for run action"
245
+
246
+ # Convert to list
247
+ if isinstance(prompts, str):
248
+ prompt_list = [prompts]
249
+ else:
250
+ prompt_list = prompts
251
+
252
+ # Validate prompts
253
+ for prompt in prompt_list:
254
+ if not self._validate_prompt(prompt):
255
+ return f"Error: Prompt must contain absolute paths starting with /: {prompt[:50]}..."
256
+
257
+ # Execute agents
258
+ start_time = time.time()
259
+
260
+ if len(prompt_list) == 1:
261
+ await tool_ctx.info("Launching agent")
262
+ result = await self._execute_agent(prompt_list[0], params.get("model"), tool_ctx)
263
+ else:
264
+ await tool_ctx.info(f"Launching {len(prompt_list)} agents in parallel")
265
+ result = await self._execute_multiple_agents(prompt_list, params.get("model"), tool_ctx)
266
+
267
+ execution_time = time.time() - start_time
268
+
269
+ return f"""Agent execution completed in {execution_time:.2f} seconds.
270
+
271
+ AGENT RESPONSE:
272
+ {result}"""
273
+
274
+ async def _handle_start(self, params: Dict[str, Any], tool_ctx: ToolContext) -> str:
275
+ """Start a new RPC agent."""
276
+ mode = params.get("mode", "oneoff")
277
+ if mode != "rpc":
278
+ return "Error: start action only valid for rpc mode"
279
+
280
+ # Generate agent ID
281
+ agent_id = str(uuid.uuid4())[:8]
282
+
283
+ # Get model
284
+ model = params.get("model") or get_default_model(self.model_override)
285
+
286
+ # Get available tools
287
+ agent_tools = get_allowed_agent_tools(
288
+ self.available_tools,
289
+ self.permission_manager,
290
+ )
291
+
292
+ # Create system prompt
293
+ system_prompt = get_system_prompt(
294
+ agent_tools,
295
+ self.permission_manager,
296
+ )
297
+
298
+ # Create RPC agent
299
+ agent = RPCAgent(agent_id, model, system_prompt, agent_tools)
300
+ self._rpc_agents[agent_id] = agent
301
+
302
+ await tool_ctx.info(f"Started RPC agent {agent_id} with model {model}")
303
+
304
+ return f"""Started RPC agent:
305
+ - ID: {agent_id}
306
+ - Model: {model}
307
+ - Tools: {len(agent_tools)}
308
+
309
+ Use 'agent --action call --agent-id {agent_id} --method <method> --args <args>' to interact."""
310
+
311
+ async def _handle_call(self, params: Dict[str, Any], tool_ctx: ToolContext) -> str:
312
+ """Call method on RPC agent."""
313
+ agent_id = params.get("agent_id")
314
+ if not agent_id:
315
+ return "Error: agent_id required for call action"
316
+
317
+ if agent_id not in self._rpc_agents:
318
+ return f"Error: Agent {agent_id} not found. Use 'agent --action list' to see active agents."
319
+
320
+ method = params.get("method")
321
+ if not method:
322
+ return "Error: method required for call action"
323
+
324
+ args = params.get("args", {})
325
+
326
+ # Call agent method
327
+ agent = self._rpc_agents[agent_id]
328
+ await tool_ctx.info(f"Calling {method} on agent {agent_id}")
329
+
330
+ try:
331
+ result = await agent.call_method(method, args, tool_ctx)
332
+ return f"Agent {agent_id} response:\n{result}"
333
+ except Exception as e:
334
+ await tool_ctx.error(f"Error calling agent: {str(e)}")
335
+ return f"Error calling agent: {str(e)}"
336
+
337
+ async def _handle_stop(self, params: Dict[str, Any], tool_ctx: ToolContext) -> str:
338
+ """Stop an RPC agent."""
339
+ agent_id = params.get("agent_id")
340
+ if not agent_id:
341
+ return "Error: agent_id required for stop action"
342
+
343
+ if agent_id not in self._rpc_agents:
344
+ return f"Error: Agent {agent_id} not found"
345
+
346
+ agent = self._rpc_agents.pop(agent_id)
347
+ await tool_ctx.info(f"Stopped agent {agent_id}")
348
+
349
+ return f"""Stopped agent {agent_id}:
350
+ - Runtime: {time.time() - agent.created_at:.2f} seconds
351
+ - Calls: {agent.call_count}"""
352
+
353
+ async def _handle_list(self, tool_ctx: ToolContext) -> str:
354
+ """List active RPC agents."""
355
+ if not self._rpc_agents:
356
+ return "No active RPC agents"
357
+
358
+ output = ["=== Active RPC Agents ==="]
359
+ for agent_id, agent in self._rpc_agents.items():
360
+ runtime = time.time() - agent.created_at
361
+ idle = time.time() - agent.last_used
362
+ output.append(f"\nAgent {agent_id}:")
363
+ output.append(f" Model: {agent.model}")
364
+ output.append(f" Runtime: {runtime:.2f}s")
365
+ output.append(f" Idle: {idle:.2f}s")
366
+ output.append(f" Calls: {agent.call_count}")
367
+
368
+ return "\n".join(output)
369
+
370
+ def _validate_prompt(self, prompt: str) -> bool:
371
+ """Validate that prompt contains absolute paths."""
372
+ absolute_path_pattern = r"/(?:[^/\s]+/)*[^/\s]+"
373
+ return bool(re.search(absolute_path_pattern, prompt))
374
+
375
+ async def _execute_agent(self, prompt: str, model: Optional[str], tool_ctx: ToolContext) -> str:
376
+ """Execute a single agent (simplified - would use full logic from agent_tool.py)."""
377
+ # This would integrate the full agent execution logic from agent_tool.py
378
+ # For now, return a placeholder
379
+ return f"Executed agent with prompt: {prompt[:100]}..."
380
+
381
+ async def _execute_multiple_agents(self, prompts: List[str], model: Optional[str], tool_ctx: ToolContext) -> str:
382
+ """Execute multiple agents in parallel."""
383
+ tasks = []
384
+ for prompt in prompts:
385
+ task = self._execute_agent(prompt, model, tool_ctx)
386
+ tasks.append(task)
387
+
388
+ results = await asyncio.gather(*tasks, return_exceptions=True)
389
+
390
+ formatted_results = []
391
+ for i, result in enumerate(results):
392
+ if isinstance(result, Exception):
393
+ formatted_results.append(f"Agent {i+1} Error:\n{str(result)}")
394
+ else:
395
+ formatted_results.append(f"Agent {i+1} Result:\n{result}")
396
+
397
+ return "\n\n---\n\n".join(formatted_results)
398
+
399
+ def register(self, mcp_server) -> None:
400
+ """Register this tool with the MCP server."""
401
+ pass
@@ -12,9 +12,8 @@ from collections.abc import Iterable
12
12
  from typing import Annotated, TypedDict, Unpack, final, override
13
13
 
14
14
  import litellm
15
- from fastmcp import Context as MCPContext
16
- from fastmcp import FastMCP
17
- from fastmcp.server.dependencies import get_context
15
+ from mcp.server.fastmcp import Context as MCPContext
16
+ from mcp.server import FastMCP
18
17
  from openai.types.chat import ChatCompletionMessageParam, ChatCompletionToolParam
19
18
  from pydantic import Field
20
19
 
@@ -535,6 +534,6 @@ AGENT RESPONSE:
535
534
  @mcp_server.tool(name=self.name, description=self.description)
536
535
  async def dispatch_agent(
537
536
  prompts: str | list[str],
537
+ ctx: MCPContext
538
538
  ) -> str:
539
- ctx = get_context()
540
539
  return await tool_self.call(ctx, prompts=prompts)
@@ -1,6 +1,6 @@
1
1
  """Common utilities for Hanzo MCP tools."""
2
2
 
3
- from fastmcp import FastMCP
3
+ from mcp.server import FastMCP
4
4
 
5
5
  from hanzo_mcp.tools.common.base import BaseTool, ToolRegistry
6
6
  from hanzo_mcp.tools.common.batch_tool import BatchTool
@@ -10,8 +10,8 @@ from abc import ABC, abstractmethod
10
10
  from collections.abc import Awaitable
11
11
  from typing import Any, Callable, final
12
12
 
13
- from fastmcp import FastMCP
14
- from fastmcp import Context as MCPContext
13
+ from mcp.server import FastMCP
14
+ from mcp.server.fastmcp import Context as MCPContext
15
15
 
16
16
 
17
17
  from hanzo_mcp.tools.common.permissions import PermissionManager
@@ -7,9 +7,8 @@ parallel or serial depending on their characteristics.
7
7
  import asyncio
8
8
  from typing import Annotated, Any, TypedDict, Unpack, final, override
9
9
 
10
- from fastmcp import Context as MCPContext
11
- from fastmcp import FastMCP
12
- from fastmcp.server.dependencies import get_context
10
+ from mcp.server.fastmcp import Context as MCPContext
11
+ from mcp.server import FastMCP
13
12
  from pydantic import Field
14
13
 
15
14
  from hanzo_mcp.tools.common.base import BaseTool
@@ -320,11 +319,10 @@ Not available: think,write,edit,multi_edit,notebook_edit
320
319
 
321
320
  @mcp_server.tool(name=self.name, description=self.description)
322
321
  async def batch(
323
- ctx: MCPContext,
324
322
  description: Description,
325
323
  invocations: Invocations,
324
+ ctx: MCPContext
326
325
  ) -> str:
327
- ctx = get_context()
328
326
  return await tool_self.call(
329
327
  ctx, description=description, invocations=invocations
330
328
  )
@@ -4,7 +4,7 @@ from typing import Dict, List, Optional, TypedDict, Unpack, Any, final
4
4
  import json
5
5
  from pathlib import Path
6
6
 
7
- from fastmcp import Context as MCPContext
7
+ from mcp.server.fastmcp import Context as MCPContext
8
8
 
9
9
  from hanzo_mcp.tools.common.base import BaseTool
10
10
  from hanzo_mcp.tools.common.permissions import PermissionManager
@@ -11,7 +11,7 @@ from collections.abc import Iterable
11
11
  from pathlib import Path
12
12
  from typing import Any, ClassVar, final
13
13
 
14
- from fastmcp import Context as MCPContext
14
+ from mcp.server.fastmcp import Context as MCPContext
15
15
  from mcp.server.lowlevel.helper_types import ReadResourceContents
16
16
 
17
17