mcp-server-shieldtv
Provides tools for controlling an NVIDIA Shield TV over the Android TV Remote protocol v2: pressing an allow-listed set of remote keys (D-pad, media, volume), launching allow-listed apps by friendly name, waking or sleeping the device, and reading its power state, foreground app, volume, and device info.
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., "@mcp-server-shieldtvwake up the Shield and launch Netflix"
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.
mcp-server-shieldtv
An MCP server for the NVIDIA Shield TV, built on the Android TV Remote protocol v2 (the same protocol the Google TV phone app uses). It lets an MCP client such as Claude press remote keys, launch apps, wake or sleep the Shield, and read its power state and foreground app.
Status: early / untested on hardware. The code is covered by unit tests against a fake remote and the MCP tool surface is smoke-tested over stdio, but it has not yet been run against a real Shield. Expect rough edges, and please open issues.
This is not an official NVIDIA project, and NVIDIA publishes no MCP server for the Shield.
Why this protocol
Remote protocol v2 (this project) | ADB | |
Setup | One-time pairing with an on-screen code | Enable Network debugging on the Shield |
Good for | D-pad, media keys, app launch, power/app state | Deep device inspection |
Risk if exposed to an LLM | Bounded: it can only press keys | High: |
Three optional ADB tools do what the remote protocol can't: get_now_playing,
get_remotes and reboot_shield. They are off until you run adb-setup, and they run
only fixed commands; the server never exposes a raw shell (see
ADB tools).
Related MCP server: mcp-gtv
Install
git clone https://github.com/SDNick484/mcp-server-shieldtv.git
cd mcp-server-shieldtv
python -m venv .venv && . .venv/bin/activate
pip install -e .Requires Python 3.11+.
Pair with your Shield (once)
The Shield and the machine running the server must be on the same network, and the machine must be able to reach the Shield on these ports:
Port | Used for |
TCP 6466 | Remote control (every tool call) |
TCP 6467 | Pairing (only during |
UDP 5353 (mDNS) |
|
TCP 5555 | ADB, only if you enable the ADB tools with |
A firewall, a guest network, or a separate IoT VLAN between the two will show up as "can't reach the Shield".
mcp-server-shieldtv discover # optional: list Android TV devices via mDNS
mcp-server-shieldtv pair # finds the Shield, or: pair --host 192.168.1.50A code appears on the TV; type it into the terminal. Pairing generates a client
certificate and key under ~/.config/mcp-server-shieldtv/ (override with
SHIELDTV_CONFIG_DIR) and saves the Shield's address in config.json.
Treat cert.pem and key.pem like a password. Anyone who has them can control your
Shield. The directory is created 0700 and the files 0600, and .gitignore excludes
them. To revoke access, remove the device from the Shield's settings and re-pair.
WSL2 note: mDNS discovery needs mirrored networking (networkingMode=mirrored in
%UserProfile%\.wslconfig, then wsl --shutdown). Otherwise pass --host.
ADB tools (optional)
The remote protocol knows which app is in front, but not what it's playing, whether your Bluetooth remotes work, or how to reboot. ADB can do all three. To enable the ADB tools:
On the Shield: Settings > Device Preferences > About, select Build seven times, then Developer options > Network debugging on.
Run
mcp-server-shieldtv adb-setup(afterpair). The TV asks "Allow USB debugging?": tick "Always allow from this computer" and choose Allow.
adb-setup creates its own ADB key (adbkey, adbkey.pub, 0600) next to the pairing
files and sets "adb": true in config.json. The ADB key grants a shell on the Shield;
guard it more carefully than the pairing files. To revoke it, use Developer options >
Revoke USB debugging authorizations.
get_now_playing reports, checked on a real Shield: the app, title, subtitle (the artist for music,
the channel for live TV), play state, and position. Live TV (YouTube TV, Sling) reports no
position, since its "position" is an offset into the stream. Music played from YouTube Music
shows up as the YouTube app. Duration and album aren't available this way. A title that
contains ", " can rarely split wrong (the system prints title and artist joined by ", ").
get_remotes lists Bluetooth remotes (a Harmony hub shows up as "Harmony Keyboard")
(every paired input device, even ones that haven't connected since the Shield started)
as working, disconnected (normal when not in use), or stuck: Bluetooth says it is
connected, but Android never created its input device, so its buttons do nothing. This
happened to both remotes on a real Shield after a reboot. The fix for a Harmony hub is to
press Off and start the activity again.
reboot_shield notes which remotes work, restarts the Shield, and waits until it has
booted (about 35 seconds on a real Shield) plus 20 seconds for remotes that reconnect on
their own. It reports any remote that came back stuck, with the fix, and any that worked
before but hasn't reconnected yet. A Harmony hub doesn't reconnect until a button is
pressed, so expect it to be listed that way; if it then does nothing, get_remotes says
whether it's stuck. The whole call takes about a minute. It is marked destructive, so MCP
clients ask before running it.
Use it with an MCP client
{
"mcpServers": {
"shieldtv": { "command": "/path/to/mcp-server-shieldtv/.venv/bin/mcp-server-shieldtv" }
}
}For Claude Code: claude mcp add shieldtv -- /path/to/.venv/bin/mcp-server-shieldtv.
To point it at a particular Shield without editing config.json, pass the host as an
environment variable: claude mcp add shieldtv -e SHIELDTV_HOST=192.168.1.50 -- ....
That Shield must already be paired with the same credentials (pair --host <ip> once per
Shield; note that pair also saves its host as the default in config.json).
Configuration
Setting | Default | What it does |
| (from | The Shield's IP address or hostname |
|
| Where the certificate, key and |
|
| Standard base directory, used when |
config.json (written by pair, safe to edit by hand):
Key | What it does |
| The Shield's address, saved by |
| Extra or overriding app names for |
|
|
When the host is set in more than one place, the most specific wins: pair --host, then
SHIELDTV_HOST, then config.json.
Tools
Tool | What it does |
| Reachability, power ( |
| The app names |
| Press an allow-listed remote key, optionally repeated 1-10 times (see below) |
| Launch an allow-listed app by friendly name, and confirm it reached the foreground |
| Wake ( |
| App, title, subtitle (artist or channel), play state and position. Needs |
| Each Bluetooth remote and whether it works, with a fix for stuck ones. Needs |
| Restart the Shield, wait until it's back, then check the remotes. Needs |
get_status and the ADB tools return structured content with a published output schema.
Allowed keys: HOME, BACK, MENU, DPAD_UP/DOWN/LEFT/RIGHT/CENTER,
MEDIA_PLAY_PAUSE, MEDIA_PLAY, MEDIA_PAUSE, MEDIA_STOP, MEDIA_NEXT,
MEDIA_PREVIOUS, MEDIA_REWIND, MEDIA_FAST_FORWARD, VOLUME_UP, VOLUME_DOWN,
VOLUME_MUTE.
Apps
Default apps, each checked on a real Shield: youtube, youtube-tv, netflix,
prime-video, disney+, hulu, plex, spotify.
Apps launch through deep links: https links, or an app's own scheme (plex://,
spotify:) where the https link would go to a browser instead. On the Shield (remote service 7.x), a bare
package name is sent as market://launch?id=<package>, which the Shield rejects and then
drops the connection. launch_app waits for the app to reach the foreground (up to 10s)
and returns an error that says what happened if it doesn't: the request was rejected, or it
was accepted but nothing opened (the app isn't installed, or no app handles the link; the
TV shows "You don't have an app that can do this").
Add your own apps
Edit ~/.config/mcp-server-shieldtv/config.json:
{
"host": "192.168.1.50",
"apps": {
"crunchyroll": { "link": "https://www.crunchyroll.com", "package": "com.crunchyroll.crunchyroid" },
"example": "https://example.com/tv"
}
}An entry is either a link, or { "link", "package" }. Adding the package is recommended:
launch_app then confirms that exact app opened, and get_status shows the friendly name.
Without it, any app other than the home screen coming to the front counts as success.
Names are case-insensitive, and an entry with the same name as a default replaces it.
Restart the server (or your MCP client) to pick up changes.
Finding the link and package: open the app on the Shield, then call get_status (or ask
"what app is open on the Shield?"); current_app_package is the package. For the link, try
the service's website address (https://www.<service>.com). With ADB enabled, Android can
tell you which app a link opens without launching anything:
adb shell cmd package query-activities --brief -a android.intent.action.VIEW \
-c android.intent.category.BROWSABLE -d 'https://tv.youtube.com'A result of com.google.android.tv.frameworkpackagestubs/.Stubs$BrowserStub means "no app"
(the TV shows "You don't have an app that can do this"). If the stub is listed alongside the
app, the app usually still opens, but its own scheme (if it has one) avoids the ambiguity.
Safety design
Keys are an allow-list. The tool schema is an enum, and the client re-checks it.
POWER,SEARCH(starts voice capture),SETTINGS,MUTE(Android's microphone mute), raw numeric key codes, and the library'stext:typing are not available.Apps are an allow-list. Only names in
list_appscan be launched.No shell and no arbitrary key codes, so a prompt-injected model has a small blast radius. ADB is off by default. When enabled, the ADB tools take no arguments and run only constant commands (
dumpsys media_session,dumpsys bluetooth_manager,dumpsys input,getprop sys.boot_completed,/proc/uptime), plus ADB's own reboot service. Code that tries any other command is refused, so nothing the model writes reaches the Shield's shell.reboot_shieldis the only tool marked destructive: it interrupts playback, so clients should confirm with you first.Credentials are private (
0600) and never logged. Logs go to stderr because stdout belongs to the MCP transport.Tools carry titles and MCP annotations (
readOnlyHint,destructiveHint,idempotentHint,openWorldHint) so clients can decide what needs confirmation.
Behavior notes
The server keeps one long-lived connection and caches pushed state. If the Shield is asleep or offline at startup it retries in the background, and tool calls return a clear "can't reach the Shield" message in the meantime.
Volume keys act on whatever the Shield is configured to control (the Shield itself, HDMI-CEC, or IR), so results depend on your setup. When volume goes to a TV or receiver over CEC, the Shield doesn't report a level, and
get_statusreturnsvolume: null.
Troubleshooting
The server's errors are written to be actionable, so the model will usually relay one of these:
"Not paired with a Shield yet." No host or credentials were found. Run
mcp-server-shieldtv pair. If you did pair, check that the server and pair use the same
SHIELDTV_CONFIG_DIR (an MCP client may launch the server with a different environment).
"The Shield rejected our pairing." The Shield no longer trusts this certificate,
typically after a factory reset or after removing the device under the Shield's
remote/connected-device settings. Run pair again.
"Can't reach the Shield at ..." The server is paired but has no connection. The Shield may be asleep, rebooting, or off the network, its IP may have changed (a DHCP reservation helps), or TCP 6466 may be blocked (see the port table above). The server keeps retrying in the background, so the next call may succeed without a restart.
"The connection to the Shield dropped; try again in a moment." The connection closed during the command. The library reconnects on its own; retry.
"The Shield rejected the launch request ..." The app entry is a bare package name (or a link the Shield refuses). Use an https link; see Apps.
"The Shield accepted ..., but the foreground app didn't change" The app is probably not installed, or nothing on the Shield handles that link. A very slow cold start can also do this; the next call then reports the app as already open.
pair says "No input to read the code from". It was run without a terminal (for
example through a tool that doesn't attach one). Run it in a regular terminal.
discover (or pair without --host) finds nothing. mDNS doesn't cross most VLANs or
guest networks, and on WSL2 it needs mirrored networking (see above). Pass --host <ip>;
the Shield shows its IP in its network/about settings, and your router's client list has
it too.
Server logs (connection attempts, retries, auth failures) go to stderr, which most MCP
clients save in their own logs. Running mcp-server-shieldtv in a terminal shows them
directly; it waits for MCP messages on stdin, so stop it with Ctrl+C.
Development
pip install -e ".[dev]"
pytest # unit + in-process MCP + stdio end-to-end tests, no Shield needed
ruff check . && ruff format --check .
mypy # strict type checking of src/Tests are layered: test_config.py (allow-lists, checked against the protocol's own key
enum), test_client.py (connection lifecycle against a fake remote), test_tools.py
(the MCP contract through an in-process client: schemas, annotations, results, errors),
and test_stdio.py (the installed entry point over stdio). CI runs all of it on
Python 3.11-3.14.
To poke at the tools interactively: npx @modelcontextprotocol/inspector mcp-server-shieldtv.
Roadmap
Publish to PyPI and the MCP registry
License
MIT. See LICENSE.
This server cannot be deployed
Maintenance
Related MCP Connectors
An authenticated remote MCP server for user-owned devices and one-shot capability invocation.
Securely control paired computers with ReMCP.
Control your Tesla - wake it, warm it up, unlock and more. Get your developer token at https://Infoseek.ai/mcp. Also requires your own Tesla developer token which is tied to your car/fleet.
Remote MCP server for Web3TV creators — manage your account over MCP.
Related MCP Servers
- AlicenseNot gradedqualityCmaintenanceA Model Context Protocol server that enables AI assistants to control Android TV devices, providing remote control functionality like navigation, playback control, app management, and device status monitoring.9MIT
- AlicenseAqualityCmaintenanceMCP server for controlling Google TV devices via natural language. Enables key presses, text input, and app launching on paired devices.5MIT
- FlicenseAqualityCmaintenanceEnables MCP clients to control Sony Bravia TVs over the local network, including power, HDMI inputs, apps, volume, and mute through the TV's JSON-RPC API.8-
- AlicenseNot gradedqualityBmaintenanceEnables controlling an Android device from an MCP client via ADB, including wireless debugging discovery, UI-tree inspection, and performing taps, swipes, typing, app actions, and shell commands without relying on screen coordinates.2 npmMIT