Skip to content

Latest commit

 

History

384 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

HPE Networking MCP Server

CI Security

Unofficial / community project. This repository is an independent, community-driven project. It is not affiliated with, endorsed by, sponsored by, or supported by Hewlett Packard Enterprise, Aruba Networks, or Juniper Networks. "HPE", "Aruba", "Aruba Central", "Aruba ClearPass", "HPE GreenLake", "Juniper", and "Juniper Mist" are trademarks of their respective owners and are used here only to describe what this software interoperates with. Please direct support and licensing questions about those products to the respective vendors.

A unified Model Context Protocol (MCP) server that brings Juniper Mist, Aruba Central, HPE GreenLake, Aruba ClearPass, Juniper Apstra, Axis Atmos Cloud, Aruba OS 8, HPE UXI, and Aruba EdgeConnect together into a single, deployable service. One container. One endpoint. All your HPE networking tools.


Why?

Managing HPE networking infrastructure with AI assistants today means juggling multiple separate MCP servers — each with its own setup, credentials, and quirks. This project consolidates them into one:

Category Mist Central GreenLake ClearPass Apstra Axis AOS8 UXI EdgeConnect
Site Health & Performance Metrics
WLANs / SSIDs
Device Inventory
Device Details (AP/Switch/GW)
Device Stats & Utilization
Client Connectivity
Events
Alerts / Alarms
Audit Logs
Application Visibility
Troubleshooting (Ping/Traceroute/Bounce)
Session Control / Client Disconnect
Configuration Management
Configuration Write (CRUD)
Radio Resource Management
Rogue AP Detection
Firmware Management
Subscriptions / Licensing
User Management
Workspaces
Scope & Configuration Hierarchy
Guest Management
NAC / Policy Management
Endpoint Profiling
Certificates
Webhooks
Reports / Scheduled Reports
Floor Plans / Sitemaps
BLE Asset Tracking / IoT Beacons
Marvis / AI Assistant
Datacenter Blueprints / Templates
Virtual Networks / EVPN / Routing Zones
Connectivity Templates / Policy Apply
Fabric Deploy / Diff Status
BGP / Protocol Session Monitoring
SASE Cloud Connectors / Tunnels
URL / Web Category Filtering
SSL Inspection Exclusions
Staged Writes + Commit Workflow
Guided Prompts
Dynamic Tool Discovery
Underlying tools 1050 + 2 prompts 669 + 12 prompts 10 142 19 25 48 + 9 prompts 21 1216
Exposed meta-tools (dynamic mode) 3 3 3 3 3 3 3 3 3
Cross-Platform 3 tools + 3 prompts 3 tools + 3 prompts 1 tool

Default tool surface (v3.0.0.0+): ships with MCP_TOOL_MODE=code by default. Code mode exposes only execute + 5 discovery tools (tags, search, get_schema, skills_list, skills_load); all 4109 underlying tools are reachable via await call_tool(name, params) inside a sandboxed Python execute() block. Smallest initial token cost (~minimal context); best for orchestrators driving small / local LLMs. Set MCP_TOOL_MODE=dynamic to use the v2.x default behavior — each platform exposes 3 meta-tools (<platform>_list_tools, <platform>_get_tool_schema, <platform>_invoke_tool) plus the 4 cross-platform static tools and 2 skills tools (24 total surface, ~3,700 tokens). The static mode was REMOVED in v3.0.0.0. Every tool's response is wrapped in a uniform envelope {ok, status, data, message, tool, platform}. v2.3.0.0 introduces Skills — markdown-defined multi-step procedures discoverable via skills_list / skills_load; see docs/TOOLS.md#skills. v2.4.0.0 adds AOS8 (48 tools + 9 prompts) — see INSTRUCTIONS.md for AOS8-specific operator guidance. v3.2.0.0 adds HPE UXI (21 tools). See docs/MIGRATING_TO_V2.md.

Aruba Central Guided Prompts

The Central module includes 12 guided prompts — multi-step workflow templates that walk the AI through common network operations tasks using the available tools. These prompts orchestrate multiple tool calls in the correct order, so you can simply invoke the prompt and let the AI handle the rest.

  • Network Health Overview — Assess the health of all sites across your network, flagging those with poor scores or high alert counts for deeper investigation.
  • Troubleshoot Site — Deep-dive into a specific site: check health metrics, review active alerts by severity, list all devices, and recommend next steps.
  • Client Connectivity Check — Investigate a client by MAC address: find the client, check the connected device's health, review site-level alerts, and identify the likely root cause.
  • Investigate Device Events — Pull recent events for a specific device to build a timeline of what happened, highlight recurring issues, and suggest follow-up actions.
  • Site Event Summary — Summarize all events at a site over a time window, grouped by category and type, to spot patterns and anomalies.
  • Failed Clients Investigation — Find all failed client connections at a site, check the health of the devices they were connected to, and identify common failure patterns.
  • Site Client Overview — Get a breakdown of all clients at a site by connection type, status, VLAN, and WLAN to understand the connectivity landscape.
  • Device Type Health — Check the health of all devices of a specific type (AP, switch, or gateway) at a site, including alerts and recent event activity.
  • Critical Alerts Review — Review all active critical alerts across the entire network, grouped by site and category, with recommended immediate actions.
  • Compare Site Health — Side-by-side comparison of health scores, device counts, client counts, and alert breakdowns across multiple sites.
  • Scope Configuration Overview — View committed configuration resources at a scope level, grouped by persona and category.
  • Scope Effective Config — View effective (inherited + committed) configuration at a scope, showing what each level contributes.

AOS8 Guided Prompts

The AOS8 module includes 9 guided prompts for common Mobility Conductor operator workflows. See INSTRUCTIONS.md for the complete parameter reference and workflow descriptions.

  • Triage Client (aos8_triage_client) — Find a client by MAC, check AP health, review auth/association events, identify likely root cause.
  • Triage AP (aos8_triage_ap) — Deep-dive an AP: radio state, clients, alarms, ARM history, event timeline.
  • Health Check (aos8_health_check) — Network-wide health: controllers, AP counts, clients, alarms, firmware drift.
  • Audit Change (aos8_audit_change) — Recent audit-trail review with high-risk-change flagging.
  • RF Analysis (aos8_rf_analysis) — Channel distribution, co-channel clusters, ARM oscillation, interferers/rogues.
  • WLAN Review (aos8_wlan_review) — SSID/VAP/AP-group/role inventory and consistency check.
  • Client Flood (aos8_client_flood) — High-client-count / failed-connection investigation at a scope.
  • Compare MD Config (aos8_compare_md_config) — Side-by-side effective-config diff between two MDs or AP groups.
  • Pre-Change Check (aos8_pre_change_check) — Pre-maintenance checklist: alarms, controller stats, audit trail, pending changes, write_memory reminder.

