Skip to main content

recreator-mcp

MCP server exposing recreator-core operations as typed tools.

A thin protocol wrapper: schema translation only, zero editing logic. Every tool translates JSON arguments into a recreator-core call and translates the result back. No transcription, no detection, no thresholds, no editorial judgment — the calling agent decides what to cut, caption, or animate and passes explicit ranges and values. Core remains fully usable as a library and CLI without this package installed.

Boundary conventions

  • All times are seconds (floating point) at the tool boundary. Core's microsecond Time type is constructed at the edge.
  • Ranges are [[start, end], ...] pairs, e.g. [[12.4, 15.9]].
  • Keyframe offsets are measured from the start of the clip, not the timeline.
  • .recreator/project.json is the source of truth and the default for every tool's project argument. Tools load it, apply the op, and save atomically (write to a temporary file in the same directory, then rename).
  • A CapCut draft is a build artifact, produced only by recreator.export.capcut.

Tools

Tool Purpose
recreator.project.load Read timeline state; source of clip ids
recreator.project.save Update project name and metadata
recreator.probe.media ffprobe duration, resolution, streams
recreator.probe.loudness Windowed RMS/peak dB series (raw measurements only)
recreator.timeline.init_from_media Construct a timeline seeded from probed media files
recreator.timeline.remove_ranges Delete ranges, rippling by default
recreator.timeline.keep_ranges Keep only the given ranges
recreator.timeline.mute_ranges Silence ranges without changing timing
recreator.timeline.bleep_ranges Censor ranges: mute, bleep, or cut
recreator.timeline.add_text_clips Add caption/text clips with character-offset styles
recreator.timeline.add_media_clips Add b-roll, overlays, music
recreator.timeline.add_keyframes Animate one property of one clip
recreator.timeline.set_transform Static scale/position/rotation/opacity/crop
recreator.export.capcut Build a CapCut draft into a caller-specified directory
recreator.validate Report structural issues before exporting
recreator.catalog.list Browse every tool this server exposes, optionally by namespace
recreator.catalog.get Full parameter schema, return shape, and example for one tool id
recreator.templates.list Browse HyperFrames route templates, optionally filtered by a query substring
recreator.templates.get Full contract for one route: input, output, triggers, interview shape, pre-fill table
recreator.templates.recommend Rank routes against a free-text prompt by counted lexical evidence
recreator.jobs.submit Submit a registered async job kind; optionally block until done
recreator.jobs.get Read one job's current record, non-blocking
recreator.jobs.wait Rejoin an existing job and block until terminal
recreator.jobs.list List recent jobs, newest first, optionally filtered by status
recreator.jobs.cancel Request cooperative cancellation of a queued or running job

Async jobs

Every tool above is fast and synchronous — this is not that. It is scaffolding for future generative-media operations (b-roll generation, TTS, image gen, cloud STT) that call out to paid third-party APIs and genuinely take minutes, mirroring the shape of Higgsfield's async job API (generate create --wait, generate get, generate wait).

A job kind is a plain Python callable registered by name in recreator_mcp.jobs; the runner itself is pure mechanism (dispatch, persistence, status) and knows nothing about what any kind does. Registering a future kind — e.g. broll.generate — is one call to register_kind, with no changes to the runner.

One real kind ships today: probe.batch, which probes N media files via core's probe_media and reports progress as it goes. Each probe is a real ffprobe subprocess, so it earns async honestly for large batches.

recreator.jobs.submit(kind="probe.batch", params={"paths": [...]})
  -> {job_id, kind, status: "queued", submitted_at}

recreator.jobs.submit(kind="probe.batch", params={"paths": [...]}, wait=true)
  -> full record with status: "completed" and result: [...]

recreator.jobs.wait(job_id="...")       # rejoin, block until terminal
recreator.jobs.get(job_id="...")        # non-blocking status check
recreator.jobs.list(status="running")   # recent jobs, newest first
recreator.jobs.cancel(job_id="...")     # cooperative — stops between units

Job record: job_id, kind, status (queued / running / completed / failed / cancelled), params, submitted_at, started_at, completed_at, progress (0.0–1.0), message, result (on success), error (on failure).

Persistence: jobs live under .recreator/jobs/, one JSON file per job, written atomically the same way as project.json (tempfile in the same directory, fsync, then Path.replace). A job submitted in one MCP session is visible to recreator.jobs.get/recreator.jobs.wait in a later one — that cross-session rejoin is the entire point of the job/status split. Cancelling a job from a session that did not submit it is rejected with an actionable error rather than silently doing nothing, since there is no in-process handle to signal.

