ptlab
# ptlab
Headless Cisco Packet Tracer labs from Python (or MCP). Write `.pkt` / graded `.pka` without Packet Tracer running. Target: **PT 9.x**, CCNA switching + routing.
[](https://github.com/brian-mwirigi/packet-tracer-mcp/actions/workflows/test.yml)
This is **not** a live PT driver. [Mats2208/MCP-Packet-Tracer](https://github.com/Mats2208/MCP-Packet-Tracer) talks to a running instance over a Lua `.pts` bridge. ptlab is the opposite: codec first, files on disk, live deploy later ([docs/PHASE2.md](docs/PHASE2.md)).
## What works today
| Layer | Status |
| --- | --- |
| Container (Twofish-EAX + XOR + zlib) | GUI-valid on PT 9.0.0 — [docs/FORMAT.md](docs/FORMAT.md) |
| Donor mutation (`clone`, `check`) | Opens in PT; structural refs are unit-tested |
| From-scratch skinny XML | **Silent drop** — blank untitled workspace |
| Title-bar load gate (`gui-gate`) | Fast; false-positive on broken cables |
| Structural gate (`check`) | Catches stale / dangling / colliding `DEV_ADDR` |
| Simulation ping (`ping-gate`) | Needs a one-time IPC ExApp registration |
| Activity Wizard `.pka` scoring in PT | Not verified yet |
| Phase 2 live bridge | Not built |
GUI-openable files are **template-backed**: harvest a real device tree from local `saves/`, mint new MAC/`DEV_ADDR`, rewrite cables. Do not trust a file that only round-trips through this library.
CCNA wedge (not full PT coverage): **PC-PT, Server-PT, 1841, 2960-24TT**, plus **2950-24**. Hold **2911** until a second independent export is diffed.
## Install
Python 3.11+:
```bash
pip install -e ".[dev]"
python -m pytest
```
Packet Tracer is optional for codec tests. The load / ping gates need Windows + PT 9.
## Two ways to write a file
**Donor-backed (opens in PT).** Clone from a harvested tree, then gate it:
```bash
ptlab harvest --limit 25
ptlab clone donor.xml lab.pkt \
--from-device PC0 --name LabPC2 \
--peer Switch0 --peer-port FastEthernet0/4 \
--keep PC0,Switch0 \
--ip 192.168.1.50 --subnet 255.255.255.0 --gateway 192.168.1.1
ptlab check lab.pkt
ptlab gui-gate lab.pkt
```
**Object graph (library / MCP).** Good for configs, inspect, and Activity Wizard XML. The `.pkt` it emits is a valid *container*, not a GUI-valid hardware dump:
```python
from ptlab import create_topology, generate_graded_activity, write_pkt
topo = create_topology(
devices=[
{"id": "R1", "kind": "router"},
{"id": "R2", "kind": "router"},
],
links=[{"a": "R1", "a_port": "GigabitEthernet0/0", "b": "R2", "b_port": "GigabitEthernet0/0"}],
)
topo.set_config("R1", """
hostname R1
interface GigabitEthernet0/0
ip address 10.0.0.1 255.255.255.252
no shutdown
router ospf 1
network 10.0.0.0 0.0.0.3 area 0
""")
topo.set_config("R2", """
hostname R2
interface GigabitEthernet0/0
ip address 10.0.0.2 255.255.255.252
no shutdown
router ospf 1
network 10.0.0.0 0.0.0.3 area 0
""")
write_pkt(topo, "lab.pkt")
generate_graded_activity(topo, output_path="lab.pka")
```
`examples/two_router_ospf.py` is this path end-to-end.
## Correctness gates
PT 9 does not error on bad XML. It opens a blank untitled workspace. A file that *loads* can still have a cable attached to the wrong device — or to nothing.
| Tier | Command | Proves | Cost |
| --- | --- | --- | --- |
| 1 load | `ptlab gui-gate lab.pkt` | Title bar is `Cisco Packet Tracer - <path>` | ~11s |
| 1b structure | `ptlab check lab.pkt` | Unique `DEV_ADDR`; cable mem-addrs match `FROM`/`TO` | instant |
| 2 ping | `ptlab ping-gate lab.pkt --from-device LabPC2 --to-device PC0` | ICMP through the clone (LabPC2 → PC0, not the switch) | PT + IPC |
Register `extras/ptlab_exapp.xml` once: **Extensions → IPC → Configure Apps → Add**. Until then `ping-gate` reports `load_ok: true, ipc_ok: false`.
Do not mint multi-device labs for students until tier 2 is green.
## CLI
```bash
ptlab decode lab.pkt lab.xml
ptlab encode lab.xml lab.pkt
ptlab inspect lab.pka
ptlab harvest --limit 25
ptlab clone donor.xml lab.pkt --from-device PC0 --name LabPC2 --peer Switch0 --peer-port FastEthernet0/4
ptlab check lab.pkt
ptlab gui-gate lab.pkt
ptlab ping-gate lab.pkt --from-device LabPC2 --to-device PC0
```
## MCP
```json
{
"mcpServers": {
"ptlab": {
"command": "python",
"args": ["-m", "ptlab_mcp"]
}
}
}
```
| Tool | Purpose |
| --- | --- |
| `create_topology` | In-memory devices + links |
| `set_device_config` | IOS-style config on a device |
| `generate_pkt` | Twofish-encrypted `.pkt` |
| `generate_graded_activity` | `.pka` with COMPARISONS scoring |
| `inspect_pkt` | Parse an existing file to a summary |
| `validate_current_topology` | Duplicate IPs, OSPF area clashes, DHCP overlap |
These tools write the object-graph serializer. GUI-openable topologies go through `ptlab clone`, not `generate_pkt`.
## Docs
| Note | Contents |
| --- | --- |
| [docs/FORMAT.md](docs/FORMAT.md) | Container pipeline, GUI experiments, gates, `.pka` layout |
| [docs/SCHEMA.md](docs/SCHEMA.md) | Python topology model |
| [docs/PHASE2.md](docs/PHASE2.md) | Live deploy — blocked until Phase 1 ping + templates |
## License
MIT. Packet Tracer is a Cisco product; this repo is an independent interoperability
tool, not affiliated with Cisco. Container format: Mirco De Zorzi ([pka2xml](https://github.com/mircodz/pka2xml))
and [jeamxn/cisco-pka-to-xml](https://github.com/jeamxn/cisco-pka-to-xml).
TDQS
Scored across 6 tools
Each tool targets a distinct operation: validation, activity generation, topology creation, configuration, serialization, and parsing. No overlapping purposes; even the two generate tools differ in output format (.pka vs .pkt).
All tools follow a consistent snake_case verb_noun pattern (validate_current_topology, generate_graded_activity, create_topology, etc.). The naming is predictable and readable.
Six tools is well-scoped for a Packet Tracer topology server, covering creation, validation, configuration, export, and inspection without redundancy or bloat.
Core workflows are covered: create, validate, configure, export, and inspect. Minor gaps like explicitly listing devices or removing devices/links exist, but agents can work around them through create and set operations.