Endpoint
https://cue.vibeset.ai/mcpTransport
Streamable HTTP (stateless)
Auth
API key, either header
Authentication
Cue’s MCP server uses your API key, not an OAuth flow. Both headers work, so a config written against either one is fine:/mcp to the app, which reads the bearer token first and
falls back to x-api-key. Bearer is what the MCP authorization spec uses and what most clients
default to.
Only tools/call is authenticated. initialize, tools/list and ping stay anonymous, because a
client has to finish a handshake before it has anywhere to put a credential.
Many remote MCP servers use OAuth and prompt you to sign in. Cue is simpler. Paste a key from the
developer portal into your client’s
headers, the same key
you would use for REST.Connect
Most MCP clients take the same config shape, a streamable-HTTP server with a header. Add thiscue
block to your client’s MCP config:
.mcp.json (Claude Code), .cursor/mcp.json (Cursor), VS
Code’s mcp.json, ~/.codeium/windsurf/mcp_config.json (Windsurf), Zed’s settings, and others.
Some clients omit "type": "http" and take url plus headers alone. A few client-specific
notes:
- CLI agents
- Desktop / IDE apps
- SDK / custom
Agents with an Cursor, Windsurf, and others accept the JSON block above in their MCP settings instead.
mcp add command register Cue in one line, for example Claude Code:What you can do
Eight tools. Five are the workflows, three are plumbing. See the Tools reference for parameters, examples, and responses.Two decisions belong to the person
By default an agent using Cue stops twice and asks, rather than running the whole job on its own:- Which track.
match_musicreturns a shortlist, not an answer. The agent should show several with their title, artist, tempo, length and whether they are instrumental, and let you pick. - Where it loops or cuts.
loop_musicreturns a plan andplan.alternates, other loop regions found in the same track.get_sync_pointsreturns a ranked list of offsets. Either way the agent should show the options and let you choose.
A typical agent flow
Ask your agent something like “Find music for this clip and loop the best track under the whole thing.” Under the hood it chains the tools:1
Ingest
ingest_video(video_url=…) returns a video_id and a poll of /v1/videos/{video_id}. Then
check_video_status(video_id) until it reads ready. Already have a video_id? Skip straight
to matching.2
Match
match_music(video_id=…) returns a ranked shortlist, each track with a track_id.3
Sync or loop
get_sync_points(video_id, track_id) for where to cut, or loop_music(video_id, track_id) to
lay a short track under a long clip.underscore sits outside that chain. It takes a script and nothing else, so an agent working from
a draft, a storyboard, a voiceover, a post, can call it before any footage exists. map_beats sits
outside it too. It takes audio you can serve and never touches a video.
If the agent already knows which track it wants, track_id is the Soundstripe song id. Pass it
straight to get_sync_points or loop_music and skip matching entirely.
The async pattern
First-time work runs off the request path so it never times out, and comes back as a job instead of a result:check_job(job_id, kind), where kind is "match", "loop", "align" or
"beats", until it returns status: "complete". Warm calls return the result directly with no
polling.
match_music(video_url=…) is the exception. It ingests only. It returns a video_id, a poll
of /v1/videos/{video_id}, and a next telling you to call match again once the video is ready.
There is no job_id in that response, so check_job has nothing to poll. Use
check_video_status(video_id), then call match_music(video_id=…).loop_music is warm only when that exact
(video_id, track_id) pair has been built before. get_sync_points is warm on that, or when Cue
already holds both the video’s sync profile and the track’s audio profile. match_music returns a
job whenever the video’s analysis is not cached yet.
Errors
Auth failures are answered at the HTTP layer, so a client can check the status code. A rejected tool call is a real HTTP401 with a WWW-Authenticate: Bearer challenge. Every other failure
comes back as an MCP tool error carrying the same reason the REST API would give, because a bare
transport status reaches an agent as an opaque failure it cannot act on:
401, missing or invalid key. An HTTP status, not a tool error. Check the header in your client config.402, no card on file, credits spent or expired, or the monthly spend cap hit. The message says which.403, the video or job belongs to another account.404, unknownvideo_idorjob_id.429, over the 60 requests per minute limit. The agent should back off and retry.502, an async match, loop or sync job failed. A failed beats job comes back as a422.
Billing
The MCP server is a thin facade over/v1. A tool call is authenticated, rate-limited,
spend-capped, and metered exactly once, identically to the REST call it wraps. Going through MCP
never bills twice.
check_video_status is never billed. check_job is not billed either. The underlying job bills
once when it completes, whether or not you ever poll it, so polling adds nothing and skipping the
poll saves nothing.
What each call costs is on Metering & pricing.
Troubleshooting
The tools don't show up
The tools don't show up
Confirm the server is connected in your client’s MCP status (
/mcp in Claude Code, the MCP or
tools panel in an IDE or desktop app) and reload or restart it after editing the config. Check
the JSON is valid and the URL is exactly https://cue.vibeset.ai/mcp.Every call fails with 401
Every call fails with 401
Your key is not reaching the server, or it is no longer valid. Both
Authorization: Bearer and
x-api-key are accepted, so the header name is not the problem. Check the key is active in the
portal, since a revoked key stops working immediately, and that your client is sending headers on
the POST rather than only on the initial handshake.A tool returns 'processing' and never finishes
A tool returns 'processing' and never finishes
That is the async pattern. Cold work returns a
job_id. The agent should call
check_job(job_id, kind) until status is complete. If it stays processing for minutes, the
video may still be analyzing, so check check_video_status first. Do not resend the original
call while a job is in flight — poll the job you have. Resending with the same idempotency_key is safe if you must: it returns the original job rather than starting a second one.loop_music says applicable: false
loop_music says applicable: false
The video is not meaningfully longer than the track, so there is nothing to loop, and you are not
billed.
hint says why in plain language and retryable says whether calling again can change
it. Use loop for clips longer than the track by 30s or more. For shorter clips, use
get_sync_points and play the track straight. See Loop.