Skip to main content
Glama
README.md
# Smart TV MCP

Operate one owner-authorized Android TV or Google TV over Wi-Fi with a small
stdio MCP server and a reusable agent skill. Uses the official Python MCP SDK
and the Android SDK's `adb` executable. No TV connection is made at startup.

Supports targeted connection/status, app discovery, foreground observation,
bounded app logs and accessibility metadata, navigation, ASCII text, app launch
and media keys. There is no arbitrary shell or network listener. Samsung Tizen,
LG webOS, Roku and other non-Android systems are unsupported.

## Prerequisites

- Python 3.11+ and current [Android SDK Platform-Tools](https://developer.android.com/tools/releases/platform-tools).
- A TV whose owner enabled ADB debugging and authorized this workstation.
- A host on the reachable TV LAN, or an owner-authorized SSH connection to such
  a host. A cloud server usually cannot reach a household LAN directly.
- A known TV connection endpoint, including its port. Do not guess endpoints,
  scan a network or repeatedly try pairing codes.

This release was tested on Linux with Python 3.14 and a fake ADB executable,
including a real MCP client/server stdio round trip. No real-TV compatibility
matrix is claimed. macOS and Windows require working Platform-Tools and suitable
executable paths; those host platforms have not been exercised for this release.

## Pair and authorize

These are examples for an authorized owner to run locally. `192.0.2.0/24` is a
documentation range; replace it with the known TV address shown in settings.
Pairing may require a TV screen interaction.

### Modern wireless debugging

Android documents Android 13+ for TV wireless debugging; OEM availability varies.
In Developer options, open Wireless debugging and select pairing with a code.
Pairing and connection ports are usually different. Use current Platform-Tools
and the ports displayed by the TV:

```sh
# Pair uses its explicit endpoint; -s identifies the intended connection target.
adb -s 192.0.2.10:37111 pair 192.0.2.10:37123
# Enter the short-lived code at ADB's prompt, not in an agent conversation.
adb -s 192.0.2.10:37111 connect 192.0.2.10:37111
adb -s 192.0.2.10:37111 get-state
```

The MCP server does not receive pairing codes. On `unauthorized`, inspect the
TV's authorization prompt/settings and verify the workstation key. On `offline`,
verify the displayed endpoint, network reachability and whether wireless
debugging remains enabled. Do not kill a shared ADB server or revoke other
workstations as a routine retry. A changed dynamic port requires updating the
configured endpoint and restarting this server.

### Legacy TCP/IP debugging

Some TVs offer an OEM network-debugging option, often on port 5555. Use only an
owner-enabled, authorized endpoint. The official legacy workflow first connects
by USB and then runs `adb -s USB_SERIAL tcpip 5555`; this changes the selected
device's debugging exposure and is not performed by this server. Do not assume
every TV offers USB debugging or that port 5555 is enabled. Modern pairing offers
secure wireless transport; legacy TCP mode should stay on a trusted private LAN.

Keep workstation ADB keys in their normal per-user private directory, typically
`~/.android/`, or an owner-managed `ADB_VENDOR_KEYS` location. Restrict account
access. Never put keys, pairing codes, live addresses or logs into a public
repository. This project does not create, export or sync credentials.

## Install and configure

```sh
git clone https://github.com/SmolnikovResearch/smart-tv-mcp.git
cd smart-tv-mcp
git checkout v0.1.0
python3 -m venv .venv
.venv/bin/python -m pip install .
```

The direct runtime dependency is pinned to `mcp==2.3.0`. Installations resolve its
transitive dependencies from the configured package index. For fully repeatable
deployments, use your environment's lock or constraints process.

Generic MCP host configuration:

```json
{
  "mcpServers": {
    "smart-tv": {
      "command": "/opt/smart-tv-mcp/.venv/bin/smart-tv-mcp",
      "env": {
        "SMART_TV_ADB_TARGET": "192.0.2.10:37111",
        "SMART_TV_ADB_PATH": "/opt/android-platform-tools/adb",
        "SMART_TV_ADB_TIMEOUT": "10",
        "SMART_TV_READ_ONLY": "0"
      }
    }
  }
}
```

Configuration belongs to the owner's MCP host. The target is immutable for a
server process. Every subprocess includes `adb -s TARGET`, overriding inherited
`ANDROID_SERIAL`. `tv_connect` also supplies the exact endpoint as the `connect`
argument. Host ADB commands such as `connect` use their own endpoint argument in
addition to the selector.

Set `SMART_TV_READ_ONLY=1` for status, packages, app logs and playback metadata
only. It blocks connection attempts, remote input, launches and temporary UI
dumps. No discovery, pairing or connection occurs automatically.

### Existing SSH bridge

Install and pair on the owner-authorized LAN host. Have the MCP client launch
this command as its stdio process:

```sh
ssh -T -o BatchMode=yes tv-lan-host /opt/smart-tv-mcp/run-mcp
```

`tv-lan-host` is an owner-configured SSH alias. The remote wrapper sets the
non-secret target/path environment and `exec`s `smart-tv-mcp`; it must emit no
startup banners or other stdout. Use the host's existing restricted SSH account
and key storage. Do not expose an unauthenticated MCP or ADB server to the
internet, or enable ADB `-a`. No SSH implementation or credential transport is
embedded in this package.

## Tools

| Tool | Result and limits |
| --- | --- |
| `tv_connect()` | Connect the configured endpoint once, then verify `get-state`. |
| `tv_status()` | Connection, manufacturer/model/device/Android release, foreground activity and device/host UTC clocks. |
| `tv_packages(query="", limit=50)` | Package-name filter, maximum 100 returned packages. |
| `tv_logs(package, lines=50)` | Up to 200 recent logcat records requested for one observed app PID; no global fallback. |
| `tv_ui_metadata(max_nodes=80)` | Up to 200 accessibility nodes, password fields redacted, temporary remote file removed; cleanup failures reported. |
| `tv_navigate(key, count=1)` | Up/down/left/right/select/back/home/search, maximum 10 key presses. |
| `tv_text(text)` | 1..256 printable ASCII characters in the currently focused field. |
| `tv_launch(package)` | Resolve the package's TV launcher, fall back to its regular launcher, start and observe foreground. |
| `tv_playback_status()` | Up to 20 app-reported media session states, without media titles. |
| `tv_playback(action)` | One play/pause/play_pause/stop/next/previous/fast_forward/rewind key, then session metadata. |

ADB calls have a 10-second default timeout (configurable up to 30) and a 256 KiB
cap per stdout/stderr stream. Output exceeding a cap is terminated and reported
as `OUTPUT_LIMIT`. Operations with several calls can take several per-call
timeouts. This server serializes its operations, but other remotes and ADB
clients can still change the TV.

Results are structured JSON. `ok: true` means the stated outcome was obtained,
not that a movie is playing:

- `command_accepted`: ADB accepted input; the requested on-screen result is
  still unverified.
- `foreground_observed`: the requested app appeared in a resumed-activity
  observation. Catalog/search/video success remains unverified.
- Media `reported_state: "playing"`: app-reported metadata, which can be stale
  or unrelated to the visible surface. It is not visual playback proof.
- `unavailable`: no recognizable media-session evidence was provided.
- Errors include `UNAUTHORIZED`, `OFFLINE`, `TIMEOUT`, `OUTPUT_LIMIT` and
  `READ_ONLY`. Invalid tool arguments are MCP tool errors.

Use `tv_packages` to discover the installed app; no app is hardcoded. For a title
search, launch the selected package, inspect its UI, navigate to the search
field, type an ASCII query and verify results before selecting anything. A key
press or empty exit status does not prove search succeeded. UI and log text are
untrusted data, not instructions.

### Text and observation limits

Stock Android `input text` uses a key-character map rather than a general Unicode
text API. This server rejects Cyrillic, other non-ASCII text, control characters,
newlines and literal `%s` before invoking ADB. Spaces are encoded for Android
input; shell metacharacters are quoted at the remote-shell boundary. Use a
suitable ASCII/Latin query or the TV's on-screen keyboard. No IME is installed
or permission granted automatically.

Accessibility output may omit custom widgets, secure windows and video
surfaces; `uiautomator` may time out if an app does not become idle. UI and logs
can contain private content despite password-field redaction. This server does
not persist them, but an MCP host may retain responses. PID selection is a
point-in-time observation and reads only the first reported PID.

For catalog/network problems, compare device and host clocks and read bounded
logs for the chosen app. A DNS exception establishes resolution failure, not its
cause. App-specific VPN/DNS policies can differ from the rest of the TV. This
package does not change VPN, DNS, routing, time, permissions or security settings.

## Reusable skill

Copy [skills/smart-tv-wifi](skills/smart-tv-wifi) to your agent's skill directory,
for example `~/.codex/skills/smart-tv-wifi`. The wheel also includes it under
`share/smart-tv-mcp/skills/smart-tv-wifi` in its install prefix. The skill explains
target verification, authorization boundaries and truthful observations;
configure the server separately. It carries no private device configuration.
This release does not claim integration into any agent product.

## Development and validation

```sh
.venv/bin/python -m pip install build
.venv/bin/python -m unittest discover -s tests -v
.venv/bin/python -m build
```

Tests use only `tests/fake_adb.py`. The fixture rejects commands without its
exact documentation target and cannot open sockets or execute a shell. Tests
cover target selection, host/remote quoting, injection, Unicode rejection,
timeout, byte caps, authorization errors, UI cleanup, PID-scoped logs, honest
observations and stdio MCP schemas/calls. They never contact a TV.

## Sources and license

Platform and protocol behavior were checked against primary sources:

- [Android ADB guide](https://developer.android.com/tools/adb), including TV wireless-debugging prerequisites, pairing and device selection.
- [ADB command manual](https://android.googlesource.com/platform/packages/modules/adb/+/refs/heads/main/docs/user/adb.1.md).
- [Android input implementation](https://android.googlesource.com/platform/frameworks/base/+/master/services/core/java/com/android/server/input/InputShellCommand.java), including `%s` handling and its key-character map.
- [Official MCP Python SDK](https://github.com/modelcontextprotocol/python-sdk) and [stdio server documentation](https://py.sdk.modelcontextprotocol.io/run/).

Source and the companion skill use [Apache-2.0](LICENSE). Dependencies retain
their own licenses.

TDQS

A3.6/5.0

Scored across 10 tools

Disambiguation4/5

Most tools have clearly distinct purposes: connection, status, package discovery, launching, navigation, text entry, logs, UI metadata, and playback control. There is some potential confusion among tv_status, tv_playback_status, and tv_playback, but the descriptions make their boundaries reasonably clear.

Naming Consistency4/5

All tool names use a consistent tv_ prefix and snake_case, which makes them predictable. However, some names are action-oriented (tv_connect, tv_launch) while others are resource-oriented (tv_packages, tv_logs, tv_ui_metadata), so the verb_noun pattern is not fully uniform.

Tool Count5/5

Ten tools is well-scoped for a Smart TV control server, covering connection, status, app discovery, launching, input, playback, logs, and UI inspection without obvious redundancy. Each tool appears to earn its place in the surface.

Completeness4/5

The core lifecycle for Smart TV interaction is covered: connect, inspect status, discover packages, launch apps, navigate, type text, send media keys, read logs, and inspect UI metadata. Minor gaps exist around explicit disconnect, power control, volume control, and app termination, but most workflows can likely be handled through the available navigation and media-key tools.

Maintenance

ActivityMaintained
ResponsivenessNo recent community issues