-
Notifications
You must be signed in to change notification settings - Fork 1
MCP Server
The Companion can expose your display as a set of MCP tools — so an LLM (Claude, an agent, an IDE assistant) can drive the wall in plain language, and, just as importantly, read it back.
This is a companion feature. It's a standalone HTTP server and has nothing to do with Home Assistant — you don't need HA to use it.
The difference from a webhook or a REST call is that the model isn't following a script you wrote. It can look at the board, decide the message won't fit, shorten it, pick a transition, show it for 30 seconds, and put back what was playing — because each of those is a tool it can choose to call.
| Where you run the companion | How |
|---|---|
| Home Assistant App | Configuration tab → mcp: true (optionally pin mcp_token) |
| Docker / anywhere |
-e COMPANION_MCP=1 (optionally -e COMPANION_MCP_TOKEN=…) |
The companion mints a bearer token on first enable and persists it. The ⚙ menu shows both the token and the server's address — typically:
http://<companion-host>:8000/mcp
The transport is streamable HTTP (not stdio), so any MCP client that speaks HTTP can
connect over your LAN. The token guards /mcp only; with MCP off, the path 404s.
Claude Code:
claude mcp add --transport http splitflap http://companion-host:8000/mcp \
--header "Authorization: Bearer <token>"Claude Desktop — add a remote MCP server (Settings → Connectors) with the same URL and
Authorization: Bearer <token> header. Any other MCP-capable client works the same way.
Eleven tools — enough to see the board, change it, and run the content engine:
| Tool | What it does |
|---|---|
get_display |
What the display is showing right now — the text, the grid, and which app or playlist is running. This is the one that makes the rest work: the model can look before it acts. |
show_message |
Show text, centred and word-wrapped. Optional style (transition) and seconds — with seconds, the board reverts to whatever was playing afterwards. |
clear_display |
Blank every module, stopping anything running. |
stop |
Stop the app/playlist but leave the last frame on the flaps. |
list_apps |
The installed apps (weather, clock, stocks, …). |
run_app |
Run one of them. |
get_app_settings |
An app's settings and their current values. |
configure_app |
Change them. |
list_playlists / run_playlist
|
The saved playlists, and run one by name. |
list_styles |
The flap transition styles show_message accepts. |
Notice what that combination buys you: read → decide → act → revert. The model can check what's on the board before overwriting it, and it can borrow the display for a moment without destroying your playlist.
Things that work today, phrased the way you'd say them to Claude:
-
"What's on the split-flap right now?" —
get_displayanswers with the literal text, so the model can read your wall back to you from anywhere. -
"Put 'STANDUP IN 5' on the board for two minutes, then put back whatever was playing."
— one
show_messagewithseconds=120; the revert is automatic. -
"Show the weather, but for Lisbon instead of here." —
get_app_settingsto see the location field,configure_appto change it,run_appto show it. The model discovers the setting names itself; you don't have to know them. -
"Something's wrong with the board — what's it doing?" —
get_displaypluslist_appsis usually enough for it to tell you the playlist is running but the weather app is erroring. -
"Make a message that fits." The grid is 15 × 3 — 45 characters. Ask for a haiku, a
countdown, a birthday greeting, and the model can see the dimensions from
get_displayand write to fit them, which is exactly the part humans find annoying. - "Run the morning playlist and set it to Celsius first."
Where it gets more interesting is when the display is one tool among several. Because the model also has your other tools, the split-flap stops being a thing you configure and becomes a thing you mention:
"Check if the build is green; if it broke, put the failing job on the split-flap in red."
"When my next meeting is within 10 minutes, flash its title on the board for 30 seconds."
"Summarise today's PRs into 45 characters and show it."
None of that needed a new app, a template, or a line of YAML — the LLM composed it out of the tools it already had.
-
The board is shared state.
show_messagewithoutsecondstakes the display over until something else changes it — that's usually what you want for a one-off, and rarely what you want inside an automation. Prefersecondswhen interrupting. -
show_messagewaits for the flaps. It returns after the modules have been sent the new text, so an agent that immediately callsget_displaysees the new board, not the old one. - The token is a bearer token in a header — treat it like a password, and keep the
companion off the public internet. It guards
/mcpand nothing else. - What can be shown still depends on what's printed on your reels → Flaps & character sets.
See also: Vestaboard-compatible API · Home Assistant · Companion
Start
Build it
- Hardware
- Module Firmware
- Provisioning
- Calibration
- Flaps & Character Sets
- SplitFlap Gateway
- Matrix Gateway
- LCD Gateway
Drive it
- Companion
- Built-in Apps
- Standalone & Docker
- Multiple Displays
- Home Assistant
- Vestaboard API
- MCP Server
- Using splitflap-os
Extend it
Reference
Hardware © Adam G Makes