mcp-code-indexer 4.0.1__py3-none-any.whl → 4.1.0__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.
- mcp_code_indexer/__init__.py +7 -5
- mcp_code_indexer/ask_handler.py +2 -2
- mcp_code_indexer/claude_api_handler.py +10 -5
- mcp_code_indexer/cleanup_manager.py +20 -12
- mcp_code_indexer/commands/makelocal.py +85 -63
- mcp_code_indexer/data/stop_words_english.txt +1 -1
- mcp_code_indexer/database/connection_health.py +29 -20
- mcp_code_indexer/database/database.py +44 -31
- mcp_code_indexer/database/database_factory.py +19 -20
- mcp_code_indexer/database/exceptions.py +10 -10
- mcp_code_indexer/database/models.py +126 -1
- mcp_code_indexer/database/path_resolver.py +22 -21
- mcp_code_indexer/database/retry_executor.py +37 -19
- mcp_code_indexer/deepask_handler.py +3 -3
- mcp_code_indexer/error_handler.py +46 -20
- mcp_code_indexer/file_scanner.py +15 -12
- mcp_code_indexer/git_hook_handler.py +71 -76
- mcp_code_indexer/logging_config.py +13 -5
- mcp_code_indexer/main.py +85 -22
- mcp_code_indexer/middleware/__init__.py +1 -1
- mcp_code_indexer/middleware/auth.py +47 -43
- mcp_code_indexer/middleware/error_middleware.py +15 -15
- mcp_code_indexer/middleware/logging.py +44 -42
- mcp_code_indexer/middleware/security.py +84 -76
- mcp_code_indexer/migrations/002_performance_indexes.sql +1 -1
- mcp_code_indexer/migrations/004_remove_branch_dependency.sql +14 -14
- mcp_code_indexer/migrations/006_vector_mode.sql +189 -0
- mcp_code_indexer/query_preprocessor.py +2 -2
- mcp_code_indexer/server/mcp_server.py +158 -94
- mcp_code_indexer/transport/__init__.py +1 -1
- mcp_code_indexer/transport/base.py +19 -17
- mcp_code_indexer/transport/http_transport.py +89 -76
- mcp_code_indexer/transport/stdio_transport.py +12 -8
- mcp_code_indexer/vector_mode/__init__.py +36 -0
- mcp_code_indexer/vector_mode/chunking/__init__.py +19 -0
- mcp_code_indexer/vector_mode/chunking/ast_chunker.py +403 -0
- mcp_code_indexer/vector_mode/chunking/chunk_optimizer.py +500 -0
- mcp_code_indexer/vector_mode/chunking/language_handlers.py +478 -0
- mcp_code_indexer/vector_mode/config.py +155 -0
- mcp_code_indexer/vector_mode/daemon.py +335 -0
- mcp_code_indexer/vector_mode/monitoring/__init__.py +19 -0
- mcp_code_indexer/vector_mode/monitoring/change_detector.py +312 -0
- mcp_code_indexer/vector_mode/monitoring/file_watcher.py +445 -0
- mcp_code_indexer/vector_mode/monitoring/merkle_tree.py +418 -0
- mcp_code_indexer/vector_mode/providers/__init__.py +72 -0
- mcp_code_indexer/vector_mode/providers/base_provider.py +230 -0
- mcp_code_indexer/vector_mode/providers/turbopuffer_client.py +338 -0
- mcp_code_indexer/vector_mode/providers/voyage_client.py +212 -0
- mcp_code_indexer/vector_mode/security/__init__.py +11 -0
- mcp_code_indexer/vector_mode/security/patterns.py +297 -0
- mcp_code_indexer/vector_mode/security/redactor.py +368 -0
- {mcp_code_indexer-4.0.1.dist-info → mcp_code_indexer-4.1.0.dist-info}/METADATA +82 -24
- mcp_code_indexer-4.1.0.dist-info/RECORD +66 -0
- mcp_code_indexer-4.0.1.dist-info/RECORD +0 -47
- {mcp_code_indexer-4.0.1.dist-info → mcp_code_indexer-4.1.0.dist-info}/LICENSE +0 -0
- {mcp_code_indexer-4.0.1.dist-info → mcp_code_indexer-4.1.0.dist-info}/WHEEL +0 -0
- {mcp_code_indexer-4.0.1.dist-info → mcp_code_indexer-4.1.0.dist-info}/entry_points.txt +0 -0
|
@@ -7,7 +7,7 @@ Provides common interface and functionality for different transport methods.
|
|
|
7
7
|
import asyncio
|
|
8
8
|
import logging
|
|
9
9
|
from abc import ABC, abstractmethod
|
|
10
|
-
from typing import Any
|
|
10
|
+
from typing import Any
|
|
11
11
|
|
|
12
12
|
logger = logging.getLogger(__name__)
|
|
13
13
|
|
|
@@ -15,66 +15,68 @@ logger = logging.getLogger(__name__)
|
|
|
15
15
|
class Transport(ABC):
|
|
16
16
|
"""
|
|
17
17
|
Abstract base class for MCP transport implementations.
|
|
18
|
-
|
|
18
|
+
|
|
19
19
|
Defines the common interface that all transports must implement
|
|
20
20
|
to work with the MCPCodeIndexServer.
|
|
21
21
|
"""
|
|
22
|
-
|
|
22
|
+
|
|
23
23
|
def __init__(self, server_instance: Any):
|
|
24
24
|
"""
|
|
25
25
|
Initialize transport with server instance.
|
|
26
|
-
|
|
26
|
+
|
|
27
27
|
Args:
|
|
28
28
|
server_instance: The MCPCodeIndexServer instance
|
|
29
29
|
"""
|
|
30
30
|
self.server = server_instance
|
|
31
31
|
self.logger = logger.getChild(self.__class__.__name__)
|
|
32
|
-
|
|
32
|
+
|
|
33
33
|
@abstractmethod
|
|
34
34
|
async def run(self) -> None:
|
|
35
35
|
"""
|
|
36
36
|
Run the transport server.
|
|
37
|
-
|
|
37
|
+
|
|
38
38
|
This method should handle the main server loop and connection
|
|
39
39
|
management for the specific transport type.
|
|
40
40
|
"""
|
|
41
41
|
pass
|
|
42
|
-
|
|
42
|
+
|
|
43
43
|
async def initialize(self) -> None:
|
|
44
44
|
"""
|
|
45
45
|
Initialize transport-specific resources.
|
|
46
|
-
|
|
46
|
+
|
|
47
47
|
Called before run() to set up any transport-specific state.
|
|
48
48
|
Default implementation does nothing.
|
|
49
49
|
"""
|
|
50
50
|
pass
|
|
51
|
-
|
|
51
|
+
|
|
52
52
|
async def cleanup(self) -> None:
|
|
53
53
|
"""
|
|
54
54
|
Clean up transport-specific resources.
|
|
55
|
-
|
|
55
|
+
|
|
56
56
|
Called when shutting down to clean up connections and resources.
|
|
57
57
|
Default implementation does nothing.
|
|
58
58
|
"""
|
|
59
59
|
pass
|
|
60
|
-
|
|
61
|
-
async def _run_with_retry(
|
|
60
|
+
|
|
61
|
+
async def _run_with_retry(
|
|
62
|
+
self, max_retries: int = 3, base_delay: float = 1.0
|
|
63
|
+
) -> None:
|
|
62
64
|
"""
|
|
63
65
|
Run transport with exponential backoff retry logic.
|
|
64
|
-
|
|
66
|
+
|
|
65
67
|
Args:
|
|
66
68
|
max_retries: Maximum number of retry attempts
|
|
67
69
|
base_delay: Base delay between retries in seconds
|
|
68
70
|
"""
|
|
69
71
|
retry_count = 0
|
|
70
|
-
|
|
72
|
+
|
|
71
73
|
while retry_count <= max_retries:
|
|
72
74
|
try:
|
|
73
75
|
await self.run()
|
|
74
76
|
break
|
|
75
77
|
except Exception as e:
|
|
76
78
|
retry_count += 1
|
|
77
|
-
|
|
79
|
+
|
|
78
80
|
if retry_count > max_retries:
|
|
79
81
|
self.logger.error(
|
|
80
82
|
"Transport failed after %d attempts",
|
|
@@ -88,7 +90,7 @@ class Transport(ABC):
|
|
|
88
90
|
},
|
|
89
91
|
)
|
|
90
92
|
raise
|
|
91
|
-
|
|
93
|
+
|
|
92
94
|
delay = base_delay * (2 ** (retry_count - 1))
|
|
93
95
|
self.logger.warning(
|
|
94
96
|
"Transport failed, retrying in %.1f seconds (attempt %d/%d)",
|
|
@@ -104,5 +106,5 @@ class Transport(ABC):
|
|
|
104
106
|
}
|
|
105
107
|
},
|
|
106
108
|
)
|
|
107
|
-
|
|
109
|
+
|
|
108
110
|
await asyncio.sleep(delay)
|
|
@@ -8,17 +8,16 @@ Server-Sent Events for streaming responses.
|
|
|
8
8
|
import asyncio
|
|
9
9
|
import json
|
|
10
10
|
import logging
|
|
11
|
-
import uuid
|
|
12
11
|
from contextlib import asynccontextmanager
|
|
13
|
-
from typing import Any, Dict, List, Optional
|
|
12
|
+
from typing import Any, AsyncGenerator, Dict, List, Optional
|
|
14
13
|
|
|
15
14
|
try:
|
|
16
|
-
|
|
17
|
-
from fastapi
|
|
15
|
+
import uvicorn
|
|
16
|
+
from fastapi import Depends, FastAPI, HTTPException
|
|
18
17
|
from fastapi.middleware.cors import CORSMiddleware
|
|
19
|
-
from fastapi.
|
|
18
|
+
from fastapi.responses import StreamingResponse
|
|
19
|
+
from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer
|
|
20
20
|
from pydantic import BaseModel, ValidationError
|
|
21
|
-
import uvicorn
|
|
22
21
|
except ImportError as e:
|
|
23
22
|
raise ImportError(
|
|
24
23
|
"HTTP transport dependencies not installed. "
|
|
@@ -26,18 +25,19 @@ except ImportError as e:
|
|
|
26
25
|
"Please reinstall mcp-code-indexer."
|
|
27
26
|
) from e
|
|
28
27
|
|
|
29
|
-
from .
|
|
28
|
+
from ..middleware.auth import HTTPAuthMiddleware
|
|
30
29
|
|
|
31
30
|
# Import middleware
|
|
32
31
|
from ..middleware.logging import HTTPLoggingMiddleware, HTTPMetricsCollector
|
|
33
|
-
from ..middleware.auth import HTTPAuthMiddleware
|
|
34
32
|
from ..middleware.security import HTTPSecurityMiddleware
|
|
33
|
+
from .base import Transport
|
|
35
34
|
|
|
36
35
|
logger = logging.getLogger(__name__)
|
|
37
36
|
|
|
38
37
|
|
|
39
38
|
class MCPRequest(BaseModel):
|
|
40
39
|
"""MCP JSON-RPC request model."""
|
|
40
|
+
|
|
41
41
|
jsonrpc: str = "2.0"
|
|
42
42
|
method: str
|
|
43
43
|
params: Dict[str, Any]
|
|
@@ -46,6 +46,7 @@ class MCPRequest(BaseModel):
|
|
|
46
46
|
|
|
47
47
|
class MCPResponse(BaseModel):
|
|
48
48
|
"""MCP JSON-RPC response model."""
|
|
49
|
+
|
|
49
50
|
jsonrpc: str = "2.0"
|
|
50
51
|
result: Optional[Any] = None
|
|
51
52
|
error: Optional[Dict[str, Any]] = None
|
|
@@ -55,11 +56,11 @@ class MCPResponse(BaseModel):
|
|
|
55
56
|
class HTTPTransport(Transport):
|
|
56
57
|
"""
|
|
57
58
|
HTTP transport implementation using FastAPI.
|
|
58
|
-
|
|
59
|
+
|
|
59
60
|
Provides REST API endpoints for MCP tools with optional authentication
|
|
60
61
|
and Server-Sent Events for streaming responses.
|
|
61
62
|
"""
|
|
62
|
-
|
|
63
|
+
|
|
63
64
|
def __init__(
|
|
64
65
|
self,
|
|
65
66
|
server_instance: Any,
|
|
@@ -70,7 +71,7 @@ class HTTPTransport(Transport):
|
|
|
70
71
|
):
|
|
71
72
|
"""
|
|
72
73
|
Initialize HTTP transport.
|
|
73
|
-
|
|
74
|
+
|
|
74
75
|
Args:
|
|
75
76
|
server_instance: The MCPCodeIndexServer instance
|
|
76
77
|
host: Host to bind the server to
|
|
@@ -83,26 +84,26 @@ class HTTPTransport(Transport):
|
|
|
83
84
|
self.port = port
|
|
84
85
|
self.auth_token = auth_token
|
|
85
86
|
self.cors_origins = cors_origins or ["*"]
|
|
86
|
-
|
|
87
|
+
|
|
87
88
|
# Connection management
|
|
88
89
|
self.active_connections: Dict[str, asyncio.Queue] = {}
|
|
89
90
|
self.app: Optional[FastAPI] = None
|
|
90
|
-
|
|
91
|
+
|
|
91
92
|
# Metrics collection
|
|
92
93
|
self.metrics = HTTPMetricsCollector()
|
|
93
|
-
|
|
94
|
+
|
|
94
95
|
async def initialize(self) -> None:
|
|
95
96
|
"""Initialize FastAPI application and routes."""
|
|
96
97
|
self.app = await self._create_app()
|
|
97
|
-
|
|
98
|
+
|
|
98
99
|
@asynccontextmanager
|
|
99
|
-
async def _lifespan(self, app: FastAPI):
|
|
100
|
+
async def _lifespan(self, app: FastAPI) -> AsyncGenerator[None, None]:
|
|
100
101
|
"""FastAPI lifespan context manager."""
|
|
101
102
|
self.logger.info("HTTP transport starting up")
|
|
102
103
|
yield
|
|
103
104
|
self.logger.info("HTTP transport shutting down")
|
|
104
105
|
await self.cleanup()
|
|
105
|
-
|
|
106
|
+
|
|
106
107
|
async def _create_app(self) -> FastAPI:
|
|
107
108
|
"""Create and configure FastAPI application."""
|
|
108
109
|
app = FastAPI(
|
|
@@ -111,23 +112,23 @@ class HTTPTransport(Transport):
|
|
|
111
112
|
version="1.0.0",
|
|
112
113
|
lifespan=self._lifespan,
|
|
113
114
|
)
|
|
114
|
-
|
|
115
|
+
|
|
115
116
|
# Add middleware stack (in reverse order of execution)
|
|
116
|
-
|
|
117
|
+
|
|
117
118
|
# Security middleware (outermost)
|
|
118
119
|
app.add_middleware(HTTPSecurityMiddleware)
|
|
119
|
-
|
|
120
|
+
|
|
120
121
|
# Logging middleware
|
|
121
122
|
app.add_middleware(HTTPLoggingMiddleware)
|
|
122
|
-
|
|
123
|
+
|
|
123
124
|
# Authentication middleware
|
|
124
125
|
if self.auth_token:
|
|
125
126
|
app.add_middleware(
|
|
126
127
|
HTTPAuthMiddleware,
|
|
127
128
|
auth_token=self.auth_token,
|
|
128
|
-
public_paths=["/health", "/docs", "/openapi.json", "/metrics"]
|
|
129
|
+
public_paths=["/health", "/docs", "/openapi.json", "/metrics"],
|
|
129
130
|
)
|
|
130
|
-
|
|
131
|
+
|
|
131
132
|
# CORS middleware (innermost, applied first)
|
|
132
133
|
app.add_middleware(
|
|
133
134
|
CORSMiddleware,
|
|
@@ -136,62 +137,64 @@ class HTTPTransport(Transport):
|
|
|
136
137
|
allow_methods=["GET", "POST", "OPTIONS"],
|
|
137
138
|
allow_headers=["*"],
|
|
138
139
|
)
|
|
139
|
-
|
|
140
|
+
|
|
140
141
|
# Authentication dependency
|
|
141
142
|
security = HTTPBearer(auto_error=False) if self.auth_token else None
|
|
142
|
-
|
|
143
|
+
|
|
143
144
|
async def verify_token(
|
|
144
|
-
credentials: Optional[HTTPAuthorizationCredentials] = Depends(security)
|
|
145
|
+
credentials: Optional[HTTPAuthorizationCredentials] = Depends(security),
|
|
145
146
|
) -> bool:
|
|
146
147
|
"""Verify Bearer token if authentication is enabled."""
|
|
147
148
|
if not self.auth_token:
|
|
148
149
|
return True
|
|
149
|
-
|
|
150
|
+
|
|
150
151
|
if not credentials or credentials.credentials != self.auth_token:
|
|
151
152
|
raise HTTPException(
|
|
152
|
-
status_code=401,
|
|
153
|
-
detail="Invalid authentication token"
|
|
153
|
+
status_code=401, detail="Invalid authentication token"
|
|
154
154
|
)
|
|
155
155
|
return True
|
|
156
|
-
|
|
156
|
+
|
|
157
157
|
@app.get("/health")
|
|
158
|
-
async def health_check():
|
|
158
|
+
async def health_check() -> Dict[str, str]:
|
|
159
159
|
"""Health check endpoint."""
|
|
160
160
|
return {"status": "healthy", "transport": "http"}
|
|
161
|
-
|
|
161
|
+
|
|
162
162
|
@app.get("/metrics")
|
|
163
|
-
async def get_metrics(
|
|
163
|
+
async def get_metrics(
|
|
164
|
+
authenticated: bool = Depends(verify_token),
|
|
165
|
+
) -> Dict[str, Any]:
|
|
164
166
|
"""Get HTTP transport metrics."""
|
|
165
167
|
metrics = {}
|
|
166
|
-
|
|
168
|
+
|
|
167
169
|
metrics["http"] = self.metrics.get_metrics()
|
|
168
|
-
|
|
170
|
+
|
|
169
171
|
# Add connection stats
|
|
170
172
|
metrics["connections"] = {
|
|
171
173
|
"active_sse_connections": len(self.active_connections),
|
|
172
174
|
"connection_ids": list(self.active_connections.keys()),
|
|
173
175
|
}
|
|
174
|
-
|
|
176
|
+
|
|
175
177
|
return metrics
|
|
176
|
-
|
|
178
|
+
|
|
177
179
|
@app.get("/tools")
|
|
178
|
-
async def list_tools(
|
|
180
|
+
async def list_tools(
|
|
181
|
+
authenticated: bool = Depends(verify_token),
|
|
182
|
+
) -> Dict[str, Any]:
|
|
179
183
|
"""List available MCP tools."""
|
|
180
184
|
tools = await self.server._handle_list_tools()
|
|
181
185
|
return {"tools": tools}
|
|
182
|
-
|
|
186
|
+
|
|
183
187
|
@app.post("/mcp", response_model=MCPResponse)
|
|
184
188
|
async def handle_mcp_request(
|
|
185
|
-
request: MCPRequest,
|
|
186
|
-
|
|
187
|
-
):
|
|
189
|
+
request: MCPRequest, authenticated: bool = Depends(verify_token)
|
|
190
|
+
) -> MCPResponse:
|
|
188
191
|
"""Handle MCP JSON-RPC requests."""
|
|
189
192
|
try:
|
|
190
193
|
# Route to appropriate tool handler
|
|
191
194
|
if request.method == "tools/call":
|
|
192
195
|
tool_name = request.params.get("name")
|
|
193
196
|
tool_arguments = request.params.get("arguments", {})
|
|
194
|
-
|
|
197
|
+
|
|
195
198
|
# Map tool names to handler methods
|
|
196
199
|
tool_handlers = {
|
|
197
200
|
"get_file_description": self.server._handle_get_file_description,
|
|
@@ -206,28 +209,34 @@ class HTTPTransport(Transport):
|
|
|
206
209
|
"search_codebase_overview": self.server._handle_search_codebase_overview,
|
|
207
210
|
"check_database_health": self.server._handle_check_database_health,
|
|
208
211
|
}
|
|
209
|
-
|
|
212
|
+
|
|
210
213
|
if tool_name not in tool_handlers:
|
|
211
214
|
return MCPResponse(
|
|
212
215
|
id=request.id,
|
|
213
|
-
error={
|
|
216
|
+
error={
|
|
217
|
+
"code": -32601,
|
|
218
|
+
"message": f"Unknown tool: {tool_name}",
|
|
219
|
+
},
|
|
214
220
|
)
|
|
215
|
-
|
|
221
|
+
|
|
216
222
|
# Execute tool handler
|
|
217
223
|
result = await tool_handlers[tool_name](tool_arguments)
|
|
218
|
-
|
|
224
|
+
|
|
219
225
|
return MCPResponse(id=request.id, result=result)
|
|
220
|
-
|
|
226
|
+
|
|
221
227
|
else:
|
|
222
228
|
return MCPResponse(
|
|
223
229
|
id=request.id,
|
|
224
|
-
error={
|
|
230
|
+
error={
|
|
231
|
+
"code": -32601,
|
|
232
|
+
"message": f"Unknown method: {request.method}",
|
|
233
|
+
},
|
|
225
234
|
)
|
|
226
|
-
|
|
235
|
+
|
|
227
236
|
except ValidationError as e:
|
|
228
237
|
return MCPResponse(
|
|
229
238
|
id=request.id,
|
|
230
|
-
error={"code": -32602, "message": f"Invalid params: {str(e)}"}
|
|
239
|
+
error={"code": -32602, "message": f"Invalid params: {str(e)}"},
|
|
231
240
|
)
|
|
232
241
|
except Exception as e:
|
|
233
242
|
self.logger.error(
|
|
@@ -242,56 +251,58 @@ class HTTPTransport(Transport):
|
|
|
242
251
|
)
|
|
243
252
|
return MCPResponse(
|
|
244
253
|
id=request.id,
|
|
245
|
-
error={"code": -32603, "message": f"Internal error: {str(e)}"}
|
|
254
|
+
error={"code": -32603, "message": f"Internal error: {str(e)}"},
|
|
246
255
|
)
|
|
247
|
-
|
|
256
|
+
|
|
248
257
|
@app.get("/events/{connection_id}")
|
|
249
258
|
async def server_sent_events(
|
|
250
|
-
connection_id: str,
|
|
251
|
-
|
|
252
|
-
):
|
|
259
|
+
connection_id: str, authenticated: bool = Depends(verify_token)
|
|
260
|
+
) -> Any:
|
|
253
261
|
"""Server-Sent Events endpoint for streaming responses."""
|
|
254
|
-
|
|
262
|
+
|
|
263
|
+
async def event_stream() -> AsyncGenerator[str, None]:
|
|
255
264
|
# Create connection queue
|
|
256
|
-
queue = asyncio.Queue()
|
|
265
|
+
queue: asyncio.Queue[Dict[str, Any]] = asyncio.Queue()
|
|
257
266
|
self.active_connections[connection_id] = queue
|
|
258
|
-
|
|
267
|
+
|
|
259
268
|
try:
|
|
260
269
|
while True:
|
|
261
270
|
# Wait for events from the queue
|
|
262
271
|
try:
|
|
263
|
-
event_data = await asyncio.wait_for(
|
|
272
|
+
event_data = await asyncio.wait_for(
|
|
273
|
+
queue.get(), timeout=30.0
|
|
274
|
+
)
|
|
264
275
|
yield f"data: {json.dumps(event_data)}\\n\\n"
|
|
265
276
|
except asyncio.TimeoutError:
|
|
266
277
|
# Send keepalive
|
|
267
278
|
yield f"data: {json.dumps({'type': 'keepalive'})}\\n\\n"
|
|
268
|
-
|
|
279
|
+
|
|
269
280
|
except asyncio.CancelledError:
|
|
270
281
|
pass
|
|
271
282
|
finally:
|
|
272
283
|
# Clean up connection
|
|
273
284
|
self.active_connections.pop(connection_id, None)
|
|
274
|
-
|
|
285
|
+
|
|
275
286
|
return StreamingResponse(
|
|
276
287
|
event_stream(),
|
|
277
288
|
media_type="text/event-stream",
|
|
278
289
|
headers={
|
|
279
290
|
"Cache-Control": "no-cache",
|
|
280
291
|
"Connection": "keep-alive",
|
|
281
|
-
}
|
|
292
|
+
},
|
|
282
293
|
)
|
|
283
|
-
|
|
294
|
+
|
|
284
295
|
return app
|
|
285
|
-
|
|
296
|
+
|
|
286
297
|
async def run(self) -> None:
|
|
287
298
|
"""
|
|
288
299
|
Run the HTTP transport server.
|
|
289
|
-
|
|
300
|
+
|
|
290
301
|
Starts the uvicorn ASGI server with the configured FastAPI application.
|
|
291
302
|
"""
|
|
292
303
|
if not self.app:
|
|
293
304
|
await self.initialize()
|
|
294
|
-
|
|
305
|
+
|
|
295
306
|
self.logger.info(
|
|
296
307
|
"Starting HTTP transport",
|
|
297
308
|
extra={
|
|
@@ -304,8 +315,10 @@ class HTTPTransport(Transport):
|
|
|
304
315
|
}
|
|
305
316
|
},
|
|
306
317
|
)
|
|
307
|
-
|
|
318
|
+
|
|
308
319
|
# Configure uvicorn
|
|
320
|
+
if not self.app:
|
|
321
|
+
raise RuntimeError("FastAPI app not initialized")
|
|
309
322
|
config = uvicorn.Config(
|
|
310
323
|
app=self.app,
|
|
311
324
|
host=self.host,
|
|
@@ -314,9 +327,9 @@ class HTTPTransport(Transport):
|
|
|
314
327
|
access_log=True,
|
|
315
328
|
loop="asyncio",
|
|
316
329
|
)
|
|
317
|
-
|
|
330
|
+
|
|
318
331
|
server = uvicorn.Server(config)
|
|
319
|
-
|
|
332
|
+
|
|
320
333
|
try:
|
|
321
334
|
await server.serve()
|
|
322
335
|
except Exception as e:
|
|
@@ -330,7 +343,7 @@ class HTTPTransport(Transport):
|
|
|
330
343
|
},
|
|
331
344
|
)
|
|
332
345
|
raise
|
|
333
|
-
|
|
346
|
+
|
|
334
347
|
async def cleanup(self) -> None:
|
|
335
348
|
"""Clean up HTTP transport resources."""
|
|
336
349
|
# Close all active SSE connections
|
|
@@ -339,24 +352,24 @@ class HTTPTransport(Transport):
|
|
|
339
352
|
await queue.put({"type": "disconnect"})
|
|
340
353
|
except Exception as e:
|
|
341
354
|
self.logger.warning(f"Error closing connection {connection_id}: {e}")
|
|
342
|
-
|
|
355
|
+
|
|
343
356
|
self.active_connections.clear()
|
|
344
357
|
self.logger.info("HTTP transport cleanup completed")
|
|
345
|
-
|
|
358
|
+
|
|
346
359
|
async def send_event(self, connection_id: str, event_data: Dict[str, Any]) -> bool:
|
|
347
360
|
"""
|
|
348
361
|
Send an event to a specific SSE connection.
|
|
349
|
-
|
|
362
|
+
|
|
350
363
|
Args:
|
|
351
364
|
connection_id: Connection identifier
|
|
352
365
|
event_data: Event data to send
|
|
353
|
-
|
|
366
|
+
|
|
354
367
|
Returns:
|
|
355
368
|
True if event was sent successfully, False otherwise
|
|
356
369
|
"""
|
|
357
370
|
if connection_id not in self.active_connections:
|
|
358
371
|
return False
|
|
359
|
-
|
|
372
|
+
|
|
360
373
|
try:
|
|
361
374
|
await self.active_connections[connection_id].put(event_data)
|
|
362
375
|
return True
|
|
@@ -19,24 +19,24 @@ logger = logging.getLogger(__name__)
|
|
|
19
19
|
class StdioTransport(Transport):
|
|
20
20
|
"""
|
|
21
21
|
Stdio transport implementation using MCP's built-in stdio server.
|
|
22
|
-
|
|
22
|
+
|
|
23
23
|
This transport maintains compatibility with the existing stdio-based
|
|
24
24
|
MCP protocol while fitting into the transport abstraction.
|
|
25
25
|
"""
|
|
26
|
-
|
|
26
|
+
|
|
27
27
|
def __init__(self, server_instance: Any):
|
|
28
28
|
"""
|
|
29
29
|
Initialize stdio transport.
|
|
30
|
-
|
|
30
|
+
|
|
31
31
|
Args:
|
|
32
32
|
server_instance: The MCPCodeIndexServer instance
|
|
33
33
|
"""
|
|
34
34
|
super().__init__(server_instance)
|
|
35
|
-
|
|
35
|
+
|
|
36
36
|
async def run(self) -> None:
|
|
37
37
|
"""
|
|
38
38
|
Run the stdio transport server.
|
|
39
|
-
|
|
39
|
+
|
|
40
40
|
Uses the mcp library's stdio_server context manager to handle
|
|
41
41
|
JSON-RPC communication over stdin/stdout with retry logic.
|
|
42
42
|
"""
|
|
@@ -49,7 +49,7 @@ class StdioTransport(Transport):
|
|
|
49
49
|
}
|
|
50
50
|
},
|
|
51
51
|
)
|
|
52
|
-
|
|
52
|
+
|
|
53
53
|
max_retries = 5
|
|
54
54
|
base_delay = 2.0 # seconds
|
|
55
55
|
|
|
@@ -59,8 +59,12 @@ class StdioTransport(Transport):
|
|
|
59
59
|
self.logger.info(
|
|
60
60
|
f"stdio_server context established (attempt {attempt + 1})"
|
|
61
61
|
)
|
|
62
|
-
initialization_options =
|
|
63
|
-
|
|
62
|
+
initialization_options = (
|
|
63
|
+
self.server.server.create_initialization_options()
|
|
64
|
+
)
|
|
65
|
+
self.logger.debug(
|
|
66
|
+
f"Initialization options: {initialization_options}"
|
|
67
|
+
)
|
|
64
68
|
|
|
65
69
|
await self.server._run_session_with_retry(
|
|
66
70
|
read_stream, write_stream, initialization_options
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Vector Mode for MCP Code Indexer.
|
|
3
|
+
|
|
4
|
+
This package provides semantic search capabilities using embeddings and vector databases.
|
|
5
|
+
Includes automated file monitoring, AST-based code chunking, and secure embedding generation.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from typing import Optional
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
import os
|
|
11
|
+
|
|
12
|
+
__version__ = "1.0.0"
|
|
13
|
+
|
|
14
|
+
def is_vector_mode_available() -> bool:
|
|
15
|
+
"""Check if vector mode dependencies are available."""
|
|
16
|
+
try:
|
|
17
|
+
import voyage
|
|
18
|
+
import turbopuffer
|
|
19
|
+
import tree_sitter
|
|
20
|
+
import watchdog
|
|
21
|
+
return True
|
|
22
|
+
except ImportError:
|
|
23
|
+
return False
|
|
24
|
+
|
|
25
|
+
def get_vector_config_path() -> Path:
|
|
26
|
+
"""Get path to vector mode configuration."""
|
|
27
|
+
config_dir = Path.home() / ".mcp-code-index" / "vector"
|
|
28
|
+
config_dir.mkdir(parents=True, exist_ok=True)
|
|
29
|
+
return config_dir / "config.yaml"
|
|
30
|
+
|
|
31
|
+
def check_api_keys() -> dict[str, bool]:
|
|
32
|
+
"""Check availability of required API keys."""
|
|
33
|
+
return {
|
|
34
|
+
"voyage": os.getenv("VOYAGE_API_KEY") is not None,
|
|
35
|
+
"turbopuffer": os.getenv("TURBOPUFFER_API_KEY") is not None,
|
|
36
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"""
|
|
2
|
+
AST-based code chunking for vector mode.
|
|
3
|
+
|
|
4
|
+
Provides semantic code chunking using Tree-sitter parsers to extract
|
|
5
|
+
meaningful code units for embedding generation.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from .ast_chunker import ASTChunker, CodeChunk
|
|
9
|
+
from .language_handlers import LanguageHandler, get_language_handler
|
|
10
|
+
from .chunk_optimizer import ChunkOptimizer, OptimizedChunk
|
|
11
|
+
|
|
12
|
+
__all__ = [
|
|
13
|
+
"ASTChunker",
|
|
14
|
+
"CodeChunk",
|
|
15
|
+
"LanguageHandler",
|
|
16
|
+
"get_language_handler",
|
|
17
|
+
"ChunkOptimizer",
|
|
18
|
+
"OptimizedChunk",
|
|
19
|
+
]
|