@xen-orchestra/mcp
v1.4.0
Published
MCP server for Xen Orchestra — allows AI assistants to query and manage XO infrastructure
Readme
@xen-orchestra/mcp
MCP server for Xen Orchestra — allows AI assistants to query and manage XO infrastructure
Install
Installation of the npm package:
npm install --global @xen-orchestra/mcpUsage
MCP (Model Context Protocol) is an open standard that lets AI assistants interact with external tools. This package provides an MCP server that connects AI assistants like Claude to your Xen Orchestra infrastructure, allowing natural language queries about your pools, hosts, and VMs.
Quick Start
Claude Desktop — Add to your config (~/.config/claude-desktop/config.json):
Using a token (recommended):
{
"mcpServers": {
"xo": {
"command": "npx",
"args": ["@xen-orchestra/mcp"],
"env": {
"XO_URL": "https://your-xo-server",
"XO_TOKEN": "your-token"
}
}
}
}Using username/password:
{
"mcpServers": {
"xo": {
"command": "npx",
"args": ["@xen-orchestra/mcp"],
"env": {
"XO_URL": "https://your-xo-server",
"XO_USERNAME": "[email protected]",
"XO_PASSWORD": "your-password"
}
}
}
}Claude Code:
# Using a token (recommended)
claude mcp add xo \
-e XO_URL=https://your-xo-server \
-e XO_TOKEN=your-token \
-- npx @xen-orchestra/mcp
# Using username/password
claude mcp add xo \
-e XO_URL=https://your-xo-server \
-e [email protected] \
-e XO_PASSWORD=your-password \
-- npx @xen-orchestra/mcpMultiple XO instances
To connect several XO servers at once, add one entry per instance with a different name (e.g., xo-production, xo-staging). Each entry runs its own process with its own credentials.
See the full documentation for configuration examples.
Prerequisites
- Node.js >= 20
- Xen Orchestra instance with REST API enabled
- An AI assistant that supports MCP (Claude Desktop, Claude Code, etc.)
Configuration
Two authentication modes are supported: token (recommended) or username/password.
| Variable | Required | Description |
| ----------------------- | ----------------------- | --------------------------------------------------------------------------------------------- |
| XO_URL | Yes | Xen Orchestra server URL (e.g., https://xo.example.com) |
| XO_TOKEN | If no username/password | Authentication token |
| XO_USERNAME | If no token | XO user with admin privileges |
| XO_PASSWORD | If no token | XO password |
| XO_MCP_ENABLE_ACTIONS | No | Set to 1 to expose write/destructive operations (see Write operations) |
To generate a token, go to the XO user page (/user) or run xo-cli create-token. If both XO_TOKEN and XO_USERNAME/XO_PASSWORD are set, token authentication takes priority.
Behind a corporate proxy
The MCP server respects HTTP_PROXY, HTTPS_PROXY, and NO_PROXY. Set them when your AI assistant uses an outbound proxy but your XOA is on the internal network.
| Variable | Effect |
| ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| HTTP_PROXY | Proxy URL for plain HTTP requests, e.g. http://proxy.corp.example:3128. Credentials are supported: http://user:pass@proxy:3128 (Basic auth only — NTLM/Kerberos proxies need a sidecar like cntlm). |
| HTTPS_PROXY | Proxy URL for HTTPS requests. Same format as HTTP_PROXY. The proxy itself is contacted over HTTP; https:// schemes are unusual. |
| NO_PROXY | Comma-separated list of hostnames that bypass the proxy. Three forms: exact host (xoa.internal.example.com), suffix wildcard (.example.com or *.example.com — matches subdomains, not the bare domain — list both if needed), or * alone to bypass everything. CIDR ranges are not supported by the underlying library. Lowercase no_proxy works too. |
Example for Claude Desktop, with a corporate proxy and a XOA on the internal network:
{
"mcpServers": {
"xo": {
"command": "npx",
"args": ["@xen-orchestra/mcp"],
"env": {
"XO_URL": "https://xoa.internal.example.com",
"XO_TOKEN": "your-token",
"HTTPS_PROXY": "http://proxy.corp.example:3128",
"NO_PROXY": "xoa.internal.example.com"
}
}
}
}Available Tools
At startup, the server fetches the OpenAPI spec from your XO instance (/rest/v0/docs/swagger.json) and generates one tool per resource domain. Domain and operation names come straight from the spec: the primary tag (tags[0]) becomes {tag}_query, and each operationId (e.g. GetVms, StartVm, HardShutdownVm) is used verbatim as the enum value. No hand-curated mapping — if the XO server adds a new endpoint, it appears automatically. Responses are rendered as markdown tables by the REST API itself (via ?markdown=true); the MCP layer only relays them.
Utility tools (always present):
| Tool | Description |
| ---------------------------- | --------------------------------------------------------------------------- |
| check_connection | Test the connection to the Xen Orchestra server |
| search_documentation | Search and retrieve Xen Orchestra documentation |
| get_infrastructure_summary | Aggregate pools/hosts/VMs across the infrastructure into a markdown summary |
Domain query tools (generated, read-only). Each tool accepts an operation enum (the OpenAPI operationId) plus optional id, filter, fields, limit. On a current xo-server the generated domains are:
- Compute:
pools_query,hosts_query,vms_query(covers VMs, templates, snapshots, controllers — tsoa groups them under thevmstag server-side) - Storage:
vdis_query(includes VDI snapshots),srs_query,vbds_query,pbds_query - Network:
networks_query,vifs_query,pifs_query - Backup:
backup_jobs_query,backup_logs_query,restore_logs_query,backup_repositories_query,backup_archives_query,schedules_query - Access:
users_query,groups_query,servers_query - Observability:
alarms_query,messages_query,events_query,tasks_query - System:
proxies_query,pgpus_query,pcis_query,sms_query,xoa_query - Misc:
docs_query
Stats endpoints (/…/stats) and binary download endpoints (.xva, .ova, .vhd, .raw, …) are deliberately not exposed — the payloads are unsuitable for LLM context. Query them via the REST API directly when needed.
Configuration
| Env var | Effect |
| ----------------------- | ---------------------------------------------------------------------------------------- |
| XO_MCP_ENABLE_ACTIONS | Set to 1 to expose write operations ({domain}_action tools). Default: read-only. |
| XO_MCP_DENY_LIST | Comma-separated list of operationIds to drop entirely, e.g. DeleteVm,HardShutdownVm. |
Write operations
Action tools (create/update/delete) are gated behind XO_MCP_ENABLE_ACTIONS=1 so the default surface stays read-only. When enabled, each domain gets a companion {domain}_action tool. Every action — from creating a snapshot to a DELETE or pools/emergency_shutdown — returns a preview and a one-shot confirm_token the assistant must send back within 5 minutes to execute.
Security model
x-mcp-exposure controls which REST endpoints this MCP server turns into LLM tools — it is not a REST API security boundary.
- The XO REST API never reads
x-mcp-exposure; the annotation lives only in the OpenAPI spec and is consumed solely by this server's tool generation. Every endpoint keeps its own RBAC/ACL authorization, which remains the actual access-control gate. - Any REST client with valid credentials — including a non-official or compromised MCP — can call a
deny-tagged endpoint directly. The annotation cannot stop it. - The benefit is a narrower threat model: limiting what an LLM can see and invoke (a guard against prompt injection and model mistakes), not authorization.
In short, x-mcp-exposure (and the mcp/require-mcp-expose lint rule that enforces it on the REST side) is a surface-control policy for the assistant's toolset. Rely on XO's RBAC/ACL for "who may do what".
Server-side kill-switch
An xo-server admin can globally block MCP by setting [mcp] enabled = false in the server config. With that flag set, the binary exits at startup with MCP disabled by admin on stderr and any active client receives 503 { "error": "mcp_disabled" } on its next request. It's not a bug — re-enable MCP in xo-server's config to recover.
Full documentation with tool parameters, examples, and troubleshooting: docs.xen-orchestra.com/mcp
Contributions
Contributions are very welcomed, either on the documentation or on the code.
You may:
- report any issue you've encountered;
- fork and create a pull request.
