roku-mcp 1.4.0 → 1.5.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.
- package/README.md +15 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -2
- package/dist/index.js.map +1 -1
- package/dist/tools/perfetto.d.ts +3 -0
- package/dist/tools/perfetto.d.ts.map +1 -0
- package/dist/tools/perfetto.js +222 -0
- package/dist/tools/perfetto.js.map +1 -0
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -209,6 +209,21 @@ Analyze `.bsprof` files generated by Roku devices. These tools use [bsprof-cli](
|
|
|
209
209
|
|
|
210
210
|
To generate a `.bsprof` file, enable the profiler in your Roku app's `manifest` (`bs_prof_enabled=true`), run the app, and download the profile from `http://<device-ip>:8080`.
|
|
211
211
|
|
|
212
|
+
### Perfetto Tracing
|
|
213
|
+
|
|
214
|
+
Record, analyze, and compare Perfetto traces from Roku devices. Requires **Roku OS 15.1+**. These tools use [roku-perfetto](https://www.npmjs.com/package/roku-perfetto) for ECP control, WebSocket recording, and PerfettoSQL analysis.
|
|
215
|
+
|
|
216
|
+
| Tool | Description |
|
|
217
|
+
|---|---|
|
|
218
|
+
| `roku_perfetto_enable` | Enable Perfetto tracing for a channel via ECP (tracing starts on next app launch) |
|
|
219
|
+
| `roku_perfetto_start` | Start recording a Perfetto trace via WebSocket binary stream |
|
|
220
|
+
| `roku_perfetto_stop` | Stop recording and return file path, size, and duration |
|
|
221
|
+
| `analyze_perfetto` | Analyze a .trace file — summary, frame-drops, key-events, observers, rendezvous, set-fields, or threads. Returns AI-friendly structured JSON with suggestions. |
|
|
222
|
+
| `compare_perfetto` | Compare two .trace files to detect performance regressions and improvements |
|
|
223
|
+
| `query_perfetto` | Run a raw PerfettoSQL query against a trace file for custom analysis |
|
|
224
|
+
|
|
225
|
+
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
|
+
|
|
212
227
|
## Requirements
|
|
213
228
|
|
|
214
229
|
- Node.js 18+
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA,OAAO,eAAe,CAAC;
|
|
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"}
|
package/dist/index.js
CHANGED
|
@@ -6,16 +6,18 @@ import { registerEcpTools } from './tools/ecp.js';
|
|
|
6
6
|
import { registerScreenshotTools } from './tools/screenshot.js';
|
|
7
7
|
import { registerConsoleTools } from './tools/console.js';
|
|
8
8
|
import { registerBsprofTools } from './tools/bsprof.js';
|
|
9
|
+
import { registerPerfettoTools } from './tools/perfetto.js';
|
|
9
10
|
export default function createServer(_options) {
|
|
10
11
|
const server = new McpServer({
|
|
11
12
|
name: 'roku-mcp',
|
|
12
|
-
version: '1.
|
|
13
|
+
version: '1.5.0',
|
|
13
14
|
});
|
|
14
15
|
registerDeployTools(server);
|
|
15
16
|
registerEcpTools(server);
|
|
16
17
|
registerScreenshotTools(server);
|
|
17
18
|
registerConsoleTools(server);
|
|
18
19
|
registerBsprofTools(server);
|
|
20
|
+
registerPerfettoTools(server);
|
|
19
21
|
return server.server;
|
|
20
22
|
}
|
|
21
23
|
const isDirectRun = typeof process !== 'undefined' &&
|
|
@@ -26,13 +28,14 @@ if (isDirectRun) {
|
|
|
26
28
|
const { StdioServerTransport } = await import('@modelcontextprotocol/sdk/server/stdio.js');
|
|
27
29
|
const server = new McpServer({
|
|
28
30
|
name: 'roku-mcp',
|
|
29
|
-
version: '1.
|
|
31
|
+
version: '1.5.0',
|
|
30
32
|
});
|
|
31
33
|
registerDeployTools(server);
|
|
32
34
|
registerEcpTools(server);
|
|
33
35
|
registerScreenshotTools(server);
|
|
34
36
|
registerConsoleTools(server);
|
|
35
37
|
registerBsprofTools(server);
|
|
38
|
+
registerPerfettoTools(server);
|
|
36
39
|
const transport = new StdioServerTransport();
|
|
37
40
|
await server.connect(transport);
|
|
38
41
|
})();
|
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;
|
|
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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"perfetto.d.ts","sourceRoot":"","sources":["../../src/tools/perfetto.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAepE,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CAkP7D"}
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import os from 'os';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
import { PerfettoClient, comparePerfetto, openTrace } from 'roku-perfetto';
|
|
5
|
+
import { resolveHost, friendlyError } from '../roku-config.js';
|
|
6
|
+
function jsonify(value) {
|
|
7
|
+
return JSON.stringify(value, (_k, v) => (typeof v === 'bigint' ? Number(v) : v), 2);
|
|
8
|
+
}
|
|
9
|
+
let client = null;
|
|
10
|
+
let clientHost = null;
|
|
11
|
+
export function registerPerfettoTools(server) {
|
|
12
|
+
// ---------------------------------------------------------------------------
|
|
13
|
+
// roku_perfetto_enable
|
|
14
|
+
// ---------------------------------------------------------------------------
|
|
15
|
+
server.registerTool('roku_perfetto_enable', {
|
|
16
|
+
description: 'Enable Perfetto tracing for a Roku channel via ECP. Tracing starts automatically on each app launch. Requires Roku OS 15.1+. There is no disable command — enabled channels are cleared on reboot.',
|
|
17
|
+
inputSchema: {
|
|
18
|
+
host: z.string().optional().describe('IP address or hostname of the Roku device'),
|
|
19
|
+
channelId: z
|
|
20
|
+
.string()
|
|
21
|
+
.optional()
|
|
22
|
+
.default('dev')
|
|
23
|
+
.describe('Channel ID to enable tracing for (default: "dev" for sideloaded apps)'),
|
|
24
|
+
},
|
|
25
|
+
}, async (params) => {
|
|
26
|
+
try {
|
|
27
|
+
const host = await resolveHost(params);
|
|
28
|
+
client = new PerfettoClient(host);
|
|
29
|
+
clientHost = host;
|
|
30
|
+
const result = await client.enable(params.channelId ?? 'dev');
|
|
31
|
+
return {
|
|
32
|
+
content: [{ type: 'text', text: jsonify(result) }],
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
catch (error) {
|
|
36
|
+
return {
|
|
37
|
+
content: [{ type: 'text', text: `Enable failed: ${friendlyError(error)}` }],
|
|
38
|
+
isError: true,
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
// ---------------------------------------------------------------------------
|
|
43
|
+
// roku_perfetto_start
|
|
44
|
+
// ---------------------------------------------------------------------------
|
|
45
|
+
server.registerTool('roku_perfetto_start', {
|
|
46
|
+
description: 'Start recording a Perfetto trace from the Roku device. Opens a binary WebSocket to stream trace data to a local file. Only one recording session at a time. Call roku_perfetto_enable first.',
|
|
47
|
+
inputSchema: {
|
|
48
|
+
host: z.string().optional().describe('IP address or hostname of the Roku device'),
|
|
49
|
+
channelId: z.string().optional().default('dev').describe('Channel ID (default: "dev")'),
|
|
50
|
+
outDir: z.string().optional().describe('Directory to save the trace file (default: OS temp directory)'),
|
|
51
|
+
outFile: z.string().optional().describe('Filename without extension (default: timestamped name)'),
|
|
52
|
+
},
|
|
53
|
+
}, async (params) => {
|
|
54
|
+
try {
|
|
55
|
+
const host = await resolveHost(params);
|
|
56
|
+
if (!client || clientHost !== host) {
|
|
57
|
+
client = new PerfettoClient(host);
|
|
58
|
+
clientHost = host;
|
|
59
|
+
}
|
|
60
|
+
const dir = params.outDir ?? os.tmpdir();
|
|
61
|
+
const name = params.outFile ?? `roku-perfetto-${Date.now()}`;
|
|
62
|
+
const filePath = path.join(dir, `${name}.trace`);
|
|
63
|
+
const session = await client.startRecording(filePath, params.channelId ?? 'dev');
|
|
64
|
+
return {
|
|
65
|
+
content: [{
|
|
66
|
+
type: 'text',
|
|
67
|
+
text: `Recording Perfetto trace on ${session.host} → ${session.filePath}\nUse roku_perfetto_stop to end recording.`,
|
|
68
|
+
}],
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
catch (error) {
|
|
72
|
+
return {
|
|
73
|
+
content: [{ type: 'text', text: `Start failed: ${friendlyError(error)}` }],
|
|
74
|
+
isError: true,
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
// ---------------------------------------------------------------------------
|
|
79
|
+
// roku_perfetto_stop
|
|
80
|
+
// ---------------------------------------------------------------------------
|
|
81
|
+
server.registerTool('roku_perfetto_stop', {
|
|
82
|
+
description: 'Stop the active Perfetto trace recording. Returns the file path, size, and duration. The resulting .trace file can be opened at https://ui.perfetto.dev/ or analyzed with analyze_perfetto.',
|
|
83
|
+
}, async () => {
|
|
84
|
+
try {
|
|
85
|
+
if (!client || !client.isRecording()) {
|
|
86
|
+
return {
|
|
87
|
+
content: [{ type: 'text', text: 'No active Perfetto recording. Use roku_perfetto_start first.' }],
|
|
88
|
+
isError: true,
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
const result = await client.stopRecording();
|
|
92
|
+
return {
|
|
93
|
+
content: [{
|
|
94
|
+
type: 'text',
|
|
95
|
+
text: `Perfetto trace saved: ${result.filePath}\nDuration: ${(result.durationMs / 1000).toFixed(1)}s | Size: ${(result.bytesWritten / 1024).toFixed(1)}KB\nOpen at: https://ui.perfetto.dev/\nOr analyze with: analyze_perfetto`,
|
|
96
|
+
}],
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
catch (error) {
|
|
100
|
+
return {
|
|
101
|
+
content: [{ type: 'text', text: `Stop failed: ${friendlyError(error)}` }],
|
|
102
|
+
isError: true,
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
// ---------------------------------------------------------------------------
|
|
107
|
+
// analyze_perfetto
|
|
108
|
+
// ---------------------------------------------------------------------------
|
|
109
|
+
server.registerTool('analyze_perfetto', {
|
|
110
|
+
description: 'Analyze a Roku Perfetto trace file (.trace). Returns structured JSON with AI-friendly suggestion fields for each analysis area. Requires the trace_processor binary (auto-downloaded on first use). Modes: summary (full report), frame-drops, key-events, observers, rendezvous, set-fields, threads.',
|
|
111
|
+
inputSchema: {
|
|
112
|
+
filePath: z.string().describe('Absolute path to the .trace file'),
|
|
113
|
+
mode: z
|
|
114
|
+
.enum(['summary', 'frame-drops', 'key-events', 'observers', 'rendezvous', 'set-fields', 'threads'])
|
|
115
|
+
.describe('Analysis mode'),
|
|
116
|
+
top: z.number().optional().describe('Number of entries in ranked lists (default 20)'),
|
|
117
|
+
threshold: z
|
|
118
|
+
.number()
|
|
119
|
+
.optional()
|
|
120
|
+
.describe('Only show entries exceeding this value in milliseconds'),
|
|
121
|
+
},
|
|
122
|
+
}, async (params) => {
|
|
123
|
+
try {
|
|
124
|
+
const opts = { top: params.top ?? 20, threshold: params.threshold };
|
|
125
|
+
const analyzer = await openTrace(params.filePath);
|
|
126
|
+
let report;
|
|
127
|
+
try {
|
|
128
|
+
switch (params.mode) {
|
|
129
|
+
case 'summary':
|
|
130
|
+
report = await analyzer.performanceSummary(opts);
|
|
131
|
+
break;
|
|
132
|
+
case 'frame-drops':
|
|
133
|
+
report = await analyzer.analyzeFrameDrops(opts);
|
|
134
|
+
break;
|
|
135
|
+
case 'key-events':
|
|
136
|
+
report = await analyzer.analyzeKeyEvents(opts);
|
|
137
|
+
break;
|
|
138
|
+
case 'observers':
|
|
139
|
+
report = await analyzer.analyzeObservers(opts);
|
|
140
|
+
break;
|
|
141
|
+
case 'rendezvous':
|
|
142
|
+
report = await analyzer.analyzeRendezvous(opts);
|
|
143
|
+
break;
|
|
144
|
+
case 'set-fields':
|
|
145
|
+
report = await analyzer.analyzeSetFields(opts);
|
|
146
|
+
break;
|
|
147
|
+
case 'threads':
|
|
148
|
+
report = await analyzer.analyzeThreads(opts);
|
|
149
|
+
break;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
finally {
|
|
153
|
+
await analyzer.close();
|
|
154
|
+
}
|
|
155
|
+
return {
|
|
156
|
+
content: [{ type: 'text', text: jsonify(report) }],
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
catch (error) {
|
|
160
|
+
return {
|
|
161
|
+
content: [{ type: 'text', text: `Analysis failed: ${friendlyError(error)}` }],
|
|
162
|
+
isError: true,
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
});
|
|
166
|
+
// ---------------------------------------------------------------------------
|
|
167
|
+
// compare_perfetto
|
|
168
|
+
// ---------------------------------------------------------------------------
|
|
169
|
+
server.registerTool('compare_perfetto', {
|
|
170
|
+
description: 'Compare two Roku Perfetto trace files to detect performance regressions and improvements. Returns metric deltas for frame drops, key events, observers, rendezvous, and setField calls.',
|
|
171
|
+
inputSchema: {
|
|
172
|
+
beforePath: z.string().describe('Absolute path to the "before" .trace file'),
|
|
173
|
+
afterPath: z.string().describe('Absolute path to the "after" .trace file'),
|
|
174
|
+
top: z.number().optional().describe('Number of entries in ranked lists (default 20)'),
|
|
175
|
+
},
|
|
176
|
+
}, async (params) => {
|
|
177
|
+
try {
|
|
178
|
+
const report = await comparePerfetto(params.beforePath, params.afterPath, {
|
|
179
|
+
top: params.top ?? 20,
|
|
180
|
+
});
|
|
181
|
+
return {
|
|
182
|
+
content: [{ type: 'text', text: jsonify(report) }],
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
catch (error) {
|
|
186
|
+
return {
|
|
187
|
+
content: [{ type: 'text', text: `Comparison failed: ${friendlyError(error)}` }],
|
|
188
|
+
isError: true,
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
});
|
|
192
|
+
// ---------------------------------------------------------------------------
|
|
193
|
+
// query_perfetto
|
|
194
|
+
// ---------------------------------------------------------------------------
|
|
195
|
+
server.registerTool('query_perfetto', {
|
|
196
|
+
description: 'Run a raw PerfettoSQL query against a Roku Perfetto trace file. Use for custom analysis beyond the built-in modes. Common queries: SELECT * FROM slice WHERE name = \'swapBuffers\' ORDER BY dur DESC; SELECT * FROM slice WHERE name = \'keyEvent\' ORDER BY dur DESC;',
|
|
197
|
+
inputSchema: {
|
|
198
|
+
filePath: z.string().describe('Absolute path to the .trace file'),
|
|
199
|
+
sql: z.string().describe('PerfettoSQL query to execute'),
|
|
200
|
+
},
|
|
201
|
+
}, async (params) => {
|
|
202
|
+
try {
|
|
203
|
+
const analyzer = await openTrace(params.filePath);
|
|
204
|
+
try {
|
|
205
|
+
const result = await analyzer.query(params.sql);
|
|
206
|
+
return {
|
|
207
|
+
content: [{ type: 'text', text: jsonify(result) }],
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
finally {
|
|
211
|
+
await analyzer.close();
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
catch (error) {
|
|
215
|
+
return {
|
|
216
|
+
content: [{ type: 'text', text: `Query failed: ${friendlyError(error)}` }],
|
|
217
|
+
isError: true,
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
//# sourceMappingURL=perfetto.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"perfetto.js","sourceRoot":"","sources":["../../src/tools/perfetto.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "roku-mcp",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.5.0",
|
|
4
4
|
"description": "MCP server for Roku device automation — deploy, ECP control, screenshots, debug console, and BrightScript profiler analysis",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -45,6 +45,7 @@
|
|
|
45
45
|
"dotenv": "^17.3.1",
|
|
46
46
|
"fast-xml-parser": "^5.5.9",
|
|
47
47
|
"roku-deploy": "^3.16.3",
|
|
48
|
+
"roku-perfetto": "^1.0.0",
|
|
48
49
|
"zod": "^4.3.6"
|
|
49
50
|
},
|
|
50
51
|
"devDependencies": {
|