mcp-vector-search 0.15.7__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.
- mcp_vector_search/__init__.py +10 -0
- mcp_vector_search/cli/__init__.py +1 -0
- mcp_vector_search/cli/commands/__init__.py +1 -0
- mcp_vector_search/cli/commands/auto_index.py +397 -0
- mcp_vector_search/cli/commands/chat.py +534 -0
- mcp_vector_search/cli/commands/config.py +393 -0
- mcp_vector_search/cli/commands/demo.py +358 -0
- mcp_vector_search/cli/commands/index.py +762 -0
- mcp_vector_search/cli/commands/init.py +658 -0
- mcp_vector_search/cli/commands/install.py +869 -0
- mcp_vector_search/cli/commands/install_old.py +700 -0
- mcp_vector_search/cli/commands/mcp.py +1254 -0
- mcp_vector_search/cli/commands/reset.py +393 -0
- mcp_vector_search/cli/commands/search.py +796 -0
- mcp_vector_search/cli/commands/setup.py +1133 -0
- mcp_vector_search/cli/commands/status.py +584 -0
- mcp_vector_search/cli/commands/uninstall.py +404 -0
- mcp_vector_search/cli/commands/visualize/__init__.py +39 -0
- mcp_vector_search/cli/commands/visualize/cli.py +265 -0
- mcp_vector_search/cli/commands/visualize/exporters/__init__.py +12 -0
- mcp_vector_search/cli/commands/visualize/exporters/html_exporter.py +33 -0
- mcp_vector_search/cli/commands/visualize/exporters/json_exporter.py +29 -0
- mcp_vector_search/cli/commands/visualize/graph_builder.py +709 -0
- mcp_vector_search/cli/commands/visualize/layout_engine.py +469 -0
- mcp_vector_search/cli/commands/visualize/server.py +201 -0
- mcp_vector_search/cli/commands/visualize/state_manager.py +428 -0
- mcp_vector_search/cli/commands/visualize/templates/__init__.py +16 -0
- mcp_vector_search/cli/commands/visualize/templates/base.py +218 -0
- mcp_vector_search/cli/commands/visualize/templates/scripts.py +3670 -0
- mcp_vector_search/cli/commands/visualize/templates/styles.py +779 -0
- mcp_vector_search/cli/commands/visualize.py.original +2536 -0
- mcp_vector_search/cli/commands/watch.py +287 -0
- mcp_vector_search/cli/didyoumean.py +520 -0
- mcp_vector_search/cli/export.py +320 -0
- mcp_vector_search/cli/history.py +295 -0
- mcp_vector_search/cli/interactive.py +342 -0
- mcp_vector_search/cli/main.py +484 -0
- mcp_vector_search/cli/output.py +414 -0
- mcp_vector_search/cli/suggestions.py +375 -0
- mcp_vector_search/config/__init__.py +1 -0
- mcp_vector_search/config/constants.py +24 -0
- mcp_vector_search/config/defaults.py +200 -0
- mcp_vector_search/config/settings.py +146 -0
- mcp_vector_search/core/__init__.py +1 -0
- mcp_vector_search/core/auto_indexer.py +298 -0
- mcp_vector_search/core/config_utils.py +394 -0
- mcp_vector_search/core/connection_pool.py +360 -0
- mcp_vector_search/core/database.py +1237 -0
- mcp_vector_search/core/directory_index.py +318 -0
- mcp_vector_search/core/embeddings.py +294 -0
- mcp_vector_search/core/exceptions.py +89 -0
- mcp_vector_search/core/factory.py +318 -0
- mcp_vector_search/core/git_hooks.py +345 -0
- mcp_vector_search/core/indexer.py +1002 -0
- mcp_vector_search/core/llm_client.py +453 -0
- mcp_vector_search/core/models.py +294 -0
- mcp_vector_search/core/project.py +350 -0
- mcp_vector_search/core/scheduler.py +330 -0
- mcp_vector_search/core/search.py +952 -0
- mcp_vector_search/core/watcher.py +322 -0
- mcp_vector_search/mcp/__init__.py +5 -0
- mcp_vector_search/mcp/__main__.py +25 -0
- mcp_vector_search/mcp/server.py +752 -0
- mcp_vector_search/parsers/__init__.py +8 -0
- mcp_vector_search/parsers/base.py +296 -0
- mcp_vector_search/parsers/dart.py +605 -0
- mcp_vector_search/parsers/html.py +413 -0
- mcp_vector_search/parsers/javascript.py +643 -0
- mcp_vector_search/parsers/php.py +694 -0
- mcp_vector_search/parsers/python.py +502 -0
- mcp_vector_search/parsers/registry.py +223 -0
- mcp_vector_search/parsers/ruby.py +678 -0
- mcp_vector_search/parsers/text.py +186 -0
- mcp_vector_search/parsers/utils.py +265 -0
- mcp_vector_search/py.typed +1 -0
- mcp_vector_search/utils/__init__.py +42 -0
- mcp_vector_search/utils/gitignore.py +250 -0
- mcp_vector_search/utils/gitignore_updater.py +212 -0
- mcp_vector_search/utils/monorepo.py +339 -0
- mcp_vector_search/utils/timing.py +338 -0
- mcp_vector_search/utils/version.py +47 -0
- mcp_vector_search-0.15.7.dist-info/METADATA +884 -0
- mcp_vector_search-0.15.7.dist-info/RECORD +86 -0
- mcp_vector_search-0.15.7.dist-info/WHEEL +4 -0
- mcp_vector_search-0.15.7.dist-info/entry_points.txt +3 -0
- mcp_vector_search-0.15.7.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,394 @@
|
|
|
1
|
+
"""Secure configuration utilities for API key storage and retrieval.
|
|
2
|
+
|
|
3
|
+
This module provides utilities for securely storing and retrieving sensitive
|
|
4
|
+
configuration data like API keys. Keys are stored in the project's config
|
|
5
|
+
directory with restrictive file permissions.
|
|
6
|
+
|
|
7
|
+
Design Decision: Local file storage with permissions
|
|
8
|
+
- Rationale: Simple, works cross-platform, no external dependencies
|
|
9
|
+
- Trade-offs: Not encrypted at rest (OS file permissions only), single-machine
|
|
10
|
+
- Alternatives considered:
|
|
11
|
+
1. OS keyring (rejected: platform-specific, complex setup)
|
|
12
|
+
2. Encrypted storage (rejected: key management complexity)
|
|
13
|
+
3. Environment variables only (rejected: poor user experience)
|
|
14
|
+
|
|
15
|
+
Priority: Environment variable (OPENROUTER_API_KEY) > Local config file
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
import json
|
|
19
|
+
import os
|
|
20
|
+
import stat
|
|
21
|
+
from pathlib import Path
|
|
22
|
+
from typing import Any
|
|
23
|
+
|
|
24
|
+
from loguru import logger
|
|
25
|
+
|
|
26
|
+
# Configuration file name
|
|
27
|
+
CONFIG_FILENAME = "config.json"
|
|
28
|
+
|
|
29
|
+
# Sensitive keys that should never be logged in full
|
|
30
|
+
SENSITIVE_KEYS = {"openrouter_api_key", "openai_api_key", "api_key", "token", "secret"}
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class ConfigManager:
|
|
34
|
+
"""Secure configuration manager for API keys and sensitive data.
|
|
35
|
+
|
|
36
|
+
Handles reading/writing configuration with proper error handling
|
|
37
|
+
and security considerations.
|
|
38
|
+
|
|
39
|
+
Security Features:
|
|
40
|
+
- Restrictive file permissions (0600 - owner read/write only)
|
|
41
|
+
- Sensitive values masked in logs
|
|
42
|
+
- Environment variable override support
|
|
43
|
+
"""
|
|
44
|
+
|
|
45
|
+
def __init__(self, config_dir: Path) -> None:
|
|
46
|
+
"""Initialize config manager.
|
|
47
|
+
|
|
48
|
+
Args:
|
|
49
|
+
config_dir: Directory to store config file (e.g., .mcp-vector-search/)
|
|
50
|
+
"""
|
|
51
|
+
self.config_dir = config_dir
|
|
52
|
+
self.config_file = config_dir / CONFIG_FILENAME
|
|
53
|
+
|
|
54
|
+
def _ensure_config_dir(self) -> None:
|
|
55
|
+
"""Ensure config directory exists with proper permissions."""
|
|
56
|
+
if not self.config_dir.exists():
|
|
57
|
+
self.config_dir.mkdir(parents=True, exist_ok=True)
|
|
58
|
+
# Set directory permissions to owner-only (0700)
|
|
59
|
+
self.config_dir.chmod(stat.S_IRWXU)
|
|
60
|
+
logger.debug(f"Created config directory: {self.config_dir}")
|
|
61
|
+
|
|
62
|
+
def _set_secure_permissions(self, file_path: Path) -> None:
|
|
63
|
+
"""Set restrictive file permissions (owner read/write only).
|
|
64
|
+
|
|
65
|
+
Args:
|
|
66
|
+
file_path: Path to file to secure
|
|
67
|
+
|
|
68
|
+
Note: Sets 0600 permissions (rw-------) on Unix-like systems.
|
|
69
|
+
Windows uses different permission model but respects intent.
|
|
70
|
+
"""
|
|
71
|
+
try:
|
|
72
|
+
# Set to 0600 (rw-------)
|
|
73
|
+
file_path.chmod(stat.S_IRUSR | stat.S_IWUSR)
|
|
74
|
+
logger.debug(f"Set secure permissions (0600) on {file_path}")
|
|
75
|
+
except Exception as e:
|
|
76
|
+
logger.warning(f"Failed to set secure permissions on {file_path}: {e}")
|
|
77
|
+
|
|
78
|
+
def _mask_sensitive_value(self, value: str) -> str:
|
|
79
|
+
"""Mask sensitive value for logging (show last 4 chars only).
|
|
80
|
+
|
|
81
|
+
Args:
|
|
82
|
+
value: Value to mask
|
|
83
|
+
|
|
84
|
+
Returns:
|
|
85
|
+
Masked value like "****1234"
|
|
86
|
+
"""
|
|
87
|
+
if not value or len(value) < 4:
|
|
88
|
+
return "****"
|
|
89
|
+
return "*" * (len(value) - 4) + value[-4:]
|
|
90
|
+
|
|
91
|
+
def load_config(self) -> dict[str, Any]:
|
|
92
|
+
"""Load configuration from file.
|
|
93
|
+
|
|
94
|
+
Returns:
|
|
95
|
+
Configuration dictionary (empty dict if file doesn't exist)
|
|
96
|
+
|
|
97
|
+
Error Handling:
|
|
98
|
+
- Missing file: Returns empty dict
|
|
99
|
+
- JSON parse error: Logs warning, returns empty dict
|
|
100
|
+
- Read error: Logs error, returns empty dict
|
|
101
|
+
"""
|
|
102
|
+
if not self.config_file.exists():
|
|
103
|
+
logger.debug(f"Config file not found: {self.config_file}")
|
|
104
|
+
return {}
|
|
105
|
+
|
|
106
|
+
try:
|
|
107
|
+
with open(self.config_file) as f:
|
|
108
|
+
config = json.load(f)
|
|
109
|
+
logger.debug(f"Loaded config from {self.config_file}")
|
|
110
|
+
return config
|
|
111
|
+
except json.JSONDecodeError as e:
|
|
112
|
+
logger.warning(f"Invalid JSON in config file {self.config_file}: {e}")
|
|
113
|
+
return {}
|
|
114
|
+
except Exception as e:
|
|
115
|
+
logger.error(f"Failed to load config from {self.config_file}: {e}")
|
|
116
|
+
return {}
|
|
117
|
+
|
|
118
|
+
def save_config(self, config: dict[str, Any]) -> None:
|
|
119
|
+
"""Save configuration to file with secure permissions.
|
|
120
|
+
|
|
121
|
+
Args:
|
|
122
|
+
config: Configuration dictionary to save
|
|
123
|
+
|
|
124
|
+
Raises:
|
|
125
|
+
OSError: If file write fails
|
|
126
|
+
ValueError: If config is not JSON-serializable
|
|
127
|
+
"""
|
|
128
|
+
self._ensure_config_dir()
|
|
129
|
+
|
|
130
|
+
try:
|
|
131
|
+
# Write to temporary file first (atomic write)
|
|
132
|
+
temp_file = self.config_file.with_suffix(".tmp")
|
|
133
|
+
|
|
134
|
+
with open(temp_file, "w") as f:
|
|
135
|
+
json.dump(config, f, indent=2)
|
|
136
|
+
|
|
137
|
+
# Set secure permissions before moving
|
|
138
|
+
self._set_secure_permissions(temp_file)
|
|
139
|
+
|
|
140
|
+
# Atomic move
|
|
141
|
+
temp_file.replace(self.config_file)
|
|
142
|
+
|
|
143
|
+
logger.debug(f"Saved config to {self.config_file}")
|
|
144
|
+
|
|
145
|
+
except Exception as e:
|
|
146
|
+
logger.error(f"Failed to save config to {self.config_file}: {e}")
|
|
147
|
+
raise
|
|
148
|
+
|
|
149
|
+
def get_value(self, key: str, default: Any = None) -> Any:
|
|
150
|
+
"""Get configuration value.
|
|
151
|
+
|
|
152
|
+
Args:
|
|
153
|
+
key: Configuration key
|
|
154
|
+
default: Default value if key not found
|
|
155
|
+
|
|
156
|
+
Returns:
|
|
157
|
+
Configuration value or default
|
|
158
|
+
"""
|
|
159
|
+
config = self.load_config()
|
|
160
|
+
return config.get(key, default)
|
|
161
|
+
|
|
162
|
+
def set_value(self, key: str, value: Any) -> None:
|
|
163
|
+
"""Set configuration value.
|
|
164
|
+
|
|
165
|
+
Args:
|
|
166
|
+
key: Configuration key
|
|
167
|
+
value: Value to set
|
|
168
|
+
"""
|
|
169
|
+
config = self.load_config()
|
|
170
|
+
config[key] = value
|
|
171
|
+
self.save_config(config)
|
|
172
|
+
|
|
173
|
+
# Log with masking for sensitive keys
|
|
174
|
+
if key.lower() in SENSITIVE_KEYS:
|
|
175
|
+
masked = self._mask_sensitive_value(str(value))
|
|
176
|
+
logger.info(f"Set {key} = {masked}")
|
|
177
|
+
else:
|
|
178
|
+
logger.info(f"Set {key} = {value}")
|
|
179
|
+
|
|
180
|
+
def delete_value(self, key: str) -> bool:
|
|
181
|
+
"""Delete configuration value.
|
|
182
|
+
|
|
183
|
+
Args:
|
|
184
|
+
key: Configuration key to delete
|
|
185
|
+
|
|
186
|
+
Returns:
|
|
187
|
+
True if key was present and deleted, False otherwise
|
|
188
|
+
"""
|
|
189
|
+
config = self.load_config()
|
|
190
|
+
if key in config:
|
|
191
|
+
del config[key]
|
|
192
|
+
self.save_config(config)
|
|
193
|
+
logger.info(f"Deleted {key} from config")
|
|
194
|
+
return True
|
|
195
|
+
return False
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
def get_openrouter_api_key(config_dir: Path | None = None) -> str | None:
|
|
199
|
+
"""Get OpenRouter API key from environment or config file.
|
|
200
|
+
|
|
201
|
+
Priority order:
|
|
202
|
+
1. OPENROUTER_API_KEY environment variable
|
|
203
|
+
2. openrouter_api_key in config file
|
|
204
|
+
|
|
205
|
+
Args:
|
|
206
|
+
config_dir: Config directory path (uses .mcp-vector-search in cwd if None)
|
|
207
|
+
|
|
208
|
+
Returns:
|
|
209
|
+
API key if found, None otherwise
|
|
210
|
+
"""
|
|
211
|
+
# Check environment variable first (highest priority)
|
|
212
|
+
api_key = os.environ.get("OPENROUTER_API_KEY")
|
|
213
|
+
if api_key:
|
|
214
|
+
logger.debug("Using OpenRouter API key from environment variable")
|
|
215
|
+
return api_key
|
|
216
|
+
|
|
217
|
+
# Check config file
|
|
218
|
+
if config_dir is None:
|
|
219
|
+
config_dir = Path.cwd() / ".mcp-vector-search"
|
|
220
|
+
|
|
221
|
+
if not config_dir.exists():
|
|
222
|
+
logger.debug(f"Config directory not found: {config_dir}")
|
|
223
|
+
return None
|
|
224
|
+
|
|
225
|
+
manager = ConfigManager(config_dir)
|
|
226
|
+
api_key = manager.get_value("openrouter_api_key")
|
|
227
|
+
|
|
228
|
+
if api_key:
|
|
229
|
+
logger.debug("Using OpenRouter API key from config file")
|
|
230
|
+
return api_key
|
|
231
|
+
|
|
232
|
+
logger.debug("OpenRouter API key not found in environment or config")
|
|
233
|
+
return None
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
def save_openrouter_api_key(api_key: str, config_dir: Path) -> None:
|
|
237
|
+
"""Save OpenRouter API key to config file.
|
|
238
|
+
|
|
239
|
+
Args:
|
|
240
|
+
api_key: API key to save
|
|
241
|
+
config_dir: Config directory path
|
|
242
|
+
|
|
243
|
+
Raises:
|
|
244
|
+
ValueError: If api_key is empty
|
|
245
|
+
OSError: If file write fails
|
|
246
|
+
"""
|
|
247
|
+
if not api_key or not api_key.strip():
|
|
248
|
+
raise ValueError("API key cannot be empty")
|
|
249
|
+
|
|
250
|
+
manager = ConfigManager(config_dir)
|
|
251
|
+
manager.set_value("openrouter_api_key", api_key.strip())
|
|
252
|
+
|
|
253
|
+
logger.info(
|
|
254
|
+
f"Saved OpenRouter API key to {manager.config_file} "
|
|
255
|
+
f"(last 4 chars: {api_key[-4:]})"
|
|
256
|
+
)
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+
def delete_openrouter_api_key(config_dir: Path) -> bool:
|
|
260
|
+
"""Delete OpenRouter API key from config file.
|
|
261
|
+
|
|
262
|
+
Args:
|
|
263
|
+
config_dir: Config directory path
|
|
264
|
+
|
|
265
|
+
Returns:
|
|
266
|
+
True if key was deleted, False if not found
|
|
267
|
+
"""
|
|
268
|
+
manager = ConfigManager(config_dir)
|
|
269
|
+
return manager.delete_value("openrouter_api_key")
|
|
270
|
+
|
|
271
|
+
|
|
272
|
+
def get_openai_api_key(config_dir: Path | None = None) -> str | None:
|
|
273
|
+
"""Get OpenAI API key from environment or config file.
|
|
274
|
+
|
|
275
|
+
Priority order:
|
|
276
|
+
1. OPENAI_API_KEY environment variable
|
|
277
|
+
2. openai_api_key in config file
|
|
278
|
+
|
|
279
|
+
Args:
|
|
280
|
+
config_dir: Config directory path (uses .mcp-vector-search in cwd if None)
|
|
281
|
+
|
|
282
|
+
Returns:
|
|
283
|
+
API key if found, None otherwise
|
|
284
|
+
"""
|
|
285
|
+
# Check environment variable first (highest priority)
|
|
286
|
+
api_key = os.environ.get("OPENAI_API_KEY")
|
|
287
|
+
if api_key:
|
|
288
|
+
logger.debug("Using OpenAI API key from environment variable")
|
|
289
|
+
return api_key
|
|
290
|
+
|
|
291
|
+
# Check config file
|
|
292
|
+
if config_dir is None:
|
|
293
|
+
config_dir = Path.cwd() / ".mcp-vector-search"
|
|
294
|
+
|
|
295
|
+
if not config_dir.exists():
|
|
296
|
+
logger.debug(f"Config directory not found: {config_dir}")
|
|
297
|
+
return None
|
|
298
|
+
|
|
299
|
+
manager = ConfigManager(config_dir)
|
|
300
|
+
api_key = manager.get_value("openai_api_key")
|
|
301
|
+
|
|
302
|
+
if api_key:
|
|
303
|
+
logger.debug("Using OpenAI API key from config file")
|
|
304
|
+
return api_key
|
|
305
|
+
|
|
306
|
+
logger.debug("OpenAI API key not found in environment or config")
|
|
307
|
+
return None
|
|
308
|
+
|
|
309
|
+
|
|
310
|
+
def save_openai_api_key(api_key: str, config_dir: Path) -> None:
|
|
311
|
+
"""Save OpenAI API key to config file.
|
|
312
|
+
|
|
313
|
+
Args:
|
|
314
|
+
api_key: API key to save
|
|
315
|
+
config_dir: Config directory path
|
|
316
|
+
|
|
317
|
+
Raises:
|
|
318
|
+
ValueError: If api_key is empty
|
|
319
|
+
OSError: If file write fails
|
|
320
|
+
"""
|
|
321
|
+
if not api_key or not api_key.strip():
|
|
322
|
+
raise ValueError("API key cannot be empty")
|
|
323
|
+
|
|
324
|
+
manager = ConfigManager(config_dir)
|
|
325
|
+
manager.set_value("openai_api_key", api_key.strip())
|
|
326
|
+
|
|
327
|
+
logger.info(
|
|
328
|
+
f"Saved OpenAI API key to {manager.config_file} (last 4 chars: {api_key[-4:]})"
|
|
329
|
+
)
|
|
330
|
+
|
|
331
|
+
|
|
332
|
+
def delete_openai_api_key(config_dir: Path) -> bool:
|
|
333
|
+
"""Delete OpenAI API key from config file.
|
|
334
|
+
|
|
335
|
+
Args:
|
|
336
|
+
config_dir: Config directory path
|
|
337
|
+
|
|
338
|
+
Returns:
|
|
339
|
+
True if key was deleted, False if not found
|
|
340
|
+
"""
|
|
341
|
+
manager = ConfigManager(config_dir)
|
|
342
|
+
return manager.delete_value("openai_api_key")
|
|
343
|
+
|
|
344
|
+
|
|
345
|
+
def get_preferred_llm_provider(config_dir: Path | None = None) -> str | None:
|
|
346
|
+
"""Get preferred LLM provider from config file.
|
|
347
|
+
|
|
348
|
+
Args:
|
|
349
|
+
config_dir: Config directory path (uses .mcp-vector-search in cwd if None)
|
|
350
|
+
|
|
351
|
+
Returns:
|
|
352
|
+
Provider name ('openai' or 'openrouter') if set, None otherwise
|
|
353
|
+
"""
|
|
354
|
+
if config_dir is None:
|
|
355
|
+
config_dir = Path.cwd() / ".mcp-vector-search"
|
|
356
|
+
|
|
357
|
+
if not config_dir.exists():
|
|
358
|
+
return None
|
|
359
|
+
|
|
360
|
+
manager = ConfigManager(config_dir)
|
|
361
|
+
return manager.get_value("preferred_llm_provider")
|
|
362
|
+
|
|
363
|
+
|
|
364
|
+
def save_preferred_llm_provider(provider: str, config_dir: Path) -> None:
|
|
365
|
+
"""Save preferred LLM provider to config file.
|
|
366
|
+
|
|
367
|
+
Args:
|
|
368
|
+
provider: Provider name ('openai' or 'openrouter')
|
|
369
|
+
config_dir: Config directory path
|
|
370
|
+
|
|
371
|
+
Raises:
|
|
372
|
+
ValueError: If provider is not valid
|
|
373
|
+
"""
|
|
374
|
+
if provider not in ("openai", "openrouter"):
|
|
375
|
+
raise ValueError(
|
|
376
|
+
f"Invalid provider: {provider}. Must be 'openai' or 'openrouter'"
|
|
377
|
+
)
|
|
378
|
+
|
|
379
|
+
manager = ConfigManager(config_dir)
|
|
380
|
+
manager.set_value("preferred_llm_provider", provider)
|
|
381
|
+
|
|
382
|
+
|
|
383
|
+
def get_config_file_path(config_dir: Path | None = None) -> Path:
|
|
384
|
+
"""Get path to config file.
|
|
385
|
+
|
|
386
|
+
Args:
|
|
387
|
+
config_dir: Config directory path (uses .mcp-vector-search in cwd if None)
|
|
388
|
+
|
|
389
|
+
Returns:
|
|
390
|
+
Path to config file
|
|
391
|
+
"""
|
|
392
|
+
if config_dir is None:
|
|
393
|
+
config_dir = Path.cwd() / ".mcp-vector-search"
|
|
394
|
+
return config_dir / CONFIG_FILENAME
|