lean-lsp-mcp 0.16.2__py3-none-any.whl → 0.17.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.
@@ -33,4 +33,7 @@ After finding a name: lean_local_search to verify, lean_hover_info for signature
33
33
 
34
34
  ## Return Formats
35
35
  List tools return JSON arrays. Empty = `[]`.
36
+
37
+ ## Error Handling
38
+ Check `isError` in responses: `true` means failure (timeout/LSP error), while `[]` with `isError: false` means no results found.
36
39
  """
lean_lsp_mcp/loogle.py CHANGED
@@ -3,6 +3,7 @@
3
3
  from __future__ import annotations
4
4
 
5
5
  import asyncio
6
+ import hashlib
6
7
  import json
7
8
  import logging
8
9
  import os
@@ -34,7 +35,7 @@ def loogle_remote(query: str, num_results: int) -> list[LoogleResult] | str:
34
35
  f"https://loogle.lean-lang.org/json?q={urllib.parse.quote(query)}",
35
36
  headers={"User-Agent": "lean-lsp-mcp/0.1"},
36
37
  )
37
- with urllib.request.urlopen(req, timeout=20) as response:
38
+ with urllib.request.urlopen(req, timeout=10) as response:
38
39
  results = orjson.loads(response.read())
39
40
  if "hits" not in results:
40
41
  return "No results found."
@@ -52,18 +53,25 @@ def loogle_remote(query: str, num_results: int) -> list[LoogleResult] | str:
52
53
 
53
54
 
54
55
  class LoogleManager:
55
- """Manages local loogle installation and async subprocess."""
56
+ """Manages local loogle installation and async subprocess.
57
+
58
+ Args:
59
+ cache_dir: Directory for loogle repo and indices (default: ~/.cache/lean-lsp-mcp/loogle)
60
+ project_path: Optional Lean project path to index its .lake/packages dependencies
61
+ """
56
62
 
57
63
  REPO_URL = "https://github.com/nomeata/loogle.git"
58
64
  READY_SIGNAL = "Loogle is ready."
59
65
 
60
- def __init__(self, cache_dir: Path | None = None):
66
+ def __init__(self, cache_dir: Path | None = None, project_path: Path | None = None):
61
67
  self.cache_dir = cache_dir or get_cache_dir()
62
68
  self.repo_dir = self.cache_dir / "repo"
63
69
  self.index_dir = self.cache_dir / "index"
70
+ self.project_path = project_path
64
71
  self.process: asyncio.subprocess.Process | None = None
65
72
  self._ready = False
66
73
  self._lock = asyncio.Lock()
74
+ self._extra_paths: list[Path] = []
67
75
 
68
76
  @property
69
77
  def binary_path(self) -> Path:
@@ -181,21 +189,54 @@ class LoogleManager:
181
189
  return False, err
182
190
  return True, ""
183
191
 
192
+ def _discover_project_paths(self) -> list[Path]:
193
+ """Find .lake/packages lib paths from the user's project."""
194
+ if not self.project_path:
195
+ return []
196
+ paths = []
197
+ # Check packages directory
198
+ lake_packages = self.project_path / ".lake" / "packages"
199
+ if lake_packages.exists():
200
+ for pkg_dir in lake_packages.iterdir():
201
+ if not pkg_dir.is_dir():
202
+ continue
203
+ lib_path = pkg_dir / ".lake" / "build" / "lib" / "lean"
204
+ if lib_path.exists():
205
+ paths.append(lib_path)
206
+ # Also add the project's own build output
207
+ project_lib = self.project_path / ".lake" / "build" / "lib" / "lean"
208
+ if project_lib.exists():
209
+ paths.append(project_lib)
210
+ return sorted(paths)
211
+
184
212
  def _get_index_path(self) -> Path:
