mcp-vector-search 0.0.3__py3-none-any.whl → 0.4.12__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 mcp-vector-search might be problematic. Click here for more details.

Files changed (49) hide show
  1. mcp_vector_search/__init__.py +3 -2
  2. mcp_vector_search/cli/commands/auto_index.py +397 -0
  3. mcp_vector_search/cli/commands/config.py +88 -40
  4. mcp_vector_search/cli/commands/index.py +198 -52
  5. mcp_vector_search/cli/commands/init.py +471 -58
  6. mcp_vector_search/cli/commands/install.py +284 -0
  7. mcp_vector_search/cli/commands/mcp.py +495 -0
  8. mcp_vector_search/cli/commands/search.py +241 -87
  9. mcp_vector_search/cli/commands/status.py +184 -58
  10. mcp_vector_search/cli/commands/watch.py +34 -35
  11. mcp_vector_search/cli/didyoumean.py +184 -0
  12. mcp_vector_search/cli/export.py +320 -0
  13. mcp_vector_search/cli/history.py +292 -0
  14. mcp_vector_search/cli/interactive.py +342 -0
  15. mcp_vector_search/cli/main.py +175 -27
  16. mcp_vector_search/cli/output.py +63 -45
  17. mcp_vector_search/config/defaults.py +50 -36
  18. mcp_vector_search/config/settings.py +49 -35
  19. mcp_vector_search/core/auto_indexer.py +298 -0
  20. mcp_vector_search/core/connection_pool.py +322 -0
  21. mcp_vector_search/core/database.py +335 -25
  22. mcp_vector_search/core/embeddings.py +73 -29
  23. mcp_vector_search/core/exceptions.py +19 -2
  24. mcp_vector_search/core/factory.py +310 -0
  25. mcp_vector_search/core/git_hooks.py +345 -0
  26. mcp_vector_search/core/indexer.py +237 -73
  27. mcp_vector_search/core/models.py +21 -19
  28. mcp_vector_search/core/project.py +73 -58
  29. mcp_vector_search/core/scheduler.py +330 -0
  30. mcp_vector_search/core/search.py +574 -86
  31. mcp_vector_search/core/watcher.py +48 -46
  32. mcp_vector_search/mcp/__init__.py +4 -0
  33. mcp_vector_search/mcp/__main__.py +25 -0
  34. mcp_vector_search/mcp/server.py +701 -0
  35. mcp_vector_search/parsers/base.py +30 -31
  36. mcp_vector_search/parsers/javascript.py +74 -48
  37. mcp_vector_search/parsers/python.py +57 -49
  38. mcp_vector_search/parsers/registry.py +47 -32
  39. mcp_vector_search/parsers/text.py +179 -0
  40. mcp_vector_search/utils/__init__.py +40 -0
  41. mcp_vector_search/utils/gitignore.py +229 -0
  42. mcp_vector_search/utils/timing.py +334 -0
  43. mcp_vector_search/utils/version.py +47 -0
  44. {mcp_vector_search-0.0.3.dist-info → mcp_vector_search-0.4.12.dist-info}/METADATA +173 -7
  45. mcp_vector_search-0.4.12.dist-info/RECORD +54 -0
  46. mcp_vector_search-0.0.3.dist-info/RECORD +0 -35
  47. {mcp_vector_search-0.0.3.dist-info → mcp_vector_search-0.4.12.dist-info}/WHEEL +0 -0
  48. {mcp_vector_search-0.0.3.dist-info → mcp_vector_search-0.4.12.dist-info}/entry_points.txt +0 -0
  49. {mcp_vector_search-0.0.3.dist-info → mcp_vector_search-0.4.12.dist-info}/licenses/LICENSE +0 -0
@@ -1,19 +1,18 @@
1
1
  """File system watcher for incremental indexing."""
2
2
 
3
3
  import asyncio
4
- from pathlib import Path
5
- from typing import List, Optional, Set, Callable, Awaitable, Union
6
- from threading import Thread
7
4
  import time
5
+ from collections.abc import Awaitable, Callable
8
6
  from concurrent.futures import Future
7
+ from pathlib import Path
9
8
 
10
9
  from loguru import logger
10
+ from watchdog.events import FileSystemEvent, FileSystemEventHandler
11
11
  from watchdog.observers import Observer
12
- from watchdog.events import FileSystemEventHandler, FileSystemEvent
13
12
 
14
13
  from ..config.settings import ProjectConfig
15
- from .indexer import SemanticIndexer
16
14
  from .database import ChromaVectorDatabase
15
+ from .indexer import SemanticIndexer
17
16
 
18
17
 
19
18
  class CodeFileHandler(FileSystemEventHandler):
@@ -21,8 +20,8 @@ class CodeFileHandler(FileSystemEventHandler):
21
20
 
