MCP App Store
by KeenEthics
Kapwing icon

Kapwing

By Kapwing, Inc.

Get New App Alerts

Overview

Translate, subtitle, and dub videos and audio from one language to another using Kapwing. Upload a video from your computer, transcribe it, add subtitles in the original or a translated language, and dub the speech into other languages in voices cloned from the original speakers. You can also browse your workspace's recent projects and media library, check your remaining credits, and export projects to MP4 with a download link when they're done. Every result links to the project in the Kapwing editor for further changes. Limitations: the plugin only works with workspaces your connected Kapwing account belongs to, and it cannot delete projects or media. Transcription, translation, and dubbing use your workspace's existing Kapwing credits. Timeline edits such as trims and transitions happen in the Kapwing editor. Dubbing replaces the audio only and is not lip-synced.

Tools

add_captions

ChatGPT
Turn subtitles on for one asset in a Kapwing project. Returns the project's editorUrl to view, edit, restyle, or export them. Needs a finished start_transcription for the asset, then this call with the transcript's languageCode (the code given to start_transcription, or the detected code poll_transcription reports when it was auto-detected). For translated captions, either run start_transcription with translateLanguageCode and pass the same value here, or pass a finished translation job's translationId (translation tools, when enabled for you). A translateLanguageCode the asset has no transcript for is refused, since the project would show source-language words labelled as translated. This does not transcribe or translate anything, and it does not produce a downloadable file; the subtitles live on the project. Pass styling to change placement, size, colour, or word-by-word animation, or omit it to keep the current look.

add_glossary_term

ChatGPT
Add one glossary override (sourceText → targetText) for a team/language-pair. Applied to future start_translation runs — the translator emits targetText verbatim wherever it sees sourceText. Use for brand names, proper nouns, or terms the model consistently mis-translates. No dedup — adding the same source text twice creates two rows.

begin_upload

ChatGPT
Start a chunked-upload session that the sidecar drives against GCS on your behalf. Upload paths at a glance (pick the smallest fit): Hosted URL, ≤500MB → upload_asset_from_url (1 MCP call; server-side fetch) Local, up to ~1GB → create_upload_url → single HTTP PUT → register_asset_from_blob Local, up to 5GB, no HTTP → begin_upload → N × upload_chunk (isFinal) → register_asset_from_blob Local, caller can't read it → upload_asset_from_computer (browser panel; user picks file; up to 5GB) Egress: only create_upload_url (PUT to storage.googleapis.com) makes an outbound request of your own, so it is the only path ruled out when a request is refused for egress or sandbox reasons. Use upload_asset_from_url when the file is hosted. begin_upload needs no outbound request either, but its chunks are base64 you would have to emit yourself and that corrupts far below one chunk (sc-59914) — a scripted-caller path and not your fallback. If you write the tool arguments yourself and the network is refused, go to upload_asset_from_computer; the user can instead allow the domain in their sandbox's network egress settings and retry, if they would rather not re-pick the file. Follow up with N × upload_chunk and set isFinal:true on the last one — the sidecar finalizes and you then call register_asset_from_blob with the returned assetId. Use this when your MCP client can only make tool calls (no out-of-band HTTP). Prefer create_upload_url whenever you can make one outbound HTTP PUT — two MCP calls plus the PUT, instead of N chunk calls. Small totals do work here: GCS's 256KB rule binds only NON-final chunks, so a lone isFinal:true chunk may be any size. Chunks cross as base64 through MCP; suggested chunk size is 8MB. Returns {uploadId, assetId, blobName, chunkSizeBytes, expiresAtMs} — or, if a prior assetId you passed turns out to already have its bytes landed, {assetId, blobName, alreadyUploaded:true} with no uploadId (skip straight to registering). Session token embeds all resumption state and is portable across sidecar pods; expires after 1h. Refused before the session opens when the team's storage is full.

create_project_from_asset

ChatGPT
Create a new Kapwing project from a single asset — the returned editorUrl opens it in the browser for editing. This is the tool to reach for when the user says "make a project from this image/video/audio" after an upload. Returns {projectId, editorUrl, thumbnailSeeded, hint}. It creates the project only and renders nothing, so download_export({projectId}) and poll_export({projectId}) report no export until one runs. To get a finished video file, export the returned projectId with the project export tool, which renders the project as it stands including later edits, then poll poll_export. The asset export tool is a different starting point: it builds and renders its own project straight from an assetId. Export tools are not enabled on every account; if neither is in your tool list, export from editorUrl in Studio. Exporting is always explicit, never a side effect of creating a project. Hand the user the editorUrl this returns, verbatim. Never build a project link yourself: the path is /{projectId}/studio/editor, not /projects/{projectId} and not /videos/{projectId}. /videos/{projectId} is the finished-video page, which shows the project's last export rather than the project, so subtitles and dubs are missing from it.

