hanzo-mcp 0.6.13__py3-none-any.whl → 0.7.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 (62) hide show
  1. hanzo_mcp/analytics/__init__.py +5 -0
  2. hanzo_mcp/analytics/posthog_analytics.py +364 -0
  3. hanzo_mcp/cli.py +3 -3
  4. hanzo_mcp/cli_enhanced.py +3 -3
  5. hanzo_mcp/config/settings.py +1 -1
  6. hanzo_mcp/config/tool_config.py +18 -4
  7. hanzo_mcp/server.py +34 -1
  8. hanzo_mcp/tools/__init__.py +65 -2
  9. hanzo_mcp/tools/agent/__init__.py +84 -3
  10. hanzo_mcp/tools/agent/agent_tool.py +102 -4
  11. hanzo_mcp/tools/agent/agent_tool_v2.py +492 -0
  12. hanzo_mcp/tools/agent/clarification_protocol.py +220 -0
  13. hanzo_mcp/tools/agent/clarification_tool.py +68 -0
  14. hanzo_mcp/tools/agent/claude_cli_tool.py +125 -0
  15. hanzo_mcp/tools/agent/claude_desktop_auth.py +508 -0
  16. hanzo_mcp/tools/agent/cli_agent_base.py +191 -0
  17. hanzo_mcp/tools/agent/code_auth.py +436 -0
  18. hanzo_mcp/tools/agent/code_auth_tool.py +194 -0
  19. hanzo_mcp/tools/agent/codex_cli_tool.py +123 -0
  20. hanzo_mcp/tools/agent/critic_tool.py +376 -0
  21. hanzo_mcp/tools/agent/gemini_cli_tool.py +128 -0
  22. hanzo_mcp/tools/agent/grok_cli_tool.py +128 -0
  23. hanzo_mcp/tools/agent/iching_tool.py +380 -0
  24. hanzo_mcp/tools/agent/network_tool.py +273 -0
  25. hanzo_mcp/tools/agent/prompt.py +62 -20
  26. hanzo_mcp/tools/agent/review_tool.py +433 -0
  27. hanzo_mcp/tools/agent/swarm_tool.py +535 -0
  28. hanzo_mcp/tools/agent/swarm_tool_v2.py +654 -0
  29. hanzo_mcp/tools/common/base.py +1 -0
  30. hanzo_mcp/tools/common/batch_tool.py +102 -10
  31. hanzo_mcp/tools/common/fastmcp_pagination.py +369 -0
  32. hanzo_mcp/tools/common/forgiving_edit.py +243 -0
  33. hanzo_mcp/tools/common/paginated_base.py +230 -0
  34. hanzo_mcp/tools/common/paginated_response.py +307 -0
  35. hanzo_mcp/tools/common/pagination.py +226 -0
  36. hanzo_mcp/tools/common/tool_list.py +3 -0
  37. hanzo_mcp/tools/common/truncate.py +101 -0
  38. hanzo_mcp/tools/filesystem/__init__.py +29 -0
  39. hanzo_mcp/tools/filesystem/ast_multi_edit.py +562 -0
  40. hanzo_mcp/tools/filesystem/directory_tree_paginated.py +338 -0
  41. hanzo_mcp/tools/lsp/__init__.py +5 -0
  42. hanzo_mcp/tools/lsp/lsp_tool.py +512 -0
  43. hanzo_mcp/tools/memory/__init__.py +76 -0
  44. hanzo_mcp/tools/memory/knowledge_tools.py +518 -0
  45. hanzo_mcp/tools/memory/memory_tools.py +456 -0
  46. hanzo_mcp/tools/search/__init__.py +6 -0
  47. hanzo_mcp/tools/search/find_tool.py +581 -0
  48. hanzo_mcp/tools/search/unified_search.py +953 -0
  49. hanzo_mcp/tools/shell/__init__.py +5 -0
  50. hanzo_mcp/tools/shell/auto_background.py +203 -0
  51. hanzo_mcp/tools/shell/base_process.py +53 -27
  52. hanzo_mcp/tools/shell/bash_tool.py +17 -33
  53. hanzo_mcp/tools/shell/npx_tool.py +15 -32
  54. hanzo_mcp/tools/shell/streaming_command.py +594 -0
  55. hanzo_mcp/tools/shell/uvx_tool.py +15 -32
  56. hanzo_mcp/types.py +23 -0
  57. {hanzo_mcp-0.6.13.dist-info → hanzo_mcp-0.7.1.dist-info}/METADATA +229 -71
  58. {hanzo_mcp-0.6.13.dist-info → hanzo_mcp-0.7.1.dist-info}/RECORD +61 -24
  59. hanzo_mcp-0.6.13.dist-info/licenses/LICENSE +0 -21
  60. {hanzo_mcp-0.6.13.dist-info → hanzo_mcp-0.7.1.dist-info}/WHEEL +0 -0
  61. {hanzo_mcp-0.6.13.dist-info → hanzo_mcp-0.7.1.dist-info}/entry_points.txt +0 -0
  62. {hanzo_mcp-0.6.13.dist-info → hanzo_mcp-0.7.1.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,220 @@
1
+ """Clarification protocol for agent-to-mainloop communication.
2
+
3
+ This module provides a protocol for agents to request clarification
4
+ from the main loop without human intervention.
5
+ """
6
+
7
+ import json
8
+ from dataclasses import dataclass
9
+ from typing import Any, Dict, List, Optional
10
+ from enum import Enum
11
+
12
+
13
+ class ClarificationType(Enum):
14
+ """Types of clarification requests."""
15
+ AMBIGUOUS_INSTRUCTION = "ambiguous_instruction"
16
+ MISSING_CONTEXT = "missing_context"
17
+ MULTIPLE_OPTIONS = "multiple_options"
18
+ CONFIRMATION_NEEDED = "confirmation_needed"
19
+ ADDITIONAL_INFO = "additional_info"
20
+
21
+
22
+ @dataclass
23
+ class ClarificationRequest:
24
+ """A request for clarification from an agent."""
25
+
26
+ agent_id: str
27
+ request_type: ClarificationType
28
+ question: str
29
+ context: Dict[str, Any]
30
+ options: Optional[List[str]] = None
31
+
32
+ def to_json(self) -> str:
33
+ """Convert to JSON for transport."""
34
+ return json.dumps({
35
+ "agent_id": self.agent_id,
36
+ "request_type": self.request_type.value,
37
+ "question": self.question,
38
+ "context": self.context,
39
+ "options": self.options
40
+ })
41
+
42
+ @classmethod
43
+ def from_json(cls, data: str) -> "ClarificationRequest":
44
+ """Create from JSON string."""
45
+ obj = json.loads(data)
46
+ return cls(
47
+ agent_id=obj["agent_id"],
48
+ request_type=ClarificationType(obj["request_type"]),
49
+ question=obj["question"],
50
+ context=obj["context"],
51
+ options=obj.get("options")
52
+ )
53
+
54
+
55
+ @dataclass
56
+ class ClarificationResponse:
57
+ """A response to a clarification request."""
58
+
59
+ request_id: str
60
+ answer: str
61
+ additional_context: Optional[Dict[str, Any]] = None
62
+
63
+ def to_json(self) -> str:
64
+ """Convert to JSON for transport."""
65
+ return json.dumps({
66
+ "request_id": self.request_id,
67
+ "answer": self.answer,
68
+ "additional_context": self.additional_context
69
+ })
70
+
71
+
72
+ class ClarificationHandler:
73
+ """Handles clarification requests from agents."""
74
+
75
+ def __init__(self):
76
+ self.pending_requests: Dict[str, ClarificationRequest] = {}
77
+ self.request_counter = 0
78
+
79
+ def create_request(
80
+ self,
81
+ agent_id: str,
82
+ request_type: ClarificationType,
83
+ question: str,
84
+ context: Dict[str, Any],
85
+ options: Optional[List[str]] = None
86
+ ) -> str:
87
+ """Create a new clarification request.
88
+
89
+ Returns:
90
+ Request ID for tracking
91
+ """
92
+ request = ClarificationRequest(
93
+ agent_id=agent_id,
94
+ request_type=request_type,
95
+ question=question,
96
+ context=context,
97
+ options=options
98
+ )
99
+
100
+ request_id = f"clarify_{self.request_counter}"
101
+ self.request_counter += 1
102
+ self.pending_requests[request_id] = request
103
+
104
+ return request_id
105
+
106
+ def handle_request(self, request: ClarificationRequest) -> ClarificationResponse:
107
+ """Handle a clarification request automatically.
108
+
109
+ This method implements automatic clarification resolution
110
+ based on context and common patterns.
111
+ """
112
+ request_id = f"clarify_{len(self.pending_requests)}"
113
+
114
+ # Handle different types of clarification
115
+ if request.request_type == ClarificationType.AMBIGUOUS_INSTRUCTION:
116
+ # Try to clarify based on context
117
+ if "file_path" in request.context:
118
+ if request.context["file_path"].endswith(".go"):
119
+ answer = "For Go files, ensure you add imports in the correct format and handle both single import and import block cases."
120
+ elif request.context["file_path"].endswith(".py"):
121
+ answer = "For Python files, add imports at the top of the file after any module docstring."
122
+ else:
123
+ answer = "Add imports according to the language's conventions."
124
+ else:
125
+ answer = "Proceed with the most reasonable interpretation based on the context."
126
+
127
+ elif request.request_type == ClarificationType.MISSING_CONTEXT:
128
+ # Provide additional context based on what's missing
129
+ if "import_path" in request.question.lower():
130
+ answer = "Use the standard import path based on the project structure. Check existing imports in similar files for patterns."
131
+ elif "format" in request.question.lower():
132
+ answer = "Match the existing code style in the file. Use the same indentation and formatting patterns."
133
+ else:
134
+ answer = "Analyze the surrounding code and project structure to infer the missing information."
135
+
136
+ elif request.request_type == ClarificationType.MULTIPLE_OPTIONS:
137
+ # Choose the best option based on context
138
+ if request.options:
139
+ # Simple heuristic: choose the first option that seems most standard
140
+ for option in request.options:
141
+ if "common" in option or "standard" in option:
142
+ answer = f"Choose option: {option}"
143
+ break
144
+ else:
145
+ answer = f"Choose option: {request.options[0]}"
146
+ else:
147
+ answer = "Choose the most conventional approach based on the codebase patterns."
148
+
149
+ elif request.request_type == ClarificationType.CONFIRMATION_NEEDED:
150
+ # Auto-confirm safe operations
151
+ if "add import" in request.question.lower():
152
+ answer = "Yes, proceed with adding the import."
153
+ elif "multi_edit" in request.question.lower():
154
+ answer = "Yes, use multi_edit for efficiency."
155
+ else:
156
+ answer = "Proceed if the operation is safe and reversible."
157
+
158
+ else: # ADDITIONAL_INFO
159
+ answer = "Continue with available information and make reasonable assumptions based on context."
160
+
161
+ return ClarificationResponse(
162
+ request_id=request_id,
163
+ answer=answer,
164
+ additional_context={"auto_resolved": True}
165
+ )
166
+
167
+
168
+ class AgentClarificationMixin:
169
+ """Mixin for agents to request clarification."""
170
+
171
+ def __init__(self, *args, **kwargs):
172
+ super().__init__(*args, **kwargs)
173
+ self.clarification_handler = ClarificationHandler()
174
+ self.clarification_count = 0
175
+ self.max_clarifications = 1 # Allow up to 1 clarification per task
176
+
177
+ async def request_clarification(
178
+ self,
179
+ request_type: ClarificationType,
180
+ question: str,
181
+ context: Dict[str, Any],
182
+ options: Optional[List[str]] = None
183
+ ) -> str:
184
+ """Request clarification from the main loop.
185
+
186
+ Args:
187
+ request_type: Type of clarification needed
188
+ question: The question to ask
189
+ context: Relevant context for the question
190
+ options: Optional list of choices
191
+
192
+ Returns:
193
+ The clarification response
194
+
195
+ Raises:
196
+ RuntimeError: If clarification limit exceeded
197
+ """
198
+ if self.clarification_count >= self.max_clarifications:
199
+ raise RuntimeError("Clarification limit exceeded")
200
+
201
+ self.clarification_count += 1
202
+
203
+ # Create request
204
+ request = ClarificationRequest(
205
+ agent_id=getattr(self, 'agent_id', 'unknown'),
206
+ request_type=request_type,
207
+ question=question,
208
+ context=context,
209
+ options=options
210
+ )
211
+
212
+ # In real implementation, this would communicate with main loop
213
+ # For now, use the automatic handler
214
+ response = self.clarification_handler.handle_request(request)
215
+
216
+ return response.answer
217
+
218
+ def format_clarification_in_output(self, question: str, answer: str) -> str:
219
+ """Format clarification exchange for output."""
220
+ return f"\n🤔 Clarification needed: {question}\n✅ Resolved: {answer}\n"
@@ -0,0 +1,68 @@
1
+ """Clarification tool for agents to request information from main loop."""
2
+
3
+ from typing import Any, Dict, List, Optional, override
4
+
5
+ from hanzo_mcp.tools.common.base import BaseTool
6
+ from mcp.server.fastmcp import Context as MCPContext
7
+ from mcp.server import FastMCP
8
+
9
+
10
+ class ClarificationTool(BaseTool):
11
+ """Tool for agents to request clarification from the main loop."""
12
+
13
+ name = "request_clarification"
14
+
15
+ @property
16
+ @override
17
+ def description(self) -> str:
18
+ """Get the tool description."""
19
+ return """Request clarification from the main loop (not the human user).
20
+
21
+ Use this when you encounter:
22
+ - Ambiguous instructions that could be interpreted multiple ways
23
+ - Missing context needed to complete the task
24
+ - Multiple valid options where you need guidance
25
+ - Operations that need confirmation before proceeding
26
+ - Need for additional information not provided
27
+
28
+ Parameters:
29
+ - type: Type of clarification (AMBIGUOUS_INSTRUCTION, MISSING_CONTEXT, MULTIPLE_OPTIONS, CONFIRMATION_NEEDED, ADDITIONAL_INFO)
30
+ - question: Clear, specific question to ask
31
+ - context: Relevant context (e.g., file_path, current_operation, etc.)
32
+ - options: Optional list of possible choices (for MULTIPLE_OPTIONS type)
33
+
34
+ You can only use this ONCE per task, so make it count!
35
+
36
+ Example:
37
+ request_clarification(
38
+ type="MISSING_CONTEXT",
39
+ question="What is the correct import path for the common package?",
40
+ context={"file_path": "/path/to/file.go", "undefined_symbol": "common"},
41
+ options=["github.com/luxfi/node/common", "github.com/project/common"]
42
+ )"""
43
+
44
+ async def call(
45
+ self,
46
+ ctx: MCPContext,
47
+ type: str,
48
+ question: str,
49
+ context: Dict[str, Any],
50
+ options: Optional[List[str]] = None
51
+ ) -> str:
52
+ """This is a placeholder - actual implementation happens in AgentTool."""
53
+ # This tool is handled specially in the agent execution
54
+ return f"Clarification request: {question}"
55
+
56
+ def register(self, server: FastMCP) -> None:
57
+ """Register the tool with the MCP server."""
58
+ tool_self = self
59
+
60
+ @server.tool(name=self.name, description=self.description)
61
+ async def request_clarification(
62
+ ctx: MCPContext,
63
+ type: str,
64
+ question: str,
65
+ context: Dict[str, Any],
66
+ options: Optional[List[str]] = None
67
+ ) -> str:
68
+ return await tool_self.call(ctx, type, question, context, options)
@@ -0,0 +1,125 @@
1
+ """Claude Code CLI agent tool.
2
+
3
+ This tool provides integration with the Claude Code CLI (claude command),
4
+ allowing programmatic execution of Claude for code tasks.
5
+ """
6
+
7
+ from typing import List, Optional, override, final
8
+ from mcp.server import FastMCP
9
+ from mcp.server.fastmcp import Context as MCPContext
10
+
11
+ from hanzo_mcp.tools.agent.cli_agent_base import CLIAgentBase
12
+ from hanzo_mcp.tools.common.permissions import PermissionManager
13
+ from hanzo_mcp.tools.agent.code_auth import get_latest_claude_model
14
+
15
+
16
+ @final
17
+ class ClaudeCLITool(CLIAgentBase):
18
+ """Tool for executing Claude Code CLI."""
19
+
20
+ def __init__(
21
+ self,
22
+ permission_manager: PermissionManager,
23
+ model: Optional[str] = None,
24
+ **kwargs
25
+ ):
26
+ """Initialize Claude CLI tool.
27
+
28
+ Args:
29
+ permission_manager: Permission manager for access control
30
+ model: Optional model override (defaults to latest Sonnet)
31
+ **kwargs: Additional arguments
32
+ """
33
+ super().__init__(
34
+ permission_manager=permission_manager,
35
+ command_name="claude",
36
+ provider_name="Claude Code",
37
+ default_model=model or get_latest_claude_model(),
38
+ env_vars=["ANTHROPIC_API_KEY", "CLAUDE_API_KEY"],
39
+ **kwargs
40
+ )
41
+
42
+ @property
43
+ @override
44
+ def name(self) -> str:
45
+ """Get the tool name."""
46
+ return "claude_cli"
47
+
48
+ @property
49
+ @override
50
+ def description(self) -> str:
51
+ """Get the tool description."""
52
+ return """Execute Claude Code CLI for advanced code tasks.
53
+
54
+ This tool runs the Claude Code CLI (claude command) for code generation,
55
+ editing, analysis, and other programming tasks. It uses the latest
56
+ Claude 3.5 Sonnet model by default.
57
+
58
+ Features:
59
+ - Direct access to Claude's coding capabilities
60
+ - File-aware context and editing
61
+ - Interactive code generation
62
+ - Supports all Claude Code CLI features
63
+
64
+ Usage:
65
+ claude_cli(prompts="Fix the bug in main.py and add tests")
66
+ claude_cli(prompts="Refactor this class to use dependency injection", model="claude-3-opus-20240229")
67
+
68
+ Requirements:
69
+ - Claude Code CLI must be installed
70
+ - ANTHROPIC_API_KEY or CLAUDE_API_KEY environment variable
71
+ """
72
+
73
+ @override
74
+ def get_cli_args(self, prompt: str, **kwargs) -> List[str]:
75
+ """Get CLI arguments for Claude.
76
+
77
+ Args:
78
+ prompt: The prompt to send
79
+ **kwargs: Additional arguments (model, temperature, etc.)
80
+
81
+ Returns:
82
+ List of command arguments
83
+ """
84
+ args = []
85
+
86
+ # Add model if specified
87
+ model = kwargs.get("model", self.default_model)
88
+ if model:
89
+ args.extend(["--model", model])
90
+
91
+ # Add temperature if specified
92
+ if "temperature" in kwargs:
93
+ args.extend(["--temperature", str(kwargs["temperature"])])
94
+
95
+ # Add max tokens if specified
96
+ if "max_tokens" in kwargs:
97
+ args.extend(["--max-tokens", str(kwargs["max_tokens"])])
98
+
99
+ # Add the prompt
100
+ args.append(prompt)
101
+
102
+ return args
103
+
104
+ @override
105
+ def register(self, mcp_server: FastMCP) -> None:
106
+ """Register this tool with the MCP server."""
107
+ tool_self = self
108
+
109
+ @mcp_server.tool(name=self.name, description=self.description)
110
+ async def claude_cli(
111
+ ctx: MCPContext,
112
+ prompts: str,
113
+ model: Optional[str] = None,
114
+ temperature: Optional[float] = None,
115
+ max_tokens: Optional[int] = None,
116
+ working_dir: Optional[str] = None,
117
+ ) -> str:
118
+ return await tool_self.call(
119
+ ctx,
120
+ prompts=prompts,
121
+ model=model,
122
+ temperature=temperature,
123
+ max_tokens=max_tokens,
124
+ working_dir=working_dir,
125
+ )