cisco-sdwan-mcp
Provides tools for interacting with Cisco Catalyst SD-WAN (vManage) to manage and monitor SD-WAN fabric inventory, device health, control and data plane state, alarms, path quality, templates, and policies.
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@cisco-sdwan-mcpwhy is the Frankfurt branch down?"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
Cisco SD-WAN MCP Server
cisco-sdwan-mcp is an MCP server for
Cisco Catalyst SD-WAN Manager (vManage), built with
FastMCP.
It gives an LLM client a working view of your SD-WAN fabric — inventory, device health, control and data plane state, alarms, path quality, templates and policies — so you can ask "why is the Frankfurt branch down?" and get an answer backed by real controller data instead of a guess.
Read-only by default. The configuration-changing tools are not registered unless you explicitly enable them, and even then every call requires a human to approve it.
Contents
Related MCP server: Packet Tracer MCP
What you get
Area | Tools |
Inventory |
|
Device health |
|
Alarms & events |
|
Path quality |
|
Templates & policy |
|
Configuration (opt-in) |
|
Plus four workflow prompts and three resources.
Three design decisions are worth knowing up front, because they shape every tool:
Responses are projected, not dumped. A vManage device record carries 60+ fields; a 200-device fabric would bury a model's context. Each tool returns the fields that answer the question and accepts
detailed=truewhen you want everything.Counts always accompany results. Every list reports
count(what matched) alongsidereturned(what you got), so "3 devices are down" is never confused with "3 devices are down in the first 100 I looked at".Failures come back as answers. A wrong password, an unreachable controller or an unknown hostname returns a readable message — often with the valid options — rather than raising. The model can then correct itself or tell you exactly what to fix.
Quickstart
Prerequisites
Python 3.11+
uv (recommended) or pip
A reachable Cisco Catalyst SD-WAN Manager (vManage) and an account on it
Make a dedicated vManage account. Give it a read-only role to start. The account's privileges are the real security boundary — see Write protection.
Install
From PyPI, if you only want to run it:
uvx cisco-sdwan-mcp # no install, run it straight
pip install cisco-sdwan-mcpFrom a checkout, if you want to change it:
git clone https://github.com/pcDamasceno/cisco-sdwan-mcp.git
cd cisco-sdwan-mcp
# with uv (recommended)
uv sync --extra dev
# with pip
pip install -e ".[dev]"Configure
cp .env.example .envThe three settings you must fill in:
SDWAN_VMANAGE_URL=https://vmanage.example.com:8443
SDWAN_USERNAME=automation-readonly
SDWAN_PASSWORD=...The server reads .env from the repository root at startup — set
SDWAN_ENV_FILE to load a different file. Variables already present in the
environment (compose env_file, Kubernetes secrets) are never overwritten by
it, and the startup log names the file it used.
Run
uv run python -m cisco_sdwan_mcp.server # or: python -m cisco_sdwan_mcp.serverThe server starts over HTTP on 0.0.0.0:8000. The MCP endpoint is at
http://localhost:8000/mcp, a health probe at http://localhost:8000/healthz,
and this README at http://localhost:8000/.
Startup logs confirm what it will talk to before any client connects:
INFO cisco_sdwan_mcp.server: vManage controller: vmanage.example.com:8443 (user automation-readonly, TLS verify: True)
INFO cisco_sdwan_mcp.server: Write tools: disabled (read-only)First call
Point an MCP client at it (see Connecting an MCP client)
and ask for get_fabric_summary. It is one round trip and exercises
authentication, TLS and reachability at once:
{
"total_devices": 42,
"by_type": {"vedge": 38, "vsmart": 2, "vbond": 1, "vmanage": 1},
"by_reachability": {"reachable": 40, "unreachable": 2},
"unreachable_count": 2,
"unreachable_devices": [{"host-name": "BR2-EDGE1", "system-ip": "10.0.0.12", "site-id": "1002"}]
}Configuration
Everything is environment-driven; .env.example is the annotated reference.
vManage connection
Variable | Default | Description |
| — | Controller URL, e.g. |
| — | Hostname instead of a full URL |
|
| Port, when using |
| — | vManage username. Required |
| — | vManage password. Required |
|
| TLS certificate verification |
| — | Path to a CA bundle — the right answer for a private CA |
|
| Seconds to wait for vManage |
|
| Default cap on records per tool call |
|
| Register the configuration tools — see below |
Server
Variable | Default | Description |
|
| Name advertised to MCP clients |
|
|
|
|
| Bind address (HTTP only) |
|
| Bind port (HTTP only) |
|
| How MCP clients authenticate to this server |
|
| Python log level |
SDWAN_USERNAME/SDWAN_PASSWORDauthenticate this server to vManage.MCP_AUTHgoverns how clients authenticate to this server. They are unrelated, and you generally want both.
TLS
vManage very often presents a self-signed or private-CA certificate. In descending order of preference:
Point
SDWAN_CA_BUNDLEat the controller's CA — verification stays on.Add the CA to
certificates/, which the Docker build installs into the container trust store automatically.Only as a last resort, on a lab you control, set
SDWAN_VERIFY_SSL=false. The server logs a warning naming the host each time it does this, because it means anything on the path can read the credentials.
Write protection
The tools that change configuration are gated twice.
Gate 1 — registration. With SDWAN_ENABLE_WRITES unset or false, the
module holding them is never imported. They do not appear in the tool list, so
a model cannot call them by mistake, misinterpretation or prompt injection.
The server is read-only by construction, not by policy.
Gate 2 — confirmation. With writes enabled, each call still asks the user
through MCP elicitation, naming the template or policy and the devices
affected, before anything reaches vManage. Clients that do not implement
elicitation cannot silently proceed — the call is refused unless the caller
passes confirm=true, which puts the decision in a human's hands either way.
SDWAN_ENABLE_WRITES=true uv run python -m cisco_sdwan_mcp.serverWARNING cisco_sdwan_mcp.tools: SDWAN_ENABLE_WRITES=true — configuration-changing tools are registered.
Each one still requires explicit user confirmation before it runs.The vManage account is the real boundary.
SDWAN_ENABLE_WRITEScontrols which tools exist in this server; it does nothing about what the account can do through any other path. If a change must be impossible, use a read-only vManage role — do not rely on this flag alone.
vManage applies configuration asynchronously: a write returns a task_id,
meaning accepted, not applied. Poll get_task_status(task_id) until it
reports done.
The intended flow for a template push, with a review step in the middle:
list_device_templates → find the template
get_device_template → see what it configures and who has it
get_template_input_variables → the exact per-device values (read-only preview)
check_device_health → never push to an already-broken device
attach_device_template → asks for approval, returns a task_id
get_task_status → confirm it actually landedTool reference
Every tool takes limit (cap on records) and most take detailed (return all
vManage fields instead of the summary set).
Inventory
Tool | What it answers |
| Device counts by type, reachability and version, plus every unreachable device. Start here for open questions. |
| Devices vManage is currently talking to. |
| One device's full record. Accepts hostname, system IP or chassis number. |
| Everything provisioned, including devices that never onboarded, have invalid certificates or carry no template. |
Device health
Tool | What it answers |
| Triage in one call — system status, control connections and BFD, with a |
| Uptime, CPU, memory, disk, last reboot reason. |
| Connections to vSmart/vBond/vManage. Check first when a device will not come up. |
| Data-plane tunnels to other edges. Check when sites reach controllers but not each other. |
| OMP peering — control up but OMP down means no overlay routes. |
| Interface status, addressing and error counters. |
These poll the device through vManage, so they reflect live state but cost a
round trip to the edge. Prefer check_device_health over three separate calls.
Alarms and events
Tool | What it answers |
| Counts by severity and component, top rules, most affected devices. Cheap — call before listing. |
| The alarms themselves. |
| Raw event stream — noisier, but shows flaps and transitions that never became alarms. |
Path quality
Tool | What it answers |
| Tunnels breaching thresholds, worst first, each saying which threshold it broke. |
| Raw per-tunnel loss/latency/jitter/vQoE. |
| Historical throughput and error counters. |
These read vManage's statistics database — fast, but only as fresh as the last collection cycle (30 minutes on most deployments). For live state, use the device health tools.
Templates and policy
Tool | What it answers |
| Templates and their attachment counts. |
| One template's definition plus attached devices. |
| The building blocks. |
| Centralized (vSmart) or localized policies, and which is active. |
| Read-only preview of the values an attachment would push. |
Prompts
Reusable workflows that encode the order an engineer actually works in — control plane before data plane, evidence before conclusions.
Prompt | Use it for |
| Structured device triage, stopping at the first real cause |
| A whole-fabric report: devices, alarms, path quality, recommendations |
| Tunnel performance, clustered by color / site / device to point at the cause |
| Pre-change review with an explicit go/no-go — recommends only, never attaches |
Resources
URI | Contents |
| Connection settings in effect — controller, user, TLS mode, whether writes are on. Never includes the password. |
| Current fabric inventory with per-device status |
| One device's full record, by hostname or system IP |
Connecting an MCP client
Claude Desktop (claude_desktop_config.json)
{
"mcpServers": {
"cisco-sdwan": {
"url": "http://localhost:8000/mcp"
}
}
}VS Code (GitHub Copilot) — .vscode/mcp.json
{
"servers": {
"cisco-sdwan": {
"type": "http",
"url": "http://localhost:8000/mcp"
}
}
}With token auth enabled
{
"mcpServers": {
"cisco-sdwan": {
"url": "http://localhost:8000/mcp",
"headers": { "Authorization": "Bearer dev-token" }
}
}
}With an OAuth mode (github, google, oauth-proxy, …) no header is needed —
MCP clients discover the flow and open the login screen themselves.
stdio (local subprocess)
From PyPI — nothing to clone, uvx fetches the package on first run:
{
"mcpServers": {
"cisco-sdwan": {
"command": "uvx",
"args": ["cisco-sdwan-mcp"],
"env": {
"MCP_TRANSPORT": "stdio",
"SDWAN_VMANAGE_URL": "https://vmanage.example.com:8443",
"SDWAN_USERNAME": "automation-readonly",
"SDWAN_PASSWORD": "..."
}
}
}
}From a checkout:
{
"mcpServers": {
"cisco-sdwan": {
"command": "uv",
"args": ["run", "python", "-m", "cisco_sdwan_mcp.server"],
"cwd": "/absolute/path/to/cisco-sdwan-mcp",
"env": {
"MCP_TRANSPORT": "stdio",
"SDWAN_VMANAGE_URL": "https://vmanage.example.com:8443",
"SDWAN_USERNAME": "automation-readonly",
"SDWAN_PASSWORD": "..."
}
}
}
}Transports: HTTP vs stdio
HTTP (default) — the deployment transport. Serves many clients concurrently, works behind load balancers, and is the only transport where
MCP_AUTHapplies. Everything indeploy/assumes it.stdio — for local single-user use where a desktop client spawns the server as a subprocess. No network listener, so
MCP_HOST/MCP_PORTandMCP_AUTHdo not apply; the process is secured by your OS user.
MCP_TRANSPORT=stdio uv run python -m cisco_sdwan_mcp.serverDeploying anywhere or serving multiple users → HTTP. One client on your own machine → either works.
Authenticating MCP clients
Authentication of clients to this server is off by default and selected at
startup with MCP_AUTH. The factory lives in cisco_sdwan_mcp/auth.py; all modes are
backed by FastMCP's built-in providers.
| Use case |
| Local development, or network-level protection (IAM, VPN, mTLS) |
| Fixed bearer tokens — quick tests only, never production |
| You already have an IdP issuing JWTs (Keycloak, Okta, Entra ID, Cognito…) |
| Your IdP issues opaque tokens (RFC 7662) |
| Full OAuth 2.1 login flow via any OAuth provider |
| Full login flow via a hosted identity provider, preconfigured |
# Development tokens (never in production — tokens sit in plain env vars)
MCP_AUTH=static MCP_AUTH_STATIC_TOKENS=dev-token uv run python -m cisco_sdwan_mcp.server
# JWT via your IdP's JWKS endpoint
MCP_AUTH=jwt
MCP_AUTH_JWKS_URI=https://idp.example.com/realms/main/protocol/openid-connect/certs
MCP_AUTH_ISSUER=https://idp.example.com/realms/main
MCP_AUTH_AUDIENCE=cisco-sdwan-mcpThe full variable reference for every mode is in .env.example.
Notes:
Auth applies to the HTTP transport only.
/healthzand/stay public — probes and humans don't carry tokens; the MCP endpoint returns401without a valid token.OAuth flows require HTTPS on the public URL in production.
To add your own scheme, write a builder in
cisco_sdwan_mcp/auth.pyand register it in_BUILDERS(provider docs).
A server exposing your WAN topology should not run
MCP_AUTH=noneon a reachable network. Seedeploy/README.md.
Docker
cp .env.example .env # fill in vManage URL and credentials
docker compose up -d --buildOr manually:
docker build -t cisco-sdwan-mcp .
docker run -p 8000:8000 \
-e SDWAN_VMANAGE_URL=https://vmanage.example.com:8443 \
-e SDWAN_USERNAME=automation-readonly \
-e SDWAN_PASSWORD=... \
cisco-sdwan-mcpHelper scripts build the image, replace any container of the same name, and
start the server at http://localhost:8000/mcp, passing your .env through:
bash scripts/run_docker.sh # Linux/macOS
.\scripts\run_docker.ps1 # Windows PowerShellThe image includes a HEALTHCHECK against /healthz, so docker ps shows
container health out of the box.
Corporate CA certificates
Drop any .crt/.pem root CA files into certificates/. The build adds them
to the container trust store and runs update-ca-certificates automatically —
which covers both a TLS-intercepting proxy and a vManage certificate signed
by your internal CA. Leave the directory empty if you don't need it.
For a proxy, HTTP_PROXY/HTTPS_PROXY/NO_PROXY are predefined Docker build
args and need no Dockerfile edits:
docker build --build-arg HTTPS_PROXY=http://proxy.example.com:8080 -t cisco-sdwan-mcp .
docker run -p 8000:8000 -e HTTPS_PROXY=http://proxy.example.com:8080 cisco-sdwan-mcpDeploying
The container is a plain HTTP server on port 8000 with a /healthz probe, so
it runs anywhere. deploy/ ships raw Kubernetes manifests, a Helm chart, a
Styrmin driver and a Cloud Run service definition — see
deploy/README.md for full walkthroughs, including the
SD-WAN-specific parts: reaching a management-network controller from the cloud,
private-CA handling, and vManage's per-account session limits.
kubectl apply -k deploy/kuberneteshelm install sdwan-mcp oci://ghcr.io/pcdamasceno/charts/cisco-sdwan-mcp \
--set sdwan.vmanageUrl=https://vmanage.example.com:8443gcloud run services replace deploy/cloud-run-service.yaml --region europe-west3This repository is also a Styrmin
Application Driver — driver.styrmin.yml and values.j2.yml at the root are
what Styrmin reads when it clones it. See
deploy/styrmin.md.
/healthz deliberately does not check vManage. A brief controller outage
should not restart pods — tools report the problem per call, and the server
recovers on its own.
Adding your own tools
Capabilities live in three packages, one module per concern. Each package's
__init__.py imports its modules so the decorators run — add a module, add one
import line.
# cisco_sdwan_mcp/tools/my_tools.py
from cisco_sdwan_mcp.sdwan.client import get_client
from cisco_sdwan_mcp.sdwan.formatting import envelope, project
from cisco_sdwan_mcp.tools._helpers import resolve_device_id, sdwan_tool
@sdwan_tool
async def get_dhcp_leases(device: str, limit: int = 100) -> dict:
"""Show DHCP leases the device is serving.
Args:
device: Hostname, system IP or chassis number.
limit: Maximum leases to return.
"""
system_ip = await resolve_device_id(device)
client = await get_client()
records = await client.get_data(
"/dataservice/device/dhcp/server", {"deviceId": system_ip}
)
fields = ("ifname", "address", "client-id", "state", "expires")
return envelope(project(records, fields), limit=limit, device=device)Then add my_tools to the import list in cisco_sdwan_mcp/tools/__init__.py.
Use @sdwan_tool rather than @mcp.tool — it registers the tool and
converts SD-WAN failures into a readable {"error", "message"} result. Reach
for the shared helpers rather than reimplementing them:
Helper | Purpose |
| Hostname / system IP / chassis → the system IP vManage's real-time endpoints need |
| GET and unwrap vManage's |
| Trim wide records to what matters |
| Add |
| Build the JSON |
| Tally a field into a summary |
The docstring is what the model reads to decide whether to call your tool —
say what question it answers, not just which endpoint it hits. Keep write
operations in config_tools.py so the registration gate keeps covering them.
Testing
uv run pytest # or: pytestThe suite runs against a fake vManage (httpx.MockTransport) rather than a
live controller, so it covers the things that actually break in the field:
the login handshake, including vManage answering a failed login with HTTP 200 and an HTML body
CSRF token handling, and controllers older than 19.2 that have no token endpoint
session expiry mid-session → one transparent re-login and retry
error translation: unreachable host, timeout, 403, non-JSON response
every read tool's filtering, projection and truncation
the write gate, verified in a fresh interpreter: the configuration tools are absent without
SDWAN_ENABLE_WRITES=trueand present with itwrite confirmation: declining, or a client that cannot prompt, must not produce an HTTP call to vManage
tests/conftest.py holds the fake controller; use it as the pattern for your
own tools.
Troubleshooting
Symptom | Cause and fix |
|
|
| Wrong username/password, or the account is locked. vManage returns HTTP 200 with the login page for a bad password — the client detects that and reports it as an auth failure. |
| Authentication worked but the account lacks privileges for that endpoint. Template and policy endpoints need more than a bare read-only role. |
| The URL points at a proxy that strips cookies, not at vManage itself. |
| DNS, routing, firewall or the wrong port. vManage commonly listens on 8443, not 443. |
| Real-time endpoints poll the device itself. Raise |
TLS / certificate verify failed | Private CA. Set |
| The endpoint doesn't exist on this vManage version — API paths vary across releases. |
A write tool "doesn't exist" | Expected: |
A write returns | The client doesn't support MCP elicitation, so it cannot ask you to approve. |
Empty results everywhere, no error | The account may be scoped to a tenant or device group with no devices. Check |
Set LOG_LEVEL=DEBUG for more detail. Note that vManage error bodies can be
verbose — check what yours returns before enabling debug logs in a shared
environment.
Project structure
.
├── cisco_sdwan_mcp/
│ ├── mcp.py ← Shared FastMCP instance, auth wiring, /healthz
│ ├── auth.py ← MCP client authentication factory (MCP_AUTH)
│ ├── server.py ← Entry point: transport selection, startup logging
│ ├── sdwan/ ← vManage integration layer (no MCP knowledge)
│ │ ├── config.py ← SDWAN_* settings
│ │ ├── client.py ← Async client: login, CSRF, session recovery
│ │ ├── formatting.py ← Projection, envelopes, vManage query builder
│ │ └── errors.py ← Exception hierarchy
│ ├── tools/
│ │ ├── _helpers.py ← @sdwan_tool, device resolution
│ │ ├── inventory_tools.py ← Devices and fabric summary
│ │ ├── monitoring_tools.py ← Control plane, BFD, OMP, interfaces, health
│ │ ├── alarm_tools.py ← Alarms and events
│ │ ├── statistics_tools.py ← Tunnel and interface statistics
│ │ ├── template_tools.py ← Templates and policies (read-only)
│ │ └── config_tools.py ← Writes — imported only when enabled
│ ├── resources/sdwan_resources.py
│ └── prompts/sdwan_prompts.py
├── tests/
│ ├── conftest.py ← Fake vManage (httpx.MockTransport)
│ ├── sample_data.py ← Representative vManage payloads
│ ├── test_client.py ← Login, session recovery, error translation
│ ├── test_formatting.py ← Projection, envelopes, query building
│ ├── test_tools.py ← Read tools via the in-memory MCP client
│ ├── test_config_tools.py ← The write confirmation gate
│ ├── test_server.py ← Registration, the write gate, public routes
│ └── test_auth.py ← MCP client auth factory
├── deploy/ ← Kubernetes + Cloud Run
├── scripts/ ← Docker helper scripts (bash / PowerShell)
├── certificates/ ← Drop-in CA certificates for Docker builds
├── .env.example ← Annotated reference of every variable
├── docker-compose.yml
├── Dockerfile
└── pyproject.tomlResources
License
This server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Servers
- AlicenseAqualityBmaintenanceAn MCP server that enables LLMs to retrieve structured network information, including routing, interfaces, MPLS, and topology, from devices using gNMI and OpenConfig models. It facilitates real-time network analysis, log filtering, and status monitoring through a standardized interface.Last updated1013BSD 3-Clause
- Alicense-qualityAmaintenanceAn MCP server that allows LLMs to create, configure, validate, and explain Cisco Packet Tracer network topologies. It provides a comprehensive suite of tools for generating deployment scripts, CLI configurations, and automated network troubleshooting.Last updated115MIT
- Flicense-qualityDmaintenanceMCP server for Cisco Catalyst SD-WAN Manager (vManage) that exposes REST API as tools for AI assistants to query and manage SD-WAN fabric, including device management, monitoring, templates, and policies.Last updated8
- Alicense-qualityDmaintenanceA read-only MCP server for Cisco Firepower Management Center (FMC) 7.4.x, allowing LLMs to query firewall configuration, search network objects, and check deployment status through natural language.Last updatedApache 2.0
Related MCP Connectors
MCP server providing access to the Scorecard API to evaluate and optimize LLM systems.
MCP server for AI agents to plan, verify, and deploy Cloudflare-native apps.
MCP server for AI access to SmartBear tools, including BugSnag, Reflect, Swagger, PactFlow, QTM4J.
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/pcDamasceno/cisco-sdwan-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server