Announcing CIMD support for MCP Client registration
Learn more

OpusClip MCP vs OpusClip API for AI Agents

Nityashree Yadunath
Product Marketing Manager

TL;DR

  • OpusClip's hosted MCP server ships 29 tools plus two thumbnail tools; clip editing and on-screen video analysis are documented there, not in the REST reference.
  • MCP auth is OAuth only, with the OpusClip organization pinned at consent. The REST API uses an organization-level API key sent as a bearer token.
  • Posting, scheduling, and sharing return an approval_url on listed connectors, and Scalekit's connector schemas describe the same flow; a self-configured mcp.opus.pro endpoint commits them directly.
  • Both paths share Pro Beta and Max caps: 900 credits per month (403), four concurrent projects (429), and a 10-credit minimum per project.
  • Scalekit's OpusClip connector handles OAuth 2.1, token storage, and refresh per user, so the MCP vs API choice doesn't change your auth infrastructure.

Why OpusClip forces a real choice

Your agent needs to turn a customer's hour-long webinar into ranked vertical clips, fix the captions, and queue the best three for Friday. OpusClip ships two ways to do that: a hosted Model Context Protocol (MCP) server and a REST API. They are not interchangeable. The tool surfaces overlap but split on editing, generation, and cleanup. The auth models differ, and the surface you reach decides whether a publish call commits or waits for a human. In a multi-tenant product, every customer also brings their own OpusClip organization, credits, and caps. Here's how to pick, and how to wire either path into a production agent.

What OpusClip MCP and OpusClip API actually are

Same vendor, same credit pool, two different contracts.

OpusClip MCP

OpusClip runs a hosted, remote MCP server over Streamable HTTP at mcp.opus.pro/mcp. It is maintained by OpusClip and listed in the official MCP Registry as io.github.opus-pro/opusclip, so there is nothing to deploy. Authentication is OAuth only: the user signs in to OpusClip in a browser and picks an organization on the consent screen. Any account can complete OAuth and read the catalog, but calling a tool requires a Pro Beta, Max, or Business plan. OpusClip documents 13 read tools, 14 write tools, two deprecated signposts, and two thumbnail-generation tools. A separate Claude Connector serves Claude.ai with the 29 workflow tools and no generation tools.

OpusClip API

The REST API lives under api.opus.pro/api. It covers projects (POST /api/clip-projects), clips (GET /api/exportable-clips), brand templates, censor jobs, transcripts, thumbnail generation, collections, and social posting. Authentication is an organization-level API key from the OpusClip dashboard, sent as Authorization: Bearer <API_KEY>, with an x-opus-org-id header for users who belong to more than one organization. API access requires the same Pro Beta, Max, or Business plans, and core project and clip endpoints allow 30 requests per minute per key. Project completion can trigger a signed webhook through conclusionActions.

Comparing them where it matters for agents

Four dimensions change what you build: what the agent can do, how it authenticates, what you operate, and which workloads fit.

What your agent can actually do

Most of the clipping workflow exists on both surfaces. The last column is the 30-tool surface Scalekit's connector exposes.

Capability
OpusClip MCP (self-configured)
OpusClip API
Scalekit OpusClip MCP connector
Submit a video for AI clipping
Yes, opusclip_submit_project
Yes, POST /api/clip-projects
Yes
List projects
Yes, opusclip_list_projects
Not documented
Yes
List ranked clips
Yes, opusclip_list_clips
Yes, GET /api/exportable-clips
Yes
Edit clips
Yes, opusclip_edit_clip
Not documented
Yes
On-screen visual analysis
Yes, opusclip_analyze_video
Not documented
Yes
Export HD, 4K, or Premiere XML
Yes, opusclip_export_clip
HD URI on clip records
Yes
Generate thumbnails
Yes
Yes, POST /api/generative-jobs
No
Delete collections or remove clips
No
Yes
No
Post, schedule, cancel, share
Yes, commits directly
Yes
Yes, returns approval_url
Completion webhook
Yes, webhookUrl
Yes, signed conclusionActions
Yes, webhookUrl
Cap headroom
Yes, opusclip_get_usage
X-RateLimit-* headers
Yes, opusclip_get_usage

Where the MCP surface is ahead

