@instush/mcp
v0.6.0
Published
Local MCP bridge for driving the Instush video editor from coding agents (Claude Code, Codex, …). Content-free translator: MCP on the agent side, a private localhost WebSocket to the editor tab.
Maintainers
Readme
@instush/mcp
A local MCP bridge that lets coding agents
(Claude Code, Codex, Cursor, …) drive the Instush video editor — by sending
instush.* API calls to a live editor tab and reading the results directly,
with no browser automation.
It is a content-free translator. It speaks MCP to the agent, and a small private WebSocket protocol to the editor tab. It carries no editor API knowledge, guides, or help text — all of that is served by the running editor, so this package rarely needs updating when the editor changes.
Agent ──MCP (stdio)──▶ @instush/mcp ──private WS (127.0.0.1)──▶ Editor tab
(this package) (instush.com)Status
Phases 1–4 built:
- MCP server (stdio) with three tools:
instush_run({ calls })— one or moreinstush.*API calls, one per line, strict-JSON arguments; returns the editor's JSON report.instush_import({ path, name?, kind? })— read a local media file and import it into the editor's Media Library (bytes travel over the local bridge as one binary WebSocket frame; imports are serialized).instush_snapshot({ ...params })— capture a frame; the editor renders it to base64 and the result carries the image INLINE as MCP image content (the agent sees the frame directly — required for clients with no file tools, e.g. Claude Desktop), plus JSON with a temp-file path for reuse. Params are forwarded opaquely — the parameter reference is served by the editor (instush://help).
- MCP resources, proxied live from the editor (nothing cached here):
instush://help(the MCP-surface API reference),instush://state(live state summary),instush://guides/<name>(authoring guides). Empty until an editor pairs;resources/list_changedfires on connect/disconnect. - 6-digit code pairing (reversed — no pairing tool needed): the first
unpaired call registers this bridge's
{ port, token }at the instush rendezvous (POST /api/mcp-pair/register, anonymous + rate-limited) and fails fast with the returned code; the agent shows it and the USER types it into the editor's pairing popover, which redeems it (POST /api/mcp-pair/:code/redeem, authenticated, single-use, 5-minute TTL) and connects. The user never has to type anything back to the agent. If the rendezvous is unreachable, the error falls back to the manualws://paste string (fully offline). - Unpaired calls after the first PARK for up to 60 s and auto-flush when the editor connects — the agent retries immediately after relaying the code and never has to ask "did you type it?". A park timeout resets the cycle, so the next retry re-issues fresh pairing info (new code if expired).
- WebSocket server on
127.0.0.1the editor connects to - Security: token handshake +
127.0.0.1-only bind +Origin+Hostchecks
Dev: point the rendezvous at the local editor's mock with
INSTUSH_PAIR_URL=https://localhost:5173/api/mcp-pair (plus
NODE_TLS_REJECT_UNAUTHORIZED=0 — node doesn't trust the mkcert dev cert).
Published on npm as @instush/mcp (run: npx -y @instush/mcp); also
distributed as a one-click Claude Desktop bundle at
https://instush.com/instush.mcpb (built with npx @anthropic-ai/mcpb pack).
Run (dev)
npm install
npm startPairing is lazy: the agent's first Instush call returns a 6-digit code to show
the user, who types it into the editor's pairing popover (cable icon) at
https://instush.com/editor/<project-id>. The manual fallback ws:// string
is printed to stderr at startup and included in the error when the
rendezvous is unreachable.
Use from an agent
Register the server (example for Claude Code):
claude mcp add --scope user instush -- node /absolute/path/to/instush-mcp/src/index.js(--scope user registers it for every project; the default local scope ties
the server to the directory you run the command from.)
Then the agent has the tools above. Try:
instush_run({ "calls": "instush.help({\"surface\":\"mcp\"})" })
instush_run({ "calls": "instush.state.summary()" })
instush_import({ "path": "/Users/me/Music/score.mp3" })
instush_snapshot({})(Or read the instush://help resource — same text as the first call.)
Protocol (node ↔ editor, private — not MCP)
JSON messages over the WebSocket, correlated by id; every editor reply is
{ id, type: "result", result } (result = a JSON string) or
{ id, type: "error", error }:
| Direction | Message |
| --- | --- |
| editor → node | { type: "hello", token } (first message; authenticates) |
| node → editor | { id, type: "run", calls } |
| node → editor | { id, type: "resources" } — MCP resource descriptors |
| node → editor | { id, type: "read", uri } — one resource's { mimeType, text } |
| node → editor | { id, type: "import", meta } + ONE binary frame (the file bytes) |
| node → editor | { id, type: "snapshot", params } — { dataUrl, width, height, mimeType } |
The editor executes run through its existing window.__instushConsole.run
pipeline; the other verbs go to window.__instushMcp (the editor-owned MCP
surface). meta / params are opaque to this package.
Security notes
- Bound to
127.0.0.1only — never reachable off the machine. - Every connection must present the per-process token in its
hello. Originis restricted tohttps://instush.com(plus localhost for dev).Hostis checked to defeat DNS-rebinding from a malicious page.- The token lives in the pairing string's URL fragment, so it is never sent over the wire — the editor reads it client-side and presents it in the handshake.
License
MIT
Privacy Policy
This bridge is a local connector and collects nothing. It relays commands
between your AI client and your own Instush editor tab over a
localhost-only WebSocket; your media and project data never pass through
Instush servers. Its single network call is the optional pairing
registration to instush.com/api/mcp-pair, which carries only a local
port number and a random pairing token (deleted within 5 minutes).
Instush's full privacy policy: https://instush.com/privacy
