mcp-code-indexer 3.1.4__py3-none-any.whl → 3.1.5__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 +8 -6
- mcp_code_indexer/ask_handler.py +105 -75
- mcp_code_indexer/claude_api_handler.py +125 -82
- mcp_code_indexer/cleanup_manager.py +107 -81
- mcp_code_indexer/database/connection_health.py +212 -161
- mcp_code_indexer/database/database.py +529 -415
- mcp_code_indexer/database/exceptions.py +167 -118
- mcp_code_indexer/database/models.py +54 -19
- mcp_code_indexer/database/retry_executor.py +139 -103
- mcp_code_indexer/deepask_handler.py +178 -140
- mcp_code_indexer/error_handler.py +88 -76
- mcp_code_indexer/file_scanner.py +163 -141
- mcp_code_indexer/git_hook_handler.py +352 -261
- mcp_code_indexer/logging_config.py +76 -94
- mcp_code_indexer/main.py +406 -320
- mcp_code_indexer/middleware/error_middleware.py +106 -71
- mcp_code_indexer/query_preprocessor.py +40 -40
- mcp_code_indexer/server/mcp_server.py +785 -470
- mcp_code_indexer/token_counter.py +54 -47
- {mcp_code_indexer-3.1.4.dist-info → mcp_code_indexer-3.1.5.dist-info}/METADATA +3 -3
- mcp_code_indexer-3.1.5.dist-info/RECORD +37 -0
- mcp_code_indexer-3.1.4.dist-info/RECORD +0 -37
- {mcp_code_indexer-3.1.4.dist-info → mcp_code_indexer-3.1.5.dist-info}/WHEEL +0 -0
- {mcp_code_indexer-3.1.4.dist-info → mcp_code_indexer-3.1.5.dist-info}/entry_points.txt +0 -0
- {mcp_code_indexer-3.1.4.dist-info → mcp_code_indexer-3.1.5.dist-info}/licenses/LICENSE +0 -0
- {mcp_code_indexer-3.1.4.dist-info → mcp_code_indexer-3.1.5.dist-info}/top_level.txt +0 -0
@@ -19,96 +19,92 @@ def setup_logging(
|
|
19
19
|
log_file: Optional[Path] = None,
|
20
20
|
enable_file_logging: bool = False,
|
21
21
|
max_bytes: int = 50 * 1024 * 1024, # 50MB
|
22
|
-
backup_count: int = 2
|
22
|
+
backup_count: int = 2,
|
23
23
|
) -> logging.Logger:
|
24
24
|
"""
|
25
25
|
Set up comprehensive logging configuration.
|
26
|
-
|
26
|
+
|
27
27
|
Args:
|
28
28
|
log_level: Logging level (DEBUG, INFO, WARNING, ERROR, CRITICAL)
|
29
29
|
log_file: Path to log file (optional)
|
30
30
|
enable_file_logging: Whether to enable file logging
|
31
31
|
max_bytes: Maximum size of log file before rotation
|
32
32
|
backup_count: Number of backup files to keep
|
33
|
-
|
33
|
+
|
34
34
|
Returns:
|
35
35
|
Configured root logger
|
36
36
|
"""
|
37
37
|
# Get root logger
|
38
38
|
root_logger = logging.getLogger()
|
39
39
|
root_logger.setLevel(getattr(logging, log_level.upper()))
|
40
|
-
|
40
|
+
|
41
41
|
# Clear existing handlers
|
42
42
|
root_logger.handlers.clear()
|
43
|
-
|
43
|
+
|
44
44
|
# Console handler (stderr to avoid interfering with MCP stdout)
|
45
45
|
console_handler = logging.StreamHandler(sys.stderr)
|
46
46
|
console_handler.setLevel(getattr(logging, log_level.upper()))
|
47
|
-
|
47
|
+
|
48
48
|
# Use structured formatter for all handlers
|
49
49
|
structured_formatter = StructuredFormatter()
|
50
50
|
console_handler.setFormatter(structured_formatter)
|
51
|
-
|
51
|
+
|
52
52
|
root_logger.addHandler(console_handler)
|
53
|
-
|
53
|
+
|
54
54
|
# File handler (optional)
|
55
55
|
if enable_file_logging and log_file:
|
56
56
|
try:
|
57
57
|
# Ensure log directory exists
|
58
58
|
log_file.parent.mkdir(parents=True, exist_ok=True)
|
59
|
-
|
59
|
+
|
60
60
|
# Rotating file handler
|
61
61
|
if max_bytes > 0:
|
62
62
|
file_handler = logging.handlers.RotatingFileHandler(
|
63
63
|
log_file,
|
64
64
|
maxBytes=max_bytes,
|
65
65
|
backupCount=backup_count,
|
66
|
-
encoding=
|
66
|
+
encoding="utf-8",
|
67
67
|
)
|
68
68
|
else:
|
69
69
|
# No size limit - use regular FileHandler
|
70
|
-
file_handler = logging.FileHandler(
|
71
|
-
log_file,
|
72
|
-
mode='a',
|
73
|
-
encoding='utf-8'
|
74
|
-
)
|
70
|
+
file_handler = logging.FileHandler(log_file, mode="a", encoding="utf-8")
|
75
71
|
file_handler.setLevel(logging.DEBUG) # File gets all levels
|
76
72
|
file_handler.setFormatter(structured_formatter)
|
77
|
-
|
73
|
+
|
78
74
|
root_logger.addHandler(file_handler)
|
79
|
-
|
75
|
+
|
80
76
|
except (OSError, PermissionError) as e:
|
81
77
|
# Log to console if file logging fails
|
82
78
|
root_logger.warning(f"Failed to set up file logging: {e}")
|
83
|
-
|
79
|
+
|
84
80
|
# Configure specific loggers
|
85
|
-
|
81
|
+
|
86
82
|
# Quiet down noisy libraries
|
87
83
|
logging.getLogger("aiosqlite").setLevel(logging.WARNING)
|
88
84
|
logging.getLogger("tiktoken").setLevel(logging.WARNING)
|
89
|
-
|
85
|
+
|
90
86
|
# MCP specific loggers
|
91
87
|
mcp_logger = logging.getLogger("mcp")
|
92
88
|
mcp_logger.setLevel(logging.INFO)
|
93
|
-
|
89
|
+
|
94
90
|
# Database logger
|
95
91
|
db_logger = logging.getLogger("src.database")
|
96
92
|
db_logger.setLevel(logging.INFO)
|
97
|
-
|
93
|
+
|
98
94
|
# Server logger
|
99
95
|
server_logger = logging.getLogger("src.server")
|
100
96
|
server_logger.setLevel(logging.INFO)
|
101
|
-
|
97
|
+
|
102
98
|
return root_logger
|
103
99
|
|
104
100
|
|
105
101
|
def get_logger(name: str) -> logging.Logger:
|
106
102
|
"""
|
107
103
|
Get a logger with the specified name.
|
108
|
-
|
104
|
+
|
109
105
|
Args:
|
110
106
|
name: Logger name (usually __name__)
|
111
|
-
|
107
|
+
|
112
108
|
Returns:
|
113
109
|
Logger instance
|
114
110
|
"""
|
@@ -116,76 +112,71 @@ def get_logger(name: str) -> logging.Logger:
|
|
116
112
|
|
117
113
|
|
118
114
|
def setup_command_logger(
|
119
|
-
command_name: str,
|
120
|
-
cache_dir: Path,
|
121
|
-
log_level: str = "DEBUG"
|
115
|
+
command_name: str, cache_dir: Path, log_level: str = "DEBUG"
|
122
116
|
) -> logging.Logger:
|
123
117
|
"""
|
124
118
|
Set up a dedicated logger for specific commands (runcommand, githook).
|
125
|
-
|
119
|
+
|
126
120
|
Args:
|
127
121
|
command_name: Name of the command (e.g., 'runcommand', 'githook')
|
128
122
|
cache_dir: Cache directory path
|
129
123
|
log_level: Logging level
|
130
|
-
|
124
|
+
|
131
125
|
Returns:
|
132
126
|
Configured logger for the command
|
133
127
|
"""
|
134
128
|
logger_name = f"mcp_code_indexer.{command_name}"
|
135
129
|
logger = logging.getLogger(logger_name)
|
136
|
-
|
130
|
+
|
137
131
|
# Don't propagate to parent loggers to avoid duplicate console output
|
138
132
|
logger.propagate = False
|
139
133
|
logger.setLevel(getattr(logging, log_level.upper()))
|
140
|
-
|
134
|
+
|
141
135
|
# Clear existing handlers
|
142
136
|
logger.handlers.clear()
|
143
|
-
|
137
|
+
|
144
138
|
# Create log file path
|
145
139
|
log_file = cache_dir / f"{command_name}.log"
|
146
|
-
|
140
|
+
|
147
141
|
try:
|
148
142
|
# Ensure cache directory exists
|
149
143
|
cache_dir.mkdir(parents=True, exist_ok=True)
|
150
|
-
|
144
|
+
|
151
145
|
# File handler with 50MB limit
|
152
146
|
file_handler = logging.handlers.RotatingFileHandler(
|
153
|
-
log_file,
|
154
|
-
maxBytes=50 * 1024 * 1024, # 50MB
|
155
|
-
backupCount=2,
|
156
|
-
encoding='utf-8'
|
147
|
+
log_file, maxBytes=50 * 1024 * 1024, backupCount=2, encoding="utf-8" # 50MB
|
157
148
|
)
|
158
149
|
file_handler.setLevel(logging.DEBUG)
|
159
|
-
|
150
|
+
|
160
151
|
# Use structured formatter
|
161
152
|
structured_formatter = StructuredFormatter()
|
162
153
|
file_handler.setFormatter(structured_formatter)
|
163
|
-
|
154
|
+
|
164
155
|
logger.addHandler(file_handler)
|
165
|
-
|
156
|
+
|
166
157
|
# Set up component loggers to also log to this command's log file
|
167
|
-
_setup_component_loggers_for_command(
|
168
|
-
|
158
|
+
_setup_component_loggers_for_command(
|
159
|
+
command_name, file_handler, structured_formatter
|
160
|
+
)
|
161
|
+
|
169
162
|
logger.info(f"=== {command_name.upper()} SESSION STARTED ===")
|
170
|
-
|
163
|
+
|
171
164
|
except (OSError, PermissionError) as e:
|
172
165
|
# Fallback to console logging
|
173
166
|
console_handler = logging.StreamHandler(sys.stderr)
|
174
167
|
console_handler.setFormatter(StructuredFormatter())
|
175
168
|
logger.addHandler(console_handler)
|
176
169
|
logger.warning(f"Failed to set up {command_name} file logging: {e}")
|
177
|
-
|
170
|
+
|
178
171
|
return logger
|
179
172
|
|
180
173
|
|
181
174
|
def _setup_component_loggers_for_command(
|
182
|
-
command_name: str,
|
183
|
-
file_handler: logging.Handler,
|
184
|
-
formatter: logging.Formatter
|
175
|
+
command_name: str, file_handler: logging.Handler, formatter: logging.Formatter
|
185
176
|
) -> None:
|
186
177
|
"""
|
187
178
|
Set up component loggers to also send logs to the command's log file.
|
188
|
-
|
179
|
+
|
189
180
|
Args:
|
190
181
|
command_name: Name of the command
|
191
182
|
file_handler: File handler to add to component loggers
|
@@ -194,49 +185,51 @@ def _setup_component_loggers_for_command(
|
|
194
185
|
# List of component logger names that should also log to command files
|
195
186
|
component_loggers = [
|
196
187
|
"mcp_code_indexer.database.database",
|
197
|
-
"mcp_code_indexer.server.mcp_server",
|
188
|
+
"mcp_code_indexer.server.mcp_server",
|
198
189
|
"mcp_code_indexer.token_counter",
|
199
190
|
"mcp_code_indexer.file_scanner",
|
200
191
|
"mcp_code_indexer.error_handler",
|
201
|
-
|
202
192
|
]
|
203
|
-
|
193
|
+
|
204
194
|
for component_logger_name in component_loggers:
|
205
195
|
component_logger = logging.getLogger(component_logger_name)
|
206
|
-
|
196
|
+
|
207
197
|
# Create a separate handler for this command to avoid interference
|
208
198
|
command_handler = logging.handlers.RotatingFileHandler(
|
209
199
|
file_handler.baseFilename,
|
210
200
|
maxBytes=file_handler.maxBytes,
|
211
201
|
backupCount=file_handler.backupCount,
|
212
|
-
encoding=
|
202
|
+
encoding="utf-8",
|
213
203
|
)
|
214
204
|
command_handler.setLevel(logging.DEBUG)
|
215
205
|
command_handler.setFormatter(formatter)
|
216
|
-
|
206
|
+
|
217
207
|
# Add a marker to identify which command this handler belongs to
|
218
208
|
command_handler._command_name = command_name
|
219
|
-
|
209
|
+
|
220
210
|
# Remove any existing handlers for this command (in case of multiple calls)
|
221
|
-
existing_handlers = [
|
211
|
+
existing_handlers = [
|
212
|
+
h
|
213
|
+
for h in component_logger.handlers
|
214
|
+
if hasattr(h, "_command_name") and h._command_name == command_name
|
215
|
+
]
|
222
216
|
for handler in existing_handlers:
|
223
217
|
component_logger.removeHandler(handler)
|
224
218
|
handler.close()
|
225
|
-
|
219
|
+
|
226
220
|
# Add the new handler
|
227
221
|
component_logger.addHandler(command_handler)
|
228
|
-
component_logger.setLevel(
|
222
|
+
component_logger.setLevel(
|
223
|
+
logging.DEBUG
|
224
|
+
) # Ensure component loggers capture all levels
|
229
225
|
|
230
226
|
|
231
227
|
def log_performance_metrics(
|
232
|
-
logger: logging.Logger,
|
233
|
-
operation: str,
|
234
|
-
duration: float,
|
235
|
-
**metrics
|
228
|
+
logger: logging.Logger, operation: str, duration: float, **metrics
|
236
229
|
) -> None:
|
237
230
|
"""
|
238
231
|
Log performance metrics in structured format.
|
239
|
-
|
232
|
+
|
240
233
|
Args:
|
241
234
|
logger: Logger instance
|
242
235
|
operation: Name of the operation
|
@@ -246,12 +239,12 @@ def log_performance_metrics(
|
|
246
239
|
perf_data = {
|
247
240
|
"operation": operation,
|
248
241
|
"duration_seconds": duration,
|
249
|
-
"metrics": metrics
|
242
|
+
"metrics": metrics,
|
250
243
|
}
|
251
|
-
|
244
|
+
|
252
245
|
logger.info(
|
253
246
|
f"Performance: {operation} completed in {duration:.3f}s",
|
254
|
-
extra={"structured_data": {"performance": perf_data}}
|
247
|
+
extra={"structured_data": {"performance": perf_data}},
|
255
248
|
)
|
256
249
|
|
257
250
|
|
@@ -259,28 +252,25 @@ def log_database_metrics(
|
|
259
252
|
logger: logging.Logger,
|
260
253
|
operation_name: str,
|
261
254
|
metrics: dict,
|
262
|
-
health_status: Optional[dict] = None
|
255
|
+
health_status: Optional[dict] = None,
|
263
256
|
) -> None:
|
264
257
|
"""
|
265
258
|
Log database performance and health metrics.
|
266
|
-
|
259
|
+
|
267
260
|
Args:
|
268
261
|
logger: Logger instance
|
269
262
|
operation_name: Name of the database operation
|
270
263
|
metrics: Database performance metrics
|
271
264
|
health_status: Current health status (optional)
|
272
265
|
"""
|
273
|
-
log_data = {
|
274
|
-
|
275
|
-
"metrics": metrics
|
276
|
-
}
|
277
|
-
|
266
|
+
log_data = {"operation": operation_name, "metrics": metrics}
|
267
|
+
|
278
268
|
if health_status:
|
279
269
|
log_data["health_status"] = health_status
|
280
|
-
|
270
|
+
|
281
271
|
logger.info(
|
282
272
|
f"Database metrics for {operation_name}",
|
283
|
-
extra={"structured_data": {"database_metrics": log_data}}
|
273
|
+
extra={"structured_data": {"database_metrics": log_data}},
|
284
274
|
)
|
285
275
|
|
286
276
|
|
@@ -290,11 +280,11 @@ def log_tool_usage(
|
|
290
280
|
arguments: dict,
|
291
281
|
success: bool,
|
292
282
|
duration: Optional[float] = None,
|
293
|
-
result_size: Optional[int] = None
|
283
|
+
result_size: Optional[int] = None,
|
294
284
|
) -> None:
|
295
285
|
"""
|
296
286
|
Log MCP tool usage for analytics.
|
297
|
-
|
287
|
+
|
298
288
|
Args:
|
299
289
|
logger: Logger instance
|
300
290
|
tool_name: Name of the MCP tool
|
@@ -310,24 +300,16 @@ def log_tool_usage(
|
|
310
300
|
safe_args[key] = f"{value[:50]}..."
|
311
301
|
else:
|
312
302
|
safe_args[key] = value
|
313
|
-
|
314
|
-
usage_data = {
|
315
|
-
|
316
|
-
"arguments": safe_args,
|
317
|
-
"success": success
|
318
|
-
}
|
319
|
-
|
303
|
+
|
304
|
+
usage_data = {"tool_name": tool_name, "arguments": safe_args, "success": success}
|
305
|
+
|
320
306
|
if duration is not None:
|
321
307
|
usage_data["duration_seconds"] = duration
|
322
|
-
|
308
|
+
|
323
309
|
if result_size is not None:
|
324
310
|
usage_data["result_size"] = result_size
|
325
|
-
|
311
|
+
|
326
312
|
level = logging.INFO if success else logging.WARNING
|
327
313
|
message = f"Tool {tool_name}: {'SUCCESS' if success else 'FAILED'}"
|
328
|
-
|
329
|
-
logger.log(
|
330
|
-
level,
|
331
|
-
message,
|
332
|
-
extra={"structured_data": {"tool_usage": usage_data}}
|
333
|
-
)
|
314
|
+
|
315
|
+
logger.log(level, message, extra={"structured_data": {"tool_usage": usage_data}})
|