hanzo-mcp 0.6.10__py3-none-any.whl → 0.6.13__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 (78) hide show
  1. hanzo_mcp/__init__.py +11 -2
  2. hanzo_mcp/cli.py +69 -19
  3. hanzo_mcp/cli_enhanced.py +15 -12
  4. hanzo_mcp/cli_plugin.py +91 -0
  5. hanzo_mcp/config/__init__.py +1 -1
  6. hanzo_mcp/config/settings.py +75 -8
  7. hanzo_mcp/config/tool_config.py +2 -2
  8. hanzo_mcp/dev_server.py +20 -15
  9. hanzo_mcp/prompts/project_system.py +1 -1
  10. hanzo_mcp/server.py +18 -4
  11. hanzo_mcp/server_enhanced.py +69 -0
  12. hanzo_mcp/tools/__init__.py +78 -30
  13. hanzo_mcp/tools/agent/__init__.py +1 -1
  14. hanzo_mcp/tools/agent/agent_tool.py +2 -2
  15. hanzo_mcp/tools/common/__init__.py +15 -1
  16. hanzo_mcp/tools/common/base.py +4 -4
  17. hanzo_mcp/tools/common/batch_tool.py +1 -1
  18. hanzo_mcp/tools/common/config_tool.py +2 -2
  19. hanzo_mcp/tools/common/context.py +2 -2
  20. hanzo_mcp/tools/common/context_fix.py +26 -0
  21. hanzo_mcp/tools/common/critic_tool.py +196 -0
  22. hanzo_mcp/tools/common/decorators.py +208 -0
  23. hanzo_mcp/tools/common/enhanced_base.py +106 -0
  24. hanzo_mcp/tools/common/mode.py +116 -0
  25. hanzo_mcp/tools/common/mode_loader.py +105 -0
  26. hanzo_mcp/tools/common/permissions.py +1 -1
  27. hanzo_mcp/tools/common/personality.py +936 -0
  28. hanzo_mcp/tools/common/plugin_loader.py +287 -0
  29. hanzo_mcp/tools/common/stats.py +4 -4
  30. hanzo_mcp/tools/common/tool_list.py +1 -1
  31. hanzo_mcp/tools/common/validation.py +1 -1
  32. hanzo_mcp/tools/config/__init__.py +3 -1
  33. hanzo_mcp/tools/config/config_tool.py +1 -1
  34. hanzo_mcp/tools/config/mode_tool.py +209 -0
  35. hanzo_mcp/tools/database/__init__.py +1 -1
  36. hanzo_mcp/tools/editor/__init__.py +1 -1
  37. hanzo_mcp/tools/filesystem/__init__.py +19 -14
  38. hanzo_mcp/tools/filesystem/batch_search.py +3 -3
  39. hanzo_mcp/tools/filesystem/diff.py +2 -2
  40. hanzo_mcp/tools/filesystem/rules_tool.py +235 -0
  41. hanzo_mcp/tools/filesystem/{unified_search.py → search_tool.py} +12 -12
  42. hanzo_mcp/tools/filesystem/{symbols_unified.py → symbols_tool.py} +104 -5
  43. hanzo_mcp/tools/filesystem/watch.py +3 -2
  44. hanzo_mcp/tools/jupyter/__init__.py +2 -2
  45. hanzo_mcp/tools/jupyter/jupyter.py +1 -1
  46. hanzo_mcp/tools/llm/__init__.py +3 -3
  47. hanzo_mcp/tools/llm/llm_tool.py +648 -143
  48. hanzo_mcp/tools/mcp/__init__.py +2 -2
  49. hanzo_mcp/tools/mcp/{mcp_unified.py → mcp_tool.py} +3 -3
  50. hanzo_mcp/tools/shell/__init__.py +6 -6
  51. hanzo_mcp/tools/shell/base_process.py +4 -2
  52. hanzo_mcp/tools/shell/bash_session_executor.py +8 -5
  53. hanzo_mcp/tools/shell/{bash_unified.py → bash_tool.py} +1 -1
  54. hanzo_mcp/tools/shell/command_executor.py +8 -6
  55. hanzo_mcp/tools/shell/{npx_unified.py → npx_tool.py} +1 -1
  56. hanzo_mcp/tools/shell/open.py +2 -2
  57. hanzo_mcp/tools/shell/{process_unified.py → process_tool.py} +1 -1
  58. hanzo_mcp/tools/shell/run_command_windows.py +1 -1
  59. hanzo_mcp/tools/shell/uvx.py +47 -2
  60. hanzo_mcp/tools/shell/uvx_background.py +47 -2
  61. hanzo_mcp/tools/shell/{uvx_unified.py → uvx_tool.py} +1 -1
  62. hanzo_mcp/tools/todo/__init__.py +14 -19
  63. hanzo_mcp/tools/todo/todo.py +22 -1
  64. hanzo_mcp/tools/vector/__init__.py +7 -3
  65. hanzo_mcp/tools/vector/ast_analyzer.py +12 -4
  66. hanzo_mcp/tools/vector/infinity_store.py +11 -5
  67. hanzo_mcp/tools/vector/project_manager.py +4 -2
  68. hanzo_mcp-0.6.13.dist-info/METADATA +359 -0
  69. {hanzo_mcp-0.6.10.dist-info → hanzo_mcp-0.6.13.dist-info}/RECORD +73 -65
  70. {hanzo_mcp-0.6.10.dist-info → hanzo_mcp-0.6.13.dist-info}/entry_points.txt +1 -0
  71. hanzo_mcp/tools/common/palette.py +0 -344
  72. hanzo_mcp/tools/common/palette_loader.py +0 -108
  73. hanzo_mcp/tools/config/palette_tool.py +0 -179
  74. hanzo_mcp/tools/llm/llm_unified.py +0 -851
  75. hanzo_mcp-0.6.10.dist-info/METADATA +0 -339
  76. {hanzo_mcp-0.6.10.dist-info → hanzo_mcp-0.6.13.dist-info}/WHEEL +0 -0
  77. {hanzo_mcp-0.6.10.dist-info → hanzo_mcp-0.6.13.dist-info}/licenses/LICENSE +0 -0
  78. {hanzo_mcp-0.6.10.dist-info → hanzo_mcp-0.6.13.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,69 @@
1
+ """Enhanced MCP server with automatic context normalization.
2
+
3
+ This module provides an enhanced FastMCP server that automatically
4
+ applies context normalization to all registered tools.
5
+ """
6
+
7
+ from typing import Any, Callable
8
+ from functools import wraps
9
+
10
+ from mcp.server import FastMCP
11
+
12
+ from hanzo_mcp.tools.common.decorators import with_context_normalization
13
+
14
+
15
+ class EnhancedFastMCP(FastMCP):
16
+ """Enhanced FastMCP server with automatic context normalization.
17
+
18
+ This server automatically wraps all tool registrations with context
19
+ normalization, ensuring that tools work properly when called externally
20
+ with serialized context parameters.
21
+ """
22
+
23
+ def tool(
24
+ self,
25
+ name: str | None = None,
26
+ description: str | None = None
27
+ ) -> Callable:
28
+ """Enhanced tool decorator that includes automatic context normalization.
29
+
30
+ Args:
31
+ name: Tool name (defaults to function name)
32
+ description: Tool description
33
+
34
+ Returns:
35
+ Decorator function that registers the tool with context normalization
36
+ """
37
+ # Get the original decorator from parent class
38
+ original_decorator = super().tool(
39
+ name=name,
40
+ description=description
41
+ )
42
+
43
+ # Create our enhanced decorator
44
+ def enhanced_decorator(func: Callable) -> Callable:
45
+ # Apply context normalization first
46
+ # Check if function has ctx parameter
47
+ import inspect
48
+ sig = inspect.signature(func)
49
+ if 'ctx' in sig.parameters:
50
+ normalized_func = with_context_normalization(func)
51
+ else:
52
+ normalized_func = func
53
+
54
+ # Then apply the original decorator
55
+ return original_decorator(normalized_func)
56
+
57
+ return enhanced_decorator
58
+
59
+
60
+ def create_enhanced_server(name: str = "hanzo") -> EnhancedFastMCP:
61
+ """Create an enhanced MCP server with automatic context normalization.
62
+
63
+ Args:
64
+ name: Server name
65
+
66
+ Returns:
67
+ Enhanced FastMCP server instance
68
+ """
69
+ return EnhancedFastMCP(name)
@@ -1,7 +1,7 @@
1
- """Tools package for Hanzo MCP.
1
+ """Tools package for Hanzo AI.
2
2
 
3
- This package contains all the tools for the Hanzo MCP server.
4
- It provides a unified interface for registering all tools with an MCP server.
3
+ This package contains all the tools for the Hanzo AI server.
4
+ It provides a interface for registering all tools with an MCP server.
5
5
 
6
6
  This includes a "think" tool implementation based on Anthropic's research showing
7
7
  improved performance for complex tool-based interactions when Claude has a dedicated
@@ -12,7 +12,7 @@ to delegate tasks to sub-agents for concurrent execution and specialized process
12
12
  from mcp.server import FastMCP
13
13
 
14
14
  from hanzo_mcp.tools.agent import register_agent_tools
15
- from hanzo_mcp.tools.common import register_batch_tool, register_thinking_tool
15
+ from hanzo_mcp.tools.common import register_batch_tool, register_thinking_tool, register_critic_tool
16
16
  from hanzo_mcp.tools.common.base import BaseTool
17
17
  from hanzo_mcp.tools.common.permissions import PermissionManager
18
18
  from hanzo_mcp.tools.common.tool_enable import ToolEnableTool
@@ -25,11 +25,13 @@ from hanzo_mcp.tools.shell import register_shell_tools
25
25
  from hanzo_mcp.tools.todo import register_todo_tools
26
26
  from hanzo_mcp.tools.vector import register_vector_tools
27
27
  from hanzo_mcp.tools.database import register_database_tools, DatabaseManager
28
- from hanzo_mcp.tools.mcp import UnifiedMCPTool, McpAddTool, McpRemoveTool, McpStatsTool
28
+ from hanzo_mcp.tools.mcp import MCPTool, McpAddTool, McpRemoveTool, McpStatsTool
29
29
  from hanzo_mcp.tools.editor import NeovimEditTool, NeovimCommandTool, NeovimSessionTool
30
- from hanzo_mcp.tools.llm import UnifiedLLMTool, LLMTool, ConsensusTool, LLMManageTool, create_provider_tools
31
- from hanzo_mcp.tools.config.palette_tool import palette_tool
32
- from hanzo_mcp.tools.common.palette_loader import PaletteLoader
30
+ from hanzo_mcp.tools.llm import LLMTool, LLMTool, ConsensusTool, LLMManageTool, create_provider_tools
31
+ from hanzo_mcp.tools.config.mode_tool import mode_tool
32
+ from hanzo_mcp.tools.common.mode_loader import ModeLoader
33
+ from hanzo_mcp.tools.common.mode import activate_mode_from_env
34
+ from hanzo_mcp.tools.common.plugin_loader import load_user_plugins
33
35
 
34
36
 
35
37
  def register_all_tools(
@@ -46,8 +48,8 @@ def register_all_tools(
46
48
  disable_search_tools: bool = False,
47
49
  enabled_tools: dict[str, bool] | None = None,
48
50
  vector_config: dict | None = None,
49
- use_palette: bool = True,
50
- force_palette: str | None = None,
51
+ use_mode: bool = True,
52
+ force_mode: str | None = None,
51
53
  ) -> None:
52
54
  """Register all Hanzo tools with the MCP server.
53
55
 
@@ -65,20 +67,36 @@ def register_all_tools(
65
67
  disable_search_tools: Whether to disable search tools (default: False)
66
68
  enabled_tools: Dictionary of individual tool enable/disable states (default: None)
67
69
  vector_config: Vector store configuration (default: None)
68
- use_palette: Whether to use palette system for tool configuration (default: True)
69
- force_palette: Force a specific palette to be active (default: None)
70
+ use_mode: Whether to use mode system for tool configuration (default: True)
71
+ force_mode: Force a specific mode to be active (default: None)
70
72
  """
71
73
  # Dictionary to store all registered tools
72
74
  all_tools: dict[str, BaseTool] = {}
73
75
 
74
- # Apply palette configuration if enabled
75
- if use_palette:
76
- tool_config = PaletteLoader.get_enabled_tools_from_palette(
76
+ # Load user plugins early
77
+ try:
78
+ plugins = load_user_plugins()
79
+ import logging
80
+ logger = logging.getLogger(__name__)
81
+ if plugins:
82
+ logger.info(f"Loaded {len(plugins)} user plugin tools: {', '.join(plugins.keys())}")
83
+ except Exception as e:
84
+ import logging
85
+ logger = logging.getLogger(__name__)
86
+ logger.warning(f"Failed to load user plugins: {e}")
87
+ plugins = {}
88
+
89
+ # Apply mode configuration if enabled
90
+ if use_mode:
91
+ # First check for mode activation from environment
92
+ activate_mode_from_env()
93
+
94
+ tool_config = ModeLoader.get_enabled_tools_from_mode(
77
95
  base_enabled_tools=enabled_tools,
78
- force_palette=force_palette
96
+ force_mode=force_mode
79
97
  )
80
- # Apply palette environment variables
81
- PaletteLoader.apply_palette_environment()
98
+ # Apply mode environment variables
99
+ ModeLoader.apply_environment_from_mode()
82
100
  else:
83
101
  # Use individual tool configuration if provided, otherwise fall back to category-level flags
84
102
  tool_config = enabled_tools or {}
@@ -97,15 +115,16 @@ def register_all_tools(
97
115
  "multi_edit": is_tool_enabled("multi_edit", not disable_write_tools),
98
116
  "directory_tree": is_tool_enabled("directory_tree", True),
99
117
  "grep": is_tool_enabled("grep", not disable_search_tools),
100
- "grep_ast": is_tool_enabled("grep_ast", not disable_search_tools),
118
+ "symbols": is_tool_enabled("symbols", not disable_search_tools),
101
119
  "git_search": is_tool_enabled("git_search", not disable_search_tools),
102
120
  "content_replace": is_tool_enabled("content_replace", not disable_write_tools),
103
121
  "batch_search": is_tool_enabled("batch_search", not disable_search_tools),
104
122
  "find_files": is_tool_enabled("find_files", True),
105
- "unified_search": is_tool_enabled("unified_search", not disable_search_tools),
123
+ "rules": is_tool_enabled("rules", True),
124
+ "search": is_tool_enabled("search", not disable_search_tools),
106
125
  }
107
126
 
108
- # Vector tools setup (needed for unified search)
127
+ # Vector tools setup (needed for search)
109
128
  project_manager = None
110
129
  vector_enabled = {
111
130
  "vector_index": is_tool_enabled("vector_index", False),
@@ -113,7 +132,7 @@ def register_all_tools(
113
132
  }
114
133
 
115
134
  # Create project manager if vector tools, batch_search, or unified_search are enabled
116
- if any(vector_enabled.values()) or filesystem_enabled.get("batch_search", False) or filesystem_enabled.get("unified_search", False):
135
+ if any(vector_enabled.values()) or filesystem_enabled.get("batch_search", False) or filesystem_enabled.get("search", False):
117
136
  if vector_config:
118
137
  from hanzo_mcp.tools.vector.project_manager import ProjectVectorManager
119
138
  search_paths = [str(path) for path in permission_manager.allowed_paths]
@@ -124,7 +143,9 @@ def register_all_tools(
124
143
  )
125
144
  # Auto-detect projects from search paths
126
145
  detected_projects = project_manager.detect_projects(search_paths)
127
- print(f"Detected {len(detected_projects)} projects with LLM.md files")
146
+ import logging
147
+ logger = logging.getLogger(__name__)
148
+ logger.info(f"Detected {len(detected_projects)} projects with LLM.md files")
128
149
 
129
150
  filesystem_tools = register_filesystem_tools(
130
151
  mcp_server,
@@ -153,7 +174,7 @@ def register_all_tools(
153
174
  all_tools[tool.name] = tool
154
175
 
155
176
  # Register agent tools if enabled
156
- agent_enabled = enable_agent_tool or is_tool_enabled("dispatch_agent", False)
177
+ agent_enabled = enable_agent_tool or is_tool_enabled("agent", False) or is_tool_enabled("dispatch_agent", False)
157
178
  if agent_enabled:
158
179
  agent_tools = register_agent_tools(
159
180
  mcp_server,
@@ -170,12 +191,15 @@ def register_all_tools(
170
191
 
171
192
  # Register todo tools if enabled
172
193
  todo_enabled = {
194
+ "todo": is_tool_enabled("todo", True),
195
+ # Backward compatibility - if old names are used, enable the unified tool
173
196
  "todo_read": is_tool_enabled("todo_read", True),
174
197
  "todo_write": is_tool_enabled("todo_write", True),
175
198
  }
176
199
 
200
+ # Enable unified todo if any of the todo tools are enabled
177
201
  if any(todo_enabled.values()):
178
- todo_tools = register_todo_tools(mcp_server, enabled_tools=todo_enabled)
202
+ todo_tools = register_todo_tools(mcp_server, enabled_tools={"todo": True})
179
203
  for tool in todo_tools:
180
204
  all_tools[tool.name] = tool
181
205
 
@@ -184,6 +208,12 @@ def register_all_tools(
184
208
  thinking_tool = register_thinking_tool(mcp_server)
185
209
  for tool in thinking_tool:
186
210
  all_tools[tool.name] = tool
211
+
212
+ # Register critic tool if enabled
213
+ if is_tool_enabled("critic", True):
214
+ critic_tools = register_critic_tool(mcp_server)
215
+ for tool in critic_tools:
216
+ all_tools[tool.name] = tool
187
217
 
188
218
  # Register vector tools if enabled (reuse project_manager if available)
189
219
  if any(vector_enabled.values()) and project_manager:
@@ -228,7 +258,7 @@ def register_all_tools(
228
258
 
229
259
  # Register unified MCP tool if enabled
230
260
  if is_tool_enabled("mcp", True):
231
- tool = UnifiedMCPTool()
261
+ tool = MCPTool()
232
262
  tool.register(mcp_server)
233
263
  all_tools[tool.name] = tool
234
264
 
@@ -273,9 +303,9 @@ def register_all_tools(
273
303
  stats_tool.register(mcp_server)
274
304
  all_tools[stats_tool.name] = stats_tool
275
305
 
276
- # Palette tool (always enabled for managing tool sets)
277
- palette_tool.register(mcp_server)
278
- all_tools[palette_tool.name] = palette_tool
306
+ # Mode tool (always enabled for managing tool sets)
307
+ mode_tool.register(mcp_server)
308
+ all_tools[mode_tool.name] = mode_tool
279
309
 
280
310
  # Register editor tools if enabled
281
311
  editor_enabled = {
@@ -301,11 +331,18 @@ def register_all_tools(
301
331
 
302
332
  # Register unified LLM tool if enabled
303
333
  if is_tool_enabled("llm", True):
304
- tool = UnifiedLLMTool()
334
+ tool = LLMTool()
305
335
  if tool.available_providers: # Only register if API keys found
306
336
  tool.register(mcp_server)
307
337
  all_tools[tool.name] = tool
308
338
 
339
+ # Register consensus tool if enabled (enabled by default)
340
+ if is_tool_enabled("consensus", True):
341
+ tool = ConsensusTool()
342
+ if tool.llm_tool.available_providers:
343
+ tool.register(mcp_server)
344
+ all_tools[tool.name] = tool
345
+
309
346
  # Register legacy LLM tools if explicitly enabled (disabled by default)
310
347
  legacy_llm_enabled = {
311
348
  "llm_legacy": is_tool_enabled("llm_legacy", False),
@@ -337,3 +374,14 @@ def register_all_tools(
337
374
  if is_tool_enabled(tool.name, False):
338
375
  tool.register(mcp_server)
339
376
  all_tools[tool.name] = tool
377
+
378
+ # Register user plugins last (so they can override built-in tools)
379
+ for plugin_name, plugin in plugins.items():
380
+ if is_tool_enabled(plugin_name, True):
381
+ try:
382
+ tool = plugin.tool_class()
383
+ tool.register(mcp_server)
384
+ all_tools[tool.name] = tool
385
+ logger.info(f"Registered plugin tool: {plugin_name}")
386
+ except Exception as e:
387
+ logger.error(f"Failed to register plugin tool {plugin_name}: {e}")
@@ -1,4 +1,4 @@
1
- """Agent tools for Hanzo MCP.
1
+ """Agent tools for Hanzo AI.
2
2
 
3
3
  This module provides tools that allow Claude to delegate tasks to sub-agents,
4
4
  enabling concurrent execution of multiple operations and specialized processing.
@@ -1,4 +1,4 @@
1
- """Agent tool implementation for Hanzo MCP.
1
+ """Agent tool implementation for Hanzo AI.
2
2
 
3
3
  This module implements the AgentTool that allows Claude to delegate tasks to sub-agents,
4
4
  enabling concurrent execution of multiple operations and specialized processing.
@@ -75,7 +75,7 @@ class AgentTool(BaseTool):
75
75
  Returns:
76
76
  Tool name
77
77
  """
78
- return "dispatch_agent"
78
+ return "agent"
79
79
 
80
80
  @property
81
81
  @override
@@ -1,10 +1,11 @@
1
- """Common utilities for Hanzo MCP tools."""
1
+ """Common utilities for Hanzo AI tools."""
2
2
 
3
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
7
7
  from hanzo_mcp.tools.common.thinking_tool import ThinkingTool
8
+ from hanzo_mcp.tools.common.critic_tool import CriticTool
8
9
 
9
10
 
10
11
  def register_thinking_tool(
@@ -20,6 +21,19 @@ def register_thinking_tool(
20
21
  return [thinking_tool]
21
22
 
22
23
 
24
+ def register_critic_tool(
25
+ mcp_server: FastMCP,
26
+ ) -> list[BaseTool]:
27
+ """Register critic tool with the MCP server.
28
+
29
+ Args:
30
+ mcp_server: The FastMCP server instance
31
+ """
32
+ critic_tool = CriticTool()
33
+ ToolRegistry.register_tool(mcp_server, critic_tool)
34
+ return [critic_tool]
35
+
36
+
23
37
  def register_batch_tool(mcp_server: FastMCP, tools: dict[str, BaseTool]) -> None:
24
38
  """Register batch tool with the MCP server.
25
39
 
@@ -1,7 +1,7 @@
1
- """Base classes for Hanzo MCP tools.
1
+ """Base classes for Hanzo AI tools.
2
2
 
3
3
  This module provides abstract base classes that define interfaces and common functionality
4
- for all tools used in Hanzo MCP. These abstractions help ensure consistent tool
4
+ for all tools used in Hanzo AI. These abstractions help ensure consistent tool
5
5
  behavior and provide a foundation for tool registration and management.
6
6
  """
7
7
 
@@ -62,7 +62,7 @@ def handle_connection_errors(
62
62
 
63
63
 
64
64
  class BaseTool(ABC):
65
- """Abstract base class for all Hanzo MCP tools.
65
+ """Abstract base class for all Hanzo AI tools.
66
66
 
67
67
  This class defines the core interface that all tools must implement, ensuring
68
68
  consistency in how tools are registered, documented, and called.
@@ -156,7 +156,7 @@ class FileSystemTool(BaseTool, ABC):
156
156
 
157
157
  @final
158
158
  class ToolRegistry:
159
- """Registry for Hanzo MCP tools.
159
+ """Registry for Hanzo AI tools.
160
160
 
161
161
  Provides functionality for registering tool implementations with an MCP server,
162
162
  handling the conversion between tool classes and MCP tool functions.
@@ -1,4 +1,4 @@
1
- """Batch tool implementation for Hanzo MCP.
1
+ """Batch tool implementation for Hanzo AI.
2
2
 
3
3
  This module provides the BatchTool that allows for executing multiple tools in
4
4
  parallel or serial depending on their characteristics.
@@ -34,7 +34,7 @@ class ConfigToolParams(TypedDict, total=False):
34
34
 
35
35
  @final
36
36
  class ConfigTool(BaseTool):
37
- """Tool for managing Hanzo MCP configuration dynamically."""
37
+ """Tool for managing Hanzo AI configuration dynamically."""
38
38
 
39
39
  def __init__(self, permission_manager: PermissionManager):
40
40
  """Initialize the configuration tool.
@@ -52,7 +52,7 @@ class ConfigTool(BaseTool):
52
52
  @property
53
53
  def description(self) -> str:
54
54
  """Get the tool description."""
55
- return """Dynamically manage Hanzo MCP configuration settings through conversation.
55
+ return """Dynamically manage Hanzo AI configuration settings through conversation.
56
56
 
57
57
  Can get/set global settings, project-specific settings, manage MCP servers, configure tools,
58
58
  and handle project workflows. Supports dot-notation for nested settings like 'agent.enabled'.
@@ -1,4 +1,4 @@
1
- """Enhanced Context for Hanzo MCP tools.
1
+ """Enhanced Context for Hanzo AI tools.
2
2
 
3
3
  This module provides an enhanced Context class that wraps the MCP Context
4
4
  and adds additional functionality specific to Hanzo tools.
@@ -17,7 +17,7 @@ from mcp.server.lowlevel.helper_types import ReadResourceContents
17
17
 
18
18
  @final
19
19
  class ToolContext:
20
- """Enhanced context for Hanzo MCP tools.
20
+ """Enhanced context for Hanzo AI tools.
21
21
 
22
22
  This class wraps the MCP Context and adds additional functionality
23
23
  for tracking tool execution, progress reporting, and resource access.
@@ -0,0 +1,26 @@
1
+ """Context handling fix for MCP tools.
2
+
3
+ This module provides backward compatibility by re-exporting the
4
+ context normalization utilities from the decorators module.
5
+
6
+ DEPRECATED: Use hanzo_mcp.tools.common.decorators directly.
7
+ """
8
+
9
+ # Re-export for backward compatibility
10
+ from hanzo_mcp.tools.common.decorators import (
11
+ MockContext,
12
+ with_context_normalization,
13
+ _is_valid_context as is_valid_context,
14
+ )
15
+
16
+ # Backward compatibility function
17
+ def normalize_context(ctx):
18
+ """Normalize context - backward compatibility wrapper.
19
+
20
+ DEPRECATED: Use decorators.with_context_normalization instead.
21
+ """
22
+ if is_valid_context(ctx):
23
+ return ctx
24
+ return MockContext()
25
+
26
+ __all__ = ['MockContext', 'normalize_context', 'with_context_normalization']
@@ -0,0 +1,196 @@
1
+ """Critic tool implementation.
2
+
3
+ This module provides the CriticTool for Claude to engage in critical analysis and code review.
4
+ """
5
+
6
+ from typing import Annotated, TypedDict, Unpack, final, override
7
+
8
+ from mcp.server.fastmcp import Context as MCPContext
9
+ from mcp.server import FastMCP
10
+ from pydantic import Field
11
+
12
+ from hanzo_mcp.tools.common.base import BaseTool
13
+ from hanzo_mcp.tools.common.context import create_tool_context
14
+
15
+
16
+ Analysis = Annotated[
17
+ str,
18
+ Field(
19
+ description="The critical analysis to perform - code review, error detection, or improvement suggestions",
20
+ min_length=1,
21
+ ),
22
+ ]
23
+
24
+
25
+ class CriticToolParams(TypedDict):
26
+ """Parameters for the CriticTool.
27
+
28
+ Attributes:
29
+ analysis: The critical analysis to perform
30
+ """
31
+
32
+ analysis: Analysis
33
+
34
+
35
+ @final
36
+ class CriticTool(BaseTool):
37
+ """Tool for Claude to engage in critical analysis and play devil's advocate."""
38
+
39
+ @property
40
+ @override
41
+ def name(self) -> str:
42
+ """Get the tool name.
43
+
44
+ Returns:
45
+ Tool name
46
+ """
47
+ return "critic"
48
+
49
+ @property
50
+ @override
51
+ def description(self) -> str:
52
+ """Get the tool description.
53
+
54
+ Returns:
55
+ Tool description
56
+ """
57
+ return """Use this tool to perform critical analysis, play devil's advocate, and ensure high standards.
58
+ This tool forces a critical thinking mode that reviews all code for errors, improvements, and edge cases.
59
+ It ensures tests are run, tests pass, and maintains high quality standards.
60
+
61
+ This is your inner critic that:
62
+ - Always questions assumptions
63
+ - Looks for potential bugs and edge cases
64
+ - Ensures proper error handling
65
+ - Verifies test coverage
66
+ - Checks for performance issues
67
+ - Reviews security implications
68
+ - Suggests improvements and refactoring
69
+ - Ensures code follows best practices
70
+ - Questions design decisions
71
+ - Looks for missing documentation
72
+
73
+ Common use cases:
74
+ 1. Before finalizing any code changes - review for bugs, edge cases, and improvements
75
+ 2. After implementing a feature - critically analyze if it truly solves the problem
76
+ 3. When tests pass too easily - question if tests are comprehensive enough
77
+ 4. Before marking a task complete - ensure all quality standards are met
78
+ 5. When something seems too simple - look for hidden complexity or missing requirements
79
+ 6. After fixing a bug - analyze if the fix addresses root cause or just symptoms
80
+
81
+ <critic_example>
82
+ Code Review Analysis:
83
+ - Implementation Issues:
84
+ * No error handling for network failures in API calls
85
+ * Missing validation for user input boundaries
86
+ * Race condition possible in concurrent updates
87
+ * Memory leak potential in event listener registration
88
+
89
+ - Test Coverage Gaps:
90
+ * No tests for error scenarios
91
+ * Missing edge case: empty array input
92
+ * No performance benchmarks for large datasets
93
+ * Integration tests don't cover authentication failures
94
+
95
+ - Security Concerns:
96
+ * SQL injection vulnerability in query construction
97
+ * Missing rate limiting on public endpoints
98
+ * Sensitive data logged in debug mode
99
+
100
+ - Performance Issues:
101
+ * O(n²) algorithm where O(n log n) is possible
102
+ * Database queries in a loop (N+1 problem)
103
+ * No caching for expensive computations
104
+
105
+ - Code Quality:
106
+ * Functions too long and doing multiple things
107
+ * Inconsistent naming conventions
108
+ * Missing type annotations
109
+ * No documentation for complex algorithms
110
+
111
+ - Design Flaws:
112
+ * Tight coupling between modules
113
+ * Hard-coded configuration values
114
+ * No abstraction for external dependencies
115
+ * Violates single responsibility principle
116
+
117
+ Recommendations:
118
+ 1. Add comprehensive error handling with retry logic
119
+ 2. Implement input validation with clear error messages
120
+ 3. Use database transactions to prevent race conditions
121
+ 4. Add memory cleanup in component unmount
122
+ 5. Parameterize SQL queries to prevent injection
123
+ 6. Implement rate limiting middleware
124
+ 7. Use environment variables for sensitive config
125
+ 8. Refactor algorithm to use sorting approach
126
+ 9. Batch database queries
127
+ 10. Add memoization for expensive calculations
128
+ </critic_example>"""
129
+
130
+ def __init__(self) -> None:
131
+ """Initialize the critic tool."""
132
+ pass
133
+
134
+ @override
135
+ async def call(
136
+ self,
137
+ ctx: MCPContext,
138
+ **params: Unpack[CriticToolParams],
139
+ ) -> str:
140
+ """Execute the tool with the given parameters.
141
+
142
+ Args:
143
+ ctx: MCP context
144
+ **params: Tool parameters
145
+
146
+ Returns:
147
+ Tool result
148
+ """
149
+ tool_ctx = create_tool_context(ctx)
150
+ tool_ctx.set_tool_info(self.name)
151
+
152
+ # Extract parameters
153
+ analysis = params.get("analysis")
154
+
155
+ # Validate required analysis parameter
156
+ if not analysis:
157
+ await tool_ctx.error(
158
+ "Parameter 'analysis' is required but was None or empty"
159
+ )
160
+ return "Error: Parameter 'analysis' is required but was None or empty"
161
+
162
+ if analysis.strip() == "":
163
+ await tool_ctx.error("Parameter 'analysis' cannot be empty")
164
+ return "Error: Parameter 'analysis' cannot be empty"
165
+
166
+ # Log the critical analysis
167
+ await tool_ctx.info("Critical analysis recorded")
168
+
169
+ # Return confirmation with reminder to act on the analysis
170
+ return """Critical analysis complete. Remember to:
171
+ 1. Address all identified issues before proceeding
172
+ 2. Run comprehensive tests to verify fixes
173
+ 3. Ensure all tests pass with proper coverage
174
+ 4. Document any design decisions or trade-offs
175
+ 5. Consider the analysis points in your implementation
176
+
177
+ Continue with improvements based on this critical review."""
178
+
179
+ @override
180
+ def register(self, mcp_server: FastMCP) -> None:
181
+ """Register this critic tool with the MCP server.
182
+
183
+ Creates a wrapper function with explicitly defined parameters that match
184
+ the tool's parameter schema and registers it with the MCP server.
185
+
186
+ Args:
187
+ mcp_server: The FastMCP server instance
188
+ """
189
+ tool_self = self # Create a reference to self for use in the closure
190
+
191
+ @mcp_server.tool(name=self.name, description=self.description)
192
+ async def critic(
193
+ analysis: Analysis,
194
+ ctx: MCPContext
195
+ ) -> str:
196
+ return await tool_self.call(ctx, analysis=analysis)