lean-lsp-mcp 0.13.1__py3-none-any.whl → 0.14.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/client_utils.py +6 -7
- lean_lsp_mcp/instructions.py +1 -0
- lean_lsp_mcp/outline_utils.py +200 -0
- lean_lsp_mcp/server.py +45 -4
- lean_lsp_mcp/utils.py +20 -1
- {lean_lsp_mcp-0.13.1.dist-info → lean_lsp_mcp-0.14.0.dist-info}/METADATA +11 -7
- lean_lsp_mcp-0.14.0.dist-info/RECORD +15 -0
- lean_lsp_mcp-0.13.1.dist-info/RECORD +0 -14
- {lean_lsp_mcp-0.13.1.dist-info → lean_lsp_mcp-0.14.0.dist-info}/WHEEL +0 -0
- {lean_lsp_mcp-0.13.1.dist-info → lean_lsp_mcp-0.14.0.dist-info}/entry_points.txt +0 -0
- {lean_lsp_mcp-0.13.1.dist-info → lean_lsp_mcp-0.14.0.dist-info}/licenses/LICENSE +0 -0
- {lean_lsp_mcp-0.13.1.dist-info → lean_lsp_mcp-0.14.0.dist-info}/top_level.txt +0 -0
lean_lsp_mcp/client_utils.py
CHANGED
|
@@ -36,14 +36,13 @@ def startup_client(ctx: Context):
|
|
|
36
36
|
client.close()
|
|
37
37
|
|
|
38
38
|
# Need to create a new client
|
|
39
|
+
# In test environments, prevent repeated cache downloads
|
|
40
|
+
prevent_cache = bool(os.environ.get("LEAN_LSP_TEST_MODE"))
|
|
39
41
|
with OutputCapture() as output:
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
logger.warning(f"Initial connection failed, trying with build: {e}")
|
|
45
|
-
client = LeanLSPClient(lean_project_path, initial_build=True)
|
|
46
|
-
logger.info(f"Connected with initial build to {lean_project_path}")
|
|
42
|
+
client = LeanLSPClient(
|
|
43
|
+
lean_project_path, initial_build=False, prevent_cache_get=prevent_cache
|
|
44
|
+
)
|
|
45
|
+
logger.info(f"Connected to Lean language server at {lean_project_path}")
|
|
47
46
|
build_output = output.get_output()
|
|
48
47
|
if build_output:
|
|
49
48
|
logger.debug(f"Build output: {build_output}")
|
lean_lsp_mcp/instructions.py
CHANGED
|
@@ -5,6 +5,7 @@ INSTRUCTIONS = """## General Rules
|
|
|
5
5
|
- Work iteratively: Small steps, intermediate sorries, frequent checks.
|
|
6
6
|
|
|
7
7
|
## Key Tools
|
|
8
|
+
- lean_file_outline: Concise skeleton of a file (imports, docstrings, declarations). Token efficient.
|
|
8
9
|
- lean_local_search: Confirm declarations (theorems/lemmas/defs/etc.) exist. VERY USEFUL AND FAST!
|
|
9
10
|
- lean_goal: Check proof state. USE OFTEN!
|
|
10
11
|
- lean_diagnostic_messages: Understand current proof situation.
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
import re
|
|
2
|
+
from typing import Dict, List, Optional, Tuple
|
|
3
|
+
from leanclient import LeanLSPClient
|
|
4
|
+
from leanclient.utils import DocumentContentChange
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
METHOD_KIND = {6, "method"}
|
|
8
|
+
KIND_TAGS = {"namespace": "Ns"}
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def _get_info_trees(client: LeanLSPClient, path: str, symbols: List[Dict]) -> Dict[str, str]:
|
|
12
|
+
"""Insert #info_trees commands, collect diagnostics, then revert changes."""
|
|
13
|
+
if not symbols:
|
|
14
|
+
return {}
|
|
15
|
+
|
|
16
|
+
symbol_by_line = {}
|
|
17
|
+
changes = []
|
|
18
|
+
for i, sym in enumerate(sorted(symbols, key=lambda s: s['range']['start']['line'])):
|
|
19
|
+
line = sym['range']['start']['line'] + i
|
|
20
|
+
symbol_by_line[line] = sym['name']
|
|
21
|
+
changes.append(DocumentContentChange("#info_trees in\n", [line, 0], [line, 0]))
|
|
22
|
+
|
|
23
|
+
client.update_file(path, changes)
|
|
24
|
+
diagnostics = client.get_diagnostics(path)
|
|
25
|
+
|
|
26
|
+
info_trees = {
|
|
27
|
+
symbol_by_line[diag['range']['start']['line']]: diag['message']
|
|
28
|
+
for diag in diagnostics
|
|
29
|
+
if diag['severity'] == 3 and diag['range']['start']['line'] in symbol_by_line
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
# Revert in reverse order
|
|
33
|
+
client.update_file(path, [
|
|
34
|
+
DocumentContentChange("", [line, 0], [line + 1, 0])
|
|
35
|
+
for line in sorted(symbol_by_line.keys(), reverse=True)
|
|
36
|
+
])
|
|
37
|
+
return info_trees
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def _extract_type(info: str, name: str) -> Optional[str]:
|
|
41
|
+
"""Extract type signature from info tree message."""
|
|
42
|
+
if m := re.search(rf' • \[Term\] {re.escape(name)} \(isBinder := true\) : ([^@]+) @', info):
|
|
43
|
+
return m.group(1).strip()
|
|
44
|
+
return None
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def _extract_fields(info: str, name: str) -> List[Tuple[str, str]]:
|
|
48
|
+
"""Extract structure/class fields from info tree message."""
|
|
49
|
+
fields = []
|
|
50
|
+
for pattern in [rf'{re.escape(name)}\.(\w+)', rf'@{re.escape(name)}\.(\w+)']:
|
|
51
|
+
for m in re.finditer(rf' • \[Term\] {pattern} \(isBinder := true\) : (.+?) @', info):
|
|
52
|
+
field_name, full_type = m.groups()
|
|
53
|
+
# Clean up the type signature
|
|
54
|
+
if ']' in full_type:
|
|
55
|
+
field_type = full_type[full_type.rfind(']')+1:].lstrip('→ ').strip()
|
|
56
|
+
elif ' → ' in full_type:
|
|
57
|
+
field_type = full_type.split(' → ')[-1].strip()
|
|
58
|
+
else:
|
|
59
|
+
field_type = full_type.strip()
|
|
60
|
+
fields.append((field_name, field_type))
|
|
61
|
+
return fields
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def _extract_declarations(content: str, start: int, end: int) -> List[Dict]:
|
|
65
|
+
"""Extract theorem/lemma/def declarations from file content."""
|
|
66
|
+
lines = content.splitlines()
|
|
67
|
+
decls, i = [], start
|
|
68
|
+
|
|
69
|
+
while i < min(end, len(lines)):
|
|
70
|
+
line = lines[i].strip()
|
|
71
|
+
for keyword in ['theorem', 'lemma', 'def']:
|
|
72
|
+
if line.startswith(f"{keyword} "):
|
|
73
|
+
name = line[len(keyword):].strip().split()[0]
|
|
74
|
+
if name and not name.startswith('_'):
|
|
75
|
+
# Collect until :=
|
|
76
|
+
decl_lines = [line]
|
|
77
|
+
j = i + 1
|
|
78
|
+
while j < min(end, len(lines)) and ':=' not in ' '.join(decl_lines):
|
|
79
|
+
if (next_line := lines[j].strip()) and not next_line.startswith('--'):
|
|
80
|
+
decl_lines.append(next_line)
|
|
81
|
+
j += 1
|
|
82
|
+
|
|
83
|
+
# Extract signature (everything before :=, minus keyword and name)
|
|
84
|
+
full_decl = ' '.join(decl_lines)
|
|
85
|
+
type_sig = None
|
|
86
|
+
if ':=' in full_decl:
|
|
87
|
+
sig_part = full_decl.split(':=', 1)[0].strip()[len(keyword):].strip()
|
|
88
|
+
if sig_part.startswith(name):
|
|
89
|
+
type_sig = sig_part[len(name):].strip()
|
|
90
|
+
|
|
91
|
+
decls.append({
|
|
92
|
+
'name': name,
|
|
93
|
+
'kind': 'method',
|
|
94
|
+
'range': {'start': {'line': i, 'character': 0},
|
|
95
|
+
'end': {'line': i, 'character': len(lines[i])}},
|
|
96
|
+
'_keyword': keyword,
|
|
97
|
+
'_type': type_sig
|
|
98
|
+
})
|
|
99
|
+
break
|
|
100
|
+
i += 1
|
|
101
|
+
return decls
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def _flatten_symbols(symbols: List[Dict], indent: int = 0, content: str = "") -> List[Tuple[Dict, int]]:
|
|
105
|
+
"""Recursively flatten symbol hierarchy, extracting declarations from namespaces."""
|
|
106
|
+
result = []
|
|
107
|
+
for sym in symbols:
|
|
108
|
+
result.append((sym, indent))
|
|
109
|
+
children = sym.get('children', [])
|
|
110
|
+
|
|
111
|
+
# Extract theorem/lemma/def from namespace bodies
|
|
112
|
+
if content and sym.get('kind') == 'namespace':
|
|
113
|
+
ns_range = sym['range']
|
|
114
|
+
ns_start = ns_range['start']['line']
|
|
115
|
+
ns_end = ns_range['end']['line']
|
|
116
|
+
children = children + _extract_declarations(content, ns_start, ns_end)
|
|
117
|
+
|
|
118
|
+
if children:
|
|
119
|
+
result.extend(_flatten_symbols(children, indent + 1, content))
|
|
120
|
+
return result
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def _detect_tag(name: str, kind: str, type_sig: str, has_fields: bool, keyword: Optional[str]) -> str:
|
|
124
|
+
"""Determine the appropriate tag for a symbol."""
|
|
125
|
+
if has_fields:
|
|
126
|
+
return "Class" if '→' in type_sig else "Struct"
|
|
127
|
+
if name == "example":
|
|
128
|
+
return "Ex"
|
|
129
|
+
if keyword in {'theorem', 'lemma'}:
|
|
130
|
+
return "Thm"
|
|
131
|
+
if type_sig and any(marker in type_sig for marker in ['∀', '=']):
|
|
132
|
+
return "Thm"
|
|
133
|
+
if type_sig and '→' in type_sig.replace(' → ', '', 1): # More than one arrow
|
|
134
|
+
return "Thm"
|
|
135
|
+
return KIND_TAGS.get(kind, "Def")
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
def _format_symbol(sym: Dict, type_sigs: Dict, fields_map: Dict, indent: int) -> str:
|
|
139
|
+
"""Format a single symbol with its type signature and fields."""
|
|
140
|
+
name = sym['name']
|
|
141
|
+
type_sig = sym.get('_type') or type_sigs.get(name, "")
|
|
142
|
+
fields = fields_map.get(name, [])
|
|
143
|
+
|
|
144
|
+
tag = _detect_tag(name, sym.get('kind', ''), type_sig, bool(fields), sym.get('_keyword'))
|
|
145
|
+
prefix = "\t" * indent
|
|
146
|
+
|
|
147
|
+
start = sym['range']['start']['line'] + 1
|
|
148
|
+
end = sym['range']['end']['line'] + 1
|
|
149
|
+
line_info = f"L{start}" if start == end else f"L{start}-{end}"
|
|
150
|
+
|
|
151
|
+
result = f"{prefix}[{tag}: {line_info}] {name}"
|
|
152
|
+
if type_sig:
|
|
153
|
+
result += f" : {type_sig}"
|
|
154
|
+
|
|
155
|
+
for fname, ftype in fields:
|
|
156
|
+
result += f"\n{prefix}\t{fname} : {ftype}"
|
|
157
|
+
|
|
158
|
+
return result + "\n"
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
def generate_outline(client: LeanLSPClient, path: str) -> str:
|
|
162
|
+
"""Generate a concise outline of a Lean file showing structure and signatures."""
|
|
163
|
+
client.open_file(path)
|
|
164
|
+
content = client.get_file_content(path)
|
|
165
|
+
|
|
166
|
+
# Extract imports
|
|
167
|
+
imports = [line.strip()[7:] for line in content.splitlines()
|
|
168
|
+
if line.strip().startswith("import ")]
|
|
169
|
+
|
|
170
|
+
symbols = client.get_document_symbols(path)
|
|
171
|
+
if not symbols and not imports:
|
|
172
|
+
return f"# {path}\n\n*No symbols or imports found*\n"
|
|
173
|
+
|
|
174
|
+
# Flatten symbol tree and extract namespace declarations
|
|
175
|
+
all_symbols = _flatten_symbols(symbols, content=content)
|
|
176
|
+
|
|
177
|
+
# Get info trees only for LSP symbols (not extracted declarations)
|
|
178
|
+
lsp_methods = [s for s, _ in all_symbols if s.get('kind') in METHOD_KIND and '_keyword' not in s]
|
|
179
|
+
info_trees = _get_info_trees(client, path, lsp_methods)
|
|
180
|
+
|
|
181
|
+
# Extract type signatures and fields from info trees
|
|
182
|
+
type_sigs = {name: sig for name, info in info_trees.items()
|
|
183
|
+
if (sig := _extract_type(info, name))}
|
|
184
|
+
fields_map = {name: fields for name, info in info_trees.items()
|
|
185
|
+
if (fields := _extract_fields(info, name))}
|
|
186
|
+
|
|
187
|
+
# Build output
|
|
188
|
+
parts = []
|
|
189
|
+
if imports:
|
|
190
|
+
parts.append("## Imports\n" + "\n".join(imports))
|
|
191
|
+
|
|
192
|
+
if symbols:
|
|
193
|
+
declarations = [
|
|
194
|
+
_format_symbol(sym, type_sigs, fields_map, indent)
|
|
195
|
+
for sym, indent in all_symbols
|
|
196
|
+
if sym.get('kind') in METHOD_KIND or sym.get('_keyword') or sym.get('kind') == 'namespace'
|
|
197
|
+
]
|
|
198
|
+
parts.append("## Declarations\n" + "".join(declarations).rstrip())
|
|
199
|
+
|
|
200
|
+
return "\n\n".join(parts) + "\n"
|
lean_lsp_mcp/server.py
CHANGED
|
@@ -26,8 +26,10 @@ from lean_lsp_mcp.client_utils import (
|
|
|
26
26
|
from lean_lsp_mcp.file_utils import get_file_contents
|
|
27
27
|
from lean_lsp_mcp.instructions import INSTRUCTIONS
|
|
28
28
|
from lean_lsp_mcp.search_utils import check_ripgrep_status, lean_local_search
|
|
29
|
+
from lean_lsp_mcp.outline_utils import generate_outline
|
|
29
30
|
from lean_lsp_mcp.utils import (
|
|
30
31
|
OutputCapture,
|
|
32
|
+
deprecated,
|
|
31
33
|
extract_range,
|
|
32
34
|
filter_diagnostics_by_position,
|
|
33
35
|
find_start_position,
|
|
@@ -236,6 +238,7 @@ async def lsp_build(
|
|
|
236
238
|
|
|
237
239
|
# File level tools
|
|
238
240
|
@mcp.tool("lean_file_contents")
|
|
241
|
+
@deprecated
|
|
239
242
|
def file_contents(ctx: Context, file_path: str, annotate_lines: bool = True) -> str:
|
|
240
243
|
"""Get the text contents of a Lean file, optionally with line numbers.
|
|
241
244
|
|
|
@@ -270,6 +273,26 @@ def file_contents(ctx: Context, file_path: str, annotate_lines: bool = True) ->
|
|
|
270
273
|
return data
|
|
271
274
|
|
|
272
275
|
|
|
276
|
+
@mcp.tool("lean_file_outline")
|
|
277
|
+
def file_outline(ctx: Context, file_path: str) -> str:
|
|
278
|
+
"""Get a concise outline showing imports and declarations with type signatures (theorems, defs, classes, structures).
|
|
279
|
+
|
|
280
|
+
Highly useful and token-efficient. Slow-ish.
|
|
281
|
+
|
|
282
|
+
Args:
|
|
283
|
+
file_path (str): Abs path to Lean file
|
|
284
|
+
|
|
285
|
+
Returns:
|
|
286
|
+
str: Markdown formatted outline or error msg
|
|
287
|
+
"""
|
|
288
|
+
rel_path = setup_client_for_file(ctx, file_path)
|
|
289
|
+
if not rel_path:
|
|
290
|
+
return "Invalid Lean file path: Unable to start LSP server or load file"
|
|
291
|
+
|
|
292
|
+
client: LeanLSPClient = ctx.request_context.lifespan_context.client
|
|
293
|
+
return generate_outline(client, rel_path)
|
|
294
|
+
|
|
295
|
+
|
|
273
296
|
@mcp.tool("lean_diagnostic_messages")
|
|
274
297
|
def diagnostic_messages(
|
|
275
298
|
ctx: Context,
|
|
@@ -706,7 +729,7 @@ def run_code(ctx: Context, code: str) -> List[str] | str:
|
|
|
706
729
|
|
|
707
730
|
@mcp.tool("lean_local_search")
|
|
708
731
|
def local_search(
|
|
709
|
-
ctx: Context, query: str, limit: int = 10
|
|
732
|
+
ctx: Context, query: str, limit: int = 10, project_root: str | None = None
|
|
710
733
|
) -> List[Dict[str, str]] | str:
|
|
711
734
|
"""Confirm declarations exist in the current workspace to prevent hallucinating APIs.
|
|
712
735
|
|
|
@@ -724,11 +747,29 @@ def local_search(
|
|
|
724
747
|
if not _RG_AVAILABLE:
|
|
725
748
|
return _RG_MESSAGE
|
|
726
749
|
|
|
727
|
-
|
|
728
|
-
|
|
750
|
+
lifespan = ctx.request_context.lifespan_context
|
|
751
|
+
stored_root = lifespan.lean_project_path
|
|
752
|
+
|
|
753
|
+
if project_root:
|
|
754
|
+
try:
|
|
755
|
+
resolved_root = Path(project_root).expanduser().resolve()
|
|
756
|
+
except OSError as exc: # pragma: no cover - defensive path handling
|
|
757
|
+
return f"Invalid project root '{project_root}': {exc}"
|
|
758
|
+
if not resolved_root.exists():
|
|
759
|
+
return f"Project root '{project_root}' does not exist."
|
|
760
|
+
lifespan.lean_project_path = resolved_root
|
|
761
|
+
else:
|
|
762
|
+
resolved_root = stored_root
|
|
763
|
+
|
|
764
|
+
if resolved_root is None:
|
|
729
765
|
return "Lean project path not set. Call a file-based tool (like lean_file_contents) first to set the project path."
|
|
730
766
|
|
|
731
|
-
|
|
767
|
+
try:
|
|
768
|
+
return lean_local_search(
|
|
769
|
+
query=query.strip(), limit=limit, project_root=resolved_root
|
|
770
|
+
)
|
|
771
|
+
except RuntimeError as exc:
|
|
772
|
+
return f"lean_local_search error:\n{exc}"
|
|
732
773
|
|
|
733
774
|
|
|
734
775
|
@mcp.tool("lean_leansearch")
|
lean_lsp_mcp/utils.py
CHANGED
|
@@ -2,7 +2,7 @@ import os
|
|
|
2
2
|
import secrets
|
|
3
3
|
import sys
|
|
4
4
|
import tempfile
|
|
5
|
-
from typing import List, Dict, Optional
|
|
5
|
+
from typing import List, Dict, Optional, Callable
|
|
6
6
|
|
|
7
7
|
from mcp.server.auth.provider import AccessToken, TokenVerifier
|
|
8
8
|
|
|
@@ -334,3 +334,22 @@ def get_declaration_range(
|
|
|
334
334
|
e,
|
|
335
335
|
)
|
|
336
336
|
return None
|
|
337
|
+
|
|
338
|
+
|
|
339
|
+
def deprecated(func_or_msg: str | Callable | None = None) -> Callable:
|
|
340
|
+
"""Mark a tool as deprecated. Can be used as @deprecated or @deprecated("msg")."""
|
|
341
|
+
msg = "Will be removed soon."
|
|
342
|
+
|
|
343
|
+
def _decorator(func: Callable) -> Callable:
|
|
344
|
+
doc = func.__doc__ or ""
|
|
345
|
+
func.__doc__ = f"DEPRECATED: {msg}\n\n{doc}"
|
|
346
|
+
return func
|
|
347
|
+
|
|
348
|
+
if isinstance(func_or_msg, str):
|
|
349
|
+
msg = func_or_msg
|
|
350
|
+
return _decorator
|
|
351
|
+
|
|
352
|
+
if func_or_msg is None:
|
|
353
|
+
return _decorator
|
|
354
|
+
|
|
355
|
+
return _decorator(func_or_msg)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: lean-lsp-mcp
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.14.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.5.
|
|
12
|
-
Requires-Dist: mcp[cli]==1.21.
|
|
11
|
+
Requires-Dist: leanclient==0.5.5
|
|
12
|
+
Requires-Dist: mcp[cli]==1.21.2
|
|
13
13
|
Requires-Dist: orjson>=3.11.1
|
|
14
14
|
Provides-Extra: lint
|
|
15
15
|
Requires-Dist: ruff>=0.2.0; extra == "lint"
|
|
@@ -66,7 +66,7 @@ MCP server that allows agentic interaction with the [Lean theorem prover](https:
|
|
|
66
66
|
### 3. Configure your IDE/Setup
|
|
67
67
|
|
|
68
68
|
<details>
|
|
69
|
-
<summary><b>VSCode</b></summary>
|
|
69
|
+
<summary><b>VSCode (Click to expand)</b></summary>
|
|
70
70
|
One-click config setup:
|
|
71
71
|
|
|
72
72
|
[](https://insiders.vscode.dev/redirect/mcp/install?name=lean-lsp&config=%7B%22command%22%3A%22uvx%22%2C%22args%22%3A%5B%22lean-lsp-mcp%22%5D%7D)
|
|
@@ -118,7 +118,7 @@ If that doesn't work, you can try cloning this repository and replace `"lean-lsp
|
|
|
118
118
|
</details>
|
|
119
119
|
|
|
120
120
|
<details>
|
|
121
|
-
<summary><b>Cursor</b></summary>
|
|
121
|
+
<summary><b>Cursor (Click to expand)</b></summary>
|
|
122
122
|
1. Open MCP Settings (File > Preferences > Cursor Settings > MCP)
|
|
123
123
|
|
|
124
124
|
2. "+ Add a new global MCP Server" > ("Create File")
|
|
@@ -138,7 +138,7 @@ If that doesn't work, you can try cloning this repository and replace `"lean-lsp
|
|
|
138
138
|
</details>
|
|
139
139
|
|
|
140
140
|
<details>
|
|
141
|
-
<summary><b>Claude Code</b></summary>
|
|
141
|
+
<summary><b>Claude Code (Click to expand)</b></summary>
|
|
142
142
|
Run one of these commands in the root directory of your Lean project (where `lakefile.toml` is located):
|
|
143
143
|
|
|
144
144
|
```bash
|
|
@@ -166,7 +166,11 @@ For the local search tool `lean_local_search`, install [ripgrep](https://github.
|
|
|
166
166
|
|
|
167
167
|
### File interactions (LSP)
|
|
168
168
|
|
|
169
|
-
####
|
|
169
|
+
#### lean_file_outline
|
|
170
|
+
|
|
171
|
+
Get a concise outline of a Lean file showing imports and declarations with type signatures (theorems, definitions, classes, structures).
|
|
172
|
+
|
|
173
|
+
#### lean_file_contents (DEPRECATED)
|
|
170
174
|
|
|
171
175
|
Get the contents of a Lean file, optionally with line number annotations.
|
|
172
176
|
|
|
@@ -0,0 +1,15 @@
|
|
|
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=HgPuB35rMitn2Xm8SCAErsFLq15trB6VMz3FDFgmPd8,4897
|
|
4
|
+
lean_lsp_mcp/file_utils.py,sha256=kCTYQSfmV-R2cm_NCi_L8W5Dcsm0_rTOPpTtpyAin78,1365
|
|
5
|
+
lean_lsp_mcp/instructions.py,sha256=GUOCDILr5N4H_kNE5hiXtzy4Sz9tu-BnE7Y0ktXIF9M,955
|
|
6
|
+
lean_lsp_mcp/outline_utils.py,sha256=bXBpLp_QnxmvwoP2y1juCYog2eln6329MAKuOXOz0-E,7807
|
|
7
|
+
lean_lsp_mcp/search_utils.py,sha256=X2LPynDNLi767UDxbxHpMccOkbnfKJKv_HxvRNxIXM4,3984
|
|
8
|
+
lean_lsp_mcp/server.py,sha256=qf0iRVeWrrvX91EmJsgbx7DW8kwn28zMs1WyfkxCh5A,37644
|
|
9
|
+
lean_lsp_mcp/utils.py,sha256=qY2Ef82SmD46y0IgyX1jimigkgr6Q8-Hrme-yUYSBGo,11094
|
|
10
|
+
lean_lsp_mcp-0.14.0.dist-info/licenses/LICENSE,sha256=CQlxnf0tQyoVrBE93JYvAUYxv6Z5Yg6sX0pwogOkFvo,1071
|
|
11
|
+
lean_lsp_mcp-0.14.0.dist-info/METADATA,sha256=8GGWXkG-1GH4Vyzo32QFITO1OhJrumsijULxDILlvcI,19855
|
|
12
|
+
lean_lsp_mcp-0.14.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
13
|
+
lean_lsp_mcp-0.14.0.dist-info/entry_points.txt,sha256=nQbvwctWkWD7I-2f4VrdVQBZYGUw8CnUnFC6QjXxOSE,51
|
|
14
|
+
lean_lsp_mcp-0.14.0.dist-info/top_level.txt,sha256=LGEK0lgMSNPIQ6mG8EO-adaZEGPi_0daDs004epOTF0,13
|
|
15
|
+
lean_lsp_mcp-0.14.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=1Xgh_fkdRpz-lqdl6kpETdwEu-IGRQGIdSdU7o9t5hc,853
|
|
6
|
-
lean_lsp_mcp/search_utils.py,sha256=X2LPynDNLi767UDxbxHpMccOkbnfKJKv_HxvRNxIXM4,3984
|
|
7
|
-
lean_lsp_mcp/server.py,sha256=g_CcgMl4aOE0MqcNv5O0DvSR7mN_fSdmJQAddDSclfA,36261
|
|
8
|
-
lean_lsp_mcp/utils.py,sha256=YE6o6eswOi47AYojITQ2RcR-DspqKtgACeV-O7xgOKM,10554
|
|
9
|
-
lean_lsp_mcp-0.13.1.dist-info/licenses/LICENSE,sha256=CQlxnf0tQyoVrBE93JYvAUYxv6Z5Yg6sX0pwogOkFvo,1071
|
|
10
|
-
lean_lsp_mcp-0.13.1.dist-info/METADATA,sha256=Y7CElIvCA6scvnk2PJyMANjW1dyg9VFk0-SBwpk4wU8,19626
|
|
11
|
-
lean_lsp_mcp-0.13.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
12
|
-
lean_lsp_mcp-0.13.1.dist-info/entry_points.txt,sha256=nQbvwctWkWD7I-2f4VrdVQBZYGUw8CnUnFC6QjXxOSE,51
|
|
13
|
-
lean_lsp_mcp-0.13.1.dist-info/top_level.txt,sha256=LGEK0lgMSNPIQ6mG8EO-adaZEGPi_0daDs004epOTF0,13
|
|
14
|
-
lean_lsp_mcp-0.13.1.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|