Skip to main content
Glama

Validate Post

validate_post
Read-only

Dry-run create_post: takes exactly the same input and runs every check creating the post would run, plus the ones that usually fail only at publish time (TikTok creator limits, YouTube upload quota, expired tokens, Pinterest boards, X credits), without creating a post, uploading media or writing anything. Nothing is published, so no confirmation is needed. Always returns a report: valid is false when any issue has severity ERROR. Issues tied to one account are listed under that account in accounts (status READY, WARNING or BLOCKED); the rest are in issues, each with a stable code, severity and message. outcome says what creating the post would do (DRAFT, SCHEDULED or PUBLISH_NOW), and requiredPermissions names the permissions it would need; a missing one shows up as a PERMISSION_DENIED issue. notChecked lists what only the platform decides at publish time, so valid: true is not a guarantee. Fix every ERROR, then call create_post with the same input. Needs the posts.draft permission.

Input Schema

NameRequiredDescriptionDefault
textNoDefault post text shared across platforms. On LinkedIn, mention a company page with @[Page Name](urn:li:organization:<id>), using the page name exactly as LinkedIn shows it
pageIdsNoFacebook page account ids from list_accounts (the account id; its pageId value is also accepted). Facebook has no connection-id array
timezoneNoIANA timezone stored with the post for display (e.g. "America/New_York"); it does not shift scheduledAtUTC
mediaUrlsNoMedia URLs to attach. Use publicUrl values from upload_media or get_upload_urls; the same publicUrl may be reused across posts. Do not reuse mediaUrls read back from a published post, which may be expiring platform links
platformsNoTarget platforms (e.g. ["LINKEDIN", "TWITTER"]). Each one needs its matching connection-id array (pageIds for FACEBOOK, googleBusinessConnectionIds for GOOGLE_BUSINESS) filled with ids from list_accounts. Optional when accountIds is sent
accountIdsNoAccount ids (or Facebook pageIds) from list_accounts to post to. AdaptlyPost adds each account's platform to platforms and its id to the matching connection-id array, so you can send these instead of platforms and the per-platform arrays, or alongside them. An id not connected to the workspace fails with 400
recurrenceNoRepeat the post on a schedule the user asked for, and confirm that schedule with them first. Needs a future scheduledAt, which becomes the first post and sets the time of day in timezone. Cannot be combined with saveAsDraft or TIKTOK. Missed slots, for example while paused, are skipped and never published late. The user can pause or stop the series at any time with pause_recurring_post or delete_recurring_post; list_recurring_posts shows it
contentTypeYesContent type: TEXT, IMAGE, VIDEO, CAROUSEL, or DOCUMENT. Must match mediaUrls. CAROUSEL takes 2 or more items in mediaUrls, up to 4 on TWITTER, BLUESKY and MASTODON, 5 on PINTEREST, 10 on INSTAGRAM, FACEBOOK, THREADS and TELEGRAM, 20 on LINKEDIN, 30 on TUMBLR and 35 on TIKTOK (JPEG or WebP photos only). INSTAGRAM and THREADS accept images and videos in the same carousel; every other platform takes images only, and YOUTUBE has no carousel. GOOGLE_BUSINESS takes TEXT or IMAGE with one JPEG or PNG image up to 5 MB, and text up to 1500 characters. TELEGRAM takes text up to 4096 characters, up to 10 images (JPEG, PNG or WebP, 10 MB each) or one MP4 or MOV video (50 MB); a caption over 1024 characters goes out as its own message after the media. DOCUMENT publishes one PDF, PPT, PPTX, DOC or DOCX file (max 100 MB, 300 pages) as a LinkedIn document post and is LinkedIn only: put exactly that one file in mediaUrls, target only LINKEDIN, and set the title with linkedinConfigs
saveAsDraftNoSave as DRAFT instead of publishing or scheduling; publish later with publish_draft. A scheduledAt sent with it is stored as the draft's planned date, and publish_draft schedules the draft for that date
scheduledAtNoAbsolute ISO 8601 instant to schedule (e.g. "2026-03-15T10:00:00Z"). Omit to post immediately; a past time also posts immediately. With saveAsDraft it is the draft's planned date instead: nothing is scheduled, and publish_draft without a date later schedules the draft for it
workspaceIdNoWorkspace to act in: an id from list_workspaces. Omit to act in the workspace list_workspaces marks current. Use the same workspaceId for every call about the same workspace, since ids from one workspace (accounts, posts, uploads) do not exist in another
thumbnailUrlNoVideo thumbnail as an https URL or an image data URI, used on every platform that takes one unless platformThumbnails overrides it
firstCommentsNoA comment posted from the same account right after the post goes out on a platform, one entry per platform. Available on LINKEDIN (1250 characters), TWITTER (280, as a reply), THREADS (500, as a reply), BLUESKY (300, as a reply), MASTODON (500, as a reply), YOUTUBE (10000), FACEBOOK (8000) and INSTAGRAM (2200). A platform the post does not go to, an empty or too long comment, a second entry for one platform, a platform that also has threadReplies, a private or made for kids YouTube video and Facebook or Instagram stories are refused with 400. The comment is posted about 10 seconds after the platform publishes (60 on YouTube) and retried up to 3 times; on X it is not retried, and a lifetime-deal account pays for it like any X post. A Facebook Page or Instagram account connected before first comments came to them must be reconnected once; until then its comment fails and firstCommentError says to reconnect. A comment that fails never fails the post: read firstCommentStatus and firstCommentError on the platform in get_post. On an update, sending the array replaces every first comment and omitting it keeps the stored ones
mediaAltTextsNoAlt text per image, in the same order as mediaUrls (max 1000 characters each; use "" to skip an image). Sent to X, Bluesky, Mastodon, Tumblr, LinkedIn, Facebook, Instagram and Threads; Pinterest uses the first one (cut to 500). TikTok, YouTube, Google Business Profile and videos ignore it
platformTextsNoPer-platform caption overrides
threadRepliesNoTurns the post into a thread: the post opens it and each text is published as a reply to the one before, from the same account, one entry per platform. Available on TWITTER (280 characters per reply, or the account's own limit on X Premium), THREADS (500), BLUESKY (300) and MASTODON (500, or the instance's limit), up to 25 replies. A platform the post does not go to, an empty or too long reply, more than 25 replies, a second entry for one platform and a platform that also has a first comment are refused with 400. An entry with an empty texts array removes the thread. Replies start about 10 seconds after the platform publishes, 2 seconds apart. A failed run is retried up to 3 times and resumes after the last reply that went out; on X it is not retried, and a lifetime-deal account pays for each reply like any X post. A thread that fails never fails the post: read threadStatus, threadReplyIds and threadError on the platform in get_post. On an update, sending the array replaces every thread and omitting it keeps the stored ones
tiktokConfigsNoTikTok per-connection config. Each entry needs connectionId + privacyLevel at minimum
tumblrConfigsNoTumblr per-blog config: tags, a title and a source link. connectionId must match an entry in tumblrConnectionIds
youtubeConfigsNoYouTube per-connection config with video title, tags, privacy, etc.
facebookConfigsNoFacebook per-page config: post type, shareToStory, video title, text background, and a grid or link carousel for several images. pageId must match an entry in pageIds
linkedinConfigsNoLinkedIn per-connection config: documentTitle names a DOCUMENT post, reshareOf reshares another LinkedIn post with the text as commentary. connectionId must match an entry in linkedinConnectionIds
instagramConfigsNoInstagram per-connection config: post type (FEED, REEL, STORY), shareToStory, collaborators, user tags, the AI info label, Reel audio, and trial reels
pinterestConfigsNoPinterest per-connection config: the board to pin to, a pin title and a destination link. Get boardId from list_pinterest_boards for that connectionId, or pass the board name as boardName instead (not both); a board belongs to one Pinterest account. A boardName that matches no board or several fails with 400. Without either the pin goes to a board named AdaptlyPost
twitterKeepLinksNoKeep links in the X text clickable. X charges over 10 times more for a post with a link, so by default every link in X text is broken (example.com becomes example. com). Only AppSumo lifetime deals can turn this on, and a kept link costs 20 credits instead of 2. true on any other plan is refused with 400; it is ignored when the X text has no link
platformThumbnailsNoA different thumbnail for one platform of a VIDEO post, one entry per platform. A platform without an entry uses the post-level thumbnailUrl and thumbnailTimestampMs. YOUTUBE, FACEBOOK, LINKEDIN and TELEGRAM take an image (thumbnailUrl); INSTAGRAM and PINTEREST take an image or a frame (thumbnailTimestampMs), the image winning when both are sent; TIKTOK takes only a frame. Posts that are not videos, Facebook and Instagram stories, a second entry for one platform and an entry the platform cannot use are refused with 400. On an update, sending the array replaces every override and omitting it keeps the stored ones
tiktokConnectionIdsNoTikTok account connection IDs to post from
tumblrConnectionIdsNoTumblr blogs to post to, by the account id from list_accounts. TUMBLR takes text up to 32768 characters, up to 30 JPEG, PNG or GIF images (10 MB each) or one MP4 video (500 MB, up to 10 minutes), and alt text; it has no first comments, threads, per-platform thumbnails, image shapes or analytics
blueskyConnectionIdsNoBluesky account connection IDs to post from
platformAspectRatiosNoReframe every image of an IMAGE or CAROUSEL post to one shape on a platform, one entry per platform. INSTAGRAM takes SQUARE, PORTRAIT or LANDSCAPE (Instagram refuses 9:16 feed and carousel images); TIKTOK takes all four. Video posts, Instagram stories and a second entry for the same platform are refused with 400. Omit a platform to keep its images as uploaded. On an update it applies to the mediaUrls sent in the same request
threadsConnectionIdsNoThreads account connection IDs to post from
thumbnailTimestampMsNoVideo frame timestamp (ms) to use as the thumbnail, used by INSTAGRAM, PINTEREST and TIKTOK unless platformThumbnails overrides it
twitterConnectionIdsNoX (Twitter) account connection IDs to post from
youtubeConnectionIdsNoYouTube channel connection IDs to post from
googleBusinessConfigsNoGoogle Business Profile per-location config: update, event or offer, plus a button. connectionId must match an entry in googleBusinessConnectionIds. GOOGLE_BUSINESS takes TEXT or IMAGE with one JPEG or PNG image up to 5 MB, and text up to 1500 characters
linkedinConnectionIdsNoLinkedIn account connection IDs to post from
mastodonConnectionIdsNoMastodon account connection IDs to post from
telegramConnectionIdsNoTelegram channels and groups to post to, by the account id from list_accounts. TELEGRAM takes text up to 4096 characters, up to 10 images (JPEG, PNG or WebP, 10 MB each) or one MP4 or MOV video (50 MB); a caption over 1024 characters goes out as its own message after the media
instagramConnectionIdsNoInstagram account connection IDs to post from
pinterestConnectionIdsNoPinterest account connection IDs to post from
googleBusinessConnectionIdsNoGoogle Business Profile locations to post to, by the account id from list_accounts. A location without an entry in googleBusinessConfigs gets a STANDARD update