The most consequential gap is editing. opusclip_edit_clip takes an ordered list of operations such as remove_filler_words, trim_section, replace_phrase, set_style, and undo, applies them in one call, and triggers a single preview re-render. A dryRun flag reports what a batch would change without saving it. OpusClip's API overview routes clip editing to the MCP documentation, and the published REST endpoints have no equivalent. opusclip_analyze_video adds keyframe-level evidence about faces, screen regions, and the active speaker, and spends no credits. An agent that reasons about a clip before cutting it needs both tools, which means MCP. Scalekit's connector also includes opusclip_preview_clips, which renders clip cards on hosts that support MCP Apps.

Where the REST API is ahead

The API owns cleanup and verifiable plumbing. Deleting a collection and removing a clip from one exist only as REST endpoints. Completion webhooks are signed: X-Opus-Signature is an HMAC-SHA256 over the raw body plus X-Opus-Salt, keyed with the organization's API secret. An OAuth-only integration never holds that secret, so it cannot verify those signatures. Thumbnail generation exists on the REST API and on mcp.opus.pro, but not in the 30 tools Scalekit's connector exposes. If thumbnails are core to your agent, plan the REST path for that step.

The auth path each one puts you on

The MCP path is OAuth only. The user completes a browser consent flow and selects one OpusClip organization, and that choice is fixed for the life of the connection. There is no list_orgs tool; switching organizations means disconnecting and reconnecting, and opusclip_whoami reports which organization a session is bound to. For B2B agents that is a clean model: one connected account maps to one customer workspace. It also means consent needs a human in a browser once. After that, tokens refresh without the user, so later runs can be headless until a refresh fails or the grant is revoked.

What the API key path changes

The REST path skips OAuth. Each customer generates an organization-level API key in the OpusClip dashboard, and every request carries it as a bearer token. Headless pipelines work from the first run, with no consent redirect. The cost is custody. The key is long-lived and organization-wide, carries no user identity, and OpusClip documents no per-endpoint scopes for it. The same secret signs webhooks, so rotating it means updating verification too. OpusClip's own guidance warns against pasting API keys into agent chat, because transcripts, logs, and model context retain them. For a deeper look at why static credentials break in production AI systems, the tradeoffs go beyond just OpusClip.

The per-user credential point both paths share

Both paths require per-user credential isolation in a multi-tenant B2B agent. MCP's OAuth flow gives you a token per connected user, bound to one organization. The API gives you a key per customer organization. In neither case does the path itself solve storage, rotation, or revocation; those are infrastructure problems regardless of which path you choose. OpusClip's per-workspace caps turn isolation into a billing problem too. A misrouted credential spends another customer's 900 monthly credits.

What you own in production

The MCP path hands you tool schemas that OpusClip maintains and a server that normalizes the API behind them. It does not hand you token storage, refresh-failure handling, revocation, or tenant isolation. The REST path hands you only the endpoints: request construction, retries, polling, webhook verification, and credential lifecycle are yours. On both paths, three OpusClip behaviors dominate day-to-day operations.

Long-running jobs and polling

opusclip_submit_project returns a project ID immediately, and clips arrive minutes later. opusclip_list_clips carries the project's stage, and an empty list during processing is not an empty result. Re-renders after opusclip_edit_clip or opusclip_create_censor_job return render_pending: true, and reading preview_url before it clears fetches the old render. opusclip_analyze_video starts a task that must be polled with its task_id. Letting the model drive those polls burns tokens every turn. Pass webhookUrl on submission, or poll in code and hand the agent finished state.

Caps, errors, and retries

When a workspace exhausts its 900 monthly credits, OpusClip returns 403 with API_MONTHLY_CAP_REACHED and a reset_at timestamp. It is deliberately not a 429, so agent frameworks don't retry in a loop. A 429 with X-Cap-Reason: concurrent means four projects are already in flight, and backoff is the right response. The REST social posting endpoints have their own limits, one request per second for posting and scheduling, and each post to X costs one credit. Call opusclip_get_usage before a batch instead of discovering the cap halfway through it.

Schema drift on each path

