Deployment¶
mcp2xiaozhi is a long-lived relay process between the Xiaozhi server and
your MCP server(s). This page explains why it must keep running and how to run
it persistently on any platform.
Why persistence matters¶
The bridge is the middle of a three-part connection — it is not a one-shot tool:
Xiaozhi server ◄── WebSocket ── mcp2xiaozhi bridge ─── SSE/HTTP ──► remote MCP
(api.xiaozhi.me) (your host) (modelscope…)
│
└── stdio ──► local MCP (optional)
- The bridge initiates both connections (it is a client on both sides).
- The Xiaozhi server never connects to your MCP server directly — every tool call flows through the bridge.
- If the bridge process dies, both connections drop and Xiaozhi tool calls fail until it restarts.
So: the host running mcp2xiaozhi must stay online. If your MCP server is
remote (SSE / StreamableHTTP, like the modelscope calculator), you can run the
bridge on any always-on machine — a VPS, a NAS, a Raspberry Pi, a Docker
container — and turn your laptop off. If your MCP server is a local stdio
process, the bridge and that process must run on the same host, so that host
must stay up.
The good news: the bridge auto-reconnects with exponential backoff, so transient network drops heal themselves. You only need something to keep the process alive.
Options at a glance¶
| Method | Platform | Boot | Logout | Best for |
|---|---|---|---|---|
| Foreground | all | ❌ | ❌ | quick testing |
nohup / tmux / screen |
Linux/macOS | ❌ | ✅ | temporary, dev |
| systemd | Linux | ✅ | ✅ | production (Linux) |
| Docker Compose | all | ✅ | ✅ | production (any) |
| NSSM service | Windows | ✅ | ✅ | production (Windows) |
Configuration & secrets (all methods)¶
Regardless of how you run the bridge, the inputs are the same:
mcp_config.json— describes your servers (see Configuration).MCP_ENDPOINT(orMCP_ENDPOINT_<NAME>) — the Xiaozhi WebSocket URL, which contains a token.
Treat the endpoint like a password
The MCP_ENDPOINT URL contains a JWT token — anyone who has it can consume
your Xiaozhi MCP quota.
- Put it in an env file or secret manager — not in the config JSON, and never commit it to a public repo.
mcp2xiaozhiredacts it in its own logs (?<redacted>), but third-party libraries under--log-level DEBUGmay print the full URL. Use the defaultINFOlevel in production.- Rotate the token from the Xiaozhi console if it ever leaks.
Linux: systemd (recommended)¶
systemd is the native way to run a long-lived service on Linux: it starts on
boot, restarts on crash, and captures logs via journald.
- Install the package system-wide:
Note the binary path with which mcp2xiaozhi (e.g. /usr/local/bin/mcp2xiaozhi).
- Put the config and endpoint where the service can read them:
sudo install -d -m 0755 /etc/mcp2xiaozhi
sudo nano /etc/mcp2xiaozhi/mcp_config.json # paste your config
sudo nano /etc/mcp2xiaozhi.env # MCP_ENDPOINT=wss://api.xiaozhi.me/mcp/?token=...
sudo chmod 600 /etc/mcp2xiaozhi.env # protect the token
- Create
/etc/systemd/system/mcp2xiaozhi.service:
[Unit]
Description=mcp2xiaozhi MCP-to-Xiaozhi bridge
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
User=mcp2xiaozhi
WorkingDirectory=/etc/mcp2xiaozhi
EnvironmentFile=/etc/mcp2xiaozhi.env
Environment=MCP_CONFIG=/etc/mcp2xiaozhi/mcp_config.json
ExecStart=/usr/local/bin/mcp2xiaozhi run --all
Restart=on-failure
RestartSec=5s
# Hardening
NoNewPrivileges=true
ProtectSystem=strict
ProtectHome=true
PrivateTmp=true
ReadWritePaths=/etc/mcp2xiaozhi
[Install]
WantedBy=multi-user.target
- Enable and start:
sudo systemctl daemon-reload
sudo systemctl enable --now mcp2xiaozhi
sudo systemctl status mcp2xiaozhi # verify "active (running)"
sudo journalctl -u mcp2xiaozhi -f # live logs
Docker / Docker Compose (recommended, any platform)¶
The repo ships a Dockerfile
and docker-compose.yml
for one-command persistent deployment on any host with Docker — including a
small VPS.
- Put
mcp_config.jsonand a.envfile (withMCP_ENDPOINT=...) next todocker-compose.yml. - Start:
restart: unless-stopped keeps it alive across crashes and reboots.
For local stdio MCP servers, either run them in linked containers (reference by
service name) or keep them on the host and use host.docker.internal.
Windows: NSSM service (recommended)¶
NSSM wraps any executable — including mcp2xiaozhi.exe —
as a native Windows service that starts on boot and restarts on failure.
# Install NSSM (winget or download from nssm.cc)
winget install nssm
# Register the bridge as a service
nssm install mcp2xiaozhi "C:\Path\To\mcp2xiaozhi.exe"
nssm set mcp2xiaozhi AppDirectory "C:\Path\To\config\dir"
nssm set mcp2xiaozhi AppParameters "run --all"
nssm set mcp2xiaozhi AppEnvironmentExtra "MCP_ENDPOINT=wss://api.xiaozhi.me/mcp/?token=..."
nssm set mcp2xiaozhi Start SERVICE_AUTO_START
nssm set mcp2xiaozhi AppStdout "C:\Path\To\logs\out.log"
nssm set mcp2xiaozhi AppStderr "C:\Path\To\logs\err.log"
nssm set mcp2xiaozhi AppRotateFiles 1
Start-Service mcp2xiaozhi
Manage with Start-Service / Stop-Service / Restart-Service, or via
services.msc.
Temporary / quick runs¶
Operations¶
Logs¶
- systemd:
journalctl -u mcp2xiaozhi -f - Docker:
docker compose logs -f - NSSM: the log files you configured (
AppStdout/AppStderr) - Default level is
INFO. Use--log-level DEBUGonly when troubleshooting, and rotate the token afterwards (third-party libs may print the endpoint).
Reconnection¶
The bridge reconnects automatically with exponential backoff on either side dropping — clean close → short retry; error → growing backoff up to 600 s. Usually you don't need to intervene.
Upgrading¶
# pip
pip install -U mcp2xiaozhi
# uv tool
uv tool upgrade mcp2xiaozhi
# Docker
docker compose pull && docker compose up -d --build
Then restart the service (systemctl restart mcp2xiaozhi, etc.).
Running multiple servers¶
Give each server its own endpoint (see
Configuration → Endpoint resolution).
With run --all, the bridge opens one WebSocket per server.
Health check (optional)¶
There is no HTTP health endpoint — the bridge is a pure relay. To monitor,
check that the process is running (systemctl is-active, docker inspect,
Get-Service) and that the logs show WebSocket connected. The keepalive
ping/pong roughly every 20 s is a good liveness signal.