hanzo-mcp 0.6.12__py3-none-any.whl → 0.7.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 (117) hide show
  1. hanzo_mcp/__init__.py +2 -2
  2. hanzo_mcp/analytics/__init__.py +5 -0
  3. hanzo_mcp/analytics/posthog_analytics.py +364 -0
  4. hanzo_mcp/cli.py +5 -5
  5. hanzo_mcp/cli_enhanced.py +7 -7
  6. hanzo_mcp/cli_plugin.py +91 -0
  7. hanzo_mcp/config/__init__.py +1 -1
  8. hanzo_mcp/config/settings.py +70 -7
  9. hanzo_mcp/config/tool_config.py +20 -6
  10. hanzo_mcp/dev_server.py +3 -3
  11. hanzo_mcp/prompts/project_system.py +1 -1
  12. hanzo_mcp/server.py +40 -3
  13. hanzo_mcp/server_enhanced.py +69 -0
  14. hanzo_mcp/tools/__init__.py +140 -31
  15. hanzo_mcp/tools/agent/__init__.py +85 -4
  16. hanzo_mcp/tools/agent/agent_tool.py +104 -6
  17. hanzo_mcp/tools/agent/agent_tool_v2.py +459 -0
  18. hanzo_mcp/tools/agent/clarification_protocol.py +220 -0
  19. hanzo_mcp/tools/agent/clarification_tool.py +68 -0
  20. hanzo_mcp/tools/agent/claude_cli_tool.py +125 -0
  21. hanzo_mcp/tools/agent/claude_desktop_auth.py +508 -0
  22. hanzo_mcp/tools/agent/cli_agent_base.py +191 -0
  23. hanzo_mcp/tools/agent/code_auth.py +436 -0
  24. hanzo_mcp/tools/agent/code_auth_tool.py +194 -0
  25. hanzo_mcp/tools/agent/codex_cli_tool.py +123 -0
  26. hanzo_mcp/tools/agent/critic_tool.py +376 -0
  27. hanzo_mcp/tools/agent/gemini_cli_tool.py +128 -0
  28. hanzo_mcp/tools/agent/grok_cli_tool.py +128 -0
  29. hanzo_mcp/tools/agent/iching_tool.py +380 -0
  30. hanzo_mcp/tools/agent/network_tool.py +273 -0
  31. hanzo_mcp/tools/agent/prompt.py +62 -20
  32. hanzo_mcp/tools/agent/review_tool.py +433 -0
  33. hanzo_mcp/tools/agent/swarm_tool.py +535 -0
  34. hanzo_mcp/tools/agent/swarm_tool_v2.py +594 -0
  35. hanzo_mcp/tools/common/__init__.py +15 -1
  36. hanzo_mcp/tools/common/base.py +5 -4
  37. hanzo_mcp/tools/common/batch_tool.py +103 -11
  38. hanzo_mcp/tools/common/config_tool.py +2 -2
  39. hanzo_mcp/tools/common/context.py +2 -2
  40. hanzo_mcp/tools/common/context_fix.py +26 -0
  41. hanzo_mcp/tools/common/critic_tool.py +196 -0
  42. hanzo_mcp/tools/common/decorators.py +208 -0
  43. hanzo_mcp/tools/common/enhanced_base.py +106 -0
  44. hanzo_mcp/tools/common/fastmcp_pagination.py +369 -0
  45. hanzo_mcp/tools/common/forgiving_edit.py +243 -0
  46. hanzo_mcp/tools/common/mode.py +116 -0
  47. hanzo_mcp/tools/common/mode_loader.py +105 -0
  48. hanzo_mcp/tools/common/paginated_base.py +230 -0
  49. hanzo_mcp/tools/common/paginated_response.py +307 -0
  50. hanzo_mcp/tools/common/pagination.py +226 -0
  51. hanzo_mcp/tools/common/permissions.py +1 -1
  52. hanzo_mcp/tools/common/personality.py +936 -0
  53. hanzo_mcp/tools/common/plugin_loader.py +287 -0
  54. hanzo_mcp/tools/common/stats.py +4 -4
  55. hanzo_mcp/tools/common/tool_list.py +4 -1
  56. hanzo_mcp/tools/common/truncate.py +101 -0
  57. hanzo_mcp/tools/common/validation.py +1 -1
  58. hanzo_mcp/tools/config/__init__.py +3 -1
  59. hanzo_mcp/tools/config/config_tool.py +1 -1
  60. hanzo_mcp/tools/config/mode_tool.py +209 -0
  61. hanzo_mcp/tools/database/__init__.py +1 -1
  62. hanzo_mcp/tools/editor/__init__.py +1 -1
  63. hanzo_mcp/tools/filesystem/__init__.py +48 -14
  64. hanzo_mcp/tools/filesystem/ast_multi_edit.py +562 -0
  65. hanzo_mcp/tools/filesystem/batch_search.py +3 -3
  66. hanzo_mcp/tools/filesystem/diff.py +2 -2
  67. hanzo_mcp/tools/filesystem/directory_tree_paginated.py +338 -0
  68. hanzo_mcp/tools/filesystem/rules_tool.py +235 -0
  69. hanzo_mcp/tools/filesystem/{unified_search.py → search_tool.py} +12 -12
  70. hanzo_mcp/tools/filesystem/{symbols_unified.py → symbols_tool.py} +104 -5
  71. hanzo_mcp/tools/filesystem/watch.py +3 -2
  72. hanzo_mcp/tools/jupyter/__init__.py +2 -2
  73. hanzo_mcp/tools/jupyter/jupyter.py +1 -1
  74. hanzo_mcp/tools/llm/__init__.py +3 -3
  75. hanzo_mcp/tools/llm/llm_tool.py +648 -143
  76. hanzo_mcp/tools/lsp/__init__.py +5 -0
  77. hanzo_mcp/tools/lsp/lsp_tool.py +512 -0
  78. hanzo_mcp/tools/mcp/__init__.py +2 -2
  79. hanzo_mcp/tools/mcp/{mcp_unified.py → mcp_tool.py} +3 -3
  80. hanzo_mcp/tools/memory/__init__.py +76 -0
  81. hanzo_mcp/tools/memory/knowledge_tools.py +518 -0
  82. hanzo_mcp/tools/memory/memory_tools.py +456 -0
  83. hanzo_mcp/tools/search/__init__.py +6 -0
  84. hanzo_mcp/tools/search/find_tool.py +581 -0
  85. hanzo_mcp/tools/search/unified_search.py +953 -0
  86. hanzo_mcp/tools/shell/__init__.py +11 -6
  87. hanzo_mcp/tools/shell/auto_background.py +203 -0
  88. hanzo_mcp/tools/shell/base_process.py +57 -29
  89. hanzo_mcp/tools/shell/bash_session_executor.py +1 -1
  90. hanzo_mcp/tools/shell/{bash_unified.py → bash_tool.py} +18 -34
  91. hanzo_mcp/tools/shell/command_executor.py +2 -2
  92. hanzo_mcp/tools/shell/{npx_unified.py → npx_tool.py} +16 -33
  93. hanzo_mcp/tools/shell/open.py +2 -2
  94. hanzo_mcp/tools/shell/{process_unified.py → process_tool.py} +1 -1
  95. hanzo_mcp/tools/shell/run_command_windows.py +1 -1
  96. hanzo_mcp/tools/shell/streaming_command.py +594 -0
  97. hanzo_mcp/tools/shell/uvx.py +47 -2
  98. hanzo_mcp/tools/shell/uvx_background.py +47 -2
  99. hanzo_mcp/tools/shell/{uvx_unified.py → uvx_tool.py} +16 -33
  100. hanzo_mcp/tools/todo/__init__.py +14 -19
  101. hanzo_mcp/tools/todo/todo.py +22 -1
  102. hanzo_mcp/tools/vector/__init__.py +1 -1
  103. hanzo_mcp/tools/vector/infinity_store.py +2 -2
  104. hanzo_mcp/tools/vector/project_manager.py +1 -1
  105. hanzo_mcp/types.py +23 -0
  106. hanzo_mcp-0.7.0.dist-info/METADATA +516 -0
  107. hanzo_mcp-0.7.0.dist-info/RECORD +180 -0
  108. {hanzo_mcp-0.6.12.dist-info → hanzo_mcp-0.7.0.dist-info}/entry_points.txt +1 -0
  109. hanzo_mcp/tools/common/palette.py +0 -344
  110. hanzo_mcp/tools/common/palette_loader.py +0 -108
  111. hanzo_mcp/tools/config/palette_tool.py +0 -179
  112. hanzo_mcp/tools/llm/llm_unified.py +0 -851
  113. hanzo_mcp-0.6.12.dist-info/METADATA +0 -339
  114. hanzo_mcp-0.6.12.dist-info/RECORD +0 -135
  115. hanzo_mcp-0.6.12.dist-info/licenses/LICENSE +0 -21
  116. {hanzo_mcp-0.6.12.dist-info → hanzo_mcp-0.7.0.dist-info}/WHEEL +0 -0
  117. {hanzo_mcp-0.6.12.dist-info → hanzo_mcp-0.7.0.dist-info}/top_level.txt +0 -0
