tree-sitter-analyzer 0.3.0__py3-none-any.whl → 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.
Potentially problematic release.
This version of tree-sitter-analyzer might be problematic. Click here for more details.
- tree_sitter_analyzer/__init__.py +4 -3
- tree_sitter_analyzer/api.py +4 -2
- tree_sitter_analyzer/cli/__init__.py +3 -3
- tree_sitter_analyzer/cli/commands/advanced_command.py +1 -1
- tree_sitter_analyzer/cli/commands/base_command.py +1 -1
- tree_sitter_analyzer/cli/commands/partial_read_command.py +2 -2
- tree_sitter_analyzer/cli/commands/summary_command.py +2 -2
- tree_sitter_analyzer/cli/commands/table_command.py +11 -8
- tree_sitter_analyzer/cli_main.py +2 -1
- tree_sitter_analyzer/core/analysis_engine.py +33 -69
- tree_sitter_analyzer/core/engine.py +6 -4
- tree_sitter_analyzer/core/parser.py +1 -1
- tree_sitter_analyzer/core/query.py +16 -8
- tree_sitter_analyzer/encoding_utils.py +0 -2
- tree_sitter_analyzer/exceptions.py +23 -23
- tree_sitter_analyzer/file_handler.py +4 -11
- tree_sitter_analyzer/formatters/java_formatter.py +8 -4
- tree_sitter_analyzer/formatters/python_formatter.py +8 -4
- tree_sitter_analyzer/interfaces/cli.py +1 -1
- tree_sitter_analyzer/interfaces/cli_adapter.py +30 -9
- tree_sitter_analyzer/interfaces/mcp_adapter.py +43 -17
- tree_sitter_analyzer/interfaces/mcp_server.py +9 -9
- tree_sitter_analyzer/java_analyzer.py +20 -51
- tree_sitter_analyzer/language_loader.py +2 -2
- tree_sitter_analyzer/languages/java_plugin.py +86 -41
- tree_sitter_analyzer/{plugins → languages}/javascript_plugin.py +3 -3
- tree_sitter_analyzer/languages/python_plugin.py +16 -6
- tree_sitter_analyzer/mcp/resources/code_file_resource.py +0 -3
- tree_sitter_analyzer/mcp/resources/project_stats_resource.py +0 -5
- tree_sitter_analyzer/mcp/server.py +4 -4
- tree_sitter_analyzer/mcp/tools/analyze_scale_tool.py +63 -30
- tree_sitter_analyzer/mcp/tools/analyze_scale_tool_cli_compatible.py +9 -4
- tree_sitter_analyzer/mcp/tools/table_format_tool.py +2 -2
- tree_sitter_analyzer/mcp/utils/__init__.py +10 -8
- tree_sitter_analyzer/models.py +1 -1
- tree_sitter_analyzer/output_manager.py +4 -10
- tree_sitter_analyzer/plugins/__init__.py +9 -62
- tree_sitter_analyzer/plugins/base.py +20 -1
- tree_sitter_analyzer/plugins/manager.py +29 -12
- tree_sitter_analyzer/query_loader.py +4 -1
- tree_sitter_analyzer/table_formatter.py +4 -1
- tree_sitter_analyzer/utils.py +6 -6
- {tree_sitter_analyzer-0.3.0.dist-info → tree_sitter_analyzer-0.4.0.dist-info}/METADATA +3 -3
- tree_sitter_analyzer-0.4.0.dist-info/RECORD +73 -0
- {tree_sitter_analyzer-0.3.0.dist-info → tree_sitter_analyzer-0.4.0.dist-info}/entry_points.txt +2 -1
- tree_sitter_analyzer/plugins/java_plugin.py +0 -608
- tree_sitter_analyzer/plugins/plugin_loader.py +0 -85
- tree_sitter_analyzer/plugins/python_plugin.py +0 -606
- tree_sitter_analyzer/plugins/registry.py +0 -374
- tree_sitter_analyzer-0.3.0.dist-info/RECORD +0 -77
- {tree_sitter_analyzer-0.3.0.dist-info → tree_sitter_analyzer-0.4.0.dist-info}/WHEEL +0 -0
|
@@ -42,7 +42,7 @@ class AnalysisError(TreeSitterAnalyzerError):
|
|
|
42
42
|
message: str,
|
|
43
43
|
file_path: str | Path | None = None,
|
|
44
44
|
language: str | None = None,
|
|
45
|
-
**kwargs,
|
|
45
|
+
**kwargs: Any,
|
|
46
46
|
) -> None:
|
|
47
47
|
context = kwargs.get("context", {})
|
|
48
48
|
if file_path:
|
|
@@ -60,7 +60,7 @@ class ParseError(TreeSitterAnalyzerError):
|
|
|
60
60
|
message: str,
|
|
61
61
|
language: str | None = None,
|
|
62
62
|
source_info: dict[str, Any] | None = None,
|
|
63
|
-
**kwargs,
|
|
63
|
+
**kwargs: Any,
|
|
64
64
|
) -> None:
|
|
65
65
|
context = kwargs.get("context", {})
|
|
66
66
|
if language:
|
|
@@ -74,7 +74,7 @@ class LanguageNotSupportedError(TreeSitterAnalyzerError):
|
|
|
74
74
|
"""Raised when a language is not supported."""
|
|
75
75
|
|
|
76
76
|
def __init__(
|
|
77
|
-
self, language: str, supported_languages: list | None = None, **kwargs
|
|
77
|
+
self, language: str, supported_languages: list[str] | None = None, **kwargs: Any
|
|
78
78
|
) -> None:
|
|
79
79
|
message = f"Language '{language}' is not supported"
|
|
80
80
|
context = kwargs.get("context", {})
|
|
@@ -93,7 +93,7 @@ class PluginError(TreeSitterAnalyzerError):
|
|
|
93
93
|
message: str,
|
|
94
94
|
plugin_name: str | None = None,
|
|
95
95
|
operation: str | None = None,
|
|
96
|
-
**kwargs,
|
|
96
|
+
**kwargs: Any,
|
|
97
97
|
) -> None:
|
|
98
98
|
context = kwargs.get("context", {})
|
|
99
99
|
if plugin_name:
|
|
@@ -112,7 +112,7 @@ class QueryError(TreeSitterAnalyzerError):
|
|
|
112
112
|
query_name: str | None = None,
|
|
113
113
|
query_string: str | None = None,
|
|
114
114
|
language: str | None = None,
|
|
115
|
-
**kwargs,
|
|
115
|
+
**kwargs: Any,
|
|
116
116
|
) -> None:
|
|
117
117
|
context = kwargs.get("context", {})
|
|
118
118
|
if query_name:
|
|
@@ -132,7 +132,7 @@ class FileHandlingError(TreeSitterAnalyzerError):
|
|
|
132
132
|
message: str,
|
|
133
133
|
file_path: str | Path | None = None,
|
|
134
134
|
operation: str | None = None,
|
|
135
|
-
**kwargs,
|
|
135
|
+
**kwargs: Any,
|
|
136
136
|
) -> None:
|
|
137
137
|
context = kwargs.get("context", {})
|
|
138
138
|
if file_path:
|
|
@@ -150,7 +150,7 @@ class ConfigurationError(TreeSitterAnalyzerError):
|
|
|
150
150
|
message: str,
|
|
151
151
|
config_key: str | None = None,
|
|
152
152
|
config_value: Any | None = None,
|
|
153
|
-
**kwargs,
|
|
153
|
+
**kwargs: Any,
|
|
154
154
|
) -> None:
|
|
155
155
|
context = kwargs.get("context", {})
|
|
156
156
|
if config_key:
|
|
@@ -168,7 +168,7 @@ class ValidationError(TreeSitterAnalyzerError):
|
|
|
168
168
|
message: str,
|
|
169
169
|
validation_type: str | None = None,
|
|
170
170
|
invalid_value: Any | None = None,
|
|
171
|
-
**kwargs,
|
|
171
|
+
**kwargs: Any,
|
|
172
172
|
) -> None:
|
|
173
173
|
context = kwargs.get("context", {})
|
|
174
174
|
if validation_type:
|
|
@@ -186,7 +186,7 @@ class MCPError(TreeSitterAnalyzerError):
|
|
|
186
186
|
message: str,
|
|
187
187
|
tool_name: str | None = None,
|
|
188
188
|
resource_uri: str | None = None,
|
|
189
|
-
**kwargs,
|
|
189
|
+
**kwargs: Any,
|
|
190
190
|
) -> None:
|
|
191
191
|
context = kwargs.get("context", {})
|
|
192
192
|
if tool_name:
|
|
@@ -200,7 +200,7 @@ class MCPError(TreeSitterAnalyzerError):
|
|
|
200
200
|
def handle_exception(
|
|
201
201
|
exception: Exception,
|
|
202
202
|
context: dict[str, Any] | None = None,
|
|
203
|
-
reraise_as: type | None = None,
|
|
203
|
+
reraise_as: type[Exception] | None = None,
|
|
204
204
|
) -> None:
|
|
205
205
|
"""
|
|
206
206
|
Handle exceptions with optional context and re-raising.
|
|
@@ -231,13 +231,13 @@ def handle_exception(
|
|
|
231
231
|
|
|
232
232
|
|
|
233
233
|
def safe_execute(
|
|
234
|
-
func,
|
|
235
|
-
*args,
|
|
236
|
-
default_return=None,
|
|
237
|
-
exception_types: tuple = (Exception,),
|
|
234
|
+
func: Any,
|
|
235
|
+
*args: Any,
|
|
236
|
+
default_return: Any = None,
|
|
237
|
+
exception_types: tuple[type[Exception], ...] = (Exception,),
|
|
238
238
|
log_errors: bool = True,
|
|
239
|
-
**kwargs,
|
|
240
|
-
):
|
|
239
|
+
**kwargs: Any,
|
|
240
|
+
) -> Any:
|
|
241
241
|
"""
|
|
242
242
|
Safely execute a function with exception handling.
|
|
243
243
|
|
|
@@ -277,7 +277,7 @@ def create_error_response(
|
|
|
277
277
|
"""
|
|
278
278
|
import traceback
|
|
279
279
|
|
|
280
|
-
response = {
|
|
280
|
+
response: dict[str, Any] = {
|
|
281
281
|
"success": False,
|
|
282
282
|
"error": {"type": exception.__class__.__name__, "message": str(exception)},
|
|
283
283
|
}
|
|
@@ -299,11 +299,11 @@ def create_error_response(
|
|
|
299
299
|
|
|
300
300
|
# Decorator for exception handling
|
|
301
301
|
def handle_exceptions(
|
|
302
|
-
default_return=None,
|
|
303
|
-
exception_types: tuple = (Exception,),
|
|
304
|
-
reraise_as: type | None = None,
|
|
302
|
+
default_return: Any = None,
|
|
303
|
+
exception_types: tuple[type[Exception], ...] = (Exception,),
|
|
304
|
+
reraise_as: type[Exception] | None = None,
|
|
305
305
|
log_errors: bool = True,
|
|
306
|
-
):
|
|
306
|
+
) -> Any:
|
|
307
307
|
"""
|
|
308
308
|
Decorator for automatic exception handling.
|
|
309
309
|
|
|
@@ -314,8 +314,8 @@ def handle_exceptions(
|
|
|
314
314
|
log_errors: Whether to log errors
|
|
315
315
|
"""
|
|
316
316
|
|
|
317
|
-
def decorator(func):
|
|
318
|
-
def wrapper(*args, **kwargs):
|
|
317
|
+
def decorator(func: Any) -> Any:
|
|
318
|
+
def wrapper(*args: Any, **kwargs: Any) -> Any:
|
|
319
319
|
try:
|
|
320
320
|
return func(*args, **kwargs)
|
|
321
321
|
except exception_types as e:
|
|
@@ -62,14 +62,10 @@ def read_file_with_fallback(file_path: str) -> bytes | None:
|
|
|
62
62
|
|
|
63
63
|
try:
|
|
64
64
|
content, detected_encoding = read_file_safe(file_path)
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
return content.encode("utf-8")
|
|
70
|
-
else:
|
|
71
|
-
log_warning(f"File {file_path} is empty or could not be read")
|
|
72
|
-
return b""
|
|
65
|
+
log_info(
|
|
66
|
+
f"Successfully read file {file_path} with encoding: {detected_encoding}"
|
|
67
|
+
)
|
|
68
|
+
return content.encode("utf-8")
|
|
73
69
|
|
|
74
70
|
except Exception as e:
|
|
75
71
|
log_error(f"Failed to read file {file_path}: {e}")
|
|
@@ -113,9 +109,6 @@ def read_file_partial(
|
|
|
113
109
|
try:
|
|
114
110
|
# ファイル全体を安全に読み込み
|
|
115
111
|
content, detected_encoding = read_file_safe(file_path)
|
|
116
|
-
if content is None:
|
|
117
|
-
log_error(f"Failed to read file: {file_path}")
|
|
118
|
-
return None
|
|
119
112
|
|
|
120
113
|
# 行に分割
|
|
121
114
|
lines = content.splitlines(keepends=True)
|
|
@@ -266,22 +266,26 @@ class JavaTableFormatter(BaseTableFormatter):
|
|
|
266
266
|
|
|
267
267
|
# Map<String,Object> -> M<S,O>
|
|
268
268
|
if "Map<" in type_name:
|
|
269
|
-
|
|
269
|
+
result = (
|
|
270
270
|
type_name.replace("Map<", "M<")
|
|
271
271
|
.replace("String", "S")
|
|
272
272
|
.replace("Object", "O")
|
|
273
273
|
)
|
|
274
|
+
return str(result)
|
|
274
275
|
|
|
275
276
|
# List<String> -> L<S>
|
|
276
277
|
if "List<" in type_name:
|
|
277
|
-
|
|
278
|
+
result = type_name.replace("List<", "L<").replace("String", "S")
|
|
279
|
+
return str(result)
|
|
278
280
|
|
|
279
281
|
# String[] -> S[]
|
|
280
282
|
if "[]" in type_name:
|
|
281
283
|
base_type = type_name.replace("[]", "")
|
|
282
284
|
if base_type:
|
|
283
|
-
|
|
285
|
+
result = type_mapping.get(base_type, base_type[0].upper()) + "[]"
|
|
286
|
+
return str(result)
|
|
284
287
|
else:
|
|
285
288
|
return "O[]"
|
|
286
289
|
|
|
287
|
-
|
|
290
|
+
result = type_mapping.get(type_name, type_name)
|
|
291
|
+
return str(result)
|
|
@@ -236,20 +236,24 @@ class PythonTableFormatter(BaseTableFormatter):
|
|
|
236
236
|
|
|
237
237
|
# List[str] -> L[s]
|
|
238
238
|
if "List[" in type_name:
|
|
239
|
-
|
|
239
|
+
result = (
|
|
240
240
|
type_name.replace("List[", "L[").replace("str", "s").replace("int", "i")
|
|
241
241
|
)
|
|
242
|
+
return str(result)
|
|
242
243
|
|
|
243
244
|
# Dict[str, int] -> D[s,i]
|
|
244
245
|
if "Dict[" in type_name:
|
|
245
|
-
|
|
246
|
+
result = (
|
|
246
247
|
type_name.replace("Dict[", "D[").replace("str", "s").replace("int", "i")
|
|
247
248
|
)
|
|
249
|
+
return str(result)
|
|
248
250
|
|
|
249
251
|
# Optional[str] -> O[s]
|
|
250
252
|
if "Optional[" in type_name:
|
|
251
|
-
|
|
253
|
+
result = type_name.replace("Optional[", "O[").replace("str", "s")
|
|
254
|
+
return str(result)
|
|
252
255
|
|
|
253
|
-
|
|
256
|
+
result = type_mapping.get(
|
|
254
257
|
type_name, type_name[:3] if len(type_name) > 3 else type_name
|
|
255
258
|
)
|
|
259
|
+
return str(result)
|
|
@@ -407,7 +407,7 @@ def format_analysis_output(result: dict[str, Any], output_format: str) -> None:
|
|
|
407
407
|
elements = result.get("elements", [])
|
|
408
408
|
if elements:
|
|
409
409
|
print(f"\nCode Elements: {len(elements)} found")
|
|
410
|
-
element_types = {}
|
|
410
|
+
element_types: dict[str, int] = {}
|
|
411
411
|
for element in elements:
|
|
412
412
|
elem_type = element.get("type", "unknown")
|
|
413
413
|
element_types[elem_type] = element_types.get(elem_type, 0) + 1
|
|
@@ -153,13 +153,34 @@ class CLIAdapter:
|
|
|
153
153
|
"file_path": result.file_path,
|
|
154
154
|
"language": result.language,
|
|
155
155
|
"package": result.package,
|
|
156
|
-
"imports": [
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
"
|
|
156
|
+
"imports": [
|
|
157
|
+
{"name": imp.name, "type": str(type(imp).__name__)}
|
|
158
|
+
for imp in result.imports
|
|
159
|
+
],
|
|
160
|
+
"classes": [
|
|
161
|
+
{"name": cls.name, "type": str(type(cls).__name__)}
|
|
162
|
+
for cls in result.classes
|
|
163
|
+
],
|
|
164
|
+
"methods": [
|
|
165
|
+
{"name": method.name, "type": str(type(method).__name__)}
|
|
166
|
+
for method in result.methods
|
|
167
|
+
],
|
|
168
|
+
"fields": [
|
|
169
|
+
{"name": field.name, "type": str(type(field).__name__)}
|
|
170
|
+
for field in result.fields
|
|
171
|
+
],
|
|
172
|
+
"annotations": [
|
|
173
|
+
{
|
|
174
|
+
"name": getattr(ann, "name", str(ann)),
|
|
175
|
+
"type": str(type(ann).__name__),
|
|
176
|
+
}
|
|
177
|
+
for ann in getattr(result, "annotations", [])
|
|
178
|
+
],
|
|
161
179
|
"analysis_time": result.analysis_time,
|
|
162
|
-
"elements": [
|
|
180
|
+
"elements": [
|
|
181
|
+
{"name": elem.name, "type": str(type(elem).__name__)}
|
|
182
|
+
for elem in result.elements
|
|
183
|
+
],
|
|
163
184
|
"success": result.success,
|
|
164
185
|
}
|
|
165
186
|
|
|
@@ -315,8 +336,8 @@ class CLIAdapter:
|
|
|
315
336
|
# Legacy AdvancedAnalyzerAdapter removed - use UnifiedAnalysisEngine directly
|
|
316
337
|
|
|
317
338
|
|
|
318
|
-
def get_analysis_engine():
|
|
339
|
+
def get_analysis_engine() -> "UnifiedAnalysisEngine":
|
|
319
340
|
"""Get analysis engine instance for testing compatibility."""
|
|
320
|
-
from ..core.analysis_engine import
|
|
341
|
+
from ..core.analysis_engine import UnifiedAnalysisEngine
|
|
321
342
|
|
|
322
|
-
return
|
|
343
|
+
return UnifiedAnalysisEngine()
|
|
@@ -5,19 +5,22 @@ MCP Adapter for Tree-Sitter Analyzer
|
|
|
5
5
|
This module provides an adapter interface for integrating with the MCP protocol.
|
|
6
6
|
"""
|
|
7
7
|
|
|
8
|
-
from typing import Any
|
|
8
|
+
from typing import TYPE_CHECKING, Any
|
|
9
9
|
|
|
10
10
|
from ..models import AnalysisResult
|
|
11
11
|
|
|
12
|
+
if TYPE_CHECKING:
|
|
13
|
+
from ..core.analysis_engine import UnifiedAnalysisEngine
|
|
12
14
|
|
|
13
|
-
|
|
15
|
+
|
|
16
|
+
def get_analysis_engine() -> "UnifiedAnalysisEngine":
|
|
14
17
|
"""Get analysis engine instance for testing compatibility."""
|
|
15
|
-
from ..core.analysis_engine import
|
|
18
|
+
from ..core.analysis_engine import UnifiedAnalysisEngine
|
|
16
19
|
|
|
17
|
-
return
|
|
20
|
+
return UnifiedAnalysisEngine()
|
|
18
21
|
|
|
19
22
|
|
|
20
|
-
def handle_mcp_resource_request(uri):
|
|
23
|
+
def handle_mcp_resource_request(uri: str) -> dict[str, Any]:
|
|
21
24
|
"""Handle MCP resource request for testing compatibility."""
|
|
22
25
|
return {
|
|
23
26
|
"contents": [
|
|
@@ -38,13 +41,15 @@ def read_file_safe(file_path: str) -> str:
|
|
|
38
41
|
class MCPAdapter:
|
|
39
42
|
"""MCP Adapter for testing compatibility."""
|
|
40
43
|
|
|
41
|
-
def __init__(self):
|
|
44
|
+
def __init__(self) -> None:
|
|
42
45
|
"""Initialize MCP Adapter."""
|
|
43
46
|
from ..core.analysis_engine import UnifiedAnalysisEngine
|
|
44
47
|
|
|
45
48
|
self.engine = UnifiedAnalysisEngine()
|
|
46
49
|
|
|
47
|
-
async def analyze_file_async(
|
|
50
|
+
async def analyze_file_async(
|
|
51
|
+
self, file_path: str, **kwargs: Any
|
|
52
|
+
) -> "AnalysisResult":
|
|
48
53
|
"""Analyze file asynchronously."""
|
|
49
54
|
from ..core.analysis_engine import AnalysisRequest
|
|
50
55
|
|
|
@@ -58,7 +63,7 @@ class MCPAdapter:
|
|
|
58
63
|
return await self.engine.analyze(request)
|
|
59
64
|
|
|
60
65
|
async def get_file_structure_async(
|
|
61
|
-
self, file_path: str, **kwargs
|
|
66
|
+
self, file_path: str, **kwargs: Any
|
|
62
67
|
) -> dict[str, Any]:
|
|
63
68
|
"""Get file structure asynchronously."""
|
|
64
69
|
result = await self.analyze_file_async(file_path, **kwargs)
|
|
@@ -66,11 +71,29 @@ class MCPAdapter:
|
|
|
66
71
|
"file_path": result.file_path,
|
|
67
72
|
"language": result.language,
|
|
68
73
|
"structure": {
|
|
69
|
-
"classes": [
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
"
|
|
74
|
+
"classes": [
|
|
75
|
+
{"name": cls.name, "type": str(type(cls).__name__)}
|
|
76
|
+
for cls in result.classes
|
|
77
|
+
],
|
|
78
|
+
"methods": [
|
|
79
|
+
{"name": method.name, "type": str(type(method).__name__)}
|
|
80
|
+
for method in result.methods
|
|
81
|
+
],
|
|
82
|
+
"fields": [
|
|
83
|
+
{"name": field.name, "type": str(type(field).__name__)}
|
|
84
|
+
for field in result.fields
|
|
85
|
+
],
|
|
86
|
+
"imports": [
|
|
87
|
+
{"name": imp.name, "type": str(type(imp).__name__)}
|
|
88
|
+
for imp in result.imports
|
|
89
|
+
],
|
|
90
|
+
"annotations": [
|
|
91
|
+
{
|
|
92
|
+
"name": getattr(ann, "name", str(ann)),
|
|
93
|
+
"type": str(type(ann).__name__),
|
|
94
|
+
}
|
|
95
|
+
for ann in getattr(result, "annotations", [])
|
|
96
|
+
],
|
|
74
97
|
},
|
|
75
98
|
"metadata": {
|
|
76
99
|
"analysis_time": result.analysis_time,
|
|
@@ -86,7 +109,7 @@ class MCPAdapter:
|
|
|
86
109
|
}
|
|
87
110
|
|
|
88
111
|
async def analyze_batch_async(
|
|
89
|
-
self, file_paths: list[str], **kwargs
|
|
112
|
+
self, file_paths: list[str], **kwargs: Any
|
|
90
113
|
) -> list["AnalysisResult"]:
|
|
91
114
|
"""Analyze multiple files asynchronously."""
|
|
92
115
|
results = []
|
|
@@ -157,17 +180,20 @@ class MCPAdapter:
|
|
|
157
180
|
"""Async cleanup."""
|
|
158
181
|
pass
|
|
159
182
|
|
|
160
|
-
def analyze_with_mcp_request(
|
|
183
|
+
async def analyze_with_mcp_request(
|
|
184
|
+
self, arguments: dict[str, Any]
|
|
185
|
+
) -> dict[str, Any]:
|
|
161
186
|
"""Analyze with MCP request."""
|
|
162
187
|
if "file_path" not in arguments:
|
|
163
188
|
raise KeyError("file_path is required in MCP request")
|
|
164
|
-
|
|
189
|
+
result = await self.analyze_file_async(arguments["file_path"])
|
|
190
|
+
return {"result": str(result), "success": True}
|
|
165
191
|
|
|
166
192
|
|
|
167
193
|
class MCPServerAdapter:
|
|
168
194
|
"""MCP Server Adapter for testing compatibility."""
|
|
169
195
|
|
|
170
|
-
def __init__(self):
|
|
196
|
+
def __init__(self) -> None:
|
|
171
197
|
"""Initialize MCP Server Adapter."""
|
|
172
198
|
self.mcp_adapter = MCPAdapter()
|
|
173
199
|
|
|
@@ -53,9 +53,9 @@ class TreeSitterAnalyzerMCPServer:
|
|
|
53
53
|
|
|
54
54
|
def create_server(self) -> Server:
|
|
55
55
|
"""Create and configure the MCP server."""
|
|
56
|
-
server = Server(self.name)
|
|
56
|
+
server: Any = Server(self.name)
|
|
57
57
|
|
|
58
|
-
@server.list_tools()
|
|
58
|
+
@server.list_tools() # type: ignore
|
|
59
59
|
async def handle_list_tools() -> list[Tool]:
|
|
60
60
|
"""List available tools."""
|
|
61
61
|
return [
|
|
@@ -224,7 +224,7 @@ class TreeSitterAnalyzerMCPServer:
|
|
|
224
224
|
),
|
|
225
225
|
]
|
|
226
226
|
|
|
227
|
-
@server.call_tool()
|
|
227
|
+
@server.call_tool() # type: ignore
|
|
228
228
|
async def handle_call_tool(
|
|
229
229
|
name: str, arguments: dict[str, Any]
|
|
230
230
|
) -> list[TextContent]:
|
|
@@ -309,25 +309,25 @@ class TreeSitterAnalyzerMCPServer:
|
|
|
309
309
|
)
|
|
310
310
|
]
|
|
311
311
|
|
|
312
|
-
@server.list_resources()
|
|
312
|
+
@server.list_resources() # type: ignore
|
|
313
313
|
async def handle_list_resources() -> list[Resource]:
|
|
314
314
|
"""List available resources."""
|
|
315
315
|
return [
|
|
316
316
|
Resource(
|
|
317
|
-
uri="code://file/{file_path}",
|
|
317
|
+
uri="code://file/{file_path}", # type: ignore
|
|
318
318
|
name="Code File Analysis",
|
|
319
319
|
description="Access to code file content and analysis",
|
|
320
320
|
mimeType="application/json",
|
|
321
321
|
),
|
|
322
322
|
Resource(
|
|
323
|
-
uri="code://stats/{stats_type}",
|
|
323
|
+
uri="code://stats/{stats_type}", # type: ignore
|
|
324
324
|
name="Project Statistics",
|
|
325
325
|
description="Access to project statistics and analysis data",
|
|
326
326
|
mimeType="application/json",
|
|
327
327
|
),
|
|
328
328
|
]
|
|
329
329
|
|
|
330
|
-
@server.read_resource()
|
|
330
|
+
@server.read_resource() # type: ignore
|
|
331
331
|
async def handle_read_resource(uri: str) -> str:
|
|
332
332
|
"""Read resource content."""
|
|
333
333
|
try:
|
|
@@ -366,7 +366,7 @@ class TreeSitterAnalyzerMCPServer:
|
|
|
366
366
|
|
|
367
367
|
self.server = server
|
|
368
368
|
log_info("MCP server created successfully")
|
|
369
|
-
return server
|
|
369
|
+
return server # type: ignore
|
|
370
370
|
|
|
371
371
|
async def run(self) -> None:
|
|
372
372
|
"""Run the MCP server."""
|
|
@@ -376,7 +376,7 @@ class TreeSitterAnalyzerMCPServer:
|
|
|
376
376
|
options = InitializationOptions(
|
|
377
377
|
server_name=self.name,
|
|
378
378
|
server_version=self.version,
|
|
379
|
-
capabilities={"tools": {}, "resources": {}},
|
|
379
|
+
capabilities={"tools": {}, "resources": {}}, # type: ignore
|
|
380
380
|
)
|
|
381
381
|
|
|
382
382
|
log_info(f"Starting MCP server: {self.name} v{self.version}")
|
|
@@ -93,10 +93,7 @@ class CodeAnalyzer:
|
|
|
93
93
|
self.source_code_bytes = source_bytes
|
|
94
94
|
self.tree = self.parser.parse(self.source_code_bytes)
|
|
95
95
|
|
|
96
|
-
|
|
97
|
-
output_error(f"ERROR: '{file_path}' のAST構築に失敗しました。")
|
|
98
|
-
return False
|
|
99
|
-
|
|
96
|
+
# Tree parsing should always succeed with valid input
|
|
100
97
|
log_info(f"INFO: '{file_path}' の解析が完了し、ASTを構築しました。")
|
|
101
98
|
return True
|
|
102
99
|
|
|
@@ -141,54 +138,26 @@ class CodeAnalyzer:
|
|
|
141
138
|
|
|
142
139
|
# Tree-sitter 0.24以降の辞書形式に対応
|
|
143
140
|
try:
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
for node in nodes:
|
|
149
|
-
try:
|
|
150
|
-
start_line = node.start_point[0] + 1
|
|
151
|
-
end_line = node.end_point[0] + 1
|
|
152
|
-
node_text = self.source_code_bytes[
|
|
153
|
-
node.start_byte : node.end_byte
|
|
154
|
-
].decode("utf-8", errors="ignore")
|
|
155
|
-
|
|
156
|
-
results.append(
|
|
157
|
-
{
|
|
158
|
-
"capture_name": capture_name,
|
|
159
|
-
"content": node_text,
|
|
160
|
-
"start_line": start_line,
|
|
161
|
-
"end_line": end_line,
|
|
162
|
-
"node_type": node.type,
|
|
163
|
-
}
|
|
164
|
-
)
|
|
165
|
-
except Exception as e:
|
|
166
|
-
output_warning(
|
|
167
|
-
f"WARNING: ノード処理中にエラーが発生しました: {e}"
|
|
168
|
-
)
|
|
169
|
-
continue
|
|
170
|
-
else:
|
|
171
|
-
# 古い形式への対応(フォールバック)
|
|
172
|
-
if hasattr(captures, "__iter__"):
|
|
173
|
-
for capture in captures:
|
|
141
|
+
# 辞書形式: {capture_name: [nodes...]}
|
|
142
|
+
for capture_name, nodes in captures.items():
|
|
143
|
+
if isinstance(nodes, list):
|
|
144
|
+
for node in nodes:
|
|
174
145
|
try:
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
}
|
|
191
|
-
)
|
|
146
|
+
start_line = node.start_point[0] + 1
|
|
147
|
+
end_line = node.end_point[0] + 1
|
|
148
|
+
node_text = self.source_code_bytes[
|
|
149
|
+
node.start_byte : node.end_byte
|
|
150
|
+
].decode("utf-8", errors="ignore")
|
|
151
|
+
|
|
152
|
+
results.append(
|
|
153
|
+
{
|
|
154
|
+
"capture_name": capture_name,
|
|
155
|
+
"content": node_text,
|
|
156
|
+
"start_line": start_line,
|
|
157
|
+
"end_line": end_line,
|
|
158
|
+
"node_type": node.type,
|
|
159
|
+
}
|
|
160
|
+
)
|
|
192
161
|
except Exception as e:
|
|
193
162
|
output_warning(
|
|
194
163
|
f"WARNING: ノード処理中にエラーが発生しました: {e}"
|
|
@@ -189,8 +189,8 @@ class LanguageLoader:
|
|
|
189
189
|
self._availability_cache.clear()
|
|
190
190
|
self._parser_cache.clear()
|
|
191
191
|
self._unavailable_languages.clear()
|
|
192
|
-
#
|
|
193
|
-
self.
|
|
192
|
+
# 可用性キャッシュもクリア
|
|
193
|
+
self._availability_cache.clear()
|
|
194
194
|
|
|
195
195
|
|
|
196
196
|
# グローバルインスタンス(最適化:シングルトンパターン)
|