From cd8bb7cca255cdd377379985deb66c85750b3566 Mon Sep 17 00:00:00 2001 From: YueGuobin Date: Sat, 13 Jun 2026 23:54:24 +0800 Subject: [PATCH] feat: Add fields filter to node_list - Matches same VALID_NODE_FIELDS as node_get - Return only selected fields per node to save tokens --- gns3server/api/routes/mcp/__init__.py | 9 ++++++--- gns3server/api/routes/mcp/nodes.py | 11 +++++++++++ 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/gns3server/api/routes/mcp/__init__.py b/gns3server/api/routes/mcp/__init__.py index 76872b624..d6036ecc9 100644 --- a/gns3server/api/routes/mcp/__init__.py +++ b/gns3server/api/routes/mcp/__init__.py @@ -396,9 +396,12 @@ async def project_readme_update( # ── Node tools ──────────────────────────────────────────────────────── @mcp.tool() -async def node_list(project_id: str) -> list[dict[str, Any]]: - """List all nodes in a project.""" - return await asyncio.to_thread(_run_handler_sync, get_nodes_handler, {"project_id": project_id}) +async def node_list( + project_id: Annotated[str, Field(description="UUID of the project")], + fields: Annotated[list[str] | None, Field(description="Optional: return only these fields per node. 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]]: + """List all nodes in a project. Use fields=[] to return only what you need.""" + return await asyncio.to_thread(_run_handler_sync, get_nodes_handler, {"project_id": project_id, "fields": fields}) @mcp.tool() diff --git a/gns3server/api/routes/mcp/nodes.py b/gns3server/api/routes/mcp/nodes.py index b034d9caa..7ba701d87 100644 --- a/gns3server/api/routes/mcp/nodes.py +++ b/gns3server/api/routes/mcp/nodes.py @@ -58,6 +58,17 @@ def get_nodes_handler(params: dict[str, Any], gns3_ctx: dict[str, Any]) -> dict[ return {"error": "project_id is required"} conn = _get_connector(gns3_ctx) nodes = conn.http_call("get", f"{conn.base_url}/projects/{project_id}/nodes").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), + } + nodes = [{k: n[k] for k in fields if k in n} for n in nodes] return {"nodes": nodes, "count": len(nodes)}