tree-sitter-analyzer 0.2.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.

Files changed (78) hide show
  1. tree_sitter_analyzer/__init__.py +134 -121
  2. tree_sitter_analyzer/__main__.py +11 -12
  3. tree_sitter_analyzer/api.py +533 -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 +235 -233
  15. tree_sitter_analyzer/cli/info_commands.py +120 -121
  16. tree_sitter_analyzer/cli_main.py +278 -276
  17. tree_sitter_analyzer/core/__init__.py +15 -20
  18. tree_sitter_analyzer/core/analysis_engine.py +555 -574
  19. tree_sitter_analyzer/core/cache_service.py +320 -330
  20. tree_sitter_analyzer/core/engine.py +559 -560
  21. tree_sitter_analyzer/core/parser.py +293 -288
  22. tree_sitter_analyzer/core/query.py +502 -502
  23. tree_sitter_analyzer/encoding_utils.py +456 -460
  24. tree_sitter_analyzer/exceptions.py +337 -340
  25. tree_sitter_analyzer/file_handler.py +210 -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 +291 -270
  30. tree_sitter_analyzer/formatters/python_formatter.py +259 -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 +343 -319
  34. tree_sitter_analyzer/interfaces/mcp_adapter.py +206 -170
  35. tree_sitter_analyzer/interfaces/mcp_server.py +405 -416
  36. tree_sitter_analyzer/java_analyzer.py +187 -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 +1174 -1113
  41. tree_sitter_analyzer/{plugins → languages}/javascript_plugin.py +446 -439
  42. tree_sitter_analyzer/languages/python_plugin.py +747 -712
  43. tree_sitter_analyzer/mcp/__init__.py +31 -32
  44. tree_sitter_analyzer/mcp/resources/__init__.py +44 -47
  45. tree_sitter_analyzer/mcp/resources/code_file_resource.py +209 -213
  46. tree_sitter_analyzer/mcp/resources/project_stats_resource.py +555 -550
  47. tree_sitter_analyzer/mcp/server.py +333 -345
  48. tree_sitter_analyzer/mcp/tools/__init__.py +30 -31
  49. tree_sitter_analyzer/mcp/tools/analyze_scale_tool.py +654 -557
  50. tree_sitter_analyzer/mcp/tools/analyze_scale_tool_cli_compatible.py +247 -245
  51. tree_sitter_analyzer/mcp/tools/base_tool.py +54 -55
  52. tree_sitter_analyzer/mcp/tools/read_partial_tool.py +300 -302
  53. tree_sitter_analyzer/mcp/tools/table_format_tool.py +362 -359
  54. tree_sitter_analyzer/mcp/tools/universal_analyze_tool.py +543 -476
  55. tree_sitter_analyzer/mcp/utils/__init__.py +107 -106
  56. tree_sitter_analyzer/mcp/utils/error_handler.py +549 -549
  57. tree_sitter_analyzer/models.py +470 -481
  58. tree_sitter_analyzer/output_manager.py +255 -264
  59. tree_sitter_analyzer/plugins/__init__.py +280 -334
  60. tree_sitter_analyzer/plugins/base.py +496 -446
  61. tree_sitter_analyzer/plugins/manager.py +379 -355
  62. tree_sitter_analyzer/queries/__init__.py +26 -27
  63. tree_sitter_analyzer/queries/java.py +391 -394
  64. tree_sitter_analyzer/queries/javascript.py +148 -149
  65. tree_sitter_analyzer/queries/python.py +285 -286
  66. tree_sitter_analyzer/queries/typescript.py +229 -230
  67. tree_sitter_analyzer/query_loader.py +257 -260
  68. tree_sitter_analyzer/table_formatter.py +471 -448
  69. tree_sitter_analyzer/utils.py +277 -277
  70. {tree_sitter_analyzer-0.2.0.dist-info → tree_sitter_analyzer-0.4.0.dist-info}/METADATA +23 -8
  71. tree_sitter_analyzer-0.4.0.dist-info/RECORD +73 -0
  72. {tree_sitter_analyzer-0.2.0.dist-info → tree_sitter_analyzer-0.4.0.dist-info}/entry_points.txt +2 -1
  73. tree_sitter_analyzer/plugins/java_plugin.py +0 -625
  74. tree_sitter_analyzer/plugins/plugin_loader.py +0 -83
  75. tree_sitter_analyzer/plugins/python_plugin.py +0 -598
  76. tree_sitter_analyzer/plugins/registry.py +0 -366
  77. tree_sitter_analyzer-0.2.0.dist-info/RECORD +0 -77
  78. {tree_sitter_analyzer-0.2.0.dist-info → tree_sitter_analyzer-0.4.0.dist-info}/WHEEL +0 -0
