diff --git a/gns3server/api/routes/mcp/__init__.py b/gns3server/api/routes/mcp/__init__.py index e80e2878d..76872b624 100644 --- a/gns3server/api/routes/mcp/__init__.py +++ b/gns3server/api/routes/mcp/__init__.py @@ -405,9 +405,12 @@ async def node_list(project_id: str) -> list[dict[str, Any]]: async def node_get( project_id: Annotated[str, Field(description="UUID of the project")], node_id: Annotated[str, Field(description="UUID of the node")], + fields: Annotated[list[str] | None, Field(description="Optional: return only these fields. e.g. [\"name\",\"status\"]. Available: name, status, node_type, console, console_type, console_host, node_id, project_id, compute_id, symbol, x, y, z, locked, ports, properties, command_line, node_directory, label, tags, template_id, width, height, aux, aux_type")] = None, ) -> list[dict[str, Any]]: - """Get detailed information about a specific node.""" - return await asyncio.to_thread(_run_handler_sync, get_node_handler, {"project_id": project_id, "node_id": node_id}) + """Get detailed information about a specific node. Use fields=[] to return only what you need.""" + return await asyncio.to_thread(_run_handler_sync, get_node_handler, { + "project_id": project_id, "node_id": node_id, "fields": fields, + }) @mcp.tool() async def node_start( diff --git a/gns3server/api/routes/mcp/nodes.py b/gns3server/api/routes/mcp/nodes.py index 2bbb6b11f..b034d9caa 100644 --- a/gns3server/api/routes/mcp/nodes.py +++ b/gns3server/api/routes/mcp/nodes.py @@ -61,13 +61,41 @@ def get_nodes_handler(params: dict[str, Any], gns3_ctx: dict[str, Any]) -> dict[ return {"nodes": nodes, "count": len(nodes)} +VALID_NODE_FIELDS = { + # NodeBase + "compute_id", "name", "node_type", "node_id", + "console", "console_type", "console_auto_start", + "aux", "aux_type", "properties", "label", "symbol", + "x", "y", "z", "locked", + "port_name_format", "port_segment_size", "first_port_name", + "custom_adapters", "tags", + # Node + "template_id", "project_id", "node_directory", "status", + "command_line", "width", "height", "ports", "console_host", +} + + def get_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") if not project_id or not node_id: return {"error": "project_id and node_id are required"} conn = _get_connector(gns3_ctx) - return conn.http_call("get", f"{conn.base_url}/projects/{project_id}/nodes/{node_id}").json() + node = conn.http_call("get", f"{conn.base_url}/projects/{project_id}/nodes/{node_id}").json() + + fields = params.get("fields") + if fields: + if not isinstance(fields, list): + return {"error": "fields must be a list of field names, e.g. [\"name\", \"status\"]"} + invalid = [f for f in fields if f not in VALID_NODE_FIELDS] + if invalid: + return { + "error": f"Unknown fields: {invalid}", + "available_fields": sorted(VALID_NODE_FIELDS), + } + return {k: node[k] for k in fields if k in node} + + return node def start_node_handler(params: dict[str, Any], gns3_ctx: dict[str, Any]) -> dict[str, Any]: