Content
<p align="center">
<img src="https://raw.githubusercontent.com/Josh-E-S/pexip-mgmt-mcp/main/.github/banner.png" alt="Infinity MCP" width="100%">
</p>
<h1 align="center">
MCP Server for Pexip Infinity
</h1>
[](https://github.com/Josh-E-S/pexip-mgmt-mcp/actions/workflows/ci.yml)
[](LICENSE)
[](https://www.python.org/downloads/)
[](https://github.com/astral-sh/ruff)
<!-- mcp-name: io.github.josh-e-s/pexip-mgmt-mcp -->
MCP (Model Context Protocol) server for the **Pexip Infinity Management API**. It
gives an LLM-based agent everything it needs to manage and operate a Pexip
Infinity deployment, exposing **122 curated tools** and covering
all four admin API categories: Configuration, Status, History, and Command.
> **Disclaimer:** This is an independent, community-built project. It is **not
> affiliated with, endorsed by, or sponsored by Pexip**. It uses Pexip's public
> Management API and sends **no telemetry or analytics**. The server needs **no
> external network access** beyond the Pexip Management Node you configure, plus
> your own identity provider if you choose to use OIDC. It never sends your data
> anywhere else.
## Quick start
You'll need your Pexip **Management Node hostname** plus credentials. **OAuth2 is
recommended for production**; **basic auth** (admin username + password) is
recommended only for **dev/lab** environments. See
[Configuration](#configuration) to set up an OAuth2 client.
The server starts **read-only** by default: it can list and report, but every
create / update / delete / control tool is removed from the catalog until you
explicitly enable writes. Connect first, confirm it works, then decide.
### ① Claude Desktop: one-click install
The simplest path: no terminal, no JSON.
1. **Download** the `.mcpb` bundle for your OS (**macOS** or **Windows**) from the
[latest release](https://github.com/Josh-E-S/pexip-mgmt-mcp/releases/latest).
2. **Double-click** it. Claude Desktop opens an install dialog showing the server
and the permissions it requests.
3. **Fill in the form** with your Management Node **host** and either an **OAuth2
client** (recommended) or an **admin username + password** (dev/lab only).
Leave **read-only** on. Click install.
4. **Try it.** Ask Claude: _"List the VMRs on my Pexip node"_ or _"Show me the
Pexip system status."_
To enable writes later, edit the server in Claude Desktop's settings and turn
**read-only** off.
<p align="center">
<img src=".github/example-mcpb-config.png" alt="Claude Desktop Extensions settings for pexip-mgmt-mcp, showing the Management Node host, Auth mode, basic and OAuth2 credential fields, Verify TLS and Read-only toggles, and the per-tool permissions list" width="640">
</p>
### ② Manual config: Claude Code & other MCP hosts
For Claude Code, Cursor, or any MCP host that takes a JSON server config.
**1. Install the server** (needs Python 3.11+):
```bash
git clone https://github.com/Josh-E-S/pexip-mgmt-mcp.git
cd pexip-mgmt-mcp
pipx install . # installs the `pexip-mgmt-mcp` command on your PATH
```
**2. Add it to your MCP host** by pointing its config at the `pexip-mgmt-mcp`
command. Use **OAuth2** for production:
```json
{
"mcpServers": {
"pexip-mgmt": {
"command": "pexip-mgmt-mcp",
"env": {
"PEXIP_HOST": "pexip-mgr.example.com",
"PEXIP_AUTH_MODE": "oauth2",
"PEXIP_OAUTH2_CLIENT_ID": "your-client-id",
"PEXIP_OAUTH2_PRIVATE_KEY": "-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----\n"
}
}
}
}
```
For **dev/lab**, use basic auth instead: swap the `PEXIP_AUTH_MODE` /
`PEXIP_OAUTH2_*` keys for `"PEXIP_USERNAME": "admin"` and `"PEXIP_PASSWORD": "..."`.
See [Configuration](#configuration) for all options.
- **Enable writes:** add `"PEXIP_READ_ONLY": "false"` to `env` (read-only is the
default).
- **Claude Code shortcut** (instead of hand-editing JSON):
```bash
# OAuth2 (recommended for production)
claude mcp add pexip-mgmt \
-e PEXIP_HOST=pexip-mgr.example.com \
-e PEXIP_AUTH_MODE=oauth2 \
-e PEXIP_OAUTH2_CLIENT_ID=your-client-id \
-e PEXIP_OAUTH2_PRIVATE_KEY="$(cat oauth2_private_key.pem)" \
-- pexip-mgmt-mcp
# basic auth (dev/lab only)
claude mcp add pexip-mgmt \
-e PEXIP_HOST=pexip-mgr.example.com \
-e PEXIP_USERNAME=admin \
-e PEXIP_PASSWORD=... \
-- pexip-mgmt-mcp
```
<!-- Screenshot: Claude Code /mcp showing pexip-mgmt connected -->
### Other ways to run
- **Docker (self-hosted HTTP transport):** run the container alongside your
Infinity, serving on `127.0.0.1:8000`. For **lab/dev**, fronting it with a
tunnel or proxy is fine. For **production**, run it as a proper service behind
a hardened reverse proxy (TLS, authentication, access control) on a managed
host, not a dev tunnel. See [DEPLOY.md](DEPLOY.md).
- **From source (developers):** clone the repo, then:
```bash
pip install -e .
cp .env.example .env # edit with your Management Node details
python -m pexip_mcp --healthcheck
# OK: connected to pexip-mgr.example.com as admin, schema fetched
```
See [Testing](#testing) for the full dev workflow.
## Contents
- [Quick start](#quick-start)
- [Coverage](#coverage)
- [How it fits together](#how-it-fits-together)
- [Examples](#examples)
- [Configuration](#configuration)
- [Quality: the eval suite](#quality-the-eval-suite)
- [Skills SDK](#skills-sdk)
- [Testing](#testing)
## Coverage
| API | Tools | What it covers |
|---|---|---|
| **Configuration** | 46 | VMRs + aliases, end users, devices, gateway rules, automatic participants, IVR themes, LDAP sync, locations + Conferencing Nodes, global settings, schema introspection, plus **generic CRUD** (`list/get/create/update/delete_resource`) over ~70 registry resources: SIP/H.323/MS-SIP proxies, TURN/STUN, Teams Connectors, Azure tenants, Google Meet tokens, MJX (endpoints, integrations, deployments), DNS/NTP/SMTP/syslog/SNMP, certificates (CA/TLS/CSR), admin roles + identity providers, backups + upgrades, web app hosting, policy profiles, and more |
| **Status** | 39 | Live conferences + per-node shards, participants + media streams, per-participant call quality, registrations, node/location status + load stats, backplanes, alarms, licensing, cloud overflow, Exchange scheduler, MJX endpoints + meetings, Teams Connector nodes + calls |
| **History** | 14 | Conference + participant CDRs, server-side aggregation (`summarize_calls`), alarm history, backplane history, registration history, node event history |
| **Command** | 23 | Active call control: dial, disconnect, mute/unmute (participant or all guests), set role, lock/unlock, transfer, layout, messages, LDAP sync, provisioning emails, backup create/restore, certificate import, snapshot, upgrade |
The full catalog with per-tool annotations and parameters is in
[TOOLS.md](TOOLS.md) (regenerate with `uv run python scripts/generate_tools_md.py`).
**This MCP server is designed to optimize tool use.** High-traffic resources
(VMRs, users, devices, rules) get dedicated typed tools; the ~70 remaining
configuration resources share five generic CRUD tools backed by a resource
registry. That keeps the catalog at 122 tools, which lowers per-request token use
and improves the agent's tool selection (verified by the
[eval suite](#quality-the-eval-suite)).
## How it fits together
<!-- Diagram source: .github/architecture.mmd
Regenerate: npx -y @mermaid-js/mermaid-cli -i .github/architecture.mmd -o .github/architecture.png -b white -s 2 -->
<p align="center">
<img src=".github/architecture.png" alt="An MCP client talks to pexip-mgmt-mcp over stdio or HTTP; the server resolves names, enforces read-only mode, and calls the four Pexip Management APIs: Configuration, Status, History, and Command" width="100%">
</p>
Source layout:
```
src/pexip_mcp/
├── client.py # Async PexipClient (all 4 API categories), 429-aware retry
├── config.py # PexipSettings (env-driven, pydantic-settings)
├── mcp_app.py # FastMCP instance + lifespan
├── server.py # Imports tool modules to trigger registration
├── __main__.py # Entry point + --healthcheck + --http
└── tools/
├── _helpers.py # get_client, resolve_id_by_field, paginate_all, annotation presets
├── command.py # Active call control + name→UUID resolvers
├── status.py # Live state + per-participant quality
├── history.py # CDRs + summarize_calls aggregation
├── resource_crud.py # Generic CRUD + resource registry (~70 resources)
├── schema.py # Live schema introspection
└── conference.py, end_user.py, device.py, gateway_rule.py, alias.py,
automatic_participant.py, infrastructure.py, ldap.py,
ivr_theme.py, global_settings.py # dedicated typed CRUD per resource
```
## Examples
Real Claude Desktop conversations against a lab Pexip node.
**A status question, answered directly:**
<p align="center">
<img src=".github/example-call-summary.png" alt="Claude Desktop: user asks how many calls this week; Claude reports 12 calls, all inbound, with total and average duration, and offers to break it down further" width="800">
</p>
**Config inventory across multiple resources in one ask:**
<p align="center">
<img src=".github/example-inventory.png" alt="Claude Desktop: user asks for a count and details of VMRs, users, and devices; Claude lists each VMR with aliases, description, tag, and view settings" width="800">
</p>
**CDRs turned into an interactive dashboard, not a data dump:**
<p align="center">
<img src=".github/example-dashboard.png" alt="Claude Desktop: user asks for interactive graphs of Infinity usage; Claude builds a dashboard with stat cards, a call-quality donut chart, a disconnect-reasons bar chart, a call timeline, and a written analysis of the traffic pattern" width="800">
</p>
## Configuration
All env vars are `PEXIP_*` prefixed and loaded from `.env` (or the process
environment). See [.env.example](.env.example) for an annotated template.
| Variable | Default | Purpose |
|---|---|---|
| `PEXIP_HOST` | required | Management Node hostname or IP, no scheme |
| `PEXIP_AUTH_MODE` | `basic` | `oauth2` (recommended for production) or `basic` (dev/lab) |
| `PEXIP_OAUTH2_CLIENT_ID` | oauth2 only | OAuth2 Client ID from the Pexip UI |
| `PEXIP_OAUTH2_PRIVATE_KEY` | oauth2 only | OAuth2 client "Private key" (ES256, shown once) |
| `PEXIP_OAUTH2_TOKEN_URL` | `https://<host>/oauth/token/` | Override the token endpoint |
| `PEXIP_OAUTH2_SCOPE` | `is_admin use_api` | OAuth2 scopes requested |
| `PEXIP_USERNAME` | basic only | Admin username (typically `admin`) |
| `PEXIP_PASSWORD` | basic only | Admin password |
| `PEXIP_VERIFY_TLS` | `true` | Set `false` for self-signed lab nodes |
| `PEXIP_TIMEOUT` | `30` | HTTP timeout in seconds |
| `PEXIP_MAX_RETRIES` | `3` | Retries on 429 rate-limit responses |
| `PEXIP_READ_ONLY` | `true` | Expose only read tools; remove all write/control tools. **On by default;** set `false` to enable writes |
| `PEXIP_ALLOW_SECURITY_RESOURCES` | `false` | Allow generic CRUD to mutate security-critical resources (SSH keys, roles, auth, certs). Only relevant when writes are enabled |
| `PEXIP_ALLOW_PLATFORM_TOOLS` | `false` | Expose platform-lifecycle command tools (backup/restore, upgrade, cert import, software upload, cloud-node start, snapshot). Removed at startup unless enabled, even with writes on |
| `PEXIP_MCP_AUTH_MODE` | `token` | How HTTP clients authenticate to this server: `token` (static bearer) or `oauth` (OIDC). Only affects `--http`. See [docs/identity.md](docs/identity.md) |
| `PEXIP_MCP_TOKEN` | unset | Bearer token for the `--http` transport (required for non-loopback binds; min 32 chars). Run `pexip-mgmt-mcp --generate-token` |
| `PEXIP_OIDC_ISSUER` | unset | Issuer URL; required when `PEXIP_MCP_AUTH_MODE=oauth` (validate OIDC JWTs from your own IdP: Entra/Google/Okta/on-prem) |
| `PEXIP_OIDC_AUDIENCE` | unset | Expected token audience; required when `PEXIP_MCP_AUTH_MODE=oauth` |
| `PEXIP_OIDC_REQUIRED_SCOPES` | unset | Space-separated scopes the JWT must carry (optional) |
| `PEXIP_OIDC_JWKS_URI` | discovered | Override the JWKS endpoint (default: discovered from the issuer) |
### Read-only mode (default)
The server runs in **read-only mode by default**: only the read tools
(list / get / schema) are exposed. Every create, update, delete, and Command-API
control tool is removed from the catalog at startup, so the LLM cannot mutate the
deployment even if it tries. This is enforced server-side: the tools are gone
from the catalog, not merely flagged with an advisory `readOnlyHint`.
To enable the mutating admin surface, set `PEXIP_READ_ONLY=false` (logged loudly
at startup). Even then, generic CRUD refuses to touch security-critical
resources (SSH keys, admin roles/permissions, authentication/SSO, TLS/CA certs)
unless you also set `PEXIP_ALLOW_SECURITY_RESOURCES=true`. Pair writes with a
least-privilege Pexip Administrator Role for defense in depth.
### Authentication: OAuth2 vs basic
Two modes, selected by `PEXIP_AUTH_MODE`:
- **`oauth2`** (recommended for production): an OAuth2 **JWT bearer assertion**
(ES256). The server signs a short-lived JWT with the client's private key and
exchanges it at `https://<host>/oauth/token/` for a 1-hour bearer token (cached
and auto-refreshed). It authenticates once per hour instead of on every request
and keeps a reusable admin password out of the server's environment.
- **`basic`** (dev/lab only): a local Management Node admin username + password.
Works out of the box on every Infinity deployment and is the quickest way to
get started, but sends admin credentials on each request. Not recommended for
production.
**Set up an OAuth2 client** in the Pexip admin UI under **Users & Devices >
OAuth2 Clients**: add a client, then copy its **Client ID** and **Private key**
(the private key is shown only once). You also enable Management API OAuth2 and
attach an Administrator Role, per Pexip's
[Managing API access via OAuth2](https://docs.pexip.com/admin/managing_API_oauth.htm).
Then set `PEXIP_AUTH_MODE=oauth2`, `PEXIP_OAUTH2_CLIENT_ID`, and
`PEXIP_OAUTH2_PRIVATE_KEY`.
<p align="center">
<img src=".github/oauth2-client-setup.png" alt="Pexip admin UI: Users & Devices > OAuth2 Clients, showing the Client ID, Client name, Role, and Private key fields" width="640">
</p>
## Quality: the eval suite
The [`evals/`](evals/) suite measures whether an LLM can drive these tools
correctly from natural language. It defines **156 scenarios** written as real
admin requests ("mute all the guests in AllHands", "point the syslog server at
10.0.0.99"), graded automatically across three layers:
| Layer | What runs | Cost |
|---|---|---|
| Deterministic (332 checks) | Every eval case is validated against the live tool registry (tools exist, parameter names match signatures) | free, in CI |
| LLM-graded (`--llm`) | Each prompt goes to Claude with the full tool catalog; multi-turn conversations with mocked API responses; graded on tool choice, parameters, and chain order | ~$2 / full run (tool catalog is prompt-cached) |
| Live (`--live`, 33 tests) | CRUD, status reads, and call commands against a real Infinity node, including auto-dialing a test call and moderating it by name | free (your lab) |
Scoring supports exact / subset / ordered-subset / any-of tool matching,
per-step parameter checks with acceptable-alternative values, and optional
steps for chains where server-side name resolution makes a lookup legitimate
but unnecessary. See [evals/README.md](evals/README.md) for the case format
and how to add scenarios.
Current state: the full suite passes: **698 passed, 12 skipped, 0 failures**
across all four layers (208 unit + 332 deterministic eval checks + LLM-graded +
33 live), 86% coverage. The 12 skips are env-gated live cases (e.g. no dial
target set). Reproduce the full run with `uv run pytest tests/ evals/ --llm
--live`, or the free deterministic subset (540 checks) with
`uv run pytest tests/ evals/`.
## Skills SDK
[`pexip-mgmt-skills/`](./pexip-mgmt-skills/) is a self-contained
[Agent Skills](https://agentskills.io) (open standard) + Claude Code plugin
package that wraps this MCP server with operator runbooks and
developer-reference skills. Built to be extractable: `cp -r pexip-mgmt-skills/`
out and you have a complete, plug-installable SDK that loads in Claude Code,
Gemini CLI, Codex CLI, Cursor, or any other compliant host.
Currently ships 9 skills across 6 domains:
| Domain | Skill | Audience |
|---|---|---|
| router | `pexip-mgmt-intake` | both, start here for open-ended requests |
| operations | `pexip-operations` | operator: kick / lock / report / configure |
| management-api | `pexip-config-api` | developer: modify Configuration API tool code |
| management-api | `pexip-status-api` | developer: Status API |
| management-api | `pexip-history-api` | developer: History API |
| management-api | `pexip-command-api` | developer: Command API |
| events | `pexip-event-sinks` | both, configure Pexip's webhook push-event destinations |
| policy | `pexip-external-policy` | developer: external policy server config (via generic CRUD) |
| room-integration | `pexip-mjx` | both, One-Touch Join |
See [`pexip-mgmt-skills/README.md`](./pexip-mgmt-skills/README.md) for the
install instructions and
[`pexip-mgmt-skills/ARCHITECTURE.md`](./pexip-mgmt-skills/ARCHITECTURE.md)
for the design rules. The companion
[awesome-pexip-skills](https://github.com/Josh-E-S/awesome-pexip-skills)
covers the **client-side** (webapp3, `@pexip/infinity`, `@pexip/media`).
Install both for full Pexip coverage.
## Testing
```bash
uv run pytest # 208 unit + 332 deterministic eval checks, 86% coverage
uv run pytest tests/ # 208 unit tests (mocked HTTP via respx), ~2s
uv run pytest evals/ # 332 deterministic eval checks, free
uv run pytest evals/ --llm # LLM-graded evals (needs ANTHROPIC_API_KEY, ~$2)
uv run pytest evals/ --live # integration against a real node (needs .env)
uv run ruff check src tests evals
```
Every run prints a coverage report, but the **80% gate is enforced only in CI on the
full suite** (`--cov-fail-under=80` in `.github/workflows/ci.yml`), so subset runs
like `uv run pytest evals/` show partial coverage without failing. Unit tests mock
the Pexip Management API with [respx](https://github.com/lundberg/respx); the retry
suite monkeypatches `asyncio.sleep` so backoff tests run instantly.
## License
[MIT](LICENSE)
MCP Config
Below is the configuration for this MCP Server. You can copy it directly to Cursor or other MCP clients.
mcp.json
Connection Info
You Might Also Like
No recommendations available