Skip to content

feat(mcp): one-click Claude Desktop extension (.mcpb), without FFmpeg - #58

Merged
yultyyev merged 6 commits into
feat/distributionfrom
feat/mcpb-bundle
Nov 7, 2026
Merged

yultyyev merged 6 commits into
feat/distributionfrom
feat/mcpb-bundle

Conversation

@yultyyev

@yultyyev yultyyev commented Nov 7, 2026 •

Copy link
Copy Markdown
Collaborator

Targets the integration branch feat/distribution. Nothing releases until that branch merges into main.

What

A one-click Claude Desktop extension: makemyclip-editor.mcpb, built in CI and attached to every GitHub release. It runs clip mcp on Claude Desktop's built-in Node 24, so the user needs no Node, no npx and no JSON config.

FFmpeg is not in the bundle. ffmpeg-static's Apple Silicon binary is FFmpeg 6.0 built with --enable-nonfree; ffmpeg -L says it "is not legally redistributable". The Intel macOS and Windows builds are GPLv3, so shipping them would make us the distributor, with the full corresponding source to host on every release. The extension instead runs the FFmpeg the user installs. That is one brew install ffmpeg or winget install Gyan.FFmpeg, and the doctor tool says so. Decided with the maintainer on 2026-11-07. A backlog note covers finding a redistributable arm64 build.

Finding an installed FFmpeg (src/system-binary.ts).

  • An app opened from the Dock gets PATH=/usr/bin:/bin:…, without Homebrew. findSystemBinary(name) returns the first executable on PATH, then in Homebrew's, MacPorts', winget's, Scoop's or Chocolatey's folder.
  • resolveFfmpeg(): MAKEMYCLIP_FFMPEG_PATH → ffmpeg-static's binary if it's on disk → an installed FFmpeg. The middle step used to return the bundled path even when the install script had never run.
  • probe.ts used its own resolution; it now uses resolveFfmpeg().
  • FFmpeg resolves per run, so one installed while the server runs works without a restart. The filter list and filtergraph-option caches are keyed by the binary's path.
  • whisper-cli uses the same lookup (whisperBinary()).
  • An empty MAKEMYCLIP_FFMPEG_PATH, MAKEMYCLIP_WHISPER_PATH or MAKEMYCLIP_WORKSPACE counts as unset. The extension passes "" for a setting left blank.