MCP tool schemas change when OpusClip ships. The editing-script tools were retired in favor of opusclip_edit_clip, and OpusClip kept the old names as no-op signposts so agents holding stale tool lists get redirected rather than erroring. That is considerate, but a cached list can still point the model at dead tools. Pin an explicit tool allowlist on the MCP path and leave opusclip_get_editing_script and opusclip_apply_editing_script out of it. The REST reference publishes OpenAPI definitions per endpoint, which gives you a contract to diff against.

When OpusClip MCP is the right call

  • Your agent edits clips conversationally. "Cut the filler words from clip 3 and make the captions yellow" maps to one opusclip_edit_clip call, previewed first with dryRun.
  • The agent inspects footage before acting, using opusclip_analyze_video to check framing or the active speaker.
  • You are building an interactive assistant in Claude Code, Cursor, or your own chat product, where the user is present for OAuth and reviews clips in context.
  • You want publishing to stop at a human: through listed connectors, posting and scheduling return an approval link instead of committing.

When the OpusClip API is the right call

  • You run a deterministic ingestion pipeline: every new episode is submitted, tracked, and filed into collections with no model in the loop.
  • Downstream jobs trigger on signed completion webhooks that you verify yourself.
  • The workflow deletes collections or removes clips from them, which the MCP surface can't do.
  • Your customers are Business-plan media teams running up to 50 concurrent projects, and you want request-level control over retries and queueing.
  • Thumbnail generation is part of the pipeline.

The credential problem that exists on both paths

Every customer in your product has their own OpusClip organization, credits, and caps. Fifty customers means fifty OAuth grants or fifty API keys, each with its own lifecycle.

What neither path gives you

The MCP path gives you a token per connected user, bound to one organization at consent. The API path gives you a long-lived key per customer. Either way, the credential must be encrypted at rest, isolated per tenant, never logged, and revocable when a customer disconnects. Refresh can fail and grants can be revoked from OpusClip's side, so your agent has to detect that and route the user back through consent instead of failing silently. A key pasted into a support ticket or a prompt keeps spending someone's credits until it is rotated. Understanding credential ownership across agent tool-calling patterns is essential before choosing either path.

Where Scalekit fits

Scalekit's OpusClip MCP connector handles the OAuth 2.1 flow with Dynamic Client Registration (DCR), token storage, and refresh, and a custom bearer connector keeps API keys in the same vault for the REST path. The MCP vs API decision doesn't change your auth infrastructure. Each call resolves the connected account of the user who triggered it, so what the user can't do, the agent can't do. Credentials never touch the agent runtime.

Recommended reading: Migrating from API keys to OAuth for MCP servers

How to connect OpusClip to your agent with Scalekit

The examples below use Python throughout: the Claude SDK for direct tool calling and LangChain for the Virtual MCP path. Python is the right default here because Virtual MCP session tokens are minted from the Python SDK; the Node.js SDK does not create them yet.

Prerequisites

You need a Scalekit environment, an OpusClip MCP connection under AgentKit > Connections, and an end user whose OpusClip organization has API access. Credentials come from Developers > API Credentials in the Scalekit dashboard; the AgentKit quickstart walks through setup.

The connection_name in every call must match the connection name configured in the Scalekit dashboard exactly. The OpusClip MCP connector docs use opusclipmcp; if you named the connection differently, use your name.

pip install scalekit-sdk-python python-dotenv anthropic
# .env SCALEKIT_ENVIRONMENT_URL=<your-environment-url> SCALEKIT_CLIENT_ID=<your-client-id> SCALEKIT_CLIENT_SECRET=<your-client-secret> ANTHROPIC_API_KEY=<your-anthropic-api-key>

Step 1: Authorize the user's OpusClip account

Create or fetch the connected account for the user. If it isn't active, send them through the authorization link. OpusClip's consent screen is where they choose the organization the agent will act in, so confirm it with opusclip_whoami afterward.

import os from dotenv import load_dotenv from scalekit import ScalekitClient load_dotenv() scalekit_client = ScalekitClient( env_url=os.getenv("SCALEKIT_ENVIRONMENT_URL"), client_id=os.getenv("SCALEKIT_CLIENT_ID"), client_secret=os.getenv("SCALEKIT_CLIENT_SECRET"), ) actions = scalekit_client.actions # Must match the connection name in AgentKit > Connections exactly CONNECTION_NAME = "opusclipmcp" IDENTIFIER = "user_123" # your app's unique ID for this user response = actions.get_or_create_connected_account( connection_name=CONNECTION_NAME, identifier=IDENTIFIER ) if response.connected_account.status != "ACTIVE": link = actions.get_authorization_link( connection_name=CONNECTION_NAME, identifier=IDENTIFIER ) print("Authorize OpusClip:", link.link) input("Press Enter after authorizing...") response = actions.get_or_create_connected_account( connection_name=CONNECTION_NAME, identifier=IDENTIFIER ) if response.connected_account.status != "ACTIVE": raise RuntimeError("OpusClip connection is not ACTIVE. Authorize it and run again.") # Confirm which OpusClip organization this connection is bound to whoami = actions.execute_tool( tool_name="opusclipmcp_opusclip_whoami", connection_name=CONNECTION_NAME, identifier=IDENTIFIER, tool_input={}, ) print(whoami.data)

Step 2: Retrieve the tools this user can call

This is not a flat catalog load. list_scoped_tools returns the tools the current user's connected account is authorized to call, and the tool_names filter narrows that to what this agent role needs. A clipping agent doesn't need all 30 OpusClip definitions in context. At the roughly 200 tokens per tool Scalekit uses as a planning estimate, the full catalog costs about 6,000 tokens per turn before the agent does any work. OpusClip's descriptions are unusually detailed, so treat that as a floor.

from google.protobuf.json_format import MessageToDict CLIPPING_TOOLS = [ "opusclipmcp_opusclip_get_usage", "opusclipmcp_opusclip_list_brand_templates", "opusclipmcp_opusclip_submit_project", "opusclipmcp_opusclip_list_clips", "opusclipmcp_opusclip_describe_clip", "opusclipmcp_opusclip_edit_clip", "opusclipmcp_opusclip_export_clip", ] scoped_response, _ = actions.tools.list_scoped_tools( identifier=IDENTIFIER, filter={"connection_names": [CONNECTION_NAME], "tool_names": CLIPPING_TOOLS}, page_size=100, ) llm_tools = [] for scoped_tool in scoped_response.tools: definition = MessageToDict(scoped_tool.tool).get("definition", {}) llm_tools.append({ "name": definition.get("name"), "description": definition.get("description", ""), "input_schema": definition.get("input_schema", {}), }) print([tool["name"] for tool in llm_tools])

Step 3: Run the agent loop with the Claude SDK

Execution goes through execute_tool. Scalekit resolves the connected account for IDENTIFIER and makes the call, so the agent never holds an OpusClip token. Upstream OpusClip errors raise ScalekitToolException. The loop passes the error code and message back to the model, so a cap-reached error ends the task instead of triggering retries.

import json import anthropic from scalekit.common.exceptions import ScalekitToolException client = anthropic.Anthropic() SYSTEM_PROMPT = ( "You turn long-form videos into short clips with OpusClip. " "Call opusclipmcp_opusclip_get_usage before submitting a project. " "After submitting, report the project ID and stop; do not poll for clips. " "If a tool reports API_MONTHLY_CAP_REACHED, stop and report the reset time." ) messages = [{ "role": "user", "content": "Clip this webinar into portrait shorts under 60 seconds: <public-video-url>", }] while True: response = client.messages.create( model="claude-sonnet-5", max_tokens=2048, system=SYSTEM_PROMPT, tools=llm_tools, messages=messages, ) messages.append({"role": "assistant", "content": response.content}) if response.stop_reason != "tool_use": print("".join(block.text for block in response.content if block.type == "text")) break tool_results = [] for block in response.content: if block.type != "tool_use": continue try: result = actions.execute_tool( tool_name=block.name, connection_name=CONNECTION_NAME, identifier=IDENTIFIER, tool_input=block.input, ) content, is_error = json.dumps(result.data, default=str), False except ScalekitToolException as e: # Upstream OpusClip error: hand the code and message back to the model content = json.dumps({ "error_code": e.tool_error_code, "error_message": e.tool_error_message, "execution_id": e.execution_id, }) is_error = True tool_results.append({ "type": "tool_result", "tool_use_id": block.id, "content": content, "is_error": is_error, }) messages.append({"role": "user", "content": tool_results})

For the same pattern with other connectors, see the Anthropic example.

Scoping OpusClip agents with a Virtual MCP server

The direct path keeps tool scoping in your code. A Virtual MCP server moves it into configuration: one scoped endpoint per agent role, one short-lived session token per user per run, and no MCP server to deploy, host, or maintain.

Create the server once per agent role

Define which connections and tools the role can see. The publisher role below can read clips and prepare scheduled posts, but it cannot submit projects, edit clips, or run censor jobs.

from scalekit.actions.models.mcp_config import McpConfigConnectionToolMapping vmcp_response = actions.mcp.create_config( name="opusclip-publisher", connection_tool_mappings=[ McpConfigConnectionToolMapping( connection_name=CONNECTION_NAME, tools=[ "opusclipmcp_opusclip_list_projects", "opusclipmcp_opusclip_list_clips", "opusclipmcp_opusclip_list_social_accounts", "opusclipmcp_opusclip_create_social_copy_job", "opusclipmcp_opusclip_get_social_copy_job", "opusclipmcp_opusclip_schedule_publish", "opusclipmcp_opusclip_list_scheduled_posts", ], ), ], ) config_id = vmcp_response.config.id mcp_server_url = vmcp_response.config.mcp_server_url

Save config_id and mcp_server_url and reuse them for every user. Adding Slack for approval notifications or Google Drive for source videos is another McpConfigConnectionToolMapping in the same list. The full lifecycle is in Set up and connect a Virtual MCP server.

Mint a session token and connect LangChain

Before each run, confirm the user's connections are active, then mint a token scoped to that user. Tokens default to about one hour, and there is no refresh endpoint; long-running hosts call create_session_token again before expiry. The example uses ChatOpenAI as in Scalekit's LangChain guide; any chat model with tool binding works.

pip install "langchain-mcp-adapters>=0.3,<1" langchain-openai
import asyncio from datetime import timedelta from langchain_core.messages import HumanMessage, ToolMessage from langchain_mcp_adapters.client import MultiServerMCPClient from langchain_openai import ChatOpenAI accounts = actions.mcp.list_mcp_connected_accounts( config_id=config_id, identifier=IDENTIFIER, include_auth_link=True ) for account in accounts.connected_accounts: if str(account.connected_account_status).upper() != "ACTIVE": raise RuntimeError( f"{account.connection_name} needs auth: {account.authentication_link}" ) mcp_token = actions.mcp.create_session_token( mcp_config_id=config_id, identifier=IDENTIFIER, expiry=timedelta(minutes=30), ).token async def run(): client = MultiServerMCPClient({ "scalekit": { "transport": "streamable_http", "url": mcp_server_url, "headers": {"Authorization": f"Bearer {mcp_token}"}, } }) tools = await client.get_tools() tool_map = {tool.name: tool for tool in tools} llm = ChatOpenAI(model="gpt-4o").bind_tools(tools) messages = [HumanMessage( "Schedule the top clip from my latest project to my LinkedIn account " "for tomorrow at 09:00 UTC, with generated copy." )] while True: response = await llm.ainvoke(messages) messages.append(response) if not response.tool_calls: print(response.content) break for tool_call in response.tool_calls: result = await tool_map[tool_call["name"]].ainvoke(tool_call["args"]) messages.append(ToolMessage(content=str(result), tool_call_id=tool_call["id"])) asyncio.run(run())

Why this matters for multi-tool and multi-tenant agents

A content operations product might run three roles against the same customers: a clipper that submits and edits, a publisher that schedules, and a reporter that only reads. Each role gets one Virtual MCP server, and each run gets a token for one user. The publisher above cannot call opusclip_submit_project, so an instruction smuggled into a video title can't spend credits through it. The endpoint is static; the identity is per-user. Pair the publisher with a Slack connection and it can drop each approval_url into the customer's channel for sign-off.

Recommended reading: Access Control for Multi-Tenant AI Agents and How Tool Calling Auth Changes When You Move from Single-Tenant to Multi-Tenant

Taking the REST API path through Scalekit

Scalekit's catalog ships OpusClip as an MCP connector. For the REST API, add your own connector. Scalekit stores each customer's OpusClip API key in the vault and proxies requests, so the key never enters your database, your agent runtime, or the model's context.

Define a custom bearer connector

OpusClip expects the key as a bearer token, so the BEARER auth pattern fits. REST connectors are created through the management API, as described in Create your own connector.

{ "display_name": "OpusClip API", "description": "OpusClip REST API with a per-customer organization API key", "auth_patterns": [ { "type": "BEARER", "display_name": "OpusClip API key", "description": "Organization API key from the OpusClip dashboard", "fields": [ { "field_name": "token", "label": "OpusClip API key", "input_type": "password", "hint": "Create it under API Access in the OpusClip dashboard", "required": true } ] } ], "proxy_url": "https://api.opus.pro", "proxy_enabled": true }

Call the API through Tool Proxy

Create a connection for the connector, and have each customer supply their key through the same authorization-link flow as Step 1. actions.request() then proxies calls with that customer's credential, and path is relative to proxy_url. See Making tool calls for the full model.

API_CONNECTION = "opusclip-api" # must match the connection name in the dashboard # Submit a project with the customer's own API key, resolved by Scalekit submit = actions.request( connection_name=API_CONNECTION, identifier=IDENTIFIER, path="/api/clip-projects", method="POST", body={ "videoUrl": "<public-video-url>", "curationPref": {"clipDurations": [[0, 60]], "genre": "Auto"}, "renderPref": {"layoutAspectRatio": "portrait"}, }, ) submit.raise_for_status() print(submit.json()) # includes the new project's ID # After the project concludes, fetch its clips clips = actions.request( connection_name=API_CONNECTION, identifier=IDENTIFIER, path="/api/exportable-clips", method="GET", query_params={"q": "findByProjectId", "projectId": "<project-id>"}, headers={"x-opus-org-id": "<org-id>"}, # only needed for multi-org users ) clips.raise_for_status() for clip in clips.json(): # Social posting endpoints expect curationId, not the composite id print(clip["curationId"], clip["title"], clip["durationMs"])

Gotchas the REST path adds

Clip records carry a composite id of {projectId}.{clipId}, while the social posting endpoints expect the bare clip ID, which is the curationId field. Posting and scheduling endpoints allow one request per second, so batch schedulers need a queue. Webhook verification needs the customer's API secret, which on this path lives in Scalekit's vault rather than your app. Decide where verification happens before enabling conclusionActions, or track completion by polling instead.

Observability for OpusClip tool calls

Agent failures are quiet. A lapsed consent, a workspace at its cap, and a post waiting on approval all look the same from the outside: the agent did nothing.

What Scalekit records

Every execute_tool call returns an execution_id, and upstream failures raise a ScalekitToolException carrying tool_error_code, tool_error_message, and execution_id. In the dashboard, AgentKit > Connected Accounts shows each account's status, refresh history, and tool execution logs. You can answer which user's OpusClip call failed, when, and with what upstream error. That is the downstream audit trail a raw token or API key in your own database doesn't give you. For a broader look at agent tool observability and whether your agent is actually working, the same patterns apply across any connector. For authentication events across your environment, see Audit Trails for Agent Auth in B2B SaaS.

Which one to build against

If your agent edits clips conversationally, analyzes footage before cutting, or lives inside an interactive host, build against OpusClip MCP. The editing and analysis tools are MCP-only, OpusClip maintains the schemas, and approval-gated publishing is the right default for anything touching a customer's social accounts. If your agent is a deterministic ingestion pipeline that needs signed webhooks, collection cleanup, thumbnails, or request-level retry control, use the REST API. Many OpusClip products will run both: a REST pipeline that ingests every new episode and an MCP-backed assistant that edits and schedules on request. Either way the credential problem is identical, and that is what needs production-grade infrastructure.

Build your OpusClip agent with Scalekit

Start with the OpusClip MCP connector docs for the full tool list and quickstart. Then browse the Scalekit connector library for the sources and destinations your agent pairs with OpusClip, including YouTube, Vimeo, Google Drive, and Slack. Comparing video tools? Read how LangChain tool calling works and where it stops for the framework-level view. For the per-user auth pattern in working code, fork an agent template such as the Slack triage agent. Plans are on the pricing page.

Building an OpusClip agent and want help with the auth design? Talk to us for immediate help.

No items found.
Agent
Auth Quickstart
On this page
Share this article
Agent
Auth Quickstart

Acquire enterprise customers with
‍zero upfront cost.

Every feature unlocked. No hidden fees.