Skip to main content
Glama
hermoso-ai

Hermoso

Official

Post to Bluesky

post_to_bluesky
Destructive

Publishes a post to Bluesky as the connected account, checking text limits, attaching images or one MP4 video, building link cards, and returning the public bsky.app URL.

Instructions

Use this when the user wants to post to Bluesky. Publishing costs no credits. Publish a post to Bluesky as the connected account. Text up to 300 characters — Bluesky ALSO caps a post at 3000 UTF-8 bytes, so an emoji-heavy post can be under 300 characters and still be refused; Hermoso checks both before spending the round trip and says which limit and by how much. MEDIA: either up to 4 images (imageUrls + altText) OR one MP4 video (videoUrl + videoAlt), never both — a Bluesky post record carries a single embed and images and video are two different embed types. Video is MP4 only, up to 300MB at Bluesky's end (Hermoso can fetch up to 150MB from a URL), with optional WebVTT caption tracks; the aspect ratio is measured from the file. Bluesky requires a CONFIRMED EMAIL on the account before it will process any video — if it is unconfirmed you get a refusal saying so, and reconnecting will not help. Links in the text are made clickable automatically. LINK CARDS: Bluesky does NOT scrape links, so a URL posted bare renders as plain blue text — the client composing the post has to build the card. Hermoso builds one AUTOMATICALLY when the post has a URL and NO media: it fetches the page, uses its title/description and uploads its image as the card thumbnail. Pass linkCard:false to suppress it, or linkCard:{uri,title,description,thumbUrl} to control it (give both title and description and the page is not fetched at all). A POST CARRIES ONE EMBED, so a card and images/video cannot both ride: if you pass linkCard explicitly ALONGSIDE media the call is REFUSED by name rather than silently dropping one, and if the URL was merely in the text the MEDIA WINS and the reply says the card was skipped (the link stays clickable either way). Returns the post's public bsky.app URL. Connect at Settings > Connectors > Bluesky.

Input Schema

NameRequiredDescriptionDefault
hookNothe post's angle — a list_hooks id or your own wording, reused exactly
textYesThe post, up to 300 characters / 3000 UTF-8 bytes.
brandNoWHICH PROFILE this post belongs to — id or exact name from list_brands (a shared workspace: its profile id). Beats the connection's pin for THIS CALL ONLY; a name that matches no profile, or two, is REFUSED and nothing is posted.
langsNoBCP-47 language tags, e.g. ['en'].
ideaIdNoshort id of the content-plan idea this post came from
recipeNothe post's FORMAT id, e.g. "slideshow" or "imessage_chat" — post_performance groups by it, so reuse one id per format
accountNoWHICH connected account of this channel to post as — its @handle or id from list_connector_accounts. Needed only when the profile has more than one bluesky account connected (several and none named is refused by name, never guessed); omit when there is one.
altTextNoAlt text — an ARRAY, one per image in the same order, or a single STRING to describe every image with it. WRITE ONE: Bluesky’s own lexicon makes `alt` a REQUIRED property of every image, so a post without it is undescribed by design rather than by omission, and Bluesky users expect it. No maximum length is published, so nothing is truncated.
subjectNowhat the post is about
captionsNoUp to 20 WebVTT caption tracks: [{lang:'en', url:'https://…/en.vtt'}] or [{lang:'en', content:'WEBVTT\n\n00:00…'}]. Each file is capped at 20000 bytes.
linkCardNoRich link card (`app.bsky.embed.external`). OMIT for the default (a card is built automatically when the post has a URL and no media). `false` never builds one. `true` builds one from the first URL in the text. An object {uri,title,description,thumbUrl} overrides any field — supply BOTH title and description and the page is never fetched. Cannot be combined with imageUrls/videoUrl: a post has ONE embed, so an explicit linkCard beside media is refused.
videoAltNoAlt text describing the video, for accessibility.
videoUrlNoOne public MP4 URL. Cannot be combined with imageUrls. Bluesky transcodes it, which takes a minute or two.
imageUrlsNoUp to 4 public image URLs to attach. Cannot be combined with videoUrl.
platformCoverNoVIDEO COVER. Bluesky has no cover setting and shows the video’s FIRST frame, so by default Hermoso checks it and, only when that frame is blank (a template ad’s empty opening card), sends Bluesky a copy with the first frame replaced by the video’s best frame. Your Library file is never changed. true = send the file exactly as it is.
allowDuplicateNopost it even though an identical post was just made
idempotencyKeyNoany stable string: a repeat within 24h returns the original post instead of posting again

Schema Changelog

