hanzo-mcp 0.3.8__py3-none-any.whl → 0.5.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.

Files changed (93) hide show
  1. hanzo_mcp/__init__.py +1 -1
  2. hanzo_mcp/cli.py +118 -170
  3. hanzo_mcp/cli_enhanced.py +438 -0
  4. hanzo_mcp/config/__init__.py +19 -0
  5. hanzo_mcp/config/settings.py +449 -0
  6. hanzo_mcp/config/tool_config.py +197 -0
  7. hanzo_mcp/prompts/__init__.py +117 -0
  8. hanzo_mcp/prompts/compact_conversation.py +77 -0
  9. hanzo_mcp/prompts/create_release.py +38 -0
  10. hanzo_mcp/prompts/project_system.py +120 -0
  11. hanzo_mcp/prompts/project_todo_reminder.py +111 -0
  12. hanzo_mcp/prompts/utils.py +286 -0
  13. hanzo_mcp/server.py +117 -99
  14. hanzo_mcp/tools/__init__.py +121 -33
  15. hanzo_mcp/tools/agent/__init__.py +8 -11
  16. hanzo_mcp/tools/agent/agent_tool.py +290 -224
  17. hanzo_mcp/tools/agent/prompt.py +16 -13
  18. hanzo_mcp/tools/agent/tool_adapter.py +9 -9
  19. hanzo_mcp/tools/common/__init__.py +17 -16
  20. hanzo_mcp/tools/common/base.py +79 -110
  21. hanzo_mcp/tools/common/batch_tool.py +330 -0
  22. hanzo_mcp/tools/common/config_tool.py +396 -0
  23. hanzo_mcp/tools/common/context.py +26 -292
  24. hanzo_mcp/tools/common/permissions.py +12 -12
  25. hanzo_mcp/tools/common/thinking_tool.py +153 -0
  26. hanzo_mcp/tools/common/validation.py +1 -63
  27. hanzo_mcp/tools/filesystem/__init__.py +97 -57
  28. hanzo_mcp/tools/filesystem/base.py +32 -24
  29. hanzo_mcp/tools/filesystem/content_replace.py +114 -107
  30. hanzo_mcp/tools/filesystem/directory_tree.py +129 -105
  31. hanzo_mcp/tools/filesystem/edit.py +279 -0
  32. hanzo_mcp/tools/filesystem/grep.py +458 -0
  33. hanzo_mcp/tools/filesystem/grep_ast_tool.py +250 -0
  34. hanzo_mcp/tools/filesystem/multi_edit.py +362 -0
  35. hanzo_mcp/tools/filesystem/read.py +255 -0
  36. hanzo_mcp/tools/filesystem/unified_search.py +689 -0
  37. hanzo_mcp/tools/filesystem/write.py +156 -0
  38. hanzo_mcp/tools/jupyter/__init__.py +41 -29
  39. hanzo_mcp/tools/jupyter/base.py +66 -57
  40. hanzo_mcp/tools/jupyter/{edit_notebook.py → notebook_edit.py} +162 -139
  41. hanzo_mcp/tools/jupyter/notebook_read.py +152 -0
  42. hanzo_mcp/tools/shell/__init__.py +29 -20
  43. hanzo_mcp/tools/shell/base.py +87 -45
  44. hanzo_mcp/tools/shell/bash_session.py +731 -0
  45. hanzo_mcp/tools/shell/bash_session_executor.py +295 -0
  46. hanzo_mcp/tools/shell/command_executor.py +435 -384
  47. hanzo_mcp/tools/shell/run_command.py +284 -131
  48. hanzo_mcp/tools/shell/run_command_windows.py +328 -0
  49. hanzo_mcp/tools/shell/session_manager.py +196 -0
  50. hanzo_mcp/tools/shell/session_storage.py +325 -0
  51. hanzo_mcp/tools/todo/__init__.py +66 -0
  52. hanzo_mcp/tools/todo/base.py +319 -0
  53. hanzo_mcp/tools/todo/todo_read.py +148 -0
  54. hanzo_mcp/tools/todo/todo_write.py +378 -0
  55. hanzo_mcp/tools/vector/__init__.py +99 -0
  56. hanzo_mcp/tools/vector/ast_analyzer.py +459 -0
  57. hanzo_mcp/tools/vector/git_ingester.py +482 -0
  58. hanzo_mcp/tools/vector/infinity_store.py +731 -0
  59. hanzo_mcp/tools/vector/mock_infinity.py +162 -0
  60. hanzo_mcp/tools/vector/project_manager.py +361 -0
  61. hanzo_mcp/tools/vector/vector_index.py +116 -0
  62. hanzo_mcp/tools/vector/vector_search.py +225 -0
  63. hanzo_mcp-0.5.1.dist-info/METADATA +276 -0
  64. hanzo_mcp-0.5.1.dist-info/RECORD +68 -0
  65. {hanzo_mcp-0.3.8.dist-info → hanzo_mcp-0.5.1.dist-info}/WHEEL +1 -1
  66. hanzo_mcp/tools/agent/base_provider.py +0 -73
  67. hanzo_mcp/tools/agent/litellm_provider.py +0 -45
  68. hanzo_mcp/tools/agent/lmstudio_agent.py +0 -385
  69. hanzo_mcp/tools/agent/lmstudio_provider.py +0 -219
  70. hanzo_mcp/tools/agent/provider_registry.py +0 -120
  71. hanzo_mcp/tools/common/error_handling.py +0 -86
  72. hanzo_mcp/tools/common/logging_config.py +0 -115
  73. hanzo_mcp/tools/common/session.py +0 -91
  74. hanzo_mcp/tools/common/think_tool.py +0 -123
  75. hanzo_mcp/tools/common/version_tool.py +0 -120
  76. hanzo_mcp/tools/filesystem/edit_file.py +0 -287
  77. hanzo_mcp/tools/filesystem/get_file_info.py +0 -170
  78. hanzo_mcp/tools/filesystem/read_files.py +0 -199
  79. hanzo_mcp/tools/filesystem/search_content.py +0 -275
  80. hanzo_mcp/tools/filesystem/write_file.py +0 -162
  81. hanzo_mcp/tools/jupyter/notebook_operations.py +0 -514
  82. hanzo_mcp/tools/jupyter/read_notebook.py +0 -165
  83. hanzo_mcp/tools/project/__init__.py +0 -64
  84. hanzo_mcp/tools/project/analysis.py +0 -886
  85. hanzo_mcp/tools/project/base.py +0 -66
  86. hanzo_mcp/tools/project/project_analyze.py +0 -173
  87. hanzo_mcp/tools/shell/run_script.py +0 -215
  88. hanzo_mcp/tools/shell/script_tool.py +0 -244
  89. hanzo_mcp-0.3.8.dist-info/METADATA +0 -196
  90. hanzo_mcp-0.3.8.dist-info/RECORD +0 -53
  91. {hanzo_mcp-0.3.8.dist-info → hanzo_mcp-0.5.1.dist-info}/entry_points.txt +0 -0
  92. {hanzo_mcp-0.3.8.dist-info → hanzo_mcp-0.5.1.dist-info}/licenses/LICENSE +0 -0
  93. {hanzo_mcp-0.3.8.dist-info → hanzo_mcp-0.5.1.dist-info}/top_level.txt +0 -0