22
21
  def __init__(
23
22
  self,
24
- file_extensions: List[str],
25
- ignore_patterns: List[str],
23
+ file_extensions: list[str],
24
+ ignore_patterns: list[str],
26
25
  callback: Callable[[str, str], Awaitable[None]],
27
26
  loop: asyncio.AbstractEventLoop,
28
27
  debounce_delay: float = 1.0,
@@ -42,23 +41,23 @@ class CodeFileHandler(FileSystemEventHandler):
42
41
  self.callback = callback
43
42
  self.loop = loop
44
43
  self.debounce_delay = debounce_delay
45
- self.pending_changes: Set[str] = set()
44
+ self.pending_changes: set[str] = set()
46
45
  self.last_change_time: float = 0
47
- self.debounce_task: Optional[Union[asyncio.Task, Future]] = None
46
+ self.debounce_task: asyncio.Task | Future | None = None
48
47
 
49
48
  def should_process_file(self, file_path: str) -> bool:
50
49
  """Check if file should be processed."""
51
50
  path = Path(file_path)
52
-
51
+
53
52
  # Check file extension
54
53
  if path.suffix not in self.file_extensions:
55
54
  return False
56
-
55
+
57
56
  # Check ignore patterns
58
57
  for pattern in self.ignore_patterns:
59
58
  if pattern in str(path):
60
59
  return False
61
-
60
+
62
61
  return True
63
62
 
64
63
  def on_modified(self, event: FileSystemEvent) -> None:
@@ -78,7 +77,7 @@ class CodeFileHandler(FileSystemEventHandler):
78
77
 
79
78
  def on_moved(self, event: FileSystemEvent) -> None:
80
79
  """Handle file move/rename."""
81
- if hasattr(event, 'dest_path'):
80
+ if hasattr(event, "dest_path"):
82
81
  # Handle rename/move
83
82
  if not event.is_directory:
84
83
  if self.should_process_file(event.src_path):
@@ -96,24 +95,22 @@ class CodeFileHandler(FileSystemEventHandler):
96
95
  self.debounce_task.cancel()
97
96
 
98
97
  # Schedule new debounce task using the stored loop
99
- future = asyncio.run_coroutine_threadsafe(
100
- self._debounced_process(), self.loop
101
- )
98
+ future = asyncio.run_coroutine_threadsafe(self._debounced_process(), self.loop)
102
99
  # Store the future as our task (it has a done() method)
103
100
  self.debounce_task = future
104
101
 
105
102
  async def _debounced_process(self) -> None:
106
103
  """Process pending changes after debounce delay."""
107
104
  await asyncio.sleep(self.debounce_delay)
108
-
105
+
109
106
  # Check if more changes occurred during debounce
110
107
  if time.time() - self.last_change_time < self.debounce_delay:
111
108
  return
112
-
109
+
113
110
  # Process all pending changes
114
111
  changes = self.pending_changes.copy()
115
112
  self.pending_changes.clear()
116
-
113
+
117
114
  for change in changes:
118
115
  change_type, file_path = change.split(":", 1)
119
116
  try:
@@ -133,7 +130,7 @@ class FileWatcher:
133
130
  database: ChromaVectorDatabase,
134
131
  ):
