hanzo-mcp 0.6.12__py3-none-any.whl → 0.7.0__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.
- hanzo_mcp/__init__.py +2 -2
- hanzo_mcp/analytics/__init__.py +5 -0
- hanzo_mcp/analytics/posthog_analytics.py +364 -0
- hanzo_mcp/cli.py +5 -5
- hanzo_mcp/cli_enhanced.py +7 -7
- hanzo_mcp/cli_plugin.py +91 -0
- hanzo_mcp/config/__init__.py +1 -1
- hanzo_mcp/config/settings.py +70 -7
- hanzo_mcp/config/tool_config.py +20 -6
- hanzo_mcp/dev_server.py +3 -3
- hanzo_mcp/prompts/project_system.py +1 -1
- hanzo_mcp/server.py +40 -3
- hanzo_mcp/server_enhanced.py +69 -0
- hanzo_mcp/tools/__init__.py +140 -31
- hanzo_mcp/tools/agent/__init__.py +85 -4
- hanzo_mcp/tools/agent/agent_tool.py +104 -6
- hanzo_mcp/tools/agent/agent_tool_v2.py +459 -0
- hanzo_mcp/tools/agent/clarification_protocol.py +220 -0
- hanzo_mcp/tools/agent/clarification_tool.py +68 -0
- hanzo_mcp/tools/agent/claude_cli_tool.py +125 -0
- hanzo_mcp/tools/agent/claude_desktop_auth.py +508 -0
- hanzo_mcp/tools/agent/cli_agent_base.py +191 -0
- hanzo_mcp/tools/agent/code_auth.py +436 -0
- hanzo_mcp/tools/agent/code_auth_tool.py +194 -0
- hanzo_mcp/tools/agent/codex_cli_tool.py +123 -0
- hanzo_mcp/tools/agent/critic_tool.py +376 -0
- hanzo_mcp/tools/agent/gemini_cli_tool.py +128 -0
- hanzo_mcp/tools/agent/grok_cli_tool.py +128 -0
- hanzo_mcp/tools/agent/iching_tool.py +380 -0
- hanzo_mcp/tools/agent/network_tool.py +273 -0
- hanzo_mcp/tools/agent/prompt.py +62 -20
- hanzo_mcp/tools/agent/review_tool.py +433 -0
- hanzo_mcp/tools/agent/swarm_tool.py +535 -0
- hanzo_mcp/tools/agent/swarm_tool_v2.py +594 -0
- hanzo_mcp/tools/common/__init__.py +15 -1
- hanzo_mcp/tools/common/base.py +5 -4
- hanzo_mcp/tools/common/batch_tool.py +103 -11
- hanzo_mcp/tools/common/config_tool.py +2 -2
- hanzo_mcp/tools/common/context.py +2 -2
- hanzo_mcp/tools/common/context_fix.py +26 -0
- hanzo_mcp/tools/common/critic_tool.py +196 -0
- hanzo_mcp/tools/common/decorators.py +208 -0
- hanzo_mcp/tools/common/enhanced_base.py +106 -0
- hanzo_mcp/tools/common/fastmcp_pagination.py +369 -0
- hanzo_mcp/tools/common/forgiving_edit.py +243 -0
- hanzo_mcp/tools/common/mode.py +116 -0
- hanzo_mcp/tools/common/mode_loader.py +105 -0
- hanzo_mcp/tools/common/paginated_base.py +230 -0
- hanzo_mcp/tools/common/paginated_response.py +307 -0
- hanzo_mcp/tools/common/pagination.py +226 -0
- hanzo_mcp/tools/common/permissions.py +1 -1
- hanzo_mcp/tools/common/personality.py +936 -0
- hanzo_mcp/tools/common/plugin_loader.py +287 -0
- hanzo_mcp/tools/common/stats.py +4 -4
- hanzo_mcp/tools/common/tool_list.py +4 -1
- hanzo_mcp/tools/common/truncate.py +101 -0
- hanzo_mcp/tools/common/validation.py +1 -1
- hanzo_mcp/tools/config/__init__.py +3 -1
- hanzo_mcp/tools/config/config_tool.py +1 -1
- hanzo_mcp/tools/config/mode_tool.py +209 -0
- hanzo_mcp/tools/database/__init__.py +1 -1
- hanzo_mcp/tools/editor/__init__.py +1 -1
- hanzo_mcp/tools/filesystem/__init__.py +48 -14
- hanzo_mcp/tools/filesystem/ast_multi_edit.py +562 -0
- hanzo_mcp/tools/filesystem/batch_search.py +3 -3
- hanzo_mcp/tools/filesystem/diff.py +2 -2
- hanzo_mcp/tools/filesystem/directory_tree_paginated.py +338 -0
- hanzo_mcp/tools/filesystem/rules_tool.py +235 -0
- hanzo_mcp/tools/filesystem/{unified_search.py → search_tool.py} +12 -12
- hanzo_mcp/tools/filesystem/{symbols_unified.py → symbols_tool.py} +104 -5
- hanzo_mcp/tools/filesystem/watch.py +3 -2
- hanzo_mcp/tools/jupyter/__init__.py +2 -2
- hanzo_mcp/tools/jupyter/jupyter.py +1 -1
- hanzo_mcp/tools/llm/__init__.py +3 -3
- hanzo_mcp/tools/llm/llm_tool.py +648 -143
- hanzo_mcp/tools/lsp/__init__.py +5 -0
- hanzo_mcp/tools/lsp/lsp_tool.py +512 -0
- hanzo_mcp/tools/mcp/__init__.py +2 -2
- hanzo_mcp/tools/mcp/{mcp_unified.py → mcp_tool.py} +3 -3
- hanzo_mcp/tools/memory/__init__.py +76 -0
- hanzo_mcp/tools/memory/knowledge_tools.py +518 -0
- hanzo_mcp/tools/memory/memory_tools.py +456 -0
- hanzo_mcp/tools/search/__init__.py +6 -0
- hanzo_mcp/tools/search/find_tool.py +581 -0
- hanzo_mcp/tools/search/unified_search.py +953 -0
- hanzo_mcp/tools/shell/__init__.py +11 -6
- hanzo_mcp/tools/shell/auto_background.py +203 -0
- hanzo_mcp/tools/shell/base_process.py +57 -29
- hanzo_mcp/tools/shell/bash_session_executor.py +1 -1
- hanzo_mcp/tools/shell/{bash_unified.py → bash_tool.py} +18 -34
- hanzo_mcp/tools/shell/command_executor.py +2 -2
- hanzo_mcp/tools/shell/{npx_unified.py → npx_tool.py} +16 -33
- hanzo_mcp/tools/shell/open.py +2 -2
- hanzo_mcp/tools/shell/{process_unified.py → process_tool.py} +1 -1
- hanzo_mcp/tools/shell/run_command_windows.py +1 -1
- hanzo_mcp/tools/shell/streaming_command.py +594 -0
- hanzo_mcp/tools/shell/uvx.py +47 -2
- hanzo_mcp/tools/shell/uvx_background.py +47 -2
- hanzo_mcp/tools/shell/{uvx_unified.py → uvx_tool.py} +16 -33
- hanzo_mcp/tools/todo/__init__.py +14 -19
- hanzo_mcp/tools/todo/todo.py +22 -1
- hanzo_mcp/tools/vector/__init__.py +1 -1
- hanzo_mcp/tools/vector/infinity_store.py +2 -2
- hanzo_mcp/tools/vector/project_manager.py +1 -1
- hanzo_mcp/types.py +23 -0
- hanzo_mcp-0.7.0.dist-info/METADATA +516 -0
- hanzo_mcp-0.7.0.dist-info/RECORD +180 -0
- {hanzo_mcp-0.6.12.dist-info → hanzo_mcp-0.7.0.dist-info}/entry_points.txt +1 -0
- hanzo_mcp/tools/common/palette.py +0 -344
- hanzo_mcp/tools/common/palette_loader.py +0 -108
- hanzo_mcp/tools/config/palette_tool.py +0 -179
- hanzo_mcp/tools/llm/llm_unified.py +0 -851
- hanzo_mcp-0.6.12.dist-info/METADATA +0 -339
- hanzo_mcp-0.6.12.dist-info/RECORD +0 -135
- hanzo_mcp-0.6.12.dist-info/licenses/LICENSE +0 -21
- {hanzo_mcp-0.6.12.dist-info → hanzo_mcp-0.7.0.dist-info}/WHEEL +0 -0
- {hanzo_mcp-0.6.12.dist-info → hanzo_mcp-0.7.0.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,512 @@
|
|
|
1
|
+
"""Language Server Protocol (LSP) tool for code intelligence.
|
|
2
|
+
|
|
3
|
+
This tool provides on-demand LSP configuration and installation for various
|
|
4
|
+
programming languages. It automatically installs language servers as needed
|
|
5
|
+
and provides code intelligence features like go-to-definition, find references,
|
|
6
|
+
rename symbol, and diagnostics.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import os
|
|
10
|
+
import subprocess
|
|
11
|
+
import json
|
|
12
|
+
import asyncio
|
|
13
|
+
import shutil
|
|
14
|
+
from typing import Dict, List, Any, Optional, Tuple
|
|
15
|
+
from pathlib import Path
|
|
16
|
+
from dataclasses import dataclass
|
|
17
|
+
import logging
|
|
18
|
+
|
|
19
|
+
from hanzo_mcp.tools.common.base import BaseTool
|
|
20
|
+
from hanzo_mcp.tools.common.decorators import with_context_normalization
|
|
21
|
+
from hanzo_mcp.types import MCPResourceDocument
|
|
22
|
+
|
|
23
|
+
# LSP server configurations
|
|
24
|
+
LSP_SERVERS = {
|
|
25
|
+
"go": {
|
|
26
|
+
"name": "gopls",
|
|
27
|
+
"install_cmd": ["go", "install", "golang.org/x/tools/gopls@latest"],
|
|
28
|
+
"check_cmd": ["gopls", "version"],
|
|
29
|
+
"start_cmd": ["gopls", "serve"],
|
|
30
|
+
"root_markers": ["go.mod", "go.sum"],
|
|
31
|
+
"file_extensions": [".go"],
|
|
32
|
+
"capabilities": ["definition", "references", "rename", "diagnostics", "hover", "completion"]
|
|
33
|
+
},
|
|
34
|
+
"python": {
|
|
35
|
+
"name": "pylsp",
|
|
36
|
+
"install_cmd": ["pip", "install", "python-lsp-server[all]"],
|
|
37
|
+
"check_cmd": ["pylsp", "--version"],
|
|
38
|
+
"start_cmd": ["pylsp"],
|
|
39
|
+
"root_markers": ["pyproject.toml", "setup.py", "requirements.txt"],
|
|
40
|
+
"file_extensions": [".py"],
|
|
41
|
+
"capabilities": ["definition", "references", "rename", "diagnostics", "hover", "completion"]
|
|
42
|
+
},
|
|
43
|
+
"typescript": {
|
|
44
|
+
"name": "typescript-language-server",
|
|
45
|
+
"install_cmd": ["npm", "install", "-g", "typescript", "typescript-language-server"],
|
|
46
|
+
"check_cmd": ["typescript-language-server", "--version"],
|
|
47
|
+
"start_cmd": ["typescript-language-server", "--stdio"],
|
|
48
|
+
"root_markers": ["tsconfig.json", "package.json"],
|
|
49
|
+
"file_extensions": [".ts", ".tsx", ".js", ".jsx"],
|
|
50
|
+
"capabilities": ["definition", "references", "rename", "diagnostics", "hover", "completion"]
|
|
51
|
+
},
|
|
52
|
+
"rust": {
|
|
53
|
+
"name": "rust-analyzer",
|
|
54
|
+
"install_cmd": ["rustup", "component", "add", "rust-analyzer"],
|
|
55
|
+
"check_cmd": ["rust-analyzer", "--version"],
|
|
56
|
+
"start_cmd": ["rust-analyzer"],
|
|
57
|
+
"root_markers": ["Cargo.toml"],
|
|
58
|
+
"file_extensions": [".rs"],
|
|
59
|
+
"capabilities": ["definition", "references", "rename", "diagnostics", "hover", "completion", "inlay_hints"]
|
|
60
|
+
},
|
|
61
|
+
"java": {
|
|
62
|
+
"name": "jdtls",
|
|
63
|
+
"install_cmd": ["brew", "install", "jdtls"], # Or manual download
|
|
64
|
+
"check_cmd": ["jdtls", "--version"],
|
|
65
|
+
"start_cmd": ["jdtls"],
|
|
66
|
+
"root_markers": ["pom.xml", "build.gradle", "build.gradle.kts"],
|
|
67
|
+
"file_extensions": [".java"],
|
|
68
|
+
"capabilities": ["definition", "references", "rename", "diagnostics", "hover", "completion"]
|
|
69
|
+
},
|
|
70
|
+
"cpp": {
|
|
71
|
+
"name": "clangd",
|
|
72
|
+
"install_cmd": ["brew", "install", "llvm"], # Or apt-get install clangd
|
|
73
|
+
"check_cmd": ["clangd", "--version"],
|
|
74
|
+
"start_cmd": ["clangd"],
|
|
75
|
+
"root_markers": ["compile_commands.json", "CMakeLists.txt"],
|
|
76
|
+
"file_extensions": [".cpp", ".cc", ".cxx", ".c", ".h", ".hpp"],
|
|
77
|
+
"capabilities": ["definition", "references", "rename", "diagnostics", "hover", "completion"]
|
|
78
|
+
},
|
|
79
|
+
"ruby": {
|
|
80
|
+
"name": "solargraph",
|
|
81
|
+
"install_cmd": ["gem", "install", "solargraph"],
|
|
82
|
+
"check_cmd": ["solargraph", "--version"],
|
|
83
|
+
"start_cmd": ["solargraph", "stdio"],
|
|
84
|
+
"root_markers": ["Gemfile", ".solargraph.yml"],
|
|
85
|
+
"file_extensions": [".rb"],
|
|
86
|
+
"capabilities": ["definition", "references", "diagnostics", "hover", "completion"]
|
|
87
|
+
},
|
|
88
|
+
"lua": {
|
|
89
|
+
"name": "lua-language-server",
|
|
90
|
+
"install_cmd": ["brew", "install", "lua-language-server"],
|
|
91
|
+
"check_cmd": ["lua-language-server", "--version"],
|
|
92
|
+
"start_cmd": ["lua-language-server"],
|
|
93
|
+
"root_markers": [".luarc.json"],
|
|
94
|
+
"file_extensions": [".lua"],
|
|
95
|
+
"capabilities": ["definition", "references", "rename", "diagnostics", "hover", "completion"]
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
@dataclass
|
|
101
|
+
class LSPServer:
|
|
102
|
+
"""Represents an LSP server instance."""
|
|
103
|
+
language: str
|
|
104
|
+
process: Optional[asyncio.subprocess.Process]
|
|
105
|
+
config: Dict[str, Any]
|
|
106
|
+
root_uri: str
|
|
107
|
+
initialized: bool = False
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
class LSPTool(BaseTool):
|
|
111
|
+
"""Language Server Protocol tool for code intelligence.
|
|
112
|
+
|
|
113
|
+
This tool automatically configures and manages LSP servers for various
|
|
114
|
+
programming languages. It installs language servers on-demand and provides
|
|
115
|
+
code intelligence features.
|
|
116
|
+
|
|
117
|
+
Features:
|
|
118
|
+
- Auto-installation of language servers
|
|
119
|
+
- Go-to-definition
|
|
120
|
+
- Find references
|
|
121
|
+
- Rename symbol
|
|
122
|
+
- Get diagnostics
|
|
123
|
+
- Hover information
|
|
124
|
+
- Code completion
|
|
125
|
+
|
|
126
|
+
Example usage:
|
|
127
|
+
|
|
128
|
+
1. Find definition of a Go function:
|
|
129
|
+
lsp("definition", file="main.go", line=10, character=15)
|
|
130
|
+
|
|
131
|
+
2. Find all references to a Python class:
|
|
132
|
+
lsp("references", file="models.py", line=25, character=10)
|
|
133
|
+
|
|
134
|
+
3. Rename a TypeScript variable:
|
|
135
|
+
lsp("rename", file="app.ts", line=30, character=20, new_name="newVarName")
|
|
136
|
+
|
|
137
|
+
4. Get diagnostics for a Rust file:
|
|
138
|
+
lsp("diagnostics", file="lib.rs")
|
|
139
|
+
|
|
140
|
+
The tool automatically detects the language based on file extension and
|
|
141
|
+
installs the appropriate language server if not already available.
|
|
142
|
+
"""
|
|
143
|
+
|
|
144
|
+
name = "lsp"
|
|
145
|
+
description = """Language Server Protocol tool for code intelligence.
|
|
146
|
+
|
|
147
|
+
Actions:
|
|
148
|
+
- definition: Go to definition of symbol at position
|
|
149
|
+
- references: Find all references to symbol
|
|
150
|
+
- rename: Rename symbol across codebase
|
|
151
|
+
- diagnostics: Get errors and warnings for file
|
|
152
|
+
- hover: Get hover information at position
|
|
153
|
+
- completion: Get code completions at position
|
|
154
|
+
- status: Check LSP server status
|
|
155
|
+
|
|
156
|
+
The tool automatically installs language servers as needed.
|
|
157
|
+
Supported languages: Go, Python, TypeScript/JavaScript, Rust, Java, C/C++, Ruby, Lua
|
|
158
|
+
"""
|
|
159
|
+
|
|
160
|
+
def __init__(self):
|
|
161
|
+
super().__init__()
|
|
162
|
+
self.servers: Dict[str, LSPServer] = {}
|
|
163
|
+
self.logger = logging.getLogger(__name__)
|
|
164
|
+
|
|
165
|
+
def _get_language_from_file(self, file_path: str) -> Optional[str]:
|
|
166
|
+
"""Detect language from file extension."""
|
|
167
|
+
ext = Path(file_path).suffix.lower()
|
|
168
|
+
|
|
169
|
+
for lang, config in LSP_SERVERS.items():
|
|
170
|
+
if ext in config["file_extensions"]:
|
|
171
|
+
return lang
|
|
172
|
+
|
|
173
|
+
return None
|
|
174
|
+
|
|
175
|
+
def _find_project_root(self, file_path: str, language: str) -> str:
|
|
176
|
+
"""Find project root based on language markers."""
|
|
177
|
+
markers = LSP_SERVERS[language]["root_markers"]
|
|
178
|
+
path = Path(file_path).resolve()
|
|
179
|
+
|
|
180
|
+
for parent in path.parents:
|
|
181
|
+
for marker in markers:
|
|
182
|
+
if (parent / marker).exists():
|
|
183
|
+
return str(parent)
|
|
184
|
+
|
|
185
|
+
return str(path.parent)
|
|
186
|
+
|
|
187
|
+
async def _check_lsp_installed(self, language: str) -> bool:
|
|
188
|
+
"""Check if LSP server is installed."""
|
|
189
|
+
config = LSP_SERVERS.get(language)
|
|
190
|
+
if not config:
|
|
191
|
+
return False
|
|
192
|
+
|
|
193
|
+
try:
|
|
194
|
+
result = await asyncio.create_subprocess_exec(
|
|
195
|
+
*config["check_cmd"],
|
|
196
|
+
stdout=asyncio.subprocess.PIPE,
|
|
197
|
+
stderr=asyncio.subprocess.PIPE
|
|
198
|
+
)
|
|
199
|
+
await result.communicate()
|
|
200
|
+
return result.returncode == 0
|
|
201
|
+
except (FileNotFoundError, OSError):
|
|
202
|
+
return False
|
|
203
|
+
|
|
204
|
+
async def _install_lsp(self, language: str) -> bool:
|
|
205
|
+
"""Install LSP server for language."""
|
|
206
|
+
config = LSP_SERVERS.get(language)
|
|
207
|
+
if not config:
|
|
208
|
+
return False
|
|
209
|
+
|
|
210
|
+
self.logger.info(f"Installing {config['name']} for {language}")
|
|
211
|
+
|
|
212
|
+
try:
|
|
213
|
+
# Check if installer is available
|
|
214
|
+
installer = config["install_cmd"][0]
|
|
215
|
+
if not shutil.which(installer):
|
|
216
|
+
self.logger.error(f"Installer {installer} not found")
|
|
217
|
+
return False
|
|
218
|
+
|
|
219
|
+
# Run installation command
|
|
220
|
+
result = await asyncio.create_subprocess_exec(
|
|
221
|
+
*config["install_cmd"],
|
|
222
|
+
stdout=asyncio.subprocess.PIPE,
|
|
223
|
+
stderr=asyncio.subprocess.PIPE
|
|
224
|
+
)
|
|
225
|
+
|
|
226
|
+
stdout, stderr = await result.communicate()
|
|
227
|
+
|
|
228
|
+
if result.returncode != 0:
|
|
229
|
+
self.logger.error(f"Installation failed: {stderr.decode()}")
|
|
230
|
+
return False
|
|
231
|
+
|
|
232
|
+
self.logger.info(f"Successfully installed {config['name']}")
|
|
233
|
+
return True
|
|
234
|
+
|
|
235
|
+
except Exception as e:
|
|
236
|
+
self.logger.error(f"Installation error: {e}")
|
|
237
|
+
return False
|
|
238
|
+
|
|
239
|
+
async def _ensure_lsp_running(self, language: str, root_uri: str) -> Optional[LSPServer]:
|
|
240
|
+
"""Ensure LSP server is running for language."""
|
|
241
|
+
# Check if already running
|
|
242
|
+
server_key = f"{language}:{root_uri}"
|
|
243
|
+
if server_key in self.servers:
|
|
244
|
+
server = self.servers[server_key]
|
|
245
|
+
if server.process and server.process.returncode is None:
|
|
246
|
+
return server
|
|
247
|
+
|
|
248
|
+
# Check if installed
|
|
249
|
+
if not await self._check_lsp_installed(language):
|
|
250
|
+
# Try to install
|
|
251
|
+
if not await self._install_lsp(language):
|
|
252
|
+
return None
|
|
253
|
+
|
|
254
|
+
# Start LSP server
|
|
255
|
+
config = LSP_SERVERS[language]
|
|
256
|
+
|
|
257
|
+
try:
|
|
258
|
+
process = await asyncio.create_subprocess_exec(
|
|
259
|
+
*config["start_cmd"],
|
|
260
|
+
stdin=asyncio.subprocess.PIPE,
|
|
261
|
+
stdout=asyncio.subprocess.PIPE,
|
|
262
|
+
stderr=asyncio.subprocess.PIPE,
|
|
263
|
+
cwd=root_uri
|
|
264
|
+
)
|
|
265
|
+
|
|
266
|
+
server = LSPServer(
|
|
267
|
+
language=language,
|
|
268
|
+
process=process,
|
|
269
|
+
config=config,
|
|
270
|
+
root_uri=root_uri
|
|
271
|
+
)
|
|
272
|
+
|
|
273
|
+
# Initialize LSP
|
|
274
|
+
await self._initialize_lsp(server)
|
|
275
|
+
|
|
276
|
+
self.servers[server_key] = server
|
|
277
|
+
return server
|
|
278
|
+
|
|
279
|
+
except Exception as e:
|
|
280
|
+
self.logger.error(f"Failed to start LSP: {e}")
|
|
281
|
+
return None
|
|
282
|
+
|
|
283
|
+
async def _initialize_lsp(self, server: LSPServer):
|
|
284
|
+
"""Send initialize request to LSP server."""
|
|
285
|
+
# This is a simplified initialization
|
|
286
|
+
# In a real implementation, you'd use the full LSP protocol
|
|
287
|
+
init_params = {
|
|
288
|
+
"processId": os.getpid(),
|
|
289
|
+
"rootUri": f"file://{server.root_uri}",
|
|
290
|
+
"capabilities": {
|
|
291
|
+
"textDocument": {
|
|
292
|
+
"definition": {"dynamicRegistration": True},
|
|
293
|
+
"references": {"dynamicRegistration": True},
|
|
294
|
+
"rename": {"dynamicRegistration": True}
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
# Send initialize request
|
|
300
|
+
request = {
|
|
301
|
+
"jsonrpc": "2.0",
|
|
302
|
+
"id": 1,
|
|
303
|
+
"method": "initialize",
|
|
304
|
+
"params": init_params
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
await self._send_request(server, request)
|
|
308
|
+
server.initialized = True
|
|
309
|
+
|
|
310
|
+
async def _send_request(self, server: LSPServer, request: Dict[str, Any]) -> Optional[Dict[str, Any]]:
|
|
311
|
+
"""Send JSON-RPC request to LSP server."""
|
|
312
|
+
if not server.process or server.process.returncode is not None:
|
|
313
|
+
return None
|
|
314
|
+
|
|
315
|
+
try:
|
|
316
|
+
# Serialize request
|
|
317
|
+
request_str = json.dumps(request)
|
|
318
|
+
content_length = len(request_str.encode('utf-8'))
|
|
319
|
+
|
|
320
|
+
# Send LSP message
|
|
321
|
+
message = f"Content-Length: {content_length}\r\n\r\n{request_str}"
|
|
322
|
+
server.process.stdin.write(message.encode('utf-8'))
|
|
323
|
+
await server.process.stdin.drain()
|
|
324
|
+
|
|
325
|
+
# Read response (simplified - real implementation needs proper parsing)
|
|
326
|
+
# This is a placeholder - actual LSP requires parsing Content-Length headers
|
|
327
|
+
response_data = await server.process.stdout.readline()
|
|
328
|
+
|
|
329
|
+
if response_data:
|
|
330
|
+
return json.loads(response_data.decode('utf-8'))
|
|
331
|
+
|
|
332
|
+
except Exception as e:
|
|
333
|
+
self.logger.error(f"LSP communication error: {e}")
|
|
334
|
+
|
|
335
|
+
return None
|
|
336
|
+
|
|
337
|
+
async def run(self,
|
|
338
|
+
action: str,
|
|
339
|
+
file: str,
|
|
340
|
+
line: Optional[int] = None,
|
|
341
|
+
character: Optional[int] = None,
|
|
342
|
+
new_name: Optional[str] = None,
|
|
343
|
+
**kwargs) -> MCPResourceDocument:
|
|
344
|
+
"""Execute LSP action.
|
|
345
|
+
|
|
346
|
+
Args:
|
|
347
|
+
action: LSP action (definition, references, rename, diagnostics, hover, completion, status)
|
|
348
|
+
file: File path to analyze
|
|
349
|
+
line: Line number (1-indexed)
|
|
350
|
+
character: Character position in line (0-indexed)
|
|
351
|
+
new_name: New name for rename action
|
|
352
|
+
"""
|
|
353
|
+
|
|
354
|
+
# Validate action
|
|
355
|
+
valid_actions = ["definition", "references", "rename", "diagnostics", "hover", "completion", "status"]
|
|
356
|
+
if action not in valid_actions:
|
|
357
|
+
return MCPResourceDocument(data={
|
|
358
|
+
"error": f"Invalid action. Must be one of: {', '.join(valid_actions)}"
|
|
359
|
+
})
|
|
360
|
+
|
|
361
|
+
# Get language from file
|
|
362
|
+
language = self._get_language_from_file(file)
|
|
363
|
+
if not language:
|
|
364
|
+
return MCPResourceDocument(data={
|
|
365
|
+
"error": f"Unsupported file type: {file}",
|
|
366
|
+
"supported_languages": list(LSP_SERVERS.keys())
|
|
367
|
+
})
|
|
368
|
+
|
|
369
|
+
# Check LSP capabilities
|
|
370
|
+
capabilities = LSP_SERVERS[language]["capabilities"]
|
|
371
|
+
if action not in capabilities and action != "status":
|
|
372
|
+
return MCPResourceDocument(data={
|
|
373
|
+
"error": f"Action '{action}' not supported for {language}",
|
|
374
|
+
"supported_actions": capabilities
|
|
375
|
+
})
|
|
376
|
+
|
|
377
|
+
# Status check
|
|
378
|
+
if action == "status":
|
|
379
|
+
installed = await self._check_lsp_installed(language)
|
|
380
|
+
return MCPResourceDocument(data={
|
|
381
|
+
"language": language,
|
|
382
|
+
"lsp_server": LSP_SERVERS[language]["name"],
|
|
383
|
+
"installed": installed,
|
|
384
|
+
"capabilities": capabilities
|
|
385
|
+
})
|
|
386
|
+
|
|
387
|
+
# Find project root
|
|
388
|
+
root_uri = self._find_project_root(file, language)
|
|
389
|
+
|
|
390
|
+
# Ensure LSP is running
|
|
391
|
+
server = await self._ensure_lsp_running(language, root_uri)
|
|
392
|
+
if not server:
|
|
393
|
+
return MCPResourceDocument(data={
|
|
394
|
+
"error": f"Failed to start LSP server for {language}",
|
|
395
|
+
"install_command": " ".join(LSP_SERVERS[language]["install_cmd"])
|
|
396
|
+
})
|
|
397
|
+
|
|
398
|
+
# Execute action
|
|
399
|
+
result = await self._execute_lsp_action(server, action, file, line, character, new_name)
|
|
400
|
+
|
|
401
|
+
return MCPResourceDocument(data=result)
|
|
402
|
+
|
|
403
|
+
async def call(self, **kwargs) -> str:
|
|
404
|
+
"""Tool interface for MCP - converts result to JSON string."""
|
|
405
|
+
result = await self.run(**kwargs)
|
|
406
|
+
return result.to_json_string()
|
|
407
|
+
|
|
408
|
+
def register(self, mcp_server) -> None:
|
|
409
|
+
"""Register tool with MCP server."""
|
|
410
|
+
from mcp.server import FastMCP
|
|
411
|
+
|
|
412
|
+
@mcp_server.tool(name=self.name, description=self.description)
|
|
413
|
+
async def lsp_handler(
|
|
414
|
+
action: str,
|
|
415
|
+
file: str,
|
|
416
|
+
line: Optional[int] = None,
|
|
417
|
+
character: Optional[int] = None,
|
|
418
|
+
new_name: Optional[str] = None,
|
|
419
|
+
) -> str:
|
|
420
|
+
"""Execute LSP action."""
|
|
421
|
+
return await self.call(
|
|
422
|
+
action=action,
|
|
423
|
+
file=file,
|
|
424
|
+
line=line,
|
|
425
|
+
character=character,
|
|
426
|
+
new_name=new_name,
|
|
427
|
+
)
|
|
428
|
+
|
|
429
|
+
async def _execute_lsp_action(self,
|
|
430
|
+
server: LSPServer,
|
|
431
|
+
action: str,
|
|
432
|
+
file: str,
|
|
433
|
+
line: Optional[int],
|
|
434
|
+
character: Optional[int],
|
|
435
|
+
new_name: Optional[str]) -> Dict[str, Any]:
|
|
436
|
+
"""Execute specific LSP action."""
|
|
437
|
+
|
|
438
|
+
# This is a simplified implementation
|
|
439
|
+
# Real implementation would use proper LSP protocol
|
|
440
|
+
|
|
441
|
+
if action == "definition":
|
|
442
|
+
# textDocument/definition request
|
|
443
|
+
return {
|
|
444
|
+
"action": "definition",
|
|
445
|
+
"file": file,
|
|
446
|
+
"position": {"line": line, "character": character},
|
|
447
|
+
"note": "LSP integration pending full implementation",
|
|
448
|
+
"fallback": "Use mcp__lsp__find_definition tool for now"
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
elif action == "references":
|
|
452
|
+
# textDocument/references request
|
|
453
|
+
return {
|
|
454
|
+
"action": "references",
|
|
455
|
+
"file": file,
|
|
456
|
+
"position": {"line": line, "character": character},
|
|
457
|
+
"note": "LSP integration pending full implementation",
|
|
458
|
+
"fallback": "Use mcp__lsp__find_references tool for now"
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
elif action == "rename":
|
|
462
|
+
# textDocument/rename request
|
|
463
|
+
return {
|
|
464
|
+
"action": "rename",
|
|
465
|
+
"file": file,
|
|
466
|
+
"position": {"line": line, "character": character},
|
|
467
|
+
"new_name": new_name,
|
|
468
|
+
"note": "LSP integration pending full implementation",
|
|
469
|
+
"fallback": "Use mcp__lsp__rename_symbol tool for now"
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
elif action == "diagnostics":
|
|
473
|
+
# textDocument/diagnostic request
|
|
474
|
+
return {
|
|
475
|
+
"action": "diagnostics",
|
|
476
|
+
"file": file,
|
|
477
|
+
"note": "LSP integration pending full implementation",
|
|
478
|
+
"fallback": "Use mcp__lsp__get_diagnostics tool for now"
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
elif action == "hover":
|
|
482
|
+
# textDocument/hover request
|
|
483
|
+
return {
|
|
484
|
+
"action": "hover",
|
|
485
|
+
"file": file,
|
|
486
|
+
"position": {"line": line, "character": character},
|
|
487
|
+
"note": "LSP integration pending full implementation"
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
elif action == "completion":
|
|
491
|
+
# textDocument/completion request
|
|
492
|
+
return {
|
|
493
|
+
"action": "completion",
|
|
494
|
+
"file": file,
|
|
495
|
+
"position": {"line": line, "character": character},
|
|
496
|
+
"note": "LSP integration pending full implementation"
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
return {"error": "Unknown action"}
|
|
500
|
+
|
|
501
|
+
async def cleanup(self):
|
|
502
|
+
"""Clean up LSP servers."""
|
|
503
|
+
for server in self.servers.values():
|
|
504
|
+
if server.process and server.process.returncode is None:
|
|
505
|
+
server.process.terminate()
|
|
506
|
+
await server.process.wait()
|
|
507
|
+
|
|
508
|
+
|
|
509
|
+
# Tool registration
|
|
510
|
+
def create_lsp_tool():
|
|
511
|
+
"""Factory function to create LSP tool."""
|
|
512
|
+
return LSPTool()
|
hanzo_mcp/tools/mcp/__init__.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"""MCP management tools."""
|
|
2
2
|
|
|
3
|
-
from hanzo_mcp.tools.mcp.
|
|
3
|
+
from hanzo_mcp.tools.mcp.mcp_tool import MCPTool
|
|
4
4
|
|
|
5
5
|
# Legacy imports
|
|
6
6
|
from hanzo_mcp.tools.mcp.mcp_add import McpAddTool
|
|
@@ -8,7 +8,7 @@ from hanzo_mcp.tools.mcp.mcp_remove import McpRemoveTool
|
|
|
8
8
|
from hanzo_mcp.tools.mcp.mcp_stats import McpStatsTool
|
|
9
9
|
|
|
10
10
|
__all__ = [
|
|
11
|
-
"
|
|
11
|
+
"MCPTool",
|
|
12
12
|
"McpAddTool",
|
|
13
13
|
"McpRemoveTool",
|
|
14
14
|
"McpStatsTool",
|
|
@@ -74,7 +74,7 @@ ConfigValue = Annotated[
|
|
|
74
74
|
AutoStart = Annotated[
|
|
75
75
|
bool,
|
|
76
76
|
Field(
|
|
77
|
-
description="Auto-start server when Hanzo
|
|
77
|
+
description="Auto-start server when Hanzo AI starts",
|
|
78
78
|
default=True,
|
|
79
79
|
),
|
|
80
80
|
]
|
|
@@ -93,8 +93,8 @@ class MCPParams(TypedDict, total=False):
|
|
|
93
93
|
|
|
94
94
|
|
|
95
95
|
@final
|
|
96
|
-
class
|
|
97
|
-
"""
|
|
96
|
+
class MCPTool(BaseTool):
|
|
97
|
+
"""Tool for managing MCP servers."""
|
|
98
98
|
|
|
99
99
|
# Config file
|
|
100
100
|
CONFIG_FILE = Path.home() / ".hanzo" / "mcp" / "servers.json"
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
"""Memory tools for MCP agents."""
|
|
2
|
+
|
|
3
|
+
from mcp.server import FastMCP
|
|
4
|
+
|
|
5
|
+
from hanzo_mcp.tools.memory.memory_tools import (
|
|
6
|
+
RecallMemoriesTool,
|
|
7
|
+
CreateMemoriesTool,
|
|
8
|
+
UpdateMemoriesTool,
|
|
9
|
+
DeleteMemoriesTool,
|
|
10
|
+
ManageMemoriesTool,
|
|
11
|
+
)
|
|
12
|
+
from hanzo_mcp.tools.memory.knowledge_tools import (
|
|
13
|
+
RecallFactsTool,
|
|
14
|
+
StoreFactsTool,
|
|
15
|
+
SummarizeToMemoryTool,
|
|
16
|
+
ManageKnowledgeBasesTool,
|
|
17
|
+
)
|
|
18
|
+
from hanzo_mcp.tools.common.base import BaseTool, ToolRegistry
|
|
19
|
+
from hanzo_mcp.tools.common.permissions import PermissionManager
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def register_memory_tools(
|
|
23
|
+
mcp_server: FastMCP,
|
|
24
|
+
permission_manager: PermissionManager,
|
|
25
|
+
user_id: str = "default",
|
|
26
|
+
project_id: str = "default",
|
|
27
|
+
**memory_config
|
|
28
|
+
) -> list[BaseTool]:
|
|
29
|
+
"""Register memory tools with the MCP server.
|
|
30
|
+
|
|
31
|
+
Args:
|
|
32
|
+
mcp_server: The FastMCP server instance
|
|
33
|
+
permission_manager: Permission manager for access control
|
|
34
|
+
user_id: User ID for memory operations
|
|
35
|
+
project_id: Project ID for memory operations
|
|
36
|
+
**memory_config: Additional memory store configuration
|
|
37
|
+
|
|
38
|
+
Returns:
|
|
39
|
+
List of registered tools
|
|
40
|
+
"""
|
|
41
|
+
# Create memory tools
|
|
42
|
+
recall_tool = RecallMemoriesTool(user_id=user_id, project_id=project_id, **memory_config)
|
|
43
|
+
create_tool = CreateMemoriesTool(user_id=user_id, project_id=project_id, **memory_config)
|
|
44
|
+
update_tool = UpdateMemoriesTool(user_id=user_id, project_id=project_id, **memory_config)
|
|
45
|
+
delete_tool = DeleteMemoriesTool(user_id=user_id, project_id=project_id, **memory_config)
|
|
46
|
+
manage_tool = ManageMemoriesTool(user_id=user_id, project_id=project_id, **memory_config)
|
|
47
|
+
|
|
48
|
+
# Create knowledge tools
|
|
49
|
+
recall_facts_tool = RecallFactsTool(user_id=user_id, project_id=project_id, **memory_config)
|
|
50
|
+
store_facts_tool = StoreFactsTool(user_id=user_id, project_id=project_id, **memory_config)
|
|
51
|
+
summarize_tool = SummarizeToMemoryTool(user_id=user_id, project_id=project_id, **memory_config)
|
|
52
|
+
manage_kb_tool = ManageKnowledgeBasesTool(user_id=user_id, project_id=project_id, **memory_config)
|
|
53
|
+
|
|
54
|
+
# Register tools
|
|
55
|
+
ToolRegistry.register_tool(mcp_server, recall_tool)
|
|
56
|
+
ToolRegistry.register_tool(mcp_server, create_tool)
|
|
57
|
+
ToolRegistry.register_tool(mcp_server, update_tool)
|
|
58
|
+
ToolRegistry.register_tool(mcp_server, delete_tool)
|
|
59
|
+
ToolRegistry.register_tool(mcp_server, manage_tool)
|
|
60
|
+
ToolRegistry.register_tool(mcp_server, recall_facts_tool)
|
|
61
|
+
ToolRegistry.register_tool(mcp_server, store_facts_tool)
|
|
62
|
+
ToolRegistry.register_tool(mcp_server, summarize_tool)
|
|
63
|
+
ToolRegistry.register_tool(mcp_server, manage_kb_tool)
|
|
64
|
+
|
|
65
|
+
# Return list of registered tools
|
|
66
|
+
return [
|
|
67
|
+
recall_tool,
|
|
68
|
+
create_tool,
|
|
69
|
+
update_tool,
|
|
70
|
+
delete_tool,
|
|
71
|
+
manage_tool,
|
|
72
|
+
recall_facts_tool,
|
|
73
|
+
store_facts_tool,
|
|
74
|
+
summarize_tool,
|
|
75
|
+
manage_kb_tool,
|
|
76
|
+
]
|