mcp-code-indexer 3.1.4__py3-none-any.whl → 3.1.6__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.
@@ -12,15 +12,19 @@ from typing import Any, Dict, Optional
12
12
 
13
13
  class DatabaseError(Exception):
14
14
  """Base exception for all database-related errors."""
15
-
16
- def __init__(self, message: str, operation_name: str = "",
17
- error_context: Optional[Dict[str, Any]] = None):
15
+
16
+ def __init__(
17
+ self,
18
+ message: str,
19
+ operation_name: str = "",
20
+ error_context: Optional[Dict[str, Any]] = None,
21
+ ):
18
22
  self.message = message
19
23
  self.operation_name = operation_name
20
24
  self.error_context = error_context or {}
21
25
  self.timestamp = datetime.now(timezone.utc)
22
26
  super().__init__(f"{operation_name}: {message}" if operation_name else message)
23
-
27
+
24
28
  def to_dict(self) -> Dict[str, Any]:
25
29
  """Convert exception to dictionary for structured logging."""
26
30
  return {
@@ -28,240 +32,283 @@ class DatabaseError(Exception):
28
32
  "message": self.message,
29
33
  "operation_name": self.operation_name,
30
34
  "timestamp": self.timestamp.isoformat(),
31
- "error_context": self.error_context
35
+ "error_context": self.error_context,
32
36
  }
33
37
 
34
38
 
35
39
  class DatabaseLockError(DatabaseError):
36
40
  """Exception for SQLite database locking issues that are retryable."""
37
-
38
- def __init__(self, message: str, retry_count: int = 0, operation_name: str = "",
39
- last_attempt: Optional[datetime] = None, lock_type: str = "unknown"):
41
+
42
+ def __init__(
43
+ self,
44
+ message: str,
45
+ retry_count: int = 0,
46
+ operation_name: str = "",
47
+ last_attempt: Optional[datetime] = None,
48
+ lock_type: str = "unknown",
49
+ ):
40
50
  self.retry_count = retry_count
41
51
  self.last_attempt = last_attempt or datetime.now(timezone.utc)
42
52
  self.lock_type = lock_type # 'read', 'write', 'exclusive'
43
-
53
+
44
54
  error_context = {
45
55
  "retry_count": retry_count,
46
56
  "last_attempt": self.last_attempt.isoformat(),
47
57
  "lock_type": lock_type,
48
- "retryable": True
58
+ "retryable": True,
49
59
  }
50
-
60
+
51
61
  super().__init__(message, operation_name, error_context)
52
62
 
53
63
 
54
64
  class DatabaseBusyError(DatabaseError):
55
65
  """Exception for SQLite database busy errors that are retryable."""
56
-
57
- def __init__(self, message: str, operation_name: str = "",
58
- busy_timeout: float = 0.0, resource_type: str = "connection"):
66
+
67
+ def __init__(
68
+ self,
69
+ message: str,
70
+ operation_name: str = "",
71
+ busy_timeout: float = 0.0,
72
+ resource_type: str = "connection",
73
+ ):
59
74
  self.busy_timeout = busy_timeout
60
75
  self.resource_type = resource_type # 'connection', 'transaction', 'table'
61
-
76
+
62
77
  error_context = {
63
78
  "busy_timeout": busy_timeout,
64
79
  "resource_type": resource_type,
65
- "retryable": True
80
+ "retryable": True,
66
81
  }
67
-
82
+
68
83
  super().__init__(message, operation_name, error_context)
69
84
 
70
85
 
71
86
  class DatabaseConnectionError(DatabaseError):
72
87
  """Exception for database connection issues."""
73
-
74
- def __init__(self, message: str, operation_name: str = "",
75
- connection_info: Optional[Dict[str, Any]] = None):
88
+
89
+ def __init__(
90
+ self,
91
+ message: str,
92
+ operation_name: str = "",
93
+ connection_info: Optional[Dict[str, Any]] = None,
94
+ ):
76
95
  self.connection_info = connection_info or {}
