docs(mcp): refresh filter vocabulary in the MCP/copilot tool descriptions

link_update, link_available_filters and the copilot packet-filter tool
still advertised the pre-kernel-datapath five types. List the full
thirteen with positional parameter formats, ranges and kernel-only
marking, point at link_available_filters as the per-link authority
(topology + compute capability gated), and document window_drop's
apply-relative schedule restart. Also add kernel_datapath to the link
field lists.

Description-only: handlers pass filters through unchanged.
This commit is contained in:
YueGuobin 2026-09-30 14:37:02 +08:00
parent cecaf298bb
commit 935c00802f
No known key found for this signature in database
3 changed files with 62 additions and 16 deletions

View File

@ -692,8 +692,8 @@ def available_filters_handler(params: dict[str, Any], gns3_ctx: dict[str, Any])
"""
List the packet filter types available for a link (GNS3 API v3 only).
Returns a list of filter descriptors (frequency_drop, packet_loss,
delay, corrupt, bpf) with their parameters.
Returns a list of filter descriptors with their parameters — the
authoritative, per-link answer (topology and compute-capability gated).
"""
project_id = params.get("project_id")
link_id = params.get("link_id")

View File

@ -122,10 +122,26 @@ class GNS3PacketFilterTool(BaseTool):
Common filter types for fault injection:
- "frequency_drop": Drop every Nth packet (parameter: frequency, -1 to 32767)
- "packet_loss": Packet loss percentage (parameter: chance, 0-100)
- "delay": Delay in ms with optional jitter (parameters: latency 0-32767, jitter 0-32767)
- "packet_loss": Packet loss percentage (parameters: chance 0-100, optional correlation)
- "delay": Delay in ms with optional jitter and distribution (parameters: latency 0-32767,
jitter 0-32767, distribution uniform|normal|pareto|paretonormal)
- "corrupt": Packet corruption percentage (parameter: chance, 0-100)
- "bpf": Berkeley Packet Filter (parameter: filter expression text)
- "rate": Bandwidth cap, tc-style string like "512kbit" (kernel links only)
- "reorder": Packet reordering (parameters: percentage, correlation, gap; needs delay;
kernel links only)
- "gemodel": Gilbert-Elliot bursty loss (parameters: p, r, 1-h; kernel links only)
- "duplicate": Packet duplication percentage (kernel links only)
- "seed": Reproducible netem random draws (kernel links only)
- "limit": Impairment queue depth in packets (kernel links only)
- "quota": Byte cap — after the quota, drop with a chance (parameters: bytes, chance;
kernel links only)
- "window_drop": Time-window outage (parameters: start_ms, outage_ms, chance, optional
period_ms, jitter_ms; start is relative to when the filter is applied — any filter
update restarts the schedule; kernel links only)
Filter availability depends on the link (kernel-only types need kernel-datapath links
and compute support): always check with action "get_available" before setting.
Input is a JSON object with:
- project_id (str): GNS3 project UUID

View File

