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.
@@ -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 (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) for filtering diagnostics. If provided, only diagnostics from this line onwards are returned.
284
- end_line (int, optional): End line (1-indexed) for filtering diagnostics. If provided with start_line, only diagnostics in this range are returned.
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
- # leanclient requires both start_line and end_line to filter
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
- if start_line is not None or end_line is not None:
298
- start_line_0 = (start_line - 1) if start_line is not None else 0
299
- end_line_0 = (end_line - 1) if end_line is not None else 999999
300
- diagnostics = client.get_diagnostics(
301
- rel_path,
302
- start_line=start_line_0,
303
- end_line=end_line_0,
304
- inactivity_timeout=10.0,
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(client.get_diagnostics(rel_path))
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.12.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.0
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,,