mcp-code-indexer 1.1.3__py3-none-any.whl → 1.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/__main__.py +11 -0
- mcp_code_indexer/main.py +265 -0
- mcp_code_indexer/server/mcp_server.py +119 -18
- {mcp_code_indexer-1.1.3.dist-info → mcp_code_indexer-1.1.5.dist-info}/METADATA +1 -1
- {mcp_code_indexer-1.1.3.dist-info → mcp_code_indexer-1.1.5.dist-info}/RECORD +9 -8
- {mcp_code_indexer-1.1.3.dist-info → mcp_code_indexer-1.1.5.dist-info}/WHEEL +0 -0
- {mcp_code_indexer-1.1.3.dist-info → mcp_code_indexer-1.1.5.dist-info}/entry_points.txt +0 -0
- {mcp_code_indexer-1.1.3.dist-info → mcp_code_indexer-1.1.5.dist-info}/licenses/LICENSE +0 -0
- {mcp_code_indexer-1.1.3.dist-info → mcp_code_indexer-1.1.5.dist-info}/top_level.txt +0 -0
mcp_code_indexer/main.py
CHANGED
@@ -7,9 +7,11 @@ Entry point for the mcp-code-indexer package when installed via pip.
|
|
7
7
|
|
8
8
|
import argparse
|
9
9
|
import asyncio
|
10
|
+
import json
|
10
11
|
import logging
|
11
12
|
import sys
|
12
13
|
from pathlib import Path
|
14
|
+
from typing import Any, Dict
|
13
15
|
|
14
16
|
from . import __version__
|
15
17
|
from .logging_config import setup_logging
|
@@ -58,13 +60,276 @@ def parse_arguments() -> argparse.Namespace:
|
|
58
60
|
help="Logging level (default: INFO)"
|
59
61
|
)
|
60
62
|
|
63
|
+
# Utility commands
|
64
|
+
parser.add_argument(
|
65
|
+
"--getprojects",
|
66
|
+
action="store_true",
|
67
|
+
help="List all projects with IDs, branches, and description counts"
|
68
|
+
)
|
69
|
+
|
70
|
+
parser.add_argument(
|
71
|
+
"--runcommand",
|
72
|
+
type=str,
|
73
|
+
help="Execute a command using JSON in MCP format (single or multi-line)"
|
74
|
+
)
|
75
|
+
|
76
|
+
parser.add_argument(
|
77
|
+
"--dumpdescriptions",
|
78
|
+
nargs="+",
|
79
|
+
metavar=("PROJECT_ID", "BRANCH"),
|
80
|
+
help="Export descriptions for a project. Usage: --dumpdescriptions PROJECT_ID [BRANCH]"
|
81
|
+
)
|
82
|
+
|
61
83
|
return parser.parse_args()
|
62
84
|
|
63
85
|
|
86
|
+
async def handle_getprojects(args: argparse.Namespace) -> None:
|
87
|
+
"""Handle --getprojects command."""
|
88
|
+
try:
|
89
|
+
from .database.database import DatabaseManager
|
90
|
+
|
91
|
+
# Initialize database
|
92
|
+
db_path = Path(args.db_path).expanduser()
|
93
|
+
db_manager = DatabaseManager(db_path)
|
94
|
+
await db_manager.initialize()
|
95
|
+
|
96
|
+
# Get all projects
|
97
|
+
projects = await db_manager.get_all_projects()
|
98
|
+
|
99
|
+
if not projects:
|
100
|
+
print("No projects found.")
|
101
|
+
return
|
102
|
+
|
103
|
+
print("Projects:")
|
104
|
+
print("-" * 80)
|
105
|
+
|
106
|
+
for project in projects:
|
107
|
+
print(f"ID: {project.id}")
|
108
|
+
print(f"Name: {project.name}")
|
109
|
+
print(f"Remote Origin: {project.remote_origin or 'N/A'}")
|
110
|
+
print(f"Upstream Origin: {project.upstream_origin or 'N/A'}")
|
111
|
+
|
112
|
+
# Get branch information
|
113
|
+
try:
|
114
|
+
branch_counts = await db_manager.get_branch_file_counts(project.id)
|
115
|
+
if branch_counts:
|
116
|
+
print("Branches:")
|
117
|
+
for branch, count in branch_counts.items():
|
118
|
+
print(f" - {branch}: {count} descriptions")
|
119
|
+
else:
|
120
|
+
print("Branches: No descriptions found")
|
121
|
+
except Exception as e:
|
122
|
+
print(f"Branches: Error loading branch info - {e}")
|
123
|
+
|
124
|
+
print("-" * 80)
|
125
|
+
|
126
|
+
except Exception as e:
|
127
|
+
print(f"Error: {e}", file=sys.stderr)
|
128
|
+
sys.exit(1)
|
129
|
+
|
130
|
+
|
131
|
+
async def handle_runcommand(args: argparse.Namespace) -> None:
|
132
|
+
"""Handle --runcommand command."""
|
133
|
+
from .server.mcp_server import MCPCodeIndexServer
|
134
|
+
|
135
|
+
try:
|
136
|
+
# Parse JSON (handle both single-line and multi-line)
|
137
|
+
json_data = json.loads(args.runcommand)
|
138
|
+
except json.JSONDecodeError as e:
|
139
|
+
print(f"Initial JSON parse failed: {e}", file=sys.stderr)
|
140
|
+
|
141
|
+
# Try to repair the JSON
|
142
|
+
try:
|
143
|
+
import re
|
144
|
+
repaired = args.runcommand
|
145
|
+
|
146
|
+
# Fix common issues
|
147
|
+
# Quote unquoted URLs and paths
|
148
|
+
url_pattern = r'("[\w]+"):\s*([a-zA-Z][a-zA-Z0-9+.-]*://[^\s,}]+|/[^\s,}]*)'
|
149
|
+
repaired = re.sub(url_pattern, r'\1: "\2"', repaired)
|
150
|
+
|
151
|
+
# Quote unquoted values
|
152
|
+
unquoted_pattern = r'("[\w]+"):\s*([a-zA-Z0-9_-]+)(?=\s*[,}])'
|
153
|
+
repaired = re.sub(unquoted_pattern, r'\1: "\2"', repaired)
|
154
|
+
|
155
|
+
# Remove trailing commas
|
156
|
+
repaired = re.sub(r',(\s*[}\]])', r'\1', repaired)
|
157
|
+
|
158
|
+
json_data = json.loads(repaired)
|
159
|
+
print(f"JSON repaired successfully", file=sys.stderr)
|
160
|
+
print(f"Original: {args.runcommand}", file=sys.stderr)
|
161
|
+
print(f"Repaired: {repaired}", file=sys.stderr)
|
162
|
+
except json.JSONDecodeError as repair_error:
|
163
|
+
print(f"JSON repair also failed: {repair_error}", file=sys.stderr)
|
164
|
+
print(f"Original JSON: {args.runcommand}", file=sys.stderr)
|
165
|
+
sys.exit(1)
|
166
|
+
|
167
|
+
# Initialize server
|
168
|
+
db_path = Path(args.db_path).expanduser()
|
169
|
+
cache_dir = Path(args.cache_dir).expanduser()
|
170
|
+
|
171
|
+
server = MCPCodeIndexServer(
|
172
|
+
token_limit=args.token_limit,
|
173
|
+
db_path=db_path,
|
174
|
+
cache_dir=cache_dir
|
175
|
+
)
|
176
|
+
await server.initialize()
|
177
|
+
|
178
|
+
# Extract the tool call information from the JSON
|
179
|
+
if "method" in json_data and json_data["method"] == "tools/call":
|
180
|
+
tool_name = json_data["params"]["name"]
|
181
|
+
tool_arguments = json_data["params"]["arguments"]
|
182
|
+
elif "projectName" in json_data and "folderPath" in json_data:
|
183
|
+
# Auto-detect: user provided just arguments, try to infer the tool
|
184
|
+
if "filePath" in json_data and "description" in json_data:
|
185
|
+
tool_name = "update_file_description"
|
186
|
+
tool_arguments = json_data
|
187
|
+
print("Auto-detected tool: update_file_description", file=sys.stderr)
|
188
|
+
elif "branch" in json_data:
|
189
|
+
tool_name = "check_codebase_size"
|
190
|
+
tool_arguments = json_data
|
191
|
+
print("Auto-detected tool: check_codebase_size", file=sys.stderr)
|
192
|
+
else:
|
193
|
+
print("Error: Could not auto-detect tool from arguments. Please use full MCP format:", file=sys.stderr)
|
194
|
+
print('{"method": "tools/call", "params": {"name": "TOOL_NAME", "arguments": {...}}}', file=sys.stderr)
|
195
|
+
sys.exit(1)
|
196
|
+
|
197
|
+
try:
|
198
|
+
# Map tool names to handler methods
|
199
|
+
tool_handlers = {
|
200
|
+
"get_file_description": server._handle_get_file_description,
|
201
|
+
"update_file_description": server._handle_update_file_description,
|
202
|
+
"check_codebase_size": server._handle_check_codebase_size,
|
203
|
+
"find_missing_descriptions": server._handle_find_missing_descriptions,
|
204
|
+
"search_descriptions": server._handle_search_descriptions,
|
205
|
+
"get_codebase_overview": server._handle_get_codebase_overview,
|
206
|
+
"merge_branch_descriptions": server._handle_merge_branch_descriptions,
|
207
|
+
}
|
208
|
+
|
209
|
+
if tool_name not in tool_handlers:
|
210
|
+
error_result = {
|
211
|
+
"error": {
|
212
|
+
"code": -32601,
|
213
|
+
"message": f"Unknown tool: {tool_name}"
|
214
|
+
}
|
215
|
+
}
|
216
|
+
print(json.dumps(error_result, indent=2))
|
217
|
+
return
|
218
|
+
|
219
|
+
# Clean HTML entities from arguments before execution
|
220
|
+
def clean_html_entities(text: str) -> str:
|
221
|
+
if not text:
|
222
|
+
return text
|
223
|
+
import html
|
224
|
+
return html.unescape(text)
|
225
|
+
|
226
|
+
def clean_arguments(arguments: dict) -> dict:
|
227
|
+
cleaned = {}
|
228
|
+
for key, value in arguments.items():
|
229
|
+
if isinstance(value, str):
|
230
|
+
cleaned[key] = clean_html_entities(value)
|
231
|
+
elif isinstance(value, list):
|
232
|
+
cleaned[key] = [
|
233
|
+
clean_html_entities(item) if isinstance(item, str) else item
|
234
|
+
for item in value
|
235
|
+
]
|
236
|
+
elif isinstance(value, dict):
|
237
|
+
cleaned[key] = clean_arguments(value)
|
238
|
+
else:
|
239
|
+
cleaned[key] = value
|
240
|
+
return cleaned
|
241
|
+
|
242
|
+
cleaned_tool_arguments = clean_arguments(tool_arguments)
|
243
|
+
|
244
|
+
# Execute the tool handler directly
|
245
|
+
result = await tool_handlers[tool_name](cleaned_tool_arguments)
|
246
|
+
print(json.dumps(result, indent=2, default=str))
|
247
|
+
except Exception as e:
|
248
|
+
error_result = {
|
249
|
+
"error": {
|
250
|
+
"code": -32603,
|
251
|
+
"message": str(e)
|
252
|
+
}
|
253
|
+
}
|
254
|
+
print(json.dumps(error_result, indent=2))
|
255
|
+
else:
|
256
|
+
print("Error: JSON must contain a valid MCP tool call", file=sys.stderr)
|
257
|
+
sys.exit(1)
|
258
|
+
|
259
|
+
|
260
|
+
async def handle_dumpdescriptions(args: argparse.Namespace) -> None:
|
261
|
+
"""Handle --dumpdescriptions command."""
|
262
|
+
from .database.database import DatabaseManager
|
263
|
+
from .token_counter import TokenCounter
|
264
|
+
|
265
|
+
if len(args.dumpdescriptions) < 1:
|
266
|
+
print("Error: Project ID is required", file=sys.stderr)
|
267
|
+
sys.exit(1)
|
268
|
+
|
269
|
+
project_id = args.dumpdescriptions[0]
|
270
|
+
branch = args.dumpdescriptions[1] if len(args.dumpdescriptions) > 1 else None
|
271
|
+
|
272
|
+
# Initialize database and token counter
|
273
|
+
db_path = Path(args.db_path).expanduser()
|
274
|
+
db_manager = DatabaseManager(db_path)
|
275
|
+
await db_manager.initialize()
|
276
|
+
|
277
|
+
token_counter = TokenCounter(args.token_limit)
|
278
|
+
|
279
|
+
# Get file descriptions
|
280
|
+
if branch:
|
281
|
+
file_descriptions = await db_manager.get_all_file_descriptions(
|
282
|
+
project_id=project_id,
|
283
|
+
branch=branch
|
284
|
+
)
|
285
|
+
print(f"File descriptions for project {project_id}, branch {branch}:")
|
286
|
+
else:
|
287
|
+
file_descriptions = await db_manager.get_all_file_descriptions(
|
288
|
+
project_id=project_id
|
289
|
+
)
|
290
|
+
print(f"File descriptions for project {project_id} (all branches):")
|
291
|
+
|
292
|
+
print("=" * 80)
|
293
|
+
|
294
|
+
if not file_descriptions:
|
295
|
+
print("No descriptions found.")
|
296
|
+
total_tokens = 0
|
297
|
+
else:
|
298
|
+
total_tokens = 0
|
299
|
+
for desc in file_descriptions:
|
300
|
+
print(f"File: {desc.file_path}")
|
301
|
+
if branch is None:
|
302
|
+
print(f"Branch: {desc.branch}")
|
303
|
+
print(f"Description: {desc.description}")
|
304
|
+
print("-" * 40)
|
305
|
+
|
306
|
+
# Count tokens for this description
|
307
|
+
desc_tokens = token_counter.count_file_description_tokens(desc)
|
308
|
+
total_tokens += desc_tokens
|
309
|
+
|
310
|
+
print("=" * 80)
|
311
|
+
print(f"Total descriptions: {len(file_descriptions)}")
|
312
|
+
print(f"Total tokens: {total_tokens}")
|
313
|
+
|
314
|
+
|
315
|
+
|
64
316
|
async def main() -> None:
|
65
317
|
"""Main entry point for the MCP server."""
|
66
318
|
args = parse_arguments()
|
67
319
|
|
320
|
+
# Handle utility commands
|
321
|
+
if args.getprojects:
|
322
|
+
await handle_getprojects(args)
|
323
|
+
return
|
324
|
+
|
325
|
+
if args.runcommand:
|
326
|
+
await handle_runcommand(args)
|
327
|
+
return
|
328
|
+
|
329
|
+
if args.dumpdescriptions:
|
330
|
+
await handle_dumpdescriptions(args)
|
331
|
+
return
|
332
|
+
|
68
333
|
# Setup structured logging
|
69
334
|
log_file = Path(args.cache_dir).expanduser() / "server.log" if args.cache_dir else None
|
70
335
|
logger = setup_logging(
|
@@ -7,8 +7,10 @@ for file description management tools.
|
|
7
7
|
|
8
8
|
import asyncio
|
9
9
|
import hashlib
|
10
|
+
import html
|
10
11
|
import json
|
11
12
|
import logging
|
13
|
+
import re
|
12
14
|
import uuid
|
13
15
|
from datetime import datetime
|
14
16
|
from pathlib import Path
|
@@ -85,6 +87,102 @@ class MCPCodeIndexServer:
|
|
85
87
|
extra={"structured_data": {"initialization": {"token_limit": token_limit}}}
|
86
88
|
)
|
87
89
|
|
90
|
+
def _clean_html_entities(self, text: str) -> str:
|
91
|
+
"""
|
92
|
+
Clean HTML entities from text to prevent encoding issues.
|
93
|
+
|
94
|
+
Args:
|
95
|
+
text: Text that may contain HTML entities
|
96
|
+
|
97
|
+
Returns:
|
98
|
+
Text with HTML entities decoded to proper characters
|
99
|
+
"""
|
100
|
+
if not text:
|
101
|
+
return text
|
102
|
+
|
103
|
+
# Decode HTML entities like < > & etc.
|
104
|
+
return html.unescape(text)
|
105
|
+
|
106
|
+
def _clean_arguments(self, arguments: Dict[str, Any]) -> Dict[str, Any]:
|
107
|
+
"""
|
108
|
+
Clean HTML entities from all text arguments.
|
109
|
+
|
110
|
+
Args:
|
111
|
+
arguments: Dictionary of arguments to clean
|
112
|
+
|
113
|
+
Returns:
|
114
|
+
Dictionary with HTML entities decoded in all string values
|
115
|
+
"""
|
116
|
+
cleaned = {}
|
117
|
+
|
118
|
+
for key, value in arguments.items():
|
119
|
+
if isinstance(value, str):
|
120
|
+
cleaned[key] = self._clean_html_entities(value)
|
121
|
+
elif isinstance(value, list):
|
122
|
+
# Clean strings in lists (like conflict resolutions)
|
123
|
+
cleaned[key] = [
|
124
|
+
self._clean_html_entities(item) if isinstance(item, str) else item
|
125
|
+
for item in value
|
126
|
+
]
|
127
|
+
elif isinstance(value, dict):
|
128
|
+
# Recursively clean nested dictionaries
|
129
|
+
cleaned[key] = self._clean_arguments(value)
|
130
|
+
else:
|
131
|
+
# Pass through other types unchanged
|
132
|
+
cleaned[key] = value
|
133
|
+
|
134
|
+
return cleaned
|
135
|
+
|
136
|
+
def _parse_json_robust(self, json_str: str) -> Dict[str, Any]:
|
137
|
+
"""
|
138
|
+
Parse JSON with automatic repair for common issues.
|
139
|
+
|
140
|
+
Args:
|
141
|
+
json_str: JSON string that may have formatting issues
|
142
|
+
|
143
|
+
Returns:
|
144
|
+
Parsed JSON dictionary
|
145
|
+
|
146
|
+
Raises:
|
147
|
+
ValueError: If JSON cannot be parsed even after repair attempts
|
148
|
+
"""
|
149
|
+
# First try normal parsing
|
150
|
+
try:
|
151
|
+
return json.loads(json_str)
|
152
|
+
except json.JSONDecodeError as original_error:
|
153
|
+
logger.warning(f"Initial JSON parse failed: {original_error}")
|
154
|
+
|
155
|
+
# Try to repair common issues
|
156
|
+
repaired = json_str
|
157
|
+
|
158
|
+
# Fix 1: Quote unquoted URLs and paths
|
159
|
+
# Look for patterns like: "key": http://... or "key": /path/...
|
160
|
+
url_pattern = r'("[\w]+"):\s*([a-zA-Z][a-zA-Z0-9+.-]*://[^\s,}]+|/[^\s,}]*)'
|
161
|
+
repaired = re.sub(url_pattern, r'\1: "\2"', repaired)
|
162
|
+
|
163
|
+
# Fix 2: Quote unquoted boolean-like strings
|
164
|
+
# Look for: "key": true-ish-string or "key": false-ish-string
|
165
|
+
bool_pattern = r'("[\w]+"):\s*([a-zA-Z][a-zA-Z0-9_-]*[a-zA-Z0-9])(?=\s*[,}])'
|
166
|
+
repaired = re.sub(bool_pattern, r'\1: "\2"', repaired)
|
167
|
+
|
168
|
+
# Fix 3: Remove trailing commas
|
169
|
+
repaired = re.sub(r',(\s*[}\]])', r'\1', repaired)
|
170
|
+
|
171
|
+
# Fix 4: Ensure proper string quoting for common unquoted values
|
172
|
+
# Handle cases like: "key": value (where value should be "value")
|
173
|
+
unquoted_pattern = r'("[\w]+"):\s*([a-zA-Z0-9_-]+)(?=\s*[,}])'
|
174
|
+
repaired = re.sub(unquoted_pattern, r'\1: "\2"', repaired)
|
175
|
+
|
176
|
+
try:
|
177
|
+
result = json.loads(repaired)
|
178
|
+
logger.info(f"Successfully repaired JSON. Original: {json_str[:100]}...")
|
179
|
+
logger.info(f"Repaired: {repaired[:100]}...")
|
180
|
+
return result
|
181
|
+
except json.JSONDecodeError as repair_error:
|
182
|
+
logger.error(f"JSON repair failed. Original: {json_str}")
|
183
|
+
logger.error(f"Repaired attempt: {repaired}")
|
184
|
+
raise ValueError(f"Could not parse JSON even after repair attempts. Original error: {original_error}, Repair error: {repair_error}")
|
185
|
+
|
88
186
|
async def initialize(self) -> None:
|
89
187
|
"""Initialize database and other resources."""
|
90
188
|
await self.db_manager.initialize()
|
@@ -116,11 +214,11 @@ class MCPCodeIndexServer:
|
|
116
214
|
"description": "Git branch name (e.g., 'main', 'develop')"
|
117
215
|
},
|
118
216
|
"remoteOrigin": {
|
119
|
-
"type":
|
217
|
+
"type": "string",
|
120
218
|
"description": "Git remote origin URL if available"
|
121
219
|
},
|
122
220
|
"upstreamOrigin": {
|
123
|
-
"type":
|
221
|
+
"type": "string",
|
124
222
|
"description": "Upstream repository URL if this is a fork"
|
125
223
|
},
|
126
224
|
"filePath": {
|
@@ -140,11 +238,11 @@ class MCPCodeIndexServer:
|
|
140
238
|
"projectName": {"type": "string", "description": "The name of the project"},
|
141
239
|
"folderPath": {"type": "string", "description": "Absolute path to the project folder on disk"},
|
142
240
|
"branch": {"type": "string", "description": "Git branch name"},
|
143
|
-
"remoteOrigin": {"type":
|
144
|
-
"upstreamOrigin": {"type":
|
241
|
+
"remoteOrigin": {"type": "string", "description": "Git remote origin URL if available"},
|
242
|
+
"upstreamOrigin": {"type": "string", "description": "Upstream repository URL if this is a fork"},
|
145
243
|
"filePath": {"type": "string", "description": "Relative path to the file from project root"},
|
146
244
|
"description": {"type": "string", "description": "Detailed description of the file's contents"},
|
147
|
-
"fileHash": {"type":
|
245
|
+
"fileHash": {"type": "string", "description": "SHA-256 hash of the file contents (optional)"}
|
148
246
|
},
|
149
247
|
"required": ["projectName", "folderPath", "branch", "filePath", "description"]
|
150
248
|
}
|
@@ -158,8 +256,8 @@ class MCPCodeIndexServer:
|
|
158
256
|
"projectName": {"type": "string", "description": "The name of the project"},
|
159
257
|
"folderPath": {"type": "string", "description": "Absolute path to the project folder on disk"},
|
160
258
|
"branch": {"type": "string", "description": "Git branch name"},
|
161
|
-
"remoteOrigin": {"type":
|
162
|
-
"upstreamOrigin": {"type":
|
259
|
+
"remoteOrigin": {"type": "string", "description": "Git remote origin URL if available"},
|
260
|
+
"upstreamOrigin": {"type": "string", "description": "Upstream repository URL if this is a fork"},
|
163
261
|
"tokenLimit": {"type": "integer", "description": "Optional token limit override (defaults to server configuration)"}
|
164
262
|
},
|
165
263
|
"required": ["projectName", "folderPath", "branch"]
|
@@ -174,8 +272,8 @@ class MCPCodeIndexServer:
|
|
174
272
|
"projectName": {"type": "string", "description": "The name of the project"},
|
175
273
|
"folderPath": {"type": "string", "description": "Absolute path to the project folder on disk"},
|
176
274
|
"branch": {"type": "string", "description": "Git branch name"},
|
177
|
-
"remoteOrigin": {"type":
|
178
|
-
"upstreamOrigin": {"type":
|
275
|
+
"remoteOrigin": {"type": "string", "description": "Git remote origin URL if available"},
|
276
|
+
"upstreamOrigin": {"type": "string", "description": "Upstream repository URL if this is a fork"},
|
179
277
|
"limit": {"type": "integer", "description": "Maximum number of missing files to return (optional)"}
|
180
278
|
},
|
181
279
|
"required": ["projectName", "folderPath", "branch"]
|
@@ -190,8 +288,8 @@ class MCPCodeIndexServer:
|
|
190
288
|
"projectName": {"type": "string", "description": "The name of the project"},
|
191
289
|
"folderPath": {"type": "string", "description": "Absolute path to the project folder on disk"},
|
192
290
|
"branch": {"type": "string", "description": "Git branch to search in"},
|
193
|
-
"remoteOrigin": {"type":
|
194
|
-
"upstreamOrigin": {"type":
|
291
|
+
"remoteOrigin": {"type": "string", "description": "Git remote origin URL if available"},
|
292
|
+
"upstreamOrigin": {"type": "string", "description": "Upstream repository URL if this is a fork"},
|
195
293
|
"query": {"type": "string", "description": "Search query (e.g., 'authentication middleware', 'database models')"},
|
196
294
|
"maxResults": {"type": "integer", "default": 20, "description": "Maximum number of results to return"}
|
197
295
|
},
|
@@ -207,8 +305,8 @@ class MCPCodeIndexServer:
|
|
207
305
|
"projectName": {"type": "string", "description": "The name of the project"},
|
208
306
|
"folderPath": {"type": "string", "description": "Absolute path to the project folder on disk"},
|
209
307
|
"branch": {"type": "string", "description": "Git branch name"},
|
210
|
-
"remoteOrigin": {"type":
|
211
|
-
"upstreamOrigin": {"type":
|
308
|
+
"remoteOrigin": {"type": "string", "description": "Git remote origin URL if available"},
|
309
|
+
"upstreamOrigin": {"type": "string", "description": "Upstream repository URL if this is a fork"}
|
212
310
|
},
|
213
311
|
"required": ["projectName", "folderPath", "branch"]
|
214
312
|
}
|
@@ -221,8 +319,8 @@ class MCPCodeIndexServer:
|
|
221
319
|
"properties": {
|
222
320
|
"projectName": {"type": "string", "description": "The name of the project"},
|
223
321
|
"folderPath": {"type": "string", "description": "Absolute path to the project folder"},
|
224
|
-
"remoteOrigin": {"type":
|
225
|
-
"upstreamOrigin": {"type":
|
322
|
+
"remoteOrigin": {"type": "string", "description": "Git remote origin URL"},
|
323
|
+
"upstreamOrigin": {"type": "string", "description": "Upstream repository URL if this is a fork"},
|
226
324
|
"sourceBranch": {"type": "string", "description": "Branch to merge from (e.g., 'feature/new-ui')"},
|
227
325
|
"targetBranch": {"type": "string", "description": "Branch to merge into (e.g., 'main')"},
|
228
326
|
"conflictResolutions": {
|
@@ -270,7 +368,10 @@ class MCPCodeIndexServer:
|
|
270
368
|
|
271
369
|
async def _execute_tool_handler(self, handler, arguments: Dict[str, Any]) -> List[types.TextContent]:
|
272
370
|
"""Execute a tool handler and format the result."""
|
273
|
-
|
371
|
+
# Clean HTML entities from all arguments before processing
|
372
|
+
cleaned_arguments = self._clean_arguments(arguments)
|
373
|
+
|
374
|
+
result = await handler(cleaned_arguments)
|
274
375
|
|
275
376
|
return [types.TextContent(
|
276
377
|
type="text",
|
@@ -410,8 +511,8 @@ class MCPCodeIndexServer:
|
|
410
511
|
if not scanner.is_valid_project_directory():
|
411
512
|
return False
|
412
513
|
|
413
|
-
current_files = scanner.
|
414
|
-
current_basenames = {
|
514
|
+
current_files = scanner.scan_directory()
|
515
|
+
current_basenames = {f.name for f in current_files}
|
415
516
|
|
416
517
|
if not current_basenames:
|
417
518
|
return False
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: mcp-code-indexer
|
3
|
-
Version: 1.1.
|
3
|
+
Version: 1.1.5
|
4
4
|
Summary: MCP server that tracks file descriptions across codebases, enabling AI agents to efficiently navigate and understand code through searchable summaries and token-aware overviews.
|
5
5
|
Author: MCP Code Indexer Contributors
|
6
6
|
Maintainer: MCP Code Indexer Contributors
|
@@ -1,8 +1,9 @@
|
|
1
1
|
mcp_code_indexer/__init__.py,sha256=PUkiM7VGRk7n2B_Ma0fzZWC0wmHCjyE15wxsvU9I54E,473
|
2
|
+
mcp_code_indexer/__main__.py,sha256=4Edinoe0ug43hobuLYcjTmGp2YJnlFYN4_8iKvUBJ0Q,213
|
2
3
|
mcp_code_indexer/error_handler.py,sha256=cNSUFFrGBMLDv4qa78c7495L1wSl_dXCRbzCJOidx-Q,11590
|
3
4
|
mcp_code_indexer/file_scanner.py,sha256=ctXeZMROgDThEtjzsANTK9TbK-fhTScMBd4iyuleBT4,11734
|
4
5
|
mcp_code_indexer/logging_config.py,sha256=5L1cYIG8IAX91yCjc5pzkbO_KPt0bvm_ABHB53LBZjI,5184
|
5
|
-
mcp_code_indexer/main.py,sha256=
|
6
|
+
mcp_code_indexer/main.py,sha256=eRc0Vl3DVDGS5XtuPCDBArgmqcBIi92O97LbE8HYGGA,13601
|
6
7
|
mcp_code_indexer/merge_handler.py,sha256=lJR8eVq2qSrF6MW9mR3Fy8UzrNAaQ7RsI2FMNXne3vQ,14692
|
7
8
|
mcp_code_indexer/token_counter.py,sha256=WrifOkbF99nWWHlRlhCHAB2KN7qr83GOHl7apE-hJcE,8460
|
8
9
|
mcp_code_indexer/database/__init__.py,sha256=aPq_aaRp0aSwOBIq9GkuMNjmLxA411zg2vhdrAuHm-w,38
|
@@ -11,12 +12,12 @@ mcp_code_indexer/database/models.py,sha256=3wOxHKb6j3zKPWFSwB5g1TLpI507vLNZcqsxZ
|
|
11
12
|
mcp_code_indexer/middleware/__init__.py,sha256=p-mP0pMsfiU2yajCPvokCUxUEkh_lu4XJP1LyyMW2ug,220
|
12
13
|
mcp_code_indexer/middleware/error_middleware.py,sha256=v6jaHmPxf3qerYdb85X1tHIXLxgcbybpitKVakFLQTA,10109
|
13
14
|
mcp_code_indexer/server/__init__.py,sha256=16xMcuriUOBlawRqWNBk6niwrvtv_JD5xvI36X1Vsmk,41
|
14
|
-
mcp_code_indexer/server/mcp_server.py,sha256=
|
15
|
+
mcp_code_indexer/server/mcp_server.py,sha256=DtfoeoOF72kdWO7fCYU9qvhg3LUyJU12KpjCCqw_Uxw,50589
|
15
16
|
mcp_code_indexer/tiktoken_cache/9b5ad71b2ce5302211f9c61530b329a4922fc6a4,sha256=Ijkht27pm96ZW3_3OFE-7xAPtR0YyTWXoRO8_-hlsqc,1681126
|
16
17
|
mcp_code_indexer/tools/__init__.py,sha256=m01mxML2UdD7y5rih_XNhNSCMzQTz7WQ_T1TeOcYlnE,49
|
17
|
-
mcp_code_indexer-1.1.
|
18
|
-
mcp_code_indexer-1.1.
|
19
|
-
mcp_code_indexer-1.1.
|
20
|
-
mcp_code_indexer-1.1.
|
21
|
-
mcp_code_indexer-1.1.
|
22
|
-
mcp_code_indexer-1.1.
|
18
|
+
mcp_code_indexer-1.1.5.dist-info/licenses/LICENSE,sha256=JN9dyPPgYwH9C-UjYM7FLNZjQ6BF7kAzpF3_4PwY4rY,1086
|
19
|
+
mcp_code_indexer-1.1.5.dist-info/METADATA,sha256=anW6T4WSZBQjIgvFaViNibdh3mgIeLW35vxiaj3wrjM,11930
|
20
|
+
mcp_code_indexer-1.1.5.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
21
|
+
mcp_code_indexer-1.1.5.dist-info/entry_points.txt,sha256=8HqWOw1Is7jOP1bvIgaSwouvT9z_Boe-9hd4NzyJOhY,68
|
22
|
+
mcp_code_indexer-1.1.5.dist-info/top_level.txt,sha256=yKYCM-gMGt-cnupGfAhnZaoEsROLB6DQ1KFUuyKx4rw,17
|
23
|
+
mcp_code_indexer-1.1.5.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|