77
-
96
+
78
97
  error_context = {
79
98
  "connection_info": self.connection_info,
80
- "retryable": False # Connection errors usually indicate config issues
99
+ "retryable": False, # Connection errors usually indicate config issues
81
100
  }
82
-
101
+
83
102
  super().__init__(message, operation_name, error_context)
84
103
 
85
104
 
86
105
  class DatabaseSchemaError(DatabaseError):
87
106
  """Exception for database schema-related errors."""
88
-
89
- def __init__(self, message: str, operation_name: str = "",
90
- schema_version: Optional[str] = None, migration_info: Optional[Dict] = None):
107
+
108
+ def __init__(
109
+ self,
110
+ message: str,
111
+ operation_name: str = "",
112
+ schema_version: Optional[str] = None,
113
+ migration_info: Optional[Dict] = None,
114
+ ):
91
115
  self.schema_version = schema_version
92
116
  self.migration_info = migration_info or {}
93
-
117
+
94
118
  error_context = {
95
119
  "schema_version": schema_version,
96
120
  "migration_info": self.migration_info,
97
- "retryable": False # Schema errors require manual intervention
121
+ "retryable": False, # Schema errors require manual intervention
98
122
  }
99
-
123
+
100
124
  super().__init__(message, operation_name, error_context)
101
125
 
102
126
 
103
127
  class DatabaseIntegrityError(DatabaseError):
104
128
  """Exception for database integrity constraint violations."""
105
-
106
- def __init__(self, message: str, operation_name: str = "",
107
- constraint_type: str = "unknown", affected_table: str = ""):
108
- self.constraint_type = constraint_type # 'primary_key', 'foreign_key', 'unique', 'check'
129
+
130
+ def __init__(
131
+ self,
132
+ message: str,
133
+ operation_name: str = "",
134
+ constraint_type: str = "unknown",
135
+ affected_table: str = "",
136
+ ):
137
+ self.constraint_type = (
138
+ constraint_type # 'primary_key', 'foreign_key', 'unique', 'check'
139
+ )
109
140
  self.affected_table = affected_table
110
-
141
+
111
142
  error_context = {
112
143
  "constraint_type": constraint_type,
113
144
  "affected_table": affected_table,
114
- "retryable": False # Integrity errors indicate data issues
145
+ "retryable": False, # Integrity errors indicate data issues
115
146
  }
116
-
147
+
117
148
  super().__init__(message, operation_name, error_context)
118
149
 
119
150
 
120
151
  class DatabaseTimeoutError(DatabaseError):
121
152
  """Exception for database operation timeouts."""
122
-
123
- def __init__(self, message: str, operation_name: str = "",
124
- timeout_seconds: float = 0.0, operation_type: str = "unknown"):
153
+
154
+ def __init__(
155
+ self,
156
+ message: str,
157
+ operation_name: str = "",
158
+ timeout_seconds: float = 0.0,
159
+ operation_type: str = "unknown",
160
+ ):
125
161
  self.timeout_seconds = timeout_seconds
126
162
  self.operation_type = operation_type # 'read', 'write', 'transaction'
127
-
163
+
128
164
  error_context = {
129
165
  "timeout_seconds": timeout_seconds,
130
166
  "operation_type": operation_type,
131
- "retryable": True # Timeouts might be transient
167
+ "retryable": True, # Timeouts might be transient
132
168
  }
133
-
169
+
134
170
  super().__init__(message, operation_name, error_context)
135
171
 
136
172
 
137
173
  def classify_sqlite_error(error: Exception, operation_name: str = "") -> DatabaseError:
138
174
  """
139
175
  Classify a raw SQLite error into our structured exception hierarchy.
140
-
176
+
141
177
  Args:
142
178
  error: Original exception from SQLite
143
179
  operation_name: Name of the operation that failed
144
-
180
+
145
181
  Returns:
146
182
  Appropriate DatabaseError subclass with context
147
183
  """
148
184
  error_message = str(error).lower()
149
185
  original_message = str(error)
150
-
186
+
151
187
  # Database locking errors
152
- if any(msg in error_message for msg in [
153
- "database is locked",
154
- "sqlite_locked",
155
- "attempt to write a readonly database"
156
- ]):
157
- lock_type = "write" if "write" in error_message or "readonly" in error_message else "read"
188
+ if any(
189
+ msg in error_message
190
+ for msg in [
191
+ "database is locked",
192
+ "sqlite_locked",
193
+ "attempt to write a readonly database",
194
+ ]
195
+ ):
196
+ lock_type = (
197
+ "write"
198
+ if "write" in error_message or "readonly" in error_message
199
+ else "read"
200
+ )
158
201
  return DatabaseLockError(
159
- original_message,
160
- operation_name=operation_name,
161
- lock_type=lock_type
202
+ original_message, operation_name=operation_name, lock_type=lock_type
162
203
  )
163
-
204
+
164
205
  # Database busy errors
165
- if any(msg in error_message for msg in [
166
- "database is busy",
167
- "sqlite_busy",
168
- "cannot start a transaction within a transaction"
169
- ]):
170
- resource_type = "transaction" if "transaction" in error_message else "connection"
206
+ if any(
207
+ msg in error_message
208
+ for msg in [
209
+ "database is busy",
210
+ "sqlite_busy",
211
+ "cannot start a transaction within a transaction",
212
+ ]
213
+ ):
214
+ resource_type = (
215
+ "transaction" if "transaction" in error_message else "connection"
216
+ )
171
217
  return DatabaseBusyError(
172
- original_message,
173
- operation_name=operation_name,
174
- resource_type=resource_type
218
+ original_message, operation_name=operation_name, resource_type=resource_type
175
219
  )
176
-
220
+
177
221
  # Connection errors
178
- if any(msg in error_message for msg in [
179
- "unable to open database",
180
- "disk i/o error",
181
- "database disk image is malformed",
182
- "no such database"
183
- ]):
184
- return DatabaseConnectionError(
185
- original_message,
186
- operation_name=operation_name
187
- )
188
-
222
+ if any(
223
+ msg in error_message
224
+ for msg in [
225
+ "unable to open database",
226
+ "disk i/o error",
227
+ "database disk image is malformed",
228
+ "no such database",
229
+ ]
230
+ ):
231
+ return DatabaseConnectionError(original_message, operation_name=operation_name)
232
+
189
233
  # Schema errors
190
- if any(msg in error_message for msg in [
191
- "no such table",
192
- "no such column",
193
- "table already exists",
194
- "syntax error"
195
- ]):
196
- return DatabaseSchemaError(
197
- original_message,
198
- operation_name=operation_name
199
- )
200
-
234
+ if any(
235
+ msg in error_message
236
+ for msg in [
237
+ "no such table",
238
+ "no such column",
239
+ "table already exists",
240
+ "syntax error",
241
+ ]
242
+ ):
243
+ return DatabaseSchemaError(original_message, operation_name=operation_name)
244
+
201
245
  # Integrity constraint errors
202
- if any(msg in error_message for msg in [
203
- "unique constraint failed",
204
- "foreign key constraint failed",
205
- "primary key constraint failed",
206
- "check constraint failed"
207
- ]):
246
+ if any(
247
+ msg in error_message
248
+ for msg in [
249
+ "unique constraint failed",
250
+ "foreign key constraint failed",
251
+ "primary key constraint failed",
252
+ "check constraint failed",
253
+ ]
254
+ ):
208
255
  constraint_type = "unknown"
209
256
  if "unique" in error_message:
210
257
  constraint_type = "unique"
211
258
  elif "foreign key" in error_message:
212
- constraint_type = "foreign_key"
259
+ constraint_type = "foreign_key"
213
260
  elif "primary key" in error_message:
214
261
  constraint_type = "primary_key"
215
262
  elif "check" in error_message:
216
263
  constraint_type = "check"
