tree-sitter-analyzer 1.9.17.1__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.
Files changed (149) hide show
  1. tree_sitter_analyzer/__init__.py +132 -0
  2. tree_sitter_analyzer/__main__.py +11 -0
  3. tree_sitter_analyzer/api.py +853 -0
  4. tree_sitter_analyzer/cli/__init__.py +39 -0
  5. tree_sitter_analyzer/cli/__main__.py +12 -0
  6. tree_sitter_analyzer/cli/argument_validator.py +89 -0
  7. tree_sitter_analyzer/cli/commands/__init__.py +26 -0
  8. tree_sitter_analyzer/cli/commands/advanced_command.py +226 -0
  9. tree_sitter_analyzer/cli/commands/base_command.py +181 -0
  10. tree_sitter_analyzer/cli/commands/default_command.py +18 -0
  11. tree_sitter_analyzer/cli/commands/find_and_grep_cli.py +188 -0
  12. tree_sitter_analyzer/cli/commands/list_files_cli.py +133 -0
  13. tree_sitter_analyzer/cli/commands/partial_read_command.py +139 -0
  14. tree_sitter_analyzer/cli/commands/query_command.py +109 -0
  15. tree_sitter_analyzer/cli/commands/search_content_cli.py +161 -0
  16. tree_sitter_analyzer/cli/commands/structure_command.py +156 -0
  17. tree_sitter_analyzer/cli/commands/summary_command.py +116 -0
  18. tree_sitter_analyzer/cli/commands/table_command.py +414 -0
  19. tree_sitter_analyzer/cli/info_commands.py +124 -0
  20. tree_sitter_analyzer/cli_main.py +472 -0
  21. tree_sitter_analyzer/constants.py +85 -0
  22. tree_sitter_analyzer/core/__init__.py +15 -0
  23. tree_sitter_analyzer/core/analysis_engine.py +580 -0
  24. tree_sitter_analyzer/core/cache_service.py +333 -0
  25. tree_sitter_analyzer/core/engine.py +585 -0
  26. tree_sitter_analyzer/core/parser.py +293 -0
  27. tree_sitter_analyzer/core/query.py +605 -0
  28. tree_sitter_analyzer/core/query_filter.py +200 -0
  29. tree_sitter_analyzer/core/query_service.py +340 -0
  30. tree_sitter_analyzer/encoding_utils.py +530 -0
  31. tree_sitter_analyzer/exceptions.py +747 -0
  32. tree_sitter_analyzer/file_handler.py +246 -0
  33. tree_sitter_analyzer/formatters/__init__.py +1 -0
  34. tree_sitter_analyzer/formatters/base_formatter.py +201 -0
  35. tree_sitter_analyzer/formatters/csharp_formatter.py +367 -0
  36. tree_sitter_analyzer/formatters/formatter_config.py +197 -0
  37. tree_sitter_analyzer/formatters/formatter_factory.py +84 -0
  38. tree_sitter_analyzer/formatters/formatter_registry.py +377 -0
  39. tree_sitter_analyzer/formatters/formatter_selector.py +96 -0
  40. tree_sitter_analyzer/formatters/go_formatter.py +368 -0
  41. tree_sitter_analyzer/formatters/html_formatter.py +498 -0
  42. tree_sitter_analyzer/formatters/java_formatter.py +423 -0
  43. tree_sitter_analyzer/formatters/javascript_formatter.py +611 -0
  44. tree_sitter_analyzer/formatters/kotlin_formatter.py +268 -0
  45. tree_sitter_analyzer/formatters/language_formatter_factory.py +123 -0
  46. tree_sitter_analyzer/formatters/legacy_formatter_adapters.py +228 -0
  47. tree_sitter_analyzer/formatters/markdown_formatter.py +725 -0
  48. tree_sitter_analyzer/formatters/php_formatter.py +301 -0
  49. tree_sitter_analyzer/formatters/python_formatter.py +830 -0
  50. tree_sitter_analyzer/formatters/ruby_formatter.py +278 -0
  51. tree_sitter_analyzer/formatters/rust_formatter.py +233 -0
  52. tree_sitter_analyzer/formatters/sql_formatter_wrapper.py +689 -0
  53. tree_sitter_analyzer/formatters/sql_formatters.py +536 -0
  54. tree_sitter_analyzer/formatters/typescript_formatter.py +543 -0
  55. tree_sitter_analyzer/formatters/yaml_formatter.py +462 -0
  56. tree_sitter_analyzer/interfaces/__init__.py +9 -0
  57. tree_sitter_analyzer/interfaces/cli.py +535 -0
  58. tree_sitter_analyzer/interfaces/cli_adapter.py +359 -0
  59. tree_sitter_analyzer/interfaces/mcp_adapter.py +224 -0
  60. tree_sitter_analyzer/interfaces/mcp_server.py +428 -0
  61. tree_sitter_analyzer/language_detector.py +553 -0
  62. tree_sitter_analyzer/language_loader.py +271 -0
  63. tree_sitter_analyzer/languages/__init__.py +10 -0
  64. tree_sitter_analyzer/languages/csharp_plugin.py +1076 -0
  65. tree_sitter_analyzer/languages/css_plugin.py +449 -0
  66. tree_sitter_analyzer/languages/go_plugin.py +836 -0
  67. tree_sitter_analyzer/languages/html_plugin.py +496 -0
  68. tree_sitter_analyzer/languages/java_plugin.py +1299 -0
  69. tree_sitter_analyzer/languages/javascript_plugin.py +1622 -0
  70. tree_sitter_analyzer/languages/kotlin_plugin.py +656 -0
  71. tree_sitter_analyzer/languages/markdown_plugin.py +1928 -0
  72. tree_sitter_analyzer/languages/php_plugin.py +862 -0
  73. tree_sitter_analyzer/languages/python_plugin.py +1636 -0
  74. tree_sitter_analyzer/languages/ruby_plugin.py +757 -0
  75. tree_sitter_analyzer/languages/rust_plugin.py +673 -0
  76. tree_sitter_analyzer/languages/sql_plugin.py +2444 -0
  77. tree_sitter_analyzer/languages/typescript_plugin.py +1892 -0
  78. tree_sitter_analyzer/languages/yaml_plugin.py +695 -0
  79. tree_sitter_analyzer/legacy_table_formatter.py +860 -0
  80. tree_sitter_analyzer/mcp/__init__.py +34 -0
  81. tree_sitter_analyzer/mcp/resources/__init__.py +43 -0
  82. tree_sitter_analyzer/mcp/resources/code_file_resource.py +208 -0
  83. tree_sitter_analyzer/mcp/resources/project_stats_resource.py +586 -0
  84. tree_sitter_analyzer/mcp/server.py +869 -0
  85. tree_sitter_analyzer/mcp/tools/__init__.py +28 -0
  86. tree_sitter_analyzer/mcp/tools/analyze_scale_tool.py +779 -0
  87. tree_sitter_analyzer/mcp/tools/analyze_scale_tool_cli_compatible.py +291 -0
  88. tree_sitter_analyzer/mcp/tools/base_tool.py +139 -0
  89. tree_sitter_analyzer/mcp/tools/fd_rg_utils.py +816 -0
  90. tree_sitter_analyzer/mcp/tools/find_and_grep_tool.py +686 -0
  91. tree_sitter_analyzer/mcp/tools/list_files_tool.py +413 -0
  92. tree_sitter_analyzer/mcp/tools/output_format_validator.py +148 -0
  93. tree_sitter_analyzer/mcp/tools/query_tool.py +443 -0
  94. tree_sitter_analyzer/mcp/tools/read_partial_tool.py +464 -0
  95. tree_sitter_analyzer/mcp/tools/search_content_tool.py +836 -0
  96. tree_sitter_analyzer/mcp/tools/table_format_tool.py +572 -0
  97. tree_sitter_analyzer/mcp/tools/universal_analyze_tool.py +653 -0
  98. tree_sitter_analyzer/mcp/utils/__init__.py +113 -0
  99. tree_sitter_analyzer/mcp/utils/error_handler.py +569 -0
  100. tree_sitter_analyzer/mcp/utils/file_output_factory.py +217 -0
  101. tree_sitter_analyzer/mcp/utils/file_output_manager.py +322 -0
  102. tree_sitter_analyzer/mcp/utils/gitignore_detector.py +358 -0
  103. tree_sitter_analyzer/mcp/utils/path_resolver.py +414 -0
  104. tree_sitter_analyzer/mcp/utils/search_cache.py +343 -0
  105. tree_sitter_analyzer/models.py +840 -0
  106. tree_sitter_analyzer/mypy_current_errors.txt +2 -0
  107. tree_sitter_analyzer/output_manager.py +255 -0
  108. tree_sitter_analyzer/platform_compat/__init__.py +3 -0
  109. tree_sitter_analyzer/platform_compat/adapter.py +324 -0
  110. tree_sitter_analyzer/platform_compat/compare.py +224 -0
  111. tree_sitter_analyzer/platform_compat/detector.py +67 -0
  112. tree_sitter_analyzer/platform_compat/fixtures.py +228 -0
  113. tree_sitter_analyzer/platform_compat/profiles.py +217 -0
  114. tree_sitter_analyzer/platform_compat/record.py +55 -0
  115. tree_sitter_analyzer/platform_compat/recorder.py +155 -0
  116. tree_sitter_analyzer/platform_compat/report.py +92 -0
  117. tree_sitter_analyzer/plugins/__init__.py +280 -0
  118. tree_sitter_analyzer/plugins/base.py +647 -0
  119. tree_sitter_analyzer/plugins/manager.py +384 -0
  120. tree_sitter_analyzer/project_detector.py +328 -0
  121. tree_sitter_analyzer/queries/__init__.py +27 -0
  122. tree_sitter_analyzer/queries/csharp.py +216 -0
  123. tree_sitter_analyzer/queries/css.py +615 -0
  124. tree_sitter_analyzer/queries/go.py +275 -0
  125. tree_sitter_analyzer/queries/html.py +543 -0
  126. tree_sitter_analyzer/queries/java.py +402 -0
  127. tree_sitter_analyzer/queries/javascript.py +724 -0
  128. tree_sitter_analyzer/queries/kotlin.py +192 -0
  129. tree_sitter_analyzer/queries/markdown.py +258 -0
  130. tree_sitter_analyzer/queries/php.py +95 -0
  131. tree_sitter_analyzer/queries/python.py +859 -0
  132. tree_sitter_analyzer/queries/ruby.py +92 -0
  133. tree_sitter_analyzer/queries/rust.py +223 -0
  134. tree_sitter_analyzer/queries/sql.py +555 -0
  135. tree_sitter_analyzer/queries/typescript.py +871 -0
  136. tree_sitter_analyzer/queries/yaml.py +236 -0
  137. tree_sitter_analyzer/query_loader.py +272 -0
  138. tree_sitter_analyzer/security/__init__.py +22 -0
  139. tree_sitter_analyzer/security/boundary_manager.py +277 -0
  140. tree_sitter_analyzer/security/regex_checker.py +297 -0
  141. tree_sitter_analyzer/security/validator.py +599 -0
  142. tree_sitter_analyzer/table_formatter.py +782 -0
  143. tree_sitter_analyzer/utils/__init__.py +53 -0
  144. tree_sitter_analyzer/utils/logging.py +433 -0
  145. tree_sitter_analyzer/utils/tree_sitter_compat.py +289 -0
  146. tree_sitter_analyzer-1.9.17.1.dist-info/METADATA +485 -0
  147. tree_sitter_analyzer-1.9.17.1.dist-info/RECORD +149 -0
  148. tree_sitter_analyzer-1.9.17.1.dist-info/WHEEL +4 -0
  149. tree_sitter_analyzer-1.9.17.1.dist-info/entry_points.txt +25 -0
