roku-mcp 1.5.2 → 1.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/README.md +176 -26
  2. package/dist/assets/streamprobe/components/StreamProbe.xml +96 -0
  3. package/dist/assets/streamprobe/manifest +11 -0
  4. package/dist/assets/streamprobe/source/main.brs +29 -0
  5. package/dist/index.d.ts.map +1 -1
  6. package/dist/index.js +8 -2
  7. package/dist/index.js.map +1 -1
  8. package/dist/stream/charles.d.ts +50 -0
  9. package/dist/stream/charles.d.ts.map +1 -0
  10. package/dist/stream/charles.js +268 -0
  11. package/dist/stream/charles.js.map +1 -0
  12. package/dist/stream/diagnose.d.ts +54 -0
  13. package/dist/stream/diagnose.d.ts.map +1 -0
  14. package/dist/stream/diagnose.js +568 -0
  15. package/dist/stream/diagnose.js.map +1 -0
  16. package/dist/stream/error-info.d.ts +50 -0
  17. package/dist/stream/error-info.d.ts.map +1 -0
  18. package/dist/stream/error-info.js +168 -0
  19. package/dist/stream/error-info.js.map +1 -0
  20. package/dist/stream/live.d.ts +43 -0
  21. package/dist/stream/live.d.ts.map +1 -0
  22. package/dist/stream/live.js +258 -0
  23. package/dist/stream/live.js.map +1 -0
  24. package/dist/stream/manifest.d.ts +90 -0
  25. package/dist/stream/manifest.d.ts.map +1 -0
  26. package/dist/stream/manifest.js +480 -0
  27. package/dist/stream/manifest.js.map +1 -0
  28. package/dist/stream/roku-specs.json +94 -0
  29. package/dist/tools/edit-ui.d.ts +3 -0
  30. package/dist/tools/edit-ui.d.ts.map +1 -0
  31. package/dist/tools/edit-ui.js +536 -0
  32. package/dist/tools/edit-ui.js.map +1 -0
  33. package/dist/tools/stream.d.ts +3 -0
  34. package/dist/tools/stream.d.ts.map +1 -0
  35. package/dist/tools/stream.js +194 -0
  36. package/dist/tools/stream.js.map +1 -0
  37. package/package.json +6 -2
package/README.md CHANGED
@@ -2,23 +2,23 @@
2
2
 
