hanzo-mcp 0.2.0__py3-none-any.whl → 0.3.1__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.

@@ -0,0 +1,120 @@
1
+ """Provider registry for agent delegation.
2
+
3
+ Manages different model providers for agent delegation.
4
+ """
5
+
6
+ import logging
7
+ from typing import Any, Dict, List, Optional, Tuple, Type
8
+
9
+ from hanzo_mcp.tools.agent.base_provider import BaseModelProvider
10
+
11
+ logger = logging.getLogger(__name__)
12
+
13
+
14
+ class ProviderRegistry:
15
+ """Registry for model providers."""
16
+
17
+ _instance = None
18
+
19
+ def __new__(cls):
20
+ """Singleton pattern to ensure only one registry instance exists."""
21
+ if cls._instance is None:
22
+ cls._instance = super(ProviderRegistry, cls).__new__(cls)
23
+ cls._instance._initialized = False
24
+ return cls._instance
25
+
26
+ def __init__(self):
27
+ """Initialize the provider registry."""
28
+ if self._initialized:
29
+ return
30
+
31
+ self.providers = {}
32
+ self.provider_classes = {}
33
+ self._initialized = True
34
+ logger.info("Provider registry initialized")
35
+
36
+ def register_provider_class(self, provider_type: str, provider_class: Type[BaseModelProvider]) -> None:
37
+ """Register a provider class with the registry.
38
+
39
+ Args:
40
+ provider_type: The type identifier for the provider
41
+ provider_class: The provider class to register
42
+ """
43
+ self.provider_classes[provider_type] = provider_class
44
+ logger.info(f"Registered provider class: {provider_type}")
45
+
46
+ async def get_provider(self, provider_type: str) -> BaseModelProvider:
47
+ """Get or create a provider instance for the given type.
48
+
49
+ Args:
50
+ provider_type: The type identifier for the provider
51
+
52
+ Returns:
53
+ A provider instance
54
+
55
+ Raises:
56
+ ValueError: If the provider type is not registered
57
+ """
58
+ # Check if we already have an instance
59
+ if provider_type in self.providers:
60
+ return self.providers[provider_type]
61
+
62
+ # Check if we have a class for this type
63
+ if provider_type not in self.provider_classes:
64
+ raise ValueError(f"Unknown provider type: {provider_type}")
65
+
66
+ # Create a new instance
67
+ provider_class = self.provider_classes[provider_type]
68
+ provider = provider_class()
69
+
70
+ # Initialize the provider
71
+ await provider.initialize()
72
+
73
+ # Store and return the provider
74
+ self.providers[provider_type] = provider
75
+ logger.info(f"Created and initialized provider: {provider_type}")
76
+ return provider
77
+
78
+ async def shutdown_all(self) -> None:
79
+ """Shutdown all providers."""
80
+ for provider_type, provider in self.providers.items():
81
+ try:
82
+ await provider.shutdown()
83
+ logger.info(f"Provider shut down: {provider_type}")
84
+ except Exception as e:
85
+ logger.error(f"Failed to shut down provider {provider_type}: {str(e)}")
86
+
87
+ self.providers = {}
88
+ logger.info("All providers shut down")
89
+
90
+ async def shutdown_provider(self, provider_type: str) -> None:
91
+ """Shutdown a specific provider.
92
+
93
+ Args:
94
+ provider_type: The type identifier for the provider
95
+ """
96
+ if provider_type not in self.providers:
97
+ logger.warning(f"Provider not found: {provider_type}")
98
+ return
99
+
100
+ try:
101
+ await self.providers[provider_type].shutdown()
102
+ del self.providers[provider_type]
103
+ logger.info(f"Provider shut down: {provider_type}")
104
+ except Exception as e:
105
+ logger.error(f"Failed to shut down provider {provider_type}: {str(e)}")
106
+
107
+
108
+ # Create a singleton instance
109
+ registry = ProviderRegistry()
110
+
111
+ # Register LiteLLM provider
112
+ from hanzo_mcp.tools.agent.litellm_provider import LiteLLMProvider
113
+ registry.register_provider_class("litellm", LiteLLMProvider)
114
+
115
+ # Try to register LM Studio provider if available
116
+ try:
117
+ from hanzo_mcp.tools.agent.lmstudio_provider import LMStudioProvider
118
+ registry.register_provider_class("lmstudio", LMStudioProvider)
119
+ except ImportError:
120
+ logger.warning("LM Studio provider not available. Install the package if needed.")
@@ -0,0 +1,86 @@
1
+ """Error handling utilities for MCP tools.
2
+
3
+ This module provides utility functions for better error handling in MCP tools.
4
+ """
5
+
6
+ import logging
7
+ import traceback
8
+ from functools import wraps
9
+ from typing import Any, Callable, TypeVar, Awaitable, cast
10
+
11
+ from mcp.server.fastmcp import Context as MCPContext
12
+
13
+ # Setup logger
14
+ logger = logging.getLogger(__name__)
15
+
16
+ # Type variables for generic function signatures
17
+ T = TypeVar('T')
18
+ F = TypeVar('F', bound=Callable[..., Awaitable[Any]])
19
+
20
+
21
+ async def log_error(ctx: MCPContext, error: Exception, message: str) -> None:
22
+ """Log an error to both the logger and the MCP context.
23
+
24
+ Args:
25
+ ctx: The MCP context
26
+ error: The exception that occurred
27
+ message: A descriptive message about the error
28
+ """
29
+ error_message = f"{message}: {str(error)}"
30
+ stack_trace = "".join(traceback.format_exception(type(error), error, error.__traceback__))
31
+
32
+ # Log to system logger
33
+ logger.error(error_message)
34
+ logger.debug(stack_trace)
35
+
36
+ # Log to MCP context if available
37
+ try:
38
+ await ctx.error(error_message)
39
+ except Exception as e:
40
+ logger.error(f"Failed to log error to MCP context: {str(e)}")
41
+
42
+
43
+ def tool_error_handler(func: F) -> F:
44
+ """Decorator for handling errors in tool execution.
45
+
46
+ This decorator wraps a tool function to catch and properly handle exceptions,
47
+ ensuring they are logged and proper error messages are returned.
48
+
49
+ Args:
50
+ func: The async tool function to wrap
51
+
52
+ Returns:
53
+ Wrapped function with error handling
54
+ """
55
+ @wraps(func)
56
+ async def wrapper(*args: Any, **kwargs: Any) -> Any:
57
+ try:
58
+ # Extract the MCP context from arguments
59
+ ctx = None
60
+ for arg in args:
61
+ if isinstance(arg, MCPContext):
62
+ ctx = arg
63
+ break
64
+
65
+ if not ctx and 'ctx' in kwargs:
66
+ ctx = kwargs['ctx']
67
+
68
+ if not ctx:
69
+ logger.warning("No MCP context found in tool arguments, error handling will be limited")
70
+
71
+ # Call the original function
72
+ return await func(*args, **kwargs)
73
+ except Exception as e:
74
+ # Log the error
75
+ error_message = f"Error in tool execution: {func.__name__}"
76
+ if ctx:
77
+ await log_error(ctx, e, error_message)
78
+ else:
79
+ stack_trace = "".join(traceback.format_exception(type(e), e, e.__traceback__))
80
+ logger.error(f"{error_message}: {str(e)}")
81
+ logger.debug(stack_trace)
82
+
83
+ # Return a friendly error message
84
+ return f"Error executing {func.__name__}: {str(e)}"
85
+
86
+ return cast(F, wrapper)
@@ -0,0 +1,84 @@
1
+ """Logging configuration for Hanzo MCP.
2
+
3
+ This module sets up logging for the Hanzo MCP project.
4
+ """
5
+
6
+ import logging
7
+ import os
8
+ import sys
9
+ from datetime import datetime
10
+ from pathlib import Path
11
+
12
+
13
+ def setup_logging(log_level: str = "INFO", log_to_file: bool = True, testing: bool = False) -> None:
14
+ """Set up logging configuration.
15
+
16
+ Args:
17
+ log_level: The logging level ("DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL")
18
+ log_to_file: Whether to log to a file in addition to the console
19
+ testing: Set to True to disable file operations for testing
20
+ """
21
+ # Convert string log level to logging constant
22
+ numeric_level = getattr(logging, log_level.upper(), None)
23
+ if not isinstance(numeric_level, int):
24
+ raise ValueError(f"Invalid log level: {log_level}")
25
+
26
+ # Create logs directory if needed
27
+ log_dir = Path.home() / ".hanzo" / "logs"
28
+ if log_to_file and not testing:
29
+ log_dir.mkdir(parents=True, exist_ok=True)
30
+
31
+ # Generate log filename based on current date
32
+ current_time = datetime.now().strftime("%Y-%m-%d")
33
+ log_file = log_dir / f"hanzo-mcp-{current_time}.log"
34
+
35
+ # Base configuration
36
+ handlers = []
37
+
38
+ # Console handler
39
+ console = logging.StreamHandler(sys.stdout)
40
+ console.setLevel(numeric_level)
41
+ console_formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
42
+ console.setFormatter(console_formatter)
43
+ handlers.append(console)
44
+
45
+ # File handler (if enabled)
46
+ if log_to_file and not testing:
47
+ file_handler = logging.FileHandler(log_file)
48
+ file_handler.setLevel(numeric_level)
49
+ file_formatter = logging.Formatter(
50
+ '%(asctime)s - %(name)s - %(levelname)s - %(filename)s:%(lineno)d - %(message)s'
51
+ )
52
+ file_handler.setFormatter(file_formatter)
53
+ handlers.append(file_handler)
54
+
55
+ # Configure root logger
56
+ logging.basicConfig(
57
+ level=numeric_level,
58
+ handlers=handlers,
59
+ force=True # Overwrite any existing configuration
60
+ )
61
+
62
+ # Set specific log levels for third-party libraries
63
+ logging.getLogger("urllib3").setLevel(logging.WARNING)
64
+ logging.getLogger("asyncio").setLevel(logging.WARNING)
65
+
66
+ # Log startup message
67
+ root_logger = logging.getLogger()
68
+ root_logger.info(f"Logging initialized at level {log_level}")
69
+ if log_to_file and not testing:
70
+ root_logger.info(f"Log file: {log_file}")
71
+
72
+
73
+ def get_log_files() -> list[str]:
74
+ """Get a list of all log files.
75
+
76
+ Returns:
77
+ List of log file paths
78
+ """
79
+ log_dir = Path.home() / ".hanzo" / "logs"
80
+ if not log_dir.exists():
81
+ return []
82
+
83
+ log_files = [str(f) for f in log_dir.glob("hanzo-mcp-*.log")]
84
+ return sorted(log_files, reverse=True)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: hanzo-mcp
3
- Version: 0.2.0
3
+ Version: 0.3.1
4
4
  Summary: MCP implementation of Hanzo capabilities