Cross-Platform Tools

Tools that span multiple platforms and return pre-aggregated results — each one replaces several individual tool calls, so the AI gets a compact answer instead of paging through raw responses.

  • Site Health Check (site_health_check) — One call returns a unified health report for a site across every enabled platform: Mist site stats and alarms, Central site health and active alerts, and (when ClearPass is configured) session and auth-failure counts for the site's network access devices. Replaces ~8–12 separate tool calls with a compact report including overall status, top alerts, and concrete next-step recommendations. Registered when at least Mist or Central is enabled; ClearPass is additive.
  • Site RF Check (site_rf_check) — One call returns per-AP, per-band radio state from Mist AND Central in parallel: current channel, bandwidth, TX power, channel utilization, and noise floor for every AP at a site. Aggregates the channel distribution per band (2.4 / 5 / 6 GHz), flags co-channel clusters and high utilization, and ships a pre-rendered ASCII RF dashboard so even clients that don't draw charts get a visual report. When site_name is omitted the tool returns a list of selectable sites with AP counts per platform — pick one and call back. Registered when at least Mist or Central is enabled.
  • Translate WLAN (translate_wlan_preview / translate_wlan_apply) — Translate a WLAN between platforms (Mist↔Central, AOS8→either) via the canonical translation engine. translate_wlan_preview fetches the source WLAN + all context and returns the exact target API calls for review (read-only, secrets redacted); translate_wlan_apply executes them against the target, gated by the target platform's write-enable flag and a confirmation prompt. Handles opmode/RADIUS/alias/VLAN/assignment translation automatically. Registered in every mode (including code). Requires Mist and/or Central.
  • Translate Config (translate_config_preview / translate_config_apply) — Translate an AOS 8 → Central config object via the same canonical engine, for the 12 non-WLAN kinds: vlan_id, named_vlan, net_group, role, policy, the AAA chain (auth_server, server_group, dot1x_auth, mac_auth, captive_portal, aaa_profile), and gateway_cluster. translate_config_preview returns the ordered Central calls per source record (read-only, PII-scrubbed); translate_config_apply executes against Central, gated by ENABLE_CENTRAL_WRITE_TOOLS + confirmation (and blocks auth_server until secret tokenization ships). Drives the aos-migration skill's Stage 9b. Registered in every mode (including code). Requires Central.

Cross-Platform WLAN Sync Prompts

  • Sync WLANs Mist → Central — Resolve Mist template variables, map fields, create Central WLAN profiles, assign to matching scopes.
  • Sync WLANs Central → Mist — Resolve Central aliases, server groups, and named VLANs, create Mist WLANs with template variables.
  • Sync WLANs Bidirectional — Compare WLANs across both platforms, show field-level differences, and sync in either direction.

Quick Start

Prerequisites

1. Get the Project

git clone https://github.com/nowireless4u/hpe-networking-mcp.git
cd hpe-networking-mcp

No build required. The docker-compose.yml pulls a pre-built image from GitHub Container Registry by default. To build from source instead, edit docker-compose.yml and swap image: for build: ..

2. Configure Credentials

Copy the environment template and fill in credentials for the platforms you use:

cp .env.example .env
# Edit .env — uncomment and fill in only the platforms you need

Only platforms with all required credentials set will be enabled. Leave unused platforms commented out or empty — they're automatically disabled.

Example .env for a Mist-only deployment:

MIST_API_TOKEN=your-mist-api-token-here
MIST_HOST=api.mist.com

Example .env for Mist + Central:

MIST_API_TOKEN=your-mist-api-token-here
MIST_HOST=api.mist.com
CENTRAL_BASE_URL=https://apigw-us5.central.arubanetworks.com
CENTRAL_CLIENT_ID=your-client-id
CENTRAL_CLIENT_SECRET=your-client-secret

Security: .env is git-ignored and never committed. Restrict file permissions: chmod 600 .env. For production deployments where env vars in docker inspect are unacceptable, see Docker Secrets (Production) below.

Axis token note: Generate the token in the Axis admin portal at Settings → Admin API → New API Token. Pick read or read+write scope and an expiration. The MCP server decodes the JWT's exp claim at startup and logs a warning when the token has fewer than 30 days remaining; the health tool also surfaces a token_expires_in_days countdown when inside that window. There is no refresh — regenerate the token in the portal before it lapses.

3. Start

Credentials from a .env file use the env overlay, which drops the base file's Docker-secret mounts so no secrets/* files are needed:

docker compose -f docker-compose.yml -f docker-compose.env.yml up -d

Using Docker secret files instead of .env? Run the plain docker compose up -d — the base compose mounts the secret files directly (unchanged from prior releases). See Docker Secrets (Production).

4. Verify

docker compose logs

Look for lines like Mist: 1050 underlying tools registered (code mode), ClearPass: 142 underlying tools registered (code mode), Axis: 25 underlying tools registered (code mode), AOS8: 48 underlying tools (code mode), UXI: 21 underlying tools (code mode), EdgeConnect: registered 187 generated tool module(s) (code mode), GreenLake: registered 919 tools across 163 tool module(s) (code mode), Tool mode: code, and Uvicorn running on http://0.0.0.0:8000. Your MCP server is running at http://localhost:8000/mcp. In the default code mode (since v3.0.0.0), only execute + 5 discovery tools (tags, search, get_schema, skills_list, skills_load) are exposed at the top level; all 4109 underlying tools are reachable via await call_tool(name, params) inside a sandboxed Python execute() block. Set MCP_TOOL_MODE=dynamic to use the v2.x meta-tool surface instead. Mist registers 2 guided prompts; Central registers 12; AOS8 registers 9.

Health & readiness probes

The server exposes plain HTTP endpoints for container/orchestrator health checks. They require no MCP stream negotiation (unlike /mcp, which can return 405/406 to a naive probe) and never call external platforms, so a transient upstream outage can't mark the process unhealthy:

Endpoint Success Purpose
GET /livez 200 ok Process is alive and serving HTTP. Use for liveness.
GET /readyz 200 ok (else 503 starting) Startup complete and ready for MCP traffic. Use for readiness/startup.
GET /healthz 200 + JSON Operator info: service, version, status, enabled platform names, uptime_seconds. No secrets.

The deeper per-platform reachability check remains the MCP health tool — it is intentionally not used for liveness, so an upstream platform being unreachable never restarts the container.

The Docker/Compose healthcheck already targets /livez. Kubernetes example:

startupProbe:
  httpGet: { path: /livez, port: 8000 }
  failureThreshold: 30
  periodSeconds: 10
livenessProbe:
  httpGet: { path: /livez, port: 8000 }
  periodSeconds: 20
  timeoutSeconds: 2
  failureThreshold: 3
