mirror of
https://github.com/NickoScope/nickol-knx-mcp.git
synced 2026-09-30 03:41:58 +02:00
Fix infinite recursion in load_project MCP tool
The server tool function `load_project` shadowed the imported project loader, so it called itself instead of the parser -> RecursionError on every real .knxproj. Alias the import as `load_project_file` and call that. Add a regression test (server.load_project on a bad path must not raise RecursionError) and a CHANGELOG entry. Found via end-to-end testing against real ETS5/ETS6 fixtures from XKNX/xknxproject. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
bba8befcde
commit
1b0fe566f7
@@ -6,6 +6,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
### Fixed
|
||||
- **Critical:** `load_project` MCP tool recursed infinitely (`RecursionError`) on every
|
||||
real `.knxproj` because the server tool function shadowed the imported project loader.
|
||||
The tool now delegates correctly. Added a regression test that fails on recursion.
|
||||
Found via end-to-end testing against real ETS5/ETS6 project files.
|
||||
|
||||
## [0.1.0] — 2026-06-28
|
||||
|
||||
Initial public beta.
|
||||
|
||||
@@ -15,7 +15,7 @@ from typing import Any, Optional
|
||||
|
||||
from mcp.server.fastmcp import FastMCP
|
||||
|
||||
from .project import load_project, LoadedProject
|
||||
from .project import load_project as load_project_file, LoadedProject
|
||||
from .analyze import validate_naming, detect_missing_status, detect_dpt_issues
|
||||
from .generate_ha import generate_ha_yaml
|
||||
from .generate_ets import generate_ets_csv, generate_ets_xml
|
||||
@@ -69,7 +69,7 @@ def load_project(path: str, password: Optional[str] = None,
|
||||
password: Project password, if the .knxproj is protected.
|
||||
language: Optional language code (e.g. 'de-DE', 'ru-RU').
|
||||
"""
|
||||
proj = load_project(path, password=password, language=language)
|
||||
proj = load_project_file(path, password=password, language=language)
|
||||
_STATE["project"] = proj
|
||||
return {
|
||||
"loaded": True,
|
||||
|
||||
@@ -126,3 +126,21 @@ rep = build_report(proj)
|
||||
print(rep["summary"])
|
||||
print("\n--- report head ---")
|
||||
print("\n".join(rep["markdown"].splitlines()[:30]))
|
||||
|
||||
# --------------------------------------------------------------------------- #
|
||||
# Regression: the MCP server tool `load_project` must delegate to the project
|
||||
# loader, not call itself (a name-shadowing bug once caused infinite recursion).
|
||||
# Calling it on a bad path must raise a normal error, never RecursionError.
|
||||
# --------------------------------------------------------------------------- #
|
||||
print("\n=== REGRESSION: server.load_project must not recurse ===")
|
||||
import nickol_knx_mcp.server as _server
|
||||
try:
|
||||
_server.load_project("/nonexistent/__no_such__.knxproj")
|
||||
except RecursionError as exc: # pragma: no cover
|
||||
raise AssertionError(
|
||||
"server.load_project recursed — load_project name-shadowing regression"
|
||||
) from exc
|
||||
except Exception as exc:
|
||||
print(f"OK: delegated, raised {type(exc).__name__} (expected, not RecursionError)")
|
||||
else:
|
||||
print("OK: delegated (no error)")
|
||||
|
||||
Reference in New Issue
Block a user