Skip to content

Presidio-Federal/cml-mcp

Repository files navigation

CML MCP Server

Build Status

A Model Context Protocol (MCP) server that enables AI agents to dynamically deploy and manage Cisco Modeling Labs (CML) network topologies through conversational interfaces.

What This Does

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

Example Conversations

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.

Quick Start

Prerequisites

  • Docker installed
  • Access to a Cisco Modeling Labs (CML) server
  • CML credentials (username/password or API token)

Run the Container

# 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

Required Environment Variables

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_TOKEN instead of username/password

Optional Environment Variables

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

Using Docker Compose

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-stopped

Then run:

docker-compose up -d

Using Environment File

For 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=false

Run with:

docker run -d -p 3010:3010 --env-file .env --name cml-mcp-server ghcr.io/presidio-federal/cml-mcp:latest

Testing with FastMCP

FastMCP provides an interactive way to test the MCP server and its tools.

Install FastMCP

pip install fastmcp

Connect to Your Running Server

# Connect to the container
fastmcp client http://localhost:3010

Example Session

$ 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"

Available Tools (17 Total)

Lab Management

  • 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

Node Operations

  • 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

Tag-Based Management

  • 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)

Topology Building

  • cml_connect_nodes - Create links between nodes
  • cml_get_next_available_interface - Find available interfaces
  • cml_get_available_coordinates - Calculate optimal node positions

Discovery

  • cml_list_node_definitions - List available device types
  • cml_list_image_definitions - List available software images

Common Workflows

Deploy a New Topology

{
  "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"}
  ]
}

Add Node to Existing Lab

{
  "tool": "cml_add_node_to_topology",
  "lab_id": "My Lab",
  "nodes": [
    {
      "label": "New-Router",
      "node_definition": "csr1000v",
      "x": 500,
      "y": 200,
      "tags": ["new"]
    }
  ]
}

Tag-Based Operations

# 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"

Building and Contributing

Clone the Repository

git clone https://github.com/Presidio-Federal/cml-mcp.git
cd cml-mcp

Build Locally

# 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

Development Setup

# 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

Adding New Tools

  1. Create tool file in tools/ directory (e.g., my_tool.py)
  2. Implement class with execute() method
  3. Add import to tools/__init__.py
  4. Register tool in server.py
  5. Test with FastMCP
  6. 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()

Troubleshooting

Container won't start

# Check logs
docker logs cml-mcp-server

# Verify environment variables
docker exec cml-mcp-server env | grep CML

Can't connect to CML

# Test from container
docker exec cml-mcp-server ping your-cml-server

# Verify CML is accessible
curl -k https://your-cml-server/

Authentication fails

  • Verify CML credentials are correct
  • Check if CML account is active
  • For special characters in password, use .env file or single quotes
  • Try using CML_TOKEN instead of username/password

SSL certificate errors

  • Set CML_VERIFY_SSL=false for self-signed certificates
  • For production, install valid SSL certificates on CML server

Architecture

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

CI/CD

The project uses GitHub Actions for automated builds:

  • Trigger: Push to main branch or version tag
  • Process: Build → Tag → Push to GitHub Container Registry
  • Tags: Version (from __init__.py), timestamp, commit SHA, latest

License

This project is maintained by Presidio Federal and is available for public use.

Support

For issues or questions:

  • Open an issue on GitHub
  • Check CML and virl2_client documentation
  • Verify CML connectivity and credentials

About

Docker container with MCP tools for Cisco Modeling Labs

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages