Content
# xiaozhi-mini
> Lightweight MCP Aggregator Bridge: xiaozhi-mini ↔ N MCP Servers, Memory Usage ~110MB (Default Configuration)
Alternative to MCPHub (~800MB), dedicated to bridging xiaozhi's backend `wss://api.xiaozhi.me/mcp/` with multiple standard MCP Servers (streamable-http / stdio). The tool automatically aggregates, prefixes routes, and maintains stable connections without disconnections.
## ✨ Features
- 🪶 **Ultra-Lightweight**: Default configuration (HA + memory + tavily) approximately 110MB, only 1/7 of MCPHub
- 🔌 **Dual Protocols**: streamable-http (automatically parses SSE) + stdio (subprocess)
- 📦 **Tool Aggregation**: Multiple upstream tools automatically prefix and merge, pushing to xiaozhi
- 💓 **Stable Connection**: Correctly responds to xiaozhi server JSON-RPC `ping`, connection no longer 1006
- 🔁 **Triple Reconnection**: Fast reconnection → Infinite reconnection → Sleep mode, seamless disconnection
- 📦 **Payload Control**: Automatically truncates tool descriptions, payload controlled within 40KB to avoid 1009
- 🔑 **Environment Variable Interpolation**: Configuration supports `${VAR}` referencing `.env` variables, secrets not hardcoded
- 📝 **YAML Configuration**: Adding MCP tools only requires modifying config.yaml + restart
- 🔒 **Secret Separation**: Token independent in `.env` file, avoiding miscommit
- 🔒 **Log Desensitization**: Automatically masks URL tokens to prevent secret leakage to logs
- 🔗 **Multi-Access Support**: One instance connects multiple xiaozhi tokens, tool list shared
- 🔄 **Upstream Self-Healing**: Upstream initialization failure automatically retries every 60 seconds, no need for manual restart
## 🚀 Quick Start
```bash
git clone https://github.com/zyunling/xiaozhi-mini.git
cd xiaozhi-mini
```
1. Configure Secrets
```bash
cp .env.example .env
# Edit .env, paste the complete wss link, HA complete address, and Tavily Key from xiaozhi backend
# XIAOZHI_URL=wss://api.xiaozhi.me/mcp/?token=your_token
# HA_MCP_URL=http://HA_IP:PORT/your_HA_long-term_token
# TAVILY_API_KEY=your_tavily_key
```
2. (Optional) Edit `config.yaml` to adjust upstreams, four are enabled by default: HA / bing-search / memory / tavily
```bash
vim config.yaml
```
3. Build & Start
```bash
docker compose up -d --build
```
4. Check Logs
```bash
docker logs -f xiaozhi-mini
```
Normal startup logs:
```text
🚀 xiaozhi-mini v2.6.0 starting...
✅ [ha] streamable-http: 14 tools
✅ [memory] stdio: 9 tools (PID: 16)
✅ [tavily] streamable-http: 5 tools
📦 3 upstreams, 28 tools
🔗 Preparing to start 1 xiaozhi connection
[xiaozhi-1] 🔗 Connecting xiaozhi: wss://api.xiaozhi.me/mcp/?token=***
[xiaozhi-1] 🟢 xiaozhi WebSocket connected
[xiaozhi-1] 📢 Notified xiaozhi tool list update
[xiaozhi-1] 📤 Pushed 28 tools to xiaozhi (payload: 32KB)
```
## ⚙️ Configuration Description
### `.env` — Secret and Address Configuration
All tokens / URLs are placed in `.env` to avoid different user addresses and modifications to `config.yaml`. Directly paste the complete link from the official backend.
```env
# ── xiaozhi MCP Connection ──────────────────────────────────────────
# Copy the complete wss link from xiaozhi official backend, paste it in
# Single access:
XIAOZHI_URL=wss://api.xiaozhi.me/mcp/?token=your_token
# Multiple access (optional, uncomment and enable in config.yaml):
# XIAOZHI_URL_1=wss://api.xiaozhi.me/mcp/?token=your_first_token
# XIAOZHI_URL_2=wss://api.xiaozhi.me/mcp/?token=your_second_token
# ── Home Assistant MCP ─────────────────────────────────────
# Fill in your HA complete MCP address (including long-term access token)
HA_MCP_URL=http://192.168.1.100:9583/your_HA_long-term_token
# ── Tavily Search ────────────────────────────────────────────
# Get API Key from https://tavily.com
TAVILY_API_KEY=your_tavily_key
```
### `config.yaml` — Function Configuration
By default, HA, memory, and tavily are enabled. All URLs/Keys are referenced from `.env` using `${VAR}`. This file generally does not need modification. For more tools (like Bing search, file system), uncomment the corresponding section.
```yaml
xiaozhi:
# Single access: complete wss URL from .env
url: "${XIAOZHI_URL}"
# Multiple access (optional, uncomment to connect multiple xiaozhi):
# urls:
# - "${XIAOZHI_URL_1}"
# - "${XIAOZHI_URL_2}"
upstreams:
ha:
type: streamable-http
url: "${HA_MCP_URL}"
headers:
Accept: "application/json, text/event-stream"
memory:
type: stdio
command: "npx"
args: ["-y", "@modelcontextprotocol/server-memory"]
env:
MEMORY_FILE_PATH: /workspaces/memory.jsonl
tavily:
type: streamable-http
url: "https://mcp.tavily.com/mcp/?tavilyApiKey=${TAVILY_API_KEY}"
headers:
Accept: "application/json, text/event-stream"
# Optional: Bing Chinese search (stdio, memory-saving default off, Tavily available)
# bing-search:
# type: stdio
# command: "npx"
# args: ["-y", "bing-cn-mcp"]
```
| Field | Description |
|------|-------------|
| `xiaozhi.url` | Complete wss URL for single access, injected from `.env` `XIAOZHI_URL` |
| `xiaozhi.urls` | Multiple access array, each item is a complete wss URL (e.g., `${XIAOZHI_URL_1}`), one instance connects multiple xiaozhi |
| `xiaozhi.reconnect_delay` | Fast reconnection delay (ms), default 2000 |
| `xiaozhi.aggressive_reconnect` | Fast reconnection mode (default true), disable for exponential backoff → infinite reconnection → sleep |
| `xiaozhi.max_quick_reconnect` | Maximum fast reconnection attempts (default 10), exceeded enters infinite reconnection |
| `xiaozhi.infinite_reconnect_delay` | Infinite reconnection interval (ms), default 30 minutes |
| `xiaozhi.max_infinite_retries` | Maximum infinite reconnection attempts (default 48, about 24 hours) |
| `xiaozhi.sleep_threshold` | Failure count before entering sleep mode (default 12) |
| `xiaozhi.sleep_interval` | Sleep interval (ms), default 2 hours |
| `xiaozhi.message_queue_max` | Cached message count limit during disconnection (default 100) |
| `upstreams.*.type` | `streamable-http` or `stdio` |
| `upstreams.*.url` | HTTP type fills endpoint URL, supports `${VAR}` environment variable interpolation |
| `upstreams.*.headers` | HTTP request headers, supports `${VAR}` environment variable interpolation |
| `upstreams.*.command/args` | stdio type fills startup command |
| `upstreams.*.env` | Optional, injects environment variables into subprocess |
> 💡 **About Connection Keep-Alive**: xiaozhi server uses JSON-RPC `ping` for application-layer keep-alive detection, client must reply `pong` (empty result), otherwise server disconnects after 30 seconds (1006). This project correctly implements this handshake, maintaining a stable connection.
> ⚠️ **About Payload Size**: xiaozhi cloud endpoint has a limit on WebSocket message size (about 40KB), exceeding triggers 1009 disconnection. This project automatically truncates tool descriptions and reduces pushed tool count when approaching the limit.
## ➕ Adding MCP Server
Edit `config.yaml`, add a section under `upstreams:` (refer to file examples), then:
```bash
docker compose restart
```
xiaozhi will automatically receive new tools (with prefixes like `tavily_search`, `filesystem_read`).
If tool count is too high, causing payload exceedance, logs will prompt:
```
⚠️ payload approaching limit (40000), truncated to X tools
```
## 📊 Comparison with MCPHub
| Metric | MCPHub | xiaozhi-mini |
|------|--------|---------------|
| Memory | ~800MB | **~110MB (default 3 upstreams)** |
| Dependencies | Node + pg + pgvector + React | Node + ws + yaml |
| Tool List | pg query | Memory cache, zero IO |
| Keep-Alive Strategy | JSON-RPC ping response | JSON-RPC ping response |
| Reconnection | Fast reconnection → Infinite reconnection → Sleep | Fast reconnection → Infinite reconnection → Sleep |
| Payload Control | None | Automatic truncation, 40KB limit |
| Multi-Access | Not supported | One instance connects multiple xiaozhi (`xiaozhi.urls`) |
| Upstream Self-Healing | None | Automatic retry every 60 seconds after failure |
| Secret Management | Written in configuration file | Independent `.env` + `${VAR}` interpolation, complete URL paste |
## 🗺️ Roadmap
- [x] Support multiple xiaozhi endpoints concurrently (v2.5+, `xiaozhi.urls`)
- [x] Upstream automatic reconnection (v2.5+, self-heals every 60 seconds)
- [x] Secret and configuration separation, complete URL in `.env` (v2.6)
- [ ] Optional Web UI (default off, memory-saving)
## 📄 License
MIT