135
132
  """Initialize file watcher.
136
-
133
+
137
134
  Args:
138
135
  project_root: Root directory to watch
139
136
  config: Project configuration
@@ -144,8 +141,8 @@ class FileWatcher:
144
141
  self.config = config
145
142
  self.indexer = indexer
146
143
  self.database = database
147
- self.observer: Optional[Observer] = None
148
- self.handler: Optional[CodeFileHandler] = None
144
+ self.observer: Observer | None = None
145
+ self.handler: CodeFileHandler | None = None
149
146
  self.is_running = False
150
147
 
151
148
  async def start(self) -> None:
@@ -155,7 +152,7 @@ class FileWatcher:
155
152
  return
156
153
 
157
154
  logger.info(f"Starting file watcher for {self.project_root}")
158
-
155
+
159
156
  # Create handler
160
157
  loop = asyncio.get_running_loop()
161
158
  self.handler = CodeFileHandler(
@@ -168,16 +165,12 @@ class FileWatcher:
168
165
 
169
166
  # Create observer
170
167
  self.observer = Observer()
171
- self.observer.schedule(
172
- self.handler,
173
- str(self.project_root),
174
- recursive=True
175
- )
168
+ self.observer.schedule(self.handler, str(self.project_root), recursive=True)
176
169
 
177
170
  # Start observer in a separate thread
178
171
  self.observer.start()
179
172
  self.is_running = True
180
-
173
+
181
174
  logger.info("File watcher started successfully")
182
175
 
183
176
  async def stop(self) -> None:
@@ -186,7 +179,7 @@ class FileWatcher:
186
179
  return
187
180
 
188
181
  logger.info("Stopping file watcher")
189
-
182
+
190
183
  if self.observer:
191
184
  self.observer.stop()
192
185
  self.observer.join()
@@ -194,28 +187,37 @@ class FileWatcher:
194
187
 
195
188
  self.handler = None
196
189
  self.is_running = False
197
-
190
+
198
191
  logger.info("File watcher stopped")
199
192
 
200
- def _get_ignore_patterns(self) -> List[str]:
193
+ def _get_ignore_patterns(self) -> list[str]:
201
194
  """Get patterns to ignore during watching."""
202
195
  default_patterns = [
203
- ".git", ".svn", ".hg",
204
- "__pycache__", ".pytest_cache",
205
- "node_modules", ".venv", "venv",
206
- ".DS_Store", "Thumbs.db",
207
- ".idea", ".vscode",
208
- "build", "dist", "target",
196
+ ".git",
197
+ ".svn",
198
+ ".hg",
199
+ "__pycache__",
200
+ ".pytest_cache",
201
+ "node_modules",
202
+ ".venv",
203
+ "venv",
204
+ ".DS_Store",
205
+ "Thumbs.db",
206
+ ".idea",
207
+ ".vscode",
208
+ "build",
209
+ "dist",
210
+ "target",
209
211
  ".mcp-vector-search", # Ignore our own index directory
210
212
  ]
211
-
213
+
212
214
  # Add any custom ignore patterns from config
213
215
  # TODO: Add custom ignore patterns to config
214
216
  return default_patterns
215
217
 
216
218
  async def _handle_file_change(self, file_path: str, change_type: str) -> None:
217
219
  """Handle a file change event.
218
-
220
+
219
221
  Args:
220
222
  file_path: Path to the changed file
221
223
  change_type: Type of change (created, modified, deleted)
@@ -230,9 +232,9 @@ class FileWatcher:
230
232
  elif change_type in ("created", "modified"):
231
233
  # Re-index the file
232
234
  await self._reindex_file(path)
233
-
235
+
234
236
  logger.info(f"Processed {change_type} for {path.name}")
235
-
237
+
236
238
  except Exception as e:
237
239
  logger.error(f"Failed to process {change_type} for {path}: {e}")
238
240
 
@@ -256,7 +258,7 @@ class FileWatcher:
256
258
 
257
259
  # Remove existing chunks first
258
260
  await self._remove_file_chunks(file_path)
259
-
261
+
260
262
  # Index the file
261
263
  chunks_indexed = await self.indexer.index_file(file_path)
262
264
  logger.debug(f"Re-indexed {file_path.name}: {chunks_indexed} chunks")
@@ -287,21 +289,21 @@ class WatcherManager:
287
289
  ) -> FileWatcher:
288
290
  """Start a file watcher for a project."""
289
291
  project_key = str(project_root)
290
-
292
+
291
293
  if project_key in self.watchers:
292
294
  logger.warning(f"Watcher already exists for {project_root}")
293
295
  return self.watchers[project_key]
294
296
 
295
297
  watcher = FileWatcher(project_root, config, indexer, database)
296
298
  await watcher.start()
297
-
299
+
298
300
  self.watchers[project_key] = watcher
299
301
  return watcher
300
302
 
301
303
  async def stop_watcher(self, project_root: Path) -> None:
302
304
  """Stop a file watcher for a project."""
303
305
  project_key = str(project_root)
304
-
306
+
305
307
  if project_key not in self.watchers:
306
308
  logger.warning(f"No watcher found for {project_root}")
307
309
  return
@@ -1 +1,5 @@
1
1
  """MCP server integration for MCP Vector Search."""
2
+
3
+ from .server import create_mcp_server, run_mcp_server
4
+
5
+ __all__ = ["create_mcp_server", "run_mcp_server"]
@@ -0,0 +1,25 @@
1
+ """Entry point for running the MCP server."""
2
+
3
+ import asyncio
4
+ import sys
5
+ from pathlib import Path
6
+
7
+ from .server import run_mcp_server
8
+
9
+
10
+ def main():
11
+ """Main entry point for the MCP server."""
12
+ # Allow specifying project root as command line argument
13
+ project_root = Path(sys.argv[1]) if len(sys.argv) > 1 else None
14
+
15
+ try:
16
+ asyncio.run(run_mcp_server(project_root))
17
+ except KeyboardInterrupt:
18
+ pass
19
+ except Exception as e:
20
+ print(f"MCP server error: {e}", file=sys.stderr)
21
+ sys.exit(1)
22
+
23
+
24
+ if __name__ == "__main__":
25
+ main()