3
3
  A [Model Context Protocol](https://modelcontextprotocol.io/) (MCP) server for Roku device automation. Exposes tools for app deployment, ECP remote control, screenshot capture, SceneGraph node inspection, and BrightScript debug console access.
4
4
 
5
- ## Installation
5
+ ## Install
6
6
 
7
- ```bash
8
- npm install -g roku-mcp
9
- ```
10
-
11
- Or run directly with npx (no install required):
7
+ The server ships as a single `npx` command. Install once globally if you prefer, or let your MCP client launch it on demand via `npx -y roku-mcp` — both work.
12
8
 
13
9
  ```bash
14
- npx roku-mcp
10
+ npm install -g roku-mcp # optional, global install
11
+ # or just point your client at: npx -y roku-mcp
15
12
  ```
16
13
 
17
- ## Client Configuration
14
+ Configure your AI client below. Every platform uses the same launch line; only the config file and reload step change.
18
15
 
19
- ### Cursor
16
+ <details>
17
+ <summary><strong>Cursor</strong> — agent-mode tools, auto-discovers MCP servers</summary>
20
18
 
21
- 1. Create `.cursor/mcp.json` in your project root:
19
+ **Prerequisites:** Cursor 0.45+ with Agent mode.
20
+
21
+ **Install:** create `.cursor/mcp.json` in your project root (or `~/.cursor/mcp.json` for global):
22
22
 
23
23
  ```json
24
24
  {
@@ -35,14 +35,22 @@ npx roku-mcp
35
35
  }
36
36
  ```
37
37
 
38
- 2. Reload the window: `Cmd+Shift+P` (macOS) / `Ctrl+Shift+P` (Windows/Linux) → **Developer: Reload Window**
39
- 3. Go to **Cursor Settings → MCP** and verify the "roku" server shows a green status. If it appears disabled, click the toggle to enable it.
38
+ Then reload the window (`Cmd+Shift+P` / `Ctrl+Shift+P` → **Developer: Reload Window**).
39
+
40
+ **Verify:** open **Cursor Settings → MCP** and confirm the `roku` row shows a green dot. If it appears disabled, click the toggle. In a new agent chat, type `list roku tools` — you should see `roku_deploy`, `roku_screenshot`, etc.
40
41
 
41
- ### VS Code
42
+ **Routing rule (recommended):** the repo also ships an [`AGENTS.md`](./AGENTS.md). Cursor picks it up automatically, so the agent knows the canonical *observe → act → wait → verify* loop and the RTA `uiElementId` gotcha without you having to re-explain it every session.
42
43
 
43
- Requires VS Code **1.99+** with the GitHub Copilot extension.
44
+ **Troubleshoot:** if the server stays grey/disabled, check `View → Output → MCP Logs` — the most common cause is `ROKU_DEVICE_HOST` unreachable on the LAN. Omit the `env` block entirely to fall back to SSDP auto-discovery.
44
45
 
45
- 1. Create `.vscode/mcp.json` in your project root:
46
+ </details>
47
+
48
+ <details>
49
+ <summary><strong>VS Code (GitHub Copilot)</strong> — agent mode, MCP via <code>.vscode/mcp.json</code></summary>
50
+
51
+ **Prerequisites:** VS Code 1.99+ with the GitHub Copilot extension and an active Copilot subscription that includes Agent mode.
52
+
53
+ **Install:** create `.vscode/mcp.json` in your project root:
46
54
 
47
55
  ```json
48
56
  {
@@ -59,12 +67,21 @@ Requires VS Code **1.99+** with the GitHub Copilot extension.
59
67
  }
60
68
  ```
61
69
 
62
- 2. Reload the window: `Cmd+Shift+P` / `Ctrl+Shift+P` → **Developer: Reload Window**
63
- 3. Open Copilot Chat and switch to **Agent mode** (select from the chat mode dropdown). The roku tools will be available there.
70
+ Reload the window (`Cmd+Shift+P` / `Ctrl+Shift+P` → **Developer: Reload Window**).
71
+
72
+ **Verify:** open Copilot Chat, switch the chat-mode dropdown to **Agent**, and ask "what roku tools do you have?". You should see the full `roku_*` list. The MCP panel (`Cmd+Shift+P` → **MCP: List Servers**) should show `roku` as `Running`.
64
73
 
65
- ### Claude Desktop
74
+ **Troubleshoot:** if the server fails to start, run `npx -y roku-mcp --help` in a regular terminal first — that confirms Node 18+ and `npx` are working before VS Code launches it.
75
+
76
+ </details>
77
+
78
+ <details>
79
+ <summary><strong>Claude Desktop</strong> — official Anthropic desktop app</summary>
80
+
81
+ **Prerequisites:** Claude Desktop (latest from claude.ai/download), Node.js 18+ on `PATH`.
82
+
83
+ **Install:** edit your Claude Desktop config:
66
84
 
67
- Add to your Claude Desktop config file:
68
85
  - macOS: `~/Library/Application Support/Claude/claude_desktop_config.json`
69
86
  - Windows: `%APPDATA%\Claude\claude_desktop_config.json`
70
87
 
@@ -83,11 +100,20 @@ Add to your Claude Desktop config file:
83
100
  }
84
101
  ```
85
102
 
86
- Restart Claude Desktop after saving.
103
+ Fully quit and restart Claude Desktop (the menu-bar icon must close — `Cmd+Q` on macOS).
104
+
105
+ **Verify:** open a new chat. The 🛠️ icon in the input bar should list `roku-mcp` with all its tools. Ask "take a Roku screenshot" — Claude will request approval for `roku_screenshot` and run it.
87
106
 
88
- ### Windsurf
107
+ **Troubleshoot:** if no tool icon appears, check `~/Library/Logs/Claude/mcp-server-roku.log` (macOS) — `command not found: npx` means Node isn't on the GUI app's `PATH`. Either install Node via the official `.pkg`, or hard-code the absolute paths: `"command": "/usr/local/bin/node"`, `"args": ["/usr/local/bin/npx", "-y", "roku-mcp"]`.
89
108
 
90
- Create `.windsurf/mcp.json` in your project root:
109
+ </details>
110
+
111
+ <details>
112
+ <summary><strong>Windsurf</strong> — Codeium's agentic IDE</summary>
113
+
114
+ **Prerequisites:** Windsurf with Cascade enabled.
115
+
116
+ **Install:** create `.windsurf/mcp.json` in your project root (or `~/.codeium/windsurf/mcp_config.json` globally):
91
117
 
92
118
  ```json
93
119
  {
@@ -104,18 +130,75 @@ Create `.windsurf/mcp.json` in your project root:
104
130
  }
