hanzo-mcp 0.5.1__py3-none-any.whl → 0.5.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/tools/__init__.py +135 -4
- hanzo_mcp/tools/common/base.py +7 -2
- hanzo_mcp/tools/common/stats.py +261 -0
- 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 +263 -0
- hanzo_mcp/tools/database/__init__.py +71 -0
- hanzo_mcp/tools/database/database_manager.py +246 -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_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 +15 -5
- hanzo_mcp/tools/filesystem/{unified_search.py → batch_search.py} +254 -131
- hanzo_mcp/tools/filesystem/find_files.py +348 -0
- hanzo_mcp/tools/filesystem/git_search.py +505 -0
- hanzo_mcp/tools/llm/__init__.py +27 -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/provider_tools.py +412 -0
- hanzo_mcp/tools/mcp/__init__.py +11 -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/shell/__init__.py +27 -7
- 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/pkill.py +262 -0
- hanzo_mcp/tools/shell/processes.py +279 -0
- hanzo_mcp/tools/shell/run_background.py +326 -0
- hanzo_mcp/tools/shell/uvx.py +187 -0
- hanzo_mcp/tools/shell/uvx_background.py +249 -0
- hanzo_mcp/tools/vector/__init__.py +5 -0
- 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/vector_search.py +11 -6
- {hanzo_mcp-0.5.1.dist-info → hanzo_mcp-0.5.2.dist-info}/METADATA +1 -1
- {hanzo_mcp-0.5.1.dist-info → hanzo_mcp-0.5.2.dist-info}/RECORD +54 -16
- {hanzo_mcp-0.5.1.dist-info → hanzo_mcp-0.5.2.dist-info}/WHEEL +0 -0
- {hanzo_mcp-0.5.1.dist-info → hanzo_mcp-0.5.2.dist-info}/entry_points.txt +0 -0
- {hanzo_mcp-0.5.1.dist-info → hanzo_mcp-0.5.2.dist-info}/licenses/LICENSE +0 -0
- {hanzo_mcp-0.5.1.dist-info → hanzo_mcp-0.5.2.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,358 @@
|
|
|
1
|
+
"""Index tool for managing vector store indexing."""
|
|
2
|
+
|
|
3
|
+
import asyncio
|
|
4
|
+
import os
|
|
5
|
+
import time
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from typing import Annotated, TypedDict, Unpack, final, override
|
|
8
|
+
|
|
9
|
+
from fastmcp import Context as MCPContext
|
|
10
|
+
from pydantic import Field
|
|
11
|
+
|
|
12
|
+
from hanzo_mcp.tools.common.base import BaseTool
|
|
13
|
+
from hanzo_mcp.tools.common.context import create_tool_context
|
|
14
|
+
from hanzo_mcp.tools.common.permissions import PermissionManager
|
|
15
|
+
from hanzo_mcp.tools.vector.git_ingester import GitIngester
|
|
16
|
+
from hanzo_mcp.tools.vector.infinity_store import InfinityVectorStore
|
|
17
|
+
from hanzo_mcp.tools.vector.project_manager import ProjectVectorManager
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
Path_str = Annotated[
|
|
21
|
+
str,
|
|
22
|
+
Field(
|
|
23
|
+
description="Path to index (defaults to current working directory)",
|
|
24
|
+
min_length=1,
|
|
25
|
+
),
|
|
26
|
+
]
|
|
27
|
+
|
|
28
|
+
IncludeGitHistory = Annotated[
|
|
29
|
+
bool,
|
|
30
|
+
Field(
|
|
31
|
+
description="Include git history in the index",
|
|
32
|
+
default=True,
|
|
33
|
+
),
|
|
34
|
+
]
|
|
35
|
+
|
|
36
|
+
FilePatterns = Annotated[
|
|
37
|
+
list[str] | None,
|
|
38
|
+
Field(
|
|
39
|
+
description="File patterns to include (e.g., ['*.py', '*.js'])",
|
|
40
|
+
default=None,
|
|
41
|
+
),
|
|
42
|
+
]
|
|
43
|
+
|
|
44
|
+
ShowStats = Annotated[
|
|
45
|
+
bool,
|
|
46
|
+
Field(
|
|
47
|
+
description="Show detailed statistics after indexing",
|
|
48
|
+
default=True,
|
|
49
|
+
),
|
|
50
|
+
]
|
|
51
|
+
|
|
52
|
+
Force = Annotated[
|
|
53
|
+
bool,
|
|
54
|
+
Field(
|
|
55
|
+
description="Force re-indexing even if already indexed",
|
|
56
|
+
default=False,
|
|
57
|
+
),
|
|
58
|
+
]
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
class IndexToolParams(TypedDict, total=False):
|
|
62
|
+
"""Parameters for the index tool."""
|
|
63
|
+
|
|
64
|
+
path: str
|
|
65
|
+
include_git_history: bool
|
|
66
|
+
file_patterns: list[str] | None
|
|
67
|
+
show_stats: bool
|
|
68
|
+
force: bool
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
@final
|
|
72
|
+
class IndexTool(BaseTool):
|
|
73
|
+
"""Tool for indexing files and git history into vector store."""
|
|
74
|
+
|
|
75
|
+
def __init__(self, permission_manager: PermissionManager):
|
|
76
|
+
"""Initialize the index tool.
|
|
77
|
+
|
|
78
|
+
Args:
|
|
79
|
+
permission_manager: Permission manager for access control
|
|
80
|
+
"""
|
|
81
|
+
self.permission_manager = permission_manager
|
|
82
|
+
self.project_manager = ProjectVectorManager(permission_manager)
|
|
83
|
+
|
|
84
|
+
@property
|
|
85
|
+
@override
|
|
86
|
+
def name(self) -> str:
|
|
87
|
+
"""Get the tool name."""
|
|
88
|
+
return "index"
|
|
89
|
+
|
|
90
|
+
@property
|
|
91
|
+
@override
|
|
92
|
+
def description(self) -> str:
|
|
93
|
+
"""Get the tool description."""
|
|
94
|
+
return """Index files and git history into the vector store for semantic search.
|
|
95
|
+
|
|
96
|
+
This tool:
|
|
97
|
+
- Indexes all project files into a vector database
|
|
98
|
+
- Includes git history (commits, diffs, blame) when available
|
|
99
|
+
- Supports incremental updates
|
|
100
|
+
- Shows statistics about indexed content
|
|
101
|
+
- Automatically creates project-specific databases
|
|
102
|
+
|
|
103
|
+
Usage:
|
|
104
|
+
- index: Index the current directory
|
|
105
|
+
- index --path /path/to/project: Index a specific path
|
|
106
|
+
- index --file-patterns "*.py" "*.js": Index only specific file types
|
|
107
|
+
- index --no-git-history: Skip git history indexing
|
|
108
|
+
- index --force: Force re-indexing of all files"""
|
|
109
|
+
|
|
110
|
+
@override
|
|
111
|
+
async def call(
|
|
112
|
+
self,
|
|
113
|
+
ctx: MCPContext,
|
|
114
|
+
**params: Unpack[IndexToolParams],
|
|
115
|
+
) -> str:
|
|
116
|
+
"""Execute the index tool.
|
|
117
|
+
|
|
118
|
+
Args:
|
|
119
|
+
ctx: MCP context
|
|
120
|
+
**params: Tool parameters
|
|
121
|
+
|
|
122
|
+
Returns:
|
|
123
|
+
Indexing result and statistics
|
|
124
|
+
"""
|
|
125
|
+
start_time = time.time()
|
|
126
|
+
tool_ctx = create_tool_context(ctx)
|
|
127
|
+
await tool_ctx.set_tool_info(self.name)
|
|
128
|
+
|
|
129
|
+
# Extract parameters
|
|
130
|
+
path = params.get("path", os.getcwd())
|
|
131
|
+
include_git_history = params.get("include_git_history", True)
|
|
132
|
+
file_patterns = params.get("file_patterns")
|
|
133
|
+
show_stats = params.get("show_stats", True)
|
|
134
|
+
force = params.get("force", False)
|
|
135
|
+
|
|
136
|
+
# Resolve absolute path
|
|
137
|
+
abs_path = os.path.abspath(path)
|
|
138
|
+
|
|
139
|
+
# Check permissions
|
|
140
|
+
if not self.permission_manager.has_permission(abs_path):
|
|
141
|
+
return f"Permission denied: {abs_path}"
|
|
142
|
+
|
|
143
|
+
# Check if path exists
|
|
144
|
+
if not os.path.exists(abs_path):
|
|
145
|
+
return f"Path does not exist: {abs_path}"
|
|
146
|
+
|
|
147
|
+
await tool_ctx.info(f"Starting indexing of {abs_path}")
|
|
148
|
+
|
|
149
|
+
try:
|
|
150
|
+
# Get or create vector store for this project
|
|
151
|
+
vector_store = self.project_manager.get_project_store(abs_path)
|
|
152
|
+
|
|
153
|
+
# Check if already indexed (unless force)
|
|
154
|
+
if not force:
|
|
155
|
+
stats = await vector_store.get_stats()
|
|
156
|
+
if stats and stats.get("document_count", 0) > 0:
|
|
157
|
+
await tool_ctx.info("Project already indexed, use --force to re-index")
|
|
158
|
+
if show_stats:
|
|
159
|
+
return self._format_stats(stats, abs_path, time.time() - start_time)
|
|
160
|
+
return "Project is already indexed. Use --force to re-index."
|
|
161
|
+
|
|
162
|
+
# Prepare file patterns
|
|
163
|
+
if file_patterns is None:
|
|
164
|
+
# Default patterns for code files
|
|
165
|
+
file_patterns = [
|
|
166
|
+
"*.py", "*.js", "*.ts", "*.jsx", "*.tsx",
|
|
167
|
+
"*.java", "*.cpp", "*.c", "*.h", "*.hpp",
|
|
168
|
+
"*.go", "*.rs", "*.rb", "*.php", "*.swift",
|
|
169
|
+
"*.kt", "*.scala", "*.cs", "*.vb", "*.fs",
|
|
170
|
+
"*.sh", "*.bash", "*.zsh", "*.fish",
|
|
171
|
+
"*.md", "*.rst", "*.txt", "*.json", "*.yaml", "*.yml",
|
|
172
|
+
"*.toml", "*.ini", "*.cfg", "*.conf",
|
|
173
|
+
"*.html", "*.css", "*.scss", "*.sass", "*.less",
|
|
174
|
+
"*.sql", "*.graphql", "*.proto",
|
|
175
|
+
"Dockerfile", "Makefile", "*.mk",
|
|
176
|
+
".gitignore", ".dockerignore", "requirements.txt",
|
|
177
|
+
"package.json", "Cargo.toml", "go.mod", "pom.xml",
|
|
178
|
+
]
|
|
179
|
+
|
|
180
|
+
# Clear existing index if force
|
|
181
|
+
if force:
|
|
182
|
+
await tool_ctx.info("Clearing existing index...")
|
|
183
|
+
await vector_store.clear()
|
|
184
|
+
|
|
185
|
+
# Index files
|
|
186
|
+
await tool_ctx.info("Indexing files...")
|
|
187
|
+
indexed_files = 0
|
|
188
|
+
total_size = 0
|
|
189
|
+
errors = []
|
|
190
|
+
|
|
191
|
+
for pattern in file_patterns:
|
|
192
|
+
pattern_files = await self._find_files(abs_path, pattern)
|
|
193
|
+
for file_path in pattern_files:
|
|
194
|
+
try:
|
|
195
|
+
# Check file size (skip very large files)
|
|
196
|
+
file_size = os.path.getsize(file_path)
|
|
197
|
+
if file_size > 10 * 1024 * 1024: # 10MB
|
|
198
|
+
await tool_ctx.warning(f"Skipping large file: {file_path}")
|
|
199
|
+
continue
|
|
200
|
+
|
|
201
|
+
# Read file content
|
|
202
|
+
try:
|
|
203
|
+
with open(file_path, "r", encoding="utf-8") as f:
|
|
204
|
+
content = f.read()
|
|
205
|
+
except UnicodeDecodeError:
|
|
206
|
+
# Skip binary files
|
|
207
|
+
continue
|
|
208
|
+
|
|
209
|
+
# Index the file
|
|
210
|
+
rel_path = os.path.relpath(file_path, abs_path)
|
|
211
|
+
await vector_store.index_document(
|
|
212
|
+
content=content,
|
|
213
|
+
metadata={
|
|
214
|
+
"type": "file",
|
|
215
|
+
"path": rel_path,
|
|
216
|
+
"absolute_path": file_path,
|
|
217
|
+
"size": file_size,
|
|
218
|
+
"extension": Path(file_path).suffix,
|
|
219
|
+
}
|
|
220
|
+
)
|
|
221
|
+
indexed_files += 1
|
|
222
|
+
total_size += file_size
|
|
223
|
+
|
|
224
|
+
if indexed_files % 100 == 0:
|
|
225
|
+
await tool_ctx.info(f"Indexed {indexed_files} files...")
|
|
226
|
+
|
|
227
|
+
except Exception as e:
|
|
228
|
+
errors.append(f"{file_path}: {str(e)}")
|
|
229
|
+
|
|
230
|
+
await tool_ctx.info(f"Indexed {indexed_files} files ({total_size / 1024 / 1024:.1f} MB)")
|
|
231
|
+
|
|
232
|
+
# Index git history if requested
|
|
233
|
+
git_stats = {}
|
|
234
|
+
if include_git_history and os.path.exists(os.path.join(abs_path, ".git")):
|
|
235
|
+
await tool_ctx.info("Indexing git history...")
|
|
236
|
+
|
|
237
|
+
git_ingester = GitIngester(vector_store)
|
|
238
|
+
git_stats = await git_ingester.ingest_repository(
|
|
239
|
+
repo_path=abs_path,
|
|
240
|
+
include_history=True,
|
|
241
|
+
include_diffs=True,
|
|
242
|
+
include_blame=True,
|
|
243
|
+
file_patterns=file_patterns,
|
|
244
|
+
)
|
|
245
|
+
|
|
246
|
+
await tool_ctx.info(
|
|
247
|
+
f"Indexed {git_stats.get('commits_indexed', 0)} commits, "
|
|
248
|
+
f"{git_stats.get('diffs_indexed', 0)} diffs"
|
|
249
|
+
)
|
|
250
|
+
|
|
251
|
+
# Get final statistics
|
|
252
|
+
if show_stats:
|
|
253
|
+
stats = await vector_store.get_stats()
|
|
254
|
+
stats.update({
|
|
255
|
+
"files_indexed": indexed_files,
|
|
256
|
+
"total_size_mb": total_size / 1024 / 1024,
|
|
257
|
+
"errors": len(errors),
|
|
258
|
+
**git_stats,
|
|
259
|
+
})
|
|
260
|
+
result = self._format_stats(stats, abs_path, time.time() - start_time)
|
|
261
|
+
|
|
262
|
+
if errors:
|
|
263
|
+
result += f"\n\nErrors ({len(errors)}):\n"
|
|
264
|
+
result += "\n".join(errors[:10]) # Show first 10 errors
|
|
265
|
+
if len(errors) > 10:
|
|
266
|
+
result += f"\n... and {len(errors) - 10} more errors"
|
|
267
|
+
|
|
268
|
+
return result
|
|
269
|
+
else:
|
|
270
|
+
return f"Successfully indexed {indexed_files} files"
|
|
271
|
+
|
|
272
|
+
except Exception as e:
|
|
273
|
+
await tool_ctx.error(f"Indexing failed: {str(e)}")
|
|
274
|
+
return f"Error during indexing: {str(e)}"
|
|
275
|
+
|
|
276
|
+
async def _find_files(self, base_path: str, pattern: str) -> list[str]:
|
|
277
|
+
"""Find files matching a pattern.
|
|
278
|
+
|
|
279
|
+
Args:
|
|
280
|
+
base_path: Base directory to search
|
|
281
|
+
pattern: File pattern to match
|
|
282
|
+
|
|
283
|
+
Returns:
|
|
284
|
+
List of matching file paths
|
|
285
|
+
"""
|
|
286
|
+
import glob
|
|
287
|
+
|
|
288
|
+
# Use glob to find files
|
|
289
|
+
if pattern.startswith("*."):
|
|
290
|
+
# Extension pattern
|
|
291
|
+
files = glob.glob(
|
|
292
|
+
os.path.join(base_path, "**", pattern),
|
|
293
|
+
recursive=True,
|
|
294
|
+
)
|
|
295
|
+
else:
|
|
296
|
+
# Exact filename
|
|
297
|
+
files = glob.glob(
|
|
298
|
+
os.path.join(base_path, "**", pattern),
|
|
299
|
+
recursive=True,
|
|
300
|
+
)
|
|
301
|
+
|
|
302
|
+
# Filter out hidden directories and common ignore patterns
|
|
303
|
+
filtered_files = []
|
|
304
|
+
ignore_dirs = {".git", "__pycache__", "node_modules", ".venv", "venv", "dist", "build"}
|
|
305
|
+
|
|
306
|
+
for file_path in files:
|
|
307
|
+
# Check if any parent directory is in ignore list
|
|
308
|
+
parts = Path(file_path).parts
|
|
309
|
+
if any(part in ignore_dirs for part in parts):
|
|
310
|
+
continue
|
|
311
|
+
if any(part.startswith(".") and part != "." for part in parts[:-1]):
|
|
312
|
+
continue # Skip hidden directories (but allow hidden files like .gitignore)
|
|
313
|
+
filtered_files.append(file_path)
|
|
314
|
+
|
|
315
|
+
return filtered_files
|
|
316
|
+
|
|
317
|
+
def _format_stats(self, stats: dict, path: str, elapsed_time: float) -> str:
|
|
318
|
+
"""Format statistics for display.
|
|
319
|
+
|
|
320
|
+
Args:
|
|
321
|
+
stats: Statistics dictionary
|
|
322
|
+
path: Indexed path
|
|
323
|
+
elapsed_time: Time taken for indexing
|
|
324
|
+
|
|
325
|
+
Returns:
|
|
326
|
+
Formatted statistics string
|
|
327
|
+
"""
|
|
328
|
+
result = f"=== Index Statistics for {path} ===\n\n"
|
|
329
|
+
|
|
330
|
+
# Basic stats
|
|
331
|
+
result += f"Indexing completed in {elapsed_time:.1f} seconds\n\n"
|
|
332
|
+
|
|
333
|
+
result += "Content Statistics:\n"
|
|
334
|
+
result += f" Documents: {stats.get('document_count', 0):,}\n"
|
|
335
|
+
result += f" Files indexed: {stats.get('files_indexed', 0):,}\n"
|
|
336
|
+
result += f" Total size: {stats.get('total_size_mb', 0):.1f} MB\n"
|
|
337
|
+
|
|
338
|
+
if stats.get("commits_indexed", 0) > 0:
|
|
339
|
+
result += f"\nGit History:\n"
|
|
340
|
+
result += f" Commits: {stats.get('commits_indexed', 0):,}\n"
|
|
341
|
+
result += f" Diffs: {stats.get('diffs_indexed', 0):,}\n"
|
|
342
|
+
result += f" Blame entries: {stats.get('blame_entries', 0):,}\n"
|
|
343
|
+
|
|
344
|
+
# Vector store info
|
|
345
|
+
result += f"\nVector Store:\n"
|
|
346
|
+
result += f" Database: {stats.get('database_name', 'default')}\n"
|
|
347
|
+
result += f" Table: {stats.get('table_name', 'documents')}\n"
|
|
348
|
+
result += f" Vectors: {stats.get('vector_count', stats.get('document_count', 0)):,}\n"
|
|
349
|
+
|
|
350
|
+
if stats.get("errors", 0) > 0:
|
|
351
|
+
result += f"\nErrors: {stats.get('errors', 0)}\n"
|
|
352
|
+
|
|
353
|
+
return result
|
|
354
|
+
|
|
355
|
+
def register(self, mcp_server) -> None:
|
|
356
|
+
"""Register this tool with the MCP server."""
|
|
357
|
+
# Tool registration is handled by the ToolRegistry
|
|
358
|
+
pass
|
|
@@ -725,6 +725,104 @@ class InfinityVectorStore:
|
|
|
725
725
|
import random
|
|
726
726
|
return [random.random() for _ in range(self.dimension)]
|
|
727
727
|
|
|
728
|
+
async def get_stats(self) -> Dict[str, Any]:
|
|
729
|
+
"""Get statistics about the vector store.
|
|
730
|
+
|
|
731
|
+
Returns:
|
|
732
|
+
Dictionary with statistics
|
|
733
|
+
"""
|
|
734
|
+
try:
|
|
735
|
+
# Get document count
|
|
736
|
+
doc_count_result = self.documents_table.output(["count(*)"]).to_pl()
|
|
737
|
+
doc_count = doc_count_result.item(0, 0) if len(doc_count_result) > 0 else 0
|
|
738
|
+
|
|
739
|
+
# Get unique file count
|
|
740
|
+
file_result = self.documents_table.output(["file_path"]).to_pl()
|
|
741
|
+
unique_files = set()
|
|
742
|
+
for row in file_result.iter_rows():
|
|
743
|
+
if row[0]:
|
|
744
|
+
unique_files.add(row[0])
|
|
745
|
+
|
|
746
|
+
# Get symbol count
|
|
747
|
+
symbol_count = 0
|
|
748
|
+
try:
|
|
749
|
+
symbol_result = self.symbols_table.output(["count(*)"]).to_pl()
|
|
750
|
+
symbol_count = symbol_result.item(0, 0) if len(symbol_result) > 0 else 0
|
|
751
|
+
except:
|
|
752
|
+
pass
|
|
753
|
+
|
|
754
|
+
# Get AST count
|
|
755
|
+
ast_count = 0
|
|
756
|
+
try:
|
|
757
|
+
ast_result = self.ast_table.output(["count(*)"]).to_pl()
|
|
758
|
+
ast_count = ast_result.item(0, 0) if len(ast_result) > 0 else 0
|
|
759
|
+
except:
|
|
760
|
+
pass
|
|
761
|
+
|
|
762
|
+
return {
|
|
763
|
+
"document_count": doc_count,
|
|
764
|
+
"vector_count": doc_count, # Each document has a vector
|
|
765
|
+
"unique_files": len(unique_files),
|
|
766
|
+
"symbol_count": symbol_count,
|
|
767
|
+
"ast_count": ast_count,
|
|
768
|
+
"database_name": self.db_name,
|
|
769
|
+
"table_name": "documents",
|
|
770
|
+
"dimension": self.dimension,
|
|
771
|
+
}
|
|
772
|
+
except Exception as e:
|
|
773
|
+
return {
|
|
774
|
+
"error": str(e),
|
|
775
|
+
"document_count": 0,
|
|
776
|
+
"vector_count": 0,
|
|
777
|
+
}
|
|
778
|
+
|
|
779
|
+
async def clear(self) -> bool:
|
|
780
|
+
"""Clear all data from the vector store.
|
|
781
|
+
|
|
782
|
+
Returns:
|
|
783
|
+
True if successful
|
|
784
|
+
"""
|
|
785
|
+
try:
|
|
786
|
+
# Delete all records from all tables
|
|
787
|
+
self.documents_table.delete()
|
|
788
|
+
|
|
789
|
+
try:
|
|
790
|
+
self.symbols_table.delete()
|
|
791
|
+
except:
|
|
792
|
+
pass
|
|
793
|
+
|
|
794
|
+
try:
|
|
795
|
+
self.ast_table.delete()
|
|
796
|
+
except:
|
|
797
|
+
pass
|
|
798
|
+
|
|
799
|
+
try:
|
|
800
|
+
self.references_table.delete()
|
|
801
|
+
except:
|
|
802
|
+
pass
|
|
803
|
+
|
|
804
|
+
return True
|
|
805
|
+
except Exception as e:
|
|
806
|
+
print(f"Error clearing vector store: {e}")
|
|
807
|
+
return False
|
|
808
|
+
|
|
809
|
+
async def index_document(
|
|
810
|
+
self,
|
|
811
|
+
content: str,
|
|
812
|
+
metadata: Dict[str, Any] = None,
|
|
813
|
+
) -> str:
|
|
814
|
+
"""Async version of add_document for consistency.
|
|
815
|
+
|
|
816
|
+
Args:
|
|
817
|
+
content: Document content
|
|
818
|
+
metadata: Additional metadata
|
|
819
|
+
|
|
820
|
+
Returns:
|
|
821
|
+
Document ID
|
|
822
|
+
"""
|
|
823
|
+
file_path = metadata.get("path") if metadata else None
|
|
824
|
+
return self.add_document(content, metadata, file_path)
|
|
825
|
+
|
|
728
826
|
def close(self):
|
|
729
827
|
"""Close the database connection."""
|
|
730
828
|
if hasattr(self, 'infinity'):
|
|
@@ -48,13 +48,18 @@ class VectorSearchTool(BaseTool):
|
|
|
48
48
|
@property
|
|
49
49
|
def description(self) -> str:
|
|
50
50
|
"""Get the tool description."""
|
|
51
|
-
return """
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
51
|
+
return """Pure semantic/vector search using Infinity embedded database.
|
|
52
|
+
|
|
53
|
+
Searches indexed documents using vector embeddings to find semantically similar content.
|
|
54
|
+
This is NOT keyword search - it finds documents based on meaning and context similarity.
|
|
55
|
+
|
|
56
|
+
Features:
|
|
57
|
+
- Searches across project-specific vector databases
|
|
58
|
+
- Returns similarity scores (0-1, higher is better)
|
|
59
|
+
- Supports filtering by project or file
|
|
60
|
+
- Automatically detects projects via LLM.md files
|
|
56
61
|
|
|
57
|
-
|
|
62
|
+
Use 'grep' for exact text/pattern matching, 'vector_search' for semantic similarity."""
|
|
58
63
|
|
|
59
64
|
async def call(
|
|
60
65
|
self,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
hanzo_mcp/__init__.py,sha256=
|
|
1
|
+
hanzo_mcp/__init__.py,sha256=_4GbNO6P83JxRoxDqEGtbxEuFjE9k2nPrWmTPz2Kngo,89
|
|
2
2
|
hanzo_mcp/cli.py,sha256=fX8HtsD44elZhYre7Dn6RmH5lUt2AFRWz6ehGeoCkUY,9784
|
|
3
3
|
hanzo_mcp/cli_enhanced.py,sha256=rqh9gqyjMuUznIlPTC5pcIGYZTKAScacMsDb1e68ReE,15819
|
|
4
4
|
hanzo_mcp/server.py,sha256=mYiIcsAtQO2c_MGExYbzk5tj2U-MjcDWfTU5T22KuwQ,8107
|
|
@@ -11,58 +11,96 @@ hanzo_mcp/prompts/create_release.py,sha256=1Z8xSTtz5vAm0rWFnERpFu7wIYExT4iXhM6nG
|
|
|
11
11
|
hanzo_mcp/prompts/project_system.py,sha256=fQhOM6AGb6VIZQE_fSPDeS9slBGVkz_f_UbNNhxPRdw,7031
|
|
12
12
|
hanzo_mcp/prompts/project_todo_reminder.py,sha256=otiBdmzxssBSb3MZZSQsjYDGLBqi1bM0HgraELP_Nf4,3645
|
|
13
13
|
hanzo_mcp/prompts/utils.py,sha256=IwxIhzZfYJ2anToPulbrpcc07u4Dozo9ok6VE3BC_4A,9963
|
|
14
|
-
hanzo_mcp/tools/__init__.py,sha256=
|
|
14
|
+
hanzo_mcp/tools/__init__.py,sha256=1_DWUUamBvBKFbsLcWFwAndbbXs4qODcqV0fyzMFBY4,12704
|
|
15
15
|
hanzo_mcp/tools/agent/__init__.py,sha256=MZ-LMIYptodQn1JpAEyNMbqRlioS4R8scgzNgsU189E,1897
|
|
16
16
|
hanzo_mcp/tools/agent/agent_tool.py,sha256=w-Oy2wPPTz79SCzmi7NsI8RU4eLbFKMTXDi-sFKrrbo,21268
|
|
17
17
|
hanzo_mcp/tools/agent/prompt.py,sha256=Wi9Z45hmQ92eUNZbOWzj9ZVCCr-fM1K9iyaRvTCAgrQ,4529
|
|
18
18
|
hanzo_mcp/tools/agent/tool_adapter.py,sha256=Od7VtD9qqDbgxhDHj0L-rohX4wOSMtYjZnU2BRuWSqI,2151
|
|
19
19
|
hanzo_mcp/tools/common/__init__.py,sha256=6LOEE9anSTsiPofgGNcD8CVHdU4SiaHjoQcRzNT2xos,921
|
|
20
|
-
hanzo_mcp/tools/common/base.py,sha256=
|
|
20
|
+
hanzo_mcp/tools/common/base.py,sha256=Es67mEZXFVYFUroxTV94YiPirsLvX-GNLjUMF95k1P0,5874
|
|
21
21
|
hanzo_mcp/tools/common/batch_tool.py,sha256=-FaZtH1cqd3xSHUrMaIvB664WEK0rKtTvzpUeEl0DhY,12073
|
|
22
22
|
hanzo_mcp/tools/common/config_tool.py,sha256=Tmjf4e6z_EfuOjS5acUQnzGo43GZxSqJC329FmQ5QFE,16241
|
|
23
23
|
hanzo_mcp/tools/common/context.py,sha256=XrgzJwPQP8ooKoReveezVgRyOSJe-zfD5-knhusBgbg,5175
|
|
24
24
|
hanzo_mcp/tools/common/permissions.py,sha256=LR1tuQAPMoaKvqNtHPRaiB0ZUb0Tbsg3e9L6vvd4FLU,7562
|
|
25
|
+
hanzo_mcp/tools/common/stats.py,sha256=urc3sS92e75D1wQCYG9GP1ZQRel3SmVPm8-htaEIF4I,9758
|
|
25
26
|
hanzo_mcp/tools/common/thinking_tool.py,sha256=pEBSymlJZJIS2X0pc-2VX2dUAPi4ho2un-wa69yYTD8,5142
|
|
27
|
+
hanzo_mcp/tools/common/tool_disable.py,sha256=Wx3PN7j9CQ2f1cMGNBJlJfqdaB13qPFcEV3idJXGZe8,4246
|
|
28
|
+
hanzo_mcp/tools/common/tool_enable.py,sha256=KK_gP-hjZRpEACxBJfZDAjNl89bnC7ueJu5fwGfbI_k,5033
|
|
29
|
+
hanzo_mcp/tools/common/tool_list.py,sha256=-pxT-pbxDLoHBXZShdy_Zjvsn4-Y2688q65PgSsg0Fk,8922
|
|
26
30
|
hanzo_mcp/tools/common/validation.py,sha256=VV3VbDvYlAYl2Bi98xE7gFo0xnmqHHUGJGNPswm97qo,1694
|
|
27
|
-
hanzo_mcp/tools/
|
|
31
|
+
hanzo_mcp/tools/database/__init__.py,sha256=6LnN5EsHFCFMQjg_jqxfOIExanAT2pLfaHyvOrvvO4U,2164
|
|
32
|
+
hanzo_mcp/tools/database/database_manager.py,sha256=urbP3T4QpTPBOH7JKjgjRbgMZAZxGm5Q_skZnZ9aHXQ,8807
|
|
33
|
+
hanzo_mcp/tools/database/graph_add.py,sha256=0Cdt5KPTVduE9c7C--HzIjx-12kmP3JEdsy28ZXtuh4,7730
|
|
34
|
+
hanzo_mcp/tools/database/graph_query.py,sha256=AgEKt-nqTALS0P9aPxMtANyEN3ik40tV8muCK9rydVc,19742
|
|
35
|
+
hanzo_mcp/tools/database/graph_remove.py,sha256=rlANurnV0cuz86IlAM1BovUUkme5V4GLsLxzdCajf_M,8765
|
|
36
|
+
hanzo_mcp/tools/database/graph_search.py,sha256=eqB3tk5g71c7GzilhIqXVsNQRzvvs1-MUxmfbSUjq0o,12555
|
|
37
|
+
hanzo_mcp/tools/database/graph_stats.py,sha256=yyxlCyClmLHC3X9lv9fU-O5YBOOg7GD-f8FSekXNVvw,13013
|
|
38
|
+
hanzo_mcp/tools/database/sql_query.py,sha256=7yhGJGkfjy339AINr8QVQICkpG3C0nTEGDzf20IN-zE,7279
|
|
39
|
+
hanzo_mcp/tools/database/sql_search.py,sha256=GyzKeVYo-pal4J2gvM_tjBnXbKYMNcURnnfJZ_oeFoI,9820
|
|
40
|
+
hanzo_mcp/tools/database/sql_stats.py,sha256=4gAIHRAVa1ydRChKGGhyA6cR8Z2cawGoxZBKQsnNJjI,8997
|
|
41
|
+
hanzo_mcp/tools/editor/__init__.py,sha256=RZKbXadNnh5fZ0-Wow-A5gAYCEfEuV_GapaR4mdjzW0,332
|
|
42
|
+
hanzo_mcp/tools/editor/neovim_command.py,sha256=_k6muOEVOBQx6ILJnUghMKlPKvO_1E9PDyM0qGC7twk,8272
|
|
43
|
+
hanzo_mcp/tools/editor/neovim_edit.py,sha256=BLZdQO7w0Aa61rf8SoLLkqPv0Zh-NGpX3fWMPAih7i8,8805
|
|
44
|
+
hanzo_mcp/tools/editor/neovim_session.py,sha256=f-iOnG5jsPrO7UrO77aMcwo0ZaP-us-KgIglVGe6bAs,11951
|
|
45
|
+
hanzo_mcp/tools/filesystem/__init__.py,sha256=8n_NklE5rTcvfVkwv-j720Ahtg6eE8HG2HHruHzJXYo,5436
|
|
28
46
|
hanzo_mcp/tools/filesystem/base.py,sha256=qwxer1jHgPIfyaUeC4QLzR9pjGWJCLP2L3qggUAulFY,3807
|
|
47
|
+
hanzo_mcp/tools/filesystem/batch_search.py,sha256=Rzp8yFzeHdnj2uR7YRIC85iz23yTr7uAl5U1_hGVjns,34467
|
|
29
48
|
hanzo_mcp/tools/filesystem/content_replace.py,sha256=hCiw9oQXS2_b6CjgC7XHOrRo5NH6H8zOFaSDS6Uwfgw,10015
|
|
30
49
|
hanzo_mcp/tools/filesystem/directory_tree.py,sha256=LZTJRmrDdSFpq9EpcTmVytimCp_glpCVKDxf7UCyq20,10755
|
|
31
50
|
hanzo_mcp/tools/filesystem/edit.py,sha256=PIlFsMjBG9WQw9IWC6dzLZly6UIBUcAUrohRkqyKFZY,10699
|
|
51
|
+
hanzo_mcp/tools/filesystem/find_files.py,sha256=AdwtY2Mz9oTPZ66juHe34_6fK_mVT-uNFVF6hhhdx8w,10948
|
|
52
|
+
hanzo_mcp/tools/filesystem/git_search.py,sha256=pab3PHI8wPxSHaaIuIWJwq1ULcdhye77xB4ORW05e7k,16209
|
|
32
53
|
hanzo_mcp/tools/filesystem/grep.py,sha256=-JKrBUk04tmObvwPh8UvBpLOc27NNndNt6eR5qSkCLs,16818
|
|
33
54
|
hanzo_mcp/tools/filesystem/grep_ast_tool.py,sha256=F-HacdAISZI_jDGJrxIcZ-dyj3OG919JUVimpvgAZNA,8142
|
|
34
55
|
hanzo_mcp/tools/filesystem/multi_edit.py,sha256=j8ytsFVsdQqJ9AWCJMQa8kWHyH4UpbBdHRIc7XepEJc,14313
|
|
35
56
|
hanzo_mcp/tools/filesystem/read.py,sha256=uF1KdIAsKL8-oQiwOfL9-dkTzKOqQK0nKLVe6hW-5KE,8892
|
|
36
|
-
hanzo_mcp/tools/filesystem/unified_search.py,sha256=zSarczfFoImqtddJnfnv-OuhENN8hPKdgptxiopmqf8,29483
|
|
37
57
|
hanzo_mcp/tools/filesystem/write.py,sha256=dkbZ61kYGRTzKPVtMG8ETYw8YHyo6YXb1cLI70ePYcQ,4833
|
|
38
58
|
hanzo_mcp/tools/jupyter/__init__.py,sha256=IJnkx6vwxP2ZJOGvUxG25fhstlny-uFnNBLjGlUt5hs,2515
|
|
39
59
|
hanzo_mcp/tools/jupyter/base.py,sha256=oxTz_exSsYni2cQJvL4gHZtC4EG5EU_1-nWyEdc-ZQ8,10090
|
|
40
60
|
hanzo_mcp/tools/jupyter/notebook_edit.py,sha256=wKEEQJ36pfgB0JHQi2nV_X7ApXqy6HXZY9XO4lZ9Efg,11848
|
|
41
61
|
hanzo_mcp/tools/jupyter/notebook_read.py,sha256=t2fkP5wAp8SBBaWHrty-uWsnn6l5WO2zIqISVSHnQus,5293
|
|
42
|
-
hanzo_mcp/tools/
|
|
62
|
+
hanzo_mcp/tools/llm/__init__.py,sha256=MdGOvR91CACz3W90U00YQs-gNnIg0OAr7sz6qTFfWXw,594
|
|
63
|
+
hanzo_mcp/tools/llm/consensus_tool.py,sha256=3mC5kNempHYxXq0I6kr19_Rit1NQxKXNGsal-iNTUpw,11611
|
|
64
|
+
hanzo_mcp/tools/llm/llm_manage.py,sha256=FHf0U7YAW8EPMwFLdTlq_u1m1V3cPPYjKTc9S_hc8Bk,15655
|
|
65
|
+
hanzo_mcp/tools/llm/llm_tool.py,sha256=I-onULpskIs3wCX4OI5vShhwYGGe472lynJ9CczwSNg,11147
|
|
66
|
+
hanzo_mcp/tools/llm/provider_tools.py,sha256=PvcapPUg4gGzlLHm-s6X1q6S841yHKjTO43tDOx1-ZE,11535
|
|
67
|
+
hanzo_mcp/tools/mcp/__init__.py,sha256=Bix1Vl4wxT2BxFI-y6fOphFZCG57oNhNqmnlBV4edZE,266
|
|
68
|
+
hanzo_mcp/tools/mcp/mcp_add.py,sha256=QEv43rb_c-xHOWAMIw9VET8r0NOdUjmu1aNBiMTJIu0,7913
|
|
69
|
+
hanzo_mcp/tools/mcp/mcp_remove.py,sha256=wQLcx4kpZapD_Kd5iet6dp_7u6GzCrxhv66Z9c5C-j8,3255
|
|
70
|
+
hanzo_mcp/tools/mcp/mcp_stats.py,sha256=sdm6KFQwCox477r03RdOOvYodnJ1vByGXy84v2CmHrs,5467
|
|
71
|
+
hanzo_mcp/tools/shell/__init__.py,sha256=LLMhZU8Fq7Rc4T0e2lBatt8cZS_G2kl6skFj346gLio,2718
|
|
43
72
|
hanzo_mcp/tools/shell/base.py,sha256=twbz3EuX64cwvNlcHraZ5CcEhDpUvMI5mLTZvMADtbQ,5821
|
|
44
73
|
hanzo_mcp/tools/shell/bash_session.py,sha256=YPtdtC0pc6Q04RJqKUy0u0RPTbiT2IGtsvFqejK5Hu4,27271
|
|
45
74
|
hanzo_mcp/tools/shell/bash_session_executor.py,sha256=zRnrzj4sdQOxO22XXBENT6k2dXt3LDk5fxjWjUYyU_Q,10723
|
|
46
75
|
hanzo_mcp/tools/shell/command_executor.py,sha256=IuoRY48PMmpKHL5CFIExebjoiRRS5ZEl73UDzYTR3kU,36406
|
|
76
|
+
hanzo_mcp/tools/shell/logs.py,sha256=c0SBlHrpTAY7eckJ0DrPUDzb5OnJTbzuyt4BoE2aTuA,8457
|
|
77
|
+
hanzo_mcp/tools/shell/npx.py,sha256=PWPp64nNQr16l1qp3fZpL4EYxuVFtJO6l8JRjQrMcyg,5072
|
|
78
|
+
hanzo_mcp/tools/shell/npx_background.py,sha256=TZncVB3RqKDLOLNWKDFCoRCFlQ7tiBIyvPCmf_K_oTg,7109
|
|
79
|
+
hanzo_mcp/tools/shell/pkill.py,sha256=j8Y7aETUonGCRF4Ikivc_3Qde4BVqNbgVypUxUuMdvs,8665
|
|
80
|
+
hanzo_mcp/tools/shell/processes.py,sha256=J3aSxacjEAhch3U8jtseYS2bHH3oav-zaFTjoM_Xgrg,9384
|
|
81
|
+
hanzo_mcp/tools/shell/run_background.py,sha256=uYcPtmP2dZPgPPTzToHiO1hToUwJ67KggQm-sN8QWKY,9702
|
|
47
82
|
hanzo_mcp/tools/shell/run_command.py,sha256=Io6LyLm8XWZKZ-Zjhx3L-H5vmdNGoqbkU9jJzwL7zLs,16137
|
|
48
83
|
hanzo_mcp/tools/shell/run_command_windows.py,sha256=MGXC76b0uYKhxg1-d9CijPP36ufRusgyq9Zurpo1vSc,15363
|
|
49
84
|
hanzo_mcp/tools/shell/session_manager.py,sha256=o8iS4PFCnq28vPqYtdtH9M8lfGyzyhtNL0hmNI13Uuc,6509
|
|
50
85
|
hanzo_mcp/tools/shell/session_storage.py,sha256=elnyFgn0FwsmVvoWAoJFAqiEeNaK4_yByT8-zXa6r-o,10141
|
|
86
|
+
hanzo_mcp/tools/shell/uvx.py,sha256=NY_f3c-llI5IwkbdiE4r5pHCUS3KI37RTcmrRqlKIRI,5023
|
|
87
|
+
hanzo_mcp/tools/shell/uvx_background.py,sha256=lY1T0k_k28VqPyFx0bfF0qmeKbpTgvj3zhHd4OcU8rs,7094
|
|
51
88
|
hanzo_mcp/tools/todo/__init__.py,sha256=Ai-rlVWcy-CkJf1H2zIsbyx0wkxzWNLR3WAbGszbXKg,1720
|
|
52
89
|
hanzo_mcp/tools/todo/base.py,sha256=8sYZYAsFE5SjHRqynZCmCIKEobWB3aZwwSApg26keDo,10655
|
|
53
90
|
hanzo_mcp/tools/todo/todo_read.py,sha256=zXI9jn-kWXGSj88tI63yoAv-EWPDpkX1E6m0QfMUQHE,4759
|
|
54
91
|
hanzo_mcp/tools/todo/todo_write.py,sha256=fTAvrxrzkpdYwi7nYcJky2wjukChYsdXu5axqIUJg_c,15465
|
|
55
|
-
hanzo_mcp/tools/vector/__init__.py,sha256=
|
|
92
|
+
hanzo_mcp/tools/vector/__init__.py,sha256=TAxOaWOnzC756n7byfdfwTcaxjuHITs586bGHZ1B6Ww,3850
|
|
56
93
|
hanzo_mcp/tools/vector/ast_analyzer.py,sha256=2bUM9j9rCNARNXXF2cuFSp2ercwZAJWlAqeRIwn46Ck,15653
|
|
57
|
-
hanzo_mcp/tools/vector/git_ingester.py,sha256=
|
|
58
|
-
hanzo_mcp/tools/vector/
|
|
94
|
+
hanzo_mcp/tools/vector/git_ingester.py,sha256=pR4_HRMT7trGhr1kGHASvhgm7Vjwh6UY-UVdXCNj07s,16367
|
|
95
|
+
hanzo_mcp/tools/vector/index_tool.py,sha256=6VWDEfA_R6ySwVCg85h4rGw_uh4ROtpL2_Ab5kMDl8A,12758
|
|
96
|
+
hanzo_mcp/tools/vector/infinity_store.py,sha256=E3YotdY798HTPs4X2IMv-SOjt-VjCC2XAFaWPeSVUQU,28382
|
|
59
97
|
hanzo_mcp/tools/vector/mock_infinity.py,sha256=QyU7FM2eTCP0BeuX8xhRe0En7hG9EFt-XzgDu-BefrI,4990
|
|
60
98
|
hanzo_mcp/tools/vector/project_manager.py,sha256=JZ6c0m4RWKbV4JjkxAI6ZgyOy2Ymk8-o4ficTLZrIo0,12500
|
|
61
99
|
hanzo_mcp/tools/vector/vector_index.py,sha256=Idp9w4g_7WVbIDY3oEX4-o7bWWQadeo3XC9lVtyS7Wg,4392
|
|
62
|
-
hanzo_mcp/tools/vector/vector_search.py,sha256=
|
|
63
|
-
hanzo_mcp-0.5.
|
|
64
|
-
hanzo_mcp-0.5.
|
|
65
|
-
hanzo_mcp-0.5.
|
|
66
|
-
hanzo_mcp-0.5.
|
|
67
|
-
hanzo_mcp-0.5.
|
|
68
|
-
hanzo_mcp-0.5.
|
|
100
|
+
hanzo_mcp/tools/vector/vector_search.py,sha256=QErsckzvwrBl_DkELIkBRE2kH-WOMxSgHLiwAFkiyMA,9636
|
|
101
|
+
hanzo_mcp-0.5.2.dist-info/licenses/LICENSE,sha256=mf1qZGFsPGskoPgytp9B-RsahfKvXsBpmaAbTLGTt8Y,1063
|
|
102
|
+
hanzo_mcp-0.5.2.dist-info/METADATA,sha256=Mk3SPG1yAlh9gooa9EvcA72JhsWIauW87RBNSLhgP5A,11995
|
|
103
|
+
hanzo_mcp-0.5.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
104
|
+
hanzo_mcp-0.5.2.dist-info/entry_points.txt,sha256=aRKOKXtuQr-idSr-yH4efnRl2v8te94AcgN3ysqqSYs,49
|
|
105
|
+
hanzo_mcp-0.5.2.dist-info/top_level.txt,sha256=eGFANatA0MHWiVlpS56fTYRIShtibrSom1uXI6XU0GU,10
|
|
106
|
+
hanzo_mcp-0.5.2.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|