screenwright
Records Android/Flutter app tutorials by driving an emulator or device via adb, including screen recording and caption burning.
Provides voice-over narration for captions using ElevenLabs TTS, with options for voice, model, and speed.
Records Flutter app tutorials on an Android emulator or device, with screen recording and synced captions.
Provides voice-over narration for captions using OpenAI TTS, with configurable voice and model.
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., "@screenwrightRecord a tutorial for setting up a new user account"
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.
π¬ Screenwright
Subtitled tutorial videos, scripted. An MCP server that turns a list of steps into a narrated screen recording β by driving a real browser (Playwright) or an Android / Flutter emulator (
adb). Captions are burned into the video and exported as an.srt.
The name is a play on Playwright (the engine under the hood) + screen.
steps[] βββΊ Playwright drives the browser βββΊ caption overlay synced per step
βββΊ video recorded βββΊ ffmpeg β .mp4 + .srt sidecarβ¨ Features
Scripted walkthroughs β
goto,click,fill,type,press,hover,scroll,wait.Presentation demos β generated 16:9 product tours with slide content, right-side action panel, zooms, transitions, subtitles and optional voice-over.
Synced subtitles β attach a
captionto any step; it's burned into the video and written to a timed.srt.Real voice-over (optional) β turn captions into a spoken narration with ElevenLabs or OpenAI TTS. Each line is pre-synthesized and the screen is held while it plays, so audio stays perfectly synced to the subtitles. Configure once via env.
Self-contained output β bundled
ffmpeg(viaffmpeg-static) produces a clean H.264.mp4.Robust β per-action timeouts; mark a step
optionalso a missing selector doesn't abort the take.Web and mobile β Playwright for the web;
adbscreen-record + ffmpeg caption-burn for Android / Flutter apps (no app source needed).Use it from an AI (MCP tool) or straight from Node.
Related MCP server: AIOProductOS Studio
π¦ Requirements
Node.js 18+
A Chromium build for Playwright:
npx playwright install chromium(run automatically onnpm install).
π Install
# from GitHub
npm install -g github:0xjesus/screenwright
# or clone
git clone https://github.com/0xjesus/screenwright.git
cd screenwright && npm installπ Use as an MCP server
Add it to your MCP client. Claude Desktop / Claude Code (claude_desktop_config.json or .mcp.json):
{
"mcpServers": {
"screenwright": {
"command": "node",
"args": ["/absolute/path/to/screenwright/src/index.js"]
}
}
}(If installed globally, you can use "command": "screenwright" with no args.)
Then just ask your assistant to record a tutorial β it calls the record_tutorial tool.
Tool: record_tutorial
Field | Type | Notes |
| string | Path to the output |
| step[] | Ordered steps (below). |
| string? | So |
|
| Default |
| bool? | Default |
| string? | e.g. |
| bool? | Burn captions into the video. Default |
| string? | Custom |
| object? | Voice-over β see ποΈ Voice-over. |
| object? |
|
Step = { caption?, narration?, action, url?, selector?, text?, key?, deltaY?, delayMs?, timeoutMs?, dwellMs?, optional? }.
selector is any Playwright selector β CSS, text=β¦, xpath=β¦, or :has-text(β¦).
Tool: record_presentation_demo π§βπ«
Use this when you want the polished "product tour / launch presentation" format around a real browser walkthrough. This tool is intentionally not slide-only: it first drives the real product with Playwright, records that browser flow, then embeds the recorded demo inside the presentation stage.
left side: title, metric card, bullets or code
right side: real operated product demo, plus optional app/image/terminal scenes
bottom: burned-in subtitle bar +
.srtoptional: ElevenLabs/OpenAI voice-over and background music
Field | Type | Notes |
| string | Path to the output |
| scene[] | Storyboard scenes. |
| object | Required. Real Playwright steps that operate the product. |
|
| Default |
| object? | Presentation layout. Use |
| object |
|
| object | Required. |
| string? | Base directory for relative image paths. |
| object? | Same TTS config as the other tools. |
| bool/object? | Optional looped music bed, ducked under narration. |
| string? | Custom |
Demo = { baseUrl?, steps, viewport?, headless?, channel?, minDurationMs?, finalHoldMs? }, where steps[] uses the same browser actions as record_tutorial.
For long desktop demos, set:
{
"layout": {
"mode": "demo-fullscreen",
"overlay": "compact",
"demoCoverage": 1,
"demoFit": "contain",
"protectDemo": true,
"safeArea": { "top": 64, "bottom": 118 },
"syncToDemo": true,
"demoLoop": false,
"technicalFullscreen": true,
"pauseDemoDuringNonDemo": true,
"defaultSceneDurationMs": 12000
},
"demo": {
"viewport": { "width": 1920, "height": 1080 },
"minDurationMs": 120000,
"finalHoldMs": 4000,
"steps": [
{ "action": "goto", "url": "/SpareParts/LandingRequests/Window", "dwellMs": 3000 },
{ "action": "click", "selector": ".sp-window-open-client", "dwellMs": 5000 }
]
}
}demo-fullscreen is the desktop walkthrough mode: the operated browser recording becomes the main canvas instead of a small right-side device. By default it uses protectDemo: true, reserving safe areas for branding and captions so explanatory UI never covers the real product screen. It also defaults technicalFullscreen: true, so technical/table/diagram scenes render as clean full slides instead of overlapping the demo stage. pauseDemoDuringNonDemo: true lets those technical scenes appear between demo segments and the real demo resumes where it left off. Keep demoFit: "contain" when every pixel of the app matters; use "cover" only when cropping is acceptable. syncToDemo: true stretches the first demo scene to the raw recording length so a long real flow is visible once instead of being cut or looped.
If demo.steps is missing, the tool fails. If brand.colors.primary or brand.colors.accent is missing, the tool fails. At least one scene must use stage.kind: "demo" or omit stage, otherwise the tool fails because the real operated product demo would not be visible. This keeps generated demos grounded in the real product and the project's visual identity.
Scene = { eyebrow?, title, caption?, narration?, durationMs?, metric?, points?, actions?, code?, stage? }.
stage.kind can be:
demoβ show the real browser demo recorded fromdemo.stepsvideoβ show a provided video filemock-appβ generated app-like phone panelimageβ show a real screenshot/image, with optional focus ringterminalβ show command/code output in the right panel
Example:
{
"output": "out/presentation-demo.mp4",
"brand": {
"name": "My SDK",
"mark": "M",
"tag": "Product tour",
"colors": { "primary": "#0057B8", "accent": "#E1251B" }
},
"demo": {
"baseUrl": "http://localhost:3000",
"steps": [
{ "action": "goto", "url": "/", "caption": "Open the app.", "networkIdle": false },
{ "action": "click", "selector": "text=Start", "dwellMs": 1000 }
]
},
"scenes": [
{
"eyebrow": "Launch",
"title": "My SDK turns a complex workflow into one API",
"caption": "My SDK turns a complex workflow into one API.",
"metric": {
"value": "1 API",
"label": "Install, connect, execute, and show receipts.",
"tags": ["SDK", "Flutter", "MCP"]
},
"points": [
{ "title": "Install", "body": "Add the package." },
{ "title": "Connect", "body": "Open the wallet flow." },
{ "title": "Execute", "body": "Send the transaction." },
{ "title": "Receipt", "body": "Show status and support states." }
],
"actions": [
{ "title": "Introduce", "body": "Start with the value." },
{ "title": "Show flow", "body": "Highlight the active action." },
{ "title": "Export", "body": "Return MP4 and SRT." }
],
"stage": {
"kind": "demo",
"title": "Reference app",
"subtitle": "The right side shows the operated app."
}
}
]
}From Node: import { recordPresentationDemo } from 'screenwright/presentation'.
Run the local example with node scripts/run-presentation-example.js.
Tool: record_android_tutorial π±
Same idea, for a Flutter / Android app on an emulator or device. Captions can't be injected into a native app, so the screen is recorded with adb screenrecord and the captions are burned in afterwards with ffmpeg (libass) from the synced .srt.
Requires: Android platform-tools (adb) + a running emulator/device (it must show in adb devices). For Flutter: flutter emulators --launch <id>, then run your app.
Field | Notes |
| Output |
| Ordered steps (below). |
|
|
| Recording size |
| Optional. |
| Voice-over β see ποΈ Voice-over. |
Step actions (coordinates are device pixels) β each may carry a caption and dwellMs:
tapβ{ x, y }textβ{ text }swipeβ{ x1, y1, x2, y2, durationMs? }keyβ{ key }(BACK,HOME,ENTER,MENU,APP_SWITCH, or aKEYCODE_*)launchβ{ package, activity? }waitβ{ dwellMs }
See examples/android-flutter.json. From Node: import { recordAndroidTutorial } from 'screenwright/android'.
ποΈ Voice-over (TTS)
Turn your captions into a real spoken narration β same tool, both drivers (web + Android). Provider and voice are set once via env (the same MCP config), so any record_* call with captions gets narrated automatically. Each line is synthesized before recording and the screen is held while it plays β the voice stays locked to the burned-in subtitles.
Configure in your MCP server env (or shell):
{
"mcpServers": {
"screenwright": {
"command": "node",
"args": ["/absolute/path/to/screenwright/src/index.js"],
"env": {
"SCREENWRIGHT_TTS_PROVIDER": "elevenlabs",
"ELEVENLABS_API_KEY": "sk_...",
"ELEVENLABS_VOICE_ID": "21m00Tcm4TlvDq8ikWAM",
"ELEVENLABS_MODEL": "eleven_multilingual_v2"
}
}
}
}OpenAI instead: SCREENWRIGHT_TTS_PROVIDER=openai, OPENAI_API_KEY=..., OPENAI_TTS_VOICE=onyx, OPENAI_TTS_MODEL=gpt-4o-mini-tts.
Per-call override (wins over env), plus a narration field when the spoken line should differ from the on-screen caption:
await recordAndroidTutorial({
output: 'out/tour.mp4',
tts: { provider: 'elevenlabs', voiceId: 'β¦', speed: 1.0, stability: 0.5 },
steps: [
{ action: 'launch', package: 'com.acme.app', caption: 'Meet Acme', narration: 'This is Acme β your market copilot.', dwellMs: 1500 },
{ action: 'tap', x: 540, y: 1925, caption: 'Generate a signal', dwellMs: 1000 },
],
});
// β { mp4, srt, durationMs, steps, captions, narration: { provider, lines } }ttsfields:provider(elevenlabs|openai),apiKey?,voiceId?/voice?,model?,speed?,stability?,similarityBoost?,style?,tailPadMs?(silence after each line, default 450).Requires the bundled
ffmpeg(already a dependency) to mix + mux the audio. Multilingual voices (e.g.eleven_multilingual_v2) narrate Spanish/English captions cleanly.No
ttsand no env provider β silent captions, exactly as before.
π§ͺ Use from Node (no MCP)
node scripts/run-example.js # records examples/demo.json
node scripts/run-example.js examples/capleton.jsonimport { recordTutorial } from 'screenwright/recorder';
await recordTutorial({
output: 'out/tour.mp4',
baseUrl: 'https://playwright.dev',
steps: [
{ action: 'goto', url: '/', caption: 'Welcome to the tour π¬', dwellMs: 2600 },
{ action: 'scroll', deltaY: 600, caption: 'Each step can carry a synced subtitleβ¦', dwellMs: 2400 },
{ action: 'goto', url: '/docs/intro', caption: 'β¦burned into the video and exported as .srt', dwellMs: 3000 },
],
});
// β { mp4, srt, durationMs, steps, captions }π‘ Tips
Don't double your subtitles. The
.mp4already has burned-in captions. Players like VLC auto-load an.srtthat shares the video's name and draw it on top. Screenwright defaults the sidecar to*.captions.srt(a non-matching name) so that doesn't happen β keep it that way, or disable the subtitle track in your player. Want selectable-only subs? PassburnIn: falsefor a clean video + the.srt.Black video in VLC on Linux? That's a GPU/output glitch, not the file. Launch with
vlc --avcodec-hw=none --vout=xcb_x11, or set those in VLC β Preferences.Headless still records β no display needed.
π License
MIT Β© 0xjesus
This server cannot be installed
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 Servers
- Flicense-qualityDmaintenanceEnables AI assistants to control browser automation through natural language prompts using Playwright, supporting visual element interaction, PDF generation, screenshots, and testing assertions.

AIOProductOS Studioofficial
AlicenseAqualityBmaintenanceTurns your AI host into a product videographer β scripted screen recordings of your own web app with a gliding cursor, camera zooms, highlight callouts, captions, and branded transitions, plus marketing-grade screenshots. Automatic dark-frame cleanup and MP4/GIF export. Free, MIT, 100% local β no account, no API keys.8141993MIT- Alicense-qualityAmaintenanceYour coding agent makes the demo video β turns a storyboard.json into a narrated, captioned product-demo MP4; deterministic replay re-renders it in CI at ~$0.362MIT
- Alicense-qualityDmaintenanceEnables browser automation using Playwright for web scraping, testing, and automation tasks through natural language commands.1,306MIT
Related MCP Connectors
Clip videos into captioned shorts, add captions & schedule posts β from Claude, ChatGPT, Cursor.
AI-powered browser automation β navigate, click, fill forms, and extract data from any website.
Transcripts from YouTube, TikTok, Instagram and podcasts (Spotify, Apple, RSS), as clean JSON.
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/0xjesus/screenwright'
If you have feedback or need assistance with the MCP directory API, please join our Discord server