Output Schema

NameRequiredDescriptionDefault
resultNoA validation report: valid, outcome (DRAFT, SCHEDULED or PUBLISH_NOW), scheduledAt, requiredPermissions, issues (each { code, severity ERROR or WARNING, message, platform, connectionId }), accounts (each { platform, connectionId, accountName, status READY, WARNING or BLOCKED, issues }), and notChecked (each { code, message }).

Schema Changelog

Changes observed during successful MCP inspections.

  1. Changed2 schema fields changed
    • changedInput schema / properties / saveAsDraft / description
      Previous value: -"Save as DRAFT instead of publishing or scheduling; publish later with publish_draft"New value: +"Save as DRAFT instead of publishing or scheduling; publish later with publish_draft. A scheduledAt sent with it is stored as the draft's planned date, and publish_draft schedules the draft for that date"
    • changedInput schema / properties / scheduledAt / description
      Previous value: -"Absolute ISO 8601 instant to schedule (e.g. \"2026-03-15T10:00:00Z\"). Omit to post immediately; a past time also posts immediately"New value: +"Absolute ISO 8601 instant to schedule (e.g. \"2026-03-15T10:00:00Z\"). Omit to post immediately; a past time also posts immediately. With saveAsDraft it is the draft's planned date instead: nothing is scheduled, and publish_draft without a date later schedules the draft for it"
  2. Changed1 schema field changed
    • changedInput schema / properties / tumblrConnectionIds / description
      Previous value: -"Tumblr blog account ids from list_accounts to post to"New value: +"Tumblr blogs to post to, by the account id from list_accounts. TUMBLR takes text up to 32768 characters, up to 30 JPEG, PNG or GIF images (10 MB each) or one MP4 video (500 MB, up to 10 minutes), and alt text; it has no first comments, threads, per-platform thumbnails, image shapes or analytics"
  3. Added

