lean-lsp-mcp 0.13.1__tar.gz → 0.13.2__tar.gz

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.
Files changed (26) hide show
  1. {lean_lsp_mcp-0.13.1/src/lean_lsp_mcp.egg-info → lean_lsp_mcp-0.13.2}/PKG-INFO +3 -3
  2. {lean_lsp_mcp-0.13.1 → lean_lsp_mcp-0.13.2}/pyproject.toml +3 -3
  3. {lean_lsp_mcp-0.13.1 → lean_lsp_mcp-0.13.2}/src/lean_lsp_mcp/client_utils.py +8 -7
  4. {lean_lsp_mcp-0.13.1 → lean_lsp_mcp-0.13.2/src/lean_lsp_mcp.egg-info}/PKG-INFO +3 -3
  5. {lean_lsp_mcp-0.13.1 → lean_lsp_mcp-0.13.2}/src/lean_lsp_mcp.egg-info/requires.txt +2 -2
  6. {lean_lsp_mcp-0.13.1 → lean_lsp_mcp-0.13.2}/tests/test_diagnostic_line_range.py +29 -127
  7. {lean_lsp_mcp-0.13.1 → lean_lsp_mcp-0.13.2}/tests/test_file_caching.py +18 -24
  8. {lean_lsp_mcp-0.13.1 → lean_lsp_mcp-0.13.2}/LICENSE +0 -0
  9. {lean_lsp_mcp-0.13.1 → lean_lsp_mcp-0.13.2}/README.md +0 -0
  10. {lean_lsp_mcp-0.13.1 → lean_lsp_mcp-0.13.2}/setup.cfg +0 -0
  11. {lean_lsp_mcp-0.13.1 → lean_lsp_mcp-0.13.2}/src/lean_lsp_mcp/__init__.py +0 -0
  12. {lean_lsp_mcp-0.13.1 → lean_lsp_mcp-0.13.2}/src/lean_lsp_mcp/__main__.py +0 -0
  13. {lean_lsp_mcp-0.13.1 → lean_lsp_mcp-0.13.2}/src/lean_lsp_mcp/file_utils.py +0 -0
  14. {lean_lsp_mcp-0.13.1 → lean_lsp_mcp-0.13.2}/src/lean_lsp_mcp/instructions.py +0 -0
  15. {lean_lsp_mcp-0.13.1 → lean_lsp_mcp-0.13.2}/src/lean_lsp_mcp/search_utils.py +0 -0
  16. {lean_lsp_mcp-0.13.1 → lean_lsp_mcp-0.13.2}/src/lean_lsp_mcp/server.py +0 -0
  17. {lean_lsp_mcp-0.13.1 → lean_lsp_mcp-0.13.2}/src/lean_lsp_mcp/utils.py +0 -0
  18. {lean_lsp_mcp-0.13.1 → lean_lsp_mcp-0.13.2}/src/lean_lsp_mcp.egg-info/SOURCES.txt +0 -0
  19. {lean_lsp_mcp-0.13.1 → lean_lsp_mcp-0.13.2}/src/lean_lsp_mcp.egg-info/dependency_links.txt +0 -0
  20. {lean_lsp_mcp-0.13.1 → lean_lsp_mcp-0.13.2}/src/lean_lsp_mcp.egg-info/entry_points.txt +0 -0
  21. {lean_lsp_mcp-0.13.1 → lean_lsp_mcp-0.13.2}/src/lean_lsp_mcp.egg-info/top_level.txt +0 -0
  22. {lean_lsp_mcp-0.13.1 → lean_lsp_mcp-0.13.2}/tests/test_editor_tools.py +0 -0
  23. {lean_lsp_mcp-0.13.1 → lean_lsp_mcp-0.13.2}/tests/test_logging.py +0 -0
  24. {lean_lsp_mcp-0.13.1 → lean_lsp_mcp-0.13.2}/tests/test_misc_tools.py +0 -0
  25. {lean_lsp_mcp-0.13.1 → lean_lsp_mcp-0.13.2}/tests/test_project_tools.py +0 -0
  26. {lean_lsp_mcp-0.13.1 → lean_lsp_mcp-0.13.2}/tests/test_search_tools.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: lean-lsp-mcp
