PyATS REST API and MCP server for Cisco device show commands
2.0K
A FastAPI-based REST API and MCP (Model Context Protocol) server for executing show commands on Cisco network devices using PyATS/Unicon with optional SSH jumphost support.
include, exclude, begin, sectionshow commands allowed;, |, &&, etc.)Pre-built images are available on Docker Hub!
Quick Start - REST API Only:
# Pull and run from Docker Hub
docker pull jeromemassey76/pyats-ro-api:latest
docker run -d -p 8000:8000 --name pyats-api jeromemassey76/pyats-ro-api:latest
# Or use production compose file
curl -O https://raw.githubusercontent.com/jerome-massey/pyats-ro-api/main/docker-compose.prod.yml
docker-compose -f docker-compose.prod.yml up -d
Quick Start - All Services (REST API + MCP):
# Download and run multi-service setup
curl -O https://raw.githubusercontent.com/jerome-massey/pyats-ro-api/main/docker-compose.mcp.prod.yml
docker-compose -f docker-compose.mcp.prod.yml up -d
# Services available:
# - REST API: http://localhost:8000
# - MCP SSE: http://localhost:3000
📖 See DOCKER_HUB.md for complete Docker Hub deployment guide
Development with hot-reload:
# Development - REST API only
make dev
# Or using docker-compose directly
docker-compose -f docker-compose.dev.yml up
Development - All Services (REST API + MCP):
# Build and run all services locally
docker-compose -f docker-compose.mcp.yml up -d
# Services available:
# - REST API: http://localhost:8000
# - MCP SSE: http://localhost:3000
Start Individual Services:
# REST API only (port 8000)
docker-compose -f docker-compose.mcp.yml up -d pyats-api
# MCP SSE only (port 3000)
docker-compose -f docker-compose.mcp.yml up -d pyats-mcp-sse
# Both services
docker-compose -f docker-compose.mcp.yml up -d
Production with HTTPS:
# Setup SSL certificates and start with Nginx
# See NGINX_DEPLOYMENT.md for complete instructions
docker-compose -f docker-compose.nginx.yml up -d
# Services available:
# - REST API: https://api.yourdomain.com (or https://localhost)
# - MCP SSE: https://mcp.yourdomain.com (or https://localhost:3000)
Endpoints:
Docker Configuration:
For jumphost support, edit your docker-compose file or use environment variables:
environment:
- JUMPHOST_HOST=jumphost.example.com
- JUMPHOST_PORT=22
- JUMPHOST_USERNAME=jumpuser
- JUMPHOST_KEY_PATH=/root/.ssh/jumphost_key
volumes:
- ~/.ssh/id_rsa:/root/.ssh/jumphost_key:ro
Or when using docker run:
docker run -d \
-p 8000:8000 \
-v ~/.ssh/id_rsa:/root/.ssh/jumphost_key:ro \
-e JUMPHOST_HOST=jumphost.example.com \
-e JUMPHOST_PORT=22 \
-e JUMPHOST_USERNAME=jumpuser \
-e JUMPHOST_KEY_PATH=/root/.ssh/jumphost_key \
jeromemassey76/pyats-ro-api:latest
If you prefer to run without Docker:
# Create virtual environment
python3 -m venv venv
source venv/bin/activate
# Install dependencies
pip install -r requirements.txt
# Configure environment (optional for jumphost)
cp .env.example .env
# Edit .env with your jumphost settings
# Run the server
python run.py
Use SSH config files and mounted keys (no more JUMPHOST_* env vars). See SSH_CONFIGURATION.md for the full guide.
Quick start:
ssh_config.d with your jumphost and device routing files (examples in SSH_CONFIGURATION.md).docker run --rm -v ${PWD}/ssh_config.d:/root/.ssh/config.d -v "${PWD}/your_ssh_key:/root/.ssh/ssh_key.mounted:ro" -p 8000:8000 pyats-ro-api:latest python run.py
Docker Compose snippet:
volumes:
- ./ssh_config.d:/root/.ssh/config.d # leave writable so entrypoint can fix perms
- ~/.ssh/my_key:/root/.ssh/ssh_key.mounted:ro
Create a .env file for API configuration (optional):
# API Configuration
API_HOST=0.0.0.0
API_PORT=8000
API_WORKERS=1
# Logging
LOG_LEVEL=INFO
With Docker:
# Development (with hot-reload)
make dev
# Production
make run
# View logs
make logs
# Stop container
make stop
Without Docker:
# Using the run script
python run.py
# Or directly with uvicorn
uvicorn app.main:app --host 0.0.0.0 --port 8000
# Development mode with hot-reload
uvicorn app.main:app --host 0.0.0.0 --port 8000 --reload
curl http://localhost:8000/health
Use Cases:
Direct Connection (No Jumphost):
curl -X POST http://localhost:8000/api/v1/execute \
-H "Content-Type: application/json" \
-d '{
"devices": [
{
"hostname": "192.168.1.1",
"port": 22,
"username": "admin",
"password": "cisco123",
"os": "iosxe"
}
],
"commands": [
{
"command": "show version"
},
{
"command": "show ip interface brief"
}
],
"timeout": 30
}'
With Pipe Options:
curl -X POST http://localhost:8000/api/v1/execute \
-H "Content-Type: application/json" \
-d '{
"devices": [
{
"hostname": "192.168.1.1",
"port": 22,
"username": "admin",
"password": "cisco123",
"os": "iosxe"
}
],
"commands": [
{
"command": "show running-config",
"pipe_option": "include",
"pipe_value": "interface"
},
{
"command": "show ip route",
"pipe_option": "exclude",
"pipe_value": "local"
},
{
"command": "show version",
"pipe_option": "begin",
"pipe_value": "Cisco IOS"
},
{
"command": "show running-config",
"pipe_option": "section",
"pipe_value": "router bgp"
}
],
"timeout": 30
}'
Multiple Devices:
curl -X POST http://localhost:8000/api/v1/execute \
-H "Content-Type: application/json" \
-d '{
"devices": [
{
"hostname": "192.168.1.1",
"username": "admin",
"password": "cisco123",
"os": "iosxe"
},
{
"hostname": "192.168.1.2",
"username": "admin",
"password": "cisco123",
"os": "nxos"
}
],
"commands": [
{
"command": "show version"
}
]
}'
Once the server is running, access the interactive API documentation:
{
"devices": [
{
"hostname": "string (IP or hostname)",
"port": "integer (default: 22)",
"username": "string",
"password": "string",
"os": "enum: ios|iosxe|iosxr|nxos|asa",
"enable_password": "string (optional)"
}
],
"commands": [
{
"command": "string (must start with 'show')",
"pipe_option": "enum: include|exclude|begin|section (optional)",
"pipe_value": "string (required if pipe_option set)"
}
],
"timeout": "integer (default: 30)",
"output_format": "enum: raw|parsed|both (default: raw)"
}
{
"results": [
{
"hostname": "string",
"success": "boolean",
"commands": [
{
"command": "string",
"output": "string",
"parsed": "object (optional)",
"parse_error": "string (optional)",
"success": "boolean",
"error": "string (optional)"
}
],
"error": "string (optional)"
}
],
"total_devices": "integer",
"successful_devices": "integer",
"failed_devices": "integer"
}
ios - Cisco IOSiosxe - Cisco IOS-XEiosxr - Cisco IOS-XRnxos - Cisco NX-OSasa - Cisco ASANote: JunOS is not supported due to incompatible command syntax (uses match instead of include). Attempts to use JunOS will return a validation error with explanation.
;, &&, ||>, <$, `, !.env files excluded from git via .gitignoreThis API also functions as an MCP server, allowing AI assistants like Claude to directly execute network commands.
execute_show_commands - Run show commands on deviceslist_supported_os - List supported operating systemslist_pipe_options - List available pipe filtersoutput_format accepts raw (default), parsed, or both using Genie parsingFor Claude Desktop (stdio):
Add to ~/.config/Claude/claude_desktop_config.json:
{
"mcpServers": {
"pyats": {
"command": "docker",
"args": [
"run", "-i", "--rm",
"pyats-unified:latest",
"python", "mcp_stdio.py"
]
}
}
}
For Remote Access (SSE):
# Start MCP SSE server
docker-compose -f docker-compose.mcp.yml up -d pyats-mcp-sse
# Available at: http://localhost:3000/sse
Full Documentation: See MCP_README.md for complete MCP setup and usage.
For production use, deploy with HTTPS using Nginx reverse proxy:
Quick Setup:
# 1. Generate SSL certificates (self-signed for testing)
mkdir -p ssl
openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
-keyout ssl/key.pem -out ssl/cert.pem -subj "/CN=localhost"
# 2. Update nginx.conf with your domain names
# 3. Start with Nginx
docker build -t pyats-unified:latest .
docker-compose -f docker-compose.nginx.yml up -d
Full Documentation: See NGINX_DEPLOYMENT.md for:
showJumphost Connection Fails:
chmod 600 ~/.ssh/key)ssh -i ~/.ssh/key user@jumphostDevice Connection Fails:
LOG_LEVEL=DEBUGCommand Timeout:
Enable debug logging:
# In .env file
LOG_LEVEL=DEBUG
Or set environment variable:
export LOG_LEVEL=DEBUG
python run.py
The development Docker setup includes:
# Start development environment
make dev
# Access container shell
make shell-dev
# View logs
make logs-dev
# Run tests inside container
make test
# Activate virtual environment
source venv/bin/activate
# Run with hot-reload
uvicorn app.main:app --host 0.0.0.0 --port 8000 --reload
# Install dev dependencies
pip install pytest pytest-asyncio httpx black flake8
# Run tests
pytest tests/
# Format code
black app/
# Lint code
flake8 app/
make help # Show all available commands
make build # Build production image
make run # Run production container
make dev # Run development container
make dev-build # Build development image
make stop # Stop containers
make clean # Remove containers and images
make logs # View logs
make shell # Access container shell
pyats-api/
├── app/
│ ├── __init__.py
│ ├── main.py # FastAPI application
│ ├── models.py # Pydantic models
│ ├── config.py # Configuration
│ └── device_manager.py # PyATS/Unicon device handling
├── examples/
│ ├── client_example.py # Python client examples
│ └── curl_examples.sh # cURL examples
├── Dockerfile # Production container
├── Dockerfile.dev # Development container
├── docker-compose.yml # Production compose
├── docker-compose.dev.yml # Development compose
├── docker-compose.mcp.yml # MCP multi-service compose
├── Makefile # Make commands
├── requirements.txt # Python dependencies
├── requirements-dev.txt # Dev/test dependencies
├── pytest.ini # Pytest configuration
├── tests/ # Unit tests
│ ├── test_models.py # Model validation tests (26 tests)
│ └── README.md # Test documentation
└── README.md # This file
## Testing
### Run Unit Tests
The project includes comprehensive unit tests for all Pydantic models and validation logic.
**Using Docker (Recommended):**
```bash
# Run all tests (26 tests)
docker run --rm -v $(pwd):/app -w /app python:3.11-slim bash -c \
"pip install -q pytest pydantic && python -m pytest tests/ -v"
# Run with coverage
docker run --rm -v $(pwd):/app -w /app python:3.11-slim bash -c \
"pip install -q pytest pydantic pytest-cov && python -m pytest tests/ --cov=app --cov-report=term"
Local Python:
pip install -r requirements-dev.txt
pytest tests/ -v
✅ 26 tests covering:
See tests/README.md for detailed test documentation.
This project is licensed under the MIT License - see the LICENSE file for details.
Copyright (c) 2025 Jerome Massey
Contributions are welcome! Please feel free to submit a Pull Request.
├── requirements.txt # Python dependencies ├── .env.example # Example environment config ├── .dockerignore # Docker ignore rules ├── .gitignore # Git ignore rules ├── run.py # Server startup script ├── quickstart.sh # Quick setup script └── README.md # This file
## License
MIT License - See LICENSE file for details
## Contributing
1. Fork the repository
2. Create a feature branch
3. Make your changes
4. Submit a pull request
## Support
For issues and questions:
- Check the troubleshooting section
- Review logs with DEBUG level
- Open an issue on GitHub
Content type
Image
Digest
sha256:ef701fd10…
Size
335.4 MB
Last updated
9 months ago
docker pull jeromemassey76/pyats-ro-api