readinessProbe:
  httpGet: { path: /readyz, port: 8000 }
  periodSeconds: 10
  timeoutSeconds: 2
  failureThreshold: 3

Kubernetes probes reach the pod IP, so keep the default MCP_HOST=0.0.0.0 bind (loopback-only binds are unreachable by the kubelet). Probes send no Origin header and are allowed through origin validation.

Docker Image

The pre-built image is available on GitHub Container Registry:

ghcr.io/nowireless4u/hpe-networking-mcp:latest
ghcr.io/nowireless4u/hpe-networking-mcp:0.6.0

You can also pull it directly:

docker pull ghcr.io/nowireless4u/hpe-networking-mcp:latest

Platform Auto-Disable

You don't need credentials for all nine platforms. The server detects which platforms have valid credentials at startup and only enables those. A platform is disabled if any of its required credentials is empty or absent — whether provided via environment variable or Docker secret file.

  • All nine platforms configured → All tools available (Mist + Central + GreenLake + ClearPass + Apstra + Axis + AOS8 + UXI + EdgeConnect)
  • Only Mist configured → Only mist_* tools available; other platforms disabled
  • Only AOS8 configured → Only aos8_* tools available; other platforms disabled
  • Only ClearPass configured → Only clearpass_* tools available; other platforms disabled
  • No valid credentials → Server refuses to start with a clear error message

Add a platform later by setting its credentials in .env (or via Docker secrets) and restarting the container. The server logs which platforms are enabled at startup:

Mist: credentials loaded (token: abcd...wxyz, host: api.mist.com)
Central: disabled (missing secrets: central_client_id, central_client_secret)
GreenLake: disabled (missing secrets: greenlake_client_id)
AOS8: disabled (missing secrets: aos8_host)
Enabled platforms: mist
Tool mode: code

Connect Your AI Client

Claude Desktop

Claude Desktop doesn't natively support streamable HTTP, so it needs a stdio-to-HTTP bridge called supergateway. This bridge translates between Claude Desktop's stdio protocol and the MCP server's HTTP endpoint.

Prerequisites: Node.js must be installed on your machine. Verify with npx --version in your terminal.

Step 1: Open the Claude Desktop configuration file in a text editor:

OS File Location
macOS ~/Library/Application Support/Claude/claude_desktop_config.json
Windows %APPDATA%\Claude\claude_desktop_config.json

Tip: You can also open this file from within Claude Desktop: go to Settings (gear icon) > Developer > Edit Config.

Step 2: If the file is empty or doesn't exist, paste this entire block as-is:

{
  "mcpServers": {
    "hpe-networking": {
      "command": "npx",
      "args": ["-y", "supergateway", "--streamableHttp", "http://localhost:8000/mcp"]
    }
  }
}

If you already have other MCP servers configured, add the "hpe-networking" entry inside the existing "mcpServers" object. Do not create a second "mcpServers" key. For example, if you already have a server called "my-other-server":

{
  "mcpServers": {
    "my-other-server": {
      "command": "some-command",
      "args": ["some-args"]
    },
    "hpe-networking": {
      "command": "npx",
      "args": ["-y", "supergateway", "--streamableHttp", "http://localhost:8000/mcp"]
    }
  }
}

Common mistakes:

  • Missing comma between server entries (add a , after the closing } of the previous server)
  • Duplicate "mcpServers" keys (only one is allowed — merge your servers into it)
  • Trailing comma after the last entry (JSON does not allow trailing commas)
  • Editing the wrong file or creating a new file instead of editing the existing one

Step 3: Save the file and fully restart Claude Desktop (quit and reopen, not just close the window). MCP servers are discovered at startup — changes won't take effect until you restart.

Step 4: Verify the server connected. In Claude Desktop, look for the MCP server icon (hammer) in the chat input area. Click it — you should see hpe-networking listed with its tools.

Claude Code

No config file needed — run this single command:

claude mcp add hpe-networking --transport http http://localhost:8000/mcp

VS Code / GitHub Copilot

