diff --git a/backend/app/schemas/nodes.py b/backend/app/schemas/nodes.py index bd3cad3..9f71db3 100644 --- a/backend/app/schemas/nodes.py +++ b/backend/app/schemas/nodes.py @@ -42,6 +42,7 @@ class NodeUpdate(BaseModel): notes: str | None = None pos_x: float | None = None pos_y: float | None = None + parent_id: str | None = None container_mode: bool | None = None custom_colors: dict[str, Any] | None = None custom_icon: str | None = None diff --git a/backend/tests/test_nodes.py b/backend/tests/test_nodes.py index eafff2c..db54305 100644 --- a/backend/tests/test_nodes.py +++ b/backend/tests/test_nodes.py @@ -102,6 +102,16 @@ async def test_update_node_container_mode(client: AsyncClient, headers: dict): assert res.json()["container_mode"] is True +async def test_update_node_parent_id(client: AsyncClient, headers: dict): + parent = await client.post("/api/v1/nodes", json={"type": "proxmox", "label": "PVE", "status": "unknown"}, headers=headers) + parent_id = parent.json()["id"] + child = await client.post("/api/v1/nodes", json={"type": "lxc", "label": "Child", "status": "unknown"}, headers=headers) + child_id = child.json()["id"] + res = await client.patch(f"/api/v1/nodes/{child_id}", json={"parent_id": parent_id}, headers=headers) + assert res.status_code == 200 + assert res.json()["parent_id"] == parent_id + + async def test_create_node_requires_auth(client: AsyncClient): res = await client.post("/api/v1/nodes", json={"type": "server", "label": "N", "status": "unknown"}) assert res.status_code == 401 diff --git a/mcp/app/tools.py b/mcp/app/tools.py index 56fb933..b9b934c 100644 --- a/mcp/app/tools.py +++ b/mcp/app/tools.py @@ -24,11 +24,12 @@ def register_tools(server: Server): "type": "object", "required": ["id"], "properties": { - "id": {"type": "string"}, - "label": {"type": "string"}, - "ip": {"type": "string"}, - "hostname": {"type": "string"}, - "status": {"type": "string"}, + "id": {"type": "string"}, + "label": {"type": "string"}, + "ip": {"type": "string"}, + "hostname": {"type": "string"}, + "status": {"type": "string"}, + "parent_id": {"type": "string", "description": "ID of the parent node (e.g. Proxmox host for a VM/LXC). Pass null to detach."}, }, }), Tool(name="delete_node", description="Delete a node from the canvas", inputSchema={ diff --git a/mcp/tests/test_tools.py b/mcp/tests/test_tools.py index 80a6cee..c0b4bcf 100644 --- a/mcp/tests/test_tools.py +++ b/mcp/tests/test_tools.py @@ -26,6 +26,12 @@ async def test_update_node(mock_backend): mock_backend.patch.assert_called_once_with("/api/v1/nodes/42", {"label": "New name"}) +@pytest.mark.anyio +async def test_update_node_parent_id(mock_backend): + await _dispatch("update_node", {"id": "42", "parent_id": "proxmox-1"}) + mock_backend.patch.assert_called_once_with("/api/v1/nodes/42", {"parent_id": "proxmox-1"}) + + @pytest.mark.anyio async def test_delete_node(mock_backend): await _dispatch("delete_node", {"id": "42"})