A Model Context Protocol (MCP) server for managing Cisco IOS XE devices via SSH using Netmiko with enterprise-grade security.
This MCP server provides secure SSH-based management capabilities for Cisco IOS XE devices, enabling:
- Device Configuration: Send configuration commands to IOS XE devices
- Monitoring Commands: Execute show commands for device monitoring
- Secure Authentication: Environment-only credentials (no password parameters)
- Password Protection: Comprehensive password masking and sanitization
- Enhanced Security: SSH-based communication with timeout controls and error protection
- HTTP Transport: Modern MCP transport for MCP clients (Cursor, LibreChat, etc.)
show_command: Execute any show command on an IOS XE device (credentials from environment only)config_command: Send configuration commands to an IOS XE device (only available whenIOS_XE_READ_ONLY=false)
For monitoring and troubleshooting workflows, you can enable read-only mode which only exposes the show_command tool:
# In .env
IOS_XE_READ_ONLY=true # Only show commands available (recommended for AI troubleshooting)
IOS_XE_READ_ONLY=false # Both show and config commands (default)| Mode | IOS_XE_READ_ONLY |
Available Tools |
|---|---|---|
| Read-Only | true |
show_command only |
| Read-Write | false (default) |
show_command + config_command |
Use Cases:
- netops-stack troubleshooting: Set
IOS_XE_READ_ONLY=true- AI can only run show commands - Configuration automation: Set
IOS_XE_READ_ONLY=false- AI can push configs
- 🔐 Environment-only credentials - No password parameters accepted
- 🔐 Read-only mode - Disable config commands for safe monitoring (
IOS_XE_READ_ONLY=true) - 🔐 Password masking - Passwords masked in all logs (
your_default_password→y*********) - 🔐 Error sanitization - Passwords removed from error messages (
***REDACTED***) - 🔐 Startup validation - Server fails securely if credentials missing
- ✅ SSH timeout controls - Configurable connection timeouts
- ✅ Enhanced error handling - Safe error reporting without credential exposure
- ✅ Non-root container execution - Minimal privilege operation
- ✅ Automatic configuration saving - Changes persisted automatically (when not read-only)
SECURITY: All credentials are loaded from environment variables only. No password parameters are accepted for maximum security.
# Secure: Only host and command required (credentials from .env)
result = show_command("show version", "switch.company.com")
result = show_command("show ip interface brief", "switch.company.com")
result = show_command("show ip route summary", "switch.company.com")
# Check BGP status
result = show_command("show ip bgp summary", "switch.company.com")
result = show_command("show bgp l2vpn evpn summary", "switch.company.com")
# Device information commands
result = show_command("show ip protocols", "switch.company.com")
result = show_command("show running-config interface gi0/1", "switch.company.com")# Secure: Only commands and host required (credentials from .env)
result = config_command([
"interface GigabitEthernet0/1",
"description Connected to Server",
"no shutdown"
], "switch.company.com")
# Network configuration
result = config_command([
"ip route 10.1.0.0 255.255.0.0 10.1.1.1"
], "switch.company.com")
# Multiple interface configuration
result = config_command([
"interface range GigabitEthernet0/1-4",
"switchport mode access",
"switchport access vlan 100"
], "switch.company.com")INFO: Loaded credentials for user: admin
INFO: Secure mode: Credentials loaded from environment only
INFO: Connecting to switch.company.com as 'admin' (pwd: y*********) to execute: show ip bgp summary
INFO: Successfully executed command on switch.company.com
REQUIRED: The server requires credentials in environment variables for security. Copy .env.example to .env and configure:
cp .env.example .env
nano .env| Variable | Description | Default | Required |
|---|---|---|---|
MCP_HOST |
Server bind address | 0.0.0.0 |
No |
MCP_PORT |
Server port | 8003 |
No |
LOG_LEVEL |
Logging level | INFO |
No |
| Variable | Description | Example | Notes |
|---|---|---|---|
IOS_XE_USERNAME |
SSH username | admin |
REQUIRED - Server fails without this |
IOS_XE_PASSWORD |
SSH password | your_default_password |
REQUIRED - Server fails without this |
| Variable | Description | Example | Notes |
|---|---|---|---|
IOS_XE_READ_ONLY |
Read-only mode | true / false |
true = show commands only (default: false) |
SSH_TIMEOUT |
SSH connection timeout | 60 |
Seconds |
DEFAULT_DEVICE_TYPE |
Netmiko device type | cisco_ios |
Usually cisco_ios for IOS XE |
# IOS XE MCP Server Configuration
# =====================================================
# REQUIRED: Device credentials (server fails without these)
IOS_XE_USERNAME=admin
IOS_XE_PASSWORD=your_default_password
# Read-Only Mode (recommended for monitoring/troubleshooting)
IOS_XE_READ_ONLY=false # Set to 'true' for show commands only
# Optional: Server configuration
MCP_HOST=0.0.0.0
MCP_PORT=8003
LOG_LEVEL=INFOSee .env.example for a comprehensive list of all available configuration options including security settings, performance tuning, and multiple device scenarios.
- Cisco IOS XE device with SSH enabled
- Valid SSH credentials (username/password)
- Network connectivity from the MCP server to the device
- SSH port (22) accessible
The server is included in the main docker-compose.yml:
# Start only IOS XE MCP server
docker-compose up -d ios-xe-mcp-server
# View logs
docker-compose logs -f ios-xe-mcp-server
# Stop server
docker-compose stop ios-xe-mcp-server# Build image
docker build -t ios-xe-mcp-server .
# Run container
docker run -d \
--name ios-xe-mcp-server \
-p 8003:8003 \
ios-xe-mcp-server# Install dependencies
uv sync
# Run server directly
uv run python ios_xe_mcp_server.py- 🔐 Environment-Only Credentials: Passwords never appear in function parameters or traces
- 🔐 Startup Validation: Server fails securely if credentials are missing from environment
- 🔐 Password Masking: All passwords masked in logs (
your_default_password→y*********) - 🔐 Error Sanitization: Passwords automatically removed from error messages (
***REDACTED***) - 🔐 No Credential Storage: Device credentials loaded from environment only
- 🔐 SSH Only: Secure encrypted communication to devices
- 🔐 Timeout Controls: SSH connections have configurable timeouts
- Server runs on configurable port (default 8003)
- Supports Docker network isolation
- SSH connections use standard port 22
- All device communication is encrypted
- No credentials transmitted in API calls
- Runs as non-root user
- Security options enabled (
no-new-privileges) - Resource limits configured
- Minimal attack surface
- Environment variables isolated per container
All operations are logged securely without exposing credentials:
# Read-Write Mode (default)
INFO: Loaded credentials for user: admin
INFO: Secure mode: Credentials loaded from environment only
INFO: ⚠️ READ-WRITE MODE: Configuration commands are available
INFO: Starting SECURE IOS XE MCP server on 0.0.0.0:8003
INFO: Mode: READ-WRITE (config enabled)
# Read-Only Mode (IOS_XE_READ_ONLY=true)
INFO: Loaded credentials for user: admin
INFO: Secure mode: Credentials loaded from environment only
INFO: 🔒 READ-ONLY MODE ENABLED: Only show commands are available
INFO: Starting SECURE IOS XE MCP server on 0.0.0.0:8003
INFO: Mode: READ-ONLY (show commands only)
🔐 Environment Credential Errors
# Server fails to start with missing credentials
ERROR: IOS_XE_USERNAME and IOS_XE_PASSWORD must be set in environment!
# Solution: Check .env file exists and has correct values
cat ios-xe-mcp-server/.env
# Verify environment variables are loaded
docker-compose exec ios-xe-mcp-server env | grep IOS_XE🔐 MCP Client Parameter Errors
# Error: Validation errors for call[show_command] - username/password unexpected
# This happens when using old function signatures
# Solution: Use new secure syntax (no credentials)
show_command("show version", "switch.company.com") # ✅ Correct
show_command("show version", "switch.company.com", username="user", password="pass") # ❌ WrongSSH Connection Failures
# Check network connectivity
ping <device-ip>
# Verify SSH service
telnet <device-ip> 22
# Check device SSH configuration
show ip ssh
# Verify credentials work manually
ssh admin@switch.company.comAuthentication Errors
# Check if credentials in .env match device
# Look for sanitized error messages (passwords are hidden)
Authentication to device failed.
Common causes:
1. Invalid credentials in environment
2. Device SSH configuration
3. Network connectivityTimeout Issues
# Increase timeout in environment variables
SSH_TIMEOUT=120
# Or check device response time
time ssh admin@switch.company.com "show version"Enable detailed logging by setting LOG_LEVEL=DEBUG in environment variables.
# View detailed logs with password masking
docker-compose logs -f ios-xe-mcp-server
# Example secure log output:
# INFO: Connecting to switch.company.com as 'admin' (pwd: y*********) to execute: show version# Test that password masking works
docker-compose exec ios-xe-mcp-server uv run python -c "
from ios_xe_mcp_server import mask_password
print('Password masking test:', mask_password('your_default_password'))
"
# Output: Password masking test: y*********Execute a show command on an IOS XE device.
🔐 SECURITY: Credentials are loaded from environment variables only. No password parameters accepted.
Parameters:
command(string): Show command to execute (e.g.,"show ip bgp summary")host(string): Device IP address or hostname (e.g.,"switch.company.com")
Returns: Command output as string
Example:
result = show_command("show version", "switch.company.com")Send configuration commands to an IOS XE device.
🔐 SECURITY: Credentials are loaded from environment variables only. No password parameters accepted.
IOS_XE_READ_ONLY=false (default). Not registered in read-only mode.
Parameters:
commands(list): List of configuration commands to sendhost(string): Device IP address or hostname
Returns: Configuration results and save status
Example:
result = config_command([
"interface GigabitEthernet0/1",
"no shutdown"
], "switch.company.com")Both tools automatically:
- ✅ Load credentials from
IOS_XE_USERNAMEandIOS_XE_PASSWORDenvironment variables - ✅ Mask passwords in logs (
y*********) - ✅ Sanitize error messages (
***REDACTED***) - ✅ Validate environment setup on server startup
- ✅ Save configuration changes automatically
This project is licensed under the Cisco Sample Code License, Version 1.1.