Skip to main content
Glama

Network Lab MCP

An AI Network Engineer Workspace for real network labs.

Why I built it

I work as a network engineer, and I often find myself wanting to validate something in a lab but simply not having enough time to do it. Customer work, troubleshooting, meetings, documentation, and many other tasks usually come first. The lab work is important, but it is also time-consuming.

That made me wonder: what if I could ask AI to do the lab investigation for me? Not just run a command, but understand the topology, connect to the right devices, inspect the current state, compare results, investigate problems, and report back with evidence.

At the same time, I did not want the AI to become a black box. As a network engineer, I still want to see what it is doing and make the final engineering decisions.

Network Lab MCP is the result: a thin foundation that lets Claude Code (or any other MCP client) understand a lab's topology, follow common operating principles, understand the current task, draw on reusable reference knowledge, and reach lab devices through a real terminal — while every terminal session stays human-observable, and every actual network engineering decision stays with the engineer.

Related MCP server: MCP-Telecom

What it does

Network Lab MCP is not a fixed test-automation tool, and it is not a network-engineering reasoning engine. It gives an MCP client two things:

  1. Lab knowledge — where the work happens, how to behave, what to accomplish, and what reusable knowledge already exists.

  2. Terminal access — a real, general-purpose way to interact with lab devices, close to how a human operator would use a terminal, without ever handing the AI the private connection details.

All actual network engineering judgment — what command to run next, how to interpret output, when a task is done — is left to Claude Code.

It also includes an independent, IOS XR-compatible human-facing CLI (./run_cli.sh) for creating and editing lab definitions (topology, private access information, scenario, reference) through a candidate/commit model, and for observing terminal activity live while the AI works.

How it works

Claude Code                          Human Operator
    |                                     |
Network Lab MCP (this repository)   ./run_cli.sh (this repository)
    |                                     |
dedicated tmux environment           Candidate configuration -> commit
(socket: network-lab-mcp)                 |
    |                                Committed lab YAML
ssh / telnet                              |  (running-config, topology,
    |                                     |   access-info, scenario, reference)
Lab Devices                          (read by Network Lab MCP above)

Five kinds of lab data are deliberately kept separate:

Concept

Role

Exposed to Claude?

running-config

WHICH topology/scenario/references MCP currently uses — a selection, stored in lab/settings.yaml

Indirectly (drives which topology/scenario/references are read)

access-info