3
- Version: 0.13.1
3
+ Version: 0.13.2
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.3
12
- Requires-Dist: mcp[cli]==1.21.1
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"
@@ -1,14 +1,14 @@
1
1
  [project]
2
2
  name = "lean-lsp-mcp"
3
- version = "0.13.1"
3
+ version = "0.13.2"
4
4
  description = "Lean Theorem Prover MCP"
5
5
  authors = [{name="Oliver Dressler", email="hey@oli.show"}]
6
6
  readme = "README.md"
7
7
  requires-python = ">=3.10"
8
8
  license = "MIT"
9
9
  dependencies = [
10
- "leanclient==0.5.3",
11
- "mcp[cli]==1.21.1",
10
+ "leanclient==0.5.5",
11
+ "mcp[cli]==1.21.2",
12
12
  "orjson>=3.11.1",
13
13
  ]
14
14
 
@@ -36,14 +36,15 @@ 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
- try:
41
- client = LeanLSPClient(lean_project_path)
42
- logger.info(f"Connected to Lean language server at {lean_project_path}")
43
- except Exception as e:
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,
44
+ initial_build=False,
45
+ prevent_cache_get=prevent_cache
46
+ )
47
+ logger.info(f"Connected to Lean language server at {lean_project_path}")
47
48
  build_output = output.get_output()
48
49
  if build_output:
49
50
  logger.debug(f"Build output: {build_output}")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: lean-lsp-mcp
3
- Version: 0.13.1
3
+ Version: 0.13.2
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.3
12
- Requires-Dist: mcp[cli]==1.21.1
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"
@@ -1,5 +1,5 @@
1
- leanclient==0.5.3
2
- mcp[cli]==1.21.1
1
+ leanclient==0.5.5
2
+ mcp[cli]==1.21.2
3
3
  orjson>=3.11.1
4
4
 
5
5
  [dev]
@@ -10,7 +10,7 @@ import pytest
10
10
  from tests.helpers.mcp_client import MCPClient, result_text
11
11
 
12
12
 
13
- @pytest.fixture()
13
+ @pytest.fixture(scope="module")
14
14
  def diagnostic_file(test_project_path: Path) -> Path:
15
15
  path = test_project_path / "DiagnosticTest.lean"
