lean-lsp-mcp 0.17.1__py3-none-any.whl → 0.18.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/models.py +8 -1
- lean_lsp_mcp/outline_utils.py +5 -1
- lean_lsp_mcp/server.py +145 -38
- lean_lsp_mcp/utils.py +25 -0
- {lean_lsp_mcp-0.17.1.dist-info → lean_lsp_mcp-0.18.0.dist-info}/METADATA +3 -2
- lean_lsp_mcp-0.18.0.dist-info/RECORD +17 -0
- lean_lsp_mcp-0.17.1.dist-info/RECORD +0 -17
- {lean_lsp_mcp-0.17.1.dist-info → lean_lsp_mcp-0.18.0.dist-info}/WHEEL +0 -0
- {lean_lsp_mcp-0.17.1.dist-info → lean_lsp_mcp-0.18.0.dist-info}/entry_points.txt +0 -0
- {lean_lsp_mcp-0.17.1.dist-info → lean_lsp_mcp-0.18.0.dist-info}/licenses/LICENSE +0 -0
- {lean_lsp_mcp-0.17.1.dist-info → lean_lsp_mcp-0.18.0.dist-info}/top_level.txt +0 -0
lean_lsp_mcp/models.py
CHANGED
|
@@ -134,11 +134,18 @@ class DeclarationInfo(BaseModel):
|
|
|
134
134
|
|
|
135
135
|
|
|
136
136
|
class DiagnosticsResult(BaseModel):
|
|
137
|
-
"""Wrapper for diagnostic messages list."""
|
|
137
|
+
"""Wrapper for diagnostic messages list with build status."""
|
|
138
138
|
|
|
139
|
+
success: bool = Field(
|
|
140
|
+
True, description="True if the queried file/range has no errors"
|
|
141
|
+
)
|
|
139
142
|
items: List[DiagnosticMessage] = Field(
|
|
140
143
|
default_factory=list, description="List of diagnostic messages"
|
|
141
144
|
)
|
|
145
|
+
failed_dependencies: List[str] = Field(
|
|
146
|
+
default_factory=list,
|
|
147
|
+
description="File paths of dependencies that failed to build",
|
|
148
|
+
)
|
|
142
149
|
|
|
143
150
|
|
|
144
151
|
class CompletionsResult(BaseModel):
|
lean_lsp_mcp/outline_utils.py
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import re
|
|
2
2
|
from typing import Dict, List, Optional, Tuple
|
|
3
|
+
|
|
3
4
|
from leanclient import LeanLSPClient
|
|
4
5
|
from leanclient.utils import DocumentContentChange
|
|
5
6
|
|
|
6
7
|
from lean_lsp_mcp.models import FileOutline, OutlineEntry
|
|
7
8
|
|
|
8
|
-
|
|
9
9
|
METHOD_KIND = {6, "method"}
|
|
10
10
|
KIND_TAGS = {"namespace": "Ns"}
|
|
11
11
|
|
|
@@ -41,6 +41,10 @@ def _get_info_trees(
|
|
|
41
41
|
for line in sorted(symbol_by_line.keys(), reverse=True)
|
|
42
42
|
],
|
|
43
43
|
)
|
|
44
|
+
|
|
45
|
+
# Force file reload to reset diagnostics after the insert/revert cycle.
|
|
46
|
+
client.open_file(path, force_reopen=True)
|
|
47
|
+
|
|
44
48
|
return info_trees
|
|
45
49
|
|
|
46
50
|
|
lean_lsp_mcp/server.py
CHANGED
|
@@ -64,17 +64,38 @@ from lean_lsp_mcp.utils import (
|
|
|
64
64
|
OutputCapture,
|
|
65
65
|
check_lsp_response,
|
|
66
66
|
deprecated,
|
|
67
|
+
extract_failed_dependency_paths,
|
|
67
68
|
extract_goals_list,
|
|
68
69
|
extract_range,
|
|
69
70
|
filter_diagnostics_by_position,
|
|
70
71
|
find_start_position,
|
|
71
72
|
get_declaration_range,
|
|
73
|
+
is_build_stderr,
|
|
72
74
|
)
|
|
73
75
|
|
|
74
76
|
# LSP Diagnostic severity: 1=error, 2=warning, 3=info, 4=hint
|
|
75
77
|
DIAGNOSTIC_SEVERITY: Dict[int, str] = {1: "error", 2: "warning", 3: "info", 4: "hint"}
|
|
76
78
|
|
|
77
79
|
|
|
80
|
+
async def _urlopen_json(req: urllib.request.Request, timeout: float):
|
|
81
|
+
"""Run urllib.request.urlopen in a worker thread to avoid blocking the event loop."""
|
|
82
|
+
|
|
83
|
+
def _do_request():
|
|
84
|
+
with urllib.request.urlopen(req, timeout=timeout) as response:
|
|
85
|
+
return orjson.loads(response.read())
|
|
86
|
+
|
|
87
|
+
return await asyncio.to_thread(_do_request)
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
async def _safe_report_progress(
|
|
91
|
+
ctx: Context, *, progress: int, total: int, message: str
|
|
92
|
+
) -> None:
|
|
93
|
+
try:
|
|
94
|
+
await ctx.report_progress(progress=progress, total=total, message=message)
|
|
95
|
+
except Exception:
|
|
96
|
+
return
|
|
97
|
+
|
|
98
|
+
|
|
78
99
|
_LOG_LEVEL = os.environ.get("LEAN_LOG_LEVEL", "INFO")
|
|
79
100
|
configure_logging("CRITICAL" if _LOG_LEVEL == "NONE" else _LOG_LEVEL)
|
|
80
101
|
logger = get_logger(__name__)
|
|
@@ -164,8 +185,7 @@ mcp = FastMCP(**mcp_kwargs)
|
|
|
164
185
|
|
|
165
186
|
def rate_limited(category: str, max_requests: int, per_seconds: int):
|
|
166
187
|
def decorator(func):
|
|
167
|
-
|
|
168
|
-
def wrapper(*args, **kwargs):
|
|
188
|
+
def _apply_rate_limit(args, kwargs):
|
|
169
189
|
ctx = kwargs.get("ctx")
|
|
170
190
|
if ctx is None:
|
|
171
191
|
if not args:
|
|
@@ -181,11 +201,33 @@ def rate_limited(category: str, max_requests: int, per_seconds: int):
|
|
|
181
201
|
if timestamp > current_time - per_seconds
|
|
182
202
|
]
|
|
183
203
|
if len(rate_limit[category]) >= max_requests:
|
|
184
|
-
return
|
|
204
|
+
return (
|
|
205
|
+
False,
|
|
206
|
+
f"Tool limit exceeded: {max_requests} requests per {per_seconds} s. Try again later.",
|
|
207
|
+
)
|
|
185
208
|
rate_limit[category].append(current_time)
|
|
186
|
-
return
|
|
209
|
+
return True, None
|
|
210
|
+
|
|
211
|
+
if asyncio.iscoroutinefunction(func):
|
|
212
|
+
|
|
213
|
+
@functools.wraps(func)
|
|
214
|
+
async def wrapper(*args, **kwargs):
|
|
215
|
+
allowed, msg = _apply_rate_limit(args, kwargs)
|
|
216
|
+
if not allowed:
|
|
217
|
+
return msg
|
|
218
|
+
return await func(*args, **kwargs)
|
|
219
|
+
|
|
220
|
+
else:
|
|
187
221
|
|
|
188
|
-
|
|
222
|
+
@functools.wraps(func)
|
|
223
|
+
def wrapper(*args, **kwargs):
|
|
224
|
+
allowed, msg = _apply_rate_limit(args, kwargs)
|
|
225
|
+
if not allowed:
|
|
226
|
+
return msg
|
|
227
|
+
return func(*args, **kwargs)
|
|
228
|
+
|
|
229
|
+
doc = wrapper.__doc__ or ""
|
|
230
|
+
wrapper.__doc__ = f"Limit: {max_requests}req/{per_seconds}s. {doc}"
|
|
189
231
|
return wrapper
|
|
190
232
|
|
|
191
233
|
return decorator
|
|
@@ -375,6 +417,7 @@ def file_outline(
|
|
|
375
417
|
|
|
376
418
|
|
|
377
419
|
def _to_diagnostic_messages(diagnostics: List[Dict]) -> List[DiagnosticMessage]:
|
|
420
|
+
"""Convert LSP diagnostics to DiagnosticMessage models."""
|
|
378
421
|
result = []
|
|
379
422
|
for diag in diagnostics:
|
|
380
423
|
r = diag.get("fullRange", diag.get("range"))
|
|
@@ -394,6 +437,52 @@ def _to_diagnostic_messages(diagnostics: List[Dict]) -> List[DiagnosticMessage]:
|
|
|
394
437
|
return result
|
|
395
438
|
|
|
396
439
|
|
|
440
|
+
def _process_diagnostics(
|
|
441
|
+
diagnostics: List[Dict], build_success: bool
|
|
442
|
+
) -> DiagnosticsResult:
|
|
443
|
+
"""Process diagnostics, extracting dependency paths from build stderr.
|
|
444
|
+
|
|
445
|
+
Args:
|
|
446
|
+
diagnostics: List of diagnostic dicts from leanclient
|
|
447
|
+
build_success: Whether the build succeeded (from leanclient.DiagnosticsResult.success)
|
|
448
|
+
"""
|
|
449
|
+
items = []
|
|
450
|
+
failed_deps: List[str] = []
|
|
451
|
+
|
|
452
|
+
for diag in diagnostics:
|
|
453
|
+
r = diag.get("fullRange", diag.get("range"))
|
|
454
|
+
if r is None:
|
|
455
|
+
continue
|
|
456
|
+
|
|
457
|
+
severity_int = diag.get("severity", 1)
|
|
458
|
+
message = diag.get("message", "")
|
|
459
|
+
line = r["start"]["line"] + 1
|
|
460
|
+
column = r["start"]["character"] + 1
|
|
461
|
+
|
|
462
|
+
# Check if this is a build failure at (1,1) - extract dependency paths, skip the item
|
|
463
|
+
if line == 1 and column == 1 and is_build_stderr(message):
|
|
464
|
+
failed_deps = extract_failed_dependency_paths(message)
|
|
465
|
+
continue # Don't include the build stderr blob as a diagnostic item
|
|
466
|
+
|
|
467
|
+
# Normal diagnostic from the queried file
|
|
468
|
+
items.append(
|
|
469
|
+
DiagnosticMessage(
|
|
470
|
+
severity=DIAGNOSTIC_SEVERITY.get(
|
|
471
|
+
severity_int, f"unknown({severity_int})"
|
|
472
|
+
),
|
|
473
|
+
message=message,
|
|
474
|
+
line=line,
|
|
475
|
+
column=column,
|
|
476
|
+
)
|
|
477
|
+
)
|
|
478
|
+
|
|
479
|
+
return DiagnosticsResult(
|
|
480
|
+
success=build_success,
|
|
481
|
+
items=items,
|
|
482
|
+
failed_dependencies=failed_deps,
|
|
483
|
+
)
|
|
484
|
+
|
|
485
|
+
|
|
397
486
|
@mcp.tool(
|
|
398
487
|
"lean_diagnostic_messages",
|
|
399
488
|
annotations=ToolAnnotations(
|
|
@@ -437,15 +526,14 @@ def diagnostic_messages(
|
|
|
437
526
|
start_line_0 = (start_line - 1) if start_line is not None else None
|
|
438
527
|
end_line_0 = (end_line - 1) if end_line is not None else None
|
|
439
528
|
|
|
440
|
-
|
|
529
|
+
result = client.get_diagnostics(
|
|
441
530
|
rel_path,
|
|
442
531
|
start_line=start_line_0,
|
|
443
532
|
end_line=end_line_0,
|
|
444
533
|
inactivity_timeout=15.0,
|
|
445
534
|
)
|
|
446
|
-
check_lsp_response(diagnostics, "get_diagnostics")
|
|
447
535
|
|
|
448
|
-
return
|
|
536
|
+
return _process_diagnostics(result.diagnostics, result.success)
|
|
449
537
|
|
|
450
538
|
|
|
451
539
|
@mcp.tool(
|
|
@@ -880,7 +968,7 @@ class LocalSearchError(Exception):
|
|
|
880
968
|
openWorldHint=False,
|
|
881
969
|
),
|
|
882
970
|
)
|
|
883
|
-
def local_search(
|
|
971
|
+
async def local_search(
|
|
884
972
|
ctx: Context,
|
|
885
973
|
query: Annotated[str, Field(description="Declaration name or prefix")],
|
|
886
974
|
limit: Annotated[int, Field(description="Max matches", ge=1)] = 10,
|
|
@@ -912,8 +1000,11 @@ def local_search(
|
|
|
912
1000
|
)
|
|
913
1001
|
|
|
914
1002
|
try:
|
|
915
|
-
raw_results =
|
|
916
|
-
|
|
1003
|
+
raw_results = await asyncio.to_thread(
|
|
1004
|
+
lean_local_search,
|
|
1005
|
+
query=query.strip(),
|
|
1006
|
+
limit=limit,
|
|
1007
|
+
project_root=resolved_root,
|
|
917
1008
|
)
|
|
918
1009
|
results = [
|
|
919
1010
|
LocalSearchResult(name=r["name"], kind=r["kind"], file=r["file"])
|
|
@@ -934,7 +1025,7 @@ def local_search(
|
|
|
934
1025
|
),
|
|
935
1026
|
)
|
|
936
1027
|
@rate_limited("leansearch", max_requests=3, per_seconds=30)
|
|
937
|
-
def leansearch(
|
|
1028
|
+
async def leansearch(
|
|
938
1029
|
ctx: Context,
|
|
939
1030
|
query: Annotated[str, Field(description="Natural language or Lean term query")],
|
|
940
1031
|
num_results: Annotated[int, Field(description="Max results", ge=1)] = 5,
|
|
@@ -954,8 +1045,10 @@ def leansearch(
|
|
|
954
1045
|
method="POST",
|
|
955
1046
|
)
|
|
956
1047
|
|
|
957
|
-
|
|
958
|
-
|
|
1048
|
+
await _safe_report_progress(
|
|
1049
|
+
ctx, progress=1, total=10, message="Awaiting response from leansearch.net"
|
|
1050
|
+
)
|
|
1051
|
+
results = await _urlopen_json(req, timeout=10)
|
|
959
1052
|
|
|
960
1053
|
if not results or not results[0]:
|
|
961
1054
|
return LeanSearchResults(items=[])
|
|
@@ -1029,7 +1122,13 @@ async def loogle(
|
|
|
1029
1122
|
)
|
|
1030
1123
|
rate_limit.append(now)
|
|
1031
1124
|
|
|
1032
|
-
|
|
1125
|
+
await _safe_report_progress(
|
|
1126
|
+
ctx,
|
|
1127
|
+
progress=1,
|
|
1128
|
+
total=10,
|
|
1129
|
+
message="Awaiting response from loogle.lean-lang.org",
|
|
1130
|
+
)
|
|
1131
|
+
result = await asyncio.to_thread(loogle_remote, query, num_results)
|
|
1033
1132
|
if isinstance(result, str):
|
|
1034
1133
|
raise LeanToolError(result) # Error message from remote
|
|
1035
1134
|
return LoogleResults(items=result)
|
|
@@ -1045,7 +1144,7 @@ async def loogle(
|
|
|
1045
1144
|
),
|
|
1046
1145
|
)
|
|
1047
1146
|
@rate_limited("leanfinder", max_requests=10, per_seconds=30)
|
|
1048
|
-
def leanfinder(
|
|
1147
|
+
async def leanfinder(
|
|
1049
1148
|
ctx: Context,
|
|
1050
1149
|
query: Annotated[str, Field(description="Mathematical concept or proof state")],
|
|
1051
1150
|
num_results: Annotated[int, Field(description="Max results", ge=1)] = 5,
|
|
@@ -1063,23 +1162,27 @@ def leanfinder(
|
|
|
1063
1162
|
)
|
|
1064
1163
|
|
|
1065
1164
|
results: List[LeanFinderResult] = []
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1165
|
+
await _safe_report_progress(
|
|
1166
|
+
ctx,
|
|
1167
|
+
progress=1,
|
|
1168
|
+
total=10,
|
|
1169
|
+
message="Awaiting response from Lean Finder (Hugging Face)",
|
|
1170
|
+
)
|
|
1171
|
+
data = await _urlopen_json(req, timeout=10)
|
|
1172
|
+
for result in data["results"]:
|
|
1173
|
+
if (
|
|
1174
|
+
"https://leanprover-community.github.io/mathlib4_docs" not in result["url"]
|
|
1175
|
+
): # Only include mathlib4 results
|
|
1176
|
+
continue
|
|
1177
|
+
match = re.search(r"pattern=(.*?)#doc", result["url"])
|
|
1178
|
+
if match:
|
|
1179
|
+
results.append(
|
|
1180
|
+
LeanFinderResult(
|
|
1181
|
+
full_name=match.group(1),
|
|
1182
|
+
formal_statement=result["formal_statement"],
|
|
1183
|
+
informal_statement=result["informal_statement"],
|
|
1082
1184
|
)
|
|
1185
|
+
)
|
|
1083
1186
|
|
|
1084
1187
|
return LeanFinderResults(items=results)
|
|
1085
1188
|
|
|
@@ -1094,7 +1197,7 @@ def leanfinder(
|
|
|
1094
1197
|
),
|
|
1095
1198
|
)
|
|
1096
1199
|
@rate_limited("lean_state_search", max_requests=3, per_seconds=30)
|
|
1097
|
-
def state_search(
|
|
1200
|
+
async def state_search(
|
|
1098
1201
|
ctx: Context,
|
|
1099
1202
|
file_path: Annotated[str, Field(description="Absolute path to Lean file")],
|
|
1100
1203
|
line: Annotated[int, Field(description="Line number (1-indexed)", ge=1)],
|
|
@@ -1126,8 +1229,10 @@ def state_search(
|
|
|
1126
1229
|
method="GET",
|
|
1127
1230
|
)
|
|
1128
1231
|
|
|
1129
|
-
|
|
1130
|
-
|
|
1232
|
+
await _safe_report_progress(
|
|
1233
|
+
ctx, progress=1, total=10, message=f"Awaiting response from {url}"
|
|
1234
|
+
)
|
|
1235
|
+
results = await _urlopen_json(req, timeout=10)
|
|
1131
1236
|
|
|
1132
1237
|
items = [StateSearchResult(name=r["name"]) for r in results]
|
|
1133
1238
|
return StateSearchResults(items=items)
|
|
@@ -1143,7 +1248,7 @@ def state_search(
|
|
|
1143
1248
|
),
|
|
1144
1249
|
)
|
|
1145
1250
|
@rate_limited("hammer_premise", max_requests=3, per_seconds=30)
|
|
1146
|
-
def hammer_premise(
|
|
1251
|
+
async def hammer_premise(
|
|
1147
1252
|
ctx: Context,
|
|
1148
1253
|
file_path: Annotated[str, Field(description="Absolute path to Lean file")],
|
|
1149
1254
|
line: Annotated[int, Field(description="Line number (1-indexed)", ge=1)],
|
|
@@ -1186,8 +1291,10 @@ def hammer_premise(
|
|
|
1186
1291
|
data=orjson.dumps(data),
|
|
1187
1292
|
)
|
|
1188
1293
|
|
|
1189
|
-
|
|
1190
|
-
|
|
1294
|
+
await _safe_report_progress(
|
|
1295
|
+
ctx, progress=1, total=10, message=f"Awaiting response from {url}"
|
|
1296
|
+
)
|
|
1297
|
+
results = await _urlopen_json(req, timeout=10)
|
|
1191
1298
|
|
|
1192
1299
|
items = [PremiseResult(name=r["name"]) for r in results]
|
|
1193
1300
|
return PremiseResults(items=items)
|
lean_lsp_mcp/utils.py
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import os
|
|
2
|
+
import re
|
|
2
3
|
import secrets
|
|
3
4
|
import sys
|
|
4
5
|
import tempfile
|
|
@@ -7,6 +8,30 @@ from typing import Any, List, Dict, Optional, Callable
|
|
|
7
8
|
from mcp.server.auth.provider import AccessToken, TokenVerifier
|
|
8
9
|
|
|
9
10
|
|
|
11
|
+
# Pattern to extract file paths from build stderr: "error: path/file.lean:line:col: message"
|
|
12
|
+
BUILD_ERROR_FILE_PATTERN = re.compile(
|
|
13
|
+
r"^(?:error|warning):\s*([^\s:]+\.lean):\d+:\d+:",
|
|
14
|
+
re.MULTILINE | re.IGNORECASE,
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def extract_failed_dependency_paths(message: str) -> List[str]:
|
|
19
|
+
"""Extract unique file paths from lake build stderr output.
|
|
20
|
+
|
|
21
|
+
Returns sorted list of .lean file paths that had errors/warnings.
|
|
22
|
+
"""
|
|
23
|
+
paths = set(BUILD_ERROR_FILE_PATTERN.findall(message))
|
|
24
|
+
return sorted(paths)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def is_build_stderr(message: str) -> bool:
|
|
28
|
+
"""Check if message looks like lake build stderr output."""
|
|
29
|
+
return (
|
|
30
|
+
"lake setup-file" in message
|
|
31
|
+
or BUILD_ERROR_FILE_PATTERN.search(message) is not None
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
|
|
10
35
|
class LeanToolError(Exception):
|
|
11
36
|
"""Exception raised when a Lean MCP tool operation fails."""
|
|
12
37
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: lean-lsp-mcp
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.18.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.
|
|
11
|
+
Requires-Dist: leanclient==0.9.0
|
|
12
12
|
Requires-Dist: mcp[cli]==1.25.0
|
|
13
13
|
Requires-Dist: orjson>=3.11.1
|
|
14
14
|
Provides-Extra: lint
|
|
@@ -18,6 +18,7 @@ Requires-Dist: ruff>=0.2.0; extra == "dev"
|
|
|
18
18
|
Requires-Dist: pytest>=8.3; extra == "dev"
|
|
19
19
|
Requires-Dist: anyio>=4.4; extra == "dev"
|
|
20
20
|
Requires-Dist: pytest-asyncio>=0.23; extra == "dev"
|
|
21
|
+
Requires-Dist: pytest-timeout>=2.3; extra == "dev"
|
|
21
22
|
Dynamic: license-file
|
|
22
23
|
|
|
23
24
|
<h1 align="center">
|
|
@@ -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=WVW5K27m54W2THoAUMKvBC--qCSD6eSYjd9bcaiMk2s,6879
|
|
8
|
+
lean_lsp_mcp/outline_utils.py,sha256=i7xL27UO2rTT48IdKXkoMq5FVJNxyA3tPuQREOBf_gU,11105
|
|
9
|
+
lean_lsp_mcp/search_utils.py,sha256=MLqKGe4bhEvyfFLIBCmiDxkbcH4O5J3vl9mWnRSb_v0,6801
|
|
10
|
+
lean_lsp_mcp/server.py,sha256=UDTuHeRX_RUxsfLrR3PqGWnAYLvmEb89iZtq0dNqymU,43859
|
|
11
|
+
lean_lsp_mcp/utils.py,sha256=dGv84a4E-szOkQVYtSE-q9GbawpiVk47qvrkTN-Clts,13478
|
|
12
|
+
lean_lsp_mcp-0.18.0.dist-info/licenses/LICENSE,sha256=CQlxnf0tQyoVrBE93JYvAUYxv6Z5Yg6sX0pwogOkFvo,1071
|
|
13
|
+
lean_lsp_mcp-0.18.0.dist-info/METADATA,sha256=xXkTL1pV9lCCopASzat8OYGppv_GRT95GTVdMVccAAw,20838
|
|
14
|
+
lean_lsp_mcp-0.18.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
15
|
+
lean_lsp_mcp-0.18.0.dist-info/entry_points.txt,sha256=nQbvwctWkWD7I-2f4VrdVQBZYGUw8CnUnFC6QjXxOSE,51
|
|
16
|
+
lean_lsp_mcp-0.18.0.dist-info/top_level.txt,sha256=LGEK0lgMSNPIQ6mG8EO-adaZEGPi_0daDs004epOTF0,13
|
|
17
|
+
lean_lsp_mcp-0.18.0.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=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,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|