codexa 0.4.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.
- codexa-0.4.0.dist-info/METADATA +650 -0
- codexa-0.4.0.dist-info/RECORD +189 -0
- codexa-0.4.0.dist-info/WHEEL +5 -0
- codexa-0.4.0.dist-info/entry_points.txt +2 -0
- codexa-0.4.0.dist-info/licenses/LICENSE +21 -0
- codexa-0.4.0.dist-info/top_level.txt +1 -0
- semantic_code_intelligence/__init__.py +5 -0
- semantic_code_intelligence/analysis/__init__.py +21 -0
- semantic_code_intelligence/analysis/ai_features.py +351 -0
- semantic_code_intelligence/bridge/__init__.py +28 -0
- semantic_code_intelligence/bridge/context_provider.py +245 -0
- semantic_code_intelligence/bridge/protocol.py +167 -0
- semantic_code_intelligence/bridge/server.py +348 -0
- semantic_code_intelligence/bridge/vscode.py +271 -0
- semantic_code_intelligence/ci/__init__.py +13 -0
- semantic_code_intelligence/ci/hooks.py +98 -0
- semantic_code_intelligence/ci/hotspots.py +272 -0
- semantic_code_intelligence/ci/impact.py +246 -0
- semantic_code_intelligence/ci/metrics.py +591 -0
- semantic_code_intelligence/ci/pr.py +412 -0
- semantic_code_intelligence/ci/quality.py +557 -0
- semantic_code_intelligence/ci/templates.py +164 -0
- semantic_code_intelligence/ci/trace.py +224 -0
- semantic_code_intelligence/cli/__init__.py +0 -0
- semantic_code_intelligence/cli/commands/__init__.py +0 -0
- semantic_code_intelligence/cli/commands/ask_cmd.py +153 -0
- semantic_code_intelligence/cli/commands/benchmark_cmd.py +303 -0
- semantic_code_intelligence/cli/commands/chat_cmd.py +252 -0
- semantic_code_intelligence/cli/commands/ci_gen_cmd.py +74 -0
- semantic_code_intelligence/cli/commands/context_cmd.py +120 -0
- semantic_code_intelligence/cli/commands/cross_refactor_cmd.py +113 -0
- semantic_code_intelligence/cli/commands/deps_cmd.py +91 -0
- semantic_code_intelligence/cli/commands/docs_cmd.py +101 -0
- semantic_code_intelligence/cli/commands/doctor_cmd.py +147 -0
- semantic_code_intelligence/cli/commands/evolve_cmd.py +171 -0
- semantic_code_intelligence/cli/commands/explain_cmd.py +112 -0
- semantic_code_intelligence/cli/commands/gate_cmd.py +135 -0
- semantic_code_intelligence/cli/commands/grep_cmd.py +234 -0
- semantic_code_intelligence/cli/commands/hotspots_cmd.py +119 -0
- semantic_code_intelligence/cli/commands/impact_cmd.py +131 -0
- semantic_code_intelligence/cli/commands/index_cmd.py +138 -0
- semantic_code_intelligence/cli/commands/init_cmd.py +152 -0
- semantic_code_intelligence/cli/commands/investigate_cmd.py +163 -0
- semantic_code_intelligence/cli/commands/languages_cmd.py +101 -0
- semantic_code_intelligence/cli/commands/lsp_cmd.py +49 -0
- semantic_code_intelligence/cli/commands/mcp_cmd.py +50 -0
- semantic_code_intelligence/cli/commands/metrics_cmd.py +264 -0
- semantic_code_intelligence/cli/commands/models_cmd.py +157 -0
- semantic_code_intelligence/cli/commands/plugin_cmd.py +275 -0
- semantic_code_intelligence/cli/commands/pr_summary_cmd.py +178 -0
- semantic_code_intelligence/cli/commands/quality_cmd.py +208 -0
- semantic_code_intelligence/cli/commands/refactor_cmd.py +103 -0
- semantic_code_intelligence/cli/commands/review_cmd.py +88 -0
- semantic_code_intelligence/cli/commands/search_cmd.py +236 -0
- semantic_code_intelligence/cli/commands/serve_cmd.py +117 -0
- semantic_code_intelligence/cli/commands/suggest_cmd.py +100 -0
- semantic_code_intelligence/cli/commands/summary_cmd.py +78 -0
- semantic_code_intelligence/cli/commands/tool_cmd.py +282 -0
- semantic_code_intelligence/cli/commands/trace_cmd.py +123 -0
- semantic_code_intelligence/cli/commands/tui_cmd.py +58 -0
- semantic_code_intelligence/cli/commands/viz_cmd.py +127 -0
- semantic_code_intelligence/cli/commands/watch_cmd.py +72 -0
- semantic_code_intelligence/cli/commands/web_cmd.py +61 -0
- semantic_code_intelligence/cli/commands/workspace_cmd.py +250 -0
- semantic_code_intelligence/cli/main.py +65 -0
- semantic_code_intelligence/cli/router.py +92 -0
- semantic_code_intelligence/config/__init__.py +0 -0
- semantic_code_intelligence/config/settings.py +260 -0
- semantic_code_intelligence/context/__init__.py +19 -0
- semantic_code_intelligence/context/engine.py +429 -0
- semantic_code_intelligence/context/memory.py +253 -0
- semantic_code_intelligence/daemon/__init__.py +1 -0
- semantic_code_intelligence/daemon/watcher.py +515 -0
- semantic_code_intelligence/docs/__init__.py +1080 -0
- semantic_code_intelligence/embeddings/__init__.py +0 -0
- semantic_code_intelligence/embeddings/enhanced.py +131 -0
- semantic_code_intelligence/embeddings/generator.py +149 -0
- semantic_code_intelligence/embeddings/model_registry.py +100 -0
- semantic_code_intelligence/evolution/__init__.py +1 -0
- semantic_code_intelligence/evolution/budget_guard.py +111 -0
- semantic_code_intelligence/evolution/commit_manager.py +88 -0
- semantic_code_intelligence/evolution/context_builder.py +131 -0
- semantic_code_intelligence/evolution/engine.py +249 -0
- semantic_code_intelligence/evolution/patch_generator.py +229 -0
- semantic_code_intelligence/evolution/task_selector.py +214 -0
- semantic_code_intelligence/evolution/test_runner.py +111 -0
- semantic_code_intelligence/indexing/__init__.py +0 -0
- semantic_code_intelligence/indexing/chunker.py +174 -0
- semantic_code_intelligence/indexing/parallel.py +86 -0
- semantic_code_intelligence/indexing/scanner.py +146 -0
- semantic_code_intelligence/indexing/semantic_chunker.py +337 -0
- semantic_code_intelligence/llm/__init__.py +62 -0
- semantic_code_intelligence/llm/cache.py +219 -0
- semantic_code_intelligence/llm/cached_provider.py +145 -0
- semantic_code_intelligence/llm/conversation.py +190 -0
- semantic_code_intelligence/llm/cross_refactor.py +272 -0
- semantic_code_intelligence/llm/investigation.py +274 -0
- semantic_code_intelligence/llm/mock_provider.py +77 -0
- semantic_code_intelligence/llm/ollama_provider.py +122 -0
- semantic_code_intelligence/llm/openai_provider.py +100 -0
- semantic_code_intelligence/llm/provider.py +92 -0
- semantic_code_intelligence/llm/rate_limiter.py +164 -0
- semantic_code_intelligence/llm/reasoning.py +438 -0
- semantic_code_intelligence/llm/safety.py +110 -0
- semantic_code_intelligence/llm/streaming.py +251 -0
- semantic_code_intelligence/lsp/__init__.py +609 -0
- semantic_code_intelligence/mcp/__init__.py +393 -0
- semantic_code_intelligence/parsing/__init__.py +19 -0
- semantic_code_intelligence/parsing/parser.py +375 -0
- semantic_code_intelligence/plugins/__init__.py +255 -0
- semantic_code_intelligence/plugins/examples/__init__.py +1 -0
- semantic_code_intelligence/plugins/examples/code_quality.py +73 -0
- semantic_code_intelligence/plugins/examples/search_annotator.py +56 -0
- semantic_code_intelligence/scalability/__init__.py +205 -0
- semantic_code_intelligence/search/__init__.py +0 -0
- semantic_code_intelligence/search/formatter.py +123 -0
- semantic_code_intelligence/search/grep.py +361 -0
- semantic_code_intelligence/search/hybrid_search.py +170 -0
- semantic_code_intelligence/search/keyword_search.py +311 -0
- semantic_code_intelligence/search/section_expander.py +103 -0
- semantic_code_intelligence/services/__init__.py +0 -0
- semantic_code_intelligence/services/indexing_service.py +630 -0
- semantic_code_intelligence/services/search_service.py +269 -0
- semantic_code_intelligence/storage/__init__.py +0 -0
- semantic_code_intelligence/storage/chunk_hash_store.py +86 -0
- semantic_code_intelligence/storage/hash_store.py +66 -0
- semantic_code_intelligence/storage/index_manifest.py +85 -0
- semantic_code_intelligence/storage/index_stats.py +138 -0
- semantic_code_intelligence/storage/query_history.py +160 -0
- semantic_code_intelligence/storage/symbol_registry.py +209 -0
- semantic_code_intelligence/storage/vector_store.py +297 -0
- semantic_code_intelligence/tests/__init__.py +0 -0
- semantic_code_intelligence/tests/test_ai_features.py +351 -0
- semantic_code_intelligence/tests/test_chunker.py +119 -0
- semantic_code_intelligence/tests/test_cli.py +188 -0
- semantic_code_intelligence/tests/test_config.py +154 -0
- semantic_code_intelligence/tests/test_context.py +381 -0
- semantic_code_intelligence/tests/test_embeddings.py +73 -0
- semantic_code_intelligence/tests/test_endtoend.py +1142 -0
- semantic_code_intelligence/tests/test_enhanced_embeddings.py +92 -0
- semantic_code_intelligence/tests/test_hash_store.py +79 -0
- semantic_code_intelligence/tests/test_logging.py +55 -0
- semantic_code_intelligence/tests/test_new_cli.py +138 -0
- semantic_code_intelligence/tests/test_parser.py +495 -0
- semantic_code_intelligence/tests/test_phase10.py +355 -0
- semantic_code_intelligence/tests/test_phase11.py +593 -0
- semantic_code_intelligence/tests/test_phase12.py +375 -0
- semantic_code_intelligence/tests/test_phase13.py +663 -0
- semantic_code_intelligence/tests/test_phase14.py +568 -0
- semantic_code_intelligence/tests/test_phase15.py +814 -0
- semantic_code_intelligence/tests/test_phase16.py +792 -0
- semantic_code_intelligence/tests/test_phase17.py +815 -0
- semantic_code_intelligence/tests/test_phase18.py +934 -0
- semantic_code_intelligence/tests/test_phase19.py +986 -0
- semantic_code_intelligence/tests/test_phase20.py +2753 -0
- semantic_code_intelligence/tests/test_phase20b.py +2058 -0
- semantic_code_intelligence/tests/test_phase20c.py +962 -0
- semantic_code_intelligence/tests/test_phase21.py +428 -0
- semantic_code_intelligence/tests/test_phase22.py +799 -0
- semantic_code_intelligence/tests/test_phase23.py +783 -0
- semantic_code_intelligence/tests/test_phase24.py +715 -0
- semantic_code_intelligence/tests/test_phase25.py +496 -0
- semantic_code_intelligence/tests/test_phase26.py +251 -0
- semantic_code_intelligence/tests/test_phase27.py +531 -0
- semantic_code_intelligence/tests/test_phase8.py +592 -0
- semantic_code_intelligence/tests/test_phase9.py +643 -0
- semantic_code_intelligence/tests/test_plugins.py +293 -0
- semantic_code_intelligence/tests/test_priority_features.py +727 -0
- semantic_code_intelligence/tests/test_router.py +41 -0
- semantic_code_intelligence/tests/test_scalability.py +138 -0
- semantic_code_intelligence/tests/test_scanner.py +125 -0
- semantic_code_intelligence/tests/test_search.py +160 -0
- semantic_code_intelligence/tests/test_semantic_chunker.py +255 -0
- semantic_code_intelligence/tests/test_tools.py +182 -0
- semantic_code_intelligence/tests/test_vector_store.py +151 -0
- semantic_code_intelligence/tests/test_watcher.py +211 -0
- semantic_code_intelligence/tools/__init__.py +442 -0
- semantic_code_intelligence/tools/executor.py +232 -0
- semantic_code_intelligence/tools/protocol.py +200 -0
- semantic_code_intelligence/tui/__init__.py +454 -0
- semantic_code_intelligence/utils/__init__.py +0 -0
- semantic_code_intelligence/utils/logging.py +112 -0
- semantic_code_intelligence/version.py +3 -0
- semantic_code_intelligence/web/__init__.py +11 -0
- semantic_code_intelligence/web/api.py +289 -0
- semantic_code_intelligence/web/server.py +397 -0
- semantic_code_intelligence/web/ui.py +659 -0
- semantic_code_intelligence/web/visualize.py +226 -0
- semantic_code_intelligence/workspace/__init__.py +427 -0
|
@@ -0,0 +1,393 @@
|
|
|
1
|
+
"""MCP server — Model Context Protocol server for AI agent integration.
|
|
2
|
+
|
|
3
|
+
Uses the official ``mcp`` SDK (https://pypi.org/project/mcp/) to expose
|
|
4
|
+
CodexA as a tool provider for Claude Desktop, Cursor, and other
|
|
5
|
+
MCP-compatible clients.
|
|
6
|
+
|
|
7
|
+
Exposes 11 tools: semantic_search, keyword_search, hybrid_search,
|
|
8
|
+
regex_search, explain_symbol, index_status, reindex, health_check,
|
|
9
|
+
get_quality_score, find_duplicates, grep_files.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
|
|
14
|
+
import asyncio
|
|
15
|
+
import json
|
|
16
|
+
from pathlib import Path
|
|
17
|
+
from typing import Any
|
|
18
|
+
|
|
19
|
+
try:
|
|
20
|
+
from mcp.server import Server
|
|
21
|
+
from mcp.server.stdio import stdio_server
|
|
22
|
+
from mcp.types import TextContent, Tool
|
|
23
|
+
_HAS_MCP = True
|
|
24
|
+
except ImportError: # mcp SDK not installed (e.g. Python 3.13)
|
|
25
|
+
_HAS_MCP = False
|
|
26
|
+
Server = None # type: ignore[assignment,misc]
|
|
27
|
+
TextContent = None # type: ignore[assignment,misc]
|
|
28
|
+
Tool = None # type: ignore[assignment,misc]
|
|
29
|
+
|
|
30
|
+
from semantic_code_intelligence.config.settings import AppConfig
|
|
31
|
+
from semantic_code_intelligence.utils.logging import get_logger
|
|
32
|
+
|
|
33
|
+
logger = get_logger("mcp")
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
# ---- Tool definitions ---------------------------------------------------
|
|
37
|
+
|
|
38
|
+
if _HAS_MCP:
|
|
39
|
+
MCP_TOOLS = [
|
|
40
|
+
Tool(
|
|
41
|
+
name="semantic_search",
|
|
42
|
+
description="Semantic vector similarity search over the indexed codebase.",
|
|
43
|
+
inputSchema={
|
|
44
|
+
"type": "object",
|
|
45
|
+
"properties": {
|
|
46
|
+
"query": {"type": "string", "description": "Natural language search query."},
|
|
47
|
+
"top_k": {"type": "integer", "description": "Number of results.", "default": 10},
|
|
48
|
+
},
|
|
49
|
+
"required": ["query"],
|
|
50
|
+
},
|
|
51
|
+
),
|
|
52
|
+
Tool(
|
|
53
|
+
name="keyword_search",
|
|
54
|
+
description="BM25-ranked keyword search over indexed code chunks.",
|
|
55
|
+
inputSchema={
|
|
56
|
+
"type": "object",
|
|
57
|
+
"properties": {
|
|
58
|
+
"query": {"type": "string", "description": "Keyword query."},
|
|
59
|
+
"top_k": {"type": "integer", "default": 10},
|
|
60
|
+
},
|
|
61
|
+
"required": ["query"],
|
|
62
|
+
},
|
|
63
|
+
),
|
|
64
|
+
Tool(
|
|
65
|
+
name="hybrid_search",
|
|
66
|
+
description="Fused semantic + BM25 search via Reciprocal Rank Fusion.",
|
|
67
|
+
inputSchema={
|
|
68
|
+
"type": "object",
|
|
69
|
+
"properties": {
|
|
70
|
+
"query": {"type": "string", "description": "Search query."},
|
|
71
|
+
"top_k": {"type": "integer", "default": 10},
|
|
72
|
+
},
|
|
73
|
+
"required": ["query"],
|
|
74
|
+
},
|
|
75
|
+
),
|
|
76
|
+
Tool(
|
|
77
|
+
name="regex_search",
|
|
78
|
+
description="Grep-compatible regex search over indexed code.",
|
|
79
|
+
inputSchema={
|
|
80
|
+
"type": "object",
|
|
81
|
+
"properties": {
|
|
82
|
+
"pattern": {"type": "string", "description": "Regex pattern."},
|
|
83
|
+
"top_k": {"type": "integer", "default": 10},
|
|
84
|
+
"case_insensitive": {"type": "boolean", "default": True},
|
|
85
|
+
},
|
|
86
|
+
"required": ["pattern"],
|
|
87
|
+
},
|
|
88
|
+
),
|
|
89
|
+
Tool(
|
|
90
|
+
name="explain_symbol",
|
|
91
|
+
description="Get detailed info about a code symbol (function, class, method).",
|
|
92
|
+
inputSchema={
|
|
93
|
+
"type": "object",
|
|
94
|
+
"properties": {
|
|
95
|
+
"symbol_name": {"type": "string", "description": "Name of the symbol."},
|
|
96
|
+
},
|
|
97
|
+
"required": ["symbol_name"],
|
|
98
|
+
},
|
|
99
|
+
),
|
|
100
|
+
Tool(
|
|
101
|
+
name="index_status",
|
|
102
|
+
description="Get the current index health and stats.",
|
|
103
|
+
inputSchema={"type": "object", "properties": {}},
|
|
104
|
+
),
|
|
105
|
+
Tool(
|
|
106
|
+
name="reindex",
|
|
107
|
+
description="Trigger a full or incremental re-index of the codebase.",
|
|
108
|
+
inputSchema={
|
|
109
|
+
"type": "object",
|
|
110
|
+
"properties": {
|
|
111
|
+
"force": {"type": "boolean", "default": False},
|
|
112
|
+
},
|
|
113
|
+
},
|
|
114
|
+
),
|
|
115
|
+
Tool(
|
|
116
|
+
name="health_check",
|
|
117
|
+
description="Check if the MCP server is running and responsive.",
|
|
118
|
+
inputSchema={"type": "object", "properties": {}},
|
|
119
|
+
),
|
|
120
|
+
Tool(
|
|
121
|
+
name="get_quality_score",
|
|
122
|
+
description="Run code quality analysis: complexity, dead code, duplicates, safety.",
|
|
123
|
+
inputSchema={
|
|
124
|
+
"type": "object",
|
|
125
|
+
"properties": {
|
|
126
|
+
"file_path": {"type": "string", "description": "Specific file to analyze (omit for full project)."},
|
|
127
|
+
},
|
|
128
|
+
},
|
|
129
|
+
),
|
|
130
|
+
Tool(
|
|
131
|
+
name="find_duplicates",
|
|
132
|
+
description="Detect duplicate or near-duplicate code blocks.",
|
|
133
|
+
inputSchema={
|
|
134
|
+
"type": "object",
|
|
135
|
+
"properties": {
|
|
136
|
+
"threshold": {"type": "number", "default": 0.75, "description": "Similarity threshold (0-1)."},
|
|
137
|
+
},
|
|
138
|
+
},
|
|
139
|
+
),
|
|
140
|
+
Tool(
|
|
141
|
+
name="grep_files",
|
|
142
|
+
description="Search raw files using regex — no index required. Uses ripgrep when available.",
|
|
143
|
+
inputSchema={
|
|
144
|
+
"type": "object",
|
|
145
|
+
"properties": {
|
|
146
|
+
"pattern": {"type": "string", "description": "Regex pattern."},
|
|
147
|
+
"file_glob": {"type": "string", "description": "Glob filter (e.g. '*.py')."},
|
|
148
|
+
"max_results": {"type": "integer", "default": 50},
|
|
149
|
+
},
|
|
150
|
+
"required": ["pattern"],
|
|
151
|
+
},
|
|
152
|
+
),
|
|
153
|
+
Tool(
|
|
154
|
+
name="get_file_context",
|
|
155
|
+
description="Retrieve full surrounding function/class context for a symbol or line in a file.",
|
|
156
|
+
inputSchema={
|
|
157
|
+
"type": "object",
|
|
158
|
+
"properties": {
|
|
159
|
+
"file_path": {"type": "string", "description": "Relative file path."},
|
|
160
|
+
"line": {"type": "integer", "description": "Line number to get context for."},
|
|
161
|
+
"symbol_name": {"type": "string", "description": "Symbol name to locate."},
|
|
162
|
+
},
|
|
163
|
+
"required": ["file_path"],
|
|
164
|
+
},
|
|
165
|
+
),
|
|
166
|
+
Tool(
|
|
167
|
+
name="list_languages",
|
|
168
|
+
description="List all programming languages supported by the tree-sitter parser.",
|
|
169
|
+
inputSchema={"type": "object", "properties": {}},
|
|
170
|
+
),
|
|
171
|
+
]
|
|
172
|
+
else:
|
|
173
|
+
MCP_TOOLS: list = [] # type: ignore[no-redef]
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
# ---- Tool dispatch -------------------------------------------------------
|
|
177
|
+
|
|
178
|
+
def _dispatch_tool(name: str, args: dict[str, Any], project_root: Path) -> Any:
|
|
179
|
+
"""Execute a tool and return its result."""
|
|
180
|
+
from semantic_code_intelligence.services.search_service import search_codebase
|
|
181
|
+
from semantic_code_intelligence.services.indexing_service import run_indexing
|
|
182
|
+
from semantic_code_intelligence.storage.index_stats import IndexStats
|
|
183
|
+
from semantic_code_intelligence.storage.symbol_registry import SymbolRegistry
|
|
184
|
+
|
|
185
|
+
index_dir = AppConfig.index_dir(project_root)
|
|
186
|
+
|
|
187
|
+
if name == "semantic_search":
|
|
188
|
+
results = search_codebase(
|
|
189
|
+
query=args["query"], project_root=project_root,
|
|
190
|
+
top_k=args.get("top_k", 10), mode="semantic",
|
|
191
|
+
)
|
|
192
|
+
return [r.to_dict() for r in results]
|
|
193
|
+
|
|
194
|
+
if name == "keyword_search":
|
|
195
|
+
results = search_codebase(
|
|
196
|
+
query=args["query"], project_root=project_root,
|
|
197
|
+
top_k=args.get("top_k", 10), mode="keyword",
|
|
198
|
+
)
|
|
199
|
+
return [r.to_dict() for r in results]
|
|
200
|
+
|
|
201
|
+
if name == "hybrid_search":
|
|
202
|
+
results = search_codebase(
|
|
203
|
+
query=args["query"], project_root=project_root,
|
|
204
|
+
top_k=args.get("top_k", 10), mode="hybrid",
|
|
205
|
+
)
|
|
206
|
+
return [r.to_dict() for r in results]
|
|
207
|
+
|
|
208
|
+
if name == "regex_search":
|
|
209
|
+
results = search_codebase(
|
|
210
|
+
query=args["pattern"], project_root=project_root,
|
|
211
|
+
top_k=args.get("top_k", 10), mode="regex",
|
|
212
|
+
case_insensitive=args.get("case_insensitive", True),
|
|
213
|
+
)
|
|
214
|
+
return [r.to_dict() for r in results]
|
|
215
|
+
|
|
216
|
+
if name == "explain_symbol":
|
|
217
|
+
registry = SymbolRegistry.load(index_dir)
|
|
218
|
+
entries = registry.find_by_name(args["symbol_name"])
|
|
219
|
+
return [
|
|
220
|
+
{
|
|
221
|
+
"name": e.name, "kind": e.kind, "file_path": e.file_path,
|
|
222
|
+
"start_line": e.start_line, "end_line": e.end_line,
|
|
223
|
+
"parent": e.parent, "parameters": e.parameters,
|
|
224
|
+
"language": e.language,
|
|
225
|
+
}
|
|
226
|
+
for e in entries
|
|
227
|
+
]
|
|
228
|
+
|
|
229
|
+
if name == "index_status":
|
|
230
|
+
try:
|
|
231
|
+
stats = IndexStats.load(index_dir)
|
|
232
|
+
return stats.to_dict() if stats else {"status": "no index"}
|
|
233
|
+
except Exception:
|
|
234
|
+
return {"status": "no index"}
|
|
235
|
+
|
|
236
|
+
if name == "reindex":
|
|
237
|
+
result = run_indexing(project_root, force=args.get("force", False))
|
|
238
|
+
return {
|
|
239
|
+
"files_indexed": result.files_indexed,
|
|
240
|
+
"chunks_created": result.chunks_created,
|
|
241
|
+
"chunks_reused": result.chunks_reused,
|
|
242
|
+
"total_vectors": result.total_vectors,
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
if name == "health_check":
|
|
246
|
+
return {"status": "ok", "project_root": str(project_root)}
|
|
247
|
+
|
|
248
|
+
if name == "get_quality_score":
|
|
249
|
+
from semantic_code_intelligence.ci.quality import analyze_project
|
|
250
|
+
file_paths = [args["file_path"]] if args.get("file_path") else None
|
|
251
|
+
report = analyze_project(project_root, file_paths=file_paths)
|
|
252
|
+
return {
|
|
253
|
+
"complexity_issues": len(report.complexity_issues),
|
|
254
|
+
"dead_code": len(report.dead_code),
|
|
255
|
+
"duplicates": len(report.duplicates),
|
|
256
|
+
"safety_issues": len(report.bandit_issues),
|
|
257
|
+
"maintainability_index": report.maintainability_index,
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
if name == "find_duplicates":
|
|
261
|
+
from semantic_code_intelligence.ci.quality import detect_duplicates
|
|
262
|
+
from semantic_code_intelligence.context.engine import ContextBuilder
|
|
263
|
+
builder = ContextBuilder()
|
|
264
|
+
from semantic_code_intelligence.indexing.scanner import scan_repository
|
|
265
|
+
from semantic_code_intelligence.config.settings import load_config
|
|
266
|
+
config = load_config(project_root)
|
|
267
|
+
scanned = scan_repository(project_root, config.index)
|
|
268
|
+
for sf in scanned:
|
|
269
|
+
try:
|
|
270
|
+
builder.index_file(str(project_root / sf.relative_path))
|
|
271
|
+
except Exception:
|
|
272
|
+
pass
|
|
273
|
+
all_syms = builder.get_all_symbols()
|
|
274
|
+
threshold = args.get("threshold", 0.75)
|
|
275
|
+
dupes = detect_duplicates(all_syms, threshold=threshold)
|
|
276
|
+
return [
|
|
277
|
+
{"symbol_a": d.symbol_a, "symbol_b": d.symbol_b,
|
|
278
|
+
"similarity": round(d.similarity, 3),
|
|
279
|
+
"file_a": d.file_a, "file_b": d.file_b}
|
|
280
|
+
for d in dupes[:20]
|
|
281
|
+
]
|
|
282
|
+
|
|
283
|
+
if name == "grep_files":
|
|
284
|
+
from semantic_code_intelligence.search.grep import grep_search
|
|
285
|
+
result = grep_search(
|
|
286
|
+
args["pattern"], project_root,
|
|
287
|
+
max_results=args.get("max_results", 50),
|
|
288
|
+
file_glob=args.get("file_glob"),
|
|
289
|
+
)
|
|
290
|
+
return result.to_dict()
|
|
291
|
+
|
|
292
|
+
if name == "get_file_context":
|
|
293
|
+
file_path = args.get("file_path", "")
|
|
294
|
+
target_line = args.get("line")
|
|
295
|
+
symbol_name = args.get("symbol_name")
|
|
296
|
+
full_path = project_root / file_path
|
|
297
|
+
if not full_path.is_file():
|
|
298
|
+
return {"error": f"File not found: {file_path}"}
|
|
299
|
+
try:
|
|
300
|
+
content = full_path.read_text(encoding="utf-8", errors="replace")
|
|
301
|
+
except OSError as exc:
|
|
302
|
+
return {"error": str(exc)}
|
|
303
|
+
|
|
304
|
+
lines = content.splitlines()
|
|
305
|
+
|
|
306
|
+
# If symbol_name given, find its line
|
|
307
|
+
if symbol_name and not target_line:
|
|
308
|
+
for i, ln in enumerate(lines, 1):
|
|
309
|
+
if symbol_name in ln:
|
|
310
|
+
target_line = i
|
|
311
|
+
break
|
|
312
|
+
if not target_line:
|
|
313
|
+
return {"error": f"Symbol '{symbol_name}' not found in {file_path}"}
|
|
314
|
+
|
|
315
|
+
if not target_line:
|
|
316
|
+
return {"error": "Provide either 'line' or 'symbol_name'."}
|
|
317
|
+
|
|
318
|
+
# Return a generous context window (±30 lines)
|
|
319
|
+
start = max(0, target_line - 31)
|
|
320
|
+
end = min(len(lines), target_line + 30)
|
|
321
|
+
context_lines = lines[start:end]
|
|
322
|
+
return {
|
|
323
|
+
"file_path": file_path,
|
|
324
|
+
"start_line": start + 1,
|
|
325
|
+
"end_line": end,
|
|
326
|
+
"content": "\n".join(context_lines),
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
if name == "list_languages":
|
|
330
|
+
from semantic_code_intelligence.parsing.parser import (
|
|
331
|
+
_LANGUAGE_MODULES,
|
|
332
|
+
EXTENSION_TO_LANGUAGE,
|
|
333
|
+
)
|
|
334
|
+
ext_map: dict[str, list[str]] = {}
|
|
335
|
+
for ext, lang in EXTENSION_TO_LANGUAGE.items():
|
|
336
|
+
ext_map.setdefault(lang, []).append(ext)
|
|
337
|
+
return [
|
|
338
|
+
{"language": lang, "module": mod, "extensions": sorted(ext_map.get(lang, []))}
|
|
339
|
+
for lang, mod in sorted(_LANGUAGE_MODULES.items())
|
|
340
|
+
]
|
|
341
|
+
|
|
342
|
+
return {"error": f"Unknown tool: {name}"}
|
|
343
|
+
|
|
344
|
+
|
|
345
|
+
# ---- Server factory ------------------------------------------------------
|
|
346
|
+
|
|
347
|
+
def _create_server(project_root: Path) -> "Server":
|
|
348
|
+
"""Create and configure an MCP ``Server`` with all CodexA tools."""
|
|
349
|
+
if not _HAS_MCP:
|
|
350
|
+
raise RuntimeError("The 'mcp' package is required but not installed.")
|
|
351
|
+
server = Server("codexa-mcp")
|
|
352
|
+
|
|
353
|
+
@server.list_tools()
|
|
354
|
+
async def handle_list_tools() -> list:
|
|
355
|
+
return MCP_TOOLS
|
|
356
|
+
|
|
357
|
+
@server.call_tool()
|
|
358
|
+
async def handle_call_tool(name: str, arguments: dict | None) -> list:
|
|
359
|
+
args = arguments or {}
|
|
360
|
+
try:
|
|
361
|
+
result = _dispatch_tool(name, args, project_root)
|
|
362
|
+
return [TextContent(type="text", text=json.dumps(result, indent=2))]
|
|
363
|
+
except Exception as e:
|
|
364
|
+
return [TextContent(type="text", text=f"Error: {e}")]
|
|
365
|
+
|
|
366
|
+
return server
|
|
367
|
+
|
|
368
|
+
|
|
369
|
+
# ---- Entry point ---------------------------------------------------------
|
|
370
|
+
|
|
371
|
+
def run_mcp_server(project_root: Path) -> None:
|
|
372
|
+
"""Run the MCP server in stdio mode.
|
|
373
|
+
|
|
374
|
+
Uses the official MCP SDK to handle JSON-RPC over stdio.
|
|
375
|
+
Compatible with Claude Desktop, Cursor, and other MCP clients.
|
|
376
|
+
"""
|
|
377
|
+
if not _HAS_MCP:
|
|
378
|
+
raise RuntimeError("The 'mcp' package is required but not installed.")
|
|
379
|
+
project_root = project_root.resolve()
|
|
380
|
+
logger.info("MCP server starting for %s", project_root)
|
|
381
|
+
|
|
382
|
+
server = _create_server(project_root)
|
|
383
|
+
|
|
384
|
+
async def _run() -> None:
|
|
385
|
+
async with stdio_server() as (read_stream, write_stream):
|
|
386
|
+
await server.run(
|
|
387
|
+
read_stream,
|
|
388
|
+
write_stream,
|
|
389
|
+
server.create_initialization_options(),
|
|
390
|
+
)
|
|
391
|
+
|
|
392
|
+
asyncio.run(_run())
|
|
393
|
+
logger.info("MCP server stopped.")
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"""Parsing package — tree-sitter based code analysis."""
|
|
2
|
+
|
|
3
|
+
from semantic_code_intelligence.parsing.parser import (
|
|
4
|
+
Symbol,
|
|
5
|
+
detect_language,
|
|
6
|
+
extract_classes,
|
|
7
|
+
extract_functions,
|
|
8
|
+
extract_imports,
|
|
9
|
+
parse_file,
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
__all__ = [
|
|
13
|
+
"Symbol",
|
|
14
|
+
"detect_language",
|
|
15
|
+
"extract_classes",
|
|
16
|
+
"extract_functions",
|
|
17
|
+
"extract_imports",
|
|
18
|
+
"parse_file",
|
|
19
|
+
]
|