Documentation
Everything you need to connect your AI agent to a Roku device with roku-mcp.
Installation
No install required for MCP usage — npx handles it. For CLI or library use:
npm install @danecodes/roku-mcp Requires Node.js 18+ and a Roku device in developer mode on your network.
Configuration
Set your Roku device IP address:
| Method | How |
|---|---|
| MCP server | Set ROKU_DEVICE_IP env var in your MCP config |
| CLI | Use --device <ip> flag (defaults to 192.168.0.30) |
| Screenshots | Set ROKU_DEV_PASSWORD if your password isn't rokudev |
Claude Code / Cursor / Windsurf
Add to .mcp.json (project root or ~/.claude/.mcp.json for global):
{
"mcpServers": {
"roku": {
"type": "stdio",
"command": "npx",
"args": ["-y", "--package", "@danecodes/roku-mcp", "roku-mcp-server"],
"env": {
"ROKU_DEVICE_IP": "192.168.0.30"
}
}
}
} Replace 192.168.0.30 with your Roku's IP address. The server starts automatically when your agent needs a Roku tool.
GitHub Copilot (VS Code)
Copilot requires the HTTP transport. Start the server in a terminal first:
ROKU_DEVICE_IP=192.168.0.30 npx --package @danecodes/roku-mcp roku-mcp-http
# roku-mcp HTTP server running at http://localhost:3141/mcp Then add to .vscode/mcp.json:
{
"servers": {
"roku": {
"type": "http",
"url": "http://localhost:3141/mcp"
}
}
} Custom port: set ROKU_MCP_PORT=8888.
OpenAI Codex CLI
Add to ~/.codex/config.toml (global) or .codex/config.toml (project):
[mcp_servers.roku]
command = "npx"
args = ["-y", "--package", "@danecodes/roku-mcp", "roku-mcp-server"]
[mcp_servers.roku.env]
ROKU_DEVICE_IP = "192.168.0.30" Device Control Tools
| Tool | Description |
|---|---|
roku_ui_tree | Get the full SceneGraph UI tree — see what's on screen |
roku_find_element | Find elements by CSS-like selector |
roku_press_key | Send remote control key press (Select, Up, Down, Left, Right, Back, Home, etc.) |
roku_type_text | Type text into keyboard inputs |
roku_screenshot | Take a screenshot, optionally save to disk |
roku_launch | Launch a channel with optional deep link params |
roku_deep_link | Deep link directly into content by ID |
roku_close_app | Close the running app (press Home) |
roku_sideload | Deploy a .zip package to the device |
roku_device_info | Get device model, software version, network info |
roku_active_app | Get the currently running app |
roku_media_player | Get playback state (position, duration, format) |
roku_installed_apps | List all installed channels |
roku_console_log | Read BrightScript debug console output (errors, print statements, crashes) |
roku_console_command | Send debug commands (bt, var, cont, step, over, out) |
roku_volume | Volume up, down, or mute |
roku_input | Send custom input parameters to the running app |
Test Runner Tools
| Tool | Description |
|---|---|
roku_wait_for | Poll until a selector appears on screen with configurable timeout — use after navigation |
roku_assert_element | Assert an element exists, is focused, or has a specific attribute value — returns pass/fail JSON |
roku_sideload_and_watch | Sideload a zip + watch console for errors/crashes — returns CI-ready pass/fail report |
roku_smoke_test | Launch app, verify UI renders, optionally verify playback — full pass/fail with step detail |
Smoke Test Example
→ roku_smoke_test(
content_id: "ABC123XYZ",
media_type: "episode",
ui_timeout: 15000,
playback_timeout: 30000
)
{
"passed": true,
"message": "PASS: App launched, UI rendered, and playback started",
"steps": [
{ "name": "launch", "passed": true,
"message": "Deep linked to \"ABC123XYZ\" in channel dev" },
{ "name": "ui_visible", "passed": true,
"message": "UI rendered after 2340ms", "elapsed_ms": 2340 },
{ "name": "playback", "passed": true,
"message": "Player reached \"play\" after 8710ms", "elapsed_ms": 8710 }
],
"player_state": {
"state": "play",
"position": "00:00:08",
"duration": "00:42:17"
}
} Agent Efficiency Tools
Token-efficient alternatives to full tree scans. Use these when you only need one piece of information.
| Tool | Description |
|---|---|
roku_focused_element | Return only the currently focused element — avoids a full tree scan |
roku_screen_name | Infer the current screen name from the SceneGraph root component |
roku_console_watch | Monitor console for a pattern match during a time window — pass/fail with matching lines |
Shift Left Quality Gates
| Tool | Description |
|---|---|
roku_cert_preflight | Run Roku cert failure checklist (back nav, Home exit, relaunch, error scan) |
roku_chanperf_sample | Sample CPU usage via chanperf for a configurable duration — high watermark + pass/fail |
Cert Preflight
Runs the common Roku certification failure checks automatically:
- Back button navigation from each screen
- Home button exits cleanly
- App relaunch after Home exit
- Console error scan during the entire flow
Selector Syntax
Find elements using CSS-like selectors against SceneGraph node names:
| Selector | Example | Matches |
|---|---|---|
| Tag name | HomePage | Node with tag HomePage |
| ID | #titleLabel | Node with name="titleLabel" |
| Tag + ID | AppButton#play_button | AppButton with name="play_button" |
| Descendant | HomePage HomeHeroCarousel | HomeHeroCarousel anywhere inside HomePage |
| Child | LayoutGroup > AppLabel | AppLabel direct child of LayoutGroup |
| Attribute | [focused="true"] | Element with that attribute value |
| Tag + attr | AppButton[text="Play"] | AppButton with text="Play" |
| Adjacent sibling | Module + Module | Module preceded by another |
| nth-child | NavTab:nth-child(2) | Second NavTab among siblings |
CLI: UI Commands
# Full SceneGraph tree
npx roku-mcp ui tree --device 192.168.0.30
npx roku-mcp ui tree --depth 4
npx roku-mcp ui tree --all-attrs
# Find elements by selector
npx roku-mcp ui find "HomePage HomeHeroCarousel"
npx roku-mcp ui find "AppButton#play_button"
npx roku-mcp ui find "AppLabel" --all-attrs
# Raw XML source
npx roku-mcp ui source CLI: Input Commands
# Key presses
npx roku-mcp press Select
npx roku-mcp press Down --times 3 --delay 200
npx roku-mcp press Back
# Text entry
npx roku-mcp type "search query"
# Launch apps
npx roku-mcp launch dev
npx roku-mcp launch dev --params '{"contentId":"12345","mediaType":"episode"}' CLI: Info Commands
npx roku-mcp info device # Device model, software, network
npx roku-mcp info app # Currently running app
npx roku-mcp info apps # All installed channels
npx roku-mcp info player # Playback state CLI: Test Commands
All test commands exit with code 1 on failure, making them CI-friendly.
# Sideload and watch for errors
npx roku-dev test sideload-watch ./target/build.zip --duration 30000
# Full smoke test with playback verification
npx roku-dev test smoke --content-id ABC123XYZ --media-type episode
# Run cert preflight checklist
npx roku-dev test cert-preflight
# Sample CPU during playback
npx roku-dev test chanperf --duration 15000 --threshold 70 Library Usage
Use roku-mcp as a TypeScript library for custom tooling:
import { EcpClient, Key, parseUiXml, findElement } from 'roku-mcp';
const roku = new EcpClient('192.168.0.30');
// Send keys
await roku.press(Key.Down, { times: 3 });
await roku.press(Key.Select);
// Inspect the UI
const xml = await roku.queryAppUi();
const tree = await parseUiXml(xml);
const button = findElement(tree, 'AppButton#play_button');
console.log(button?.attrs.focused); // "true"
console.log(button?.attrs.text); // "Play"
// Query state
const player = await roku.queryMediaPlayer();
const app = await roku.queryActiveApp(); HTTP Transport
For editors that don't support stdio (like VS Code with Copilot), use the HTTP transport:
# Start the HTTP server
ROKU_DEVICE_IP=192.168.0.30 npx --package @danecodes/roku-mcp roku-mcp-http
# Custom port
ROKU_MCP_PORT=8888 ROKU_DEVICE_IP=192.168.0.30 npx --package @danecodes/roku-mcp roku-mcp-http The server runs at http://localhost:3141/mcp by default and speaks the MCP HTTP transport protocol.
App Context File
Provide your agent with app-specific context by creating a roku-app.md file and setting the ROKU_APP_CONTEXT environment variable:
{
"mcpServers": {
"roku": {
"type": "stdio",
"command": "npx",
"args": ["-y", "--package", "@danecodes/roku-mcp", "roku-mcp-server"],
"env": {
"ROKU_DEVICE_IP": "192.168.0.30",
"ROKU_APP_CONTEXT": "./roku-app.md"
}
}
}
} The context file can contain screen names, navigation patterns, content IDs, and any other information your agent needs to work effectively with your specific Roku app.
Auto-Approve Tool Calls
To skip the approval prompt for every Roku tool call in Claude Code, add to ~/.claude/settings.json:
{
"permissions": {
"allow": ["mcp__roku"]
}
} You can also use roku-mcp init to append a Roku playbook to your project's CLAUDE.md, giving the agent built-in knowledge of how to navigate your app.