Skip to main content
Glama
README.md
# afc-mcp

MCP server for Aruba Fabric Composer (AFC) focused on inventory, network state and
VMware vCenter/vSphere visibility. Read-only.

## Included

- `server.py`: FastMCP entrypoint exposing the MCP tools (transport: streamable-http).
- `afc_client.py`: AFC API client with auth token handling and mapped read-only endpoints.
- `Dockerfile`: container image for the MCP server.
- `docker-compose.yml`: local runtime (host port `8010` → container `8000`).
- `.env.example`: environment variable template.

## Quick start

1. Create env file:

```bash
cp .env.example .env
```

2. Fill at least:

- `AFC_BASE_URL` (host root **without** `/api`, e.g. `https://afc.example.local`)
- `AFC_USERNAME`
- `AFC_PASSWORD`
- `AFC_VERIFY_SSL` (optional, default `false`)
- `AFC_TIMEOUT` (optional, default `30`)

3. Build and run:

```bash
docker compose up --build -d
```

4. Check logs:

```bash
docker compose logs -f
```

## Endpoint

The server speaks MCP over **streamable-HTTP**:

```
URL:  http://<docker-host>:8010/mcp
```

By default the endpoint is open. You can enable Bearer-token authentication so
only clients presenting a valid token can call the tools (see below).

## Authentication (Bearer token)

Authentication is **optional and disabled by default** (backward compatible).
When enabled, every MCP request must carry an `Authorization: Bearer <token>`
header; requests without a valid token are rejected with `401`.

Tokens are *named* (one per client) and stored in `secrets/.tokens` (git-ignored,
mounted read-write into the container).

1. Create the first token (run on the host or inside the container):

```bash
# on the host (stdlib only, no dependencies needed)
cd afc-mcp
python afc_token_manager.py generate --name "vscode-dev" --description "Laptop VSCode"

# ...or inside the running container
docker compose exec afc-mcp python afc_token_manager.py generate --name "vscode-dev"
```

The command prints the clear-text token **once** — copy it now.

2. Enable auth and (re)start the server:

```bash
# in .env or the shell environment
AFC_AUTH_ENABLED=true

docker compose up -d --build
```

> Safety net: if `AFC_AUTH_ENABLED=true` but no token exists yet, the server
> starts in **LOCKED** mode and refuses every request (`503`) until a token is
> created and the container restarted. This prevents accidentally exposing an
> open endpoint.

Manage tokens with the CLI:

```bash
python afc_token_manager.py list                 # masked preview
python afc_token_manager.py show --name vscode-dev  # reveal a value
python afc_token_manager.py revoke --name vscode-dev
```

Revoking or adding a token requires a container restart to take effect.

> Note: `MCP_HOST` / `MCP_PORT` only control **where the server listens** inside
> the container (`0.0.0.0:8000`, mapped to host `8010`). They are unrelated to
> authentication — they say *where* the server listens, not *who* may call it.

## Integrate with VS Code

VS Code (with GitHub Copilot / agent mode) discovers MCP servers from an `mcp.json` file.

1. Create `.vscode/mcp.json` in your workspace (or add to your user `mcp.json`):

```json
{
  "servers": {
    "afc-mcp": {
      "type": "http",
      "url": "http://localhost:8010/mcp"
    }
  }
}
```

> Replace `localhost` with the Docker host address if the container runs elsewhere.
>
> If authentication is enabled, add the Bearer token as a header:
>
> ```json
> {
>   "servers": {
>     "afc-mcp": {
>       "type": "http",
>       "url": "http://localhost:8010/mcp",
>       "headers": { "Authorization": "Bearer afc_xxxxxxxx" }
>     }
>   }
> }
> ```

2. Open the Command Palette → **MCP: List Servers**, select `afc-mcp` and start it.
3. In the Chat view (Agent mode), the AFC tools become available under the tools picker.

## Integrate with Claude Desktop