create_pronunciation

ChatGPT
Add one pronunciation override for a team. Once saved, start_dubbing_tts runs for this team will apply the override automatically to any occurrence of sourceTerm. Use for brand names that TTS mispronounces, foreign-language proper nouns, or acronyms.

create_upload_url

ChatGPT
Mint a GCS resumable-upload URL for a local file the caller will PUT themselves, then register with register_asset_from_blob. Your PUT goes to storage.googleapis.com. If your environment uses an egress allowlist, an agent proxy or a sandbox and you cannot confirm that host is allowed, do not spend a call here — go to upload_asset_from_computer, where the user's browser does the transfer. Being able to read the file is not evidence of egress. Upload paths at a glance (pick the smallest fit): Hosted URL, ≤500MB → upload_asset_from_url (1 MCP call; server-side fetch) Local, up to ~1GB → create_upload_url → single HTTP PUT → register_asset_from_blob Local, up to 5GB, no HTTP → begin_upload → N × upload_chunk (isFinal) → register_asset_from_blob Local, caller can't read it → upload_asset_from_computer (browser panel; user picks file; up to 5GB) Egress: only create_upload_url (PUT to storage.googleapis.com) makes an outbound request of your own, so it is the only path ruled out when a request is refused for egress or sandbox reasons. Use upload_asset_from_url when the file is hosted. begin_upload needs no outbound request either, but its chunks are base64 you would have to emit yourself and that corrupts far below one chunk (sc-59914) — a scripted-caller path and not your fallback. If you write the tool arguments yourself and the network is refused, go to upload_asset_from_computer; the user can instead allow the domain in their sandbox's network egress settings and retry, if they would rather not re-pick the file. Sweet spot: local files up to ~1GB, when your client can do one HTTP PUT to an opaque URL. Beyond ~1GB or on flaky networks, prefer begin_upload (chunked, no HTTP needed) from a script, or upload_asset_from_computer. Returns {uploadUrl, assetId, blobName, readUri, contentType} — or, if a prior assetId you passed turns out to already have its bytes landed, {assetId, blobName, alreadyUploaded:true, readUri} with no uploadUrl (skip straight to registering). Two-step flow: (1) PUT the entire body to uploadUrl in one request; (2) call register_asset_from_blob({assetId}) (or {blobName, fileName, contentType, teamId} for an older client) to attach it as a team asset. URL is single-use and expires — pass this call's assetId back in on a retry instead of starting a new pending upload. Refused before minting when the team's storage is full.

download_export

