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.
- tree_sitter_analyzer/__init__.py +132 -0
- tree_sitter_analyzer/__main__.py +11 -0
- tree_sitter_analyzer/api.py +853 -0
- tree_sitter_analyzer/cli/__init__.py +39 -0
- tree_sitter_analyzer/cli/__main__.py +12 -0
- tree_sitter_analyzer/cli/argument_validator.py +89 -0
- tree_sitter_analyzer/cli/commands/__init__.py +26 -0
- tree_sitter_analyzer/cli/commands/advanced_command.py +226 -0
- tree_sitter_analyzer/cli/commands/base_command.py +181 -0
- tree_sitter_analyzer/cli/commands/default_command.py +18 -0
- tree_sitter_analyzer/cli/commands/find_and_grep_cli.py +188 -0
- tree_sitter_analyzer/cli/commands/list_files_cli.py +133 -0
- tree_sitter_analyzer/cli/commands/partial_read_command.py +139 -0
- tree_sitter_analyzer/cli/commands/query_command.py +109 -0
- tree_sitter_analyzer/cli/commands/search_content_cli.py +161 -0
- tree_sitter_analyzer/cli/commands/structure_command.py +156 -0
- tree_sitter_analyzer/cli/commands/summary_command.py +116 -0
- tree_sitter_analyzer/cli/commands/table_command.py +414 -0
- tree_sitter_analyzer/cli/info_commands.py +124 -0
- tree_sitter_analyzer/cli_main.py +472 -0
- tree_sitter_analyzer/constants.py +85 -0
- tree_sitter_analyzer/core/__init__.py +15 -0
- tree_sitter_analyzer/core/analysis_engine.py +580 -0
- tree_sitter_analyzer/core/cache_service.py +333 -0
- tree_sitter_analyzer/core/engine.py +585 -0
- tree_sitter_analyzer/core/parser.py +293 -0
- tree_sitter_analyzer/core/query.py +605 -0
- tree_sitter_analyzer/core/query_filter.py +200 -0
- tree_sitter_analyzer/core/query_service.py +340 -0
- tree_sitter_analyzer/encoding_utils.py +530 -0
- tree_sitter_analyzer/exceptions.py +747 -0
- tree_sitter_analyzer/file_handler.py +246 -0
- tree_sitter_analyzer/formatters/__init__.py +1 -0
- tree_sitter_analyzer/formatters/base_formatter.py +201 -0
- tree_sitter_analyzer/formatters/csharp_formatter.py +367 -0
- tree_sitter_analyzer/formatters/formatter_config.py +197 -0
- tree_sitter_analyzer/formatters/formatter_factory.py +84 -0
- tree_sitter_analyzer/formatters/formatter_registry.py +377 -0
- tree_sitter_analyzer/formatters/formatter_selector.py +96 -0
- tree_sitter_analyzer/formatters/go_formatter.py +368 -0
- tree_sitter_analyzer/formatters/html_formatter.py +498 -0
- tree_sitter_analyzer/formatters/java_formatter.py +423 -0
- tree_sitter_analyzer/formatters/javascript_formatter.py +611 -0
- tree_sitter_analyzer/formatters/kotlin_formatter.py +268 -0
- tree_sitter_analyzer/formatters/language_formatter_factory.py +123 -0
- tree_sitter_analyzer/formatters/legacy_formatter_adapters.py +228 -0
- tree_sitter_analyzer/formatters/markdown_formatter.py +725 -0
- tree_sitter_analyzer/formatters/php_formatter.py +301 -0
- tree_sitter_analyzer/formatters/python_formatter.py +830 -0
- tree_sitter_analyzer/formatters/ruby_formatter.py +278 -0
- tree_sitter_analyzer/formatters/rust_formatter.py +233 -0
- tree_sitter_analyzer/formatters/sql_formatter_wrapper.py +689 -0
- tree_sitter_analyzer/formatters/sql_formatters.py +536 -0
- tree_sitter_analyzer/formatters/typescript_formatter.py +543 -0
- tree_sitter_analyzer/formatters/yaml_formatter.py +462 -0
- tree_sitter_analyzer/interfaces/__init__.py +9 -0
- tree_sitter_analyzer/interfaces/cli.py +535 -0
- tree_sitter_analyzer/interfaces/cli_adapter.py +359 -0
- tree_sitter_analyzer/interfaces/mcp_adapter.py +224 -0
- tree_sitter_analyzer/interfaces/mcp_server.py +428 -0
- tree_sitter_analyzer/language_detector.py +553 -0
- tree_sitter_analyzer/language_loader.py +271 -0
- tree_sitter_analyzer/languages/__init__.py +10 -0
- tree_sitter_analyzer/languages/csharp_plugin.py +1076 -0
- tree_sitter_analyzer/languages/css_plugin.py +449 -0
- tree_sitter_analyzer/languages/go_plugin.py +836 -0
- tree_sitter_analyzer/languages/html_plugin.py +496 -0
- tree_sitter_analyzer/languages/java_plugin.py +1299 -0
- tree_sitter_analyzer/languages/javascript_plugin.py +1622 -0
- tree_sitter_analyzer/languages/kotlin_plugin.py +656 -0
- tree_sitter_analyzer/languages/markdown_plugin.py +1928 -0
- tree_sitter_analyzer/languages/php_plugin.py +862 -0
- tree_sitter_analyzer/languages/python_plugin.py +1636 -0
- tree_sitter_analyzer/languages/ruby_plugin.py +757 -0
- tree_sitter_analyzer/languages/rust_plugin.py +673 -0
- tree_sitter_analyzer/languages/sql_plugin.py +2444 -0
- tree_sitter_analyzer/languages/typescript_plugin.py +1892 -0
- tree_sitter_analyzer/languages/yaml_plugin.py +695 -0
- tree_sitter_analyzer/legacy_table_formatter.py +860 -0
- tree_sitter_analyzer/mcp/__init__.py +34 -0
- tree_sitter_analyzer/mcp/resources/__init__.py +43 -0
- tree_sitter_analyzer/mcp/resources/code_file_resource.py +208 -0
- tree_sitter_analyzer/mcp/resources/project_stats_resource.py +586 -0
- tree_sitter_analyzer/mcp/server.py +869 -0
- tree_sitter_analyzer/mcp/tools/__init__.py +28 -0
- tree_sitter_analyzer/mcp/tools/analyze_scale_tool.py +779 -0
- tree_sitter_analyzer/mcp/tools/analyze_scale_tool_cli_compatible.py +291 -0
- tree_sitter_analyzer/mcp/tools/base_tool.py +139 -0
- tree_sitter_analyzer/mcp/tools/fd_rg_utils.py +816 -0
- tree_sitter_analyzer/mcp/tools/find_and_grep_tool.py +686 -0
- tree_sitter_analyzer/mcp/tools/list_files_tool.py +413 -0
- tree_sitter_analyzer/mcp/tools/output_format_validator.py +148 -0
- tree_sitter_analyzer/mcp/tools/query_tool.py +443 -0
- tree_sitter_analyzer/mcp/tools/read_partial_tool.py +464 -0
- tree_sitter_analyzer/mcp/tools/search_content_tool.py +836 -0
- tree_sitter_analyzer/mcp/tools/table_format_tool.py +572 -0
- tree_sitter_analyzer/mcp/tools/universal_analyze_tool.py +653 -0
- tree_sitter_analyzer/mcp/utils/__init__.py +113 -0
- tree_sitter_analyzer/mcp/utils/error_handler.py +569 -0
- tree_sitter_analyzer/mcp/utils/file_output_factory.py +217 -0
- tree_sitter_analyzer/mcp/utils/file_output_manager.py +322 -0
- tree_sitter_analyzer/mcp/utils/gitignore_detector.py +358 -0
- tree_sitter_analyzer/mcp/utils/path_resolver.py +414 -0
- tree_sitter_analyzer/mcp/utils/search_cache.py +343 -0
- tree_sitter_analyzer/models.py +840 -0
- tree_sitter_analyzer/mypy_current_errors.txt +2 -0
- tree_sitter_analyzer/output_manager.py +255 -0
- tree_sitter_analyzer/platform_compat/__init__.py +3 -0
- tree_sitter_analyzer/platform_compat/adapter.py +324 -0
- tree_sitter_analyzer/platform_compat/compare.py +224 -0
- tree_sitter_analyzer/platform_compat/detector.py +67 -0
- tree_sitter_analyzer/platform_compat/fixtures.py +228 -0
- tree_sitter_analyzer/platform_compat/profiles.py +217 -0
- tree_sitter_analyzer/platform_compat/record.py +55 -0
- tree_sitter_analyzer/platform_compat/recorder.py +155 -0
- tree_sitter_analyzer/platform_compat/report.py +92 -0
- tree_sitter_analyzer/plugins/__init__.py +280 -0
- tree_sitter_analyzer/plugins/base.py +647 -0
- tree_sitter_analyzer/plugins/manager.py +384 -0
- tree_sitter_analyzer/project_detector.py +328 -0
- tree_sitter_analyzer/queries/__init__.py +27 -0
- tree_sitter_analyzer/queries/csharp.py +216 -0
- tree_sitter_analyzer/queries/css.py +615 -0
- tree_sitter_analyzer/queries/go.py +275 -0
- tree_sitter_analyzer/queries/html.py +543 -0
- tree_sitter_analyzer/queries/java.py +402 -0
- tree_sitter_analyzer/queries/javascript.py +724 -0
- tree_sitter_analyzer/queries/kotlin.py +192 -0
- tree_sitter_analyzer/queries/markdown.py +258 -0
- tree_sitter_analyzer/queries/php.py +95 -0
- tree_sitter_analyzer/queries/python.py +859 -0
- tree_sitter_analyzer/queries/ruby.py +92 -0
- tree_sitter_analyzer/queries/rust.py +223 -0
- tree_sitter_analyzer/queries/sql.py +555 -0
- tree_sitter_analyzer/queries/typescript.py +871 -0
- tree_sitter_analyzer/queries/yaml.py +236 -0
- tree_sitter_analyzer/query_loader.py +272 -0
- tree_sitter_analyzer/security/__init__.py +22 -0
- tree_sitter_analyzer/security/boundary_manager.py +277 -0
- tree_sitter_analyzer/security/regex_checker.py +297 -0
- tree_sitter_analyzer/security/validator.py +599 -0
- tree_sitter_analyzer/table_formatter.py +782 -0
- tree_sitter_analyzer/utils/__init__.py +53 -0
- tree_sitter_analyzer/utils/logging.py +433 -0
- tree_sitter_analyzer/utils/tree_sitter_compat.py +289 -0
- tree_sitter_analyzer-1.9.17.1.dist-info/METADATA +485 -0
- tree_sitter_analyzer-1.9.17.1.dist-info/RECORD +149 -0
- tree_sitter_analyzer-1.9.17.1.dist-info/WHEEL +4 -0
- tree_sitter_analyzer-1.9.17.1.dist-info/entry_points.txt +25 -0
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
CLI-Compatible Analyze Code Scale MCP Tool
|
|
4
|
+
|
|
5
|
+
This tool provides code scale analysis with output format
|
|
6
|
+
that matches the CLI --advanced --statistics output exactly.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import time
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
from typing import Any, cast
|
|
12
|
+
|
|
13
|
+
from ...core.analysis_engine import get_analysis_engine
|
|
14
|
+
from ...language_detector import detect_language_from_file
|
|
15
|
+
from ...utils import setup_logger
|
|
16
|
+
|
|
17
|
+
# Set up logging
|
|
18
|
+
logger = setup_logger(__name__)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class AnalyzeScaleToolCLICompatible:
|
|
22
|
+
"""
|
|
23
|
+
MCP Tool for analyzing code scale with CLI-compatible output format.
|
|
24
|
+
|
|
25
|
+
This tool matches the exact output format of CLI --advanced --statistics.
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
def __init__(self) -> None:
|
|
29
|
+
"""Initialize the CLI-compatible analyze scale tool."""
|
|
30
|
+
self.analysis_engine = get_analysis_engine()
|
|
31
|
+
logger.info("AnalyzeScaleToolCLICompatible initialized")
|
|
32
|
+
|
|
33
|
+
def get_tool_schema(self) -> dict[str, Any]:
|
|
34
|
+
"""
|
|
35
|
+
Get the MCP tool schema for analyze_code_scale.
|
|
36
|
+
|
|
37
|
+
Returns:
|
|
38
|
+
Dictionary containing the tool schema
|
|
39
|
+
"""
|
|
40
|
+
return {
|
|
41
|
+
"type": "object",
|
|
42
|
+
"properties": {
|
|
43
|
+
"file_path": {
|
|
44
|
+
"type": "string",
|
|
45
|
+
"description": "Path to the code file to analyze",
|
|
46
|
+
},
|
|
47
|
+
"language": {
|
|
48
|
+
"type": "string",
|
|
49
|
+
"description": "Programming language (optional, auto-detected if not specified)",
|
|
50
|
+
},
|
|
51
|
+
"include_complexity": {
|
|
52
|
+
"type": "boolean",
|
|
53
|
+
"description": "Include complexity metrics in the analysis",
|
|
54
|
+
"default": True,
|
|
55
|
+
},
|
|
56
|
+
"include_details": {
|
|
57
|
+
"type": "boolean",
|
|
58
|
+
"description": "Include detailed element information",
|
|
59
|
+
"default": False,
|
|
60
|
+
},
|
|
61
|
+
},
|
|
62
|
+
"required": ["file_path"],
|
|
63
|
+
"additionalProperties": False,
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
async def execute(self, arguments: dict[str, Any]) -> dict[str, Any]:
|
|
67
|
+
"""
|
|
68
|
+
Execute the analyze_code_scale tool with CLI-compatible output.
|
|
69
|
+
|
|
70
|
+
Args:
|
|
71
|
+
arguments: Tool arguments containing file_path and optional parameters
|
|
72
|
+
|
|
73
|
+
Returns:
|
|
74
|
+
Dictionary containing analysis results in CLI-compatible format
|
|
75
|
+
|
|
76
|
+
Raises:
|
|
77
|
+
ValueError: If required arguments are missing or invalid
|
|
78
|
+
FileNotFoundError: If the specified file doesn't exist
|
|
79
|
+
"""
|
|
80
|
+
# Validate required arguments
|
|
81
|
+
if "file_path" not in arguments:
|
|
82
|
+
raise ValueError("file_path is required")
|
|
83
|
+
|
|
84
|
+
file_path = arguments["file_path"]
|
|
85
|
+
language = arguments.get("language")
|
|
86
|
+
# include_complexity = arguments.get("include_complexity", True) # Not used currently
|
|
87
|
+
# include_details = arguments.get("include_details", False) # Not used currently
|
|
88
|
+
|
|
89
|
+
# Validate file exists
|
|
90
|
+
if not Path(file_path).exists():
|
|
91
|
+
raise FileNotFoundError(f"File not found: {file_path}")
|
|
92
|
+
|
|
93
|
+
# Detect language if not specified
|
|
94
|
+
if not language:
|
|
95
|
+
language = detect_language_from_file(file_path)
|
|
96
|
+
if language == "unknown":
|
|
97
|
+
raise ValueError(f"Could not detect language for file: {file_path}")
|
|
98
|
+
|
|
99
|
+
logger.info(f"Analyzing code scale for {file_path} (language: {language})")
|
|
100
|
+
|
|
101
|
+
try:
|
|
102
|
+
start_time = time.time()
|
|
103
|
+
|
|
104
|
+
# Use AdvancedAnalyzer for comprehensive analysis
|
|
105
|
+
analysis_result = await self.analysis_engine.analyze_file(file_path)
|
|
106
|
+
|
|
107
|
+
# Handle potential None result (for testing purposes with mocked engine)
|
|
108
|
+
# This can only happen in tests where the engine is mocked to return None
|
|
109
|
+
# Use cast to tell MyPy this is possible in testing scenarios
|
|
110
|
+
if cast(Any, analysis_result) is None:
|
|
111
|
+
return {
|
|
112
|
+
"file_path": file_path,
|
|
113
|
+
"success": False,
|
|
114
|
+
"package_name": None,
|
|
115
|
+
"element_counts": {
|
|
116
|
+
"imports": 0,
|
|
117
|
+
"classes": 0,
|
|
118
|
+
"methods": 0,
|
|
119
|
+
"fields": 0,
|
|
120
|
+
"annotations": 0,
|
|
121
|
+
},
|
|
122
|
+
"analysis_time_ms": round((time.time() - start_time) * 1000, 2),
|
|
123
|
+
"error_message": f"Failed to analyze file: {file_path}",
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
# Calculate analysis time
|
|
127
|
+
analysis_time_ms = round((time.time() - start_time) * 1000, 2)
|
|
128
|
+
|
|
129
|
+
# Build CLI-compatible result structure (exact match with CLI --advanced --statistics)
|
|
130
|
+
result = {
|
|
131
|
+
"file_path": file_path,
|
|
132
|
+
"success": True,
|
|
133
|
+
"package_name": (
|
|
134
|
+
analysis_result.package.name
|
|
135
|
+
if analysis_result.package
|
|
136
|
+
and hasattr(analysis_result.package, "name")
|
|
137
|
+
else None
|
|
138
|
+
),
|
|
139
|
+
"element_counts": {
|
|
140
|
+
"imports": len(
|
|
141
|
+
[
|
|
142
|
+
e
|
|
143
|
+
for e in analysis_result.elements
|
|
144
|
+
if getattr(e, "element_type", "") == "import"
|
|
145
|
+
]
|
|
146
|
+
),
|
|
147
|
+
"classes": len(
|
|
148
|
+
[
|
|
149
|
+
e
|
|
150
|
+
for e in analysis_result.elements
|
|
151
|
+
if getattr(e, "element_type", "") == "class"
|
|
152
|
+
]
|
|
153
|
+
),
|
|
154
|
+
"methods": len(
|
|
155
|
+
[
|
|
156
|
+
e
|
|
157
|
+
for e in analysis_result.elements
|
|
158
|
+
if getattr(e, "element_type", "") == "function"
|
|
159
|
+
]
|
|
160
|
+
),
|
|
161
|
+
"fields": len(
|
|
162
|
+
[
|
|
163
|
+
e
|
|
164
|
+
for e in analysis_result.elements
|
|
165
|
+
if getattr(e, "element_type", "") == "variable"
|
|
166
|
+
]
|
|
167
|
+
),
|
|
168
|
+
"annotations": len(
|
|
169
|
+
[
|
|
170
|
+
e
|
|
171
|
+
for e in analysis_result.elements
|
|
172
|
+
if getattr(e, "element_type", "") == "annotation"
|
|
173
|
+
]
|
|
174
|
+
),
|
|
175
|
+
},
|
|
176
|
+
"analysis_time_ms": analysis_time_ms,
|
|
177
|
+
"error_message": None,
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
classes_count = len(
|
|
181
|
+
[
|
|
182
|
+
e
|
|
183
|
+
for e in analysis_result.elements
|
|
184
|
+
if getattr(e, "element_type", "") == "class"
|
|
185
|
+
]
|
|
186
|
+
)
|
|
187
|
+
methods_count = len(
|
|
188
|
+
[
|
|
189
|
+
e
|
|
190
|
+
for e in analysis_result.elements
|
|
191
|
+
if getattr(e, "element_type", "") == "function"
|
|
192
|
+
]
|
|
193
|
+
)
|
|
194
|
+
logger.info(
|
|
195
|
+
f"Successfully analyzed {file_path}: {classes_count} classes, "
|
|
196
|
+
f"{methods_count} methods, {analysis_time_ms}ms"
|
|
197
|
+
)
|
|
198
|
+
|
|
199
|
+
return result
|
|
200
|
+
|
|
201
|
+
except Exception as e:
|
|
202
|
+
logger.error(f"Error analyzing {file_path}: {e}")
|
|
203
|
+
# Return CLI-compatible error format
|
|
204
|
+
return {
|
|
205
|
+
"file_path": file_path,
|
|
206
|
+
"success": False,
|
|
207
|
+
"package_name": None,
|
|
208
|
+
"element_counts": {
|
|
209
|
+
"imports": 0,
|
|
210
|
+
"classes": 0,
|
|
211
|
+
"methods": 0,
|
|
212
|
+
"fields": 0,
|
|
213
|
+
"annotations": 0,
|
|
214
|
+
},
|
|
215
|
+
"analysis_time_ms": 0.0,
|
|
216
|
+
"error_message": str(e),
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
def validate_arguments(self, arguments: dict[str, Any]) -> bool:
|
|
220
|
+
"""
|
|
221
|
+
Validate tool arguments against the schema.
|
|
222
|
+
|
|
223
|
+
Args:
|
|
224
|
+
arguments: Arguments to validate
|
|
225
|
+
|
|
226
|
+
Returns:
|
|
227
|
+
True if arguments are valid
|
|
228
|
+
|
|
229
|
+
Raises:
|
|
230
|
+
ValueError: If arguments are invalid
|
|
231
|
+
"""
|
|
232
|
+
schema = self.get_tool_schema()
|
|
233
|
+
required_fields = schema.get("required", [])
|
|
234
|
+
|
|
235
|
+
# Check required fields
|
|
236
|
+
for field in required_fields:
|
|
237
|
+
if field not in arguments:
|
|
238
|
+
raise ValueError(f"Required field '{field}' is missing")
|
|
239
|
+
|
|
240
|
+
# Validate file_path
|
|
241
|
+
if "file_path" in arguments:
|
|
242
|
+
file_path = arguments["file_path"]
|
|
243
|
+
if not isinstance(file_path, str):
|
|
244
|
+
raise ValueError("file_path must be a string")
|
|
245
|
+
if not file_path.strip():
|
|
246
|
+
raise ValueError("file_path cannot be empty")
|
|
247
|
+
|
|
248
|
+
# Validate optional fields
|
|
249
|
+
if "language" in arguments:
|
|
250
|
+
language = arguments["language"]
|
|
251
|
+
if not isinstance(language, str):
|
|
252
|
+
raise ValueError("language must be a string")
|
|
253
|
+
|
|
254
|
+
if "include_complexity" in arguments:
|
|
255
|
+
include_complexity = arguments["include_complexity"]
|
|
256
|
+
if not isinstance(include_complexity, bool):
|
|
257
|
+
raise ValueError("include_complexity must be a boolean")
|
|
258
|
+
|
|
259
|
+
if "include_details" in arguments:
|
|
260
|
+
include_details = arguments["include_details"]
|
|
261
|
+
if not isinstance(include_details, bool):
|
|
262
|
+
raise ValueError("include_details must be a boolean")
|
|
263
|
+
|
|
264
|
+
return True
|
|
265
|
+
|
|
266
|
+
def get_tool_definition(self) -> Any:
|
|
267
|
+
"""
|
|
268
|
+
Get the MCP tool definition for analyze_code_scale.
|
|
269
|
+
|
|
270
|
+
Returns:
|
|
271
|
+
Tool definition object compatible with MCP server
|
|
272
|
+
"""
|
|
273
|
+
try:
|
|
274
|
+
from mcp.types import Tool
|
|
275
|
+
|
|
276
|
+
return Tool(
|
|
277
|
+
name="analyze_code_scale",
|
|
278
|
+
description="Analyze code scale, complexity, and structure metrics with CLI-compatible output format",
|
|
279
|
+
inputSchema=self.get_tool_schema(),
|
|
280
|
+
)
|
|
281
|
+
except ImportError:
|
|
282
|
+
# Fallback for when MCP is not available
|
|
283
|
+
return {
|
|
284
|
+
"name": "analyze_code_scale",
|
|
285
|
+
"description": "Analyze code scale, complexity, and structure metrics with CLI-compatible output format",
|
|
286
|
+
"inputSchema": self.get_tool_schema(),
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
|
|
290
|
+
# Tool instance for easy access
|
|
291
|
+
analyze_scale_tool_cli_compatible = AnalyzeScaleToolCLICompatible()
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Base Tool Protocol for MCP Tools
|
|
4
|
+
|
|
5
|
+
This module defines the base class that all MCP tools should inherit from
|
|
6
|
+
to ensure consistent behavior and project path management.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from abc import ABC, abstractmethod
|
|
10
|
+
from typing import Any
|
|
11
|
+
|
|
12
|
+
from ...security import SecurityValidator
|
|
13
|
+
from ...utils import setup_logger
|
|
14
|
+
from ..utils.path_resolver import PathResolver
|
|
15
|
+
|
|
16
|
+
# Set up logging
|
|
17
|
+
logger = setup_logger(__name__)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class BaseMCPTool(ABC):
|
|
21
|
+
"""
|
|
22
|
+
Base class for all MCP tools.
|
|
23
|
+
|
|
24
|
+
Provides common functionality including project path management,
|
|
25
|
+
security validation, and path resolution.
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
def __init__(self, project_root: str | None = None) -> None:
|
|
29
|
+
"""
|
|
30
|
+
Initialize the base MCP tool.
|
|
31
|
+
|
|
32
|
+
Args:
|
|
33
|
+
project_root: Optional project root directory
|
|
34
|
+
"""
|
|
35
|
+
self.project_root = project_root
|
|
36
|
+
self.security_validator = SecurityValidator(project_root)
|
|
37
|
+
self.path_resolver = PathResolver(project_root)
|
|
38
|
+
logger.debug(
|
|
39
|
+
f"{self.__class__.__name__} initialized with project root: {project_root}"
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
def set_project_path(self, project_path: str) -> None:
|
|
43
|
+
"""
|
|
44
|
+
Update the project path for all components.
|
|
45
|
+
|
|
46
|
+
Args:
|
|
47
|
+
project_path: New project root directory
|
|
48
|
+
"""
|
|
49
|
+
self.project_root = project_path
|
|
50
|
+
self.security_validator = SecurityValidator(project_path)
|
|
51
|
+
self.path_resolver = PathResolver(project_path)
|
|
52
|
+
logger.info(
|
|
53
|
+
f"{self.__class__.__name__} project path updated to: {project_path}"
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
@abstractmethod
|
|
57
|
+
def get_tool_definition(self) -> Any:
|
|
58
|
+
"""
|
|
59
|
+
Get the MCP tool definition.
|
|
60
|
+
|
|
61
|
+
Returns:
|
|
62
|
+
Tool definition object compatible with MCP server
|
|
63
|
+
"""
|
|
64
|
+
pass
|
|
65
|
+
|
|
66
|
+
@abstractmethod
|
|
67
|
+
async def execute(self, arguments: dict[str, Any]) -> dict[str, Any]:
|
|
68
|
+
"""
|
|
69
|
+
Execute the tool with the given arguments.
|
|
70
|
+
|
|
71
|
+
Args:
|
|
72
|
+
arguments: Tool arguments
|
|
73
|
+
|
|
74
|
+
Returns:
|
|
75
|
+
Dictionary containing execution results
|
|
76
|
+
"""
|
|
77
|
+
pass
|
|
78
|
+
|
|
79
|
+
@abstractmethod
|
|
80
|
+
def validate_arguments(self, arguments: dict[str, Any]) -> bool:
|
|
81
|
+
"""
|
|
82
|
+
Validate tool arguments.
|
|
83
|
+
|
|
84
|
+
Args:
|
|
85
|
+
arguments: Arguments to validate
|
|
86
|
+
|
|
87
|
+
Returns:
|
|
88
|
+
True if arguments are valid
|
|
89
|
+
|
|
90
|
+
Raises:
|
|
91
|
+
ValueError: If arguments are invalid
|
|
92
|
+
"""
|
|
93
|
+
pass
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
# Keep the protocol for backward compatibility
|
|
97
|
+
class MCPTool(BaseMCPTool):
|
|
98
|
+
"""
|
|
99
|
+
Protocol for MCP tools (deprecated, use BaseMCPTool instead).
|
|
100
|
+
|
|
101
|
+
All MCP tools must implement this protocol to ensure they have
|
|
102
|
+
the required methods for integration with the MCP server.
|
|
103
|
+
"""
|
|
104
|
+
|
|
105
|
+
def get_tool_definition(self) -> Any:
|
|
106
|
+
"""
|
|
107
|
+
Get the MCP tool definition.
|
|
108
|
+
|
|
109
|
+
Returns:
|
|
110
|
+
Tool definition object compatible with MCP server
|
|
111
|
+
"""
|
|
112
|
+
...
|
|
113
|
+
|
|
114
|
+
async def execute(self, arguments: dict[str, Any]) -> dict[str, Any]:
|
|
115
|
+
"""
|
|
116
|
+
Execute the tool with the given arguments.
|
|
117
|
+
|
|
118
|
+
Args:
|
|
119
|
+
arguments: Tool arguments
|
|
120
|
+
|
|
121
|
+
Returns:
|
|
122
|
+
Dictionary containing execution results
|
|
123
|
+
"""
|
|
124
|
+
raise NotImplementedError("Subclasses must implement execute method")
|
|
125
|
+
|
|
126
|
+
def validate_arguments(self, arguments: dict[str, Any]) -> bool:
|
|
127
|
+
"""
|
|
128
|
+
Validate tool arguments.
|
|
129
|
+
|
|
130
|
+
Args:
|
|
131
|
+
arguments: Arguments to validate
|
|
132
|
+
|
|
133
|
+
Returns:
|
|
134
|
+
True if arguments are valid
|
|
135
|
+
|
|
136
|
+
Raises:
|
|
137
|
+
ValueError: If arguments are invalid
|
|
138
|
+
"""
|
|
139
|
+
raise NotImplementedError("Subclasses must implement validate_arguments method")
|