lean-lsp-mcp 0.11.2__py3-none-any.whl → 0.12.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.
@@ -34,7 +34,6 @@ def startup_client(ctx: Context):
34
34
  return # Client already set up correctly - reuse it!
35
35
  # Different project path - close old client
36
36
  client.close()
37
- ctx.request_context.lifespan_context.file_content_hashes.clear()
38
37
 
39
38
  # Need to create a new client
40
39
  with OutputCapture() as output:
@@ -64,59 +63,75 @@ def valid_lean_project_path(path: Path | str) -> bool:
64
63
  return (path_obj / "lean-toolchain").is_file()
65
64
 
66
65
 
67
- def setup_client_for_file(ctx: Context, file_path: str) -> str | None:
68
- """Ensure the LSP client matches the file's Lean project and return its relative path."""
66
+ def infer_project_path(ctx: Context, file_path: str) -> Path | None:
67
+ """Infer and cache the Lean project path for a file WITHOUT starting the client.
68
+
69
+ Walks up the directory tree to find a lean-toolchain file, caches the result.
70
+ Sets ctx.request_context.lifespan_context.lean_project_path if found.
71
+
72
+ Side effects when path changes:
73
+ - Next LSP tool will restart the client for the new project
74
+ - File content hashes will be cleared
69
75
 
76
+ Args:
77
+ ctx (Context): Context object
78
+ file_path (str): Absolute or relative path to a Lean file
79
+
80
+ Returns:
81
+ Path | None: The resolved project path if found, None otherwise
82
+ """
70
83
  lifespan = ctx.request_context.lifespan_context
71
- project_cache = getattr(lifespan, "project_cache", {})
72
84
  if not hasattr(lifespan, "project_cache"):
73
- lifespan.project_cache = project_cache
85
+ lifespan.project_cache = {}
74
86
 
75
87
  abs_file_path = os.path.abspath(file_path)
76
88
  file_dir = os.path.dirname(abs_file_path)
77
89
 
78
- def activate_project(project_path: Path, cache_dirs: list[str]) -> str | None:
79
- project_path_obj = project_path
80
- rel = get_relative_file_path(project_path_obj, file_path)
81
- if rel is None:
90
+ def set_project_path(project_path: Path, cache_dirs: list[str]) -> Path | None:
91
+ """Validate file is in project, set path, update cache."""
92
+ if get_relative_file_path(project_path, file_path) is None:
82
93
  return None
83
94
 
84
- project_path_obj = project_path_obj.resolve()
85
- lifespan.lean_project_path = project_path_obj
95
+ project_path = project_path.resolve()
96
+ lifespan.lean_project_path = project_path
86
97
 
87
- cache_targets: list[str] = []
88
- for directory in cache_dirs + [str(project_path_obj)]:
89
- if directory and directory not in cache_targets:
90
- cache_targets.append(directory)
98
+ # Update all relevant directories in cache
99
+ for directory in set(cache_dirs + [str(project_path)]):
100
+ if directory:
101
+ lifespan.project_cache[directory] = project_path
91
102
 
92
- for directory in cache_targets:
93
- project_cache[directory] = project_path_obj
103
+ return project_path
94
104
 
95
- startup_client(ctx)
96
- return rel
105
+ # Fast path: current project already valid for this file
106
+ if lifespan.lean_project_path and set_project_path(
107
+ lifespan.lean_project_path, [file_dir]
108
+ ):
109
+ return lifespan.lean_project_path
97
110
 
98
- # Fast path: current Lean project already valid for this file
99
- if lifespan.lean_project_path is not None:
100
- rel_path = activate_project(lifespan.lean_project_path, [file_dir])
101
- if rel_path is not None:
102
- return rel_path
103
-
104
- # Walk up from file directory to root, using cache hits or lean-toolchain
105
- prev_dir = None
111
+ # Walk up directory tree using cache and lean-toolchain detection
106
112
  current_dir = file_dir
107
- while current_dir and current_dir != prev_dir:
108
- cached_root = project_cache.get(current_dir)
113
+ while current_dir and current_dir != os.path.dirname(current_dir):
114
+ cached_root = lifespan.project_cache.get(current_dir)
115
+
109
116
  if cached_root:
110
- rel_path = activate_project(Path(cached_root), [current_dir])
111
- if rel_path is not None:
112
- return rel_path
117
+ if result := set_project_path(Path(cached_root), [current_dir]):
118
+ return result
113
119
  elif valid_lean_project_path(current_dir):
