lean-lsp-mcp 0.11.1__py3-none-any.whl → 0.11.3__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/client_utils.py +52 -36
- lean_lsp_mcp/search_utils.py +26 -39
- lean_lsp_mcp/server.py +51 -70
- {lean_lsp_mcp-0.11.1.dist-info → lean_lsp_mcp-0.11.3.dist-info}/METADATA +5 -1
- lean_lsp_mcp-0.11.3.dist-info/RECORD +14 -0
- lean_lsp_mcp-0.11.1.dist-info/RECORD +0 -14
- {lean_lsp_mcp-0.11.1.dist-info → lean_lsp_mcp-0.11.3.dist-info}/WHEEL +0 -0
- {lean_lsp_mcp-0.11.1.dist-info → lean_lsp_mcp-0.11.3.dist-info}/entry_points.txt +0 -0
- {lean_lsp_mcp-0.11.1.dist-info → lean_lsp_mcp-0.11.3.dist-info}/licenses/LICENSE +0 -0
- {lean_lsp_mcp-0.11.1.dist-info → lean_lsp_mcp-0.11.3.dist-info}/top_level.txt +0 -0
lean_lsp_mcp/client_utils.py
CHANGED
|
@@ -64,59 +64,75 @@ def valid_lean_project_path(path: Path | str) -> bool:
|
|
|
64
64
|
return (path_obj / "lean-toolchain").is_file()
|
|
65
65
|
|
|
66
66
|
|
|
67
|
-
def
|
|
68
|
-
"""
|
|
67
|
+
def infer_project_path(ctx: Context, file_path: str) -> Path | None:
|
|
68
|
+
"""Infer and cache the Lean project path for a file WITHOUT starting the client.
|
|
69
|
+
|
|
70
|
+
Walks up the directory tree to find a lean-toolchain file, caches the result.
|
|
71
|
+
Sets ctx.request_context.lifespan_context.lean_project_path if found.
|
|
72
|
+
|
|
73
|
+
Side effects when path changes:
|
|
74
|
+
- Next LSP tool will restart the client for the new project
|
|
75
|
+
- File content hashes will be cleared
|
|
69
76
|
|
|
77
|
+
Args:
|
|
78
|
+
ctx (Context): Context object
|
|
79
|
+
file_path (str): Absolute or relative path to a Lean file
|
|
80
|
+
|
|
81
|
+
Returns:
|
|
82
|
+
Path | None: The resolved project path if found, None otherwise
|
|
83
|
+
"""
|
|
70
84
|
lifespan = ctx.request_context.lifespan_context
|
|
71
|
-
project_cache = getattr(lifespan, "project_cache", {})
|
|
72
85
|
if not hasattr(lifespan, "project_cache"):
|
|
73
|
-
lifespan.project_cache =
|
|
86
|
+
lifespan.project_cache = {}
|
|
74
87
|
|
|
75
88
|
abs_file_path = os.path.abspath(file_path)
|
|
76
89
|
file_dir = os.path.dirname(abs_file_path)
|
|
77
90
|
|
|
78
|
-
def
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
if rel is None:
|
|
91
|
+
def set_project_path(project_path: Path, cache_dirs: list[str]) -> Path | None:
|
|
92
|
+
"""Validate file is in project, set path, update cache."""
|
|
93
|
+
if get_relative_file_path(project_path, file_path) is None:
|
|
82
94
|
return None
|
|
83
95
|
|
|
84
|
-
|
|
85
|
-
lifespan.lean_project_path =
|
|
96
|
+
project_path = project_path.resolve()
|
|
97
|
+
lifespan.lean_project_path = project_path
|
|
86
98
|
|
|
87
|
-
|
|
88
|
-
for directory in cache_dirs + [str(
|
|
89
|
-
if directory
|
|
90
|
-
|
|
99
|
+
# Update all relevant directories in cache
|
|
100
|
+
for directory in set(cache_dirs + [str(project_path)]):
|
|
101
|
+
if directory:
|
|
102
|
+
lifespan.project_cache[directory] = project_path
|
|
91
103
|
|
|
92
|
-
|
|
93
|
-
project_cache[directory] = project_path_obj
|
|
104
|
+
return project_path
|
|
94
105
|
|
|
95
|
-
|
|
96
|
-
|
|
106
|
+
# Fast path: current project already valid for this file
|
|
107
|
+
if lifespan.lean_project_path and set_project_path(
|
|
108
|
+
lifespan.lean_project_path, [file_dir]
|
|
109
|
+
):
|
|
110
|
+
return lifespan.lean_project_path
|
|
97
111
|
|
|
98
|
-
#
|
|
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
|
|
112
|
+
# Walk up directory tree using cache and lean-toolchain detection
|
|
106
113
|
current_dir = file_dir
|
|
107
|
-
while current_dir and current_dir !=
|
|
108
|
-
cached_root = project_cache.get(current_dir)
|
|
114
|
+
while current_dir and current_dir != os.path.dirname(current_dir):
|
|
115
|
+
cached_root = lifespan.project_cache.get(current_dir)
|
|
116
|
+
|
|
109
117
|
if cached_root:
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
return rel_path
|
|
118
|
+
if result := set_project_path(Path(cached_root), [current_dir]):
|
|
119
|
+
return result
|
|
113
120
|
elif valid_lean_project_path(current_dir):
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
return rel_path
|
|
121
|
+
if result := set_project_path(Path(current_dir), [current_dir]):
|
|
122
|
+
return result
|
|
117
123
|
else:
|
|
118
|
-
project_cache[current_dir] = ""
|
|
119
|
-
|
|
124
|
+
lifespan.project_cache[current_dir] = "" # Mark as checked
|
|
125
|
+
|
|
120
126
|
current_dir = os.path.dirname(current_dir)
|
|
121
127
|
|
|
122
128
|
return None
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
def setup_client_for_file(ctx: Context, file_path: str) -> str | None:
|
|
132
|
+
"""Ensure the LSP client matches the file's Lean project and return its relative path."""
|
|
133
|
+
project_path = infer_project_path(ctx, file_path)
|
|
134
|
+
if project_path is None:
|
|
135
|
+
return None
|
|
136
|
+
|
|
137
|
+
startup_client(ctx)
|
|
138
|
+
return get_relative_file_path(project_path, file_path)
|
lean_lsp_mcp/search_utils.py
CHANGED
|
@@ -56,10 +56,10 @@ def lean_local_search(
|
|
|
56
56
|
) -> list[dict[str, str]]:
|
|
57
57
|
"""Search Lean declarations matching ``query`` using ripgrep; results include theorems, lemmas, defs, classes, instances, structures, inductives, abbrevs, and opaque decls."""
|
|
58
58
|
root = (project_root or Path.cwd()).resolve()
|
|
59
|
-
|
|
60
|
-
|
|
59
|
+
|
|
60
|
+
pattern = (
|
|
61
61
|
rf"^\s*(?:theorem|lemma|def|axiom|class|instance|structure|inductive|abbrev|opaque)\s+"
|
|
62
|
-
rf"{
|
|
62
|
+
rf"(?:[A-Za-z0-9_'.]+\.)*{re.escape(query)}[A-Za-z0-9_'.]*(?:\s|:)"
|
|
63
63
|
)
|
|
64
64
|
|
|
65
65
|
command = [
|
|
@@ -70,68 +70,55 @@ def lean_local_search(
|
|
|
70
70
|
"--hidden",
|
|
71
71
|
"--color",
|
|
72
72
|
"never",
|
|
73
|
+
"--no-messages",
|
|
73
74
|
"-g",
|
|
74
75
|
"*.lean",
|
|
75
76
|
"-g",
|
|
76
77
|
"!.git/**",
|
|
77
78
|
"-g",
|
|
78
79
|
"!.lake/build/**",
|
|
79
|
-
|
|
80
|
-
|
|
80
|
+
pattern,
|
|
81
|
+
str(root),
|
|
81
82
|
]
|
|
82
83
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
command.append(lean_src_path)
|
|
86
|
-
|
|
87
|
-
completed = subprocess.run(
|
|
88
|
-
command,
|
|
89
|
-
capture_output=True,
|
|
90
|
-
text=True,
|
|
91
|
-
cwd=str(root),
|
|
92
|
-
)
|
|
84
|
+
if lean_src := _get_lean_src_search_path():
|
|
85
|
+
command.append(lean_src)
|
|
93
86
|
|
|
94
|
-
|
|
87
|
+
result = subprocess.run(command, capture_output=True, text=True, cwd=str(root))
|
|
95
88
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
event = _json_loads(raw_line)
|
|
101
|
-
|
|
102
|
-
if event.get("type") != "match":
|
|
89
|
+
matches = []
|
|
90
|
+
for line in result.stdout.splitlines():
|
|
91
|
+
if not line or (event := _json_loads(line)).get("type") != "match":
|
|
103
92
|
continue
|
|
104
93
|
|
|
105
94
|
data = event["data"]
|
|
106
|
-
|
|
107
|
-
parts = line_text.lstrip().split(maxsplit=2)
|
|
95
|
+
parts = data["lines"]["text"].lstrip().split(maxsplit=2)
|
|
108
96
|
if len(parts) < 2:
|
|
109
97
|
continue
|
|
110
98
|
|
|
111
|
-
decl_kind,
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
path_text = data["path"]["text"]
|
|
115
|
-
file_path = Path(path_text)
|
|
116
|
-
absolute_path = (
|
|
99
|
+
decl_kind, decl_name = parts[0], parts[1].rstrip(":")
|
|
100
|
+
file_path = Path(data["path"]["text"])
|
|
101
|
+
abs_path = (
|
|
117
102
|
file_path if file_path.is_absolute() else (root / file_path).resolve()
|
|
118
103
|
)
|
|
104
|
+
|
|
119
105
|
try:
|
|
120
|
-
display_path = str(
|
|
106
|
+
display_path = str(abs_path.relative_to(root))
|
|
121
107
|
except ValueError:
|
|
122
108
|
display_path = str(file_path)
|
|
123
109
|
|
|
124
|
-
|
|
110
|
+
matches.append({"name": decl_name, "kind": decl_kind, "file": display_path})
|
|
125
111
|
|
|
126
|
-
if len(
|
|
112
|
+
if len(matches) >= limit:
|
|
127
113
|
break
|
|
128
114
|
|
|
129
|
-
if
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
115
|
+
if result.returncode not in (0, 1) and not matches:
|
|
116
|
+
error_msg = f"ripgrep exited with code {result.returncode}"
|
|
117
|
+
if result.stderr:
|
|
118
|
+
error_msg += f"\n{result.stderr}"
|
|
119
|
+
raise RuntimeError(error_msg)
|
|
133
120
|
|
|
134
|
-
return
|
|
121
|
+
return matches
|
|
135
122
|
|
|
136
123
|
|
|
137
124
|
@lru_cache(maxsize=1)
|
lean_lsp_mcp/server.py
CHANGED
|
@@ -18,7 +18,11 @@ 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
|
|
21
|
+
from lean_lsp_mcp.client_utils import (
|
|
22
|
+
setup_client_for_file,
|
|
23
|
+
startup_client,
|
|
24
|
+
infer_project_path,
|
|
25
|
+
)
|
|
22
26
|
from lean_lsp_mcp.file_utils import get_file_contents, update_file
|
|
23
27
|
from lean_lsp_mcp.instructions import INSTRUCTIONS
|
|
24
28
|
from lean_lsp_mcp.search_utils import check_ripgrep_status, lean_local_search
|
|
@@ -154,10 +158,7 @@ async def lsp_build(
|
|
|
154
158
|
ctx.request_context.lifespan_context.lean_project_path = lean_project_path_obj
|
|
155
159
|
|
|
156
160
|
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
|
-
)
|
|
161
|
+
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
162
|
|
|
162
163
|
build_output = ""
|
|
163
164
|
try:
|
|
@@ -247,6 +248,10 @@ def file_contents(ctx: Context, file_path: str, annotate_lines: bool = True) ->
|
|
|
247
248
|
Returns:
|
|
248
249
|
str: File content or error msg
|
|
249
250
|
"""
|
|
251
|
+
# Infer project path but do not start a client
|
|
252
|
+
if file_path.endswith(".lean"):
|
|
253
|
+
infer_project_path(ctx, file_path) # Silently fails for non-project files
|
|
254
|
+
|
|
250
255
|
try:
|
|
251
256
|
data = get_file_contents(file_path)
|
|
252
257
|
except FileNotFoundError:
|
|
@@ -572,9 +577,7 @@ def multi_attempt(
|
|
|
572
577
|
# Apply the change to the file, capture diagnostics and goal state
|
|
573
578
|
client.update_file(rel_path, [change])
|
|
574
579
|
diag = client.get_diagnostics(rel_path)
|
|
575
|
-
formatted_diag = "\n".join(
|
|
576
|
-
format_diagnostics(diag, select_line=line - 1)
|
|
577
|
-
)
|
|
580
|
+
formatted_diag = "\n".join(format_diagnostics(diag, select_line=line - 1))
|
|
578
581
|
# Use the snippet text length without any trailing newline for the column
|
|
579
582
|
goal = client.get_goal(rel_path, line - 1, len(snippet_str))
|
|
580
583
|
formatted_goal = format_goal(goal, "Missing goal")
|
|
@@ -585,7 +588,9 @@ def multi_attempt(
|
|
|
585
588
|
try:
|
|
586
589
|
client.close_files([rel_path])
|
|
587
590
|
except Exception as exc: # pragma: no cover - close failures only logged
|
|
588
|
-
logger.warning(
|
|
591
|
+
logger.warning(
|
|
592
|
+
"Failed to close `%s` after multi_attempt: %s", rel_path, exc
|
|
593
|
+
)
|
|
589
594
|
|
|
590
595
|
|
|
591
596
|
@mcp.tool("lean_run_code")
|
|
@@ -604,7 +609,7 @@ def run_code(ctx: Context, code: str) -> List[str] | str:
|
|
|
604
609
|
lifespan_context = ctx.request_context.lifespan_context
|
|
605
610
|
lean_project_path = lifespan_context.lean_project_path
|
|
606
611
|
if lean_project_path is None:
|
|
607
|
-
return "No valid Lean project path found. Run another tool (e.g. `
|
|
612
|
+
return "No valid Lean project path found. Run another tool (e.g. `lean_file_contents`) first to set it up."
|
|
608
613
|
|
|
609
614
|
# Use a unique snippet filename to avoid collisions under concurrency
|
|
610
615
|
rel_path = f"_mcp_snippet_{uuid.uuid4().hex}.lean"
|
|
@@ -683,11 +688,10 @@ def local_search(
|
|
|
683
688
|
return _RG_MESSAGE
|
|
684
689
|
|
|
685
690
|
stored_root = ctx.request_context.lifespan_context.lean_project_path
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
)
|
|
690
|
-
return results
|
|
691
|
+
if stored_root is None:
|
|
692
|
+
return "Lean project path not set. Call a file-based tool (like lean_file_contents) first to set the project path."
|
|
693
|
+
|
|
694
|
+
return lean_local_search(query=query.strip(), limit=limit, project_root=stored_root)
|
|
691
695
|
|
|
692
696
|
|
|
693
697
|
@mcp.tool("lean_leansearch")
|
|
@@ -711,9 +715,7 @@ def leansearch(ctx: Context, query: str, num_results: int = 5) -> List[Dict] | s
|
|
|
711
715
|
"""
|
|
712
716
|
try:
|
|
713
717
|
headers = {"User-Agent": "lean-lsp-mcp/0.1", "Content-Type": "application/json"}
|
|
714
|
-
payload = orjson.dumps(
|
|
715
|
-
{"num_results": str(num_results), "query": [query]}
|
|
716
|
-
)
|
|
718
|
+
payload = orjson.dumps({"num_results": str(num_results), "query": [query]})
|
|
717
719
|
|
|
718
720
|
req = urllib.request.Request(
|
|
719
721
|
"https://leansearch.net/search",
|
|
@@ -784,73 +786,52 @@ def loogle(ctx: Context, query: str, num_results: int = 8) -> List[dict] | str:
|
|
|
784
786
|
|
|
785
787
|
@mcp.tool("lean_leanfinder")
|
|
786
788
|
@rate_limited("leanfinder", max_requests=10, per_seconds=30)
|
|
787
|
-
def leanfinder(
|
|
788
|
-
|
|
789
|
-
) -> List[tuple] | str:
|
|
790
|
-
"""Search Mathlib theorems/definitions semantically by mathematical concept using Lean Finder.
|
|
789
|
+
def leanfinder(ctx: Context, query: str, num_results: int = 5) -> List[Dict] | str:
|
|
790
|
+
"""Search Mathlib theorems/definitions semantically by mathematical concept or proof state using Lean Finder.
|
|
791
791
|
|
|
792
792
|
Effective query types:
|
|
793
|
-
-
|
|
794
|
-
-
|
|
795
|
-
-
|
|
796
|
-
-
|
|
793
|
+
- Natural language mathematical statement: "For any natural numbers n and m, the sum n+m is equal to m+n."
|
|
794
|
+
- Natural language questions: "I'm working with algebraic elements over a field extension … Does this imply that the minimal polynomials of x and y are equal?"
|
|
795
|
+
- Proof state. For better results, enter a proof state followed by how you want to transform the proof state.
|
|
796
|
+
- Statement definition: Fragment or the whole statement definition.
|
|
797
797
|
|
|
798
|
-
Tips:
|
|
798
|
+
Tips: Multiple targeted queries beat one complex query.
|
|
799
799
|
|
|
800
800
|
Args:
|
|
801
|
-
query (str): Mathematical
|
|
801
|
+
query (str): Mathematical concept or proof state
|
|
802
802
|
num_results (int, optional): Max results. Defaults to 5.
|
|
803
803
|
|
|
804
804
|
Returns:
|
|
805
|
-
List[
|
|
805
|
+
List[Dict] | str: List of Lean statement objects (full name, formal statement, informal statement) or error msg
|
|
806
806
|
"""
|
|
807
807
|
try:
|
|
808
808
|
headers = {"User-Agent": "lean-lsp-mcp/0.1", "Content-Type": "application/json"}
|
|
809
|
-
|
|
810
|
-
|
|
809
|
+
request_url = (
|
|
810
|
+
"https://bxrituxuhpc70w8w.us-east-1.aws.endpoints.huggingface.cloud"
|
|
811
|
+
)
|
|
812
|
+
payload = orjson.dumps({"inputs": query, "top_k": int(num_results)})
|
|
811
813
|
req = urllib.request.Request(
|
|
812
|
-
"
|
|
813
|
-
data=payload,
|
|
814
|
-
headers=headers,
|
|
815
|
-
method="POST",
|
|
814
|
+
request_url, data=payload, headers=headers, method="POST"
|
|
816
815
|
)
|
|
817
816
|
|
|
818
|
-
|
|
819
|
-
event_data = orjson.loads(response.read())
|
|
820
|
-
event_id = event_data.get("event_id")
|
|
821
|
-
|
|
822
|
-
if not event_id:
|
|
823
|
-
return "Lean Finder has timed out or errored. It might be warming up, try a second time in 2 minutes."
|
|
824
|
-
|
|
825
|
-
result_url = f"https://delta-lab-ai-lean-finder.hf.space/gradio_api/call/retrieve/{event_id}"
|
|
826
|
-
req = urllib.request.Request(result_url, headers=headers, method="GET")
|
|
827
|
-
|
|
817
|
+
results = []
|
|
828
818
|
with urllib.request.urlopen(req, timeout=30) as response:
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
if
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
if formal:
|
|
846
|
-
results.append((
|
|
847
|
-
formal.group(1).strip(),
|
|
848
|
-
informal.group(1).strip() if informal else ""
|
|
849
|
-
))
|
|
850
|
-
|
|
851
|
-
return results if results else "Lean Finder: No results parsed"
|
|
852
|
-
|
|
853
|
-
return "Lean Finder: No results received"
|
|
819
|
+
data = orjson.loads(response.read())
|
|
820
|
+
for result in data["results"]:
|
|
821
|
+
if (
|
|
822
|
+
"https://leanprover-community.github.io/mathlib4_docs"
|
|
823
|
+
not in result["url"]
|
|
824
|
+
): # Do not include results from other sources other than mathlib4, since users might not have imported them
|
|
825
|
+
continue
|
|
826
|
+
full_name = re.search(r"pattern=(.*?)#doc", result["url"]).group(1)
|
|
827
|
+
obj = {
|
|
828
|
+
"full_name": full_name,
|
|
829
|
+
"formal_statement": result["formal_statement"],
|
|
830
|
+
"informal_statement": result["informal_statement"],
|
|
831
|
+
}
|
|
832
|
+
results.append(obj)
|
|
833
|
+
|
|
834
|
+
return results if results else "Lean Finder: No results parsed"
|
|
854
835
|
except Exception as e:
|
|
855
836
|
return f"Lean Finder Error:\n{str(e)}"
|
|
856
837
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: lean-lsp-mcp
|
|
3
|
-
Version: 0.11.
|
|
3
|
+
Version: 0.11.3
|
|
4
4
|
Summary: Lean Theorem Prover MCP
|
|
5
5
|
Author-email: Oliver Dressler <hey@oli.show>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -543,6 +543,10 @@ uv sync --all-extras
|
|
|
543
543
|
uv run pytest tests
|
|
544
544
|
```
|
|
545
545
|
|
|
546
|
+
## Publications using lean-lsp-mcp
|
|
547
|
+
|
|
548
|
+
- Ax-Prover: A Deep Reasoning Agentic Framework for Theorem Proving in Mathematics and Quantum Physics [arxiv](https://arxiv.org/abs/2510.12787)
|
|
549
|
+
|
|
546
550
|
## Related Projects
|
|
547
551
|
|
|
548
552
|
- [LeanTool](https://github.com/GasStationManager/LeanTool)
|
|
@@ -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=FNfiEWQagRj9tmo2xUDxbSZYVz5_RfaNH6OApgNQ9ZM,5065
|
|
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=zwiHeMlgRpERNIF-E_6OKzI8a-YmAx09EVX3QO2gJAk,34792
|
|
8
|
+
lean_lsp_mcp/utils.py,sha256=zLu2VIhaX4yocY07F3Z94LB2jRGrkH1ID9SjR3poE9A,8255
|
|
9
|
+
lean_lsp_mcp-0.11.3.dist-info/licenses/LICENSE,sha256=CQlxnf0tQyoVrBE93JYvAUYxv6Z5Yg6sX0pwogOkFvo,1071
|
|
10
|
+
lean_lsp_mcp-0.11.3.dist-info/METADATA,sha256=1mf98hyaVVN1lZLWMZEdtSV6-nDZ7KAFyVLPRc7gbx8,19626
|
|
11
|
+
lean_lsp_mcp-0.11.3.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
12
|
+
lean_lsp_mcp-0.11.3.dist-info/entry_points.txt,sha256=nQbvwctWkWD7I-2f4VrdVQBZYGUw8CnUnFC6QjXxOSE,51
|
|
13
|
+
lean_lsp_mcp-0.11.3.dist-info/top_level.txt,sha256=LGEK0lgMSNPIQ6mG8EO-adaZEGPi_0daDs004epOTF0,13
|
|
14
|
+
lean_lsp_mcp-0.11.3.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=cNpWzR1TuNyvRxCeCIb0HoEHGsCIW1ypqN1R1ZpbkdI,4170
|
|
7
|
-
lean_lsp_mcp/server.py,sha256=jK2bwi7iiM_agXtwRNFAMIpAo5FLDJ2FKfdDLvbaWfY,35433
|
|
8
|
-
lean_lsp_mcp/utils.py,sha256=zLu2VIhaX4yocY07F3Z94LB2jRGrkH1ID9SjR3poE9A,8255
|
|
9
|
-
lean_lsp_mcp-0.11.1.dist-info/licenses/LICENSE,sha256=CQlxnf0tQyoVrBE93JYvAUYxv6Z5Yg6sX0pwogOkFvo,1071
|
|
10
|
-
lean_lsp_mcp-0.11.1.dist-info/METADATA,sha256=IWxzDA2qqh5yK7jxCr36R6z7HuwwLV2bzoFlGqUWEoQ,19444
|
|
11
|
-
lean_lsp_mcp-0.11.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
12
|
-
lean_lsp_mcp-0.11.1.dist-info/entry_points.txt,sha256=nQbvwctWkWD7I-2f4VrdVQBZYGUw8CnUnFC6QjXxOSE,51
|
|
13
|
-
lean_lsp_mcp-0.11.1.dist-info/top_level.txt,sha256=LGEK0lgMSNPIQ6mG8EO-adaZEGPi_0daDs004epOTF0,13
|
|
14
|
-
lean_lsp_mcp-0.11.1.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|