hanzo-mcp 0.5.1__py3-none-any.whl → 0.6.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.
- hanzo_mcp/__init__.py +1 -1
- hanzo_mcp/cli.py +32 -0
- hanzo_mcp/dev_server.py +246 -0
- hanzo_mcp/prompts/__init__.py +1 -1
- hanzo_mcp/prompts/project_system.py +43 -7
- hanzo_mcp/server.py +5 -1
- hanzo_mcp/tools/__init__.py +168 -6
- hanzo_mcp/tools/agent/__init__.py +1 -1
- hanzo_mcp/tools/agent/agent.py +401 -0
- hanzo_mcp/tools/agent/agent_tool.py +3 -4
- hanzo_mcp/tools/common/__init__.py +1 -1
- hanzo_mcp/tools/common/base.py +9 -4
- hanzo_mcp/tools/common/batch_tool.py +3 -5
- hanzo_mcp/tools/common/config_tool.py +1 -1
- hanzo_mcp/tools/common/context.py +1 -1
- hanzo_mcp/tools/common/palette.py +344 -0
- hanzo_mcp/tools/common/palette_loader.py +108 -0
- hanzo_mcp/tools/common/stats.py +261 -0
- hanzo_mcp/tools/common/thinking_tool.py +3 -5
- hanzo_mcp/tools/common/tool_disable.py +144 -0
- hanzo_mcp/tools/common/tool_enable.py +182 -0
- hanzo_mcp/tools/common/tool_list.py +260 -0
- hanzo_mcp/tools/config/__init__.py +10 -0
- hanzo_mcp/tools/config/config_tool.py +212 -0
- hanzo_mcp/tools/config/index_config.py +176 -0
- hanzo_mcp/tools/config/palette_tool.py +166 -0
- hanzo_mcp/tools/database/__init__.py +71 -0
- hanzo_mcp/tools/database/database_manager.py +246 -0
- hanzo_mcp/tools/database/graph.py +482 -0
- hanzo_mcp/tools/database/graph_add.py +257 -0
- hanzo_mcp/tools/database/graph_query.py +536 -0
- hanzo_mcp/tools/database/graph_remove.py +267 -0
- hanzo_mcp/tools/database/graph_search.py +348 -0
- hanzo_mcp/tools/database/graph_stats.py +345 -0
- hanzo_mcp/tools/database/sql.py +411 -0
- hanzo_mcp/tools/database/sql_query.py +229 -0
- hanzo_mcp/tools/database/sql_search.py +296 -0
- hanzo_mcp/tools/database/sql_stats.py +254 -0
- hanzo_mcp/tools/editor/__init__.py +11 -0
- hanzo_mcp/tools/editor/neovim_command.py +272 -0
- hanzo_mcp/tools/editor/neovim_edit.py +290 -0
- hanzo_mcp/tools/editor/neovim_session.py +356 -0
- hanzo_mcp/tools/filesystem/__init__.py +52 -13
- hanzo_mcp/tools/filesystem/base.py +1 -1
- hanzo_mcp/tools/filesystem/batch_search.py +812 -0
- hanzo_mcp/tools/filesystem/content_replace.py +3 -5
- hanzo_mcp/tools/filesystem/diff.py +193 -0
- hanzo_mcp/tools/filesystem/directory_tree.py +3 -5
- hanzo_mcp/tools/filesystem/edit.py +3 -5
- hanzo_mcp/tools/filesystem/find.py +443 -0
- hanzo_mcp/tools/filesystem/find_files.py +348 -0
- hanzo_mcp/tools/filesystem/git_search.py +505 -0
- hanzo_mcp/tools/filesystem/grep.py +2 -2
- hanzo_mcp/tools/filesystem/multi_edit.py +3 -5
- hanzo_mcp/tools/filesystem/read.py +17 -5
- hanzo_mcp/tools/filesystem/{grep_ast_tool.py → symbols.py} +17 -27
- hanzo_mcp/tools/filesystem/symbols_unified.py +376 -0
- hanzo_mcp/tools/filesystem/tree.py +268 -0
- hanzo_mcp/tools/filesystem/unified_search.py +465 -443
- hanzo_mcp/tools/filesystem/unix_aliases.py +99 -0
- hanzo_mcp/tools/filesystem/watch.py +174 -0
- hanzo_mcp/tools/filesystem/write.py +3 -5
- hanzo_mcp/tools/jupyter/__init__.py +9 -12
- hanzo_mcp/tools/jupyter/base.py +1 -1
- hanzo_mcp/tools/jupyter/jupyter.py +326 -0
- hanzo_mcp/tools/jupyter/notebook_edit.py +3 -4
- hanzo_mcp/tools/jupyter/notebook_read.py +3 -5
- hanzo_mcp/tools/llm/__init__.py +31 -0
- hanzo_mcp/tools/llm/consensus_tool.py +351 -0
- hanzo_mcp/tools/llm/llm_manage.py +413 -0
- hanzo_mcp/tools/llm/llm_tool.py +346 -0
- hanzo_mcp/tools/llm/llm_unified.py +851 -0
- hanzo_mcp/tools/llm/provider_tools.py +412 -0
- hanzo_mcp/tools/mcp/__init__.py +15 -0
- hanzo_mcp/tools/mcp/mcp_add.py +263 -0
- hanzo_mcp/tools/mcp/mcp_remove.py +127 -0
- hanzo_mcp/tools/mcp/mcp_stats.py +165 -0
- hanzo_mcp/tools/mcp/mcp_unified.py +503 -0
- hanzo_mcp/tools/shell/__init__.py +21 -23
- hanzo_mcp/tools/shell/base.py +1 -1
- hanzo_mcp/tools/shell/base_process.py +303 -0
- hanzo_mcp/tools/shell/bash_unified.py +134 -0
- hanzo_mcp/tools/shell/logs.py +265 -0
- hanzo_mcp/tools/shell/npx.py +194 -0
- hanzo_mcp/tools/shell/npx_background.py +254 -0
- hanzo_mcp/tools/shell/npx_unified.py +101 -0
- hanzo_mcp/tools/shell/open.py +107 -0
- hanzo_mcp/tools/shell/pkill.py +262 -0
- hanzo_mcp/tools/shell/process_unified.py +131 -0
- hanzo_mcp/tools/shell/processes.py +279 -0
- hanzo_mcp/tools/shell/run_background.py +326 -0
- hanzo_mcp/tools/shell/run_command.py +3 -4
- hanzo_mcp/tools/shell/run_command_windows.py +3 -4
- hanzo_mcp/tools/shell/uvx.py +187 -0
- hanzo_mcp/tools/shell/uvx_background.py +249 -0
- hanzo_mcp/tools/shell/uvx_unified.py +101 -0
- hanzo_mcp/tools/todo/__init__.py +1 -1
- hanzo_mcp/tools/todo/base.py +1 -1
- hanzo_mcp/tools/todo/todo.py +265 -0
- hanzo_mcp/tools/todo/todo_read.py +3 -5
- hanzo_mcp/tools/todo/todo_write.py +3 -5
- hanzo_mcp/tools/vector/__init__.py +6 -1
- hanzo_mcp/tools/vector/git_ingester.py +3 -0
- hanzo_mcp/tools/vector/index_tool.py +358 -0
- hanzo_mcp/tools/vector/infinity_store.py +98 -0
- hanzo_mcp/tools/vector/project_manager.py +27 -5
- hanzo_mcp/tools/vector/vector.py +311 -0
- hanzo_mcp/tools/vector/vector_index.py +1 -1
- hanzo_mcp/tools/vector/vector_search.py +12 -7
- hanzo_mcp-0.6.1.dist-info/METADATA +336 -0
- hanzo_mcp-0.6.1.dist-info/RECORD +134 -0
- hanzo_mcp-0.6.1.dist-info/entry_points.txt +3 -0
- hanzo_mcp-0.5.1.dist-info/METADATA +0 -276
- hanzo_mcp-0.5.1.dist-info/RECORD +0 -68
- hanzo_mcp-0.5.1.dist-info/entry_points.txt +0 -2
- {hanzo_mcp-0.5.1.dist-info → hanzo_mcp-0.6.1.dist-info}/WHEEL +0 -0
- {hanzo_mcp-0.5.1.dist-info → hanzo_mcp-0.6.1.dist-info}/licenses/LICENSE +0 -0
- {hanzo_mcp-0.5.1.dist-info → hanzo_mcp-0.6.1.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,356 @@
|
|
|
1
|
+
"""Manage Neovim sessions."""
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
import subprocess
|
|
5
|
+
import shutil
|
|
6
|
+
import json
|
|
7
|
+
from typing import Annotated, Optional, TypedDict, Unpack, final, override, List
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
from datetime import datetime
|
|
10
|
+
|
|
11
|
+
from mcp.server.fastmcp import Context as MCPContext
|
|
12
|
+
from pydantic import Field
|
|
13
|
+
|
|
14
|
+
from hanzo_mcp.tools.common.base import BaseTool
|
|
15
|
+
from hanzo_mcp.tools.common.context import create_tool_context
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
Action = Annotated[
|
|
19
|
+
str,
|
|
20
|
+
Field(
|
|
21
|
+
description="Action to perform: save, restore, list, delete",
|
|
22
|
+
min_length=1,
|
|
23
|
+
),
|
|
24
|
+
]
|
|
25
|
+
|
|
26
|
+
SessionName = Annotated[
|
|
27
|
+
Optional[str],
|
|
28
|
+
Field(
|
|
29
|
+
description="Name of the session",
|
|
30
|
+
default=None,
|
|
31
|
+
),
|
|
32
|
+
]
|
|
33
|
+
|
|
34
|
+
ProjectPath = Annotated[
|
|
35
|
+
Optional[str],
|
|
36
|
+
Field(
|
|
37
|
+
description="Project path (defaults to current directory)",
|
|
38
|
+
default=None,
|
|
39
|
+
),
|
|
40
|
+
]
|
|
41
|
+
|
|
42
|
+
AutoName = Annotated[
|
|
43
|
+
bool,
|
|
44
|
+
Field(
|
|
45
|
+
description="Auto-generate session name based on project and timestamp",
|
|
46
|
+
default=False,
|
|
47
|
+
),
|
|
48
|
+
]
|
|
49
|
+
|
|
50
|
+
Overwrite = Annotated[
|
|
51
|
+
bool,
|
|
52
|
+
Field(
|
|
53
|
+
description="Overwrite existing session",
|
|
54
|
+
default=False,
|
|
55
|
+
),
|
|
56
|
+
]
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
class NeovimSessionParams(TypedDict, total=False):
|
|
60
|
+
"""Parameters for Neovim session tool."""
|
|
61
|
+
|
|
62
|
+
action: str
|
|
63
|
+
session_name: Optional[str]
|
|
64
|
+
project_path: Optional[str]
|
|
65
|
+
auto_name: bool
|
|
66
|
+
overwrite: bool
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
@final
|
|
70
|
+
class NeovimSessionTool(BaseTool):
|
|
71
|
+
"""Tool for managing Neovim sessions."""
|
|
72
|
+
|
|
73
|
+
def __init__(self):
|
|
74
|
+
"""Initialize the Neovim session tool."""
|
|
75
|
+
self.session_dir = Path.home() / ".hanzo" / "neovim" / "sessions"
|
|
76
|
+
self.session_dir.mkdir(parents=True, exist_ok=True)
|
|
77
|
+
|
|
78
|
+
@property
|
|
79
|
+
@override
|
|
80
|
+
def name(self) -> str:
|
|
81
|
+
"""Get the tool name."""
|
|
82
|
+
return "neovim_session"
|
|
83
|
+
|
|
84
|
+
@property
|
|
85
|
+
@override
|
|
86
|
+
def description(self) -> str:
|
|
87
|
+
"""Get the tool description."""
|
|
88
|
+
return """Save and restore Neovim editing sessions.
|
|
89
|
+
|
|
90
|
+
Manage Neovim sessions to save your workspace state including:
|
|
91
|
+
- Open files and buffers
|
|
92
|
+
- Window layouts and splits
|
|
93
|
+
- Cursor positions
|
|
94
|
+
- Marks and registers
|
|
95
|
+
- Local options and mappings
|
|
96
|
+
|
|
97
|
+
Actions:
|
|
98
|
+
- save: Save current Neovim session
|
|
99
|
+
- restore: Restore a saved session
|
|
100
|
+
- list: List all saved sessions
|
|
101
|
+
- delete: Delete a saved session
|
|
102
|
+
|
|
103
|
+
Examples:
|
|
104
|
+
- neovim_session --action save --session-name "feature-work"
|
|
105
|
+
- neovim_session --action save --auto-name # Auto-generate name
|
|
106
|
+
- neovim_session --action restore --session-name "feature-work"
|
|
107
|
+
- neovim_session --action list
|
|
108
|
+
- neovim_session --action list --project-path /path/to/project
|
|
109
|
+
- neovim_session --action delete --session-name "old-session"
|
|
110
|
+
|
|
111
|
+
Sessions are stored in ~/.hanzo/neovim/sessions/
|
|
112
|
+
Project-specific sessions are automatically organized by project path.
|
|
113
|
+
|
|
114
|
+
Note: Requires Neovim to be installed.
|
|
115
|
+
"""
|
|
116
|
+
|
|
117
|
+
@override
|
|
118
|
+
async def call(
|
|
119
|
+
self,
|
|
120
|
+
ctx: MCPContext,
|
|
121
|
+
**params: Unpack[NeovimSessionParams],
|
|
122
|
+
) -> str:
|
|
123
|
+
"""Manage Neovim session.
|
|
124
|
+
|
|
125
|
+
Args:
|
|
126
|
+
ctx: MCP context
|
|
127
|
+
**params: Tool parameters
|
|
128
|
+
|
|
129
|
+
Returns:
|
|
130
|
+
Result of the session operation
|
|
131
|
+
"""
|
|
132
|
+
tool_ctx = create_tool_context(ctx)
|
|
133
|
+
await tool_ctx.set_tool_info(self.name)
|
|
134
|
+
|
|
135
|
+
# Extract parameters
|
|
136
|
+
action = params.get("action")
|
|
137
|
+
if not action:
|
|
138
|
+
return "Error: action is required (save, restore, list, delete)"
|
|
139
|
+
|
|
140
|
+
session_name = params.get("session_name")
|
|
141
|
+
project_path = params.get("project_path") or os.getcwd()
|
|
142
|
+
auto_name = params.get("auto_name", False)
|
|
143
|
+
overwrite = params.get("overwrite", False)
|
|
144
|
+
|
|
145
|
+
# Validate action
|
|
146
|
+
valid_actions = ["save", "restore", "list", "delete"]
|
|
147
|
+
if action not in valid_actions:
|
|
148
|
+
return f"Error: Invalid action '{action}'. Must be one of: {', '.join(valid_actions)}"
|
|
149
|
+
|
|
150
|
+
# Check if Neovim is available
|
|
151
|
+
nvim_cmd = shutil.which("nvim")
|
|
152
|
+
if not nvim_cmd and action in ["save", "restore"]:
|
|
153
|
+
return "Error: Neovim (nvim) not found. Install it first."
|
|
154
|
+
|
|
155
|
+
# Get project-specific session directory
|
|
156
|
+
project_hash = str(hash(os.path.abspath(project_path)) % 10**8)
|
|
157
|
+
project_name = os.path.basename(project_path) or "root"
|
|
158
|
+
project_session_dir = self.session_dir / f"{project_name}_{project_hash}"
|
|
159
|
+
project_session_dir.mkdir(exist_ok=True)
|
|
160
|
+
|
|
161
|
+
# Handle different actions
|
|
162
|
+
if action == "save":
|
|
163
|
+
return await self._save_session(
|
|
164
|
+
tool_ctx, session_name, project_session_dir,
|
|
165
|
+
auto_name, overwrite, project_path
|
|
166
|
+
)
|
|
167
|
+
elif action == "restore":
|
|
168
|
+
return await self._restore_session(
|
|
169
|
+
tool_ctx, session_name, project_session_dir
|
|
170
|
+
)
|
|
171
|
+
elif action == "list":
|
|
172
|
+
return self._list_sessions(project_session_dir, project_path)
|
|
173
|
+
elif action == "delete":
|
|
174
|
+
return self._delete_session(session_name, project_session_dir)
|
|
175
|
+
|
|
176
|
+
async def _save_session(
|
|
177
|
+
self, tool_ctx, session_name: Optional[str],
|
|
178
|
+
project_dir: Path, auto_name: bool, overwrite: bool,
|
|
179
|
+
project_path: str
|
|
180
|
+
) -> str:
|
|
181
|
+
"""Save Neovim session."""
|
|
182
|
+
# Generate session name if needed
|
|
183
|
+
if auto_name or not session_name:
|
|
184
|
+
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
|
185
|
+
session_name = f"session_{timestamp}"
|
|
186
|
+
|
|
187
|
+
# Sanitize session name
|
|
188
|
+
session_name = session_name.replace("/", "_").replace(" ", "_")
|
|
189
|
+
|
|
190
|
+
session_file = project_dir / f"{session_name}.vim"
|
|
191
|
+
metadata_file = project_dir / f"{session_name}.json"
|
|
192
|
+
|
|
193
|
+
# Check if exists
|
|
194
|
+
if session_file.exists() and not overwrite:
|
|
195
|
+
return f"Error: Session '{session_name}' already exists. Use --overwrite to replace."
|
|
196
|
+
|
|
197
|
+
await tool_ctx.info(f"Saving Neovim session: {session_name}")
|
|
198
|
+
|
|
199
|
+
# Create temporary vim script to save session
|
|
200
|
+
vim_script = f'''
|
|
201
|
+
:mksession! {session_file}
|
|
202
|
+
:echo "Session saved to {session_file}"
|
|
203
|
+
:qa
|
|
204
|
+
'''
|
|
205
|
+
|
|
206
|
+
try:
|
|
207
|
+
# Run Neovim to save session
|
|
208
|
+
# First, check if Neovim is already running
|
|
209
|
+
# For now, we'll create a new instance
|
|
210
|
+
result = subprocess.run(
|
|
211
|
+
["nvim", "-c", vim_script.strip()],
|
|
212
|
+
capture_output=True,
|
|
213
|
+
text=True
|
|
214
|
+
)
|
|
215
|
+
|
|
216
|
+
if result.returncode != 0 and result.stderr:
|
|
217
|
+
return f"Error saving session: {result.stderr}"
|
|
218
|
+
|
|
219
|
+
# Save metadata
|
|
220
|
+
metadata = {
|
|
221
|
+
"name": session_name,
|
|
222
|
+
"created_at": datetime.now().isoformat(),
|
|
223
|
+
"project_path": project_path,
|
|
224
|
+
"description": f"Neovim session for {project_path}"
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
with open(metadata_file, 'w') as f:
|
|
228
|
+
json.dump(metadata, f, indent=2)
|
|
229
|
+
|
|
230
|
+
return f"""Successfully saved Neovim session '{session_name}'
|
|
231
|
+
|
|
232
|
+
Session file: {session_file}
|
|
233
|
+
Project: {project_path}
|
|
234
|
+
|
|
235
|
+
To restore this session:
|
|
236
|
+
neovim_session --action restore --session-name "{session_name}"
|
|
237
|
+
|
|
238
|
+
Or manually in Neovim:
|
|
239
|
+
:source {session_file}"""
|
|
240
|
+
|
|
241
|
+
except Exception as e:
|
|
242
|
+
return f"Error saving session: {str(e)}"
|
|
243
|
+
|
|
244
|
+
async def _restore_session(
|
|
245
|
+
self, tool_ctx, session_name: Optional[str],
|
|
246
|
+
project_dir: Path
|
|
247
|
+
) -> str:
|
|
248
|
+
"""Restore Neovim session."""
|
|
249
|
+
if not session_name:
|
|
250
|
+
# List available sessions
|
|
251
|
+
sessions = list(project_dir.glob("*.vim"))
|
|
252
|
+
if not sessions:
|
|
253
|
+
return "Error: No sessions found for this project. Use 'neovim_session --action list' to see all sessions."
|
|
254
|
+
|
|
255
|
+
# Use most recent
|
|
256
|
+
sessions.sort(key=lambda x: x.stat().st_mtime, reverse=True)
|
|
257
|
+
session_file = sessions[0]
|
|
258
|
+
session_name = session_file.stem
|
|
259
|
+
else:
|
|
260
|
+
session_file = project_dir / f"{session_name}.vim"
|
|
261
|
+
if not session_file.exists():
|
|
262
|
+
return f"Error: Session '{session_name}' not found. Use 'neovim_session --action list' to see available sessions."
|
|
263
|
+
|
|
264
|
+
await tool_ctx.info(f"Restoring Neovim session: {session_name}")
|
|
265
|
+
|
|
266
|
+
try:
|
|
267
|
+
# Open Neovim with the session
|
|
268
|
+
subprocess.run(["nvim", "-S", str(session_file)])
|
|
269
|
+
|
|
270
|
+
return f"Restored Neovim session '{session_name}'"
|
|
271
|
+
|
|
272
|
+
except Exception as e:
|
|
273
|
+
return f"Error restoring session: {str(e)}"
|
|
274
|
+
|
|
275
|
+
def _list_sessions(self, project_dir: Path, project_path: str) -> str:
|
|
276
|
+
"""List available sessions."""
|
|
277
|
+
output = ["=== Neovim Sessions ==="]
|
|
278
|
+
output.append(f"Project: {project_path}\n")
|
|
279
|
+
|
|
280
|
+
# List project-specific sessions
|
|
281
|
+
sessions = list(project_dir.glob("*.vim"))
|
|
282
|
+
|
|
283
|
+
if sessions:
|
|
284
|
+
output.append("Project Sessions:")
|
|
285
|
+
sessions.sort(key=lambda x: x.stat().st_mtime, reverse=True)
|
|
286
|
+
|
|
287
|
+
for session_file in sessions:
|
|
288
|
+
session_name = session_file.stem
|
|
289
|
+
metadata_file = project_dir / f"{session_name}.json"
|
|
290
|
+
|
|
291
|
+
# Get metadata if available
|
|
292
|
+
created_at = "Unknown"
|
|
293
|
+
if metadata_file.exists():
|
|
294
|
+
try:
|
|
295
|
+
with open(metadata_file, 'r') as f:
|
|
296
|
+
metadata = json.load(f)
|
|
297
|
+
created_at = metadata.get("created_at", "Unknown")
|
|
298
|
+
if created_at != "Unknown":
|
|
299
|
+
# Format date
|
|
300
|
+
dt = datetime.fromisoformat(created_at)
|
|
301
|
+
created_at = dt.strftime("%Y-%m-%d %H:%M:%S")
|
|
302
|
+
except:
|
|
303
|
+
pass
|
|
304
|
+
|
|
305
|
+
# Get file size
|
|
306
|
+
size = session_file.stat().st_size
|
|
307
|
+
size_kb = size / 1024
|
|
308
|
+
|
|
309
|
+
output.append(f" - {session_name}")
|
|
310
|
+
output.append(f" Created: {created_at}")
|
|
311
|
+
output.append(f" Size: {size_kb:.1f} KB")
|
|
312
|
+
else:
|
|
313
|
+
output.append("No sessions found for this project.")
|
|
314
|
+
|
|
315
|
+
# Also list all sessions
|
|
316
|
+
all_sessions = list(self.session_dir.rglob("*.vim"))
|
|
317
|
+
other_sessions = [s for s in all_sessions if s.parent != project_dir]
|
|
318
|
+
|
|
319
|
+
if other_sessions:
|
|
320
|
+
output.append("\nOther Projects' Sessions:")
|
|
321
|
+
for session_file in other_sessions[:10]: # Show max 10
|
|
322
|
+
project_name = session_file.parent.name
|
|
323
|
+
session_name = session_file.stem
|
|
324
|
+
output.append(f" - {project_name}/{session_name}")
|
|
325
|
+
|
|
326
|
+
if len(other_sessions) > 10:
|
|
327
|
+
output.append(f" ... and {len(other_sessions) - 10} more")
|
|
328
|
+
|
|
329
|
+
output.append("\nUse 'neovim_session --action restore --session-name <name>' to restore a session.")
|
|
330
|
+
|
|
331
|
+
return "\n".join(output)
|
|
332
|
+
|
|
333
|
+
def _delete_session(self, session_name: Optional[str], project_dir: Path) -> str:
|
|
334
|
+
"""Delete a session."""
|
|
335
|
+
if not session_name:
|
|
336
|
+
return "Error: session_name is required for delete action"
|
|
337
|
+
|
|
338
|
+
session_file = project_dir / f"{session_name}.vim"
|
|
339
|
+
metadata_file = project_dir / f"{session_name}.json"
|
|
340
|
+
|
|
341
|
+
if not session_file.exists():
|
|
342
|
+
return f"Error: Session '{session_name}' not found"
|
|
343
|
+
|
|
344
|
+
try:
|
|
345
|
+
session_file.unlink()
|
|
346
|
+
if metadata_file.exists():
|
|
347
|
+
metadata_file.unlink()
|
|
348
|
+
|
|
349
|
+
return f"Successfully deleted session '{session_name}'"
|
|
350
|
+
|
|
351
|
+
except Exception as e:
|
|
352
|
+
return f"Error deleting session: {str(e)}"
|
|
353
|
+
|
|
354
|
+
def register(self, mcp_server) -> None:
|
|
355
|
+
"""Register this tool with the MCP server."""
|
|
356
|
+
pass
|
|
@@ -4,7 +4,7 @@ This package provides tools for interacting with the filesystem, including readi
|
|
|
4
4
|
and editing files, directory navigation, and content searching.
|
|
5
5
|
"""
|
|
6
6
|
|
|
7
|
-
from
|
|
7
|
+
from mcp.server import FastMCP
|
|
8
8
|
|
|
9
9
|
from hanzo_mcp.tools.common.base import BaseTool, ToolRegistry
|
|
10
10
|
|
|
@@ -13,11 +13,16 @@ from hanzo_mcp.tools.filesystem.content_replace import ContentReplaceTool
|
|
|
13
13
|
from hanzo_mcp.tools.filesystem.directory_tree import DirectoryTreeTool
|
|
14
14
|
from hanzo_mcp.tools.filesystem.edit import Edit
|
|
15
15
|
from hanzo_mcp.tools.filesystem.grep import Grep
|
|
16
|
-
from hanzo_mcp.tools.filesystem.
|
|
16
|
+
from hanzo_mcp.tools.filesystem.symbols import SymbolsTool
|
|
17
|
+
from hanzo_mcp.tools.filesystem.git_search import GitSearchTool
|
|
17
18
|
from hanzo_mcp.tools.filesystem.multi_edit import MultiEdit
|
|
18
19
|
from hanzo_mcp.tools.filesystem.read import ReadTool
|
|
19
20
|
from hanzo_mcp.tools.filesystem.write import Write
|
|
21
|
+
from hanzo_mcp.tools.filesystem.batch_search import BatchSearchTool
|
|
22
|
+
from hanzo_mcp.tools.filesystem.find_files import FindFilesTool
|
|
20
23
|
from hanzo_mcp.tools.filesystem.unified_search import UnifiedSearchTool
|
|
24
|
+
from hanzo_mcp.tools.filesystem.watch import watch_tool
|
|
25
|
+
from hanzo_mcp.tools.filesystem.diff import create_diff_tool
|
|
21
26
|
|
|
22
27
|
# Export all tool classes
|
|
23
28
|
__all__ = [
|
|
@@ -28,7 +33,10 @@ __all__ = [
|
|
|
28
33
|
"DirectoryTreeTool",
|
|
29
34
|
"Grep",
|
|
30
35
|
"ContentReplaceTool",
|
|
31
|
-
"
|
|
36
|
+
"SymbolsTool",
|
|
37
|
+
"GitSearchTool",
|
|
38
|
+
"BatchSearchTool",
|
|
39
|
+
"FindFilesTool",
|
|
32
40
|
"UnifiedSearchTool",
|
|
33
41
|
"get_filesystem_tools",
|
|
34
42
|
"register_filesystem_tools",
|
|
@@ -37,33 +45,46 @@ __all__ = [
|
|
|
37
45
|
|
|
38
46
|
def get_read_only_filesystem_tools(
|
|
39
47
|
permission_manager: PermissionManager,
|
|
48
|
+
project_manager=None,
|
|
40
49
|
) -> list[BaseTool]:
|
|
41
50
|
"""Create instances of read-only filesystem tools.
|
|
42
51
|
|
|
43
52
|
Args:
|
|
44
53
|
permission_manager: Permission manager for access control
|
|
54
|
+
project_manager: Optional project manager for unified search
|
|
45
55
|
|
|
46
56
|
Returns:
|
|
47
57
|
List of read-only filesystem tool instances
|
|
48
58
|
"""
|
|
49
|
-
|
|
59
|
+
tools = [
|
|
50
60
|
ReadTool(permission_manager),
|
|
51
61
|
DirectoryTreeTool(permission_manager),
|
|
52
62
|
Grep(permission_manager),
|
|
53
|
-
|
|
63
|
+
SymbolsTool(permission_manager),
|
|
64
|
+
GitSearchTool(permission_manager),
|
|
65
|
+
FindFilesTool(permission_manager),
|
|
66
|
+
watch_tool,
|
|
67
|
+
create_diff_tool(permission_manager),
|
|
54
68
|
]
|
|
69
|
+
|
|
70
|
+
# Add unified search if project manager is available
|
|
71
|
+
if project_manager:
|
|
72
|
+
tools.append(UnifiedSearchTool(permission_manager, project_manager))
|
|
73
|
+
|
|
74
|
+
return tools
|
|
55
75
|
|
|
56
76
|
|
|
57
|
-
def get_filesystem_tools(permission_manager: PermissionManager) -> list[BaseTool]:
|
|
77
|
+
def get_filesystem_tools(permission_manager: PermissionManager, project_manager=None) -> list[BaseTool]:
|
|
58
78
|
"""Create instances of all filesystem tools.
|
|
59
79
|
|
|
60
80
|
Args:
|
|
61
81
|
permission_manager: Permission manager for access control
|
|
82
|
+
project_manager: Optional project manager for unified search
|
|
62
83
|
|
|
63
84
|
Returns:
|
|
64
85
|
List of filesystem tool instances
|
|
65
86
|
"""
|
|
66
|
-
|
|
87
|
+
tools = [
|
|
67
88
|
ReadTool(permission_manager),
|
|
68
89
|
Write(permission_manager),
|
|
69
90
|
Edit(permission_manager),
|
|
@@ -71,8 +92,18 @@ def get_filesystem_tools(permission_manager: PermissionManager) -> list[BaseTool
|
|
|
71
92
|
DirectoryTreeTool(permission_manager),
|
|
72
93
|
Grep(permission_manager),
|
|
73
94
|
ContentReplaceTool(permission_manager),
|
|
74
|
-
|
|
95
|
+
SymbolsTool(permission_manager),
|
|
96
|
+
GitSearchTool(permission_manager),
|
|
97
|
+
FindFilesTool(permission_manager),
|
|
98
|
+
watch_tool,
|
|
99
|
+
create_diff_tool(permission_manager),
|
|
75
100
|
]
|
|
101
|
+
|
|
102
|
+
# Add unified search if project manager is available
|
|
103
|
+
if project_manager:
|
|
104
|
+
tools.append(UnifiedSearchTool(permission_manager, project_manager))
|
|
105
|
+
|
|
106
|
+
return tools
|
|
76
107
|
|
|
77
108
|
|
|
78
109
|
def register_filesystem_tools(
|
|
@@ -104,9 +135,14 @@ def register_filesystem_tools(
|
|
|
104
135
|
"multi_edit": MultiEdit,
|
|
105
136
|
"directory_tree": DirectoryTreeTool,
|
|
106
137
|
"grep": Grep,
|
|
107
|
-
"grep_ast":
|
|
138
|
+
"grep_ast": SymbolsTool, # Using correct import name
|
|
139
|
+
"git_search": GitSearchTool,
|
|
108
140
|
"content_replace": ContentReplaceTool,
|
|
141
|
+
"batch_search": BatchSearchTool,
|
|
142
|
+
"find_files": FindFilesTool,
|
|
109
143
|
"unified_search": UnifiedSearchTool,
|
|
144
|
+
"watch": lambda pm: watch_tool, # Singleton instance
|
|
145
|
+
"diff": create_diff_tool,
|
|
110
146
|
}
|
|
111
147
|
|
|
112
148
|
tools = []
|
|
@@ -116,9 +152,12 @@ def register_filesystem_tools(
|
|
|
116
152
|
for tool_name, enabled in enabled_tools.items():
|
|
117
153
|
if enabled and tool_name in tool_classes:
|
|
118
154
|
tool_class = tool_classes[tool_name]
|
|
119
|
-
if tool_name
|
|
120
|
-
#
|
|
155
|
+
if tool_name in ["batch_search", "unified_search"]:
|
|
156
|
+
# Batch search and unified search require project_manager
|
|
121
157
|
tools.append(tool_class(permission_manager, project_manager))
|
|
158
|
+
elif tool_name == "watch":
|
|
159
|
+
# Watch tool is a singleton
|
|
160
|
+
tools.append(tool_class(permission_manager))
|
|
122
161
|
else:
|
|
123
162
|
tools.append(tool_class(permission_manager))
|
|
124
163
|
else:
|
|
@@ -131,7 +170,7 @@ def register_filesystem_tools(
|
|
|
131
170
|
]
|
|
132
171
|
elif disable_write_tools:
|
|
133
172
|
# Read-only tools including search
|
|
134
|
-
tools = get_read_only_filesystem_tools(permission_manager)
|
|
173
|
+
tools = get_read_only_filesystem_tools(permission_manager, project_manager)
|
|
135
174
|
elif disable_search_tools:
|
|
136
175
|
# Write tools but no search
|
|
137
176
|
tools = [
|
|
@@ -144,7 +183,7 @@ def register_filesystem_tools(
|
|
|
144
183
|
]
|
|
145
184
|
else:
|
|
146
185
|
# All tools
|
|
147
|
-
tools = get_filesystem_tools(permission_manager)
|
|
186
|
+
tools = get_filesystem_tools(permission_manager, project_manager)
|
|
148
187
|
|
|
149
188
|
ToolRegistry.register_tools(mcp_server, tools)
|
|
150
189
|
return tools
|
|
@@ -8,7 +8,7 @@ from abc import ABC
|
|
|
8
8
|
from pathlib import Path
|
|
9
9
|
from typing import Any
|
|
10
10
|
|
|
11
|
-
from fastmcp import Context as MCPContext
|
|
11
|
+
from mcp.server.fastmcp import Context as MCPContext
|
|
12
12
|
|
|
13
13
|
from hanzo_mcp.tools.common.base import FileSystemTool
|
|
14
14
|
from hanzo_mcp.tools.common.context import ToolContext, create_tool_context
|