pyats-network
$
npx mdskill add automateyournetwork/netclaw/pyats-networkAutomates network device tasks using pyATS on Cisco IOS-XE/NX-OS devices
- Runs CLI commands, checks interface status, and applies configurations
- Uses pyATS, Python 3, and environment variable PYATS_TESTBED_PATH
- Executes commands based on user input and device inventory from testbed
- Returns structured output via MCP protocol for integration with workflows
SKILL.md
.github/skills/pyats-networkView on GitHub ↗
---
name: pyats-network
description: "Network device automation via pyATS - run show commands, ping, apply config, learn config/logging, list devices, run Linux commands, execute dynamic tests 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."
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
- **R1** (devnetsandboxiosxec8k.cisco.com) — Cisco IOS-XE, C8000v/CSR1kv
## How to Call Tools
Use the `$MCP_CALL` protocol handler to invoke MCP tools:
```bash
PYATS_TESTBED_PATH=$PYATS_TESTBED_PATH python3 $MCP_CALL "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 "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 "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 <name>`
- `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 <name>`
- `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 "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 "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 "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 "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 "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
```