SendShort MCP
MCP server for the SendShort public API. Lets agents import a YouTube long video, turn it into shorts, and schedule shorts and slideshows to the user's TikTok and YouTube accounts.
Professional or Business plan required.
Setup
Add the hosted server's URL to your AI app. The first time it connects, a SendShort page opens: sign in, check which app is asking, and click Allow. No API key needed.
https://app.sendshort.ai/api/mcp
Open Settings → Preferences → Connect your AI agent in SendShort for one-click installs and per-app steps.
| What runs | Nothing on your machine | Nothing on your machine | npx sendshort-mcp (Node.js 18+) |
| Credentials | SendShort sign-in (OAuth) | ss_sk_… key in a header | ss_sk_… key in an env var |
| Clients | Claude (web, Desktop, Code), ChatGPT, Cursor, VS Code, Codex | Any HTTP MCP client | Any stdio MCP client |
Use an API key only when your client can't sign in with OAuth, or for scripts that call the HTTP API.
Claude and Claude Desktop
Settings → Connectors → Add custom connector, paste the URL, then click Connect and allow access on the SendShort page that opens.
Claude Code
claude mcp add --transport http --scope user sendshort https://app.sendshort.ai/api/mcp
Then type /mcp, select sendshort and choose Authenticate.
ChatGPT
Settings → Apps & Connectors, create a connector with the URL and OAuth authentication, then allow access on the SendShort page that opens. Custom connectors may require developer mode (under Advanced settings).
Cursor
One-click install is on the settings page. Or add this to ~/.cursor/mcp.json:
{
"mcpServers": {
"sendshort": { "url": "https://app.sendshort.ai/api/mcp" }
}
}
VS Code
One-click install is on the settings page. Or add this to .vscode/mcp.json:
{
"servers": {
"sendshort": { "type": "http", "url": "https://app.sendshort.ai/api/mcp" }
}
}
Codex
codex mcp add sendshort --url https://app.sendshort.ai/api/mcp
codex mcp login sendshort
With an API key instead
Create a key under Settings → Preferences → API keys. It's shown once. Keys expire after 1 year by default; you can pick 30 days, 90 days, 1 year, or never. Send it only in the Authorization header, never in the URL:
claude mcp add --transport http --scope user sendshort https://app.sendshort.ai/api/mcp \
--header "Authorization: Bearer ss_sk_yourkey"
In JSON configs, add "headers": { "Authorization": "Bearer ss_sk_yourkey" } next to url.
Rate limit
The hosted server allows 120 requests per minute per connected app or API key, counting every call including status polls. Over the limit it returns HTTP 429 with a Retry-After header. Job starts also have their own per-minute limits (see Limits).
Local npm package
Use @latest so npx picks up new releases without changing your config:
npx -y sendshort-mcp@latest
Package: sendshort-mcp on npm
The server exits at startup if the key is missing or malformed.
Claude Desktop
{
"mcpServers": {
"sendshort": {
"command": "npx",
"args": ["-y", "sendshort-mcp@latest"],
"env": {
"SENDSHORT_API_KEY": "ss_sk_yourkey"
}
}
}
}
OpenClaw
OpenClaw uses mcp.servers in ~/.openclaw/openclaw.json, not mcpServers.
openclaw mcp add sendshort \
--command npx \
--arg -y \
--arg sendshort-mcp@latest \
--env SENDSHORT_API_KEY=ss_sk_yourkey
openclaw mcp probe sendshort
Hermes
mcp_servers:
sendshort:
command: "npx"
args: ["-y", "sendshort-mcp@latest"]
env:
SENDSHORT_API_KEY: ss_sk_yourkey
Security
- Connected apps (signed in with OAuth) appear under Settings → Preferences → Connected apps, with when each was last used. Disconnect stops an app on its next request.
- Only Claude, ChatGPT, Cursor, VS Code, and apps that finish sign-in on your own machine can be allowed. The consent page refuses any other app, so a look-alike app can't get access by using a familiar name.
- A connected app can use the MCP tools and nothing else in your account. It counts against your plan's quota like anything you create yourself.
- An API key has full access to your SendShort API quota. Don't commit it or paste it into shared chats. Revoke it under Settings → Preferences → API keys if it leaks. Revocation takes effect on the next request.
- Each account can have up to 10 active keys. Use one key per tool or machine so you can revoke them one at a time. Connected apps don't count toward this limit.
- Tools declare MCP annotations. Status, list, and poll tools are marked read-only, so clients can auto-approve them.
import_youtube, clip_video, and create_music_video start jobs that use quota. schedule_short and schedule_slideshow post publicly on the user's accounts. cancel_scheduled_post and delete_video are marked destructive.
Tools
get_status | Plan, long-video slots (max 8), IMPORT_SHORTS / clip quota, clip rate-limit, and youtube_import_rate_limit |
import_youtube | Preflight quota, then queue a long-video import from a YouTube, Twitch, Kick, or direct video file link. Returns a job_id quickly |
get_import_status | Poll an import job. video_id is set only when the file is in storage |
list_videos | The user's imported long videos, newest first, with slot usage (max 8) |
delete_video | Delete a long video to free a slot. Shorts made from it stay |
list_moments | List moments for a video_id (gpt and created). Optional generate=true creates GPT moments if none exist |
list_themes | Caption theme presets for clip_video.theme |
list_templates | Caller’s starter templates for clip_video.starter_template_id |
clip_video | Queue clipping for an imported video_id with optional moment selection and styling |
get_clip_status | Poll a clip job. download_url is set only when the MP4 exists in storage |
create_music_video | Queue a 9:16 music video (max 60s) from a music link, public audio file, or uploaded audio |
get_music_video_status | Poll a music-video job. download_url is set only when the MP4 exists |
list_social_accounts | Connected TikTok and YouTube accounts to schedule to |
list_shorts | The user's shorts (made in the app or the API), newest first, with the source video_id |
get_short | One short, with a signed download_url once it is exported (app or API) |
list_slideshows | The user's slideshows, newest first |
schedule_short | Schedule an exported short to TikTok and/or YouTube |
schedule_slideshow | Schedule a slideshow to TikTok as a photo post (TikTok only) |
list_scheduled_posts | Scheduled, failed, and (optionally) published posts with per-account status |
cancel_scheduled_post | Delete a scheduled post before it publishes |
You must have rights to the track and any uploaded video. SendShort does not grant a music license. Lyric captions are speech-to-text and can mis-hear sung lyrics, including copyrighted lyrics.
import_youtube arguments:
youtube_url | string | required | Any supported video link: YouTube (watch, shorts, live, youtu.be), Twitch, Kick, or a public direct link to a video file. The field name is historical |
lang | string | en | Transcription language code |
list_moments arguments:
video_id | number | required | Imported videos.id |
generate | boolean | false | Generate GPT moments when the video has none yet |
clip_video arguments:
video_id | number | required | Imported videos.id owned by the API key user |
count | 1–20 | 5 | Used only when moment_ids is omitted. Ignored when moment_ids is set |
moment_ids | number[] | — | Specific moment ids from list_moments (1–20). Quota = array length; allows re-clipping created moments |
theme | number | 18 | Caption theme id from list_themes |
crop_style | string | — | none | blur | basic | caption | rounded-square |
short_format | string | — | 9:16 | 16:9 | 1:1 | 4:5 |
starter_template_id | uuid | — | From list_templates. Omit it to use the account's default template, or send null for no template. Explicit crop_style / short_format still win |
brolls / zoom / silents / transitions / audio_effects | boolean | false | Enhance toggles |
translate | boolean | false | Translate captions |
translateList | string[] | [] | Target language codes when translating |
style | object | — | Optional caption overrides (font, colors, position, emojis, etc.) |
Merge order for caption fields: theme defaults → starter template → style overrides → explicit crop_style / short_format.
get_status fields to keep straight:
import_shorts_* / clip_quota_* — monthly shorts quota. Clip jobs consume this by the number of shorts created. YouTube import does not consume it. Value "unlimited" (plan -1) means no monthly cap.
long_videos — concurrent long-video slots (max 8). If remaining is 0, free a slot with list_videos and delete_video, or delete a video from Home in the SendShort app.
youtube_import_rate_limit — YouTube import starts per minute for this API key (3/min).
rate_limit — clip starts per minute for this API key (5/min).
Agent flow
get_status — confirm Professional/Business access, remaining long_videos slots, import_shorts_remaining, and rate limits. If long_videos.remaining is 0, call list_videos, ask the user which video to remove, then call delete_video.
import_youtube { "youtube_url": "https://www.youtube.com/watch?v=…" } → job_id (status: queued).
- Poll
get_import_status every 5–15s until video_id is set (status: complete).
list_moments with that video_id and generate: true if needed; optionally list_themes / list_templates.
clip_video with moment_ids and styling (or count for random GPT picks) → clip job_id.
- Poll
get_clip_status until top-level status is complete, or each clip has download_url.
To get the file of a short the user made in the app, find it with list_shorts, then call get_short. download_url is null until the short is exported in the editor.
Example prompt:
Import https://www.youtube.com/watch?v=dQw4w9WgXcQ into SendShort, list moments, clip the highest-virality one with theme Hormozi and crop_style blur, then poll until a download URL is ready.
YouTube download can take several minutes. A job with no SSE heartbeat for 45 minutes is marked failed. Clip transcription + render can take several more minutes; a clip job that never creates shorts within ~6 minutes is marked failed.
Scheduling posts
list_social_accounts — connected accounts. If none, the user connects one in the SendShort app (Settings → Preferences).
list_shorts or list_slideshows to find the item (or use the short id from get_clip_status).
schedule_short or schedule_slideshow with publish_at (ISO 8601 with timezone; omit to post within ~5 minutes).
list_scheduled_posts to see whether it went out. Posts appear in the SendShort calendar too.
schedule_short arguments (set tiktok, youtube, or both):
short_id | number | required | Must be exported (rendered) |
publish_at | string | now | ISO 8601 with timezone, not in the past |
tiktok.account_ids | string[] | required | From list_social_accounts |
tiktok.caption | string | "" | Max 2200 characters |
tiktok.privacy_level | string | PUBLIC_TO_EVERYONE | Or MUTUAL_FOLLOW_FRIENDS, FOLLOWER_OF_CREATOR, SELF_ONLY |
tiktok.allow_comments / allow_duet / allow_stitch | boolean | true | |
tiktok.draft | boolean | false | Send to the TikTok inbox to finish in the app instead of posting |
youtube.account_ids | string[] | required | From list_social_accounts |
youtube.title | string | short name | Max 100 characters |
youtube.description | string | "" | Max 5000 bytes |
youtube.tags | string[] | ["shorts"] | |
youtube.privacy | string | public | public | unlisted | private |
schedule_slideshow takes slideshow_id, publish_at, and tiktok (account_ids, caption up to 4000 characters, privacy_level, allow_*). Slides are rendered before posting when they changed.
When a short has licensed music, its attribution is added to the TikTok caption and the YouTube description.
HTTP API
Same backend the MCP tools call. Base URL: https://app.sendshort.ai
Authorization: Bearer ss_sk_…
Content-Type: application/json
401 invalid_api_key | Missing, malformed, revoked, or expired key |
401 invalid_token | OAuth access token expired, or the app was disconnected. The client refreshes or signs in again |
403 api_access_required | Plan does not include API/MCP access (Professional or Business required) |
403 forbidden | Video not owned, or LONG_VIDEO_TO_SHORT is off |
403 quota_exceeded | Long-video slots full, or count exceeds remaining IMPORT_SHORTS |
429 rate_limit | Clip: 5 starts/min/key. YouTube import: 3 starts/min/key |
GET /api/v1/status
curl -sS https://app.sendshort.ai/api/v1/status \
-H "Authorization: Bearer $SENDSHORT_API_KEY"
POST /api/v1/import
Returns 202 after auth, feature check, long-video quota check, and rate-limit reservation. Download continues in the background.
curl -sS -X POST https://app.sendshort.ai/api/v1/import \
-H "Authorization: Bearer $SENDSHORT_API_KEY" \
-H "Content-Type: application/json" \
-d '{"youtube_url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ"}'
Optional body field: lang (default en).
GET /api/v1/import/:job_id
Poll until video_id is non-null.
JOB=$(curl -sS -X POST https://app.sendshort.ai/api/v1/import \
-H "Authorization: Bearer $SENDSHORT_API_KEY" \
-H "Content-Type: application/json" \
-d '{"youtube_url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ"}' | jq -r .job_id)
while true; do
curl -sS "https://app.sendshort.ai/api/v1/import/$JOB" \
-H "Authorization: Bearer $SENDSHORT_API_KEY" | tee /tmp/import.json
jq -e '.status == "complete" and .video_id != null' /tmp/import.json && break
sleep 10
done
POST /api/v1/clip
Returns 202 after auth, ownership check, IMPORT_SHORTS preflight, and rate-limit reservation. When moment_ids is present, quota uses moment_ids.length and count is ignored. Generation continues in the background. Successful jobs increment IMPORT_SHORTS usage by the number of shorts created.
curl -sS -X POST https://app.sendshort.ai/api/v1/clip \
-H "Authorization: Bearer $SENDSHORT_API_KEY" \
-H "Content-Type: application/json" \
-d '{"video_id": 12345, "moment_ids": [101, 102], "theme": 1, "crop_style": "blur"}'
Optional body fields: count, moment_ids, theme, crop_style, short_format, starter_template_id, brolls, zoom, silents, transitions, audio_effects, translate, translateList, style.
GET /api/v1/videos/:video_id/moments
curl -sS "https://app.sendshort.ai/api/v1/videos/12345/moments?generate=true" \
-H "Authorization: Bearer $SENDSHORT_API_KEY"
GET /api/v1/themes
curl -sS https://app.sendshort.ai/api/v1/themes \
-H "Authorization: Bearer $SENDSHORT_API_KEY"
GET /api/v1/templates
curl -sS https://app.sendshort.ai/api/v1/templates \
-H "Authorization: Bearer $SENDSHORT_API_KEY"
GET /api/v1/clip/:job_id
Poll until download_url is non-null. Signed MP4 URLs are short-lived.
JOB=$(curl -sS -X POST https://app.sendshort.ai/api/v1/clip \
-H "Authorization: Bearer $SENDSHORT_API_KEY" \
-H "Content-Type: application/json" \
-d '{"video_id": 12345, "count": 1}' | jq -r .job_id)
while true; do
curl -sS "https://app.sendshort.ai/api/v1/clip/$JOB" \
-H "Authorization: Bearer $SENDSHORT_API_KEY" | tee /tmp/job.json
jq -e '.status == "complete" or .clips[0].download_url != null' /tmp/job.json && break
sleep 10
done
Pending clip fields: has_subtitles, render_claimed, render_id, processing.
Long videos and shorts
GET /api/v1/videos?search=&limit=&offset= | list_videos |
POST /api/v1/videos/:video_id/delete | delete_video |
GET /api/v1/shorts/:short_id | get_short |
curl -sS https://app.sendshort.ai/api/v1/shorts/12345 \
-H "Authorization: Bearer $SENDSHORT_API_KEY"
get_short returns export_status (exported or not_exported) and a short-lived signed download_url when exported.
Scheduling endpoints
GET /api/v1/social-accounts | list_social_accounts |
GET /api/v1/shorts?search=&limit=&offset= | list_shorts |
GET /api/v1/slideshows?search=&limit=&offset= | list_slideshows |
POST /api/v1/schedule/short | schedule_short (same body) |
POST /api/v1/schedule/slideshow | schedule_slideshow (same body) |
GET /api/v1/schedule?include_past=true | list_scheduled_posts |
POST /api/v1/schedule/:schedule_id/cancel | cancel_scheduled_post |
curl -sS -X POST https://app.sendshort.ai/api/v1/schedule/short \
-H "Authorization: Bearer $SENDSHORT_API_KEY" \
-H "Content-Type: application/json" \
-d '{"short_id": 12345, "publish_at": "2026-10-02T18:00:00+02:00",
"tiktok": {"account_ids": ["…"], "caption": "New drop #shorts"},
"youtube": {"account_ids": ["…"], "title": "New drop"}}'
Limits
- Professional or Business plan (API_ACCESS feature)
- YouTube import requires
LONG_VIDEO_TO_SHORT
- At most 8 long videos at a time; free a slot with
delete_video or in the SendShort app (Home)
- 3 YouTube import starts / minute / API key
- 5 clip starts / minute / API key
- 120 hosted MCP requests / minute / connected app or API key (all calls, including polls)
- YouTube import does not consume
IMPORT_SHORTS
- Each clip job consumes
IMPORT_SHORTS by shorts actually created (preflight requires remaining >= count or remaining >= moment_ids.length)
- No schedule or social publish yet
- Music videos: Starter 0, Professional 10 per month, Business unlimited (
MUSIC_VIDEO). Quota increments only when a job completes. In-flight jobs count against remaining. Failed and timed-out jobs consume 0
- 3 music-video starts / minute / API key
- Music videos are 9:16 and trimmed to 60 seconds from the start. Sources longer than 7 minutes are rejected before trim
media_type: gameplay (default) or aesthetic (a background clip from the SendShort library), or custom (your own video; requires video_url or video_path)
sound_wave and beat_bars default to true on music-video jobs
- Upload audio or custom video with
POST /api/v1/music-video/upload ({"kind":"audio"} or {"kind":"video"}), PUT the file to upload_url, then pass key as audio_path or video_path