@@ -0,0 +1,428 @@
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
+ from .. import __version__
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 Resource, TextContent, Tool
22
+
23
+ MCP_AVAILABLE = True
24
+ except ImportError:
25
+ MCP_AVAILABLE = False
26
+
27
+ # Fallback types for development without MCP
28
+ class Server: # type: ignore
29
+ pass
30
+
31
+ class InitializationOptions: # type: ignore
32
+ def __init__(self, **kwargs: Any) -> None:
33
+ pass
34
+
35
+ class Tool: # type: ignore
36
+ pass
37
+
38
+ class Resource: # type: ignore
39
+ pass
40
+
41
+ class TextContent: # type: ignore
42
+ pass
43
+
44
+ def stdio_server() -> None: # type: ignore[misc]
45
+ pass
46
+
47
+
48
+ from .. import api
49
+ from ..utils import log_error, log_info
50
+
51
+ # Configure logging for MCP
52
+ logging.basicConfig(
53
+ level=logging.INFO, format="%(asctime)s - %(name)s - %(levelname)s - %(message)s"
54
+ )
55
+ logger = logging.getLogger(__name__)
56
+
57
+
58
+ class TreeSitterAnalyzerMCPServer:
59
+ """
60
+ MCP Server for Tree-sitter Analyzer using the new API facade.
61
+
62
+ This server provides code analysis capabilities through the Model Context Protocol,
63
+ using the unified API facade for all operations.
64
+ """
65
+
66
+ def __init__(self) -> None:
67
+ """Initialize the MCP server."""
68
+ if not MCP_AVAILABLE:
69
+ raise ImportError("MCP library not available. Please install mcp package.")
70
+
71
+ self.server: Server | None = None
72
+ self.name = "tree-sitter-analyzer"
73
+ self.version = __version__
74
+
75
+ log_info(f"Initializing {self.name} v{self.version}")
76
+
77
+ def create_server(self) -> Server:
78
+ """Create and configure the MCP server."""
79
+ server: Any = Server(self.name)
80
+
81
+ @server.list_tools() # type: ignore
82
+ async def handle_list_tools() -> list[Tool]:
83
+ """List available tools."""
84
+ return [
85
+ Tool(
86
+ name="analyze_file",
87
+ description="Analyze a source code file comprehensively",
88
+ inputSchema={
89
+ "type": "object",
90
+ "properties": {
91
+ "file_path": {
92
+ "type": "string",
93
+ "description": "Path to the source file to analyze",
94
+ },
95
+ "language": {
96
+ "type": "string",
97
+ "description": "Programming language (optional, auto-detected if not specified)",
98
+ },
99
+ "queries": {
100
+ "type": "array",
101
+ "items": {"type": "string"},
102
+ "description": "List of query names to execute (optional)",
103
+ },
104
+ "include_elements": {
105
+ "type": "boolean",
106
+ "description": "Whether to extract code elements",
107
+ "default": True,
108
+ },
109
+ "include_queries": {
110
+ "type": "boolean",
111
+ "description": "Whether to execute queries",
112
+ "default": True,
113
+ },
114
+ },
115
+ "required": ["file_path"],
116
+ "additionalProperties": False,
117
+ },
118
+ ),
119
+ Tool(
120
+ name="analyze_code",
121
+ description="Analyze source code directly (without file)",
122
+ inputSchema={
123
+ "type": "object",
124
+ "properties": {
125
+ "source_code": {
126
+ "type": "string",
127
+ "description": "Source code string to analyze",
128
+ },
129
+ "language": {
130
+ "type": "string",
131
+ "description": "Programming language",
132
+ },
133
+ "queries": {
134
+ "type": "array",
135
+ "items": {"type": "string"},
136
+ "description": "List of query names to execute (optional)",
137
+ },
138
+ "include_elements": {
139
+ "type": "boolean",
140
+ "description": "Whether to extract code elements",
141
+ "default": True,
142
+ },
143
+ "include_queries": {
144
+ "type": "boolean",
145
+ "description": "Whether to execute queries",
146
+ "default": True,
147
+ },
148
+ },
149
+ "required": ["source_code", "language"],
150
+ "additionalProperties": False,
151
+ },
152
+ ),
153
+ Tool(
154
+ name="extract_elements",
155
+ description="Extract code elements from a file",
156
+ inputSchema={
157
+ "type": "object",
158
+ "properties": {
159
+ "file_path": {
160
+ "type": "string",
161
+ "description": "Path to the source file",
162
+ },
163
+ "language": {
164
+ "type": "string",
165
+ "description": "Programming language (optional, auto-detected if not specified)",
166
+ },
167
+ "element_types": {
168
+ "type": "array",
169
+ "items": {"type": "string"},
170
+ "description": "Types of elements to extract (optional)",
171
+ },
172
+ },
173
+ "required": ["file_path"],
174
+ "additionalProperties": False,
175
+ },
176
+ ),
177
+ Tool(
178
+ name="execute_query",
179
+ description="Execute a specific query on a file",
180
+ inputSchema={
181
+ "type": "object",
182
+ "properties": {
183
+ "file_path": {
184
+ "type": "string",
185
+ "description": "Path to the source file",
186
+ },
187
+ "query_name": {
188
+ "type": "string",
189
+ "description": "Name of the query to execute",
190
+ },
191
+ "language": {
192
+ "type": "string",
193
+ "description": "Programming language (optional, auto-detected if not specified)",
194
+ },
195
+ },
196
+ "required": ["file_path", "query_name"],
197
+ "additionalProperties": False,
198
+ },
199
+ ),
200
+ Tool(
201
+ name="validate_file",
202
+ description="Validate a source code file",
203
+ inputSchema={
204
+ "type": "object",
205
+ "properties": {
206
+ "file_path": {
207
+ "type": "string",
208
+ "description": "Path to the source file to validate",
209
+ }
210
+ },
211
+ "required": ["file_path"],
212
+ "additionalProperties": False,
213
+ },
214
+ ),
215
+ Tool(
216
+ name="get_supported_languages",
217
+ description="Get list of supported programming languages",
218
+ inputSchema={
219
+ "type": "object",
220
+ "properties": {},
221
+ "additionalProperties": False,
222
+ },
223
+ ),
224
+ Tool(
225
+ name="get_available_queries",
226
+ description="Get available queries for a specific language",
227
+ inputSchema={
228
+ "type": "object",
229
+ "properties": {
230
+ "language": {
231
+ "type": "string",
232
+ "description": "Programming language name",
233
+ }
234
+ },
235
+ "required": ["language"],
236
+ "additionalProperties": False,
237
+ },
238
+ ),
239
+ Tool(
240
+ name="get_framework_info",
241
+ description="Get information about the analyzer framework",
242
+ inputSchema={
243
+ "type": "object",
244
+ "properties": {},
245
+ "additionalProperties": False,
246
+ },
247
+ ),
248
+ ]
249
+
250
+ @server.call_tool() # type: ignore
251
+ async def handle_call_tool(
252
+ name: str, arguments: dict[str, Any]
253
+ ) -> list[TextContent]:
254
+ """Handle tool calls."""
255
+ try:
256
+ result = None
257
+
258
+ if name == "analyze_file":
259
+ result = api.analyze_file(
260
+ file_path=arguments["file_path"],
261
+ language=arguments.get("language"),
262
+ queries=arguments.get("queries"),
263
+ include_elements=arguments.get("include_elements", True),
264
+ include_queries=arguments.get("include_queries", True),
265
+ )
266
+
267
+ elif name == "analyze_code":
268
+ result = api.analyze_code(
269
+ source_code=arguments["source_code"],
270
+ language=arguments["language"],
271
+ queries=arguments.get("queries"),
272
+ include_elements=arguments.get("include_elements", True),
273
+ include_queries=arguments.get("include_queries", True),
274
+ )
275
+
276
+ elif name == "extract_elements":
277
+ result = api.extract_elements(
278
+ file_path=arguments["file_path"],
279
+ language=arguments.get("language"),
280
+ element_types=arguments.get("element_types"),
281
+ )
282
+
283
+ elif name == "execute_query":
284
+ result = api.execute_query(
285
+ file_path=arguments["file_path"],
286
+ query_name=arguments["query_name"],
287
+ language=arguments.get("language"),
288
+ )
289
+
290
+ elif name == "validate_file":
291
+ result = api.validate_file(arguments["file_path"])
292
+
293
+ elif name == "get_supported_languages":
294
+ result = {
295
+ "languages": api.get_supported_languages(),
296
+ "total": len(api.get_supported_languages()),
297
+ }
298
+
299
+ elif name == "get_available_queries":
300
+ queries = api.get_available_queries(arguments["language"])
301
+ result = {
302
+ "language": arguments["language"],
303
+ "queries": queries,
304
+ "total": len(queries),
305
+ }
306
+
307
+ elif name == "get_framework_info":
308
+ result = api.get_framework_info()
309
+
310
+ else:
311
+ raise ValueError(f"Unknown tool: {name}")
312
+
313
+ return [
314
+ TextContent(
315
+ type="text",
316
+ text=json.dumps(result, indent=2, ensure_ascii=False),
317
+ )
318
+ ]
319
+
320
+ except Exception as e:
321
+ log_error(f"Tool call error for {name}: {e}")
322
+ error_result = {
323
+ "error": str(e),
324
+ "tool": name,
325
+ "arguments": arguments,
326
+ "success": False,
327
+ }
328
+ return [
329
+ TextContent(
330
+ type="text",
331
+ text=json.dumps(error_result, indent=2, ensure_ascii=False),
332
+ )
333
+ ]
334
+
335
+ @server.list_resources() # type: ignore
336
+ async def handle_list_resources() -> list[Resource]:
337
+ """List available resources."""
338
+ return [
339
+ Resource(
340
+ uri="code://file/{file_path}", # type: ignore[arg-type]
341
+ name="Code File Analysis",
342
+ description="Access to code file content and analysis",
343
+ mimeType="application/json",
344
+ ),
345
+ Resource(
346
+ uri="code://stats/{stats_type}", # type: ignore[arg-type]
347
+ name="Project Statistics",
348
+ description="Access to project statistics and analysis data",
349
+ mimeType="application/json",
350
+ ),
351
+ ]
352
+
353
+ @server.read_resource() # type: ignore
354
+ async def handle_read_resource(uri: str) -> str:
355
+ """Read resource content."""
356
+ try:
357
+ if uri.startswith("code://file/"):
358
+ # Extract file path from URI
359
+ file_path = uri[len("code://file/") :]
360
+
361
+ # Analyze the file
362
+ result = api.analyze_file(file_path)
363
+ return json.dumps(result, indent=2, ensure_ascii=False)
364
+
365
+ elif uri.startswith("code://stats/"):
366
+ # Extract stats type from URI
367
+ stats_type = uri[len("code://stats/") :]
368
+
369
+ # Get framework info as basic stats
370
+ if stats_type == "framework":
371
+ result = api.get_framework_info()
372
+ elif stats_type == "languages":
373
+ result = {
374
+ "supported_languages": api.get_supported_languages(),
375
+ "total_languages": len(api.get_supported_languages()),
376
+ }
377
+ else:
378
+ raise ValueError(f"Unknown stats type: {stats_type}")
379
+
380
+ return json.dumps(result, indent=2, ensure_ascii=False)
381
+
382
+ else:
383
+ raise ValueError(f"Resource not found: {uri}")
384
+
385
+ except Exception as e:
386
+ log_error(f"Resource read error for {uri}: {e}")
387
+ error_result = {"error": str(e), "uri": uri, "success": False}
388
+ return json.dumps(error_result, indent=2, ensure_ascii=False)
389
+
390
+ self.server = server
391
+ log_info("MCP server created successfully")
392
+ return server # type: ignore[no-any-return]
393
+
394
+ async def run(self) -> None:
395
+ """Run the MCP server."""
396
+ server = self.create_server()
397
+
398
+ # Initialize server options
399
+ options = InitializationOptions(
400
+ server_name=self.name,
401
+ server_version=self.version,
402
+ capabilities={"tools": {}, "resources": {}}, # type: ignore[arg-type]
403
+ )
404
+
405
+ log_info(f"Starting MCP server: {self.name} v{self.version}")
406
+
407
+ try:
408
+ async with stdio_server() as (read_stream, write_stream):
409
+ await server.run(read_stream, write_stream, options)
410
+ except Exception as e:
411
+ log_error(f"Server error: {e}")
412
+ raise
413
+
414
+
415
+ async def main() -> None:
416
+ """Main entry point for the MCP server."""
417
+ try:
418
+ server = TreeSitterAnalyzerMCPServer()
419
+ await server.run()
420
+ except KeyboardInterrupt:
421
+ log_info("Server stopped by user")
422
+ except Exception as e:
423
+ log_error(f"Server failed: {e}")
424
+ sys.exit(1)
425
+
426
+
427
+ if __name__ == "__main__":
428
+ asyncio.run(main())