lean-lsp-mcp 0.12.1__py3-none-any.whl → 0.13.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.
@@ -1,5 +1,5 @@
1
1
  INSTRUCTIONS = """## General Rules
2
- - All line and column numbers are 1-indexed (use lean_file_contents if unsure).
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 the current proof situation.
10
+ - lean_diagnostic_messages: Understand current proof situation.
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
 
@@ -282,6 +286,10 @@ def diagnostic_messages(
282
286
  file_path (str): Abs path to Lean file
283
287
  start_line (int, optional): Start line (1-indexed). Filters from this line.
284
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.
292
+ Slow, requires waiting for full file analysis.
285
293
 
286
294
  Returns:
287
295
  List[str] | str: Diagnostic msgs or error msg
@@ -292,6 +300,13 @@ def diagnostic_messages(
292
300
 
293
301
  client: LeanLSPClient = ctx.request_context.lifespan_context.client
294
302
 
303
+ # If declaration_name is provided, get its range and use that for filtering
304
+ if declaration_name:
305
+ decl_range = get_declaration_range(client, rel_path, declaration_name)
306
+ if decl_range is None:
307
+ return f"Declaration '{declaration_name}' not found in file. Check the name (case-sensitive) and try again."
308
+ start_line, end_line = decl_range
309
+
295
310
  # Convert 1-indexed to 0-indexed for leanclient
296
311
  start_line_0 = (start_line - 1) if start_line is not None else None
297
312
  end_line_0 = (end_line - 1) if end_line is not None else None
@@ -300,7 +315,7 @@ def diagnostic_messages(
300
315
  rel_path,
301
316
  start_line=start_line_0,
302
317
  end_line=end_line_0,
303
- inactivity_timeout=8.0,
318
+ inactivity_timeout=15.0,
304
319
  )
305
320
 
306
321
  return format_diagnostics(diagnostics)
@@ -658,7 +673,7 @@ def run_code(ctx: Context, code: str) -> List[str] | str:
658
673
  client.open_file(rel_path)
659
674
  opened_file = True
660
675
  diagnostics = format_diagnostics(
661
- client.get_diagnostics(rel_path, inactivity_timeout=8.0)
676
+ client.get_diagnostics(rel_path, inactivity_timeout=15.0)
662
677
  )
663
678
  finally:
664
679
  if opened_file:
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.12.1
3
+ Version: 0.13.1
4
4
  Summary: Lean Theorem Prover MCP
5
5
  Author-email: Oliver Dressler <hey@oli.show>
6
6
  License-Expression: MIT
@@ -8,8 +8,8 @@ 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.1
12
- Requires-Dist: mcp[cli]==1.21.0
11
+ Requires-Dist: leanclient==0.5.3
12
+ Requires-Dist: mcp[cli]==1.21.1
13
13
  Requires-Dist: orjson>=3.11.1
14
14
  Provides-Extra: lint
15
15
  Requires-Dist: ruff>=0.2.0; 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=1Xgh_fkdRpz-lqdl6kpETdwEu-IGRQGIdSdU7o9t5hc,853
6
+ lean_lsp_mcp/search_utils.py,sha256=X2LPynDNLi767UDxbxHpMccOkbnfKJKv_HxvRNxIXM4,3984
7
+ lean_lsp_mcp/server.py,sha256=g_CcgMl4aOE0MqcNv5O0DvSR7mN_fSdmJQAddDSclfA,36261
8
+ lean_lsp_mcp/utils.py,sha256=YE6o6eswOi47AYojITQ2RcR-DspqKtgACeV-O7xgOKM,10554
9
+ lean_lsp_mcp-0.13.1.dist-info/licenses/LICENSE,sha256=CQlxnf0tQyoVrBE93JYvAUYxv6Z5Yg6sX0pwogOkFvo,1071
10
+ lean_lsp_mcp-0.13.1.dist-info/METADATA,sha256=Y7CElIvCA6scvnk2PJyMANjW1dyg9VFk0-SBwpk4wU8,19626
11
+ lean_lsp_mcp-0.13.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
12
+ lean_lsp_mcp-0.13.1.dist-info/entry_points.txt,sha256=nQbvwctWkWD7I-2f4VrdVQBZYGUw8CnUnFC6QjXxOSE,51
13
+ lean_lsp_mcp-0.13.1.dist-info/top_level.txt,sha256=LGEK0lgMSNPIQ6mG8EO-adaZEGPi_0daDs004epOTF0,13
14
+ lean_lsp_mcp-0.13.1.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=AiPu6yw_m1wQdooXyhgKUGuL-Kmin4GNoz79iZNnTps,35457
8
- lean_lsp_mcp/utils.py,sha256=zLu2VIhaX4yocY07F3Z94LB2jRGrkH1ID9SjR3poE9A,8255
9
- lean_lsp_mcp-0.12.1.dist-info/licenses/LICENSE,sha256=CQlxnf0tQyoVrBE93JYvAUYxv6Z5Yg6sX0pwogOkFvo,1071
10
- lean_lsp_mcp-0.12.1.dist-info/METADATA,sha256=fwr43eEL0JDzu2wv9JJPoInsJyRL1dlGcz8RUhpPXs8,19626
11
- lean_lsp_mcp-0.12.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
12
- lean_lsp_mcp-0.12.1.dist-info/entry_points.txt,sha256=nQbvwctWkWD7I-2f4VrdVQBZYGUw8CnUnFC6QjXxOSE,51
13
- lean_lsp_mcp-0.12.1.dist-info/top_level.txt,sha256=LGEK0lgMSNPIQ6mG8EO-adaZEGPi_0daDs004epOTF0,13
14
- lean_lsp_mcp-0.12.1.dist-info/RECORD,,