114
- rel_path = activate_project(Path(current_dir), [current_dir])
115
- if rel_path is not None:
116
- return rel_path
120
+ if result := set_project_path(Path(current_dir), [current_dir]):
121
+ return result
117
122
  else:
118
- project_cache[current_dir] = ""
119
- prev_dir = current_dir
123
+ lifespan.project_cache[current_dir] = "" # Mark as checked
124
+
120
125
  current_dir = os.path.dirname(current_dir)
121
126
 
122
127
  return None
128
+
129
+
130
+ def setup_client_for_file(ctx: Context, file_path: str) -> str | None:
131
+ """Ensure the LSP client matches the file's Lean project and return its relative path."""
132
+ project_path = infer_project_path(ctx, file_path)
133
+ if project_path is None:
134
+ return None
135
+
136
+ startup_client(ctx)
137
+ return get_relative_file_path(project_path, file_path)
@@ -1,13 +1,6 @@
1
- from typing import Optional, Dict
1
+ from typing import Optional
2
2
  from pathlib import Path
3
3
 
4
- from mcp.server.fastmcp import Context
5
- from mcp.server.fastmcp.utilities.logging import get_logger
6
- from leanclient import LeanLSPClient
7
-
8
-
9
- logger = get_logger(__name__)
10
-
11
4
 
12
5
  def get_relative_file_path(lean_project_path: Path, file_path: str) -> Optional[str]:
13
6
  """Convert path relative to project path.
@@ -21,25 +14,24 @@ def get_relative_file_path(lean_project_path: Path, file_path: str) -> Optional[
21
14
  """
22
15
  file_path_obj = Path(file_path)
23
16
 
24
- # Check if absolute path
17
+ # Absolute path under project
25
18
  if file_path_obj.is_absolute() and file_path_obj.exists():
26
19
  try:
27
20
  return str(file_path_obj.relative_to(lean_project_path))
28
21
  except ValueError:
29
- # File is not in this project
30
22
  return None
31
23
 
32
- # Check if relative to project path
24
+ # Relative to project path
33
25
  path = lean_project_path / file_path
34
26
  if path.exists():
35
27
  return str(path.relative_to(lean_project_path))
36
28
 
37
- # Check if relative to CWD
29
+ # Relative to CWD, but only if inside project root
38
30
  cwd = Path.cwd()
39
31
  path = cwd / file_path
40
32
  if path.exists():
41
33
  try:
42
- return str(path.relative_to(lean_project_path))
34
+ return str(path.resolve().relative_to(lean_project_path))
43
35
  except ValueError:
44
36
  return None
45
37
 
@@ -55,46 +47,3 @@ def get_file_contents(abs_path: str) -> str:
55
47
  continue
56
48
  with open(abs_path, "r", encoding=None) as f:
57
49
  return f.read()
58
-
59
-
60
- def update_file(ctx: Context, rel_path: str) -> str:
61
- """Update the file contents in the context.
62
- Args:
63
- ctx (Context): Context object.
64
- rel_path (str): Relative file path.
65
-
66
- Returns:
67
- str: Updated file contents.
68
- """
69
- # Get file contents and hash
70
- abs_path = ctx.request_context.lifespan_context.lean_project_path / rel_path
71
- file_content = get_file_contents(str(abs_path))
72
- hashed_file = hash(file_content)
73
-
74
- # Check if file_contents have changed
75
- file_content_hashes: Dict[str, str] = (
76
- ctx.request_context.lifespan_context.file_content_hashes
77
- )
78
- if rel_path not in file_content_hashes:
79
- file_content_hashes[rel_path] = hashed_file
80
- return file_content
81
-
82
- elif hashed_file == file_content_hashes[rel_path]:
83
- return file_content
84
-
85
- # Update file_contents
86
- file_content_hashes[rel_path] = hashed_file
87
-
88
- # Reload file in LSP
89
- client: LeanLSPClient = ctx.request_context.lifespan_context.client
90
- try:
91
- client.close_files([rel_path])
92
- except FileNotFoundError as e:
93
- logger.warning(
94
- f"Attempted to close file {rel_path} that wasn't open in LSP client: {e}"
95
- )
96
- except Exception as e:
97
- logger.error(
98
- f"Unexpected error closing file {rel_path}: {type(e).__name__}: {e}"
99
- )
100
- return file_content
lean_lsp_mcp/server.py CHANGED
@@ -18,8 +18,12 @@ from mcp.server.fastmcp.utilities.logging import get_logger, configure_logging
18
18
  from mcp.server.auth.settings import AuthSettings
19
19
  from leanclient import LeanLSPClient, DocumentContentChange
20
20
 
21
- from lean_lsp_mcp.client_utils import setup_client_for_file, startup_client
22
- from lean_lsp_mcp.file_utils import get_file_contents, update_file
21
+ from lean_lsp_mcp.client_utils import (
22
+ setup_client_for_file,
23
+ startup_client,
24
+ infer_project_path,
25
+ )
26
+ from lean_lsp_mcp.file_utils import get_file_contents
23
27
  from lean_lsp_mcp.instructions import INSTRUCTIONS
24
28
  from lean_lsp_mcp.search_utils import check_ripgrep_status, lean_local_search
25
29
  from lean_lsp_mcp.utils import (
@@ -47,7 +51,6 @@ _RG_AVAILABLE, _RG_MESSAGE = check_ripgrep_status()
47
51
  class AppContext:
48
52
  lean_project_path: Path | None
49
53
  client: LeanLSPClient | None
50
- file_content_hashes: Dict[str, str]
51
54
  rate_limit: Dict[str, List[int]]
52
55
  lean_search_available: bool
53
56
 
@@ -64,7 +67,6 @@ async def app_lifespan(server: FastMCP) -> AsyncIterator[AppContext]:
64
67
  context = AppContext(
65
68
  lean_project_path=lean_project_path,
66
69
  client=None,
67
- file_content_hashes={},
68
70
  rate_limit={
69
71
  "leansearch": [],
70
72
  "loogle": [],
@@ -154,10 +156,7 @@ async def lsp_build(
154
156
  ctx.request_context.lifespan_context.lean_project_path = lean_project_path_obj
155
157
 
156
158
  if lean_project_path_obj is None:
157
- return (
158
- "Lean project path not known yet. Provide `lean_project_path` explicitly or call a "
159
- "tool that infers it (e.g. `lean_goal`) before running `lean_build`."
160
- )
159
+ return "Lean project path not known yet. Provide `lean_project_path` explicitly or call a tool that infers it (e.g. `lean_file_contents`) before running `lean_build`."
161
160
 
162
161
  build_output = ""
163
162
  try:
@@ -165,7 +164,6 @@ async def lsp_build(
165
164
  if client:
166
165
  ctx.request_context.lifespan_context.client = None
167
166
  client.close()
168
- ctx.request_context.lifespan_context.file_content_hashes.clear()
169
167
 
170
168
  if clean:
171
169
  subprocess.run(["lake", "clean"], cwd=lean_project_path_obj, check=False)
@@ -247,6 +245,10 @@ def file_contents(ctx: Context, file_path: str, annotate_lines: bool = True) ->
247
245
  Returns:
248
246
  str: File content or error msg
249
247
  """
248
+ # Infer project path but do not start a client
249
+ if file_path.endswith(".lean"):
250
+ infer_project_path(ctx, file_path) # Silently fails for non-project files
251
+
250
252
  try:
251
253
  data = get_file_contents(file_path)
252
254
  except FileNotFoundError:
@@ -266,13 +268,20 @@ def file_contents(ctx: Context, file_path: str, annotate_lines: bool = True) ->
266
268
 
267
269
 
268
270
  @mcp.tool("lean_diagnostic_messages")
269
- def diagnostic_messages(ctx: Context, file_path: str) -> List[str] | str:
271
+ def diagnostic_messages(
272
+ ctx: Context,
273
+ file_path: str,
274
+ start_line: Optional[int] = None,
275
+ end_line: Optional[int] = None,
276
+ ) -> List[str] | str:
270
277
  """Get all diagnostic msgs (errors, warnings, infos) for a Lean file.
271
278
 
272
279
  "no goals to be solved" means code may need removal.
273
280
 
274
281
  Args:
275
282
  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.
276
285
 
277
286
  Returns:
278
287
  List[str] | str: Diagnostic msgs or error msg
@@ -281,10 +290,22 @@ def diagnostic_messages(ctx: Context, file_path: str) -> List[str] | str:
281
290
  if not rel_path:
282
291
  return "Invalid Lean file path: Unable to start LSP server or load file"
283
292
 
284
- update_file(ctx, rel_path)
285
-
286
293
  client: LeanLSPClient = ctx.request_context.lifespan_context.client
287
- diagnostics = client.get_diagnostics(rel_path)
294
+
295
+ # leanclient requires both start_line and end_line to filter
296
+ # 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)
308
+
288
309
  return format_diagnostics(diagnostics)
289
310
 
290
311
 
@@ -309,8 +330,9 @@ def goal(ctx: Context, file_path: str, line: int, column: Optional[int] = None)
309
330
  if not rel_path:
310
331
  return "Invalid Lean file path: Unable to start LSP server or load file"
311
332
 
312
- content = update_file(ctx, rel_path)
313
333
  client: LeanLSPClient = ctx.request_context.lifespan_context.client
334
+ client.open_file(rel_path)
335
+ content = client.get_file_content(rel_path)
314
336
 
315
337
  if column is None:
316
338
  lines = content.splitlines()
@@ -355,14 +377,15 @@ def term_goal(
355
377
  if not rel_path:
356
378
  return "Invalid Lean file path: Unable to start LSP server or load file"
357
379
 
358
- content = update_file(ctx, rel_path)
380
+ client: LeanLSPClient = ctx.request_context.lifespan_context.client
381
+ client.open_file(rel_path)
382
+ content = client.get_file_content(rel_path)
359
383
  if column is None:
360
384
  lines = content.splitlines()
361
385
  if line < 1 or line > len(lines):
362
386
  return "Line number out of range. Try elsewhere?"
363
387
  column = len(content.splitlines()[line - 1])
364
388
 
365
- client: LeanLSPClient = ctx.request_context.lifespan_context.client
366
389
  term_goal = client.get_term_goal(rel_path, line - 1, column - 1)
367
390
  f_line = format_line(content, line, column)
368
391
  if term_goal is None:
@@ -389,8 +412,9 @@ def hover(ctx: Context, file_path: str, line: int, column: int) -> str:
389
412
  if not rel_path:
390
413
  return "Invalid Lean file path: Unable to start LSP server or load file"
391
414
 
392
- file_content = update_file(ctx, rel_path)
393
415
  client: LeanLSPClient = ctx.request_context.lifespan_context.client
416
+ client.open_file(rel_path)
417
+ file_content = client.get_file_content(rel_path)
394
418
  hover_info = client.get_hover(rel_path, line - 1, column - 1)
395
419
  if hover_info is None:
396
420
  f_line = format_line(file_content, line, column)
@@ -435,9 +459,10 @@ def completions(
435
459
  rel_path = setup_client_for_file(ctx, file_path)
436
460
  if not rel_path:
437
461
  return "Invalid Lean file path: Unable to start LSP server or load file"
438
- content = update_file(ctx, rel_path)
439
462
 
440
463
  client: LeanLSPClient = ctx.request_context.lifespan_context.client
464
+ client.open_file(rel_path)
465
+ content = client.get_file_content(rel_path)
441
466
  completions = client.get_completions(rel_path, line - 1, column - 1)
442
467
  formatted = [c["label"] for c in completions if "label" in c]
443
468
  f_line = format_line(content, line, column)
@@ -497,14 +522,16 @@ def declaration_file(ctx: Context, file_path: str, symbol: str) -> str:
497
522
  rel_path = setup_client_for_file(ctx, file_path)
498
523
  if not rel_path:
499
524
  return "Invalid Lean file path: Unable to start LSP server or load file"
500
- orig_file_content = update_file(ctx, rel_path)
525
+
526
+ client: LeanLSPClient = ctx.request_context.lifespan_context.client
527
+ client.open_file(rel_path)
528
+ orig_file_content = client.get_file_content(rel_path)
501
529
 
502
530
  # Find the first occurence of the symbol (line and column) in the file,
503
531
  position = find_start_position(orig_file_content, symbol)
504
532
  if not position:
505
533
  return f"Symbol `{symbol}` (case sensitive) not found in file `{rel_path}`. Add it first, then try again."
506
534
 
507
- client: LeanLSPClient = ctx.request_context.lifespan_context.client
508
535
  declaration = client.get_declarations(
509
536
  rel_path, position["line"], position["column"]
510
537
  )
@@ -552,8 +579,9 @@ def multi_attempt(
552
579
  rel_path = setup_client_for_file(ctx, file_path)
553
580
  if not rel_path:
554
581
  return "Invalid Lean file path: Unable to start LSP server or load file"
555
- update_file(ctx, rel_path)
582
+
556
583
  client: LeanLSPClient = ctx.request_context.lifespan_context.client
584
+ client.open_file(rel_path)
557
585
 
558
586
  try:
559
587
  client.open_file(rel_path)
@@ -604,7 +632,7 @@ def run_code(ctx: Context, code: str) -> List[str] | str:
604
632
  lifespan_context = ctx.request_context.lifespan_context
605
633
  lean_project_path = lifespan_context.lean_project_path
606
634
  if lean_project_path is None:
607
- return "No valid Lean project path found. Run another tool (e.g. `lean_diagnostic_messages`) first to set it up or set the LEAN_PROJECT_PATH environment variable."
635
+ return "No valid Lean project path found. Run another tool (e.g. `lean_file_contents`) first to set it up."
608
636
 
609
637
  # Use a unique snippet filename to avoid collisions under concurrency
610
638
  rel_path = f"_mcp_snippet_{uuid.uuid4().hex}.lean"
@@ -684,7 +712,7 @@ def local_search(
684
712
 
685
713
  stored_root = ctx.request_context.lifespan_context.lean_project_path
686
714
  if stored_root is None:
687
- return "Lean project path not set. Call a file-based tool (like lean_goal) first to set the project path."
715
+ return "Lean project path not set. Call a file-based tool (like lean_file_contents) first to set the project path."
688
716
 
689
717
  return lean_local_search(query=query.strip(), limit=limit, project_root=stored_root)
690
718
 
@@ -853,8 +881,9 @@ def state_search(
853
881
  if not rel_path:
854
882
  return "Invalid Lean file path: Unable to start LSP server or load file"
855
883
 
856
- file_contents = update_file(ctx, rel_path)
857
884
  client: LeanLSPClient = ctx.request_context.lifespan_context.client
885
+ client.open_file(rel_path)
886
+ file_contents = client.get_file_content(rel_path)
858
887
  goal = client.get_goal(rel_path, line - 1, column - 1)
859
888
 
860
889
  f_line = format_line(file_contents, line, column)
@@ -903,8 +932,9 @@ def hammer_premise(
903
932
  if not rel_path:
904
933
  return "Invalid Lean file path: Unable to start LSP server or load file"
905
934
 
906
- file_contents = update_file(ctx, rel_path)
907
935
  client: LeanLSPClient = ctx.request_context.lifespan_context.client
936
+ client.open_file(rel_path)
937
+ file_contents = client.get_file_content(rel_path)
908
938
  goal = client.get_goal(rel_path, line - 1, column - 1)
909
939
 
910
940
  f_line = format_line(file_contents, line, column)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: lean-lsp-mcp
3
- Version: 0.11.2
3
+ Version: 0.12.0
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.4.0
12
- Requires-Dist: mcp[cli]==1.19.0
11
+ Requires-Dist: leanclient==0.5.0
12
+ Requires-Dist: mcp[cli]==1.21.0
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=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,,
@@ -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=Z-2AoFBA36UO8oeauTNUtquE6Yz_ZRTKz_9n1iapyJ4,4519
4
- lean_lsp_mcp/file_utils.py,sha256=qddegF-T5-egZop8dPe_3Cma-3rRSKsAErVDQLecmbE,2916
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=knA8HsJZeUNnMqNdv2rnAnS__Eblr0aPnlwKH1FrLbA,34661
8
- lean_lsp_mcp/utils.py,sha256=zLu2VIhaX4yocY07F3Z94LB2jRGrkH1ID9SjR3poE9A,8255
9
- lean_lsp_mcp-0.11.2.dist-info/licenses/LICENSE,sha256=CQlxnf0tQyoVrBE93JYvAUYxv6Z5Yg6sX0pwogOkFvo,1071
10
- lean_lsp_mcp-0.11.2.dist-info/METADATA,sha256=2sGVG-wCFYATL2iKhQ7Twt_6jcvPumAk7P74y_2uCto,19626
11
- lean_lsp_mcp-0.11.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
12
- lean_lsp_mcp-0.11.2.dist-info/entry_points.txt,sha256=nQbvwctWkWD7I-2f4VrdVQBZYGUw8CnUnFC6QjXxOSE,51
13
- lean_lsp_mcp-0.11.2.dist-info/top_level.txt,sha256=LGEK0lgMSNPIQ6mG8EO-adaZEGPi_0daDs004epOTF0,13
14
- lean_lsp_mcp-0.11.2.dist-info/RECORD,,