185
- return self.index_dir / f"mathlib-{self._get_mathlib_version()}.idx"
213
+ base = f"mathlib-{self._get_mathlib_version()}"
214
+ if self._extra_paths:
215
+ # Include hash of extra paths for project-specific index
216
+ paths_str = ":".join(str(p) for p in sorted(self._extra_paths))
217
+ path_hash = hashlib.sha256(paths_str.encode()).hexdigest()[:8]
218
+ return self.index_dir / f"{base}-{path_hash}.idx"
219
+ return self.index_dir / f"{base}.idx"
186
220
 
187
221
  def _cleanup_old_indices(self) -> None:
188
- """Remove old index files from previous mathlib versions."""
222
+ """Remove old index files from previous mathlib versions.
223
+
224
+ Cleans up both mathlib-only indexes (mathlib-<version>.idx) and
225
+ project-specific indexes (mathlib-<version>-<hash>.idx) that don't
226
+ match the current mathlib version.
227
+ """
189
228
  if not self.index_dir.exists():
190
229
  return
191
- current = self._get_index_path()
230
+ current_mathlib = f"mathlib-{self._get_mathlib_version()}"
192
231
  for idx in self.index_dir.glob("*.idx"):
193
- if idx != current:
194
- try:
195
- idx.unlink()
196
- logger.info(f"Removed old index: {idx.name}")
197
- except Exception:
198
- pass
232
+ # Keep indexes with current mathlib version (both base and project-specific)
233
+ if idx.name.startswith(current_mathlib):
234
+ continue
235
+ try:
236
+ idx.unlink()
237
+ logger.info(f"Removed old index: {idx.name}")
238
+ except Exception:
239
+ pass
199
240
 
200
241
  def _build_index(self) -> Path | None:
201
242
  index_path = self._get_index_path()
@@ -205,17 +246,37 @@ class LoogleManager:
205
246
  return None
206
247
  self.index_dir.mkdir(parents=True, exist_ok=True)
207
248
  self._cleanup_old_indices()