Add to your .vscode/mcp.json (create the file if it doesn't exist) or VS Code MCP settings:

{
  "servers": {
    "hpe-networking": {
      "type": "streamable-http",
      "url": "http://localhost:8000/mcp"
    }
  }
}

PII Tokenization (v2.3.1.0+)

Tool responses are walked before reaching the AI:

  • MAC normalization (always-on, no toggle) — every MAC address in tool responses is rewritten to canonical aa:bb:cc:dd:ee:ff form (lowercase, colon-separated). Mist's API returns MACs in four different formats across endpoints; consistent format means the AI can correlate aa:bb:cc:dd:ee:ff to itself across audits.
  • PII tokenization (opt-in via ENABLE_PII_TOKENIZATION=true) — sensitive fields (PSKs, RADIUS secrets, certificates) and customer-identifying values (hostnames, FQDNs, emails, usernames, hardware serials) are replaced with session-stable [[KIND:uuid]] tokens before reaching the AI. The AI can pass tokens back into write tools and the inbound side substitutes plaintext before the API call. Storage is in-memory only; the keymap is keyed by Mcp-Session-Id and never touches disk.

Deliberately NOT tokenized:

  • MAC addresses — observable to anyone in radio range (BSSID broadcast, client probe requests). Normalized only.
  • SSIDs / ESSIDs — broadcast in beacon frames (refined in v2.3.1.1).
  • Platform UUIDs like org_id, site_id, device_id, template_id — already opaque random identifiers in Mist's API; replacing one UUID with another adds no privacy (refined in v2.3.1.1).
  • Geographic dataaddress, city, state, zip, latitude, longitude, room, building — typically findable on the company's website (refined in v2.3.1.1).
  • All IP addresses (v2.3.1.2) — internal RFC1918, public WAN, CIDR ranges. Internal subnet topology is generally known to anyone on-network, and CIDR / route analysis is a core audit task.

Always tokenized regardless of field name (v2.3.1.2):

  • Email addresses — caught even when the field is named name, username, or anything else. Mist's MPSK pattern of using the user's email as the PSK display name was a leak before this.
  • AWS-signed URL credentials — any string containing X-Amz-Security-Token, X-Amz-Credential, or X-Amz-Signature is treated as a temporary AWS credential and tokenized whole as APITOKEN. Catches the portal_template_url leak from Mist's S3-backed captive-portal previews.

Central coverage (v2.3.1.3): the ruleset now also covers Central response shapes — user_name, updated_by, created_by (audit-log fields) tokenize as USER; hyphenated keys like wpa-passphrase and shared-secret match the same secret rules as their snake_case equivalents. Central organizational structure (device_group_name, scope_name) passes through as cleartext.

Round-trip works for every kind. Same plaintext → same token within a session. WLAN sync, AOS 8 → AOS 10 migration, and mass PSK rotation all work because tokenization is round-trippable.

Tokenization reference

Secrets — tokenized unconditionally when the field name matches (case-insensitive; hyphens and spaces normalized to underscores):

Category Field names Token form
WPA2 / WPA3 keys psk, passphrase, wpa_passphrase, wpa2_passphrase, wpa3_psk, ppsk [[PSK:uuid]]
SAE / Personal-Plus sae_password, sae_pwd [[PSK:uuid]]
VRRP cluster key vrrp_passphrase [[PSK:uuid]]
RADIUS shared secret shared_secret, radius_secret, radsec_secret, coa_secret (v3.1.0.4 / #321); structural pairs rad_key.key, coa_servers[].secret [[RAD:uuid]]
EAP / inner password eap_password, inner_password [[RAD:uuid]]
TACACS+ structural pair tacacs_key.key [[TACACS:uuid]]
CoA / RFC-3576 endpoint identifiers coa_servers[].ip, rfc_3576_server_list[].name; AOS 8 single-server detail-form wrapper key "RFC 3576 Server <ip>" rewritten in place (v3.1.0.4 / #319) [[COA:uuid]]
SNMP community community, community_string [[SNMP:uuid]]
SNMP v3 passwords auth_password, priv_password, snmp_v3_auth_pass, snmp_v3_priv_pass [[SNMP:uuid]]
Admin / enable passwords admin_password, manager_password, support_user_password, enable_password, enable_secret, cli_password [[PASSWORD:uuid]]
IPSec / VPN PSK pre_shared_key, ipsec_psk, vpn_psk [[VPNPSK:uuid]]
API tokens / OAuth api_token, apitoken, api_key, apikey, client_secret [[APITOKEN:uuid]]
OAuth bearer / refresh bearer_token, access_token, refresh_token [[APITOKEN:uuid]]
Webhook secrets webhook_secret, webhook_token [[APITOKEN:uuid]]
AWS-signed URL (value-shape) Any string containing X-Amz-Security-Token, X-Amz-Credential, or X-Amz-Signature [[APITOKEN:uuid]]
Certificates cert, certificate, client_cert, server_cert, ca_cert, chain, pkcs12, p12_data, pem [[CERT:uuid]]
Certificate PEM in free-text PEM blocks (-----BEGIN ... -----END) found in description / notes / comment [[CERT:uuid]]
Private keys private_key, privkey, kerberos_keytab, keytab [[KEY:uuid]]
Private-key PEM in free-text PEM key blocks in description / notes / comment [[KEY:uuid]]
Generic (shape-checked) password, pwd → PASSWORD; secret → RAD; token, key → APITOKEN. Only when value passes length ≥ 8 + character-class diversity check (suppresses {"key": "ssid"}). varies

Source-masked secrets — when the source platform itself masks the value (v3.1.0.4 / #276):

Source value Rewritten to Notes
******** (any field — AOS 8 masks RADIUS / TACACS / RFC-3576 shared secrets server-side) REPLACE_ME Literal directive, not a token. An unambiguous "operator must set this" marker for migration output — ******** reads as "redacted/hidden", REPLACE_ME reads as an instruction. The walk is idempotent and REPLACE_ME is never tokenized even in an exact-match secret field.

Identifiers — tokenized when the field name matches:

Category Field names Token form
Hostnames / FQDNs hostname, host_name, fqdn [[HOSTNAME:uuid]]
Device names device_name, ap_name, controller_name, switch_name [[HOSTNAME:uuid]]
AAA-server host carve-out host (AOS 8 RADIUS / TACACS / LDAP server IP or FQDN) [[HOSTNAME:uuid]]
Bare name (heuristic) name field when parent dict has ≥ 2 device-shape siblings: mac, model, serial, device_type, hw_rev, firmware, version, release_type [[HOSTNAME:uuid]]
Usernames username, user, user_name, login [[USER:uuid]]
Personal names first_name, last_name, full_name, display_name [[USER:uuid]]
Audit-log actors updated_by, created_by [[USER:uuid]]
Email email field; also matched by value-shape regex in any string field [[EMAIL:uuid]]
Phone phone, phone_number, mobile [[PHONE:uuid]]
Hardware serials serial, serial_number, sn [[SERIAL:uuid]]
Cellular IMEI imei [[IMEI:uuid]]
Cellular IMSI imsi [[IMSI:uuid]]
Cellular ICCID iccid [[ICCID:uuid]]

Cleartext — deliberately NOT tokenized:

Field family Why cleartext
MAC addresses Observable in radio range (BSSID, probe requests). Normalized to canonical aa:bb:cc:dd:ee:ff regardless of ENABLE_PII_TOKENIZATION.
IP addresses (generic) On-network topology is generally known; CIDR/route analysis is core audit utility. Carve-outs: CoA endpoint IPs and RADIUS/TACACS server host IPs (AOS 8 AAA-server detail) ARE tokenized.
SSIDs / ESSIDs Broadcast in beacon frames
Platform UUIDs org_id, site_id, device_id, template_id, scope_id — already opaque random IDs
Geographic data address, city, state, zip, latitude, longitude, room, building — typically public on company websites
Organizational labels org_name, site_name, vlan_name, subnet_name, scope_name, device_group_name — architectural labels, not people
Source-masked placeholders e.g. AOS 8 ******** — tokenizing the mask creates a dangerous illusion; round-trip restores only the placeholder and silently breaks downstream writes

Audit logging fires on every tokenize/detokenize event in docker compose logs — kind, token ID, value-hash (SHA-256 truncated), but never the plaintext.

Limitations: Mist ruleset only this release; Central / GreenLake / ClearPass / Apstra / Axis follow in the next minor. Anything the user pastes into the AI prompt is outside our threat model — that turn already has the literal in conversation context. The keymap dies on server restart; saved chat references to old tokens become unresolvable.

Network Security

The MCP HTTP transport ships with two layers of defense out of the box:

  1. Loopback-only port publishdocker-compose.yml publishes 127.0.0.1:8000:8000, so only processes on the host can reach the MCP endpoint. No LAN host can connect, even if it knows the IP. (Inside the container the app still binds 0.0.0.0 — that's the container's own network namespace and is what Docker's port-forwarder forwards into; do not change it.)
  2. Origin header allowlist — required by the MCP Streamable HTTP spec to defend against browser-driven DNS rebinding. The server rejects any request whose Origin is set to a value outside ALLOWED_ORIGINS (default: localhost and 127.0.0.1). Non-browser clients (supergateway, curl, native MCP clients) don't send Origin and pass through.

If you put the server behind an authenticating reverse proxy that already validates origins, set ALLOWED_ORIGINS=* to bypass the in-process check. See the Configuration table for details.

Multi-host / remote access is not a supported configuration. The server has no built-in authentication on /mcp — anyone who can reach the port has full admin of every connected platform. If you need remote access, terminate at a reverse proxy (nginx + mTLS / OIDC, Cloudflare Access, Tailscale Funnel, etc.) and never expose the published port directly.

Secrets

Credentials are loaded via a two-tier lookup for each value:

  1. Docker secret file at SECRETS_DIR/<name> — highest priority (production)
  2. Environment variable with the UPPER_CASE equivalent — fallback (development)

If both exist, the Docker secret file wins. If neither exists, the platform is disabled.

Environment Variables (.env file)

The quickest setup for development — no per-platform secret files. Create a .env and start with the env overlay (docker-compose.env.yml), which removes the base file's Docker-secret mounts so no secrets/* files need to exist:

cp .env.example .env
chmod 600 .env
# Edit .env with your credentials
docker compose -f docker-compose.yml -f docker-compose.env.yml up -d

The .env file is git-ignored and never leaves your machine. For multiple isolated instances, add --env-file .env-<name> -p <name> (see .env.example).

Docker Secrets (Production)

For production deployments where credentials must not be visible in docker inspect or /proc/<pid>/environ, use Docker secret files. They are wired into the base docker-compose.yml, so the plain command works unchanged from prior releases — just create the files and start:

# Create secret files from templates
cp secrets/mist_api_token.example secrets/mist_api_token
cp secrets/mist_host.example secrets/mist_host
# Edit with real values...

docker compose up -d

A credential present as a secret file takes priority over the matching environment variable, so you can mix the two (secret files for sensitive platforms, .env for the rest — omit the env overlay when you want the secret mounts).

Docker secrets are:

  • Mounted read-only at /run/secrets/ inside the container
  • Not visible in docker inspect or environment variables
  • Not baked into the Docker image

Note: the base docker-compose.yml declares a secret file for every platform, so plain docker compose up -d expects those files to exist. Either create a file per platform you use (and comment out the rest in the secrets: sections), or supply credentials via .env with the env overlay above — that path needs no secrets/* files at all.

Credential Lookup Reference

Environment Variable Docker Secret File Required Platform
MIST_API_TOKEN mist_api_token yes Mist
MIST_HOST mist_host yes Mist
CENTRAL_BASE_URL central_base_url yes Central
CENTRAL_CLIENT_ID central_client_id yes Central
CENTRAL_CLIENT_SECRET central_client_secret yes Central
GREENLAKE_API_BASE_URL greenlake_api_base_url yes GreenLake
GREENLAKE_CLIENT_ID greenlake_client_id yes GreenLake
GREENLAKE_CLIENT_SECRET greenlake_client_secret yes GreenLake
GREENLAKE_WORKSPACE_ID greenlake_workspace_id yes GreenLake
CLEARPASS_SERVER clearpass_server yes ClearPass
CLEARPASS_CLIENT_ID clearpass_client_id yes ClearPass
CLEARPASS_CLIENT_SECRET clearpass_client_secret yes ClearPass
CLEARPASS_VERIFY_SSL clearpass_verify_ssl no (default: true) ClearPass
APSTRA_SERVER apstra_server yes Apstra
APSTRA_USERNAME apstra_username yes Apstra
APSTRA_PASSWORD apstra_password yes Apstra
APSTRA_PORT apstra_port no (default: 443) Apstra
APSTRA_VERIFY_SSL apstra_verify_ssl no (default: true) Apstra
AXIS_API_TOKEN axis_api_token yes Axis
AOS8_HOST aos8_host yes AOS8
AOS8_USERNAME aos8_username yes AOS8
AOS8_PASSWORD aos8_password yes AOS8
AOS8_PORT aos8_port no (default: 4343) AOS8
AOS8_VERIFY_SSL aos8_verify_ssl no (default: true) AOS8
UXI_CLIENT_ID uxi_client_id yes UXI
UXI_CLIENT_SECRET uxi_client_secret yes UXI

Where to Obtain Credentials

Juniper Mist

Credential How to Obtain
MIST_API_TOKEN Mist Dashboard > Organization > Settings > API Token
MIST_HOST api.mist.com (Global), api.eu.mist.com (EU), api.gc1.mist.com (GovCloud)

Aruba Central

Credential How to Obtain
CENTRAL_BASE_URL HPE GreenLake Platform > Aruba Central > API Gateway
CENTRAL_CLIENT_ID HPE GreenLake Platform > API Clients
CENTRAL_CLIENT_SECRET HPE GreenLake Platform > API Clients

HPE GreenLake

Credential How to Obtain
GREENLAKE_API_BASE_URL Typically https://global.api.greenlake.hpe.com
GREENLAKE_CLIENT_ID HPE GreenLake Platform > API Clients
GREENLAKE_CLIENT_SECRET HPE GreenLake Platform > API Clients
GREENLAKE_WORKSPACE_ID HPE GreenLake Platform > Workspaces

Aruba ClearPass

Credential How to Obtain
CLEARPASS_SERVER https://your-clearpass-server/api — the CPPM server hostname with /api path
CLEARPASS_CLIENT_ID ClearPass Admin > API Clients > Create API Client
CLEARPASS_CLIENT_SECRET ClearPass Admin > API Clients > Client Secret
CLEARPASS_VERIFY_SSL true (default) or false for self-signed certificates

Aruba OS 8 / Mobility Conductor

Credential Required Default Purpose
AOS8_HOST yes Conductor or standalone controller hostname/IP
AOS8_USERNAME yes API username with sufficient role
AOS8_PASSWORD yes API password
AOS8_PORT no 4343 HTTPS port (Mobility Conductor API port)
AOS8_VERIFY_SSL no true Set to false for self-signed certs (logged as WARNING)

Set ENABLE_AOS8_WRITE_TOOLS=true to expose the 12 AOS8 write tools (gated by elicitation middleware; default false).

HPE UXI / Aruba User Experience Insight

Credential Required Default Purpose
UXI_CLIENT_ID yes HPE SSO OAuth2 client ID
UXI_CLIENT_SECRET yes HPE SSO OAuth2 client secret

Set ENABLE_UXI_WRITE_TOOLS=true to expose the 10 UXI write tools (gated by elicitation middleware; default false).


Architecture

┌─────────────────────────────────────────────────────────────────────────────────────────┐
│                       MCP Client (Claude, VS Code, etc.)                                │
└───────────────────────────────────────┬─────────────────────────────────────────────────┘
                                        │ Streamable HTTP
                                        ▼
┌─────────────────────────────────────────────────────────────────────────────────────────┐
│      HPE Networking MCP Server (:8000)  —  MCP_TOOL_MODE=code (default since v3.0.0.0) │
│                                                                                         │
│   Exposed to the AI  (6 tools in code mode):                                            │
│     • execute (sandboxed Python; await call_tool(name, params) inside)                  │
│     • 5 discovery tools: tags, search, get_schema, skills_list, skills_load             │
│   Set MCP_TOOL_MODE=dynamic to use the v2.x meta-tool surface (24 tools).               │
│                                                                                         │
│ ┌────────┐ ┌────────┐ ┌────────┐ ┌────────┐ ┌────────┐ ┌────────┐ ┌────────┐ ┌────────┐│
│ │  Mist  │ │Central │ │GreenLk │ │ClrPass │ │ Apstra │ │  Axis  │ │  AOS8  │ │  UXI   ││
│ │ mist_* │ │centrl_*│ │grnlake │ │clrpass │ │apstra_*│ │ axis_* │ │ aos8_* │ │ uxi_*  ││
│ │ 1050   │ │669tools│ │10 tools│ │142 tool│ │19 tools│ │25 tools│ │48 tools│ │21 tools││
│ │+2 prmt │ │+12prmt │ │        │ │        │ │        │ │        │ │+9 prmt │ │        ││
│                                                                                         │
│  All 4109 underlying tools reachable via call_tool() in code mode or via                │
│  per-platform meta-tools (<platform>_list_tools / get_schema / invoke) in dynamic mode. │
│ └───┬────┘ └───┬────┘ └───┬────┘ └───┬────┘ └───┬────┘ └───┬────┘ └───┬────┘ └───┬────┘│
│     │          │          │          │          │          │          │          │       │
└─────┼──────────┼──────────┼──────────┼──────────┼──────────┼──────────┼──────────┼───────┘
      ▼          ▼          ▼          ▼          ▼          ▼          ▼          ▼
  Mist Cloud  Aruba    GreenLake  ClearPass    Apstra      Axis    Mobility    HPE UXI
     API      Central     API       CPPM API  Fabric API  Atmos   Conductor     API
              API                                          Cloud      API

  + EdgeConnect (Silver Peak) Orchestrator API — edgeconnect_* (1216 SD-WAN tools, /gms/rest)

Key design decisions:

  • FastMCP framework with Python 3.12+
  • Streamable HTTP transport (modern MCP standard)
  • Code tool mode by default (since v3.0.0.0) — only execute + 5 discovery tools exposed; all 4109 underlying tools reachable via await call_tool(name, params) inside the sandbox. Smallest initial token cost; best for orchestrators driving small / local LLMs. Set MCP_TOOL_MODE=dynamic for the v2.x meta-tool surface (24 tools, ~3,700 tokens).
  • Tool namespacingmist_*, central_*, greenlake_*, clearpass_*, apstra_*, axis_*, aos8_*, uxi_* prefixes prevent collisions
  • Platform isolation — each module manages its own API client and auth; a failing platform doesn't affect the others
  • Non-root container — runs as mcpuser (uid 1000)

Write Operations and Safety

Write/mutation tools (e.g., creating WLANs in Mist, modifying configurations) are supported with safety controls:

  • Disabled by default — enable per-platform with ENABLE_MIST_WRITE_TOOLS=true, ENABLE_CENTRAL_WRITE_TOOLS=true, ENABLE_CLEARPASS_WRITE_TOOLS=true, ENABLE_APSTRA_WRITE_TOOLS=true, ENABLE_AXIS_WRITE_TOOLS=true, ENABLE_AOS8_WRITE_TOOLS=true, or ENABLE_GREENLAKE_WRITE_TOOLS=true
  • Elicitation required — write tools prompt for user confirmation before executing
  • Annotation-based — all tools carry MCP annotations (readOnlyHint, destructiveHint, etc.)
Environment Variable Default Effect
ENABLE_MIST_WRITE_TOOLS false Enable Mist write/mutation tools
ENABLE_CENTRAL_WRITE_TOOLS false Enable Central write/mutation tools
ENABLE_CLEARPASS_WRITE_TOOLS false Enable ClearPass write/mutation tools
ENABLE_APSTRA_WRITE_TOOLS false Enable Apstra write/mutation tools
ENABLE_AXIS_WRITE_TOOLS false Enable Axis Atmos Cloud write/mutation tools (every write stages — call axis_commit_changes to apply)
ENABLE_AOS8_WRITE_TOOLS false Enable AOS8 write tools (12 tools; every write returns requires_write_memory_for — call aos8_write_memory explicitly)
ENABLE_UXI_WRITE_TOOLS false Enable UXI write tools (10 tools: sensor/agent/group/assignment mutations)
ENABLE_GREENLAKE_WRITE_TOOLS false Enable GreenLake write tools (424 spec-generated writes/deletes + greenlake_bulk_add_devices bulk CSV device onboarding)
MCP_APP_ENABLE false Single switch for all MCP-Apps capabilities. Registers the FastMCP FileUpload provider (file_manager drag/pick upload UI + list_files for file metadata) and the GenerativeUI provider (generate_prefab_ui — the model writes a live Prefab dashboard from data it collected — + search_prefab_components). All UIs render only in MCP-Apps hosts (e.g. Claude Desktop / ChatGPT / claude.ai). Uploaded files are read server-side only — the content-returning read_file tool is deliberately removed so upload contents (device serials/MACs, AOS 8 PSKs / RADIUS secrets) never enter the model context; consuming tools (greenlake_bulk_add_devices, aos8_parse_config) read by name inside the server.
DISABLE_ELICITATION false Skip user confirmation for write tools (use with caution)

Reliability

The server transparently retries transient API failures so the AI doesn't have to reason about flakey upstream services.

  • 5xx errors on read tools — auto-retried with exponential backoff (1s, 2s, 4s — 3 attempts total)
  • 429 rate-limit responses — retried on both reads and writes (always safe — the server is asking us to slow down). Honors the Retry-After response header when present, capped at 60s
  • Write tools (5xx) — never auto-retried for idempotency safety. The error surfaces to the AI which can decide whether to re-issue
  • 4xx errors (other than 429) — never retried; surface immediately

The retry logic detects transient failures in two patterns: response-dict (Mist/Central/ClearPass return {"status_code": 503, ...}) and httpx exception (GreenLake/Apstra/Axis raise httpx.HTTPStatusError). Read/write classification is via the underlying tool's tags — anything tagged *_write or *_write_delete is treated as a write.

Environment Variable Default Effect
RETRY_MAX_ATTEMPTS 3 Max attempts including the first call. Set to 1 to disable retry entirely
RETRY_INITIAL_DELAY 1.0 Initial backoff delay in seconds (doubles per attempt up to RETRY_MAX_DELAY)
RETRY_MAX_DELAY 60.0 Cap on a single sleep — also caps Retry-After header values

Configuration

Environment Variable Default Description
MCP_PORT 8000 Port the MCP server listens on
MCP_HOST 0.0.0.0 Bind address inside the container's namespace — leave at 0.0.0.0 so Docker's port-forwarder can reach the app. Restrict who can reach the host port via ports: in compose, not this.
ALLOWED_ORIGINS http://localhost:<HOST_PORT>,http://127.0.0.1:<HOST_PORT> Comma-separated allowlist for the Origin request header (DNS-rebinding defense per MCP spec). Compose derives the default from HOST_PORT so multi-instance setups get the right origin automatically. Browsers always send Origin; non-browser clients (supergateway, curl) don't and pass through. Set to * to disable the check (use only behind an auth proxy).
SECRETS_DIR /run/secrets Directory for Docker secret files (credentials in files here take priority over env vars)
LOG_LEVEL info Logging level (debug, info, warning, error)
ENABLE_MIST_WRITE_TOOLS false Enable Mist write/mutation tools
ENABLE_CENTRAL_WRITE_TOOLS false Enable Central write/mutation tools
ENABLE_CLEARPASS_WRITE_TOOLS false Enable ClearPass write/mutation tools
ENABLE_APSTRA_WRITE_TOOLS false Enable Apstra write/mutation tools
ENABLE_AXIS_WRITE_TOOLS false Enable Axis write/mutation tools (staged; commit with axis_commit_changes)
ENABLE_AOS8_WRITE_TOOLS false Enable AOS8 write tools (call aos8_write_memory after each change to persist)
ENABLE_UXI_WRITE_TOOLS false Enable UXI write tools (sensor/agent/group/assignment mutations)
ENABLE_GREENLAKE_WRITE_TOOLS false Enable GreenLake write tools (424 spec-generated writes/deletes + greenlake_bulk_add_devices bulk CSV device onboarding)
DISABLE_ELICITATION false Disable write confirmation prompts
MCP_TOOL_MODE code Tool exposure: code (default since v3.0.0.0 — 6 tools at top level: execute + 5 discovery; all 4109 underlying tools reachable via call_tool() inside the sandbox) or dynamic (24 tools — 4 cross-platform + 21 per-platform meta-tools + 2 skills tools; underlying tools hidden until invoked via <platform>_invoke_tool). The static value was REMOVED in v3.0.0.0
RETRY_MAX_ATTEMPTS 3 Max retry attempts on transient failures (5xx reads, 429 reads+writes). Set to 1 to disable retry
RETRY_INITIAL_DELAY 1.0 Initial retry backoff seconds (exponential: 1s, 2s, 4s)
RETRY_MAX_DELAY 60.0 Cap on a single retry sleep (also caps Retry-After header values)
ENABLE_PII_TOKENIZATION false Tokenize sensitive fields (PSKs, RADIUS secrets, hostnames, emails, etc.) in tool responses before they reach the AI. Round-trips: AI passes tokens back into write tools and the middleware substitutes plaintext. MAC normalization is always-on regardless of this toggle. See PII Tokenization above.
PII_MAX_TOKENS_PER_SESSION 10000 Soft cap on the per-session keymap size. Cap-hit logs a warning and falls through with plaintext rather than erroring out the call.
PII_SESSION_TTL_SECONDS 3600 Idle-TTL for per-session token keymaps. A session not accessed within this window is purged on the next tool call, bounding how long plaintext secrets linger for dead sessions. 0/negative disables the sweep (keymaps live until restart).
CODE_SANDBOX_MAX_DURATION_SECS 30.0 Wall-clock budget for a single code-mode execute() block. Raise it when driving poll-and-wait tools (e.g. central_cable_test, which blocks up to ~25s). Invalid / non-positive values fall back to 30.0.

Development

All development happens inside Docker containers.

Build from Source

Edit docker-compose.yml — comment out image: and uncomment build::

services:
  hpe-networking-mcp:
    # image: ghcr.io/nowireless4u/hpe-networking-mcp:latest
    build: .

Then rebuild:

docker compose up -d --build

Running Tests

docker compose -f docker-compose.yml -f docker-compose.dev.yml run --rm \
  hpe-networking-mcp uv run pytest tests/ -v

Full CI Check

Run this before pushing to catch issues early:

docker compose -f docker-compose.yml -f docker-compose.dev.yml run --rm \
  hpe-networking-mcp sh -c \
  "uv run ruff check . && uv run ruff format --check . && \
   uv run mypy src/ --ignore-missing-imports && uv run pytest tests/ -q"

See CONTRIBUTING.md for the full development workflow.


Project Structure

hpe-networking-mcp/
├── src/hpe_networking_mcp/
│   ├── __main__.py              # CLI entry point
│   ├── server.py                # FastMCP server setup and lifespan
│   ├── config.py                # Credential loading (env vars + Docker secrets)
│   ├── INSTRUCTIONS.md          # LLM instructions for all platforms
│   ├── middleware/              # null-strip, validation-catch, sandbox-error-catch, elicitation, retry
│   ├── skills/                  # Markdown-defined multi-step procedures + skills engine
│   └── platforms/
│       ├── _common/             # Shared tool registry, capability annotations, async auth primitive
│       ├── health.py            # Cross-platform health probe tool
│       ├── mist/                # 1050 Mist tools (spec-driven) + 2 prompts + API client
│       ├── central/             # 669 Central tools + 12 prompts + API client
│       ├── greenlake/           # 919 GreenLake tools (spec-generated) + bulk_add + OAuth2 client
│       ├── clearpass/           # 142 ClearPass tools + async httpx client
│       ├── apstra/              # 19 Apstra tools + async httpx client
│       ├── axis/                # 25 Axis Atmos Cloud tools + httpx client (JWT bearer)
│       ├── aos8/                # 48 AOS8 tools + 9 prompts + UIDARUBA session client
│       ├── uxi/                 # 21 UXI tools + OAuth2 client
│       ├── edgeconnect/         # 1216 EdgeConnect (Silver Peak) Orchestrator tools (spec-driven) + session/API-key client
│       ├── manage_wlan.py       # Cross-platform WLAN management tool
│       ├── sync_prompts.py      # Cross-platform WLAN sync prompts
│       ├── site_health_check.py # Cross-platform site health aggregator
│       └── site_rf_check.py     # Cross-platform Wi-Fi RF dashboard
├── tests/                       # Unit and integration tests (2150+ unit tests)
├── docs/                        # PRD, PRP, tool reference
├── secrets/                     # Secret file templates (.example only in git)
├── .env.example                 # Environment variable template (copy to .env)
├── .github/workflows/           # CI, security, Docker publish
├── Dockerfile                   # Multi-stage build, non-root user
├── docker-compose.yml           # Base — mounts Docker secret files (plain `up -d`)
├── docker-compose.env.yml       # Overlay — drops secret mounts for the `.env` path
└── docker-compose.dev.yml       # Development (mounts tests)

Troubleshooting

Viewing Logs

Always start here when something isn't working:

docker compose logs                        # All logs
docker compose logs --tail 50              # Last 50 lines
docker compose logs -f                     # Follow live

Platform Disabled at Startup

If a platform shows as disabled, its credentials are missing or empty:

Mist: disabled (mist_api_token secret not found)
Central: disabled (missing secrets: central_client_id, central_client_secret)

Fix: Set the missing credentials in your .env file (or the matching Docker secret file). If you intended to disable that platform, ignore the message — the server continues running with the platforms that do have credentials.

Container exits immediately with invalid mount config for type "bind"

Error response from daemon: invalid mount config for type "bind":
bind source path does not exist: .../secrets/apstra_verify_ssl

This means the base docker-compose.yml declares a secret file for a platform whose file doesn't exist on disk (Docker requires every referenced secret file to be present before the container starts).

Fix — pick one:

  • Using .env for credentials? Add the env overlay, which removes all secret mounts: docker compose -f docker-compose.yml -f docker-compose.env.yml up -d. No secrets/* files are needed on this path.
  • Using Docker secret files? Create the missing file (cp secrets/<name>.example secrets/<name> and populate it), or comment out that platform's entries in the secrets: sections of docker-compose.yml.

Authentication Failures

MistPermission Denied or 401 Unauthorized:

  • Verify your API token is valid in the Mist Dashboard
  • Check that mist_host matches your region (api.mist.com, api.eu.mist.com, api.gc1.mist.com)

CentralLogin Failed or token errors:

  • Verify central_base_url matches your Central instance (e.g., https://us5.api.central.arubanetworks.com)
  • Ensure the OAuth2 client ID and secret are correct and not expired
  • Check that the API client has the correct scopes in HPE GreenLake Platform

GreenLakeAccess token acquisition failed:

  • Verify greenlake_api_base_url (typically https://global.api.greenlake.hpe.com)
  • Check that the client credentials are valid and the workspace ID is correct
  • Token refresh happens automatically — if it fails, check the logs for details

ClearPassClearPass: failed to initialize:

  • Verify clearpass_server is the correct CPPM hostname with /api path (e.g., https://clearpass.example.com/api)
  • Ensure the OAuth2 API client has been created in ClearPass Admin with client_credentials grant type
  • For self-signed certificates, set clearpass_verify_ssl to false
  • Check the logs for the specific error: docker compose logs | grep ClearPass

ApstraApstra: failed to initialize or login errors:

  • Verify apstra_server is just the hostname (no scheme, no port), e.g., apstra.example.com
  • Set apstra_port only if your Apstra server listens somewhere other than 443
  • Ensure apstra_username and apstra_password belong to an Apstra account that can reach /api/user/login
  • For self-signed Apstra certificates, set apstra_verify_ssl to false (defaults to true)
  • Check the logs for the specific error: docker compose logs | grep Apstra

Connection Refused on Port 8000

docker compose ps                          # Check container is running
docker compose restart                     # Restart the container

If port 8000 is already in use by another service, change the port in docker-compose.yml (keep the 127.0.0.1: prefix):

ports:
  - "127.0.0.1:8080:8000"    # Map to port 8080 instead, loopback-only

If you change the host port via HOST_PORT, the Compose default for ALLOWED_ORIGINS tracks it automatically (http://localhost:<HOST_PORT>). You only need to set ALLOWED_ORIGINS explicitly if you're using a custom domain or reverse-proxy URL.

Tools Not Appearing in AI Client

  1. Check the server is running: docker compose logs | grep "registered"
  2. Verify the endpoint is reachable: curl -s -o /dev/null -w "%{http_code}" http://localhost:8000/mcp (expect 406 — this is normal for a plain GET)
  3. Restart your AI client after adding or changing the MCP server config — tools are discovered at session start

Claude Desktop: "Not a valid MCP server configuration"

Claude Desktop doesn't natively support streamable HTTP — it requires a stdio bridge. Use supergateway:

{
  "mcpServers": {
    "hpe-networking": {
      "command": "npx",
      "args": ["-y", "supergateway", "--streamableHttp", "http://localhost:8000/mcp"]
    }
  }
}

If tools time out after ~4 minutes, check that:

  • The Docker container is running and healthy: docker compose ps
  • Node.js is installed: npx --version
  • The container didn't lose connectivity after sleep: docker compose restart

Tool Surface Looks Wrong (6 tools vs. 4109)

Since v3.0.0.0, the server defaults to MCP_TOOL_MODE=code: only execute + 5 discovery tools (tags, search, get_schema, skills_list, skills_load) are visible at the top level. All 4109 underlying tools are reachable via await call_tool(name, params) inside a sandboxed Python execute() block. A correctly configured server with all 9 platforms enabled will advertise 6 tools to the AI client.

Check the mode in the logs:

docker compose logs | grep "Tool mode"
# "Tool mode: code"      → default since v3.0.0.0 (6 exposed tools, 4109 underlying via call_tool)
# "Tool mode: dynamic"   → opt-in to v2.x meta-tool surface (24 exposed: 21 per-platform + 4 cross-platform + 2 skills)

To use the v2.x meta-tool discovery surface (each platform exposes <platform>_list_tools, <platform>_get_tool_schema, <platform>_invoke_tool), set MCP_TOOL_MODE=dynamic in docker-compose.yml under environment:

- MCP_TOOL_MODE=dynamic   # 24 exposed; per-platform meta-tools + cross-platform + skills
- MCP_TOOL_MODE=code      # 6 exposed; sandboxed call_tool() reaches all 4109 (default since v3.0.0.0)

The static mode (every underlying tool visible up front) was REMOVED in v3.0.0.0 — at 4109 tools / ~64K tokens it was no longer practical. Setting MCP_TOOL_MODE=static now raises an error at startup with a migration message.

See docs/MIGRATING_TO_V2.md for the v1.x → v2.x meta-tool history.

Write Tools Not Visible

Write tools are disabled by default. Enable them per-platform in docker-compose.yml:

- ENABLE_MIST_WRITE_TOOLS=true
- ENABLE_CENTRAL_WRITE_TOOLS=true
- ENABLE_CLEARPASS_WRITE_TOOLS=true

Then restart: docker compose restart

Container Crashes or Restarts

Check the exit code and logs:

docker compose ps -a                       # Check exit code
docker compose logs --tail 100             # Check recent logs

Common causes:

  • No valid credentials — the server exits if zero platforms can be initialized
  • Port conflict — another service is using port 8000
  • Out of memory — increase Docker's memory allocation

Contributing

Contributions are welcome! The main branch is protected — all changes go through pull requests with CI checks. See CONTRIBUTING.md for the full development workflow.

For a complete list of every tool and its parameters, see docs/TOOLS.md.


License

MIT

About

Unified MCP server for Juniper Mist, Aruba Central, and HPE GreenLake — one container, one endpoint, all your HPE networking tools

Resources

Contributing

Stars

26 stars

Watchers

7 watching

Forks

Releases

Packages

Used by

Contributors

Languages