16
16
  content = textwrap.dedent(
@@ -34,46 +34,30 @@ def diagnostic_file(test_project_path: Path) -> Path:
34
34
  trivial
35
35
  """
36
36
  ).strip()
37
- path.write_text(content + "\n", encoding="utf-8")
37
+ if not path.exists() or path.read_text(encoding="utf-8") != content + "\n":
38
+ path.write_text(content + "\n", encoding="utf-8")
38
39
  return path
39
40
 
40
41
 
41
42
  @pytest.mark.asyncio
42
- async def test_diagnostic_messages_without_line_range(
43
+ async def test_diagnostic_messages_line_filtering(
43
44
  mcp_client_factory: Callable[[], AsyncContextManager[MCPClient]],
44
45
  diagnostic_file: Path,
45
46
  ) -> None:
46
- """Test getting all diagnostic messages without line range filtering."""
47
+ """Test all line range filtering scenarios in one client session."""
47
48
  async with mcp_client_factory() as client:
49
+ # Test 1: Get all diagnostic messages without line range filtering
48
50
  diagnostics = await client.call_tool(
49
51
  "lean_diagnostic_messages",
50
52
  {"file_path": str(diagnostic_file)},
51
53
  )
52
54
  diag_text = result_text(diagnostics)
53
-
54
55
  # Should contain both errors
55
56
  assert "string" in diag_text.lower() or "error" in diag_text.lower()
56
- # Check that multiple diagnostics are returned (at least the two errors we created)
57
57
  assert diag_text.count("severity") >= 2
58
-
59
-
60
- @pytest.mark.asyncio
61
- async def test_diagnostic_messages_with_start_line(
62
- mcp_client_factory: Callable[[], AsyncContextManager[MCPClient]],
63
- diagnostic_file: Path,
64
- ) -> None:
65
- """Test getting diagnostic messages starting from a specific line."""
66
- async with mcp_client_factory() as client:
67
- # First get all diagnostics to see what we have
68
- all_diagnostics = await client.call_tool(
69
- "lean_diagnostic_messages",
70
- {
71
- "file_path": str(diagnostic_file),
72
- },
73
- )
74
- all_diag_text = result_text(all_diagnostics)
75
-
76
- # Get diagnostics starting from line 10 (should only include the second error)
58
+ all_diag_text = diag_text
59
+
60
+ # Test 2: Get diagnostics starting from line 10
77
61
  diagnostics = await client.call_tool(
78
62
  "lean_diagnostic_messages",
79
63
  {
@@ -82,35 +66,15 @@ async def test_diagnostic_messages_with_start_line(
82
66
  },
83
67
  )
84
68
  diag_text = result_text(diagnostics)
85
-
86
69
  # Should contain the second error (line 13: anotherError)
87
70
  assert "123" in diag_text or "error" in diag_text.lower()
88
- # Should have fewer diagnostics than all_diagnostics
89
71
  assert len(diag_text) < len(all_diag_text)
90
-
91
-
92
- @pytest.mark.asyncio
93
- async def test_diagnostic_messages_with_line_range(
94
- mcp_client_factory: Callable[[], AsyncContextManager[MCPClient]],
95
- diagnostic_file: Path,
96
- ) -> None:
97
- """Test getting diagnostic messages for a specific line range."""
98
- async with mcp_client_factory() as client:
99
- # First, get all diagnostics to see what lines they're actually on
100
- all_diagnostics = await client.call_tool(
101
- "lean_diagnostic_messages",
102
- {"file_path": str(diagnostic_file)},
103
- )
104
- all_diag_text = result_text(all_diagnostics)
105
-
106
- # Extract line numbers from the diagnostics (format: "l7c23-l7c31")
72
+
73
+ # Test 3: Get diagnostics for specific line range
107
74
  import re
108
-
109
75
  line_matches = re.findall(r"l(\d+)c", all_diag_text)
110
76
  if line_matches:
111
77
  first_error_line = int(line_matches[0])
112
-
113
- # Get diagnostics only up to that error line
114
78
  diagnostics = await client.call_tool(
115
79
  "lean_diagnostic_messages",
116
80
  {
@@ -120,21 +84,10 @@ async def test_diagnostic_messages_with_line_range(
120
84
  },
121
85
  )
122
86
  diag_text = result_text(diagnostics)
123
-
124
- # Should contain the first error
125
87
  assert "string" in diag_text.lower() or len(diag_text) > 0
126
- # Should be fewer diagnostics than all
127
88
  assert len(diag_text) < len(all_diag_text)
128
-
129
-
130
- @pytest.mark.asyncio
131
- async def test_diagnostic_messages_with_no_errors_in_range(
132
- mcp_client_factory: Callable[[], AsyncContextManager[MCPClient]],
133
- diagnostic_file: Path,
134
- ) -> None:
135
- """Test getting diagnostic messages for a range with no errors."""
136
- async with mcp_client_factory() as client:
137
- # Get diagnostics only for lines 14-17 (valid theorem, should have no errors)
89
+
90
+ # Test 4: Get diagnostics for range with no errors (lines 14-17)
138
91
  diagnostics = await client.call_tool(
139
92
  "lean_diagnostic_messages",
140
93
  {
@@ -144,9 +97,6 @@ async def test_diagnostic_messages_with_no_errors_in_range(
144
97
  },
145
98
  )
146
99
  diag_text = result_text(diagnostics)
147
-
148
- # Should indicate no errors or be empty
149
- # The exact format depends on how the tool formats an empty result
150
100
  assert (
151
101
  "no" in diag_text.lower()
152
102
  or len(diag_text.strip()) == 0
@@ -154,7 +104,7 @@ async def test_diagnostic_messages_with_no_errors_in_range(
154
104
  )
155
105
 
156
106
 
157
- @pytest.fixture()
107
+ @pytest.fixture(scope="module")
158
108
  def declaration_diagnostic_file(test_project_path: Path) -> Path:
159
109
  """Create a test file with multiple declarations, some with errors."""
160
110
  path = test_project_path / "DeclarationDiagnosticTest.lean"
@@ -175,29 +125,28 @@ def declaration_diagnostic_file(test_project_path: Path) -> Path:
175
125
  def anotherValidFunction : String := "hello"
176
126
  """
177
127
  ).strip()
178
- path.write_text(content + "\n", encoding="utf-8")
128
+ if not path.exists() or path.read_text(encoding="utf-8") != content + "\n":
129
+ path.write_text(content + "\n", encoding="utf-8")
179
130
  return path
180
131
 
181
132
 
182
133
  @pytest.mark.asyncio
183
- async def test_diagnostic_messages_with_declaration_name_valid(
134
+ async def test_diagnostic_messages_declaration_filtering(
184
135
  mcp_client_factory: Callable[[], AsyncContextManager[MCPClient]],
185
136
  declaration_diagnostic_file: Path,
186
137
  ) -> None:
187
- """Test filtering diagnostics by a specific declaration name."""
138
+ """Test all declaration-based filtering scenarios in one client session."""
188
139
  async with mcp_client_factory() as client:
189
- # Get all diagnostics first to verify file has errors
140
+ # Test 1: Get all diagnostics first to verify file has errors
190
141
  all_diagnostics = await client.call_tool(
191
142
  "lean_diagnostic_messages",
192
143
  {"file_path": str(declaration_diagnostic_file)},
193
144
  )
194
145
  all_diag_text = result_text(all_diagnostics)
195
-
196
- # File should have diagnostics (contains intentional errors)
197
146
  assert len(all_diag_text) > 0
198
147
  assert "string" in all_diag_text.lower() or "type" in all_diag_text.lower()
199
148
 
200
- # Get diagnostics for firstTheorem only
149
+ # Test 2: Get diagnostics for firstTheorem only
201
150
  diagnostics = await client.call_tool(
202
151
  "lean_diagnostic_messages",
203
152
  {
@@ -206,33 +155,10 @@ async def test_diagnostic_messages_with_declaration_name_valid(
206
155
  },
207
156
  )
208
157
  diag_text = result_text(diagnostics)
209
-
210
- # Should contain error from firstTheorem
211
- # The exact error message may vary, but should reference the theorem
212
158
  assert len(diag_text) > 0
213
-
214
- # Filtered diagnostics should be shorter than or equal to all diagnostics
215
159
  assert len(diag_text) <= len(all_diag_text)
216
160
 
217
-
218
- @pytest.mark.asyncio
219
- async def test_diagnostic_messages_with_declaration_name_with_errors(
220
- mcp_client_factory: Callable[[], AsyncContextManager[MCPClient]],
221
- declaration_diagnostic_file: Path,
222
- ) -> None:
223
- """Test filtering by declaration that has type errors."""
224
- async with mcp_client_factory() as client:
225
- # Get all diagnostics first to verify file has errors
226
- all_diagnostics = await client.call_tool(
227
- "lean_diagnostic_messages",
228
- {"file_path": str(declaration_diagnostic_file)},
229
- )
230
- all_diag_text = result_text(all_diagnostics)
231
-
232
- # File should have diagnostics (contains intentional errors)
233
- assert len(all_diag_text) > 0
234
-
235
- # Get diagnostics for secondTheorem (has type error in statement)
161
+ # Test 3: Get diagnostics for secondTheorem (has type error in statement)
236
162
  diagnostics = await client.call_tool(
237
163
  "lean_diagnostic_messages",
238
164
  {
@@ -241,20 +167,10 @@ async def test_diagnostic_messages_with_declaration_name_with_errors(
241
167
  },
242
168
  )
243
169
  diag_text = result_text(diagnostics)
244
-
245
- # secondTheorem has type errors, should have diagnostics
246
170
  assert len(diag_text) > 0
247
171
  assert isinstance(diag_text, str)
248
172
 
249
-
250
- @pytest.mark.asyncio
251
- async def test_diagnostic_messages_with_declaration_name_no_errors(
252
- mcp_client_factory: Callable[[], AsyncContextManager[MCPClient]],
253
- declaration_diagnostic_file: Path,
254
- ) -> None:
255
- """Test filtering by declaration that has no errors."""
256
- async with mcp_client_factory() as client:
257
- # Get diagnostics for validFunction (no errors)
173
+ # Test 4: Get diagnostics for validFunction (no errors)
258
174
  diagnostics = await client.call_tool(
259
175
  "lean_diagnostic_messages",
260
176
  {
@@ -263,8 +179,6 @@ async def test_diagnostic_messages_with_declaration_name_no_errors(
263
179
  },
264
180
  )
265
181
  diag_text = result_text(diagnostics)
266
-
267
- # Should indicate no errors or be empty
268
182
  assert (
269
183
  "no" in diag_text.lower()
270
184
  or len(diag_text.strip()) == 0
@@ -273,13 +187,13 @@ async def test_diagnostic_messages_with_declaration_name_no_errors(
273
187
 
274
188
 
275
189
  @pytest.mark.asyncio
276
- async def test_diagnostic_messages_with_nonexistent_declaration(
190
+ async def test_diagnostic_messages_declaration_edge_cases(
277
191
  mcp_client_factory: Callable[[], AsyncContextManager[MCPClient]],
278
192
  declaration_diagnostic_file: Path,
279
193
  ) -> None:
280
- """Test error handling when declaration name doesn't exist."""
194
+ """Test edge cases for declaration-based filtering."""
281
195
  async with mcp_client_factory() as client:
282
- # Try to get diagnostics for non-existent declaration
196
+ # Test 1: Non-existent declaration
283
197
  result = await client.call_tool(
284
198
  "lean_diagnostic_messages",
285
199
  {
@@ -288,21 +202,10 @@ async def test_diagnostic_messages_with_nonexistent_declaration(
288
202
  },
289
203
  )
290
204
  result_str = result_text(result)
291
-
292
- # Should return error message about declaration not found
293
205
  assert "not found" in result_str.lower()
294
206
  assert "nonExistentTheorem" in result_str
295
207
 
296
-
297
- @pytest.mark.asyncio
298
- async def test_diagnostic_messages_declaration_name_takes_precedence(
299
- mcp_client_factory: Callable[[], AsyncContextManager[MCPClient]],
300
- declaration_diagnostic_file: Path,
301
- ) -> None:
302
- """Test that declaration_name takes precedence over start_line/end_line."""
303
- async with mcp_client_factory() as client:
304
- # Use declaration_name with conflicting start_line/end_line
305
- # declaration_name should take precedence
208
+ # Test 2: declaration_name takes precedence over start_line/end_line
306
209
  diagnostics = await client.call_tool(
307
210
  "lean_diagnostic_messages",
308
211
  {
@@ -313,13 +216,11 @@ async def test_diagnostic_messages_declaration_name_takes_precedence(
313
216
  },
314
217
  )
315
218
  diag_text = result_text(diagnostics)
316
-
317
219
  # Should get diagnostics for firstTheorem, not lines 1-3
318
- # (which would only include imports)
319
220
  assert len(diag_text) > 0
320
221
 
321
222
 
322
- @pytest.fixture()
223
+ @pytest.fixture(scope="module")
323
224
  def kernel_error_file(test_project_path: Path) -> Path:
324
225
  """File with kernel error as first error (issue #63)."""
325
226
  path = test_project_path / "KernelErrorTest.lean"
@@ -334,7 +235,8 @@ def kernel_error_file(test_project_path: Path) -> Path:
334
235
  lemma test_lemma : False := by rfl
335
236
  """
336
237
  ).strip()
337
- path.write_text(content + "\n", encoding="utf-8")
238
+ if not path.exists() or path.read_text(encoding="utf-8") != content + "\n":
239
+ path.write_text(content + "\n", encoding="utf-8")
338
240
  return path
339
241
 
340
242
 
@@ -25,19 +25,34 @@ theorem cachedTheorem : cachedValue = 42 := by rfl
25
25
 
26
26
 
27
27
  @pytest.mark.asyncio
28
- async def test_disk_changes_detected(
28
+ async def test_file_caching(
29
29
  mcp_client_factory: Callable[[], AsyncContextManager[MCPClient]],
30
30
  cache_test_file: Path,
31
31
  ) -> None:
32
- """Disk changes must be detected and reprocessed correctly."""
32
+ """Test file caching: disk changes detected and tools share state correctly."""
33
33
 
34
34
  async with mcp_client_factory() as client:
35
+ # Test 1: Multiple tools share file state correctly
36
+ await client.call_tool(
37
+ "lean_diagnostic_messages", {"file_path": str(cache_test_file)}
38
+ )
39
+ await client.call_tool(
40
+ "lean_goal", {"file_path": str(cache_test_file), "line": 5}
41
+ )
42
+ hover = await client.call_tool(
43
+ "lean_hover_info",
44
+ {"file_path": str(cache_test_file), "line": 3, "column": 5},
45
+ )
46
+ assert "cachedValue" in result_text(hover)
47
+
48
+ # Test 2: Disk changes are detected and reprocessed correctly
35
49
  goal1 = await client.call_tool(
36
50
  "lean_goal", {"file_path": str(cache_test_file), "line": 5}
37
51
  )
38
52
  result1 = result_text(goal1)
39
53
  assert "no goals" in result1.lower()
40
54
 
55
+ # Modify file on disk
41
56
  cache_test_file.write_text(
42
57
  """import Mathlib
43
58
 
@@ -48,6 +63,7 @@ theorem cachedTheorem : cachedValue = 42 := by sorry
48
63
  encoding="utf-8",
49
64
  )
50
65
 
66
+ # Verify change is detected
51
67
  goal2 = await client.call_tool(
52
68
  "lean_goal", {"file_path": str(cache_test_file), "line": 5}
53
69
  )
@@ -56,25 +72,3 @@ theorem cachedTheorem : cachedValue = 42 := by sorry
56
72
  assert "cachedValue = 42" in result2, (
57
73
  f"Should show goal at sorry, got: {result2}"
58
74
  )
59
-
60
-
61
- @pytest.mark.asyncio
62
- async def test_multiple_tools_share_file(
63
- mcp_client_factory: Callable[[], AsyncContextManager[MCPClient]],
64
- cache_test_file: Path,
65
- ) -> None:
66
- """Different tools must reuse cached file state correctly."""
67
-
68
- async with mcp_client_factory() as client:
69
- await client.call_tool(
70
- "lean_diagnostic_messages", {"file_path": str(cache_test_file)}
71
- )
72
- await client.call_tool(
73
- "lean_goal", {"file_path": str(cache_test_file), "line": 5}
74
- )
75
- hover = await client.call_tool(
76
- "lean_hover_info",
77
- {"file_path": str(cache_test_file), "line": 3, "column": 5},
78
- )
79
-
80
- assert "cachedValue" in result_text(hover)
File without changes
File without changes
File without changes