roku-dev-doc-mcp
Provides search and retrieval for Roku and BrightScript development documentation, covering components, interfaces, SceneGraph nodes, language syntax, manifest keys, and developer guides.
Click on "Install 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., "@roku-dev-doc-mcpsearch the Roku docs for roArray"
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.
roku-dev-doc-mcp
A local Model Context Protocol server that
serves Roku / BrightScript development documentation, mirrored from the docs/
folder of rokudev/dev-doc (branch v2.0)
and indexed in SQLite with an FTS5 full-text index.
Read-only reference tooling: it never writes to the upstream repo, and needs no authentication for the public repo.
600+ documents, ~6 MB of markdown, indexed in about a second
Offline after first sync — everything is served from a local SQLite file
Context-aware reads — large docs are returned as a section outline rather than dumping 90k tokens into your context window
Install
npm install -g .Then point an MCP client at the roku-dev-doc-mcp command.
Claude Code
claude mcp add roku-docs -- roku-dev-doc-mcpClaude Desktop / generic MCP config
{
"mcpServers": {
"roku-docs": {
"command": "roku-dev-doc-mcp"
}
}
}During development, before installing globally:
{
"mcpServers": {
"roku-docs": {
"command": "node",
"args": ["/absolute/path/to/roku-dev-doc-mcp/bin/roku-dev-doc-mcp.js"]
}
}
}On first run the index is empty, so the server kicks off a sync in the background — it connects immediately rather than blocking the client's startup, and tools report that the index is still building until it lands (about a second). To pre-warm it instead:
roku-dev-doc-mcp --syncAfter that, the server refreshes itself in the background whenever the index is
more than 7 days old. That refresh never blocks startup and never makes
tools unavailable — the existing docs keep being served while it runs, so it is
invisible to callers. Set ROKU_DOCS_MCP_MAX_AGE_DAYS to change the threshold,
or 0 to disable it and rely on manual --sync only.
Related MCP server: opencode-docs
Using it in a project
You don't need to add anything to your project's CLAUDE.md. Adding the
server is the whole setup.
The server sends its own usage guidance to the client during initialization, via
the MCP instructions field (the INSTRUCTIONS constant in src/server.js, the
single source of truth for that text). Any project that connects gets the rules
automatically: prefer a lookup over a guess for ro* / if* symbols, SceneGraph
node fields and defaults, BrightScript syntax, manifest keys and certification
requirements — plus the folder map under Where things live.
CLAUDE.md is for what the server cannot know: your target Roku OS version,
SceneGraph vs the legacy SDK, house patterns, directories to avoid. If you have
such rules and want them shared across several Roku projects, reference one file
rather than copying it, so it cannot drift:
@~/.claude/roku-project-conventions.mdCaveat. The MCP spec makes
instructionsoptional for clients to honour. Claude Code and Claude Desktop both surface it; a third-party client may not. The individual tool descriptions are therefore written to stand on their own — on a client that ignoresinstructionsthe tools still work correctly, they just lose the "prefer a lookup over a guess" nudge.
Tools
Tool | Arguments | Purpose |
|
| BM25-ranked full-text search with highlighted snippets |
|
| Read one page; large pages return an outline |
|
| Browse the folder tree |
|
| Re-sync from GitHub, returns a change summary |
search_roku_docs accepts plain keywords as well as FTS5 syntax
("exact phrase", term*, AND/OR/NOT). A malformed query degrades to a
plain keyword search rather than erroring.
get_roku_doc resolves paths leniently — roarray,
roarray.md, and the full
docs/REFERENCES/brightscript/components/roarray.md all work.
Where things live
Pass any of these as prefix to narrow a search:
Topic |
|
Components ( |
|
Interfaces ( |
|
Events ( |
|
Language / syntax |
|
SceneGraph nodes |
|
Guides, tooling, debugging, Roku Pay |
|
Device / hardware specs |
|
Components are ro*, interfaces are if* — searching the exact symbol name is
usually the fastest route. Typical flow:
search_roku_docs("Task control RUN") →
get_roku_doc("docs/REFERENCES/scenegraph/control-nodes/task.md").
Large documents
Doc sizes are heavily skewed: most are a few KB, but the largest is ~354 KB (roughly 90k tokens). Anything over 12 KB returns a numbered section outline instead of the body:
This document is too large to return whole (346.5 KB, 76 sections). Call
`get_roku_doc` again with a `section` number or heading text from this outline:
1. Ingest specifications (772 B)
2. MovieLabs (644 B)
3. Roku content policies (24 B)
…Then request a section by number (section: 24) or by heading text
(section: "closed captions"). Search results for large docs also report which
section best matches the query — so arriving from a search costs no extra round
trip, you just pass that section straight to get_roku_doc. full: true forces
the whole document.
12 KB (~3k tokens) is the measured break-even for this corpus: 80% of docs still
return whole in a single call, the worst-case return drops from ~10.5k to ~3.2k
tokens, and the high-frequency BrightScript component and interface pages
(medians ~1.4 KB and ~3.3 KB) are unaffected. Tune it with
ROKU_DOCS_MCP_MAX_DOC_BYTES if your context budget differs.
Resources
Docs are also exposed as MCP resources under docs://{path}, e.g.
docs://docs/REFERENCES/brightscript/components/roarray.md, with path
completion. Resources are the idiomatic mechanism for browsable read content;
the tools above are better suited to search and refresh.
CLI
roku-dev-doc-mcp # start the MCP server on stdio (default)
roku-dev-doc-mcp --sync # sync the index and exit
roku-dev-doc-mcp --sync --force
roku-dev-doc-mcp --status # show index status
roku-dev-doc-mcp --helpHow the sync works
A sync runs when the index is empty at startup, when it is older than the
staleness threshold at startup, or when you ask for one (refresh_roku_docs
or --sync). All startup syncs are non-blocking. Only one sync runs at a time —
concurrent callers share the same in-flight run.
Read the head SHA of the
v2.0branch. If it matches the last synced SHA and the index is non-empty, stop — nothing to do.Download the repo tarball for that exact commit (~1.4 MB, one request). Pinning to the SHA means a branch that moves mid-sync can't leave a recorded SHA that was never actually fetched.
Stream it through gunzip + tar in memory, keeping only
docs/**/*.md.Recompute each file's git blob SHA locally (
sha1("blob <len>\0" + bytes)) and compare against the stored value. Unchanged files are skipped, so a re-sync only touches what actually moved and the change summary stays accurate.Delete rows no longer present upstream, then update the sync markers.
One HTTP request per sync (plus one cheap API call for the SHA check). The tarball is never written to disk.
last_synced_at records when the index was last confirmed in sync with
upstream, so it advances even when the SHA check finds nothing to download.
Without that, an index past the staleness threshold whose upstream never moves
would re-check on every single launch instead of once per interval.
Titles and frontmatter
Every upstream doc carries YAML frontmatter, and most have no # heading at
all — so the frontmatter title is the primary source, with a first-heading
and then filename fallback. Frontmatter is parsed out into title and
excerpt columns and stripped from the stored content, so the full-text index
holds prose rather than YAML keys.
Eight docs are frontmatter-only section landing pages upstream with no body; these are indexed by title and reported as such when read.
Configuration
Variable | Purpose |
| Override the data directory |
| Raise the GitHub API rate limit (optional) |
| Whole-doc size threshold (default 12000) |
| Hard ceiling on any one response (default 60000) |
| Background refresh threshold in days (default 7, |
The SQLite file lives in a per-user data directory, not in the install directory:
macOS —
~/Library/Application Support/roku-dev-doc-mcp/docs.sqliteLinux —
$XDG_DATA_HOME/roku-dev-doc-mcp/or~/.local/share/roku-dev-doc-mcp/Windows —
%LOCALAPPDATA%\roku-dev-doc-mcp\Data\
Only one unauthenticated GitHub API call is made per sync (the branch SHA
check), so the 60/hour unauthenticated limit is not a practical concern.
GITHUB_TOKEN is only worth setting for very frequent automatic refreshes.
Development
npm install
npm testroku-dev-doc-mcp/
├── bin/roku-dev-doc-mcp.js # CLI entrypoint (shebang), arg handling only
├── src/
│ ├── server.js # MCP server, tool + resource registration
│ ├── sync.js # GitHub fetch, extract, frontmatter, upsert
│ ├── sections.js # markdown section splitting for large docs
│ ├── db.js # SQLite schema, FTS5 triggers, queries
│ └── paths.js # per-user data dir resolution
└── test/
├── parsing.test.js # frontmatter, sections, query sanitising
└── staleness.test.js # background-refresh threshold logicPlain Node.js ESM, no TypeScript, no build step. Requires Node >= 18.
License
The Unlicense — this software is released into the public domain. Do whatever you like with it: copy, modify, publish, sell, or redistribute, for any purpose, with no attribution required. See LICENSE.
Two things the dedication does not cover, since they were never mine to give away:
The documentation content itself. The docs this server mirrors belong to Roku and are reproduced unmodified from rokudev/dev-doc. The public-domain dedication applies to the code in this repository, not to the indexed text.
Dependencies. Each npm dependency keeps its own license — currently MIT (
@modelcontextprotocol/sdk,better-sqlite3,zod), ISC (yaml), and Blue Oak 1.0.0 (tar). All permissive, but they are not public domain.
Available Tools
4 toolsget_roku_docRead a Roku docARead-only
Return the markdown content of a single documentation page by path. Documents larger than 11.7 KB are returned as a numbered section outline instead; pass section (a number or heading text) to read one of those sections. Search results for such documents already name the best-matching section, so you can request it directly without fetching the outline first.
| Name | Required | Description | Default |
|---|---|---|---|
| full | No | Return the entire document even if large (may be very long) | |
| path | Yes | Doc path, e.g. "docs/REFERENCES/brightscript/components/roarray.md" | |
| section | No | Section number from the outline, or heading text to match |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true and openWorldHint=false, and the description is consistent with them — no contradiction. The description adds genuinely non-obvious behavior: the 11.7 KB threshold triggers an outline instead of content, and sections can be requested directly by number or heading text. This is exactly the kind of conditional behavior an agent could not infer from the schema.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three sentences, each earning its place: core function, conditional large-document behavior, and workflow optimization. The main purpose is front-loaded, and there is zero filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no output schema, the description carries the burden of explaining return behavior, and it covers all modes: full markdown for small docs, numbered outline for large docs, and section content when `section` is passed. Minor gaps remain — error behavior for nonexistent paths and the interaction when both `full` and `section` are supplied — but nothing that would cause a misdirected call.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3. The description adds value beyond the schema by tying `section` to the outline mechanism and explaining the search-to-section workflow, which clarifies how the parameters interact in practice rather than just what they mean.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific verb and resource: 'Return the markdown content of a single documentation page by path.' The phrase 'single... by path' clearly separates it from the sibling tools (search, list, refresh), so an agent can pick it without opening their schemas.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description gives clear workflow context: for documents over 11.7 KB, expect an outline and pass `section` to read a part; and if search results already name the best-matching section, you can skip the outline fetch entirely. It does not explicitly name sibling tools or state when-not-to-use, but the search-results reference effectively encodes the efficient call path.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_roku_docsList Roku docsARead-only
Browse the documentation tree. With no prefix, lists the top-level sections and their document counts; with a prefix, lists the subfolders and documents beneath it.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | Maximum documents to list (default 200) | |
| prefix | No | Folder to list, e.g. "docs/REFERENCES/scenegraph" |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The description adds meaningful behavioral detail beyond the readOnlyHint annotation, including the hierarchical tree browsing semantics, document counts at top level, and subfolder/document listing with a prefix. It does not contradict the annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is two concise sentences with no filler. It front-loads the main purpose and immediately gives the two relevant usage modes.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The description adequately explains the tool's behavior and return-like information (sections, counts, subfolders, documents) for a browsing operation. It is complete enough given the simple optional parameters and read-only annotation, though it does not detail error or edge-case behavior.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Both parameters are already fully described in the input schema with type information and example values. The description adds minimal new parameter context, so the baseline score of 3 is appropriate.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the tool's function: browsing the documentation tree and listing sections, subfolders, and documents. It is specific about behavior with and without a prefix, but does not explicitly differentiate itself from sibling tools like search_roku_docs.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description provides clear usage context by explaining the no-prefix and with-prefix behaviors. However, it does not state when this tool should be used over alternatives such as search_roku_docs or get_roku_doc.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
refresh_roku_docsRefresh Roku docsAIdempotent
Re-sync the local index from rokudev/dev-doc@v2.0. Skips the download when the upstream branch has not moved since the last sync; pass force: true to re-index regardless. Returns a summary of what changed.
| Name | Required | Description | Default |
|---|---|---|---|
| force | No | Re-download and re-index even if the branch has not changed |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The description adds meaningful behavior beyond the annotations: it explains that the download is skipped if the upstream branch hasn't moved, that force:true overrides this, and that a summary of changes is returned. This complements the idempotentHint and explains the mutation mechanism without contradicting any annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two tight, high-signal sentences, with the core action and the key exception front-loaded. No filler or repetition of schema content.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a tool with one optional parameter, clear annotations, and no output schema, the description covers the action, the conditional skip behavior, the override flag, and the return value. The sibling context confirms its role in the doc tool set, so nothing an agent needs to invoke it correctly is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, and the schema already explains force clearly. The description adds only a slight rephrasing ('re-index regardless'), so it does not significantly enrich parameter understanding beyond the schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb ('Re-sync'), resource ('local index'), and upstream source ('rokudev/dev-doc@v2.0'). This clearly distinguishes it from the read-only siblings search_roku_docs, get_roku_doc, and list_roku_docs.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description implies the use case: run this when you need to re-sync the local index from the upstream repo. However, it does not explicitly state when to choose this over the sibling tools or mention any preconditions like network access or repository availability.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
search_roku_docsSearch Roku docsARead-only
Full-text search across the Roku/BrightScript documentation, ranked by relevance. Supports plain keywords as well as FTS5 syntax ("exact phrase", term*, AND/OR/NOT). Returns the path, title, and a highlighted snippet for each hit — use get_roku_doc to read a result in full.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | Maximum results to return (default 10) | |
| query | Yes | Search terms, e.g. "roArray Push" or "video playback" | |
| prefix | No | Restrict to a folder, e.g. "docs/REFERENCES/brightscript" |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true, so the read-only nature is covered. The description adds meaningful behavioral detail: it returns a path, title, and highlighted snippet, supports FTS5 syntax, and ranks results by relevance. No contradiction with annotations exists.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three focused sentences, front-loaded with the core purpose, then syntax details, then return format and follow-up tool. Every sentence earns its place with no redundancy.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
There is no output schema, so the description appropriately describes the return shape (path, title, snippet). It also covers search syntax and directs the agent to the proper next step. Combined with full schema documentation for params, the agent has everything needed to call this tool correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3. The description adds value beyond the schema by explaining query flexibility ('plain keywords as well as FTS5 syntax'), giving examples, and clarifying that results are relevance-ranked. This extra context helps an agent craft effective queries.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific verb ('Full-text search'), a specific resource ('Roku/BrightScript documentation'), and the distinctive behavior ('ranked by relevance'). It clearly separates this tool from its siblings: get_roku_doc reads a full document, while search finds matches.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description tells the agent to use get_roku_doc to read a result in full, providing a clear follow-up pathway. It does not explicitly contrast with list_roku_docs or state when not to use this tool, but the search-vs-browse distinction is implied by the wording.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
TDQS
Each tool has a distinct role: search (query), get (fetch by path), list (browse tree), refresh (sync index). There is no overlap or ambiguity between them.
All tools follow a consistent verb_noun pattern with snake_case: search_, get_, list_, refresh_. The singular 'doc' for get_roku_doc is a natural contrast to the plural forms and matches the single-page return.
Four tools is well-scoped for a documentation MCP server, covering discovery, retrieval, and maintenance without unnecessary sprawl or redundancy.
The tool surface fully covers the core workflow: browse (list), search (search), read (get), and keep the index current (refresh). There are no obvious gaps for a documentation access server.
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Connectors
DevDocs.io keyless docs index + entry search + content (Angular, MDN, Rust, etc.).
Provides access to Google's public developer documentation.
Read-only search and Markdown access to liz's public docs, prompts, resources, and an MCP App.
Read-only MCP server for the OrchestKit docs: full-text search + Markdown fetch. No auth.
Related MCP Servers
- FlicenseNot gradedqualityBmaintenanceMCP server for Roku BrightScript documentation and device control, enabling doc search, device introspection, keypress/keysequence input, app launch, and sideloading.
- FlicenseNot gradedqualityDmaintenanceScrapes, stores, and searches documentation locally, enabling AI assistants to access and query documentation via MCP.4
- FlicenseNot gradedqualityCmaintenanceProvides local cached access to PaperMC documentation with full-text search, category browsing, and update detection.
- AlicenseAqualityCmaintenanceAll-in-one developer tool and MCP Server for Roku development, featuring ECP device control, automated channel sideloading, BrightScript debugging, and real-time log monitoring.3528MIT
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/artemisaiev/roku-dev-doc-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server