lean-lsp-mcp 0.12.0__py3-none-any.whl → 0.13.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.
- lean_lsp_mcp/instructions.py +2 -2
- lean_lsp_mcp/server.py +28 -15
- lean_lsp_mcp/utils.py +75 -0
- {lean_lsp_mcp-0.12.0.dist-info → lean_lsp_mcp-0.13.0.dist-info}/METADATA +2 -2
- lean_lsp_mcp-0.13.0.dist-info/RECORD +14 -0
- lean_lsp_mcp-0.12.0.dist-info/RECORD +0 -14
- {lean_lsp_mcp-0.12.0.dist-info → lean_lsp_mcp-0.13.0.dist-info}/WHEEL +0 -0
- {lean_lsp_mcp-0.12.0.dist-info → lean_lsp_mcp-0.13.0.dist-info}/entry_points.txt +0 -0
- {lean_lsp_mcp-0.12.0.dist-info → lean_lsp_mcp-0.13.0.dist-info}/licenses/LICENSE +0 -0
- {lean_lsp_mcp-0.12.0.dist-info → lean_lsp_mcp-0.13.0.dist-info}/top_level.txt +0 -0
lean_lsp_mcp/instructions.py
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
INSTRUCTIONS = """## General Rules
|
|
2
|
-
- All line and column numbers are 1-indexed
|
|
2
|
+
- All line and column numbers are 1-indexed.
|
|
3
3
|
- Always analyze/search context before each file edit.
|
|
4
4
|
- This MCP does NOT make permanent file changes. Use other tools for editing.
|
|
5
5
|
- Work iteratively: Small steps, intermediate sorries, frequent checks.
|
|
@@ -7,7 +7,7 @@ INSTRUCTIONS = """## General Rules
|
|
|
7
7
|
## Key Tools
|
|
8
8
|
- lean_local_search: Confirm declarations (theorems/lemmas/defs/etc.) exist. VERY USEFUL AND FAST!
|
|
9
9
|
- lean_goal: Check proof state. USE OFTEN!
|
|
10
|
-
- lean_diagnostic_messages: Understand
|
|
10
|
+
- lean_diagnostic_messages: Understand current proof situation (use declaration_name to focus on one proof).
|
|
11
11
|
- lean_hover_info: Documentation about terms and lean syntax.
|
|
12
12
|
- lean_leansearch: Search theorems using natural language or Lean terms.
|
|
13
13
|
- lean_loogle: Search definitions and theorems by name, type, or subexpression.
|
lean_lsp_mcp/server.py
CHANGED
|
@@ -34,6 +34,7 @@ from lean_lsp_mcp.utils import (
|
|
|
34
34
|
format_diagnostics,
|
|
35
35
|
format_goal,
|
|
36
36
|
format_line,
|
|
37
|
+
get_declaration_range,
|
|
37
38
|
OptionalTokenVerifier,
|
|
38
39
|
)
|
|
39
40
|
|
|
@@ -238,6 +239,8 @@ async def lsp_build(
|
|
|
238
239
|
def file_contents(ctx: Context, file_path: str, annotate_lines: bool = True) -> str:
|
|
239
240
|
"""Get the text contents of a Lean file, optionally with line numbers.
|
|
240
241
|
|
|
242
|
+
Use sparingly (bloats context). Mainly when unsure about line numbers.
|
|
243
|
+
|
|
241
244
|
Args:
|
|
242
245
|
file_path (str): Abs path to Lean file
|
|
243
246
|
annotate_lines (bool, optional): Annotate lines with line numbers. Defaults to True.
|
|
@@ -273,6 +276,7 @@ def diagnostic_messages(
|
|
|
273
276
|
file_path: str,
|
|
274
277
|
start_line: Optional[int] = None,
|
|
275
278
|
end_line: Optional[int] = None,
|
|
279
|
+
declaration_name: Optional[str] = None,
|
|
276
280
|
) -> List[str] | str:
|
|
277
281
|
"""Get all diagnostic msgs (errors, warnings, infos) for a Lean file.
|
|
278
282
|
|
|
@@ -280,8 +284,11 @@ def diagnostic_messages(
|
|
|
280
284
|
|
|
281
285
|
Args:
|
|
282
286
|
file_path (str): Abs path to Lean file
|
|
283
|
-
start_line (int, optional): Start line (1-indexed)
|
|
284
|
-
end_line (int, optional): End line (1-indexed)
|
|
287
|
+
start_line (int, optional): Start line (1-indexed). Filters from this line.
|
|
288
|
+
end_line (int, optional): End line (1-indexed). Filters to this line.
|
|
289
|
+
declaration_name (str, optional): Name of a specific theorem/lemma/definition.
|
|
290
|
+
If provided, only returns diagnostics within that declaration.
|
|
291
|
+
Takes precedence over start_line/end_line.
|
|
285
292
|
|
|
286
293
|
Returns:
|
|
287
294
|
List[str] | str: Diagnostic msgs or error msg
|
|
@@ -292,19 +299,23 @@ def diagnostic_messages(
|
|
|
292
299
|
|
|
293
300
|
client: LeanLSPClient = ctx.request_context.lifespan_context.client
|
|
294
301
|
|
|
295
|
-
#
|
|
302
|
+
# If declaration_name is provided, get its range and use that for filtering
|
|
303
|
+
if declaration_name:
|
|
304
|
+
decl_range = get_declaration_range(client, rel_path, declaration_name)
|
|
305
|
+
if decl_range is None:
|
|
306
|
+
return f"Declaration '{declaration_name}' not found in file. Check the name (case-sensitive) and try again."
|
|
307
|
+
start_line, end_line = decl_range
|
|
308
|
+
|
|
296
309
|
# Convert 1-indexed to 0-indexed for leanclient
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
else:
|
|
307
|
-
diagnostics = client.get_diagnostics(rel_path, inactivity_timeout=10.0)
|
|
310
|
+
start_line_0 = (start_line - 1) if start_line is not None else None
|
|
311
|
+
end_line_0 = (end_line - 1) if end_line is not None else None
|
|
312
|
+
|
|
313
|
+
diagnostics = client.get_diagnostics(
|
|
314
|
+
rel_path,
|
|
315
|
+
start_line=start_line_0,
|
|
316
|
+
end_line=end_line_0,
|
|
317
|
+
inactivity_timeout=8.0,
|
|
318
|
+
)
|
|
308
319
|
|
|
309
320
|
return format_diagnostics(diagnostics)
|
|
310
321
|
|
|
@@ -660,7 +671,9 @@ def run_code(ctx: Context, code: str) -> List[str] | str:
|
|
|
660
671
|
assert client is not None # startup_client guarantees an initialized client
|
|
661
672
|
client.open_file(rel_path)
|
|
662
673
|
opened_file = True
|
|
663
|
-
diagnostics = format_diagnostics(
|
|
674
|
+
diagnostics = format_diagnostics(
|
|
675
|
+
client.get_diagnostics(rel_path, inactivity_timeout=8.0)
|
|
676
|
+
)
|
|
664
677
|
finally:
|
|
665
678
|
if opened_file:
|
|
666
679
|
try:
|
lean_lsp_mcp/utils.py
CHANGED
|
@@ -259,3 +259,78 @@ def filter_diagnostics_by_position(
|
|
|
259
259
|
matches.append(diagnostic)
|
|
260
260
|
|
|
261
261
|
return matches
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
def search_symbols(symbols: List[Dict], target_name: str) -> Dict | None:
|
|
265
|
+
"""Recursively search through symbols and their children.
|
|
266
|
+
|
|
267
|
+
Args:
|
|
268
|
+
symbols: List of LSP document symbols
|
|
269
|
+
target_name: Name of the symbol to find (case-sensitive)
|
|
270
|
+
|
|
271
|
+
Returns:
|
|
272
|
+
The matching symbol dict, or None if not found
|
|
273
|
+
"""
|
|
274
|
+
for symbol in symbols:
|
|
275
|
+
if symbol.get("name") == target_name:
|
|
276
|
+
return symbol
|
|
277
|
+
# Search nested declarations (children)
|
|
278
|
+
children = symbol.get("children", [])
|
|
279
|
+
if children:
|
|
280
|
+
result = search_symbols(children, target_name)
|
|
281
|
+
if result:
|
|
282
|
+
return result
|
|
283
|
+
return None
|
|
284
|
+
|
|
285
|
+
|
|
286
|
+
def get_declaration_range(
|
|
287
|
+
client, file_path: str, declaration_name: str
|
|
288
|
+
) -> tuple[int, int] | None:
|
|
289
|
+
"""Get the line range (1-indexed) of a declaration by name using LSP document symbols.
|
|
290
|
+
|
|
291
|
+
Args:
|
|
292
|
+
client: The Lean LSP client instance (LeanLSPClient)
|
|
293
|
+
file_path: Relative path to the Lean file
|
|
294
|
+
declaration_name: Name of the declaration to find (case-sensitive)
|
|
295
|
+
|
|
296
|
+
Returns:
|
|
297
|
+
Tuple of (start_line, end_line) as 1-indexed integers, or None if not found
|
|
298
|
+
"""
|
|
299
|
+
from lean_lsp_mcp.server import logger
|
|
300
|
+
|
|
301
|
+
try:
|
|
302
|
+
# Ensure file is opened (LSP needs this to analyze the file)
|
|
303
|
+
client.open_file(file_path)
|
|
304
|
+
|
|
305
|
+
# Get document symbols from LSP
|
|
306
|
+
symbols = client.get_document_symbols(file_path)
|
|
307
|
+
|
|
308
|
+
if not symbols:
|
|
309
|
+
logger.debug(
|
|
310
|
+
"No document symbols returned for '%s' - file may not be processed yet",
|
|
311
|
+
file_path,
|
|
312
|
+
)
|
|
313
|
+
return None
|
|
314
|
+
|
|
315
|
+
matching_symbol = search_symbols(symbols, declaration_name)
|
|
316
|
+
if not matching_symbol:
|
|
317
|
+
return None
|
|
318
|
+
|
|
319
|
+
# Extract range - LSP returns 0-indexed, convert to 1-indexed
|
|
320
|
+
range_info = matching_symbol.get("range")
|
|
321
|
+
if not range_info:
|
|
322
|
+
return None
|
|
323
|
+
|
|
324
|
+
start_line = range_info["start"]["line"] + 1
|
|
325
|
+
end_line = range_info["end"]["line"] + 1
|
|
326
|
+
|
|
327
|
+
return (start_line, end_line)
|
|
328
|
+
|
|
329
|
+
except Exception as e:
|
|
330
|
+
logger.warning(
|
|
331
|
+
"Failed to get declaration range for '%s' in '%s': %s",
|
|
332
|
+
declaration_name,
|
|
333
|
+
file_path,
|
|
334
|
+
e,
|
|
335
|
+
)
|
|
336
|
+
return None
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: lean-lsp-mcp
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.13.0
|
|
4
4
|
Summary: Lean Theorem Prover MCP
|
|
5
5
|
Author-email: Oliver Dressler <hey@oli.show>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -8,7 +8,7 @@ Project-URL: Repository, https://github.com/oOo0oOo/lean-lsp-mcp
|
|
|
8
8
|
Requires-Python: >=3.10
|
|
9
9
|
Description-Content-Type: text/markdown
|
|
10
10
|
License-File: LICENSE
|
|
11
|
-
Requires-Dist: leanclient==0.5.
|
|
11
|
+
Requires-Dist: leanclient==0.5.2
|
|
12
12
|
Requires-Dist: mcp[cli]==1.21.0
|
|
13
13
|
Requires-Dist: orjson>=3.11.1
|
|
14
14
|
Provides-Extra: lint
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
lean_lsp_mcp/__init__.py,sha256=lxqDq0G_sI2iu2Nniy-pTW7BE9Ux7ZXeDoGf0OAWIDc,763
|
|
2
|
+
lean_lsp_mcp/__main__.py,sha256=XnpTzfJc0T-j9tHtdkA8ovTr1c139ffTewcJGhxYDaM,49
|
|
3
|
+
lean_lsp_mcp/client_utils.py,sha256=hF941DEeRE3ICMgMhv9J4vv6bO4hZPJOAcFU03yIDXs,4988
|
|
4
|
+
lean_lsp_mcp/file_utils.py,sha256=kCTYQSfmV-R2cm_NCi_L8W5Dcsm0_rTOPpTtpyAin78,1365
|
|
5
|
+
lean_lsp_mcp/instructions.py,sha256=hJuWqRBOEAtzkQT83W2YgjgOJss9GZIGk2M1Y45X6bI,898
|
|
6
|
+
lean_lsp_mcp/search_utils.py,sha256=X2LPynDNLi767UDxbxHpMccOkbnfKJKv_HxvRNxIXM4,3984
|
|
7
|
+
lean_lsp_mcp/server.py,sha256=K-m_a8CiR8cUlyyZEq9t_R3Y07KZ-mZ_Ljws42nPCLU,36200
|
|
8
|
+
lean_lsp_mcp/utils.py,sha256=YE6o6eswOi47AYojITQ2RcR-DspqKtgACeV-O7xgOKM,10554
|
|
9
|
+
lean_lsp_mcp-0.13.0.dist-info/licenses/LICENSE,sha256=CQlxnf0tQyoVrBE93JYvAUYxv6Z5Yg6sX0pwogOkFvo,1071
|
|
10
|
+
lean_lsp_mcp-0.13.0.dist-info/METADATA,sha256=OMClNzemdx8HJSJ5QvJ4ZYRz4ZqtyFvSDDztn2gH1Lk,19626
|
|
11
|
+
lean_lsp_mcp-0.13.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
12
|
+
lean_lsp_mcp-0.13.0.dist-info/entry_points.txt,sha256=nQbvwctWkWD7I-2f4VrdVQBZYGUw8CnUnFC6QjXxOSE,51
|
|
13
|
+
lean_lsp_mcp-0.13.0.dist-info/top_level.txt,sha256=LGEK0lgMSNPIQ6mG8EO-adaZEGPi_0daDs004epOTF0,13
|
|
14
|
+
lean_lsp_mcp-0.13.0.dist-info/RECORD,,
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
lean_lsp_mcp/__init__.py,sha256=lxqDq0G_sI2iu2Nniy-pTW7BE9Ux7ZXeDoGf0OAWIDc,763
|
|
2
|
-
lean_lsp_mcp/__main__.py,sha256=XnpTzfJc0T-j9tHtdkA8ovTr1c139ffTewcJGhxYDaM,49
|
|
3
|
-
lean_lsp_mcp/client_utils.py,sha256=hF941DEeRE3ICMgMhv9J4vv6bO4hZPJOAcFU03yIDXs,4988
|
|
4
|
-
lean_lsp_mcp/file_utils.py,sha256=kCTYQSfmV-R2cm_NCi_L8W5Dcsm0_rTOPpTtpyAin78,1365
|
|
5
|
-
lean_lsp_mcp/instructions.py,sha256=y_gHlbeJoKnPohmcSVrQQds6mbBO1en-lxnXAfEypZE,892
|
|
6
|
-
lean_lsp_mcp/search_utils.py,sha256=X2LPynDNLi767UDxbxHpMccOkbnfKJKv_HxvRNxIXM4,3984
|
|
7
|
-
lean_lsp_mcp/server.py,sha256=oxuytxr58QLS-lCVRxYgtlLGEW0uHPhw2gKC5oRChqk,35799
|
|
8
|
-
lean_lsp_mcp/utils.py,sha256=zLu2VIhaX4yocY07F3Z94LB2jRGrkH1ID9SjR3poE9A,8255
|
|
9
|
-
lean_lsp_mcp-0.12.0.dist-info/licenses/LICENSE,sha256=CQlxnf0tQyoVrBE93JYvAUYxv6Z5Yg6sX0pwogOkFvo,1071
|
|
10
|
-
lean_lsp_mcp-0.12.0.dist-info/METADATA,sha256=wMGQwNNQxVIt_hxZZgOZDIJ1E5wQmq2h4x9hRJrBgjw,19626
|
|
11
|
-
lean_lsp_mcp-0.12.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
12
|
-
lean_lsp_mcp-0.12.0.dist-info/entry_points.txt,sha256=nQbvwctWkWD7I-2f4VrdVQBZYGUw8CnUnFC6QjXxOSE,51
|
|
13
|
-
lean_lsp_mcp-0.12.0.dist-info/top_level.txt,sha256=LGEK0lgMSNPIQ6mG8EO-adaZEGPi_0daDs004epOTF0,13
|
|
14
|
-
lean_lsp_mcp-0.12.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|