A Model Context Protocol (MCP) server that enables AI agents to dynamically deploy and manage Cisco Modeling Labs (CML) network topologies through conversational interfaces.
This MCP server provides AI agents with the ability to:
- Deploy complete network topologies conversationally - Describe what you want, and the agent builds it
- Dynamically modify existing labs - Add nodes, connect devices, reconfigure on the fly
- Manage lab lifecycle - Start, stop, and remove nodes and entire labs
- Query lab state - Inspect topologies, find nodes by tags, check configurations
Deploy a complete topology:
"Can you deploy a topology named ai-test-network with two WAN routers connected to each other, an HQ site connected to both with a ubuntu image connected, and a datacenter with a FTD firewall connected to both routers and a splunk instance connected to the datacenter firewall?"
Expand existing topology:
"Can you add a Splunk instance to my lab-name topology?"
Manage labs:
"Start all nodes tagged 'production' in my lab" "Stop the entire test-lab" "Remove the temporary test nodes from my topology"
The agent understands natural language, translates it into the appropriate CML operations, and executes them using these tools.
- Docker installed
- Access to a Cisco Modeling Labs (CML) server
- CML credentials (username/password or API token)
# Pull the latest image
docker pull ghcr.io/presidio-federal/cml-mcp:latest
# Run with environment variables
docker run -d \
-p 3010:3010 \
-e CML_HOST=your-cml-server.example.com \
-e CML_USERNAME=admin \
-e CML_PASSWORD=your-password \
-e DISABLE_JWT_AUTH=true \
-e CML_VERIFY_SSL=false \
--name cml-mcp-server \
ghcr.io/presidio-federal/cml-mcp:latest| Variable | Description | Example |
|---|---|---|
CML_HOST |
CML server hostname or IP (without https://) | cml.example.com or 10.10.20.50 |
CML_USERNAME |
CML username | admin |
CML_PASSWORD |
CML password | yourpassword |
Alternative authentication:
- Use
CML_TOKENinstead of username/password
| Variable | Description | Default |
|---|---|---|
CML_VERIFY_SSL |
Verify SSL certificates | false |
DISABLE_JWT_AUTH |
Disable JWT authentication | false |
PORT |
Server port | 3010 |
HOST |
Server host | 0.0.0.0 |
Create docker-compose.yml:
version: '3.8'
services:
cml-mcp:
image: ghcr.io/presidio-federal/cml-mcp:latest
ports:
- "3010:3010"
environment:
- CML_HOST=your-cml-server.example.com
- CML_USERNAME=admin
- CML_PASSWORD=your-password
- DISABLE_JWT_AUTH=true
- CML_VERIFY_SSL=false
restart: unless-stoppedThen run:
docker-compose up -dFor passwords with special characters, use an .env file:
# .env
CML_HOST=your-cml-server.example.com
CML_USERNAME=admin
CML_PASSWORD=P@ssw0rd!$pecial#chars
DISABLE_JWT_AUTH=true
CML_VERIFY_SSL=falseRun with:
docker run -d -p 3010:3010 --env-file .env --name cml-mcp-server ghcr.io/presidio-federal/cml-mcp:latestFastMCP provides an interactive way to test the MCP server and its tools.
pip install fastmcp# Connect to the container
fastmcp client http://localhost:3010$ fastmcp client http://localhost:3010
Connected to: CML Python MCP Server v1.0.0
Available tools:
- cml_list_labs
- cml_get_lab_details
- cml_create_topology
- cml_start_stop_lab
- ... (and more)
# List all labs
> cml_list_labs
# Get details about a lab
> cml_get_lab_details --lab_id="My Lab"
# Create a new topology
> cml_create_topology --lab_title="Test" --node_configs='[{"label":"R1","node_definition":"csr1000v"}]'
# Start entire lab
> cml_start_stop_lab --lab_id="Test" --operation="start"- cml_list_labs - List all labs with metadata
- cml_get_lab_details - Get complete lab topology (nodes, links, interfaces)
- cml_start_stop_lab - Start or stop entire lab
- cml_create_topology - Create complete topology with nodes and links
- cml_add_node_to_topology - Add nodes to existing lab
- cml_remove_node_from_topology - Remove nodes from lab
- cml_get_node_info - Get detailed node information
- cml_apply_configs - Apply configurations to nodes
- cml_manage_node_tags - Add or remove tags from nodes
- cml_find_nodes_by_tags - Find nodes with specific tags
- cml_start_nodes_by_tag - Start nodes matching tag(s)
- cml_stop_nodes_by_tag - Stop nodes matching tag(s)
- cml_connect_nodes - Create links between nodes
- cml_get_next_available_interface - Find available interfaces
- cml_get_available_coordinates - Calculate optimal node positions
- cml_list_node_definitions - List available device types
- cml_list_image_definitions - List available software images
{
"tool": "cml_create_topology",
"lab_title": "Hub-and-Spoke",
"node_configs": [
{
"label": "Hub-Router",
"node_definition": "csr1000v",
"x": 200,
"y": 100,
"config": "hostname Hub",
"tags": ["core", "hub"]
},
{
"label": "Spoke-1",
"node_definition": "iosv",
"x": 100,
"y": 300,
"tags": ["spoke"]
}
],
"link_configs": [
{"node_a": "Hub-Router", "node_b": "Spoke-1"}
]
}{
"tool": "cml_add_node_to_topology",
"lab_id": "My Lab",
"nodes": [
{
"label": "New-Router",
"node_definition": "csr1000v",
"x": 500,
"y": 200,
"tags": ["new"]
}
]
}# Tag nodes
cml_manage_node_tags --lab_id="My Lab" --node_ids='["uuid1","uuid2"]' --operation="add" --tags='["production"]'
# Start tagged nodes
cml_start_nodes_by_tag --lab_id="My Lab" --tags="production"
# Stop tagged nodes
cml_stop_nodes_by_tag --lab_id="My Lab" --tags="test"git clone https://github.com/Presidio-Federal/cml-mcp.git
cd cml-mcp# Build the container
docker build -t cml-mcp:local .
# Run your local build
docker run -d -p 3010:3010 \
-e CML_HOST=your-cml-server.com \
-e CML_USERNAME=admin \
-e CML_PASSWORD=password \
-e DISABLE_JWT_AUTH=true \
--name cml-mcp-test \
cml-mcp:local# Create virtual environment
python -m venv venv
source venv/bin/activate # On Windows: venv\Scripts\activate
# Install dependencies
pip install -r requirements.txt
# Create .env file
cat > .env << EOF
CML_HOST=your-cml-server.example.com
CML_USERNAME=admin
CML_PASSWORD=your-password
DISABLE_JWT_AUTH=true
CML_VERIFY_SSL=false
EOF
# Run locally
python -m server
# Test with FastMCP in another terminal
fastmcp client http://localhost:3010- Create tool file in
tools/directory (e.g.,my_tool.py) - Implement class with
execute()method - Add import to
tools/__init__.py - Register tool in
server.py - Test with FastMCP
- Submit pull request
Tool Template:
"""
My Tool Description
"""
import logging
from typing import Dict, Any
from virl2_client import ClientLibrary
logger = logging.getLogger(__name__)
class MyTool:
"""Tool that does something useful."""
def execute(self, client: ClientLibrary, param1: str) -> Dict[str, Any]:
"""
Execute the tool.
Args:
client: Authenticated CML client
param1: Description
Returns:
Dictionary with results
"""
try:
# Implementation here
return {
"ok": True,
"result": "success"
}
except Exception as e:
logger.error(f"Error: {e}")
return {
"ok": False,
"error": str(e)
}
# Create singleton
my_tool = MyTool()# Check logs
docker logs cml-mcp-server
# Verify environment variables
docker exec cml-mcp-server env | grep CML# Test from container
docker exec cml-mcp-server ping your-cml-server
# Verify CML is accessible
curl -k https://your-cml-server/- Verify CML credentials are correct
- Check if CML account is active
- For special characters in password, use
.envfile or single quotes - Try using
CML_TOKENinstead of username/password
- Set
CML_VERIFY_SSL=falsefor self-signed certificates - For production, install valid SSL certificates on CML server
Technology Stack:
- Framework: FastMCP (Python MCP server framework)
- CML SDK: virl2_client (official Cisco Modeling Labs Python SDK)
- Transport: HTTP Streaming
- Container: Multi-stage Docker build with Python 3.11
Why Python SDK:
- Persistent connections with automatic retry
- Intelligent operation batching
- Built-in caching and connection pooling
- Cisco-maintained compatibility
The project uses GitHub Actions for automated builds:
- Trigger: Push to
mainbranch or version tag - Process: Build → Tag → Push to GitHub Container Registry
- Tags: Version (from
__init__.py), timestamp, commit SHA, latest
This project is maintained by Presidio Federal and is available for public use.
For issues or questions:
- Open an issue on GitHub
- Check CML and virl2_client documentation
- Verify CML connectivity and credentials