ChatGPT
Return the download URL for a Kapwing project's latest completed export. Read-only, URL-first framing. Pass projectId for a specific project, or omit it (passing teamId instead) to get the caller's most recent export across projects the caller created (creator-scoped, not team-wide — will not surface a coworker's export). If the project has no export yet, nothing has exported it — creating a project does not. To export a project, use the project export tool, which renders an existing projectId as it stands; if no export tool is in your list, export from Studio via its editorUrl. get_export_history lists historical exports. For a status-first framing that also detects in-flight exports, use poll_export. Hand the user the editorUrl this returns, verbatim. Never build a project link yourself: the path is /{projectId}/studio/editor, not /projects/{projectId} and not /videos/{projectId}. /videos/{projectId} is the finished-video page, which shows the project's last export rather than the project, so subtitles and dubs are missing from it.

export_project

ChatGPT
EXPORT an existing Kapwing project to MP4 and return a job to poll. Use this when the user wants a finished video file from a project that already exists — "export this project", "render it", "give me the MP4". Takes the projectId of a project you already have; it exports that project as it currently stands — including edits made in the browser and any captions added with the subtitle tools, with their styling and animation — and does NOT create a new one. Returns {projectId, editorUrl, jobId, hint}; the export runs asynchronously, so poll poll_export({projectId}) until ready === true and then take the url. This spends real time and the account's export quota, so never call it speculatively, and if a project is already exporting, poll it rather than exporting it again — a second call queues a second export and bills for both. Settings are fixed: MP4, source resolution, fast encode — there is nothing to configure. A project longer than the plan's per-export cap (4 minutes on Free, 2 hours on paid plans) is refused before anything is queued, with the cap and remedy in the error; so is a Free-plan export that would take the workspace past its 30 exported minutes for the month. A project with AI persona (lip-synced avatar) layers is refused, because its lip sync spends credits the user confirms in the editor: send them to editorUrl for that one. It is also not enabled on every account: if it is absent from your tool list, open editorUrl and export from the browser rather than substituting another tool.

export_project_from_asset

ChatGPT
Create a Kapwing project from a single asset and EXPORT it to MP4 with default export settings. Use this when the user wants a finished video file — "export this", "render it", "give me the MP4". Returns {projectId, editorUrl, jobId, hint}; the export runs asynchronously, so poll poll_export({projectId}) until ready === true and then take the url. Unlike create_project_from_asset, which only creates a project and exports nothing, this runs a real export, so it takes real time and consumes the account's export quota. Never call it speculatively. It is also not enabled on every account: if it is absent from your tool list, say so rather than substituting create_project_from_asset, which creates a project without exporting anything and is not the same result. Settings are fixed: MP4, source resolution, fast encode — there is nothing to configure. A source longer than the plan's per-export cap (4 minutes on Free, 2 hours on paid plans) is refused before anything is queued, with the cap and remedy in the error; so is a Free-plan export that would take the workspace past its 30 exported minutes for the month.

get_account

ChatGPT
Get the calling user's Kapwing account overview: their workspaces/teams with plan tier, member count, and the same-email-domain subset of members (so the caller can see their coworkers), plus the caller's own email. Each workspace also reports its plan state — credits and other allowances as used/limit/REMAINING, which allowances are depleted, storage used against the plan's cap, and the plan's export duration, watermark, and per-file upload limits. Call this to answer "how many credits do I have left?" and to check headroom BEFORE starting a transcription, dubbing, translation, or export, since those actions are not blocked with a legible reason when an allowance runs out. Read notes before reporting any of these numbers — it says which are enforced and which are plan policy only. Read-only. Members outside the caller's email domain are excluded — a personal-email member of a company workspace is not surfaced.

get_asset_transcription

ChatGPT
Fetch a completed transcription's words. Returns the full word list with per-word timestamps and speaker labels when available. Use the assetTranscriptionId returned by poll_transcription once done:true — not the transcription-job id. Word objects use snake_case start_time / end_time, matching the backend transcription shape so downstream per-word/per-chunk tooling can consume them without remapping. English transcripts include filler words (um, uh) unless start_transcription was called with disfluencies:false; the Studio strips them from subtitles at render time.

get_dubbing_voice_clone_by_voice_id

ChatGPT
Look up a dubbing voice-clone record by its provider voiceId. Reverse of list_dubbing_voice_clones (which is by source asset). Returns found:false when the voiceId isn't tracked as a Kapwing dubbing clone — a stock provider voice would return found:false.

get_export_history

ChatGPT
Per-project (not team-scoped): page through the export history for one Kapwing project — each export's status, format, resolution, timestamps and output URL. Read-only, cursor-paginated. Required arg is projectId, not teamId. Hand the user the editorUrl this returns, verbatim. Never build a project link yourself: the path is /{projectId}/studio/editor, not /projects/{projectId} and not /videos/{projectId}. /videos/{projectId} is the finished-video page, which shows the project's last export rather than the project, so subtitles and dubs are missing from it.

get_export_preview

ChatGPT
Get preview details for a single Kapwing export — status, format, resolution, output URL, plus the owning team and whether the caller is a member. Read-only. Pass an exportId from get_export_history.

get_glossary_terms_for_pair

ChatGPT
Fetch the glossary terms that apply to one specific (source → target) language pair for a team, as a flat map of source text → target text. This is the exact map start_translation merges into the translation request. Empty map means no overrides for this pair.

get_project_subtitles

ChatGPT
Read a project's existing subtitles. Returns every asset on its timeline, which transcript each one's subtitles are reading from (attachedTranscript), every other transcript the asset already has, and the timed lines themselves. Read-only. This is how you translate or re-caption a project someone else built — the lines come back in exactly the shape the translation tools take, linesLanguageCode is the language they are written in, and the assetTranscriptionId it reports is the one get_asset_transcription wants, so neither needs a transcription job of your own. linesSource says what the lines are: frames is what the viewer sees now, uploaded is an SRT or an earlier translation, transcript means Studio has never cached lines for this project and these were built from the transcript the same way Studio would. An asset with attachedTranscript: null has no subtitles turned on yet; attachedTranscript.status: "missing" alongside a translationId means the lines came from a translation job rather than a transcript. Hand the user the editorUrl this returns, verbatim. Never build a project link yourself: the path is /{projectId}/studio/editor, not /projects/{projectId} and not /videos/{projectId}. /videos/{projectId} is the finished-video page, which shows the project's last export rather than the project, so subtitles and dubs are missing from it.

get_team_storage_usage

ChatGPT
Report a Kapwing team's media-library storage: bytes used against the plan's storage cap and the room left, plus the media library's all-origins total. Read-only. Pass a teamId from the get_account tool. Two different byte counts come back — compare usedAgainstLimitBytes with storageLimitBytes, and never subtract totalSizeBytes (a wider set) from the cap. See note.

get_translation

ChatGPT
Fetch a completed translation. Returns the translated subtitle lines aligned to the source timings, plus provider metadata. Provide translationId (preferred) OR transcriptionId (looks up the translation attached to that transcription). Passing neither is rejected client-side.

list_brandkit_voice_clones

ChatGPT
List the team's saved brand-kit voice clones — team-level presets a caller can plug into start_dubbing_tts chunks via elevenLabsId. Distinct from list_dubbing_voice_clones, which is scoped to one source asset's speaker-derived clones. Response includes a signed sampleURL for playing back the training sample (may be omitted if no sample was retained).

list_dubbing_tts_cache

ChatGPT
Fetch the cached TTS records for a set of ids. Each record carries a signed outputAssetUrl to play or download the generated audio (it expires; call again for a fresh one), the audio's outputAssetId, and outputTranscriptionId (transcription of the generated audio, useful for timing checks). The URL here is the way to reach the audio: outputAssetId is minted outside team storage, so poll_asset and the project tools refuse it. Batched: pass all ids from poll_dubbing_tts's completed response in one call. Order of returned items is not guaranteed to match input order — join on ttsCacheId. Pass the teamId the dub ran under.

list_dubbing_voice_clones

ChatGPT
List the trained voice clones attached to one source asset. Each entry pairs a speaker label with the provider voiceId you feed into start_dubbing_tts chunks. If the same speaker has multiple entries, prefer the most recent (higher lastUsed).

list_folders

ChatGPT
List the folders in a Kapwing workspace/team (folder id, name, type, parent). Read-only. Pass a teamId from get_account. Use parentFolderId (or "root") to walk one level at a time; use limit to cap the response. Unfiltered team trees can be hundreds of folders.

list_glossary_terms

ChatGPT
List all glossary terms saved for one team. Glossary terms are source→target text overrides the translation pipeline uses to force specific translations (e.g. brand names, proper nouns). Returned as an array; group by (sourceLanguageCode, targetLanguageCode) client-side to filter for a specific language pair. For a single language pair, use get_glossary_terms_for_pair instead.

list_languages

ChatGPT
List the language codes Kapwing accepts, per purpose: spoken (the language of audio or source text), translation (translation targets) and dubbing (dubbing targets). Each entry is a code and an English name. Codes differ by purpose: German is de-DE when spoken but de as a translation target. Language fields also accept a name or a differently cased code and resolve it, so this list is for choosing a language, not for formatting one.

list_media_library

ChatGPT
Page through a Kapwing workspace's media library — the team's uploads and AI generations (filename, type, dimensions, duration, size, AI-generated flag). Read-only, cursor-paginated: pass the returned nextCursor to walk all items. Pass a teamId from get_account; every assetId returned is one the team-scoped tools accept for that same team, so carry the teamId straight through to your next call (unless the envelope carries assetIdsUnverified, which means that check could not run — see its note). search is a tokenized full-text match, not a substring filter — see its arg description before relying on it to narrow results. Brand kit assets are not listed here, so an id for one has to come from elsewhere. Rate limited to 60 calls/minute per authenticated caller (a transport with no caller identity, the same one that sets assetIdsUnverified, has nothing to bucket against and is not limited): pass pageSize: 100 when walking a whole library, since the server default of 25 makes a large library run out of budget before it runs out of pages.

list_pronunciations

ChatGPT
List all pronunciation overrides for a team. Each entry pairs a sourceTerm (the raw word) with a pronunciation (either an IPA string or a plain phonetic respelling — the format field says which). The TTS pipeline for dubbing consults this list per team before speaking, so a brand name written unusually can be forced to sound right without editing the source transcript.

list_recent_projects

ChatGPT
List the calling user's most recent Kapwing projects (name, duration, last editor, last-saved, thumbnail, share code) and their most recent Kai chats. Read-only. Default scope is personal (only projects the caller created). Pass scope: "team" to fan out to the whole team surface — that view includes coworkers' projects and thumbnails, so use it only when the caller explicitly wants the team view. Pass a teamId from the get_account tool. When you show these to a user, include each project's lastSaved date: "recent" here means recent for the workspace, not recent in time, and the list routinely reaches back months. Without dates, an old project by a since-departed teammate reads as current activity — a real report from release review, 2026-08-06. Hand the user the editorUrl this returns, verbatim. Never build a project link yourself: the path is /{projectId}/studio/editor, not /projects/{projectId} and not /videos/{projectId}. /videos/{projectId} is the finished-video page, which shows the project's last export rather than the project, so subtitles and dubs are missing from it.

list_workspace_exports

ChatGPT
List the most recent completed exports across a whole Kapwing workspace, newest first, each with its download url. Use this for "download my last 10 exports", "the 50 most recent exports" or "every export in this workspace". Read-only, cursor-paginated. Pass teamId. Team-wide by default: it includes coworkers' exports from projects in shared folders, never from a coworker's private folder; pass scope: "personal" for only the caller's own projects. For "all", pass limit: 50 and follow nextCursor until hasNextPage is false; a page can come back shorter than limit, so never treat a short or empty page as the end, and tell the user roughly how many there are (total) before fetching every page — it is counted on the first page, so projects added, moved or deleted mid-listing can make the pages add up to a slightly different number. If projectsTruncated is true, the workspace has more projects than this listing covers, so tell the user exports of older projects are missing. This returns links, not files: hand the user the url values or download them yourself if you can. Links are time-limited: if one has expired, call again for a fresh one; if a fresh link still fails, the file is gone, so say so instead of retrying. For one project's history use get_export_history; for a single latest export use download_export. Hand the user the editorUrl this returns, verbatim. Never build a project link yourself: the path is /{projectId}/studio/editor, not /projects/{projectId} and not /videos/{projectId}. /videos/{projectId} is the finished-video page, which shows the project's last export rather than the project, so subtitles and dubs are missing from it.

ping

ChatGPT
Smoke test that the MCP server can reach the configured Kapwing backend. Calls a trivial GET endpoint and returns the parsed body.

poll_asset

ChatGPT
Poll an asset's ingest status. Its companion is URL ingest, which returns before the server-side download finishes — call this in an out-of-process loop (once every few seconds) until ready === true. Returns status mapped to a string (pending / uploading / uploaded / failed / queued) plus a ready boolean and, when available, the signed URL, content type, name, and duration. An upload that completed synchronously already returns ready:true on the first call. Once the media has been probed, usually within a minute of ready:true, dimensions appears for images and video and durationSeconds for audio and video; a PDF gets neither. This tool only reads, so it never starts that probe; the tools that need the metadata start it themselves when it is missing.

poll_brandkit_voice_cloning

ChatGPT
Poll a brand-kit voice-cloning job's status. On status:'complete' the trained ElevenLabs voiceId is populated — feed it into start_dubbing_tts chunks, or into any other tool that takes an ElevenLabs voiceId. Persisting it as a named team preset is done from the Kapwing web UI and is intentionally not on the MCP surface. On status:'error', error carries the failure details.

poll_dubbing

ChatGPT
Poll dubbing started by start_dubbing. Returns one entry per workflow with status, done, failed and the current stage (and failureReason when the backend stopped it for a known cause, such as no speech in the video), plus projectIdByLanguage / editorUrlByLanguage once known, and dubbedAudioAssets / backgroundAudioAssetId once the audio has rendered. done:true on every entry means the dubbed audio has been laid onto the project's timeline, so the editorUrl is worth opening and an export of that project will carry the dub. Dubbing runs a transcribe, translate, voice-clone, TTS and audio-render chain, so expect minutes — poll every 15-30 seconds rather than tightly.

poll_dubbing_tts

ChatGPT
Poll a dubbing TTS job. status:'complete' populates ttsCacheIds — pass those directly to list_dubbing_tts_cache({ttsCacheIds}) for the generated audio URLs. status:'error' puts details in error. status:'pending' means poll again. A complete job means the audio was generated, NOT that the project was dubbed: the dub is a standalone audio asset and nothing is written to the project. projectId / editorUrl name the source project, which still plays its original audio.

poll_dubbing_voice_cloning

ChatGPT
Poll a voice-cloning job's status. status:'complete' means the clones are trained and the speaker→voiceId map is in speakerVoiceCloneIds; persist it with save_dubbing_voice_clones({assetId, teamId, speakerVoiceCloneIds}) — list_dubbing_voice_clones({assetId}) reads only what that save writes, so it stays empty until then — or feed the voiceIds straight into start_dubbing_tts chunks. status:'error' puts the failure in error — retry with adjusted time ranges. status:'pending' means poll again.

poll_export

ChatGPT
Poll a project's export status. Companion to the dedicated export tool — call in an out-of-process loop until ready === true. Returns status: processing (export in flight), done (completed export available, url set), or none (nothing running and no completed export yet). An export sits in processing until the ks-jobs worker finishes. A project that has never been exported answers none — creating a project does not export one, and polling never starts an export. Use download_export for a strictly URL-first read. Hand the user the editorUrl this returns, verbatim. Never build a project link yourself: the path is /{projectId}/studio/editor, not /projects/{projectId} and not /videos/{projectId}. /videos/{projectId} is the finished-video page, which shows the project's last export rather than the project, so subtitles and dubs are missing from it.

poll_transcription

ChatGPT
Poll a transcription's progress. Returns done:true once the transcription is ready — at that point assetTranscriptionId is populated and you should hand it to get_asset_transcription to fetch the actual words. While pending, progress reports which stages have completed (downloaded → extracted → conformed → speech → done). If progress.error is set the job failed and further polling won't help. If stalled:true the job has gone too long without progress: stop polling and ask the user before starting a new transcription, since a new job is charged transcription credits again.

poll_translate_text

ChatGPT
Poll a translate-text job's status and, if complete, its translated chunks. Poll this after start_translate_text until status:'complete' — each entry in translations pairs the original text with translatedText. On status:'error', error carries the failure details. On status:'pending', poll again in a few seconds.

poll_translation

ChatGPT
Poll a translation job's progress. Returns done:true once the translated subtitles are ready — at that point call get_translation({translationId}) for the aligned output (or read the subtitles array directly off this response, which the backend also includes when done). progress is a fraction 0..1 while running.

register_asset_from_blob

ChatGPT
Register a GCS blob (previously uploaded via create_upload_url or a completed upload_chunk chain) as a Kapwing team asset. Final step for the self-upload paths. Upload paths at a glance (pick the smallest fit): Hosted URL, ≤500MB → upload_asset_from_url (1 MCP call; server-side fetch) Local, up to ~1GB → create_upload_url → single HTTP PUT → register_asset_from_blob Local, up to 5GB, no HTTP → begin_upload → N × upload_chunk (isFinal) → register_asset_from_blob Local, caller can't read it → upload_asset_from_computer (browser panel; user picks file; up to 5GB) Egress: only create_upload_url (PUT to storage.googleapis.com) makes an outbound request of your own, so it is the only path ruled out when a request is refused for egress or sandbox reasons. Use upload_asset_from_url when the file is hosted. begin_upload needs no outbound request either, but its chunks are base64 you would have to emit yourself and that corrupts far below one chunk (sc-59914) — a scripted-caller path and not your fallback. If you write the tool arguments yourself and the network is refused, go to upload_asset_from_computer; the user can instead allow the domain in their sandbox's network egress settings and retry, if they would rather not re-pick the file. Returns {assetId, teamAssetId, status, ...} — same shape as upload_asset_from_url. On this path the blob is already in GCS so the returned status is typically uploaded (settled) on the first call. If it isn't, poll with poll_asset({assetId}) until ready:true. Prefer {assetId} — the id a mint tool returned. {blobName} alone also still works, for compatibility with an older mint result. A rejection that cannot be retried (e.g. the file was too large for the team's plan) is a structured MCP error containing {assetId, permanentlyFailed:true, error} — do not retry with this assetId; start a new upload instead.