5
5
  Author-email: Hanzo Industries Inc <dev@hanzo.ai>
6
6
  License: MIT
@@ -1,14 +1,21 @@
1
- hanzo_mcp/__init__.py,sha256=crlSOFdbUf7WY8A-esMOCLI-YKa7IwfxXTsOZvFswvg,89
2
- hanzo_mcp/cli.py,sha256=nfTY2lDvdxUR7KTk5j89CGpgLCj9YwAb2mqBzWlJBHI,8070
3
- hanzo_mcp/server.py,sha256=rZcu0JsOQlnnbBVV128qpXEl-ocWEVToBDeA8wmt068,5913
1
+ hanzo_mcp/__init__.py,sha256=roRFuV1ugS6Zf28PtFGKzOnpJMDFncOz6fR3TlG-LF0,89
2
+ hanzo_mcp/cli.py,sha256=ST428ys32fputErgrmLNJV9oYHbKGwkjrPBJfKh_K_Y,10820
3
+ hanzo_mcp/server.py,sha256=bV4ywWOgm7BwZrbZwv1qoRKbAsMYT08aCGQwuFS8raM,6678
4
4
  hanzo_mcp/tools/__init__.py,sha256=9LbWAPfSntDwLaAex3dagsHO4BgZQHKj5E6UX-Gmyb4,3496