217
-
264
+
218
265
  return DatabaseIntegrityError(
219
266
  original_message,
220
267
  operation_name=operation_name,
221
- constraint_type=constraint_type
268
+ constraint_type=constraint_type,
222
269
  )
223
-
270
+
224
271
  # Default to generic database error
225
272
  return DatabaseError(
226
273
  original_message,
227
274
  operation_name=operation_name,
228
- error_context={"original_error_type": type(error).__name__}
275
+ error_context={"original_error_type": type(error).__name__},
229
276
  )
230
277
 
231
278
 
232
279
  def is_retryable_error(error: Exception) -> bool:
233
280
  """
234
281
  Determine if an error is retryable based on our classification.
235
-
282
+
236
283
  Args:
237
284
  error: Exception to check
238
-
285
+
239
286
  Returns:
240
287
  True if the error should trigger a retry
241
288
  """
242
289
  if isinstance(error, DatabaseError):
243
290
  return error.error_context.get("retryable", False)
244
-
291
+
245
292
  # For raw exceptions, use simple classification
246
293
  error_message = str(error).lower()
247
294
  retryable_patterns = [
248
295
  "database is locked",
249
296
  "database is busy",
250
- "sqlite_busy",
297
+ "sqlite_busy",
251
298
  "sqlite_locked",
252
- "cannot start a transaction within a transaction"
299
+ "cannot start a transaction within a transaction",
253
300
  ]
254
-
301
+
255
302
  return any(pattern in error_message for pattern in retryable_patterns)
256
303
 
257
304
 
258
305
  def get_error_classification_stats(errors: list) -> Dict[str, Any]:
259
306
  """
260
307
  Analyze a list of errors and provide classification statistics.
261
-
308
+
262
309
  Args:
263
310
  errors: List of Exception objects to analyze
264
-
311
+
265
312
  Returns:
266
313
  Dictionary with error classification statistics
267
314
  """
@@ -270,34 +317,36 @@ def get_error_classification_stats(errors: list) -> Dict[str, Any]:
270
317
  "error_types": {},
271
318
  "retryable_count": 0,
272
319
  "non_retryable_count": 0,
273
- "most_common_errors": {}
320
+ "most_common_errors": {},
274
321
  }
275
-
322
+
276
323
  error_messages = {}
277
-
324
+
278
325
  for error in errors:
279
326
  # Classify error
280
- classified = classify_sqlite_error(error) if not isinstance(error, DatabaseError) else error
327
+ classified = (
328
+ classify_sqlite_error(error)
329
+ if not isinstance(error, DatabaseError)
330
+ else error
331
+ )
281
332
  error_type = type(classified).__name__
282
-
333
+
283
334
  # Count by type
284
335
  stats["error_types"][error_type] = stats["error_types"].get(error_type, 0) + 1
285
-
336
+
286
337
  # Count retryable vs non-retryable
287
338
  if is_retryable_error(classified):
288
339
  stats["retryable_count"] += 1
289
340
  else:
290
341
  stats["non_retryable_count"] += 1
291
-
342
+
292
343
  # Track common error messages
293
344
  message = str(error)
294
345
  error_messages[message] = error_messages.get(message, 0) + 1
295
-
346
+
296
347
  # Find most common error messages
297
348
  stats["most_common_errors"] = sorted(
298
- error_messages.items(),
299
- key=lambda x: x[1],
300
- reverse=True
349
+ error_messages.items(), key=lambda x: x[1], reverse=True
301
350
  )[:5]
302
-
351
+
303
352
  return stats
@@ -14,42 +14,57 @@ from pydantic import BaseModel, Field
14
14
  class Project(BaseModel):
15
15
  """
16
16
  Represents a tracked project/repository.
17
-
17
+
18
18
  Projects are identified by project name and folder paths,
19
19
  allowing tracking across different local copies without git coupling.
20
20
  """
21
+
21
22
  id: str = Field(..., description="Generated unique identifier")
22
23
  name: str = Field(..., description="User-provided project name")