rename_project

ChatGPT
Rename a Kapwing project. Changes only the project's name, nothing on its timeline, and needs edit access to the project. Returns previousName, so a wrong rename can be undone by renaming back when it is non-empty. An editor tab that already has the project open picks up the new name live. Hand the user the editorUrl this returns, verbatim. Never build a project link yourself: the path is /{projectId}/studio/editor, not /projects/{projectId} and not /videos/{projectId}. /videos/{projectId} is the finished-video page, which shows the project's last export rather than the project, so subtitles and dubs are missing from it.

save_dubbing_voice_clones

ChatGPT
Persist a speaker→voiceId mapping for one source asset. Writes one dubbing_voice_clones row per entry, all with provider:'elevenlabs'. Not idempotent — repeat calls with the same map insert duplicates (the read side prefers the most recent). Empty map is a no-op. Response carries no body; success = no exception.

start_brandkit_voice_cloning

ChatGPT
Start a brand-kit voice-cloning job. Consumes one clone against the team's plan-tier max-clones quota. Rate-limited to 2 calls per minute — training runs are expensive and the quota is only enforced against clones already saved from the web UI. Returns the job — poll poll_brandkit_voice_cloning({jobId}) until status:'complete' to get the ElevenLabs voiceId. This differs from start_dubbing_voice_cloning in two ways: (a) it's team-level (a preset voice, not per-source-asset) and (b) it takes full assets (no time-range slicing per speaker). Only clone or reproduce voices the user owns or has permission to use.