5
5
  hanzo_mcp/tools/agent/__init__.py,sha256=0eyQqqdAy7WCZEqUfV6xh66bDpQI9neB6iDjWf0_pJI,2189
6
6
  hanzo_mcp/tools/agent/agent_tool.py,sha256=qXu62ZRGM0o9mxOiRVFy-ABIZtEJ8z03DqAXshKAieI,19180
7
+ hanzo_mcp/tools/agent/base_provider.py,sha256=dd1J75Q0wl_t_Gcuwc8Ft3PWxeDsmf0B3ybhRsUEgmA,2080
8
+ hanzo_mcp/tools/agent/litellm_provider.py,sha256=6mVOLSNpfjAJKj6aBMflyYU6DRJpEqIxgu8wSzeRmxU,1334
9
+ hanzo_mcp/tools/agent/lmstudio_agent.py,sha256=c4-VIDRXksv39HYpXzqwyF1PisoNd73R3Txa8Mkzibg,14510
10
+ hanzo_mcp/tools/agent/lmstudio_provider.py,sha256=-gX8fxebyWaC7bqmZERR6QQc62Paj62IIDIu_rBu0wM,7876
7
11
  hanzo_mcp/tools/agent/prompt.py,sha256=jmYRI4Sm2D3LwEdC2qWakpqupgfGg8CT6grmx4NEDaA,4431
