brs-mcp
Scaffolds runnable Roku channels from an AppSpec, zips them, and optionally sideloads them to a Roku device in developer mode.
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., "@brs-mcp Generate a screensaver channel from my AppSpec and sideload to my Roku"
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.
brs-mcp
An MCP server that scaffolds runnable Roku channels (BrightScript + SceneGraph) from a validated
AppSpec, zips them, and optionally sideloads them to a Roku in developer mode.
What it does
An AI assistant (or any MCP client) passes a strict, versioned AppSpec. The server returns a complete project tree, produces a sideload-ready zip, and optionally installs it on a Roku device. Every generated file comes from a curated, hand-authored, device-tested template; the server never asks an LLM to write BrightScript.
Same spec in, same bytes out, every time. Zips are byte-reproducible across hosts (sorted entries, fixed mtime). Re-running on a machine in a different time zone produces identical output.
For a fuller styled reference with per-tool I/O examples, see docs/index.html.
Related MCP server: Echo MCP Server
Install
For a styled walkthrough with prereqs, four numbered steps, and update guidance on one page, see docs/install.html.
npm install -g brs-mcpWire it into your MCP client (e.g., Claude Desktop):
{
"mcpServers": {
"brs-mcp": {
"command": "brs-mcp"
}
}
}The server speaks MCP over stdio. Logs go to stderr; stdout is reserved for JSON-RPC.
Verify
One-liner that cold-fetches the published package and exercises a real MCP initialize handshake. No global install required:
echo '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"x","version":"1"}}}' | npx -y brs-mcpExpected response on stdout (one JSON object, formatted here for readability):
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"protocolVersion": "2024-11-05",
"capabilities": { "tools": {} },
"serverInfo": { "name": "brs-mcp", "version": "0.1.0" }
}
}Exit code 0, stderr silent. If you see any stdout output that isn't a single JSON-RPC object, something in your shell is polluting the transport; verify with npx -y brs-mcp <<< '{}' 2>/dev/null | head -c 200.
Tools
list_templates
List every template bundled with this server.
Input:
{}Output:
{ "templates": [{ "id", "category", "version", "description" }] }
get_template_schema
Return the JSON Schema (Draft 7) for a template's AppSpec, plus a minimal example.
Input:
{ "id": "video_grid_channel" }Output:
{ "schema": <JSON Schema>, "example_spec": <AppSpec> }
generate_app
Render a Roku channel project from a validated AppSpec. Optionally zip and sideload.
Input:
{ "spec", "output_dir", "assets_root"?, "overwrite"?, "zip"?, "sideload"? }Output (success):
{ "ok": true, "project_dir", "files_written", "zip_path"?, "sideload"? }sideloadimplieszip: true(enforced by schema).
package_app
Zip an already-generated project directory into a sideload-ready archive. Validates a top-level manifest. Output is byte-reproducible.
Input:
{ "project_dir", "output_zip"? }Output:
{ "ok": true, "zip_path", "size_bytes", "entry_count" }
sideload_app
Install a zip on a Roku in developer mode via HTTP Digest-authenticated multipart POST to /plugin_install. dev_password is never logged or echoed.
Input:
{ "zip_path", "device_ip", "dev_password" }Output:
{ "ok": true, "status": "installed" | "identical", "message", "duration_ms", "raw_html"? }
Templates
screensaver
Roku screensaver channel. Three styles:
slideshow: crossfade through bundled images.animated: bouncing colored shapes (no images required).quadrant: 4-up grid of bundled images with rotating cells.
{
"template": "screensaver",
"spec_version": 1,
"app": { "name": "My Screensaver", "major_version": 1, "minor_version": 0, "build_version": 0 },
"style": "animated"
}Roku menu label note: When sideloaded via the dev web server, screensavers always appear in
Settings -> Theme -> Screensaversas the literal string(dev), not as theapp.namevalue. This is a Roku dev-build UX quirk and applies to every sideloaded screensaver including Roku's own canonical samples. Thescreensaver_titlemanifest field IS still required (and is presumably used once the screensaver is published through the channel store), but for sideload-based testing, look for the(dev)entry.
video_grid_channel
VOD grid: home (RowList) → detail → video player. Consumes mRSS, Roku Direct Publisher JSON, or a custom JSON feed.
{
"template": "video_grid_channel",
"spec_version": 1,
"app": { "name": "Example", "major_version": 1, "minor_version": 0, "build_version": 0 },
"branding": {
"primary_color": "#E50914",
"background_color": "#141414",
"text_color": "#FFFFFF",
"splash": { "hd": "./splash_hd.png", "fhd": "./splash_fhd.png" },
"icon": { "hd": "./icon_hd.png", "fhd": "./icon_fhd.png" }
},
"content": {
"feed_url": "https://example.com/feed.json",
"feed_format": "roku_direct_publisher_json"
}
}Highlights: deep-link aware (Main(args) + roInput runtime listener); centralized screen-stack with focus restoration; canonical Roku Overhang brand bar; BusySpinner during feed load; async feed fetch via roUrlTransfer.asyncGetToString.
Error taxonomy
Code | Meaning |
| Tool input or |
|
|
|
|
| Path is on the blocklist or outside an allowed directory. |
| Target directory exists and is non-empty (overwrite not requested). |
| A spec-referenced asset (icon/splash/etc.) is missing. |
|
|
| EJS render error inside a template. |
| Writer failed during atomic project write. |
| Packager failed. |
|
|
| Network failure reaching the Roku. |
| Roku is not in developer mode. |
| HTTP Digest auth was rejected. |
| Roku returned a failure body (e.g., bad archive). |
| Operation timed out before the device responded. |
Every failure response carries { ok: false, stage, code, message, details? }. stage is one of validate, render, write, package, sideload.
Architecture
Strict one-way dependency flow under src/:
tools/: MCP handlers. Composition root.templates/: Static template registry + EJS engine + render helpers. No network.build/: Atomic writer + deterministic zip packager (yazl, STORED, forced DOS timestamps). No network.device/: The ONLY module that imports a network client (undici). RFC 2617 Digest auth, multipart streaming viafs.openAsBlob, parsed Roku response markers.spec/: Shared zod schemas + error factory.
Real-device fixtures
test/fixtures/roku-responses/ contains the HTML response bodies the parser is tested against. They are captured against a live Roku via scripts/smoke.ts. Provenance and capture date live in test/fixtures/roku-responses/README.md.
Determinism
No Date.now(), no Math.random(), no clock skew leak into template output or the zip. Same AppSpec produces the same bytes on any host.
Contributing
Run npm install once after cloning. This installs the husky pre-commit hook via the prepare lifecycle script. Without it, your first commit bypasses the lint / format check.
The full local quality gate is:
npm run format:check && npm run lint && npm run typecheck && npm run build && npm testLicense
MIT; see LICENSE.
Available Tools
5 toolsgenerate_appB
Render a Roku channel project from a validated AppSpec and write it to output_dir. Set zip: true to also produce a sideload-ready archive at <output_dir>.zip. Set sideload: { device_ip, dev_password } to install the zip on a Roku device in developer mode (this implies zip: true).
Inputs: output_dir (required), spec, assets_root, overwrite, +2 more. Returns: ok, project_dir, files_written, zip_path, +1 more
get_template_schemaA
Return the JSON Schema (Draft 7) for a registered template's AppSpec, plus a minimal example spec.
Inputs: id (required). Returns: schema, example_spec
list_templatesA
List every template bundled with this server. Returns metadata only; fetch a template's schema with get_template_schema.
Inputs: no named parameters. Returns: templates
package_appA
Zip an already-generated Roku project directory into a sideload-ready archive. Validates that project_dir contains a top-level manifest. Output zip is byte-reproducible across hosts.
Inputs: project_dir (required), output_zip. Returns: ok, zip_path, size_bytes, entry_count
sideload_appA
Install a zip on a Roku device in developer mode via HTTP Digest-authenticated multipart POST to /plugin_install. Returns the parsed device response. dev_password is never logged or echoed.
Inputs: zip_path (required), device_ip (required), dev_password (required). Returns: ok, status, message, raw_html, +1 more
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
TDQS
Scored across 5 tools
Each tool has a clearly distinct purpose: generating a project, fetching schema, listing templates, packaging, and sideloading. No overlap exists.
All tool names follow a consistent verb_noun pattern in lowercase snake_case (generate_app, get_template_schema, list_templates, package_app, sideload_app), making the set predictable.
With 5 tools covering the core workflow of generating, packaging, and deploying Roku channels, the count is well-scoped and appropriate for the domain.
The tool surface covers the main lifecycle: template discovery, schema retrieval, project generation, packaging, and sideloading. A minor gap is the lack of a standalone validation tool, but generate_app implicitly validates the AppSpec.
Related MCP Connectors
An MCP server that let you interact with Cycloid.io Internal Development Portal and Platform
The official MCP Server for the Mux API
The official Planning Center MCP server for interacting with your ministry's data.
Model Context Protocol server for the Apideck Unified API. Connect any MCP-compatible agent framework to 100+ accounting systems, HRIS platforms, file storage providers, and more through one integration. More information https://www.apideck.com/mcp-server
Related MCP Servers
- AlicenseAqualityDmaintenanceA MCP server do create and deploy backend applications using https://heim.dev636 npm3MIT
- FlicenseNot gradedqualityDmaintenanceA simple demonstration MCP server that provides an echo tool and resource for learning how to build MCP servers. Serves as a starting point and template for creating custom MCP server implementations.1-
- FlicenseNot gradedqualityBmaintenanceMCP server for Roku BrightScript documentation and device control, enabling doc search, device introspection, keypress/keysequence input, app launch, and sideloading.-
- AlicenseNot gradedqualityDmaintenanceEnables building MCP servers using TypeSpec, with tools for learning, scaffolding projects, and compiling TypeSpec to generate server assets.24,572 npm9MIT