hanzo-mcp 0.5.2__py3-none-any.whl → 0.6.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of hanzo-mcp might be problematic. Click here for more details.
- 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 +66 -35
- 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 +2 -2
- 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 +1 -1
- hanzo_mcp/tools/common/thinking_tool.py +3 -5
- hanzo_mcp/tools/common/tool_disable.py +1 -1
- hanzo_mcp/tools/common/tool_enable.py +1 -1
- hanzo_mcp/tools/common/tool_list.py +49 -52
- 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 +1 -1
- hanzo_mcp/tools/database/graph.py +482 -0
- hanzo_mcp/tools/database/graph_add.py +1 -1
- hanzo_mcp/tools/database/graph_query.py +1 -1
- hanzo_mcp/tools/database/graph_remove.py +1 -1
- hanzo_mcp/tools/database/graph_search.py +1 -1
- hanzo_mcp/tools/database/graph_stats.py +1 -1
- hanzo_mcp/tools/database/sql.py +411 -0
- hanzo_mcp/tools/database/sql_query.py +1 -1
- hanzo_mcp/tools/database/sql_search.py +1 -1
- hanzo_mcp/tools/database/sql_stats.py +1 -1
- hanzo_mcp/tools/editor/neovim_command.py +1 -1
- hanzo_mcp/tools/editor/neovim_edit.py +1 -1
- hanzo_mcp/tools/editor/neovim_session.py +1 -1
- hanzo_mcp/tools/filesystem/__init__.py +42 -13
- hanzo_mcp/tools/filesystem/base.py +1 -1
- hanzo_mcp/tools/filesystem/batch_search.py +4 -4
- 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 +1 -1
- hanzo_mcp/tools/filesystem/git_search.py +1 -1
- 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 +711 -0
- 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 +4 -0
- hanzo_mcp/tools/llm/consensus_tool.py +1 -1
- hanzo_mcp/tools/llm/llm_manage.py +1 -1
- hanzo_mcp/tools/llm/llm_tool.py +1 -1
- hanzo_mcp/tools/llm/llm_unified.py +851 -0
- hanzo_mcp/tools/llm/provider_tools.py +1 -1
- hanzo_mcp/tools/mcp/__init__.py +4 -0
- hanzo_mcp/tools/mcp/mcp_add.py +1 -1
- hanzo_mcp/tools/mcp/mcp_remove.py +1 -1
- hanzo_mcp/tools/mcp/mcp_stats.py +1 -1
- hanzo_mcp/tools/mcp/mcp_unified.py +503 -0
- hanzo_mcp/tools/shell/__init__.py +20 -42
- 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 +1 -1
- hanzo_mcp/tools/shell/npx.py +1 -1
- hanzo_mcp/tools/shell/npx_background.py +1 -1
- hanzo_mcp/tools/shell/npx_unified.py +101 -0
- hanzo_mcp/tools/shell/open.py +107 -0
- hanzo_mcp/tools/shell/pkill.py +1 -1
- hanzo_mcp/tools/shell/process_unified.py +131 -0
- hanzo_mcp/tools/shell/processes.py +1 -1
- hanzo_mcp/tools/shell/run_background.py +1 -1
- 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 +1 -1
- hanzo_mcp/tools/shell/uvx_background.py +1 -1
- 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 +1 -1
- hanzo_mcp/tools/vector/index_tool.py +1 -1
- 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 +1 -1
- hanzo_mcp-0.6.2.dist-info/METADATA +336 -0
- hanzo_mcp-0.6.2.dist-info/RECORD +134 -0
- hanzo_mcp-0.6.2.dist-info/entry_points.txt +3 -0
- hanzo_mcp-0.5.2.dist-info/METADATA +0 -276
- hanzo_mcp-0.5.2.dist-info/RECORD +0 -106
- hanzo_mcp-0.5.2.dist-info/entry_points.txt +0 -2
- {hanzo_mcp-0.5.2.dist-info → hanzo_mcp-0.6.2.dist-info}/WHEEL +0 -0
- {hanzo_mcp-0.5.2.dist-info → hanzo_mcp-0.6.2.dist-info}/licenses/LICENSE +0 -0
- {hanzo_mcp-0.5.2.dist-info → hanzo_mcp-0.6.2.dist-info}/top_level.txt +0 -0
|
@@ -8,6 +8,7 @@ import asyncio
|
|
|
8
8
|
from concurrent.futures import ThreadPoolExecutor
|
|
9
9
|
|
|
10
10
|
from .infinity_store import InfinityVectorStore, SearchResult
|
|
11
|
+
from hanzo_mcp.tools.config.index_config import IndexConfig, IndexScope
|
|
11
12
|
|
|
12
13
|
|
|
13
14
|
@dataclass
|
|
@@ -38,12 +39,14 @@ class ProjectVectorManager:
|
|
|
38
39
|
self.embedding_model = embedding_model
|
|
39
40
|
self.dimension = dimension
|
|
40
41
|
|
|
42
|
+
# Set up index configuration
|
|
43
|
+
self.index_config = IndexConfig()
|
|
44
|
+
|
|
41
45
|
# Set up global database path
|
|
42
46
|
if global_db_path:
|
|
43
47
|
self.global_db_path = Path(global_db_path)
|
|
44
48
|
else:
|
|
45
|
-
|
|
46
|
-
self.global_db_path = get_config_dir() / "db"
|
|
49
|
+
self.global_db_path = self.index_config.get_index_path("vector")
|
|
47
50
|
|
|
48
51
|
self.global_db_path.mkdir(parents=True, exist_ok=True)
|
|
49
52
|
|
|
@@ -158,14 +161,25 @@ class ProjectVectorManager:
|
|
|
158
161
|
Returns:
|
|
159
162
|
Vector store instance
|
|
160
163
|
"""
|
|
161
|
-
|
|
164
|
+
# Check indexing scope
|
|
165
|
+
if project_info:
|
|
166
|
+
scope = self.index_config.get_scope(str(project_info.root_path))
|
|
167
|
+
if scope == IndexScope.GLOBAL:
|
|
168
|
+
# Even for project files, use global store if configured
|
|
169
|
+
return self._get_global_store()
|
|
170
|
+
else:
|
|
162
171
|
return self._get_global_store()
|
|
163
172
|
|
|
173
|
+
# Use project-specific store
|
|
164
174
|
project_key = str(project_info.root_path)
|
|
165
175
|
|
|
166
176
|
if project_key not in self.vector_stores:
|
|
177
|
+
# Get index path based on configuration
|
|
178
|
+
index_path = self.index_config.get_index_path("vector", str(project_info.root_path))
|
|
179
|
+
index_path.mkdir(parents=True, exist_ok=True)
|
|
180
|
+
|
|
167
181
|
self.vector_stores[project_key] = InfinityVectorStore(
|
|
168
|
-
data_path=str(
|
|
182
|
+
data_path=str(index_path),
|
|
169
183
|
embedding_model=self.embedding_model,
|
|
170
184
|
dimension=self.dimension,
|
|
171
185
|
)
|
|
@@ -190,10 +204,14 @@ class ProjectVectorManager:
|
|
|
190
204
|
Returns:
|
|
191
205
|
Tuple of (document IDs, project info or None for global)
|
|
192
206
|
"""
|
|
207
|
+
# Check if indexing is enabled
|
|
208
|
+
if not self.index_config.is_indexing_enabled("vector"):
|
|
209
|
+
return [], None
|
|
210
|
+
|
|
193
211
|
# Find project for this file
|
|
194
212
|
project_info = self.get_project_for_path(file_path)
|
|
195
213
|
|
|
196
|
-
# Get appropriate vector store
|
|
214
|
+
# Get appropriate vector store based on scope configuration
|
|
197
215
|
vector_store = self.get_vector_store(project_info)
|
|
198
216
|
|
|
199
217
|
# Add file metadata
|
|
@@ -201,8 +219,12 @@ class ProjectVectorManager:
|
|
|
201
219
|
if project_info:
|
|
202
220
|
file_metadata["project_name"] = project_info.name
|
|
203
221
|
file_metadata["project_root"] = str(project_info.root_path)
|
|
222
|
+
# Check actual scope used
|
|
223
|
+
scope = self.index_config.get_scope(str(project_info.root_path))
|
|
224
|
+
file_metadata["index_scope"] = scope.value
|
|
204
225
|
else:
|
|
205
226
|
file_metadata["project_name"] = "global"
|
|
227
|
+
file_metadata["index_scope"] = "global"
|
|
206
228
|
|
|
207
229
|
# Add file to store
|
|
208
230
|
doc_ids = vector_store.add_file(
|
|
@@ -0,0 +1,311 @@
|
|
|
1
|
+
"""Unified vector store tool."""
|
|
2
|
+
|
|
3
|
+
from typing import Annotated, TypedDict, Unpack, final, override, Optional, List, Dict, Any
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
from mcp.server.fastmcp import Context as MCPContext
|
|
7
|
+
from pydantic import Field
|
|
8
|
+
|
|
9
|
+
from hanzo_mcp.tools.common.base import BaseTool
|
|
10
|
+
from hanzo_mcp.tools.common.permissions import PermissionManager
|
|
11
|
+
from hanzo_mcp.tools.vector.project_manager import ProjectVectorManager
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
# Parameter types
|
|
15
|
+
Action = Annotated[
|
|
16
|
+
str,
|
|
17
|
+
Field(
|
|
18
|
+
description="Action: search (default), index, stats, clear",
|
|
19
|
+
default="search",
|
|
20
|
+
),
|
|
21
|
+
]
|
|
22
|
+
|
|
23
|
+
Query = Annotated[
|
|
24
|
+
Optional[str],
|
|
25
|
+
Field(
|
|
26
|
+
description="Search query for semantic similarity",
|
|
27
|
+
default=None,
|
|
28
|
+
),
|
|
29
|
+
]
|
|
30
|
+
|
|
31
|
+
Path = Annotated[
|
|
32
|
+
Optional[str],
|
|
33
|
+
Field(
|
|
34
|
+
description="Path to index or search within",
|
|
35
|
+
default=".",
|
|
36
|
+
),
|
|
37
|
+
]
|
|
38
|
+
|
|
39
|
+
Include = Annotated[
|
|
40
|
+
Optional[str],
|
|
41
|
+
Field(
|
|
42
|
+
description="File pattern to include (e.g., '*.py')",
|
|
43
|
+
default=None,
|
|
44
|
+
),
|
|
45
|
+
]
|
|
46
|
+
|
|
47
|
+
Exclude = Annotated[
|
|
48
|
+
Optional[str],
|
|
49
|
+
Field(
|
|
50
|
+
description="File pattern to exclude",
|
|
51
|
+
default=None,
|
|
52
|
+
),
|
|
53
|
+
]
|
|
54
|
+
|
|
55
|
+
Limit = Annotated[
|
|
56
|
+
int,
|
|
57
|
+
Field(
|
|
58
|
+
description="Maximum results to return",
|
|
59
|
+
default=10,
|
|
60
|
+
),
|
|
61
|
+
]
|
|
62
|
+
|
|
63
|
+
IncludeGit = Annotated[
|
|
64
|
+
bool,
|
|
65
|
+
Field(
|
|
66
|
+
description="Include git history in indexing",
|
|
67
|
+
default=True,
|
|
68
|
+
),
|
|
69
|
+
]
|
|
70
|
+
|
|
71
|
+
ForceReindex = Annotated[
|
|
72
|
+
bool,
|
|
73
|
+
Field(
|
|
74
|
+
description="Force reindexing even if up to date",
|
|
75
|
+
default=False,
|
|
76
|
+
),
|
|
77
|
+
]
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
class VectorParams(TypedDict, total=False):
|
|
81
|
+
"""Parameters for vector tool."""
|
|
82
|
+
action: str
|
|
83
|
+
query: Optional[str]
|
|
84
|
+
path: Optional[str]
|
|
85
|
+
include: Optional[str]
|
|
86
|
+
exclude: Optional[str]
|
|
87
|
+
limit: int
|
|
88
|
+
include_git: bool
|
|
89
|
+
force_reindex: bool
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
@final
|
|
93
|
+
class VectorTool(BaseTool):
|
|
94
|
+
"""Unified vector store tool for semantic search."""
|
|
95
|
+
|
|
96
|
+
def __init__(self, permission_manager: PermissionManager, project_manager: ProjectVectorManager):
|
|
97
|
+
"""Initialize the vector tool."""
|
|
98
|
+
super().__init__(permission_manager)
|
|
99
|
+
self.project_manager = project_manager
|
|
100
|
+
|
|
101
|
+
@property
|
|
102
|
+
@override
|
|
103
|
+
def name(self) -> str:
|
|
104
|
+
"""Get the tool name."""
|
|
105
|
+
return "vector"
|
|
106
|
+
|
|
107
|
+
@property
|
|
108
|
+
@override
|
|
109
|
+
def description(self) -> str:
|
|
110
|
+
"""Get the tool description."""
|
|
111
|
+
return """Semantic search with embeddings. Actions: search (default), index, stats, clear.
|
|
112
|
+
|
|
113
|
+
Usage:
|
|
114
|
+
vector "find authentication logic"
|
|
115
|
+
vector --action index --path ./src --include "*.py"
|
|
116
|
+
vector --action stats
|
|
117
|
+
vector --action clear --path ./old_code
|
|
118
|
+
"""
|
|
119
|
+
|
|
120
|
+
@override
|
|
121
|
+
async def call(
|
|
122
|
+
self,
|
|
123
|
+
ctx: MCPContext,
|
|
124
|
+
**params: Unpack[VectorParams],
|
|
125
|
+
) -> str:
|
|
126
|
+
"""Execute vector operation."""
|
|
127
|
+
tool_ctx = self.create_tool_context(ctx)
|
|
128
|
+
|
|
129
|
+
# Extract action
|
|
130
|
+
action = params.get("action", "search")
|
|
131
|
+
|
|
132
|
+
# Route to appropriate handler
|
|
133
|
+
if action == "search":
|
|
134
|
+
return await self._handle_search(params, tool_ctx)
|
|
135
|
+
elif action == "index":
|
|
136
|
+
return await self._handle_index(params, tool_ctx)
|
|
137
|
+
elif action == "stats":
|
|
138
|
+
return await self._handle_stats(params, tool_ctx)
|
|
139
|
+
elif action == "clear":
|
|
140
|
+
return await self._handle_clear(params, tool_ctx)
|
|
141
|
+
else:
|
|
142
|
+
return f"Error: Unknown action '{action}'. Valid actions: search, index, stats, clear"
|
|
143
|
+
|
|
144
|
+
async def _handle_search(self, params: Dict[str, Any], tool_ctx) -> str:
|
|
145
|
+
"""Handle semantic search."""
|
|
146
|
+
query = params.get("query")
|
|
147
|
+
if not query:
|
|
148
|
+
return "Error: query is required for search action"
|
|
149
|
+
|
|
150
|
+
path = params.get("path", ".")
|
|
151
|
+
limit = params.get("limit", 10)
|
|
152
|
+
|
|
153
|
+
# Validate path
|
|
154
|
+
allowed, error_msg = await self.check_path_allowed(path, tool_ctx)
|
|
155
|
+
if not allowed:
|
|
156
|
+
return error_msg
|
|
157
|
+
|
|
158
|
+
try:
|
|
159
|
+
# Determine search scope
|
|
160
|
+
project = self.project_manager.get_project_for_path(path)
|
|
161
|
+
if not project:
|
|
162
|
+
return "Error: No indexed project found for this path. Run 'vector --action index' first."
|
|
163
|
+
|
|
164
|
+
# Search
|
|
165
|
+
await tool_ctx.info(f"Searching for: {query}")
|
|
166
|
+
results = project.search(query, k=limit)
|
|
167
|
+
|
|
168
|
+
if not results:
|
|
169
|
+
return f"No results found for: {query}"
|
|
170
|
+
|
|
171
|
+
# Format results
|
|
172
|
+
output = [f"=== Vector Search Results for '{query}' ==="]
|
|
173
|
+
output.append(f"Found {len(results)} matches\n")
|
|
174
|
+
|
|
175
|
+
for i, result in enumerate(results, 1):
|
|
176
|
+
score = result.get("score", 0)
|
|
177
|
+
file_path = result.get("file_path", "unknown")
|
|
178
|
+
content = result.get("content", "")
|
|
179
|
+
chunk_type = result.get("metadata", {}).get("type", "content")
|
|
180
|
+
|
|
181
|
+
output.append(f"Result {i} - Score: {score:.1%}")
|
|
182
|
+
output.append(f"File: {file_path}")
|
|
183
|
+
if chunk_type != "content":
|
|
184
|
+
output.append(f"Type: {chunk_type}")
|
|
185
|
+
output.append("-" * 60)
|
|
186
|
+
|
|
187
|
+
# Truncate content if too long
|
|
188
|
+
if len(content) > 300:
|
|
189
|
+
content = content[:300] + "..."
|
|
190
|
+
output.append(content)
|
|
191
|
+
output.append("")
|
|
192
|
+
|
|
193
|
+
return "\n".join(output)
|
|
194
|
+
|
|
195
|
+
except Exception as e:
|
|
196
|
+
await tool_ctx.error(f"Search failed: {str(e)}")
|
|
197
|
+
return f"Error during search: {str(e)}"
|
|
198
|
+
|
|
199
|
+
async def _handle_index(self, params: Dict[str, Any], tool_ctx) -> str:
|
|
200
|
+
"""Handle indexing files."""
|
|
201
|
+
path = params.get("path", ".")
|
|
202
|
+
include = params.get("include")
|
|
203
|
+
exclude = params.get("exclude")
|
|
204
|
+
include_git = params.get("include_git", True)
|
|
205
|
+
force = params.get("force_reindex", False)
|
|
206
|
+
|
|
207
|
+
# Validate path
|
|
208
|
+
allowed, error_msg = await self.check_path_allowed(path, tool_ctx)
|
|
209
|
+
if not allowed:
|
|
210
|
+
return error_msg
|
|
211
|
+
|
|
212
|
+
try:
|
|
213
|
+
await tool_ctx.info(f"Indexing {path}...")
|
|
214
|
+
|
|
215
|
+
# Get or create project
|
|
216
|
+
project = self.project_manager.get_or_create_project(path)
|
|
217
|
+
|
|
218
|
+
# Index files
|
|
219
|
+
stats = await project.index_directory(
|
|
220
|
+
path,
|
|
221
|
+
include_pattern=include,
|
|
222
|
+
exclude_pattern=exclude,
|
|
223
|
+
force_reindex=force
|
|
224
|
+
)
|
|
225
|
+
|
|
226
|
+
# Index git history if requested
|
|
227
|
+
if include_git and Path(path).joinpath(".git").exists():
|
|
228
|
+
await tool_ctx.info("Indexing git history...")
|
|
229
|
+
git_stats = await project.index_git_history(path)
|
|
230
|
+
stats["git_commits"] = git_stats.get("commits_indexed", 0)
|
|
231
|
+
|
|
232
|
+
# Format output
|
|
233
|
+
output = [f"=== Indexing Complete ==="]
|
|
234
|
+
output.append(f"Path: {path}")
|
|
235
|
+
output.append(f"Files indexed: {stats.get('files_indexed', 0)}")
|
|
236
|
+
output.append(f"Chunks created: {stats.get('chunks_created', 0)}")
|
|
237
|
+
if stats.get("git_commits"):
|
|
238
|
+
output.append(f"Git commits indexed: {stats['git_commits']}")
|
|
239
|
+
output.append(f"Total documents: {project.get_stats().get('total_documents', 0)}")
|
|
240
|
+
|
|
241
|
+
return "\n".join(output)
|
|
242
|
+
|
|
243
|
+
except Exception as e:
|
|
244
|
+
await tool_ctx.error(f"Indexing failed: {str(e)}")
|
|
245
|
+
return f"Error during indexing: {str(e)}"
|
|
246
|
+
|
|
247
|
+
async def _handle_stats(self, params: Dict[str, Any], tool_ctx) -> str:
|
|
248
|
+
"""Get vector store statistics."""
|
|
249
|
+
path = params.get("path")
|
|
250
|
+
|
|
251
|
+
try:
|
|
252
|
+
if path:
|
|
253
|
+
# Stats for specific project
|
|
254
|
+
project = self.project_manager.get_project_for_path(path)
|
|
255
|
+
if not project:
|
|
256
|
+
return f"No indexed project found for path: {path}"
|
|
257
|
+
|
|
258
|
+
stats = project.get_stats()
|
|
259
|
+
output = [f"=== Vector Store Stats for {project.name} ==="]
|
|
260
|
+
else:
|
|
261
|
+
# Global stats
|
|
262
|
+
stats = self.project_manager.get_global_stats()
|
|
263
|
+
output = ["=== Global Vector Store Stats ==="]
|
|
264
|
+
|
|
265
|
+
output.append(f"Total documents: {stats.get('total_documents', 0)}")
|
|
266
|
+
output.append(f"Total size: {stats.get('total_size_mb', 0):.1f} MB")
|
|
267
|
+
|
|
268
|
+
if stats.get("projects"):
|
|
269
|
+
output.append(f"\nProjects indexed: {len(stats['projects'])}")
|
|
270
|
+
for proj in stats["projects"]:
|
|
271
|
+
output.append(f" - {proj['name']}: {proj['documents']} docs, {proj['size_mb']:.1f} MB")
|
|
272
|
+
|
|
273
|
+
return "\n".join(output)
|
|
274
|
+
|
|
275
|
+
except Exception as e:
|
|
276
|
+
await tool_ctx.error(f"Failed to get stats: {str(e)}")
|
|
277
|
+
return f"Error getting stats: {str(e)}"
|
|
278
|
+
|
|
279
|
+
async def _handle_clear(self, params: Dict[str, Any], tool_ctx) -> str:
|
|
280
|
+
"""Clear vector store."""
|
|
281
|
+
path = params.get("path")
|
|
282
|
+
|
|
283
|
+
if not path:
|
|
284
|
+
return "Error: path is required for clear action"
|
|
285
|
+
|
|
286
|
+
# Validate path
|
|
287
|
+
allowed, error_msg = await self.check_path_allowed(path, tool_ctx)
|
|
288
|
+
if not allowed:
|
|
289
|
+
return error_msg
|
|
290
|
+
|
|
291
|
+
try:
|
|
292
|
+
project = self.project_manager.get_project_for_path(path)
|
|
293
|
+
if not project:
|
|
294
|
+
return f"No indexed project found for path: {path}"
|
|
295
|
+
|
|
296
|
+
# Get stats before clearing
|
|
297
|
+
stats = project.get_stats()
|
|
298
|
+
doc_count = stats.get("total_documents", 0)
|
|
299
|
+
|
|
300
|
+
# Clear
|
|
301
|
+
project.clear()
|
|
302
|
+
|
|
303
|
+
return f"Cleared {doc_count} documents from vector store for {project.name}"
|
|
304
|
+
|
|
305
|
+
except Exception as e:
|
|
306
|
+
await tool_ctx.error(f"Failed to clear: {str(e)}")
|
|
307
|
+
return f"Error clearing vector store: {str(e)}"
|
|
308
|
+
|
|
309
|
+
def register(self, mcp_server) -> None:
|
|
310
|
+
"""Register this tool with the MCP server."""
|
|
311
|
+
pass
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
from typing import Dict, List, Optional, TypedDict, Unpack, final
|
|
4
4
|
from pathlib import Path
|
|
5
5
|
|
|
6
|
-
from fastmcp import Context as MCPContext
|
|
6
|
+
from mcp.server.fastmcp import Context as MCPContext
|
|
7
7
|
from pydantic import Field
|
|
8
8
|
|
|
9
9
|
from hanzo_mcp.tools.common.base import BaseTool
|
|
@@ -4,7 +4,7 @@ from typing import Dict, List, Optional, TypedDict, Unpack, final
|
|
|
4
4
|
import json
|
|
5
5
|
import asyncio
|
|
6
6
|
|
|
7
|
-
from fastmcp import Context as MCPContext
|
|
7
|
+
from mcp.server.fastmcp import Context as MCPContext
|
|
8
8
|
from pydantic import Field
|
|
9
9
|
|
|
10
10
|
from hanzo_mcp.tools.common.base import BaseTool
|