hanzo-mcp 0.9.0__py3-none-any.whl → 0.9.2__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 (135) hide show
  1. hanzo_mcp/__init__.py +1 -1
  2. hanzo_mcp/analytics/posthog_analytics.py +14 -1
  3. hanzo_mcp/cli.py +108 -4
  4. hanzo_mcp/server.py +11 -0
  5. hanzo_mcp/tools/__init__.py +3 -16
  6. hanzo_mcp/tools/agent/__init__.py +5 -0
  7. hanzo_mcp/tools/agent/agent.py +5 -0
  8. hanzo_mcp/tools/agent/agent_tool.py +3 -17
  9. hanzo_mcp/tools/agent/agent_tool_v1_deprecated.py +623 -0
  10. hanzo_mcp/tools/agent/clarification_tool.py +7 -1
  11. hanzo_mcp/tools/agent/claude_desktop_auth.py +16 -6
  12. hanzo_mcp/tools/agent/cli_agent_base.py +5 -0
  13. hanzo_mcp/tools/agent/cli_tools.py +26 -0
  14. hanzo_mcp/tools/agent/code_auth_tool.py +5 -0
  15. hanzo_mcp/tools/agent/critic_tool.py +7 -1
  16. hanzo_mcp/tools/agent/iching_tool.py +5 -0
  17. hanzo_mcp/tools/agent/network_tool.py +5 -0
  18. hanzo_mcp/tools/agent/review_tool.py +7 -1
  19. hanzo_mcp/tools/agent/swarm_alias.py +5 -0
  20. hanzo_mcp/tools/agent/swarm_tool.py +701 -0
  21. hanzo_mcp/tools/agent/swarm_tool_v1_deprecated.py +554 -0
  22. hanzo_mcp/tools/agent/unified_cli_tools.py +5 -0
  23. hanzo_mcp/tools/common/auto_timeout.py +254 -0
  24. hanzo_mcp/tools/common/base.py +4 -0
  25. hanzo_mcp/tools/common/batch_tool.py +5 -0
  26. hanzo_mcp/tools/common/config_tool.py +5 -0
  27. hanzo_mcp/tools/common/critic_tool.py +5 -0
  28. hanzo_mcp/tools/common/paginated_base.py +4 -0
  29. hanzo_mcp/tools/common/permissions.py +38 -12
  30. hanzo_mcp/tools/common/personality.py +673 -980
  31. hanzo_mcp/tools/common/stats.py +5 -0
  32. hanzo_mcp/tools/common/thinking_tool.py +5 -0
  33. hanzo_mcp/tools/common/timeout_parser.py +103 -0
  34. hanzo_mcp/tools/common/tool_disable.py +5 -0
  35. hanzo_mcp/tools/common/tool_enable.py +5 -0
  36. hanzo_mcp/tools/common/tool_list.py +5 -0
  37. hanzo_mcp/tools/config/config_tool.py +5 -0
  38. hanzo_mcp/tools/config/mode_tool.py +5 -0
  39. hanzo_mcp/tools/database/graph.py +5 -0
  40. hanzo_mcp/tools/database/graph_add.py +5 -0
  41. hanzo_mcp/tools/database/graph_query.py +5 -0
  42. hanzo_mcp/tools/database/graph_remove.py +5 -0
  43. hanzo_mcp/tools/database/graph_search.py +5 -0
  44. hanzo_mcp/tools/database/graph_stats.py +5 -0
  45. hanzo_mcp/tools/database/sql.py +5 -0
  46. hanzo_mcp/tools/database/sql_query.py +2 -0
  47. hanzo_mcp/tools/database/sql_search.py +5 -0
  48. hanzo_mcp/tools/database/sql_stats.py +5 -0
  49. hanzo_mcp/tools/editor/neovim_command.py +5 -0
  50. hanzo_mcp/tools/editor/neovim_edit.py +7 -2
  51. hanzo_mcp/tools/editor/neovim_session.py +5 -0
  52. hanzo_mcp/tools/filesystem/__init__.py +23 -26
  53. hanzo_mcp/tools/filesystem/ast_tool.py +3 -4
  54. hanzo_mcp/tools/filesystem/base.py +2 -18
  55. hanzo_mcp/tools/filesystem/batch_search.py +825 -0
  56. hanzo_mcp/tools/filesystem/content_replace.py +5 -3
  57. hanzo_mcp/tools/filesystem/diff.py +5 -0
  58. hanzo_mcp/tools/filesystem/directory_tree.py +34 -281
  59. hanzo_mcp/tools/filesystem/directory_tree_paginated.py +345 -0
  60. hanzo_mcp/tools/filesystem/edit.py +6 -5
  61. hanzo_mcp/tools/filesystem/find.py +177 -311
  62. hanzo_mcp/tools/filesystem/find_files.py +370 -0
  63. hanzo_mcp/tools/filesystem/git_search.py +5 -3
  64. hanzo_mcp/tools/filesystem/grep.py +454 -0
  65. hanzo_mcp/tools/filesystem/multi_edit.py +6 -5
  66. hanzo_mcp/tools/filesystem/read.py +10 -9
  67. hanzo_mcp/tools/filesystem/rules_tool.py +6 -4
  68. hanzo_mcp/tools/filesystem/search_tool.py +728 -0
  69. hanzo_mcp/tools/filesystem/symbols_tool.py +510 -0
  70. hanzo_mcp/tools/filesystem/tree.py +273 -0
  71. hanzo_mcp/tools/filesystem/watch.py +6 -1
  72. hanzo_mcp/tools/filesystem/write.py +13 -7
  73. hanzo_mcp/tools/jupyter/jupyter.py +30 -2
  74. hanzo_mcp/tools/jupyter/notebook_edit.py +298 -0
  75. hanzo_mcp/tools/jupyter/notebook_read.py +148 -0
  76. hanzo_mcp/tools/llm/consensus_tool.py +8 -6
  77. hanzo_mcp/tools/llm/llm_manage.py +5 -0
  78. hanzo_mcp/tools/llm/llm_tool.py +2 -0
  79. hanzo_mcp/tools/llm/llm_unified.py +5 -0
  80. hanzo_mcp/tools/llm/provider_tools.py +5 -0
  81. hanzo_mcp/tools/lsp/lsp_tool.py +475 -622
  82. hanzo_mcp/tools/mcp/mcp_add.py +7 -2
  83. hanzo_mcp/tools/mcp/mcp_remove.py +15 -2
  84. hanzo_mcp/tools/mcp/mcp_stats.py +5 -0
  85. hanzo_mcp/tools/mcp/mcp_tool.py +5 -0
  86. hanzo_mcp/tools/memory/knowledge_tools.py +14 -0
  87. hanzo_mcp/tools/memory/memory_tools.py +17 -0
  88. hanzo_mcp/tools/search/find_tool.py +5 -3
  89. hanzo_mcp/tools/search/unified_search.py +3 -1
  90. hanzo_mcp/tools/shell/__init__.py +2 -14
  91. hanzo_mcp/tools/shell/base_process.py +4 -2
  92. hanzo_mcp/tools/shell/bash_tool.py +2 -0
  93. hanzo_mcp/tools/shell/command_executor.py +7 -7
  94. hanzo_mcp/tools/shell/logs.py +5 -0
  95. hanzo_mcp/tools/shell/npx.py +5 -0
  96. hanzo_mcp/tools/shell/npx_background.py +5 -0
  97. hanzo_mcp/tools/shell/npx_tool.py +5 -0
  98. hanzo_mcp/tools/shell/open.py +5 -0
  99. hanzo_mcp/tools/shell/pkill.py +5 -0
  100. hanzo_mcp/tools/shell/process_tool.py +5 -0
  101. hanzo_mcp/tools/shell/processes.py +5 -0
  102. hanzo_mcp/tools/shell/run_background.py +5 -0
  103. hanzo_mcp/tools/shell/run_command.py +2 -0
  104. hanzo_mcp/tools/shell/run_command_windows.py +5 -0
  105. hanzo_mcp/tools/shell/streaming_command.py +5 -0
  106. hanzo_mcp/tools/shell/uvx.py +5 -0
  107. hanzo_mcp/tools/shell/uvx_background.py +5 -0
  108. hanzo_mcp/tools/shell/uvx_tool.py +5 -0
  109. hanzo_mcp/tools/shell/zsh_tool.py +3 -0
  110. hanzo_mcp/tools/todo/todo.py +5 -0
  111. hanzo_mcp/tools/todo/todo_read.py +142 -0
  112. hanzo_mcp/tools/todo/todo_write.py +367 -0
  113. hanzo_mcp/tools/vector/__init__.py +42 -95
  114. hanzo_mcp/tools/vector/index_tool.py +5 -0
  115. hanzo_mcp/tools/vector/vector.py +5 -0
  116. hanzo_mcp/tools/vector/vector_index.py +5 -0
  117. hanzo_mcp/tools/vector/vector_search.py +5 -0
  118. {hanzo_mcp-0.9.0.dist-info → hanzo_mcp-0.9.2.dist-info}/METADATA +1 -1
  119. hanzo_mcp-0.9.2.dist-info/RECORD +195 -0
  120. hanzo_mcp/tools/common/path_utils.py +0 -34
  121. hanzo_mcp/tools/compiler/__init__.py +0 -8
  122. hanzo_mcp/tools/compiler/sandboxed_compiler.py +0 -681
  123. hanzo_mcp/tools/environment/__init__.py +0 -8
  124. hanzo_mcp/tools/environment/environment_detector.py +0 -594
  125. hanzo_mcp/tools/filesystem/search.py +0 -1160
  126. hanzo_mcp/tools/framework/__init__.py +0 -8
  127. hanzo_mcp/tools/framework/framework_modes.py +0 -714
  128. hanzo_mcp/tools/memory/conversation_memory.py +0 -636
  129. hanzo_mcp/tools/shell/run_tool.py +0 -56
  130. hanzo_mcp/tools/vector/node_tool.py +0 -538
  131. hanzo_mcp/tools/vector/unified_vector.py +0 -384
  132. hanzo_mcp-0.9.0.dist-info/RECORD +0 -191
  133. {hanzo_mcp-0.9.0.dist-info → hanzo_mcp-0.9.2.dist-info}/WHEEL +0 -0
  134. {hanzo_mcp-0.9.0.dist-info → hanzo_mcp-0.9.2.dist-info}/entry_points.txt +0 -0
  135. {hanzo_mcp-0.9.0.dist-info → hanzo_mcp-0.9.2.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,148 @@
1
+ """Read notebook tool implementation.
2
+
3
+ This module provides the NotebookReadTool for reading Jupyter notebook files.
4
+ """
5
+
6
+ import json
7
+ from typing import Unpack, Annotated, TypedDict, final, override
8
+ from pathlib import Path
9
+
10
+ from pydantic import Field
11
+ from mcp.server import FastMCP
12
+
13
+ from hanzo_mcp.tools.common.auto_timeout import auto_timeout
14
+ from mcp.server.fastmcp import Context as MCPContext
15
+
16
+ from hanzo_mcp.tools.jupyter.base import JupyterBaseTool
17
+
18
+ NotebookPath = Annotated[
19
+ str,
20
+ Field(
21
+ description="The absolute path to the Jupyter notebook file to read (must be absolute, not relative)",
22
+ ),
23
+ ]
24
+
25
+
26
+ class NotebookReadToolParams(TypedDict):
27
+ """Parameters for the NotebookReadTool.
28
+
29
+ Attributes:
30
+ notebook_path: The absolute path to the Jupyter notebook file to read (must be absolute, not relative)
31
+ """
32
+
33
+ notebook_path: NotebookPath
34
+
35
+
36
+ @final
37
+ class NotebookReadTool(JupyterBaseTool):
38
+ """Tool for reading Jupyter notebook files."""
39
+
40
+ @property
41
+ @override
42
+ def name(self) -> str:
43
+ """Get the tool name.
44
+
45
+ Returns:
46
+ Tool name
47
+ """
48
+ return "notebook_read"
49
+
50
+ @property
51
+ @override
52
+ def description(self) -> str:
53
+ """Get the tool description.
54
+
55
+ Returns:
56
+ Tool description
57
+ """
58
+ return "Reads a Jupyter notebook (.ipynb file) and returns all of the cells with their outputs. Jupyter notebooks are interactive documents that combine code, text, and visualizations, commonly used for data analysis and scientific computing. The notebook_path parameter must be an absolute path, not a relative path."
59
+
60
+ @override
61
+ @auto_timeout("notebook_read")
62
+
63
+
64
+ async def call(
65
+ self,
66
+ ctx: MCPContext,
67
+ **params: Unpack[NotebookReadToolParams],
68
+ ) -> str:
69
+ """Execute the tool with the given parameters.
70
+
71
+ Args:
72
+ ctx: MCP context
73
+ **params: Tool parameters
74
+
75
+ Returns:
76
+ Tool result
77
+ """
78
+ tool_ctx = self.create_tool_context(ctx)
79
+ self.set_tool_context_info(tool_ctx)
80
+
81
+ # Extract parameters
82
+ notebook_path: NotebookPath = params["notebook_path"]
83
+
84
+ # Validate path parameter
85
+ path_validation = self.validate_path(notebook_path)
86
+ if path_validation.is_error:
87
+ await tool_ctx.error(path_validation.error_message)
88
+ return f"Error: {path_validation.error_message}"
89
+
90
+ await tool_ctx.info(f"Reading notebook: {notebook_path}")
91
+
92
+ # Check if path is allowed
93
+ if not self.is_path_allowed(notebook_path):
94
+ await tool_ctx.error(f"Access denied - path outside allowed directories: {notebook_path}")
95
+ return f"Error: Access denied - path outside allowed directories: {notebook_path}"
96
+
97
+ try:
98
+ file_path = Path(notebook_path)
99
+
100
+ if not file_path.exists():
101
+ await tool_ctx.error(f"File does not exist: {notebook_path}")
102
+ return f"Error: File does not exist: {notebook_path}"
103
+
104
+ if not file_path.is_file():
105
+ await tool_ctx.error(f"Path is not a file: {notebook_path}")
106
+ return f"Error: Path is not a file: {notebook_path}"
107
+
108
+ # Check file extension
109
+ if file_path.suffix.lower() != ".ipynb":
110
+ await tool_ctx.error(f"File is not a Jupyter notebook: {notebook_path}")
111
+ return f"Error: File is not a Jupyter notebook: {notebook_path}"
112
+
113
+ # Read and parse the notebook
114
+ try:
115
+ # This will read the file, so we don't need to read it separately
116
+ _, processed_cells = await self.parse_notebook(file_path)
117
+
118
+ # Format the notebook content as a readable string
119
+ result = self.format_notebook_cells(processed_cells)
120
+
121
+ await tool_ctx.info(f"Successfully read notebook: {notebook_path} ({len(processed_cells)} cells)")
122
+ return result
123
+ except json.JSONDecodeError:
124
+ await tool_ctx.error(f"Invalid notebook format: {notebook_path}")
125
+ return f"Error: Invalid notebook format: {notebook_path}"
126
+ except UnicodeDecodeError:
127
+ await tool_ctx.error(f"Cannot read notebook file: {notebook_path}")
128
+ return f"Error: Cannot read notebook file: {notebook_path}"
129
+ except Exception as e:
130
+ await tool_ctx.error(f"Error reading notebook: {str(e)}")
131
+ return f"Error reading notebook: {str(e)}"
132
+
133
+ @override
134
+ def register(self, mcp_server: FastMCP) -> None:
135
+ """Register this read notebook tool with the MCP server.
136
+
137
+ Creates a wrapper function with explicitly defined parameters that match
138
+ the tool's parameter schema and registers it with the MCP server.
139
+
140
+ Args:
141
+ mcp_server: The FastMCP server instance
142
+ """
143
+
144
+ tool_self = self # Create a reference to self for use in the closure
145
+
146
+ @mcp_server.tool(name=self.name, description=self.description)
147
+ async def notebook_read(notebook_path: NotebookPath, ctx: MCPContext) -> str:
148
+ return await tool_self.call(ctx, notebook_path=notebook_path)
@@ -15,9 +15,10 @@ from typing import (
15
15
  from pydantic import Field
16
16
  from mcp.server.fastmcp import Context as MCPContext
17
17
 
18
+ from hanzo_mcp.tools.common.auto_timeout import auto_timeout
18
19
  from hanzo_mcp.tools.common.base import BaseTool
19
20
  from hanzo_mcp.tools.llm.llm_tool import LLMTool
20
- from hanzo_mcp.tools.common.context import create_tool_context
21
+ from hanzo_mcp.tools.common.context import create_tool_context, ToolContext
21
22
 
22
23
  Prompt = Annotated[
23
24
  str,
@@ -155,6 +156,7 @@ The tool will:
155
156
  """
156
157
 
157
158
  @override
159
+ @auto_timeout("consensus")
158
160
  async def call(
159
161
  self,
160
162
  ctx: MCPContext,
@@ -269,10 +271,10 @@ The tool will:
269
271
  if max_tokens:
270
272
  params["max_tokens"] = max_tokens
271
273
 
272
- # Create a mock context for the LLM tool
273
- mock_ctx = type("MockContext", (), {"client": None})()
274
+ # Create a proper context for the LLM tool
275
+ tool_ctx = create_tool_context("consensus", self.server)
274
276
 
275
- result = await asyncio.wait_for(self.llm_tool.call(mock_ctx, **params), timeout=timeout)
277
+ result = await asyncio.wait_for(self.llm_tool.call(tool_ctx, **params), timeout=timeout)
276
278
  return (model, result)
277
279
  except asyncio.TimeoutError:
278
280
  return (model, f"Error: Timeout after {timeout} seconds")
@@ -317,7 +319,7 @@ Be concise but thorough. Focus on providing actionable insights."""
317
319
 
318
320
  try:
319
321
  # Use the LLM tool to get the aggregation
320
- mock_ctx = type("MockContext", (), {"client": None})()
322
+ tool_ctx = create_tool_context("consensus_aggregation", self.server)
321
323
 
322
324
  aggregation_params = {
323
325
  "model": aggregation_model,
@@ -326,7 +328,7 @@ Be concise but thorough. Focus on providing actionable insights."""
326
328
  "system_prompt": "You are an expert at analyzing and synthesizing multiple AI responses to provide balanced, insightful consensus.",
327
329
  }
328
330
 
329
- result = await self.llm_tool.call(mock_ctx, **aggregation_params)
331
+ result = await self.llm_tool.call(tool_ctx, **aggregation_params)
330
332
  return result
331
333
 
332
334
  except Exception:
@@ -7,6 +7,8 @@ from pathlib import Path
7
7
  from pydantic import Field
8
8
  from mcp.server.fastmcp import Context as MCPContext
9
9
 
10
+ from hanzo_mcp.tools.common.auto_timeout import auto_timeout
11
+
10
12
  from hanzo_mcp.tools.common.base import BaseTool
11
13
  from hanzo_mcp.tools.llm.llm_tool import LLMTool
12
14
  from hanzo_mcp.tools.common.context import create_tool_context
@@ -110,6 +112,9 @@ Providers are automatically detected based on environment variables:
110
112
  """
111
113
 
112
114
  @override
115
+ @auto_timeout("llm_manage")
116
+
117
+
113
118
  async def call(
114
119
  self,
115
120
  ctx: MCPContext,
@@ -21,6 +21,7 @@ from mcp.server.fastmcp import Context as MCPContext
21
21
 
22
22
  from hanzo_mcp.tools.common.base import BaseTool
23
23
  from hanzo_mcp.tools.common.context import create_tool_context
24
+ from hanzo_mcp.tools.common.auto_timeout import auto_timeout
24
25
 
25
26
  # Check if litellm is available
26
27
  try:
@@ -263,6 +264,7 @@ llm --action models --provider openai
263
264
  Available: {", ".join(available) if available else "None"}"""
264
265
 
265
266
  @override
267
+ @auto_timeout("llm")
266
268
  async def call(
267
269
  self,
268
270
  ctx: MCPContext,
@@ -19,6 +19,8 @@ from pathlib import Path
19
19
  from pydantic import Field
20
20
  from mcp.server.fastmcp import Context as MCPContext
21
21
 
22
+ from hanzo_mcp.tools.common.auto_timeout import auto_timeout
23
+
22
24
  from hanzo_mcp.tools.common.base import BaseTool
23
25
  from hanzo_mcp.tools.common.context import create_tool_context
24
26
 
@@ -263,6 +265,9 @@ llm --action models --provider openai
263
265
  Available: {", ".join(available) if available else "None"}"""
264
266
 
265
267
  @override
268
+ @auto_timeout("llm_unified")
269
+
270
+
266
271
  async def call(
267
272
  self,
268
273
  ctx: MCPContext,
@@ -5,6 +5,8 @@ from typing import Dict, Unpack, Optional, Annotated, TypedDict, final, override
5
5
  from pydantic import Field
6
6
  from mcp.server.fastmcp import Context as MCPContext
7
7
 
8
+ from hanzo_mcp.tools.common.auto_timeout import auto_timeout
9
+
8
10
  from hanzo_mcp.tools.common.base import BaseTool
9
11
  from hanzo_mcp.tools.llm.llm_tool import LLMTool
10
12
 
@@ -98,6 +100,9 @@ class BaseProviderTool(BaseTool):
98
100
  return model
99
101
 
100
102
  @override
103
+ @auto_timeout("provider_tools")
104
+
105
+
101
106
  async def call(
102
107
  self,
103
108
  ctx: MCPContext,