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
hanzo_mcp/__init__.py CHANGED
@@ -1,3 +1,12 @@
1
- """Hanzo MCP - Implementation of Hanzo capabilities using MCP."""
1
+ """Hanzo AI - Implementation of Hanzo capabilities using MCP."""
2
2
 
3
- __version__ = "0.6.10"
3
+ # Configure FastMCP logging globally for stdio transport
4
+ import os
5
+ if os.environ.get("HANZO_MCP_TRANSPORT") == "stdio":
6
+ try:
7
+ from fastmcp.utilities.logging import configure_logging
8
+ configure_logging(level="ERROR")
9
+ except ImportError:
10
+ pass
11
+
12
+ __version__ = "0.6.13"
hanzo_mcp/cli.py CHANGED
@@ -1,7 +1,8 @@
1
- """Command-line interface for the Hanzo MCP server."""
1
+ """Command-line interface for the Hanzo AI server."""
2
2
 
3
3
  import argparse
4
4
  import json
5
+ import logging
5
6
  import os
6
7
  import signal
7
8
  import sys
@@ -12,14 +13,34 @@ from hanzo_mcp.server import HanzoMCPServer
12
13
 
13
14
 
14
15
  def main() -> None:
15
- """Run the CLI for the Hanzo MCP server."""
16
- # Set up signal handler to ensure clean exit
17
- def signal_handler(signum, frame):
18
- print("\nReceived interrupt signal, shutting down...")
19
- sys.exit(0)
16
+ """Run the CLI for the Hanzo AI server."""
20
17
 
21
- signal.signal(signal.SIGINT, signal_handler)
22
- signal.signal(signal.SIGTERM, signal_handler)
18
+ # Pre-parse arguments to check transport type early
19
+ import sys
20
+ early_parser = argparse.ArgumentParser(add_help=False)
21
+ early_parser.add_argument("--transport", choices=["stdio", "sse"], default="stdio")
22
+ early_args, _ = early_parser.parse_known_args()
23
+
24
+ # Configure logging VERY early based on transport
25
+ if early_args.transport == "stdio":
26
+ # Set environment variable for server to detect stdio mode
27
+ import os
28
+ os.environ["HANZO_MCP_TRANSPORT"] = "stdio"
29
+
30
+ # For stdio transport, disable ALL logging immediately
31
+ from fastmcp.utilities.logging import configure_logging
32
+ # Set to ERROR to suppress INFO/WARNING messages from FastMCP
33
+ configure_logging(level="ERROR")
34
+
35
+ # Also configure standard logging to ERROR level
36
+ logging.basicConfig(
37
+ level=logging.ERROR, # Only show errors
38
+ handlers=[] # No handlers for stdio to prevent protocol corruption
39
+ )
40
+
41
+ # Redirect stderr to devnull for stdio transport to prevent any output
42
+ import sys
43
+ sys.stderr = open(os.devnull, 'w')
23
44
 
24
45
  parser = argparse.ArgumentParser(
25
46
  description="MCP server implementing Hanzo AI capabilities"
@@ -212,6 +233,32 @@ def main() -> None:
212
233
  )
213
234
  return
214
235
 
236
+ # Get logger
237
+ logger = logging.getLogger(__name__)
238
+
239
+ # Set up signal handler to ensure clean exit
240
+ def signal_handler(signum, frame):
241
+ if transport != "stdio":
242
+ logger.info("\nReceived interrupt signal, shutting down...")
243
+ sys.exit(0)
244
+
245
+ signal.signal(signal.SIGINT, signal_handler)
246
+ signal.signal(signal.SIGTERM, signal_handler)
247
+
248
+ # Configure logging based on transport (stdio already configured early)
249
+ if transport != "stdio":
250
+ # For SSE transport, logging is fine
251
+ log_level_map = {
252
+ "DEBUG": logging.DEBUG,
253
+ "INFO": logging.INFO,
254
+ "WARNING": logging.WARNING,
255
+ "ERROR": logging.ERROR
256
+ }
257
+ logging.basicConfig(
258
+ level=log_level_map.get(log_level, logging.INFO),
259
+ format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
260
+ )
261
+
215
262
  # If no allowed paths are specified, use the current directory
216
263
  if not allowed_paths:
217
264
  allowed_paths = [os.getcwd()]
@@ -263,10 +310,11 @@ def main() -> None:
263
310
  # Transport will be automatically cast to Literal['stdio', 'sse'] by the server
264
311
  server.run(transport=transport)
265
312
  except KeyboardInterrupt:
266
- print("\nShutting down...")
313
+ if transport != "stdio":
314
+ logger.info("\nShutting down...")
267
315
  sys.exit(0)
268
316
  except Exception as e:
269
- print(f"Server error: {e}", file=sys.stderr)
317
+ logger.error(f"Server error: {e}")
270
318
  sys.exit(1)
271
319
 
272
320
 
@@ -342,27 +390,29 @@ def install_claude_desktop_config(
342
390
  existing_config["mcpServers"][name] = config["mcpServers"][name]
343
391
  config = existing_config
344
392
  except Exception as e:
345
- print(f"Error reading existing config: {e}")
346
- print("Creating new config file.")
393
+ logger = logging.getLogger(__name__)
394
+ logger.error(f"Error reading existing config: {e}")
395
+ logger.info("Creating new config file.")
347
396
 
348
397
  # Write the config file
349
398
  with open(config_file, mode="w") as f:
350
399
  json.dump(config, f, indent=2)
351
400
 
352
- print(f"Successfully installed {name} in Claude Desktop configuration.")
353
- print(f"Config file: {config_file}")
401
+ logger = logging.getLogger(__name__)
402
+ logger.info(f"Successfully installed {name} in Claude Desktop configuration.")
403
+ logger.info(f"Config file: {config_file}")
354
404
 
355
405
  if allowed_paths:
356
- print("\nAllowed paths:")
406
+ logger.info("\nAllowed paths:")
357
407
  for path in allowed_paths:
358
- print(f"- {path}")
408
+ logger.info(f"- {path}")
359
409
  else:
360
- print(f"\nDefault allowed path: {home}")
410
+ logger.info(f"\nDefault allowed path: {home}")
361
411
 
362
- print(
412
+ logger.info(
363
413
  "\nYou can modify allowed paths in the config file directly."
364
414
  )
365
- print("Restart Claude Desktop for changes to take effect.")
415
+ logger.info("Restart Claude Desktop for changes to take effect.")
366
416
 
367
417
 
368
418
  if __name__ == "__main__":
hanzo_mcp/cli_enhanced.py CHANGED
@@ -1,7 +1,8 @@
1
- """Enhanced command-line interface for the Hanzo MCP server with full tool configuration."""
1
+ """Enhanced command-line interface for the Hanzo AI server with full tool configuration."""
2
2
 
3
3
  import argparse
4
4
  import json
5
+ import logging
5
6
  import os
6
7
  import sys
7
8
  from pathlib import Path
@@ -14,7 +15,7 @@ from hanzo_mcp.server import HanzoMCPServer
14
15
  def create_parser() -> argparse.ArgumentParser:
15
16
  """Create the argument parser with all tool configuration options."""
16
17
  parser = argparse.ArgumentParser(
17
- description="Hanzo MCP server with comprehensive tool configuration",
18
+ description="Hanzo AI server with comprehensive tool configuration",
18
19
  formatter_class=argparse.RawDescriptionHelpFormatter,
19
20
  epilog="""
20
21
  Tool Configuration:
@@ -344,8 +345,9 @@ def apply_cli_overrides(args: argparse.Namespace) -> Dict[str, Any]:
344
345
 
345
346
  def list_tools(settings: HanzoMCPSettings) -> None:
346
347
  """List all tools and their current status."""
347
- print("Hanzo MCP Tools Status:")
348
- print("=" * 50)
348
+ logger = logging.getLogger(__name__)
349
+ logger.info("Hanzo AI Tools Status:")
350
+ logger.info("=" * 50)
349
351
 
350
352
  categories = {}
351
353
  for tool_name, tool_config in TOOL_REGISTRY.items():
@@ -358,18 +360,18 @@ def list_tools(settings: HanzoMCPSettings) -> None:
358
360
  categories[category].append((tool_name, status, tool_config.description))
359
361
 
360
362
  for category, tools in categories.items():
361
- print(f"\n{category.upper()} TOOLS:")
362
- print("-" * 30)
363
+ logger.info(f"\n{category.upper()} TOOLS:")
364
+ logger.info("-" * 30)
363
365
  for tool_name, status, description in tools:
364
- print(f" {status} {tool_name:<15} - {description}")
366
+ logger.info(f" {status} {tool_name:<15} - {description}")
365
367
 
366
- print(f"\nTotal: {len(TOOL_REGISTRY)} tools")
368
+ logger.info(f"\nTotal: {len(TOOL_REGISTRY)} tools")
367
369
  enabled_count = len(settings.get_enabled_tools())
368
- print(f"Enabled: {enabled_count}, Disabled: {len(TOOL_REGISTRY) - enabled_count}")
370
+ logger.info(f"Enabled: {enabled_count}, Disabled: {len(TOOL_REGISTRY) - enabled_count}")
369
371
 
370
372
 
371
373
  def main() -> None:
372
- """Run the enhanced CLI for the Hanzo MCP server."""
374
+ """Run the enhanced CLI for the Hanzo AI server."""
373
375
  parser = create_parser()
374
376
  args = parser.parse_args()
375
377
 
@@ -385,14 +387,15 @@ def main() -> None:
385
387
  settings = load_settings(project_dir=project_dir, config_overrides=config_overrides)
386
388
 
387
389
  # Handle configuration saving
390
+ logger = logging.getLogger(__name__)
388
391
  if hasattr(args, 'save_config') and args.save_config:
389
392
  saved_path = save_settings(settings, global_config=True)
390
- print(f"Configuration saved to: {saved_path}")
393
+ logger.info(f"Configuration saved to: {saved_path}")
391
394
  return
392
395
 
393
396
  if hasattr(args, 'save_project_config') and args.save_project_config:
394
397
  saved_path = save_settings(settings, global_config=False)
395
- print(f"Project configuration saved to: {saved_path}")
398
+ logger.info(f"Project configuration saved to: {saved_path}")
396
399
  return
397
400
 
398
401
  # Handle installation
@@ -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()
@@ -1,4 +1,4 @@
1
- """Configuration management for Hanzo MCP.
1
+ """Configuration management for Hanzo AI.
2
2
 
3
3
  This module provides a comprehensive configuration system that supports:
4
4
  - CLI arguments for individual tool control
@@ -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
@@ -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()
@@ -364,7 +422,9 @@ def load_settings(
364
422
  global_config = json.load(f)
365
423
  settings = _merge_config(settings, global_config)
366
424
  except Exception as e:
367
- print(f"Warning: Failed to load global config: {e}")
425
+ import logging
426
+ logger = logging.getLogger(__name__)
427
+ logger.warning(f"Failed to load global config: {e}")
368
428
 
369
429
  # Load project config
370
430
  project_config_path = get_project_config_path(project_dir)
@@ -374,7 +434,14 @@ def load_settings(
374
434
  project_config = json.load(f)
375
435
  settings = _merge_config(settings, project_config)
376
436
  except Exception as e:
377
- print(f"Warning: Failed to load project config: {e}")
437
+ import logging
438
+ logger = logging.getLogger(__name__)
439
+ logger.warning(f"Failed to load project config: {e}")
440
+
441
+ # Apply environment variables
442
+ env_config = _load_from_env()
443
+ if env_config:
444
+ settings = _merge_config(settings, env_config)
378
445
 
379
446
  # Apply CLI overrides
380
447
  if 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"
hanzo_mcp/dev_server.py CHANGED
@@ -1,6 +1,7 @@
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
+ import logging
4
5
  import os
5
6
  import sys
6
7
  import time
@@ -67,8 +68,9 @@ class MCPReloadHandler(FileSystemEventHandler):
67
68
 
68
69
  self.last_reload = current_time
69
70
 
70
- print(f"\n🔄 File changed: {event.src_path}")
71
- print("🔄 Reloading MCP server...")
71
+ logger = logging.getLogger(__name__)
72
+ logger.info(f"\n🔄 File changed: {event.src_path}")
73
+ logger.info("🔄 Reloading MCP server...")
72
74
 
73
75
  self.restart_callback()
74
76
 
@@ -133,9 +135,10 @@ class DevServer:
133
135
  self.observer.schedule(handler, path, recursive=True)
134
136
 
135
137
  self.observer.start()
136
- print(f"👀 Watching for changes in: {package_dir}")
138
+ logger = logging.getLogger(__name__)
139
+ logger.info(f"👀 Watching for changes in: {package_dir}")
137
140
  if self.project_dir:
138
- print(f"👀 Also watching: {self.project_dir}")
141
+ logger.info(f"👀 Also watching: {self.project_dir}")
139
142
 
140
143
  def stop_file_watcher(self):
141
144
  """Stop the file watcher."""
@@ -146,18 +149,20 @@ class DevServer:
146
149
  def restart_server(self):
147
150
  """Restart the MCP server."""
148
151
  # Since MCP servers run in the same process, we need to handle this differently
149
- # For now, we'll print a message indicating a restart is needed
150
- print("\n⚠️ Server restart required. Please restart the MCP client to reload changes.")
151
- print("💡 Tip: In development, consider using the MCP test client for easier reloading.")
152
+ # For now, we'll log a message indicating a restart is needed
153
+ logger = logging.getLogger(__name__)
154
+ logger.warning("\n⚠️ Server restart required. Please restart the MCP client to reload changes.")
155
+ logger.info("💡 Tip: In development, consider using the MCP test client for easier reloading.")
152
156
 
153
157
  async def run_async(self, transport: str = "stdio"):
154
158
  """Run the development server asynchronously."""
155
159
  self.running = True
156
160
 
157
- print(f"\n🚀 Starting Hanzo MCP in development mode...")
158
- print(f"🔧 Hot reload enabled - watching for file changes")
159
- print(f"📁 Project: {self.project_dir or 'current directory'}")
160
- print(f"🌐 Transport: {transport}\n")
161
+ logger = logging.getLogger(__name__)
162
+ logger.info(f"\n🚀 Starting Hanzo AI in development mode...")
163
+ logger.info(f"🔧 Hot reload enabled - watching for file changes")
164
+ logger.info(f"📁 Project: {self.project_dir or 'current directory'}")
165
+ logger.info(f"🌐 Transport: {transport}\n")
161
166
 
162
167
  # Start file watcher
163
168
  self.start_file_watcher()
@@ -170,11 +175,11 @@ class DevServer:
170
175
  server.run(transport=transport)
171
176
 
172
177
  except KeyboardInterrupt:
173
- print("\n\n🛑 Shutting down development server...")
178
+ logger.info("\n\n🛑 Shutting down development server...")
174
179
  finally:
175
180
  self.running = False
176
181
  self.stop_file_watcher()
177
- print("👋 Development server stopped")
182
+ logger.info("👋 Development server stopped")
178
183
 
179
184
  def run(self, transport: str = "stdio"):
180
185
  """Run the development server."""
@@ -189,7 +194,7 @@ def run_dev_server():
189
194
  """Entry point for development server."""
190
195
  import argparse
191
196
 
192
- 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")
193
198
  parser.add_argument(
194
199
  "--name",
195
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
@@ -1,6 +1,7 @@
1
1
  """MCP server implementing Hanzo capabilities."""
2
2
 
3
3
  import atexit
4
+ import logging
4
5
  import signal
5
6
  import threading
6
7
  import time
@@ -15,6 +16,9 @@ except ImportError:
15
16
  # Fallback for older MCP versions
16
17
  from mcp.server import FastMCP
17
18
 
19
+ # Import our enhanced server
20
+ from hanzo_mcp.server_enhanced import EnhancedFastMCP
21
+
18
22
  from hanzo_mcp.prompts import register_all_prompts
19
23
  from hanzo_mcp.tools import register_all_tools
20
24
 
@@ -48,7 +52,7 @@ class HanzoMCPServer:
48
52
  enabled_tools: dict[str, bool] | None = None,
49
53
  disabled_tools: list[str] | None = None,
50
54
  ):
51
- """Initialize the Hanzo MCP server.
55
+ """Initialize the Hanzo AI server.
52
56
 
53
57
  Args:
54
58
  name: The name of the server
@@ -71,7 +75,8 @@ class HanzoMCPServer:
71
75
  enabled_tools: Dictionary of individual tool enable states (default: None)
72
76
  disabled_tools: List of tool names to disable (default: None)
73
77
  """
74
- 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)
75
80
 
76
81
  # Initialize permissions and command executor
77
82
  self.permission_manager = PermissionManager()
@@ -154,7 +159,10 @@ class HanzoMCPServer:
154
159
  # Register signal handlers for graceful shutdown
155
160
  def signal_handler(signum, frame):
156
161
  import sys
157
- print("\nShutting down gracefully...")
162
+ # Only log if not stdio transport
163
+ if hasattr(self, '_transport') and self._transport != 'stdio':
164
+ logger = logging.getLogger(__name__)
165
+ logger.info("\nShutting down gracefully...")
158
166
  self._cleanup_sessions()
159
167
  self._shutdown_event.set()
160
168
  sys.exit(0)
@@ -189,7 +197,10 @@ class HanzoMCPServer:
189
197
  try:
190
198
  cleared_count = SessionStorage.clear_all_sessions()
191
199
  if cleared_count > 0:
192
- print(f"Cleaned up {cleared_count} tmux sessions on shutdown")
200
+ # Only log if not stdio transport
201
+ if hasattr(self, '_transport') and self._transport != 'stdio':
202
+ logger = logging.getLogger(__name__)
203
+ logger.info(f"Cleaned up {cleared_count} tmux sessions on shutdown")
193
204
  except Exception:
194
205
  # Ignore cleanup errors during shutdown
195
206
  pass
@@ -201,6 +212,9 @@ class HanzoMCPServer:
201
212
  transport: The transport to use (stdio or sse)
202
213
  allowed_paths: list of paths that the server is allowed to access
203
214
  """
215
+ # Store transport for later use
216
+ self._transport = transport
217
+
204
218
  # Add allowed paths if provided
205
219
  allowed_paths_list = allowed_paths or []
206
220
  for path in allowed_paths_list: