Omnissa Horizon MCP Server
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@Omnissa Horizon MCP ServerRestart Jim's hung desktop and check if the Sales pool is healthy."
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
Omnissa Horizon MCP Server (Python / FastMCP)
A Model Context Protocol server exposing
support/helpdesk operations for an Omnissa Horizon Server 2506 environment,
built with Python FastMCP. It targets the Horizon REST API documented at
https://developer.omnissa.com/horizon-apis/horizon-server/versions/2506/.
Works both as a direct
python -mprocess and as a Docker container (see "Running via Docker" below).
Features (helpdesk / support)
User & session management
find_user_session/list_sessions— find a user's desktop/session(s)restart_user_desktop/restart_session— reboot a user's desktoplogoff_user/logoff_session— log a user off (graceful or forced)disconnect_session— detach the display without logging offsend_session_message— popup a message to a user's session
Utilization (processes / CPU / memory / network)
user_processes/session_processes— running processes + CPU/mem/disk %user_cpu_memory/session_utilization— CPU, memory, disk & latency statssession_network_performance— estimated bandwidth (kbps), round-trip latency and packet loss (PCoIP/BLAST) to diagnose slow/thin sessionsend_session_process— terminate a runaway process
Pool & environment health
list_desktop_pools/desktop_pool_status— pool health report (total / available / in-use / errors), view model described belowlist_machines+restart/reset/shutdown/enter-maintenance/exit-maintenancemachinemonitor_connection_servers/monitor_gateways/monitor_farms/monitor_rds_servers/monitor_ad_domains/monitor_event_databasehorizon_ping/horizon_config_summary— connectivity & config
The MCP exposes the most common support/helpdesk actions so an assistant can "restart Jim's hung desktop", "check Bob's CPU/memory", or "is the Sales pool healthy?" directly against Horizon.
Related MCP server: vcf-mcp-sddc-vc
Geometry & requirements
Python 3.9+ (tested on 3.12)
Install:
pip install -r requirements.txt
Configuration (secure service-account credentials)
Credentials are never hard-coded. Provide them via a config JSON file or environment variables (precedence: explicit args > env > file).
Option A — interactive wizard (recommended)
cd omnissa-horizon-mcp
python -m omnissa_horizon_mcp config init --config ./horizon.jsonThis prompts for the Connection Server URL, AD domain, service account username
and password (password entered without echoing), and writes the file with
0600 owner-only permissions. Example result:
{
"base_url": "https://horizon.example.com",
"domain": "EXAMPLE",
"username": "svc-horizon-mcp",
"password": "…",
"verify_ssl": true
}Option B — environment variables
export HORIZON_BASE_URL="https://horizon.example.com"
export HORIZON_DOMAIN="EXAMPLE"
export HORIZON_USERNAME="svc-horizon-mcp"
export HORIZON_PASSWORD="…"
export HORIZON_VERIFY_SSL="false" # default true; set false for test CAs
export HORIZON_TIMEOUT="30"
# optional, to point at a config file:
export HORIZON_CONFIG_FILE="./horizon.json"A .env file is also honored if python-dotenv is installed and loaded (see
.env.example). See omnissa_horizon_mcp/config.py for all HORIZON_* vars.
Verify credentials before connecting to an MCP client
python -m omnissa_horizon_mcp ping --config ./horizon.jsonRunning the MCP server
# stdio transport (used by Claude Desktop / MCP clients)
python -m omnissa_horizon_mcp --config ./horizon.json # note: --config passthroughBy default the server resolves config from
HORIZON_CONFIG_FILE/ env vars. To point at a specific file, setexport HORIZON_CONFIG_FILE=./horizon.jsonbefore launching, and add a.envload if desired.
Example MCP client config (Claude Desktop claude_desktop_config.json)
{
"mcpServers": {
"omnissa-horizon": {
"command": "python",
"args": ["-m", "omnissa_horizon_mcp"],
"env": {
"HORIZON_CONFIG_FILE": "/abs/path/to/horizon.json"
}
}
}
}How desktop_pool_status computes "available" vs "used"
It combines three sources:
/inventory/v1/desktop-pools— pool metadata (id, name, type, source, enabled)/inventory/v1/machines— machines grouped by pool; non-AVAILABLE/erroring machines are excluded from availability/inventory/v1/sessions+/monitor/desktops— in-use session count and the pool's aggregated monitor status (OK/ERROR/…)
Result summary: total_machines, total_in_use_sessions, total_errors, plus
per-pool machines_by_state, in_use_sessions, available_estimate and any
cloning/state errors.
Horizon 2506 API notes
A few Horizon REST details that this server handles explicitly (verified against the 2506 OpenAPI spec and the Horizon Server REST Pagination, Filter and Sorting Guide):
Inventory filters use a JSON filter object, not the
field 'value'string form.list_sessions/list_machinesbuild e.g.{"type":"Equals","name":"desktop_pool_id","value":"…"}, chained with an"And"wrapper for multiple clauses. Field/operator choices:user_name(Contains),desktop_pool_id(Equals),session_state(Equals) for sessions;name(Contains),desktop_pool_id(Equals),state(Equals) for machines.Sessions are listed from
/inventory/v7/sessions(notv1): the baseSessionInfomodel returned by/inventory/v1/sessionshas nouser_namefield (only the rawuser_idSID), so it cannot be filtered or displayed by username.user_nameis added from the versioned session models (v4+); v7 is the current 2506 model.find_user_sessiontherefore filters server-side onuser_name(Equals/Contains) and falls back to an unfiltered client-side scan. If sessions are visible but none expose auser_name, the tool raises a clear error about the likely missing service-account privilege instead of silently reporting zero matches.Helpdesk performance calls use
/helpdesk/v1/...withsession_id(historical-data,process,display-protocol, andremote-process/action/end-remote-process). The/helpdesk/v2/...endpoints takeinternal_session_id, a different internal identifier that rejects an inventory session id (helpdesk.session.find.error).
Security notes
Passwords are never logged;
redacted()masks them.All requests use
verify_ssl(on by default). Set false only for self-signed/test environments.Mutating operations (
restart,logoff,reset,end_session_process) require the appropriate Horizon privileges; check the Connection Server roles of the service account.
Running via Docker
The project ships a Dockerfile, docker-compose.yml and .dockerignore. Run
as a non-root user; the app runs over stdio by default (embedded MCP client)
and can also run as a long-lived HTTP/SSE daemon.
Build
docker build -t omnissa-horizon-mcp .Option A — stdio (embedded MCP client, e.g. Claude Desktop)
Create your config file with config init, then run the container
interactively so stdio stays attached; pass credentials via env or a
mounted config file:
docker run -i --rm \
-e HORIZON_CONFIG_FILE=/config/horizon.json \
-v "$(pwd)/horizon.json:/config/horizon.json:ro" \
omnissa-horizon-mcpPoint the MCP client at the container, e.g. Claude Desktop:
{
"mcpServers": {
"omnissa-horizon": {
"command": "docker",
"args": ["run", "-i", "--rm", "-e", "HORIZON_CONFIG_FILE=/config/horizon.json",
"-v", "/abs/path/horizon.json:/config/horizon.json:ro",
"omnissa-horizon-mcp"]
}
}
}Or pass secrets via -e instead of mounting a file:
docker run -i --rm \
-e HORIZON_BASE_URL=https://horizon.example.com \
-e HORIZON_DOMAIN=EXAMPLE \
-e HORIZON_USERNAME=svc-horizon-mcp \
-e HORIZON_PASSWORD=*** \
-e HORIZON_VERIFY_SSL=false \
omnissa-horizon-mcpFile-permission note: the container runs as an unprivileged user, so a mounted
horizon.jsonmust be world/group-readable for it to open, or it will fail on permissions. Prefer env vars, orchownthe file to the container UID, if you hitPermission denied.
Option B — network daemon (HTTP / SSE / streamable-http)
docker run -d --name horizon-mcp -p 8000:8000 \
-e HORIZON_CONFIG_FILE=/config/horizon.json \
-v "$(pwd)/horizon.json:/config/horizon.json:ro" \
omnissa-horizon-mcp --transport http --host 0.0.0.0 --port 8000or with Compose (bundled):
docker compose up -d --buildThen connect an MCP client that supports HTTP/SSE to
http://<host>:8000/mcp. Available transports: stdio (default), sse,
streamable-http, http.
Verify the daemon is up
curl -i http://localhost:8000/health # HTTP 200 + JSON status
curl -i http://localhost:8000/healthz # alias
curl -i http://localhost:8000/mcp # expect JSON-RPC "missing session ID"A health check that requires no Horizon credentials and does not block on a
login is exposed at /health (alias /healthz) whenever the daemon transport
(http / sse / streamable-http) is running:
{"status":"ok","service":"omnissa-horizon-mcp","version":"0.1.0","uptime_seconds":123,"healthy":true}The Docker HEALTHCHECK and the bundled docker-compose.yml healthcheck both
hit /health automatically.
Authentication (bearer token)
The network transport is gated behind a bearer token when configured. With
a token set, every endpoint except /health and /healthz requires
Authorization: Bearer <token> and returns 401 otherwise.
Set it via the env var HORIZON_MCP_AUTH_TOKEN or the --token flag. Generate
a strong one:
export HORIZON_MCP_AUTH_TOKEN="$(openssl rand -hex 32)"docker run -d --name horizon-mcp -p 8000:8000 \
-e HORIZON_CONFIG_FILE=/config/horizon.json \
-e HORIZON_MCP_AUTH_TOKEN="$HORIZON_MCP_AUTH_TOKEN" \
-v "$(pwd)/horizon.json:/config/horizon.json:ro" \
omnissa-horizon-mcp --transport http --host 0.0.0.0 --port 8000Clients must then send the header on every request:
curl -H "Authorization: Bearer $HORIZON_MCP_AUTH_TOKEN" http://host:8000/mcpNotes:
When
HORIZON_MCP_AUTH_TOKENis unset/empty, auth is disabled (open), preserving the default behaviour. The bundleddocker-compose.ymlrequires the variable so daemon deployments are secured by default.If the token is ever compromised, rotate it and redeploy — the running process does not cache it across restarts.
/healthstays public so load balancers / uptime monitors can probe liveness without a secret.
Project layout
omnissa-horizon-mcp/
├── README.md
├── requirements.txt
├── Dockerfile
├── docker-compose.yml
├── .dockerignore
├── .env.example
└── omnissa_horizon_mcp/
├── __init__.py
├── __main__.py # python -m entrypoint
├── server.py # FastMCP app + CLI (config init / ping / run)
├── config.py # secure config & credential resolution
├── client.py # Horizon REST client (auth, pagination, errors)
└── tools/
├── __init__.py # registers all tool modules
├── _common.py # user-session helpers
├── connection_tools.py
├── session_tools.py
├── utilization_tools.py
├── pool_tools.py
└── machine_tools.pyThis server cannot be deployed
Maintenance
Related MCP Connectors
Deploy, monitor, and manage your OpenClaw AI assistants via natural language.
Provides capabilities that let LLM agents perform a range of infrastructure management tasks.
An AI concierge that turns static forms into adaptive AI conversations. From any MCP client.
Interact with your Google Cloud Composer resources using natural language commands.
Related MCP Servers
- AlicenseBqualityDmaintenanceEnables AI assistants to manage and monitor VergeOS virtualization platforms through natural language, including VM operations, network management, tenant administration, and cluster monitoring.21MIT
- AlicenseNot gradedqualityCmaintenanceEnables natural language interaction with VMware SDDC Manager and vCenter APIs through MCP tools, allowing users to query workload domains, VMs, clusters, and more.MIT
- AlicenseAqualityBmaintenanceMCP server for managing VMware Horizon VDI environments via the Horizon 8 REST API, enabling AI agents to monitor sessions, manage pools, and perform operations like logoff, reset, and image push with safety previews and audit logging.2797 PyPI1MIT
- AlicenseNot gradedqualityCmaintenanceEnables AI clients to query SD-WAN fabric health, devices, tunnels, BFD sessions, OMP peers, alarms, policies, and configuration state via natural language, with deterministic correlation and diagnostics for incident assessment.Apache 2.0