--- name: pyats-network description: "Network device automation via pyATS - run show commands, ping, apply config, learn config/logging, list devices, run Linux commands on Cisco IOS-XE/NX-OS devices. Use when running CLI commands on routers or switches, checking interface status, applying configuration changes, or collecting device data via pyATS. To author or design a new aetest validation script (rather than execute an existing one), use `pyats-dynamic-test` instead." license: Apache-2.0 user-invocable: true metadata: { "openclaw": { "requires": { "bins": ["python3"], "env": ["PYATS_TESTBED_PATH"] } } } --- # pyATS Network Device Tool ## Server & Testbed - **Server script:** `$PYATS_MCP_SCRIPT` - **Testbed:** `$PYATS_TESTBED_PATH` - **Environment variable:** `PYATS_TESTBED_PATH=$PYATS_TESTBED_PATH` ## Available Devices Devices are defined in `$PYATS_TESTBED_PATH` and vary per installation — run `pyats_list_devices` first to see the actual device names, aliases, and platforms available in this testbed (e.g. `R1` on Cisco IOS-XE). Examples below use `R1` as a placeholder device name; substitute the real device name(s) returned by `pyats_list_devices`. ## How to Call Tools Use the `$MCP_CALL` protocol handler to invoke MCP tools: ```bash PYATS_TESTBED_PATH=$PYATS_TESTBED_PATH python3 $MCP_CALL "${PYATS_PYTHON:-python3} -u $PYATS_MCP_SCRIPT" TOOL_NAME 'ARGS_JSON' ``` ## All 8 Available Tools ### 1. `pyats_list_devices` List all devices in the testbed with their properties: name, alias, type, OS, platform, connection types, credentials summary. ```bash PYATS_TESTBED_PATH=$PYATS_TESTBED_PATH python3 $MCP_CALL "${PYATS_PYTHON:-python3} -u $PYATS_MCP_SCRIPT" pyats_list_devices '{}' ``` **Use when:** Starting any session — always list devices first to confirm connectivity and inventory. ### 2. `pyats_run_show_command` Execute any show command with automatic Genie structured parsing. Returns parsed JSON when a Genie parser exists, raw text otherwise. - `device_name` (string): Target device from testbed - `command` (string): Show command — **must** start with "show" ```bash PYATS_TESTBED_PATH=$PYATS_TESTBED_PATH python3 $MCP_CALL "${PYATS_PYTHON:-python3} -u $PYATS_MCP_SCRIPT" pyats_run_show_command '{"device_name":"R1","command":"show ip interface brief"}' ``` **Validation rules:** - Must start with "show" - No pipes (`|`), redirects (`>`), or shell characters - Cannot include: copy, delete, erase, reload, write, configure keywords - Do NOT use for `show running-config` or `show logging` — use the dedicated tools **Commands with Genie parsers (structured JSON output) on IOS-XE:** Routing: - `show ip route` / `show ip route vrf ` - `show ip protocols` - `show ip bgp` / `show ip bgp summary` / `show ip bgp neighbors` - `show ip ospf` / `show ip ospf neighbor` / `show ip ospf interface` / `show ip ospf database` - `show ip eigrp neighbors` / `show ip eigrp topology` - `show isis neighbors` / `show isis database` - `show ip static route` Interfaces: - `show ip interface brief` / `show ipv6 interface brief` - `show interfaces` / `show interfaces ` - `show interfaces status` - `show interfaces counters` - `show ip interface` L2/Switching: - `show vlan` / `show vlan brief` - `show spanning-tree` / `show spanning-tree detail` - `show mac address-table` - `show etherchannel summary` Neighbors: - `show cdp neighbors` / `show cdp neighbors detail` - `show lldp neighbors` / `show lldp neighbors detail` FHRP: - `show standby` / `show standby brief` - `show vrrp` / `show vrrp brief` System: - `show version` - `show inventory` - `show processes cpu` / `show processes cpu sorted` - `show processes memory` / `show processes memory sorted` - `show platform` - `show ntp associations` / `show ntp status` - `show snmp` - `show clock` - `show bootflash` - `show license` Security: - `show access-lists` / `show ip access-lists` - `show crypto isakmp sa` / `show crypto ipsec sa` - `show dot1x` - `show port-security` - `show authentication sessions` QoS: - `show policy-map` / `show policy-map interface` VRF / MPLS: - `show vrf` / `show vrf detail` - `show mpls forwarding-table` - `show mpls ldp neighbor` Other: - `show arp` - `show ip nat translations` - `show ip dhcp binding` - `show track` - `show route-map` - `show ip prefix-list` - `show bfd neighbors` - `show flow monitor` ### 3. `pyats_configure_device` Apply configuration changes to a device. Automatically enters config mode and exits. - `device_name` (string): Target device - `config_commands` (list of strings OR multiline string): Configuration lines ```bash PYATS_TESTBED_PATH=$PYATS_TESTBED_PATH python3 $MCP_CALL "${PYATS_PYTHON:-python3} -u $PYATS_MCP_SCRIPT" pyats_configure_device '{"device_name":"R1","config_commands":["interface Loopback99","ip address 99.99.99.99 255.255.255.255","description NetClaw-Test","no shutdown"]}' ``` **Rules:** - Do NOT include `configure terminal`, `conf t`, or `end` — the tool handles mode transitions - DO include `exit` commands when you need to return to a higher config context - Preserves indentation for submode commands (route-maps, ACLs, etc.) - **Blocked commands:** `write erase`, `erase`, `reload`, `delete`, `format` ### 4. `pyats_show_running_config` Retrieve the full running configuration from a device. - `device_name` (string): Target device ```bash PYATS_TESTBED_PATH=$PYATS_TESTBED_PATH python3 $MCP_CALL "${PYATS_PYTHON:-python3} -u $PYATS_MCP_SCRIPT" pyats_show_running_config '{"device_name":"R1"}' ``` **Use when:** Capturing configuration baselines, auditing config, pre/post change verification. ### 5. `pyats_show_logging` Fetch system logs (last 250 entries) from a device. - `device_name` (string): Target device ```bash PYATS_TESTBED_PATH=$PYATS_TESTBED_PATH python3 $MCP_CALL "${PYATS_PYTHON:-python3} -u $PYATS_MCP_SCRIPT" pyats_show_logging '{"device_name":"R1"}' ``` **Use when:** Checking for errors, tracebacks, interface flaps, protocol events after changes. ### 6. `pyats_ping_from_network_device` Execute ping from the network device itself (not from the MCP client). - `device_name` (string): Source device - `command` (string): Ping command (e.g., `ping 8.8.8.8`, `ping 10.0.0.1 repeat 100 source Loopback0`) ```bash PYATS_TESTBED_PATH=$PYATS_TESTBED_PATH python3 $MCP_CALL "${PYATS_PYTHON:-python3} -u $PYATS_MCP_SCRIPT" pyats_ping_from_network_device '{"device_name":"R1","command":"ping 8.8.8.8"}' ``` **Returns:** Structured JSON with success rate %, RTT stats, packet loss when Genie parsing succeeds. ### 7. `pyats_run_linux_command` Execute shell commands on Linux-based devices in the testbed. - `device_name` (string): Linux-capable device - `command` (string): Shell command **Use when:** The testbed includes Linux hosts (containers, VMs) for system administration tasks. ### 8. `pyats_run_dynamic_test` Execute a complete pyATS AEtest validation script inline. The script runs in a sandboxed environment. - `test_script_content` (string): Complete Python AEtest script ```bash PYATS_TESTBED_PATH=$PYATS_TESTBED_PATH python3 $MCP_CALL "${PYATS_PYTHON:-python3} -u $PYATS_MCP_SCRIPT" pyats_run_dynamic_test '{"test_script_content":"import logging\nfrom pyats import aetest\n\nlogger = logging.getLogger(__name__)\n\nTEST_DATA = {\"expected_interfaces\": [\"GigabitEthernet1\", \"Loopback0\"]}\n\nclass InterfaceTest(aetest.Testcase):\n @aetest.test\n def verify_interfaces(self):\n for intf in TEST_DATA[\"expected_interfaces\"]:\n logger.info(f\"Checking interface: {intf}\")\n assert intf.startswith(\"Gi\") or intf.startswith(\"Loop\"), f\"Unexpected interface type: {intf}\"\n\nif __name__ == \"__main__\":\n aetest.main()"}' ``` **Rules:** - Must define `TEST_DATA` as a Python dict literal (not loaded from file/network) - Cannot connect to devices (embed all data inline) - 300-second timeout - **Banned imports:** os, sys, subprocess, shutil, socket, pathlib, pickle, yaml, requests, urllib, http, ssl - **Banned functions:** `__import__()`, `eval()`, `exec()`, `compile()`, `open()`, `json.loads()` **Use when:** Complex pass/fail validation, multi-step assertions, compliance checks on data already collected via show commands. ## Alternative: Direct Python pyATS For operations beyond the 8 MCP tools, use pyATS directly: ```python from pyats.topology import loader tb = loader.load('$PYATS_TESTBED_PATH') device = tb.devices['R1'] device.connect(learn_hostname=True, log_stdout=False) output = device.parse('show ip interface brief') print(output) device.disconnect() ``` ### Genie Learn (multi-command feature snapshots) ```python from genie.testbed import load testbed = load('$PYATS_TESTBED_PATH') dev = testbed.devices['R1'] dev.connect(learn_hostname=True, log_stdout=False) # Learn returns an OS-agnostic normalized data model ospf = dev.learn('ospf') # Neighbors, interfaces, database, areas, LSAs bgp = dev.learn('bgp') # Neighbors, address-families, routes, state intf = dev.learn('interface') # All interfaces: status, IP, counters, MTU, speed routing = dev.learn('routing') # Full routing table from all protocols platform = dev.learn('platform') # Hardware, modules, images, slots print(ospf.info) dev.disconnect() ``` All 34 learnable features: acl, arp, bgp, config, device, dot1x, eigrp, fdb, hsrp, igmp, interface, isis, lag, lisp, lldp, mcast, mld, msdp, nd, ntp, ospf, pim, platform, prefix_list, rip, route_policy, routing, static_routing, stp, terminal, utils, vlan, vrf, vxlan. ### Genie Diff (state comparison) ```python from genie.utils.diff import Diff # Capture before state before = dev.learn('ospf') # ... make changes ... # Capture after state after = dev.learn('ospf') diff = Diff(before.info, after.info) diff.findDiff() print(diff) # Shows + additions and - deletions ``` ## Failure Behavior - If a tool call fails with an authentication or connection error, check that `PYATS_MCP_SCRIPT`, `PYATS_TESTBED_PATH` are set and valid before assuming a data or device problem. - On a tool error (timeout, unreachable host, malformed response), report the failure and its error message directly to the user rather than fabricating or guessing at results. - For a confirmed read-only call, check connectivity and retry once if appropriate. For any call that changes state or sends a message, a timeout does not prove the action failed: inspect current state or delivery status before retrying, preserve the required approval/change gates, and do not repeat an action whose outcome is unknown. ## MCP Tasks For eligible tools, a client declaring the current Tasks extension may receive a task handle. Retain it and poll for the terminal result; do not resubmit pending work. Ordinary clients continue receiving foreground results. A handle is not execution success or approval: preserve required baseline/change-control checks before invocation and verify the completed result afterward. Cancellation cannot undo commands already sent. Investigate unknown outcomes before retrying. pyATS retains completed results in SQLite and lets started work finish; other FastMCP tools default to ephemeral state and cooperative cancellation. See `docs/MCP-TASKS.md`.