hanzo-mcp 0.7.6__py3-none-any.whl → 0.8.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 +7 -1
- hanzo_mcp/__main__.py +1 -1
- hanzo_mcp/analytics/__init__.py +2 -2
- hanzo_mcp/analytics/posthog_analytics.py +76 -82
- hanzo_mcp/cli.py +31 -36
- hanzo_mcp/cli_enhanced.py +94 -72
- hanzo_mcp/cli_plugin.py +27 -17
- hanzo_mcp/config/__init__.py +2 -2
- hanzo_mcp/config/settings.py +112 -88
- hanzo_mcp/config/tool_config.py +32 -34
- hanzo_mcp/dev_server.py +66 -67
- hanzo_mcp/prompts/__init__.py +94 -12
- hanzo_mcp/prompts/enhanced_prompts.py +809 -0
- hanzo_mcp/prompts/example_custom_prompt.py +6 -5
- hanzo_mcp/prompts/project_todo_reminder.py +0 -1
- hanzo_mcp/prompts/tool_explorer.py +10 -7
- hanzo_mcp/server.py +17 -21
- hanzo_mcp/server_enhanced.py +15 -22
- hanzo_mcp/tools/__init__.py +56 -28
- hanzo_mcp/tools/agent/__init__.py +16 -19
- hanzo_mcp/tools/agent/agent.py +82 -65
- hanzo_mcp/tools/agent/agent_tool.py +152 -122
- hanzo_mcp/tools/agent/agent_tool_v1_deprecated.py +66 -62
- hanzo_mcp/tools/agent/clarification_protocol.py +55 -50
- hanzo_mcp/tools/agent/clarification_tool.py +11 -10
- hanzo_mcp/tools/agent/claude_cli_tool.py +21 -20
- hanzo_mcp/tools/agent/claude_desktop_auth.py +130 -144
- hanzo_mcp/tools/agent/cli_agent_base.py +59 -53
- hanzo_mcp/tools/agent/code_auth.py +102 -107
- hanzo_mcp/tools/agent/code_auth_tool.py +28 -27
- hanzo_mcp/tools/agent/codex_cli_tool.py +20 -19
- hanzo_mcp/tools/agent/critic_tool.py +86 -73
- hanzo_mcp/tools/agent/gemini_cli_tool.py +21 -20
- hanzo_mcp/tools/agent/grok_cli_tool.py +21 -20
- hanzo_mcp/tools/agent/iching_tool.py +404 -139
- hanzo_mcp/tools/agent/network_tool.py +89 -73
- hanzo_mcp/tools/agent/prompt.py +2 -1
- hanzo_mcp/tools/agent/review_tool.py +101 -98
- hanzo_mcp/tools/agent/swarm_alias.py +87 -0
- hanzo_mcp/tools/agent/swarm_tool.py +246 -161
- hanzo_mcp/tools/agent/swarm_tool_v1_deprecated.py +134 -92
- hanzo_mcp/tools/agent/tool_adapter.py +21 -11
- hanzo_mcp/tools/common/__init__.py +1 -1
- hanzo_mcp/tools/common/base.py +3 -5
- hanzo_mcp/tools/common/batch_tool.py +46 -39
- hanzo_mcp/tools/common/config_tool.py +120 -84
- hanzo_mcp/tools/common/context.py +1 -5
- hanzo_mcp/tools/common/context_fix.py +5 -3
- hanzo_mcp/tools/common/critic_tool.py +4 -8
- hanzo_mcp/tools/common/decorators.py +58 -56
- hanzo_mcp/tools/common/enhanced_base.py +29 -32
- hanzo_mcp/tools/common/fastmcp_pagination.py +91 -94
- hanzo_mcp/tools/common/forgiving_edit.py +91 -87
- hanzo_mcp/tools/common/mode.py +15 -17
- hanzo_mcp/tools/common/mode_loader.py +27 -24
- hanzo_mcp/tools/common/paginated_base.py +61 -53
- hanzo_mcp/tools/common/paginated_response.py +72 -79
- hanzo_mcp/tools/common/pagination.py +50 -53
- hanzo_mcp/tools/common/permissions.py +4 -4
- hanzo_mcp/tools/common/personality.py +186 -138
- hanzo_mcp/tools/common/plugin_loader.py +54 -54
- hanzo_mcp/tools/common/stats.py +65 -47
- hanzo_mcp/tools/common/test_helpers.py +31 -0
- hanzo_mcp/tools/common/thinking_tool.py +4 -8
- hanzo_mcp/tools/common/tool_disable.py +17 -12
- hanzo_mcp/tools/common/tool_enable.py +13 -14
- hanzo_mcp/tools/common/tool_list.py +36 -28
- hanzo_mcp/tools/common/truncate.py +23 -23
- hanzo_mcp/tools/config/__init__.py +4 -4
- hanzo_mcp/tools/config/config_tool.py +42 -29
- hanzo_mcp/tools/config/index_config.py +37 -34
- hanzo_mcp/tools/config/mode_tool.py +175 -55
- hanzo_mcp/tools/database/__init__.py +15 -12
- hanzo_mcp/tools/database/database_manager.py +77 -75
- hanzo_mcp/tools/database/graph.py +137 -91
- hanzo_mcp/tools/database/graph_add.py +30 -18
- hanzo_mcp/tools/database/graph_query.py +178 -102
- hanzo_mcp/tools/database/graph_remove.py +33 -28
- hanzo_mcp/tools/database/graph_search.py +97 -75
- hanzo_mcp/tools/database/graph_stats.py +91 -59
- hanzo_mcp/tools/database/sql.py +107 -79
- hanzo_mcp/tools/database/sql_query.py +30 -24
- hanzo_mcp/tools/database/sql_search.py +29 -25
- hanzo_mcp/tools/database/sql_stats.py +47 -35
- hanzo_mcp/tools/editor/neovim_command.py +25 -28
- hanzo_mcp/tools/editor/neovim_edit.py +21 -23
- hanzo_mcp/tools/editor/neovim_session.py +60 -54
- hanzo_mcp/tools/filesystem/__init__.py +31 -30
- hanzo_mcp/tools/filesystem/ast_multi_edit.py +329 -249
- hanzo_mcp/tools/filesystem/ast_tool.py +4 -4
- hanzo_mcp/tools/filesystem/base.py +1 -1
- hanzo_mcp/tools/filesystem/batch_search.py +316 -224
- hanzo_mcp/tools/filesystem/content_replace.py +4 -4
- hanzo_mcp/tools/filesystem/diff.py +71 -59
- hanzo_mcp/tools/filesystem/directory_tree.py +7 -7
- hanzo_mcp/tools/filesystem/directory_tree_paginated.py +49 -37
- hanzo_mcp/tools/filesystem/edit.py +4 -4
- hanzo_mcp/tools/filesystem/find.py +173 -80
- hanzo_mcp/tools/filesystem/find_files.py +73 -52
- hanzo_mcp/tools/filesystem/git_search.py +157 -104
- hanzo_mcp/tools/filesystem/grep.py +8 -8
- hanzo_mcp/tools/filesystem/multi_edit.py +4 -8
- hanzo_mcp/tools/filesystem/read.py +12 -10
- hanzo_mcp/tools/filesystem/rules_tool.py +59 -43
- hanzo_mcp/tools/filesystem/search_tool.py +263 -207
- hanzo_mcp/tools/filesystem/symbols_tool.py +94 -54
- hanzo_mcp/tools/filesystem/tree.py +35 -33
- hanzo_mcp/tools/filesystem/unix_aliases.py +13 -18
- hanzo_mcp/tools/filesystem/watch.py +37 -36
- hanzo_mcp/tools/filesystem/write.py +4 -8
- hanzo_mcp/tools/jupyter/__init__.py +4 -4
- hanzo_mcp/tools/jupyter/base.py +4 -5
- hanzo_mcp/tools/jupyter/jupyter.py +67 -47
- hanzo_mcp/tools/jupyter/notebook_edit.py +4 -4
- hanzo_mcp/tools/jupyter/notebook_read.py +4 -7
- hanzo_mcp/tools/llm/__init__.py +5 -7
- hanzo_mcp/tools/llm/consensus_tool.py +72 -52
- hanzo_mcp/tools/llm/llm_manage.py +101 -60
- hanzo_mcp/tools/llm/llm_tool.py +226 -166
- hanzo_mcp/tools/llm/provider_tools.py +25 -26
- hanzo_mcp/tools/lsp/__init__.py +1 -1
- hanzo_mcp/tools/lsp/lsp_tool.py +228 -143
- hanzo_mcp/tools/mcp/__init__.py +2 -3
- hanzo_mcp/tools/mcp/mcp_add.py +27 -25
- hanzo_mcp/tools/mcp/mcp_remove.py +7 -8
- hanzo_mcp/tools/mcp/mcp_stats.py +23 -22
- hanzo_mcp/tools/mcp/mcp_tool.py +129 -98
- hanzo_mcp/tools/memory/__init__.py +39 -21
- hanzo_mcp/tools/memory/knowledge_tools.py +124 -99
- hanzo_mcp/tools/memory/memory_tools.py +90 -108
- hanzo_mcp/tools/search/__init__.py +7 -2
- hanzo_mcp/tools/search/find_tool.py +297 -212
- hanzo_mcp/tools/search/unified_search.py +366 -314
- hanzo_mcp/tools/shell/__init__.py +8 -7
- hanzo_mcp/tools/shell/auto_background.py +56 -49
- hanzo_mcp/tools/shell/base.py +1 -1
- hanzo_mcp/tools/shell/base_process.py +75 -75
- hanzo_mcp/tools/shell/bash_session.py +2 -2
- hanzo_mcp/tools/shell/bash_session_executor.py +4 -4
- hanzo_mcp/tools/shell/bash_tool.py +24 -31
- hanzo_mcp/tools/shell/command_executor.py +12 -12
- hanzo_mcp/tools/shell/logs.py +43 -33
- hanzo_mcp/tools/shell/npx.py +13 -13
- hanzo_mcp/tools/shell/npx_background.py +24 -21
- hanzo_mcp/tools/shell/npx_tool.py +18 -22
- hanzo_mcp/tools/shell/open.py +19 -21
- hanzo_mcp/tools/shell/pkill.py +31 -26
- hanzo_mcp/tools/shell/process_tool.py +32 -32
- hanzo_mcp/tools/shell/processes.py +57 -58
- hanzo_mcp/tools/shell/run_background.py +24 -25
- hanzo_mcp/tools/shell/run_command.py +5 -5
- hanzo_mcp/tools/shell/run_command_windows.py +5 -5
- hanzo_mcp/tools/shell/session_storage.py +3 -3
- hanzo_mcp/tools/shell/streaming_command.py +141 -126
- hanzo_mcp/tools/shell/uvx.py +24 -25
- hanzo_mcp/tools/shell/uvx_background.py +35 -33
- hanzo_mcp/tools/shell/uvx_tool.py +18 -22
- hanzo_mcp/tools/todo/__init__.py +6 -2
- hanzo_mcp/tools/todo/todo.py +50 -37
- hanzo_mcp/tools/todo/todo_read.py +5 -8
- hanzo_mcp/tools/todo/todo_write.py +5 -7
- hanzo_mcp/tools/vector/__init__.py +40 -28
- hanzo_mcp/tools/vector/ast_analyzer.py +176 -143
- hanzo_mcp/tools/vector/git_ingester.py +170 -179
- hanzo_mcp/tools/vector/index_tool.py +96 -44
- hanzo_mcp/tools/vector/infinity_store.py +283 -228
- hanzo_mcp/tools/vector/mock_infinity.py +39 -40
- hanzo_mcp/tools/vector/project_manager.py +88 -78
- hanzo_mcp/tools/vector/vector.py +59 -42
- hanzo_mcp/tools/vector/vector_index.py +30 -27
- hanzo_mcp/tools/vector/vector_search.py +64 -45
- hanzo_mcp/types.py +6 -4
- {hanzo_mcp-0.7.6.dist-info → hanzo_mcp-0.8.0.dist-info}/METADATA +1 -1
- hanzo_mcp-0.8.0.dist-info/RECORD +185 -0
- hanzo_mcp-0.7.6.dist-info/RECORD +0 -182
- {hanzo_mcp-0.7.6.dist-info → hanzo_mcp-0.8.0.dist-info}/WHEEL +0 -0
- {hanzo_mcp-0.7.6.dist-info → hanzo_mcp-0.8.0.dist-info}/entry_points.txt +0 -0
- {hanzo_mcp-0.7.6.dist-info → hanzo_mcp-0.8.0.dist-info}/top_level.txt +0 -0
hanzo_mcp/tools/lsp/lsp_tool.py
CHANGED
|
@@ -7,18 +7,16 @@ rename symbol, and diagnostics.
|
|
|
7
7
|
"""
|
|
8
8
|
|
|
9
9
|
import os
|
|
10
|
-
import subprocess
|
|
11
10
|
import json
|
|
12
|
-
import asyncio
|
|
13
11
|
import shutil
|
|
14
|
-
|
|
12
|
+
import asyncio
|
|
13
|
+
import logging
|
|
14
|
+
from typing import Any, Dict, Optional
|
|
15
15
|
from pathlib import Path
|
|
16
16
|
from dataclasses import dataclass
|
|
17
|
-
import logging
|
|
18
17
|
|
|
19
|
-
from hanzo_mcp.tools.common.base import BaseTool
|
|
20
|
-
from hanzo_mcp.tools.common.decorators import with_context_normalization
|
|
21
18
|
from hanzo_mcp.types import MCPResourceDocument
|
|
19
|
+
from hanzo_mcp.tools.common.base import BaseTool
|
|
22
20
|
|
|
23
21
|
# LSP server configurations
|
|
24
22
|
LSP_SERVERS = {
|
|
@@ -29,7 +27,14 @@ LSP_SERVERS = {
|
|
|
29
27
|
"start_cmd": ["gopls", "serve"],
|
|
30
28
|
"root_markers": ["go.mod", "go.sum"],
|
|
31
29
|
"file_extensions": [".go"],
|
|
32
|
-
"capabilities": [
|
|
30
|
+
"capabilities": [
|
|
31
|
+
"definition",
|
|
32
|
+
"references",
|
|
33
|
+
"rename",
|
|
34
|
+
"diagnostics",
|
|
35
|
+
"hover",
|
|
36
|
+
"completion",
|
|
37
|
+
],
|
|
33
38
|
},
|
|
34
39
|
"python": {
|
|
35
40
|
"name": "pylsp",
|
|
@@ -38,16 +43,36 @@ LSP_SERVERS = {
|
|
|
38
43
|
"start_cmd": ["pylsp"],
|
|
39
44
|
"root_markers": ["pyproject.toml", "setup.py", "requirements.txt"],
|
|
40
45
|
"file_extensions": [".py"],
|
|
41
|
-
"capabilities": [
|
|
46
|
+
"capabilities": [
|
|
47
|
+
"definition",
|
|
48
|
+
"references",
|
|
49
|
+
"rename",
|
|
50
|
+
"diagnostics",
|
|
51
|
+
"hover",
|
|
52
|
+
"completion",
|
|
53
|
+
],
|
|
42
54
|
},
|
|
43
55
|
"typescript": {
|
|
44
56
|
"name": "typescript-language-server",
|
|
45
|
-
"install_cmd": [
|
|
57
|
+
"install_cmd": [
|
|
58
|
+
"npm",
|
|
59
|
+
"install",
|
|
60
|
+
"-g",
|
|
61
|
+
"typescript",
|
|
62
|
+
"typescript-language-server",
|
|
63
|
+
],
|
|
46
64
|
"check_cmd": ["typescript-language-server", "--version"],
|
|
47
65
|
"start_cmd": ["typescript-language-server", "--stdio"],
|
|
48
66
|
"root_markers": ["tsconfig.json", "package.json"],
|
|
49
67
|
"file_extensions": [".ts", ".tsx", ".js", ".jsx"],
|
|
50
|
-
"capabilities": [
|
|
68
|
+
"capabilities": [
|
|
69
|
+
"definition",
|
|
70
|
+
"references",
|
|
71
|
+
"rename",
|
|
72
|
+
"diagnostics",
|
|
73
|
+
"hover",
|
|
74
|
+
"completion",
|
|
75
|
+
],
|
|
51
76
|
},
|
|
52
77
|
"rust": {
|
|
53
78
|
"name": "rust-analyzer",
|
|
@@ -56,7 +81,15 @@ LSP_SERVERS = {
|
|
|
56
81
|
"start_cmd": ["rust-analyzer"],
|
|
57
82
|
"root_markers": ["Cargo.toml"],
|
|
58
83
|
"file_extensions": [".rs"],
|
|
59
|
-
"capabilities": [
|
|
84
|
+
"capabilities": [
|
|
85
|
+
"definition",
|
|
86
|
+
"references",
|
|
87
|
+
"rename",
|
|
88
|
+
"diagnostics",
|
|
89
|
+
"hover",
|
|
90
|
+
"completion",
|
|
91
|
+
"inlay_hints",
|
|
92
|
+
],
|
|
60
93
|
},
|
|
61
94
|
"java": {
|
|
62
95
|
"name": "jdtls",
|
|
@@ -65,7 +98,14 @@ LSP_SERVERS = {
|
|
|
65
98
|
"start_cmd": ["jdtls"],
|
|
66
99
|
"root_markers": ["pom.xml", "build.gradle", "build.gradle.kts"],
|
|
67
100
|
"file_extensions": [".java"],
|
|
68
|
-
"capabilities": [
|
|
101
|
+
"capabilities": [
|
|
102
|
+
"definition",
|
|
103
|
+
"references",
|
|
104
|
+
"rename",
|
|
105
|
+
"diagnostics",
|
|
106
|
+
"hover",
|
|
107
|
+
"completion",
|
|
108
|
+
],
|
|
69
109
|
},
|
|
70
110
|
"cpp": {
|
|
71
111
|
"name": "clangd",
|
|
@@ -74,7 +114,14 @@ LSP_SERVERS = {
|
|
|
74
114
|
"start_cmd": ["clangd"],
|
|
75
115
|
"root_markers": ["compile_commands.json", "CMakeLists.txt"],
|
|
76
116
|
"file_extensions": [".cpp", ".cc", ".cxx", ".c", ".h", ".hpp"],
|
|
77
|
-
"capabilities": [
|
|
117
|
+
"capabilities": [
|
|
118
|
+
"definition",
|
|
119
|
+
"references",
|
|
120
|
+
"rename",
|
|
121
|
+
"diagnostics",
|
|
122
|
+
"hover",
|
|
123
|
+
"completion",
|
|
124
|
+
],
|
|
78
125
|
},
|
|
79
126
|
"ruby": {
|
|
80
127
|
"name": "solargraph",
|
|
@@ -83,7 +130,13 @@ LSP_SERVERS = {
|
|
|
83
130
|
"start_cmd": ["solargraph", "stdio"],
|
|
84
131
|
"root_markers": ["Gemfile", ".solargraph.yml"],
|
|
85
132
|
"file_extensions": [".rb"],
|
|
86
|
-
"capabilities": [
|
|
133
|
+
"capabilities": [
|
|
134
|
+
"definition",
|
|
135
|
+
"references",
|
|
136
|
+
"diagnostics",
|
|
137
|
+
"hover",
|
|
138
|
+
"completion",
|
|
139
|
+
],
|
|
87
140
|
},
|
|
88
141
|
"lua": {
|
|
89
142
|
"name": "lua-language-server",
|
|
@@ -92,14 +145,22 @@ LSP_SERVERS = {
|
|
|
92
145
|
"start_cmd": ["lua-language-server"],
|
|
93
146
|
"root_markers": [".luarc.json"],
|
|
94
147
|
"file_extensions": [".lua"],
|
|
95
|
-
"capabilities": [
|
|
96
|
-
|
|
148
|
+
"capabilities": [
|
|
149
|
+
"definition",
|
|
150
|
+
"references",
|
|
151
|
+
"rename",
|
|
152
|
+
"diagnostics",
|
|
153
|
+
"hover",
|
|
154
|
+
"completion",
|
|
155
|
+
],
|
|
156
|
+
},
|
|
97
157
|
}
|
|
98
158
|
|
|
99
159
|
|
|
100
160
|
@dataclass
|
|
101
161
|
class LSPServer:
|
|
102
162
|
"""Represents an LSP server instance."""
|
|
163
|
+
|
|
103
164
|
language: str
|
|
104
165
|
process: Optional[asyncio.subprocess.Process]
|
|
105
166
|
config: Dict[str, Any]
|
|
@@ -109,11 +170,11 @@ class LSPServer:
|
|
|
109
170
|
|
|
110
171
|
class LSPTool(BaseTool):
|
|
111
172
|
"""Language Server Protocol tool for code intelligence.
|
|
112
|
-
|
|
173
|
+
|
|
113
174
|
This tool automatically configures and manages LSP servers for various
|
|
114
175
|
programming languages. It installs language servers on-demand and provides
|
|
115
176
|
code intelligence features.
|
|
116
|
-
|
|
177
|
+
|
|
117
178
|
Features:
|
|
118
179
|
- Auto-installation of language servers
|
|
119
180
|
- Go-to-definition
|
|
@@ -122,25 +183,25 @@ class LSPTool(BaseTool):
|
|
|
122
183
|
- Get diagnostics
|
|
123
184
|
- Hover information
|
|
124
185
|
- Code completion
|
|
125
|
-
|
|
186
|
+
|
|
126
187
|
Example usage:
|
|
127
|
-
|
|
188
|
+
|
|
128
189
|
1. Find definition of a Go function:
|
|
129
190
|
lsp("definition", file="main.go", line=10, character=15)
|
|
130
|
-
|
|
191
|
+
|
|
131
192
|
2. Find all references to a Python class:
|
|
132
193
|
lsp("references", file="models.py", line=25, character=10)
|
|
133
|
-
|
|
194
|
+
|
|
134
195
|
3. Rename a TypeScript variable:
|
|
135
196
|
lsp("rename", file="app.ts", line=30, character=20, new_name="newVarName")
|
|
136
|
-
|
|
197
|
+
|
|
137
198
|
4. Get diagnostics for a Rust file:
|
|
138
199
|
lsp("diagnostics", file="lib.rs")
|
|
139
|
-
|
|
200
|
+
|
|
140
201
|
The tool automatically detects the language based on file extension and
|
|
141
202
|
installs the appropriate language server if not already available.
|
|
142
203
|
"""
|
|
143
|
-
|
|
204
|
+
|
|
144
205
|
name = "lsp"
|
|
145
206
|
description = """Language Server Protocol tool for code intelligence.
|
|
146
207
|
|
|
@@ -156,87 +217,89 @@ class LSPTool(BaseTool):
|
|
|
156
217
|
The tool automatically installs language servers as needed.
|
|
157
218
|
Supported languages: Go, Python, TypeScript/JavaScript, Rust, Java, C/C++, Ruby, Lua
|
|
158
219
|
"""
|
|
159
|
-
|
|
220
|
+
|
|
160
221
|
def __init__(self):
|
|
161
222
|
super().__init__()
|
|
162
223
|
self.servers: Dict[str, LSPServer] = {}
|
|
163
224
|
self.logger = logging.getLogger(__name__)
|
|
164
|
-
|
|
225
|
+
|
|
165
226
|
def _get_language_from_file(self, file_path: str) -> Optional[str]:
|
|
166
227
|
"""Detect language from file extension."""
|
|
167
228
|
ext = Path(file_path).suffix.lower()
|
|
168
|
-
|
|
229
|
+
|
|
169
230
|
for lang, config in LSP_SERVERS.items():
|
|
170
231
|
if ext in config["file_extensions"]:
|
|
171
232
|
return lang
|
|
172
|
-
|
|
233
|
+
|
|
173
234
|
return None
|
|
174
|
-
|
|
235
|
+
|
|
175
236
|
def _find_project_root(self, file_path: str, language: str) -> str:
|
|
176
237
|
"""Find project root based on language markers."""
|
|
177
238
|
markers = LSP_SERVERS[language]["root_markers"]
|
|
178
239
|
path = Path(file_path).resolve()
|
|
179
|
-
|
|
240
|
+
|
|
180
241
|
for parent in path.parents:
|
|
181
242
|
for marker in markers:
|
|
182
243
|
if (parent / marker).exists():
|
|
183
244
|
return str(parent)
|
|
184
|
-
|
|
245
|
+
|
|
185
246
|
return str(path.parent)
|
|
186
|
-
|
|
247
|
+
|
|
187
248
|
async def _check_lsp_installed(self, language: str) -> bool:
|
|
188
249
|
"""Check if LSP server is installed."""
|
|
189
250
|
config = LSP_SERVERS.get(language)
|
|
190
251
|
if not config:
|
|
191
252
|
return False
|
|
192
|
-
|
|
253
|
+
|
|
193
254
|
try:
|
|
194
255
|
result = await asyncio.create_subprocess_exec(
|
|
195
256
|
*config["check_cmd"],
|
|
196
257
|
stdout=asyncio.subprocess.PIPE,
|
|
197
|
-
stderr=asyncio.subprocess.PIPE
|
|
258
|
+
stderr=asyncio.subprocess.PIPE,
|
|
198
259
|
)
|
|
199
260
|
await result.communicate()
|
|
200
261
|
return result.returncode == 0
|
|
201
262
|
except (FileNotFoundError, OSError):
|
|
202
263
|
return False
|
|
203
|
-
|
|
264
|
+
|
|
204
265
|
async def _install_lsp(self, language: str) -> bool:
|
|
205
266
|
"""Install LSP server for language."""
|
|
206
267
|
config = LSP_SERVERS.get(language)
|
|
207
268
|
if not config:
|
|
208
269
|
return False
|
|
209
|
-
|
|
270
|
+
|
|
210
271
|
self.logger.info(f"Installing {config['name']} for {language}")
|
|
211
|
-
|
|
272
|
+
|
|
212
273
|
try:
|
|
213
274
|
# Check if installer is available
|
|
214
275
|
installer = config["install_cmd"][0]
|
|
215
276
|
if not shutil.which(installer):
|
|
216
277
|
self.logger.error(f"Installer {installer} not found")
|
|
217
278
|
return False
|
|
218
|
-
|
|
279
|
+
|
|
219
280
|
# Run installation command
|
|
220
281
|
result = await asyncio.create_subprocess_exec(
|
|
221
282
|
*config["install_cmd"],
|
|
222
283
|
stdout=asyncio.subprocess.PIPE,
|
|
223
|
-
stderr=asyncio.subprocess.PIPE
|
|
284
|
+
stderr=asyncio.subprocess.PIPE,
|
|
224
285
|
)
|
|
225
|
-
|
|
286
|
+
|
|
226
287
|
stdout, stderr = await result.communicate()
|
|
227
|
-
|
|
288
|
+
|
|
228
289
|
if result.returncode != 0:
|
|
229
290
|
self.logger.error(f"Installation failed: {stderr.decode()}")
|
|
230
291
|
return False
|
|
231
|
-
|
|
292
|
+
|
|
232
293
|
self.logger.info(f"Successfully installed {config['name']}")
|
|
233
294
|
return True
|
|
234
|
-
|
|
295
|
+
|
|
235
296
|
except Exception as e:
|
|
236
297
|
self.logger.error(f"Installation error: {e}")
|
|
237
298
|
return False
|
|
238
|
-
|
|
239
|
-
async def _ensure_lsp_running(
|
|
299
|
+
|
|
300
|
+
async def _ensure_lsp_running(
|
|
301
|
+
self, language: str, root_uri: str
|
|
302
|
+
) -> Optional[LSPServer]:
|
|
240
303
|
"""Ensure LSP server is running for language."""
|
|
241
304
|
# Check if already running
|
|
242
305
|
server_key = f"{language}:{root_uri}"
|
|
@@ -244,42 +307,39 @@ class LSPTool(BaseTool):
|
|
|
244
307
|
server = self.servers[server_key]
|
|
245
308
|
if server.process and server.process.returncode is None:
|
|
246
309
|
return server
|
|
247
|
-
|
|
310
|
+
|
|
248
311
|
# Check if installed
|
|
249
312
|
if not await self._check_lsp_installed(language):
|
|
250
313
|
# Try to install
|
|
251
314
|
if not await self._install_lsp(language):
|
|
252
315
|
return None
|
|
253
|
-
|
|
316
|
+
|
|
254
317
|
# Start LSP server
|
|
255
318
|
config = LSP_SERVERS[language]
|
|
256
|
-
|
|
319
|
+
|
|
257
320
|
try:
|
|
258
321
|
process = await asyncio.create_subprocess_exec(
|
|
259
322
|
*config["start_cmd"],
|
|
260
323
|
stdin=asyncio.subprocess.PIPE,
|
|
261
324
|
stdout=asyncio.subprocess.PIPE,
|
|
262
325
|
stderr=asyncio.subprocess.PIPE,
|
|
263
|
-
cwd=root_uri
|
|
326
|
+
cwd=root_uri,
|
|
264
327
|
)
|
|
265
|
-
|
|
328
|
+
|
|
266
329
|
server = LSPServer(
|
|
267
|
-
language=language,
|
|
268
|
-
process=process,
|
|
269
|
-
config=config,
|
|
270
|
-
root_uri=root_uri
|
|
330
|
+
language=language, process=process, config=config, root_uri=root_uri
|
|
271
331
|
)
|
|
272
|
-
|
|
332
|
+
|
|
273
333
|
# Initialize LSP
|
|
274
334
|
await self._initialize_lsp(server)
|
|
275
|
-
|
|
335
|
+
|
|
276
336
|
self.servers[server_key] = server
|
|
277
337
|
return server
|
|
278
|
-
|
|
338
|
+
|
|
279
339
|
except Exception as e:
|
|
280
340
|
self.logger.error(f"Failed to start LSP: {e}")
|
|
281
341
|
return None
|
|
282
|
-
|
|
342
|
+
|
|
283
343
|
async def _initialize_lsp(self, server: LSPServer):
|
|
284
344
|
"""Send initialize request to LSP server."""
|
|
285
345
|
# This is a simplified initialization
|
|
@@ -291,58 +351,62 @@ class LSPTool(BaseTool):
|
|
|
291
351
|
"textDocument": {
|
|
292
352
|
"definition": {"dynamicRegistration": True},
|
|
293
353
|
"references": {"dynamicRegistration": True},
|
|
294
|
-
"rename": {"dynamicRegistration": True}
|
|
354
|
+
"rename": {"dynamicRegistration": True},
|
|
295
355
|
}
|
|
296
|
-
}
|
|
356
|
+
},
|
|
297
357
|
}
|
|
298
|
-
|
|
358
|
+
|
|
299
359
|
# Send initialize request
|
|
300
360
|
request = {
|
|
301
361
|
"jsonrpc": "2.0",
|
|
302
362
|
"id": 1,
|
|
303
363
|
"method": "initialize",
|
|
304
|
-
"params": init_params
|
|
364
|
+
"params": init_params,
|
|
305
365
|
}
|
|
306
|
-
|
|
366
|
+
|
|
307
367
|
await self._send_request(server, request)
|
|
308
368
|
server.initialized = True
|
|
309
|
-
|
|
310
|
-
async def _send_request(
|
|
369
|
+
|
|
370
|
+
async def _send_request(
|
|
371
|
+
self, server: LSPServer, request: Dict[str, Any]
|
|
372
|
+
) -> Optional[Dict[str, Any]]:
|
|
311
373
|
"""Send JSON-RPC request to LSP server."""
|
|
312
374
|
if not server.process or server.process.returncode is not None:
|
|
313
375
|
return None
|
|
314
|
-
|
|
376
|
+
|
|
315
377
|
try:
|
|
316
378
|
# Serialize request
|
|
317
379
|
request_str = json.dumps(request)
|
|
318
|
-
content_length = len(request_str.encode(
|
|
319
|
-
|
|
380
|
+
content_length = len(request_str.encode("utf-8"))
|
|
381
|
+
|
|
320
382
|
# Send LSP message
|
|
321
383
|
message = f"Content-Length: {content_length}\r\n\r\n{request_str}"
|
|
322
|
-
server.process.stdin.write(message.encode(
|
|
384
|
+
server.process.stdin.write(message.encode("utf-8"))
|
|
323
385
|
await server.process.stdin.drain()
|
|
324
|
-
|
|
386
|
+
|
|
325
387
|
# Read response (simplified - real implementation needs proper parsing)
|
|
326
388
|
# This is a placeholder - actual LSP requires parsing Content-Length headers
|
|
327
389
|
response_data = await server.process.stdout.readline()
|
|
328
|
-
|
|
390
|
+
|
|
329
391
|
if response_data:
|
|
330
|
-
return json.loads(response_data.decode(
|
|
331
|
-
|
|
392
|
+
return json.loads(response_data.decode("utf-8"))
|
|
393
|
+
|
|
332
394
|
except Exception as e:
|
|
333
395
|
self.logger.error(f"LSP communication error: {e}")
|
|
334
|
-
|
|
396
|
+
|
|
335
397
|
return None
|
|
336
|
-
|
|
337
|
-
async def run(
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
398
|
+
|
|
399
|
+
async def run(
|
|
400
|
+
self,
|
|
401
|
+
action: str,
|
|
402
|
+
file: str,
|
|
403
|
+
line: Optional[int] = None,
|
|
404
|
+
character: Optional[int] = None,
|
|
405
|
+
new_name: Optional[str] = None,
|
|
406
|
+
**kwargs,
|
|
407
|
+
) -> MCPResourceDocument:
|
|
344
408
|
"""Execute LSP action.
|
|
345
|
-
|
|
409
|
+
|
|
346
410
|
Args:
|
|
347
411
|
action: LSP action (definition, references, rename, diagnostics, hover, completion, status)
|
|
348
412
|
file: File path to analyze
|
|
@@ -350,65 +414,84 @@ class LSPTool(BaseTool):
|
|
|
350
414
|
character: Character position in line (0-indexed)
|
|
351
415
|
new_name: New name for rename action
|
|
352
416
|
"""
|
|
353
|
-
|
|
417
|
+
|
|
354
418
|
# Validate action
|
|
355
|
-
valid_actions = [
|
|
419
|
+
valid_actions = [
|
|
420
|
+
"definition",
|
|
421
|
+
"references",
|
|
422
|
+
"rename",
|
|
423
|
+
"diagnostics",
|
|
424
|
+
"hover",
|
|
425
|
+
"completion",
|
|
426
|
+
"status",
|
|
427
|
+
]
|
|
356
428
|
if action not in valid_actions:
|
|
357
|
-
return MCPResourceDocument(
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
429
|
+
return MCPResourceDocument(
|
|
430
|
+
data={
|
|
431
|
+
"error": f"Invalid action. Must be one of: {', '.join(valid_actions)}"
|
|
432
|
+
}
|
|
433
|
+
)
|
|
434
|
+
|
|
361
435
|
# Get language from file
|
|
362
436
|
language = self._get_language_from_file(file)
|
|
363
437
|
if not language:
|
|
364
|
-
return MCPResourceDocument(
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
438
|
+
return MCPResourceDocument(
|
|
439
|
+
data={
|
|
440
|
+
"error": f"Unsupported file type: {file}",
|
|
441
|
+
"supported_languages": list(LSP_SERVERS.keys()),
|
|
442
|
+
}
|
|
443
|
+
)
|
|
444
|
+
|
|
369
445
|
# Check LSP capabilities
|
|
370
446
|
capabilities = LSP_SERVERS[language]["capabilities"]
|
|
371
447
|
if action not in capabilities and action != "status":
|
|
372
|
-
return MCPResourceDocument(
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
448
|
+
return MCPResourceDocument(
|
|
449
|
+
data={
|
|
450
|
+
"error": f"Action '{action}' not supported for {language}",
|
|
451
|
+
"supported_actions": capabilities,
|
|
452
|
+
}
|
|
453
|
+
)
|
|
454
|
+
|
|
377
455
|
# Status check
|
|
378
456
|
if action == "status":
|
|
379
457
|
installed = await self._check_lsp_installed(language)
|
|
380
|
-
return MCPResourceDocument(
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
458
|
+
return MCPResourceDocument(
|
|
459
|
+
data={
|
|
460
|
+
"language": language,
|
|
461
|
+
"lsp_server": LSP_SERVERS[language]["name"],
|
|
462
|
+
"installed": installed,
|
|
463
|
+
"capabilities": capabilities,
|
|
464
|
+
}
|
|
465
|
+
)
|
|
466
|
+
|
|
387
467
|
# Find project root
|
|
388
468
|
root_uri = self._find_project_root(file, language)
|
|
389
|
-
|
|
469
|
+
|
|
390
470
|
# Ensure LSP is running
|
|
391
471
|
server = await self._ensure_lsp_running(language, root_uri)
|
|
392
472
|
if not server:
|
|
393
|
-
return MCPResourceDocument(
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
473
|
+
return MCPResourceDocument(
|
|
474
|
+
data={
|
|
475
|
+
"error": f"Failed to start LSP server for {language}",
|
|
476
|
+
"install_command": " ".join(LSP_SERVERS[language]["install_cmd"]),
|
|
477
|
+
}
|
|
478
|
+
)
|
|
479
|
+
|
|
398
480
|
# Execute action
|
|
399
|
-
result = await self._execute_lsp_action(
|
|
400
|
-
|
|
481
|
+
result = await self._execute_lsp_action(
|
|
482
|
+
server, action, file, line, character, new_name
|
|
483
|
+
)
|
|
484
|
+
|
|
401
485
|
return MCPResourceDocument(data=result)
|
|
402
|
-
|
|
486
|
+
|
|
403
487
|
async def call(self, **kwargs) -> str:
|
|
404
488
|
"""Tool interface for MCP - converts result to JSON string."""
|
|
405
489
|
result = await self.run(**kwargs)
|
|
406
490
|
return result.to_json_string()
|
|
407
|
-
|
|
491
|
+
|
|
408
492
|
def register(self, mcp_server) -> None:
|
|
409
493
|
"""Register tool with MCP server."""
|
|
410
|
-
|
|
411
|
-
|
|
494
|
+
|
|
412
495
|
@mcp_server.tool(name=self.name, description=self.description)
|
|
413
496
|
async def lsp_handler(
|
|
414
497
|
action: str,
|
|
@@ -425,19 +508,21 @@ class LSPTool(BaseTool):
|
|
|
425
508
|
character=character,
|
|
426
509
|
new_name=new_name,
|
|
427
510
|
)
|
|
428
|
-
|
|
429
|
-
async def _execute_lsp_action(
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
511
|
+
|
|
512
|
+
async def _execute_lsp_action(
|
|
513
|
+
self,
|
|
514
|
+
server: LSPServer,
|
|
515
|
+
action: str,
|
|
516
|
+
file: str,
|
|
517
|
+
line: Optional[int],
|
|
518
|
+
character: Optional[int],
|
|
519
|
+
new_name: Optional[str],
|
|
520
|
+
) -> Dict[str, Any]:
|
|
436
521
|
"""Execute specific LSP action."""
|
|
437
|
-
|
|
522
|
+
|
|
438
523
|
# This is a simplified implementation
|
|
439
524
|
# Real implementation would use proper LSP protocol
|
|
440
|
-
|
|
525
|
+
|
|
441
526
|
if action == "definition":
|
|
442
527
|
# textDocument/definition request
|
|
443
528
|
return {
|
|
@@ -445,9 +530,9 @@ class LSPTool(BaseTool):
|
|
|
445
530
|
"file": file,
|
|
446
531
|
"position": {"line": line, "character": character},
|
|
447
532
|
"note": "LSP integration pending full implementation",
|
|
448
|
-
"fallback": "Use mcp__lsp__find_definition tool for now"
|
|
533
|
+
"fallback": "Use mcp__lsp__find_definition tool for now",
|
|
449
534
|
}
|
|
450
|
-
|
|
535
|
+
|
|
451
536
|
elif action == "references":
|
|
452
537
|
# textDocument/references request
|
|
453
538
|
return {
|
|
@@ -455,9 +540,9 @@ class LSPTool(BaseTool):
|
|
|
455
540
|
"file": file,
|
|
456
541
|
"position": {"line": line, "character": character},
|
|
457
542
|
"note": "LSP integration pending full implementation",
|
|
458
|
-
"fallback": "Use mcp__lsp__find_references tool for now"
|
|
543
|
+
"fallback": "Use mcp__lsp__find_references tool for now",
|
|
459
544
|
}
|
|
460
|
-
|
|
545
|
+
|
|
461
546
|
elif action == "rename":
|
|
462
547
|
# textDocument/rename request
|
|
463
548
|
return {
|
|
@@ -466,38 +551,38 @@ class LSPTool(BaseTool):
|
|
|
466
551
|
"position": {"line": line, "character": character},
|
|
467
552
|
"new_name": new_name,
|
|
468
553
|
"note": "LSP integration pending full implementation",
|
|
469
|
-
"fallback": "Use mcp__lsp__rename_symbol tool for now"
|
|
554
|
+
"fallback": "Use mcp__lsp__rename_symbol tool for now",
|
|
470
555
|
}
|
|
471
|
-
|
|
556
|
+
|
|
472
557
|
elif action == "diagnostics":
|
|
473
558
|
# textDocument/diagnostic request
|
|
474
559
|
return {
|
|
475
560
|
"action": "diagnostics",
|
|
476
561
|
"file": file,
|
|
477
562
|
"note": "LSP integration pending full implementation",
|
|
478
|
-
"fallback": "Use mcp__lsp__get_diagnostics tool for now"
|
|
563
|
+
"fallback": "Use mcp__lsp__get_diagnostics tool for now",
|
|
479
564
|
}
|
|
480
|
-
|
|
565
|
+
|
|
481
566
|
elif action == "hover":
|
|
482
567
|
# textDocument/hover request
|
|
483
568
|
return {
|
|
484
569
|
"action": "hover",
|
|
485
570
|
"file": file,
|
|
486
571
|
"position": {"line": line, "character": character},
|
|
487
|
-
"note": "LSP integration pending full implementation"
|
|
572
|
+
"note": "LSP integration pending full implementation",
|
|
488
573
|
}
|
|
489
|
-
|
|
574
|
+
|
|
490
575
|
elif action == "completion":
|
|
491
576
|
# textDocument/completion request
|
|
492
577
|
return {
|
|
493
578
|
"action": "completion",
|
|
494
579
|
"file": file,
|
|
495
580
|
"position": {"line": line, "character": character},
|
|
496
|
-
"note": "LSP integration pending full implementation"
|
|
581
|
+
"note": "LSP integration pending full implementation",
|
|
497
582
|
}
|
|
498
|
-
|
|
583
|
+
|
|
499
584
|
return {"error": "Unknown action"}
|
|
500
|
-
|
|
585
|
+
|
|
501
586
|
async def cleanup(self):
|
|
502
587
|
"""Clean up LSP servers."""
|
|
503
588
|
for server in self.servers.values():
|
|
@@ -509,4 +594,4 @@ class LSPTool(BaseTool):
|
|
|
509
594
|
# Tool registration
|
|
510
595
|
def create_lsp_tool():
|
|
511
596
|
"""Factory function to create LSP tool."""
|
|
512
|
-
return LSPTool()
|
|
597
|
+
return LSPTool()
|