Cancellation is cooperative: a job kind calls cancel.raise_if_cancelled() between units of work (e.g. between files in a batch), so it takes effect at a clean boundary rather than killing a thread mid-unit.

Catalog

recreator.catalog.list and recreator.catalog.get let an agent discover operations and their parameters at runtime instead of a skill hardcoding them in markdown — the same affordance as higgsfield model list / model get. The catalog is generated by introspecting the live registered tools (mcp.list_tools()), so it can never drift from the actual tool set; only each tool's namespace and whether it mutates project.json are curated, everything else (params, types, defaults, descriptions) is reflected straight from the tool's JSON input schema.

recreator.catalog.list                                   # every tool, grouped
recreator.catalog.list(namespace="timeline")              # one namespace
recreator.catalog.get(id="recreator.timeline.remove_ranges")  # full schema

Templates

recreator.templates.list and recreator.templates.get expose the ten HyperFrames route templates — the same affordance as higgsfield preset list / preset resolve, so an agent can browse and resolve a route contract at runtime instead of a skill hardcoding it in markdown. Both tools parse skills/recreator/hyperframes/references/routes/*.md directly (never hand-copied) and cache the result in-process, keyed by the routes directory's mtime so edits on disk are picked up without a restart.

recreator.templates.list                                       # all 10 routes
recreator.templates.list(query="launch")                        # substring filter
recreator.templates.get(id="talking-head-recut")                # full contract

recreator.templates.recommend occupies the slot where Higgsfield has generate cost — re:Creator has no metered spend to estimate, so it recommends a route instead. Its ranking is counted, attributable lexical evidence only: a trigger phrase found verbatim in the prompt, a keyword overlap with a route's input/output text, or an exact production_type row hit from production-map.md. Candidates are ordered lexicographically by the per-kind evidence count, most specific signal first — trigger_phrase (a route author's own curated wording) outranks production_type_row (an exact lookup-table hit), which outranks keyword_overlap (incidental shared words) — because a curated phrase is structurally a stronger match than a stray common word, not because of any tuned weight: nothing is scaled or multiplied, only compared count-by-count. score is the total evidence count; signals is the {trigger_phrase, production_type_row, keyword_overlap} breakdown the ordering is actually computed from, so a caller can reproduce the ranking by hand from what is returned. Ties break on route id for byte-stable output. This is measurement in the sense CLAUDE.md allows, not a recommendation engine: it returns ranked candidates plus a note, never a single verdict, and an empty list rather than a fallback guess when nothing matches.

recreator.templates.recommend(prompt="turn this website into a video")
  -> {
       candidates: [
         {
           route: "product-launch-video",
           score: 3,
           signals: {trigger_phrase: 1, production_type_row: 0, keyword_overlap: 2},
           evidence: [
             {kind: "trigger_phrase", detail: "turn this website into a video"},
             {kind: "keyword_overlap:input", detail: "website"},
             {kind: "keyword_overlap:output", detail: "video"}
           ],
           contract_summary: "A product promo, launch video, site tour, or showcase MP4."
         },
         ...
       ],
       note: "Ranking orders candidates by counted evidence, most specific signal first ..."
     }

The routes directory is located relative to the installed package or by walking up from the current working directory to the repo root; set RECREATOR_SKILLS_DIR to override it explicitly (e.g. under HTTP transport running from an unrelated cwd). RECREATOR_MCP_* is reserved for transport and auth configuration, not this lookup.

HyperFrames template registry

The recreator.hyperframes.templates.* tools front a Cloudflare Worker that serves the mirrored HyperFrames template catalog (metadata in D1, source zips in R2). The worker is the only source: there is no local-file fallback and no direct call to hyperframes.dev or static.heygen.ai, so an agent is never uncertain which registry answered it.

recreator.hyperframes.templates.tags()
recreator.hyperframes.templates.list(aspect="9:16", type="block", tags=["ad-template"])
recreator.hyperframes.templates.get(name="ai-chat-reveal")
recreator.hyperframes.templates.pull(name="ai-chat-reveal", dest="./templates/hero")

Discovery runs widest to narrowest. tags publishes the vocabulary a filter may use, each with its template count; list filters; get returns everything known about the few finalists chosen.

list filters on hard facts onlyaspect, type, tags (ANDed), min_duration, max_duration, origin, limit — and returns the worker's candidates in the worker's order (name ASC), untouched. It does no scoring, no relevance sort, and picks no winner: the response carries a ranking note saying so. Which few templates are worth showing the user, and in what order, is the agent's judgment — it has the brief, the footage, and the user's taste, and the registry has none of those. This is the same boundary recreator.templates.recommend respects, taken one step further: here there is not even counted lexical evidence to report, because filtering on facts is all the registry is asked to do.

Each candidate carries six fields — name, title, type, aspect, duration_s, tags — and nothing else. The description, dimensions, provenance and raw_tags live on get, which is called on a handful of finalists rather than on all 380; on the live registry the former full shape was 143.7 KB against 54.1 KB now, and description alone was half of it.

Only canonical tags filter — those carried by at least 8 templates. An unknown or merely-rare tag is rejected with near matches and the canonical tag it folds into, never answered with an empty list, because an empty list reads as "no such template exists" and sends an agent away from templates that were there all along. Call tags when unsure. Filtering on a concrete aspect also returns aspect-agnostic (any) templates, which adopt whatever canvas hosts them — 219 of 380 are such components, and excluding them would hide most of the usable pieces.

pull is the one tool in this namespace that writes to disk, so it is deliberately cautious:

  • It refuses a non-empty destination unless overwrite=true, the same consent rule the CapCut install path follows.
  • It rejects the whole archive if any member would write outside the destination — a ../ path, an absolute path, or a symlink — before extracting a single byte, so a rejected pull leaves nothing half-unpacked.
  • A template whose source lives with the HyperFrames CLI rather than in R2 raises with the exact npx hyperframes catalog --query "<name>" command to run instead. That is a different install path, not a failure.

Presigned R2 download URLs are short-lived (300s TTL) and single-use. Where this client mints one itself it is followed once, immediately, and never logged or written to disk.

pull without a registry credential

Downloading a template is two steps with different credential needs. Minting the presigned URL requires the registry bearer token — a server-side credential. Fetching that URL requires nothing at all. So pull splits them:

recreator.registry.get_source_url(name="ai-chat-reveal")   # remote MCP, holds the token
  -> { name, url, bytes, expires_in_seconds: 300 }

recreator.hyperframes.templates.pull(                      # here, no credential read
    name="ai-chat-reveal",
    dest="./templates/hero",
    source_url="https://...",   # from the call above
    declared_bytes=48213,       # its `bytes`, for the size check
)

With source_url supplied, resolve_registry_config() is never called and neither RECREATOR_REGISTRY_URL nor RECREATOR_REGISTRY_TOKEN is read. The URL is the whole authorization. It must be https:// — a caller-supplied URL fetched blind is an SSRF vector, so anything else is refused outright. Because the URL expires after 300s and is single-use, a failed download means mint a fresh one, not retry the same one.

Omit source_url and pull resolves the URL itself, exactly as before. That path still needs the credential locally, and is the one to use when you are developing against the registry directly.

Flag Env var Default
--registry-url RECREATOR_REGISTRY_URL none — required
--registry-token-file RECREATOR_REGISTRY_TOKEN none — required

Both halves are required for every tool in the namespace except a pull given a source_url, and are resolved CLI flag > env var, inside the call. With either missing, those tools raise a ToolError naming both environment variables; they never fall back silently. The base URL must be https:// — the client refuses to put a bearer token on a plaintext request.

RECREATOR_REGISTRY_URL=https://<worker>.workers.dev \
RECREATOR_REGISTRY_TOKEN=<token> recreator-mcp

Safety

recreator.export.capcut writes to the out_dir you specify and never defaults anywhere inside CapCut's own library. Passing install=true additionally copies the built draft into the user's real CapCut drafts folder, where CapCut will show it as a project; that step needs overwrite=true to replace an existing draft of the same name. Leave install off unless the user asked for it.

Configuration

Claude Code — .mcp.json

Place at the repository root (or merge into an existing .mcp.json):

{
  "mcpServers": {
    "recreator": {
      "command": "uv",
      "args": ["run", "--package", "recreator-mcp", "recreator-mcp"],
      "cwd": "/absolute/path/to/ReCreator"
    }
  }
}

Codex — ~/.codex/config.toml

[mcp_servers.recreator]
command = "uv"
args = ["run", "--package", "recreator-mcp", "recreator-mcp"]
cwd = "/absolute/path/to/ReCreator"

Replace /absolute/path/to/ReCreator with the workspace root. Both hosts launch the server over stdio. If the package is installed into an environment already on PATH, command = "recreator-mcp" with no args works as well.

Install

From the workspace root:

uv sync --all-packages --all-groups

uv sync --all-groups alone installs only the root dev group, which does not include this package; --all-packages is what puts recreator-mcp and the mcp SDK into the environment.

CLI fallback

Hosts without MCP support reach the same operations through recreator-core's CLI, which needs no MCP installed:

recreator project init --media <path>
recreator probe <path>
recreator timeline remove-ranges --ranges '[[12.4,15.9]]'
recreator export capcut --draft-name "my-cut"

The effect on project.json is identical either way.

Transports

recreator-mcp runs over stdio (default, unchanged) or streamable HTTP. Local files stay local either way: HTTP mode does not move project.json or media off the machine the server runs on, it just changes how a client talks to the same server.

recreator-mcp                                    # stdio, exactly as before
recreator-mcp --transport http                   # HTTP, 127.0.0.1:8000/mcp
recreator-mcp --transport http --host 0.0.0.0 --port 9000 --path /recreator
Flag Env var Default
--transport {stdio,http} RECREATOR_MCP_TRANSPORT stdio
--host RECREATOR_MCP_HOST 127.0.0.1
--port RECREATOR_MCP_PORT 8000
--path RECREATOR_MCP_PATH /mcp

CLI flags win over environment variables, which win over the defaults above. Binding 0.0.0.0 is never the default — it takes an explicit --host 0.0.0.0 or RECREATOR_MCP_HOST=0.0.0.0.

Authentication

HTTP mode requires a bearer token; stdio needs none (the host process already controls who can talk to it). Set the token via RECREATOR_MCP_TOKEN or --token-file <path>, then send it as Authorization: Bearer <token>.

RECREATOR_MCP_TOKEN=$(openssl rand -hex 32) recreator-mcp --transport http

Starting --transport http with no token configured refuses to start:

Refusing to start HTTP transport without a bearer token: an unauthenticated
HTTP MCP server would expose filesystem-touching tools to the network.
Fix it by either setting RECREATOR_MCP_TOKEN (or passing --token-file), or, if
you understand the risk and are only binding to a trusted network, pass
--insecure.

Pass --insecure only for a deliberately trusted network (e.g. 127.0.0.1 loopback during local development) — it accepts every request unauthenticated and prints a warning on startup. Token comparison uses a constant-time compare so a network attacker cannot recover the token by timing responses.

This is a static pre-shared token, not full OAuth. It is not the token-based auth Higgsfield's hosted MCP server uses against a user account — there is no provisioning, rotation, or per-client identity. The verifier is structured so a real OAuthAuthorizationServerProvider can replace it later without reshaping how transports are wired up.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

recreator_mcp-0.1.4.tar.gz (52.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

recreator_mcp-0.1.4-py3-none-any.whl (48.9 kB view details)

Uploaded Python 3

File details

Details for the file recreator_mcp-0.1.4.tar.gz.

File metadata

  • Download URL: recreator_mcp-0.1.4.tar.gz
  • Upload date:
  • Size: 52.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.12.7 {"installer":{"name":"uv","version":"0.12.7","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for recreator_mcp-0.1.4.tar.gz
Algorithm Hash digest
SHA256 0ab4b9b9e8769b2a4c72a2d6b1bc4a27a13b9f4c7e34dc68b275be09b01dbf60
MD5 6beebb39e6a3775a8192b4b379ab4214
BLAKE2b-256 1c52010346cceb8b07b5e36d07c06d4ee08d28d80e1517a896ea080e759a3666

See more details on using hashes here.

File details

Details for the file recreator_mcp-0.1.4-py3-none-any.whl.

File metadata

  • Download URL: recreator_mcp-0.1.4-py3-none-any.whl
  • Upload date:
  • Size: 48.9 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.12.7 {"installer":{"name":"uv","version":"0.12.7","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for recreator_mcp-0.1.4-py3-none-any.whl
Algorithm Hash digest
SHA256 05971051c192751a8a3dc0cb9064e0932b82498ac3972ce293494e0040e97b65
MD5 498f71d473e8fbbe2031e6ee068de5cc
BLAKE2b-256 7bdbbc7d8e4eb7ce41cb852c7f5ac1958bd74170ded8c336a36c06133e8b1dfb

See more details on using hashes here.

Release history Release notifications | RSS feed

This release

0.1.4 This release

2 files

0.1.3

2 files

0.1.2

2 files

0.1.1

2 files

0.1.0

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page