23
- aliases: List[str] = Field(default_factory=list, description="Alternative identifiers")
24
- created: datetime = Field(default_factory=datetime.utcnow, description="Creation timestamp")
25
- last_accessed: datetime = Field(default_factory=datetime.utcnow, description="Last access timestamp")
24
+ aliases: List[str] = Field(
25
+ default_factory=list, description="Alternative identifiers"
26
+ )
27
+ created: datetime = Field(
28
+ default_factory=datetime.utcnow, description="Creation timestamp"
29
+ )
30
+ last_accessed: datetime = Field(
31
+ default_factory=datetime.utcnow, description="Last access timestamp"
32
+ )
26
33
 
27
34
 
28
35
  class FileDescription(BaseModel):
29
36
  """
30
37
  Represents a file description within a project.
31
-
38
+
32
39
  Stores detailed summaries of file contents including purpose, components,
33
40
  and relationships to enable efficient codebase navigation.
34
41
  """
42
+
35
43
  id: Optional[int] = Field(None, description="Database ID")
36
44
  project_id: str = Field(..., description="Reference to project")
37
45
  file_path: str = Field(..., description="Relative path from project root")
38
46
  description: str = Field(..., description="Detailed content description")
39
47
  file_hash: Optional[str] = Field(None, description="SHA-256 of file contents")
40
- last_modified: datetime = Field(default_factory=datetime.utcnow, description="Last update timestamp")
48
+ last_modified: datetime = Field(
49
+ default_factory=datetime.utcnow, description="Last update timestamp"
50
+ )
41
51
  version: int = Field(default=1, description="For optimistic concurrency control")
42
- source_project_id: Optional[str] = Field(None, description="Source project if copied from upstream")
43
- to_be_cleaned: Optional[int] = Field(None, description="UNIX timestamp for cleanup, NULL = active")
52
+ source_project_id: Optional[str] = Field(
53
+ None, description="Source project if copied from upstream"
54
+ )
55
+ to_be_cleaned: Optional[int] = Field(
56
+ None, description="UNIX timestamp for cleanup, NULL = active"
57
+ )
44
58
 
45
59
 
46
60
  class MergeConflict(BaseModel):
47
61
  """
48
62
  Represents a merge conflict between file descriptions.
49
-
63
+
50
64
  Used during branch merging when the same file has different descriptions
51
65
  in source and target branches.
52
66
  """
67
+
53
68
  id: Optional[int] = Field(None, description="Database ID")
54
69
  project_id: str = Field(..., description="Project identifier")
55
70
  file_path: str = Field(..., description="Path to conflicted file")
@@ -58,53 +73,65 @@ class MergeConflict(BaseModel):
58
73
  source_description: str = Field(..., description="Description from source branch")
59
74
  target_description: str = Field(..., description="Description from target branch")
60
75
  resolution: Optional[str] = Field(None, description="AI-provided resolution")
61
- created: datetime = Field(default_factory=datetime.utcnow, description="Creation timestamp")
76
+ created: datetime = Field(
77
+ default_factory=datetime.utcnow, description="Creation timestamp"
78
+ )
62
79
 
63
80
 
64
81
  class ProjectOverview(BaseModel):
65
82
  """
66
83
  Represents a condensed, interpretive overview of an entire codebase.
67
-
84
+
68
85
  Stores a comprehensive narrative that captures architecture, components,
69
86
  relationships, and design patterns in a single document rather than
70
87
  individual file descriptions.
71
88
  """
89
+
72
90
  project_id: str = Field(..., description="Reference to project")
73
91
  overview: str = Field(..., description="Comprehensive codebase narrative")
74
- last_modified: datetime = Field(default_factory=datetime.utcnow, description="Last update timestamp")
92
+ last_modified: datetime = Field(
93
+ default_factory=datetime.utcnow, description="Last update timestamp"
94
+ )
75
95
  total_files: int = Field(..., description="Number of files in codebase")
76
- total_tokens: int = Field(..., description="Total tokens in individual descriptions")
96
+ total_tokens: int = Field(
97
+ ..., description="Total tokens in individual descriptions"
98
+ )
77
99
 