105
131
  ```
106
132
 
107
- Reload the window after saving.
133
+ Reload the window. In Cascade settings, refresh the MCP server list.
134
+
135
+ **Verify:** ask Cascade "list available MCP tools" — `roku_*` entries should appear.
108
136
 
109
- ### Any MCP-compatible client
137
+ **Troubleshoot:** Windsurf occasionally caches a stale server. If reloads don't pick up edits, fully quit Windsurf and reopen.
110
138
 
111
- The server uses **stdio transport**. Any client that supports MCP can launch it with:
139
+ </details>
140
+
141
+ <details>
142
+ <summary><strong>Any MCP-compatible client</strong> (Continue, Cline, Zed, Codex, Claude Code, OpenCode, …)</summary>
143
+
144
+ The server speaks the standard MCP **stdio** transport. Anywhere you can declare:
112
145
 
113
146
  ```
114
147
  command: npx
115
148
  args: ["-y", "roku-mcp"]
149
+ env: { ROKU_DEVICE_HOST: "...", ROKU_DEVICE_PASSWORD: "..." }
150
+ ```
151
+
152
+ …the server runs. Both env vars are optional: omit `ROKU_DEVICE_HOST` to use SSDP auto-discovery on the LAN, and pass `host` / `password` on each tool call to override.
153
+
154
+ The repo-root [`AGENTS.md`](./AGENTS.md) is picked up automatically by Claude Code, OpenCode, Codex CLI, and any other tool that follows the AGENTS.md convention, so those agents inherit the same usage guidance without extra setup.
155
+
156
+ </details>
157
+
158
+ ## Agent rules
159
+
160
+ For better agent behavior (the `observe → act → wait → verify` loop, the RTA `uiElementId` gotcha that silently breaks `roku_edit_node`, when to use which tool), the repo ships drop-in rules pre-formatted for each major agent. The body is identical — only the wrapper differs to match what each agent auto-loads.
161
+
162
+ | Agent | File | Install path |
163
+ |---|---|---|
164
+ | Cursor | [`rules/cursor/roku-mcp.mdc`](./rules/cursor/roku-mcp.mdc) | `.cursor/rules/roku-mcp.mdc` |
165
+ | Claude Code | [`rules/claude-code/CLAUDE.md`](./rules/claude-code/CLAUDE.md) | `CLAUDE.md` (repo root) |
166
+ | Claude Desktop | [`rules/claude-desktop/roku-mcp.md`](./rules/claude-desktop/roku-mcp.md) | Settings → System Prompt |
167
+ | Windsurf | [`rules/windsurf/roku-mcp.md`](./rules/windsurf/roku-mcp.md) | `.windsurf/rules/roku-mcp.md` |
168
+ | Codex CLI / OpenCode / Aider | [`AGENTS.md`](./AGENTS.md) (repo root) | `AGENTS.md` (repo root) |
169
+
170
+ See [`rules/README.md`](./rules/README.md) for one-line copy commands per agent.
171
+
172
+ ## Quickstart
173
+
174
+ Once your client is wired up, these three prompts work out of the box and exercise the main capability groups.
175
+
176
+ **1. Confirm the device is reachable**
177
+
178
+ ```
179
+ Use roku-mcp to query my Roku device info and tell me the model, firmware, and IP.
180
+ ```
181
+
182
+ The agent should call `roku_query_device_info` (and possibly `roku_discover` if no host is configured) and reply in one turn.
183
+
184
+ **2. Drive the UI with screenshots**
185
+
186
+ ```
187
+ Press Home, wait 2 seconds, take a screenshot, then move Right twice and
188
+ screenshot again. Describe what changed between the two screenshots.
116
189
  ```
117
190
 
118
- Pass `ROKU_DEVICE_HOST` and `ROKU_DEVICE_PASSWORD` as environment variables, or omit the host to use SSDP auto-discovery.
191
+ This exercises `roku_keypress`, `roku_sleep`, and `roku_screenshot` — the standard *observe → act → wait → verify* loop.
192
+
193
+ **3. Live-edit a running channel (requires RTA — see [Runtime UI Editing](#runtime-ui-editing))**
194
+
195
+ ```
196
+ On the currently running dev channel, find the label that shows the page
197
+ title, change its text to "Hello from MCP", verify the change with
198
+ roku_get_value, then change it back.
199
+ ```
200
+
201
+ The agent will `roku_query_app_ui` to locate the node, grab the `uiElementId`, call `roku_edit_node` + `roku_get_value`, and roll back — proving end-to-end RTA wiring.
119
202
 
120
203
  ## Environment Variables
121
204
 
@@ -197,6 +280,63 @@ Or simply add the two expected variables to your `.env` alongside your existing
197
280
  | `roku_console_send` | Send a command to the debug console and auto-disconnect |
198
281
  | `roku_console_disconnect` | Close the console connection (safety net) |
199
282
 
283
+ ### Runtime UI Editing
284
+
285
+ Edit the SceneGraph UI of a running sideloaded dev channel without redeploying — the same backend the SceneGraph Inspector in the [vscode-brightscript-language](https://github.com/rokucommunity/vscode-brightscript-language) extension uses. These tools speak the [roku-test-automation](https://github.com/triwav/roku-test-automation) (RTA) protocol against the channel's **On-Device Component** on TCP port 9000, so changes apply instantly with no debugger pause.
286
+
287
+ | Tool | Description |
288
+ |---|---|
289
+ | `roku_edit_node` | Set one or more fields on a node by id (visible, translation, text, color, opacity, etc.) |
290
+ | `roku_set_node_visible` | Show or hide a node by id |
291
+ | `roku_move_node` | Move a node to `[x, y]` |
292
+ | `roku_focus_node` | Set focus on a node by id (RTA `focusNode`) |
293
+ | `roku_remove_node` | Detach a node from its parent at runtime (RTA `removeNode`) |
294
+ | `roku_create_node` | Create a new node and append it to a parent by id (RTA `createChild`) |
295
+ | `roku_get_value` | Read a field value (or full `keyPath`) from the running scene (RTA `getValue`) |
296
+ | `roku_observe_field` | Wait for a field to change or match a value (RTA `onFieldChangeOnce`) — great for "wait until X" assertions |
297
+
298
+ **How it works.** Each tool opens a short-lived TCP connection to the RTA On-Device Component (port 9000) in the running channel, sends a length-prefixed JSON request (`setValue` / `getValue` / `focusNode` / ...) and parses the framed JSON response. Mutations apply on the SceneGraph thread without breaking into the debugger.
299
+
300
+ **Node addressing.** `nodeId` accepts either:
301
+ - A `uiElementId` like `RTA_1773` (visible in the `roku_query_sg_nodes` / `roku_query_app_ui` output for every node when RTA is running). This is the most robust target.
302
+ - A real BrightScript `id` attribute defined on the node in the channel source.
303
+
304
+ Anything matching `/^RTA_\d+$/` is sent with RTA `base: "elementId"`; otherwise it's sent with `base: "scene"`.
305
+
306
+ #### Required: install the RTA On-Device Component in your channel
307
+
308
+ These tools only work when the channel under test bundles RTA. You have two options:
309
+
310
+ **Option A — let `roku-debug` inject RTA on every sideload (recommended for VS Code users).**
311
+
312
+ 1. In your `launch.json`, enable:
313
+
314
+ ```json
315
+ "injectRdbOnDeviceComponent": true
316
+ ```
317
+
318
+ 2. In `source/main.brs`, add the marker comment immediately after `screen.show()`:
319
+
320
+ ```brightscript
321
+ sub main()
322
+ screen = createObject("roSGScreen")
323
+ ' ...
324
+ screen.show()
325
+ ' vscode_rdb_on_device_component_entry
326
+ ' ...
327
+ end sub
328
+ ```
329
+
330
+ **Option B — bundle the RTA component yourself.** Copy the `device/components` folder from [roku-test-automation](https://github.com/triwav/roku-test-automation) into your channel and instantiate `RTA_OnDeviceComponent` once from your scene.
331
+
332
+ Either way, on a successful install you will see this in the device log at channel launch:
333
+
334
+ ```
335
+ [RTA][INFO] OnDeviceComponent init
336
+ ```
337
+
338
+ If a runtime UI tool returns a connection-refused error, RTA is not present in the running channel — apply one of the two install options above and redeploy.
339
+
200
340
  ### BrightScript Profiler
201
341
 
202
342
  Analyze `.bsprof` files generated by Roku devices. These tools use [bsprof-cli](https://www.npmjs.com/package/bsprof-cli) to parse the binary profiler format and return structured JSON reports.
@@ -224,6 +364,16 @@ Record, analyze, and compare Perfetto traces from Roku devices. Requires **Roku
224
364
 
225
365
  Workflow: enable tracing → start recording → interact with app → stop recording → analyze. The `.trace` files can also be opened at [ui.perfetto.dev](https://ui.perfetto.dev/).
226
366
 
367
+ ### Stream Diagnosis
368
+
369
+ Figure out why an HLS/DASH stream that plays everywhere else fails on Roku.
370
+
371
+ | Tool | Description |
372
+ |---|---|
373
+ | `roku_diagnose_stream` | Correlate the Video-node `errorInfo`, the manifest (a fetchable `url` or pasted `content`), and an optional Charles/HAR capture into ranked Roku-specific root causes (cause, evidence, confidence, severity, fix, doc link). Optionally `captureLive=true` deploys the bundled StreamProbe harness to capture the device error first. |
374
+
375
+ It detects Roku-specific gotchas such as muxed audio+video in fMP4/CMAF HLS (plays video but silent), video codecs/levels beyond the hardware decoder (e.g. 4K H.264 or 8K HEVC), token/403-gated CDNs, and DRM license rejections. The static manifest checks are treated as hints; when you supply the real device `errorInfo` the diagnosis is corroborated to high confidence.
376
+
227
377
  ## Requirements
228
378
 
229
379
  - Node.js 18+
@@ -0,0 +1,96 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+
3
+ <!-- StreamProbe scene: plays the stream described by deep-link args and exposes
4
+ the Video node error fields (errorCode/errorMsg/errorInfo) for RTA capture. -->
5
+
6
+ <component name="StreamProbe" extends="Scene">
7
+
8
+ <interface>
9
+ <field id="launchArgs" type="assocarray" onChange="onLaunchArgs" />
10
+ <!-- Mirror fields the diagnoser reads via RTA getValue. -->
11
+ <field id="probeState" type="string" value="idle" />
12
+ <field id="probeErrorCode" type="integer" value="0" />
13
+ <field id="probeErrorMsg" type="string" value="" />
14
+ <field id="probeErrorInfo" type="string" value="" />
15
+ </interface>
16
+
17
+ <script type="text/brightscript">
18
+ <![CDATA[
19
+
20
+ sub init()
21
+ m.video = m.top.findNode("probeVideo")
22
+ m.video.observeField("state", "onVideoState")
23
+ end sub
24
+
25
+ ' Re-entrant: launch args may arrive after init.
26
+ sub onLaunchArgs()
27
+ startPlayback(m.top.launchArgs)
28
+ end sub
29
+
30
+ sub startPlayback(args as dynamic)
31
+ if args = invalid then return
32
+
33
+ url = pickArg(args, "input_url", "url")
34
+ if url = invalid or url = "" then return
35
+
36
+ format = pickArg(args, "input_format", "format")
37
+ if format = invalid or format = "" then format = "hls"
38
+
39
+ content = createObject("RoSGNode", "ContentNode")
40
+ content.title = "StreamProbe"
41
+ content.streamFormat = format
42
+ content.url = url
43
+
44
+ keySystem = pickArg(args, "input_drm", "drm")
45
+ licenseUrl = pickArg(args, "input_license_url", "licenseServerURL")
46
+ if keySystem <> invalid and keySystem <> "" then
47
+ drmParams = { KeySystem: keySystem }
48
+ if licenseUrl <> invalid and licenseUrl <> "" then
49
+ drmParams.licenseServerURL = licenseUrl
50
+ end if
51
+ headerName = pickArg(args, "input_license_header_name", invalid)
52
+ headerValue = pickArg(args, "input_license_header_value", invalid)
53
+ if headerName <> invalid and headerName <> "" then
54
+ drmParams.licenseReqHeaders = [ { name: headerName, value: headerValue } ]
55
+ end if
56
+ content.drmParams = drmParams
57
+ end if
58
+
59
+ m.video.content = content
60
+ m.video.setFocus(true)
61
+ m.video.control = "play"
62
+ m.top.probeState = "requested"
63
+ end sub
64
+
65
+ function pickArg(args as object, primary as string, fallback as dynamic) as dynamic
66
+ if args.doesExist(primary) then return args[primary]
67
+ if fallback <> invalid and args.doesExist(fallback) then return args[fallback]
68
+ return invalid
69
+ end function
70
+
71
+ sub onVideoState()
72
+ st = m.video.state
73
+ m.top.probeState = st
74
+ ' Mirror to console (port 8085) so the error can be captured without RTA.
75
+ print "[StreamProbe] state changed "; st
76
+ if st = "error" then
77
+ m.top.probeErrorCode = m.video.errorCode
78
+ m.top.probeErrorMsg = m.video.errorMsg
79
+ info = m.video.errorInfo
80
+ print "[StreamProbe] errorCode = "; m.video.errorCode
81
+ print "[StreamProbe] errorMsg = "; m.video.errorMsg
82
+ if info <> invalid then
83
+ m.top.probeErrorInfo = formatJSON(info)
84
+ print "[StreamProbe] errorInfo = "; m.top.probeErrorInfo
85
+ end if
86
+ end if
87
+ end sub
88
+
89
+ ]]>
90
+ </script>
91
+
92
+ <children>
93
+ <Video id="probeVideo" width="1920" height="1080" />
94
+ </children>
95
+
96
+ </component>
@@ -0,0 +1,11 @@
1
+ title=StreamProbe
2
+ subtitle=roku-mcp stream diagnoser harness
3
+ major_version=1
4
+ minor_version=0
5
+ build_version=00001
6
+
7
+ ui_resolutions=fhd
8
+ rsg_version=1.2
9
+
10
+ requires_widevine_drm=1
11
+ requires_widevine_version=1.0
@@ -0,0 +1,29 @@
1
+ ' StreamProbe — minimal harness used by roku-mcp's roku_diagnose_stream tool to
2
+ ' attempt playback of an arbitrary stream and expose the Video node's error
3
+ ' fields for capture over RTA.
4
+
5
+ sub Main(args as dynamic)
6
+ showStreamProbe(args)
7
+ end sub
8
+
9
+ sub showStreamProbe(args as dynamic)
10
+ screen = CreateObject("roSGScreen")
11
+ m.port = CreateObject("roMessagePort")
12
+ screen.setMessagePort(m.port)
13
+ scene = screen.CreateScene("StreamProbe")
14
+ screen.show()
15
+ ' vscode_rdb_on_device_component_entry
16
+
17
+ ' Forward deep-link launch args (input_url, input_format, input_*) to the scene.
18
+ if args <> invalid
19
+ scene.launchArgs = args
20
+ end if
21
+
22
+ while true
23
+ msg = wait(0, m.port)
24
+ msgType = type(msg)
25
+ if msgType = "roSGScreenEvent"
26
+ if msg.isScreenClosed() then return
27
+ end if
28
+ end while
29
+ end sub
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA,OAAO,eAAe,CAAC;AASvB,MAAM,CAAC,OAAO,UAAU,YAAY,CAAC,QAAQ,CAAC,EAAE;IAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAclF"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA,OAAO,eAAe,CAAC;AAWvB,MAAM,CAAC,OAAO,UAAU,YAAY,CAAC,QAAQ,CAAC,EAAE;IAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgBlF"}
package/dist/index.js CHANGED
@@ -7,10 +7,12 @@ import { registerScreenshotTools } from './tools/screenshot.js';
7
7
  import { registerConsoleTools } from './tools/console.js';
8
8
  import { registerBsprofTools } from './tools/bsprof.js';
9
9
  import { registerPerfettoTools } from './tools/perfetto.js';
10
+ import { registerEditUiTools } from './tools/edit-ui.js';
11
+ import { registerStreamTools } from './tools/stream.js';
10
12
  export default function createServer(_options) {
11
13
  const server = new McpServer({
12
14
  name: 'roku-mcp',
13
- version: '1.5.2',
15
+ version: '1.7.0',
14
16
  });
15
17
  registerDeployTools(server);
16
18
  registerEcpTools(server);
@@ -18,6 +20,8 @@ export default function createServer(_options) {
18
20
  registerConsoleTools(server);
19
21
  registerBsprofTools(server);
20
22
  registerPerfettoTools(server);
23
+ registerEditUiTools(server);
24
+ registerStreamTools(server);
21
25
  return server.server;
22
26
  }
23
27
  const isDirectRun = typeof process !== 'undefined' &&
@@ -28,7 +32,7 @@ if (isDirectRun) {
28
32
  const { StdioServerTransport } = await import('@modelcontextprotocol/sdk/server/stdio.js');
29
33
  const server = new McpServer({
30
34
  name: 'roku-mcp',
31
- version: '1.5.2',
35
+ version: '1.7.0',
32
36
  });
33
37
  registerDeployTools(server);
34
38
  registerEcpTools(server);
@@ -36,6 +40,8 @@ if (isDirectRun) {
36
40
  registerConsoleTools(server);
37
41
  registerBsprofTools(server);
38
42
  registerPerfettoTools(server);
43
+ registerEditUiTools(server);
44
+ registerStreamTools(server);
39
45
  const transport = new StdioServerTransport();
40
46
  await server.connect(transport);
41
47
  })();
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA,OAAO,eAAe,CAAC;AACvB,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AACxD,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAClD,OAAO,EAAE,uBAAuB,EAAE,MAAM,uBAAuB,CAAC;AAChE,OAAO,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAC1D,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AACxD,OAAO,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AAE5D,MAAM,CAAC,OAAO,UAAU,YAAY,CAAC,QAA8C;IACjF,MAAM,MAAM,GAAG,IAAI,SAAS,CAAC;QAC3B,IAAI,EAAE,UAAU;QAChB,OAAO,EAAE,OAAO;KACjB,CAAC,CAAC;IAEH,mBAAmB,CAAC,MAAM,CAAC,CAAC;IAC5B,gBAAgB,CAAC,MAAM,CAAC,CAAC;IACzB,uBAAuB,CAAC,MAAM,CAAC,CAAC;IAChC,oBAAoB,CAAC,MAAM,CAAC,CAAC;IAC7B,mBAAmB,CAAC,MAAM,CAAC,CAAC;IAC5B,qBAAqB,CAAC,MAAM,CAAC,CAAC;IAE9B,OAAO,MAAM,CAAC,MAAM,CAAC;AACvB,CAAC;AAED,MAAM,WAAW,GACf,OAAO,OAAO,KAAK,WAAW;IAC9B,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;IACf,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC,CAAC;AAEnF,IAAI,WAAW,EAAE,CAAC;IAChB,CAAC,KAAK,IAAI,EAAE;QACV,MAAM,EAAE,oBAAoB,EAAE,GAAG,MAAM,MAAM,CAAC,2CAA2C,CAAC,CAAC;QAC3F,MAAM,MAAM,GAAG,IAAI,SAAS,CAAC;YAC3B,IAAI,EAAE,UAAU;YAChB,OAAO,EAAE,OAAO;SACjB,CAAC,CAAC;QAEH,mBAAmB,CAAC,MAAM,CAAC,CAAC;QAC5B,gBAAgB,CAAC,MAAM,CAAC,CAAC;QACzB,uBAAuB,CAAC,MAAM,CAAC,CAAC;QAChC,oBAAoB,CAAC,MAAM,CAAC,CAAC;QAC7B,mBAAmB,CAAC,MAAM,CAAC,CAAC;QAC5B,qBAAqB,CAAC,MAAM,CAAC,CAAC;QAE9B,MAAM,SAAS,GAAG,IAAI,oBAAoB,EAAE,CAAC;QAC7C,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IAClC,CAAC,CAAC,EAAE,CAAC;AACP,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA,OAAO,eAAe,CAAC;AACvB,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AACxD,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAClD,OAAO,EAAE,uBAAuB,EAAE,MAAM,uBAAuB,CAAC;AAChE,OAAO,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAC1D,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AACxD,OAAO,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AAC5D,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAExD,MAAM,CAAC,OAAO,UAAU,YAAY,CAAC,QAA8C;IACjF,MAAM,MAAM,GAAG,IAAI,SAAS,CAAC;QAC3B,IAAI,EAAE,UAAU;QAChB,OAAO,EAAE,OAAO;KACjB,CAAC,CAAC;IAEH,mBAAmB,CAAC,MAAM,CAAC,CAAC;IAC5B,gBAAgB,CAAC,MAAM,CAAC,CAAC;IACzB,uBAAuB,CAAC,MAAM,CAAC,CAAC;IAChC,oBAAoB,CAAC,MAAM,CAAC,CAAC;IAC7B,mBAAmB,CAAC,MAAM,CAAC,CAAC;IAC5B,qBAAqB,CAAC,MAAM,CAAC,CAAC;IAC9B,mBAAmB,CAAC,MAAM,CAAC,CAAC;IAC5B,mBAAmB,CAAC,MAAM,CAAC,CAAC;IAE5B,OAAO,MAAM,CAAC,MAAM,CAAC;AACvB,CAAC;AAED,MAAM,WAAW,GACf,OAAO,OAAO,KAAK,WAAW;IAC9B,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;IACf,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC,CAAC;AAEnF,IAAI,WAAW,EAAE,CAAC;IAChB,CAAC,KAAK,IAAI,EAAE;QACV,MAAM,EAAE,oBAAoB,EAAE,GAAG,MAAM,MAAM,CAAC,2CAA2C,CAAC,CAAC;QAC3F,MAAM,MAAM,GAAG,IAAI,SAAS,CAAC;YAC3B,IAAI,EAAE,UAAU;YAChB,OAAO,EAAE,OAAO;SACjB,CAAC,CAAC;QAEH,mBAAmB,CAAC,MAAM,CAAC,CAAC;QAC5B,gBAAgB,CAAC,MAAM,CAAC,CAAC;QACzB,uBAAuB,CAAC,MAAM,CAAC,CAAC;QAChC,oBAAoB,CAAC,MAAM,CAAC,CAAC;QAC7B,mBAAmB,CAAC,MAAM,CAAC,CAAC;QAC5B,qBAAqB,CAAC,MAAM,CAAC,CAAC;QAC9B,mBAAmB,CAAC,MAAM,CAAC,CAAC;QAC5B,mBAAmB,CAAC,MAAM,CAAC,CAAC;QAE5B,MAAM,SAAS,GAAG,IAAI,oBAAoB,EAAE,CAAC;QAC7C,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IAClC,CAAC,CAAC,EAAE,CAAC;AACP,CAAC"}
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Parse a Charles Proxy session (or HAR capture) into normalized HTTP entries
3
+ * and classify each request as manifest / segment / license / key / other.
4
+ *
5
+ * Supported export formats:
6
+ * - Charles ".chlsj" (JSON Session File): either a single JSON array of
7
+ * request objects, or newline-delimited JSON (one object per line).
8
+ * - ".har" (HTTP Archive): the standard `{ log: { entries: [...] } }` shape
9
+ * produced by Charles "Export > HAR" and by browser devtools.
10
+ *
11
+ * NOTE: the binary ".chls" format is NOT supported (it is a proprietary binary
12
+ * blob). Callers should instruct the user to export as ".chlsj" or ".har".
13
+ *
14
+ * Charles .chlsj entry shape (varies slightly by version) roughly looks like:
15
+ * {
16
+ * "host": "...", "method": "GET", "path": "/x.m3u8",
17
+ * "scheme": "https", "status": 200,
18
+ * "request": { "header": { "headers": [{ "name", "value" }] }, "body": {...} },
19
+ * "response": { "header": { "headers": [...] }, "body": { "text"|"encoded", "contentType" } },
20
+ * "times": { "totalDuration": 123 }
21
+ * }
22
+ */
23
+ export interface NormalizedHttpEntry {
24
+ url: string;
25
+ host: string;
26
+ path: string;
27
+ method: string;
28
+ status: number;
29
+ reqHeaders: Record<string, string>;
30
+ respHeaders: Record<string, string>;
31
+ /** Decoded response body text when available (manifests, license errors). */
32
+ respBody?: string;
33
+ /** Response content-type, lowercased. */
34
+ mimeType?: string;
35
+ durationMs?: number;
36
+ kind: HttpEntryKind;
37
+ }
38
+ export type HttpEntryKind = 'manifest' | 'segment' | 'license' | 'key' | 'other';
39
+ export interface CharlesSession {
40
+ entries: NormalizedHttpEntry[];
41
+ /** Entries with a non-2xx status or otherwise suspicious response. */
42
+ failing: NormalizedHttpEntry[];
43
+ format: 'chlsj' | 'har';
44
+ notes: string[];
45
+ }
46
+ /** Parse the raw text of a Charles .chlsj or .har file. */
47
+ export declare function parseCharlesText(text: string): CharlesSession;
48
+ /** A request is "failing" if it has a non-2xx status, or an empty body where one is expected. */
49
+ export declare function isFailing(entry: NormalizedHttpEntry): boolean;
50
+ //# sourceMappingURL=charles.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"charles.d.ts","sourceRoot":"","sources":["../../src/stream/charles.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,MAAM,WAAW,mBAAmB;IAClC,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACnC,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACpC,6EAA6E;IAC7E,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,yCAAyC;IACzC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,aAAa,CAAC;CACrB;AAED,MAAM,MAAM,aAAa,GAAG,UAAU,GAAG,SAAS,GAAG,SAAS,GAAG,KAAK,GAAG,OAAO,CAAC;AAEjF,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,mBAAmB,EAAE,CAAC;IAC/B,sEAAsE;IACtE,OAAO,EAAE,mBAAmB,EAAE,CAAC;IAC/B,MAAM,EAAE,OAAO,GAAG,KAAK,CAAC;IACxB,KAAK,EAAE,MAAM,EAAE,CAAC;CACjB;AAqKD,2DAA2D;AAC3D,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,cAAc,CA4D7D;AAQD,iGAAiG;AACjG,wBAAgB,SAAS,CAAC,KAAK,EAAE,mBAAmB,GAAG,OAAO,CAQ7D"}