hpe-cx-mcp
Allows the server to resolve device credentials from HashiCorp Vault for inventory management.
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., "@hpe-cx-mcpCheck interface status and VLANs on all switches in the Boston site"
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.
ArubaOS-CX MCP Server (hpe-cx-mcp)
A Model Context Protocol (MCP) server that
exposes Aruba CX (AOS-CX) switches to MCP-capable AI agents (Claude, VS Code
Copilot, etc.). It turns the switch REST API (/rest/v10.x) and the SSH CLI into
a curated set of safe, structured tools for observability, troubleshooting and
configuration of a campus / data-center fabric (VLANs, routing, BGP/OSPF,
EVPN-VXLAN, VSX/VSF, port access / 802.1X, NAE, ARC…).
The server runs as a Docker container, speaks MCP over streamable HTTP, and ships with optional named Bearer-token authentication and JSON audit logging.
Quick start
cd cx-mcp
# 1) Provide credentials (git-ignored)
cp .env.example .env # then edit: set ARUBA_DEFAULT_PASSWORD (and any source tokens)
# 2) Provide the device list (git-ignored)
cp inventory/inventory.example.yaml inventory/inventory.yaml # then edit: your switches & IPs
# 3) Build and start
docker compose up -d --build
# 4) Watch it come up
docker compose logs -f hpe-cx-mcp # wait for "✅ hpe-cx-mcp server is up and running"The MCP endpoint is then available at http://<docker-host>:8002/mcp. Point
your MCP client at it (see §9). Full details and
platform notes are in §3.
Related MCP server: API-Central
Table of contents
1. What this server does
Single entry point to a fleet of AOS-CX switches described in an inventory.
Read (observe): interfaces, VLANs, routing/ARP/MAC tables, BGP/OSPF/EVPN, VXLAN tunnels, VSX/VSF stack state, hardware health, logs, 802.1X / port-access, NAE scripts, application recognition (ARC), full configs.
Write (configure): VLAN services, loopbacks, routed ports, VRFs, BGP, OSPF, EVPN/VXLAN, port authentication, virtual-MAC, ARC — each paired with a
verify_*read-back tool.Safety rails:
Per-device
access_mode(read-onlyby default; writes are denied unless a device is explicitlyread-write).Site-scoped operations (
siteparameter) to act on a group of devices.SSH write-command detection to block configuration changes via raw CLI on read-only devices.
Dynamic inventory: merge the local file with NetBox / Nautobot sources of truth, with optional HashiCorp Vault credential resolution.
2. Available tools
Tools are grouped by purpose. Read tools require a device to be reachable; write
tools additionally require the device to be read-write.
Inventory & sessions
Tool | Role |
| List inventory devices (optional |
| List sites and their attached devices. |
| List configured sources and their priority ( |
| Search devices by name/site/tenant/tag/custom field across sources. |
| Resolve a device by name or management IP across all sources. |
| Reload the local file and re-pull external sources. |
| Run a read-only diagnostic on every device of a site. |
| Close pooled REST/SSH sessions (call at end of a workflow). |
Raw access (escape hatches)
Tool | Role |
| Primary CLI escape hatch: run arbitrary CLI command(s) over SSH (output not exposed by REST). |
| Fallback for |
| Try to list CLI commands supported via REST |
| Raw GET against an arbitrary REST path. |
System & hardware
get_system_info, get_hardware_health, get_boot_history, get_transceivers,
get_ssh_config, get_logs.
Containers & licensing
get_containers (on-switch application containers: status, image, CPU/memory
limits, VRF networks), get_feature_pack (licensing / subscription state:
management mode, validity, expiration, per-feature enforcement).
Cloud management
get_aruba_central (HPE ANW Central / Aruba Central connection state: connected,
instantiation, config source, location, VRF/source IP, Activate connectivity).
L2 / L3 state
get_interfaces, get_loopbacks, get_routed_ports, get_vlan_interfaces,
get_vlans, get_lldp_neighbors, get_mac_table, get_arp_table,
get_routing_table, get_spanning_tree.
Routing protocols
get_bgp_neighbors, get_bgp_config, get_bgp_routes, get_ospf_overview,
get_ospf_neighbors, get_ospf_interfaces.
EVPN / VXLAN
get_evpn_config, get_evpn_routes, get_evpn_multihoming, get_vxlan_config,
get_vxlan_tunnels, get_vxlan_static_peers, get_evpn_vtep_neighbors.
High availability (VSX / VSF)
get_vsx_status, get_vsx_config, get_vsx_sync, get_vsf_status,
get_vsf_config, get_maintenance_mode.
NAE (Network Analytics Engine)
get_nae_scripts, get_nae_script, get_nae_agents, get_nae_agent.
Port access / AAA / 802.1X
get_port_access_clients, get_port_access_client_detail,
get_port_access_auth_config, get_port_access_summary,
get_port_access_policies, get_port_access_roles, get_port_access_gbps,
get_gbp_role_maps, get_port_access_abps, get_radius_servers,
get_tacacs_servers, get_aaa_authentication, get_aaa_accounting.
Application Recognition & Control (ARC)
get_app_recognition, get_app_visibility.
Configuration management
list_configs, get_config, get_full_config, compare_configs,
manage_config (save / checkpoint / rollback).
Configure (write) + verify pairs
Each configure_* tool has a matching verify_* read-back tool:
Configure | Verify | Scope |
| — | VLAN + optional SVI |
|
| Loopback (router-id / VTEP source) |
|
| L3 port |
|
| VTEP |
|
| Global EVPN |
|
| OSPF instance |
|
| BGP router |
|
| VRF + route-targets |
|
| 802.1X / MAC-Auth |
|
| ARC |
|
| Global EVPN virtual-MAC |
Write protection: a
configure_*/create_*/delete_*/manage_configcall on aread-onlydevice is refused. Mark the deviceaccess_mode: read-writein the inventory to allow changes.
Tool exposure: flat toolset (default) vs legacy atomic tools
The server can expose its capabilities in two mutually exclusive ways, chosen
by the CX_FLAT_TOOLSET flag (see §5):
Flat toolset (CX_FLAT_TOOLSET=true — the default). The ~101 atomic tools
listed above are collapsed into ~23 flat dispatchers driven by a scope
(and, for writes, an action) argument. The underlying REST client code is
unchanged — the dispatchers only route to it, so there is no behavioural
regression. Every read dispatcher also accepts device: str | list, site
or source (an external source-of-truth query) and fans the call out in
parallel, returning one envelope {scope, results, errors, summary}. An optional
limit caps long list fields in the response.
Dispatcher |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Plus 7 kept-atomic tools: list_devices, list_sites, get_logs,
run_ssh_commands, manage_config, logout, rollback. Write dispatchers keep
the plan → apply → verify lifecycle and the per-device read-only guard. Domain
fields are passed in a params object (keys documented in each dispatcher's
docstring).
Legacy atomic tools (CX_FLAT_TOOLSET=false). The full per-tool catalog above
is exposed instead, optionally shaped by the three layers below. Use this for an
instant rollback to the previous behaviour.
Progressive disclosure, functional prefixes & write safety (legacy mode only)
Three optional layers (active only when CX_FLAT_TOOLSET=false, each gated
by its own env flag — see §5) shape how the legacy
tools are exposed:
1. Progressive disclosure (CX_DEFERRED_TOOLS) — instead of advertising the
full catalog (100+ tools), the server publishes only ~27 Tier-1 tools (the
most-used read/diagnostic tools, the escape hatches, the orchestrators and the
meta-tools). Every other tool is deferred (Tier-2) and reached on demand via
two meta-tools:
Meta-tool | Role |
| Discover deferred tools by keyword. Returns each match's name, description, tags, |
| Execute a deferred tool by name with an |
This keeps the agent's tool list small and cheap while leaving the entire surface reachable.
2. Functional prefixes (CX_TOOL_PREFIXES) — advertised tools are renamed
<domain>__<tool> to group them by domain, e.g. routing__get_bgp_neighbors,
overlay__configure_evpn, service__create_vlan_service, meta__invoke_tool.
Domains: inventory, exec, system, interface, switching, routing,
overlay, redundancy, security, app, nae, config, service, meta.
invoke_tool accepts either the prefixed or the bare name.
3. Write safety (CX_WRITE_SAFETY) — a preview→apply workflow with rollback:
Meta-tool | Role |
| Apply a previewed write by its |
| Undo a reversible applied write by its |
Workflow: call any write tool with apply=false (the default) to get a plan and
a dry_run_token; then call apply_plan(dry_run_token=…) to apply that exact plan.
Idempotent configure_* merges have no automatic inverse and are reported as
unsupported by rollback. When CX_REQUIRE_DRY_RUN_TOKEN=true, a direct apply
(apply=true) through invoke_tool is refused — callers must go through the
preview→apply_plan path.
3. Installation (macOS / Linux / Windows)
Prerequisites
Docker and Docker Compose v2 (
docker compose …).macOS / Windows: Docker Desktop.
Linux: Docker Engine + the Compose plugin.
Network reachability from the Docker host to the switches' management IPs (HTTPS/443 for REST, TCP/22 for SSH).
REST access must be configured on the target devices and in the correct VRF: in Read-Write mode for Read and Write access, and Read-only mode for read-only access.
SSH access must also be configured on the target devices for the tools that require it.
Configure (first run)
Secrets and deployment-specific settings live outside docker-compose.yml,
in files that are git-ignored so they are never committed. Two templates are
shipped — copy each one and fill it in:
cd cx-mcp
# 1) Credentials & external source tokens → .env (git-ignored)
cp .env.example .env
# then edit .env and set at least ARUBA_DEFAULT_PASSWORD
# 2) Device inventory → inventory/inventory.yaml (git-ignored)
cp inventory/inventory.example.yaml inventory/inventory.yaml
# then edit it: list your switches, their IPs and per-device access_mode.env is injected into the container via env_file: in docker-compose.yml.
Minimum content (see .env.example for the full list):
ARUBA_DEFAULT_USERNAME=admin
ARUBA_DEFAULT_PASSWORD=your-switch-password
ARUBA_API_VERSION=latest
# Optional external sources of truth (leave empty if unused):
NETBOX_URL=
NETBOX_TOKEN=
INFRAHUB_URL=
INFRAHUB_TOKEN=Never commit
.envorinventory/inventory.yaml— they hold real credentials and device IPs. Only the*.exampletemplates are tracked by git.
Build & start (all platforms)
cd cx-mcp
docker compose up -d --buildThe server listens on http://<host>:8002/mcp (host port 8002 → container
8000, see docker-compose.yml). The image is built as hpe-cx-mcp:latest and
runs as the container hpe-cx-mcp.
Check it is running:
docker compose logs -f hpe-cx-mcp
# look for, in order:
# "Uvicorn running on http://0.0.0.0:8000"
# "✅ hpe-cx-mcp server is up and running on http://0.0.0.0:8000 — if your agent
# already has an open MCP connection, reset it (MCP: Disconnect → Connect) …"The ✅ … server is up and running line is emitted once the listener is ready.
If startup fails instead, the server logs ❌ hpe-cx-mcp server failed to start
followed by the full traceback (then exits non-zero).
Note: every
docker compose up -d --buildrebuilds the image and restarts the server, which invalidates any existing MCP session. After a rebuild, reconnect your client (MCP: Disconnect → Connect) to pick up the current tools.
Platform notes
Linux
Bind-mounted folders are owned by your host user. The container runs as uid 1000; if your host user is not uid 1000, make the writable folders readable/writable by uid 1000:
mkdir -p logs secrets sudo chown -R 1000:1000 logs secrets chmod 700 secretsTo reach switches on the host's local L2 network you may uncomment
network_mode: hostindocker-compose.yml(Linux only).
macOS (Docker Desktop)
File sharing is handled by the VM; bind mounts work out of the box and uid remapping is automatic — no manual
chownneeded in most cases.network_mode: hostis not supported the same way as on Linux; keep the defaultports:mapping (8002:8000).
Windows (Docker Desktop + WSL2)
Run the commands from a WSL2 shell or PowerShell. Storing the project inside the WSL2 filesystem (e.g.
\\wsl$\…/~/cx-mcp) is strongly recommended for correct file permissions and performance.Use forward slashes in
docker-compose.ymlvolume paths (./inventory:/app/inventory:ro).network_mode: hostis not available; keep theports:mapping.
4. Volumes
Three host folders are mounted into the container:
Host path | Container path | Mode | Purpose |
|
| read-only ( | Device inventory ( |
|
| read-write | Audit log output ( |
|
| read-write | Named Bearer tokens ( |
volumes:
- ./inventory:/app/inventory:ro
- ./logs:/app/logs
- ./secrets:/app/secretsThe application code is baked into the image — only these data folders are mounted. After changing any
*.py, rebuild withdocker compose up -d --build(a plain restart is not enough).
Ownership (Linux): logs/ and secrets/ must be writable by container uid
1000. secrets/ should be 0700 and its .tokens file is written 0600 by the
server itself.
5. Environment variables
Secrets and deployment-specific values (credentials, external-source tokens) are
provided through the git-ignored .env file, which docker-compose.yml
loads via env_file: (copy .env.example to .env, see
§3). Non-secret operational flags
(MCP_*, CX_*, INVENTORY_FILE) are set directly in docker-compose.yml
under environment:. Booleans accept true/1/yes/on.
Transport
Variable | Default | Description |
|
| MCP transport. |
|
| Bind address inside the container. |
|
| Bind port inside the container (mapped to host |
|
| URL path guarded by the security middleware. |
Device credentials & API (set in .env; overridable per device in the inventory)
Variable | Default | Description |
|
| Default REST/SSH username. |
| (empty) | Default password. Required unless set per device. |
|
| Default REST API version ( |
|
| Default SSH port. |
Inventory & external sources
Variable | Default | Description |
|
| Path to the inventory file (YAML/JSON/TOML). |
| — | NetBox source connection (set in |
| — | Nautobot source connection (set in |
| — | Infrahub source connection (GraphQL API; set in |
| — | Generic per-named-source connection. |
| — | HashiCorp Vault for credential resolution. |
Bearer authentication (optional, OFF by default)
Variable | Default | Description |
|
| Require a valid Bearer token on every request. If enabled with no token yet, the server starts in LOCKED mode and refuses every MCP request with HTTP 503 until you create the first token and restart. |
|
| Token store path. |
|
| Trust |
Audit logging (optional, OFF by default)
Variable | Default | Description |
|
| Emit a JSON record per tool call. |
|
| Output file (rotating, 10 MB × 5). |
|
|
|
|
| Also mirror records to stdout ( |
Progressive disclosure, prefixes & write safety (optional)
Variable | Default | Description |
|
| Collapse the ~101 atomic tools into ~23 flat |
|
| (Legacy mode only) Advertise only Tier-1 tools; reach the rest via |
|
| (Legacy mode only) Rename advertised tools |
|
| Allow write tools to run through |
|
| Enable the |
|
| Refuse a direct |
|
| Lifetime (seconds) of a |
|
| Directory for the write-safety stores ( |
6. Inventory management
The inventory file (inventory/inventory.yaml) declares the devices and how to reach them. It is git-ignored (it holds real IPs and credentials); create it once from the shipped template:
cp inventory/inventory.example.yaml inventory/inventory.yamlValues in the file override environment variables. Supported formats: YAML, JSON, TOML.
Minimal example
defaults:
username: admin
password: "secret"
api_version: latest # auto-detect the newest REST version
verify_ssl: false
timeout: 30
access_mode: read-only # writes denied unless overridden per device
devices:
Spine1:
host: 192.0.2.21
description: "Core switch"
tags: [core, spine]
site: campus-principal
access_mode: read-write # allow configuration changes on this device
Access-01:
host: 192.0.2.23
site: campus-principalPer-device options
host (required), username, password, api_version, verify_ssl, timeout,
tags, description, site, ssh_port, ssh_username, ssh_password,
access_mode (read-only | read-write), vault (true to fetch credentials
from Vault).
Sites
The site concept is optional and lets tools target a group of devices
(list_devices(site=…), run_on_site(site, …)). Use either a per-device site:
field or a top-level sites: block grouping devices.
Inventory source options
There are several ways to decide where the device list comes from:
Local only (default) — devices from the file:
source: local # may be omittedSingle external source — pull from a source of truth:
source: netbox sources: netbox: type: netbox # netbox | nautobot | infrahub url: https://netbox.example.com token: "<api-token>" # or via NETBOX_TOKEN env var verify_ssl: falseMerged sources with priority — a device present in several sources is taken from the higher-priority one:
source: [local, netbox] source_priority: [local, netbox] # local wins over netbox
Credential resolution priority (highest first):
Device-specific credentials set on the device entry.
HashiCorp Vault (when
vaultis enabled globally or per device).Environment variables / inventory defaults.
After editing the inventory, apply changes without rebuilding via the
refresh_inventory tool, or restart the container.
Startup validation (fail-fast)
The inventory file is validated at startup. If it cannot be parsed (YAML/JSON/
TOML syntax error) or violates the expected schema (e.g. a mis-indented source:
key, or source set to a non-string/list value), the server logs a specific
English error and refuses to start rather than silently running with an empty
or partial inventory:
❌ Inventory file '/app/inventory/inventory.yaml' failed validation — the server will NOT start.
YAML syntax error: expected '<document start>', but found '<block mapping start>'
in "<unicode string>", line 22, column 1
Fix the inventory file, then restart the container.The container exits with a non-zero status code (visible in docker logs /
docker compose ps). Fix the reported line and restart. Notes:
A missing inventory file is only a warning (it can be mounted later) — the server still starts.
External source reachability (NetBox / Nautobot / Infrahub being down) is not fatal: the parsed local inventory remains usable and the dynamic merge degrades gracefully.
The runtime
refresh_inventorytool applies the same validation but never crashes a running server: on a bad file it returns an error and keeps the previously loaded inventory.
7. Security: Bearer auth & audit logging
Both features are disabled by default and fully backward compatible.
Authentication (
CX_AUTH_ENABLED=true): every request to/mcpmust carryAuthorization: Bearer <token>. Missing/invalid tokens get HTTP 401. The token's name becomes theactorrecorded in the audit log, so you always know who did what. If auth is enabled but no token exists yet, the server still starts but in LOCKED mode: every MCP request is refused with HTTP 503 (fail-closed) so the services are unreachable. Create the first token (see §8) and restart the container to unlock — the token store is loaded once at startup.Audit (
CX_AUDIT_ENABLED=true): one JSON line per tool call inlogs/audit.jsonl, includingactor,src_ip,tool,category(read/write), targeteddevice, redactedarguments,outcome, HTTPstatus_codeandduration_ms. Secrets (passwords/tokens) are masked.
Enable both:
# docker-compose.yml
CX_AUTH_ENABLED: "true"
CX_AUDIT_ENABLED: "true"docker compose up -d --build8. Token management
Tokens are stored in secrets/.tokens (perms 0600). Manage them inside the
running container with the bundled CLI:
# Create a named token (prints the secret once — save it)
docker compose exec hpe-cx-mcp python cx_token_manager.py generate --name vscode-dev
# List tokens (names, descriptions, created — secret truncated)
docker compose exec hpe-cx-mcp python cx_token_manager.py list
# Show one token
docker compose exec hpe-cx-mcp python cx_token_manager.py show --name vscode-dev
# Revoke a token
docker compose exec hpe-cx-mcp python cx_token_manager.py revoke --name vscode-devGenerated tokens are prefixed cx_. Use one distinct token per client/agent to
get per-actor attribution in the audit log.
First token: when authentication is enabled, the server starts LOCKED (HTTP 503 on every request) until a token exists. After creating the first token, apply it without a restart by hot-reloading (see below):
docker compose exec hpe-cx-mcp python cx_reload.py(a
docker compose restart hpe-cx-mcpalso works).
Hot reload (no rebuild / no restart)
Token and inventory files are loaded into memory at startup. After editing
secrets/.tokens (via the CLI above) or inventory/inventory.yaml, apply the
changes to the running server by sending it a reload signal:
docker compose exec hpe-cx-mcp python cx_reload.pyThis reloads both the tokens and the inventory in place — adding/revoking a token, or adding/updating a device, takes effect on the next request. The command only sends the signal; the outcome (counts, errors) is written to the logs:
docker compose logs --tail=20 hpe-cx-mcpReloading is manual and explicit — there is no automatic file watching.
If clients connect through a shared relay, all calls appear under the relay's single token; for per-agent attribution, connect directly to
hpe-cx-mcpwith distinct tokens.
9. Connecting an MCP client
Point your MCP client at the streamable-HTTP endpoint:
URL: http://<docker-host>:8002/mcpWhen authentication is enabled, add the header:
Authorization: Bearer cx_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxExample (VS Code mcp.json style):
{
"servers": {
"hpe-cx-mcp": {
"type": "http",
"url": "http://localhost:8002/mcp",
"headers": { "Authorization": "Bearer cx_xxxxxxxxxxxxxxxxxxxx" }
}
}
}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
- AlicenseNot gradedqualityDmaintenanceProvides AI assistants with direct access to multi-vendor network devices for tasks like configuration management, health checks, and topology discovery through 35 specialized tools. It enables natural language control over platforms including Cisco, Juniper, and Nokia using SSH, NETCONF, and SNMP protocols.11MIT
- AlicenseAqualityAmaintenanceEnables conversational automation of HPE Aruba Central network operations through Claude Code. Provides 88 tools across monitoring, configuration, and operations domains for device migration, SSID management, switch provisioning, and GreenLake Platform integration.162MIT
- FlicenseNot gradedqualityDmaintenanceEnables AI agents to interact with Cisco IOS-XE network devices over SSH using structured tools. Provides read and write capabilities for network management with built-in validation and security.
- AlicenseNot gradedqualityCmaintenanceMCP server for network operations that lets AI assistants interact with Cisco/Juniper network devices through safe, well-defined tools like compliance audits and configuration backups.MIT
Related MCP Connectors
Connect MCP clients to 2,000+ AI models without managing provider API keys.
Manage SRG+ hubs, channels, content, assets, users, and workspaces from any MCP-aware AI agent.
Let AI operate servers without SSH. Choose actions, approve risky changes, and audit every step.
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/legalla/hpe-cx-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server