mirror of
https://github.com/GNS3/gns3-server.git
synced 2026-11-03 05:51:15 +02:00
fix(mcp): remove unreliable node_reload / node_reload_all tools
For Docker nodes, reload bottoms out as a raw POST /containers/{id}/restart
to the Docker daemon, bypassing GNS3's start/stop lifecycle (uBridge
re-attach, console servers, NIC setup). The container restarts at the
Docker level but GNS3's plumbing goes out of sync, and the daemon call can
block up to the controller's 240s timeout — manifesting as "reload hangs /
no response". stop+start runs the full lifecycle and is reliable.
Remove the node_reload and node_reload_all MCP tools (tool functions,
handlers, NODE_TOOLS entry, tests, docs). The underlying REST endpoints
(POST /nodes/{id}/reload, POST /nodes/reload) are kept for native API
users. MCP callers should use node_stop + node_start (partial) or
close/open project (full restart) instead.
This commit is contained in:
5 files changed
+4
-78
No files matched your search
@@ -86,13 +86,13 @@ from .device_config import (
|
||||
)
|
||||
from .nodes import (
|
||||
get_nodes_handler, get_node_handler, start_node_handler,
|
||||
stop_node_handler, reload_node_handler, suspend_node_handler,
|
||||
stop_node_handler, suspend_node_handler,
|
||||
create_node_handler, delete_node_handler, update_node_handler,
|
||||
get_node_console_info_handler,
|
||||
list_node_files_handler, get_node_file_handler,
|
||||
write_node_file_handler, delete_node_file_handler,
|
||||
start_all_nodes_handler, stop_all_nodes_handler,
|
||||
suspend_all_nodes_handler, reload_all_nodes_handler,
|
||||
suspend_all_nodes_handler,
|
||||
duplicate_node_handler, isolate_node_handler,
|
||||
unisolate_node_handler, get_node_links_handler,
|
||||
)
|
||||
@@ -470,20 +470,6 @@ async def node_stop(
|
||||
params["node_id"] = node_id
|
||||
return await asyncio.to_thread(_run_handler_sync, stop_node_handler, params)
|
||||
|
||||
@mcp.tool()
|
||||
async def node_reload(
|
||||
project_id: Annotated[str, Field(description="UUID of the project")],
|
||||
node_id: Annotated[str | None, Field(description="Node UUID (single mode)")] = None,
|
||||
node_ids: Annotated[list[str] | None, Field(description="Batch mode: [\"uuid1\",\"uuid2\"] — reload multiple nodes in parallel")] = None,
|
||||
) -> list[dict[str, Any]]:
|
||||
"""Reload (restart) one or more nodes. Provide node_id for single, or node_ids for batch."""
|
||||
params = {"project_id": project_id}
|
||||
if node_ids:
|
||||
params["node_ids"] = node_ids
|
||||
else:
|
||||
params["node_id"] = node_id
|
||||
return await asyncio.to_thread(_run_handler_sync, reload_node_handler, params)
|
||||
|
||||
@mcp.tool()
|
||||
async def node_suspend(
|
||||
project_id: Annotated[str, Field(description="UUID of the project")],
|
||||
@@ -884,16 +870,6 @@ async def node_suspend_all(
|
||||
})
|
||||
|
||||
|
||||
@mcp.tool()
|
||||
async def node_reload_all(
|
||||
project_id: Annotated[str, Field(description="UUID of the project")],
|
||||
) -> list[dict[str, Any]]:
|
||||
"""Reload (restart) all nodes in a project."""
|
||||
return await asyncio.to_thread(_run_handler_sync, reload_all_nodes_handler, {
|
||||
"project_id": project_id,
|
||||
})
|
||||
|
||||
|
||||
@mcp.tool()
|
||||
async def node_duplicate(
|
||||
project_id: Annotated[str, Field(description="UUID of the project")],
|
||||
|
||||
@@ -159,24 +159,6 @@ def stop_node_handler(params: dict[str, Any], gns3_ctx: dict[str, Any]) -> dict[
|
||||
return {"message": f"Node {node_id} stopped", "node_id": node_id}
|
||||
|
||||
|
||||
def reload_node_handler(params: dict[str, Any], gns3_ctx: dict[str, Any]) -> dict[str, Any]:
|
||||
project_id = params.get("project_id")
|
||||
if not project_id:
|
||||
return {"error": "project_id is required"}
|
||||
node_ids = params.get("node_ids")
|
||||
if node_ids:
|
||||
if not isinstance(node_ids, list):
|
||||
return {"error": "node_ids must be a list"}
|
||||
conn = _get_connector(gns3_ctx)
|
||||
return _batch_lifecycle(project_id, node_ids, "reload", conn, "reloaded")
|
||||
node_id = params.get("node_id")
|
||||
if not node_id:
|
||||
return {"error": "node_id or node_ids is required"}
|
||||
conn = _get_connector(gns3_ctx)
|
||||
conn.http_call("post", f"{conn.base_url}/projects/{project_id}/nodes/{node_id}/reload")
|
||||
return {"message": f"Node {node_id} reloaded", "node_id": node_id}
|
||||
|
||||
|
||||
def suspend_node_handler(params: dict[str, Any], gns3_ctx: dict[str, Any]) -> dict[str, Any]:
|
||||
project_id = params.get("project_id")
|
||||
if not project_id:
|
||||
@@ -452,15 +434,6 @@ def suspend_all_nodes_handler(params: dict[str, Any], gns3_ctx: dict[str, Any])
|
||||
return {"message": "All nodes suspended", "project_id": project_id}
|
||||
|
||||
|
||||
def reload_all_nodes_handler(params: dict[str, Any], gns3_ctx: dict[str, Any]) -> dict[str, Any]:
|
||||
project_id = params.get("project_id")
|
||||
if not project_id:
|
||||
return {"error": "project_id is required"}
|
||||
conn = _get_connector(gns3_ctx)
|
||||
conn.http_call("post", f"{conn.base_url}/projects/{project_id}/nodes/reload")
|
||||
return {"message": "All nodes reloaded", "project_id": project_id}
|
||||
|
||||
|
||||
def duplicate_node_handler(params: dict[str, Any], gns3_ctx: dict[str, Any]) -> dict[str, Any]:
|
||||
project_id = params.get("project_id")
|
||||
node_id = params.get("node_id")
|
||||
@@ -556,19 +529,6 @@ NODE_TOOLS = [
|
||||
},
|
||||
"handler": stop_node_handler,
|
||||
},
|
||||
{
|
||||
"name": "reload_node",
|
||||
"description": "Reload (restart) a node in a project",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"project_id": {"type": "string", "description": "Project UUID"},
|
||||
"node_id": {"type": "string", "description": "Node UUID"},
|
||||
},
|
||||
"required": ["project_id", "node_id"],
|
||||
},
|
||||
"handler": reload_node_handler,
|
||||
},
|
||||
{
|
||||
"name": "suspend_node",
|
||||
"description": "Suspend a node in a project",
|
||||
|
||||
Reference in new issue
Block a user