Claude Desktop connects to local (stdio) servers by default. To reach this
streamable-HTTP server, bridge it with [`mcp-remote`](https://www.npmjs.com/package/mcp-remote).

Edit `claude_desktop_config.json`:

- macOS: `~/Library/Application Support/Claude/claude_desktop_config.json`
- Windows: `%APPDATA%\Claude\claude_desktop_config.json`

```json
{
  "mcpServers": {
    "afc-mcp": {
      "command": "npx",
      "args": ["-y", "mcp-remote", "http://localhost:8010/mcp"]
    }
  }
}
```

Restart Claude Desktop; `afc-mcp` appears in the tools menu.

> Alternatively, recent Claude Desktop builds support remote MCP servers directly under
> **Settings → Connectors → Add custom connector** using the same URL.

## Available MCP tools

### Server & system
- `get_server_status` — MCP server reachability/health.
- `get_system_info` — AFC system information.

### Switches & fabrics
- `list_switches`, `get_switch` — switch inventory and per-switch detail (ports, software, tags).
- `list_fabrics`, `get_fabric` — fabric inventory and members.

### Routing & overlay
- `list_vrfs`, `get_vrf` — VRF inventory and detail.
- `get_vrf_routes` — VRF IP routing table (RIB) with next-hop lookup. Accepts VRF
  and switch by **name or UUID**; with a `destination` (host IP or CIDR) it does a
  longest-prefix match and returns the winning route(s) and next hop(s).
- `get_vrf_arp` — VRF ARP table (IP-to-MAC bindings) learned on the switches.
  Accepts VRF and switch by **name or UUID**; pass `switch` to scope the table to
  one switch. Each entry exposes the IP/MAC, interface, physical port, owning
  switch and neighbor reachability state (reachable, stale, incomplete, …).
- `get_vrf_ip_interfaces` — VRF L3 (IP) interfaces. Accepts VRF and switch by
  **name or UUID**; filter by `if_type` (`routed`, `vlan`, `loopback`, `evpn`).
  With `include_status` (default), the operational state (admin up/down, MAC,
  IP MTU, duplex, IPv4 address) is returned alongside the configuration.
- `get_vrf_static_routes` — IP static routes configured in a VRF (destination
  prefix, next hop or nullroute/discard, next-hop interface, distance, tag, type
  and applied switches). Accepts VRF and switch by **name or UUID**.
- `get_vrf_bgp_status`, `get_vrf_bgp_summary` — BGP state and summary per VRF.
- `get_vrf_ospf_neighbors`, `get_vrf_ospf_summary` — OSPF neighbors and summary per VRF.
- `list_evpn`, `list_evpn_routes` — EVPN instances and routes.
- `get_vrf_virtual_environment` — virtual environment bound to a VRF.

### Fabric underlay/overlay & network services
- `list_leaf_spine` — Leaf-Spine peer configurations (the fabric underlay building
  blocks: name, QoS trust, per-switch leaf-spine peers). Pass `fabric` (**name or
  UUID**) to scope to one fabric, or omit it to list across all fabrics. Set
  `include_interface=True` to expand the underlying leaf-spine interface details.
- `get_leaf_spine` — one Leaf-Spine configuration by UUID within a fabric.
- `list_l2_leaf_spine` — Layer-2 Leaf-Spine configurations, optionally scoped by
  `fabric` and expanded with `include_lag=True`.
- `list_subleaf_leaf` — Subleaf-Leaf configurations (subleaf switches attached below
  leaves: name, type and per-switch `subleaf_leaf_peers`). Pass `fabric` (**name or
  UUID**) to scope to one fabric, or omit it to list across all fabrics. Pass
  `peer_uuid` to fetch a single subleaf-leaf peer instead of the full list (`fabric`
  is then required). `include_lag` (default `True`) expands the full subleaf-leaf
  LAG/LACP details; set it to `False` for a lighter response. `resolve_switch_names`
  (default `True`) enriches each peer and LAG member with the switch name, physical
  ports and `endpoints` (`switch:port`), so the LAG members and upstream devices are
  identifiable without extra lookups.
- `list_vsx` — VSX pair configurations (the redundant switch-pairing / MLAG layer:
  name, system MAC, keep-alive VRF/UDP port, ISL/keep-alive timers, health and the
  two `vsx_peers`). Pass `fabric` (**name or UUID**) to scope to one fabric, or omit
  it to list across all fabrics. Set `include_isl_lag` / `include_keep_alive_interface`
  to expand the ISL LAG and keep-alive interface details.
- `get_vsx` — one VSX pair configuration by UUID within a fabric.
- `list_ntp_configurations` — NTP client configurations (servers and the
  fabrics/switches they are applied to). Filter by `fabric`/`switch` (**name or
  UUID**) and `in_use_only`.
- `list_dns_configurations` — DNS client configurations (name servers, domain
  name/search list and applied fabrics/switches). Filter by `fabric`/`switch`
  (**name or UUID**) and `in_use_only`.


### Multi-Fabric & Stretched VLAN
- `list_multi_hop_vxlan` — Multi-Fabric (Multi-Hop VXLAN) configurations that stitch
  fabrics together: how one fabric's border leader peers with remote fabrics over
  L3 eBGP to extend VXLAN across sites (`name`, local `fabric_uuid`/`fabric_name`,
  `border_leader`/`border_leader_name`, `l3_ebgp_borders` and `remote_fabrics` with
  each remote's site/fabric name, border leader, ASN and BGP peer IPs). Pass `fabric`
  (**name or UUID**) to scope to one fabric, or omit it to list across all fabrics.
- `get_multi_hop_vxlan` — one Multi-Hop VXLAN configuration by UUID within a fabric.
- `list_stretched_vlans` — Stretched VLAN (EVPN Multi-Site) configurations spanning
  multiple fabrics: the `fabric_uuids` (with resolved `fabric_names`), the
  `stretched_vlans` range (e.g. `"5, 20-50, 70"`) and the `global_route_targets`.
  Pass `fabric` (**name or UUID**) to return only objects that include that fabric,
  or omit it to list across all fabrics.
- `get_stretched_vlan` — one Stretched VLAN (EVPN Multi-Site) configuration by UUID.


### Sites & overview
- `list_afc_sites`, `get_afc_site_inventory` — AFC (remote) sites and their inventory.
- `get_network_overview` — aggregated network state snapshot.

### Health
- `list_health_alerts` — active AFC health alerts.
- `run_health_check` — aggregated health (alerts, switch/fabric health, BGP/OSPF
  adjacencies, optional HA + license status).

### Integrations & VMware vCenter/vSphere
- `list_integrations` — all integration packs, their remote servers and connection state.
- `list_vmware_integrations` — vSphere-only: one entry per configured vCenter with
  server address, connection state and fault message.
- `get_vmware_inventory` — VMware hosts with **location** (vCenter, datacenter, cluster,
  domain) and **status** (physical NIC states, VM power breakdown), plus vSwitches,
  Port Groups and VMs. Optional filter by ESXi host name.
- `list_vmware_vms` — flat VM inventory with **power status** and **placement**
  (ESXi host, cluster, datacenter, vCenter), IPs and tags. Optional filters:
  `power_state`, `host_name`.
- `get_vm_attachment` — trace a VM's end-to-end network attachment (vNIC → Port Group →
  vSwitch → host uplink → physical switch/port).

## Notes

- `AFC_BASE_URL` must be the host root **without** `/api`; the client adds the `/api` prefix.
- API authentication uses `POST /api/auth/token` with `X-Auth-Username` and
  `X-Auth-Password` headers; the token is reused and refreshed automatically on `401`.
- The vSphere pack does not expose a per-host power/health field: host status is derived
  from physical NIC states and VM power counts; `vcenter` is the vSphere instance UUID.