mcp-gtv
by kud
README.md
<div align="center">





**MCP server for Google TV — control paired devices (keys, text, app launch) via @kud/gtv.**
[Features](#-features) • [Quick Start](#-quick-start) • [MCP Client Setup](#-mcp-client-setup) • [Tools Reference](#-tools-reference) • [Development](#-development)
</div>
> **Proof of concept — experimental and unfinished.**
> This package is not yet published to npm. The API, tool names, and behaviour may change or break at any time. Run it from source (see [Development](#-development)). Do not use in production.
`@kud/mcp-gtv` exposes Google TV control to any MCP client, letting an assistant open apps, control playback, type into search fields, and read back what's currently on the TV.
## 🌟 Features
- 🔌 **Zero credentials** — reads paired devices from `~/.config/gtv/config.json`; no API key, no token, no extra setup
- 📺 **Device switching** — list all paired TVs and switch the active target at any time during a session
- 🎮 **Full remote control** — send any key from navigation and media to volume, power, and input
- ⌨️ **IME text input** — type arbitrary text into the focused field directly, without keycode mapping
- 🚀 **App launcher** — launch Netflix, YouTube, Prime Video, Spotify, and more by name, or pass any raw deep-link URI
- 📡 **State feedback** — every control tool returns the TV's resulting state (power, volume, foreground app) so the model can confirm its action landed
- 🤖 **Broad MCP client support** — stdio transport works with Claude Desktop, Claude Code, Cursor, and any MCP-compatible client
## 🚀 Quick Start
### 1. Pair your TV first
Pairing is handled by `@kud/gtv-cli`, not this server. If you have not paired a device yet:
```sh
npx @kud/gtv-cli pair
```
Follow the PIN prompt on the TV. The paired device is written to `~/.config/gtv/config.json` — the shared config store that `mcp-gtv` reads automatically. You only need to do this once per device.
### 2. Run the server from source
The package is not yet on npm. Clone the repo and run with `tsx`:
```sh
git clone https://github.com/kud/mcp-gtv.git
cd mcp-gtv
npm install
npm run dev
```
### 3. Ask naturally
Once an MCP client has the server connected:
> "What's playing on the TV right now?"
> "Open Netflix"
> "Turn up the volume"
> "Go back to the home screen"
> "Type 'Blade Runner' into the search field"
### What it's good at
This server works well for discrete, confirmable actions: opening apps, controlling playback, adjusting volume, typing into search, and checking what's currently on screen. It is **not** suited to navigating menus or lists inside an app — see [Known Limitations](#-known-limitations).
## 🔧 MCP Client Setup
The server uses **stdio transport** and requires no environment variables.
> **Not yet on npm.** The `npx @kud/mcp-gtv` form shown below will not work until the first publish. For now, use the local dev config instead.
### Local development (current)
Add to `.mcp.json` in your project root, pointing at your local clone:
```json
{
"mcpServers": {
"mcp-gtv": {
"command": "npx",
"args": ["tsx", "/path/to/mcp-gtv/src/index.ts"]
}
}
}
```
The repo also ships a `.mcp.json` at the root — open the repo in Claude Code and it will be picked up automatically.
### Claude Desktop / Claude Code (once published)
```json
{
"mcpServers": {
"mcp-gtv": {
"command": "npx",
"args": ["-y", "@kud/mcp-gtv"]
}
}
}
```
**Claude Desktop** — edit `~/Library/Application Support/Claude/claude_desktop_config.json`.
**Claude Code** — add to `.mcp.json` in your project root, or to `~/.claude/mcp.json` for global availability.
### Cursor / other MCP clients
Use the same `npx -y @kud/mcp-gtv` command with stdio transport once published. Consult your client's documentation for the exact config location.
## 🛠 Tools Reference
Every control tool returns the TV's resulting state (connected, powered, foreground app, volume) so the model can confirm its action landed.
| Tool | Description | Arguments |
| ------------------ | ------------------------------------------------------------------------------------------------------------------- | ---------------- |
| `gtv_list_devices` | List all paired devices, marking the current one | — |
| `gtv_set_device` | Switch the active device by host (IP) or name | `device: string` |
| `gtv_get_state` | Read live state: connected, powered, foreground app (package + friendly name), volume | — |
| `gtv_send_key` | Send a remote key press; power and volume keys wait for the state echo | `key: string` |
| `gtv_type_text` | Type text into the focused field via IME | `text: string` |
| `gtv_launch_app` | Launch an app by catalog name/id or deep-link URI; waits for and returns the foreground app with a `confirmed` flag | `app: string` |
### Valid keys for `gtv_send_key`
```
home back power up down left right select
play stop next prev fwd rwd
vol-up vol-down mute
menu search sleep wakeup input enter
channel-up channel-down info guide settings
```
### App catalogue for `gtv_launch_app`
| ID | App |
| ------------ | ----------- |
| `netflix` | Netflix |
| `youtube` | YouTube |
| `primevideo` | Prime Video |
| `plex` | Plex |
| `putio` | Put.io |
| `arte` | Arte |
| `disney` | Disney+ |
| `spotify` | Spotify |
| `twitch` | Twitch |
| `max` | Max |
You can also pass any raw deep-link URI directly (e.g. `intent://...` or `market://launch?id=com.example.app`).
## ⚠️ Known Limitations
- **No screen awareness.** The Android TV Remote protocol only streams power state, volume, and foreground app package — never screen contents, focus position, or menu structure. The server can launch apps, control playback, type into search, and confirm those actions landed. It **cannot** see the screen, so navigating menus, folders, or lists inside an app is not reliably possible. Deep in-app navigation would require an ADB or computer-vision channel; that is deliberately out of scope.
- **Store-mediated app launching.** App launch uses `market://launch?id=<package>`, which the Play Store may auto-launch OR simply display as a store listing (showing an "Open" button) even when the app is installed. Reliable direct launch is not available over the remote protocol. When the store page opens instead, send `select` to open the app.
- **Current device only.** All tools act on the device selected by `gtv_set_device` (or the default from the config). Interactive pairing over MCP is not implemented — pair first with `npx @kud/gtv-cli pair`.
- **First-call latency.** The first tool call in a session opens a TLS connection and waits for the TV's telemetry burst to settle (~350 ms quiet, capped at 2 s). Subsequent calls reuse the warm session and are near-instant.
## 🔧 Development
### Project layout
```
mcp-gtv/
├── src/
│ └── index.ts # MCP server — session management, all tool handlers
├── test/
│ └── tools.test.ts # Unit tests (Node built-in test runner)
├── dist/ # Compiled output (tsup, gitignored)
├── .mcp.json # Local MCP client config for dev (auto-loaded by Claude Code)
├── TESTING.md # Manual test plan — run against a real TV before publishing
└── tsup.config.ts
```
### Scripts
| Script | Purpose |
| --------------------- | ----------------------------------------------- |
| `npm run dev` | Run source directly with `tsx` |
| `npm run build` | Compile to `dist/` via tsup |
| `npm run build:watch` | Watch mode |
| `npm run test` | Run test suite (Node built-in runner) |
| `npm run typecheck` | TypeScript type check only |
| `npm run inspect` | Open MCP Inspector for interactive tool testing |
### Clone and run
```sh
git clone https://github.com/kud/mcp-gtv.git
cd mcp-gtv
npm install
npm run dev
```
Use `npm run inspect` to open the [MCP Inspector](https://github.com/modelcontextprotocol/inspector) and exercise all tools interactively without a full MCP client.
### Testing
A manual test plan that covers every tool against a real TV lives in [`TESTING.md`](TESTING.md). Run through it before any publish.
### Releasing
Releases are tag-driven via GitHub Actions with OIDC Trusted Publishers — no manual `npm publish` needed. The first publish has not happened yet; once the initial OTP bootstrap is done, subsequent releases will follow the tag workflow:
```sh
git tag v0.2.0
git push origin v0.2.0
```
## 🏗 Tech Stack
| Package | Role |
| ------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------ |
| [`@kud/gtv`](https://github.com/kud/gtv) | Google TV domain library — device store, key codes, app catalogue, remote connection |
| [`@modelcontextprotocol/sdk`](https://github.com/modelcontextprotocol/typescript-sdk) | MCP server primitives (`McpServer`, `StdioServerTransport`) |
| [`zod`](https://github.com/colinhacks/zod) | Tool input schema validation |
| [`tsup`](https://github.com/egoist/tsup) | ESM bundler / compiler |
| [`tsx`](https://github.com/privatenumber/tsx) | TypeScript execution for dev and tests |
### Ecosystem
```
@kud/androidtv-remote ← low-level pairing & remote protocol
↑
@kud/gtv ← domain library (devices, keys, apps)
↑
┌────┴────┐
│ │
mcp-gtv gtv-cli ← MCP surface / terminal surface
```
This server is the MCP client surface of the ecosystem. [`@kud/gtv-cli`](https://github.com/kud/gtv-cli) is the interactive terminal counterpart — and the tool you use to pair devices before this server can control them.
---
MIT © [kud](https://github.com/kud) — Made with ❤️
TDQS
A4.1/5.0
Scored across 5 tools
Disambiguation5/5
Each tool targets a distinct operation: device selection, device listing, app launching, key sending, and text typing. There is no overlap in their purposes.
Naming Consistency5/5
All tools follow a consistent 'gtv_verb_noun' pattern (e.g., gtv_launch_app, gtv_list_devices), making the naming predictable and easy to understand.
Tool Count5/5
With 5 tools, the server covers the essential remote control operations for Google TV devices without being overly narrow or bloated.
Completeness4/5
The tool set covers device management, navigation, text input, and app launching. A minor gap might be the lack of a tool to retrieve device info or current state, but the core remote control functionality is well-covered.
Maintenance
ActivityStale
ResponsivenessNo issues