The extension (mcpb/manifest.json, pnpm build:mcpb).

  • Manifest 0.3: server.type: node, node ${__dirname}/dist/cli.js mcp, compatibility: { platforms: [darwin, win32], runtimes.node: >=24.0.0 }, the 8 tools, and icon.png from the logo.
  • Four optional settings: Media folder (MAKEMYCLIP_WORKSPACE), FFmpeg, Whisper model and whisper-cli. Each defaults to "", so a blank setting never reaches the server as a literal ${user_config.x}.
  • A blank media folder means ~/Documents/MakeMyClip, chosen in code (MAKEMYCLIP_BUNDLE=mcpb). The spec allows ${DOCUMENTS} in a default, but mcpb's own config builder passes it through unexpanded.
  • scripts/build-mcpb.ts stages dist/, README, LICENSE, the icon and a flat pnpm install --prod --frozen-lockfile --ignore-scripts --config.node-linker=hoisted. mcpb pack follows symlinks, so pnpm's default layout would pack its store.
    • --ignore-scripts keeps ffmpeg-static from downloading FFmpeg, and the build fails if an ffmpeg binary is in the stage.
    • It then runs mcpb validate and mcpb pack. The result is 4.4 MB packed, 13.8 MB unpacked, one bundle for both platforms.
    • The asset name has no version, so releases/latest/download/makemyclip-editor.mcpb is a stable link.
  • Not signed: the released mcpb sign produces bundles Claude Desktop rejects (mcpb#194, fixed only on mcpb's main branch). Unsigned bundles install.
  • @anthropic-ai/mcpb 2.1.2 is a pinned devDependency (MIT, build-time only).

Doctor in the extension. With MAKEMYCLIP_BUNDLE=mcpb:

  • the FFmpeg fix says the extension doesn't include FFmpeg and gives the install command;
  • a new howToSet field maps each env var to the extension setting that sets it (outside the extension, it points at the shell profile or MCP client config).

CI and release.

  • ci.yml runs pnpm build:mcpb on every PR.
  • .releaserc.json: @semantic-release/exec gets prepareCmd: pnpm build:mcpb, which runs after @semantic-release/npm writes the version into package.json. @semantic-release/github uploads release/*.mcpb.

Docs.

  • README install section: "As a Claude Desktop extension" (install FFmpeg, then open the .mcpb).
  • The License section and the two FFmpeg Q&As no longer say the bundled FFmpeg is GPL everywhere: the Apple Silicon build is nonfree, and the .mcpb ships none.
  • llms.txt updated to match.
  • docs/backlog/apple-silicon-ffmpeg-build.md.

Why

Distribution slice 2 of 4. The finish line: a new user installs the editor in one step from Claude Desktop, the official MCP Registry or a Claude Code plugin marketplace, and clip doctor says what's missing. Claude Desktop users mostly don't have Node, and the JSON-config route needs npx, which an app opened from the Dock often can't find.

How to test

pnpm build && pnpm build:mcpb          # release/makemyclip-editor.mcpb
pnpm exec mcpb info release/makemyclip-editor.mcpb

Double-click the .mcpb with Claude Desktop installed and ask "is my setup OK?".

Checks

  • pnpm test: 772 passed, 24 skipped.
  • pnpm type-check is clean. pnpm lint has 0 errors (the 7 warnings were already there). pnpm build and pnpm build:mcpb pass.
  • New tests:
    • tests/system-binary.test.ts: PATH before install folders, winget's .exe on Windows, the bare name when nothing is installed, and a non-executable file on PATH being skipped (real files).
    • tests/mcpb-manifest.test.ts: the manifest passes mcpb's 0.3 schema, and its tool list equals the MCP server's. Through mcpb's own getMcpConfigForManifest, the launch config is node /ext/dist/cli.js mcp, blank settings become "", and picked settings pass through.
    • tests/doctor.test.ts: Homebrew's FFmpeg and whisper-cli found for the extension; the extension's "doesn't include FFmpeg" fix; howToSet; the new resolveFfmpeg order, including a bundled binary that isn't on disk and an empty override.
    • tests/require-drawtext.test.ts: a newly resolved FFmpeg gets its own filter listing.
    • tests/workspace.test.ts: an empty MAKEMYCLIP_WORKSPACE falls back, to ~/Documents/MakeMyClip in the extension.
  • Bundle smoke test (macOS, by hand): mcpb unpack the built bundle, then start node dist/cli.js mcp from it as Claude Desktop would. The env had PATH=/usr/bin:/bin, blank settings and MAKEMYCLIP_BUNDLE=mcpb, and the MCP SDK client ran over stdio.
    • The bundle has no FFmpeg (the build checks), and all 8 tools are listed.
    • doctor finds /opt/homebrew/bin/ffmpeg 7.1.1 with drawtext and vidstab, and /opt/homebrew/bin/whisper-cli.
    • timeline_edit (add_media + a caption track) and timeline_export render a 3 s mp4 with burned-in text.
  • Claude Desktop 4.23118.0 (macOS, by hand): the locally built bundle (open -a Claude release/makemyclip-editor.mcpb) installed with all settings blank.
    • Claude Desktop's log shows "Using built-in Node.js for MCP server", then initialize and tools/list succeeding.
    • The default media folder ~/Documents/MakeMyClip was created.
    • It showed version 0.0.0-development, as expected for a local build. The release build gets the semantic-release version.
    • Not yet seen: a doctor answer inside a Claude Desktop chat.
  • Codex review (codex review --base feat/clip-doctor, gpt-6.1-sol), 4 passes:
    • Pass 1, P2: listFilters() and filterGraphFileOption() were cached once per process, but FFmpeg now resolves per run, so after a switch captions could stay refused or get the wrong option. Fixed in c566a06: cached per resolved binary.
    • Pass 1, P2: the PATH lookup accepted a non-executable file that the shell would skip. Fixed in c566a06: it requires the execute bit (any file on Windows).
    • Pass 2, P2: the resolveWhisper test didn't allow whisper-cli.exe. Fixed in 5132d3b.
    • Pass 3, P2: that fix wasn't Biome-formatted, which would block CI. Fixed in 5132d3b.
    • Pass 4: no findings.

yultyyev and others added 6 commits November 7, 2026 12:33
…undled

When ffmpeg-static's binary isn't on disk (install scripts skipped, or
the Claude Desktop extension, which leaves FFmpeg out), FFmpeg resolves
to the first ffmpeg on PATH or in Homebrew's, MacPorts', winget's,
Scoop's or Chocolatey's folder. whisper-cli uses the same lookup, since
an app opened from the Dock lacks Homebrew on PATH. An empty
MAKEMYCLIP_FFMPEG_PATH or MAKEMYCLIP_WHISPER_PATH counts as unset, and
FFmpeg resolves per run, so one installed while the server runs works.
probe uses the shared resolution instead of its own. doctor names the
extension's settings when MAKEMYCLIP_BUNDLE=mcpb.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
pnpm build:mcpb packs release/makemyclip-editor.mcpb: the built CLI and
a flat production install, run by Claude Desktop's built-in Node 24 as
`clip mcp`. FFmpeg stays out (the Apple Silicon build ffmpeg-static
downloads is not redistributable); the build fails if a binary slips in.
Settings: media folder, FFmpeg, whisper model and whisper-cli, each
optional. A blank media folder in the extension means
Documents/MakeMyClip. The manifest test checks the mcpb 0.3 schema, the
tool list against the server, and the launch env through mcpb's own
config builder.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…FFmpeg license notes

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…bles on PATH

The filter list and the filtergraph-file option were cached once per
process, but FFmpeg now resolves per run: after a switch, captions
could stay refused or get the wrong option. Both are now cached per
resolved path. The PATH lookup also skips files without the execute
bit, as the shell does, instead of picking one execa can't run.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@yultyyev
yultyyev merged commit aaad159 into feat/distribution Nov 7, 2026
1 check passed
@github-actions

github-actions Bot commented Nov 7, 2026

Copy link
Copy Markdown

🎉 This PR is included in version 0.4.1 🎉

The release is available on:

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant