tree-sitter-analyzer 0.7.0__py3-none-any.whl → 0.8.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 (69) hide show
  1. tree_sitter_analyzer/__init__.py +132 -132
  2. tree_sitter_analyzer/__main__.py +11 -11
  3. tree_sitter_analyzer/api.py +533 -533
  4. tree_sitter_analyzer/cli/__init__.py +39 -39
  5. tree_sitter_analyzer/cli/__main__.py +12 -12
  6. tree_sitter_analyzer/cli/commands/__init__.py +26 -26
  7. tree_sitter_analyzer/cli/commands/advanced_command.py +88 -88
  8. tree_sitter_analyzer/cli/commands/base_command.py +160 -160
  9. tree_sitter_analyzer/cli/commands/default_command.py +18 -18
  10. tree_sitter_analyzer/cli/commands/partial_read_command.py +141 -141
  11. tree_sitter_analyzer/cli/commands/query_command.py +81 -81
  12. tree_sitter_analyzer/cli/commands/structure_command.py +138 -138
  13. tree_sitter_analyzer/cli/commands/summary_command.py +101 -101
  14. tree_sitter_analyzer/cli/commands/table_command.py +235 -235
  15. tree_sitter_analyzer/cli/info_commands.py +121 -121
  16. tree_sitter_analyzer/cli_main.py +297 -297
  17. tree_sitter_analyzer/core/__init__.py +15 -15
  18. tree_sitter_analyzer/core/analysis_engine.py +555 -555
  19. tree_sitter_analyzer/core/cache_service.py +320 -320
  20. tree_sitter_analyzer/core/engine.py +566 -566
  21. tree_sitter_analyzer/core/parser.py +293 -293
  22. tree_sitter_analyzer/encoding_utils.py +459 -459
  23. tree_sitter_analyzer/exceptions.py +406 -337
  24. tree_sitter_analyzer/file_handler.py +210 -210
  25. tree_sitter_analyzer/formatters/__init__.py +1 -1
  26. tree_sitter_analyzer/formatters/base_formatter.py +167 -167
  27. tree_sitter_analyzer/formatters/formatter_factory.py +78 -78
  28. tree_sitter_analyzer/interfaces/__init__.py +9 -9
  29. tree_sitter_analyzer/interfaces/cli.py +528 -528
  30. tree_sitter_analyzer/interfaces/cli_adapter.py +343 -343
  31. tree_sitter_analyzer/interfaces/mcp_adapter.py +206 -206
  32. tree_sitter_analyzer/interfaces/mcp_server.py +425 -405
  33. tree_sitter_analyzer/languages/__init__.py +10 -10
  34. tree_sitter_analyzer/languages/javascript_plugin.py +446 -446
  35. tree_sitter_analyzer/languages/python_plugin.py +755 -755
  36. tree_sitter_analyzer/mcp/__init__.py +31 -31
  37. tree_sitter_analyzer/mcp/resources/__init__.py +44 -44
  38. tree_sitter_analyzer/mcp/resources/code_file_resource.py +209 -209
  39. tree_sitter_analyzer/mcp/server.py +346 -333
  40. tree_sitter_analyzer/mcp/tools/__init__.py +30 -30
  41. tree_sitter_analyzer/mcp/tools/analyze_scale_tool.py +654 -654
  42. tree_sitter_analyzer/mcp/tools/analyze_scale_tool_cli_compatible.py +247 -247
  43. tree_sitter_analyzer/mcp/tools/base_tool.py +54 -54
  44. tree_sitter_analyzer/mcp/tools/read_partial_tool.py +300 -300
  45. tree_sitter_analyzer/mcp/tools/table_format_tool.py +362 -362
  46. tree_sitter_analyzer/mcp/tools/universal_analyze_tool.py +543 -543
  47. tree_sitter_analyzer/mcp/utils/__init__.py +107 -107
  48. tree_sitter_analyzer/mcp/utils/error_handler.py +549 -549
  49. tree_sitter_analyzer/output_manager.py +253 -253
  50. tree_sitter_analyzer/plugins/__init__.py +280 -280
  51. tree_sitter_analyzer/plugins/base.py +529 -529
  52. tree_sitter_analyzer/plugins/manager.py +379 -379
  53. tree_sitter_analyzer/queries/__init__.py +26 -26
  54. tree_sitter_analyzer/queries/java.py +391 -391
  55. tree_sitter_analyzer/queries/javascript.py +148 -148
  56. tree_sitter_analyzer/queries/python.py +285 -285
  57. tree_sitter_analyzer/queries/typescript.py +229 -229
  58. tree_sitter_analyzer/query_loader.py +257 -257
  59. tree_sitter_analyzer/security/__init__.py +22 -0
  60. tree_sitter_analyzer/security/boundary_manager.py +237 -0
  61. tree_sitter_analyzer/security/regex_checker.py +292 -0
  62. tree_sitter_analyzer/security/validator.py +224 -0
  63. tree_sitter_analyzer/table_formatter.py +652 -589
  64. tree_sitter_analyzer/utils.py +277 -277
  65. {tree_sitter_analyzer-0.7.0.dist-info → tree_sitter_analyzer-0.8.0.dist-info}/METADATA +4 -1
  66. tree_sitter_analyzer-0.8.0.dist-info/RECORD +76 -0
  67. tree_sitter_analyzer-0.7.0.dist-info/RECORD +0 -72
  68. {tree_sitter_analyzer-0.7.0.dist-info → tree_sitter_analyzer-0.8.0.dist-info}/WHEEL +0 -0
  69. {tree_sitter_analyzer-0.7.0.dist-info → tree_sitter_analyzer-0.8.0.dist-info}/entry_points.txt +0 -0
