MCP clients¶
The server speaks the Model Context Protocol. Any compliant client can attach to it.
Setting the bearer token¶
When transport.auth.type: bearer is configured (see
configuration reference), every HTTP
request must include:
Always run bearer mode over HTTPS
Bearer tokens travel in plaintext inside the Authorization header.
Serving them over http:// exposes the token to anyone on the network
path (Wi-Fi neighbours, ISP, transparent proxies) — equivalent to no
auth on a shared network. Always front a bearer-mode endpoint with
HTTPS via a reverse proxy (Caddy auto-issues a cert with one line of
config; nginx, Traefik, or a corporate gateway work equally well). The
URLs in the examples below use http:// only for local-loopback
illustration.
How you set the header depends on the client:
- Claude Desktop — add a
headersblock under the SSE/streamable-http server entry inclaude_desktop_config.json:
{
"mcpServers": {
"sdwan": {
"url": "https://your-host/mcp",
"headers": {
"Authorization": "Bearer ${SDWAN_MCP_TOKEN}"
}
}
}
}
- fastmcp Python client — pass
headers=when constructing the client:
from fastmcp import Client
async with Client(
"https://your-host/mcp",
headers={"Authorization": f"Bearer {os.environ['SDWAN_MCP_TOKEN']}"},
) as client:
...
- Cline / Continue / other MCP clients — check the client's docs for
custom HTTP headers. The header name is
Authorization, the value isBearer <token>.
Generating a token¶
The server rejects tokens shorter than 8 characters outright and warns on anything under 16. Aim for ≥32 characters of URL-safe base64.
Rate-limiting and brute force¶
The middleware uses constant-time comparison so individual rejections leak
no timing information, and rejection logs are throttled (at most 10
WARNING lines per minute followed by a "suppressed N more" rollup) so a
brute-force flood cannot fill the disk. There is no built-in rate limit
or lockout on rejection rate itself — front the endpoint with a reverse
proxy that does (nginx limit_req, Caddy rate_limit, or fail2ban) if
the network surface is hostile.
Recommended: launch via uvx¶
The blocks below use uvx, which fetches and runs the
published package on demand — no source checkout and no absolute paths to maintain.
If you installed the package instead (uv tool install / pipx), replace
"command": "uvx", "args": ["catalyst-sdwan-super-mcp"] with
"command": "sdwan-mcp", "args": []. To enable mutations, append "--read-write"
to args.
Claude Code¶
One command:
claude mcp add sdwan \
-e VMANAGE_HOST=sandbox-sdwan-2.cisco.com \
-e VMANAGE_USERNAME=devnetuser \
-e VMANAGE_PASSWORD='RG!_Yw919_83' \
-e VMANAGE_VERIFY_SSL=false \
-- uvx catalyst-sdwan-super-mcp
(VMANAGE_VERIFY_SSL=false is only for the self-signed DevNet sandbox — omit it for a production vManage with a valid certificate.)
…or commit a project-local .mcp.json (global config: ~/.claude/mcp.json):
{
"mcpServers": {
"sdwan": {
"command": "uvx",
"args": ["catalyst-sdwan-super-mcp"],
"env": {
"VMANAGE_HOST": "sandbox-sdwan-2.cisco.com",
"VMANAGE_USERNAME": "devnetuser",
"VMANAGE_PASSWORD": "RG!_Yw919_83",
"VMANAGE_VERIFY_SSL": "false"
}
}
}
}
Claude Desktop (stdio)¶
Edit ~/Library/Application Support/Claude/claude_desktop_config.json (macOS) or %APPDATA%\Claude\claude_desktop_config.json (Windows):
{
"mcpServers": {
"sdwan": {
"command": "uvx",
"args": ["catalyst-sdwan-super-mcp"],
"env": {
"VMANAGE_HOST": "sandbox-sdwan-2.cisco.com",
"VMANAGE_USERNAME": "devnetuser",
"VMANAGE_PASSWORD": "RG!_Yw919_83",
"VMANAGE_VERIFY_SSL": "false"
}
}
}
}
Restart Claude Desktop. You should see the sdwan server in the MCP indicator.
Cursor¶
Add to ~/.cursor/mcp.json (global) or .cursor/mcp.json (per project):
{
"mcpServers": {
"sdwan": {
"command": "uvx",
"args": ["catalyst-sdwan-super-mcp"],
"env": {
"VMANAGE_HOST": "sandbox-sdwan-2.cisco.com",
"VMANAGE_USERNAME": "devnetuser",
"VMANAGE_PASSWORD": "RG!_Yw919_83",
"VMANAGE_VERIFY_SSL": "false"
}
}
}
}
Other stdio clients (Cline, Continue, Windsurf, Zed, …) use the same shape: the
uvx catalyst-sdwan-super-mcp command with VMANAGE_* in the environment.
Docker (stdio)¶
{
"mcpServers": {
"sdwan": {
"command": "docker",
"args": [
"run", "-i", "--rm",
"-e", "VMANAGE_HOST",
"-e", "VMANAGE_USERNAME",
"-e", "VMANAGE_PASSWORD",
"-v", "/absolute/path/to/specs:/app/specs",
"catalyst-sdwan-super-mcp"
]
}
}
}
SSE / streamable-HTTP¶
For clients that connect over the network rather than spawning a subprocess:
When exposing the server over the network, configure bearer token auth via
transport.auth.type: bearer in sdwan-mcp.yaml and set the header on the client
as shown in the Setting the bearer token section above.