@ -697,7 +697,7 @@ async def link_list(
fields: Annotated[
list[str] | None,
Field(
description='Optional: return only these fields. e.g. ["link_id","nodes"]. Available: link_id, project_id, link_type, nodes, suspend, filters, capturing, capture_file_name, link_style'
description='Optional: return only these fields. e.g. ["link_id","nodes"]. Available: link_id, project_id, link_type, kernel_datapath, nodes, suspend, filters, capturing, capture_file_name, link_style'
),
] = None,
) -> list[dict[str, Any]]:
@ -733,7 +733,7 @@ async def link_create(
list[str] | None,
Field(
description="Response fields to include (default: [link_id, link_type, nodes]). "
"Available: link_id, project_id, link_type, nodes, suspend, "
"Available: link_id, project_id, link_type, kernel_datapath, nodes, suspend, "
"link_style, filters, show_filters_icon, capturing, "
"capture_file_name, capture_file_path, capture_compute_id, wireshark"
),
@ -787,24 +787,51 @@ async def link_update(
Supported kwargs:
- suspend: boolean - Suspend or resume the link
- filters: dict - Packet filters (must use array format):
* frequency_drop: [N] - Drop every Nth packet (N: -1 to 32767)
* packet_loss: [rate] - Packet loss percentage (rate: 0 to 100)
* delay: [ms, jitter] - Latency and jitter in milliseconds
* corrupt: [rate] - Packet corruption percentage (rate: 0 to 100)
* bpf: [expression] - Berkeley Packet Filter expression
- filters: dict - Packet filters (values MUST be positional arrays). Types
marked (K) run on kernel-datapath links only (Docker-to-Docker on one
compute) and are rejected with 409 on relay links; which types a given
link accepts is authoritatively answered by link_available_filters:
* frequency_drop: [N] - Drop every Nth packet (N: -1 to 32767; -1 = drop
everything). Exact on kernel links (eBPF counter)
* packet_loss: [chance, correl?] - Loss percentage (0-100); correl (K)
makes consecutive losses dependent (bursty)
* delay: [ms, jitter?, distribution?] - Latency 1-32767 ms, jitter 0-32767
ms; distribution (K) one of uniform|normal|pareto|paretonormal, needs
jitter > 0
* corrupt: [chance] - Corruption percentage (0-100)
* bpf: [expression] - Berkeley Packet Filter expression, one per line,
any match drops
* rate (K): ["512kbit"] - Bandwidth cap, tc-style int+unit (bit/kbit/
mbit/gbit/bps/kbps/mbps, max 100gbit)
* reorder (K): [pct, correl?, gap?] - Requires delay to also be set
* gemodel (K): [p, r, 1-h] - Gilbert-Elliot bursty loss; mutually
exclusive with packet_loss
* duplicate (K): [pct, correl?] - Duplication percentage
* seed (K): [u32] - Makes netem random draws reproducible
* limit (K): [pkts] - Impairment queue depth (default 1000; raise for
low rate + long delay)
* quota (K): [bytes, pct] - After the byte quota is consumed, each
further packet drops with the chance (100 = hard cutoff)
* window_drop (K): [start_ms, outage_ms, pct, period_ms?, jitter_ms?] -
Drop with chance pct inside [start, start+outage): 3 args = single
outage (traffic passes before and after), 4 args = recurring flap
(period >= outage), 5 args = per-cycle timing randomized within +/-
jitter. start is measured from the moment this filter is applied and
ANY filter update / node restart restarts the schedule; start 0 =
outage begins immediately (active, not disabled)
Example filters:
{"filters": {"frequency_drop": [10]}}
{"filters": {"delay": [100, 10]}}
{"filters": {"packet_loss": [5]}}
{"filters": {"window_drop": [0, 2000, 100]}}
{"filters": {"delay": [50, 5], "packet_loss": [2]}}
To clear all filters: {"filters": {}}
Filters are applied **bidirectionally** — a packet crossing the link twice
(e.g. ping round-trip) is filtered in both directions independently.
For example, packet_loss: [50] gives ~75% observed loss (1 - 0.5²), not 50%.
For example, packet_loss: [50] gives ~75% observed loss (1 - 0.5²), not 50%;
window_drop pct likewise applies per direction.
ARP frames also pass through filters; at high loss/corrupt rates, pre-set
static ARP entries to avoid false "Destination Host Unreachable" errors.
"""
@ -817,8 +844,11 @@ async def link_available_filters(
project_id: Annotated[str, Field(description="UUID of the project")],
link_id: Annotated[str, Field(description="UUID of the link")],
) -> list[dict[str, Any]]:
"""List the packet filter types available for a link (frequency_drop, packet_loss, delay, corrupt, bpf)
with their parameters. Use before setting filters with link_update."""
"""List the packet filter types available for a link with their parameters
and ranges — the authoritative answer for what link_update accepts on THIS
link (topology and compute-capability gated: kernel-only types appear only
on kernel-datapath links whose compute reports support). Call this before
setting filters; a type absent here will be rejected with 409."""
return await asyncio.to_thread(
_run_handler_sync,
available_filters_handler,