tree-sitter-analyzer 0.1.3__py3-none-any.whl → 0.3.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.

Files changed (79) hide show
  1. tree_sitter_analyzer/__init__.py +133 -121
  2. tree_sitter_analyzer/__main__.py +11 -12
  3. tree_sitter_analyzer/api.py +531 -539
  4. tree_sitter_analyzer/cli/__init__.py +39 -39
  5. tree_sitter_analyzer/cli/__main__.py +12 -13
  6. tree_sitter_analyzer/cli/commands/__init__.py +26 -27
  7. tree_sitter_analyzer/cli/commands/advanced_command.py +88 -88
  8. tree_sitter_analyzer/cli/commands/base_command.py +160 -155
  9. tree_sitter_analyzer/cli/commands/default_command.py +18 -19
  10. tree_sitter_analyzer/cli/commands/partial_read_command.py +141 -133
  11. tree_sitter_analyzer/cli/commands/query_command.py +81 -82
  12. tree_sitter_analyzer/cli/commands/structure_command.py +138 -121
  13. tree_sitter_analyzer/cli/commands/summary_command.py +101 -93
  14. tree_sitter_analyzer/cli/commands/table_command.py +232 -233
  15. tree_sitter_analyzer/cli/info_commands.py +120 -121
  16. tree_sitter_analyzer/cli_main.py +277 -276
  17. tree_sitter_analyzer/core/__init__.py +15 -20
  18. tree_sitter_analyzer/core/analysis_engine.py +591 -574
  19. tree_sitter_analyzer/core/cache_service.py +320 -330
  20. tree_sitter_analyzer/core/engine.py +557 -560
  21. tree_sitter_analyzer/core/parser.py +293 -288
  22. tree_sitter_analyzer/core/query.py +494 -502
  23. tree_sitter_analyzer/encoding_utils.py +458 -460
  24. tree_sitter_analyzer/exceptions.py +337 -340
  25. tree_sitter_analyzer/file_handler.py +217 -222
  26. tree_sitter_analyzer/formatters/__init__.py +1 -1
  27. tree_sitter_analyzer/formatters/base_formatter.py +167 -168
  28. tree_sitter_analyzer/formatters/formatter_factory.py +78 -74
  29. tree_sitter_analyzer/formatters/java_formatter.py +287 -270
  30. tree_sitter_analyzer/formatters/python_formatter.py +255 -235
  31. tree_sitter_analyzer/interfaces/__init__.py +9 -10
  32. tree_sitter_analyzer/interfaces/cli.py +528 -557
  33. tree_sitter_analyzer/interfaces/cli_adapter.py +322 -319
  34. tree_sitter_analyzer/interfaces/mcp_adapter.py +180 -170
  35. tree_sitter_analyzer/interfaces/mcp_server.py +405 -416
  36. tree_sitter_analyzer/java_analyzer.py +218 -219
  37. tree_sitter_analyzer/language_detector.py +398 -400
  38. tree_sitter_analyzer/language_loader.py +224 -228
  39. tree_sitter_analyzer/languages/__init__.py +10 -11
  40. tree_sitter_analyzer/languages/java_plugin.py +1129 -1113
  41. tree_sitter_analyzer/languages/python_plugin.py +737 -712
  42. tree_sitter_analyzer/mcp/__init__.py +31 -32
  43. tree_sitter_analyzer/mcp/resources/__init__.py +44 -47
  44. tree_sitter_analyzer/mcp/resources/code_file_resource.py +212 -213
  45. tree_sitter_analyzer/mcp/resources/project_stats_resource.py +560 -550
  46. tree_sitter_analyzer/mcp/server.py +333 -345
  47. tree_sitter_analyzer/mcp/tools/__init__.py +30 -31
  48. tree_sitter_analyzer/mcp/tools/analyze_scale_tool.py +621 -557
  49. tree_sitter_analyzer/mcp/tools/analyze_scale_tool_cli_compatible.py +242 -245
  50. tree_sitter_analyzer/mcp/tools/base_tool.py +54 -55
  51. tree_sitter_analyzer/mcp/tools/read_partial_tool.py +300 -302
  52. tree_sitter_analyzer/mcp/tools/table_format_tool.py +362 -359
  53. tree_sitter_analyzer/mcp/tools/universal_analyze_tool.py +543 -476
  54. tree_sitter_analyzer/mcp/utils/__init__.py +105 -106
  55. tree_sitter_analyzer/mcp/utils/error_handler.py +549 -549
  56. tree_sitter_analyzer/models.py +470 -481
  57. tree_sitter_analyzer/output_manager.py +261 -264
  58. tree_sitter_analyzer/plugins/__init__.py +333 -334
  59. tree_sitter_analyzer/plugins/base.py +477 -446
  60. tree_sitter_analyzer/plugins/java_plugin.py +608 -625
  61. tree_sitter_analyzer/plugins/javascript_plugin.py +446 -439
  62. tree_sitter_analyzer/plugins/manager.py +362 -355
  63. tree_sitter_analyzer/plugins/plugin_loader.py +85 -83
  64. tree_sitter_analyzer/plugins/python_plugin.py +606 -598
  65. tree_sitter_analyzer/plugins/registry.py +374 -366
  66. tree_sitter_analyzer/queries/__init__.py +26 -27
  67. tree_sitter_analyzer/queries/java.py +391 -394
  68. tree_sitter_analyzer/queries/javascript.py +148 -149
  69. tree_sitter_analyzer/queries/python.py +285 -286
  70. tree_sitter_analyzer/queries/typescript.py +229 -230
  71. tree_sitter_analyzer/query_loader.py +254 -260
  72. tree_sitter_analyzer/table_formatter.py +468 -448
  73. tree_sitter_analyzer/utils.py +277 -277
  74. tree_sitter_analyzer-0.3.0.dist-info/METADATA +346 -0
  75. tree_sitter_analyzer-0.3.0.dist-info/RECORD +77 -0
  76. tree_sitter_analyzer-0.1.3.dist-info/METADATA +0 -444
  77. tree_sitter_analyzer-0.1.3.dist-info/RECORD +0 -77
  78. {tree_sitter_analyzer-0.1.3.dist-info → tree_sitter_analyzer-0.3.0.dist-info}/WHEEL +0 -0
  79. {tree_sitter_analyzer-0.1.3.dist-info → tree_sitter_analyzer-0.3.0.dist-info}/entry_points.txt +0 -0