Changes observed during successful MCP inspections.

  1. Changed2 schema fields changedv0.1.374
    • changedInput schema / properties / account / description
      Previous value: -"WHICH connected account of this channel to post as — its @handle or id from list_connector_accounts. Needed only when the brand has more than one bluesky account connected (several and none named is refused by name, never guessed); omit when there is one."New value: +"WHICH connected account of this channel to post as — its @handle or id from list_connector_accounts. Needed only when the profile has more than one bluesky account connected (several and none named is refused by name, never guessed); omit when there is one."
    • changedInput schema / properties / brand / description
      Previous value: -"WHICH BRAND this post belongs to — id or exact name from list_brands (a shared workspace: its profile id). Beats the connection's pin for THIS CALL ONLY; a name that matches no brand, or two, is REFUSED and nothing is posted."New value: +"WHICH PROFILE this post belongs to — id or exact name from list_brands (a shared workspace: its profile id). Beats the connection's pin for THIS CALL ONLY; a name that matches no profile, or two, is REFUSED and nothing is posted."
  2. Changed1 schema field changedv0.1.320
    • changedInput schema / properties / brand / description
      Previous value: -"WHICH BRAND this post belongs to — the id or exact name from list_brands (a workspace shared with you: its profile id). Use it whenever the account has more than one brand and you are not certain which one this connection is pinned to: it beats the pin for THIS CALL ONLY and changes nothing about the connection. A name that matches no brand, or two brands, is REFUSED and nothing is posted — never resolved to the pin, which is the account you were guarding against."New value: +"WHICH BRAND this post belongs to — id or exact name from list_brands (a shared workspace: its profile id). Beats the connection's pin for THIS CALL ONLY; a name that matches no brand, or two, is REFUSED and nothing is posted."
  3. Changed6 schema fields changedv0.1.281
    • addedInput schema / properties / allowDuplicate
      Added value: +{
      +  "description": "post it even though an identical post was just made",
      +  "type": "boolean"
      +}
    • addedInput schema / properties / hook
      Added value: +{
      +  "description": "the post's angle — a list_hooks id or your own wording, reused exactly",
      +  "type": "string"
      +}
    • addedInput schema / properties / ideaId
      Added value: +{
      +  "description": "short id of the content-plan idea this post came from",
      +  "type": "string"
      +}
    • addedInput schema / properties / idempotencyKey
      Added value: +{
      +  "description": "any stable string: a repeat within 24h returns the original post instead of posting again",
      +  "type": "string"
      +}
    • addedInput schema / properties / recipe
      Added value: +{
      +  "description": "the post's FORMAT id, e.g. \"slideshow\" or \"imessage_chat\" — post_performance groups by it, so reuse one id per format",
      +  "type": "string"
      +}
    • addedInput schema / properties / subject
      Added value: +{
      +  "description": "what the post is about",
      +  "type": "string"
      +}
  4. Changed1 schema field changedv0.1.272
    • addedInput schema / properties / brand
      Added value: +{
      +  "description": "WHICH BRAND this post belongs to — the id or exact name from list_brands (a workspace shared with you: its profile id). Use it whenever the account has more than one brand and you are not certain which one this connection is pinned to: it beats the pin for THIS CALL ONLY and changes nothing about the connection. A name that matches no brand, or two brands, is REFUSED and nothing is posted — never resolved to the pin, which is the account you were guarding against.",
      +  "type": "string"
      +}
  5. Changed1 schema field changedv0.1.256
    • addedInput schema / properties / platformCover
      Added value: +{
      +  "description": "VIDEO COVER. Bluesky has no cover setting and shows the video’s FIRST frame, so by default Hermoso checks it and, only when that frame is blank (a template ad’s empty opening card), sends Bluesky a copy with the first frame replaced by the video’s best frame. Your Library file is never changed. true = send the file exactly as it is.",
      +  "type": "boolean"
      +}
  6. Changed1 schema field changedv0.1.189
    • addedInput schema / properties / account
      Added value: +{
      +  "description": "WHICH connected account of this channel to post as — its @handle or id from list_connector_accounts. Needed only when the brand has more than one bluesky account connected (several and none named is refused by name, never guessed); omit when there is one.",
      +  "type": "string"
      +}
  7. Addedv0.1.161

TDQS

A4.6/5.0
Behavior5/5

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

Annotations already declare this as non-read-only, open-world, destructive, and non-idempotent, but the description adds rich behavioral detail beyond them: credit cost, dual character/byte limits with preflight checks, media embed constraints, the confirmed-email gate for video, automatic link-card construction and refusal cases, and the returned public URL. Nothing here contradicts the annotations.

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?

It front-loads the trigger, cost, and core action, then groups constraints under MEDIA and LINK CARDS, which is appropriate for a 17-parameter tool. It is dense and occasionally emphatic, but most sentences carry operational constraints rather than filler.

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?

With no output schema, the description supplies the return value (the post's public bsky.app URL). It also covers connector setup, text and media limits, link-card interactions, refusal modes, and account/profile selection, giving an agent enough to invoke it correctly.

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 coverage is 100%, so the baseline would be 3, and the schema already documents most parameters in detail. The description still adds meaningful cross-parameter semantics: automatic linkCard behavior versus explicit control, the one-embed rule, media winning over a URL-derived card, idempotencyKey repeat behavior, and platformCover first-frame logic.

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?

The description states a specific verb ('publish a post') and resource ('Bluesky') and scopes it to the connected account, with the trigger condition stated up front. The channel-specific name and text make it clearly distinguishable from sibling posting tools such as post_to_x or post_to_linkedin.

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?

It gives a clear usage trigger ('Use this when the user wants to post to Bluesky') and enumerates important conditions such as confirmed email for video, media exclusivity, link-card behavior, and connector setup. It does not explicitly name scheduling or alternate-platform siblings as alternatives, so it falls short of full when-not/alternatives guidance.

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

Deploy Server

Other Tools