---
title: Connect an AI client (MCP)
description: Connect Claude Code, Codex CLI, Gemini CLI, Cursor, Claude Desktop, claude.ai, ChatGPT or the OpenAI SDKs to your panel's admin MCP endpoint, with the full tool list.
url: https://nexcmp.com/docs/configure-with-ai/mcp-admin/
applies_to: NexCMP 1.6.1
updated: 2026-11-02
---

# Connect an AI client (MCP)

> **Where in the admin:** System › Settings › API & AI agents (`/agent/keys`), tab **Connect an AI** shows the endpoint URL and a ready-to-copy snippet for each client, filled with a key you just created.

## What it does

The **Model Context Protocol (MCP)** lets an AI client call tools. Your panel serves an admin MCP server at `https://cloud.example.com/mcp/admin`. Connect your own AI tool to it and ask in plain language: "what is left to set up?", "add my Proxmox cluster", "create plans with a 40% margin". The client calls the admin tools below; the panel checks scopes, asks you before anything risky and records every call.

::: info Two MCP endpoints
`/mcp` is the **customer** endpoint (customer API keys, `vk_`; see [MCP server](https://nexcmp.com/docs/developers/mcp/)). `/mcp/admin` is the **admin** endpoint described here (admin keys `ak_` or OAuth). The two never mix: admin tools are not visible on `/mcp`, and `/mcp/admin` accepts only admin keys.
:::

## Requirements

| Item | Requirement |
|---|---|
| Endpoint | `https://cloud.example.com/mcp/admin` on your panel's own domain. Copy the exact URL from the **Connect an AI** tab. The same page lists a local URL (`http://127.0.0.1:<port>/mcp/admin`) for tools running on the server itself. |
| Credentials | An [admin API key](https://nexcmp.com/docs/configure-with-ai/admin-api-keys/) (`ak_...`) with the scopes you want, **or** OAuth sign-in for web connectors ([OAuth connectors](https://nexcmp.com/docs/configure-with-ai/oauth-connectors/)). |
| Client | Any MCP client that supports the **Streamable HTTP** transport and a custom `Authorization` header. Protocol versions 2025-11-25, 2025-06-18 and 2025-03-26. |
| Network | If you put your own reverse proxy in front of the panel, it must forward `/mcp/admin`. Web connectors (claude.ai, ChatGPT) connect from the vendor's servers, so the URL must be reachable from the internet. |

## Prepare the infrastructure

Nothing to install on the server. If you restrict the key by IP, make sure the address your client connects from is in the range. Test reachability first:

```bash
curl -i -X POST https://cloud.example.com/mcp/admin
```

An answer of `401` with a `WWW-Authenticate: Bearer ... resource_metadata=...` header means the endpoint is up and wants credentials.

## Connect in NexCMP

1. Create a key: **System › Settings › API & AI agents › API keys › New key**. For a first try use `admin:read` only; add write scopes when you need them. See [Admin API keys](https://nexcmp.com/docs/configure-with-ai/admin-api-keys/).
2. Open the tab **Connect an AI**. While the "key created" dialog is still fresh, every snippet below already contains your real key. Otherwise it shows the placeholder `ak_YOUR_KEY`.
3. Copy the snippet for your client and run or paste it as described next.

![Connect an AI tab with the MCP URL and one card with a copy button per client](https://nexcmp.com/docs/images/configure-with-ai/mcp-admin-01-connect.webp)

### Claude Code

```bash
claude mcp add --transport http nexcmp https://cloud.example.com/mcp/admin \
  --header "Authorization: Bearer ak_XXXX"
```

Then start `claude` and ask "list the tools from nexcmp" or "what is left to set up?".

Checked on 30/10/2026 with Claude Code 2.1: `claude mcp add --transport http …` writes the server (add `-s project` to keep it in the
project's `.mcp.json` — Claude Code asks you to approve project servers the first time). For scripts, `claude -p "…" --mcp-config .mcp.json
--strict-mcp-config --allowedTools "mcp__nexcmp__*"` runs without the approval prompt and lets Claude use only the panel's tools.

### Claude Desktop

Needs Node.js 18 or newer. Add this to `claude_desktop_config.json` (it uses the `mcp-remote` bridge) and restart Claude Desktop:

```json
{
  "mcpServers": {
    "nexcmp": {
      "command": "npx",
      "args": ["-y", "mcp-remote", "https://cloud.example.com/mcp/admin", "--header", "Authorization:Bearer ak_XXXX"]
    }
  }
}
```

If your system mangles the space in the header argument, put the whole value in an environment variable instead, exactly as for the customer endpoint: `"args": [..., "--header", "Authorization:${NEXCMP_AUTH_HEADER}"]` with `"env": { "NEXCMP_AUTH_HEADER": "Bearer ak_XXXX" }`.

### claude.ai (custom connector)

No key needed: it signs in with OAuth. In claude.ai open the connectors settings, add a custom connector, paste `https://cloud.example.com/mcp/admin`, then sign in with your NexCMP admin account and choose the permissions on the consent screen. Details: [OAuth connectors](https://nexcmp.com/docs/configure-with-ai/oauth-connectors/).

### ChatGPT (developer mode)

No key needed. In ChatGPT turn on developer mode, add an MCP connector with the URL `https://cloud.example.com/mcp/admin`, choose OAuth and sign in with your NexCMP admin account. Details: [OAuth connectors](https://nexcmp.com/docs/configure-with-ai/oauth-connectors/).

### Codex CLI

Add to `~/.codex/config.toml`:

```toml
[mcp_servers.nexcmp]
url = "https://cloud.example.com/mcp/admin"
bearer_token_env_var = "NEXCMP_KEY"
```

Then export the key in the shell that starts Codex:

```bash
export NEXCMP_KEY=ak_XXXX
codex
codex mcp list   # shows nexcmp with "Bearer token" auth
```

::: tip Non-interactive `codex exec`
In `codex exec` (scripts, CI) Codex does not ask before calling tools, and it skips tools that change data. Add
`default_tools_approval_mode = "approve"` under `[mcp_servers.nexcmp]` to let it call them; the panel still returns a preview and
asks for confirmation for money, security and delete actions. Checked with Codex CLI 0.166 on 30/10/2026.
:::

### Gemini CLI

Add to `~/.gemini/settings.json`:

```json
{
  "mcpServers": {
    "nexcmp": {
      "httpUrl": "https://cloud.example.com/mcp/admin",
      "headers": { "Authorization": "Bearer ak_XXXX" }
    }
  }
}
```

Gemini CLI turns MCP servers off in folders it does not trust. Start it in a folder you trust (or trust the folder when it asks), then
check with `gemini mcp list` — it shows `nexcmp … Connected`. You can keep the key out of the file with `"Authorization": "Bearer $NEXCMP_KEY"`.
Checked with Gemini CLI 0.66 on 30/10/2026.

### Cursor

Add to `~/.cursor/mcp.json` (or `.cursor/mcp.json` in a project):

```json
{
  "mcpServers": {
    "nexcmp": {
      "url": "https://cloud.example.com/mcp/admin",
      "headers": { "Authorization": "Bearer ak_XXXX" }
    }
  }
}
```

::: tip Keep the key out of config files
Where the client supports it, read the key from an environment variable instead of writing it in the file: Codex does this by default (`bearer_token_env_var`), and Cursor accepts `"Bearer ${env:NEXCMP_KEY}"`. Never commit a file that contains a key.
:::

### OpenAI Agents SDK (Python)

```python
from agents.mcp import MCPServerStreamableHttp

nexcmp = MCPServerStreamableHttp(
    params={"url": "https://cloud.example.com/mcp/admin",
            "headers": {"Authorization": "Bearer ak_XXXX"}},
    name="nexcmp",
)
```

A complete run (checked with `openai-agents` 0.24 on 30/10/2026 — a read tool, then a balance change that returned the confirmation preview
and was applied on the second call with `confirm_token`):

```python
import asyncio
from agents import Agent, Runner
from agents.mcp import MCPServerStreamableHttp

async def main():
    async with MCPServerStreamableHttp(params={"url": "https://cloud.example.com/mcp/admin",
            "headers": {"Authorization": "Bearer ak_XXXX"}}, name="nexcmp") as nexcmp:
        agent = Agent(name="admin", model="gpt-5", mcp_servers=[nexcmp],
                      instructions="You operate a hosting panel through the nexcmp tools. Ask me before confirming money actions.")
        result = await Runner.run(agent, "Run system_health and summarise it in one line.")
        print(result.final_output)

asyncio.run(main())
```

### OpenAI Responses API

Pass the server as a tool of type `mcp`. Keep `require_approval` on `always` so your program asks you before each call:

```json
{
  "type": "mcp",
  "server_label": "nexcmp",
  "server_url": "https://cloud.example.com/mcp/admin",
  "headers": { "Authorization": "Bearer ak_XXXX" },
  "require_approval": "always"
}
```

### Anything else (plain HTTP)

A client without MCP can call the same tools over HTTP. `GET /v1/admin/agent/tools` lists them, `POST /v1/admin/agent/tools/<name>` runs one with a JSON body of arguments, and `GET /v1/admin/openapi.json` returns an OpenAPI 3.1 description (each tool is one operation). All need `Authorization: Bearer ak_...`.

## Verify

1. Without a client, list the tools:

   ```bash
   curl -s https://cloud.example.com/mcp/admin \
     -H "Authorization: Bearer ak_XXXX" \
     -H "Content-Type: application/json" \
     -H "Accept: application/json, text/event-stream" \
     -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'
   ```

   The answer is a JSON-RPC result with the tools your scopes allow.
2. In your client ask "run system_health". It returns alerts, the health of connected infrastructure, payments, email, AI and the license state.
3. Open **API & AI agents › Activity**: the calls are listed with the client name and `mcp`.
4. On the server, `sudo nexcmp status` prints a line "MCP admin" that says `ready` when the endpoint answers (HTTP 401 without a key is the healthy state).

## Tools

The server offers 58 tools. A client only sees the tools its key's scopes allow: read tools need `admin:read`, write tools need their own scope. Write tools also get an optional `confirm_token` argument (see [Confirmation](#confirmation-confirm-token)). Arguments are checked against each tool's schema: unknown or mistyped arguments are rejected with a message that names them.

Prices are in USD: plan prices in dollars per hour (`hourly_usd`), balances in cents. Secrets can be set but are never returned.

### Overview and logs

| Tool | What it does | Scope | Changes data | Asks you |
|---|---|---|---|---|
| `system_health` | Alerts that need attention, infrastructure health, payments, email, AI, license and update status. | `admin:read` | No | No |
| `setup_checklist` | What is configured and what is left: license, infrastructure, zone, OS templates, plans, payments, email, branding, legal, test order. | `admin:read` | No | No |
| `license_status` | License status and whether a newer NexCMP version exists. | `admin:read` | No | No |
| `audit_search` | Search the admin audit log and the API-key log. | `admin:read` | No | No |

### Settings, branding, legal, modules

| Tool | What it does | Scope | Changes data | Asks you |
|---|---|---|---|---|
| `settings_get` | Read general settings (`keys` filters; a trailing `*` matches a prefix). | `admin:read` | No | No |
| `settings_update` | Change general settings from a flat `values` object. Only keys listed by `settings_get` are accepted. | `settings:write` | Yes | When a key touches payments, sign-in security, site-wide switches or prices |
| `branding_get` | Brand name, colour, display currency, seller name, logo and favicon URLs. | `admin:read` | No | No |
| `branding_update` | Set brand name (up to 60 characters), colour (`#rrggbb`), display currency (`usd` or `vnd`) and seller name. | `settings:write` | Yes | No |
| `branding_logo_set` | Upload a light or dark logo or a favicon from an https URL or base64 (PNG, JPEG, WEBP, ICO or SVG, up to 512 KB). | `settings:write` | Yes | No |
| `legal_get` | Terms of service and privacy policy (English and Vietnamese) and the neutral defaults. | `admin:read` | No | No |
| `legal_update` | Set terms and privacy text (simple markdown, no HTML, up to 50 KB each). An empty string restores the default. | `settings:write` | Yes | No |
| `modules_get` | Which optional modules are on and which can be switched. | `admin:read` | No | No |
| `module_set` | Switch an optional module on or off (`key` must be one of `settable`). | `settings:write` | Yes | No |

### Email and alerts

| Tool | What it does | Scope | Changes data | Asks you |
|---|---|---|---|---|
| `smtp_get` | Outgoing email settings (password masked) and queue status. | `admin:read` | No | No |
| `smtp_configure` | Set the SMTP server. Changing host, port or security needs the password again. | `settings:write` (+ `secrets:write` for the password) | Yes | No |
| `smtp_test` | Send a real test email with the saved settings. | `settings:write` | Sends an email | No |
| `telegram_get` | Admin notification channels (chat ids; bot tokens are never returned). | `admin:read` | No | No |
| `telegram_configure` | Add or update the Telegram alert channel (bot token and chat id) and switch admin alerts on or off. | `settings:write` (+ `secrets:write` for the token) | Yes | No |
| `telegram_test` | Send a test message to the Telegram channel. | `settings:write` | Sends a message | No |

### Payments and currency

| Tool | What it does | Scope | Changes data | Asks you |
|---|---|---|---|---|
| `payments_get` | Status of Stripe, PayPal, crypto (USDT) and bank transfer, plus enabled channels and fees. | `admin:read` | No | No |
| `payment_configure` | Set the credentials of one gateway (`stripe`, `paypal`, `crypto` or `bank`). | `billing:write` (+ `secrets:write`) | Yes | Always |
| `payment_test` | Check a gateway's saved credentials with the provider. | `billing:write` | No | No |
| `currency_get` | Display currency and the USD to VND rate (automatic or manual). | `admin:read` | No | No |
| `currency_set` | Set the display currency and/or a manual rate (0 means automatic, otherwise 15000 to 50000). | `billing:write` | Yes | When you change the manual rate |

### Infrastructure and zones

| Tool | What it does | Scope | Changes data | Asks you |
|---|---|---|---|---|
| `cloudstack_get` | Saved CloudStack endpoint and zone and network ids (keys masked). | `admin:read` | No | No |
| `cloudstack_test` | Test an endpoint with an API key and secret without saving; lists zones. | `infra:write` (+ `secrets:write`, because you pass the keys) | No | No |
| `cloudstack_connect` | Save the CloudStack connection. | `infra:write` (+ `secrets:write`) | Yes | Always |
| `proxmox_list` | Proxmox VE clusters added as sources, with health. | `admin:read` | No | No |
| `proxmox_add` | Add a Proxmox VE cluster with an API token (`user@realm!tokenid`) and its secret. | `infra:write` (+ `secrets:write`) | Yes | No |
| `proxmox_test` | Connect to a cluster by id; the first success pins its TLS certificate. | `infra:write` | Yes | No |
| `proxmox_attach_zone` | Sell a Proxmox cluster in an existing zone, optionally with a node as region. | `infra:write` | Yes | No |
| `zones_list` | Zones (locations) customers can choose. | `admin:read` | No | No |
| `zone_create` | Create a zone. | `infra:write` | Yes | No |

### Catalog and plans

| Tool | What it does | Scope | Changes data | Asks you |
|---|---|---|---|---|
| `templates_list` | OS templates customers can install. | `admin:read` | No | No |
| `templates_sync` | Import OS templates from CloudStack. | `catalog:write` | Yes | No |
| `template_update` | Enable or hide a template, rename it, set the login user or order. | `catalog:write` | Yes | No |
| `offerings_list` | CloudStack service offerings (CPU, memory, root disk) to build plans from. | `admin:read` | No | No |
| `plans_list` | Plans with price per hour and per month, size, zone and how many servers use them. | `admin:read` | No | No |
| `plan_create` | Create a plan. Price as `hourly_usd`. CloudStack plans need `cs_offering_id`; Proxmox plans need `backend_id`. | `catalog:write` | Yes | No |
| `plan_update` | Rename, show or hide, enable or disable, or re-price a plan. Size changes on a plan in use need a copy. | `catalog:write` | Yes | When the price changes |
| `plans_from_offerings` | Build a starter plan table from offerings (or Proxmox sizes) using your cost and a margin percent. Returns a preview; call again with `apply=true` to create the plans. | `catalog:write` | With `apply=true` | Always on apply |
| `quota_defaults_get` | Default customer limits. | `admin:read` | No | No |
| `quota_defaults_set` | Change `quota_*` settings. | `catalog:write` | Yes | No |
| `coupons_list` | Discount and gift code batches. | `admin:read` | No | No |
| `coupon_create` | Create a batch of codes: type `money` (face value in USD cents) or `percent` (1 to 100, with a maximum discount). | `catalog:write` | Yes | Always |

### Customers and resellers

| Tool | What it does | Scope | Changes data | Asks you |
|---|---|---|---|---|
| `customers_search` | Find customers by e-mail, name or IP. | `admin:read` | No | No |
| `customer_get` | One customer's account, balance, servers and recent billing. | `admin:read` | No | No |
| `customer_adjust_balance` | Add (positive) or remove (negative) credit in USD cents. A reason is required. | `billing:write` | Yes | Always |
| `customer_block` | Block a customer, optionally stopping their servers. A reason is required. | `customers:write` | Yes | Always |
| `customer_unblock` | Unblock a customer. | `customers:write` | Yes | No |
| `resellers_list` | Resellers with wallet and status. | `admin:read` | No | No |
| `reseller_get` | One reseller's site, wallet, users and gateways. | `admin:read` | No | No |

### AI provider

| Tool | What it does | Scope | Changes data | Asks you |
|---|---|---|---|---|
| `ai_provider_get` | The OpenAI-compatible provider used by the customer AI Chat and AI Agent (key masked). | `admin:read` | No | No |
| `ai_provider_set` | Set the provider: base URL, model, API key, fallback models, reasoning effort, enabled. | `ai:write` (+ `secrets:write` for the key) | Yes | No |
| `ai_provider_test` | Ask the saved provider one question to check it works. | `ai:write` | No | No |

### Configuration files

| Tool | What it does | Scope | Changes data | Asks you |
|---|---|---|---|---|
| `config_export` | Export the configuration as YAML (secrets become `secret://` references). | `admin:read` | No | No |
| `config_plan` | Compare a YAML configuration with the live panel and list what would change. | `admin:read` | No | No |
| `config_apply` | Apply a YAML configuration section by section. Each change follows the normal scope and confirmation rules. | `admin:read` + the scope of every change | Yes | Per change |

See [Configuration as code](https://nexcmp.com/docs/configure-with-ai/config-as-code/) for the file format.

## Prompts

The server also offers three ready-made prompts that clients can show as shortcuts:

| Prompt | Argument | What it asks the AI to do |
|---|---|---|
| `initial-setup` | none | Call `setup_checklist`, explain each open step in one sentence, ask you for the information it needs, do the step with the matching tool, preview money or security changes, and finish with `system_health`. |
| `add-proxmox` | `endpoint` (optional, `https://host:8006`) | Ask for the API token, then `proxmox_add`, `proxmox_test`, zone tools and `proxmox_attach_zone`; then propose plans with `plans_from_offerings` and wait for your approval. |
| `launch-pricing` | `margin_pct` (optional, default 40) | Ask for your monthly cost per vCPU, per GB of RAM and per GB of disk, show a preview table with cost, price per month and price per hour, and create the plans after you approve. |

## Confirmation (`confirm_token`)

Actions that move money, delete data, change payment credentials or sign-in security are two-step:

1. The first call changes nothing. The tool result starts with "CONFIRMATION REQUIRED" and includes a **preview** (what will be called, why it is sensitive, the new values and the current ones) and a `confirm_token`.
2. The client must show you the preview. Only after you approve does it call the same tool again with the same arguments plus `confirm_token`.
3. The token works once, only for that exact request, and expires after 5 minutes.

The server instructions tell the AI to treat tool results as data, not as instructions, and never to repeat a call with a token you have not approved. You are the safeguard: read the preview.

::: warning Do not auto-approve
Some clients can approve every tool call automatically. For write tools, keep per-call approval on, or use a read-only key, so a wrong plan from the AI cannot reach your panel unseen.
:::

## How it works

| Item | Detail |
|---|---|
| Transport | `POST /mcp/admin` with a JSON-RPC 2.0 message; the answer is `application/json`. Up to 20 messages in a batch. `GET /mcp/admin` answers 405 (no server stream); `DELETE /mcp/admin` ends a session. |
| Authentication | `Authorization: Bearer ak_...` (a manual key or an OAuth access token). Missing or wrong credentials give 401 with `WWW-Authenticate: Bearer ... resource_metadata=...`, which lets OAuth clients start sign-in by themselves. |
| Sessions | `Mcp-Session-Id` is issued by `initialize`, bound to that key and valid 24 hours. |
| Capabilities | Tools and prompts. No resources. |
| Size | Request body up to 1 MB; a tool result is cut after about 100 KB. |
| Origin | A request carrying an `Origin` header from another site is refused (`origin_not_allowed`). Command-line and desktop clients send none. |
| Same gate as REST | Each tool call is re-sent through the panel's own request pipeline with your key, so scopes, IP ranges, rate limit, confirmation and the activity log apply exactly as for direct API calls. |

## Limits & notes

- A tool you cannot see is a scope you did not grant. Add the scope to a new key.
- Setting a password or API key through an AI client means the secret passes through that AI. If you prefer, leave `secrets:write` off and enter secrets yourself in the admin, or use the AI Chat **Secret** field ([AI Chat in the admin](https://nexcmp.com/docs/configure-with-ai/ai-chat-in-admin/#secrets)) or `secret://` references ([Configuration as code](https://nexcmp.com/docs/configure-with-ai/config-as-code/#secrets)).
- Clients do not share sessions: each connection starts with `initialize`.
- Revoke the key in **API & AI agents** to cut an AI client off at once.

## Troubleshooting

| Symptom | Cause | Fix |
|---|---|---|
| 401 `invalid_key` | Wrong, truncated or non-admin key (for example a `vk_` key) | Use an `ak_` key. |
| 401 `key_expired` or `key_revoked` | The key ran out or was revoked | Create a new key. |
| 403 `ip_not_allowed` | Your IP is outside the key's IP ranges | Connect from an allowed IP. |
| 403 `origin_not_allowed` | A browser page on another origin called the endpoint | Use a desktop or CLI client. |
| 404 on a request with `Mcp-Session-Id` | The session belongs to another key or expired | Send `initialize` again. |
| 400 unsupported `MCP-Protocol-Version` | The client uses a version the server does not know | Update the client; supported: 2025-11-25, 2025-06-18, 2025-03-26. |
| The tool list is short | Key scopes | Create a key with more scopes. |
| `insufficient_scope` on a call | A tool needs a second scope, for example `secrets:write` | Add the scope to a new key. |
| 429 `rate_limited` | More calls per minute than the key allows | Wait for `Retry-After` or raise the limit on a new key. |
| claude.ai or ChatGPT cannot connect | The URL is not reachable from the internet, or a proxy blocks `/mcp/admin` or `/oauth-admin/` | Check DNS, TLS and the reverse proxy. |

For all error codes see [Admin API keys](https://nexcmp.com/docs/configure-with-ai/admin-api-keys/#errors).