@@ -1,120 +0,0 @@
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.")
@@ -1,86 +0,0 @@
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)
@@ -1,115 +0,0 @@
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
- from typing import Optional
12
-
13
-
14
- def setup_logging(
15
- log_level: str = "INFO",
16
- log_to_file: bool = True,
17
- log_to_console: bool = False, # Changed default to False
18
- transport: Optional[str] = None,
19
- testing: bool = False
20
- ) -> None:
21
- """Set up logging configuration.
22
-
23
- Args:
24
- log_level: The logging level ("DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL")
25
- log_to_file: Whether to log to a file in addition to the console (default: True)
26
- log_to_console: Whether to log to the console (default: False to avoid stdio transport conflicts)
27
- transport: The transport mechanism being used ("stdio" or "sse")
28
- testing: Set to True to disable file operations for testing
29
- """
30
- # Convert string log level to logging constant
31
- numeric_level = getattr(logging, log_level.upper(), None)
32
- if not isinstance(numeric_level, int):
33
- raise ValueError(f"Invalid log level: {log_level}")
34
-
35
- # Create logs directory if needed
36
- log_dir = Path.home() / ".hanzo" / "logs"
37
- if log_to_file and not testing:
38
- log_dir.mkdir(parents=True, exist_ok=True)
39
-
40
- # Generate log filename based on current date
41
- current_time = datetime.now().strftime("%Y-%m-%d")
42
- log_file = log_dir / f"hanzo-mcp-{current_time}.log"
43
-
44
- # Base configuration
45
- handlers = []
46
-
47
- # Console handler - Always use stderr to avoid interfering with stdio transport
48
- # Disable console logging when using stdio transport to avoid protocol corruption
49
- if log_to_console and (transport != "stdio"):
50
- console = logging.StreamHandler(sys.stderr)
51
- console.setLevel(numeric_level)
52
- console_formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
53
- console.setFormatter(console_formatter)
54
- handlers.append(console)
55
-
56
- # File handler (if enabled)
57
- if log_to_file and not testing:
58
- file_handler = logging.FileHandler(log_file)
59
- file_handler.setLevel(numeric_level)
60
- file_formatter = logging.Formatter(
61
- '%(asctime)s - %(name)s - %(levelname)s - %(filename)s:%(lineno)d - %(message)s'
62
- )
63
- file_handler.setFormatter(file_formatter)
64
- handlers.append(file_handler)
65
-
66
- # Configure root logger
67
- logging.basicConfig(
68
- level=numeric_level,
69
- handlers=handlers,
70
- force=True # Overwrite any existing configuration
71
- )
72
-
73
- # Set specific log levels for third-party libraries
74
- logging.getLogger("urllib3").setLevel(logging.WARNING)
75
- logging.getLogger("asyncio").setLevel(logging.WARNING)
76
-
77
- # Log startup message
78
- root_logger = logging.getLogger()
79
- root_logger.info(f"Logging initialized at level {log_level}")
80
- if log_to_file and not testing:
81
- root_logger.info(f"Log file: {log_file}")
82
- if not log_to_console or transport == "stdio":
83
- root_logger.info("Console logging disabled")
84
-
85
-
86
- def get_log_files() -> list[str]:
87
- """Get a list of all log files.
88
-
89
- Returns:
90
- List of log file paths
91
- """
92
- log_dir = Path.home() / ".hanzo" / "logs"
93
- if not log_dir.exists():
94
- return []
95
-
96
- log_files = [str(f) for f in log_dir.glob("hanzo-mcp-*.log")]
97
- return sorted(log_files, reverse=True)
98
-
99
-
100
- def get_current_log_file() -> Optional[str]:
101
- """Get the path to the current log file.
102
-
103
- Returns:
104
- The path to the current log file, or None if no log file exists
105
- """
106
- log_dir = Path.home() / ".hanzo" / "logs"
107
- if not log_dir.exists():
108
- return None
109
-
110
- current_time = datetime.now().strftime("%Y-%m-%d")
111
- log_file = log_dir / f"hanzo-mcp-{current_time}.log"
112
-
113
- if log_file.exists():
114
- return str(log_file)
115
- return None
@@ -1,91 +0,0 @@
1
- """Session management for maintaining state across tool executions."""
2
-
3
- import os
4
- from pathlib import Path
5
- from typing import Dict, Optional, final
6
-
7
-
8
- @final
9
- class SessionManager:
10
- """Manages session state across tool executions."""
11
-
12
- _instances: Dict[str, "SessionManager"] = {}
13
-
14
- @classmethod
15
- def get_instance(cls, session_id: str) -> "SessionManager":
16
- """Get or create a session manager instance for the given session ID.
17
-
18
- Args:
19
- session_id: The session ID
20
-
21
- Returns:
22
- The session manager instance
23
- """
24
- if session_id not in cls._instances:
25
- cls._instances[session_id] = cls(session_id)
26
- return cls._instances[session_id]
27
-
28
- def __init__(self, session_id: str):
29
- """Initialize the session manager.
30
-
31
- Args:
32
- session_id: The session ID
33
- """
34
- self.session_id = session_id
35
- self._current_working_dir: Optional[Path] = None
36
- self._initial_working_dir: Optional[Path] = None
37
- self._environment_vars: Dict[str, str] = {}
38
-
39
- @property
40
- def current_working_dir(self) -> Path:
41
- """Get the current working directory.
42
-
43
- Returns:
44
- The current working directory
45
- """
46
- if self._current_working_dir is None:
47
- # Default to project directory if set, otherwise use current directory
48
- self._current_working_dir = Path(os.getcwd())
49
- self._initial_working_dir = self._current_working_dir
50
- return self._current_working_dir
51
-
52
- def set_working_dir(self, path: Path) -> None:
53
- """Set the current working directory.
54
-
55
- Args:
56
- path: The path to set as the current working directory
57
- """
58
- self._current_working_dir = path
59
-
60
- def reset_working_dir(self) -> None:
61
- """Reset the working directory to the initial directory."""
62
- if self._initial_working_dir is not None:
63
- self._current_working_dir = self._initial_working_dir
64
-
65
- def set_env_var(self, key: str, value: str) -> None:
66
- """Set an environment variable.
67
-
68
- Args:
69
- key: The environment variable name
70
- value: The environment variable value
71
- """
72
- self._environment_vars[key] = value
73
-
74
- def get_env_var(self, key: str) -> Optional[str]:
75
- """Get an environment variable.
76
-
77
- Args:
78
- key: The environment variable name
79
-
80
- Returns:
81
- The environment variable value, or None if not set
82
- """
83
- return self._environment_vars.get(key)
84
-
85
- def get_env_vars(self) -> Dict[str, str]:
86
- """Get all environment variables.
87
-
88
- Returns:
89
- A dictionary of environment variables
90
- """
91
- return self._environment_vars.copy()
@@ -1,123 +0,0 @@
1
- """Thinking tool implementation.
2
-
3
- This module provides the ThinkingTool for Claude to engage in structured thinking.
4
- """
5
-
6
- from typing import Any, final, override
7
-
8
- from mcp.server.fastmcp import Context as MCPContext
9
- from mcp.server.fastmcp import FastMCP
10
-
11
- from hanzo_mcp.tools.common.base import BaseTool
12
- from hanzo_mcp.tools.common.context import create_tool_context
13
-
14
-
15
- @final
16
- class ThinkingTool(BaseTool):
17
- """Tool for Claude to engage in structured thinking."""
18
-
19
- @property
20
- @override
21
- def name(self) -> str:
22
- """Get the tool name.
23
-
24
- Returns:
25
- Tool name
26
- """
27
- return "think"
28
-
29
- @property
30
- @override
31
- def description(self) -> str:
32
- """Get the tool description.
33
-
34
- Returns:
35
- Tool description
36
- """
37
- return """Use the tool to think about something.
38
-
39
- It will not obtain new information or make any changes to the repository, but just log the thought. Use it when complex reasoning or brainstorming is needed. For example, if you explore the repo and discover the source of a bug, call this tool to brainstorm several unique ways of fixing the bug, and assess which change(s) are likely to be simplest and most effective. Alternatively, if you receive some test results, call this tool to brainstorm ways to fix the failing tests."""
40
-
41
- @property
42
- @override
43
- def parameters(self) -> dict[str, Any]:
44
- """Get the parameter specifications for the tool.
45
-
46
- Returns:
47
- Parameter specifications
48
- """
49
- return {
50
- "properties": {
51
- "thought": {
52
- "title": "Thought",
53
- "type": "string"
54
- }
55
- },
56
- "required": ["thought"],
57
- "title": "thinkArguments",
58
- "type": "object"
59
- }
60
-
61
- @property
62
- @override
63
- def required(self) -> list[str]:
64
- """Get the list of required parameter names.
65
-
66
- Returns:
67
- List of required parameter names
68
- """
69
- return ["thought"]
70
-
71
- def __init__(self) -> None:
72
- """Initialize the thinking tool."""
73
- pass
74
-
75
- @override
76
- async def call(self, ctx: MCPContext, **params: Any) -> str:
77
- """Execute the tool with the given parameters.
78
-
79
- Args:
80
- ctx: MCP context
81
- **params: Tool parameters
82
-
83
- Returns:
84
- Tool result
85
- """
86
- tool_ctx = create_tool_context(ctx)
87
- tool_ctx.set_tool_info(self.name)
88
-
89
- # Extract parameters
90
- thought = params.get("thought")
91
-
92
- # Validate required thought parameter
93
- if not thought:
94
- await tool_ctx.error(
95
- "Parameter 'thought' is required but was None or empty"
96
- )
97
- return "Error: Parameter 'thought' is required but was None or empty"
98
-
99
- if thought.strip() == "":
100
- await tool_ctx.error("Parameter 'thought' cannot be empty")
101
- return "Error: Parameter 'thought' cannot be empty"
102
-
103
- # Log the thought but don't take action
104
- await tool_ctx.info("Thinking process recorded")
105
-
106
- # Return confirmation
107
- return "I've recorded your thinking process. You can continue with your next action based on this analysis."
108
-
109
- @override
110
- def register(self, mcp_server: FastMCP) -> None:
111
- """Register this thinking tool with the MCP server.
112
-
113
- Creates a wrapper function with explicitly defined parameters that match
114
- the tool's parameter schema and registers it with the MCP server.
115
-
116
- Args:
117
- mcp_server: The FastMCP server instance
118
- """
119
- tool_self = self # Create a reference to self for use in the closure
120
-
121
- @mcp_server.tool(name=self.name, description=self.mcp_description)
122
- async def think(thought: str, ctx: MCPContext) -> str:
123
- return await tool_self.call(ctx, thought=thought)
@@ -1,120 +0,0 @@
1
- """Version tool for displaying project version information."""
2
-
3
- from typing import Any, Dict, TypedDict, final, override
4
-
5
- from mcp.server.fastmcp import Context as MCPContext
6
- from mcp.server.fastmcp import FastMCP
7
-
8
- from hanzo_mcp.tools.common.base import BaseTool
9
- from hanzo_mcp.tools.common.context import create_tool_context
10
-
11
-
12
- class VersionToolResponse(TypedDict):
13
- """Response from the version tool."""
14
-
15
- version: str
16
- package_name: str
17
-
18
-
19
- @final
20
- class VersionTool(BaseTool):
21
- """Tool for displaying version information about the Hanzo MCP package."""
22
-
23
- @property
24
- @override
25
- def name(self) -> str:
26
- """Get the tool name.
27
-
28
- Returns:
29
- Tool name
30
- """
31
- return "version"
32
-
33
- @property
34
- @override
35
- def description(self) -> str:
36
- """Get the tool description.
37
-
38
- Returns:
39
- Tool description
40
- """
41
- return "Display the current version of hanzo-mcp"
42
-
43
- @property
44
- @override
45
- def parameters(self) -> dict[str, Any]:
46
- """Get the parameter specifications for the tool.
47
-
48
- Returns:
49
- Parameter specifications
50
- """
51
- return {
52
- "properties": {},
53
- "required": [],
54
- "title": "versionArguments",
55
- "type": "object"
56
- }
57
-
58
- @property
59
- @override
60
- def required(self) -> list[str]:
61
- """Get the list of required parameter names.
62
-
63
- Returns:
64
- List of required parameter names
65
- """
66
- return []
67
-
68
- def __init__(self, mcp_server: FastMCP) -> None:
69
- """Initialize the version tool and register it with the server.
70
-
71
- Args:
72
- mcp_server: The MCP server to register with
73
- """
74
- self.register(mcp_server)
75
-
76
- @override
77
- async def call(self, ctx: MCPContext, **params: Any) -> str:
78
- """Execute the tool with the given parameters.
79
-
80
- Args:
81
- ctx: MCP context
82
- **params: Tool parameters
83
-
84
- Returns:
85
- Tool result with version information
86
- """
87
- tool_ctx = create_tool_context(ctx)
88
- tool_ctx.set_tool_info(self.name)
89
-
90
- version_info = self.get_version()
91
- await tool_ctx.info(f"Hanzo MCP version: {version_info['version']}")
92
-
93
- return f"Hanzo MCP version: {version_info['version']}"
94
-
95
- def get_version(self) -> VersionToolResponse:
96
- """Get the current version of the hanzo-mcp package.
97
-
98
- Returns:
99
- A dictionary containing the package name and version
100
- """
101
- # Directly use the __version__ from the hanzo_mcp package
102
- from hanzo_mcp import __version__
103
-
104
- return {"version": __version__, "package_name": "hanzo-mcp"}
105
-
106
- @override
107
- def register(self, mcp_server: FastMCP) -> None:
108
- """Register this version tool with the MCP server.
109
-
110
- Creates a wrapper function that calls this tool's call method and
111
- registers it with the MCP server.
112
-
113
- Args:
114
- mcp_server: The FastMCP server instance
115
- """
116
- tool_self = self # Create a reference to self for use in the closure
117
-
118
- @mcp_server.tool(name=self.name, description=self.mcp_description)
119
- async def version(ctx: MCPContext) -> str:
120
- return await tool_self.call(ctx)