brs-mcp

brs-mcp

An MCP server that scaffolds runnable Roku channels from a validated AppSpec, zips them, and optionally sideloads to a Roku device.

Category
访问服务器

README

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.

Node License: MIT MCP

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.

Install

For a styled walkthrough with prereqs, four numbered steps, and update guidance on one page, see docs/install.html.

npm install -g brs-mcp

Wire 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-mcp

Expected 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"? }
  • sideload implies zip: 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 -> Screensavers as the literal string (dev), not as the app.name value. This is a Roku dev-build UX quirk and applies to every sideloaded screensaver including Roku's own canonical samples. The screensaver_title manifest 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
SCHEMA_MISMATCH Tool input or AppSpec failed validation.
UNKNOWN_TEMPLATE spec.template is not a registered id.
UNSUPPORTED_SPEC_VERSION spec.spec_version is not supported by this server.
PATH_REFUSED Path is on the blocklist or outside an allowed directory.
OUTPUT_DIR_NOT_EMPTY Target directory exists and is non-empty (overwrite not requested).
ASSET_NOT_FOUND A spec-referenced asset (icon/splash/etc.) is missing.
PROJECT_DIR_INVALID package_app input is not a valid Roku project directory.
RENDER_FAILED EJS render error inside a template.
WRITE_FAILED Writer failed during atomic project write.
ZIP_FAILED Packager failed.
ZIP_NOT_FOUND sideload_app could not find the supplied zip.
DEVICE_UNREACHABLE Network failure reaching the Roku.
DEVICE_NOT_DEV_MODE Roku is not in developer mode.
DEVICE_AUTH_FAILED HTTP Digest auth was rejected.
SIDELOAD_REJECTED Roku returned a failure body (e.g., bad archive).
SIDELOAD_TIMEOUT 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 via fs.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 test

License

MIT; see LICENSE.

推荐服务器

Baidu Map

Baidu Map

百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。

官方
精选
JavaScript
Playwright MCP Server

Playwright MCP Server

一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。

官方
精选
TypeScript
Magic Component Platform (MCP)

Magic Component Platform (MCP)

一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。

官方
精选
本地
TypeScript
Audiense Insights MCP Server

Audiense Insights MCP Server

通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。

官方
精选
本地
TypeScript
VeyraX

VeyraX

一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。

官方
精选
本地
graphlit-mcp-server

graphlit-mcp-server

模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。

官方
精选
TypeScript
Kagi MCP Server

Kagi MCP Server

一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。

官方
精选
Python
e2b-mcp-server

e2b-mcp-server

使用 MCP 通过 e2b 运行代码。

官方
精选
Neon MCP Server

Neon MCP Server

用于与 Neon 管理 API 和数据库交互的 MCP 服务器

官方
精选
Exa MCP Server

Exa MCP Server

模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。

官方
精选