208
- logger.info("Building search index...")
209
- try:
210
- self._run(
211
- [str(self.binary_path), "--write-index", str(index_path), "--json", ""],
212
- timeout=600,
249
+
250
+ # Build command with extra paths
251
+ cmd = [str(self.binary_path), "--write-index", str(index_path), "--json"]
252
+ for path in self._extra_paths:
253
+ cmd.extend(["--path", str(path)])
254
+ cmd.append("") # Empty query for index building
255
+
256
+ if self._extra_paths:
257
+ logger.info(
258
+ f"Building search index with {len(self._extra_paths)} extra paths..."
213
259
  )
260
+ else:
261
+ logger.info("Building search index...")
262
+ try:
263
+ self._run(cmd, timeout=600)
214
264
  return index_path if index_path.exists() else None
215
265
  except Exception as e:
216
266
  logger.error(f"Index build error: {e}")
217
267
  return None
218
268
 
269
+ def set_project_path(self, project_path: Path | None) -> bool:
270
+ """Update project path and rediscover extra paths. Returns True if paths changed."""
271
+ self.project_path = project_path
272
+ new_paths = self._discover_project_paths()
273
+ if new_paths != self._extra_paths:
274
+ self._extra_paths = new_paths
275
+ if new_paths:
276
+ logger.info(f"Discovered {len(new_paths)} project library paths")
277
+ return True
278
+ return False
279
+
219
280
  def ensure_installed(self) -> bool:
220
281
  ok, err = self._check_prerequisites()
221
282
  if not ok:
@@ -223,6 +284,10 @@ class LoogleManager:
223
284
  return False
224
285
  if not self._clone_repo() or not self._build_loogle():
225
286
  return False
287
+ # Discover project paths before building index
288
+ self._extra_paths = self._discover_project_paths()
289
+ if self._extra_paths:
290
+ logger.info(f"Indexing {len(self._extra_paths)} project library paths")
226
291
  if not self._build_index():
227
292
  logger.warning("Index build failed, loogle will build on startup")
228
293
  return self.is_installed
@@ -234,10 +299,26 @@ class LoogleManager:
234
299
  if not ok:
235
300
  logger.error(f"Loogle environment check failed: {err}")
236
301
  return False
302
+
303
+ # Check if project paths changed and we need to rebuild index
304
+ if self.project_path:
305
+ new_paths = self._discover_project_paths()
306
+ if new_paths != self._extra_paths:
307
+ self._extra_paths = new_paths
308
+ # Build new index if paths changed
309
+ self._build_index()
310
+
237
311
  cmd = [str(self.binary_path), "--json", "--interactive"]
238
312
  if (idx := self._get_index_path()).exists():
239
313
  cmd.extend(["--read-index", str(idx)])
240
- logger.info("Starting loogle subprocess...")
314
+ # Add extra paths for runtime search (in case not all are indexed)
315
+ for path in self._extra_paths:
316
+ cmd.extend(["--path", str(path)])
317
+
318
+ if self._extra_paths:
319
+ logger.info(f"Starting loogle with {len(self._extra_paths)} extra paths...")
320
+ else:
321
+ logger.info("Starting loogle subprocess...")
241
322
  try:
242
323
  self.process = await asyncio.create_subprocess_exec(
243
324
  *cmd,
lean_lsp_mcp/models.py CHANGED
@@ -46,7 +46,15 @@ class DiagnosticMessage(BaseModel):
46
46
 
47
47
  class GoalState(BaseModel):
48
48
  line_context: str = Field(description="Source line where goals were queried")
49
- goals: str = Field(description="Goal state (before→after if column omitted)")
49
+ goals: Optional[List[str]] = Field(
50
+ None, description="Goal list at specified column position"
51
+ )
52
+ goals_before: Optional[List[str]] = Field(
53
+ None, description="Goals at line start (when column omitted)"
54
+ )
55
+ goals_after: Optional[List[str]] = Field(
56
+ None, description="Goals at line end (when column omitted)"
57
+ )
50
58
 
51
59
 
52
60
  class CompletionItem(BaseModel):
@@ -94,8 +102,8 @@ class FileOutline(BaseModel):
94
102
 
95
103
  class AttemptResult(BaseModel):
96
104
  snippet: str = Field(description="Code snippet that was tried")
97
- goal_state: Optional[str] = Field(
98
- None, description="Goal state after applying snippet"
105
+ goals: List[str] = Field(
106
+ default_factory=list, description="Goal list after applying snippet"
99
107
  )
100
108
  diagnostics: List[DiagnosticMessage] = Field(
101
109
  default_factory=list, description="Diagnostics for this attempt"
lean_lsp_mcp/server.py CHANGED
@@ -1,82 +1,80 @@
1
1
  import asyncio
2
+ import functools
2
3
  import os
3
4
  import re
4
5
  import time
5
- from typing import Annotated, List, Optional, Dict
6
- from contextlib import asynccontextmanager
7
- from collections.abc import AsyncIterator
8
- from dataclasses import dataclass
9
6
  import urllib
10
- import orjson
11
- import functools
12
7
  import uuid
8
+ from collections.abc import AsyncIterator
9
+ from contextlib import asynccontextmanager
10
+ from dataclasses import dataclass
13
11
  from pathlib import Path
12
+ from typing import Annotated, Dict, List, Optional
14
13
 
15
- from pydantic import Field
16
- from mcp.server.fastmcp import Context, FastMCP
17
- from mcp.server.fastmcp.utilities.logging import get_logger, configure_logging
14
+ import orjson
15
+ from leanclient import DocumentContentChange, LeanLSPClient
18
16
  from mcp.server.auth.settings import AuthSettings
17
+ from mcp.server.fastmcp import Context, FastMCP
18
+ from mcp.server.fastmcp.utilities.logging import configure_logging, get_logger
19
19
  from mcp.types import ToolAnnotations
20
- from leanclient import LeanLSPClient, DocumentContentChange
20
+ from pydantic import Field
21
21
 
22
22
  from lean_lsp_mcp.client_utils import (
23
+ infer_project_path,
23
24
  setup_client_for_file,
24
25
  startup_client,
25
- infer_project_path,
26
26
  )
27
27
  from lean_lsp_mcp.file_utils import get_file_contents
28
28
  from lean_lsp_mcp.instructions import INSTRUCTIONS
29
- from lean_lsp_mcp.search_utils import check_ripgrep_status, lean_local_search
30
29
  from lean_lsp_mcp.loogle import LoogleManager, loogle_remote
31
- from lean_lsp_mcp.outline_utils import generate_outline_data
32
30
  from lean_lsp_mcp.models import (
33
- LocalSearchResult,
34
- LeanSearchResult,
35
- LoogleResult,
36
- LeanFinderResult,
37
- StateSearchResult,
38
- PremiseResult,
39
- DiagnosticMessage,
40
- GoalState,
41
- CompletionItem,
42
- HoverInfo,
43
- TermGoalState,
44
- FileOutline,
45
31
  AttemptResult,
46
32
  BuildResult,
47
- RunResult,
33
+ CompletionItem,
34
+ CompletionsResult,
48
35
  DeclarationInfo,
36
+ DiagnosticMessage,
49
37
  # Wrapper models for list-returning tools
50
38
  DiagnosticsResult,
51
- CompletionsResult,
52
- MultiAttemptResult,
53
- LocalSearchResults,
39
+ FileOutline,
40
+ GoalState,
41
+ HoverInfo,
42
+ LeanFinderResult,
43
+ LeanFinderResults,
44
+ LeanSearchResult,
54
45
  LeanSearchResults,
46
+ LocalSearchResult,
47
+ LocalSearchResults,
48
+ LoogleResult,
55
49
  LoogleResults,
56
- LeanFinderResults,
57
- StateSearchResults,
50
+ MultiAttemptResult,
51
+ PremiseResult,
58
52
  PremiseResults,
53
+ RunResult,
54
+ StateSearchResult,
55
+ StateSearchResults,
56
+ TermGoalState,
59
57
  )
58
+ from lean_lsp_mcp.outline_utils import generate_outline_data
59
+ from lean_lsp_mcp.search_utils import check_ripgrep_status, lean_local_search
60
60
  from lean_lsp_mcp.utils import (
61
61
  COMPLETION_KIND,
62
+ LeanToolError,
63
+ OptionalTokenVerifier,
62
64
  OutputCapture,
65
+ check_lsp_response,
63
66
  deprecated,
67
+ extract_goals_list,
64
68
  extract_range,
65
69
  filter_diagnostics_by_position,
66
70
  find_start_position,
67
- format_goal,
68
71
  get_declaration_range,
69
- OptionalTokenVerifier,
70
72
  )
71
73
 
72
74
  # LSP Diagnostic severity: 1=error, 2=warning, 3=info, 4=hint
73
75
  DIAGNOSTIC_SEVERITY: Dict[int, str] = {1: "error", 2: "warning", 3: "info", 4: "hint"}
74
76
 
75
77
 
76
- class LeanToolError(Exception):
77
- pass
78
-
79
-
80
78
  _LOG_LEVEL = os.environ.get("LEAN_LOG_LEVEL", "INFO")
81
79
  configure_logging("CRITICAL" if _LOG_LEVEL == "NONE" else _LOG_LEVEL)
82
80
  logger = get_logger(__name__)
@@ -110,7 +108,7 @@ async def app_lifespan(server: FastMCP) -> AsyncIterator[AppContext]:
110
108
  # Initialize local loogle if enabled via env var or CLI
111
109
  if os.environ.get("LEAN_LOOGLE_LOCAL", "").lower() in ("1", "true", "yes"):
112
110
  logger.info("Local loogle enabled, initializing...")
113
- loogle_manager = LoogleManager()
111
+ loogle_manager = LoogleManager(project_path=lean_project_path)
114
112
  if loogle_manager.ensure_installed():
115
113
  if await loogle_manager.start():
116
114
  loogle_local_available = True
@@ -445,6 +443,7 @@ def diagnostic_messages(
445
443
  end_line=end_line_0,
446
444
  inactivity_timeout=15.0,
447
445
  )
446
+ check_lsp_response(diagnostics, "get_diagnostics")
448
447
 
449
448
  return DiagnosticsResult(items=_to_diagnostic_messages(diagnostics))
450
449
 
@@ -494,15 +493,18 @@ def goal(
494
493
  (i for i, c in enumerate(line_context) if not c.isspace()), 0
495
494
  )
496
495
  goal_start = client.get_goal(rel_path, line - 1, column_start)
496
+ check_lsp_response(goal_start, "get_goal", allow_none=True)
497
497
  goal_end = client.get_goal(rel_path, line - 1, column_end)
498
- before = format_goal(goal_start, None)
499
- after = format_goal(goal_end, None)
500
- goals = f"{before} → {after}" if before != after else after
501
- return GoalState(line_context=line_context, goals=goals)
498
+ return GoalState(
499
+ line_context=line_context,
500
+ goals_before=extract_goals_list(goal_start),
501
+ goals_after=extract_goals_list(goal_end),
502
+ )
502
503
  else:
503
504
  goal_result = client.get_goal(rel_path, line - 1, column - 1)
505
+ check_lsp_response(goal_result, "get_goal", allow_none=True)
504
506
  return GoalState(
505
- line_context=line_context, goals=format_goal(goal_result, None)
507
+ line_context=line_context, goals=extract_goals_list(goal_result)
506
508
  )
507
509
 
508
510
 
@@ -543,6 +545,7 @@ def term_goal(
543
545
  column = len(line_context)
544
546
 
545
547
  term_goal_result = client.get_term_goal(rel_path, line - 1, column - 1)
548
+ check_lsp_response(term_goal_result, "get_term_goal", allow_none=True)
546
549
  expected_type = None
547
550
  if term_goal_result is not None:
548
551
  rendered = term_goal_result.get("goal")
@@ -578,6 +581,7 @@ def hover(
578
581
  client.open_file(rel_path)
579
582
  file_content = client.get_file_content(rel_path)
580
583
  hover_info = client.get_hover(rel_path, line - 1, column - 1)
584
+ check_lsp_response(hover_info, "get_hover", allow_none=True)
581
585
  if hover_info is None:
582
586
  raise LeanToolError(f"No hover information at line {line}, column {column}")
583
587
 
@@ -589,6 +593,7 @@ def hover(
589
593
 
590
594
  # Add diagnostics if available
591
595
  diagnostics = client.get_diagnostics(rel_path)
596
+ check_lsp_response(diagnostics, "get_diagnostics")
592
597
  filtered = filter_diagnostics_by_position(diagnostics, line - 1, column - 1)
593
598
 
594
599
  return HoverInfo(
@@ -625,6 +630,7 @@ def completions(
625
630
  client.open_file(rel_path)
626
631
  content = client.get_file_content(rel_path)
627
632
  raw_completions = client.get_completions(rel_path, line - 1, column - 1)
633
+ check_lsp_response(raw_completions, "get_completions")
628
634
 
629
635
  # Convert to CompletionItem models
630
636
  items: List[CompletionItem] = []
@@ -770,14 +776,15 @@ def multi_attempt(
770
776
  # Apply the change to the file, capture diagnostics and goal state
771
777
  client.update_file(rel_path, [change])
772
778
  diag = client.get_diagnostics(rel_path)
779
+ check_lsp_response(diag, "get_diagnostics")
773
780
  filtered_diag = filter_diagnostics_by_position(diag, line - 1, None)
774
781
  # Use the snippet text length without any trailing newline for the column
775
782
  goal_result = client.get_goal(rel_path, line - 1, len(snippet_str))
776
- goal_state = format_goal(goal_result, None)
783
+ goals = extract_goals_list(goal_result)
777
784
  results.append(
778
785
  AttemptResult(
779
786
  snippet=snippet_str,
780
- goal_state=goal_state,
787
+ goals=goals,
781
788
  diagnostics=_to_diagnostic_messages(filtered_diag),
782
789
  )
783
790
  )
@@ -838,6 +845,7 @@ def run_code(
838
845
  client.open_file(rel_path)
839
846
  opened_file = True
840
847
  raw_diagnostics = client.get_diagnostics(rel_path, inactivity_timeout=15.0)
848
+ check_lsp_response(raw_diagnostics, "get_diagnostics")
841
849
  finally:
842
850
  if opened_file:
843
851
  try:
@@ -946,7 +954,7 @@ def leansearch(
946
954
  method="POST",
947
955
  )
948
956
 
949
- with urllib.request.urlopen(req, timeout=20) as response:
957
+ with urllib.request.urlopen(req, timeout=10) as response:
950
958
  results = orjson.loads(response.read())
951
959
 
952
960
  if not results or not results[0]:
@@ -990,6 +998,11 @@ async def loogle(
990
998
 
991
999
  # Try local loogle first if available (no rate limiting)
992
1000
  if app_ctx.loogle_local_available and app_ctx.loogle_manager:
1001
+ # Update project path if it changed (adds new library paths)
1002
+ if app_ctx.lean_project_path != app_ctx.loogle_manager.project_path:
1003
+ if app_ctx.loogle_manager.set_project_path(app_ctx.lean_project_path):
1004
+ # Restart to pick up new paths
1005
+ await app_ctx.loogle_manager.stop()
993
1006
  try:
994
1007
  results = await app_ctx.loogle_manager.query(query, num_results)
995
1008
  if not results:
@@ -1050,7 +1063,7 @@ def leanfinder(
1050
1063
  )
1051
1064
 
1052
1065
  results: List[LeanFinderResult] = []
1053
- with urllib.request.urlopen(req, timeout=30) as response:
1066
+ with urllib.request.urlopen(req, timeout=10) as response:
1054
1067
  data = orjson.loads(response.read())
1055
1068
  for result in data["results"]:
1056
1069
  if (
@@ -1113,7 +1126,7 @@ def state_search(
1113
1126
  method="GET",
1114
1127
  )
1115
1128
 
1116
- with urllib.request.urlopen(req, timeout=20) as response:
1129
+ with urllib.request.urlopen(req, timeout=10) as response:
1117
1130
  results = orjson.loads(response.read())
1118
1131
 
1119
1132
  items = [StateSearchResult(name=r["name"]) for r in results]
@@ -1173,7 +1186,7 @@ def hammer_premise(
1173
1186
  data=orjson.dumps(data),
1174
1187
  )
1175
1188
 
1176
- with urllib.request.urlopen(req, timeout=20) as response:
1189
+ with urllib.request.urlopen(req, timeout=10) as response:
1177
1190
  results = orjson.loads(response.read())
1178
1191
 
1179
1192
  items = [PremiseResult(name=r["name"]) for r in results]
lean_lsp_mcp/utils.py CHANGED
@@ -2,11 +2,39 @@ import os
2
2
  import secrets
3
3
  import sys
4
4
  import tempfile
5
- from typing import List, Dict, Optional, Callable
5
+ from typing import Any, List, Dict, Optional, Callable
6
6
 
7
7
  from mcp.server.auth.provider import AccessToken, TokenVerifier
8
8
 
9
9
 
10
+ class LeanToolError(Exception):
11
+ """Exception raised when a Lean MCP tool operation fails."""
12
+
13
+ pass
14
+
15
+
16
+ def check_lsp_response(
17
+ response: Any, operation: str, *, allow_none: bool = False
18
+ ) -> Any:
19
+ """Check an LSP response for error patterns and raise if found.
20
+
21
+ Args:
22
+ response: The response from a leanclient LSP operation
23
+ operation: Human-readable description of the operation
24
+ allow_none: If False (default), None raises LeanToolError (timeout).
25
+ If True, None is allowed (for operations where None is valid).
26
+
27
+ Raises:
28
+ LeanToolError: If response indicates failure
29
+ """
30
+ if response is None and not allow_none:
31
+ raise LeanToolError(f"LSP timeout during {operation}")
32
+ if isinstance(response, dict) and "error" in response:
33
+ msg = response["error"].get("message", "unknown error")
34
+ raise LeanToolError(f"LSP error during {operation}: {msg}")
35
+ return response
36
+
37
+
10
38
  class OutputCapture:
11
39
  """Capture any output to stdout and stderr at the file descriptor level."""
12
40
 
@@ -80,11 +108,11 @@ def format_diagnostics(diagnostics: List[Dict], select_line: int = -1) -> List[s
80
108
  return msgs
81
109
 
82
110
 
83
- def format_goal(goal, default_msg):
84
- if goal is None:
85
- return default_msg
86
- rendered = goal.get("rendered")
87
- return rendered.replace("```lean\n", "").replace("\n```", "") if rendered else None
111
+ def extract_goals_list(goal_response: dict | None) -> List[str]:
112
+ """Extract goals list from LSP response, returning empty list if no goals."""
113
+ if goal_response is None:
114
+ return []
115
+ return goal_response.get("goals", [])
88
116
 
89
117
 
90
118
  def _utf16_index_to_py_index(text: str, utf16_index: int) -> int | None:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: lean-lsp-mcp
3
- Version: 0.16.2
3
+ Version: 0.17.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.6.2
12
- Requires-Dist: mcp[cli]==1.24.0
11
+ Requires-Dist: leanclient==0.8.0
12
+ Requires-Dist: mcp[cli]==1.25.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,17 @@
1
+ lean_lsp_mcp/__init__.py,sha256=MN_bNFyb5-p33JWWGbrlUYBd1UUMQKtZYGC9KCh2mtM,1403
2
+ lean_lsp_mcp/__main__.py,sha256=XnpTzfJc0T-j9tHtdkA8ovTr1c139ffTewcJGhxYDaM,49
3
+ lean_lsp_mcp/client_utils.py,sha256=HgPuB35rMitn2Xm8SCAErsFLq15trB6VMz3FDFgmPd8,4897
4
+ lean_lsp_mcp/file_utils.py,sha256=kCTYQSfmV-R2cm_NCi_L8W5Dcsm0_rTOPpTtpyAin78,1365
5
+ lean_lsp_mcp/instructions.py,sha256=iJk_oD67tqNaC8K5OXEuXafULKSbbiHjZAsSRebOwdw,1904
6
+ lean_lsp_mcp/loogle.py,sha256=zUgnDWoTIqa4G6GXStAIxxJUR545YbU8Z-8KMjddKV0,15500
7
+ lean_lsp_mcp/models.py,sha256=2pLmvNsrMdn4vO1k119Jw8gqYeaeGKewWW0q1TabBCY,6604
8
+ lean_lsp_mcp/outline_utils.py,sha256=-eoZNbx2eaKaYmuyFJnwUMWP8I9YXNWusue_2OYpDBM,10981
9
+ lean_lsp_mcp/search_utils.py,sha256=MLqKGe4bhEvyfFLIBCmiDxkbcH4O5J3vl9mWnRSb_v0,6801
10
+ lean_lsp_mcp/server.py,sha256=1G7-FZz1TNJ7uU36eSTm9yponWyMO3QhQiyOhhTNOH8,40626
11
+ lean_lsp_mcp/utils.py,sha256=MmGgdhrLEvCtRRVNgN_vflO9_A25h76QbJXhBD-OKt0,12721
12
+ lean_lsp_mcp-0.17.1.dist-info/licenses/LICENSE,sha256=CQlxnf0tQyoVrBE93JYvAUYxv6Z5Yg6sX0pwogOkFvo,1071
13
+ lean_lsp_mcp-0.17.1.dist-info/METADATA,sha256=K0Ygues0l2TNEY6nXV4uyEds2QZfMV2DQ0kwId1uBHQ,20787
14
+ lean_lsp_mcp-0.17.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
15
+ lean_lsp_mcp-0.17.1.dist-info/entry_points.txt,sha256=nQbvwctWkWD7I-2f4VrdVQBZYGUw8CnUnFC6QjXxOSE,51
16
+ lean_lsp_mcp-0.17.1.dist-info/top_level.txt,sha256=LGEK0lgMSNPIQ6mG8EO-adaZEGPi_0daDs004epOTF0,13
17
+ lean_lsp_mcp-0.17.1.dist-info/RECORD,,
@@ -1,17 +0,0 @@
1
- lean_lsp_mcp/__init__.py,sha256=MN_bNFyb5-p33JWWGbrlUYBd1UUMQKtZYGC9KCh2mtM,1403
2
- lean_lsp_mcp/__main__.py,sha256=XnpTzfJc0T-j9tHtdkA8ovTr1c139ffTewcJGhxYDaM,49
3
- lean_lsp_mcp/client_utils.py,sha256=HgPuB35rMitn2Xm8SCAErsFLq15trB6VMz3FDFgmPd8,4897
4
- lean_lsp_mcp/file_utils.py,sha256=kCTYQSfmV-R2cm_NCi_L8W5Dcsm0_rTOPpTtpyAin78,1365
5
- lean_lsp_mcp/instructions.py,sha256=S1y834V8v-SFSYJlxxy6Dj-Z0szMyEBT5SkEyM6Npr8,1756
6
- lean_lsp_mcp/loogle.py,sha256=ChybtPM8jOxP8s28358yNqcLiYvGlQqkAEFFLzR87Zw,11971
7
- lean_lsp_mcp/models.py,sha256=gDfyAX09YzKtjpKzuo6JtA2mNDc9pRWJ7iT44nHwi94,6326
8
- lean_lsp_mcp/outline_utils.py,sha256=-eoZNbx2eaKaYmuyFJnwUMWP8I9YXNWusue_2OYpDBM,10981
9
- lean_lsp_mcp/search_utils.py,sha256=MLqKGe4bhEvyfFLIBCmiDxkbcH4O5J3vl9mWnRSb_v0,6801
10
- lean_lsp_mcp/server.py,sha256=AvjzoS8lwomUtIP2wrBln4z28-cXzCf1hNgXd9O1w4E,39749
11
- lean_lsp_mcp/utils.py,sha256=355kzyB3dkwU7_4Mfcg--JXEorFaE2gtqs6-HbH5rRE,11722
12
- lean_lsp_mcp-0.16.2.dist-info/licenses/LICENSE,sha256=CQlxnf0tQyoVrBE93JYvAUYxv6Z5Yg6sX0pwogOkFvo,1071
13
- lean_lsp_mcp-0.16.2.dist-info/METADATA,sha256=Wrhb1l5m-Up77bQyegUdesd0k1ryWhe0C6dIHIWJ5mM,20787
14
- lean_lsp_mcp-0.16.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
15
- lean_lsp_mcp-0.16.2.dist-info/entry_points.txt,sha256=nQbvwctWkWD7I-2f4VrdVQBZYGUw8CnUnFC6QjXxOSE,51
16
- lean_lsp_mcp-0.16.2.dist-info/top_level.txt,sha256=LGEK0lgMSNPIQ6mG8EO-adaZEGPi_0daDs004epOTF0,13
17
- lean_lsp_mcp-0.16.2.dist-info/RECORD,,