diff --git a/gns3server/agent/gns3_copilot/gns3_client/api_handlers.py b/gns3server/agent/gns3_copilot/gns3_client/api_handlers.py index dc674ccb8..9c31d07d0 100644 --- a/gns3server/agent/gns3_copilot/gns3_client/api_handlers.py +++ b/gns3server/agent/gns3_copilot/gns3_client/api_handlers.py @@ -900,7 +900,7 @@ def link_marker_handler(params: dict[str, Any], gns3_ctx: dict[str, Any]) -> dic if not bpf: return {"error": "bpf is required for create action"} body: dict[str, Any] = {"bpf": bpf} - for opt in ("name", "tag", "capture_node_id", "color", "highlight_duration"): + for opt in ("name", "tag", "capture_node_id", "color", "highlight_duration", "data_link_type"): if params.get(opt) is not None: body[opt] = params[opt] # direction: "tx"/"rx" set a one-way filter; "both"/omitted = no filter. diff --git a/gns3server/agent/mcp/__init__.py b/gns3server/agent/mcp/__init__.py index 191d819cf..b04a764ff 100644 --- a/gns3server/agent/mcp/__init__.py +++ b/gns3server/agent/mcp/__init__.py @@ -1035,6 +1035,7 @@ async def link_marker( capture_node_id: Annotated[str | None, Field(description="UUID of the endpoint whose uBridge hosts the marker (the observer; tx/rx are from its perspective). Must be a link endpoint and marker-capable. Omit to auto-pick.")] = None, color: Annotated[str | None, Field(description="Hex color for UI highlight, e.g. '#ff5722'")] = None, highlight_duration: Annotated[int | None, Field(description="UI highlight duration in milliseconds")] = None, + data_link_type: Annotated[str | None, Field(description="pcap link-layer type for serial links (create-only): DLT_C_HDLC / DLT_PPP_SERIAL / DLT_FRELAY / DLT_ATM_RFC1483, matching the encapsulation on the serial link. Omit = DLT_EN10MB (Ethernet). Ignored on update — changing it would invalidate the capture file.")] = None, ) -> list[dict[str, Any]]: """Manage traffic-insight markers on a link. @@ -1051,7 +1052,7 @@ async def link_marker( and cannot be modified or deleted via this tool. """ params = {"project_id": project_id, "link_id": link_id, "action": action} - for opt in ("bpf", "marker_name", "name", "tag", "enabled", "direction", "capture_node_id", "color", "highlight_duration"): + for opt in ("bpf", "marker_name", "name", "tag", "enabled", "direction", "capture_node_id", "color", "highlight_duration", "data_link_type"): val = locals().get(opt) if val is not None: params[opt] = val diff --git a/tests/agent/mcp/test_handlers.py b/tests/agent/mcp/test_handlers.py index 42738dc37..0a85e0256 100644 --- a/tests/agent/mcp/test_handlers.py +++ b/tests/agent/mcp/test_handlers.py @@ -437,6 +437,33 @@ class TestLinkMarker: json_data={"bpf": "icmp"}, ) + def test_create_data_link_type_passthrough(self, ctx): + """create passes a serial WAN encapsulation through; update ignores it.""" + from gns3server.agent.gns3_copilot.gns3_client.api_handlers import link_marker_handler + with patch(f"{AH}._get_connector") as m: + conn = _mock_conn({"name": "icmp"}) + m.return_value = conn + link_marker_handler( + {"project_id": "p", "link_id": "l", "action": "create", + "bpf": "icmp", "data_link_type": "DLT_C_HDLC"}, ctx, + ) + conn.http_call.assert_called_with( + "post", "http://192.168.1.3:3080/v3/projects/p/links/l/markers", + json_data={"bpf": "icmp", "data_link_type": "DLT_C_HDLC"}, + ) + + conn = _mock_conn({"name": "icmp"}) + m.return_value = conn + link_marker_handler( + {"project_id": "p", "link_id": "l", "action": "update", + "marker_name": "icmp", "tag": 1, "data_link_type": "DLT_PPP_SERIAL"}, ctx, + ) + # create-only: dropped from the update body + conn.http_call.assert_called_with( + "put", "http://192.168.1.3:3080/v3/projects/p/links/l/markers/icmp", + json_data={"tag": 1}, + ) + def test_create_direction_tx(self, ctx): from gns3server.agent.gns3_copilot.gns3_client.api_handlers import link_marker_handler with patch(f"{AH}._get_connector") as m: