mcut

Development environment

Install the mcut monorepo, run local development, and validate changes.

Prerequisites

mcut is a Bun workspace monorepo managed with Turbo.

  • Use Bun 1.3.14.
  • Package consumers should use Node 20.11 or newer.
  • Run commands from the repository root unless a package README says otherwise.

Install

bun install

CI uses a frozen lockfile:

bun install --frozen-lockfile

Workspace layout

  • packages/*: publishable @mcut/* SDK packages.
  • examples/*: runnable examples for headless editing and MCP workflows.
  • apps/studio holds the Studio renderer, a static Next export, and the shadcn registry source.
  • apps/desktop holds the Electron shell that serves the export and hosts the live bridge.
  • apps/web: the marketing site and /docs implementation.
  • docs/: maintainer and release process notes for the repository.

Run locally

Studio in the desktop window

Run this from the repo root:

bun dev

bun dev builds the packages, starts next dev on port 3000, and opens the Electron app with MCUT_DEV_URL=http://localhost:3000 so the window loads the dev server with hot reload. The app hosts the live bridge on port 44737 and prints its MCP_URL line, which bun dev echoes. Paste that URL into any Streamable HTTP MCP client, or use Add to Cursor from the app's MCP menu. Ctrl-C stops both processes.

bun dev watches apps/desktop/src and packages/desktop-ipc/src. It rebuilds with tsdown, retypechecks apps/desktop on a contract change, and relaunches the Electron window. A main or preload edit reaches the window without a restart. Renderer edits still hot reload through the Next dev server.

Main runs with --inspect=9229 and the renderer with --remote-debugging-port=9222. dist/main.mjs and dist/preload.cjs carry source maps. .vscode/launch.json has Attach to Studio, which attaches to both while bun dev runs.

bun run desktop:check is the one command that builds, typechecks, lints, packages, and smokes the desktop app.

The token comes from the app's user data directory, see Install Studio. MCUT_STUDIO_PORT and MCUT_BRIDGE_PORT change the two ports.

Browser bridge without Electron

The standalone bridge is the developer path for a browser tab, the same process bunx -p @mcut/mcp-server mcut-bridge start runs outside the repository:

bun run scripts/mcut-local-dev.ts bridge

It listens on 44737 with the fixed token mcut-local-dev. Start bun run --cwd apps/studio dev in another terminal and open the URL from bun run scripts/mcut-local-dev.ts url. bun run scripts/mcut-local-dev.ts mcp-url prints the matching MCP URL.

The package also exposes compatibility stdio entrypoints: mcut-mcp for file-backed projects, mcut-mcp-live for clients that should start their own live bridge, and mcut-bridge mcp for clients that cannot connect over HTTP.

Docs site only

bun run dev:web

Validate changes

Use the narrowest command that covers your change while working, then run the broader checks before opening a pull request.

bun run build
bun run typecheck
bun run test
bun run lint
bun run smoke:packages

bun run test includes a compositor pixel test that renders in Playwright Chromium. Install that browser once with bunx playwright install chromium.

Release validation runs the full package gate:

bun run release:check

release:check builds packages, typechecks, runs tests, runs package smoke checks, and validates package metadata.

Studio smoke by surface

apps/studio/smoke drives every user-facing Studio feature the way a user does, on three surfaces. embed is the static export the homepage serves, opened in Chromium. electron-dev is apps/desktop/dist under the development Electron. installed is a packaged binary such as the released AppImage or the .app inside the DMG. features.ts is the one registry. It closes the feature ids and the surfaces, gives each feature a tier, and marks the cells a surface cannot offer, such as the AssemblyAI key field on the site. One driver per feature runs on every surface and asserts a literal, so the report has one pass, fail, blocked, or unsupported row per feature per surface, with a screenshot for each.

bun apps/studio/smoke/coverage.ts
node --no-warnings apps/studio/smoke/run.ts --surface embed
xvfb-run --auto-servernum -- node --no-warnings apps/studio/smoke/run.ts --surface electron-dev --tier fast
xvfb-run --auto-servernum -- node --no-warnings apps/studio/smoke/run.ts --surface installed --electron release/mcut-studio.AppImage

coverage.ts prints the matrix and the cells with no driver, and fails with --strict when a gap exists. run.ts writes report.md, report.json, and one screenshot per feature under reports/smoke/<surface>. The runner runs under node, because Playwright's _electron.launch hangs under Bun.

The on-device caption drivers download the Whisper model over the real network when huggingface.co answers. When it does not, set MCUT_WHISPER_MIRROR to a directory holding onnx-community/<model>/... and the harness serves it on 127.0.0.1 and routes the Hub and jsDelivr requests there. Without either the row reports blocked. The Center person driver needs no network, because the YuNet face model ships in @mcut/media, and it fails if Studio requests the model file. ASSEMBLYAI_API_KEY turns the AssemblyAI row into a real cloud transcription. MCUT_CHROME_PATH points the embed at Google Chrome for MP4 export.

.github/workflows/smoke.yml runs the fast tier of electron-dev on every pull request that touches Studio, the desktop shell, or a package, and the full tier of every surface nightly and on workflow_dispatch. The installed jobs download the latest mcut-desktop@* release, or the tag given as the release input, and smoke the AppImage on Ubuntu and the arm64 DMG on macos-15.

Website docs

The website docs are written in apps/web/content/docs. The web app generates Fumadocs sources and static Markdown mirrors during build, and copies the shadcn registry from apps/studio/public/r into public/r so https://mcut.com/r/<item>.json serves the committed registry:

bun run --filter=mcut-web build

Use the web package checks when editing docs or site code:

bun run --filter=mcut-web typecheck
bun run --filter=mcut-web lint

On this page