HOW TO ACCESS devices — private connection data (address/transport/port/username/password), lab/access-info/*.yaml

Never

topology

WHAT EXISTS / HOW IT IS CONNECTED — safe logical devices, device type, links, lab/topologies/*.yaml

Yes, via get_active_topology()

scenario

WHAT TO DO for the current task, lab/scenarios/*.yaml

Yes, via get_execution_instructions()

reference

Reusable, validated knowledge, lab/references/*.yaml

Yes, via get_execution_instructions()

access-info  = HOW TO ACCESS DEVICES     = private, never sent to Claude
topology     = WHAT EXISTS / CONNECTIVITY = safe, sent to Claude
scenario     = WHAT TO DO                 = sent to Claude
reference    = REUSABLE KNOWLEDGE         = sent to Claude
running-config = WHICH topology/scenario/references MCP uses right now

running-config is a selection, not a definition: it never contains device data itself, only the names of the topology/scenario/references currently in effect. Topology/access-info/scenario/reference are definitions: each one is a named, independently authored/edited YAML document. Editing a definition (e.g. topology lab1 in the CLI) never changes which definition MCP currently uses; only committing a running-config change does that.

Placed alongside Principles, Terminal, Claude Code, and Workspace, the full responsibility model looks like this:

Concept

Role

Meaning

running-config

SELECTION

Which topology/scenario/references MCP currently uses (lab/settings.yaml)

Topology

WHERE / WHAT EXISTS

Safe logical devices, device type, and links — no private access data

access-info

PRIVATE DEVICE ACCESS

Address/transport/port/username/password — never exposed to Claude

Principles

HOW TO BEHAVE

Common operating rules that apply to every scenario

Scenario

WHAT TO DO

What must be accomplished for the current task

References

REUSABLE KNOWLEDGE

Reusable validated guidance, operational knowledge, known values, and lab-specific know-how

Terminal

ACTION INTERFACE

How Claude interacts with lab devices

Claude Code

REASONING

Determines how to accomplish the task

Workspace

TASK ARTIFACT AREA

Where Claude stores evidence, analysis, configurations, validation results, designs, and reports

The Workspace is deliberately not managed by Network Lab MCP: Claude Code uses its own current working directory (the task workspace it was started from) to store evidence, configurations, and reports, organized however the task requires. See docs/architecture.md for the full picture, including the device-access resolution flow.

Key design principles

  • Exactly seven MCP tools, and nothing else. get_active_topology, get_execution_instructions, terminal_open, terminal_send, terminal_read, terminal_list, terminal_close. There is no batch/ parallel tool, no config-mutation tool, and no tool that returns access-info. See docs/mcp_tools.md.

  • Real, human-observable terminals. Terminal sessions run in a dedicated tmux environment, driven the same way a human operator would drive one: read the pane, decide what to send, send it. Nothing here parses device prompts or maintains a device-CLI state machine — Claude Code does that reasoning itself. A human can watch the exact same session live with monitor terminal <device>.

  • Private access-info, never exposed to Claude. Device addresses, usernames, and passwords live in a separate access-info definition that no MCP tool ever returns. terminal_open(device) receives only a logical device name; Network Lab MCP resolves the private connection details itself.

  • Candidate/commit configuration, IOS XR-style. The human CLI edits lab definitions and the running-config selection through a candidate → commit model — nothing is written to disk, and nothing is visible to Claude Code, until an explicit commit succeeds.

  • Discovery is candidate-first, never automatic. discover topology populates a topology candidate for human review; it never auto-commits and never auto-selects the result as the active topology.

  • Fail closed, not silently guessed. A missing access-info selection, an unresolvable device, a device-type mismatch between topology and access-info, an ambiguous Discovery neighbor — every one of these is a clear, sanitized error rather than a guess.

Quick Start

Prerequisites

  • Linux (developed and validated here). Likely compatible with macOS, since it depends only on tmux/OpenSSH/a POSIX shell, but that has not been formally validated in this project. Native Windows is not supported — tmux has no native Windows build (WSL2, which provides a real Linux environment, should work but has not been formally validated either).

  • Python 3.10 or newer (see pyproject.toml's requires-python).

  • tmux — the terminal session backbone; every managed session depends on it.

  • An OpenSSH client (ssh) — required for any transport: ssh device.

  • A telnet client — only required if any device uses transport: telnet.

  • An MCP-capable client, such as Claude Code.

cd ~/work/network-lab-mcp

python3 -m venv .venv
source .venv/bin/activate

pip install -e .

Register the server once, from anywhere, after installing it into an activated environment:

claude mcp add --scope user --transport stdio network-lab -- network-lab-mcp

(Verified against Claude Code CLI 2.1.277's claude mcp add --help; if a newer Claude Code version changes this syntax, follow its own --help output instead of this snippet.)

--scope user registers the server for the current user across all projects. network-lab-mcp must be resolvable on PATH at the time Claude Code launches it — for example, by installing it into an environment that is active in your shell, or by using that environment's absolute path in place of network-lab-mcp.

Set up your local running-config selection once:

cp lab/settings.example.yaml lab/settings.yaml

Then start Claude Code from whatever task workspace you like (it does not need to be this repository):

cd ~/work/customer-lab-investigation
claude

Network Lab MCP does not depend on that working directory. One Network Lab MCP checkout owns exactly one lab root, and that lab root can contain multiple topologies, access-info definitions, scenarios, and references; lab/settings.yaml selects which topology/scenario/references are currently in effect.

Advanced / isolated-lab-only: some workflows run Claude Code with claude --permission-mode bypassPermissions to avoid per-command approval prompts. This skips Claude Code's own safety confirmations entirely and is only appropriate in an isolated lab environment you fully control — you are assuming that risk yourself. It is not the normal or recommended way to run Claude Code against this project.

Run the human configuration CLI to review or edit lab definitions:

./run_cli.sh
network-lab#

Run help inside it for a Quick Start covering the typical configuration workflow, and help claude for how Claude Code uses Network Lab MCP.

Supported installation model

Network Lab MCP supports exactly one deployment model: a local repository checkout, installed with pip install -e .. The repository checkout owns the lab/ directory, and the MCP server resolves it relative to its own source location — never relative to the current working directory of whatever process launched it. pip install ., installing from a built wheel, or pip install network-lab-mcp from a package index are not supportedlab/ is repository-local operational data, not a Python package resource, so a non-editable install has no lab directory to find.

Example workflow

The tracked sample.yaml files use RFC 5737 documentation-only addresses, which are not reachable — Discovery cannot actually connect to them. For real Discovery/terminal access, create your own private access-info first (never edit the tracked sample directly):

cp lab/access-info/sample.yaml lab/access-info/my_lab.yaml
# edit lab/access-info/my_lab.yaml with your real device addresses and
# credentials, then select it: running-config's `access-info my_lab`

The transcript below is illustrative output from a configured, reachable lab (not the tracked sample environment) — access-info name, target counts, and observation counts will differ for your own lab:

network-lab# configure
network-lab(config)# discover topology
Discovering topology from access-info 'my_lab'...
Discovery complete.

  Access-info:          my_lab
  IOS XR targets:       2
  IOS XE targets:       0
  IOS targets:          0
  Connected:            2
  LLDP observations:    4
  CDP observations:     0
  Managed links:        2
  Unresolved neighbors: 0
  L3 enrichment:        2/2 devices, 2 interfaces
  Topology candidate:   my_lab

network-lab(config-topology-my_lab)# show configuration
...
network-lab(config-topology-my_lab)# commit
Commit complete.
network-lab(config-topology-my_lab)# root
network-lab(config)# running-config
network-lab(config-running)# topology my_lab
network-lab(config-running)# commit
Commit complete.

get_active_topology() (and every other MCP tool) keeps returning the previously active topology until that final explicit running-config topology/commit step — Discovery's own commit only persists the topology definition, never the active selection.

From there, a typical Claude Code session calls get_active_topology() and get_execution_instructions() to learn where to work and what to accomplish, then terminal_open()/terminal_send()/terminal_read() to investigate the devices that matter for the task, reporting back with evidence saved in its own task workspace.

Human-observable terminals

Because every session lives in a dedicated tmux environment, an engineer can watch exactly what the AI is doing, live, without interfering with it:

network-lab# monitor terminal R1
RP/0/RP0/CPU0:R1#show version
...
RP/0/RP0/CPU0:R1#

--------------------------------------------------------------------------------
Monitoring terminal R1 | Read-only | Source: managed | Status: active | q: quit
--------------------------------------------------------------------------------

monitor terminal <device-id> (EXEC-only, strictly read-only) streams the currently preferred terminal session's activity into the local terminal while keeping a live status bar at the bottom — everything it prints stays in the terminal emulator's own scrollback, exactly like ordinary command output. It prefers a normal managed session, falling back to an active Discovery bootstrap session when no managed session exists yet, and several independent monitors (of the same or different devices) can run at once from separate ./run_cli.sh windows. See "monitor terminal" in the CLI reference for the full behavior.

Historical evidence does not depend on the monitor being open: every device session, managed or Discovery, is also captured to a persistent per-session log file (logs/terminal/<device-id>/*.log), reviewable with show logging — see docs/cli_reference.md.

Security model

Credentials never reach the AI

Real access-info YAML stores device usernames and passwords directly (this is a lab tool; it does not introduce a separate secret store). Credentials are used only to drive interactive terminal login. Network Lab MCP:

  • never returns access-info from any MCP tool: get_active_topology() returns only the safe topology, and get_execution_instructions() never includes it either;

  • never logs passwords or terminal_send() input text, and never echoes that input text back in tool responses;

  • never includes credentials in generated documentation or error messages;

  • never persists credentials into any separate runtime database — tmux is the only session state, and there isn't a second one.

terminal_open(device) receives only a logical device name from Claude — never an address, username, or password. Network Lab MCP resolves the private connection details itself:

  1. Read the committed running-config and resolve the active topology.

  2. Verify the device exists in the active topology.

  3. Resolve the running-config's selected access-info (active_access_info) — fail closed if none is selected, or if the selected definition does not exist on disk.

  4. Load only that one access-info definition and look up the device in it — fail closed if it is absent. No other access-info file is ever searched.

  5. If both the topology and the resolved access-info specify type, normalize both through the shared device-type enum and compare — fail closed on a mismatch.

  6. If the device has an optional jump_host reference, resolve it within the same access-info definition and attach it for a single-hop OpenSSH ProxyJump connection (see "Single-hop SSH jump hosts" below); otherwise connect directly.

  7. Only then does the tmux/ssh/telnet path run. Once the session exists, terminal_open() also completes private target authentication if the target's own login prompt actually appears — see "Private managed-terminal authentication" below.

Selecting which access-info definition this resolution reads is done through running-config's access-info <name> / no access-info — the same candidate/commit model as topology/scenario/reference selection, and just as invisible to terminal_open() until committed.

Private managed-terminal authentication

Network Lab MCP can complete target authentication for a managed terminal over either transport, using the selected private access-info definition — SSH password authentication, or classic-IOS-style Telnet username/password login. Credentials remain inside Network Lab MCP and are not exposed to the AI/MCP client:

Claude
   |
   | terminal_open(R1)
   v
Network Lab MCP
   |
   +--> committed active access-info
   |        |
   |        +--> private username/password
   |
   v
native SSH/Telnet in tmux
   |
   +--> verify target login prompt (SSH password prompt, or Telnet
   |    Username:/Password:)
   |
   +--> send credentials privately, via a stdin-based tmux buffer paste --
   |    never as a command-line argument to any process
   |
   v
authenticated terminal

Neither transport's credentials ever cross the MCP boundary, appear in any process's command-line arguments, or appear in an exception message. terminal_open() recognizes only OpenSSH's own client-side password prompt for SSH (never a device-CLI-specific prompt, so this works for every device type, not just IOS XR) and only answers it once that prompt can be confidently attributed to the target device — a jump host's own password prompt (single-hop ProxyJump) is never answered; that hop must still use non-interactive key/agent authentication. Key/agent authentication that succeeds without ever showing a password prompt is completely unaffected — no password is sent. For Telnet, only a bounded classic-IOS- style login sequence is automated (optional Username:, then Password:, then the device's own exec prompt) — never a generic prompt-answering loop, and never enable/TACACS/OTP/MFA automation. If authentication definitively fails or is rejected (either transport), terminal_open() fails with a sanitized error (never the credential itself) and, if it created a new session for this attempt, closes it; a pre-existing session is never destroyed just because a later open encounters an unusual state, and an already-authenticated session stays fully idempotent (no send, no disturbance). See docs/architecture.md for the full design.

Telnet itself remains unencrypted, transmitting the login and all session content in the clear — Network Lab MCP only keeps the credential private from the AI/MCP client, it does not (and cannot) make Telnet a secure transport. Telnet remains appropriate only for isolated lab environments.

Password display policy

Network Lab MCP is primarily a lab tool, so explicit local CLI configuration displayshow running-config / show configuration / bare show for an access-info device or jump host — shows password in clear text, not masked:

network-lab(config-access-device-R1)# show running-config
access-info sample
 device R1
  type iosxr
  address 192.0.2.11
  transport ssh
  port 22
  username example-user
  password example-password
 !
!

This is the only place a password is ever shown in clear text. Every other boundary is unchanged and unweakened:

  • MCP tool results (get_active_topology(), get_execution_instructions(), every terminal_*() return value) never include it.

  • Logs, exceptions, and every %-prefixed error message never include it.

  • ? help and Tab completion never reveal or offer it as a candidate.

  • The CLI's in-memory command history never retains a password-setting command, even abbreviated.

Single-hop SSH jump hosts (ProxyJump)

access-info can declare reusable jump_hosts, each a generic endpoint (type: host — never a network-device type) reached over SSH:

name: sample

jump_hosts:
  jump1:
    type: host
    address: 192.0.2.10
    transport: ssh
    port: 22
    username: example-user
    password: example-password

devices:
  R1:
    type: iosxr
    address: 192.0.2.11
    transport: ssh
    port: 22
    username: example-user
    password: example-password
    jump_host: jump1

A device's optional jump_host field references one jump host by name within the same access-info definition. terminal_open() then launches native OpenSSH with -J (conceptually ssh -J example-user@192.0.2.10:22 -p 22 example-user@192.0.2.11) instead of connecting directly — no shell-hop automation, just OpenSSH's own ProxyJump tunneling one SSH connection through another. The tmux pane still only ever shows one interactive session to read/send against, exactly like a direct connection.

Constraints (all enforced by lab.validate_access_info_data(), so a manually edited, invalid committed file fails closed the same way a rejected CLI commit would): a jump host's type must resolve to exactly host; a jump host's transport, if set, must be ssh; a device's transport must also be ssh when it references a jump_host; and single-hop only — a jump host has no jump_host field of its own. See docs/cli_reference.md for the full command reference (jump-host <name> under access-info definition mode).

Dedicated tmux environment

All Network Lab MCP terminal sessions run on a dedicated tmux server, separate from any tmux environment you use interactively, so terminal_list()/terminal_close() are always safe from interfering with unrelated sessions. Production topology-device sessions (network-lab-device-<device-id>), Discovery's private bootstrap sessions (network-lab-discovery-<device-id>), and local validation-only sessions (network-lab-validation-<validation-id>) live in structurally distinct namespaces, distinguished only by a fixed prefix — never by pattern-matching on a device's name, so a topology device literally named validation-router maps to the ordinary production session network-lab-device-validation-router, not a validation session. Terminal sessions live in tmux independent of the MCP server process: the server keeps no session registry of its own, never destroys a session on exit, and a restarted server rediscovers and reuses any existing session instead of creating a duplicate.

stdio / stdout rule

The MCP server communicates over stdio, and stdout is reserved for MCP protocol traffic. It never uses print(), prints no startup banner, and sends all diagnostic logging to stderr.

Supported device types & Discovery scope

A device's optional type field, when present (in either topology or access-info), must be one of:

  • iosxr — Cisco IOS XR

  • iosxe — Cisco IOS XE

  • ios — classic Cisco IOS (its own explicit type, never a compatibility label under iosxe — a device that actually runs classic IOS, not IOS XE, should be typed ios)

  • nxos — Cisco NX-OS

  • host — generic host / endpoint (e.g. a jump host, or a traffic generator)

lab.normalize_device_type() is the single validation primitive for this enum: an unambiguous abbreviation like type nx normalizes to nxos, and exact ios always wins over abbreviation resolution (never rejected merely because it is also a prefix of iosxr/iosxe).

Topology discovery

discover topology (global configuration mode only) reads the committed active_access_info and runs read-only neighbor discovery against its iosxr, iosxe, and ios devices — host is skipped (not an error); nxos is unsupported and skipped:

Device type

Discovery protocols

iosxr

LLDP + CDP

iosxe

LLDP + CDP

ios (classic IOS)

CDP only

It never installs/activates a package, enables LLDP/CDP, or changes router configuration; it requires all selected targets to succeed for neighbor discovery (any login/command/timeout failure fails the whole operation before the prior candidate is touched), and it always ends in topology configuration mode with the result applied as a candidate — exactly like a manually typed topology <name>. It never commits and never changes active_topology itself; committing the result, and separately selecting it as the active topology, both remain explicit human steps. NX-OS discovery, SNMP/NETCONF/RESTCONF, multi-hop jump chains, and a generic discovery/plugin framework are all out of scope. See docs/architecture.md for the full pipeline, including multi-protocol link reconciliation and how an unresolved neighbor's evidence stays reviewable through the observing device's own terminal log.

L3 topology enrichment

Alongside neighbor discovery, each interface with a directly observed IPv4 address may also be enriched with that address plus its VRF:

devices:
  R1:
    type: iosxr
    interfaces:
      GigabitEthernet0/0/0/2:
        ipv4_address: 10.0.12.1
        vrf: default

This is deliberately narrow: no prefix length is ever inferred, no subnet/link inference from addresses is ever performed, and no operational state (up/down, holdtime, counters) is ever persisted into topology — ask the device directly for that. get_active_topology() returns this data with no schema change, since it already returns the whole validated topology mapping verbatim.

Real-lab acceptance tests are gated

Real-lab tests are never run by a plain pytest:

NETWORK_LAB_REAL_TESTS=1 pytest tests/test_real_lab_iosxr.py -v --tb=line

(--tb=line, and never --showlocals, so a real device's password never ends up in a failure traceback.)

Documentation

  • docs/architecture.md — the full system architecture: design goals, configuration model, MCP interface, device access, terminal architecture, topology discovery, the CLI control plane, security boundaries, and persistence/lifecycle.

  • docs/mcp_tools.md — the current seven-tool MCP contract.

  • docs/cli_reference.md — the full human CLI command reference.

  • docs/scenario_format.md — the current scenario/reference format.

Directory structure

network-lab-mcp/
├── pyproject.toml
├── README.md
├── .gitignore
├── run_cli.sh                 # human CLI launcher
│
├── src/
│   └── network_lab_mcp/
│       ├── __init__.py
│       ├── mcp_server.py      # stdio MCP server, defines the 7 tools
│       ├── lab.py             # running-config/topology/access-info/scenario/reference loading
│       ├── terminal.py        # tmux session management, ssh/telnet launch
│       ├── discovery.py       # topology discovery (LLDP/CDP + L3 enrichment)
│       │
│       └── cli/                       # human-facing CLI
│           ├── __init__.py
│           ├── main.py                # REPL, prompt rendering, key bindings, dispatch
│           ├── config.py              # candidate configuration, dirty state, commit/clear
│           ├── grammar.py             # command grammar single source of truth
│           └── editor.py              # external ($VISUAL/$EDITOR/vim) YAML editor support
│
├── lab/
│   ├── settings.example.yaml  # tracked template (running-config)
│   ├── settings.yaml          # local only, gitignored (running-config)
│   ├── principles.yaml
│   │
│   ├── access-info/
│   │   ├── sample.yaml        # tracked; fictional sample only
│   │   └── ...                # any other file here is local/private, gitignored
│   │
│   ├── topologies/
│   │   └── sample.yaml        # tracked; safe logical data, documentation-only addresses
│   │
│   ├── scenarios/
│   │   └── sample.yaml        # tracked; every other file here is local/private, gitignored
│   │
│   └── references/
│       └── sample.yaml        # tracked; every other file here is local/private, gitignored
│
└── docs/
    ├── architecture.md
    ├── mcp_tools.md
    ├── cli_reference.md
    └── scenario_format.md

lab/access-info/sample.yaml and lab/topologies/sample.yaml are independent sample files that happen to share a name purely by convention (both are the one canonical public sample for their respective concept) — running-config's explicit access-info <name> / topology <name> selections are the only real association between an access-info definition and a topology; filenames are never matched to infer one.

Sample lab

lab/topologies/sample.yaml, lab/access-info/sample.yaml, lab/scenarios/sample.yaml, and lab/references/sample.yaml are tracked in git and describe one small, coherent fictional lab (R1/R2, an optional jump1 jump host). The topology holds only safe logical data; the access-info definition holds the matching fictional connection data, using only documentation-only addresses from the RFC 5737 192.0.2.0/24 range. These addresses are not reachable and must not be used as real connectivity targets, and the sample password (example-password) is not a real credential — create your own private access-info for a real lab. The samples exist to validate YAML loading, MCP structured output, device-access resolution, and device-name/session-name mapping.

Git safety design

This repository is meant to be shared publicly, but real access-info YAML contains device names, management addresses, usernames, passwords, and other private lab information; real scenario/reference files may also describe private task or customer context. Rather than relying on documentation alone, .gitignore provides a default technical guard: only each concept's one fictional sample.yaml is tracked, and every other file under lab/topologies/, lab/access-info/, lab/scenarios/, and lab/references/ is gitignored by default, regardless of its name.

lab/settings.yaml
lab/topologies/*.yaml
!lab/topologies/sample.yaml
lab/access-info/*.yaml
!lab/access-info/sample.yaml
lab/scenarios/*.yaml
!lab/scenarios/sample.yaml
lab/references/*.yaml
!lab/references/sample.yaml

This is not a complete security boundary — it is a default that lowers the chance of accidentally committing real credentials or private lab data to a public repository. Treat any access-info file that leaves this repository as sensitive regardless of what git tracks. Topology YAML never carries private access fields at all (lab.validate_topology_data() rejects address/transport/port/username/password outright), so it is a much lower-risk file even before considering .gitignore.

Current limitations

  • Topology discovery supports IOS XR (LLDP + CDP), IOS XE (LLDP + CDP), and classic IOS (CDP only): no NX-OS discovery, no SNMP/NETCONF/RESTCONF, and no generic discovery plugin framework.

  • L3 topology enrichment deliberately stores only a directly observed IPv4 address + VRF per interface: no prefix length, no subnet/link inference from addresses, and no operational state is ever persisted into topology.

  • No automatic stale-link pruning after Discovery.

  • terminal_open() automates SSH password authentication and classic-IOS- style Telnet username/password login only; it does not automate a host-key confirmation prompt, and terminal_send()/terminal_read() themselves remain a simple, unattended capture/send with no login automation of their own beyond that one-time terminal_open() step.

  • Non-editable/wheel installation is not supported.

  • access-info has no external-editor support (structured CLI editing only), unlike topology/scenario/reference.

  • Single-hop OpenSSH ProxyJump only: a jump host cannot itself reference another jump host, and only type: host / transport: ssh jump hosts are supported.

  • The case-only topology-name collision safeguard (topology <name>) is the only such safeguard; there is no equivalent for device <name> or for access-info/jump-host/scenario/reference names (identifiers remain fully case-sensitive regardless).

  • Scenario/reference schema is intentionally not fixed — only "valid YAML, root is a mapping" is enforced (see docs/scenario_format.md).

MCP SDK

Network Lab MCP uses the official MCP Python SDK, pinned as mcp>=2.2,<3 in pyproject.toml. It uses the SDK's MCPServer class over the stdio transport. See docs/mcp_tools.md for the tool reference.

Version, license

Run help in the CLI (./run_cli.sh) for a Quick Start covering the typical configuration workflow, and show version (EXEC mode only) for the exact version, release date, source revision, and license currently running. help claude explains how Claude Code uses Network Lab MCP.

Network Lab MCP is licensed under the GNU General Public License v3.0 (see LICENSE). Version, author, and license metadata are declared once in pyproject.toml and read at runtime via importlib.metadatashow version's output and this README are the same source, never two independently maintained copies.

Available Tools

7 tools
get_active_topologyA

Return the currently active lab topology: where the work is performed and which devices and links exist. Settings and the topology YAML are reloaded from disk on every call, so editing lab/settings.yaml takes effect immediately without restarting this server.

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

TDQS

A4.2/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries the full burden of behavioral disclosure. It adds valuable context by noting that settings and YAML are reloaded from disk on every call, so edits take effect immediately. This reveals the tool's fresh-read behavior and implies it is non-mutating, though it does not explicitly state side-effect safety.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is two sentences with no filler. The main purpose is front-loaded in the first sentence, and the second sentence adds a single, relevant behavioral note about reloading. Every word earns its place.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a simple retrieval tool with no parameters and no output schema, the description covers the key points: what it returns and a notable behavioral characteristic. It does not specify the exact return format (e.g., JSON structure), but the mention of 'topology YAML' gives a hint. Overall, it is sufficiently complete for an agent to invoke correctly.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The tool has zero parameters, so the baseline is 4. The description does not need to explain parameter semantics since none exist, and it correctly omits any param-related details.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the specific verb 'Return' and the resource 'currently active lab topology', and explains what it contains (where work is performed, devices, links). It distinguishes itself from sibling tools like terminal operations by being the only topology-fetching tool, so an agent can easily identify its purpose.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description implies when to use it (when you need the active topology) but does not explicitly contrast with alternatives or state exclusions. Siblings are unrelated to topology, so the lack of explicit routing is less critical, but the guidance remains implicit rather than explicit.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_execution_instructionsA

Return the operating principles, the active scenario, and the active reference knowledge for the current task: how to behave, what to accomplish, and what reusable knowledge is available. Reloaded from disk on every call.

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

TDQS

A4.4/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries the full burden, and it adds a meaningful behavioral detail: 'Reloaded from disk on every call,' indicating fresh data each invocation. The use of 'Return' also implies a read-only operation, though side effects, authorization, and error behavior are not explicitly disclosed.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is two sentences with no fluff: it front-loads the returned content and adds the reload behavior in a single closing clause. Every word earns its place.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a zero-parameter, no-output-schema tool, the description is mostly complete: it names all three content areas and explains their purpose. It does not specify the exact structure of the returned data, but for this simple introspection tool that is a minor gap rather than a blocker.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The tool has zero parameters, so the baseline is 4. The description focuses on what the call returns rather than parameters, which is appropriate for a parameterless retrieval tool.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description uses a specific verb ('Return') and names three concrete resources: operating principles, active scenario, and active reference knowledge. This makes the tool's purpose immediately clear and distinguishes it from terminal-focused siblings and get_active_topology.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The phrase 'for the current task' conveys when to use this tool: when the agent needs behavioral guidance, task context, or reusable knowledge. It does not explicitly discuss alternatives or exclusions, but the sibling tools have clearly different functions, so usage context is reasonably clear.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

terminal_closeA

Close a device's managed production terminal session. Only ever targets the production session namespace; cannot affect validation sessions. Deliberately does not require the device to still be present in the active topology, so a stale session left over from before the active topology changed can always be closed.

ParametersJSON Schema
NameRequiredDescriptionDefault
deviceYes

TDQS

A4.1/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries the behavioral disclosure burden. It reveals non-obvious behavior: it only targets the production session namespaceĺ and deliberately works even when the device is absent from the active topology. It does not mention result/error behavior, but the key operational limits are disclosed.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Three sentences with no filler: the action is stated first, followed by a critical exclusion and a useful stale-session edge case. Slightly wordy in the third sentence, but every sentence contributes meaningful information.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a one-parameter tool with no output schema incl, the description covers target, scope, and an important edge case. It does not explain success/error responses or whether an open session must exist, but an agent can generally invoke the tool correctly with the provided information.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The only parameter, device, is minimally described by the schema. The description clarifies that the device owns the session being closed, but it does not specify the identifier format or where to source it. With 0% schema coverage, this is only partial compensation, though reasonable for a single string parameter.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description opens with a specific verb and resource: 'Close a device's managed production terminal session.' It also immediately distinguishes itself from related terminal tools by scoping to the production session namespace and explicitly stating it cannot affect validation sessions.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description gives clear context for when to use the tool: closing production terminal sessions, including stale sessions from before a topology change. It also states what it cannot do (affect validation sessions), which helps an agent avoid misusing it, though it does not explicitly name a sibling alternative.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

terminal_listA

List every currently managed production terminal session, regardless of whether each device is still present in the active topology -- including a stale session left over from before the active topology changed, so it remains visible and closeable via terminal_close() even though terminal_send()/terminal_read() will refuse to use it. Local validation sessions are never included, regardless of whether a device happens to be named similarly to a validation identifier.

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

TDQS

A4.1/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries the full behavioral burden. It discloses important edge-case behavior: stale sessions remain visible and closeable, terminal_send()/terminal_read() will refuse them, and validation sessions are always excluded. This is substantial behavior disclosure, though it does not mention output format or error behavior.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is front-loaded with the core action and then expands on meaningful edge cases. It is somewhat dense and long-winded, but each clause adds essential selection and invocation context, so the length is justified.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a zero-parameter listing tool with no output schema, the description is nearly complete: it defines what is included, what is excluded, and how results relate to sibling operations. It could be more complete by stating the return shape, but the core semantics are fully covered.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The tool has zero parameters and an empty input schema, so there is no parameter semantics to clarify. The description correctly focuses on behavior rather than parameters, and the baseline of 4 applies because no argument documentation is needed.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description opens with a specific verb and resource: 'List every currently managed production terminal session.' It clearly differentiates itself from get_active_topology by noting it includes sessions regardless of active topology, and from terminal_send/read/close by specifying stale sessions are closeable but not usable for I/O.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description gives strong contextual cues: use it to see stale sessions and close them via terminal_close(), and it confirms local validation sessions are excluded. However, it never explicitly states when to choose this over get_active_topology or when not to use it, so the guidance remains implied rather than direct.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

terminal_openA

Open (or reuse) a terminal session for a device in the active topology, launching ssh or telnet inside a dedicated tmux environment. The active topology is reloaded from disk before opening the session. An interactive login prompt from the device itself is answered automatically using the active access-info definition's own private credentials, once it can be safely confirmed to belong to the target device (never a jump host) -- credentials are never returned by this tool or any other. This works for SSH (the client's own password prompt) and for Telnet (a classic-IOS-style Username:/Password: sequence); which one applies depends on the device's own configured transport. A host-key confirmation prompt, or any other situation this cannot safely resolve on its own, is still left for terminal_read()/terminal_send() to handle interactively.

ParametersJSON Schema
NameRequiredDescriptionDefault
deviceYes

TDQS

A4.1/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries the full burden of disclosing behavior. It reveals that credentials are never returned, that topology is reloaded, that login prompts are auto-answered with private credentials, that it avoids jump hosts, and that it handles both SSH and Telnet. This is exceptionally transparent, leaving no critical ambiguity about what the tool does.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is lengthy but each sentence adds essential detail about security, transport handling, and fallback behavior. The core purpose is front-loaded, and the complexity justifies the length. It avoids redundancy, though it could be slightly tightened without losing substance.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

The description covers the opening behavior, authentication, and fallback handling, but does not mention what the tool returns (e.g., a session identifier or status) and does not explain error conditions or what happens if the device is not found. With no output schema, this information is missing, leaving the agent uncertain about the tool's result.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters2/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The single parameter 'device' has no schema description (0% coverage), and the description only says 'for a device in the active topology' without specifying how the device is identified (e.g., by name, ID, or IP). It does not add format or value guidance beyond what the schema already provides. Given the low schema coverage, the description should compensate but does not sufficiently clarify the parameter's semantics.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool's purpose: open or reuse a terminal session for a device in the active topology, launching ssh or telnet in tmux. It distinguishes itself from sibling tools like terminal_send and terminal_read by focusing on session opening rather than interactive I/O. The verb and resource are specific and unambiguous.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description gives clear context on when to use this tool (to establish a session) and explicitly routes interactive prompts that the tool cannot safely resolve (host-key confirmations) to terminal_read()/terminal_send(). However, it does not explicitly state when not to use this tool, such as for simply sending a command without opening a session, though this is implied by sibling tool names.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

terminal_readA

Capture recent terminal output for a device, so the caller can inspect the current prompt, command output, a password/interactive prompt, paging state, or unexpected errors. lines limits how much recent scrollback is returned (default 100); the underlying tmux session retains a much larger history buffer. The device must still be present in the active topology, reloaded from disk on every call -- an already-open session does not stay readable if the active topology changes to no longer include this device.

ParametersJSON Schema
NameRequiredDescriptionDefault
linesNo
deviceYes

TDQS

A4.3/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries the burden of behavioral disclosure. It explicitly states that the device must be present in the active topology, that sessions are reloaded from disk on every call, and that an open session becomes unreadable if the topology changes. This is significant behavioral context that helps the agent avoid stale-state mistakes. However, it does not mention what happens if the device is not present (error type), nor whether this operation has side effects (it appears read-only, but not explicitly stated). Thus a 4, not 5.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is concise but packs significant information. It opens with a clear purpose statement, then details the 'lines' parameter, and ends with a critical caveat about topology. The sentences are purposeful and front-load the most important info. The caveat is placed at the end, which is acceptable. No filler. A 4 because it is slightly long and could be more concise, but each sentence earns its place.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool's complexity (a read operation on a terminal session with a topology dependency), the description covers the key usage and behavioral aspects. It doesn't have an output schema, so it doesn't explain the return format, but that might be inferable. The description is complete enough for an agent to call it correctly: it knows the tool reads output, the 'lines' limit, and the topology dependency. The only missing piece is what happens when the device is absent, which could be inferred as an error. Thus a 4.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The schema has 0% description coverage (no parameter descriptions in the schema), so the description must compensate. It explains the 'lines' parameter: 'limits how much recent scrollback is returned (default 100)'. It does not explain 'device' beyond the tool's purpose, but the purpose makes it clear that 'device' identifies which device's output to capture. This adds some meaning beyond the schema's raw type and default, but not exhaustive – e.g., no information about acceptable formats for device IDs. Thus a 3.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool's purpose: capturing recent terminal output for a device, and explicitly enumerates the kinds of output it can capture (prompt, command output, interactive prompts, paging, errors). This distinguishes it from sibling tools like terminal_send (which sends input) and terminal_open (which opens a session). The verb 'Capture' is specific and the resource is 'terminal output'.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides explicit guidance on when to use the tool: to inspect terminal output for a device during interactive sessions or after commands. It also gives a crucial exclusion: the device must be in the active topology, and the session does not remain readable if topology changes. This helps the agent choose this tool over alternatives like terminal_list (which lists sessions) and terminal_open (which creates a session).

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

terminal_sendA

Send input to a device's open terminal session. The device must still be present in the active topology, reloaded from disk on every call -- an already-open session does not stay usable if the active topology changes to no longer include this device.

Execution order is fixed and deterministic: if text is given it is sent literally first, then any keys (e.g. "C-c", "Tab", "Up") are sent in the supplied order, then Enter is sent last if enter is true. Nothing is deduplicated: passing keys=["Enter"] together with enter=true sends Enter twice. text is delivered to the pane without ever passing through a subprocess's own argv or appearing in an error message, and this MCP server does not separately log or echo it back in any tool result. It is not hidden from the device's own terminal transcript, though: this project intentionally records the raw pane output via tmux pipe-pane (see show logging), and if the remote endpoint echoes back what it received, that echo may naturally appear in that transcript like any other terminal output.

ParametersJSON Schema
NameRequiredDescriptionDefault
keysNo
textNo
enterNo
deviceYes

TDQS

A4.7/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries the full burden and excels at it. It discloses fixed execution order, no deduplication, that text bypasses argv and error messages, that the server does not log or echo input, and that tmux pipe-pane still records raw pane output. This is unusually transparent for a side-effecting tool.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is long but dense, with no filler. It front-loads the primary purpose and then adds necessary behavioral, ordering, and logging caveats. Every sentence earns its place.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

The description covers prerequisites, ordering, logging, and privacy nuances almost completely. The only notable gap is return-value semantics: there is no output schema, and the description references 'tool result' only in passing without explaining what the agent receives on success.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, but the description compensates fully by explaining the semantics of device, text, keys, and enter, including ordering and the duplicate-Enter edge case. It adds meaning well beyond the bare parameter names in the input schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a specific verb and resource ('Send input to a device's open terminal session') and clearly differentiates the tool from siblings such as terminal_read, terminal_list, and terminal_open by focusing on sending input. It also narrows the scope to open sessions within the active topology.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It provides strong operational context: the device must still be present in the active topology and the session is reloaded from disk on every call. However, it does not explicitly name sibling alternatives or state when not to use this tool, so the guidance is clear but not fully explicit.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

  1. 7 tool updatesv0.1.0
    • First observedget_active_topology
    • First observedget_execution_instructions
    • First observedterminal_close
    • First observedterminal_list
    • First observedterminal_open
    • First observedterminal_read
    • First observedterminal_send

TDQS

A4.4/5.0

Scored across 7 tools

Disambiguation5/5

Each tool has a clearly distinct purpose: two retrieve context (topology, instructions) and five handle terminal session lifecycle (open, send, read, list, close). There is no overlap or ambiguity between any of them.

Naming Consistency5/5

Tool names follow a consistent snake_case convention with a clear pattern: `get_*` for information retrieval and `terminal_*` for terminal operations. This makes the naming predictable and easy to navigate.

Tool Count5/5

With 7 tools, the server is well-scoped for its purpose: two context-gathering tools and five terminal management tools cover the necessary actions without unnecessary bloat or gaps.

Completeness5/5

The terminal session lifecycle is fully covered: open, send, read, list, and close. The topology and instructions tools provide sufficient context for the agent to operate. No obvious missing operations exist for the stated domain.

Maintenance

ActivityMaintained
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    D
    maintenance
    Provides AI assistants with direct access to multi-vendor network devices for tasks like configuration management, health checks, and topology discovery through 35 specialized tools. It enables natural language control over platforms including Cisco, Juniper, and Nokia using SSH, NETCONF, and SNMP protocols.
    11
    MIT
  • F
    license
    Not graded
    quality
    D
    maintenance
    Enables AI assistants to execute SSH commands on network devices using natural language, supporting multiple vendors and authentication methods for automated network management.
    -
  • F
    license
    Not graded
    quality
    C
    maintenance
    Enables AI assistants to manage network devices via gNMI protocol, including querying capabilities, reading/modifying configurations, and subscribing to telemetry data through natural language.
    -