@@ -1,416 +1,405 @@
1
- #!/usr/bin/env python3
2
- # -*- coding: utf-8 -*-
3
- """
4
- MCP Server Interface
5
-
6
- New MCP server implementation that uses the API facade for all operations.
7
- Provides a clean separation between MCP protocol concerns and core analysis logic.
8
- """
9
-
10
- import asyncio
11
- import json
12
- import logging
13
- import sys
14
- from pathlib import Path
15
- from typing import Any, Dict, List, Optional
16
-
17
- try:
18
- from mcp.server import Server
19
- from mcp.server.models import InitializationOptions
20
- from mcp.server.stdio import stdio_server
21
- from mcp.types import (
22
- Resource,
23
- TextContent,
24
- Tool,
25
- )
26
- MCP_AVAILABLE = True
27
- except ImportError:
28
- MCP_AVAILABLE = False
29
-
30
- from .. import api
31
- from ..utils import log_error, log_info, log_warning
32
-
33
- # Configure logging for MCP
34
- logging.basicConfig(
35
- level=logging.INFO,
36
- format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
37
- )
38
- logger = logging.getLogger(__name__)
39
-
40
-
41
- class TreeSitterAnalyzerMCPServer:
42
- """
43
- MCP Server for Tree-sitter Analyzer using the new API facade.
44
-
45
- This server provides code analysis capabilities through the Model Context Protocol,
46
- using the unified API facade for all operations.
47
- """
48
-
49
- def __init__(self) -> None:
50
- """Initialize the MCP server."""
51
- if not MCP_AVAILABLE:
52
- raise ImportError("MCP library not available. Please install mcp package.")
53
-
54
- self.server: Optional[Server] = None
55
- self.name = "tree-sitter-analyzer"
56
- self.version = "2.0.0"
57
-
58
- log_info(f"Initializing {self.name} v{self.version}")
59
-
60
- def create_server(self) -> Server:
61
- """Create and configure the MCP server."""
62
- server = Server(self.name)
63
-
64
- @server.list_tools()
65
- async def handle_list_tools() -> List[Tool]:
66
- """List available tools."""
67
- return [
68
- Tool(
69
- name="analyze_file",
70
- description="Analyze a source code file comprehensively",
71
- inputSchema={
72
- "type": "object",
73
- "properties": {
74
- "file_path": {
75
- "type": "string",
76
- "description": "Path to the source file to analyze"
77
- },
78
- "language": {
79
- "type": "string",
80
- "description": "Programming language (optional, auto-detected if not specified)"
81
- },
82
- "queries": {
83
- "type": "array",
84
- "items": {"type": "string"},
85
- "description": "List of query names to execute (optional)"
86
- },
87
- "include_elements": {
88
- "type": "boolean",
89
- "description": "Whether to extract code elements",
90
- "default": True
91
- },
92
- "include_queries": {
93
- "type": "boolean",
94
- "description": "Whether to execute queries",
95
- "default": True
96
- }
97
- },
98
- "required": ["file_path"],
99
- "additionalProperties": False
100
- }
101
- ),
102
- Tool(
103
- name="analyze_code",
104
- description="Analyze source code directly (without file)",
105
- inputSchema={
106
- "type": "object",
107
- "properties": {
108
- "source_code": {
109
- "type": "string",
110
- "description": "Source code string to analyze"
111
- },
112
- "language": {
113
- "type": "string",
114
- "description": "Programming language"
115
- },
116
- "queries": {
117
- "type": "array",
118
- "items": {"type": "string"},
119
- "description": "List of query names to execute (optional)"
120
- },
121
- "include_elements": {
122
- "type": "boolean",
123
- "description": "Whether to extract code elements",
124
- "default": True
125
- },
126
- "include_queries": {
127
- "type": "boolean",
128
- "description": "Whether to execute queries",
129
- "default": True
130
- }
131
- },
132
- "required": ["source_code", "language"],
133
- "additionalProperties": False
134
- }
135
- ),
136
- Tool(
137
- name="extract_elements",
138
- description="Extract code elements from a file",
139
- inputSchema={
140
- "type": "object",
141
- "properties": {
142
- "file_path": {
143
- "type": "string",
144
- "description": "Path to the source file"
145
- },
146
- "language": {
147
- "type": "string",
148
- "description": "Programming language (optional, auto-detected if not specified)"
149
- },
150
- "element_types": {
151
- "type": "array",
152
- "items": {"type": "string"},
153
- "description": "Types of elements to extract (optional)"
154
- }
155
- },
156
- "required": ["file_path"],
157
- "additionalProperties": False
158
- }
159
- ),
160
- Tool(
161
- name="execute_query",
162
- description="Execute a specific query on a file",
163
- inputSchema={
164
- "type": "object",
165
- "properties": {
166
- "file_path": {
167
- "type": "string",
168
- "description": "Path to the source file"
169
- },
170
- "query_name": {
171
- "type": "string",
172
- "description": "Name of the query to execute"
173
- },
174
- "language": {
175
- "type": "string",
176
- "description": "Programming language (optional, auto-detected if not specified)"
177
- }
178
- },
179
- "required": ["file_path", "query_name"],
180
- "additionalProperties": False
181
- }
182
- ),
183
- Tool(
184
- name="validate_file",
185
- description="Validate a source code file",
186
- inputSchema={
187
- "type": "object",
188
- "properties": {
189
- "file_path": {
190
- "type": "string",
191
- "description": "Path to the source file to validate"
192
- }
193
- },
194
- "required": ["file_path"],
195
- "additionalProperties": False
196
- }
197
- ),
198
- Tool(
199
- name="get_supported_languages",
200
- description="Get list of supported programming languages",
201
- inputSchema={
202
- "type": "object",
203
- "properties": {},
204
- "additionalProperties": False
205
- }
206
- ),
207
- Tool(
208
- name="get_available_queries",
209
- description="Get available queries for a specific language",
210
- inputSchema={
211
- "type": "object",
212
- "properties": {
213
- "language": {
214
- "type": "string",
215
- "description": "Programming language name"
216
- }
217
- },
218
- "required": ["language"],
219
- "additionalProperties": False
220
- }
221
- ),
222
- Tool(
223
- name="get_framework_info",
224
- description="Get information about the analyzer framework",
225
- inputSchema={
226
- "type": "object",
227
- "properties": {},
228
- "additionalProperties": False
229
- }
230
- )
231
- ]
232
-
233
- @server.call_tool()
234
- async def handle_call_tool(name: str, arguments: Dict[str, Any]) -> List[TextContent]:
235
- """Handle tool calls."""
236
- try:
237
- result = None
238
-
239
- if name == "analyze_file":
240
- result = api.analyze_file(
241
- file_path=arguments["file_path"],
242
- language=arguments.get("language"),
243
- queries=arguments.get("queries"),
244
- include_elements=arguments.get("include_elements", True),
245
- include_queries=arguments.get("include_queries", True)
246
- )
247
-
248
- elif name == "analyze_code":
249
- result = api.analyze_code(
250
- source_code=arguments["source_code"],
251
- language=arguments["language"],
252
- queries=arguments.get("queries"),
253
- include_elements=arguments.get("include_elements", True),
254
- include_queries=arguments.get("include_queries", True)
255
- )
256
-
257
- elif name == "extract_elements":
258
- result = api.extract_elements(
259
- file_path=arguments["file_path"],
260
- language=arguments.get("language"),
261
- element_types=arguments.get("element_types")
262
- )
263
-
264
- elif name == "execute_query":
265
- result = api.execute_query(
266
- file_path=arguments["file_path"],
267
- query_name=arguments["query_name"],
268
- language=arguments.get("language")
269
- )
270
-
271
- elif name == "validate_file":
272
- result = api.validate_file(arguments["file_path"])
273
-
274
- elif name == "get_supported_languages":
275
- result = {
276
- "languages": api.get_supported_languages(),
277
- "total": len(api.get_supported_languages())
278
- }
279
-
280
- elif name == "get_available_queries":
281
- queries = api.get_available_queries(arguments["language"])
282
- result = {
283
- "language": arguments["language"],
284
- "queries": queries,
285
- "total": len(queries)
286
- }
287
-
288
- elif name == "get_framework_info":
289
- result = api.get_framework_info()
290
-
291
- else:
292
- raise ValueError(f"Unknown tool: {name}")
293
-
294
- return [
295
- TextContent(
296
- type="text",
297
- text=json.dumps(result, indent=2, ensure_ascii=False)
298
- )
299
- ]
300
-
301
- except Exception as e:
302
- log_error(f"Tool call error for {name}: {e}")
303
- error_result = {
304
- "error": str(e),
305
- "tool": name,
306
- "arguments": arguments,
307
- "success": False
308
- }
309
- return [
310
- TextContent(
311
- type="text",
312
- text=json.dumps(error_result, indent=2, ensure_ascii=False)
313
- )
314
- ]
315
-
316
- @server.list_resources()
317
- async def handle_list_resources() -> List[Resource]:
318
- """List available resources."""
319
- return [
320
- Resource(
321
- uri="code://file/{file_path}",
322
- name="Code File Analysis",
323
- description="Access to code file content and analysis",
324
- mimeType="application/json"
325
- ),
326
- Resource(
327
- uri="code://stats/{stats_type}",
328
- name="Project Statistics",
329
- description="Access to project statistics and analysis data",
330
- mimeType="application/json"
331
- )
332
- ]
333
-
334
- @server.read_resource()
335
- async def handle_read_resource(uri: str) -> str:
336
- """Read resource content."""
337
- try:
338
- if uri.startswith("code://file/"):
339
- # Extract file path from URI
340
- file_path = uri[len("code://file/"):]
341
-
342
- # Analyze the file
343
- result = api.analyze_file(file_path)
344
- return json.dumps(result, indent=2, ensure_ascii=False)
345
-
346
- elif uri.startswith("code://stats/"):
347
- # Extract stats type from URI
348
- stats_type = uri[len("code://stats/"):]
349
-
350
- # Get framework info as basic stats
351
- if stats_type == "framework":
352
- result = api.get_framework_info()
353
- elif stats_type == "languages":
354
- result = {
355
- "supported_languages": api.get_supported_languages(),
356
- "total_languages": len(api.get_supported_languages())
357
- }
358
- else:
359
- raise ValueError(f"Unknown stats type: {stats_type}")
360
-
361
- return json.dumps(result, indent=2, ensure_ascii=False)
362
-
363
- else:
364
- raise ValueError(f"Resource not found: {uri}")
365
-
366
- except Exception as e:
367
- log_error(f"Resource read error for {uri}: {e}")
368
- error_result = {
369
- "error": str(e),
370
- "uri": uri,
371
- "success": False
372
- }
373
- return json.dumps(error_result, indent=2, ensure_ascii=False)
374
-
375
- self.server = server
376
- log_info("MCP server created successfully")
377
- return server
378
-
379
- async def run(self) -> None:
380
- """Run the MCP server."""
381
- server = self.create_server()
382
-
383
- # Initialize server options
384
- options = InitializationOptions(
385
- server_name=self.name,
386
- server_version=self.version,
387
- capabilities={
388
- "tools": {},
389
- "resources": {}
390
- }
391
- )
392
-
393
- log_info(f"Starting MCP server: {self.name} v{self.version}")
394
-
395
- try:
396
- async with stdio_server() as (read_stream, write_stream):
397
- await server.run(read_stream, write_stream, options)
398
- except Exception as e:
399
- log_error(f"Server error: {e}")
400
- raise
401
-
402
-
403
- async def main() -> None:
404
- """Main entry point for the MCP server."""
405
- try:
406
- server = TreeSitterAnalyzerMCPServer()
407
- await server.run()
408
- except KeyboardInterrupt:
409
- log_info("Server stopped by user")
410
- except Exception as e:
411
- log_error(f"Server failed: {e}")
412
- sys.exit(1)
413
-
414
-
415
- if __name__ == "__main__":
416
- asyncio.run(main())
1
+ #!/usr/bin/env python3
2
+ """
3
+ MCP Server Interface
4
+
5
+ New MCP server implementation that uses the API facade for all operations.
6
+ Provides a clean separation between MCP protocol concerns and core analysis logic.
7
+ """
8
+
9
+ import asyncio
10
+ import json
11
+ import logging
12
+ import sys
13
+ from typing import Any
14
+
15
+ try:
16
+ from mcp.server import Server
17
+ from mcp.server.models import InitializationOptions
18
+ from mcp.server.stdio import stdio_server
19
+ from mcp.types import Resource, TextContent, Tool
20
+
21
+ MCP_AVAILABLE = True
22
+ except ImportError:
23
+ MCP_AVAILABLE = False
24
+
25
+ from .. import api
26
+ from ..utils import log_error, log_info
27
+
28
+ # Configure logging for MCP
29
+ logging.basicConfig(
30
+ level=logging.INFO, format="%(asctime)s - %(name)s - %(levelname)s - %(message)s"
31
+ )
32
+ logger = logging.getLogger(__name__)
33
+
34
+
35
+ class TreeSitterAnalyzerMCPServer:
36
+ """
37
+ MCP Server for Tree-sitter Analyzer using the new API facade.
38
+
39
+ This server provides code analysis capabilities through the Model Context Protocol,
40
+ using the unified API facade for all operations.
41
+ """
42
+
43
+ def __init__(self) -> None:
44
+ """Initialize the MCP server."""
45
+ if not MCP_AVAILABLE:
46
+ raise ImportError("MCP library not available. Please install mcp package.")
47
+
48
+ self.server: Server | None = None
49
+ self.name = "tree-sitter-analyzer"
50
+ self.version = "2.0.0"
51
+
52
+ log_info(f"Initializing {self.name} v{self.version}")
53
+
54
+ def create_server(self) -> Server:
55
+ """Create and configure the MCP server."""
56
+ server: Any = Server(self.name)
57
+
58
+ @server.list_tools() # type: ignore
59
+ async def handle_list_tools() -> list[Tool]:
60
+ """List available tools."""
61
+ return [
62
+ Tool(
63
+ name="analyze_file",
64
+ description="Analyze a source code file comprehensively",
65
+ inputSchema={
66
+ "type": "object",
67
+ "properties": {
68
+ "file_path": {
69
+ "type": "string",
70
+ "description": "Path to the source file to analyze",
71
+ },
72
+ "language": {
73
+ "type": "string",
74
+ "description": "Programming language (optional, auto-detected if not specified)",
75
+ },
76
+ "queries": {
77
+ "type": "array",
78
+ "items": {"type": "string"},
79
+ "description": "List of query names to execute (optional)",
80
+ },
81
+ "include_elements": {
82
+ "type": "boolean",
83
+ "description": "Whether to extract code elements",
84
+ "default": True,
85
+ },
86
+ "include_queries": {
87
+ "type": "boolean",
88
+ "description": "Whether to execute queries",
89
+ "default": True,
90
+ },
91
+ },
92
+ "required": ["file_path"],
93
+ "additionalProperties": False,
94
+ },
95
+ ),
96
+ Tool(
97
+ name="analyze_code",
98
+ description="Analyze source code directly (without file)",
99
+ inputSchema={
100
+ "type": "object",
101
+ "properties": {
102
+ "source_code": {
103
+ "type": "string",
104
+ "description": "Source code string to analyze",
105
+ },
106
+ "language": {
107
+ "type": "string",
108
+ "description": "Programming language",
109
+ },
110
+ "queries": {
111
+ "type": "array",
112
+ "items": {"type": "string"},
113
+ "description": "List of query names to execute (optional)",
114
+ },
115
+ "include_elements": {
116
+ "type": "boolean",
117
+ "description": "Whether to extract code elements",
118
+ "default": True,
119
+ },
120
+ "include_queries": {
121
+ "type": "boolean",
122
+ "description": "Whether to execute queries",
123
+ "default": True,
124
+ },
125
+ },
126
+ "required": ["source_code", "language"],
127
+ "additionalProperties": False,
128
+ },
129
+ ),
130
+ Tool(
131
+ name="extract_elements",
132
+ description="Extract code elements from a file",
133
+ inputSchema={
134
+ "type": "object",
135
+ "properties": {
136
+ "file_path": {
137
+ "type": "string",
138
+ "description": "Path to the source file",
139
+ },
140
+ "language": {
141
+ "type": "string",
142
+ "description": "Programming language (optional, auto-detected if not specified)",
143
+ },
144
+ "element_types": {
145
+ "type": "array",
146
+ "items": {"type": "string"},
147
+ "description": "Types of elements to extract (optional)",
148
+ },
149
+ },
150
+ "required": ["file_path"],
151
+ "additionalProperties": False,
152
+ },
153
+ ),
154
+ Tool(
155
+ name="execute_query",
156
+ description="Execute a specific query on a file",
157
+ inputSchema={
158
+ "type": "object",
159
+ "properties": {
160
+ "file_path": {
161
+ "type": "string",
162
+ "description": "Path to the source file",
163
+ },
164
+ "query_name": {
165
+ "type": "string",
166
+ "description": "Name of the query to execute",
167
+ },
168
+ "language": {
169
+ "type": "string",
170
+ "description": "Programming language (optional, auto-detected if not specified)",
171
+ },
172
+ },
173
+ "required": ["file_path", "query_name"],
174
+ "additionalProperties": False,
175
+ },
176
+ ),
177
+ Tool(
178
+ name="validate_file",
179
+ description="Validate a source code file",
180
+ inputSchema={
181
+ "type": "object",
182
+ "properties": {
183
+ "file_path": {
184
+ "type": "string",
185
+ "description": "Path to the source file to validate",
186
+ }
187
+ },
188
+ "required": ["file_path"],
189
+ "additionalProperties": False,
190
+ },
191
+ ),
192
+ Tool(
193
+ name="get_supported_languages",
194
+ description="Get list of supported programming languages",
195
+ inputSchema={
196
+ "type": "object",
197
+ "properties": {},
198
+ "additionalProperties": False,
199
+ },
200
+ ),
201
+ Tool(
202
+ name="get_available_queries",
203
+ description="Get available queries for a specific language",
204
+ inputSchema={
205
+ "type": "object",
206
+ "properties": {
207
+ "language": {
208
+ "type": "string",
209
+ "description": "Programming language name",
210
+ }
211
+ },
212
+ "required": ["language"],
213
+ "additionalProperties": False,
214
+ },
215
+ ),
216
+ Tool(
217
+ name="get_framework_info",
218
+ description="Get information about the analyzer framework",
219
+ inputSchema={
220
+ "type": "object",
221
+ "properties": {},
222
+ "additionalProperties": False,
223
+ },
224
+ ),
225
+ ]
226
+
227
+ @server.call_tool() # type: ignore
228
+ async def handle_call_tool(
229
+ name: str, arguments: dict[str, Any]
230
+ ) -> list[TextContent]:
231
+ """Handle tool calls."""
232
+ try:
233
+ result = None
234
+
235
+ if name == "analyze_file":
236
+ result = api.analyze_file(
237
+ file_path=arguments["file_path"],
238
+ language=arguments.get("language"),
239
+ queries=arguments.get("queries"),
240
+ include_elements=arguments.get("include_elements", True),
241
+ include_queries=arguments.get("include_queries", True),
242
+ )
243
+
244
+ elif name == "analyze_code":
245
+ result = api.analyze_code(
246
+ source_code=arguments["source_code"],
247
+ language=arguments["language"],
248
+ queries=arguments.get("queries"),
249
+ include_elements=arguments.get("include_elements", True),
250
+ include_queries=arguments.get("include_queries", True),
251
+ )
252
+
253
+ elif name == "extract_elements":
254
+ result = api.extract_elements(
255
+ file_path=arguments["file_path"],
256
+ language=arguments.get("language"),
257
+ element_types=arguments.get("element_types"),
258
+ )
259
+
260
+ elif name == "execute_query":
261
+ result = api.execute_query(
262
+ file_path=arguments["file_path"],
263
+ query_name=arguments["query_name"],
264
+ language=arguments.get("language"),
265
+ )
266
+
267
+ elif name == "validate_file":
268
+ result = api.validate_file(arguments["file_path"])
269
+
270
+ elif name == "get_supported_languages":
271
+ result = {
272
+ "languages": api.get_supported_languages(),
273
+ "total": len(api.get_supported_languages()),
274
+ }
275
+
276
+ elif name == "get_available_queries":
277
+ queries = api.get_available_queries(arguments["language"])
278
+ result = {
279
+ "language": arguments["language"],
280
+ "queries": queries,
281
+ "total": len(queries),
282
+ }
283
+
284
+ elif name == "get_framework_info":
285
+ result = api.get_framework_info()
286
+
287
+ else:
288
+ raise ValueError(f"Unknown tool: {name}")
289
+
290
+ return [
291
+ TextContent(
292
+ type="text",
293
+ text=json.dumps(result, indent=2, ensure_ascii=False),
294
+ )
295
+ ]
296
+
297
+ except Exception as e:
298
+ log_error(f"Tool call error for {name}: {e}")
299
+ error_result = {
300
+ "error": str(e),
301
+ "tool": name,
302
+ "arguments": arguments,
303
+ "success": False,
304
+ }
305
+ return [
306
+ TextContent(
307
+ type="text",
308
+ text=json.dumps(error_result, indent=2, ensure_ascii=False),
309
+ )
310
+ ]
311
+
312
+ @server.list_resources() # type: ignore
313
+ async def handle_list_resources() -> list[Resource]:
314
+ """List available resources."""
315
+ return [
316
+ Resource(
317
+ uri="code://file/{file_path}", # type: ignore
318
+ name="Code File Analysis",
319
+ description="Access to code file content and analysis",
320
+ mimeType="application/json",
321
+ ),
322
+ Resource(
323
+ uri="code://stats/{stats_type}", # type: ignore
324
+ name="Project Statistics",
325
+ description="Access to project statistics and analysis data",
326
+ mimeType="application/json",
327
+ ),
328
+ ]
329
+
330
+ @server.read_resource() # type: ignore
331
+ async def handle_read_resource(uri: str) -> str:
332
+ """Read resource content."""
333
+ try:
334
+ if uri.startswith("code://file/"):
335
+ # Extract file path from URI
336
+ file_path = uri[len("code://file/") :]
337
+
338
+ # Analyze the file
339
+ result = api.analyze_file(file_path)
340
+ return json.dumps(result, indent=2, ensure_ascii=False)
341
+
342
+ elif uri.startswith("code://stats/"):
343
+ # Extract stats type from URI
344
+ stats_type = uri[len("code://stats/") :]
345
+
346
+ # Get framework info as basic stats
347
+ if stats_type == "framework":
348
+ result = api.get_framework_info()
349
+ elif stats_type == "languages":
350
+ result = {
351
+ "supported_languages": api.get_supported_languages(),
352
+ "total_languages": len(api.get_supported_languages()),
353
+ }
354
+ else:
355
+ raise ValueError(f"Unknown stats type: {stats_type}")
356
+
357
+ return json.dumps(result, indent=2, ensure_ascii=False)
358
+
359
+ else:
360
+ raise ValueError(f"Resource not found: {uri}")
361
+
362
+ except Exception as e:
363
+ log_error(f"Resource read error for {uri}: {e}")
364
+ error_result = {"error": str(e), "uri": uri, "success": False}
365
+ return json.dumps(error_result, indent=2, ensure_ascii=False)
366
+
367
+ self.server = server
368
+ log_info("MCP server created successfully")
369
+ return server # type: ignore
370
+
371
+ async def run(self) -> None:
372
+ """Run the MCP server."""
373
+ server = self.create_server()
374
+
375
+ # Initialize server options
376
+ options = InitializationOptions(
377
+ server_name=self.name,
378
+ server_version=self.version,
379
+ capabilities={"tools": {}, "resources": {}}, # type: ignore
380
+ )
381
+
382
+ log_info(f"Starting MCP server: {self.name} v{self.version}")
383
+
384
+ try:
385
+ async with stdio_server() as (read_stream, write_stream):
386
+ await server.run(read_stream, write_stream, options)
387
+ except Exception as e:
388
+ log_error(f"Server error: {e}")
389
+ raise
390
+
391
+
392
+ async def main() -> None:
393
+ """Main entry point for the MCP server."""
394
+ try:
395
+ server = TreeSitterAnalyzerMCPServer()
396
+ await server.run()
397
+ except KeyboardInterrupt:
398
+ log_info("Server stopped by user")
399
+ except Exception as e:
400
+ log_error(f"Server failed: {e}")
401
+ sys.exit(1)
402
+
403
+
404
+ if __name__ == "__main__":
405
+ asyncio.run(main())