{"name":"com.multi-upload-tool/mcp","title":"Multi Upload Tool","version":"0.1.0","description":"Schedule and publish videos to TikTok, YouTube, Instagram and 5 more networks.","websiteUrl":"https://multi-upload-tool.com/integrations/mcp","documentationUrl":"https://docs.multi-upload-tool.com/mcp","remotes":[{"type":"streamable-http","url":"https://api.multi-upload-tool.com/mcp"}],"serverInfo":{"name":"multi-upload-tool","version":"0.1.0"},"authentication":{"required":true,"schemes":["oauth2"]},"tools":[{"name":"list_accounts","description":"List the social media accounts connected to the authenticated user (or current team workspace). Returns id, platform, accountName, status, tags and lastUsedAt. Call this first to discover which accountIds can be used in create_post. Workspaces can hold many accounts: narrow with platform, tag or search instead of reading the whole list. Only accounts with status 'active' can publish.","inputSchema":{"type":"object","properties":{"platform":{"type":"string","enum":["tiktok","instagram","facebook","linkedin","bluesky","threads","pinterest","youtube","x"],"description":"Only accounts on this platform"},"tag":{"type":"string","description":"Only accounts carrying this tag (exact match, case-insensitive). Users tag accounts to group them, e.g. by client."},"search":{"type":"string","description":"Case-insensitive substring of the account name, e.g. part of a handle"}},"additionalProperties":false},"annotations":{"readOnlyHint":true,"openWorldHint":false}},{"name":"create_post","description":"Publish content to one or more connected social accounts. SIDE EFFECT: this publishes (or schedules) live posts — confirm with the user before calling.\n\nInputs:\n- accountIds (string[], required): ids returned by list_accounts; you can mix platforms in a single call.\n- text (string, optional when mediaUrls is non-empty): caption/description shared across platforms.\n- mediaUrls (string[], optional, max 10): public http(s) URLs of images/videos to attach. The server downloads them. The file is on the user's device instead? Use create_media_upload first.\n- scheduledAt (string, optional): ISO 8601 timestamp with offset; omit for immediate publishing. Max 6 months ahead.\n- platformSettings (object, optional): per-platform settings. Some platforms REQUIRE a key here:\n    * tiktok REQUIRES privacyLevel (PUBLIC_TO_EVERYONE | MUTUAL_FOLLOW_FRIENDS | SELF_ONLY).\n    * youtube REQUIRES title (1-100 chars).\n    * pinterest REQUIRES boardId.\n    * instagram, facebook, linkedin, bluesky, threads, x have no required settings.\n    * x is billed in X credits: 1 per post, 15 when the text contains a link. The post fails if the user is out of credits.\n    * x options: replySettings, madeWithAi, paidPartnership, altText + taggedUsers (images), subtitlesUrl + subtitlesLanguage (video), pollOptions + pollDurationMinutes (text only, no media), communityId + shareWithFollowers, superFollowersOnly, premium (25,000-char posts, 125-min videos).\n    * x refuses the same text or media file on two of the user's X accounts within 30 days (X bans identical cross-account content): give each X account its own text and media.\n    * instagram.asStory=true publishes a story instead of a feed post: exactly one JPEG (≤8 MB) or MP4/MOV (3-60s, ≤100 MB), text is dropped, no collaborators or location. It disappears after 24h.\n\nMedia requirements:\n- Text-only posts (no mediaUrls) are accepted on: linkedin, bluesky, threads, facebook, x.\n- Media is REQUIRED on: tiktok (video or image-carousel), instagram (image/video/reel), youtube (video), pinterest (image or video).\n- LinkedIn additionally supports document uploads (PDF/DOC/PPT) when mediaUrls points to a document file.\n\nCall get_platform_capabilities(platform) to see the full media support matrix and settingsSchema before calling this tool.\n\nReturns: { success, posts: [{ accountId, platform, status: queued|scheduled|failed, postId, scheduledFor, error }], note? }. Mixed batches partially succeed — when some fail, `note` lists the only accountIds to retry; re-sending the others publishes duplicates.\n\nThe server downloads mediaUrls before answering, so a large file can make the call time out. Always pass a fresh idempotencyKey (e.g. a UUID) per intended post: retrying with the SAME key after a timeout never publishes twice — it returns the first call's result with idempotentReplay: true. Without a key, do NOT retry blindly — call list_recent_posts to check first.","inputSchema":{"type":"object","properties":{"accountIds":{"type":"array","items":{"type":"string"},"minItems":1,"maxItems":20,"description":"Connected account ids from list_accounts"},"text":{"type":"string","description":"Post text / caption"},"mediaUrls":{"type":"array","items":{"type":"string","format":"uri"},"maxItems":10,"description":"Public http(s) URLs of images or videos to attach"},"scheduledAt":{"type":"string","format":"date-time","description":"ISO 8601 — omit for immediate publish"},"status":{"type":"string","enum":["draft","pending_approval"],"description":"Create the post without publishing it: 'draft' (you or the user keeps editing) or 'pending_approval' (a human approves it in the app or through create_review_link). In a team that requires approval, a member's posts are always 'pending_approval'."},"overrides":{"type":"object","description":"Per-network or per-account variants: keys are a platform name (\"bluesky\") or an accountId, values { text?, title? }. An accountId key wins over its platform, which wins over the shared text. Use it when one caption cannot fit every network (Bluesky 300 chars vs YouTube 5000).","additionalProperties":{"type":"object","properties":{"text":{"type":"string"},"title":{"type":"string"}},"additionalProperties":false}},"idempotencyKey":{"type":"string","maxLength":255,"description":"Unique key for this post (e.g. a UUID). Reusing it within 24h returns the first result instead of publishing again."},"platformSettings":{"type":"object","description":"Per-platform required settings. See tool description and get_platform_capabilities.","properties":{"tiktok":{"type":"object","properties":{"privacyLevel":{"type":"string","enum":["PUBLIC_TO_EVERYONE","MUTUAL_FOLLOW_FRIENDS","SELF_ONLY"]},"allowComments":{"type":"boolean"},"allowDuet":{"type":"boolean"},"allowStitch":{"type":"boolean"},"isAigc":{"type":"boolean"}},"required":["privacyLevel"],"additionalProperties":false},"instagram":{"type":"object","properties":{"shareToFeed":{"type":"boolean"},"asStory":{"type":"boolean"},"collaborators":{"type":"string"},"locationId":{"type":"string"},"isAigc":{"type":"boolean"},"trialReel":{"type":"string","enum":["manual","auto"]}},"additionalProperties":false},"facebook":{"type":"object","properties":{"link":{"type":"string","format":"uri"},"isAigc":{"type":"boolean"}},"additionalProperties":false},"linkedin":{"type":"object","properties":{"visibility":{"type":"string","enum":["PUBLIC","CONNECTIONS"]}},"additionalProperties":false},"youtube":{"type":"object","properties":{"title":{"type":"string","minLength":1,"maxLength":100},"categoryId":{"type":"string"},"privacyStatus":{"type":"string","enum":["public","unlisted","private"]},"madeForKids":{"type":"boolean"},"tags":{"type":"array","items":{"type":"string"}}},"required":["title"],"additionalProperties":false},"pinterest":{"type":"object","properties":{"boardId":{"type":"string","minLength":1},"link":{"type":"string","format":"uri"}},"required":["boardId"],"additionalProperties":false},"bluesky":{"type":"object","properties":{"langs":{"type":"array","items":{"type":"string"}},"labels":{"type":"array","items":{"type":"string"}}},"additionalProperties":false},"threads":{"type":"object","properties":{"replyControl":{"type":"string","enum":["everyone","accounts_you_follow","mentioned_only"]},"altText":{"type":"string"}},"additionalProperties":false},"x":{"type":"object","properties":{"replySettings":{"type":"string","enum":["everyone","following","mentionedUsers","subscribers","verified"]},"madeWithAi":{"type":"boolean"},"paidPartnership":{"type":"boolean"},"altText":{"type":"string","maxLength":1000},"taggedUsers":{"type":"array","items":{"type":"string"},"maxItems":10},"subtitlesUrl":{"type":"string","format":"uri"},"subtitlesLanguage":{"type":"string","minLength":2,"maxLength":2},"pollOptions":{"type":"array","items":{"type":"string","minLength":1,"maxLength":25},"minItems":2,"maxItems":4},"pollDurationMinutes":{"type":"integer","minimum":5,"maximum":10080},"communityId":{"type":"string","pattern":"^[0-9]{1,19}$"},"shareWithFollowers":{"type":"boolean"},"superFollowersOnly":{"type":"boolean"},"premium":{"type":"boolean"}},"additionalProperties":false}},"additionalProperties":false}},"required":["accountIds"],"additionalProperties":false},"annotations":{"readOnlyHint":false,"destructiveHint":false,"idempotentHint":false,"openWorldHint":true}},{"name":"validate_post","description":"Dry run of create_post: takes the same arguments and runs the same checks (account status, plan, daily caps, monthly quota, queue room, caption and title limits, required platformSettings, scheduling window) without publishing anything.\nCall it before create_post on any batch, or whenever unsure — a post rejected here would otherwise fail later, sometimes hours later for scheduled posts.\nmediaUrls are not downloaded: file format, size and duration are only checked when the post is actually published.\n\nReturns: { valid, errors: [{ accountId, platform, error }], targets: [{ accountId, platform, scheduledFor }] }. An error with accountId null applies to the whole batch.","inputSchema":{"type":"object","properties":{"accountIds":{"type":"array","items":{"type":"string"},"minItems":1,"maxItems":20,"description":"Connected account ids from list_accounts"},"text":{"type":"string","description":"Post text / caption"},"mediaUrls":{"type":"array","items":{"type":"string","format":"uri"},"maxItems":10,"description":"Public http(s) URLs of images or videos to attach"},"scheduledAt":{"type":"string","format":"date-time","description":"ISO 8601 — omit for immediate publish"},"status":{"type":"string","enum":["draft","pending_approval"],"description":"Create the post without publishing it: 'draft' (you or the user keeps editing) or 'pending_approval' (a human approves it in the app or through create_review_link). In a team that requires approval, a member's posts are always 'pending_approval'."},"overrides":{"type":"object","description":"Per-network or per-account variants: keys are a platform name (\"bluesky\") or an accountId, values { text?, title? }. An accountId key wins over its platform, which wins over the shared text. Use it when one caption cannot fit every network (Bluesky 300 chars vs YouTube 5000).","additionalProperties":{"type":"object","properties":{"text":{"type":"string"},"title":{"type":"string"}},"additionalProperties":false}},"idempotencyKey":{"type":"string","maxLength":255,"description":"Unique key for this post (e.g. a UUID). Reusing it within 24h returns the first result instead of publishing again."},"platformSettings":{"type":"object","description":"Per-platform required settings. See tool description and get_platform_capabilities.","properties":{"tiktok":{"type":"object","properties":{"privacyLevel":{"type":"string","enum":["PUBLIC_TO_EVERYONE","MUTUAL_FOLLOW_FRIENDS","SELF_ONLY"]},"allowComments":{"type":"boolean"},"allowDuet":{"type":"boolean"},"allowStitch":{"type":"boolean"},"isAigc":{"type":"boolean"}},"required":["privacyLevel"],"additionalProperties":false},"instagram":{"type":"object","properties":{"shareToFeed":{"type":"boolean"},"asStory":{"type":"boolean"},"collaborators":{"type":"string"},"locationId":{"type":"string"},"isAigc":{"type":"boolean"},"trialReel":{"type":"string","enum":["manual","auto"]}},"additionalProperties":false},"facebook":{"type":"object","properties":{"link":{"type":"string","format":"uri"},"isAigc":{"type":"boolean"}},"additionalProperties":false},"linkedin":{"type":"object","properties":{"visibility":{"type":"string","enum":["PUBLIC","CONNECTIONS"]}},"additionalProperties":false},"youtube":{"type":"object","properties":{"title":{"type":"string","minLength":1,"maxLength":100},"categoryId":{"type":"string"},"privacyStatus":{"type":"string","enum":["public","unlisted","private"]},"madeForKids":{"type":"boolean"},"tags":{"type":"array","items":{"type":"string"}}},"required":["title"],"additionalProperties":false},"pinterest":{"type":"object","properties":{"boardId":{"type":"string","minLength":1},"link":{"type":"string","format":"uri"}},"required":["boardId"],"additionalProperties":false},"bluesky":{"type":"object","properties":{"langs":{"type":"array","items":{"type":"string"}},"labels":{"type":"array","items":{"type":"string"}}},"additionalProperties":false},"threads":{"type":"object","properties":{"replyControl":{"type":"string","enum":["everyone","accounts_you_follow","mentioned_only"]},"altText":{"type":"string"}},"additionalProperties":false},"x":{"type":"object","properties":{"replySettings":{"type":"string","enum":["everyone","following","mentionedUsers","subscribers","verified"]},"madeWithAi":{"type":"boolean"},"paidPartnership":{"type":"boolean"},"altText":{"type":"string","maxLength":1000},"taggedUsers":{"type":"array","items":{"type":"string"},"maxItems":10},"subtitlesUrl":{"type":"string","format":"uri"},"subtitlesLanguage":{"type":"string","minLength":2,"maxLength":2},"pollOptions":{"type":"array","items":{"type":"string","minLength":1,"maxLength":25},"minItems":2,"maxItems":4},"pollDurationMinutes":{"type":"integer","minimum":5,"maximum":10080},"communityId":{"type":"string","pattern":"^[0-9]{1,19}$"},"shareWithFollowers":{"type":"boolean"},"superFollowersOnly":{"type":"boolean"},"premium":{"type":"boolean"}},"additionalProperties":false}},"additionalProperties":false}},"required":["accountIds"],"additionalProperties":false},"annotations":{"readOnlyHint":true,"openWorldHint":false}},{"name":"get_post_status","description":"Get the current status of one post. Returns status (pending | processing | completed | scheduled | failed), the full caption, the platform's videoUrl once published, and on failure errorMessage plus failure: { code, retryable, nextAction } (see retry_post).","inputSchema":{"type":"object","properties":{"postId":{"oneOf":[{"type":"integer"},{"type":"string"}],"description":"postId returned by create_post or list_recent_posts"}},"required":["postId"],"additionalProperties":false},"annotations":{"readOnlyHint":true,"openWorldHint":false}},{"name":"list_recent_posts","description":"List recent posts published, scheduled, or failed by the authenticated user. Useful to avoid duplicates, reference past content, or audit failures. Captions are cut to 200 characters — call get_post_status for the full text. hasMore: true means older posts exist beyond this page; narrow with accountId, platform or status rather than raising limit.","inputSchema":{"type":"object","properties":{"limit":{"type":"integer","minimum":1,"maximum":50,"description":"How many posts to return (default 10)"},"accountId":{"type":"string","description":"Filter by connected account id"},"platform":{"type":"string","enum":["tiktok","instagram","facebook","linkedin","bluesky","threads","pinterest","youtube","x"],"description":"Filter by platform"},"status":{"type":"string","enum":["pending","processing","completed","failed","scheduled","draft","pending_approval"]}},"additionalProperties":false},"annotations":{"readOnlyHint":true,"openWorldHint":false}},{"name":"get_platform_capabilities","description":"Return the character limits, daily post cap, supported media types, and required platformSettings schema for a given platform. Call this before create_post to ensure inputs are valid.","inputSchema":{"type":"object","properties":{"platform":{"type":"string","enum":["tiktok","instagram","facebook","linkedin","bluesky","threads","pinterest","youtube","x"]}},"required":["platform"],"additionalProperties":false},"annotations":{"readOnlyHint":true,"openWorldHint":false}},{"name":"get_hashtag_history","description":"Return the hashtags this user has used most often in their recent posts on the given platform, plus platform-specific guidance and (for Instagram) the list of hashtags that would cause the post to be rejected. Does NOT generate new hashtags — use your own creative ability for that; this tool gives you the user's historical preferences which you cannot see otherwise.","inputSchema":{"type":"object","properties":{"platform":{"type":"string","enum":["tiktok","instagram","facebook","linkedin","bluesky","threads","pinterest","youtube","x"]},"limit":{"type":"integer","minimum":1,"maximum":30,"description":"How many hashtags to return (default 20)"}},"required":["platform"],"additionalProperties":false},"annotations":{"readOnlyHint":true,"openWorldHint":false}},{"name":"get_analytics","description":"Read published-post performance: views, likes, comments, shares, plus growth over the last 30 days. summary compares the last 30 days with the 30 before (views gained, follower change, posts published; null = not measured, never 0) and followerHistory gives one follower total per day. Call with no argument for every account at once, with accountId for one account, or with postId for a single post. Numbers come from a nightly sync, so they lag the platform by up to a day — lastRefreshedAt says when they were last measured, and a metric a platform does not report comes back as 0. Account and global calls also return bestTimes: the weekday and hours whose posts averaged the most views, to feed scheduledAt in create_post. This is read-only; it never publishes.","inputSchema":{"type":"object","properties":{"accountId":{"type":"string","description":"Connected account id (from list_accounts). Omit for all accounts combined."},"postId":{"oneOf":[{"type":"integer"},{"type":"string"}],"description":"postId of one post (from create_post or list_recent_posts). Returns that post's metrics only."},"limit":{"type":"integer","minimum":1,"maximum":50,"description":"How many top posts to return (default 10). Ignored with postId."},"source":{"type":"string","enum":["all","mut","external"],"description":"Which posts topPosts ranks: all (default), mut (published through Multi Upload Tool) or external (posted directly on the platform). Totals and summary always cover every post."},"timezone":{"type":"string","description":"IANA timezone the bestTimes weekday/hour buckets are read in, e.g. Europe/Paris. Defaults to UTC — always pass the user's own zone, because an hour-of-day ranking read in the wrong zone is silently off by the whole offset."}},"additionalProperties":false},"annotations":{"readOnlyHint":true,"openWorldHint":false}},{"name":"search","description":"Search your posts (caption and title) and connected accounts by keyword. Returns { results: [{ id, title, url }] }; pass an id to fetch for the full record. Read-only.","inputSchema":{"type":"object","properties":{"query":{"type":"string","description":"Words to look for, e.g. a product name or a hashtag."}},"required":["query"],"additionalProperties":false},"annotations":{"readOnlyHint":true,"openWorldHint":false}},{"name":"fetch","description":"Read one search result in full, by the id search returned (post:<id> or account:<id>): a post's caption, status, schedule or publication time and platform URL, or an account's details. Read-only.","inputSchema":{"type":"object","properties":{"id":{"type":"string","description":"An id returned by search."}},"required":["id"],"additionalProperties":false},"annotations":{"readOnlyHint":true,"openWorldHint":false}},{"name":"reschedule_post","description":"Move a scheduled post to another time, and optionally replace its text. Only posts still in the 'scheduled' status can move — published ones are final. scheduledAt is ISO 8601 with an offset, at most 6 months ahead. SIDE EFFECT: changes what goes out and when — confirm with the user first.","inputSchema":{"type":"object","properties":{"postId":{"oneOf":[{"type":"integer"},{"type":"string"}],"description":"uploadId from create_post or list_recent_posts."},"scheduledAt":{"type":"string","description":"New time, ISO 8601 with offset, e.g. 2026-10-02T18:00:00+02:00."},"text":{"type":"string","description":"Optional new caption/description."}},"required":["postId","scheduledAt"],"additionalProperties":false},"annotations":{"readOnlyHint":false,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false}},{"name":"cancel_post","description":"Cancel a scheduled post (or clear a failed one) so it never goes out. Published posts cannot be cancelled here — this does not delete anything on the platform. SIDE EFFECT: irreversible — confirm with the user first.","inputSchema":{"type":"object","properties":{"postId":{"oneOf":[{"type":"integer"},{"type":"string"}],"description":"uploadId from create_post or list_recent_posts."}},"required":["postId"],"additionalProperties":false},"annotations":{"readOnlyHint":false,"destructiveHint":true,"idempotentHint":false,"openWorldHint":false}},{"name":"create_media_upload","description":"Get a one-time upload link for a file the user has on their device (phone or computer) — use it when there is no public URL for the media and you cannot upload it yourself (no shell). Tool calls cannot carry files, so the user uploads in their browser.\nFlow: 1) call this, 2) show uploadUrl to the user as a clickable link and ask them to drop the video/images there, 3) when they say it is done, call check_media_upload(dropId), 4) pass its mediaUrls to create_post.\nThe link works for 1 hour and once. Up to 35 files, 5 GB in total, video and images only; counts against the plan's storage quota. Uploaded media not used in a post within 24 hours is deleted.\nReturns { dropId, uploadUrl, expiresAt, limits }.","inputSchema":{"type":"object","properties":{},"additionalProperties":false},"annotations":{"readOnlyHint":false,"destructiveHint":false,"idempotentHint":false,"openWorldHint":false}},{"name":"check_media_upload","description":"Status of an upload link from create_media_upload: waiting (the user has not finished), ready (with mediaUrls to pass to create_post), or expired (create a new link). Read-only.","inputSchema":{"type":"object","properties":{"dropId":{"type":"string","description":"dropId from create_media_upload"}},"required":["dropId"],"additionalProperties":false},"annotations":{"readOnlyHint":true,"openWorldHint":false}},{"name":"create_review_link","description":"Create a no-login link where the user's client approves or sends back posts awaiting approval (create_post with status 'pending_approval' or 'draft'). Approving a post there schedules it at its date, or publishes it now if the date has passed.\nWithout postIds: every post awaiting approval (max 50). Drafts included are submitted. Only an owner or admin can create one. The link lasts 14 days.\nReturns { url, expiresAt, uploadIds }. Give url to the user; do not approve posts yourself.","inputSchema":{"type":"object","properties":{"postIds":{"type":"array","items":{"type":"integer"},"maxItems":50,"description":"Posts to include; default: all awaiting approval."}},"additionalProperties":false},"annotations":{"readOnlyHint":false,"destructiveHint":false,"idempotentHint":false,"openWorldHint":false}},{"name":"retry_post","description":"Re-send ONE failed post (a postId is one account of a post). The other accounts of the same post are separate postIds and are not touched, so nothing is published twice. SIDE EFFECT: publishes — confirm with the user first.\nRead `failure` on get_post_status / list_recent_posts first: failure.retryable false means retrying will fail again (bad media, content flagged, cancelled) and the call is refused unless force=true.\nfailure.nextAction: retry | retry_later (platform cap, retry after it resets) | reconnect_account | replace_media | edit_post | check_platform (may already be published: look before forcing) | none.\nRefused also when the account is disconnected, the post is still being retried automatically, or its media was deleted (kept 1 day after the failure).","inputSchema":{"type":"object","properties":{"postId":{"oneOf":[{"type":"integer"},{"type":"string"}],"description":"postId of a failed post, from create_post or list_recent_posts."},"force":{"type":"boolean","description":"Retry even when failure.retryable is false. Only after checking the platform."}},"required":["postId"],"additionalProperties":false},"annotations":{"readOnlyHint":false,"destructiveHint":false,"idempotentHint":false,"openWorldHint":true}},{"name":"retry_all_failed","description":"Re-send every retryable failed post of the last 24 hours, optionally only for one accountId or platform. Posts that went out are never touched. Returns { retried: [postId], skipped: [{ uploadId, platform, code, reason }] }. SIDE EFFECT: publishes — confirm with the user first.","inputSchema":{"type":"object","properties":{"accountId":{"type":"string","description":"Only this account's failed posts."},"platform":{"type":"string","enum":["tiktok","instagram","facebook","linkedin","bluesky","threads","pinterest","youtube","x"],"description":"Only this platform's failed posts."}},"additionalProperties":false},"annotations":{"readOnlyHint":false,"destructiveHint":false,"idempotentHint":false,"openWorldHint":true}},{"name":"get_upload_limits","description":"How many more posts an account can publish today before hitting the platform's daily cap, and when the count resets. For an X account it also returns xCredits (balance, cost per post: 1, or 15 with a link, and the packs): an X post fails without enough credits. Check it before a batch of create_post calls. Read-only.","inputSchema":{"type":"object","properties":{"accountId":{"type":"string","description":"Connected account id from list_accounts."}},"required":["accountId"],"additionalProperties":false},"annotations":{"readOnlyHint":true,"openWorldHint":false}},{"name":"list_pools","description":"List the upload pools: queues of videos drip-fed to accounts on recurring weekly slots (time, days, accounts), with how many items are queued. Requires a plan with upload pools. Read-only.","inputSchema":{"type":"object","properties":{},"additionalProperties":false},"annotations":{"readOnlyHint":true,"openWorldHint":false}},{"name":"add_to_pool","description":"Queue 1 to 50 videos into an upload pool; each goes out at the pool's next free slot. mediaUrl must be publicly reachable (it is checked before queuing). SIDE EFFECT: these will be published automatically — confirm with the user first.","inputSchema":{"type":"object","properties":{"poolId":{"type":"string","description":"poolId from list_pools."},"items":{"type":"array","minItems":1,"maxItems":50,"items":{"type":"object","properties":{"mediaUrl":{"type":"string","description":"Public URL of the video."},"title":{"type":"string","description":"Title (YouTube) / label, 1-200 characters."},"text":{"type":"string","description":"Caption."}},"required":["mediaUrl","title"],"additionalProperties":false}}},"required":["poolId","items"],"additionalProperties":false},"annotations":{"readOnlyHint":false,"destructiveHint":false,"idempotentHint":false,"openWorldHint":true}}],"resources":[],"prompts":[]}