12
+ hanzo_mcp/tools/agent/provider_registry.py,sha256=A-Wd5B5hLBznQV4wWbxE-yyvBPnbuUL2OtSJUz2K_H0,4236
8
13
  hanzo_mcp/tools/agent/tool_adapter.py,sha256=g9NKfIET0WOsm0r28xEXsibsprpI1C6ripcM6QwU-rI,2172
9
14
  hanzo_mcp/tools/common/__init__.py,sha256=felrGAEqLWOa8fuHjTCS7khqtlA-izz8k865ky7R-f8,797
10
15
  hanzo_mcp/tools/common/base.py,sha256=O7Lgft0XiC9Iyi3fYsmoWWrvKDK2Aa-FJLxPgnNJRJY,7341
11
16
  hanzo_mcp/tools/common/context.py,sha256=ReIfcm37j5qnLQ8G_-d88ad5uC1OKkjQZKG9HdJPybI,13145
17
+ hanzo_mcp/tools/common/error_handling.py,sha256=rluaHpXV89pjcf8JxgYB6EdqVkcxihb3pyEO2gN-u7w,2789
18
+ hanzo_mcp/tools/common/logging_config.py,sha256=b1swgWV3nuhpVT0TCcbxlOC0gmiRLmbey_t2LOWEMbU,2793
12
19
  hanzo_mcp/tools/common/permissions.py,sha256=4YCfA2PJUOl-z_47n5uaRXO8gAZ_shMaPhpi1dilgRE,7594
13
20
  hanzo_mcp/tools/common/session.py,sha256=csX5ZhgBjO2bdXXXPpsUPzOCc7Tib-apYe01AP8sh8k,2774
14
21
  hanzo_mcp/tools/common/think_tool.py,sha256=I-O6ipM-PUofkNoMMzv37Y_2Yfx9mh7F1upTTsfRN4M,4046
@@ -38,9 +45,9 @@ hanzo_mcp/tools/shell/command_executor.py,sha256=5GcJvg54uty9fl_tkGdWTBcHyjxuynQ
38
45
  hanzo_mcp/tools/shell/run_command.py,sha256=r7HBw0lqabgkGnVsDXmLnrTo0SU9g8gLvzpa-9n-cmM,6891
39
46
  hanzo_mcp/tools/shell/run_script.py,sha256=CLYnDc0Ze8plkXU6d98RgE4UrBg-fwaMVdcn9Fc6Ixw,7432
40
47
  hanzo_mcp/tools/shell/script_tool.py,sha256=s63tawIZBvwgm_kU9P7A3D4v2ulVw7j4l_rpsa_zGuc,8680
41
- hanzo_mcp-0.2.0.dist-info/licenses/LICENSE,sha256=mf1qZGFsPGskoPgytp9B-RsahfKvXsBpmaAbTLGTt8Y,1063
42
- hanzo_mcp-0.2.0.dist-info/METADATA,sha256=dAdJtsK8J9wl9dVksKcMPvaj7xcxtPjcyhbIekF_BX0,7550
43
- hanzo_mcp-0.2.0.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
44
- hanzo_mcp-0.2.0.dist-info/entry_points.txt,sha256=aRKOKXtuQr-idSr-yH4efnRl2v8te94AcgN3ysqqSYs,49
45
- hanzo_mcp-0.2.0.dist-info/top_level.txt,sha256=eGFANatA0MHWiVlpS56fTYRIShtibrSom1uXI6XU0GU,10
46
- hanzo_mcp-0.2.0.dist-info/RECORD,,
48
+ hanzo_mcp-0.3.1.dist-info/licenses/LICENSE,sha256=mf1qZGFsPGskoPgytp9B-RsahfKvXsBpmaAbTLGTt8Y,1063
49
+ hanzo_mcp-0.3.1.dist-info/METADATA,sha256=ay2jJEkPi-ivKlvoCRyCNj57-M0tT36tL4w5nuK_SQ0,7550
50
+ hanzo_mcp-0.3.1.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
51
+ hanzo_mcp-0.3.1.dist-info/entry_points.txt,sha256=aRKOKXtuQr-idSr-yH4efnRl2v8te94AcgN3ysqqSYs,49
52
+ hanzo_mcp-0.3.1.dist-info/top_level.txt,sha256=eGFANatA0MHWiVlpS56fTYRIShtibrSom1uXI6XU0GU,10
53
+ hanzo_mcp-0.3.1.dist-info/RECORD,,