diff --git a/mcp/app/tools.py b/mcp/app/tools.py index 85ef890..30dac22 100644 --- a/mcp/app/tools.py +++ b/mcp/app/tools.py @@ -228,7 +228,12 @@ async def _dispatch(name: str, args: dict) -> dict: return await backend.get("/api/v1/nodes") if name == "list_pending_devices": - return await backend.get("/api/v1/scan/pending") + # Backend /scan/pending returns the whole inventory: approved rows stay + # listed so the frontend can show a canvas-presence badge. This tool + # promises only devices "not yet approved or hidden", so filter to + # actual pending rows (legacy rows without a status count as pending). + devices = await backend.get("/api/v1/scan/pending") + return [d for d in devices if d.get("status", "pending") == "pending"] if name == "list_designs": return await backend.get("/api/v1/designs") diff --git a/mcp/tests/test_tools.py b/mcp/tests/test_tools.py index db5ad1f..a8dc37f 100644 --- a/mcp/tests/test_tools.py +++ b/mcp/tests/test_tools.py @@ -266,3 +266,19 @@ def test_create_design_schema_requires_name(): async def test_unknown_tool(): with pytest.raises(ValueError, match="Unknown tool"): await _dispatch("nonexistent", {}) + + +@pytest.mark.anyio +async def test_list_pending_devices_filters_non_pending(mock_backend): + """The tool promises devices *not yet approved or hidden*; the backend + endpoint returns the whole inventory including approved rows (they carry + the canvas-presence badge). The tool must filter to status == "pending" + and keep legacy rows that lack the field.""" + mock_backend.get = AsyncMock(return_value=[ + {"id": "p1", "ip": "192.168.1.50", "status": "pending"}, + {"id": "a1", "ip": "192.168.1.60", "status": "approved"}, + {"id": "h1", "ip": "192.168.1.70", "status": "hidden"}, + {"id": "legacy", "ip": "192.168.1.80"}, + ]) + result = await _dispatch("list_pending_devices", {}) + assert [d["id"] for d in result] == ["p1", "legacy"]