nabaztag-mcp
by maubau
README.md
# nabaztag-ai π°
[](https://github.com/maubau/nabaztag-ai/actions/workflows/ci.yml)
> AI brain for the original Nabaztag rabbit β LLM tool-use embodiment, voice pipeline, MCP server. Physical AI, 2006 edition.
## What & why
In 2006 the Nabaztag:tag was the first consumer connected device β a Wi-Fi rabbit that read the news and wiggled its ears. Twenty years later, this project revives a **stock, unopened** Nabaztag:tag as the body of a modern AI assistant: wake word β speech-to-text β an LLM with tool use β text-to-speech, played through the rabbit's own speaker, with the LLM deciding ear positions and LED moods itself.
There is also a **full-duplex mode**: the wake word opens a continuous conversation and you can talk over the rabbit mid-sentence. That needs playback which can be cancelled β the 2006 decoder cannot be β so it routes audio through the reSpeaker instead, whose on-chip echo cancellation was measured before being relied on. The rabbit keeps the ears and the lights.
The rabbit stays 100% original. It talks to a self-hosted [OpenJabNab](https://github.com/OpenJabNab/OpenJabNab) server; modern audio input comes from an external reSpeaker 4-mic array; the brain runs on a UDOO Bolt (any Linux PC or SBC works) β closing a 20-year loop between the first consumer connected device and modern edge AI.
Full spec: [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md).
## Hardware BOM
| Item | Role | ~Price |
| :--- | :--- | :--- |
| Nabaztag:tag (V2), stock | Body: speaker, ears, LEDs, RFID, button | ~β¬70 (second-hand) |
| reSpeaker Flex XVF3800 Circular-4 | Ears-in: 4-mic array, beamforming, DoA | ~β¬50 |
| UDOO Bolt (or any Linux PC/SBC) | Brain: OJN server, rabbit-brain, MCP | you probably have one |
| *(usually nothing)* β the Bolt's own M.2 Wi-Fi module runs the AP | Dedicated legacy WPA-TKIP AP (Β§4.1) | β |
## Quickstart (no hardware needed)
```bash
git clone https://github.com/<you>/nabaztag-ai && cd nabaztag-ai
python -m venv .venv && source .venv/bin/activate
pip install -e "brain[dev]"
pytest # runs against the mock-OJN simulator
python -m rabbit_brain.body.demo --mock-ojn # drive a simulated rabbit
```
With real hardware, start from **Gate S0** (the rabbit's legacy Wi-Fi segment β see [docs/ARCHITECTURE.md Β§4.1](docs/ARCHITECTURE.md) and [ojn/](ojn/)).
On the Bolt, the audio-in pipeline (reSpeaker capture, openWakeWord, silero-vad, DoA) needs the extras and the XVF3800 udev rule:
```bash
sudo apt install libportaudio2 # sounddevice's runtime library
brain/scripts/install-audio.sh # add --stt-local for stt_profile: local
sudo install -m 644 brain/udev/70-respeaker-flex.rules /etc/udev/rules.d/
sudo udevadm control --reload && sudo udevadm trigger
python -m rabbit_brain.audio.demo --config config.yaml --mock-ojn # mic smoke test
```
`config.yaml` is gitignored, so `git pull` never updates it β a config created from an older example can silently keep stale settings (e.g. a missing `deepgram.endpointing`). Check it against the current shape after pulling:
```bash
brain/scripts/config-doctor.py config.yaml # report drift
brain/scripts/config-doctor.py config.yaml --fix # rewrite present-but-stale keys in place
```
(The script exists because openWakeWord's Linux dependency on tflite-runtime has no Python 3.12 wheel; we only use its ONNX backend, so it is installed `--no-deps` on top of the `brain[audio]` extra. CI runs the same script on Ubuntu 24.04/3.12.)
### Full voice runtime
One persistent process owns the whole conversation loop (one BodyController, one Mp3Server on :8090, one EventListener on :8091):
```bash
python -m rabbit_brain.runtime --config config.yaml
```
Pipeline: reSpeaker β openWakeWord β **Deepgram Flux** (recognition *and* end-of-turn detection in one pass, `stt_profile: flux`) β **OpenAI Responses** (`gpt-5.4-mini`) β **Piper** (self-hosted, `it_IT-paola-medium` / `en_GB-alba-medium`, voice routed by the STT-detected language; Deepgram Aura as automatic fallback) β local MP3 β OpenJabNab β Nabaztag speaker. Set `conversation.mode: realtime` (with `audio_out.backend: local`) for the full-duplex path instead. `stt_profile: cloud` switches back to nova-3 + a local silero VAD window, which is what Flux replaces. The runtime loads `.env` automatically (shell environment wins; `--env-file ''` disables). Needs `OPENAI_API_KEY`, `DEEPGRAM_API_KEY` and the OJN vars in `.env` (values never logged). **Do not run the runtime and the MCP server at the same time** β both bind :8090/:8091 and each would create its own BodyController. Pick one per session.
Rabbit-facing MP3 delivery goes through **Apache**, not the built-in server: the MTL decoder ignores aiohttp-served audio (hardware finding). Install the alias once (`ojn/apache/brain-audio.conf.example`) and set `NABAZTAG_MP3_SERVE_HTTP=0` + `NABAZTAG_MP3_BASE_URL=http://192.168.66.1/brain-audio` in `.env`. A plain `chmod` on the audio dir is **not** enough (403: www-data can't traverse your home directory) β the conf example includes the `setfacl` commands that hardware-confirmed fix it.
## Architecture
```
Nabaztag:tag ββViolet protoββΊ OpenJabNab (self-hosted) ββRESTββΊ rabbit-brain
stock, WPA-TKIP segment on the Bolt wakeβVADβSTTβLLM(tools)βTTS
β
reSpeaker XVF3800 ββUSB audio + DoAβββββββββββββββββββββββΊ BodyController (arbiter)
β²
nabaztag-mcp βββ β Claude Desktop/Code
```
All body output funnels through the `BodyController` (single owner of the body, priority queue); bodies are swappable behind the `BodyAdapter` protocol.
## Roadmap
- **v1** β Nabaztag:tag via OpenJabNab (this repo, phases S0 β 4)
- **P1** β `ReachyMiniAdapter`: same brain, different body (comparison protocol)
- **P2** β `VentunoQLocalProfile`: fully local STT/TTS/LLM on Arduino VENTUNO Q
## License
Apache-2.0 (root, covers `brain/`, `mcp/`, `demos/`, docs, configs) β with one exception:
[ojn/plugin_events/](ojn/plugin_events/) is an OpenJabNab plugin (webhook egress for
button/RFID events) and, as a derivative work of OpenJabNab, is licensed under **OpenJabNab's
GPL v2** (its LICENSE is OJN's COPYING, copied verbatim). It is cleanly separated: no code is
shared with `brain/` or `mcp/` β the brain talks to OJN only over HTTP. The choreography
plugin foreseen in the architecture turned out to be unnecessary (Gate G0).
*Nabaztag is a trademark of its respective owner; this is an independent community project, not affiliated with Violet/Aldebaran.*
This server cannot be deployed
Maintenance
ActivitySlowing
ResponsivenessNo issues