
record-demo
✓ Official★ 705by apify · part of apify/mcpc
Record or regenerate the mcpc demo GIFs (the README hero docs/images/mcpc-demo.gif and the focused tapes in docs/vhs/) with VHS. Use whenever asked to create, refresh, restyle, shorten, or fix a terminal demo/animation/GIF of mcpc. The tapes drive real mcpc commands; for the authenticated step this skill ALWAYS prompts for a short-lived, low-permission TEST token first (never production). Captures the VHS + mcpc gotchas learned the hard way — read it fully before editing a tape.
This is the playbook your agent receives when the skill activates — you don't need to read it to use the skill, but it's here to audit before installing.
record-demo: VHS demo GIFs for mcpc
The tapes in docs/vhs/*.tape are VHS
scripts that drive a real shell session — VHS types each command, runs it
against a live MCP server, captures the terminal, and renders a GIF. The README
hero is docs/images/mcpc-demo.gif, built from docs/vhs/mcpc-demo.tape.
This file is the accumulated know-how. Read all of it before touching a tape — most rules below were discovered by hitting the wall, and skipping them wastes whole render cycles (~1–2 min each).
The hero flow (mcpc-demo.tape)
A basic-use story across both transports:
mcpc— empty state (no sessions, no profiles)mcpc connect mcp.json:filesystem— local stdio server (auto-names@filesystem)mcpc— session list (now shows the live session)mcpc @filesystem tools-listmcpc @filesystem tools-list --json— JSON output, syntax-highlighted, no jqmcpc connect mcp.apify.com -H "Authorization: Bearer $APIFY_TOKEN"— remote HTTP server (auto-names@apify)mcpc @apify tools-listmcpc @apify tools-get search-actors— inspect one tool's input schemamcpc @apify tools-call search-actors keywords:="web scraper" limit:=3mcpc @apify close
Ten commands run ~45s; there is no hard 30s cap for this flow.
Style conventions (the current standard — match these)
- No
# commentsin the visible script. The commands are self-descriptive. - No
| head, no2>/dev/nullon visible commands. Show real output even if long — it scrolls naturally. (connect≈ 86 lines incl. an "Available commands" list,tools-call search-actors≈ 56, filesystemtools-list --json≈ 300.) - Continuous session — never
clearbetween steps. Put a single blank-lineEnterbefore each command (after the first) so it's separated from the previous output, like a real terminal session. - Colored prompt + bold-white typed commands. In the hidden setup block:
Type 'export PS1="\[\e[1;38;2;25;230;77m\]$\[\e[0m\] \[\e[1;97m\]"' Enter Type "trap 'tput sgr0' DEBUG" EnterPS1is a bold bright-green$, then ends with\[\e[1;97m\]so the typed input renders bold bright-white.- The
DEBUGtrap runstput sgr0before every command so the bold-white input does not bleed into command output.
- Empty state needs a clean home — hidden:
Type 'export MCPC_HOME_DIR="$(mktemp -d)"'somcpcshows "No active MCP sessions / No OAuth profiles". - Color is automatic for non-piped commands — mcpc detects the TTY and emits
color (256-color, plenty vivid). You only need
export FORCE_COLOR=3(+COLORTERM=truecolorfor exact hex) when a command is piped (mcpc turns color off when stdout isn't a TTY). The current tapes avoid pipes, so they don't need it. (The CLI palette lives insrc/cli/output.ts,RAINBOW_SATURATION, bumped to 78% for vividness — that's where the demo colors come from.)
VHS gotchas (these will bite you)
- ASCII prompt symbols only. Multibyte glyphs (
❯,»,▶) break bash prompt rendering under VHS and show up as garbage like92m]. Use$(or>), styled with color + bold. Typequoting: use single quotes around any command containing double quotes. A\"inside a double-quotedTypebreaks VHS's parser. e.g.Type 'mcpc @apify tools-call search-actors keywords:="web scraper" limit:=3'andType 'export PS1="\[\e[…m\]$\[\e[0m\] "'.- Output/Screenshot paths: must not start with a digit (
Output 1-foo.giffails to parse) and must not be long absolute paths (the parser chokes). Use short, letter-leading, relative names and runvhsfromdocs/vhs/. Screenshotis unreliable (frequently exits 2 even though the GIF rendered fine). Don't depend on it — pull frames from the finished GIF instead:ffmpeg -y -ss <seconds> -i x.gif -vframes 1 frame.png, then Read the PNG.- Renders are slow (~1–2 min each: real-time timeline + Chromium + ffmpeg
encode). Render tapes one at a time — a
forloop over several blows the 5-minute command timeout. Extracted frames often land mid-typing; sample a few timestamps around when output should be on screen. - Hidden connects leak into the recording if bash falls behind. When a tape
connects in the hidden setup (so the feature commands run against a ready
session, e.g. tools/scripting/grep), a slow connect lets VHS type ahead; bash
then echoes the buffered commands and runs the
clearafterShow, so the setup spills into frame. Pattern that works: type the connect(s), then one generousSleep(7–8s) so they finish, thenclear, then anotherSleep(~1.5s) beforeShow. (Tapes that connect visibly as their first command don't need this — their hidden setup is just fast exports +clear.)
Stdio servers in a headless / proxied box
npx-launched stdio servers are too slow here.npx -y <pkg>'s registry round-trip exceeds mcpc's 60s connect handshake, soconnecttimes out (MCP error -32001). Fixes: pre-install the server (npm i -g <pkg>) and put the direct binary inmcp.json(e.g.mcp-server-filesystem, starts in ~0.3s — also a cleaner session header), or usenpx --prefer-offline -y <pkg>once the npm cache is warm (~5s; plainnpx -ystill does the slow registry check even when cached).- Puppeteer does NOT work for headless recording.
@modelcontextprotocol/server-puppeteerlaunches Chromium eagerly on startup and hangs/times out as root in the container. Use@modelcontextprotocol/server-filesysteminstead (14 recognizable tools, instant).docs/vhs/mcp.jsondefines thefilesystementry via its global binary — install it first:npm i -g @modelcontextprotocol/server-filesystem.
Auth token (the authenticated step)
- Always prompt for the token first. Insist on a short-lived, low-permission token from a TEST / throwaway account — never production. Apify: https://console.apify.com/settings/integrations. Tell the user to revoke it as soon as the recording is done.
- Pass it inline, for the render only:
APIFY_TOKEN=… vhs mcpc-demo.tape. The tape references$APIFY_TOKEN(never the literal), typed inside single quotes so bash expands it at run time — the value is never on screen, never in the GIF, never committed. Always verify a connect frame shows$APIFY_TOKEN, not the value. connectauto-names the session:mcp.apify.com → @apify,mcp.json:filesystem → @filesystem. No@nameneeded.- No-token alternative (public, anonymous):
mcpc connect "https://mcp.apify.com/?tools=search-actors,fetch-actor-details,docs".
Keychain warning (headless only)
On a box with no keyring, the bearer-token connect prints
[keychain] OS keychain unavailable, falling back to file-based credential storage …. It is environment-specific (won't appear on a normal desktop
with a keyring) and there is no env var to suppress it — it's a logger.warn
in src/lib/auth/keychain.ts gated only by keychain availability and JSON mode.
A dbus-run-session + gnome-keyring-daemon wrapper does not fix it in this
sandbox (raising the dbus fd limit is blocked). Options: leave it (honest), or add
a targeted 2>/dev/null to just that one connect command.
Render and verify
cd docs/vhs
APIFY_TOKEN=… vhs mcpc-demo.tape # real-time; be patient
ffprobe -v error -show_entries format=duration -of csv=p=0 mcpc-demo.gif # check length
ffmpeg -y -ss 12 -i mcpc-demo.gif -vframes 1 /tmp/f.png # spot-check a frame, then Read it
cp mcpc-demo.gif ../images/mcpc-demo.gif # update the README heroCheck each frame class: empty state, stdio connect + session list, tools-list, JSON, remote connect (token NOT visible), tool-call result, close. Then revoke the token.
Optimize the GIF size (do this before committing)
VHS GIFs are large (the hero is ~5 MB raw). Shrink them losslessly-ish with
gifsicle (kornel.ski/lossygif) — --lossy
drops imperceptible inter-frame detail and cuts ~60% off a text-terminal GIF
with no visible quality loss:
# in place; lossy=200 ≈ 60% smaller, text stays crisp (verified by frame diff)
for f in docs/images/mcpc-demo.gif docs/vhs/*.gif; do
[ "$f" = docs/vhs/mcpc-demo.gif ] && continue # ignored raw hero
gifsicle -O3 --lossy=200 -b "$f"
doneAfter optimizing, re-extract a colored frame (ffmpeg -ss 12 …) and eyeball it —
lossy=200 is the sweet spot; going much higher smears the antialiased text.
When the optimized hero lands on main, bump the README cache-buster
(mcpc-demo.gif?v=N → ?v=N+1): GitHub's camo image proxy caches by full URL,
so a new ?v= is what makes it re-fetch the smaller file instead of serving the
old cached copy.
What's committed
docs/images/mcpc-demo.gif— the README hero (canonical copy).docs/vhs/*.gif— the per-feature recordings are committed too, so they're easy to find and reuse..gitignoreignores onlydocs/vhs/mcpc-demo.gif(the hero's raw output, since it's committed underdocs/images/).proxy.gifneeds a token to record and isn't committed until recorded.
The tapes
| Tape | Records |
|---|---|
mcpc-demo.tape | Hero basic-use flow (stdio + remote) → docs/images/mcpc-demo.gif |
quickstart.tape | Minimal connect → list → call |
tools.tape | tools-list / tools-get / tools-call, inline JSON, stdin |
scripting.tape | --json piped through jq (code mode) |
grep.tape | Dynamic tool discovery with mcpc grep across two sessions (Apify + filesystem) |
proxy.tape | MCP proxy / AI sandboxing (keeps a bearer token on purpose) |
All focused tapes follow the same conventions as the hero (bold $ prompt,
bold-white commands, no comments, blank-line separation, mktemp home).
quickstart/tools/scripting/grep are token-free (public ?tools= URL);
grep also connects a local filesystem stdio server so it can search across two
sessions. proxy.tape keeps a bearer token because demonstrating that you can
proxy a credentialed session without leaking the token is its entire point (it's
the one focused tape that needs a token to record).
npx skills add https://github.com/apify/mcpc --skill record-demoRun this in your project — your agent picks the skill up automatically.
Prerequisites
mcpc --version # the CLI being demoed (npm i -g @apify/mcpc, or build + pnpm link this repo)
vhs --version # brew install vhs (needs ttyd + ffmpeg on PATH)
mcp-server-filesystem # npm i -g @modelcontextprotocol/server-filesystem (the stdio demo server)Headless + root: VHS drives a Chromium (go-rod auto-downloads it to
~/.cache/rod) that refuses to start without --no-sandbox. If renders fail to
launch Chromium, wrap the binary once:
CHROME=$(find ~/.cache/rod/browser -name chrome -type f | head -1)
mv "$CHROME" "$CHROME-real"
printf '#!/bin/sh\nexec "$(dirname "$0")/chrome-real" --no-sandbox --disable-gpu --disable-dev-shm-usage "$@"\n' > "$CHROME"
chmod +x "$CHROME"(Run vhs once first to trigger the Chromium download.)
No common issues documented yet. If you hit a problem, the repository's GitHub Issues page is the best place to look.
Licensed under Apache-2.0— you can use, modify, and redistribute it under that license's terms.
View the full license file on GitHub →