TDQS

A4.8/5.0
Behavior5/5

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

Annotations (readOnlyHint true, destructiveHint false) already declare the safety profile and the description is fully consistent with them while adding substantial detail on top: which extra pre-publish checks run (TikTok creator limits, YouTube quota, expired tokens, Pinterest boards, X credits), that nothing is written, and the crucial caveat that 'notChecked lists what only the platform decides at publish time, so valid: true is not a guarantee.' That caveat is exactly the kind of nuance annotations cannot express.

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 correctly — purpose and the create_post relationship come first, reporting semantics after. It is a single dense paragraph where every sentence carries information (checks run, report shape, fix-then-create loop), but the run-on parenthetical lists of platform failures and the return-field enumeration make it heavier than necessary when an output schema already documents the report.

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

Completeness5/5

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

Given 40 parameters, nested config objects, a real output schema and an open-world external dependency surface, the description covers everything an agent needs: prerequisites (posts.draft), the fix-iterate loop, how account-scoped issues differ from global issues, and the honesty boundary around publish-time checks. The return-value detail is partly redundant with the output schema but reinforces interpretation rather than substituting for it.

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?

Schema description coverage is 100% across 40 parameters, so the baseline is 3. The description earns the extra point by establishing that the input surface is identical to create_post ('takes exactly the same input'), which tells the agent it can reuse the payload it already built rather than assuming dry-run-specific fields. It does not annotate individual parameters, but it does not need to.

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?

Opens with a precise verb+resource framing: 'Dry-run create_post: takes exactly the same input and runs every check creating the post would run' — the agent immediately knows this is the validation twin of create_post. It further specifies what is checked and what is deliberately not produced (no post, no media upload, no writes), so it is distinguishable from create_post and validate_bulk_posts without opening 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 Guidelines5/5

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

Explicit workflow routing: 'Fix every ERROR, then call create_post with the same input' names the alternative and the exact condition that selects it. It also states 'no confirmation is needed' and notes the posts.draft permission, removing the two most likely reasons an agent would hesitate or ask the user first.

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