@@ -1,4 +1,4 @@
1
- """Settings management for Hanzo MCP.
1
+ """Settings management for Hanzo AI.
2
2
 
3
3
  Handles loading and saving configuration from multiple sources:
4
4
  1. Default settings
@@ -59,6 +59,7 @@ class AgentConfig:
59
59
  enabled: bool = False
60
60
  model: Optional[str] = None
61
61
  api_key: Optional[str] = None
62
+ hanzo_api_key: Optional[str] = None # HANZO_API_KEY support
62
63
  base_url: Optional[str] = None
63
64
  max_tokens: Optional[int] = None
64
65
  max_iterations: int = 10
@@ -70,7 +71,7 @@ class ServerConfig:
70
71
  """Configuration for the MCP server."""
71
72
  name: str = "hanzo-mcp"
72
73
  host: str = "127.0.0.1"
73
- port: int = 3000
74
+ port: int = 8888
74
75
  transport: str = "stdio" # stdio or sse
75
76
  log_level: str = "INFO"
76
77
  command_timeout: float = 120.0
@@ -78,7 +79,7 @@ class ServerConfig:
78
79
 
79
80
  @dataclass
80
81
  class HanzoMCPSettings:
81
- """Complete configuration for Hanzo MCP."""
82
+ """Complete configuration for Hanzo AI."""
82
83
  # Server settings
83
84
  server: ServerConfig = field(default_factory=ServerConfig)
84
85
 
@@ -106,6 +107,9 @@ class HanzoMCPSettings:
106
107
  projects: Dict[str, ProjectConfig] = field(default_factory=dict)
107
108
  current_project: Optional[str] = None
108
109
 
110
+ # Mode configuration
111
+ active_mode: Optional[str] = None
112
+
109
113
  def __post_init__(self):
110
114
  """Initialize default tool states if not specified."""
111
115
  if not self.enabled_tools:
@@ -250,7 +254,7 @@ class HanzoMCPSettings:
250
254
 
251
255
 
252
256
  def get_config_dir() -> Path:
253
- """Get the configuration directory for Hanzo MCP."""
257
+ """Get the configuration directory for Hanzo AI."""
254
258
  if os.name == "nt": # Windows
255
259
  config_dir = Path(os.environ.get("APPDATA", "")) / "hanzo"
256
260
  else: # Unix/macOS
@@ -341,6 +345,59 @@ def detect_project_from_path(file_path: str) -> Optional[Dict[str, str]]:
341
345
  return None
342
346
 
343
347
 
348
+ def _load_from_env() -> Dict[str, Any]:
349
+ """Load configuration from environment variables."""
350
+ config = {}
351
+
352
+ # Check for agent API keys
353
+ has_api_keys = False
354
+
355
+ # HANZO_API_KEY
356
+ if hanzo_key := os.environ.get("HANZO_API_KEY"):
357
+ config.setdefault("agent", {})["hanzo_api_key"] = hanzo_key
358
+ config["agent"]["enabled"] = True
359
+ has_api_keys = True
360
+
361
+ # Check for other API keys
362
+ api_key_env_vars = [
363
+ ("OPENAI_API_KEY", "openai"),
364
+ ("ANTHROPIC_API_KEY", "anthropic"),
365
+ ("GOOGLE_API_KEY", "google"),
366
+ ("GROQ_API_KEY", "groq"),
367
+ ("TOGETHER_API_KEY", "together"),
368
+ ("MISTRAL_API_KEY", "mistral"),
369
+ ("PERPLEXITY_API_KEY", "perplexity"),
370
+ ]
371
+
372
+ for env_var, provider in api_key_env_vars:
373
+ if os.environ.get(env_var):
374
+ has_api_keys = True
375
+ break
376
+
377
+ # Auto-enable agent and consensus tools if API keys present
378
+ if has_api_keys:
379
+ config.setdefault("enabled_tools", {})
380
+ config["enabled_tools"]["agent"] = True
381
+ config["enabled_tools"]["consensus"] = True
382
+ config.setdefault("agent", {})["enabled"] = True
383
+
384
+ # Check for MODE/PERSONALITY/HANZO_MODE
385
+ if mode := os.environ.get("HANZO_MODE") or os.environ.get("PERSONALITY") or os.environ.get("MODE"):
386
+ config["active_mode"] = mode
387
+
388
+ # Check for other environment overrides
389
+ if project_dir := os.environ.get("HANZO_PROJECT_DIR"):
390
+ config["project_dir"] = project_dir
391
+
392
+ if log_level := os.environ.get("HANZO_LOG_LEVEL"):
393
+ config.setdefault("server", {})["log_level"] = log_level
394
+
395
+ if allowed_paths := os.environ.get("HANZO_ALLOWED_PATHS"):
396
+ config["allowed_paths"] = allowed_paths.split(":")
397
+
398
+ return config
399
+
400
+
344
401
  def load_settings(
345
402
  project_dir: Optional[str] = None,
346
403
  config_overrides: Optional[Dict[str, Any]] = None
@@ -349,9 +406,10 @@ def load_settings(
349
406
 
350
407
  Priority (highest to lowest):
351
408
  1. config_overrides (usually from CLI)
352
- 2. Project-specific config file
353
- 3. Global config file
354
- 4. Defaults
409
+ 2. Environment variables
410
+ 3. Project-specific config file
411
+ 4. Global config file
412
+ 5. Defaults
355
413
  """
356
414
  # Start with defaults
357
415
  settings = HanzoMCPSettings()
@@ -380,6 +438,11 @@ def load_settings(
380
438
  logger = logging.getLogger(__name__)
381
439
  logger.warning(f"Failed to load project config: {e}")
382
440
 
441
+ # Apply environment variables
442
+ env_config = _load_from_env()
443
+ if env_config:
444
+ settings = _merge_config(settings, env_config)
445
+
383
446
  # Apply CLI overrides
384
447
  if config_overrides:
385
448
  settings = _merge_config(settings, config_overrides)
@@ -1,4 +1,4 @@
1
- """Tool configuration definitions for Hanzo MCP."""
1
+ """Tool configuration definitions for Hanzo AI."""
2
2
 
3
3
  from enum import Enum
4
4
  from typing import Dict, List, Optional
@@ -6,7 +6,7 @@ from dataclasses import dataclass
6
6
 
7
7
 
8
8
  class ToolCategory(str, Enum):
9
- """Categories of tools available in Hanzo MCP."""
9
+ """Categories of tools available in Hanzo AI."""
10
10
  FILESYSTEM = "filesystem"
11
11
  SHELL = "shell"
12
12
  JUPYTER = "jupyter"
@@ -71,11 +71,11 @@ TOOL_REGISTRY: Dict[str, ToolConfig] = {
71
71
  description="Fast content search using ripgrep or fallback Python implementation",
72
72
  cli_flag="--disable-grep"
73
73
  ),
74
- "grep_ast": ToolConfig(
75
- name="grep_ast",
74
+ "ast": ToolConfig(
75
+ name="ast",
76
76
  category=ToolCategory.FILESYSTEM,
77
77
  description="Search source code with AST context using tree-sitter",
78
- cli_flag="--disable-grep-ast"
78
+ cli_flag="--disable-ast"
79
79
  ),
80
80
  "content_replace": ToolConfig(
81
81
  name="content_replace",
@@ -120,7 +120,7 @@ TOOL_REGISTRY: Dict[str, ToolConfig] = {
120
120
  cli_flag="--disable-todo-write"
121
121
  ),
122
122
 
123
- # Agent Tools (1)
123
+ # Agent Tools (3)
124
124
  "dispatch_agent": ToolConfig(
125
125
  name="dispatch_agent",
126
126
  category=ToolCategory.AGENT,
@@ -128,6 +128,20 @@ TOOL_REGISTRY: Dict[str, ToolConfig] = {
128
128
  description="Delegate tasks to sub-agents for concurrent/specialized processing",
129
129
  cli_flag="--enable-dispatch-agent"
130
130
  ),
131
+ "swarm": ToolConfig(
132
+ name="swarm",
133
+ category=ToolCategory.AGENT,
134
+ enabled=False, # Disabled by default
135
+ description="Execute multiple agent tasks in parallel across different files",
136
+ cli_flag="--enable-swarm"
137
+ ),
138
+ "hierarchical_swarm": ToolConfig(
139
+ name="hierarchical_swarm",
140
+ category=ToolCategory.AGENT,
141
+ enabled=False, # Disabled by default
142
+ description="Execute hierarchical agent swarms with Claude Code integration",
143
+ cli_flag="--enable-hierarchical-swarm"
144
+ ),
131
145
 
132
146
  # Common Tools (3)
133
147
  "think": ToolConfig(
hanzo_mcp/dev_server.py CHANGED
@@ -1,4 +1,4 @@
1
- """Development server with hot reload for Hanzo MCP."""
1
+ """Development server with hot reload for Hanzo AI."""
2
2
 
3
3
  import asyncio
4
4
  import logging
@@ -159,7 +159,7 @@ class DevServer:
159
159
  self.running = True
160
160
 
161
161
  logger = logging.getLogger(__name__)
162
- logger.info(f"\n🚀 Starting Hanzo MCP in development mode...")
162
+ logger.info(f"\n🚀 Starting Hanzo AI in development mode...")
163
163
  logger.info(f"🔧 Hot reload enabled - watching for file changes")
164
164
  logger.info(f"📁 Project: {self.project_dir or 'current directory'}")
165
165
  logger.info(f"🌐 Transport: {transport}\n")
@@ -194,7 +194,7 @@ def run_dev_server():
194
194
  """Entry point for development server."""
195
195
  import argparse
196
196
 
197
- parser = argparse.ArgumentParser(description="Run Hanzo MCP in development mode with hot reload")
197
+ parser = argparse.ArgumentParser(description="Run Hanzo AI in development mode with hot reload")
198
198
  parser.add_argument(
199
199
  "--name",
200
200
  type=str,
@@ -30,7 +30,7 @@ Recent commits:
30
30
  </project_info>
31
31
 
32
32
  <available_tools>
33
- Hanzo MCP provides 65+ tools organized by category. Key tools include:
33
+ Hanzo AI provides 65+ tools organized by category. Key tools include:
34
34
 
35
35
  # File Operations
36
36
  - read, write, edit, multi_edit: File manipulation
hanzo_mcp/server.py CHANGED
@@ -16,6 +16,9 @@ except ImportError:
16
16
  # Fallback for older MCP versions
17
17
  from mcp.server import FastMCP
18
18
 
19
+ # Import our enhanced server
20
+ from hanzo_mcp.server_enhanced import EnhancedFastMCP
21
+
19
22
  from hanzo_mcp.prompts import register_all_prompts
20
23
  from hanzo_mcp.tools import register_all_tools
21
24
 
@@ -45,11 +48,11 @@ class HanzoMCPServer:
45
48
  disable_write_tools: bool = False,
46
49
  disable_search_tools: bool = False,
47
50
  host: str = "127.0.0.1",
48
- port: int = 3000,
51
+ port: int = 8888,
49
52
  enabled_tools: dict[str, bool] | None = None,
50
53
  disabled_tools: list[str] | None = None,
51
54
  ):
52
- """Initialize the Hanzo MCP server.
55
+ """Initialize the Hanzo AI server.
53
56
 
54
57
  Args:
55
58
  name: The name of the server
@@ -72,7 +75,8 @@ class HanzoMCPServer:
72
75
  enabled_tools: Dictionary of individual tool enable states (default: None)
73
76
  disabled_tools: List of tool names to disable (default: None)
74
77
  """
75
- self.mcp = mcp_instance if mcp_instance is not None else FastMCP(name)
78
+ # Use enhanced server for automatic context normalization
79
+ self.mcp = mcp_instance if mcp_instance is not None else EnhancedFastMCP(name)
76
80
 
77
81
  # Initialize permissions and command executor
78
82
  self.permission_manager = PermissionManager()
@@ -222,3 +226,36 @@ class HanzoMCPServer:
222
226
  # Run the server
223
227
  transport_type = cast(Literal["stdio", "sse"], transport)
224
228
  self.mcp.run(transport=transport_type)
229
+
230
+
231
+ def create_server(
232
+ name: str = "hanzo-mcp",
233
+ allowed_paths: list[str] | None = None,
234
+ enable_all_tools: bool = False,
235
+ **kwargs
236
+ ) -> HanzoMCPServer:
237
+ """Create a Hanzo MCP server instance.
238
+
239
+ Args:
240
+ name: Server name
241
+ allowed_paths: List of allowed file paths
242
+ enable_all_tools: Enable all tools including agent tools
243
+ **kwargs: Additional server configuration
244
+
245
+ Returns:
246
+ HanzoMCPServer instance
247
+ """
248
+ if enable_all_tools:
249
+ kwargs['enable_agent_tool'] = True
250
+
251
+ return HanzoMCPServer(
252
+ name=name,
253
+ allowed_paths=allowed_paths,
254
+ **kwargs
255
+ )
256
+
257
+
258
+ def main():
259
+ """Main entry point for the server."""
260
+ from hanzo_mcp.cli import main as cli_main
261
+ cli_main()
@@ -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,28 @@ 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
+
29
+ # Try to import memory tools, but don't fail if hanzo-memory is not installed
30
+ try:
31
+ from hanzo_mcp.tools.memory import register_memory_tools
32
+ MEMORY_TOOLS_AVAILABLE = True
33
+ except ImportError:
34
+ MEMORY_TOOLS_AVAILABLE = False
35
+ register_memory_tools = None
36
+ from hanzo_mcp.tools.mcp import MCPTool, McpAddTool, McpRemoveTool, McpStatsTool
29
37
  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
38
+ from hanzo_mcp.tools.llm import LLMTool, LLMTool, ConsensusTool, LLMManageTool, create_provider_tools
39
+ from hanzo_mcp.tools.config.mode_tool import mode_tool
40
+ from hanzo_mcp.tools.common.mode_loader import ModeLoader
41
+ from hanzo_mcp.tools.common.mode import activate_mode_from_env
42
+ from hanzo_mcp.tools.common.plugin_loader import load_user_plugins
43
+
44
+ # Try to import LSP tool
45
+ try:
46
+ from hanzo_mcp.tools.lsp import LSPTool, create_lsp_tool
47
+ LSP_TOOL_AVAILABLE = True
48
+ except ImportError:
49
+ LSP_TOOL_AVAILABLE = False
33
50
 
34
51
 
35
52
  def register_all_tools(
@@ -46,8 +63,8 @@ def register_all_tools(
46
63
  disable_search_tools: bool = False,
47
64
  enabled_tools: dict[str, bool] | None = None,
48
65
  vector_config: dict | None = None,
49
- use_palette: bool = True,
50
- force_palette: str | None = None,
66
+ use_mode: bool = True,
67
+ force_mode: str | None = None,
51
68
  ) -> None:
52
69
  """Register all Hanzo tools with the MCP server.
53
70
 
@@ -65,20 +82,36 @@ def register_all_tools(
65
82
  disable_search_tools: Whether to disable search tools (default: False)
66
83
  enabled_tools: Dictionary of individual tool enable/disable states (default: None)
67
84
  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)
85
+ use_mode: Whether to use mode system for tool configuration (default: True)
86
+ force_mode: Force a specific mode to be active (default: None)
70
87
  """
71
88
  # Dictionary to store all registered tools
72
89
  all_tools: dict[str, BaseTool] = {}
73
90
 
74
- # Apply palette configuration if enabled
75
- if use_palette:
76
- tool_config = PaletteLoader.get_enabled_tools_from_palette(
91
+ # Load user plugins early
92
+ try:
93
+ plugins = load_user_plugins()
94
+ import logging
95
+ logger = logging.getLogger(__name__)
96
+ if plugins:
97
+ logger.info(f"Loaded {len(plugins)} user plugin tools: {', '.join(plugins.keys())}")
98
+ except Exception as e:
99
+ import logging
100
+ logger = logging.getLogger(__name__)
101
+ logger.warning(f"Failed to load user plugins: {e}")
102
+ plugins = {}
103
+
104
+ # Apply mode configuration if enabled
105
+ if use_mode:
106
+ # First check for mode activation from environment
107
+ activate_mode_from_env()
108
+
109
+ tool_config = ModeLoader.get_enabled_tools_from_mode(
77
110
  base_enabled_tools=enabled_tools,
78
- force_palette=force_palette
111
+ force_mode=force_mode
79
112
  )
80
- # Apply palette environment variables
81
- PaletteLoader.apply_palette_environment()
113
+ # Apply mode environment variables
114
+ ModeLoader.apply_environment_from_mode()
82
115
  else:
83
116
  # Use individual tool configuration if provided, otherwise fall back to category-level flags
84
117
  tool_config = enabled_tools or {}
@@ -97,15 +130,18 @@ def register_all_tools(
97
130
  "multi_edit": is_tool_enabled("multi_edit", not disable_write_tools),
98
131
  "directory_tree": is_tool_enabled("directory_tree", True),
99
132
  "grep": is_tool_enabled("grep", not disable_search_tools),
100
- "grep_ast": is_tool_enabled("grep_ast", not disable_search_tools),
133
+ "symbols": is_tool_enabled("symbols", not disable_search_tools),
101
134
  "git_search": is_tool_enabled("git_search", not disable_search_tools),
102
135
  "content_replace": is_tool_enabled("content_replace", not disable_write_tools),
103
136
  "batch_search": is_tool_enabled("batch_search", not disable_search_tools),
104
137
  "find_files": is_tool_enabled("find_files", True),
105
- "unified_search": is_tool_enabled("unified_search", not disable_search_tools),
138
+ "rules": is_tool_enabled("rules", True),
139
+ "search": is_tool_enabled("search", not disable_search_tools),
140
+ "unified_search": is_tool_enabled("unified_search", True), # Primary search tool
141
+ "find": is_tool_enabled("find", True), # Fast file finder
106
142
  }
107
143
 
108
- # Vector tools setup (needed for unified search)
144
+ # Vector tools setup (needed for search)
109
145
  project_manager = None
110
146
  vector_enabled = {
111
147
  "vector_index": is_tool_enabled("vector_index", False),
@@ -113,7 +149,7 @@ def register_all_tools(
113
149
  }
114
150
 
115
151
  # 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):
152
+ if any(vector_enabled.values()) or filesystem_enabled.get("batch_search", False) or filesystem_enabled.get("search", False):
117
153
  if vector_config:
118
154
  from hanzo_mcp.tools.vector.project_manager import ProjectVectorManager
119
155
  search_paths = [str(path) for path in permission_manager.allowed_paths]
@@ -155,8 +191,10 @@ def register_all_tools(
155
191
  all_tools[tool.name] = tool
156
192
 
157
193
  # Register agent tools if enabled
158
- agent_enabled = enable_agent_tool or is_tool_enabled("dispatch_agent", False)
159
- if agent_enabled:
194
+ agent_enabled = enable_agent_tool or is_tool_enabled("agent", False) or is_tool_enabled("dispatch_agent", False)
195
+ swarm_enabled = is_tool_enabled("swarm", False)
196
+
197
+ if agent_enabled or swarm_enabled:
160
198
  agent_tools = register_agent_tools(
161
199
  mcp_server,
162
200
  permission_manager,
@@ -167,17 +205,27 @@ def register_all_tools(
167
205
  agent_max_iterations=agent_max_iterations,
168
206
  agent_max_tool_uses=agent_max_tool_uses,
169
207
  )
208
+ # Filter based on what's enabled
170
209
  for tool in agent_tools:
171
- all_tools[tool.name] = tool
210
+ if tool.name == "agent" and agent_enabled:
211
+ all_tools[tool.name] = tool
212
+ elif tool.name == "swarm" and swarm_enabled:
213
+ all_tools[tool.name] = tool
214
+ elif tool.name in ["claude", "codex", "gemini", "grok", "code_auth"]:
215
+ # CLI tools and auth are always included when agent tools are enabled
216
+ all_tools[tool.name] = tool
172
217
 
173
218
  # Register todo tools if enabled
174
219
  todo_enabled = {
220
+ "todo": is_tool_enabled("todo", True),
221
+ # Backward compatibility - if old names are used, enable the unified tool
175
222
  "todo_read": is_tool_enabled("todo_read", True),
176
223
  "todo_write": is_tool_enabled("todo_write", True),
177
224
  }
178
225
 
226
+ # Enable unified todo if any of the todo tools are enabled
179
227
  if any(todo_enabled.values()):
180
- todo_tools = register_todo_tools(mcp_server, enabled_tools=todo_enabled)
228
+ todo_tools = register_todo_tools(mcp_server, enabled_tools={"todo": True})
181
229
  for tool in todo_tools:
182
230
  all_tools[tool.name] = tool
183
231
 
@@ -186,6 +234,12 @@ def register_all_tools(
186
234
  thinking_tool = register_thinking_tool(mcp_server)
187
235
  for tool in thinking_tool:
188
236
  all_tools[tool.name] = tool
237
+
238
+ # Register critic tool if enabled
239
+ if is_tool_enabled("critic", True):
240
+ critic_tools = register_critic_tool(mcp_server)
241
+ for tool in critic_tools:
242
+ all_tools[tool.name] = tool
189
243
 
190
244
  # Register vector tools if enabled (reuse project_manager if available)
191
245
  if any(vector_enabled.values()) and project_manager:
@@ -230,7 +284,7 @@ def register_all_tools(
230
284
 
231
285
  # Register unified MCP tool if enabled
232
286
  if is_tool_enabled("mcp", True):
233
- tool = UnifiedMCPTool()
287
+ tool = MCPTool()
234
288
  tool.register(mcp_server)
235
289
  all_tools[tool.name] = tool
236
290
 
@@ -275,9 +329,9 @@ def register_all_tools(
275
329
  stats_tool.register(mcp_server)
276
330
  all_tools[stats_tool.name] = stats_tool
277
331
 
278
- # Palette tool (always enabled for managing tool sets)
279
- palette_tool.register(mcp_server)
280
- all_tools[palette_tool.name] = palette_tool
332
+ # Mode tool (always enabled for managing tool sets)
333
+ mode_tool.register(mcp_server)
334
+ all_tools[mode_tool.name] = mode_tool
281
335
 
282
336
  # Register editor tools if enabled
283
337
  editor_enabled = {
@@ -303,11 +357,18 @@ def register_all_tools(
303
357
 
304
358
  # Register unified LLM tool if enabled
305
359
  if is_tool_enabled("llm", True):
306
- tool = UnifiedLLMTool()
360
+ tool = LLMTool()
307
361
  if tool.available_providers: # Only register if API keys found
308
362
  tool.register(mcp_server)
309
363
  all_tools[tool.name] = tool
310
364
 
365
+ # Register consensus tool if enabled (enabled by default)
366
+ if is_tool_enabled("consensus", True):
367
+ tool = ConsensusTool()
368
+ if tool.llm_tool.available_providers:
369
+ tool.register(mcp_server)
370
+ all_tools[tool.name] = tool
371
+
311
372
  # Register legacy LLM tools if explicitly enabled (disabled by default)
312
373
  legacy_llm_enabled = {
313
374
  "llm_legacy": is_tool_enabled("llm_legacy", False),
@@ -339,3 +400,51 @@ def register_all_tools(
339
400
  if is_tool_enabled(tool.name, False):
340
401
  tool.register(mcp_server)
341
402
  all_tools[tool.name] = tool
403
+
404
+ # Register memory tools if enabled
405
+ memory_enabled = {
406
+ "recall_memories": is_tool_enabled("recall_memories", True),
407
+ "create_memories": is_tool_enabled("create_memories", True),
408
+ "update_memories": is_tool_enabled("update_memories", True),
409
+ "delete_memories": is_tool_enabled("delete_memories", True),
410
+ "manage_memories": is_tool_enabled("manage_memories", True),
411
+ "recall_facts": is_tool_enabled("recall_facts", True),
412
+ "store_facts": is_tool_enabled("store_facts", True),
413
+ "summarize_to_memory": is_tool_enabled("summarize_to_memory", True),
414
+ "manage_knowledge_bases": is_tool_enabled("manage_knowledge_bases", True),
415
+ }
416
+
417
+ if any(memory_enabled.values()) and MEMORY_TOOLS_AVAILABLE:
418
+ try:
419
+ memory_tools = register_memory_tools(
420
+ mcp_server,
421
+ permission_manager,
422
+ user_id="default",
423
+ project_id="default"
424
+ )
425
+ # Filter based on enabled state
426
+ for tool in memory_tools:
427
+ if memory_enabled.get(tool.name, True):
428
+ all_tools[tool.name] = tool
429
+ except Exception as e:
430
+ logger.warning(f"Failed to register memory tools: {e}")
431
+
432
+ # Register LSP tool if enabled
433
+ if is_tool_enabled("lsp", True) and LSP_TOOL_AVAILABLE:
434
+ try:
435
+ tool = create_lsp_tool()
436
+ tool.register(mcp_server)
437
+ all_tools[tool.name] = tool
438
+ except Exception as e:
439
+ logger.warning(f"Failed to register LSP tool: {e}")
440
+
441
+ # Register user plugins last (so they can override built-in tools)
442
+ for plugin_name, plugin in plugins.items():
443
+ if is_tool_enabled(plugin_name, True):
444
+ try:
445
+ tool = plugin.tool_class()
446
+ tool.register(mcp_server)
447
+ all_tools[tool.name] = tool
448
+ logger.info(f"Registered plugin tool: {plugin_name}")
449
+ except Exception as e:
450
+ logger.error(f"Failed to register plugin tool {plugin_name}: {e}")