Skip to main content
Glama
uzzikie

sangfor-mcp-server

by uzzikie
README.md
# sangfor-mcp-server

A [FastMCP](https://gofastmcp.com) server exposing read-only inspection tools
for a Sangfor HCI / Sangfor Cloud Platform (SCP) cluster.

Strictly read-only by design: every tool maps to a `GET` endpoint. No VM
lifecycle (create/delete/power/migrate/clone), no disk/NIC mutation, no
snapshot create/delete/revert is exposed here — see the endpoint table below.

## Environment variables

| Variable | Required | Default | Description |
|---|---|---|---|
| `SANGFOR_ACCESS_KEY` | Yes | — | SigV4 access key issued by Sangfor for API access. |
| `SANGFOR_SECRET_KEY` | Yes | — | SigV4 secret key paired with `SANGFOR_ACCESS_KEY`. |
| `SANGFOR_BASE_URL` | Yes | — | Base URL of the target Sangfor cluster, e.g. `https://your-cluster.example.com:4430`. No trailing slash. |
| `SANGFOR_VM_API_VERSION` | No | `20240725` | API version for the VM list/detail endpoints only. Lower it (e.g. `20180725`) if the target SCP build predates the default — every other endpoint in this server is pinned to a fixed, already-confirmed version. |
| `SANGFOR_VERIFY_SSL` | No | `true` | Set to `false` to skip TLS certificate verification, for clusters on self-signed certs. |
| `MCP_API_KEY` | No | unset | Shared secret MCP *clients* must send as `Authorization: Bearer` to *this server*. Left unset by default since whatever fronts this container may already gate access on its own side; set it to add a second, independent gate. Generate with `openssl rand -hex 32`. |

## Auth model

`SANGFOR_ACCESS_KEY` / `SANGFOR_SECRET_KEY` are held server-side. Every
request to Sangfor is signed with a non-standard SigV4 variant (see
`sigv4.py`) using this pair — there's no login/session/token-expiry to
manage, and no per-user Sangfor identity behind it: every MCP client that
reaches this server shares this key's access to the one configured cluster.

Single cluster per container: point one deployment at one
`SANGFOR_BASE_URL`. For multiple clusters, run one container per cluster
rather than adding a `cluster` selector argument to every tool.

## Tools

**Clusters & resource pools** — `sangfor_list_clusters`,
`sangfor_get_cluster_details`, `sangfor_list_resource_pools`,
`sangfor_get_resource_pool_details`.

**Nodes** — `sangfor_list_nodes`.

**VMs** — `sangfor_list_vms`, `sangfor_get_vm_details`,
`sangfor_get_vm_power_status`, `sangfor_list_vm_disks`,
`sangfor_list_vm_nics`, `sangfor_get_vm_guest_info` (the last four are
projections of `sangfor_get_vm_details` — one Sangfor API call underlies all
of them, since VM detail already returns disks/networks/OS info in one
response).

**Snapshots** — `sangfor_list_vm_snapshots`, `sangfor_get_vm_snapshot_details`.

**Networking** — `sangfor_list_vpcs`, `sangfor_list_subnets`,
`sangfor_list_virtual_switches`.

**Storage** — `sangfor_list_datastores`, `sangfor_get_datastore_details`.

**Metrics** — `sangfor_get_resource_metrics` (CPU/memory/disk-IO/network
time-series for a VM, host, or resource pool).

**Images** — `sangfor_list_images`.

Every endpoint version used is pinned to what was confirmed against the
Sangfor OpenAPI reference, except the VM list/detail endpoints
(`SANGFOR_VM_API_VERSION`, default `20240725`) which ship as repeated,
additive revisions — lower it if the target SCP build predates the default.

## Local development

```bash
cd sangfor-mcp-server
pip install -e .
SANGFOR_ACCESS_KEY=... \
SANGFOR_SECRET_KEY=... \
SANGFOR_BASE_URL=https://<your-sangfor-host>:4430 \
SANGFOR_VERIFY_SSL=false \
uvicorn sangfor_mcp_server.server:app --reload --port 3000
```

## Deploy

```bash
docker build -t uzzikie/sangfor-mcp:latest .
docker push uzzikie/sangfor-mcp:latest
```

Run the image with `SANGFOR_ACCESS_KEY`, `SANGFOR_SECRET_KEY`, and
`SANGFOR_BASE_URL` set as its environment, serving `/mcp` on port `3000` — no
persistent volume needed, the container is fully stateless.