78
100
 
79
101
  class CodebaseOverview(BaseModel):
80
102
  """
81
103
  Represents a complete codebase structure with file descriptions.
82
-
104
+
83
105
  Provides hierarchical view of project files with token count information
84
106
  to help determine whether to use full overview or search-based approach.
85
107
  """
108
+
86
109
  project_name: str = Field(..., description="Project name")
87
110
  total_files: int = Field(..., description="Total number of tracked files")
88
111
  total_tokens: int = Field(..., description="Total token count for all descriptions")
89
112
  is_large: bool = Field(..., description="True if exceeds configured token limit")
90
113
  token_limit: int = Field(..., description="Current token limit setting")
91
- structure: 'FolderNode' = Field(..., description="Hierarchical folder structure")
114
+ structure: "FolderNode" = Field(..., description="Hierarchical folder structure")
92
115
 
93
116
 
94
117
  class FolderNode(BaseModel):
95
118
  """
96
119
  Represents a folder in the codebase hierarchy.
97
120
  """
121
+
98
122
  name: str = Field(..., description="Folder name")
99
123
  path: str = Field(..., description="Full path from project root")
100
- files: List['FileNode'] = Field(default_factory=list, description="Files in this folder")
101
- folders: List['FolderNode'] = Field(default_factory=list, description="Subfolders")
124
+ files: List["FileNode"] = Field(
125
+ default_factory=list, description="Files in this folder"
126
+ )
127
+ folders: List["FolderNode"] = Field(default_factory=list, description="Subfolders")
102
128
 
103
129
 
104
130
  class FileNode(BaseModel):
105
131
  """
106
132
  Represents a file in the codebase hierarchy.
107
133
  """
134
+
108
135
  name: str = Field(..., description="File name")
109
136
  path: str = Field(..., description="Full path from project root")
110
137
  description: str = Field(..., description="File description")
@@ -114,6 +141,7 @@ class SearchResult(BaseModel):
114
141
  """
115
142
  Represents a search result with relevance scoring.
116
143
  """
144
+
117
145
  file_path: str = Field(..., description="Path to the matching file")
118
146
  description: str = Field(..., description="File description")
119
147
  relevance_score: float = Field(..., description="Search relevance score")
@@ -124,11 +152,16 @@ class CodebaseSizeInfo(BaseModel):
124
152
  """
125
153
  Information about codebase size and token usage.
126
154
  """
155
+
127
156
  total_tokens: int = Field(..., description="Total token count")
128
157
  is_large: bool = Field(..., description="Whether codebase exceeds token limit")
129
- recommendation: str = Field(..., description="Recommended approach (use_search or use_overview)")
158
+ recommendation: str = Field(
159
+ ..., description="Recommended approach (use_search or use_overview)"
160
+ )
130
161
  token_limit: int = Field(..., description="Configured token limit")
131
- cleaned_up_files: List[str] = Field(default_factory=list, description="Files removed during cleanup")
162
+ cleaned_up_files: List[str] = Field(
163
+ default_factory=list, description="Files removed during cleanup"
164
+ )
132
165
  cleaned_up_count: int = Field(default=0, description="Number of files cleaned up")
133
166
 
134
167
 
@@ -136,6 +169,7 @@ class WordFrequencyTerm(BaseModel):
136
169
  """
137
170
  Represents a term and its frequency from word analysis.
138
171
  """
172
+
139
173
  term: str = Field(..., description="The word/term")
140
174
  frequency: int = Field(..., description="Number of occurrences")
141
175
 
@@ -144,6 +178,7 @@ class WordFrequencyResult(BaseModel):
144
178
  """
145
179
  Results from word frequency analysis of file descriptions.
146
180
  """
181
+
147
182
  top_terms: List[WordFrequencyTerm] = Field(..., description="Top frequent terms")
148
183
  total_terms_analyzed: int = Field(..., description="Total terms processed")
149
184
  total_unique_terms: int = Field(..., description="Number of unique terms found")