tv-control
Integrates with Android-based TV devices via ADB and Android TV Remote v2, enabling remote control, app launch, input handling, and device status checks.
Uses Authentik forward-auth proxy headers to identify users and groups and enforce tv-admin or site-scoped access to the TV control panel.
Controls NVIDIA Shield TV devices over ADB and the Android TV Remote v2 protocol, including D-pad/media keys, app launching, power/wake, status, screenshots, and fast-key pairing.
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., "@tv-controlLaunch Netflix on the living room Shield and turn the volume up"
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.
tv-control
A web panel and MCP server for a small fleet of TVs: NVIDIA Shields and Fire TVs over ADB, LG webOS TVs over aiowebostv and Wake-on-LAN. One container serves both, driven by one devices.yml.
Features
Remote control per device: D-pad, media, volume, and home keys; app launch with quick-launch buttons; on-screen toasts.
Fast keys on Shields: after a one-time pairing, keys go over the Android TV Remote v2 protocol (androidtvremote2) in milliseconds instead of about 1 s per
adb shell input. Unpaired Shields and any Remote v2 failure fall back to ADB.Live status over Server-Sent Events (
/events, one update per device every 10 s), so cards refresh without polling.Installable PWA: web manifest and icons, content-hashed static assets served
immutable.Power, wake (WoL through an SSH relay on the TV's LAN), input switching, and sleep timers. LG webOS inputs switch through aiowebostv; Fire TV inputs switch through a passthrough intent (needs
input_ids, does not wake the TV).Shield "find my remote" (locator), device status, screenshots, and an admin screen preview.
Health checks, including paired TVs: a Shield with
paired_tvreportsstandbywhen its TV is off, and a fault only when the TV is unreachable while the Shield is awake and playing.Site LAN scan for new ADB or webOS devices, with an admin approve step.
MCP server (streamable HTTP) with the same verbs, for an AI agent or a script.
python3 /app/app.py call <verb> key=valuecalls it from inside the container.An app denylist:
denied_appsentries are never launched, by the panel or by MCP.
Related MCP server: TVWizard
Security model
The panel is meant to sit behind a reverse proxy with forward auth (built and tested with Authentik proxy providers).
Every panel route except
/healthzneeds the headerX-TV-Proxy-Keyequal toTV_PROXY_KEY. Only the reverse proxy sets it, so a client that reaches the port directly cannot forge the user headers. WithoutTV_PROXY_KEYthe panel answers 503.The user comes from
X-authentik-username, and groups fromX-authentik-groups.Group
tv-adminsees and drives every device. Grouptv-site-<site>sees only that site's devices and can use key, launch, sleep timer, power, wake, input, and toast.Reboot, pair (webOS pairing and Shield fast-key pairing), baseline, lockdown, scan, approve, screenshot, and screen preview are admin-only. A denial returns 403 with
error,cause, andnext_step.POST routes need
X-TV-Panel: 1, which a cross-site form cannot send. Real baseline and lockdown runs need the device name typed as confirmation; MCP can only dry-run them.Power and input changes: at most one per 5 s per device (429). Every POST writes one audit line to stdout.
Responses carry a strict CSP (
script-src 'self', no inline script or style,frame-ancestors 'none') andX-Frame-Options: DENY.MCP on
MCP_PORTrequiresAuthorization: Bearer $TV_CONTROL_MCP_TOKEN; without a token it answers 503.tv-control never disconnects devices from the shared ADB server, powers a Shield off, or wipes or removes apps. Static tests guard this.
Configuration
Variable | Default | Purpose |
| (unset) | Shared secret the reverse proxy sends as |
| (unset) | Bearer token for |
|
| Public panel URL, shown in error messages. |
| (unset) | Extra bind address (for example a Tailscale IP) next to 127.0.0.1. Bound with |
|
| Panel and |
|
| MCP streamable HTTP at |
|
| Device inventory. |
|
| Optional |
|
| Host path of the |
|
| Android TV Remote v2 client certificates and pairing markers, one set per Shield. |
| under | State files. |
tv-control runs adb against the server on 127.0.0.1:5037. If another container shares that server, keep both on the same adb version, or each client restarts the server.
devices.yml
See devices.example.yml. Keep the real file outside this repo and mount it read-only.
Key | Purpose |
| Unique device name, used by every verb. |
|
|
| Site key; must exist under |
| Primary ADB (5555) or webOS address. |
| Fallback address, tried when |
| Any of |
| Display name, WoL MAC, |
| Input key to button label, for example |
| Fire TV only: input key to the Android TvInputInfo id (from |
| The device name of the TV a Shield feeds; drives the paired-TV health status. |
| Substrings of package names or titles that are never launched. |
| Quick-launch buttons: |
| Private LAN that the scan probes. |
|
|
| Read only by the optional |
Compose example
services:
tv-control:
image: ghcr.io/baker-scripts/tv-control:latest
container_name: tv-control
network_mode: host
user: "1000:1000"
read_only: true
cap_drop: [ALL]
security_opt: [no-new-privileges:true]
tmpfs: [/tmp]
environment:
TV_PROXY_KEY: ${TV_PROXY_KEY}
TV_CONTROL_MCP_TOKEN: ${TV_CONTROL_MCP_TOKEN}
TV_PANEL_URL: https://tv.example.com
HOME: /config
volumes:
- ./devices.yml:/scripts/devices.yml:ro
- ./config:/config
restart: unless-stoppedHOME=/config keeps the ADB key (/config/.android/adbkey) on the volume. Point the reverse proxy at 127.0.0.1:8011 (or TS_IP:8011) and have it set X-TV-Proxy-Key.
Pair fast keys (Shield)
Open the panel as a
tv-adminmember. The Shield must be reachable on itsip(Remote v2 uses TCP 6466 and 6467).On the Shield card, press "Pair fast keys". The Shield shows a code on screen.
Type the code into the prompt. The certificate is saved under
REMOTE_DIR, and later key presses use Remote v2.
Wake, sleep, and power stay on ADB, so they work while the Shield's remote service is down. To re-pair, delete that Shield's files under REMOTE_DIR and repeat the steps.
Development
uv sync --locked
uv run --locked ruff check . && uv run --locked ruff format --check .
uv run --locked mypy .
uv run --locked pytest # unit tests, 80% coverage gate
uv run --locked playwright install chromium
uv run --locked pytest -m e2e --no-cov # Playwright smoke at 375 px and 1280 pxTests need yq (mikefarah/yq) on PATH. Set DEVICES_FILE to lint your own inventory with tests/test_lint.py.
License
GPL-3.0. See LICENSE.
This server cannot be deployed
Maintenance
Related MCP Connectors
Control Android TV from any AI. 38 MCP tools: playback, recap, recommend, smart-home, schedules.
Give your AI agent the power to cast to your home TV and control an interactive canvas. By ArdaBot.
Drive real Android & iOS devices and web browsers from natural language for mobile + web QA. 290+ tools across device control, app management, automation sessions, browser automation, and flow recording / replay. Bearer-auth — get a token at robotactions.com → Profile → API Tokens.
Drive real devices from your AI Coding tool. Embed a client SDK (Unity, Godot, Flutter, iOS/macOS, Android, React Native, Web) in your app, then capture screenshots, traverse the UI tree, inject taps and key events, and run automated test tasks on the physical device over a secure relay.
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceTurns AI assistants like Claude and ChatGPT into a remote control for LG webOS smart TVs, enabling power control, volume, app launching, input switching, and more, all locally without cloud APIs.5MIT
- FlicenseNot gradedqualityDmaintenanceTurns any Claude-compatible AI chat into a universal remote for Android TV and Google TV via a cloud relay, enabling natural language control of your TV from anywhere.-
- AlicenseNot gradedqualityCmaintenanceEnables control of LG webOS TVs over the LAN, including reading status and picture settings, applying picture presets for consoles like PS5 and Switch 2, switching inputs, powering on/off via Wake-on-LAN, backing up and restoring settings, and showing on-screen notifications. It runs as a Streamable HTTP endpoint secured with mandatory Bearer authentication.MIT
- AlicenseNot gradedqualityCmaintenanceEnables an AI agent to control an LG webOS TV by reading playback status, listing and launching apps, adjusting volume or mute, and powering it off.MIT