@@ -1,359 +1,362 @@
1
- #!/usr/bin/env python3
2
- # -*- coding: utf-8 -*-
3
- """
4
- Table Format MCP Tool
5
-
6
- This tool provides table-formatted output for code analysis results through the MCP protocol,
7
- equivalent to the CLI --table=full option functionality.
8
- """
9
-
10
- import logging
11
- import os
12
- from pathlib import Path
13
- from typing import Any, Dict, Optional
14
-
15
- from ...core.analysis_engine import get_analysis_engine, AnalysisRequest
16
- from ...language_detector import detect_language_from_file
17
- from ...table_formatter import TableFormatter
18
- from ...utils import setup_logger
19
- from ..utils import get_performance_monitor
20
-
21
- # Set up logging
22
- logger = setup_logger(__name__)
23
-
24
-
25
- class TableFormatTool:
26
- """
27
- MCP Tool for formatting code analysis results as tables.
28
-
29
- This tool integrates with existing table_formatter and analyzer components
30
- to provide table-formatted output through the MCP protocol, equivalent to
31
- the CLI --table=full option.
32
- """
33
-
34
- def __init__(self) -> None:
35
- """Initialize the table format tool."""
36
- self.logger = logger
37
- self.analysis_engine = get_analysis_engine()
38
- logger.info("TableFormatTool initialized")
39
-
40
- def get_tool_schema(self) -> Dict[str, Any]:
41
- """
42
- Get the MCP tool schema for format_table.
43
-
44
- Returns:
45
- Dictionary containing the tool schema
46
- """
47
- return {
48
- "type": "object",
49
- "properties": {
50
- "file_path": {
51
- "type": "string",
52
- "description": "Path to the code file to analyze and format",
53
- },
54
- "format_type": {
55
- "type": "string",
56
- "description": "Table format type",
57
- "enum": ["full", "compact", "csv"],
58
- "default": "full",
59
- },
60
- "language": {
61
- "type": "string",
62
- "description": "Programming language (optional, auto-detected if not specified)",
63
- },
64
- },
65
- "required": ["file_path"],
66
- "additionalProperties": False,
67
- }
68
-
69
- def validate_arguments(self, arguments: Dict[str, Any]) -> bool:
70
- """
71
- Validate tool arguments.
72
-
73
- Args:
74
- arguments: Dictionary of arguments to validate
75
-
76
- Returns:
77
- True if arguments are valid
78
-
79
- Raises:
80
- ValueError: If arguments are invalid
81
- """
82
- # Check required fields
83
- if "file_path" not in arguments:
84
- raise ValueError("Required field 'file_path' is missing")
85
-
86
- # Validate file_path
87
- file_path = arguments["file_path"]
88
- if not isinstance(file_path, str):
89
- raise ValueError("file_path must be a string")
90
- if not file_path.strip():
91
- raise ValueError("file_path cannot be empty")
92
-
93
- # Validate format_type if provided
94
- if "format_type" in arguments:
95
- format_type = arguments["format_type"]
96
- if not isinstance(format_type, str):
97
- raise ValueError("format_type must be a string")
98
- if format_type not in ["full", "compact", "csv"]:
99
- raise ValueError("format_type must be one of: full, compact, csv")
100
-
101
- # Validate language if provided
102
- if "language" in arguments:
103
- language = arguments["language"]
104
- if not isinstance(language, str):
105
- raise ValueError("language must be a string")
106
-
107
- return True
108
-
109
- def _convert_parameters(self, parameters):
110
- """Convert parameters to expected format"""
111
- result = []
112
- for param in parameters:
113
- if isinstance(param, dict):
114
- result.append({
115
- "name": param.get('name', 'param'),
116
- "type": param.get('type', 'Object')
117
- })
118
- else:
119
- result.append({
120
- "name": getattr(param, 'name', 'param'),
121
- "type": getattr(param, 'param_type', 'Object')
122
- })
123
- return result
124
-
125
- def _get_method_modifiers(self, method) -> list:
126
- """Extract method modifiers as a list"""
127
- modifiers = []
128
- if getattr(method, 'is_static', False):
129
- modifiers.append('static')
130
- if getattr(method, 'is_final', False):
131
- modifiers.append('final')
132
- if getattr(method, 'is_abstract', False):
133
- modifiers.append('abstract')
134
- return modifiers
135
-
136
- def _get_method_parameters(self, method):
137
- """Get method parameters in the correct format for TableFormatter"""
138
- parameters = getattr(method, 'parameters', [])
139
-
140
- # If parameters is already a list of strings (like "int value"), convert to dict format
141
- if parameters and isinstance(parameters[0], str):
142
- result = []
143
- for param_str in parameters:
144
- parts = param_str.strip().split()
145
- if len(parts) >= 2:
146
- param_type = ' '.join(parts[:-1]) # Everything except last part is type
147
- param_name = parts[-1] # Last part is name
148
- result.append({
149
- "name": param_name,
150
- "type": param_type
151
- })
152
- elif len(parts) == 1:
153
- # Only type, no name
154
- result.append({
155
- "name": "param",
156
- "type": parts[0]
157
- })
158
- return result
159
-
160
- # Fallback to original conversion method
161
- return self._convert_parameters(parameters)
162
-
163
- def _get_field_modifiers(self, field) -> list:
164
- """Extract field modifiers as a list"""
165
- modifiers = []
166
-
167
- # Add visibility to modifiers for CLI compatibility
168
- visibility = getattr(field, 'visibility', 'private')
169
- if visibility and visibility != 'package':
170
- modifiers.append(visibility)
171
-
172
- if getattr(field, 'is_static', False):
173
- modifiers.append('static')
174
- if getattr(field, 'is_final', False):
175
- modifiers.append('final')
176
- return modifiers
177
-
178
- def _convert_analysis_result_to_dict(self, result) -> Dict[str, Any]:
179
- """Convert AnalysisResult to dictionary format expected by TableFormatter"""
180
- # Extract elements by type
181
- classes = [e for e in result.elements if e.__class__.__name__ == 'Class']
182
- methods = [e for e in result.elements if e.__class__.__name__ == 'Function']
183
- fields = [e for e in result.elements if e.__class__.__name__ == 'Variable']
184
- imports = [e for e in result.elements if e.__class__.__name__ == 'Import']
185
- packages = [e for e in result.elements if e.__class__.__name__ == 'Package']
186
-
187
- # Convert package to expected format
188
- package_info = None
189
- if packages:
190
- package_info = {"name": packages[0].name}
191
-
192
- return {
193
- "file_path": result.file_path,
194
- "language": result.language,
195
- "package": package_info,
196
- "classes": [
197
- {
198
- "name": getattr(cls, 'name', 'unknown'),
199
- "line_range": {
200
- "start": getattr(cls, 'start_line', 0),
201
- "end": getattr(cls, 'end_line', 0)
202
- },
203
- "type": getattr(cls, 'class_type', 'class'),
204
- "visibility": "public", # Force all classes to public for CLI compatibility
205
- "extends": getattr(cls, 'extends_class', None),
206
- "implements": getattr(cls, 'implements_interfaces', []),
207
- "annotations": []
208
- } for cls in classes
209
- ],
210
- "methods": [
211
- {
212
- "name": getattr(method, 'name', 'unknown'),
213
- "line_range": {
214
- "start": getattr(method, 'start_line', 0),
215
- "end": getattr(method, 'end_line', 0)
216
- },
217
- "return_type": getattr(method, 'return_type', 'void'),
218
- "parameters": self._get_method_parameters(method),
219
- "visibility": getattr(method, 'visibility', 'public'),
220
- "is_static": getattr(method, 'is_static', False),
221
- "is_constructor": getattr(method, 'is_constructor', False),
222
- "complexity_score": getattr(method, 'complexity_score', 0),
223
- "modifiers": self._get_method_modifiers(method),
224
- "annotations": []
225
- } for method in methods
226
- ],
227
- "fields": [
228
- {
229
- "name": getattr(field, 'name', 'unknown'),
230
- "type": getattr(field, 'field_type', 'Object'),
231
- "line_range": {
232
- "start": getattr(field, 'start_line', 0),
233
- "end": getattr(field, 'end_line', 0)
234
- },
235
- "visibility": getattr(field, 'visibility', 'private'),
236
- "modifiers": self._get_field_modifiers(field),
237
- "annotations": []
238
- } for field in fields
239
- ],
240
- "imports": [
241
- {
242
- "name": getattr(imp, 'name', 'unknown'),
243
- "statement": getattr(imp, 'name', ''), # Use name for CLI compatibility
244
- "is_static": getattr(imp, 'is_static', False),
245
- "is_wildcard": getattr(imp, 'is_wildcard', False)
246
- } for imp in imports
247
- ],
248
- "statistics": {
249
- "class_count": len(classes),
250
- "method_count": len(methods),
251
- "field_count": len(fields),
252
- "import_count": len(imports),
253
- "total_lines": result.line_count
254
- }
255
- }
256
-
257
- async def execute(self, args: Dict[str, Any]) -> Dict[str, Any]:
258
- """Execute table formatting tool."""
259
- try:
260
- # Validate arguments first
261
- if "file_path" not in args:
262
- raise ValueError("file_path is required")
263
-
264
- file_path = args["file_path"]
265
- format_type = args.get("format_type", "full")
266
- language = args.get("language")
267
-
268
- # Validate file exists
269
- if not Path(file_path).exists():
270
- raise FileNotFoundError(f"File not found: {file_path}")
271
-
272
- # Detect language if not provided
273
- if not language:
274
- language = detect_language_from_file(file_path)
275
-
276
- # Use performance monitoring
277
- monitor = get_performance_monitor()
278
- with monitor.measure_operation("table_format_analysis"):
279
- # Analyze structure using the unified analysis engine
280
- request = AnalysisRequest(
281
- file_path=file_path,
282
- language=language,
283
- include_complexity=True,
284
- include_details=True
285
- )
286
- structure_result = await self.analysis_engine.analyze(request)
287
-
288
- if structure_result is None:
289
- raise RuntimeError(
290
- f"Failed to analyze structure for file: {file_path}"
291
- )
292
-
293
- # Create table formatter
294
- formatter = TableFormatter(format_type)
295
-
296
- # Convert AnalysisResult to dict format for TableFormatter
297
- structure_dict = self._convert_analysis_result_to_dict(structure_result)
298
-
299
- # Format table
300
- table_output = formatter.format_structure(structure_dict)
301
-
302
- # Ensure output format matches CLI exactly
303
- # Fix line ending differences: normalize to Unix-style LF (\n)
304
- table_output = table_output.replace("\r\n", "\n").replace("\r", "\n")
305
-
306
- # CLI uses sys.stdout.buffer.write() which doesn't add trailing newline
307
- # Ensure MCP output matches this behavior exactly
308
- # Remove any trailing whitespace and newlines to match CLI output
309
- table_output = table_output.rstrip()
310
-
311
- # Extract metadata from structure dict
312
- metadata = {}
313
- if "statistics" in structure_dict:
314
- stats = structure_dict["statistics"]
315
- metadata = {
316
- "classes_count": stats.get("class_count", 0),
317
- "methods_count": stats.get("method_count", 0),
318
- "fields_count": stats.get("field_count", 0),
319
- "total_lines": stats.get("total_lines", 0),
320
- }
321
-
322
- return {
323
- "table_output": table_output,
324
- "format_type": format_type,
325
- "file_path": file_path,
326
- "language": language,
327
- "metadata": metadata,
328
- }
329
-
330
- except Exception as e:
331
- self.logger.error(f"Error in table format tool: {e}")
332
- raise
333
-
334
- def get_tool_definition(self) -> Any:
335
- """
336
- Get the MCP tool definition for format_table.
337
-
338
- Returns:
339
- Tool definition object compatible with MCP server
340
- """
341
- try:
342
- from mcp.types import Tool
343
-
344
- return Tool(
345
- name="format_table",
346
- description="Format code analysis results as tables (equivalent to CLI --table=full option)",
347
- inputSchema=self.get_tool_schema(),
348
- )
349
- except ImportError:
350
- # Fallback for when MCP is not available
351
- return {
352
- "name": "format_table",
353
- "description": "Format code analysis results as tables (equivalent to CLI --table=full option)",
354
- "inputSchema": self.get_tool_schema(),
355
- }
356
-
357
-
358
- # Tool instance for easy access
359
- table_format_tool = TableFormatTool()
1
+ #!/usr/bin/env python3
2
+ """
3
+ Table Format MCP Tool
4
+
5
+ This tool provides table-formatted output for code analysis results through the MCP protocol,
6
+ equivalent to the CLI --table=full option functionality.
7
+ """
8
+
9
+ from pathlib import Path
10
+ from typing import Any
11
+
12
+ from ...core.analysis_engine import AnalysisRequest, get_analysis_engine
13
+ from ...language_detector import detect_language_from_file
14
+ from ...table_formatter import TableFormatter
15
+ from ...utils import setup_logger
16
+ from ..utils import get_performance_monitor
17
+
18
+ # Set up logging
19
+ logger = setup_logger(__name__)
20
+
21
+
22
+ class TableFormatTool:
23
+ """
24
+ MCP Tool for formatting code analysis results as tables.
25
+
26
+ This tool integrates with existing table_formatter and analyzer components
27
+ to provide table-formatted output through the MCP protocol, equivalent to
28
+ the CLI --table=full option.
29
+ """
30
+
31
+ def __init__(self) -> None:
32
+ """Initialize the table format tool."""
33
+ self.logger = logger
34
+ self.analysis_engine = get_analysis_engine()
35
+ logger.info("TableFormatTool initialized")
36
+
37
+ def get_tool_schema(self) -> dict[str, Any]:
38
+ """
39
+ Get the MCP tool schema for analyze_code_structure.
40
+
41
+ Returns:
42
+ Dictionary containing the tool schema
43
+ """
44
+ return {
45
+ "type": "object",
46
+ "properties": {
47
+ "file_path": {
48
+ "type": "string",
49
+ "description": "Path to the code file to analyze and format",
50
+ },
51
+ "format_type": {
52
+ "type": "string",
53
+ "description": "Table format type",
54
+ "enum": ["full", "compact", "csv"],
55
+ "default": "full",
56
+ },
57
+ "language": {
58
+ "type": "string",
59
+ "description": "Programming language (optional, auto-detected if not specified)",
60
+ },
61
+ },
62
+ "required": ["file_path"],
63
+ "additionalProperties": False,
64
+ }
65
+
66
+ def validate_arguments(self, arguments: dict[str, Any]) -> bool:
67
+ """
68
+ Validate tool arguments.
69
+
70
+ Args:
71
+ arguments: Dictionary of arguments to validate
72
+
73
+ Returns:
74
+ True if arguments are valid
75
+
76
+ Raises:
77
+ ValueError: If arguments are invalid
78
+ """
79
+ # Check required fields
80
+ if "file_path" not in arguments:
81
+ raise ValueError("Required field 'file_path' is missing")
82
+
83
+ # Validate file_path
84
+ file_path = arguments["file_path"]
85
+ if not isinstance(file_path, str):
86
+ raise ValueError("file_path must be a string")
87
+ if not file_path.strip():
88
+ raise ValueError("file_path cannot be empty")
89
+
90
+ # Validate format_type if provided
91
+ if "format_type" in arguments:
92
+ format_type = arguments["format_type"]
93
+ if not isinstance(format_type, str):
94
+ raise ValueError("format_type must be a string")
95
+ if format_type not in ["full", "compact", "csv"]:
96
+ raise ValueError("format_type must be one of: full, compact, csv")
97
+
98
+ # Validate language if provided
99
+ if "language" in arguments:
100
+ language = arguments["language"]
101
+ if not isinstance(language, str):
102
+ raise ValueError("language must be a string")
103
+
104
+ return True
105
+
106
+ def _convert_parameters(self, parameters: Any) -> list[dict[str, str]]:
107
+ """Convert parameters to expected format"""
108
+ result = []
109
+ for param in parameters:
110
+ if isinstance(param, dict):
111
+ result.append(
112
+ {
113
+ "name": param.get("name", "param"),
114
+ "type": param.get("type", "Object"),
115
+ }
116
+ )
117
+ else:
118
+ result.append(
119
+ {
120
+ "name": getattr(param, "name", "param"),
121
+ "type": getattr(param, "param_type", "Object"),
122
+ }
123
+ )
124
+ return result
125
+
126
+ def _get_method_modifiers(self, method: Any) -> list[str]:
127
+ """Extract method modifiers as a list"""
128
+ modifiers = []
129
+ if getattr(method, "is_static", False):
130
+ modifiers.append("static")
131
+ if getattr(method, "is_final", False):
132
+ modifiers.append("final")
133
+ if getattr(method, "is_abstract", False):
134
+ modifiers.append("abstract")
135
+ return modifiers
136
+
137
+ def _get_method_parameters(self, method: Any) -> list[dict[str, str]]:
138
+ """Get method parameters in the correct format for TableFormatter"""
139
+ parameters = getattr(method, "parameters", [])
140
+
141
+ # If parameters is already a list of strings (like "int value"), convert to dict format
142
+ if parameters and isinstance(parameters[0], str):
143
+ result = []
144
+ for param_str in parameters:
145
+ parts = param_str.strip().split()
146
+ if len(parts) >= 2:
147
+ param_type = " ".join(
148
+ parts[:-1]
149
+ ) # Everything except last part is type
150
+ param_name = parts[-1] # Last part is name
151
+ result.append({"name": param_name, "type": param_type})
152
+ elif len(parts) == 1:
153
+ # Only type, no name
154
+ result.append({"name": "param", "type": parts[0]})
155
+ return result
156
+
157
+ # Fallback to original conversion method
158
+ return self._convert_parameters(parameters)
159
+
160
+ def _get_field_modifiers(self, field) -> list:
161
+ """Extract field modifiers as a list"""
162
+ modifiers = []
163
+
164
+ # Add visibility to modifiers for CLI compatibility
165
+ visibility = getattr(field, "visibility", "private")
166
+ if visibility and visibility != "package":
167
+ modifiers.append(visibility)
168
+
169
+ if getattr(field, "is_static", False):
170
+ modifiers.append("static")
171
+ if getattr(field, "is_final", False):
172
+ modifiers.append("final")
173
+ return modifiers
174
+
175
+ def _convert_analysis_result_to_dict(self, result) -> dict[str, Any]:
176
+ """Convert AnalysisResult to dictionary format expected by TableFormatter"""
177
+ # Extract elements by type
178
+ classes = [e for e in result.elements if e.__class__.__name__ == "Class"]
179
+ methods = [e for e in result.elements if e.__class__.__name__ == "Function"]
180
+ fields = [e for e in result.elements if e.__class__.__name__ == "Variable"]
181
+ imports = [e for e in result.elements if e.__class__.__name__ == "Import"]
182
+ packages = [e for e in result.elements if e.__class__.__name__ == "Package"]
183
+
184
+ # Convert package to expected format
185
+ package_info = None
186
+ if packages:
187
+ package_info = {"name": packages[0].name}
188
+
189
+ return {
190
+ "file_path": result.file_path,
191
+ "language": result.language,
192
+ "package": package_info,
193
+ "classes": [
194
+ {
195
+ "name": getattr(cls, "name", "unknown"),
196
+ "line_range": {
197
+ "start": getattr(cls, "start_line", 0),
198
+ "end": getattr(cls, "end_line", 0),
199
+ },
200
+ "type": getattr(cls, "class_type", "class"),
201
+ "visibility": "public", # Force all classes to public for CLI compatibility
202
+ "extends": getattr(cls, "extends_class", None),
203
+ "implements": getattr(cls, "implements_interfaces", []),
204
+ "annotations": [],
205
+ }
206
+ for cls in classes
207
+ ],
208
+ "methods": [
209
+ {
210
+ "name": getattr(method, "name", "unknown"),
211
+ "line_range": {
212
+ "start": getattr(method, "start_line", 0),
213
+ "end": getattr(method, "end_line", 0),
214
+ },
215
+ "return_type": getattr(method, "return_type", "void"),
216
+ "parameters": self._get_method_parameters(method),
217
+ "visibility": getattr(method, "visibility", "public"),
218
+ "is_static": getattr(method, "is_static", False),
219
+ "is_constructor": getattr(method, "is_constructor", False),
220
+ "complexity_score": getattr(method, "complexity_score", 0),
221
+ "modifiers": self._get_method_modifiers(method),
222
+ "annotations": [],
223
+ }
224
+ for method in methods
225
+ ],
226
+ "fields": [
227
+ {
228
+ "name": getattr(field, "name", "unknown"),
229
+ "type": getattr(field, "field_type", "Object"),
230
+ "line_range": {
231
+ "start": getattr(field, "start_line", 0),
232
+ "end": getattr(field, "end_line", 0),
233
+ },
234
+ "visibility": getattr(field, "visibility", "private"),
235
+ "modifiers": self._get_field_modifiers(field),
236
+ "annotations": [],
237
+ }
238
+ for field in fields
239
+ ],
240
+ "imports": [
241
+ {
242
+ "name": getattr(imp, "name", "unknown"),
243
+ "statement": getattr(
244
+ imp, "name", ""
245
+ ), # Use name for CLI compatibility
246
+ "is_static": getattr(imp, "is_static", False),
247
+ "is_wildcard": getattr(imp, "is_wildcard", False),
248
+ }
249
+ for imp in imports
250
+ ],
251
+ "statistics": {
252
+ "class_count": len(classes),
253
+ "method_count": len(methods),
254
+ "field_count": len(fields),
255
+ "import_count": len(imports),
256
+ "total_lines": result.line_count,
257
+ },
258
+ }
259
+
260
+ async def execute(self, args: dict[str, Any]) -> dict[str, Any]:
261
+ """Execute code structure analysis tool."""
262
+ try:
263
+ # Validate arguments first
264
+ if "file_path" not in args:
265
+ raise ValueError("file_path is required")
266
+
267
+ file_path = args["file_path"]
268
+ format_type = args.get("format_type", "full")
269
+ language = args.get("language")
270
+
271
+ # Validate file exists
272
+ if not Path(file_path).exists():
273
+ raise FileNotFoundError(f"File not found: {file_path}")
274
+
275
+ # Detect language if not provided
276
+ if not language:
277
+ language = detect_language_from_file(file_path)
278
+
279
+ # Use performance monitoring
280
+ monitor = get_performance_monitor()
281
+ with monitor.measure_operation("code_structure_analysis"):
282
+ # Analyze structure using the unified analysis engine
283
+ request = AnalysisRequest(
284
+ file_path=file_path,
285
+ language=language,
286
+ include_complexity=True,
287
+ include_details=True,
288
+ )
289
+ structure_result = await self.analysis_engine.analyze(request)
290
+
291
+ if structure_result is None:
292
+ raise RuntimeError(
293
+ f"Failed to analyze structure for file: {file_path}"
294
+ )
295
+
296
+ # Create table formatter
297
+ formatter = TableFormatter(format_type)
298
+
299
+ # Convert AnalysisResult to dict format for TableFormatter
300
+ structure_dict = self._convert_analysis_result_to_dict(structure_result)
301
+
302
+ # Format table
303
+ table_output = formatter.format_structure(structure_dict)
304
+
305
+ # Ensure output format matches CLI exactly
306
+ # Fix line ending differences: normalize to Unix-style LF (\n)
307
+ table_output = table_output.replace("\r\n", "\n").replace("\r", "\n")
308
+
309
+ # CLI uses sys.stdout.buffer.write() which doesn't add trailing newline
310
+ # Ensure MCP output matches this behavior exactly
311
+ # Remove any trailing whitespace and newlines to match CLI output
312
+ table_output = table_output.rstrip()
313
+
314
+ # Extract metadata from structure dict
315
+ metadata = {}
316
+ if "statistics" in structure_dict:
317
+ stats = structure_dict["statistics"]
318
+ metadata = {
319
+ "classes_count": stats.get("class_count", 0),
320
+ "methods_count": stats.get("method_count", 0),
321
+ "fields_count": stats.get("field_count", 0),
322
+ "total_lines": stats.get("total_lines", 0),
323
+ }
324
+
325
+ return {
326
+ "table_output": table_output,
327
+ "format_type": format_type,
328
+ "file_path": file_path,
329
+ "language": language,
330
+ "metadata": metadata,
331
+ }
332
+
333
+ except Exception as e:
334
+ self.logger.error(f"Error in code structure analysis tool: {e}")
335
+ raise
336
+
337
+ def get_tool_definition(self) -> Any:
338
+ """
339
+ Get the MCP tool definition for analyze_code_structure.
340
+
341
+ Returns:
342
+ Tool definition object compatible with MCP server
343
+ """
344
+ try:
345
+ from mcp.types import Tool
346
+
347
+ return Tool(
348
+ name="analyze_code_structure",
349
+ description="Analyze code structure and generate detailed overview tables (classes, methods, fields) for large files",
350
+ inputSchema=self.get_tool_schema(),
351
+ )
352
+ except ImportError:
353
+ # Fallback for when MCP is not available
354
+ return {
355
+ "name": "analyze_code_structure",
356
+ "description": "Analyze code structure and generate detailed overview tables (classes, methods, fields) for large files",
357
+ "inputSchema": self.get_tool_schema(),
358
+ }
359
+
360
+
361
+ # Tool instance for easy access
362
+ table_format_tool = TableFormatTool()