start_dubbing

ChatGPT
Dub a project's audio into one or more languages, end to end, and leave the result on the project's timeline. This is the dubbing tool to reach for: it runs the whole pipeline (transcribe, translate, clone the speakers' voices, synthesize speech, split the original vocals out, lay the new audio onto the timeline) and the finished dub is playable in the editor. Returns one workflowId per source asset plus projectIdByLanguage; poll with poll_dubbing({workflowIds, projectId}) until done:true. For a single target language the dub replaces the audio on the project you passed. For several, the backend copies the project once per language and projectIdByLanguage names each copy — the project you passed is left alone. The start_dubbing_voice_cloning / start_dubbing_tts tools are the individual stages of this same pipeline, exposed for callers that need to drive one step by hand; they do NOT attach anything to a project, so use them only when this tool does not fit. Dubbing is audio-only: mouth movement is not re-timed, so the result is not lip-synced. Only clone or reproduce voices the user owns or has permission to use.

start_dubbing_tts

ChatGPT
Advanced / single-stage. Prefer start_dubbing, which runs this stage inside the full pipeline and leaves a playable dub on the project timeline. Start a text-to-speech pass over translated word chunks to generate a dub. Returns the AI job — poll poll_dubbing_tts({jobId}) until status:'complete', then fetch the generated audio via list_dubbing_tts_cache({ttsCacheIds}) using the ids on the completed job. Consumes team credits per character spoken. The dub is delivered as standalone audio assets: nothing is added to the project, and projectId / editorUrl here name the project the dub was generated FROM, not a dubbed version of it. Opening that project shows the original, unchanged. Tell the user the dub is an audio file to download, and do not describe the project as dubbed. Only clone or reproduce voices the user owns or has permission to use.

start_dubbing_voice_cloning

ChatGPT
Advanced / single-stage. Prefer start_dubbing, which runs this stage as part of the full pipeline and leaves a playable dub on the project; this tool trains voice clones only and attaches nothing. Start a voice-cloning job that trains a per-speaker voice clone from segments of a source asset. Returns the job. Reuse behavior: a COMPLETED job on the same asset whose speaker labels all still have live voice ids is returned as-is — the match is asset + speaker labels, NOT the ranges, so re-running with different ranges for the same labels reuses the old clones instead of retraining (use new speaker labels to force a retrain); an IN-PROGRESS job matches on assetId ONLY — starting a new clone with different ranges while another one is running on the same asset returns the in-flight job, not a new one. Poll with poll_dubbing_voice_cloning({jobId}) until status:'complete'; that response (and the reuse response here) carries the speaker→voiceId map in speakerVoiceCloneIds. Persist it with save_dubbing_voice_clones and feed those voiceIds into start_dubbing_tts chunks. list_dubbing_voice_clones reads what the save writes, so it stays empty until then. Only clone or reproduce voices the user owns or has permission to use.

start_transcription

ChatGPT
Start a transcription job for one asset. The spoken language may be given as languageCode or left for the pipeline to detect. Returns a transcriptionId immediately — the actual transcription runs in the background. Poll with poll_transcription({transcriptionId}) until done:true, then fetch the words with get_asset_transcription({assetTranscriptionId}). If translateLanguageCode is set, sourceTranscriptionId is also returned for the untranslated source transcription. Consumes transcription credits from the caller's team.

start_translate_text

ChatGPT
Translate plain-text chunks (no subtitle-frame identifiers) into a target language via the AI-jobs pipeline. Returns a translateTextJobId immediately — poll with poll_translate_text({translateTextJobId}) until status:'complete'. For timed subtitle lines with frame ids, use start_translation instead (that path polls via poll_translation). Consumes translation credits.

start_translation

ChatGPT
Translate a batch of timed subtitle lines. Returns a translationId immediately — the job runs in the background. Poll poll_translation({translationId}) until done:true, then fetch the aligned translation with get_translation({translationId}). The job is not attached to any project. To show its lines as captions, pass the finished translationId to the captioning tool. For source→target on plain text without timings, use start_translate_text instead. Consumes translation credits from the caller's team; the team's glossary (if any) is applied automatically.

upload_asset_from_computer

ChatGPT
Open an interactive upload panel so the user can pick media off their own computer and upload it into a Kapwing team library. Reach for a byte path first when you can read the file AND you know your outbound HTTP is unrestricted. Come here when you cannot read the bytes at all, and when the network is refused and you are the one writing the arguments. In a sandbox or behind an egress allowlist or agent proxy, come straight here instead of spending a call to find out — create_upload_url PUTs to storage.googleapis.com. Being able to read an attached file is not evidence of egress: in some clients the bytes are readable and the network is not. Upload paths at a glance (pick the smallest fit): Hosted URL, ≤500MB → upload_asset_from_url (1 MCP call; server-side fetch) Local, up to ~1GB → create_upload_url → single HTTP PUT → register_asset_from_blob Local, up to 5GB, no HTTP → begin_upload → N × upload_chunk (isFinal) → register_asset_from_blob Local, caller can't read it → upload_asset_from_computer (browser panel; user picks file; up to 5GB) Egress: only create_upload_url (PUT to storage.googleapis.com) makes an outbound request of your own, so it is the only path ruled out when a request is refused for egress or sandbox reasons. Use upload_asset_from_url when the file is hosted. begin_upload needs no outbound request either, but its chunks are base64 you would have to emit yourself and that corrupts far below one chunk (sc-59914) — a scripted-caller path and not your fallback. If you write the tool arguments yourself and the network is refused, go to upload_asset_from_computer; the user can instead allow the domain in their sandbox's network egress settings and retry, if they would rather not re-pick the file. Use this when the user points at a file only THEY can reach ("these photos", "the video on my desktop") and you cannot read its bytes — it is the ONLY upload path that works in that case. The user's browser uploads directly to storage, so file size is bounded by their connection rather than by anything on your side, and no base64 passes through the conversation. Returns team scope for the panel; the panel reports each finished upload's assetId back into the conversation, along with a link to the media library, so you can hand the user the asset or feed it to whichever project tool you have. Do not ask the user to hand over a file you have already uploaded successfully — being asked to re-pick clips they had just attached is a real report from release review, 2026-08-06, and it reads as the tool being broken. Uploads land in ONE workspace and the user should know which. Pass teamId when you already know the target; omit it and a single-workspace user resolves automatically. If they have several and you did not pass one, this call comes back with the list — ask the user which workspace, then call again with their answer. The result then carries teamName: say it back to the user, since the panel is the last chance to catch a wrong destination before their files are in it. The exception is workspaceVerified:false, which means the workspace lookup was unreachable and the id went unchecked — there is no name to give in that case, so say the destination could not be confirmed instead of naming one. A workspace whose storage is full is refused here, before the panel opens. Requires a host supporting the MCP Apps extension (Claude Desktop, claude.ai). On hosts without it, fall back to upload_asset_from_url for hosted media, or create_upload_url if you can make an outbound HTTP PUT.

upload_asset_from_url

ChatGPT
Import media into the caller's Kapwing team library from a URL. Upload paths at a glance (pick the smallest fit): Hosted URL, ≤500MB → upload_asset_from_url (1 MCP call; server-side fetch) Local, up to ~1GB → create_upload_url → single HTTP PUT → register_asset_from_blob Local, up to 5GB, no HTTP → begin_upload → N × upload_chunk (isFinal) → register_asset_from_blob Local, caller can't read it → upload_asset_from_computer (browser panel; user picks file; up to 5GB) Egress: only create_upload_url (PUT to storage.googleapis.com) makes an outbound request of your own, so it is the only path ruled out when a request is refused for egress or sandbox reasons. Use upload_asset_from_url when the file is hosted. begin_upload needs no outbound request either, but its chunks are base64 you would have to emit yourself and that corrupts far below one chunk (sc-59914) — a scripted-caller path and not your fallback. If you write the tool arguments yourself and the network is refused, go to upload_asset_from_computer; the user can instead allow the domain in their sandbox's network egress settings and retry, if they would rather not re-pick the file. Server downloads the bytes; returns an asset id immediately — the background download continues out of process, so the asset may still be pending. Poll poll_asset({assetId}) until ready:true before assuming the URL resolves. Use for direct media URLs or signed URLs from an external DAM. Social/video platform pages (YouTube, TikTok, Instagram, X, Vimeo, Facebook) and Google Drive share links are not supported and are rejected. Refused before the fetch starts when the team's storage is full or the probed size would not fit.

upload_chunk

ChatGPT
Push one chunk of a session started by begin_upload. Upload paths at a glance (pick the smallest fit): Hosted URL, ≤500MB → upload_asset_from_url (1 MCP call; server-side fetch) Local, up to ~1GB → create_upload_url → single HTTP PUT → register_asset_from_blob Local, up to 5GB, no HTTP → begin_upload → N × upload_chunk (isFinal) → register_asset_from_blob Local, caller can't read it → upload_asset_from_computer (browser panel; user picks file; up to 5GB) Egress: only create_upload_url (PUT to storage.googleapis.com) makes an outbound request of your own, so it is the only path ruled out when a request is refused for egress or sandbox reasons. Use upload_asset_from_url when the file is hosted. begin_upload needs no outbound request either, but its chunks are base64 you would have to emit yourself and that corrupts far below one chunk (sc-59914) — a scripted-caller path and not your fallback. If you write the tool arguments yourself and the network is refused, go to upload_asset_from_computer; the user can instead allow the domain in their sandbox's network egress settings and retry, if they would rather not re-pick the file. Set isFinal:true on the last chunk. The sidecar streams your chunk into GCS with proper Content-Range and lets GCS enforce contiguity — you don't touch GCS. Returns {ok, nextOffsetBytes, assetId, blobName, committedBytes, finalized}. committedBytes is what GCS reports it actually persisted, which can fall SHORT of what you sent — always start the next chunk at the returned nextOffsetBytes rather than at your own running total, and when a hint names a short commit, resend from there instead of advancing. On isFinal:true success, call register_asset_from_blob({assetId}) (or {blobName, fileName, contentType, teamId} for an older session) to attach it.

Capabilities

WritesInteractive

Example Prompts

Click any prompt to copy it.

Similar Apps