Skip to main content
Glama

Validate a post (dry run)

validate_post
Read-onlyIdempotent

Checks a post against every account's network rules without saving anything and returns ALL issues at once. It approves nothing by itself: read next_step, which says in plain words what to do (fix issues, ask the human to confirm and pass confirmation_token, or create_post) (caption length, format support, media requirements, video length and shape, YouTube title, TikTok privacy and commercial fields, disconnected accounts). With schedule_at it also reports the X link allowance, the key's caps and whether the send would need confirmation_token from the returned plan, plus the dispatch plan. Run it before create_post when in doubt; it needs only the read scope and works in a sandbox.

Input Schema

NameRequiredDescriptionDefault
pollNoA poll on X, LinkedIn and Mastodon. A poll post carries no media.
labelNoInternal name for the post (never published).
formatNoFormat for every account. Default per network: Facebook/Instagram FEED (REEL with a video), YouTube SHORT, TikTok VIDEO, LinkedIn FEED (VIDEO with a video), X FEED. Unsupported on a network = an issue, not a silent change.
captionYesMaster text. Goes to every account unless per_channel overrides it. X: 280 characters; Bluesky: 300; Mastodon: 500 (links count as 23); Instagram and TikTok: 2,200; Telegram: 4,096 for text, 1,024 under photos, a video or a file.
cta_urlNoLink to track. Each account gets its own short link with UTM tags, placed in the caption or the first comment per network. Never added on X (paid links).
gbp_endNoGoogle Business Profile event or offer: when it ends (not before gbp_start), same format.
post_idNoExisting draft or approved post; enables checking its approval state and preserving its tracked URLs.
campaignNoutm_campaign value for the tracked links.
cta_textNo
draft_idNoReserved post ID returned by validate_post or needs_confirmation. Pass it unchanged to create_post so previewed tracked URLs and the confirmation plan remain exact.
gbp_linkNoGoogle Business Profile: where the button (or an offer's "redeem online") leads, https. Omitted: cta_url. Either way it becomes a tracked short link; links in the post's text aren't clickable on Google.
gbp_typeNoGoogle Business Profile: STANDARD (an update, the default), EVENT or OFFER. Events and offers need gbp_title, gbp_start and gbp_end.
x_threadNoX: up to 5 follow-up posts that continue the post as a thread (each is a paid X post).
alt_textsNoImage descriptions for screen readers, by media id (images in media_ids). Sent to Instagram, Facebook, X, LinkedIn, Bluesky and Mastodon. Stored on the image, so later posts reuse it; an empty string clears it.
gbp_startNoGoogle Business Profile event or offer: when it starts, in the location's own time, "2026-11-05" (the whole day) or "2026-11-05T18:30". No time zone.
gbp_termsNoGoogle Business Profile offer: terms and conditions (optional).
gbp_titleNoGoogle Business Profile event or offer: its title (required there, up to 58 characters).
group_idsNoAccount groups — every active account in them is added to channel_ids.
media_idsNoMedia from upload_media_from_url, in display order. Instagram, TikTok and YouTube need media; Reels/Shorts/Video need a video file.
gbp_buttonNoGoogle Business Profile update or event: the button. CALL dials the profile's phone number (no link); the others lead to gbp_link or cta_url. Omitted with a link: LEARN_MORE. Offers get Google's own button instead.
gbp_couponNoGoogle Business Profile offer: a coupon code (optional).
channel_idsNoAccounts to publish to (ids from list_channels).
per_channelNoPer-account overrides and network fields. TikTok accounts need tiktok_privacy here.
schedule_atNoOptional: also check allowances, caps and confirmation for this time.
video_titleNoVideo title on Facebook and LinkedIn videos (YouTube uses youtube_title).
ai_generatedNoMade or meaningfully altered with AI. Sent as each network's AI label: Instagram, Facebook Reels, YouTube (altered or synthetic content), TikTok (AI-generated), X (made with AI), Pinterest (AI-modified disclosure).
workspace_idYesWorkspace id from list_workspaces
youtube_tagsNoYouTube tags, 500 characters in total.
first_commentNoPosted right after the post as its first comment (the usual place for a lead link). Facebook, Instagram, YouTube: a comment. X: a reply (a link in it counts against the X link allowance). LinkedIn company Pages: a comment as the Page. LinkedIn profiles and TikTok: skipped (list_channels first_comment=false).
post_languageNoMastodon: the post's language as an ISO 639-1 code (he, en, es…); omitted = the account's default.
threads_topicNoThreads: the post's topic, 1–50 characters, no period or ampersand (e.g. Recipes).
youtube_titleNoYouTube video title (default: first line of the caption). Overridable per channel.
cover_media_idNoCover image for the post's video: an IMAGE id from upload_media_from_url (not in media_ids). A cover the network refuses never fails the post: the result carries a notice. null removes it. Per network ("Cover at the start of the video" is cover_first_frame): Facebook: The image becomes the thumbnail of the video or Reel. Stories have no cover. Instagram: The image becomes the Reel's cover, in the feed and on the profile. Stories have no cover. YouTube: The image becomes the video's thumbnail. YouTube allows this only for phone-verified channels (youtube.com/verify); without that, the video goes up with a thumbnail YouTube picks. X: X has no cover setting: it shows the video's first frame. To show your cover, turn on "Cover at the start of the video". LinkedIn: The image becomes the video's thumbnail. TikTok: TikTok takes no cover image through its API, only a moment of the video, and applies even that only to public posts (a private post shows the first frame). To always show your cover, private posts included, turn on "Cover at the start of the video". Bluesky: Bluesky has no cover setting: it shows the video's first frame. To show your cover, turn on "Cover at the start of the video". Telegram: The image becomes the video's cover in the channel. Mastodon: The image is shown on the video on Mastodon until it's played. Threads: Threads has no cover setting: it shows the video's first frame. To show your cover, turn on "Cover at the start of the video". Pinterest: The image becomes the video Pin's cover on Pinterest. Google Business Profile: Google Business Profile posts take no video, so there is no cover.
paid_promotionNoPaid promotion or partnership disclosure: YouTube (paid product placement), X (paid partnership). TikTok uses tiktok_commercial.
pinterest_linkNoPinterest: where the Pin leads (https). Omitted: cta_url. Either way it becomes a tracked short link; links in the description aren't clickable on Pinterest.
content_warningNoMastodon: a content warning shown before the post (readers tap to see it; it also hides images). Counts toward the 500 characters.
pinterest_titleNoPinterest: the Pin's title, up to 100 characters (feeds show about the first 40). The caption is the Pin's description (≤800).
stagger_secondsNoDeprecated: [min, max] — use stagger_min_seconds and stagger_max_seconds.
telegram_silentNoTelegram: send without a sound, so channel subscribers get no notification.
tiktok_cover_msNoTikTok: which frame of the video becomes the cover, in milliseconds from the start (TikTok can't take a cover image). TikTok applies it to public posts only; private posts show the first frame. To show your cover image on every TikTok post, public or private, use cover_media_id with cover_first_frame instead. Default: the first frame.
youtube_licenseNoYouTube license (default youtube).
youtube_privacyNoYouTube: public (default), unlisted or private.
x_reply_settingsNoX: who can reply (default everyone): following, mentionedUsers, subscribers or verified.
cover_first_frameNoPut the cover image (cover_media_id) at the start of the video, as its first two frames (~1/15 s), so networks that show the video's first frame show your cover: true = only where the network can't take a cover image (TikTok, X, Bluesky, Stories), "all" = on every network, false = never. The original video isn't changed; Shager publishes a copy. Default when omitted: true if the post has a cover and goes to TikTok, X or Bluesky, else false.
threads_link_cardNoThreads text post (no media): an https link shown as a card under the text (link_attachment). Without it, Threads previews the first link in the text.
pinterest_board_idNoREQUIRED for a Pinterest account (per_channel, since each account has its own boards): the board the Pin goes to, one of that account's pinterest.boards from list_channels.
youtube_embeddableNoYouTube: allow embedding on other sites (default true).
facebook_pin_to_topNoFacebook: pin the post to the top of the Page (feed posts).
instagram_user_tagsNoInstagram single photo: people tagged on it, x/y from 0 to 1 measured from the top-left.
linkedin_visibilityNoLinkedIn: PUBLIC (default), CONNECTIONS or LOGGED_IN.
mastodon_visibilityNoMastodon: who sees the post: public (default), unlisted (quiet public: not in public timelines or hashtags) or private (followers only).
stagger_max_secondsNoLongest pause between two accounts of the same network (default 90).
stagger_min_secondsNoShortest pause between two accounts of the same network (default 30). Facebook and Instagram share one lane.
youtube_category_idNoYouTube category id (default 22, People & Blogs).
youtube_playlist_idNoYouTube: add the video to this playlist of the channel (per_channel, since each channel has its own playlists).
facebook_link_buttonNoFacebook text post: a button (e.g. LEARN_MORE, SHOP_NOW, SIGN_UP, BOOK_NOW) that opens the post's link, usually cta_url. If Facebook refuses the button, the post goes without it.
instagram_trial_reelNoInstagram trial Reel, shown to non-followers first: MANUAL (you share it later from the app) or SS_PERFORMANCE (shared automatically if it performs well).
instagram_location_idNoInstagram: the location shown on the post, as the numeric id of a Facebook Page for that place (Meta's location_id; Shager has no place search yet, so pass an id you already know). Feed photos, carousels and Reels; per_channel to vary it per account.
threads_reply_controlNoThreads: who can reply: everyone (default), accounts_you_follow, mentioned_only or followers_only.
youtube_made_for_kidsNoYouTube's made-for-kids declaration (COPPA). Required explicitly when targeting YouTube; declare whether the video is made for children.
instagram_comments_offNoInstagram: turn comments off right after publishing.
instagram_collaboratorsNoInstagram: up to 3 usernames invited as collaborators (feed, carousel, Reel). The post appears on their profile once they accept in the app.
instagram_share_to_feedNoInstagram Reel: also show it in the profile's main grid (default true).
linkedin_disable_reshareNoLinkedIn: stop others from resharing the post.
youtube_notify_subscribersNoYouTube: notify subscribers about the upload (default true).

Schema Changelog

Changes observed during successful MCP inspections.

  1. Changed17 schema fields changed
    • changedInput schema / properties / cover_media_id / description
      Previous value: -"Cover image for the post's video: an IMAGE id from upload_media_from_url (not in media_ids). A cover the network refuses never fails the post: the result carries a notice. null removes it. Per network (\"Cover at the start of the video\" is cover_first_frame): Facebook: The image becomes the thumbnail of the video or Reel. Stories have no cover. Instagram: The image becomes the Reel's cover, in the feed and on the profile. Stories have no cover. YouTube: The image becomes the video's thumbnail. YouTube allows this only for phone-verified channels (youtube.com/verify); without that, the video goes up with a thumbnail YouTube picks. X: X has no cover setting: it shows the video's first frame. To show your cover, turn on \"Cover at the start of the video\". LinkedIn: The image becomes the video's thumbnail. TikTok: TikTok takes no cover image through its API, only a moment of the video, and applies even that only to public posts (a private post shows the first frame). To always show your cover, private posts included, turn on \"Cover at the start of the video\". Bluesky: Bluesky has no cover setting: it shows the video's first frame. To show your cover, turn on \"Cover at the start of the video\". Telegram: The image becomes the video's cover in the channel. Mastodon: The image is shown on the video on Mastodon until it's played. Threads: Threads has no cover setting: it shows the video's first frame. To show your cover, turn on \"Cover at the start of the video\". Pinterest: The image becomes the video Pin's cover on Pinterest."New value: +"Cover image for the post's video: an IMAGE id from upload_media_from_url (not in media_ids). A cover the network refuses never fails the post: the result carries a notice. null removes it. Per network (\"Cover at the start of the video\" is cover_first_frame): Facebook: The image becomes the thumbnail of the video or Reel. Stories have no cover. Instagram: The image becomes the Reel's cover, in the feed and on the profile. Stories have no cover. YouTube: The image becomes the video's thumbnail. YouTube allows this only for phone-verified channels (youtube.com/verify); without that, the video goes up with a thumbnail YouTube picks. X: X has no cover setting: it shows the video's first frame. To show your cover, turn on \"Cover at the start of the video\". LinkedIn: The image becomes the video's thumbnail. TikTok: TikTok takes no cover image through its API, only a moment of the video, and applies even that only to public posts (a private post shows the first frame). To always show your cover, private posts included, turn on \"Cover at the start of the video\". Bluesky: Bluesky has no cover setting: it shows the video's first frame. To show your cover, turn on \"Cover at the start of the video\". Telegram: The image becomes the video's cover in the channel. Mastodon: The image is shown on the video on Mastodon until it's played. Threads: Threads has no cover setting: it shows the video's first frame. To show your cover, turn on \"Cover at the start of the video\". Pinterest: The image becomes the video Pin's cover on Pinterest. Google Business Profile: Google Business Profile posts take no video, so there is no cover."
    • addedInput schema / properties / gbp_button
      Added value: +{
      +  "description": "Google Business Profile update or event: the button. CALL dials the profile's phone number (no link); the others lead to gbp_link or cta_url. Omitted with a link: LEARN_MORE. Offers get Google's own button instead.",
      +  "enum": [
      +    "LEARN_MORE",
      +    "BOOK",
      +    "ORDER",
      +    "SHOP",
      +    "SIGN_UP",
      +    "CALL"
      +  ],
      +  "type": "string"
      +}
    • addedInput schema / properties / gbp_coupon
      Added value: +{
      +  "description": "Google Business Profile offer: a coupon code (optional).",
      +  "maxLength": 58,
      +  "type": "string"
      +}
    • addedInput schema / properties / gbp_end
      Added value: +{
      +  "description": "Google Business Profile event or offer: when it ends (not before gbp_start), same format.",
      +  "pattern": "^\\d{4}-\\d{2}-\\d{2}(T\\d{2}:\\d{2})?$",
      +  "type": "string"
      +}
    • addedInput schema / properties / gbp_link
      Added value: +{
      +  "description": "Google Business Profile: where the button (or an offer's \"redeem online\") leads, https. Omitted: cta_url. Either way it becomes a tracked short link; links in the post's text aren't clickable on Google.",
      +  "maxLength": 2048,
      +  "type": "string"
      +}
    • addedInput schema / properties / gbp_start
      Added value: +{
      +  "description": "Google Business Profile event or offer: when it starts, in the location's own time, \"2026-11-05\" (the whole day) or \"2026-11-05T18:30\". No time zone.",
      +  "pattern": "^\\d{4}-\\d{2}-\\d{2}(T\\d{2}:\\d{2})?$",
      +  "type": "string"
      +}
    • addedInput schema / properties / gbp_terms
      Added value: +{
      +  "description": "Google Business Profile offer: terms and conditions (optional).",
      +  "maxLength": 1500,
      +  "type": "string"
      +}
    • addedInput schema / properties / gbp_title
      Added value: +{
      +  "description": "Google Business Profile event or offer: its title (required there, up to 58 characters).",
      +  "maxLength": 58,
      +  "type": "string"
      +}
    • addedInput schema / properties / gbp_type
      Added value: +{
      +  "description": "Google Business Profile: STANDARD (an update, the default), EVENT or OFFER. Events and offers need gbp_title, gbp_start and gbp_end.",
      +  "enum": [
      +    "STANDARD",
      +    "EVENT",
      +    "OFFER"
      +  ],
      +  "type": "string"
      +}
    • addedInput schema / properties / per_channel / items / properties / gbp_button
      Added value: +{
      +  "description": "Google Business Profile update or event: the button. CALL dials the profile's phone number (no link); the others lead to gbp_link or cta_url. Omitted with a link: LEARN_MORE. Offers get Google's own button instead.",
      +  "enum": [
      +    "LEARN_MORE",
      +    "BOOK",
      +    "ORDER",
      +    "SHOP",
      +    "SIGN_UP",
      +    "CALL"
      +  ],
      +  "type": "string"
      +}
    • addedInput schema / properties / per_channel / items / properties / gbp_coupon
      Added value: +{
      +  "description": "Google Business Profile offer: a coupon code (optional).",
      +  "maxLength": 58,
      +  "type": "string"
      +}
    • addedInput schema / properties / per_channel / items / properties / gbp_end
      Added value: +{
      +  "description": "Google Business Profile event or offer: when it ends (not before gbp_start), same format.",
      +  "pattern": "^\\d{4}-\\d{2}-\\d{2}(T\\d{2}:\\d{2})?$",
      +  "type": "string"
      +}
    • addedInput schema / properties / per_channel / items / properties / gbp_link
      Added value: +{
      +  "description": "Google Business Profile: where the button (or an offer's \"redeem online\") leads, https. Omitted: cta_url. Either way it becomes a tracked short link; links in the post's text aren't clickable on Google.",
      +  "maxLength": 2048,
      +  "type": "string"
      +}
    • addedInput schema / properties / per_channel / items / properties / gbp_start
      Added value: +{
      +  "description": "Google Business Profile event or offer: when it starts, in the location's own time, \"2026-11-05\" (the whole day) or \"2026-11-05T18:30\". No time zone.",
      +  "pattern": "^\\d{4}-\\d{2}-\\d{2}(T\\d{2}:\\d{2})?$",
      +  "type": "string"
      +}
    • addedInput schema / properties / per_channel / items / properties / gbp_terms
      Added value: +{
      +  "description": "Google Business Profile offer: terms and conditions (optional).",
      +  "maxLength": 1500,
      +  "type": "string"
      +}
    • addedInput schema / properties / per_channel / items / properties / gbp_title
      Added value: +{
      +  "description": "Google Business Profile event or offer: its title (required there, up to 58 characters).",
      +  "maxLength": 58,
      +  "type": "string"
      +}
    • addedInput schema / properties / per_channel / items / properties / gbp_type
      Added value: +{
      +  "description": "Google Business Profile: STANDARD (an update, the default), EVENT or OFFER. Events and offers need gbp_title, gbp_start and gbp_end.",
      +  "enum": [
      +    "STANDARD",
      +    "EVENT",
      +    "OFFER"
      +  ],
      +  "type": "string"
      +}
  2. Changed9 schema fields changed
    • changedInput schema / properties / ai_generated / description
      Previous value: -"Made or meaningfully altered with AI. Sent as each network's AI label: Instagram, Facebook Reels, YouTube (altered or synthetic content), TikTok (AI-generated), X (made with AI)."New value: +"Made or meaningfully altered with AI. Sent as each network's AI label: Instagram, Facebook Reels, YouTube (altered or synthetic content), TikTok (AI-generated), X (made with AI), Pinterest (AI-modified disclosure)."
    • changedInput schema / properties / cover_media_id / description
      Previous value: -"Cover image for the post's video: an IMAGE id from upload_media_from_url (not in media_ids). A cover the network refuses never fails the post: the result carries a notice. null removes it. Per network (\"Cover at the start of the video\" is cover_first_frame): Facebook: The image becomes the thumbnail of the video or Reel. Stories have no cover. Instagram: The image becomes the Reel's cover, in the feed and on the profile. Stories have no cover. YouTube: The image becomes the video's thumbnail. YouTube allows this only for phone-verified channels (youtube.com/verify); without that, the video goes up with a thumbnail YouTube picks. X: X has no cover setting: it shows the video's first frame. To show your cover, turn on \"Cover at the start of the video\". LinkedIn: The image becomes the video's thumbnail. TikTok: TikTok takes no cover image through its API, only a moment of the video, and applies even that only to public posts (a private post shows the first frame). To always show your cover, private posts included, turn on \"Cover at the start of the video\". Bluesky: Bluesky has no cover setting: it shows the video's first frame. To show your cover, turn on \"Cover at the start of the video\". Telegram: The image becomes the video's cover in the channel. Mastodon: The image is shown on the video on Mastodon until it's played. Threads: Threads has no cover setting: it shows the video's first frame. To show your cover, turn on \"Cover at the start of the video\"."New value: +"Cover image for the post's video: an IMAGE id from upload_media_from_url (not in media_ids). A cover the network refuses never fails the post: the result carries a notice. null removes it. Per network (\"Cover at the start of the video\" is cover_first_frame): Facebook: The image becomes the thumbnail of the video or Reel. Stories have no cover. Instagram: The image becomes the Reel's cover, in the feed and on the profile. Stories have no cover. YouTube: The image becomes the video's thumbnail. YouTube allows this only for phone-verified channels (youtube.com/verify); without that, the video goes up with a thumbnail YouTube picks. X: X has no cover setting: it shows the video's first frame. To show your cover, turn on \"Cover at the start of the video\". LinkedIn: The image becomes the video's thumbnail. TikTok: TikTok takes no cover image through its API, only a moment of the video, and applies even that only to public posts (a private post shows the first frame). To always show your cover, private posts included, turn on \"Cover at the start of the video\". Bluesky: Bluesky has no cover setting: it shows the video's first frame. To show your cover, turn on \"Cover at the start of the video\". Telegram: The image becomes the video's cover in the channel. Mastodon: The image is shown on the video on Mastodon until it's played. Threads: Threads has no cover setting: it shows the video's first frame. To show your cover, turn on \"Cover at the start of the video\". Pinterest: The image becomes the video Pin's cover on Pinterest."
    • changedInput schema / properties / per_channel / items / properties / ai_generated / description
      Previous value: -"Made or meaningfully altered with AI. Sent as each network's AI label: Instagram, Facebook Reels, YouTube (altered or synthetic content), TikTok (AI-generated), X (made with AI)."New value: +"Made or meaningfully altered with AI. Sent as each network's AI label: Instagram, Facebook Reels, YouTube (altered or synthetic content), TikTok (AI-generated), X (made with AI), Pinterest (AI-modified disclosure)."
    • addedInput schema / properties / per_channel / items / properties / pinterest_board_id
      Added value: +{
      +  "description": "REQUIRED for a Pinterest account (per_channel, since each account has its own boards): the board the Pin goes to, one of that account's pinterest.boards from list_channels.",
      +  "pattern": "^\\d{1,30}$",
      +  "type": "string"
      +}
    • addedInput schema / properties / per_channel / items / properties / pinterest_link
      Added value: +{
      +  "description": "Pinterest: where the Pin leads (https). Omitted: cta_url. Either way it becomes a tracked short link; links in the description aren't clickable on Pinterest.",
      +  "maxLength": 2048,
      +  "type": "string"
      +}
    • addedInput schema / properties / per_channel / items / properties / pinterest_title
      Added value: +{
      +  "description": "Pinterest: the Pin's title, up to 100 characters (feeds show about the first 40). The caption is the Pin's description (≤800).",
      +  "maxLength": 100,
      +  "type": "string"
      +}
    • addedInput schema / properties / pinterest_board_id
      Added value: +{
      +  "description": "REQUIRED for a Pinterest account (per_channel, since each account has its own boards): the board the Pin goes to, one of that account's pinterest.boards from list_channels.",
      +  "pattern": "^\\d{1,30}$",
      +  "type": "string"
      +}
    • addedInput schema / properties / pinterest_link
      Added value: +{
      +  "description": "Pinterest: where the Pin leads (https). Omitted: cta_url. Either way it becomes a tracked short link; links in the description aren't clickable on Pinterest.",
      +  "maxLength": 2048,
      +  "type": "string"
      +}
    • addedInput schema / properties / pinterest_title
      Added value: +{
      +  "description": "Pinterest: the Pin's title, up to 100 characters (feeds show about the first 40). The caption is the Pin's description (≤800).",
      +  "maxLength": 100,
      +  "type": "string"
      +}
  3. Changed23 schema fields changed
    • changedInput schema / properties / alt_texts / description
      Previous value: -"Image descriptions for screen readers, by media id (images in media_ids). Sent to Instagram, Facebook, X, LinkedIn and Bluesky. Stored on the image, so later posts reuse it; an empty string clears it."New value: +"Image descriptions for screen readers, by media id (images in media_ids). Sent to Instagram, Facebook, X, LinkedIn, Bluesky and Mastodon. Stored on the image, so later posts reuse it; an empty string clears it."
    • changedInput schema / properties / caption / description
      Previous value: -"Master text. Goes to every account unless per_channel overrides it. X: 280 characters; Bluesky: 300; Instagram and TikTok: 2,200; Telegram: 4,096 for text, 1,024 under photos, a video or a file."New value: +"Master text. Goes to every account unless per_channel overrides it. X: 280 characters; Bluesky: 300; Mastodon: 500 (links count as 23); Instagram and TikTok: 2,200; Telegram: 4,096 for text, 1,024 under photos, a video or a file."
    • addedInput schema / properties / content_warning
      Added value: +{
      +  "description": "Mastodon: a content warning shown before the post (readers tap to see it; it also hides images). Counts toward the 500 characters.",
      +  "maxLength": 500,
      +  "type": "string"
      +}
    • changedInput schema / properties / cover_media_id / description
      Previous value: -"Cover image for the post's video: an IMAGE id from upload_media_from_url (not in media_ids). A cover the network refuses never fails the post: the result carries a notice. null removes it. Per network (\"Cover at the start of the video\" is cover_first_frame): Facebook: The image becomes the thumbnail of the video or Reel. Stories have no cover. Instagram: The image becomes the Reel's cover, in the feed and on the profile. Stories have no cover. YouTube: The image becomes the video's thumbnail. YouTube allows this only for phone-verified channels (youtube.com/verify); without that, the video goes up with a thumbnail YouTube picks. X: X has no cover setting: it shows the video's first frame. To show your cover, turn on \"Cover at the start of the video\". LinkedIn: The image becomes the video's thumbnail. TikTok: TikTok takes no cover image through its API, only a moment of the video, and applies even that only to public posts (a private post shows the first frame). To always show your cover, private posts included, turn on \"Cover at the start of the video\". Bluesky: Bluesky has no cover setting: it shows the video's first frame. To show your cover, turn on \"Cover at the start of the video\". Telegram: The image becomes the video's cover in the channel."New value: +"Cover image for the post's video: an IMAGE id from upload_media_from_url (not in media_ids). A cover the network refuses never fails the post: the result carries a notice. null removes it. Per network (\"Cover at the start of the video\" is cover_first_frame): Facebook: The image becomes the thumbnail of the video or Reel. Stories have no cover. Instagram: The image becomes the Reel's cover, in the feed and on the profile. Stories have no cover. YouTube: The image becomes the video's thumbnail. YouTube allows this only for phone-verified channels (youtube.com/verify); without that, the video goes up with a thumbnail YouTube picks. X: X has no cover setting: it shows the video's first frame. To show your cover, turn on \"Cover at the start of the video\". LinkedIn: The image becomes the video's thumbnail. TikTok: TikTok takes no cover image through its API, only a moment of the video, and applies even that only to public posts (a private post shows the first frame). To always show your cover, private posts included, turn on \"Cover at the start of the video\". Bluesky: Bluesky has no cover setting: it shows the video's first frame. To show your cover, turn on \"Cover at the start of the video\". Telegram: The image becomes the video's cover in the channel. Mastodon: The image is shown on the video on Mastodon until it's played. Threads: Threads has no cover setting: it shows the video's first frame. To show your cover, turn on \"Cover at the start of the video\"."
    • addedInput schema / properties / mastodon_visibility
      Added value: +{
      +  "description": "Mastodon: who sees the post: public (default), unlisted (quiet public: not in public timelines or hashtags) or private (followers only).",
      +  "enum": [
      +    "public",
      +    "unlisted",
      +    "private"
      +  ],
      +  "type": "string"
      +}
    • addedInput schema / properties / per_channel / items / properties / content_warning
      Added value: +{
      +  "description": "Mastodon: a content warning shown before the post (readers tap to see it; it also hides images). Counts toward the 500 characters.",
      +  "maxLength": 500,
      +  "type": "string"
      +}
    • addedInput schema / properties / per_channel / items / properties / mastodon_visibility
      Added value: +{
      +  "description": "Mastodon: who sees the post: public (default), unlisted (quiet public: not in public timelines or hashtags) or private (followers only).",
      +  "enum": [
      +    "public",
      +    "unlisted",
      +    "private"
      +  ],
      +  "type": "string"
      +}
    • changedInput schema / properties / per_channel / items / properties / poll / description
      Previous value: -"A poll on X and LinkedIn. A poll post carries no media."New value: +"A poll on X, LinkedIn and Mastodon. A poll post carries no media."
    • changedInput schema / properties / per_channel / items / properties / poll / properties / options / description
      Previous value: -"2–4 answers: X ≤25 characters each, LinkedIn ≤30."New value: +"2–4 answers: X ≤25 characters each, LinkedIn ≤30, Mastodon ≤50."
    • changedInput schema / properties / per_channel / items / properties / poll / properties / options / items / maxLength
      Previous value: -30New value: +50
    • changedInput schema / properties / per_channel / items / properties / poll / properties / question / description
      Previous value: -"LinkedIn only (required there, ≤140). On X the post text is the question."New value: +"LinkedIn only (required there, ≤140). On X and Mastodon the post text is the question."
    • addedInput schema / properties / per_channel / items / properties / post_language
      Added value: +{
      +  "description": "Mastodon: the post's language as an ISO 639-1 code (he, en, es…); omitted = the account's default.",
      +  "pattern": "^[a-z]{2,3}$",
      +  "type": "string"
      +}
    • addedInput schema / properties / per_channel / items / properties / threads_link_card
      Added value: +{
      +  "description": "Threads text post (no media): an https link shown as a card under the text (link_attachment). Without it, Threads previews the first link in the text.",
      +  "maxLength": 2048,
      +  "type": "string"
      +}
    • addedInput schema / properties / per_channel / items / properties / threads_reply_control
      Added value: +{
      +  "description": "Threads: who can reply: everyone (default), accounts_you_follow, mentioned_only or followers_only.",
      +  "enum": [
      +    "everyone",
      +    "accounts_you_follow",
      +    "mentioned_only",
      +    "followers_only"
      +  ],
      +  "type": "string"
      +}
    • addedInput schema / properties / per_channel / items / properties / threads_topic
      Added value: +{
      +  "description": "Threads: the post's topic, 1–50 characters, no period or ampersand (e.g. Recipes).",
      +  "maxLength": 50,
      +  "type": "string"
      +}
    • changedInput schema / properties / poll / description
      Previous value: -"A poll on X and LinkedIn. A poll post carries no media."New value: +"A poll on X, LinkedIn and Mastodon. A poll post carries no media."
    • changedInput schema / properties / poll / properties / options / description
      Previous value: -"2–4 answers: X ≤25 characters each, LinkedIn ≤30."New value: +"2–4 answers: X ≤25 characters each, LinkedIn ≤30, Mastodon ≤50."
    • changedInput schema / properties / poll / properties / options / items / maxLength
      Previous value: -30New value: +50
    • changedInput schema / properties / poll / properties / question / description
      Previous value: -"LinkedIn only (required there, ≤140). On X the post text is the question."New value: +"LinkedIn only (required there, ≤140). On X and Mastodon the post text is the question."
    • addedInput schema / properties / post_language
      Added value: +{
      +  "description": "Mastodon: the post's language as an ISO 639-1 code (he, en, es…); omitted = the account's default.",
      +  "pattern": "^[a-z]{2,3}$",
      +  "type": "string"
      +}
    • addedInput schema / properties / threads_link_card
      Added value: +{
      +  "description": "Threads text post (no media): an https link shown as a card under the text (link_attachment). Without it, Threads previews the first link in the text.",
      +  "maxLength": 2048,
      +  "type": "string"
      +}
    • addedInput schema / properties / threads_reply_control
      Added value: +{
      +  "description": "Threads: who can reply: everyone (default), accounts_you_follow, mentioned_only or followers_only.",
      +  "enum": [
      +    "everyone",
      +    "accounts_you_follow",
      +    "mentioned_only",
      +    "followers_only"
      +  ],
      +  "type": "string"
      +}
    • addedInput schema / properties / threads_topic
      Added value: +{
      +  "description": "Threads: the post's topic, 1–50 characters, no period or ampersand (e.g. Recipes).",
      +  "maxLength": 50,
      +  "type": "string"
      +}
  4. Changed4 schema fields changed
    • changedInput schema / properties / caption / description
      Previous value: -"Master text. Goes to every account unless per_channel overrides it. X: 280 characters; Bluesky: 300; Instagram and TikTok: 2,200."New value: +"Master text. Goes to every account unless per_channel overrides it. X: 280 characters; Bluesky: 300; Instagram and TikTok: 2,200; Telegram: 4,096 for text, 1,024 under photos, a video or a file."
    • changedInput schema / properties / cover_media_id / description
      Previous value: -"Cover image for the post's video: an IMAGE id from upload_media_from_url (not in media_ids). A cover the network refuses never fails the post: the result carries a notice. null removes it. Per network (\"Cover at the start of the video\" is cover_first_frame): Facebook: The image becomes the thumbnail of the video or Reel. Stories have no cover. Instagram: The image becomes the Reel's cover, in the feed and on the profile. Stories have no cover. YouTube: The image becomes the video's thumbnail. YouTube allows this only for phone-verified channels (youtube.com/verify); without that, the video goes up with a thumbnail YouTube picks. X: X has no cover setting: it shows the video's first frame. To show your cover, turn on \"Cover at the start of the video\". LinkedIn: The image becomes the video's thumbnail. TikTok: TikTok takes no cover image through its API, only a moment of the video, and applies even that only to public posts (a private post shows the first frame). To always show your cover, private posts included, turn on \"Cover at the start of the video\". Bluesky: Bluesky has no cover setting: it shows the video's first frame. To show your cover, turn on \"Cover at the start of the video\"."New value: +"Cover image for the post's video: an IMAGE id from upload_media_from_url (not in media_ids). A cover the network refuses never fails the post: the result carries a notice. null removes it. Per network (\"Cover at the start of the video\" is cover_first_frame): Facebook: The image becomes the thumbnail of the video or Reel. Stories have no cover. Instagram: The image becomes the Reel's cover, in the feed and on the profile. Stories have no cover. YouTube: The image becomes the video's thumbnail. YouTube allows this only for phone-verified channels (youtube.com/verify); without that, the video goes up with a thumbnail YouTube picks. X: X has no cover setting: it shows the video's first frame. To show your cover, turn on \"Cover at the start of the video\". LinkedIn: The image becomes the video's thumbnail. TikTok: TikTok takes no cover image through its API, only a moment of the video, and applies even that only to public posts (a private post shows the first frame). To always show your cover, private posts included, turn on \"Cover at the start of the video\". Bluesky: Bluesky has no cover setting: it shows the video's first frame. To show your cover, turn on \"Cover at the start of the video\". Telegram: The image becomes the video's cover in the channel."
    • addedInput schema / properties / per_channel / items / properties / telegram_silent
      Added value: +{
      +  "description": "Telegram: send without a sound, so channel subscribers get no notification.",
      +  "type": "boolean"
      +}
    • addedInput schema / properties / telegram_silent
      Added value: +{
      +  "description": "Telegram: send without a sound, so channel subscribers get no notification.",
      +  "type": "boolean"
      +}
  5. Changed4 schema fields changed
    • changedInput schema / properties / alt_texts / description
      Previous value: -"Image descriptions for screen readers, by media id (images in media_ids). Sent to Instagram, Facebook, X and LinkedIn. Stored on the image, so later posts reuse it; an empty string clears it."New value: +"Image descriptions for screen readers, by media id (images in media_ids). Sent to Instagram, Facebook, X, LinkedIn and Bluesky. Stored on the image, so later posts reuse it; an empty string clears it."
    • changedInput schema / properties / caption / description
      Previous value: -"Master text. Goes to every account unless per_channel overrides it. X: 280 characters; Instagram and TikTok: 2,200."New value: +"Master text. Goes to every account unless per_channel overrides it. X: 280 characters; Bluesky: 300; Instagram and TikTok: 2,200."
    • changedInput schema / properties / cover_first_frame / description
      Previous value: -"Put the cover image (cover_media_id) at the start of the video, as its first two frames (~1/15 s), so networks that show the video's first frame show your cover: true = only where the network can't take a cover image (TikTok, X, Stories), \"all\" = on every network, false = never. The original video isn't changed; Shager publishes a copy. Default when omitted: true if the post has a cover and goes to TikTok or X, else false."New value: +"Put the cover image (cover_media_id) at the start of the video, as its first two frames (~1/15 s), so networks that show the video's first frame show your cover: true = only where the network can't take a cover image (TikTok, X, Bluesky, Stories), \"all\" = on every network, false = never. The original video isn't changed; Shager publishes a copy. Default when omitted: true if the post has a cover and goes to TikTok, X or Bluesky, else false."
    • changedInput schema / properties / cover_media_id / description
      Previous value: -"Cover image for the post's video: an IMAGE id from upload_media_from_url (not in media_ids). A cover the network refuses never fails the post: the result carries a notice. null removes it. Per network (\"Cover at the start of the video\" is cover_first_frame): Facebook: The image becomes the thumbnail of the video or Reel. Stories have no cover. Instagram: The image becomes the Reel's cover, in the feed and on the profile. Stories have no cover. YouTube: The image becomes the video's thumbnail. YouTube allows this only for phone-verified channels (youtube.com/verify); without that, the video goes up with a thumbnail YouTube picks. X: X has no cover setting: it shows the video's first frame. To show your cover, turn on \"Cover at the start of the video\". LinkedIn: The image becomes the video's thumbnail. TikTok: TikTok takes no cover image through its API, only a moment of the video, and applies even that only to public posts (a private post shows the first frame). To always show your cover, private posts included, turn on \"Cover at the start of the video\"."New value: +"Cover image for the post's video: an IMAGE id from upload_media_from_url (not in media_ids). A cover the network refuses never fails the post: the result carries a notice. null removes it. Per network (\"Cover at the start of the video\" is cover_first_frame): Facebook: The image becomes the thumbnail of the video or Reel. Stories have no cover. Instagram: The image becomes the Reel's cover, in the feed and on the profile. Stories have no cover. YouTube: The image becomes the video's thumbnail. YouTube allows this only for phone-verified channels (youtube.com/verify); without that, the video goes up with a thumbnail YouTube picks. X: X has no cover setting: it shows the video's first frame. To show your cover, turn on \"Cover at the start of the video\". LinkedIn: The image becomes the video's thumbnail. TikTok: TikTok takes no cover image through its API, only a moment of the video, and applies even that only to public posts (a private post shows the first frame). To always show your cover, private posts included, turn on \"Cover at the start of the video\". Bluesky: Bluesky has no cover setting: it shows the video's first frame. To show your cover, turn on \"Cover at the start of the video\"."
  6. Changed1 schema field changed
    • addedInput schema / properties / per_channel / items / properties / delay_minutes
      Added value: +{
      +  "description": "A different time for this account: it gets the post this many minutes after schedule_at (0 = with the post, up to 7 days). For 'Instagram at 9:00, LinkedIn at 12:00', schedule_at is 9:00 and LinkedIn has 180. Give every account of a network the same value to move the whole network. Still one post: one approval, one entry in the plan's monthly count.",
      +  "maximum": 10080,
      +  "minimum": 0,
      +  "type": "integer"
      +}
  7. First observed

TDQS

A4.5/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnly/idempotent/non-destructive, and the description still adds substantive context: nothing is persisted, ALL issues are returned at once rather than failing on the first, the tool never approves anything itself, it needs only the read scope, and it works in a sandbox. The schedule_at paragraph discloses extra outputs (X link allowance, key caps, confirmation need, dispatch plan) that no structured field conveys.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded with the core behavior and free of filler. The long parenthetical listing the checked rule categories is awkwardly spliced into the middle of the next_step sentence, forcing a re-read, but the overall length is justified for a tool this complex.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a 65-parameter tool with no output schema, the description carries the essential return semantics: an issue list, a next_step string, and a confirmation/dispatch plan when schedule_at is given. It does not describe the shape of individual issue objects or how post_id/draft_id affect the check beyond what per-parameter schema text already says.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

With 98% schema coverage the schema already documents nearly all 65 params, so the baseline is met. The description adds real value on top by explaining what schedule_at unlocks (allowances, caps, confirmation_token, dispatch plan) and by naming the validation categories (caption length, format support, media requirements, video shape, TikTok privacy/commercial, disconnected accounts), which tells the agent which inputs the check exercises.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource ('Checks a post against every account's network rules') and pins down the dry-run nature immediately ('without saving anything'). It also names the sibling it complements ('Run it before create_post'), so an agent can distinguish it from create_post/schedule_post without reading a schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Gives a clear usage rule ('Run it before create_post when in doubt') and an explicit follow-up protocol via next_step (fix issues, request confirmation, or create). It does not cover when not to bother calling it (e.g. trivial single-account posts) or alternatives beyond create_post, so it falls short of a full 5.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Try in Browser

Glama MCP Gateway

Add one secure layer between your agents and this server.

Resources