Skip to main content
Glama
hermoso-ai

Hermoso

Official

Post to Bluesky

post_to_bluesky
Destructive

Publish a post to Bluesky with up to 4 images or one MP4 video, automatic link cards, and checks for both character and byte limits before sending.

Instructions

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, or here with connect_connector, with a handle and an APP PASSWORD.

Input Schema

TableJSON 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 (destructiveHint=true, openWorldHint=true, idempotentHint=false) are consistent, and the description adds far more: 300-char/3000-byte double cap, MP4-only/300MB limits, the confirmed-email prerequisite for video, one-embed exclusivity rules, and the 24h idempotency behavior. This is exactly the behavioral disclosure a mutation tool needs.

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 is long, but for a 17-parameter tool with intricate media/embed/link-card semantics almost every sentence carries a distinct rule and the core action is front-loaded. Only mild trimming seems possible without losing real constraints.

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 17 params, a media/embed model, auth prerequisites, and no output schema, the description is remarkably complete — it even states the return value ('the post's public bsky.app URL') and the connection path. An agent has everything needed to call 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 is 3, but the description still adds cross-parameter semantics the schema states piecemeal: the altText-is-required-by-lexicon rationale, the default linkCard construction, and the 'media wins' fallback when a URL is only in the text. It reinforces and connects the parameters rather than merely repeating them.

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 opens with a specific verb+resource+scope: 'Publish a post to Bluesky as the connected account.' This distinguishes it from the many sibling post_to_* tools (post_to_x, post_to_linkedin, post_to_meta) without needing to open any 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?

It gives extensive conditional guidance — when linkCard is built automatically vs suppressed, when media wins over a card, when it is refused, and how to connect. It does not explicitly compare against sibling posting tools, but the media/embed/link-card routing rules are unusually thorough for a publish action.

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