hanzo-mcp 0.6.12__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.
- hanzo_mcp/__init__.py +2 -2
- hanzo_mcp/cli.py +2 -2
- hanzo_mcp/cli_enhanced.py +4 -4
- hanzo_mcp/cli_plugin.py +91 -0
- hanzo_mcp/config/__init__.py +1 -1
- hanzo_mcp/config/settings.py +69 -6
- hanzo_mcp/config/tool_config.py +2 -2
- hanzo_mcp/dev_server.py +3 -3
- hanzo_mcp/prompts/project_system.py +1 -1
- hanzo_mcp/server.py +6 -2
- hanzo_mcp/server_enhanced.py +69 -0
- hanzo_mcp/tools/__init__.py +75 -29
- hanzo_mcp/tools/agent/__init__.py +1 -1
- hanzo_mcp/tools/agent/agent_tool.py +2 -2
- hanzo_mcp/tools/common/__init__.py +15 -1
- hanzo_mcp/tools/common/base.py +4 -4
- hanzo_mcp/tools/common/batch_tool.py +1 -1
- hanzo_mcp/tools/common/config_tool.py +2 -2
- hanzo_mcp/tools/common/context.py +2 -2
- hanzo_mcp/tools/common/context_fix.py +26 -0
- hanzo_mcp/tools/common/critic_tool.py +196 -0
- hanzo_mcp/tools/common/decorators.py +208 -0
- hanzo_mcp/tools/common/enhanced_base.py +106 -0
- hanzo_mcp/tools/common/mode.py +116 -0
- hanzo_mcp/tools/common/mode_loader.py +105 -0
- hanzo_mcp/tools/common/permissions.py +1 -1
- hanzo_mcp/tools/common/personality.py +936 -0
- hanzo_mcp/tools/common/plugin_loader.py +287 -0
- hanzo_mcp/tools/common/stats.py +4 -4
- hanzo_mcp/tools/common/tool_list.py +1 -1
- hanzo_mcp/tools/common/validation.py +1 -1
- hanzo_mcp/tools/config/__init__.py +3 -1
- hanzo_mcp/tools/config/config_tool.py +1 -1
- hanzo_mcp/tools/config/mode_tool.py +209 -0
- hanzo_mcp/tools/database/__init__.py +1 -1
- hanzo_mcp/tools/editor/__init__.py +1 -1
- hanzo_mcp/tools/filesystem/__init__.py +19 -14
- hanzo_mcp/tools/filesystem/batch_search.py +3 -3
- hanzo_mcp/tools/filesystem/diff.py +2 -2
- hanzo_mcp/tools/filesystem/rules_tool.py +235 -0
- hanzo_mcp/tools/filesystem/{unified_search.py → search_tool.py} +12 -12
- hanzo_mcp/tools/filesystem/{symbols_unified.py → symbols_tool.py} +104 -5
- hanzo_mcp/tools/filesystem/watch.py +3 -2
- hanzo_mcp/tools/jupyter/__init__.py +2 -2
- hanzo_mcp/tools/jupyter/jupyter.py +1 -1
- hanzo_mcp/tools/llm/__init__.py +3 -3
- hanzo_mcp/tools/llm/llm_tool.py +648 -143
- hanzo_mcp/tools/mcp/__init__.py +2 -2
- hanzo_mcp/tools/mcp/{mcp_unified.py → mcp_tool.py} +3 -3
- hanzo_mcp/tools/shell/__init__.py +6 -6
- hanzo_mcp/tools/shell/base_process.py +4 -2
- hanzo_mcp/tools/shell/bash_session_executor.py +1 -1
- hanzo_mcp/tools/shell/{bash_unified.py → bash_tool.py} +1 -1
- hanzo_mcp/tools/shell/command_executor.py +2 -2
- hanzo_mcp/tools/shell/{npx_unified.py → npx_tool.py} +1 -1
- hanzo_mcp/tools/shell/open.py +2 -2
- hanzo_mcp/tools/shell/{process_unified.py → process_tool.py} +1 -1
- hanzo_mcp/tools/shell/run_command_windows.py +1 -1
- hanzo_mcp/tools/shell/uvx.py +47 -2
- hanzo_mcp/tools/shell/uvx_background.py +47 -2
- hanzo_mcp/tools/shell/{uvx_unified.py → uvx_tool.py} +1 -1
- hanzo_mcp/tools/todo/__init__.py +14 -19
- hanzo_mcp/tools/todo/todo.py +22 -1
- hanzo_mcp/tools/vector/__init__.py +1 -1
- hanzo_mcp/tools/vector/infinity_store.py +2 -2
- hanzo_mcp/tools/vector/project_manager.py +1 -1
- hanzo_mcp-0.6.13.dist-info/METADATA +359 -0
- {hanzo_mcp-0.6.12.dist-info → hanzo_mcp-0.6.13.dist-info}/RECORD +72 -64
- {hanzo_mcp-0.6.12.dist-info → hanzo_mcp-0.6.13.dist-info}/entry_points.txt +1 -0
- hanzo_mcp/tools/common/palette.py +0 -344
- hanzo_mcp/tools/common/palette_loader.py +0 -108
- hanzo_mcp/tools/config/palette_tool.py +0 -179
- hanzo_mcp/tools/llm/llm_unified.py +0 -851
- hanzo_mcp-0.6.12.dist-info/METADATA +0 -339
- {hanzo_mcp-0.6.12.dist-info → hanzo_mcp-0.6.13.dist-info}/WHEEL +0 -0
- {hanzo_mcp-0.6.12.dist-info → hanzo_mcp-0.6.13.dist-info}/licenses/LICENSE +0 -0
- {hanzo_mcp-0.6.12.dist-info → hanzo_mcp-0.6.13.dist-info}/top_level.txt +0 -0
hanzo_mcp/__init__.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"""Hanzo
|
|
1
|
+
"""Hanzo AI - Implementation of Hanzo capabilities using MCP."""
|
|
2
2
|
|
|
3
3
|
# Configure FastMCP logging globally for stdio transport
|
|
4
4
|
import os
|
|
@@ -9,4 +9,4 @@ if os.environ.get("HANZO_MCP_TRANSPORT") == "stdio":
|
|
|
9
9
|
except ImportError:
|
|
10
10
|
pass
|
|
11
11
|
|
|
12
|
-
__version__ = "0.6.
|
|
12
|
+
__version__ = "0.6.13"
|
hanzo_mcp/cli.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"""Command-line interface for the Hanzo
|
|
1
|
+
"""Command-line interface for the Hanzo AI server."""
|
|
2
2
|
|
|
3
3
|
import argparse
|
|
4
4
|
import json
|
|
@@ -13,7 +13,7 @@ from hanzo_mcp.server import HanzoMCPServer
|
|
|
13
13
|
|
|
14
14
|
|
|
15
15
|
def main() -> None:
|
|
16
|
-
"""Run the CLI for the Hanzo
|
|
16
|
+
"""Run the CLI for the Hanzo AI server."""
|
|
17
17
|
|
|
18
18
|
# Pre-parse arguments to check transport type early
|
|
19
19
|
import sys
|
hanzo_mcp/cli_enhanced.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"""Enhanced command-line interface for the Hanzo
|
|
1
|
+
"""Enhanced command-line interface for the Hanzo AI server with full tool configuration."""
|
|
2
2
|
|
|
3
3
|
import argparse
|
|
4
4
|
import json
|
|
@@ -15,7 +15,7 @@ from hanzo_mcp.server import HanzoMCPServer
|
|
|
15
15
|
def create_parser() -> argparse.ArgumentParser:
|
|
16
16
|
"""Create the argument parser with all tool configuration options."""
|
|
17
17
|
parser = argparse.ArgumentParser(
|
|
18
|
-
description="Hanzo
|
|
18
|
+
description="Hanzo AI server with comprehensive tool configuration",
|
|
19
19
|
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
20
20
|
epilog="""
|
|
21
21
|
Tool Configuration:
|
|
@@ -346,7 +346,7 @@ def apply_cli_overrides(args: argparse.Namespace) -> Dict[str, Any]:
|
|
|
346
346
|
def list_tools(settings: HanzoMCPSettings) -> None:
|
|
347
347
|
"""List all tools and their current status."""
|
|
348
348
|
logger = logging.getLogger(__name__)
|
|
349
|
-
logger.info("Hanzo
|
|
349
|
+
logger.info("Hanzo AI Tools Status:")
|
|
350
350
|
logger.info("=" * 50)
|
|
351
351
|
|
|
352
352
|
categories = {}
|
|
@@ -371,7 +371,7 @@ def list_tools(settings: HanzoMCPSettings) -> None:
|
|
|
371
371
|
|
|
372
372
|
|
|
373
373
|
def main() -> None:
|
|
374
|
-
"""Run the enhanced CLI for the Hanzo
|
|
374
|
+
"""Run the enhanced CLI for the Hanzo AI server."""
|
|
375
375
|
parser = create_parser()
|
|
376
376
|
args = parser.parse_args()
|
|
377
377
|
|
hanzo_mcp/cli_plugin.py
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""CLI for managing Hanzo MCP plugins."""
|
|
3
|
+
|
|
4
|
+
import argparse
|
|
5
|
+
import sys
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
from hanzo_mcp.tools.common.plugin_loader import create_plugin_template, list_plugin_tools
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def main():
|
|
12
|
+
"""Main CLI entry point."""
|
|
13
|
+
parser = argparse.ArgumentParser(
|
|
14
|
+
description="Hanzo MCP Plugin Manager",
|
|
15
|
+
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
16
|
+
epilog="""
|
|
17
|
+
Examples:
|
|
18
|
+
# Create a new plugin template
|
|
19
|
+
hanzo-plugin create mytool
|
|
20
|
+
|
|
21
|
+
# List installed plugins
|
|
22
|
+
hanzo-plugin list
|
|
23
|
+
|
|
24
|
+
# Create plugin in specific directory
|
|
25
|
+
hanzo-plugin create mytool --output /path/to/plugins
|
|
26
|
+
"""
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
subparsers = parser.add_subparsers(dest="command", help="Command to run")
|
|
30
|
+
|
|
31
|
+
# Create command
|
|
32
|
+
create_parser = subparsers.add_parser("create", help="Create a new plugin template")
|
|
33
|
+
create_parser.add_argument("name", help="Name of the tool (e.g., 'mytool')")
|
|
34
|
+
create_parser.add_argument(
|
|
35
|
+
"--output", "-o",
|
|
36
|
+
type=Path,
|
|
37
|
+
default=Path.home() / ".hanzo" / "plugins",
|
|
38
|
+
help="Output directory for the plugin (default: ~/.hanzo/plugins)"
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
# List command
|
|
42
|
+
list_parser = subparsers.add_parser("list", help="List installed plugins")
|
|
43
|
+
|
|
44
|
+
args = parser.parse_args()
|
|
45
|
+
|
|
46
|
+
if args.command == "create":
|
|
47
|
+
# Create plugin template
|
|
48
|
+
output_dir = args.output / args.name
|
|
49
|
+
try:
|
|
50
|
+
create_plugin_template(output_dir, args.name)
|
|
51
|
+
print(f"\n✅ Plugin template created successfully!")
|
|
52
|
+
print(f"\nTo use your plugin:")
|
|
53
|
+
print(f"1. Edit the tool implementation in {output_dir / f'{args.name}_tool.py'}")
|
|
54
|
+
print(f"2. Restart Hanzo MCP to load the plugin")
|
|
55
|
+
print(f"3. Add '{args.name}' to your mode's tool list")
|
|
56
|
+
except Exception as e:
|
|
57
|
+
print(f"❌ Error creating plugin: {e}", file=sys.stderr)
|
|
58
|
+
sys.exit(1)
|
|
59
|
+
|
|
60
|
+
elif args.command == "list":
|
|
61
|
+
# List installed plugins
|
|
62
|
+
try:
|
|
63
|
+
from hanzo_mcp.tools.common.plugin_loader import load_user_plugins
|
|
64
|
+
plugins = load_user_plugins()
|
|
65
|
+
|
|
66
|
+
if not plugins:
|
|
67
|
+
print("No plugins installed.")
|
|
68
|
+
print("\nPlugin directories:")
|
|
69
|
+
print(" ~/.hanzo/plugins/")
|
|
70
|
+
print(" ./.hanzo/plugins/")
|
|
71
|
+
print(" $HANZO_PLUGIN_PATH")
|
|
72
|
+
else:
|
|
73
|
+
print(f"Installed plugins ({len(plugins)}):")
|
|
74
|
+
for name, plugin in plugins.items():
|
|
75
|
+
print(f"\n {name}:")
|
|
76
|
+
print(f" Source: {plugin.source_path}")
|
|
77
|
+
if plugin.metadata:
|
|
78
|
+
print(f" Version: {plugin.metadata.get('version', 'unknown')}")
|
|
79
|
+
print(f" Author: {plugin.metadata.get('author', 'unknown')}")
|
|
80
|
+
print(f" Description: {plugin.metadata.get('description', '')}")
|
|
81
|
+
except Exception as e:
|
|
82
|
+
print(f"❌ Error listing plugins: {e}", file=sys.stderr)
|
|
83
|
+
sys.exit(1)
|
|
84
|
+
|
|
85
|
+
else:
|
|
86
|
+
parser.print_help()
|
|
87
|
+
sys.exit(1)
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
if __name__ == "__main__":
|
|
91
|
+
main()
|
hanzo_mcp/config/__init__.py
CHANGED
hanzo_mcp/config/settings.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"""Settings management for Hanzo
|
|
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
|
|
@@ -78,7 +79,7 @@ class ServerConfig:
|
|
|
78
79
|
|
|
79
80
|
@dataclass
|
|
80
81
|
class HanzoMCPSettings:
|
|
81
|
-
"""Complete configuration for Hanzo
|
|
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
|
|
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.
|
|
353
|
-
3.
|
|
354
|
-
4.
|
|
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)
|
hanzo_mcp/config/tool_config.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"""Tool configuration definitions for Hanzo
|
|
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
|
|
9
|
+
"""Categories of tools available in Hanzo AI."""
|
|
10
10
|
FILESYSTEM = "filesystem"
|
|
11
11
|
SHELL = "shell"
|
|
12
12
|
JUPYTER = "jupyter"
|
hanzo_mcp/dev_server.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"""Development server with hot reload for Hanzo
|
|
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
|
|
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
|
|
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
|
|
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
|
|
|
@@ -49,7 +52,7 @@ class HanzoMCPServer:
|
|
|
49
52
|
enabled_tools: dict[str, bool] | None = None,
|
|
50
53
|
disabled_tools: list[str] | None = None,
|
|
51
54
|
):
|
|
52
|
-
"""Initialize the Hanzo
|
|
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
|
-
|
|
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()
|
|
@@ -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)
|
hanzo_mcp/tools/__init__.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
"""Tools package for Hanzo
|
|
1
|
+
"""Tools package for Hanzo AI.
|
|
2
2
|
|
|
3
|
-
This package contains all the tools for the Hanzo
|
|
4
|
-
It provides a
|
|
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
|
|
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
|
|
31
|
-
from hanzo_mcp.tools.config.
|
|
32
|
-
from hanzo_mcp.tools.common.
|
|
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
|
-
|
|
50
|
-
|
|
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
|
-
|
|
69
|
-
|
|
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
|
-
#
|
|
75
|
-
|
|
76
|
-
|
|
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
|
-
|
|
96
|
+
force_mode=force_mode
|
|
79
97
|
)
|
|
80
|
-
# Apply
|
|
81
|
-
|
|
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
|
-
"
|
|
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
|
-
"
|
|
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
|
|
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("
|
|
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]
|
|
@@ -155,7 +174,7 @@ def register_all_tools(
|
|
|
155
174
|
all_tools[tool.name] = tool
|
|
156
175
|
|
|
157
176
|
# Register agent tools if enabled
|
|
158
|
-
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)
|
|
159
178
|
if agent_enabled:
|
|
160
179
|
agent_tools = register_agent_tools(
|
|
161
180
|
mcp_server,
|
|
@@ -172,12 +191,15 @@ def register_all_tools(
|
|
|
172
191
|
|
|
173
192
|
# Register todo tools if enabled
|
|
174
193
|
todo_enabled = {
|
|
194
|
+
"todo": is_tool_enabled("todo", True),
|
|
195
|
+
# Backward compatibility - if old names are used, enable the unified tool
|
|
175
196
|
"todo_read": is_tool_enabled("todo_read", True),
|
|
176
197
|
"todo_write": is_tool_enabled("todo_write", True),
|
|
177
198
|
}
|
|
178
199
|
|
|
200
|
+
# Enable unified todo if any of the todo tools are enabled
|
|
179
201
|
if any(todo_enabled.values()):
|
|
180
|
-
todo_tools = register_todo_tools(mcp_server, enabled_tools=
|
|
202
|
+
todo_tools = register_todo_tools(mcp_server, enabled_tools={"todo": True})
|
|
181
203
|
for tool in todo_tools:
|
|
182
204
|
all_tools[tool.name] = tool
|
|
183
205
|
|
|
@@ -186,6 +208,12 @@ def register_all_tools(
|
|
|
186
208
|
thinking_tool = register_thinking_tool(mcp_server)
|
|
187
209
|
for tool in thinking_tool:
|
|
188
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
|
|
189
217
|
|
|
190
218
|
# Register vector tools if enabled (reuse project_manager if available)
|
|
191
219
|
if any(vector_enabled.values()) and project_manager:
|
|
@@ -230,7 +258,7 @@ def register_all_tools(
|
|
|
230
258
|
|
|
231
259
|
# Register unified MCP tool if enabled
|
|
232
260
|
if is_tool_enabled("mcp", True):
|
|
233
|
-
tool =
|
|
261
|
+
tool = MCPTool()
|
|
234
262
|
tool.register(mcp_server)
|
|
235
263
|
all_tools[tool.name] = tool
|
|
236
264
|
|
|
@@ -275,9 +303,9 @@ def register_all_tools(
|
|
|
275
303
|
stats_tool.register(mcp_server)
|
|
276
304
|
all_tools[stats_tool.name] = stats_tool
|
|
277
305
|
|
|
278
|
-
#
|
|
279
|
-
|
|
280
|
-
all_tools[
|
|
306
|
+
# Mode tool (always enabled for managing tool sets)
|
|
307
|
+
mode_tool.register(mcp_server)
|
|
308
|
+
all_tools[mode_tool.name] = mode_tool
|
|
281
309
|
|
|
282
310
|
# Register editor tools if enabled
|
|
283
311
|
editor_enabled = {
|
|
@@ -303,11 +331,18 @@ def register_all_tools(
|
|
|
303
331
|
|
|
304
332
|
# Register unified LLM tool if enabled
|
|
305
333
|
if is_tool_enabled("llm", True):
|
|
306
|
-
tool =
|
|
334
|
+
tool = LLMTool()
|
|
307
335
|
if tool.available_providers: # Only register if API keys found
|
|
308
336
|
tool.register(mcp_server)
|
|
309
337
|
all_tools[tool.name] = tool
|
|
310
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
|
+
|
|
311
346
|
# Register legacy LLM tools if explicitly enabled (disabled by default)
|
|
312
347
|
legacy_llm_enabled = {
|
|
313
348
|
"llm_legacy": is_tool_enabled("llm_legacy", False),
|
|
@@ -339,3 +374,14 @@ def register_all_tools(
|
|
|
339
374
|
if is_tool_enabled(tool.name, False):
|
|
340
375
|
tool.register(mcp_server)
|
|
341
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 tool implementation for Hanzo
|
|
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 "
|
|
78
|
+
return "agent"
|
|
79
79
|
|
|
80
80
|
@property
|
|
81
81
|
@override
|