@@ -1,300 +1,300 @@
1
- #!/usr/bin/env python3
2
- """
3
- Read Code Partial MCP Tool
4
-
5
- This tool provides partial file reading functionality through the MCP protocol,
6
- allowing selective content extraction with line and column range support.
7
- """
8
-
9
- import json
10
- from pathlib import Path
11
- from typing import Any
12
-
13
- from ...file_handler import read_file_partial
14
- from ...utils import setup_logger
15
-
16
- # Set up logging
17
- logger = setup_logger(__name__)
18
-
19
-
20
- class ReadPartialTool:
21
- """
22
- MCP Tool for reading partial content from code files.
23
-
24
- This tool integrates with existing file_handler functionality to provide
25
- selective file content reading through the MCP protocol.
26
- """
27
-
28
- def __init__(self) -> None:
29
- """Initialize the read partial tool."""
30
- logger.info("ReadPartialTool initialized")
31
-
32
- def get_tool_schema(self) -> dict[str, Any]:
33
- """
34
- Get the MCP tool schema for read_code_partial.
35
-
36
- Returns:
37
- Dictionary containing the tool schema
38
- """
39
- return {
40
- "type": "object",
41
- "properties": {
42
- "file_path": {
43
- "type": "string",
44
- "description": "Path to the code file to read",
45
- },
46
- "start_line": {
47
- "type": "integer",
48
- "description": "Starting line number (1-based)",
49
- "minimum": 1,
50
- },
51
- "end_line": {
52
- "type": "integer",
53
- "description": "Ending line number (1-based, optional - reads to end if not specified)",
54
- "minimum": 1,
55
- },
56
- "start_column": {
57
- "type": "integer",
58
- "description": "Starting column number (0-based, optional)",
59
- "minimum": 0,
60
- },
61
- "end_column": {
62
- "type": "integer",
63
- "description": "Ending column number (0-based, optional)",
64
- "minimum": 0,
65
- },
66
- "format": {
67
- "type": "string",
68
- "description": "Output format for the content",
69
- "enum": ["text", "json"],
70
- "default": "text",
71
- },
72
- },
73
- "required": ["file_path", "start_line"],
74
- "additionalProperties": False,
75
- }
76
-
77
- async def execute(self, arguments: dict[str, Any]) -> dict[str, Any]:
78
- """
79
- Execute the read_code_partial tool.
80
-
81
- Args:
82
- arguments: Tool arguments containing file_path, line/column ranges, and format
83
-
84
- Returns:
85
- Dictionary containing the partial file content and metadata (CLI --partial-read compatible format)
86
-
87
- Raises:
88
- ValueError: If required arguments are missing or invalid
89
- FileNotFoundError: If the specified file doesn't exist
90
- """
91
- # Validate required arguments
92
- if "file_path" not in arguments:
93
- raise ValueError("file_path is required")
94
-
95
- if "start_line" not in arguments:
96
- raise ValueError("start_line is required")
97
-
98
- file_path = arguments["file_path"]
99
- start_line = arguments["start_line"]
100
- end_line = arguments.get("end_line")
101
- start_column = arguments.get("start_column")
102
- end_column = arguments.get("end_column")
103
- # output_format = arguments.get("format", "text") # Not used currently
104
-
105
- # Validate file exists
106
- if not Path(file_path).exists():
107
- raise FileNotFoundError(f"File not found: {file_path}")
108
-
109
- # Validate line numbers
110
- if start_line < 1:
111
- raise ValueError("start_line must be >= 1")
112
-
113
- if end_line is not None and end_line < start_line:
114
- raise ValueError("end_line must be >= start_line")
115
-
116
- # Validate column numbers
117
- if start_column is not None and start_column < 0:
118
- raise ValueError("start_column must be >= 0")
119
-
120
- if end_column is not None and end_column < 0:
121
- raise ValueError("end_column must be >= 0")
122
-
123
- logger.info(
124
- f"Reading partial content from {file_path}: lines {start_line}-{end_line or 'end'}"
125
- )
126
-
127
- try:
128
- # Use existing file_handler functionality
129
- # Use performance monitoring with proper context manager
130
- from ...mcp.utils import get_performance_monitor
131
-
132
- with get_performance_monitor().measure_operation("read_code_partial"):
133
- content = self._read_file_partial(
134
- file_path, start_line, end_line, start_column, end_column
135
- )
136
-
137
- if content is None:
138
- raise RuntimeError(
139
- f"Failed to read partial content from file: {file_path}"
140
- )
141
-
142
- # Build result structure compatible with CLI --partial-read format
143
- result_data = {
144
- "file_path": file_path,
145
- "range": {
146
- "start_line": start_line,
147
- "end_line": end_line,
148
- "start_column": start_column,
149
- "end_column": end_column,
150
- },
151
- "content": content,
152
- "content_length": len(content),
153
- }
154
-
155
- # Format as JSON string like CLI does
156
- json_output = json.dumps(result_data, indent=2, ensure_ascii=False)
157
-
158
- # Build range info for header
159
- range_info = f"Line {start_line}"
160
- if end_line:
161
- range_info += f"-{end_line}"
162
-
163
- # Build CLI-compatible output with header and JSON (without log message)
164
- cli_output = (
165
- f"--- Partial Read Result ---\n"
166
- f"File: {file_path}\n"
167
- f"Range: {range_info}\n"
168
- f"Characters read: {len(content)}\n"
169
- f"{json_output}"
170
- )
171
-
172
- logger.info(
173
- f"Successfully read {len(content)} characters from {file_path}"
174
- )
175
-
176
- return {"partial_content_result": cli_output}
177
-
178
- except Exception as e:
179
- logger.error(f"Error reading partial content from {file_path}: {e}")
180
- raise
181
-
182
- def _read_file_partial(
183
- self,
184
- file_path: str,
185
- start_line: int,
186
- end_line: int | None = None,
187
- start_column: int | None = None,
188
- end_column: int | None = None,
189
- ) -> str | None:
190
- """
191
- Internal method to read partial file content.
192
-
193
- This method wraps the existing read_file_partial function from file_handler.
194
-
195
- Args:
196
- file_path: Path to the file to read
197
- start_line: Starting line number (1-based)
198
- end_line: Ending line number (1-based, optional)
199
- start_column: Starting column number (0-based, optional)
200
- end_column: Ending column number (0-based, optional)
201
-
202
- Returns:
203
- Partial file content as string, or None if error
204
- """
205
- return read_file_partial(
206
- file_path, start_line, end_line, start_column, end_column
207
- )
208
-
209
- def validate_arguments(self, arguments: dict[str, Any]) -> bool:
210
- """
211
- Validate tool arguments against the schema.
212
-
213
- Args:
214
- arguments: Arguments to validate
215
-
216
- Returns:
217
- True if arguments are valid
218
-
219
- Raises:
220
- ValueError: If arguments are invalid
221
- """
222
- schema = self.get_tool_schema()
223
- required_fields = schema.get("required", [])
224
-
225
- # Check required fields
226
- for field in required_fields:
227
- if field not in arguments:
228
- raise ValueError(f"Required field '{field}' is missing")
229
-
230
- # Validate file_path
231
- if "file_path" in arguments:
232
- file_path = arguments["file_path"]
233
- if not isinstance(file_path, str):
234
- raise ValueError("file_path must be a string")
235
- if not file_path.strip():
236
- raise ValueError("file_path cannot be empty")
237
-
238
- # Validate start_line
239
- if "start_line" in arguments:
240
- start_line = arguments["start_line"]
241
- if not isinstance(start_line, int):
242
- raise ValueError("start_line must be an integer")
243
- if start_line < 1:
244
- raise ValueError("start_line must be >= 1")
245
-
246
- # Validate end_line
247
- if "end_line" in arguments:
248
- end_line = arguments["end_line"]
249
- if not isinstance(end_line, int):
250
- raise ValueError("end_line must be an integer")
251
- if end_line < 1:
252
- raise ValueError("end_line must be >= 1")
253
- if "start_line" in arguments and end_line < arguments["start_line"]:
254
- raise ValueError("end_line must be >= start_line")
255
-
256
- # Validate column numbers
257
- for col_field in ["start_column", "end_column"]:
258
- if col_field in arguments:
259
- col_value = arguments[col_field]
260
- if not isinstance(col_value, int):
261
- raise ValueError(f"{col_field} must be an integer")
262
- if col_value < 0:
263
- raise ValueError(f"{col_field} must be >= 0")
264
-
265
- # Validate format
266
- if "format" in arguments:
267
- format_value = arguments["format"]
268
- if not isinstance(format_value, str):
269
- raise ValueError("format must be a string")
270
- if format_value not in ["text", "json"]:
271
- raise ValueError("format must be 'text' or 'json'")
272
-
273
- return True
274
-
275
- def get_tool_definition(self) -> Any:
276
- """
277
- Get the MCP tool definition for read_code_partial.
278
-
279
- Returns:
280
- Tool definition object compatible with MCP server
281
- """
282
- try:
283
- from mcp.types import Tool
284
-
285
- return Tool(
286
- name="read_code_partial",
287
- description="Read partial content from code files with line and column range support (equivalent to CLI --partial-read option)",
288
- inputSchema=self.get_tool_schema(),
289
- )
290
- except ImportError:
291
- # Fallback for when MCP is not available
292
- return {
293
- "name": "read_code_partial",
294
- "description": "Read partial content from code files with line and column range support (equivalent to CLI --partial-read option)",
295
- "inputSchema": self.get_tool_schema(),
296
- }
297
-
298
-
299
- # Tool instance for easy access
300
- read_partial_tool = ReadPartialTool()
1
+ #!/usr/bin/env python3
2
+ """
3
+ Read Code Partial MCP Tool
4
+
5
+ This tool provides partial file reading functionality through the MCP protocol,
6
+ allowing selective content extraction with line and column range support.
7
+ """
8
+
9
+ import json
10
+ from pathlib import Path
11
+ from typing import Any
12
+
13
+ from ...file_handler import read_file_partial
14
+ from ...utils import setup_logger
15
+
16
+ # Set up logging
17
+ logger = setup_logger(__name__)
18
+
19
+
20
+ class ReadPartialTool:
21
+ """
22
+ MCP Tool for reading partial content from code files.
23
+
24
+ This tool integrates with existing file_handler functionality to provide
25
+ selective file content reading through the MCP protocol.
26
+ """
27
+
28
+ def __init__(self) -> None:
29
+ """Initialize the read partial tool."""
30
+ logger.info("ReadPartialTool initialized")
31
+
32
+ def get_tool_schema(self) -> dict[str, Any]:
33
+ """
34
+ Get the MCP tool schema for read_code_partial.
35
+
36
+ Returns:
37
+ Dictionary containing the tool schema
38
+ """
39
+ return {
40
+ "type": "object",
41
+ "properties": {
42
+ "file_path": {
43
+ "type": "string",
44
+ "description": "Path to the code file to read",
45
+ },
46
+ "start_line": {
47
+ "type": "integer",
48
+ "description": "Starting line number (1-based)",
49
+ "minimum": 1,
50
+ },
51
+ "end_line": {
52
+ "type": "integer",
53
+ "description": "Ending line number (1-based, optional - reads to end if not specified)",
54
+ "minimum": 1,
55
+ },
56
+ "start_column": {
57
+ "type": "integer",
58
+ "description": "Starting column number (0-based, optional)",
59
+ "minimum": 0,
60
+ },
61
+ "end_column": {
62
+ "type": "integer",
63
+ "description": "Ending column number (0-based, optional)",
64
+ "minimum": 0,
65
+ },
66
+ "format": {
67
+ "type": "string",
68
+ "description": "Output format for the content",
69
+ "enum": ["text", "json"],
70
+ "default": "text",
71
+ },
72
+ },
73
+ "required": ["file_path", "start_line"],
74
+ "additionalProperties": False,
75
+ }
76
+
77
+ async def execute(self, arguments: dict[str, Any]) -> dict[str, Any]:
78
+ """
79
+ Execute the read_code_partial tool.
80
+
81
+ Args:
82
+ arguments: Tool arguments containing file_path, line/column ranges, and format
83
+
84
+ Returns:
85
+ Dictionary containing the partial file content and metadata (CLI --partial-read compatible format)
86
+
87
+ Raises:
88
+ ValueError: If required arguments are missing or invalid
89
+ FileNotFoundError: If the specified file doesn't exist
90
+ """
91
+ # Validate required arguments
92
+ if "file_path" not in arguments:
93
+ raise ValueError("file_path is required")
94
+
95
+ if "start_line" not in arguments:
96
+ raise ValueError("start_line is required")
97
+
98
+ file_path = arguments["file_path"]
99
+ start_line = arguments["start_line"]
100
+ end_line = arguments.get("end_line")
101
+ start_column = arguments.get("start_column")
102
+ end_column = arguments.get("end_column")
103
+ # output_format = arguments.get("format", "text") # Not used currently
104
+
105
+ # Validate file exists
106
+ if not Path(file_path).exists():
107
+ raise FileNotFoundError(f"File not found: {file_path}")
108
+
109
+ # Validate line numbers
110
+ if start_line < 1:
111
+ raise ValueError("start_line must be >= 1")
112
+
113
+ if end_line is not None and end_line < start_line:
114
+ raise ValueError("end_line must be >= start_line")
115
+
116
+ # Validate column numbers
117
+ if start_column is not None and start_column < 0:
118
+ raise ValueError("start_column must be >= 0")
119
+
120
+ if end_column is not None and end_column < 0:
121
+ raise ValueError("end_column must be >= 0")
122
+
123
+ logger.info(
124
+ f"Reading partial content from {file_path}: lines {start_line}-{end_line or 'end'}"
125
+ )
126
+
127
+ try:
128
+ # Use existing file_handler functionality
129
+ # Use performance monitoring with proper context manager
130
+ from ...mcp.utils import get_performance_monitor
131
+
132
+ with get_performance_monitor().measure_operation("read_code_partial"):
133
+ content = self._read_file_partial(
134
+ file_path, start_line, end_line, start_column, end_column
135
+ )
136
+
137
+ if content is None:
138
+ raise RuntimeError(
139
+ f"Failed to read partial content from file: {file_path}"
140
+ )
141
+
142
+ # Build result structure compatible with CLI --partial-read format
143
+ result_data = {
144
+ "file_path": file_path,
145
+ "range": {
146
+ "start_line": start_line,
147
+ "end_line": end_line,
148
+ "start_column": start_column,
149
+ "end_column": end_column,
150
+ },
151
+ "content": content,
152
+ "content_length": len(content),
153
+ }
154
+
155
+ # Format as JSON string like CLI does
156
+ json_output = json.dumps(result_data, indent=2, ensure_ascii=False)
157
+
158
+ # Build range info for header
159
+ range_info = f"Line {start_line}"
160
+ if end_line:
161
+ range_info += f"-{end_line}"
162
+
163
+ # Build CLI-compatible output with header and JSON (without log message)
164
+ cli_output = (
165
+ f"--- Partial Read Result ---\n"
166
+ f"File: {file_path}\n"
167
+ f"Range: {range_info}\n"
168
+ f"Characters read: {len(content)}\n"
169
+ f"{json_output}"
170
+ )
171
+
172
+ logger.info(
173
+ f"Successfully read {len(content)} characters from {file_path}"
174
+ )
175
+
176
+ return {"partial_content_result": cli_output}
177
+
178
+ except Exception as e:
179
+ logger.error(f"Error reading partial content from {file_path}: {e}")
180
+ raise
181
+
182
+ def _read_file_partial(
183
+ self,
184
+ file_path: str,
185
+ start_line: int,
186
+ end_line: int | None = None,
187
+ start_column: int | None = None,
188
+ end_column: int | None = None,
189
+ ) -> str | None:
190
+ """
191
+ Internal method to read partial file content.
192
+
193
+ This method wraps the existing read_file_partial function from file_handler.
194
+
195
+ Args:
196
+ file_path: Path to the file to read
197
+ start_line: Starting line number (1-based)
198
+ end_line: Ending line number (1-based, optional)
199
+ start_column: Starting column number (0-based, optional)
200
+ end_column: Ending column number (0-based, optional)
201
+
202
+ Returns:
203
+ Partial file content as string, or None if error
204
+ """
205
+ return read_file_partial(
206
+ file_path, start_line, end_line, start_column, end_column
207
+ )
208
+
209
+ def validate_arguments(self, arguments: dict[str, Any]) -> bool:
210
+ """
211
+ Validate tool arguments against the schema.
212
+
213
+ Args:
214
+ arguments: Arguments to validate
215
+
216
+ Returns:
217
+ True if arguments are valid
218
+
219
+ Raises:
220
+ ValueError: If arguments are invalid
221
+ """
222
+ schema = self.get_tool_schema()
223
+ required_fields = schema.get("required", [])
224
+
225
+ # Check required fields
226
+ for field in required_fields:
227
+ if field not in arguments:
228
+ raise ValueError(f"Required field '{field}' is missing")
229
+
230
+ # Validate file_path
231
+ if "file_path" in arguments:
232
+ file_path = arguments["file_path"]
233
+ if not isinstance(file_path, str):
234
+ raise ValueError("file_path must be a string")
235
+ if not file_path.strip():
236
+ raise ValueError("file_path cannot be empty")
237
+
238
+ # Validate start_line
239
+ if "start_line" in arguments:
240
+ start_line = arguments["start_line"]
241
+ if not isinstance(start_line, int):
242
+ raise ValueError("start_line must be an integer")
243
+ if start_line < 1:
244
+ raise ValueError("start_line must be >= 1")
245
+
246
+ # Validate end_line
247
+ if "end_line" in arguments:
248
+ end_line = arguments["end_line"]
249
+ if not isinstance(end_line, int):
250
+ raise ValueError("end_line must be an integer")
251
+ if end_line < 1:
252
+ raise ValueError("end_line must be >= 1")
253
+ if "start_line" in arguments and end_line < arguments["start_line"]:
254
+ raise ValueError("end_line must be >= start_line")
255
+
256
+ # Validate column numbers
257
+ for col_field in ["start_column", "end_column"]:
258
+ if col_field in arguments:
259
+ col_value = arguments[col_field]
260
+ if not isinstance(col_value, int):
261
+ raise ValueError(f"{col_field} must be an integer")
262
+ if col_value < 0:
263
+ raise ValueError(f"{col_field} must be >= 0")
264
+
265
+ # Validate format
266
+ if "format" in arguments:
267
+ format_value = arguments["format"]
268
+ if not isinstance(format_value, str):
269
+ raise ValueError("format must be a string")
270
+ if format_value not in ["text", "json"]:
271
+ raise ValueError("format must be 'text' or 'json'")
272
+
273
+ return True
274
+
275
+ def get_tool_definition(self) -> Any:
276
+ """
277
+ Get the MCP tool definition for read_code_partial.
278
+
279
+ Returns:
280
+ Tool definition object compatible with MCP server
281
+ """
282
+ try:
283
+ from mcp.types import Tool
284
+
285
+ return Tool(
286
+ name="read_code_partial",
287
+ description="Read partial content from code files with line and column range support (equivalent to CLI --partial-read option)",
288
+ inputSchema=self.get_tool_schema(),
289
+ )
290
+ except ImportError:
291
+ # Fallback for when MCP is not available
292
+ return {
293
+ "name": "read_code_partial",
294
+ "description": "Read partial content from code files with line and column range support (equivalent to CLI --partial-read option)",
295
+ "inputSchema": self.get_tool_schema(),
296
+ }
297
+
298
+
299
+ # Tool instance for easy access
300
+ read_partial_tool = ReadPartialTool()