{"id":51320590,"url":"https://github.com/tmhsdigital/screencast-mcp","last_synced_at":"2026-07-01T13:01:21.572Z","repository":{"id":365100020,"uuid":"1270504727","full_name":"TMHSDigital/screencast-mcp","owner":"TMHSDigital","description":"MCP server for Windows screen recording, frame sampling, and minimal ffmpeg edits","archived":false,"fork":false,"pushed_at":"2026-06-15T20:55:01.000Z","size":70,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-15T22:23:03.628Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"https://tmhsdigital.github.io/screencast-mcp/","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/TMHSDigital.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":"ROADMAP.md","authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":"AGENTS.md","dco":null,"cla":null}},"created_at":"2026-06-15T19:24:39.000Z","updated_at":"2026-06-15T20:56:40.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/TMHSDigital/screencast-mcp","commit_stats":null,"previous_names":["tmhsdigital/screencast-mcp"],"tags_count":5,"template":false,"template_full_name":null,"purl":"pkg:github/TMHSDigital/screencast-mcp","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TMHSDigital%2Fscreencast-mcp","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TMHSDigital%2Fscreencast-mcp/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TMHSDigital%2Fscreencast-mcp/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TMHSDigital%2Fscreencast-mcp/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/TMHSDigital","download_url":"https://codeload.github.com/TMHSDigital/screencast-mcp/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TMHSDigital%2Fscreencast-mcp/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":35007278,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-07-01T02:00:05.325Z","response_time":130,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-07-01T13:01:20.531Z","updated_at":"2026-07-01T13:01:21.558Z","avatar_url":"https://github.com/TMHSDigital.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cdiv align=\"center\"\u003e\n\n# Screencast MCP\n\n**An MCP server that lets an agent record the screen, _watch_ the footage, and make minimal ffmpeg edits — over stdio, on Windows.**\n\nCapture the desktop, a monitor, a window, or a region; sample a recording into frames the agent can actually look at; trim, crop, scale, overlay, compress, redact, and convert. No cloud, no streaming — ffmpeg and ffprobe wrapped behind nineteen typed tools.\n\n\u003cbr /\u003e\n\n[![Documentation](https://img.shields.io/badge/Documentation-2D7FF9?style=for-the-badge\u0026logo=readthedocs\u0026logoColor=white)](https://tmhsdigital.github.io/screencast-mcp/)\n\n\u003cbr /\u003e\n\n[![CI](https://github.com/TMHSDigital/screencast-mcp/actions/workflows/ci.yml/badge.svg)](https://github.com/TMHSDigital/screencast-mcp/actions/workflows/ci.yml)\n[![License: CC BY-NC-ND 4.0](https://img.shields.io/badge/license-CC--BY--NC--ND--4.0-green)](LICENSE)\n![Last commit](https://img.shields.io/github/last-commit/TMHSDigital/screencast-mcp)\n![Repo size](https://img.shields.io/github/repo-size/TMHSDigital/screencast-mcp)\n![Top language](https://img.shields.io/github/languages/top/TMHSDigital/screencast-mcp)\n\n![TypeScript](https://img.shields.io/badge/TypeScript-3178C6?logo=typescript\u0026logoColor=white)\n![Node.js](https://img.shields.io/badge/Node.js_%E2%89%A520-339933?logo=node.js\u0026logoColor=white)\n![Model Context Protocol](https://img.shields.io/badge/MCP-stdio-6E56CF)\n![ffmpeg](https://img.shields.io/badge/ffmpeg-required-007808?logo=ffmpeg\u0026logoColor=white)\n![Capture](https://img.shields.io/badge/capture-Windows_gdigrab-0078D6?logo=windows\u0026logoColor=white)\n\n\u003c/div\u003e\n\n\u003e [!NOTE]\n\u003e Screen capture uses `gdigrab` and is Windows-only; the watch, edit, and produce tools work anywhere ffmpeg runs. The full pipeline is in place: capture, watch, the edit surface, `redact_region` safety redaction, system-audio capture, and the produce layer (transitions, title cards, music bed, aspect variants, platform export). Cross-platform capture backends are next — see [ROADMAP.md](ROADMAP.md).\n\n## Overview\n\nScreencast MCP gives an agent a screen recorder it can drive and reason about. It speaks [Model Context Protocol](https://modelcontextprotocol.io) over stdio and exposes ffmpeg as a small, typed tool surface rather than a flag soup. The defining capability is the **watch loop**: an agent records footage, samples it into PNG frames, and views those frames to confirm what actually happened on screen.\n\nThe design choices are deliberate rather than incidental:\n\n- **Capture is always explicit.** A recording or screenshot happens only on a tool call — nothing auto-fires and there is no background or scheduled capture.\n- **Footage is made viewable.** `sample_frames` turns a video into images an agent can open, so \"watch what happened\" is a first-class operation, not an afterthought.\n- **Presets over raw flags.** Quality is `draft` / `standard` / `high`; the agent never reasons about codecs, CRF, or pixel formats.\n- **Safe by default.** Output lands under `SCREENCAST_HOME`, never inside a project checkout, and the public repo's `.gitignore` blocks captured media from being committed.\n- **Crash-safe sessions.** A recording interrupted by a crash is reconciled on the next start (orphan reaping), so no ffmpeg child silently outlives the server.\n\n## Tools\n\nTwenty-five tools across four concerns. The manifest in [`mcp-tools.json`](mcp-tools.json) is the canonical surface and is kept in sync with `src/tools/`.\n\n### Capture\n\n| Tool | Purpose |\n| --- | --- |\n| `start_recording` | Start a background recording. `target` = `full` \\| `monitor:\u003cindex\u003e` \\| `window:\u003ctitle\u003e` \\| `region:\u003cx\u003e,\u003cy\u003e,\u003cw\u003e,\u003ch\u003e`; optional `fps`, `quality`, and `audio` (set `audio.source` = `system` to also capture loopback audio). Returns a session id and output path. |\n| `stop_recording` | Stop a session by id. Sends ffmpeg a graceful quit so the file is **finalized, not truncated**. Returns the final path and duration. |\n| `list_sessions` | List active and finished sessions. |\n| `get_session` | Inspect a single session by id. |\n| `screenshot` | Capture a single PNG of a `target`. |\n| `list_audio_devices` | List the DirectShow audio devices ffmpeg can see and flag a likely system-audio loopback device for `start_recording`. |\n\n### Watch\n\n| Tool | Purpose |\n| --- | --- |\n| `sample_frames` | Extract frames from a video — at a fixed `fps` or at explicit `timestamps` — so the agent can view what happened. Returns the frame paths. |\n| `get_media_info` | ffprobe wrapper: duration, resolution, fps, codecs, container format, and size. |\n\n### Minimal edit\n\n| Tool | Purpose |\n| --- | --- |\n| `trim` | Cut a sub-clip by `start` + (`end` or `duration`). Stream-copy for speed. |\n| `concat` | Join two or more videos into one. |\n| `convert` | Convert between `mp4`, `gif`, and `webm`. |\n\n### Edit surface\n\nThese tools re-encode (a filter rewrites pixels, so stream copy does not apply). They reuse the same `draft`/`standard`/`high` presets as capture.\n\n| Tool | Purpose |\n| --- | --- |\n| `crop` | Crop to a pixel rectangle (`x`, `y`, `width`, `height`). A rectangle that runs off the frame is rejected, not silently clamped. |\n| `scale` | Resize to a `width` and/or `height`. One side keeps the aspect ratio; both set an exact size. |\n| `speed` | Change playback speed by a `factor` (\u003e1 faster, \u003c1 slower). Audio is retempo'd when present. |\n| `overlay` | Composite a logo, watermark, or picture-in-picture onto a base video at a position, optionally scaled and time-limited. |\n| `compress` | Re-encode smaller with a `light`/`medium`/`heavy` CRF ladder and an optional `maxWidth` that only downscales. |\n| `extract_audio` | Write the audio track to its own file (`mp3`, `aac`, `wav`, or `copy`). |\n| `clip` | Extract one or more frame-accurate sub-segments to separate files. Unlike `trim`, it re-encodes so cuts land exactly on the given times. |\n\n### Redact\n\n| Tool | Purpose |\n| --- | --- |\n| `redact_region` | Cover declared rectangles in a video. `style` is `box` (default, a solid irreversible fill), `blur`, or `pixelate`; each region may be limited to a `start`/`end` window and expanded with `pad`. |\n\n\u003e [!IMPORTANT]\n\u003e `redact_region` covers the regions **you** declare. It is not automatic secret detection, so it cannot find a secret you did not point it at. The default `box` style is a solid fill, which is irreversible; `blur` and `pixelate` are softer but can be partially recovered, so prefer `box` for real secrets. A region that falls outside the frame is rejected rather than silently doing nothing.\n\n### Produce\n\nThe production layer turns raw clips into a finished piece. Tools that combine clips auto-normalize each input to a common resolution, fps, and audio rate first, so heterogeneous sources compose cleanly.\n\n| Tool | Purpose |\n| --- | --- |\n| `xfade_transition` | Crossfade two videos into one with an `xfade` transition (`fade`, `wipeleft`, `slideup`, ...). Audio is crossfaded when both clips have a track. |\n| `assemble_highlights` | Stitch two or more clips into one, with hard cuts (`transition: \"cut\"`) or an xfade transition between each. |\n| `title_card` | Generate a standalone title card (centered text on a solid background, with a silent track so it composes). Text uses a bundled font, so no system font is needed. |\n| `music_bed` | Lay a music track under a video: looped/trimmed to length, faded in and out, leveled, and mixed with any existing audio (optionally ducked). |\n| `reframe` | Re-aspect to `16:9`, `9:16`, `1:1`, or `4:5` with `pad` (letterbox, no content lost) or `crop` (fill, trims overflow). |\n| `export_preset` | Encode a platform-ready file (`youtube`, `instagram_reel`, `tiktok`, `x`, `square`): reframes, caps fps, and encodes H.264 at the platform bitrate with faststart. |\n\n### Targets\n\nEvery capture tool takes a single `target` string, so an agent never has to juggle quoting:\n\n| Target | Captures |\n| --- | --- |\n| `full` | The whole virtual desktop |\n| `monitor:\u003cindex\u003e` | One display; `0` is always primary |\n| `window:\u003ctitle\u003e` | The on-screen rectangle a window occupies (case-insensitive exact title, else substring; topmost wins) |\n| `region:\u003cx\u003e,\u003cy\u003e,\u003cw\u003e,\u003ch\u003e` | An absolute pixel rectangle |\n\nOutput is written under `SCREENCAST_HOME` (default `\u003chomedir\u003e/.screencast-mcp`) into `recordings/`, `frames/`, `screenshots/`, and `edits/`. Any tool also accepts an explicit output path.\n\n## Prerequisites\n\n`ffmpeg` and `ffprobe` are external dependencies and must be on `PATH` (or pointed at via the `FFMPEG_PATH` / `FFPROBE_PATH` environment variables). The server detects them per call and returns a clear error with an install hint if either is missing.\n\n| Platform | Install |\n| --- | --- |\n| Windows | `winget install Gyan.FFmpeg` or `choco install ffmpeg` |\n| macOS | `brew install ffmpeg` |\n| Linux | `apt install ffmpeg` |\n\n## Installation\n\n```bash\nnpm install -g @tmhs/screencast-mcp\n```\n\nOr run it from a clone:\n\n```bash\ngit clone https://github.com/TMHSDigital/screencast-mcp.git\ncd screencast-mcp\nnpm install\nnpm run build      # produces dist/index.js, the server entry point\n```\n\n### MCP client configuration\n\n```json\n{\n  \"mcpServers\": {\n    \"screencast\": {\n      \"command\": \"npx\",\n      \"args\": [\"-y\", \"@tmhs/screencast-mcp\"]\n    }\n  }\n}\n```\n\n\u003e [!TIP]\n\u003e Running from a clone instead of the published package? Point the client straight at the build: `\"command\": \"node\"`, `\"args\": [\"C:/path/to/screencast-mcp/dist/index.js\"]`.\n\n## Usage\n\nA typical watch loop — record, sample, look:\n\n```jsonc\n// 1. record a region for a few seconds\nstart_recording { \"target\": \"region:0,0,1280,720\", \"quality\": \"draft\" }\n//    -\u003e { \"sessionId\": \"rec-…\", \"outputPath\": \"…/recordings/rec-….mp4\" }\n\n// 2. finalize the file\nstop_recording { \"sessionId\": \"rec-…\" }\n//    -\u003e { \"durationSec\": 4.2, \"finalizedGracefully\": true }\n\n// 3. turn it into frames the agent can view\nsample_frames { \"input\": \"…/recordings/rec-….mp4\", \"timestamps\": [0.5, 2, 3.5] }\n//    -\u003e { \"frames\": [\"…/frames/…/frame_000_0.5s.png\", …] }\n```\n\n`screenshot { \"target\": \"window:My App\" }` is the one-shot equivalent for a still.\n\n## Windows notes\n\n- **Multi-monitor offsets.** `gdigrab` has no \"capture monitor N\" selector, so a monitor target captures the whole virtual desktop and crops to that display's pixel bounds (from `System.Windows.Forms.Screen.AllScreens`). `monitor:1` grabs the second display at its real offset; `monitor:0` is always primary.\n- **Window capture** resolves the window to the on-screen **rectangle** it occupies and captures that, rather than the window's own surface — `gdigrab`'s native `title=` grab returns a blank frame for GPU- or DirectComposition-composited windows (Chrome, Electron, UWP). The window must be **visible, on top, and not minimized** (a minimized window is rejected with a clear error), the capture includes anything drawn over that rectangle, and for `start_recording` the rectangle is fixed **once at start**. True per-window background capture (Windows Graphics Capture API) is a future phase.\n- **Fullscreen-exclusive apps** often produce black frames under `gdigrab`; run the source in borderless-windowed mode for reliable capture.\n- **System audio needs a loopback device.** `gdigrab` is video-only, so `start_recording` with `audio.source` = `system` captures from a separate dshow input. Windows has no native loopback, so this needs a virtual-audio device (enable Stereo Mix, or install a driver such as screen-capture-recorder's `virtual-audio-capturer` or VB-CABLE). Run `list_audio_devices` to find it. Microphone capture is intentionally not supported.\n\n## Threat model\n\n\u003e [!WARNING]\n\u003e Screen capture can record **anything** on screen at the moment of capture — passwords, tokens, private messages, and other secrets. `window:` captures the screen rectangle a window occupies, so overlays, notifications, or another window drawn over it are captured too. Treat recordings, screenshots, and sampled frames as sensitive by default.\n\n- **Capture is always explicit.** Nothing auto-fires; capture is gated behind an explicit tool call.\n- **Output stays local.** Files are written to the local filesystem only — never uploaded, streamed, or transmitted anywhere.\n- **Public repo, private captures.** The `.gitignore` blocks recordings, frames, screenshots, and common video/image output so test media cannot be committed by accident.\n- **Review before sharing.** Sample frames or inspect a recording before handing a file to another tool or person, so you know what it contains.\n- **Redaction is declared, not detected.** `redact_region` covers only the rectangles you specify, so it depends on you (or the agent) having found the secret first. Use the default `box` style for a solid irreversible fill, and still review the output before sharing.\n- **System audio is sensitive too.** When `audio.source` = `system`, the recording captures everything playing on the machine (call audio, notifications, media). Treat audio-bearing recordings with the same care as the video.\n\n## Project structure\n\n```\n.\n├── src/\n│   ├── index.ts          # MCP server entry (stdio); registers every tool, reaps orphans\n│   ├── context.ts        # shared session-store singleton\n│   ├── tools/            # one file per tool (capture, watch, edit)\n│   ├── utils/            # ffmpeg, monitors, windows, sessions, paths, targets\n│   └── __tests__/        # vitest unit tests + guarded local-capture harness\n├── docs/                 # GitHub Pages documentation site\n├── mcp-tools.json        # canonical tool manifest (kept in sync with src/tools)\n├── .github/workflows/    # CI, release, npm publish, Pages, ecosystem drift check\n├── ROADMAP.md · CONTRIBUTING.md · SECURITY.md · LICENSE\n└── package.json\n```\n\n## Development\n\n```bash\nnpm install\nnpm run build      # tsc -\u003e dist/\nnpm test           # vitest (pure unit tests; no ffmpeg or display required)\nnpm run dev        # tsx watch\n```\n\nThe capture path can't be exercised on CI's headless Linux runners, so an end-to-end harness lives behind a flag and is skipped by default:\n\n```bash\nRUN_LOCAL_CAPTURE_TESTS=1 npm test   # Windows + ffmpeg + a real display\n```\n\n## Contributing\n\nIssues and pull requests are welcome — see [CONTRIBUTING.md](CONTRIBUTING.md) and the [Code of Conduct](CODE_OF_CONDUCT.md). Security reports go through [SECURITY.md](SECURITY.md).\n\n## License\n\nReleased under the [CC-BY-NC-ND-4.0](LICENSE) license.\n\n\u003cdiv align=\"center\"\u003e\n\u003cbr /\u003e\n\n[**Documentation**](https://tmhsdigital.github.io/screencast-mcp/) · [**Roadmap**](ROADMAP.md) · [**Report an issue**](https://github.com/TMHSDigital/screencast-mcp/issues) · [**License**](LICENSE)\n\n\u003csub\u003eBuilt by \u003ca href=\"https://github.com/TMHSDigital\"\u003eTMHSDigital\u003c/a\u003e · \u003ca href=\"#screencast-mcp\"\u003eBack to top ↑\u003c/a\u003e\u003c/sub\u003e\n\n\u003c/div\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftmhsdigital%2Fscreencast-mcp","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftmhsdigital%2Fscreencast-mcp","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftmhsdigital%2Fscreencast-mcp/lists"}