Skip to main content
Glama
hermoso-ai

Hermoso

Official

Hermoso: MCP, CLI & Skills

The AI marketing agent with an AI ad generator built in, run from any AI agent: Claude Code, Claude.ai, Cursor, Codex, or your own scripts. Research the ads already winning in a market, generate finished image & video ads (your real product composited in, copy + CTA included), publish them to your own social channels, and build & manage the ad campaigns behind them, all over MCP tools, a CLI, or installable Claude skills.

906 tools. tools/list is always the authoritative set; hermoso_capabilities (free) returns the live model catalog with exact per-render credit costs plus the full capability map.

A Chrome extension too. Hermoso: save and clone ads saves any ad to your swipefile in one click, clones it for your brand, and pulls any company's ads from its website.

Most of it costs nothing. Publishing and scheduling posts, building and managing paid campaigns, analytics and insights, comments and DMs, connectors, profiles and team seats are free on every plan, with no per-post or per-channel fee and no seat pricing. Credits are spent only on running an AI model (image, video, voice, text, planning, post-production) and on Ad Spy research, and posting an ad you already rendered is never a second charge. The one exception is X (Twitter), where posting and reads bill a few credits per call because X charges per API request.

What it connects to. Ad platforms: Meta, Google Ads, TikTok Ads, LinkedIn Ads, Reddit Ads, X Ads, Pinterest Ads, Snapchat Ads, Microsoft Advertising, Apple Search Ads, ChatGPT Ads and AppLovin Ads, plus product feeds in Google Merchant Center. Publishing and scheduling — ten channels: Facebook, Instagram, Threads, TikTok, YouTube, X, LinkedIn, Pinterest, Bluesky and Telegram. Messaging: WhatsApp (you message a person, so it is not an eleventh publishing channel) and DM automations on Instagram, Facebook Pages and Messenger, and X (a keyword comment, a DM, a story reply or an ig.me / m.me link gets an automatic reply). Ad research: the Meta, Google and LinkedIn ad libraries plus organic TikTok, Instagram, YouTube, Threads and Reddit. Analytics: Google Analytics 4, Google Search Console and every connected platform's own post and campaign insights. Files: Google Drive, Sheets, Docs and OneDrive.

It is not all-or-nothing. Research, creation, publishing/scheduling and ads management are four independent areas — no tool requires that you used another one first. Publish or schedule creative you already have and generate nothing here (upload_file turns any local or external file into a URL every publish, schedule and ad-build tool accepts); build and read campaigns on your own ad accounts with your own creative; research competitors with no brand drafted and no channel connected; or generate a file with nothing connected at all and just download it. Use the one piece you need, or all of it together.

Install in one command

This repo is a plugin marketplace, a Gemini CLI extension and a skills package at once, so a coding agent takes Hermoso in one line. Every install brings the same five skills (hermoso-research, hermoso-generate, hermoso-ad-from-brand, hermoso-product-photoshoot, hermoso-marketing), and the skills drive the hermoso CLI through npx. No tool list is loaded into your session: a CLI command costs nothing until it runs, and it reaches every tool. The first time, your agent runs npx -y hermoso auth login, which opens a browser to sign in.

Claude Code:

claude plugin marketplace add hermoso-ai/hermoso && claude plugin install hermoso@hermoso

Inside a session the same thing is /plugin marketplace add hermoso-ai/hermoso then /plugin install hermoso@hermoso.

Codex CLI (it reads the same marketplace file):

codex plugin marketplace add hermoso-ai/hermoso && codex plugin add hermoso@hermoso

Gemini CLI:

gemini extensions install https://github.com/hermoso-ai/hermoso

Grok (xAI) comes in three shapes, and each one connects differently:

  • grok.com and the Grok apps take the hosted connector: open Plugins → Connectors → New Connector → Custom (or go straight to grok.com/connectors), paste https://app.hermoso.ai/mcp and sign in with your Hermoso account.

  • Grok Bot: add the Hermoso Marketing Bot template. It arrives wired to Hermoso and asks for a key, which you create in the app under MCP & CLI → Terminal & API keys.

  • Grok Build, xAI's terminal agent, runs a local MCP server. Sign in once, then add it:

    npx -y hermoso auth login
    grok mcp add hermoso -- npx -y hermoso mcp

Cursor, OpenCode, GitHub Copilot, Windsurf and about 75 more agents, through the open skills installer:

npx skills add hermoso-ai/hermoso

It asks which agents to install into; -a opencode (or cursor, github-copilot, windsurf) picks one, and --skill hermoso-research installs a single skill. VS Code agent plugins can also take this repo whole: run Chat: Install Plugin From Source and paste https://github.com/hermoso-ai/hermoso.

ChatGPT and Claude.ai run in a browser and cannot run a CLI, so they use the hosted connector instead (see below). The ChatGPT desktop app and ChatGPT workspace admins can also read this repo's .claude-plugin/marketplace.json, which installs the skills.

No browser on the machine? Create a key in the app under MCP & CLI and run npx -y hermoso auth login --token <key>.

Related MCP server: Ad Library MCP

Which surface should your agent use?

Two shapes, and the right one is decided by what your client can do, not by which we prefer.

Your client

Use

Why

Runs in a browser: Claude.ai, ChatGPT, Claude Desktop

the hosted connector https://app.hermoso.ai/mcp

It cannot spawn a local process, so a URL is the only shape it has. Nothing to install, no key to paste, and the full toolset arrives with your saved brand context. This is the right answer for these clients, not a lesser one.

Can run a shell: Claude Code, Codex, Gemini CLI, Cursor, Cline, OpenClaw, Hermes, your own scripts

the one-command install above, or the CLI itself (npm install -g hermoso)

A tool manifest is loaded into every session whether or not a tool is called. A shell command costs nothing until it runs, and it reaches every tool rather than the default roster.

The CLI answers the questions a tool manifest would, on demand and only when asked:

npx -y hermoso tools --search reddit   # every matching tool, name + one line
npx -y hermoso tools plan_ad           # one tool's full argument schema
npx -y hermoso call plan_ad --json '{"product":"…"}'   # run it

tools and tools <name> read a registry bundled in the package (no key, no network, no sign-in), so an agent can browse the entire product before anyone signs in. Only call spends, and only that needs hermoso auth login once.

Want the tools in your coding agent's own list as well? That is the MCP server, and it is optional. One hermoso auth login covers the CLI and lets claude mcp add hermoso -- npx -y hermoso mcp pick the key up with no env block, so the agent can reach for a native tool when it wants structured results and shell out when it wants breadth. It costs context in every session, so add it when a session settles into one area and makes many calls there; enable_tools({groups:['ads']}) then turns campaign management on in a single free call.

Your agent can sign itself up

An agent with no Hermoso account can provision one, get its own key, and be rendering ads in the same session. No human at a browser, no ticket, no waiting.

# 1. Start a signup. This call takes no credential, because the credential is what it creates.
curl -sX POST https://app.hermoso.ai/v1/signup \
  -H 'content-type: application/json' \
  -d '{"plan":"pro","period":"mo","email":"you@yourcompany.com"}'
# -> { "id": "cs_...", "checkout_url": "https://checkout.stripe.com/...", "claim_token": "hsc_...", "email": { "address": "you@yourcompany.com", "verified": false } }
# email = the human behind the account. A verification link goes there; the account works before it is clicked.
# It is a contact mailbox only, never a sign-in. GET /v1/account/email reports the state; POST /v1/account/email/resend re-sends or changes it.

# 2. Pay at checkout_url. Store claim_token first: it is returned only in that response.

# 3. Claim it. Poll until status is "ready".
curl -sX POST https://app.hermoso.ai/v1/signup/cs_.../claim \
  -H 'content-type: application/json' \
  -d '{"claim_token":"hsc_..."}'
# -> { "status": "ready", "api_key": "hmk_...", "credits": 3000 }

That hmk_ key is the same credential everything else on this page takes: /v1, the MCP server, the CLI. Point your client at it and the full surface is open.

Paying is something a browser-capable agent can already do itself. Checkout is Stripe's own hosted page, so Claude in Chrome and clients like it complete it unattended today. Everything else is a one-click handoff: send checkout_url to whoever holds the card. The same shape covers you later, once you are running: buy_credits and upgrade_plan mint a ready-to-pay link for more credits or a bigger plan, and billing_status reads the balance any time.

An agent with its own payment credential can pay with no human at all. POST /api/billing/machine-payment with {"packId": "pack-1k"} answers HTTP 402 carrying a WWW-Authenticate: Payment challenge (Stripe, through the Machine Payments Protocol); pay the challenge and retry, and the same credit pack lands on the same balance. GET /api/billing/config lists the packs under machinePayments. Same packs, same prices, no per-call billing.

The agentic path takes a paid plan. Any of them. The free plan is there for a person signing up at app.hermoso.ai, and asking for it here returns a refusal that says so. Nothing is created until the payment completes, so an unpaid signup leaves no account behind and charges nothing.

One thing still wants a person, and it is worth knowing up front. Connecting a social or ad account means an OAuth consent screen, and a consent screen cannot be completed headlessly on any platform. list_connectors shows what is already connected and what is not. Everything else runs with no browser at all: research, generation, publishing to a channel that is already connected, campaign builds, reporting.

Full request and response shapes, plus every other endpoint, are in the OpenAPI document at app.hermoso.ai/openapi.json, served live from the same table that mounts the routes.

Instant: the hosted Claude.ai connector

Paste https://app.hermoso.ai/mcp?src=readme into Claude → Customize → Connectors → Add → Add custom connector, press Continue, choose Sign in now under Authentication (Claude's detector pre-selects "No sign-in" because our discovery handshake is open, and with that your first request comes back "Authentication required"), press Add and Connect, approve with your Hermoso account, and you are done: the full toolset with your saved brand context, billed to your plan.

Quickstart for Claude Code (one command)

  1. Get an account at app.hermoso.ai. The free tier is included; plans and credits are the same ones the web Studio uses. Or skip the browser entirely and let your agent sign itself up on a paid plan with POST /v1/signup (above).

  2. Install the plugin. It adds the five Hermoso skills, which drive the hermoso CLI through npx:

claude plugin marketplace add hermoso-ai/hermoso && claude plugin install hermoso@hermoso

Already inside a session? /plugin marketplace add hermoso-ai/hermoso, then /plugin install hermoso@hermoso. Codex, Gemini CLI and the rest are in Install in one command. 3. Sign in once. npx -y hermoso auth login opens your browser; your agent also runs it by itself the first time it needs Hermoso. On a machine with no browser, use npx -y hermoso auth login --token hmk_… with a key from the MCP & CLI tab, under Terminal & API keys (Settings → API keys, or https://app.hermoso.ai/?tab=api-keys, go straight there). 4. Ask for what you want, in your normal prompts. Claude Code picks the Hermoso skill for the job and runs the commands. You type none of them.

Rather install the CLI by hand? npm install -g hermoso puts the same hermoso command on your PATH, and the skills use it when it is there.

The hosted URL works in Claude Code too, but the plugin is the lighter path there because it loads no tool list into your sessions. If you want the connector: claude mcp add --transport http hermoso "https://app.hermoso.ai/mcp?src=readme", and claude mcp list reports ! Needs authentication until you run claude mcp login hermoso once and approve in your browser (--no-browser prints the link on a headless machine). Measured against Claude Code 2.1.282 on 2026-09-25.

Your agent now has the full studio with your profile's context: the brand details, products, logos and learned memory you set up in the web app apply automatically (get_brand shows what's saved; omit brand in plan_ad/plan_variations to use it). Renders bill your Hermoso credits, at the same prices as the Studio. Only AI model runs and Ad Spy research spend credits; publishing, scheduling, ads management and analytics are free on every plan (posting to X and reading X data are the one per-call exception, managing X ads is free).

1. MCP server (stdio), optional in a coding agent

hermoso mcp runs a stdio MCP server exposing the full toolset, for a client that wants Hermoso's tools in its own list. The published hermoso package means no clone: npx -y hermoso mcp fetches and runs it. Sign in once with the CLI and no key goes into any client config, because hermoso mcp reads the bearer hermoso auth login stored:

npm install -g hermoso && hermoso auth login && claude mcp add hermoso -- npx -y hermoso mcp

On a machine with no browser, skip the sign-in and pass the key to the client instead:

claude mcp add hermoso -e HERMOSO_TOKEN=hmk_… -- npx -y hermoso mcp

Cursor / Codex: sign in the same way, then add to mcp.json (Codex uses the TOML equivalent). Drop the env block entirely if you signed in above; it is there for CI, where the process cannot read your home directory:

{ "mcpServers": { "hermoso": { "command": "npx", "args": ["-y", "hermoso", "mcp"],
  "env": { "HERMOSO_API_BASE": "https://app.hermoso.ai", "HERMOSO_TOKEN": "<your token>" } } } }

Then ask your agent: “Generate an image ad with Hermoso.”

What the 906 tools cover

Ad spy / research — find_competitors, competitor_teardown, pull_competitor_ads, research_ads; the Meta / Google / LinkedIn ad libraries (search_meta_ads, search_google_ads, search_linkedin_ads); organic social (search_tiktok, search_instagram, search_youtube, search_reddit, search_threads); fetch_social_data, mine_angles, analyze_video, check_ad_policy, list_skills / get_skill.

Create — draft_brand → plan_ad → render_ad (the Studio quality pipeline: composited text, clean speech, music, brand end card), or generate_image / generate_video / generate_avatar (UGC creators + lip-sync). The workspace's saved cast is reusable: list_creators returns every saved creator with their portrait url, save_creator adds one, delete_creator drops one — re-pass a portrait to generate_avatar / generate_video / recast_motion and the SAME person stars in every ad, instead of a new face each render. Also make_template_ad (native HTML ad formats), make_explainer, product_sizzle, make_thumbnail, clone_static, recast_motion, reframe_video, upscale_video, dub_video, change_voice, finish_video, fix_beat, stitch_video, clip_video, post_edit, plus plan_variations + score_ad to fan out and rank. Length is yours to set: pass durationSeconds to plan_ad and the storyboard is authored to it — a length that fits one clip of the render model renders as a single continuous take, longer is stitched from acts (on a 15s-clip model, 40s = 15+15+10), never time-compressed. What fits one clip is the model's own maximum, not a fixed number: most video models cap a clip at 15 seconds and the longest-clip one takes 30 seconds in one unbroken take with native synchronized audio. hermoso_capabilities is the live list — durations, resolutions and the exact credit cost of every tier — and naming that model in model is how you get it, since an unnamed render is routed by a narrower auto-pool.

Ads Studio (make_static_ads): pick products and a count and get a batch of finished static ads in one call, each a different selling angle and a different layout, with your real product photo and logo laid on. The product catalog it draws from (list_products, add_product, refresh_products, update_product, remove_product) is a cache of each product's live page, so names, photos and prices come from the page itself.

Swap a person into any video (recast_hook, recast_motion): keep the scene, the cuts and the sound and change who is on screen, using your own photo, a saved creator or an AI person (in the web Studio you can describe a presenter in words). Real faces from your own photos need a paid plan; AI people work on every plan.

Raw model playground — the full catalog (30+ image / video / voice / writing models, each with its exact per-render credit cost) with no ad framing: generate_image / generate_video with useBrand:false, generate_voice, generate_text. The image default is Nano Banana 2.1.

Publish to your own channels — ten of them: Facebook, Instagram and Threads (post_to_meta), TikTok (post_to_tiktok), YouTube (post_to_youtube + update_youtube_video, youtube_video_insights, comments read/reply), X (post_to_x, x_post_metrics, x_post_insights, x_mentions, list_x_dms, send_x_dm), LinkedIn profile and company Pages (post_to_linkedin, post_to_linkedin_page), Pinterest (post_to_pinterest + boards), Bluesky (post_to_bluesky, delete_bluesky_post, bluesky_post_metrics, plus list_bluesky_convos / read_bluesky_dm / send_bluesky_dm) and Telegram (post_to_telegram, delete_telegram_message, list_telegram_chats). schedule_post / list_scheduled / cancel_scheduled give you one content calendar over exactly that set. upload_file brings in any external or local media, not just Hermoso renders. X posting bills credits per API call (X charges per request); a post containing a link costs 13× one without. Held back, and named rather than hidden: Google Business Profile is built (post_to_google_business, reviews, Q&A, insights) and is not offered — Google allowlists that API per project and ours reads 0 QPM, so every call would 403 for every user. It is in schedule_post's channel enum and refused at enqueue.

Message customers on WhatsApp — messaging, not an eleventh publishing channel: you message a person, and nothing here posts to a feed. list_whatsapp_accounts finds the Business Account and its numbers, list_whatsapp_templates / create_whatsapp_template / delete_whatsapp_template manage the templates Meta reviews, and send_whatsapp_message sends one — confirm-gated, because it reaches a real phone and Meta bills the business for the conversation. Two limits that are permanent facts about Meta's API rather than anything pending: Hermoso does not receive WhatsApp webhooks, so there is no message history to read — it is not an inbox surface and list_inbox does not cover it — and outside the 24-hour window that opens when the customer messages first, WhatsApp accepts an APPROVED template and nothing else.

Automate Instagram DMs — save_instagram_dm_automation sets up a rule that answers people for the brand: a comment with a keyword on one post, the next post or any post gets a private reply, and a DM, a story reply or an ig.me link gets an answer, with optional link buttons, a public comment reply, a follow gate and a dry run. test_instagram_dm_automation shows exactly what a sample comment or DM would send without sending it, and list_instagram_dm_automations / delete_instagram_dm_automation read each rule's stats and send log or remove it. Each person gets one reply per post, Meta allows a private reply within 7 days of the comment, and no AI runs on the send path, so it costs no credits.

Automate DMs on Facebook and X too — save_dm_automation (with test_dm_automation, list_dm_automations and delete_dm_automation) takes a channel: instagram, facebook (a keyword comment on a Page post or an ad gets a private reply in Messenger; a Messenger keyword or an m.me link gets an answer) or x (a keyword @mention or a reply under your post gets a public reply; a DM keyword gets a DM back). X only allows an automated DM after the person has DMed you, and a DM saying STOP opts that person out. Facebook is free like Instagram. X bills us per call, so an X automation is charged in credits for every mention, reply or DM X delivers plus each reply or DM sent, and running out of credits stops its X rules.

Autopilot posting (set_post_refill, get_post_refill, run_post_refill): say how often, where and what about ("2 posts a day on Instagram and Bluesky about our slow-morning routine, let me review first") and Hermoso makes new posts from your brand and fills your calendar. Pick review first (each batch waits for your approval, edit or redo) or post automatically (straight onto the schedule, still pullable). It follows the notes you leave and writes from what has worked on each channel. Credits are used only when a post is made.

Run the ads — full campaign trees, built paused and read back before anything is reported, with every spend change confirm-gated, on twelve platforms: Meta, Google Ads, LinkedIn Ads, Reddit Ads, Pinterest Ads, Microsoft Advertising, ChatGPT Ads (OpenAI's Advertiser API), X Ads, TikTok Ads, Snapchat Ads, Apple Ads (Apple Search Ads on the App Store) and AppLovin Ads (reporting and the website pixel on the keys every account has; campaign building once AppLovin enables Campaign Management API access on the account, and server-side conversion events with the Conversion API key AppLovin issues on request). Each has list + report + create + budget/status tools (e.g. list_google_ads_campaigns, google_ads_report, create_google_ads_campaign, set_google_ads_budget, set_google_ads_status). Snapchat needs one extra step the others do not: an ad points at a CREATIVE, and every Snapchat creative must carry a Public Profile id — build it with upload_snapchat_ads_creative.

Feed the shopping surfaces — Google Merchant Center is the catalog a retail Performance Max or Shopping campaign advertises (create_google_ads_performance_max_campaign takes a merchantCenterId), and you manage it from here: accounts and account status, data sources, product upsert / update / delete, per-region inventory, quota, merchant_report for product-level performance, notifications and conversion sources, plus the disapproval loop — list_merchant_issues says what is wrong and merchant_issue_help returns Google's own documented fix. Promotions need the merchant's own enrolment in Google's promotions program; without it Google refuses that sub-API outright. Microsoft Merchant Center is covered on the same shape (stores, catalogs, products, issues) for Bing Shopping.

Measure what the ads achieved — Google Analytics 4 closes the loop. Every other connector here reports what an ad cost; this is the one that reports what it did. analytics_report breaks sessions, users, conversions and revenue down by channel, source/medium, campaign, landing page, country, device or date, so the campaign Hermoso built and the revenue it drove sit in one conversation. analytics_realtime shows who is on the site right now. Start at list_analytics_properties — the tools take a numeric property id, not the G-XXXXXXXXX Measurement ID from your tracking snippet, and this is what resolves one from the other. It writes as well as reads: create_analytics_key_event marks an event GA4 already collects as a key event — which is what makes it importable into Google Ads as a conversion — and create_analytics_custom_dimension registers an event parameter so reports can break down by it, with list_analytics_definitions showing what the property already measures. It signs in with the same Google account as Google Ads, YouTube and Drive, but it is its own connection. GA4 only — the API has no Universal Analytics surface. A custom dimension can be archived but never deleted, and a property holds 50 event-scoped ones.

Files — Google Drive CRUD (save_to_drive, list_drive_files, update_drive_file, delete_drive_file, create_drive_folder), Google Sheets (create_sheet, append_to_sheet, read_sheet), Google Docs (create_doc, append_to_doc), and OneDrive (save_to_onedrive + full CRUD).

Workspace & account — profiles (list_brands, create_brand, use_brand, update_brand, delete_brand; one account holds many profiles, each a brand, a client, a creator or a personal workspace, so an agency runs every client through here), memory (remember, forget, list_memory), custom skills (save_skill, get_skill, list_skills, delete_skill — the one library, which absorbed the old AI-Employee personas), team (list_team, invite_member, remove_member, set_role), settings (get_settings, update_settings — including the language every ad, script and plan is written in), connectors (list_connectors, list_connector_accounts, set_connector_accounts, disconnect_connector), and billing (hermoso_credits, billing_status, buy_credits, upgrade_plan, set_auto_reload), plus list_jobs / get_job for async renders.

Connector accounts are picked, not guessed. One person often administers several Facebook Pages, Google Ads customers or LinkedIn company Pages. Only the accounts ticked for a profile are usable — enforced server-side, and an empty selection shares nothing. Linking a new account is the one step that is not headless (it is an OAuth consent screen, so the user does it in the app).

Render jobs queue server-side and poll to completion, returning a served URL.

2. CLI: the context-free path for terminal agents

bin/hermoso.mjs exposes the full MCP toolset as subprocess commands, so an agent can shell out instead of carrying a fat tool manifest.

npm install -g hermoso                             # installs `hermoso`
hermoso capabilities                               # valid model ids + costs (run first)
hermoso create --brand "YourBrand" --product "your best-selling product" --format image
hermoso generate image --prompt "…" --ref ./product.png --wait
hermoso generate video --prompt "…" --duration 8 --wait
hermoso competitors yourbrand.com
hermoso research "Liquid Death’s longest-running ads"

Add --json to any command for machine output.

Those shortcuts are the common path, not the limit. Every tool the MCP server has is reachable here too, including the ad-campaign and analytics groups a connector leaves out of its default roster:

hermoso tools                          # every tool, grouped, name + one line
hermoso tools --group ads --search reddit   # narrow it
hermoso tools create_meta_campaign     # that tool's full argument schema
hermoso call create_meta_campaign --json '{"name":"…"}'   # run it
hermoso create_meta_campaign --name "…"                   # same thing, shorter

call goes through the same handler, the same argument validation and the same confirm/spend gates the MCP server uses, so there is no second implementation to drift. tools and tools <name> read a registry bundled in the package, so they need no key, no network and no sign-in.

3. Skills: what a coding agent installs

skills/ holds five installable skills:

Skill

What it does

hermoso-research

Find competitors, pull their real running ads, surface the hooks worth copying

hermoso-generate

A prompt to a finished image, video, avatar clip or stitched cut

hermoso-ad-from-brand

A brand or domain to one finished, on-brand ad, concept and copy included

hermoso-product-photoshoot

A real product photo composited into studio, lifestyle or hero scenes

hermoso-marketing

The whole loop: research, create, publish and schedule, paid campaigns, measure

They are what every one-command install at the top of this page adds, in Claude Code, Codex, Gemini CLI and through npx skills add. From a clone, copying works too:

cp -r skills/* ~/.claude/skills/

Then invoke /hermoso-ad-from-brand an ad for yourbrand.com, our hero product.

Ask for it in your own words

The skills pick themselves. These are whole prompts, not commands:

  • Find my top 5 competitors for [product + URL], pull their best Meta and TikTok ads from the last 90 days, and tell me the 3 hooks and 2 formats worth stealing, with the evidence.

  • Make this week's ads for [brand]: two hooks in two formats, one UGC and one product visual, 9:16. Score them and policy check them before I ship.

  • Here is a competitor ad: [link]. Break down why it works, then rebuild the structure with my product, my branding and a fresh hook.

  • Fill my social queue for the next 7 days across Instagram, TikTok, X and LinkedIn, repurposed from my best post, with per-channel captions.

  • Take last week's winner and build a $20/day test campaign on Meta and TikTok, three ad sets, one angle each. Leave it paused and read the tree back so I can check it.

  • Pull last month's campaign performance across every platform. Which two creatives won, why, and what should next month's brief say?

Configuration

Env

Meaning

HERMOSO_API_BASE

The Hermoso API origin (default https://app.hermoso.ai — set http://localhost:3000 if you run the app yourself)

HERMOSO_TOKEN

Bearer agent key (hmk_…) — required against the hosted app

HERMOSO_PROFILE

Profile id, for accounts with multiple profiles

HERMOSO_OWNER

Only for a profile another account shared with you (a team workspace): the owning account id. Set it together with HERMOSO_PROFILE, and set HERMOSO_PROFILE to that workspace's profileUuid (a profile's short slug is refused). Run list_brands (or hermoso list_brands from the CLI) to print both values for every workspace you can enter. The server re-authorizes the pair on every request, so a wrong value is refused, never trusted.

mcp/http.mjs is the hosted remote-connector transport (paste-a-URL into Claude.ai → Connectors). It ships in this repo for transparency and refuses to mount without authenticated identity — no anonymous spend, ever.

License

MIT © Hermoso

Available Tools

185 tools
add_subtitlesAdd subtitles to a videoAInspect

Burn subtitles into ANY video and get the .srt too; nothing is cut or re-rendered. Set textStyle only when the user describes a look (default: white sentence case, thin outline, bottom). Timed per sentence, or auto:'words' for phrases on their spoken words. Or pass cues to burn your own lines exactly, untranscribed (reel-style phrases, stage directions). burn:false returns only the .srt. Takes a video file or URL, a YouTube/Vimeo-style page, or a TikTok / Reel / X post link. No speech is refused and refunded. Runs in the background and lands in the Library.

ParametersJSON Schema
NameRequiredDescriptionDefault
autoNo
burnNofalse = only the .srt
cuesNoyour own lines in seconds, no overlap, max 90 chars, no emoji
videoYesthe video to subtitle
textStyleNothe look: words, a preset or fields; omit for the default
wordsPerCueNo

TDQS

A4.3/5.0
Behavior4/5

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

Annotations already declare the write profile (readOnlyHint=false, idempotentHint=false, destructiveHint=false), so the bar is lower. The description still adds real behavioral context the annotations lack: source-is-not-re-rendered, background execution landing in the Library, and the notable 'no speech is refused and refunded' failure/refund behavior.

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 action and outcome, then branches into style/cue/burn options. Dense and slightly list-like across several long sentences, but every sentence carries information an agent needs; little is wasted.

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?

No output schema exists, yet the description explains the return surface (.srt output, burn:false mode, Library landing) and accepted input types (file/URL, YouTube/Vimeo page, TikTok/Reel/X link). Sufficient for correct invocation, though the exact job/response shape at completion is only gestured at.

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 67% schema coverage the schema documents several params, but the description adds semantics beyond it: the textStyle default (white sentence case, thin outline, bottom), the distinction between timed-per-sentence and auto:'words', and that cues burn lines 'exactly, untranscribed' with reel/stage-direction syntax. Only wordsPerCue is left undocumented.

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+resource ('Burn subtitles into ANY video and get the .srt too') and immediately scopes it against likely siblings by clarifying 'nothing is cut or re-rendered', separating it from edit_video/reframe_video/stitch_video. An agent can identify the tool's job without opening the 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 concrete when-to-use rules: set textStyle only when the user describes a look, use auto:'words' for word-timed phrases, pass cues for your own lines, burn:false for .srt only. It does not name alternative sibling tools (e.g., edit_video or dub_video), so the routing guidance is clear but not fully disambiguated.

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

analyze_videoAnalyze videoAInspect

A video ad's structure: verbatim transcript (voiceover + on-screen text), beat list, duration and sampled frame times; study a reference before remixing it. ~A transcription call.

ParametersJSON Schema
NameRequiredDescriptionDefault
urlYesthe video URL (a served /generated/ path or a public http(s) video)
wordsNotrue | 'only': each spoken word's start/end
framesNoalso return the frames as images
anchorsNo{afterWord|beforeWord|atWord, occurrence?, offset?} -> s

TDQS

A3.6/5.0
Behavior3/5

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

Annotations already declare openWorldHint=true, idempotentHint=false and destructiveHint=false, and the description's '~A transcription call.' hints that this is a compute/credit-bearing operation rather than a pure metadata read — relevant given readOnlyHint=false. But it does not explain the cost, latency, or async/non-idempotent behavior that the annotations imply.

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?

Very compact and front-loaded, leading with the returned structure and closing with the use case. It is telegraphic and sentence-fragmentary, which slightly hurts readability, but every clause earns its place.

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?

There is no output schema, and the description compensates by enumerating what is returned (transcript, beat list, duration, frame times). Combined with four annotated parameters and a required url, an agent has enough to invoke it correctly; only cost/async behavior remains undisclosed.

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

Parameters3/5

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

Schema description coverage is 100%, so all four parameters (url, words, frames, anchors) are already documented in the schema. The description alludes to two of them ('transcript' for words, 'sampled frame times' for frames) but adds no syntax or format details beyond the schema. Baseline 3 is appropriate when the schema carries the parameter burden.

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

Purpose4/5

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

The description names a specific verb (analyze) and the exact artifact produced: verbatim transcript, beat list, duration and sampled frame times for a video ad. That is well beyond restating the title. Sibling differentiation is only implicit, via the phrase 'before remixing it', rather than naming a concrete alternative.

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?

'study a reference before remixing it' gives a clear triggering context for the tool and ties it to the remix/clone family (clone_video, remix_static, etc.). It stops short of stating when not to use it or naming a specific alternative tool, so it is context without exclusions.

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

append_to_docAppend text to a Google DocAInspect

Append text to the end of a Google Doc Hermoso can reach — one it created (pass the documentId from create_doc) or one the user handed over with the Google file picker in the app (find its id with list_drive_files). Optional dropdown:{title, options:[2-50], selected} appends a Google Docs dropdown chip after the text — e.g. text "Status: " + dropdown {title:"Status", options:["Draft","In review","Approved"]} gives a brief a status the team can click to change; the chip is confirmed by reading the doc back. Set it later with update_doc dropdowns.

ParametersJSON Schema
NameRequiredDescriptionDefault
textNotext to append at the end of the doc (optional when a dropdown is passed)
dropdownNoa dropdown chip to append after the text
documentIdYesthe document id from create_doc

TDQS

A4.3/5.0
Behavior4/5

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

Annotations cover mutation semantics (readOnlyHint=false, destructiveHint=false, openWorldHint=true, idempotentHint=false). The description adds value by disclosing that the dropdown chip is 'confirmed by reading the doc back' — an irregular verification step an agent would not otherwise expect. It does not describe failure modes, rate limits, or permissions, keeping this at a 4.

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?

The description front-loads the core action and then details the dropdown nuance and confirmation step. It is a single tight paragraph, but the parenthetical about the dropdown example is fairly dense and could be split for readability. No wasted marketing language.

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?

With annotations, full schema coverage, and no output schema, the description covers what an agent needs: the action, how to obtain documentId, the dropdown behavior including its confirmation, and a pointer to update_doc for later changes. It stops short of covering edge cases like doc permissions failures, which is acceptable given the annotation set.

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 schema carries parameter documentation and the baseline is 3. The description goes beyond by explaining the combined use case of `text` and `dropdown` (e.g., 'Status: ' prefix followed by a status chip), and by clarifying that appending a dropdown benefits from later update via update_doc dropdowns.

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 (append) and resource (text to end of a Google Doc) and clarifies the two reachability paths: docs it created via create_doc's documentId, or user-picked docs found with list_drive_files. This distinguishes it from read_doc and update_doc in the sibling list.

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?

Description explains when to use append (adding text at the end) and how to obtain documentId for both ownership cases, and points to update_doc dropdowns for later correction. It does not explicitly rule out in-place edits or explain when to prefer append over update_doc, but the context is strong enough for an agent to route correctly.

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

append_to_sheetAppend rows to a Google SheetAInspect

Append rows to a Google Sheet Hermoso can reach — one it created (pass the spreadsheetId from create_sheet) or one the user handed over with the Google file picker in the app (find its id with list_drive_files). rows = array of row arrays.

ParametersJSON Schema
NameRequiredDescriptionDefault
rowsYesrows to append — array of row arrays
rangeNorange to append at (default A1 / first sheet)
spreadsheetIdYesthe spreadsheet id from create_sheet

TDQS

A4.2/5.0
Behavior3/5

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

Annotations already convey readOnlyHint=false, idempotentHint=false, and destructiveHint=false, so the mutation profile is established. The description adds useful access-scope context ('Hermoso can reach') and the row structure, but it does not disclose details such as append behavior at the end of the sheet, duplicate rows on retry, or any response shape.

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

Conciseness5/5

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

Two sentences, front-loaded with the core action, and every clause earns its place by explaining reachable sheets, ID provenance, and row structure. No fluff or redundancy.

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?

Given the annotations, the 100% schema coverage, and the tool's moderate complexity, the description provides enough for an agent to invoke it correctly. It covers resource eligibility and parameter sourcing. It lacks explicit guidance on what the tool returns after appending, but that is not essential for correct invocation.

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%, so the baseline is 3. The description adds value by telling the agent exactly where to get spreadsheetId ('pass the spreadsheetId from create_sheet', 'find its id with list_drive_files'), which is actionable guidance beyond the schema's generic parameter description.

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 ('Append rows') and a specific resource ('a Google Sheet'), and adds meaningful scope: sheets Hermoso created or the user handed over via the file picker. It is clearly distinguishable from sibling tools like append_to_doc and update_sheet.

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?

The description gives clear context for when to use the tool by explaining which sheets are eligible and how to obtain the spreadsheetId from create_sheet or list_drive_files. It does not explicitly contrast with alternatives like update_sheet or clear_sheet_range, so it stops short of a 5.

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

backfill_postsImport a channel’s past postsA
Idempotent
Inspect

Import this brand's PAST posts from a channel into the performance record, so 'which hook works' can draw on history rather than only on what was published since Hermoso started recording. Supports facebook, instagram, threads, youtube, tiktok, pinterest and bluesky; the others say plainly why they cannot (LinkedIn and Reddit have no enumerate-my-posts endpoint on our grant, X bills per read so it is excluded from bulk import, Google Business has had no per-post insights since 2023, and Telegram's Bot API cannot read a chat's past messages at all — nothing published before Hermoso is recoverable through a bot token). BOUNDED, RESUMABLE AND QUOTED: it runs as a DRY RUN by default and tells you how many posts it found and what reading them will cost — pass confirm:true to import, and pass the returned cursor to continue. AN IMPORTED POST IS WEAKER EVIDENCE THAN A RECORDED ONE and is labelled 'backfilled': its hook is recovered ONLY where the post matches a Hermoso creation by asset or caption. A post made outside Hermoso stays UNATTRIBUTED — it counts toward channel and format totals but never votes on which hook works. Never guess a hook from a caption. Free.

ParametersJSON Schema
NameRequiredDescriptionDefault
limitNohow many posts this page (default 50, max 200)
cursorNoresume from a previous run
channelYeswhich channel to import from
confirmNoactually import — omit for a dry run that only quotes the cost
accountRefNowhich Page / account, when the profile has more than one

TDQS

A4.4/5.0
Behavior3/5

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

Annotations already declare readOnlyHint=false, openWorldHint=true, idempotentHint=true, destructiveHint=false, so the mutation/idempotency profile is known. The description adds genuinely useful context beyond that: dry-run default with cost quoting, the resumable cursor, the 'backfilled' weaker-evidence labeling, and the unattributed-post rule. It is strong on behavioral nuance, though the 'Free' claim and evidence semantics are the main additions over structured fields.

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-loads the core action and destination before channel support and mechanics, and the ordering is logical. It is dense and lengthy, but the length is largely justified by the breadth of channel-specific exclusions, so little is genuinely wasted.

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?

For a mutation tool with five params and no output schema, the description covers the essentials an agent needs: return-relevant info (count and cost), dry-run/confirm semantics, resumability, and the evidence-strength and attribution caveats that affect downstream use.

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 all five parameters are already documented. The description ties confirm and cursor into a coherent two-step resumable workflow (quote cost, then confirm, then continue via cursor), adding flow-level meaning beyond the per-field descriptions, though it largely reinforces what the schema already states.

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 (import) and resource (this brand's past posts from a channel) plus the destination (the performance record) and the reason (so hook analysis can draw on history). This clearly distinguishes it from siblings like list_published_posts, collect_post_metrics, or search_posts.

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?

Enumerates which channels are supported and, crucially, explains for each unsupported channel (LinkedIn, Reddit, X, Google Business, Telegram) why import is impossible. It also specifies the dry-run-by-default flow and how to proceed (confirm:true, then cursor), leaving nothing to inference.

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

billing_statusBilling statusA
Read-onlyIdempotent
Inspect

Show this account's billing at a glance: current plan (id + label + the price it is ACTUALLY billed — quote plan.priceUsd per plan.period, not plan.monthlyUsd), credit balance, whether auto-reload is on, whether a card is on file, and whether YOU (this key) have ADMIN rights to change billing. Read-only, free. Call it before upgrade_plan / set_auto_reload to know what's possible — members have read-only billing. IN A SHARED TEAM WORKSPACE A MEMBER SEES THE PLAN AND THE BALANCE ONLY: the workspace owner's payment card and auto-reload belong to them and are not reported (billingScope:'member'). Never tell a member there is no card on file — the honest answer is that you cannot see it.

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

TDQS

A4.8/5.0
Behavior5/5

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

Beyond the read-only annotations, it discloses cost ('free'), admin-rights visibility, and a critical shared-team-workspace behavior: a member sees plan and balance only, card and auto-reload are hidden (billingScope:'member'), and agents must not claim no card exists. This is exactly the kind of behavioral context annotations cannot supply.

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?

The description is front-loaded with the core purpose and every major sentence adds useful routing or permission information. It is somewhat long and emphatic for a simple status tool, with minor redundancy against annotations ('Read-only'), but the length is mostly justified by the member-scope caveat.

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 fully enumerates the returned fields and the member-scoped visibility rules. Combined with annotations covering safety and idempotency, it gives an agent everything needed to call and interpret this tool.

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?

The tool takes zero parameters, so there is no parameter semantics to clarify; the baseline for a 0-param schema is 4. The description does not need to compensate for schema gaps because the input schema is empty.

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 and resource ('Show this account's billing at a glance') and enumerates exactly what is returned: plan id/label/actual billed price, credit balance, auto-reload, card on file, and admin rights. It also differentiates the tool from upgrade_plan and set_auto_reload by positioning it as the prerequisite read.

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?

It explicitly says to call it before upgrade_plan / set_auto_reload to know what's possible, notes it is read-only and free, and explains the member/workspace-owner split for billing visibility. That gives both the trigger and the permissions context for when this tool is appropriate.

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

buy_creditsBuy creditsAInspect

Out of credits? Top up with a credit PACK. Call with no argument to list the available packs (id · credits · price). If the account has a saved card and you have billing-admin rights, calling with pack quotes the exact charge and calling again with confirm:true AND the quote's quote_token charges the saved card instantly (same one-click top-up as the app — no redirect). If there's no saved card yet, you get a Stripe checkout URL to hand your human for the FIRST purchase; their card saves for one-click after that. Packs only; subscriptions are managed by a person in Settings -> Billing. IF YOU ARE AN AGENT HOLDING YOUR OWN PAYMENT CREDENTIAL, there is a third path that needs no human at all: POST /api/billing/machine-payment with a packId answers HTTP 402 carrying an MPP challenge, and grants the pack once you authorise and retry with the credential — the same packs, the same prices, the same credits. GET /api/billing/config carries a machinePayments block listing the packs with their per-credit rates and saying whether that lane is enabled on this server. Most agents do NOT have their own credential yet, so the checkout link above remains the normal path. To stop running out entirely, turn on auto-reload with set_auto_reload (admin) — low balances then top themselves up from the saved card automatically.

ParametersJSON Schema
NameRequiredDescriptionDefault
packNothe pack id to buy (e.g. pack-2k) — omit to list the available packs first
confirmNoset true to actually charge the saved card for `pack` (required for the one-click charge; ignored on the checkout-link path)
quote_tokenNothe quoteToken returned by the quote step — REQUIRED (with confirm:true) to charge; it binds the exact pack + price you quoted (10-minute validity) and makes a retried confirm idempotent

TDQS

A5/5.0
Behavior5/5

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

The description goes well beyond the sparse annotations (readOnlyHint=false, openWorldHint=true) by disclosing that the tool charges a saved card, requires billing-admin rights, produces a Stripe checkout URL on first purchase, and mentions the 10-minute validity of quote_token plus idempotency on retried confirm. It also reveals the HTTP 402 MPP challenge flow and the config endpoint, giving a full behavioral picture. No contradiction with annotations.

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

Conciseness5/5

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

The description is long but every sentence earns its place given the tool's three distinct paths, auth requirements, and exception cases. It is front-loaded with the core use case and uses typographic emphasis (caps, bold) to highlight the machine-payment lane. The structure flows logically from normal flow to edge cases to alternative tools, keeping it scannable despite its length.

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 must explain result formats, and it does: list returns packs with id·credits·price, quote yields quote_token, first purchase yields a Stripe checkout URL, and machine-payment answers with an HTTP 402 MPP challenge. It also covers prerequisites, auth, exclusions, and sibling alternatives, making the tool fully callable without external documentation.

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

Parameters5/5

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

Though the schema covers 100% of parameters, the description adds critical workflow semantics: 'Call with no argument to list the available packs,' explains that pack selects a pack id and triggers a quote, confirm:true actually charges, and quote_token binds the exact price, has 10-minute validity, and makes retries idempotent. This is precisely the kind of meaning that lets an agent invoke the tool correctly without guessing.

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 clearly identifies the resource (credit packs) and the action (top up/buy), and explicitly delineates three distinct paths: listing packs, one-click charge with quote_token+confirm, and the machine-payment lane for credential-holding agents. It also distinguishes itself from subscriptions and names the sibling set_auto_reload, leaving no ambiguity about what this tool does.

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?

The description gives explicit when-to-use guidance: it starts with 'Out of credits? Top up with a credit PACK,' explains the exact call sequence for quoting and confirming, notes when the checkout URL applies vs. the one-click path, and explicitly routes auto-reload to set_auto_reload ('To stop running out entirely...'). It also states that subscriptions are handled by a person, preventing misuse.

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

call_toolRun any Hermoso tool by nameA
Destructive
Inspect

Run ANY Hermoso tool by name — including the paid-campaign, analytics and channel-admin tools that are not in this session's starting list — with the same permissions, the same account and the same result as calling it directly. Get the exact name and its args from find_tools first. This is the route on hosts that cannot reload their tool list mid-conversation (claude.ai, ChatGPT): enable_tools switches a group on server-side, but such a host keeps the list it fetched at connect time. Arguments are validated against the tool's own schema and a mistake is answered with the expected parameters, not a silent default. Refused by name, with the way out, when the tool needs a connector this workspace has not made or is withheld by the host's own policy.

ParametersJSON Schema
NameRequiredDescriptionDefault
argsNothe tool's arguments as an object, exactly as its own schema takes them
nameYesthe tool name exactly as find_tools returned it, e.g. create_meta_lead_form

TDQS

A4.9/5.0
Behavior5/5

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

Beyond the annotations, the description discloses that invoking through call_tool uses the same permissions, account, and result as a direct call; that arguments are validated against the target tool's schema; that invalid arguments return the expected parameters rather than silently defaulting; and that tools requiring unavailable connectors or withheld by host policy are refused by name with an actionable way out. This is rich, non-obvious behavioral context.

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

Conciseness5/5

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

Every sentence earns its place: purpose, source of parameter values, host-specific rationale, validation behavior, and refusal conditions. The most important action and scope are front-loaded, and the length is justified by the complexity and risk of a generic tool dispatcher.

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 the tool's complexity as a dynamic dispatcher and the absence of an output schema, the description fully covers how to invoke it, how to source parameters, what validation behavior to expect, and when/why it may refuse. The statement that results match a direct call also informs the agent about return behavior without needing an output schema.

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. The description adds operational meaning by directing the agent to obtain the exact name and args from find_tools and by explaining that args must match the target tool's own schema, with validation errors surfacing expected parameters. This goes slightly beyond the schema's bare definitions.

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 and resource: 'Run ANY Hermoso tool by name', and immediately clarifies its unique scope—it can invoke tools not in the session's starting list, including paid-campaign, analytics, and channel-admin tools. This clearly distinguishes it from the many specific sibling tools and from enable_tools.

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?

The description gives explicit when-to-use guidance: it is the route on hosts like claude.ai and ChatGPT that cannot reload their tool list mid-conversation. It also names the alternative (enable_tools) and explains why that alternative fails on such hosts, plus instructs the agent to get the exact name and args from find_tools first.

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

cancel_scheduledCancel a scheduled postA
DestructiveIdempotent
Inspect

Remove a queued post before it goes out. Get the id from list_scheduled. Only works while it is still queued — something already published cannot be unsent (use manage_meta_post to delete a Facebook/Instagram post after the fact).

ParametersJSON Schema
NameRequiredDescriptionDefault
idYesthe scheduled post id from list_scheduled
brandNoWHICH PROFILE this post is in — id or exact name from list_brands; needed when it is not the profile this connection is pinned to. A name that matches no profile, or two, is REFUSED.

TDQS

A4.5/5.0
Behavior4/5

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

Annotations already declare destructiveHint=true, idempotentHint=true, and openWorldHint=true, so safety and idempotency are covered. The description adds genuinely new behavioral context: the operation is state-dependent and silently inapplicable once a post has published. It stops short of describing auth/brand requirements, but the added constraint is meaningful.

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

Conciseness5/5

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

Three tight clauses, front-loaded with the core action, then the id source, then the state constraint and alternative. No sentence is 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?

For a destructive mutation with no output schema, the definition covers action, prerequisite lookup, state constraint, and the fallback tool. Combined with annotations that carry the safety profile, 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.

Parameters3/5

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

Schema coverage is 100% and both parameters are fully documented in the schema, including the 'brand' profile-matching rule. The description only reinforces where 'id' comes from, adding nothing beyond the schema, so the baseline 3 applies.

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 and resource — 'Remove a queued post before it goes out' — and clearly distinguishes this from siblings like reschedule_post, retry_scheduled, and duplicate_scheduled, which act on the same scheduled-post resource but do not remove it.

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?

It gives explicit preconditions ('Only works while it is still queued'), a clear when-not ('something already published cannot be unsent'), and names the alternative for that case ('use manage_meta_post to delete a Facebook/Instagram post after the fact'). It also routes the agent to list_scheduled to obtain the id.

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

change_voiceChange narrator voiceAInspect

Swap the narration of a finished video into a different voice — keeps the performance, lip-sync, and background sound. Use when the user likes the video but wants a different narrator voice; use dub_video only for language translation. Paid; returns the served URL.

ParametersJSON Schema
NameRequiredDescriptionDefault
videoYesthe source video URL
voiceNotarget narrator voice preset name, e.g. 'Aria', 'George', 'Rachel', 'Sarah', 'Brian', 'Charlotte' (defaults to a warm female read). A saved VOICE CLONE of the user's own voice counts as a preset here — name it the way it is saved on their cast

TDQS

A4.4/5.0
Behavior4/5

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

Annotations declare it is not read-only, open-world, not idempotent, and not destructive. The description adds behavior beyond annotations: it preserves performance, lip-sync, and background sound, notes it is paid, and says it returns the served URL. It does not mention permission or billing amount details, but the added context is meaningful.

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

Conciseness5/5

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

Two sentences with zero waste. The main operation and preservation behavior are front-loaded, followed by routing guidance and a brief return note.

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?

With no output schema, the description explains the return value as the served URL. It also covers preservation behavior and cost status. Missing details such as credit amount or processing time are minor for a tool whose annotations and schema are otherwise complete.

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

Parameters3/5

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

Schema coverage is 100%, and the schema already documents both parameters thoroughly, including default voice behavior and voice-clone naming. The description adds no further syntax or format detail beyond what the schema provides, so baseline 3 applies.

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 and resource: swapping narration of a finished video into a different voice. It also distinguishes the operation from sibling dub_video, which is reserved for translation.

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?

It gives explicit usage conditions: 'Use when the user likes the video but wants a different narrator voice' and directs language translation to dub_video. No inference is needed to choose between them.

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

check_ad_policyCheck ad policyAInspect

Pre-flight ad copy against Meta's REAL, live Advertising Standards before you run it — a flat 1-credit check. Pulls Meta's actual policy pages and returns a verdict (pass / fix / block) where every flagged issue QUOTES Meta's own policy text verbatim plus a compliant rewrite that keeps the sell. It's a check, not an edit — it never changes the creative. Especially worth running for regulated-adjacent categories (health/supplements, weight-loss or beauty results claims, finance/crypto/insurance, alcohol, dating, gambling) or ANY strong/absolute/guaranteed claim.

ParametersJSON Schema
NameRequiredDescriptionDefault
copyYesthe ad copy / script / on-screen text to check
claimsNothe claims / proof points the ad makes
categoryNothe product category — helps pick the relevant policy pages
imageDescriptionNoa description of the creative / image when relevant

TDQS

A4.4/5.0
Behavior5/5

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

Discloses behavior the annotations don't: a flat 1-credit cost, that it pulls Meta's live policy pages, the exact return shape (pass/fix/block verdict with verbatim policy quotes and a compliant rewrite), and that it is non-mutating for the creative. This is rich context beyond the readOnlyHint/openWorldHint flags and compensates for the absent output schema.

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-loads the core purpose and the cost, then layers in return behavior and use cases. Every sentence earns its place, though the emphasis-heavy single paragraph (caps, em-dashes) is slightly longer than needed.

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?

Despite having no output schema and four parameters, the description covers cost, data source, return verdict, and non-mutation behavior, plus which inputs matter most. An agent has everything needed to decide to call it and to call it correctly.

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

Parameters3/5

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

Schema description coverage is 100%, so all four parameters are already documented in the schema, warranting the baseline 3. The description adds only marginal semantic help by implying the category parameter should map to regulated-adjacent verticals; it adds no format or value guidance beyond the schema.

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+resource ('Pre-flight ad copy against Meta's REAL, live Advertising Standards') and the exact deliverable (a pass/fix/block verdict). It clearly distinguishes itself from sibling generation/analysis tools by framing itself as a pre-publication policy gate and a 'check, not an edit.'

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 strong when-to-use guidance: run it before you publish, and especially for regulated-adjacent categories and absolute/guaranteed claims. It also draws a clear functional boundary ('it never changes the creative'). It stops short of naming a competing sibling (e.g., score_ad, plan_ad) or stating when not to use it, so it lacks the explicit alternatives of a 5.

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

clear_sheet_rangeClear a range in a Google SheetA
DestructiveIdempotent
Inspect

Empty a range of cells in a Google Sheet, leaving the rows themselves in place. DESTRUCTIVE: call it WITHOUT confirm first and nothing is cleared — you get back the real number of filled cells in that exact range. Show the user that number, get an unambiguous yes, then call again with confirm:true AND confirmCells set to it. The echo is not ceremony: it is what catches naming A1:Z1000 when you meant A1:Z10, which is the mistake that actually happens. There is deliberately NO default range. The clear is read back and reported as confirmed only if the range really is empty afterwards. To remove a whole tab instead, use manage_sheet_tabs.

ParametersJSON Schema
NameRequiredDescriptionDefault
rangeYesthe range to clear, e.g. "A2:D50" or "Sheet1!A2:D50"
confirmNo
sheetUrlNo
confirmCellsNoecho back the filled-cell count the unconfirmed call reported
spreadsheetIdNo

TDQS

A4.9/5.0
Behavior5/5

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

Annotations already flag destructiveHint=true and readOnlyHint=false, so the destructive nature is known. The description adds valuable behavioral context beyond that: the two-call confirm flow, the echo mechanism (confirmCells) that catches naming mistakes, the fact that there is deliberately NO default range, that the clear is read back and reported as confirmed only if the range is truly empty, and that rows are left in place. This is rich, non-obvious behavioral disclosure that goes far beyond the structured data.

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

Conciseness5/5

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

Every sentence earns its place. The description front-loads what the tool does, then explains the safety protocol, the echo rationale, the no-default-range design, and the alternative tool — all in roughly 110 words. Nothing is wasted or redundant with the schema or annotations.

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?

For a destructive mutation tool with 5 params, no output schema, and only partial schema coverage, this description is remarkably complete. It covers the operation, the safety workflow, the exact parameters to set in each call, a common failure mode, the absence of a default, the post-condition verification, and points to the sibling for a related but different task. There is no meaningful gap an agent would face when invoking 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 description coverage is only 40%: 'range' and 'confirmCells' have descriptions, but 'confirm' and 'sheetUrl' and 'spreadsheetId' do not. The description partially compensates by explaining confirm (the two-call protocol) and confirmCells (echo the filled-cell count the unconfirmed call reported). It doesn't explain the relationship between sheetUrl and spreadsheetId, but the core usage semantics of the non-obvious parameters are well covered. With 5 params and 40% coverage, the description carries a large share of the burden and does so effectively.

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 clearly states the verb 'Empty a range of cells in a Google Sheet' and specifies a resource (cells in a range) and scope (leaving rows in place). It distinguishes itself from the sibling manage_sheet_tabs by explicitly naming it as the alternative for removing a whole tab, and from other sheet operations by focusing specifically on clearing a range.

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?

The description gives explicit when-to-use and how-to-use guidance: call once without confirm to get the filled-cell count, show the user that number, get confirmation, then call again with confirm:true AND confirmCells set. It also explicitly names the alternative for a different task: 'To remove a whole tab instead, use manage_sheet_tabs.' This is exactly the kind of exclusionary guidance that helps an agent decide.

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

clip_videoClip a long videoAInspect

Cut ONE long video into several RANKED, ready-to-post short clips (podcast, webinar, interview, talk, long ad cut -> Reels/Shorts/TikTok). Transcribes with timestamps, picks the strongest SELF-CONTAINED moments, then cuts + reframes each with ffmpeg — no video model renders anything, so it is fast and cheap. THE VERTICAL REFRAME IS SUBJECT-AWARE: one cheap vision call per clip (billed as its own event) picks a SINGLE crop offset held for the whole clip, so a speaker sitting camera-left is not cropped out and the framing never drifts inside a clip; with nothing to discard or no single subject it stays dead centre — read reframedToSubject and each clip's reframeWhy back rather than assuming either way. ACCEPTS: a YouTube link (or Vimeo / Loom / Dailymotion / Streamable / Rumble / Wistia / Twitch / TED), a direct https .mp4/.mov/.webm, or a Hermoso /generated/ URL (upload_file turns a local file into one). NOT supported: TikTok / Instagram / Facebook links, and anything age-restricted, private, members-only, geo-blocked or still LIVE — those fail fast with the real reason and are fully refunded, so ask for a direct file or an upload rather than retrying. Source ~15s to ~600MB; only the first ~40 minutes is analysed (truncated:true says so). Cost: a ~7-credit hold settled to the exact transcription + encode cost, plus the clip-selection model's tokens as their own small event. RETURNS clips[] — each its OWN served mp4 URL, title, hook, ready-to-post caption, 0-100 score and source timecode. SUBTITLES ARE BURNED IN BY DEFAULT (slim white CAPS, thin black outline, bottom safe band, no box) because short-form is watched on mute; captions:false for clean footage. TIMING IS APPROXIMATE, NOT WORD-LEVEL — cues follow the transcript's per-sentence timestamps, split by character count; never promise frame-accurate sync. captionsBurned counts the clips that really carry a burned track and captionNote says why any are bare.

ParametersJSON Schema
NameRequiredDescriptionDefault
countNohow many clips to cut, 1-8 (default 4)
videoYesthe long video to clip — a YouTube/Vimeo/Loom/Dailymotion/Streamable/Rumble/Wistia/Twitch/TED watch URL, a direct https .mp4/.mov/.webm, or a Hermoso /generated/ URL
captionsNoburn subtitles into every clip. DEFAULT TRUE — a clip cut from a podcast or a talk is watched on mute, and the words are the product. Set false for clean footage. A clip whose window carries no readable speech is delivered bare rather than captioned with a guess, and the result says which.
aspectRatioNoclip shape: '9:16' (default), '1:1', '16:9', any 'W:H', or 'keep' for the source framing

TDQS

A4.5/5.0
Behavior5/5

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

Annotations only say readOnly=false/openWorld/idempotent=false/destructive=false. The description adds real behavioral context the annotations cannot: the ~7-credit hold settled to actual cost plus a separate vision-call event, fast/cheap because no video model renders, only first ~40 min analysed with a truncated flag, failed/unsupported inputs 'fail fast... and are fully refunded', and captions burned in by default. That is exceptional depth beyond the structured hints.

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

Conciseness3/5

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

Extremely information-dense and mostly front-loaded, but the wall of CAPS-emphasised clauses, parenthetical engine lists, and refund/truncation/caption caveats make it hard to scan; several sentences are doing three jobs at once. Some of this could be trimmed without losing the substance, so it lands at adequate-but-overstuffed.

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?

A no-output-schema mutation-ish tool that nonetheless explains inputs accepted/rejected, cost model and refund behavior, truncation, reframe logic (subject-aware single-offset crop, centre fallback, reframedToSubject/reframeWhy fields), captions default, and return shape (clips[] with URL/title/hook/caption/score/timecode). Nothing an agent needs to invoke it correctly is missing.

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%, so the baseline is 3. The description still adds real value: it explains the default and rationale for captions:false, notes the SUBTITLES default in prose, and mentions captionsBurned/captionNote outcomes tied to the captions parameter. It adds meaning beyond the schema rather than repeating it, warranting a 4.

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 ('cut ONE long video into several RANKED, ready-to-post short clips') and resource, and the parenthetical domain list (podcast/webinar/interview/talk/ad -> Reels/Shorts/TikTok) pins the exact use case. A reader can distinguish it from reframe_video, edit_video, and stitch_video without opening schemas.

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 explicit accept/reject conditions ('YouTube link... or direct https .mp4', 'NOT supported: TikTok / Instagram / Facebook links, and anything age-restricted, private...'), including the correct fallback ('ask for a direct file or an upload rather than retrying'). It does not name sibling alternatives (e.g. reframe_video for an existing short, edit_video) as when-to-use forks, so it stops just short of 5.

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

clone_staticClone a static adAInspect

One-click STATIC-AD CLONE (the web app calls it Clone): rebuild a competitor/reference STATIC (image) ad as an on-brand version — SAME layout, composition and energy, but YOUR product, brand colours, logo and voice, with every trace of the source brand removed. Pass imageUrl = the static ad image to clone. Uses your saved brand (pass brandId to target a specific profile — that switches this key's active profile like use_brand). IMAGES ONLY — for a video ad use clone_video with its link, then render_ad. Bills as one image generation.

ParametersJSON Schema
NameRequiredDescriptionDefault
brandIdNoa profile id/name from list_brands to clone for; omit to use the active profile
imageUrlYesthe URL of the static ad image to clone. Using a real person’s face, likeness or voice confirms you are them or have their consent, take full, unlimited responsibility for its use and will cover any claim against Hermoso (hermoso.ai/terms). Paid plan only; recorded.

TDQS

A4.6/5.0
Behavior4/5

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

Annotations declare non-read-only, non-idempotent, open-world behavior, and the description adds genuinely useful context beyond them: billing cost ('Bills as one image generation'), a side effect ('switches this key's active profile like use_brand'), and the guarantee that source branding is removed. It does not describe failure modes or output format, but the added billing and side-effect disclosure is strong.

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?

Core purpose is front-loaded in the first clause and alternatives follow. It is somewhat dense with capitalized emphasis and a long trailing billing sentence, but nearly every sentence carries distinct information (scope, parameter, alternative, billing).

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 still conveys what is produced (on-brand static ad, source branding removed), the billing model, the profile side effect, and the sibling alternative. Nothing an agent needs to call it correctly is missing.

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 baseline is 3, but the description adds meaning beyond the schema: it clarifies imageUrl is the static ad image to clone and that brandId has the side effect of switching the active profile like use_brand — information not present in the parameter descriptions.

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 ('STATIC-AD CLONE ... rebuild a competitor/reference STATIC (image) ad as an on-brand version') and explicitly distinguishes itself from clone_video. An agent can identify exactly what this produces (an on-brand static ad) and what it is not.

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 when-to-use ('for a static image ad'), when-not ('IMAGES ONLY — for a video ad use clone_video with its link, then render_ad'), and how to target a specific brand profile. Alternatives are named with the condition that selects them.

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

clone_videoClone a video for your brandAInspect

Remake a video you like FOR THIS BRAND from its link — a TikTok, Instagram Reel, Facebook video or reel, X post, YouTube Short or video, or a direct video file URL. Hermoso WATCHES it first (frames across the whole clip plus a transcript of the voiceover, on-screen text and cut map), then plans a storyboard that keeps its hook device, structure, jump cuts and pacing while swapping in THIS brand's product, cast, setting and words — never the original's words, face or brand. The new ad MATCHES THE ORIGINAL'S LENGTH (capped at 60s) unless durationSeconds is given. Renders nothing: pass the returned creative to render_ad to make the video. Costs the plan plus about 2 credits to read the link. The reply says exactly what was watched, and when a platform will not hand over the footage (YouTube sometimes refuses servers) it says the plan rests on the captions and thumbnail only. For a local file, upload_file it first and pass the URL.

ParametersJSON Schema
NameRequiredDescriptionDefault
urlYesthe video to clone — a TikTok / Instagram Reel / Facebook / X / YouTube link, or a direct https video file URL
brandNobrand name or profile object; OMIT to use the workspace's saved brand (see get_brand)
changesNowhat to change or keep from the original, in the user's words (e.g. "same hook but in a gym", "keep the jump cut, older creator")
productNowhat the new ad sells, plus any angle or offer; omit to use the saved brand's product
languageNolanguage for the new ad's script and copy — default English
durationSecondsNooverride the length in seconds; omit to match the original

TDQS

A4.6/5.0
Behavior4/5

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

With annotations only declaring readOnlyHint=false and openWorldHint=true, the description carries real weight: it discloses that Hermoso WATCHES the clip (frames, transcript, cut map), states the cost (~2 credits), warns that YouTube sometimes refuses server fetches and the plan then rests on captions/thumbnail only, and caps length at 60s. This is unusually rich behavioral disclosure; the only thing missing is any error/failure mode beyond the YouTube case.

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 a dense single block, front-loaded with the core action and brand-scoping, then moves to behavior, cost, and edge cases. Every sentence does work, but the run-on middle section (watching behavior + storyboard rules) could be split for faster scanning.

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?

There is no output schema, so the description compensates by describing the reply ("says exactly what was watched") and the degraded-caption case. Combined with the cost note and the render_ad handoff, an agent has everything needed to call this and act on the result.

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%, so the baseline is 3, but the description adds meaning beyond the schema: the 60-second cap and the default of matching the original's length unless durationSeconds is given, plus the brand-omission fallback. That is genuine added value over the structured fields.

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?

"Remake a video you like FOR THIS BRAND from its link" gives a specific verb (clone/remake) and resource (a source video), with an explicit scope qualifier (for this brand). It is immediately distinguishable from siblings like dub_video, clip_video, reframe_video and analyze_video, which are never named but clearly occupy different verbs.

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?

It routes the agent explicitly: renders nothing, so pass the returned creative to render_ad; for a local file, use upload_file first; omit brand to fall back to get_brand's saved brand. These are concrete when-to-use and what-to-do-next instructions with named alternatives.

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

collect_post_metricsRead how the recorded posts performedA
Idempotent
Inspect

Fetch fresh performance numbers for this brand's recorded posts and store them as a time-series. Metrics ACCRUE, so a post is read at ~24 hours and again at ~7 days; this collects whichever readings are due and skips the ones already taken. A channel that cannot report a metric records it as ABSENT with the reason — never as zero — and a read that fails is recorded as 'could not tell', which contributes to nothing. X IS SKIPPED BY DEFAULT because X bills us per API call: pass includeMetered:true to include it, and tell the user it costs credits BEFORE you do. The skip is always reported so a channel missing from the numbers is never mistaken for one that performed badly. Free except for X.

ParametersJSON Schema
NameRequiredDescriptionDefault
maxNocap how many posts to read in this run (default 40)
brandNoWHICH PROFILE the post lives in — id or exact name from list_brands (a shared workspace: its profile id). Needed when it was published in a profile this connection is not pinned to; applies to THIS CALL ONLY. A name that matches no profile, or two, is REFUSED and nothing is done.
remeasureNoALSO re-read posts older than 7 days whose every reading came back empty or failed — use after post_performance reports posts "read but empty", or once a channel's reader has been fixed. Otherwise those windows stay closed.
includeMeteredNoalso read X, which BILLS CREDITS per post read — ask the user first

TDQS

A4.6/5.0
Behavior5/5

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

Discloses behaviors well beyond the annotations: absence is recorded as ABSENT with a reason rather than zero, failed reads become 'could not tell' and contribute nothing, and the metered-X skip is always reported so a missing channel isn't misread as poor performance. The write-to-time-series nature is also stated, which is consistent with readOnlyHint=false and idempotentHint=true.

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-loads the purpose, then layers the accrual model, absent/failed handling, and the metered-X caveat in a logical order with no filler sentences. It is on the longer side with heavy capitalization emphasis, which is slightly dense but each sentence carries distinct information.

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 still explains what happens to results (stored as a time-series), how missing data is represented, and how skips are surfaced. Nothing an agent needs to invoke this correctly or interpret its effects is missing.

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 adds real value by explaining the credit cost of includeMetered and the user-notification requirement, which the schema only partially covers. It does not add much for 'max' or 'brand' beyond the schema text, keeping it from a 5.

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 ('Fetch fresh performance numbers for this brand's recorded posts and store them as a time-series'), and the accrual/skip behavior makes it clearly distinct from a plain metrics reader like post_performance. An agent can tell what it does and how it differs without opening the 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 clear context for use: readings are due at ~24h and ~7d, only due readings are collected, and X is skipped unless includeMetered:true is passed (with an instruction to tell the user about credits first). It stops short of explicitly naming a sibling alternative for the 'just show me the numbers' case, so it is strong context without full when-not routing.

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

competitor_teardownCompetitor teardownAInspect

Tear a competitor's ad strategy down into an actionable playbook: their opening-hook MIX, longest-running campaign THEMES, the WHITE SPACE nobody in their set runs, 2-3 render-ready COUNTER-PLAYS, and the territories they own that you should avoid. Pass competitor {name, domain?}. CONTRACT: supply ads (raw ad objects from a prior pull_competitor_ads / search_meta_ads call) to tear exactly those down, OR omit ads and this pulls the competitor's real Meta ads first (spends a credit or two, longest-running = proven winners). Auto-tailors the white space + counter-plays to YOUR saved brand. Spends credits (free when you pass ads).

ParametersJSON Schema
NameRequiredDescriptionDefault
adsNoad objects to tear down (from pull_competitor_ads / search_meta_ads). Omit to auto-pull their Meta ads first.
languageNooutput language (default English)
competitorYesthe competitor to tear down

TDQS

A4.5/5.0
Behavior4/5

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

Annotations already signal a non-read-only, open-world, non-idempotent, non-destructive call, and the description adds materially: it spends credits, is free when ads are passed, and auto-pulls Meta ads otherwise. It does not describe failure modes or auth requirements, keeping it short of a 5.

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-loads the outcome, then the CONTRACT, then the credit note. The all-caps emphasis is slightly noisy and the sentence is dense, but nearly every clause adds actionable information.

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 complex, non-read-only tool with no output schema, the description covers the deliverables, the input contract, and the cost. It is largely self-sufficient, missing only explicit mention of what a failed/empty auto-pull returns.

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 baseline is 3, but the description adds real meaning: `ads` is defined as raw ad objects from a prior pull and tied to the tear-down contract, and `competitor` {name, domain?} is explained with domain sharpening the auto-pull match.

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 (tear down) and resource (competitor ad strategy), and enumerates the concrete outputs: hook mix, campaign themes, white space, counter-plays, and owned territories. This is clearly distinguishable from every sibling, including the ad-pull tools it depends on.

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?

Gives an explicit contract: pass `ads` from a prior pull_competitor_ads/search_meta_ads call, or omit to auto-pull. Names the two upstream siblings by name and explains the consequence of each path (credit spend vs. proven winners).

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

connect_connectorConnect a paste-a-key accountA
DestructiveIdempotent
Inspect

Connect an account that links with a KEY rather than a sign-in screen, from here, with no browser: AppLovin Ads, Stripe, ChatGPT Ads, Apple Ads, Bluesky, Telegram, Bing Webmaster Tools, PostHog, Mixpanel, Amplitude, Slack, Discord, Webhook. Pass provider and fields in that provider's own field names (listed below, * = required). The key is checked live with the provider before anything is saved, exactly as the app's Connectors page checks it, and the reply is read back from the saved connection. OFFER BOTH WAYS AND LET THE USER CHOOSE: a key pasted into this chat stays in this conversation's history, while pasting it in the app (Workspace > Connectors, or the one-click link https://app.hermoso.ai/?connect=) keeps it out of the chat. Hermoso never repeats a submitted key back. An account that connects through the provider's own sign-in screen (OAuth) cannot be connected here: this answers with the link to hand the user instead. Apple Ads with no key material first generates a signing key pair and returns the public key to register with Apple plus a setupToken to send back. Fields: applovin_ads {reportKey, eventKey, apiKey, capiKey, accountId} · stripe {apiKey*} · openai_ads {apiKey*} · apple_ads {clientId, teamId, keyId, privateKey, setupToken, orgId} · bluesky {identifier*, appPassword*, pds} · telegram {token*} · bing_webmaster {apiKey*} · posthog {apiKey*, region, host, projectId} · mixpanel {username*, secret*, projectId*, region, workspaceId} · amplitude {apiKey*, secretKey*, region, host} · slack {webhookUrl*} · discord {webhookUrl*} · webhook {webhookUrl*}.

ParametersJSON Schema
NameRequiredDescriptionDefault
fieldsNothat provider's own field names and values, e.g. {"apiKey":"…"}; the names for each provider are in the description
providerYesthe connector id: applovin_ads, stripe, openai_ads, apple_ads, bluesky, telegram, bing_webmaster, posthog, mixpanel, amplitude, slack, discord, webhook

TDQS

A4.9/5.0
Behavior5/5

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

Annotations declare mutation semantics (destructiveHint=true, idempotentHint=true, openWorldHint=true) but say nothing about validation, credential safety, or special-case flows. The description adds all three: the key is validated live with the provider before anything is saved, the reply is read back from the saved connection, Hermoso never echoes a submitted key, pasting in chat leaves the key in conversation history, and Apple Ads without key material first generates a key pair and returns a public key plus a setupToken. That is substantial behavior beyond the annotation set, with no contradiction.

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 the length is driven by 13 providers with non-overlapping field sets and by genuine special cases (Apple Ads key generation, OAuth fallback), so nearly every clause earns its place. The purpose is front-loaded before the provider list and the credential-handling policy. The dense single-paragraph layout and mid-sentence ALL-CAPS instruction cost it a point on structure.

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 still tells the agent what comes back ('the reply is read back from the saved connection') and what the Apple Ads special case returns (public key + setupToken). Combined with the provider/field inventory, live-validation behavior, and the OAuth fallback, nothing an agent needs to call this correctly is missing.

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

Parameters5/5

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

The schema only says fields are 'that provider's own field names and values' and defers explicitly to the description, so the description is the sole source of per-provider parameter meaning. It enumerates each provider's exact field names with '*' marking required ones (e.g. stripe {apiKey*}, mixpanel {username*, secret*, projectId*, region, workspaceId}), which is far more than the 100%-covered top-level schema provides.

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+resource with scope: 'Connect an account that links with a KEY rather than a sign-in screen, from here, with no browser', then enumerates the exact 13 supported providers. It explicitly carves itself away from the OAuth path ('An account that connects through the provider's own sign-in screen (OAuth) cannot be connected here'), so an agent can distinguish it from sibling connector tools like list_connectors, set_connector_accounts, and leave_connector without opening 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 Guidelines5/5

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

Gives explicit when-to-use and when-not-to-use guidance: use this for key-based providers, and if the provider is OAuth-only, 'this answers with the link to hand the user instead'. It even prescribes agent behavior for the credential-handling decision ('OFFER BOTH WAYS AND LET THE USER CHOOSE') and contrasts the two paste locations, which is the real routing decision the agent must make.

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

convert_onedrive_fileConvert a OneDrive file to PDF or JPGA
Read-onlyIdempotent
Inspect

Turn a file already in the user’s OneDrive into a PDF or a JPG — Microsoft does the conversion on its own servers, so nothing is re-encoded here and nothing is lost in a screenshot. It reads about 130 source formats, which is the point: PowerPoint and Word decks, Excel, Photoshop PSD, Illustrator AI, Sketch, 3D (fbx/glb/obj), video (mp4/mov/webm), HEIC from an iPhone, and the raw camera formats (CR2, NEF, ARW, DNG) that nothing else in this product can open. Use it to turn a client’s deck into images you can actually put in an ad, to get a usable JPG out of a designer’s PSD or a photographer’s raw file, or to hand someone a PDF of a spreadsheet. CONVERTING TO JPG REQUIRES BOTH width AND height — Microsoft refuses the call without them. The result is stored at a durable Hermoso URL you can pass straight to a render or a post; Microsoft’s own conversion link expires within minutes, so do not hand that one to anyone. Needs OneDrive connected — no new permission.

ParametersJSON Schema
NameRequiredDescriptionDefault
widthNoREQUIRED for jpg — output width in pixels
fileIdYesthe OneDrive item id, from list_onedrive_files
formatNodefault pdf
heightNoREQUIRED for jpg — output height in pixels

TDQS

A4.6/5.0
Behavior5/5

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

Goes well beyond the annotations: conversion runs on Microsoft's servers, nothing is re-encoded locally, output lands on a durable Hermoso URL while Microsoft's link expires in minutes, and OneDrive must be connected with no new permission needed. That is exactly the kind of operational context annotations don't cover.

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 action and the hard JPG constraint, then use cases. It is long and includes a lengthy format enumeration plus some flourish ('which is the point'), but nearly every sentence carries selection-relevant information.

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?

No output schema exists, and the description compensates by explaining where the result is stored (durable Hermoso URL) and warning against the expiring Microsoft link. Combined with the stated auth requirement, an agent has everything needed 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 baseline is 3, but the description adds rationale the schema lacks — explaining that Microsoft refuses the JPG call without both width and height — reinforcing why the constraint matters rather than just restating it.

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 (convert) and resource (a file in the user's OneDrive) with the target formats (PDF/JPG), and it clearly distinguishes itself from list_onedrive_files/get_onedrive_file by describing transformation rather than retrieval.

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?

Provides concrete when-to-use scenarios (client deck to ad images, PSD/raw to JPG, spreadsheet to PDF) and states a hard prerequisite (OneDrive connected, JPG needs width+height). It doesn't explicitly route to a sibling alternative, so it falls short of a 5.

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

create_brandCreate a profileA
Idempotent
Inspect

Add a NEW profile (a brand, client, creator or personal workspace) and switch to it. Each has its OWN brand details, memory, swipefile, Library, avatars, skills, playbooks and connectors; nothing leaks between them. Use draft_brand to FILL it and update_brand to edit it. Re-running with the same name returns the existing profile instead of a duplicate. Free (the ~50-credit research cascade only starts when you then run draft_brand).

ParametersJSON Schema
NameRequiredDescriptionDefault
nameYesthe name for the new profile (a brand, client or creator name)
activateNoswitch this connection to the new profile (default true); everything you do next scopes to it

TDQS

A4.3/5.0
Behavior4/5

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

Annotations already declare idempotentHint=true and destructiveHint=false, but the description adds real context beyond them: profile isolation ('nothing leaks between them'), the concrete idempotency semantics (same name returns the existing profile), and the cost model. It does not describe what the response body contains, which is the main remaining gap.

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 action and scope, then alternatives, then the idempotency and cost caveats; every sentence carries information. Heavy capitalization for emphasis is slightly noisy but does not bloat the text.

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 two-parameter creation tool with no output schema, the description covers purpose, isolation, idempotency, cost, and sibling routing. It stops short of saying what the returned profile object looks like or what identifier the agent should reuse, which is the only notable omission.

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

Parameters3/5

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

Schema description coverage is 100% and both parameters (name, activate) are documented in the schema, so the baseline of 3 applies. The description reinforces the switch semantics ('switch to it') but adds no syntax or format detail beyond the schema.

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 ('Add a NEW profile') and enumerates the domain it covers (brand, client, creator, personal workspace), which lets an agent distinguish it from get_brand, list_brands, use_brand, and update_brand without opening a schema. The parenthetical definition of what a profile is adds precision rather than restating the title.

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?

Explicitly routes the agent to the sibling tools that continue the workflow ('Use draft_brand to FILL it and update_brand to edit it') and gives the cost condition that selects between them (free now; ~50-credit cascade only when draft_brand runs). The duplicate-name behavior also tells the agent it is safe to call speculatively.

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

create_docCreate a Google DocAInspect

Create a new Google Doc in the user’s Drive with a title + optional body text — e.g. export ad copy, a creative brief, or a report. Returns the document id + URL. Needs Google Drive connected (Settings > Connectors > Google Drive — one connection covers Drive, Sheets and Docs).

ParametersJSON Schema
NameRequiredDescriptionDefault
textNobody text to insert
titleNodocument title

TDQS

A4.1/5.0
Behavior4/5

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

Annotations already carry the safety profile (readOnlyHint=false, destructiveHint=false, idempotentHint=false), so the bar is lower. The description adds value beyond annotations by disclosing the auth/connection requirement (Settings > Connectors > Google Drive, one connection covers all three) and the return payload (document id + URL), neither of which the annotations provide. No contradiction with the write-creating-new semantics.

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

Conciseness5/5

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

Three sentences, no filler. The core action and scope are front-loaded in sentence one, the return value in sentence two, and the prerequisite in sentence three. Every sentence earns its place, and the example list is compact but illustrative.

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?

For a simple 2-parameter creation tool with full schema coverage and annotations covering the safety profile, nothing an agent needs is missing: inputs are schema-documented, the return value is stated (important since there is no output schema), the auth prerequisite is explicit, and non-idempotency is covered by annotations. The description is fully self-sufficient for correct invocation.

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

Parameters3/5

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

Schema description coverage is 100% — both `text` ('body text to insert') and `title` ('document title') are already documented, so the baseline is 3. The description adds only marginal value: the word 'optional' in 'optional body text' (consistent with 0 required params) and content-type examples for the body. It doesn't go beyond the schema in any substantive way.

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 ('Create'), resource ('a new Google Doc in the user's Drive'), and inputs ('title + optional body text'), with concrete content examples (ad copy, creative brief, report). The qualifier 'new' plus the stated return of 'document id + URL' clearly distinguishes it from the doc-modification siblings (append_to_doc, update_doc, read_doc) without needing to inspect those schemas.

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

Usage Guidelines3/5

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

The description implies usage through content-type examples and states an explicit prerequisite ('Needs Google Drive connected...'). However, it never names alternatives or exclusion conditions — e.g., it doesn't say 'use append_to_doc for existing docs' or 'use create_sheet for spreadsheets' — leaving the actual selection rule ambiguous given the large sibling list.

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

create_drive_folderCreate a Drive folderAInspect

Create a folder in the user’s Google Drive (optionally nested under parentId) to organize saved files. Returns the folder id + webViewLink. Use that ID as update_drive_file’s moveToFolderId or as parentId for a nested folder. NOTE: save_to_drive’s folder is a NAME, not this id — it find-or-creates a folder by that name, so pass the folder NAME there (or omit and just save, then move with update_drive_file).

ParametersJSON Schema
NameRequiredDescriptionDefault
nameYesfolder name
parentIdNoparent folder id for a nested folder (default: Drive root)

TDQS

A4.7/5.0
Behavior4/5

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

Annotations already signal non-read-only and non-idempotent behavior, and the description adds the return format (folder id + webViewLink) plus how the ID flows to sibling tools. It also clarifies the find-or-create behavior of save_to_drive, which is useful context beyond the structured annotations. No contradiction with annotations.

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

Conciseness5/5

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

Three sentences, each with distinct value: purpose, return value and downstream usage, and a cross-tool caveat. No filler and the core action is front-loaded. It is appropriately sized for the tool's complexity.

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?

For a two-parameter create tool with no output schema, the description covers the return value, nesting behavior, and downstream consumption examples. It also addresses the confusing relationship with save_to_drive, making the tool effectively self-contained. An agent can call this correctly without additional clarification.

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% with both name and parentId documented, so the schema carries the basic meaning. The description goes further by explaining that parentId creates a nested folder (defaulting to root) and that the returned ID is used downstream. This adds practical parameter usage that the schema alone does not provide.

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 'Create a folder in the user's Google Drive' — a specific verb and resource — and clarifies it can be nested under parentId. It clearly distinguishes itself from related tools like save_to_drive and update_drive_file by explaining the folder creation role. Unambiguous and immediately actionable.

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?

The description explicitly tells the agent to use the returned ID as update_drive_file's moveToFolderId or as parentId for a nested folder. It also warns that save_to_drive's folder parameter takes a NAME, not this ID, steering the agent away from a common misuse. This provides direct alternative routing and practical context.

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

create_onedrive_folderCreate a OneDrive folderAInspect

Create a folder in the user’s OneDrive (optionally nested under parentId) to organize saved files. Returns the folder id + webViewLink. Use that id as update_onedrive_file’s moveToFolderId or as parentId for a nested folder. NOTE: save_to_onedrive’s folder is a NAME (find-or-created), not this id.

ParametersJSON Schema
NameRequiredDescriptionDefault
nameYesfolder name
parentIdNoparent folder id for a nested folder (default: OneDrive root)

TDQS

A4.5/5.0
Behavior4/5

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

Annotations already establish that this is not read-only and not destructive. The description adds useful behavioral context by stating the return value includes folder id + webViewLink, which is beyond the annotations. It could mention idempotency implications or duplicate-name behavior, but the essential side effect is clear.

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

Conciseness5/5

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

Three compact sentences, each earning its place: the first explains the action, the second explains how to consume the result, and the third clarifies a critical distinction from save_to_onedrive. No filler or repetition.

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?

The tool is simple, with only two well-documented parameters and no output schema. The description supplies the missing return-format information (folder id + webViewLink) and how to use it in dependent tools, making the definition fully actionable.

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

Parameters3/5

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

Schema description coverage is 100%, so name and parentId are already documented. The description adds 'optionally nested under parentId' and mentions the root default, but this is largely restating what the schema already provides. It does not add significant new parameter meaning beyond the structured schema.

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 uses a specific verb and resource: 'Create a folder in the user's OneDrive'. It immediately distinguishes this from Google Drive siblings by naming OneDrive, and it includes the optional parentId nesting behavior. The tool's role is unambiguous.

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?

The description explicitly explains how to use the returned folder id — as update_onedrive_file's moveToFolderId or as parentId for a nested folder. It also warns that save_to_onedrive's `folder` parameter expects a NAME, not this id, which prevents a likely misuse and distinguishes this tool from a related sibling.

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

create_sheetCreate a Google SheetAInspect

Create a new Google Spreadsheet in the user’s Drive and optionally fill it with rows — e.g. export a swipefile, ad list, or performance report. Pass rows as an array of row arrays (first row = headers). Returns the spreadsheet id + URL. Needs Google Drive connected (Settings > Connectors > Google Drive — one connection covers Drive, Sheets and Docs).

ParametersJSON Schema
NameRequiredDescriptionDefault
rowsNorows to write — array of row arrays; first row = headers
titleNospreadsheet title

TDQS

A4.1/5.0
Behavior4/5

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

Annotations already declare readOnlyHint=false, idempotentHint=false, and destructiveHint=false, covering the write-non-destructive profile. The description adds useful behavioral context: rows are written as an array of row arrays with the first row as headers, and the call returns the spreadsheet id plus URL. It also notes the cross-tool connection requirement. No contradiction with 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?

Three sentences, each earning its place: primary action with examples, row format, and return value plus prerequisite. The example list ('swipefile, ad list, or performance report') is slightly illustrative but useful for selecting the tool. Well front-loaded with the core behavior.

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 2-parameter creation tool with no output schema, the description covers the essential call information: what is created, how to supply optional rows, what the response contains, and the required connector. Minor absences — default title behavior, handling of title collisions, or explicit guidance to prefer append_to_sheet for existing spreadsheets — keep it from a perfect score.

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

Parameters3/5

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

Schema description coverage is 100%, with both rows and title documented in the schema. The description reinforces that the first row is headers and that rows are optional, but adds little beyond the schema's own parameter descriptions. Baseline 3 is appropriate since the schema does the heavy lifting.

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 begins with a crisp verb+resource statement — 'Create a new Google Spreadsheet in the user’s Drive' — and adds the optional fill-with-rows behavior. It clearly distinguishes from siblings like create_doc and append_to_sheet by emphasizing a brand-new spreadsheet, leaving no ambiguity about the tool's core function.

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?

The description gives concrete usage examples (export a swipefile, ad list, performance report) and states the hard prerequisite of having Google Drive connected, including where to configure it. It doesn't explicitly name the alternative for appending to existing sheets, but the 'new spreadsheet' framing implies when to use this over append_to_sheet.

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

delete_brandDelete a profileA
DestructiveIdempotent
Inspect

PERMANENTLY delete a profile and EVERYTHING in it — brand details, memory, swipefile, Library creations, generated assets, avatars, skills, playbooks, chats — and disconnect its connected accounts. Irreversible, and it applies to everyone the workspace is shared with. Call it WITHOUT confirm first: it reports exactly what that workspace holds. Show the user that inventory verbatim, get an unambiguous yes, then call again with confirm:true — plus, if the workspace is not empty, confirmName set to its exact name and confirmConnectors set to the number of connected accounts it reported. Those two exist because confirming INTENT does not prove you picked the right WORKSPACE, and a wrong target is how a live profile was destroyed. The account's FIRST/anchor profile cannot be deleted this way (it holds the workspace's root storage) — that one is replaced from the app.

ParametersJSON Schema
NameRequiredDescriptionDefault
brandYesprofile id or exact name from list_brands
confirmNoREQUIRED true — this destroys the whole profile and cannot be undone
confirmNameNothe workspace's EXACT name, required when it is not empty — copy it from the inventory this tool returned, after the user has agreed to it
confirmConnectorsNothe number of connected accounts the inventory reported, required when there is at least one — the user must specifically agree to losing them, because reconnecting each needs a browser and no agent can do it

TDQS

A4.8/5.0
Behavior5/5

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

Annotations only declare destructiveHint=true; the description adds the far more useful context of what is destroyed, that it is irreversible, that it affects everyone the workspace is shared with, that reconnecting accounts requires a browser no agent can do, and that a first-call inventory must be shown verbatim before confirming. This is well beyond what the structured annotations convey.

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?

The destructive scope is front-loaded in the first clause and every subsequent sentence carries operational weight (the inventory step, the confirmation guards, the anchor-profile exception). It is dense and long, but the length is justified by the irreversibility of the operation; only minor tightening is possible.

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?

For a destructive tool with no output schema, the description covers the return of the first (inventory) call, the required arguments for the second call, the shared-workspace consequences, and the one case where the tool will not work. An agent has everything needed to call it correctly and safely.

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% and the schema descriptions already explain confirm, confirmName, and confirmConnectors, so the baseline is 3. The description adds real value beyond the schema by explaining WHY the name/count guards exist ('confirming INTENT does not prove you picked the right WORKSPACE'), plus the sequencing of when each becomes required.

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 (permanently delete), a specific resource (profile), and enumerates the blast radius (brand details, memory, swipefile, Library, assets, avatars, skills, playbooks, chats, connected accounts). It is unambiguously distinguishable from create_brand, update_brand, and get_brand in the sibling list.

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?

Explicitly prescribes the two-phase flow (call WITHOUT confirm first to get the inventory, then again with confirm:true), and names the exclusion case: the account's first/anchor profile cannot be deleted this way and must be replaced from the app. There is no ambiguity about when this tool applies.

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

delete_creatorDelete a creatorA
DestructiveIdempotent
Inspect

Remove a saved creator from this workspace’s cast by id (from list_creators). Records a cross-device delete so they don’t reappear on the user’s other devices. It only drops the roster entry — ads already rendered with that person are untouched — and the same portrait can be saved again with save_creator, so no confirm is needed.

ParametersJSON Schema
NameRequiredDescriptionDefault
idYesthe creator id (from list_creators)
brandNoprofile id/name from list_brands, this call only

TDQS

A4.5/5.0
Behavior5/5

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

Goes well beyond the annotations (destructiveHint, idempotentHint) by disclosing cross-device delete propagation, that only the roster entry is dropped, that already-rendered ads are untouched, and that the action is reversible via save_creator. This is exactly the behavioral context an agent needs for a destructive operation.

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

Conciseness5/5

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

Two tightly written sentences, front-loaded with the action and id source, followed by the behavioral nuances. No filler; every clause carries information an agent would otherwise have to guess.

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 annotations covering the safety profile and no output schema, the description supplies the remaining essentials: scope, reversibility, cross-device effect, and what is not affected. Nothing needed to call it correctly is missing.

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

Parameters3/5

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

Schema description coverage is 100%, so both parameters are already documented. The description reinforces the id source (list_creators) but says nothing about the optional 'brand' parameter, adding little beyond the schema. Baseline 3 applies.

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 (Remove) and resource (a saved creator) with scope ('from this workspace's cast') and the id source ('from list_creators'). This cleanly separates it from siblings like list_creators, find_creators, save_creator, and update_saved_creator.

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 names list_creators as the source of the id and points to save_creator as the recovery path, and explicitly says 'no confirm is needed' — useful invocation guidance. It does not explicitly contrast against update_saved_creator for modifying rather than removing, so it falls short of full when/when-not coverage.

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

delete_drive_fileDelete a Drive fileA
DestructiveIdempotent
Inspect

Delete a Drive file. By default it goes to Trash (recoverable); pass permanent:true to delete it forever. Pass fileId (from list_drive_files) + confirm:true. Irreversible when permanent — confirm with the user first.

ParametersJSON Schema
NameRequiredDescriptionDefault
fileIdYesthe Drive file id
confirmNoREQUIRED true
permanentNotrue = delete forever; default trashes (recoverable)

TDQS

A4.7/5.0
Behavior5/5

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

Annotations already declare destructiveHint=true, and the description goes further by explaining that deletion defaults to the recoverable Trash, that permanent:true is irreversible, and that user confirmation should precede permanent deletion. This adds meaningful behavioral context beyond the annotations and does not contradict them.

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

Conciseness5/5

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

Three short, purposeful sentences cover the operation, default behavior, parameter requirements, and the safety warning. The irreversible caveat is front-loaded before invocation details, with no wasted words.

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?

For a three-parameter delete tool with no output schema, the description supplies the essential operational details: recoverability, irreversibility, required confirmation, and how to obtain fileId. Given the existing destructive annotation, nothing critical is missing for correct selection and invocation.

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 adds real value by specifying that fileId comes from list_drive_files and by reinforcing that confirm:true is required. It also ties permanent:true to irreversibility, matching and slightly enriching the schema's parameter descriptions.

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 ('Delete a Drive file'), then adds the Trash-vs-permanent distinction so the operation is unambiguous. The resource is clearly Google Drive, distinguishable from siblings like delete_onedrive_file and update_drive_file.

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 clear invocation guidance: fileId should come from list_drive_files, confirm:true must be passed, and the user should be consulted before a permanent deletion. It does not explicitly name alternatives or exclusions, but the sibling resource differences are evident from the tool names.

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

delete_linkedin_lead_subscriptionStop a LinkedIn lead webhookA
DestructiveIdempotent
Inspect

Remove a lead notification webhook (subscriptionId from list_linkedin_lead_subscriptions). Leads themselves are unaffected and stay readable; only the real-time delivery stops. Read back from LinkedIn. Free.

ParametersJSON Schema
NameRequiredDescriptionDefault
pageIdNothe company Page that owns the form — from list_linkedin_pages; omit when one Page is shared
adAccountIdNoread forms owned by an AD ACCOUNT instead of a Page
subscriptionIdYes

TDQS

A4.7/5.0
Behavior5/5

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

Annotations already indicate destructive, idempotent, and open-world behavior, and the description adds crucial scoping: only the webhook is removed, while leads remain intact. 'Read back from LinkedIn' also discloses the external side effect, and there is no contradiction with 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.

Conciseness5/5

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

Three short sentences front-load the core action and immediately clarify the destructive scope. Every sentence earns its place: the action, the non-destructive effect on leads, and the external synchronization/cost note.

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?

Even without an output schema, the description covers what the call does, what it affects, and where to get the required parameter. The idempotent and open-world hints come from annotations, making this complete enough for an agent to select and invoke the tool 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?

The schema already documents pageId and adAccountId, and the description adds meaning for the required subscriptionId by tying it directly to list_linkedin_lead_subscriptions. Since 67% of parameters are schema-described, the description meaningfully compensates for the remaining undocumented parameter.

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 the specific verb 'Remove' and the exact resource, 'a lead notification webhook', and anchors the required subscriptionId to its source, list_linkedin_lead_subscriptions. It also explicitly scopes what is not affected ('Leads themselves are unaffected'), which distinguishes it from any lead-deletion or subscription-creation tool.

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?

The description tells the agent where to obtain the subscriptionId and makes the intended use case clear: stop real-time delivery while keeping leads readable. It does not explicitly enumerate exclusions versus every sibling tool, but the context is sufficient for selection.

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

delete_onedrive_fileDelete a OneDrive fileA
DestructiveIdempotent
Inspect

Delete a OneDrive item — it moves to the OneDrive recycle bin (recoverable there). Pass fileId (from list_onedrive_files) + confirm:true. Confirm the exact file with the user first.

ParametersJSON Schema
NameRequiredDescriptionDefault
fileIdYesthe OneDrive item id
confirmNoREQUIRED true

TDQS

A4.5/5.0
Behavior4/5

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

Annotations already indicate destructiveHint=true, so the description adds meaningful context by disclosing that the item moves to the OneDrive recycle bin and is recoverable there. It also communicates the confirmation safeguard, which is valuable behavioral context beyond the schema and annotations.

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

Conciseness5/5

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

The description is three short sentences with no filler. It front-loads the core behavior, then covers the parameter source and confirmation requirement. Every sentence earns its place.

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?

For a simple two-parameter delete operation with no output schema, the description covers the action, the recoverability consequence, the source of the identifier, the required confirmation flag, and the user-confirmation step. Nothing essential is missing for an agent 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%, but the description adds important semantic guidance: fileId should come from list_onedrive_files, and confirm must be true. This compensates for the schema's inconsistency where confirm is described as 'REQUIRED true' but is not listed in the required array.

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 ('Delete') and a specific resource ('a OneDrive item'), and immediately clarifies scope by noting it is a recycle-bin move rather than a permanent purge. This clearly differentiates it from related siblings like update_onedrive_file, get_onedrive_file, and save_to_onedrive.

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 concrete usage instructions: pass fileId from list_onedrive_files, pass confirm:true, and confirm the exact file with the user first. This is strong practical guidance, though it does not explicitly name alternatives or conditions when this tool should not be used.

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

delete_playbookDelete a playbookA
DestructiveIdempotent
Inspect

Delete a saved playbook by id (from list_playbooks). Records a cross-device delete so it does not come back on the next sync. Minor + re-creatable, so no confirm needed.

ParametersJSON Schema
NameRequiredDescriptionDefault
idYesthe playbook id (from list_playbooks)
brandNoprofile id/name from list_brands, this call only

TDQS

A4.2/5.0
Behavior4/5

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

Annotations already declare destructiveHint=true, idempotentHint=true and readOnlyHint=false, but the description adds non-obvious behavior the annotations cannot convey: the delete is recorded cross-device so the playbook will not reappear on the next sync, and the item is characterized as minor and re-creatable. It stops short of 5 because it says nothing about auth requirements or failure modes.

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

Conciseness5/5

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

Three short sentences, front-loaded with the action and identifier, then the durability semantics, then the confirmation guidance. No filler and every sentence carries information.

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?

Given rich annotations and a fully documented schema, the description supplies the remaining pieces an agent needs (id provenance, sync-proof deletion, no confirmation). No output schema exists but a delete returns little, so the only real omission is error/failure behavior.

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

Parameters3/5

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

Schema description coverage is 100%, so both parameters are already documented in the schema, and the description's 'id' reference merely repeats that. The optional 'brand' parameter is not addressed in the description at all, so it adds no meaning beyond the structured fields.

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 ('Delete a saved playbook') plus the identifier source, and the id-provenance callout ('from list_playbooks') clearly distinguishes it from the sibling save_playbook and the list_playbooks read tool.

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 clear usage context: the id must come from list_playbooks, and it pre-empts a confirmation workflow ('Minor + re-creatable, so no confirm needed'). It does not name a sibling alternative or an explicit when-not-to-use case, so it stops short of the 5 bar.

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

delete_skillDelete a custom skillA
DestructiveIdempotent
Inspect

Delete one of the workspace’s CUSTOM skills by id (from list_skills). Built-in skills/recipes can’t be deleted. Minor + re-creatable, so no confirm needed.

ParametersJSON Schema
NameRequiredDescriptionDefault
idYesthe custom skill id (from list_skills)
brandNoprofile id/name from list_brands, this call only

TDQS

A4.2/5.0
Behavior4/5

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

Annotations already declare destructiveHint=true and idempotentHint=true, so the description benefits from that lower bar. It still adds non-annotation context: only custom skills are removable, and the deletion is described as minor/re-creatable so no confirmation is needed — useful impact guidance for a destructive call.

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

Conciseness5/5

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

Three short sentences, front-loaded with the primary action and scope, then the constraint, then the confirmation policy. No filler or repetition.

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 two-parameter delete with no output schema and full schema coverage, the description covers scope, the id source, non-deletable resources, and confirmation policy. It omits only failure behavior for an invalid or non-custom id, a minor gap given the annotations.

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

Parameters3/5

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

Schema description coverage is 100% and both parameters are documented in the schema, so the baseline is 3. The description reinforces that 'id' is a custom-skill id sourced from list_skills but adds nothing about the 'brand' parameter's semantics beyond what the schema states.

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?

Specific verb ('Delete') + resource ('skill') + scope ('CUSTOM skills', excluding built-ins and recipes), and it names the sibling that supplies the id (list_skills). An agent can immediately distinguish it from save_skill, get_skill, list_skills, and delete_playbook.

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?

Clearly implies when to use it (removing user-created skills) and states a hard exclusion: built-in skills and recipes cannot be deleted. It routes the agent to list_skills for the id, but gives no guidance on what to do if the id is unknown/invalid or on prerequisites.

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

diagnose_postsWhat to fix next, post by postA
Read-onlyIdempotent
Inspect

WHAT TO FIX NEXT, post by post — and the tool that fixes it. post_performance tells you which hook is AHEAD; this tells you what is WRONG with a given post and where the next edit goes. Under-distributed is A HOOK PROBLEM (change the opening: mine_angles, then list_hooks, then plan_variations). Seen but not held is A RETENTION PROBLEM (plan_variations to rebuild the middle against the same hook). Seen, held, and still not converting is AN OFFER PROBLEM. FOUR REFUSALS, AND YOU SHOULD REPEAT THEM RATHER THAN PAPER OVER THEM: (1) a post younger than ~24h is TOO EARLY and is never called a failure — it has not had its run; (2) a metric the platform does not publish is UNMEASURED, never zero — Facebook has published no post reach since 2026-06-15, Reddit publishes no impressions, and Google Business publishes nothing per-post at all; (3) below 5 measured posts on a channel there is no baseline of the brand's own, and the ONLY fallback is a published short-video hook floor that is NOT our measured number and does not transfer off TikTok/Instagram/YouTube — it is attributed in the output and you should attribute it too; (4) it does not always find a problem, and 'nothing here needs fixing' is a real answer rather than a failure to look. Hermoso cannot see conversions for an organic post — no channel reports installs or purchases against a post id — so the offer rung runs ONLY when the user tells you they are not converting and you pass converting:false. Print summary verbatim. Read-only, 0 credits.

ParametersJSON Schema
NameRequiredDescriptionDefault
limitNohow many recent posts to diagnose (default 25, max 200). The baseline is always built from EVERY post recorded for the brand, never only these, so a bad month can never become its own definition of normal.
channelNorestrict to one channel: facebook, instagram, threads, x, linkedin, youtube, tiktok, reddit, pinterest
convertingNopass false ONLY when the user has told you these posts are getting seen and are not converting — it re-reads the ones that are earning their reach as an offer problem instead of a win. Omit when you do not know; we cannot measure it.

TDQS

A4.6/5.0
Behavior5/5

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

Annotations cover the safety profile (readOnly, idempotent, non-destructive, closed-world), yet the description adds substantial behavioral context beyond them: the 24h 'too early' rule, the unmeasured-vs-zero distinction with dated platform examples, the <5-post baseline fallback and its non-transferability, the inability to see conversions, and the 0-credit cost. This is unusually rich disclosure.

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?

Long and heavy with all-caps emphasis, but front-loaded with the core purpose and problem-routing before the four refusals. Nearly every sentence carries actionable information; it is dense rather than padded, though the volume pushes against ideal brevity.

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?

No output schema exists, and the description compensates by stating that 'nothing here needs fixing' is a valid result, that summary must be printed verbatim, and that fallback baselines are attributed in the output. Complete for a diagnostic read tool.

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

Parameters3/5

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

Schema description coverage is 100%, so the schema already documents limit, channel, and converting with detailed semantics (baseline built from every post, channel enum list, when to pass false). The description largely reinforces rather than extends this, so the baseline 3 applies.

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 specific verb+resource ('WHAT TO FIX NEXT, post by post') and immediately distinguishes itself from the sibling post_performance: 'post_performance tells you which hook is AHEAD; this tells you what is WRONG with a given post.' An agent can route between the two without opening either 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?

Explicitly maps diagnostic outcomes to next actions (under-distributed → mine_angles, list_hooks, plan_variations; seen-not-held → plan_variations; seen-held-not-converting → offer problem), names the sibling it is not, and specifies the exact condition for the converting:false parameter. Nothing is left to inference.

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

disconnect_connectorDisconnect a connected accountA
DestructiveIdempotent
Inspect

Disconnect a third-party account from this workspace (Meta, Google Ads, Google Drive/Sheets/Docs, YouTube, TikTok, LinkedIn, X, Reddit, Pinterest, Google Business, Microsoft Advertising/OneDrive, Slack, …). This always drops the stored credentials, so every tool for that provider stops working immediately and posts/campaigns already published are NOT affected. WHETHER IT ALSO REVOKES THE GRANT AT THE PROVIDER DEPENDS ON THE PROVIDER — a few (Threads, Microsoft) publish no revocation endpoint, so the authorisation stays in place until the user removes it in that provider's own settings. The unconfirmed call reports which it is for this provider (list_connectors also carries it as revokesAtProvider) — relay that verbatim rather than promising a revoke. RECONNECTING NEEDS A BROWSER (the provider's consent screen) — an agent cannot undo this. Name the provider to the user, then call with confirm:true. Use list_connectors for the exact provider ids.

ParametersJSON Schema
NameRequiredDescriptionDefault
accountNoon a channel with several connected accounts (TikTok, X, YouTube, Threads, Bluesky, Telegram, Reddit, Pinterest): remove ONLY this account (@handle or id from list_connector_accounts) and keep the others
confirmNoREQUIRED true — reconnecting a sign-in account afterwards needs the user's browser; a paste-a-key account is reconnected with connect_connector
providerYesprovider id exactly as list_connectors reports it, e.g. "meta", "google_ads", "youtube", "linkedin"

TDQS

A4.6/5.0
Behavior5/5

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

Goes well beyond the annotations (destructiveHint/idempotentHint/openWorldHint) by disclosing the concrete consequences: stored credentials are always dropped, every provider tool stops working immediately, already-published posts/campaigns are unaffected, and revoke-at-provider behaviour varies by provider (Threads, Microsoft lack revocation endpoints). It also warns that reconnecting requires the user's browser and that the unconfirmed call reports revoke status.

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-loads the core action and its main consequence before the provider-specific caveats, and every sentence carries information. It is dense and the all-caps emphasis plus the duplicated browser-warning (also present in the confirm param schema) make it slightly noisier than necessary.

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?

For a destructive, no-output-schema tool, the description covers everything needed: scope, irreversible credential loss, provider-dependent revocation, the confirm requirement, and how to enumerate valid provider ids. Nothing essential to correct invocation is missing.

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 already 100%, so the baseline is 3. The description nonetheless adds usable meaning: why confirm must be true (browser-based reconnection) and that account removes ONLY one handle while keeping others, plus pointing to list_connectors for valid provider ids.

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 — 'Disconnect a third-party account from this workspace' — and enumerates the affected providers (Meta, Google Ads, YouTube, Slack, ...). An agent can immediately distinguish this from connect_connector and from rendering/posting tools.

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 concrete workflow: 'Name the provider to the user, then call with confirm:true. Use list_connectors for the exact provider ids,' and explains that the account param narrows removal on multi-account channels. It does not, however, mention when NOT to use it, e.g. versus the sibling leave_connector, so the alternative-selection guidance is incomplete.

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

draft_brandDraft profileA
Destructive
Inspect

Fill in a profile (a business brand or a creator) from a website domain, a free-text description, or a social handle, into a {name, products, logo, …} object you can pass to plan_ad / generate. 0 credits. IMPORTANT: a domain can resolve to a DIFFERENT company than intended (e.g. bala.com is an engineering firm, not the Bala fitness brand at shopbala.com). Before spending any credits on research or renders, VERIFY the returned name (and summary) match the brand the user meant; if it looks wrong, re-draft with the correct domain or a description (pass save:false until confirmed) — this tool cannot ask the user, so the caller owns that check.

ParametersJSON Schema
NameRequiredDescriptionDefault
saveNosave onto the active profile (like Studio onboarding) so plan_ad/create use it automatically. Default: saves only when NO brand is saved yet; pass true to REPLACE the saved profile (the drafted fields overwrite the saved ones), false to never save
domainNoa website to scrape
platformNoplatform for socialHandle (instagram/tiktok/…)
descriptionNoa free-text brand description (no website)
socialHandleNoa social handle to draft from (influencers/creators) — pair with platform

TDQS

A4.5/5.0
Behavior5/5

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

Annotations cover the safety profile (destructiveHint=true, openWorldHint=true, non-idempotent), and the description adds context beyond them: zero credit cost, that it scrapes external sources, that save:true overwrites the saved profile, and that the tool cannot ask the user for confirmation. The destructive/overwrite behavior aligns with destructiveHint rather than contradicting it.

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 purpose and cost, then the risk warning, then the remedy. All sentences carry information, though the bala.com/shopbala.com illustration is verbose for the point being made.

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 shape, the cost, the verification obligation, and the save semantics — everything needed to call it safely and correctly. Nothing material for correct invocation is missing.

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%, so the baseline is 3; the description goes further by tying save:false to the verification workflow and by warning that a domain may resolve to a different company, which changes how the agent should choose the domain vs description parameter. It does not add much beyond the schema for platform/socialHandle.

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

Purpose4/5

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

States a specific verb (draft/fill in) and resource (a brand/creator profile), and names the output shape ({name, products, logo, …}) plus the downstream consumers plan_ad and generate. It does not explicitly contrast itself with the sibling brand tools (create_brand, update_brand, get_brand), so an agent must infer the 'draft vs persist' distinction from the save parameter.

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 about when to use it (before spending credits on research or renders), what to do when the result looks wrong (re-draft with correct domain or a description, pass save:false until confirmed), and who owns the verification step. This is exactly the when/when-not/alternative guidance the dimension asks for.

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

dub_videoDub videoAInspect

Localize a finished video into another language WITHOUT re-rendering it: the spoken track is transcribed, translated, re-voiced and lip-synced back onto the SAME footage, so the visuals, timing and edit are untouched. Just pass the video and the language — the script is read off the source automatically (pass script only to override what it heard). Paid; returns the served URL of the localized video.

ParametersJSON Schema
NameRequiredDescriptionDefault
videoYesthe source video URL
voiceNooptional target voice preset, e.g. 'Aria' (warm female) or 'George' (confident male). Defaults to a voice matching the source speaker's register.
scriptNoOPTIONAL override for the original spoken words. Leave this out — the source video is transcribed automatically. Only pass it when you already know the exact script and the auto-transcript got it wrong.
languageYestarget language, e.g. 'Spanish', 'de', 'French (Canada)'

TDQS

A4/5.0
Behavior4/5

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

Annotations already declare non-readonly, non-destructive, open-world behavior, but the description adds material context: the source footage is left untouched (nothing re-rendered), the transcription is automatic, and the operation is Paid. It also discloses the return value (served URL of the localized video). Cost and output disclosure go beyond the annotation set.

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

Conciseness5/5

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

Front-loads the differentiating trait (no re-rendering, visuals/timing/edit untouched) before mechanics and cost. Two dense sentences with no padding, and the routing-relevant 'just pass the video and the language' instruction is easy to find.

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?

With no output schema, the description helpfully states the return is the served URL of the localized video, and annotations cover the safety profile. The only gap is the absence of any routing against similar siblings, but for calling the tool correctly the definition is essentially complete.

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

Parameters3/5

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

Schema description coverage is 100%, so the schema already documents all four parameters, and the description largely restates them (script auto-read, voice default matching the speaker's register). With the schema doing the heavy lifting, baseline 3 is appropriate; no additional syntax or format detail is added.

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 (localize/dub) and resource (a finished video) and explains the mechanism: transcription, translation, re-voicing and lip-sync onto the SAME footage without re-rendering. This distinguishes it from siblings like change_voice (voice only), add_subtitles (text only) and localize_ad (ads rather than finished videos).

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

Usage Guidelines3/5

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

Gives clear invocation guidance ('just pass the video and the language') and an important scoping rule for `script` (only to override the auto-transcript). However it never states when to prefer this tool over nearby alternatives such as localize_ad or change_voice, and offers no exclusions, so usage is implied rather than explicit.

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

duplicate_scheduledDuplicate a scheduled postA
Destructive
Inspect

Copy an existing scheduled or already-published post into a NEW queued post — the way to run a creative again, reuse a post that worked as the starting point for the next one, or re-send something after it went out. It copies the caption, media, per-channel captions, title, description, tags and the target board / Page / company Page / listing, and ANY of those can be overridden in the same call. Give a new time in at, or useQueue:true to drop it into the brand’s next free posting slot. The copy is INDEPENDENT — editing or cancelling it never touches the original — and it is a genuinely new post rather than a re-send, so it publishes even where the original already did. To re-fire only the channels that FAILED, use retry_scheduled instead.

ParametersJSON Schema
NameRequiredDescriptionDefault
atNowhen the copy goes out — ISO timestamp or epoch milliseconds (default: an hour from now)
idYesthe post to copy, from list_scheduled
linkNo
brandNoWHICH PROFILE this post is in — id or exact name from list_brands; needed when it is not the profile this connection is pinned to. A name that matches no profile, or two, is REFUSED.
titleNo
chatIdNoTELEGRAM — which chat, group or channel the copy goes to (@username or numeric id)
ideaIdNoshort id of the content-plan idea this post came from
pageIdNoFACEBOOK / INSTAGRAM / THREADS — which connected Page (list_meta_pages)
recipeNothe post's FORMAT id, e.g. "slideshow" or "imessage_chat" — post_performance groups by it, so reuse one id per format
boardIdNoPINTEREST — the board for the copy (list_pinterest_boards)
messageNoa different caption for the copy
captionsNoper-channel caption overrides for the copy
channelsNopost the copy to these channels instead of the original’s
imageUrlNo
timezoneNoIANA zone for the queue, e.g. "America/New_York"
useQueueNoinstead of naming a time, take the brand’s next free posting slot
videoUrlNo
imageUrlsNoCAROUSEL — an ORDERED list of image URLs published as ONE swipeable post
locationIdNoGOOGLE BUSINESS — which listing (list_business_locations)
visibilityNo
linkedinOrganizationIdNoLINKEDIN — publish the copy as this company Page (list_linkedin_pages)

TDQS

A4.5/5.0
Behavior4/5

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

Annotations declare readOnlyHint=false, destructiveHint=true, idempotentHint=false and openWorldHint=true, so safety is partly covered. The description adds genuinely useful context beyond that: the copy is INDEPENDENT (editing/cancelling never touches the original), it is a new post rather than a re-send, and it publishes even where the original already did. It stops short of explaining retry/idempotency implications for repeated calls, which the idempotentHint=false annotation implies.

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 action, then override capability, then the at/useQueue mechanism, then independence, then the sibling redirect. Dense and slightly long for a single description, but each clause carries distinct information.

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 21-parameter mutation tool with no output schema, the description covers the operation's semantics, override scope, scheduling options, and the key sibling alternative. Remaining gaps (e.g., what the call returns, behavior on invalid channel/board combinations) are minor against the rich schema and annotations.

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 76%, so the schema already documents most parameters. The description still adds meaning by enumerating what is copied (caption, media, per-channel captions, title, description, tags, target board/Page/company Page/listing) and clarifying that ANY of them can be overridden in the same call, plus the `at` vs useQueue:true choice. It does not add much beyond the schema for the platform-specific id params.

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 ('Copy an existing scheduled or already-published post into a NEW queued post') and immediately distinguishes the outcome from a re-send. It also names the sibling it is not (retry_scheduled), so an agent can separate it from reschedule/cancel/retry 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?

Gives explicit use cases (re-run a creative, reuse a winning post as a starting point, re-send after publish) and an explicit alternative with its selecting condition: 'To re-fire only the channels that FAILED, use retry_scheduled instead.' When/when-not guidance is present.

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

edit_imageEdit an imageAInspect

EDIT an existing image in place with a plain-language instruction and keep everything else: 'make the headline bigger', 'add our logo bottom right', 'swap the background for a kitchen', 'remove the person on the left', 'erase all the text'. Pass image (URL, Library item, upload_file URL or local path) and instruction. The same edit the web Studio's Edit runs: composition, aspect ratio, people and every untouched line of text stay as they are; the saved brand's real name and website are pinned so an added line never invents one, and the brand's real logo is attached when the instruction asks for the logo. Set removal:true when the edit STRIPS text, branding or an object, so nothing branded is put back. When the saved brand has a product photo and the ad shows that product, the photo rides with the edit and the product's label is read and checked against it: re-printed from the photo only when it came out wrong (the check alone is charged when it is right; one small extra charge finds the product first). fixLabel:false turns that off. For a precise region, pass mask (see generate_image). One image edit's credits; returns the new image URL. For a new image from a prompt use generate_image; to rebuild a competitor's ad for your brand use clone_static.

ParametersJSON Schema
NameRequiredDescriptionDefault
maskNooptional mask image (URL or local path) marking the region to change: transparent = change, or white = change on an opaque mask
imageYesthe image to edit: URL, Library item URL, upload_file URL or local path
dryRunNotrue = return the exact credits this edit reserves and render nothing
removalNotrue when the edit REMOVES text, branding, a logo, a watermark, a person or an object, so the brand name and logo are not re-added
fixLabelNofalse = leave the product label exactly as rendered: no product lookup, no label check, no re-print (default on when the ad shows the brand’s saved product)
instructionYesthe change to make, in plain words (pass the user’s own words for a removal or plain photo edit)

TDQS

A4.8/5.0
Behavior5/5

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

Adds substantial context beyond the annotations: what is preserved (composition, aspect ratio, people, untouched text), that the brand name/website are pinned and the brand logo attached, the semantics of removal:true, the product-label re-print/check behavior with its charging model, and 'One image edit's credits; returns the new image URL.' This is rich disclosure of side effects and costs.

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 purpose and routed alternatives, and every clause carries behavioral information. However the product-photo/label sentence is dense and nested, and the overall block could be tightened; some clauses require re-reading despite earning their place.

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?

For a 6-param, no-output-schema mutation tool, the description covers preservation rules, credit cost, return value (new image URL), and brand/product safety behavior. Nothing an agent needs to invoke it correctly is missing.

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 baseline is 3, but the description adds real meaning beyond the schema: removal:true 'so nothing branded is put back' and fixLabel:false disabling product lookup/check/re-print clarify intent beyond the field text. It also frames mask as the precise-region control.

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 specific verb+resource ('EDIT an existing image in place') plus a plain-language instruction, and closes by naming both sibling alternatives (generate_image, clone_static). An agent can distinguish this from generate_image/clone_static without opening 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 Guidelines5/5

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

Explicit routing: 'For a new image from a prompt use generate_image; to rebuild a competitor's ad for your brand use clone_static.' It also states the conditional for removal:true and fixLabel:false and points to mask usage, giving clear when/when-not guidance.

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

edit_videoEdit a video clipAInspect

EDIT an existing clip from a plain instruction (video-to-video): the motion, timing, framing and cut stay, the named thing changes. 'change only the mug to red', 'make it nighttime', 'restyle it as claymation'. Your words are wrapped so the model keeps everything else identical, changes only what you named and repeats that lock; lighting is kept unless the change needs new light (set lighting to force either); literal:true sends your words as written. One change per call holds best. previewFirstFrame:true edits ONE still first (one image edit; previewAt picks the second) and quotes the clip; nothing else runs until you call again, ideally with previewStill. The reply scores how well the shot held outside the change (free) and flags an edit that touched more than asked. NOT for cuts/trims/end cards (post_edit), a new video (generate_video / render_ad), translation (dub_video) or a saved creator's face (recast_motion). Best on 3-15s clips.

ParametersJSON Schema
NameRequiredDescriptionDefault
videoYesthe source video URL (a render, job result or list_library)
engineNodefault auto: Seedance 2.5 Edit without a real-looking person, else Kling O3 Edit
literalNotrue = send the instruction exactly as written, with no preserve/lock wrapper
elementsNoOPTIONAL identity/product grounding (≤4): a creator portrait or the real product photo, so the edit restores the REAL thing. Describe each one in the instruction
lightingNodefault auto: keep the source light unless the change needs new light (night, a lamp, fire). 'preserve' or 'relight' forces it
faceRouteNo'face_lane' = the user's "I own the rights to this face" for a real person in the source clip (paid plans). Only on the user's say-so, never on your own.
keepAudioNodefault true: keep the source audio; false = silent
previewAtNosecond to preview (default 0); resend with previewStill
referenceNoOPTIONAL image URL that anchors the MATERIAL of what changes (a fabric, a finish, a colour swatch, the real product). Only its surface is used, never its framing or light
instructionYesthe change, in the user’s own words
previewStillNothe still a previewFirstFrame call returned; the clip then matches the changed thing to it
interactionIdNoOPTIONAL: the interactionId an earlier Gemini Omni render or edit returned; the edit continues that clip on the same Omni model. If it cannot run, the video editor edits it and the reply says so.
previewFirstFrameNotrue = edit ONE still of the first frame first and quote the clip; the paid clip does not run

TDQS

A4.8/5.0
Behavior5/5

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

Goes well beyond the standard readOnly/openWorld/idempotent/destructive hints: it discloses the preserve-and-lock wrapping behavior, that the quality score is free, that a previewFirstFrame call does not run the paid clip (implying normal runs are paid), and that the reply flags over-reaching edits. This is exactly the cost/behavior context 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 with the core purpose and examples before the workflow and exclusion details, and every sentence carries usable information for a 13-parameter tool. It is dense to the point of run-on in the previewFirstFrame/previewAt sentence, which costs a point.

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 13 params, no output schema, and 3 enums, the description covers what an agent needs: model routing (engine), lighting semantics, the preview/approval loop, rights handling via faceRoute, and expected reply content (quality score plus over-edit flag). Parameter-level detail is safely delegated to a fully documented schema.

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. The description still adds value beyond the schema by explaining the interaction between previewFirstFrame, previewAt and previewStill ('previewAt picks the second'), and by giving concrete instruction phrasing for the required instruction param. Minor gain, but real.

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 specific verb+resource+mechanism ('EDIT an existing clip from a plain instruction (video-to-video)') and immediately constrains scope: motion/timing/framing/cut stay, only the named thing changes. It names the exact siblings it is not for (post_edit, generate_video, render_ad, dub_video, recast_motion), so an agent can disambiguate without opening 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 Guidelines5/5

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

Explicit when-not routing to five alternatives, plus positive-fit guidance ('Best on 3-15s clips', 'One change per call holds best'). The preview workflow is given a concrete when-to-use path (previewFirstFrame then call again, ideally with previewStill). Nothing about tool selection is left to inference.

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

enable_toolsTurn on more Hermoso toolsA
Idempotent
Inspect

LIST a group of tools that is not in this session's roster. IT IS NOT HOW YOU REACH A TOOL — call_tool runs any Hermoso tool whether or not it is listed, and that works everywhere. Use this when the session will use MANY tools from one area and you want them in your list. WORKS ON CLIENTS THAT RE-READ THE TOOL LIST (stdio, the CLI, Cursor, Claude Code); a host that fixed its roster at connect time — claude.ai and ChatGPT do — will not show the new tools until it reconnects, and this tool says so in its reply rather than reporting a success you cannot use. The connect-time route that always works is ?tools=all on the server URL. The default roster is CORE-FIRST: the core tools plus a few that make the connection drivable. Every other tool is held out of the LIST on SIZE alone — the whole registry is several hundred thousand tokens of schema re-sent on every turn, and a roster far past the 30-50 tool mark measurably degrades tool choice. The heaviest groups are ads, analytics, channel_admin: paid-campaign management across every ad platform is most of the total schema weight. NOTHING held out is unfinished or unsafe, and nothing is unreachable — find_tools finds it and call_tool runs it. CALL THIS WHEN A WHOLE AREA IS IN PLAY. If the user settles into building, budgeting, targeting or reporting on ad campaigns, call enable_tools({groups:['ads']}) and the tools appear. If they ask about their own site or product analytics, a tag/tracking container, or how a search engine crawls, indexes or ranks their site, call enable_tools({groups:['analytics']}). Groups: core, research, create, channels, channel_admin, analytics, ads, files, workspace — or 'all'. Free, instant, and it never turns anything off.

ParametersJSON Schema
NameRequiredDescriptionDefault
groupsYesGroups to switch on, e.g. ['ads']. Unknown names are refused by name rather than ignored.

TDQS

A4.8/5.0
Behavior5/5

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

Annotations only cover the safety profile (readOnlyHint=false, idempotentHint=true, destructiveHint=false). The description goes far beyond: it discloses which clients re-read the tool list vs which need a reconnect, that it reports the failure rather than faking success, the token-size rationale for the default roster, and that nothing is held out for safety reasons. This is rich behavioral context annotations cannot carry.

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-loads the most important clarification ('IT IS NOT HOW YOU REACH A TOOL') before the details, and every section carries real signal. It is on the long side for a one-parameter tool, with some repetition around reachability, but little is genuinely wasted.

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?

No output schema and one parameter, yet the description fully covers usage, alternatives, client behavior, rationale, and valid group values. An agent has everything needed to decide and invoke 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% for the single param, so the baseline is 3. The description adds the full list of valid group names (core, research, create, channels, channel_admin, analytics, ads, files, workspace, 'all') that the schema lacks (no enums), plus worked example calls, which meaningfully extends beyond the schema.

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 precise verb+resource: 'LIST a group of tools that is not in this session's roster.' It explicitly separates itself from call_tool and find_tools, so an agent knows exactly what this does and what it is not.

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?

Gives explicit when-to-use ('CALL THIS WHEN A WHOLE AREA IS IN PLAY') with concrete triggers for ads and analytics, names the alternative routes (call_tool, find_tools, ?tools=all), and states the client-compatibility condition that governs whether it will actually take effect.

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

error_detailError detailA
Read-onlyIdempotent
Inspect

One error group in full by fingerprint (from list_errors): every field, plus the most recent redacted occurrences — status, connector, job id, workspace, and a shape-only echo of the inputs. This is what makes a bug reproducible. Read-only, 0 credits.

ParametersJSON Schema
NameRequiredDescriptionDefault
fingerprintYesthe `fp` value from list_errors

TDQS

A4.1/5.0
Behavior4/5

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

Annotations cover read-only/idempotent/non-destructive, and the description adds non-obvious traits: zero credit cost and that occurrences are 'redacted' with only a 'shape-only echo of the inputs' returned. That redaction and shape-only detail is genuine behavioral disclosure beyond 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?

Dense and front-loaded: the retrieval action and key come first, then return contents, then cost. The closing 'This is what makes a bug reproducible' is mild marketing but does convey intent efficiently.

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?

With no output schema, the description helpfully enumerates the return fields (status, connector, job id, workspace, shape-only input echo), which is what the agent needs to know. Only minor gaps remain, such as ordering or limits on 'most recent occurrences'.

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

Parameters3/5

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

Schema coverage is 100% and the single fingerprint parameter is already documented as 'the `fp` value from list_errors'. The description restates this mapping but adds no format or syntax detail beyond the schema, so baseline 3 applies.

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 retrieval action on a named resource ('one error group in full by fingerprint') and names list_errors as the source of the fingerprint, so it is clearly distinguishable from the sibling that lists error groups.

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?

The parenthetical '(from list_errors)' tells the agent where the required fingerprint comes from, which is the key prerequisite. It lacks an explicit 'use this when... not that' exclusion, but the usage context is clear.

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

export_swipefile_deckSwipefile to Google SlidesAInspect

Turn a SWIPEFILE COLLECTION into a real Google Slides deck — one slide per saved ad, carrying the creative, the brand, the ad copy, the run dates with the run length, and the platform. This is the thing a marketer actually presents to a client or a team; until now the swipefile’s only export was JSON. Returns the presentation id + URL. Creates a NEW deck every time: under the drive.file scope Hermoso can only touch files it created, so it cannot add slides to a deck the user already has. A creative whose ad-library link has expired cannot be embedded — Meta signs those URLs with a short expiry — so that slide says so in words and keeps its copy and run dates, and the reply reports how many. Needs Google Drive connected (Settings > Connectors > Google Drive — one connection covers Drive, Sheets, Docs and Slides).

ParametersJSON Schema
NameRequiredDescriptionDefault
limitNomax ads to include, 1-60 (default 30)
titleNodeck title (default: the collection name)
collectionNothe swipefile collection to export, by name or id (default: the first collection)

TDQS

A4.5/5.0
Behavior5/5

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

Even though annotations already mark this as non-read-only and non-idempotent, the description adds valuable behavioral detail: a new deck is always created, drive.file scope restricts access to files Hermoso created, expired ad-library links cannot be embedded and are handled gracefully, and the reply reports the count. This goes well beyond the structured annotations.

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

Conciseness5/5

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

The description is front-loaded with the core purpose and then each subsequent sentence adds essential behavior, constraints, edge cases, or prerequisites. There is no filler or tautology; every sentence earns its place.

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?

The description is complete for a tool with no output schema: it states the return value (presentation id + URL), creation semantics, permissions limitations, expired-link handling, and connection prerequisite. Combined with the fully documented input schema, an agent has enough context to invoke this tool correctly.

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

Parameters3/5

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

The input schema already documents all three parameters with 100% coverage, so the baseline is 3. The description does not add meaningfully to the parameter semantics beyond reinforcing that a collection maps to slides; it mostly discusses tool-level behavior rather than parameter-specific details.

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 action and resource: turning a swipefile collection into a Google Slides deck with one slide per ad containing concrete fields. It clearly differentiates itself from related sibling tools like save_to_swipefile or create_doc by emphasizing the export-to-presentation purpose.

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?

The description gives clear context: this is for presenting to clients/teams, it creates a new deck every time, it cannot add to existing decks, and it requires Google Drive to be connected. It stops short of explicitly naming alternative sibling tools or stating 'use X instead,' so it is not a 5.

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

fetch_app_screensFetch App Store screensA
Idempotent
Inspect

Pull an APP brand's REAL App Store screenshots into the workspace brand, so a screen-hungry native format can use them. Use when the brand has 0–1 app screens on file and you want make_template_ad(template:'app-ui-tour'), or the user asks to 'pull my app's screenshots'. Pass appName (defaults to the saved brand's name). FREE — a keyless App Store lookup. It needs a CONFIDENT match: an ambiguous or unknown app returns 0 screens and saves nothing, which you should relay plainly rather than retrying with guesses. On success the screens are saved to the brand (durable URLs) and are immediately usable.

ParametersJSON Schema
NameRequiredDescriptionDefault
appNameNothe app's name to look up on the App Store — defaults to the saved brand's name
brandIdNoa profile id/name from list_brands to save the screens onto; omit to use the active profile

TDQS

A4.2/5.0
Behavior3/5

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

Annotations already declare this a non-readonly write (readOnlyHint=false) with openWorld access and idempotency. The description adds genuinely useful context beyond them: it is FREE/keyless, needs a confident match, and ambiguous lookups save nothing. It does not, however, describe where screens are stored beyond 'durable URLs' or any limits on count.

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-loads the core action and keeps each subsequent sentence purposeful (trigger, param default, cost, failure mode, success outcome). Dense but not padded; slightly long versus the minimum needed.

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 carries the full burden and does so: it covers the success result (screens saved with durable, immediately usable URLs) and the failure result (0 screens, nothing saved). Nothing an agent needs to call and interpret it is missing.

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

Parameters3/5

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

Schema coverage is 100%, so appName (defaults to saved brand name) and brandId (profile id/name from list_brands) are fully documented in the schema. The description only restates the appName default, adding no syntax or format detail beyond schema — baseline 3.

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+resource: pulling a brand's REAL App Store screenshots into the workspace brand. It distinguishes itself from siblings by naming the companion tool it feeds (make_template_ad with template:'app-ui-tour'), so an agent can route correctly 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?

Gives explicit trigger conditions ('brand has 0-1 app screens on file', 'user asks to pull my app's screenshots') and names the downstream consumer. It also prescribes behavior on failure: relay the 0-screen result plainly rather than retrying with guesses.

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

fetch_assetFetch assetA
Read-onlyIdempotent
Inspect

Resolve a generated asset reference (a /generated/… path or any URL) to a clickable absolute URL + a direct download URL.

ParametersJSON Schema
NameRequiredDescriptionDefault
urlYesthe asset url or /generated/ path
nameNooptional filename for the download

TDQS

A3.8/5.0
Behavior4/5

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

The annotations already declare readOnlyHint, idempotentHint, and destructiveHint, so safety behavior is covered. The description adds useful behavioral context beyond the annotations: it explains that a generated asset reference or URL is resolved into a clickable absolute URL and a direct download URL.

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

Conciseness5/5

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

The description is a single, well-structured sentence that front-loads the action, the accepted input, and the resulting output. Every phrase contributes necessary information with no waste.

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 low-complexity tool with full schema coverage and rich annotations, the description is nearly complete: it covers what the tool does and what it returns, even though no output schema exists. It omits only alternative-tool guidance and does not address the optional 'name' parameter.

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

Parameters3/5

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

Schema description coverage is 100%, so both parameters are already documented in the schema. The description repeats the URL input example and does not mention the optional 'name' parameter or add syntax beyond what the schema provides, making 3 the appropriate baseline.

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

Purpose4/5

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

The description uses a specific verb ('Resolve') and a precise resource ('generated asset reference'), and it states the output form. It is clear, but it does not explicitly name or distinguish itself from any sibling tool such as get_drive_file or fetch_social_data.

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

Usage Guidelines3/5

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

The description implies usage by describing the accepted input ('a /generated/… path or any URL'), but it never states when to use this tool versus alternatives or any exclusions. Guidance is present only at an implied level.

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

fetch_social_dataFetch social dataAInspect

Generic escape hatch for any ALLOWLISTED long-tail social/web endpoint the dedicated search_* tools don't cover — e.g. {path:'/v1/instagram/profile', params:{handle:'nike'}}. Allowlisted platform families: TikTok (+ TikTok Shop), Instagram, YouTube, Facebook (organic profiles/posts/events/marketplace), LinkedIn (organic posts/companies), Twitter/X, Reddit, Threads, Snapchat, Pinterest, Twitch, Bluesky, Truth Social, Rumble, Spotify, SoundCloud, GitHub, Google search, link-in-bio pages (Linktree etc.). Param names vary per endpoint (profiles use handle, keyword searches use query, Reddit uses subreddit). WARNING: returns RAW provider JSON — large and messy; prefer the dedicated search_* tools. Spends credits.

ParametersJSON Schema
NameRequiredDescriptionDefault
pathYesexact endpoint path, e.g. '/v1/tiktok/profile' — non-allowlisted paths are rejected
paramsNoendpoint query params, e.g. {handle:'nike'}

TDQS

A4.8/5.0
Behavior5/5

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

Annotations cover the safety profile (non-read-only, open-world, non-idempotent, non-destructive), and the description adds real behavioral context beyond them: it consumes credits, enforces an allowlist that rejects other paths, and returns raw provider JSON that is large and messy. That is materially useful disclosure for a mutation-adjacent, credit-spending call.

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 purpose and example, with the prefer-alternatives warning positioned after the core statement. The long enumeration of platform families is density rather than filler, since it communicates the allowlist scope, though it is the longest part of the block and pushes the WARNING late.

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?

For a generic passthrough with a 2-param schema, an allowlist constraint, and no output schema, the description supplies everything needed: scope, allowlist, param-name variability, cost, and return-format expectations. Nothing an agent needs to call it correctly is missing.

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% and the schema already documents path and params, so the baseline is 3. The description goes further by noting that param names vary per endpoint (handle for profiles, query for keyword searches, subreddit for Reddit) and gives example shapes, which is genuine added meaning.

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 names a precise verb and resource — 'Generic escape hatch for any ALLOWLISTED long-tail social/web endpoint' — and explicitly frames it against the dedicated search_* siblings. An agent knows exactly what class of call this performs without opening the 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?

It states when to use it (endpoints the dedicated search_* tools don't cover), when not to (a WARNING says prefer the dedicated search_* tools because output is large/messy), and gives a concrete invocation example. Routing guidance is explicit rather than inferred.

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

find_competitorsFind competitorsAInspect

Discover a brand's competitor / similar / adjacent brands from its domain (Claude grounded by web search). mode=competitors (default, excludes the searched company), inspiration (best relevant ads incl. it), or company. Costs a few credits for the discovery model (no ad-data charge); free inside a new account's first-brand setup.

ParametersJSON Schema
NameRequiredDescriptionDefault
modeNo'competitors' (default, excludes the searched company), 'inspiration' (best relevant ads incl. it), or 'company'
domainYesthe brand domain, e.g. flourish.com

TDQS

A4.1/5.0
Behavior4/5

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

Annotations already cover readOnly, openWorld, idempotent, destructive hints, and the description adds important behavioral cost structure ('Costs a few credits for the discovery model (no ad-data charge); free inside a new account's first-brand setup') which the agent needs for planning. It also discloses the grounding mechanism ('Claude grounded by web search'). It stops short of describing return format or rate limits, but the credit model is valuable non-structured context.

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's a single dense sentence but front-loads the core action and mode distinctions before the cost note. Slightly packed, but every clause earns its place by covering purpose, modes, and billing.

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 discovery tool with no output schema and only two params, the description covers purpose, mode selection, grounding source, and the credit/free-first-brand nuance. The main gap is not describing the shape of the returned competitor list, though that's a minor omission given the task is exploratory discovery.

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

Parameters3/5

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

Schema coverage is 100%, so both parameters including the mode enum are fully documented in the schema itself. The description restates the mode options but adds no syntax, format, or validation detail beyond what the schema provides, so the baseline of 3 applies.

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 and resource ('Discover a brand's competitor / similar / adjacent brands from its domain') and distinguishes the three modes with their distinct outputs, so an agent can tell it apart from siblings like search_meta_ads or pull_competitor_ads without opening the 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 specifies which mode to use ('competitors' default excludes the searched company; 'inspiration' includes it; 'company'), giving practical selection guidance. However it doesn't state when to use this tool versus sibling alternatives like find_creators or competitor_teardown, so it stops short of full when/when-not guidance.

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

find_creatorsFind the creators already winning in a nicheAInspect

Scan organic TikTok, Instagram Reels and YouTube for a niche across a few query variants, fold the posts into creators, and rank them on median views, engagement rate and how often they show up for that niche; the top rows get follower counts AND public contact info (an Instagram business email / phone / category, the bio link, an email in a TikTok bio) so outreach can start from the result. Real people, not AI actors — for influencer sourcing, UGC casting and partnership prospecting ("who should we send product to?"). About one credit per search call (platforms × queries, default 3 × 3) plus one per enriched profile; repeats inside 20 minutes are free. Then shortlist (save_to_swipefile), check a profile (instagram_profile / fetch_social_data), draft outreach (generate_text), or approve them for Partnership Ads (manage_meta_partnership_creator). marketplace:true also searches Instagram’s creator marketplace (Meta’s own creator directory: followers, badges, marketplace email) for the same niche and returns those rows beside the ranked list.

ParametersJSON Schema
NameRequiredDescriptionDefault
limitNocreators to return, 1–30 (default 12)
nicheYesproduct category, topic or hashtag — "calorie tracker app", "matcha", "#cleanbeauty"
enrichNoread follower counts for the top 6 (default true, ~1 credit each)
queriesNoquery variants per platform, 1–4 (default 3); each is a paid search call
platformsNodefault all three
marketplaceNoalso search Instagram’s creator marketplace (Meta’s own creator directory, free) for the same niche; needs the Meta connector
minAvgViewsNo
minEngagementNointeractions per view, 0–1 (0.05 = 5%)

TDQS

A4.8/5.0
Behavior5/5

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

Beyond the annotations, it discloses cost model (one credit per search call = platforms × queries, plus one per enriched profile), a 20-minute free-repeat caching window, enrichment scope (top 6, default), and the Meta connector prerequisite for marketplace:true — rich behavioral context the annotations do not carry.

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 scan/rank behavior and value, but the single dense paragraph is long and packs workflow, pricing, and marketplace details into run-on clauses. Every element is useful, but structure could be tighter.

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?

No output schema exists, yet the description specifies return content (ranked creators with median views, engagement rate, follower counts, and contact info). Combined with cost and workflow context, 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 already 88%, so the schema documents most params. The description still adds the credit formula tying platforms and queries together, the enrich default/cost, and the marketplace connector dependency, going modestly beyond the schema text.

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 (scan/fold/rank) across named platforms and resources, and clearly distinguishes itself from list_creators and search_* siblings by describing aggregation into ranked creators. An agent knows exactly what this returns without opening the 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?

Explicitly frames the use cases (influencer sourcing, UGC casting, partnership prospecting) and names the downstream alternatives (save_to_swipefile, instagram_profile / fetch_social_data, generate_text, manage_meta_partnership_creator), routing the agent through the workflow.

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

find_soundFind a soundAInspect

A sound for an edit by name ('the FAAAA sound'), by the moment ('bad news reaction') or by link (TikTok sound, meme-sound page, post, audio file): a durable mp3 and where it starts and lands. Named/described sounds come from what TikTok uses now; pick takes another candidate.

ParametersJSON Schema
NameRequiredDescriptionDefault
urlNo
pickNo
queryNo

TDQS

A4.4/5.0
Behavior4/5

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

The description adds meaningful behavioral detail beyond the annotations: the result is a durable mp3 with start/end positions, and pick selects another candidate from the current TikTok-derived set. This is useful context that the schema and annotations do not provide. No contradiction with the annotations exists.

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

Conciseness5/5

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

The entire description fits into one dense sentence with examples and the key caveat about TikTok's current sounds. There is no filler, and the most important information—what the tool returns and how to invoke it—is front-loaded.

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?

With no output schema, the description still tells the agent the essential return shape (durable mp3 and placement) and the main input modes. It does not explain no-result or error behavior, but for a simple finder with open-world annotations this is sufficient for correct invocation.

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 0%, so the description carries the full burden for parameter meaning. It maps query to name/moment, url to link types (TikTok sound, meme-sound page, post, audio file), and pick to alternative-candidate selection. It could be more precise about pick's numbering and whether parameters are mutually exclusive, but it compensates well for the bare schema.

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 exactly what find_sound does: find a sound for an edit by name, moment, or link, and return a durable mp3 plus its start/end placement. It goes beyond the title and distinguishes this tool from generic TikTok search or music-generation siblings by specifying the output and the three lookup routes.

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?

The intended use case is clear: an editor needing a sound by name, described moment, or source link. The description also notes that named/described sounds are based on what TikTok currently uses, which helps the agent understand the data source. It does not explicitly name alternative sibling tools or state when not to use it, so it stops short of a 5.

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

find_toolsFind a Hermoso tool by name or taskA
Read-onlyIdempotent
Inspect

Search EVERY Hermoso tool — your starting list is deliberately short, and everything else in the product is here — by name, task or group. Each row gives the tool's PARAMETERS in one line, its CREDIT COST (free means free on every plan; a tool that runs a model quotes the live per-model figure) and its recent HEALTH on this server (failure rate and typical duration, or "no recent calls", which means unseen and not broken). Use it the moment the user asks for something you do not see a tool for (a campaign, an ad set, a lead form, a click-to-WhatsApp ad, a report, keywords, audiences): a tool missing from your list is NEVER proof the feature is missing. Then run the tool with call_tool. A tool that is failing or needs a connector this workspace has not made is ranked last and marked, never hidden — pass onlyHealthy:true if you want those left out. Free, read-only.

ParametersJSON Schema
NameRequiredDescriptionDefault
groupNolimit to one group: core, research, create, channels, channel_admin, analytics, ads, files, workspace
limitNohow many to return (default 12, max 40)
queryNowords from the task or the tool name, e.g. "lead form", "whatsapp", "google ads keyword", "meta insights"
onlyHealthyNoleave out tools that are failing their recent calls or that need a connector this workspace has not made. Default false — nothing is hidden unless you ask, because a missing row reads as a missing capability.

TDQS

A4.4/5.0
Behavior5/5

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

Beyond the readOnly/idempotent annotations, the description discloses meaningful behaviors: failing tools or those needing missing connectors are 'ranked last and marked, never hidden,' and the meaning of 'no recent calls' is explained. It also clarifies how credit cost is presented (free vs. live per-model figure). This is rich behavioral context beyond 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?

The description is dense but efficient, covering purpose, usage trigger, output contents, and behavioral nuances in a few sentences. It uses dashes to pack information without redundancy. Slightly long, but every sentence serves a purpose and the key scoping statement is front-loaded.

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?

There is no output schema, but the description compensates by stating exactly what each returned row contains (parameters, credit cost, health). It also covers the critical next step (call_tool) and the onlyHealthy filter. For a discovery tool with optional parameters, this is complete context for an agent to use it correctly.

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

Parameters3/5

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

The schema covers 100% of parameters with descriptions, so the baseline is 3. The tool description itself does not add parameter-level semantics beyond what the schema already provides; it only reiterates the onlyHealthy behavior in prose. No additional meaning is given for group, limit, or query beyond their schema descriptions.

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 clear verb and resource: 'Search EVERY Hermoso tool ... by name, task or group.' It distinguishes itself from the short default tool list and from siblings by emphasizing that it covers the full tool set and returns metadata (parameters, cost, health). This leaves no ambiguity about what the tool does.

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?

The description gives an explicit trigger condition: 'Use it the moment the user asks for something you do not see a tool for' and provides concrete examples (campaign, ad set, lead form). It also instructs to follow up with call_tool. It does not explicitly name when-not-to-use alternatives like the list_* tools, so it stops short of a perfect 5.

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

finish_videoFinish videoAInspect

Post-process an EXISTING rendered video (its served mp4 URL) with the proven direct-response 'reviewer' finish and/or a film-grain pass — no AI model, ~30s, a couple of credits. pills=true composites a header pill (e.g. '10/10 would buy again'), a brand-accent sub-pill, and 3-4 green-check proof pills cascading in on the beat (YOU author the copy: header ≤40 chars, sub ≤34, each point ≤44 — concrete real benefits, never fabricated stats). grain=true applies a subtle camera-grain finish that makes photoreal AI renders look phone-shot ('less AI') — works alone or with pills. Returns a NEW video; the original is untouched.

ParametersJSON Schema
NameRequiredDescriptionDefault
subNoaccent sub-pill copy, ≤34 chars (usually the product/brand)
grainNodefault false — anti-AI film-grain finish
pillsNodefault true — set false for a grain-only pass
accentNobrand accent hex for the sub-pill
headerNoheader pill copy, ≤40 chars (required when pills is on)
pointsNo3-4 proof points, ≤44 chars each
videoUrlYesthe served URL of the video to finish (from a previous render/job)

TDQS

A4.6/5.0
Behavior5/5

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

Despite having annotations, the description adds substantial behavioral detail beyond them: 'no AI model, ~30s, a couple of credits', the exact compositing behavior of pills, the anti-AI intent of grain, and crucially 'Returns a NEW video; the original is untouched' which reinforces destructiveHint=false. This is exactly the extra context annotations cannot carry.

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?

The core action is front-loaded in the first clause, and every sentence carries useful information about modes, cost, and return behavior. It is dense and slightly overloaded with em-dashes and parentheticals, and the char-limit repetition duplicates the schema, so it is not maximally tight.

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 correctly carries the return contract ('Returns a NEW video; the original is untouched') plus cost, runtime, and the two operating modes. For a 7-parameter tool with one required field, an agent has everything needed 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 is 3, but the description meaningfully enriches semantics: it explains what pills=true composites (header pill, brand-accent sub-pill, cascading green-check proof pills), that grain applies a subtle camera-grain finish, and that the caller authors the copy with real (never fabricated) benefits. It repeats the char limits already in the schema, which limits it below 5.

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 precise verb and resource ('Post-process an EXISTING rendered video (its served mp4 URL)') and clearly distinguishes this finishing step from generation/editing siblings by naming the two passes it performs (reviewer pills, film-grain). An agent can tell it apart from generate_video, edit_video, or upscale_video without opening the 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 clear usage context: the input must be an already-rendered video URL from a previous render/job, and it explains the two modes ('pills=true ... grain=true ... works alone or with pills'). However, it never explicitly names an alternative tool or states when NOT to use it (e.g. versus edit_video or upscale_video), so routing is left partly to inference.

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

fix_beatFix a video beatAInspect

Surgically re-render ONE time window (1.5-8s) of an existing rendered video and splice it back on the VIDEO TRACK ONLY — the rest of the video and ALL audio stay byte-identical. Use when one beat/shot is broken ('the shot at 8 seconds glitches') and a full re-render would waste the parts that worked; bills only the replacement clip's seconds (~1/3 of a full render). Do NOT pick a window covering spoken dialogue (a video-only splice under speech breaks lip-sync) — pass speechWindows to enforce this.

ParametersJSON Schema
NameRequiredDescriptionDefault
dryRunNotrue = return the exact credits this fix reserves and render nothing
promptYeswhat the replacement footage should show — describe the shot, matching the master's style
refImageNooptional product/style anchor image URL
videoUrlYesthe served URL of the master video to fix
endSecondsYeswindow end in seconds (window 1.5-8s)
startSecondsYeswindow start in seconds
speechWindowsNo[[start,end],...] windows with spoken lines — the fix window must not overlap these

TDQS

A4.8/5.0
Behavior5/5

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

Goes well beyond the annotations: billing model ('bills only the replacement clip's seconds (~1/3 of a full render)'), preservation guarantee ('the rest of the video and ALL audio stay byte-identical'), and the harm mode of a bad window ('a video-only splice under speech breaks lip-sync'). Annotations only declare non-readonly/non-idempotent/open-world; the description carries the real behavioral burden.

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?

Dense but front-loaded and every clause earns its place: purpose first, then routing rationale, then the dialogue warning. Slightly packed into one long flow, but no wasted sentences.

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?

For a mutating, non-idempotent, open-world tool with no output schema and 7 params, the description covers cost, side effects, scope limits, and the critical failure mode. Nothing an agent needs to call it correctly is missing.

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 baseline is 3, but the description adds meaning beyond schema: the 1.5-8s window constraint, what dryRun reserves (exact credits), and the lip-sync rationale that makes speechWindows a correctness requirement rather than a hint. Adds genuine value over the field descriptions.

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+resource+scope: 'Surgically re-render ONE time window (1.5-8s) of an existing rendered video and splice it back on the VIDEO TRACK ONLY.' The scope (single window, video track only, audio untouched) distinguishes it sharply from full-render siblings like render_ad or edit_video.

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?

Gives an explicit when ('one beat/shot is broken') including an example utterance, explains the alternative it displaces ('a full re-render would waste the parts that worked'), and adds an explicit exclusion ('Do NOT pick a window covering spoken dialogue') with a mitigation ('pass speechWindows').

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

forgetForget a memoryA
DestructiveIdempotent
Inspect

Delete a saved Memory item by its id (from list_memory). Records a cross-device delete so it doesn’t come back. Minor + re-creatable (you can remember it again), so no confirm needed.

ParametersJSON Schema
NameRequiredDescriptionDefault
idYesthe memory item id (from list_memory)
brandNoprofile id/name from list_brands, this call only

TDQS

A4.2/5.0
Behavior4/5

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

Annotations already declare destructiveHint=true and idempotentHint=true, so the description's added value is the cross-device delete behavior (it won't come back) and the re-creatable/reversible nature that justifies skipping confirmation. That is genuine context beyond the annotation flags, though it doesn't cover error cases (e.g., unknown id) or response behavior.

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

Conciseness5/5

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

Three tight sentences with the action and key front-loaded, then persistence behavior, then the confirmation rationale. No filler.

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 single-parameter destructive tool with no output schema, the description covers action, key source, durability, and reversibility. Minor gap: no mention of failure behavior for an invalid or already-deleted id.

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

Parameters3/5

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

Schema coverage is 100% and the id parameter is already documented as coming from list_memory, so the description mostly restates the schema. The optional brand parameter ('this call only') is never addressed in the description.

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 (Delete) and resource (a saved Memory item) plus the lookup key (id from list_memory), which cleanly separates it from siblings like list_memory, remember, and tidy_memory.

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 clear context: the id comes from list_memory, and it explicitly says no confirmation is needed because the action is minor and re-creatable. It does not name when to prefer tidy_memory or other memory-management siblings, but the routing for this tool is unambiguous.

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

format_sheetFormat a Google SheetA
DestructiveIdempotent
Inspect

Make an exported sheet readable: bold the header row, FREEZE it so it stays visible while scrolling, and auto-size the columns so nothing is cut off. Worth calling right after create_sheet — a raw export with unsized columns and a header that scrolls away is the difference between a spreadsheet someone reads and one they close. Changes no cell VALUE, so it is never gated. The defaults do all three on the first tab; pass tab to pick another, freezeRows:0 to skip freezing, boldHeader:false or autoResize:false to skip those.

ParametersJSON Schema
NameRequiredDescriptionDefault
tabNotab title or numeric sheetId (default: the first tab)
sheetUrlNo
autoResizeNo
boldHeaderNo
freezeRowsNohow many top rows to freeze (default 1, 0 = none)
spreadsheetIdNo

TDQS

A3.9/5.0
Behavior3/5

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

Annotations already declare readOnlyHint=false, idempotentHint=true and destructiveHint=true, and the description adds the useful 'never gated' behavioral fact plus the helpful defaults (all three actions on the first tab, how to skip each). However, it frames itself as non-destructive ('Changes no cell VALUE') without warning that applying bold/auto-resize can overwrite existing cell formatting, which sits in tension with destructiveHint=true.

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?

The core action is front-loaded and the parameter/skip mechanics come last, which is the right order. It is somewhat long and the 'difference between a spreadsheet someone reads and one they close' clause is editorial filler that does not add operational information.

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 formatting tool with annotations and no output schema, the definition covers what changes, the defaults, how to opt out of each step, and gating behavior. The main missing piece is whether existing formatting is overwritten, which matters given destructiveHint=true.

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

Parameters3/5

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

With only 33% schema description coverage, the description must compensate. It explains the semantics of tab, freezeRows (0 = skip), boldHeader and autoResize (false = skip), which helps, but leaves sheetUrl and spreadsheetId entirely undocumented in both schema and description. Partial compensation only.

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 names a concrete verb and resource (format an exported Google Sheet) and enumerates the exact operations: bold the header, freeze it, auto-size columns. It clearly separates this from content-mutating siblings by stating it 'Changes no cell VALUE,' so an agent can distinguish it from update_sheet/append_to_sheet 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 Guidelines4/5

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

It gives explicit timing guidance ('Worth calling right after create_sheet') and, via the 'changes no cell VALUE' note, implicitly routes value edits to other tools. It stops short of stating exclusions for the close siblings (update_sheet, manage_sheet_tabs, clear_sheet_range), so it is clear context without full when-not guidance.

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

generate_avatarGenerate talking avatarAInspect

ANIMATE A PHOTO so it talks: the presenter in image says script word for word, a separate voice lip-synced onto the still. Use it ONLY when the user asks for an animated photo / talking photo / lip-sync look by name; for any other talk-to-camera or spokesperson ask use generate_video with speak (the person filmed saying it, which reads as real footage). About 1-3 min, holds the pose steady, 480p/720p. The per-second credit price is in hermoso_capabilities (avatarEngines); dryRun:true returns this exact job's hold without rendering. Blocks until done where the host allows, else returns a job id to poll with get_job. Requires canAvatar. Spends credits; a refusal before rendering costs nothing.

ParametersJSON Schema
NameRequiredDescriptionDefault
seedNofixed seed, only for an engine other than standard
imageYeslocal path or URL of the presenter portrait. Using a real person’s face, likeness or voice confirms you are them or have their consent, take full, unlimited responsibility for its use and will cover any claim against Hermoso (hermoso.ai/terms). Paid plan only; recorded.
voiceNovoice name (Aria / Sarah / George / Adam). Leave it out and the voice matches the person in `image`; if nobody can be read, the call is refused free asking for one
dryRunNoreturn the credits this exact job would hold, without rendering
engineNoleave out for the standard engine; only an engine listed in hermoso_capabilities avatarEngines is accepted
promptNomotion direction, only for an engine other than standard that hermoso_capabilities lists
scriptYesthe words the avatar speaks
resolutionNo'720p' (default) or '480p'
acceptQueueNoonly for an engine hermoso_capabilities marks oneAtATime: wait in line

TDQS

A4.8/5.0
Behavior5/5

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

Annotations only cover safety/idempotency flags, but the description adds the traits that actually matter: ~1-3 min runtime, steady pose, 480p/720p output, pricing source (hermoso_capabilities avatarEngines), dryRun returning the exact hold cost, blocking-vs-job-id semantics, the canAvatar requirement, credit spend, and that a pre-render refusal is free.

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 imperative core ('ANIMATE A PHOTO so it talks') and every clause carries load-bearing information. It is a dense run-on paragraph, however, so the pricing, job-polling, and permission details all compete in a single block rather than being separately scannable.

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?

For a 9-param, no-output-schema, credit-spending generation tool, the description covers everything an agent needs: execution mode (block vs job id + get_job), cost source, permissions, error/free-refusal behavior, and resolution options. Nothing material is left unstated.

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%, so the baseline is 3; the description still adds relational meaning the schema doesn't spell out, tying `image`+`script` to the core behavior ('says `script` word for word') and describing dryRun as returning 'this exact job's hold without rendering'. It doesn't document the less-central params like seed, engine, prompt, acceptQueue.

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 ('ANIMATE A PHOTO so it talks') with the exact mechanism: a still portrait lip-synced to a separate voice. It explicitly contrasts itself with the sibling generate_video and the 'speak' mode, so an agent can distinguish the two without reading either 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?

Gives an explicit gate ('Use it ONLY when the user asks for an animated photo / talking photo / lip-sync look by name') plus the alternative and its condition ('for any other talk-to-camera or spokesperson ask use generate_video with `speak`'). When-to-use, when-not-to-use, and the substitute are all named.

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

generate_imageGenerate ad imageAInspect

Render a finished ad IMAGE and return its served URL. refImages (local paths or URLs) force product-accurate compositing (drops a real product into the scene). MULTI-BRAND CAUTION: useBrand hydration pulls the SAVED workspace brand — when working a brand that is NOT the saved one (a fresh draft_brand), pass that brand's own productImages/logo as refImages (and useBrand:false) or the output composites the WRONG brand's product. NOTE that the saved-brand hydration also decides the ENGINE: attaching product photos routes the render to the compositing model, so a model you named is only honoured when no references ride — pass raw:true (or useBrand:false) to render on exactly the model you asked for. model = a catalog id from hermoso_capabilities (omit for the default). PUTTING A REAL PRODUCT IN A REAL PERSON’S HANDS, or a garment on them, is a DIFFERENT KIND OF ROW and you must name it: the ids marked needsRefs with a refsMax in hermoso_capabilities take a person photo first and up to three product/garment photos after it, and they EDIT THE PHOTOGRAPH rather than compositing — THE PERSON IS RE-POSED to hold or wear the thing, so their stance and hands change while their face, clothing, setting and lighting are kept. That is not an object swap in a fixed frame; if you needed the rest of the photograph untouched, this is the wrong tool. Every finished render says which way it went. RAW MODEL ACCESS: raw:true dispatches your prompt to the model BYTE-IDENTICAL — no rewriting, no appended guidance, no negative prompt, no brand references attached on your behalf. Credits, the durable delivery of the finished asset and the per-model validation are unchanged. Fast (seconds). Spends credits.

ParametersJSON Schema
NameRequiredDescriptionDefault
rawNoRAW MODEL ACCESS: run the caller’s prompt on the named model with no Hermoso adjustments at all — the prompt reaches the provider byte-identical (no hex-to-colour-name rewrite, no prepended fidelity preamble) and NO saved-brand product photos are attached, so the model you name is the model that renders. Use it to drive the raw catalog; leave it off for an on-brand ad. Billing, the durable Library landing and per-model validation are unchanged.
maskNoMASKED EDIT — change ONE region of an image and keep the rest: a local path or URL of a mask image for refImages[0] (the image being edited). Either convention works and the reply says which it read: TRANSPARENT pixels = change, or, on a mask with no transparency, WHITE = change and black = keep. Any size; it is scaled to the image. The mask GUIDES the edit rather than stencilling it: the new content can blend a little past its edge. Runs on the model hermoso_capabilities marks `refs.mask` (gpt-image-2.5): leave `model` empty or name that one — any other named model is refused, free. Needs refImages; the result keeps the source image's own frame, so aspectRatio is not applied.
modelNoimage model id from hermoso_capabilities. A model whose `refs.mode` is "edit" there (gpt-image-2.5) takes your refImages on ITS OWN editor, up to its `refs.max`, instead of the default compositor
promptNoREQUIRED on every model EXCEPT the pose rows below. the full image prompt — subject, composition, lighting, and any on-image ad text. ON A POSE MODEL (product-in-hand / try-on) THIS IS EXTRA DIRECTION AND IT IS OPTIONAL — leave it out and the pose is built for you. If you do write one, DESCRIBE THE POSE ("she holds the bottle upright in her right hand at chest height, label to camera"); do NOT phrase it as a swap ("replace the mug with the bottle"), which is REFUSED for free, because the product then comes out the size of whatever it replaced — a 30ml bottle rendered mug-sized in testing.
fixLabelNodefault true: when the saved brand's product photo rides in this render, the product's label on the finished image is READ and compared with the photo, and re-printed from the photo at close range ONLY if it came out wrong (a label that is already right costs only the check, a credit or two; a re-print adds about ten). The reply says whether the label was checked, fixed or left as rendered (`labelPass`). Pass false when the user wants the packaging left exactly as generated: nothing is checked or re-printed.
useBrandNodefault true: with no refImages, the server hydrates the SAVED brand’s product/logo references so the output lands on-brand; pass false for a pure prompt-only render
imageSizeNopixel-size preset for models that support it: 1K/2K, and 4K on the models hermoso_capabilities lists with a 4K imageSize price (a 4K ask on any other model is refused, free) — omit for the default
refImagesNolocal file paths or URLs of product/logo references to composite in. ON THE POSE MODELS — any row hermoso_capabilities marks `needsRefs` with a `refsMax`, such as putting your product in someone’s hands or a virtual try-on — THE ORDER IS THE CONTRACT AND IT IS NOT A COMPOSITE: refImages[0] is the PERSON photo, and the rest (up to `refsMax` minus one) are the product or garment photos. Reversed, you get the product wearing the person. A 4th product is dropped and the reply says so. Using a real person’s face, likeness or voice confirms you are them or have their consent, take full, unlimited responsibility for its use and will cover any claim against Hermoso (hermoso.ai/terms). Paid plan only; recorded.
aspectRatioNoe.g. '1:1', '9:16', '16:9', '4:5'. Each model draws its own list (hermoso_capabilities prints it per model, e.g. Nano Banana 2 goes to 1:8 and 8:1); a ratio the chosen model cannot draw is refused before anything is charged
logoPlacementNooverlay: real logo file laid flat. in_scene: on something in the scene, from the file, checked. auto (default): from the prompt

TDQS

A4.4/5.0
Behavior5/5

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

Annotations cover only the generic profile (readOnly=false, destructive=false, openWorld=true, idempotent=false), and the description adds substantial non-obvious behavior: credits are spent, renders are fast (seconds), the finished asset lands durably in the Library, per-model validation runs, and several invalid requests are refused for free. It also discloses the engine-selection side effect (attached references route to the compositing model and override a named model) and the cost of label re-printing.

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

Conciseness3/5

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

Front-loaded with the purpose, then organized by caution, and the length is defensible for a 10-parameter credit-spending tool. However, it duplicates schema descriptions almost verbatim (raw, mask, refImages, useBrand) and leans on ALL-CAPS emphasis in multiple paragraphs, which inflates it without adding new information.

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 10 optional parameters, no output schema, and a paid multi-model render pipeline, the description covers what an agent needs: return shape (served URL, labelPass, which mode was used), cost signals, refusal behavior, reference-ordering contract, and consent/terms obligations for real likenesses. Nothing material is missing.

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 per-parameter baseline is 3, but the description adds cross-parameter interaction semantics the schema does not express: useBrand hydration determines the engine, a named model is only honoured when no references ride, and mask requires refImages and suppresses aspectRatio. Those interactions are the genuinely additive part; the rest largely restates schema text.

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

Purpose4/5

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

Opens with a concrete verb, resource and outcome: 'Render a finished ad IMAGE and return its served URL.' It also carves out what this tool is not ('if you needed the rest of the photograph untouched, this is the wrong tool'), which helps separate it from the edit/composite family. It never names sibling tools such as render_ad, edit_image, or generate_avatar, so the differentiation is conceptual rather than explicit.

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 when-to-use and when-not: the MULTI-BRAND CAUTION tells the agent to pass a draft brand's own productImages/logo with useBrand:false, the pose-model paragraph states when to name a needsRefs row and warns it is 'not an object swap in a fixed frame,' and the raw section states when raw:true is appropriate. Exclusions are stated with consequences (reversed refImages order, swap-style prompts refused free), which is exactly the routing guidance an agent needs.

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

generate_musicGenerate musicAInspect

RAW music: describe it (genre, mood, instruments, tempo), get an instrumental MP3. Flat fee: explainerMusicCredits in hermoso_capabilities.

ParametersJSON Schema
NameRequiredDescriptionDefault
promptYesthe music in words, e.g. 'lo-fi jazz, brushed drums, 80 bpm'

TDQS

A3.6/5.0
Behavior4/5

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

Annotations cover the safety profile (not read-only, open-world, non-idempotent, non-destructive), so the description adds real value by disclosing the flat-fee credit cost model and the exact output format (instrumental MP3). It does not say whether generation is synchronous or returns a job handle for get_job/list_jobs, which is a notable omission for a generative tool.

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?

Two compact sentences, front-loaded with the input/output contract before the cost note. The 'explainerMusicCredits'/'hermoso_capabilities' reference is cryptic but takes no excessive space.

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

Completeness3/5

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

For a single-param, no-output-schema tool the description covers input and output reasonably. However, it omits whether the call is async (requiring get_job polling), prompt length limits, and why the fee is described as 'explainerMusicCredits' for a RAW music tool – gaps an agent would care about.

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

Parameters3/5

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

Only one parameter with 100% schema description coverage, so the baseline is 3. The description's genre/mood/instruments/tempo list largely restates the schema's own example ('lo-fi jazz, brushed drums, 80 bpm') rather than adding new syntax, length limits, or formatting guidance.

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

Purpose4/5

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

States a specific verb+resource ('RAW music: describe it ... get an instrumental MP3') and names the concrete artifact produced. It implicitly distinguishes itself from siblings like generate_voice and generate_video by specifying instrumental MP3, though it never names an alternative outright.

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

Usage Guidelines3/5

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

Usage is implied rather than stated – the 'RAW music' framing suggests this is the bare text-to-instrumental path, and it points to hermoso_capabilities for fee detail, but there is no explicit when-to-use/when-not or comparison against sibling music-related tools (find_sound, make_explainer).

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

generate_textGenerate textAInspect

Text generation against the writing-model catalog (Claude, Gemini, GPT, Llama, DeepSeek…) — ad copy, hooks, scripts, rewrites, brainstorms. Prompt-only, no ad assembly (for a finished on-brand creative use plan_ad -> render_ad). BY DEFAULT the model answers as a marketing copywriter (a short house system prompt is applied, which is what you want for ad copy); pass raw:true for a plain, unstyled answer from the model itself with NO system prompt at all. model = a writing-model id from hermoso_capabilities (omit for the default Claude orchestrator). Paid (a credit or two by length).

ParametersJSON Schema
NameRequiredDescriptionDefault
rawNoRAW MODEL ACCESS: send the prompt with NO Hermoso system prompt — the model answers as itself rather than as an ad copywriter. Use it whenever the ask is not marketing copy (analysis, code, extraction, a plain question). Default false: the copywriter framing is applied.
modelNoa writing-model id from hermoso_capabilities (a Claude / Gemini / GPT / Llama / DeepSeek id) — omit for the default
promptYesthe writing task / question

TDQS

A4.7/5.0
Behavior5/5

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

Annotations only give safety/scope hints (readOnly=false, openWorld=true, idempotent=false), but the description adds substantial behavior: a default house system prompt is applied and what it does, how raw:true suppresses it, the model source (hermoso_capabilities), the default orchestrator, and that the call is paid by length. That is real disclosure beyond structured fields.

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 purpose and scope, and each clause earns its place (use cases, alternative path, default-vs-raw behavior, cost). The model enumeration and parentheticals are slightly dense but not wasteful.

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?

With no output schema needed for a free-text generator and annotations covering safety, the description supplies the operationally important details (raw default, model source, cost, no ad assembly). It could note latency/response-size behavior, but nothing essential for correct invocation is missing.

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 bar is a baseline 3, but the description goes further by explaining that model ids come from hermoso_capabilities, that omission picks the default Claude orchestrator, and by elaborating the raw default behavior—adding meaning the schema's terse text does not fully carry.

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 (generate text) and resource (the writing-model catalog), enumerating concrete use cases (ad copy, hooks, scripts, rewrites, brainstorms). It explicitly contrasts itself with plan_ad -> render_ad for a finished on-brand creative, so an agent can route without opening the 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?

Names the alternative path explicitly ('for a finished on-brand creative use plan_ad -> render_ad') and states the condition that selects raw:true ('whenever the ask is not marketing copy'). Both the when-to-use and when-not are covered.

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

generate_videoGenerate videoAInspect

Render a RAW video clip from your own prompt and return its served mp4 URL. For finished brand ADS prefer render_ad (it runs the Studio quality pipeline — composited text, clean speech, end card, music); use this for raw/experimental clips or precise manual control. ONE generation = one continuous clip up to the model’s longest listed duration — the longest-clip model in the catalog today renders a full multi-beat spot of up to 30 SECONDS in ONE unbroken take with native synchronized audio, so never assume a generic 8–10s cap and never stitch something that fits one clip; durationSeconds must be one of the model’s durations from hermoso_capabilities, which is the live list. TO GET A SPECIFIC MODEL, NAME IT in model: an unnamed render is routed by the server’s own auto-pool, which is narrower than the catalog, so the longest-clip and highest-resolution models are reached by naming them. Renders take 1–3 min. refImage anchors the opening frame; ttsScript adds a voiceover. AUDIO IS NOT FREE AND NOT OPTIONAL BY DEFAULT: a clip delivered with no audio of its own gets a music bed composed and CHARGED on top of the render (see musicMood and audio) — on a cheap short draft the bed can cost as much as the clip. Pass refVideo (a clip URL) to EDIT an existing video instead — the omni engine transforms that clip per your prompt, inheriting its canvas + length (aspectRatio/durationSeconds are ignored for an edit). RAW MODEL ACCESS: by default a few small guards are appended (packaging/label safety with no reference image, a negative prompt where the model takes one, reference-binding lines) and hex colour codes become colour names; raw:true dispatches your prompt to the model BYTE-IDENTICAL — no rewriting, no appended guidance, no negative prompt, no brand references attached on your behalf. Credits, the durable delivery of the finished asset and the per-model validation are unchanged. Spends credits (Starter plan is video-blocked server-side).

ParametersJSON Schema
NameRequiredDescriptionDefault
rawNoRAW MODEL ACCESS: dispatch this prompt to the model BYTE-IDENTICAL — no appended packaging/label guidance, no negative prompt, no reference-binding lines, no hex-to-colour-name rewrite. Use it when you want the model itself rather than Hermoso's render craft. Two vendor-required fixes still apply: extra @ImageN tokens are dropped and an over-long prompt is trimmed at a sentence (on Veo it is refused with the length and the cap instead). Billing, durable delivery and per-model validation are unchanged.
loopNotrue = a seamless loop whose last frame flows back into its first. Only models with loop true in hermoso_capabilities; needs refImage and cannot be combined with endImage.
audioNodefault true. false = render SILENT: no native model audio, no music bed, and no bed charge held or billed. This is the ONLY way to decline the automatic bed (see musicMood) — leave it alone for anything that should have sound, and do not combine it with ttsScript.
modelNovideo model id from hermoso_capabilities; a named model is never swapped without asking. Omit to let the router pick
shotsNoMULTI-SHOT: one clip cut into these shots, in order. The seconds must add up to a length the model renders; replaces `prompt` (send either). Only models with multiShot true in hermoso_capabilities. Each shot prompt is capped per model (shotPromptMaxChars there; Kling 3.0: 512 characters); a longer one is refused free, never cut.
speakNoA PERSON SAYING THESE EXACT WORDS TO CAMERA — the default for any "make my photo talk" / spokesperson / talk-to-camera ask. Pass with `creator` or a portrait as `refImage`: the video model films them saying it in their own voice, matched to who they are, and the length follows the words (leave durationSeconds out). `prompt` is then the staging (e.g. "natural", "walking in a park"). Real footage, not an animated photo; generate_avatar is the animated-photo look, only when asked for by name
anglesNoOPTIONAL, OFF BY DEFAULT: with `creator`, also send the extra views that creator already has saved (their pose plates, up to 2) beside their portrait. A test showed no visible improvement over the portrait alone, and each extra view adds about 25 s before the render starts, so leave it off unless asked. Views are skipped when the face library is busy, and the render always goes ahead on the portrait.
extendNotrue = EXTEND refVideo: the same clip continues per your prompt for durationSeconds more (each model’s extend.minSeconds..maxSeconds in hermoso_capabilities), delivered as ONE clip, source then continuation. Needs model named (a model with extend in hermoso_capabilities).
promptNothe video prompt / shot description (for a refVideo edit, this is the transformation instruction); optional with `shots`
creatorNoSTAR A SAVED CREATOR in this clip — their id from list_creators, or the name you know them by, or a PRESET AI creator from list_creators presets (exact name or id; free, no generation). Their saved portrait rides first among the references as the on-camera person, with their saved consent, exactly as render_ad casts them; a real person saved from a photo keeps their real face on camera. An unknown name is refused by name, nothing charged.
endImageNolocal path or URL of the LAST frame: the clip travels from refImage (required with it) to this image. Only models with endFrame true in hermoso_capabilities take it; any other named model is refused by name, nothing charged.
refImageNolocal path or URL to anchor the first frame. Using a real person’s face, likeness or voice confirms you are them or have their consent, take full, unlimited responsibility for its use and will cover any claim against Hermoso (hermoso.ai/terms). Paid plan only; recorded.
refVideoNoURL of an existing video to EDIT rather than generate from scratch — the clip is transformed per your prompt, inheriting the SOURCE clip’s canvas (aspect ratio) and, on every model hermoso_capabilities marks `sourceLength`, its LENGTH too: those endpoints have no duration parameter, their listed `durations` are the per-second price ladder, and a durationSeconds you send is reported back as unused rather than silently dropped. Trim the source to change the length. Omit `model` for the default editor, or name a model whose videoEdit is true in hermoso_capabilities (a named model that cannot edit is refused, nothing charged); refImage rides along as the look of what the edit adds. With extend:true this is instead the clip to EXTEND. Omit to generate a fresh clip.
ttsVoiceNovoice name, e.g. Rachel / George
faceRouteNoONLY after a render came back saying the video model's safety check flagged a person's face: 'face_lane' is the user's choice "I own the rights to this face". Send the SAME request again with it and the same face is rendered on Seedance through the face library, at the normal price. Sending it is the user's confirmation that they have the rights to that face (paid plans, like every real face). Never set it on your own; the other choices that refusal names are a different model (`model`) or another creator (`creator`).
musicMoodNoWHICH mood the music bed is composed in (upbeat / calm / warm / epic / tense / playful / elegant / hype / chill / dramatic). It does NOT decide WHETHER there is one: a clip that comes back with no audio track — every model hermoso_capabilities lists as "silent", plus any audio model that returned mute — gets a bed composed and CHARGED automatically, at the flat per-track fee hermoso_capabilities reports as explainerMusicCredits, and omitting this field only means the mood defaults to "warm". Pass audio:false for a genuinely silent clip with no bed and no bed charge.
refImagesNoSEVERAL reference images (local paths or URLs) — a person, products, a place — that must all appear in the clip. Only models whose `refs.max` in hermoso_capabilities is above 1 use more than one, and each uses at most that many; with `refs.promptAddressed` true, name them in your prompt as Image 1, Image 2… in this order. minimax-h3-max-ref takes up to 9 and keeps each one as a reference rather than a first frame. On a model that takes one image, only the first is used.
ttsScriptNovoiceover script to speak
cameraMoveNoA named camera move around the still in refImage, spelled exactly as the enum gives it: an orbit (a quarter turn, the default), orbiting left, a half turn or a full turntable, a rise, a crane up, a push in, a pull back, or a reveal. Only the camera-controls model renders one (minimax-h3-max-camera, listed with its moves in hermoso_capabilities): pass it with model omitted and that model is picked, or with that model named; any other named model is refused by name with nothing charged. Needs refImage.
resolutionNo'1080p' default (what we ship and bill for); '480p'/'720p' = cheaper draft passes, '4k' = premium final delivery (more credits). NOT EVERY MODEL OFFERS EVERY TIER — this enum is what the tool accepts, and each model's OWN `resolutions` list in hermoso_capabilities is what it can actually render. Ask for a tier the chosen model does not list and it is rendered at that model's best available tier instead, with nothing in the reply saying so — so check `resolutions` before promising anyone 1080p or 4k.
aspectRatioNodefault '9:16'
interactionIdNowith extend:true on an Omni model: the interactionId returned by an earlier render on that model — continues it from its own stored context instead of re-uploading refVideo.
durationSecondsNolength of THIS ONE clip in seconds — pick one of the CHOSEN model’s listed durations from hermoso_capabilities (never a generic guess: the lists differ per model, from 4–8s on the short models up to 30s on the longest-clip one). This is a single continuous generation, so it CANNOT exceed that model’s longest clip: a longer ask is REFUSED with nothing rendered and nothing charged (it is never quietly truncated). Past ~15s only the long-clip models qualify, and an unnamed render is routed by the narrower auto-pool — so NAME the model in `model` when you are asking for a long single take. For a spot longer than any one clip, use plan_ad with durationSeconds then render_ad, which stitches acts of at most one model clip each (on a 15s-clip model, 40s = 15+15+10).
cameraTrajectoryNoYour own ordered camera path, 2 to 12 keyframes, for the camera-controls model only (same rule as cameraMove; overrides it). The first pose is held until its time and the last pose is held to the end. A value outside these bounds is refused by name, nothing charged.

TDQS

A4.8/5.0
Behavior5/5

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

Annotations only carry generic write/open-world hints, so the description carries real burden and delivers: it discloses credit spend, the auto-charged music bed (and how to decline it via audio:false), the raw:true byte-identical dispatch and its two vendor fixes, refusals that are free vs truncation, and 1-3 min render times. This is well beyond what the annotations provide.

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 purpose and sibling routing before edge cases, and every major caveat earns its place for a 24-param tool. However there is repetition (music-bed charging and audio:false are restated in multiple sections), which costs it the top tier.

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?

For a zero-required, 24-parameter tool with no output schema, the description covers the pricing/validation model, plan restrictions, refusal-vs-truncation behavior, and the per-model capability lookups (hermoso_capabilities) an agent needs. Nothing required to call it correctly appears to be missing.

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 adds genuine routing semantics not in the schema — the auto-pool is narrower than the catalog so long/high-res models must be named, and unnamed prompts get guards appended. It adds value over structured fields rather than 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 first sentence gives a precise verb+resource+output ('Render a RAW video clip from your own prompt and return its served mp4 URL'), and it immediately names and distinguishes the sibling render_ad. An agent can route between raw clips and brand ads without opening either 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 when-to-use and when-not-to-use: prefer render_ad for finished ads, this for raw/experimental or manual control; refVideo to edit, extend to continue, plan_ad+render_ad for multi-clip spots. Alternatives and the selecting conditions are spelled out, not implied.

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

generate_voiceGenerate voiceoverAInspect

RAW text-to-speech from the voice-model catalog: speak a script in a chosen voice and return the served MP3 URL. For a standalone voiceover / narration clip — NOT for adding audio to a video (render_ad and generate_video voice their own spots; change_voice re-voices a finished clip). engine picks the voice model (default 'seed-audio'; also 'eleven-v3', 'minimax-speech', 'kokoro'); voice is a preset name from that engine (see hermoso_capabilities -> voice engines) — a name that engine does not have is REFUSED for free with its real list, and a few engines generate their own voice and take no preset at all (the reply says which voice actually spoke). Paid (a couple of credits by length; ≤900 characters).

ParametersJSON Schema
NameRequiredDescriptionDefault
textYesthe script to speak (≤900 characters)
voiceNoa voice preset from the chosen engine (e.g. 'Aria'/'George' on eleven-v3, 'stokie_en' on seed-audio) — omit for the engine default
engineNovoice-engine id: 'seed-audio' (default), 'eleven-v3', 'minimax-speech', or 'kokoro' — listed in hermoso_capabilities

TDQS

A4.8/5.0
Behavior5/5

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

Annotations only state readOnly=false, idempotent=false, openWorld=true, destructive=false. The description goes well beyond: it discloses cost ('a couple of credits by length'), a hard input limit ('≤900 characters'), graceful failure semantics ('a name that engine does not have is REFUSED for free with its real list'), and the surprising case where some engines ignore the preset and report which voice actually spoke.

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?

The purpose and the primary exclusion are front-loaded, and every clause carries information (limits, cost, error behavior, defaults). It is dense to the point of being a wall of nested parentheticals, which costs it the top score, but no sentence is 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 still states the return value ('the served MP3 URL'). Combined with cost, length cap, default engine, voice-selection rules, and the pointer to hermoso_capabilities for valid voices, an agent has everything needed to call this 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 adds real meaning beyond the schema: the engine default ('seed-audio'), the cross-parameter constraint that a voice must belong to the chosen engine, and the case where some engines take no preset at all. It stops short of documenting the output shape per parameter, but the added semantics are substantive.

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+resource ('RAW text-to-speech from the voice-model catalog: speak a script in a chosen voice and return the served MP3 URL') and immediately distinguishes itself from siblings by naming render_ad, generate_video, and change_voice as different. An agent can identify the tool's job without opening the 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?

Explicitly gives the when ('standalone voiceover / narration clip') and the when-not ('NOT for adding audio to a video'), then names the three alternatives and why they differ. This is textbook routing guidance with no inference required.

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

get_brandGet saved profileA
Read-onlyIdempotent
Inspect

What Hermoso ALREADY KNOWS for the active profile: the same saved profile (products, logos, palette, positioning) + learned memory the web Studio uses. Call it when you need to know whether a brand is on file: if hasBrand is true you can omit brand everywhere; if false, onboard with draft_brand. Not a required first step before a render: the create tools read the saved brand by themselves. 0 credits.

ParametersJSON Schema
NameRequiredDescriptionDefault
brandNoprofile id/name from list_brands, this call only

TDQS

A4.3/5.0
Behavior4/5

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

Annotations already cover the safety profile (readOnly, idempotent, non-destructive, closed-world), so the description adds the cost signal (0 credits) and the important behavioral fact that create/render tools auto-read the saved brand, meaning this call is often unnecessary. It stops short of describing the exact response shape, but adds real context beyond 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?

Front-loaded with what the tool returns, then routing guidance, then the exclusion. Dense but every clause earns its place; slightly verbose in the framing of the hasBrand branch, but no filler.

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?

No output schema exists, yet the description still enumerates the returned payload (products, logos, palette, positioning, learned memory) and references the hasBrand field an agent needs to act on. Complete enough for a single-param read tool, with only the precise field structure left implicit.

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

Parameters3/5

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

Schema coverage is 100% and the single 'brand' parameter is already documented as a profile id/name from list_brands scoped to this call only. The description reinforces the 'active profile' concept but adds no format or syntax beyond the schema, so baseline 3 applies.

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+resource (retrieves the saved brand profile for the active profile) and enumerates what it contains: products, logos, palette, positioning, plus learned memory. It is clearly distinguishable from siblings like list_brands, create_brand, draft_brand, and use_brand, which it either names or implicitly contrasts.

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 when-to-use (check whether a brand is on file), the branching consequence (if hasBrand is true omit brand everywhere; if false onboard with draft_brand), and an explicit when-NOT (not a required first step before a render, because the create tools read the saved brand themselves). Alternatives are named directly.

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

get_drive_fileGet a Drive file’s detailsA
Read-onlyIdempotent
Inspect

Fetch one Drive file’s metadata — name, type, size, modified time, a webViewLink to open it and a webContentLink to download it. Pass fileId (from list_drive_files). Read-only.

ParametersJSON Schema
NameRequiredDescriptionDefault
fileIdYesthe Drive file id (from list_drive_files)

TDQS

A4/5.0
Behavior3/5

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

Annotations already declare readOnlyHint, idempotentHint, openWorldHint and destructiveHint=false, so 'Read-only' largely restates structured data. The description does add useful disclosure of what metadata comes back (webViewLink to open, webContentLink to download), which is beyond the annotations, but no auth or rate-limit context.

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

Conciseness5/5

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

Two sentences, front-loaded with the primary action and the enumerated return fields, then the parameter hint and safety note. No filler; every clause carries information.

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?

With no output schema, the description usefully enumerates the key return fields, covering what the agent needs to know before calling. Minor gaps remain around error behavior for invalid or inaccessible file ids, but the core need is met.

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

Parameters3/5

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

Schema description coverage is 100%, with fileId fully documented, so the baseline is 3. The description only repeats the origin of the id ('from list_drive_files') without adding format or constraint detail beyond the schema.

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 (Fetch) and resource (one Drive file's metadata) and enumerates the returned fields, distinguishing it from list_drive_files, update_drive_file, and the OneDrive equivalents. An agent can identify the tool without opening either 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?

Clearly indicates the context (pass fileId, sourced from list_drive_files), which routes the agent from the list tool to this one. It lacks explicit when-not guidance or exclusion relative to e.g. get_onedrive_file, so it falls short of a 5.

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

get_jobGet render jobA
Read-onlyIdempotent
Inspect

Poll a render job by id. Returns status (queued|running|done|error), progress, and on done the served media URL. Renders take 1–3 minutes: keep calling this until done/error without asking the user — several calls is normal, not a stall. An id that does not exist on this account answers status "not_found" — that is FINAL: stop polling it, and do not re-fire the render (that double-charges).

ParametersJSON Schema
NameRequiredDescriptionDefault
idYesthe job id, e.g. job_xxx

TDQS

A4.5/5.0
Behavior5/5

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

Annotations already cover the safety profile (readOnly, idempotent, non-destructive), yet the description adds real operational context: expected latency, that repeated polling is intended rather than a stall, that 'not_found' is terminal, and that re-firing a render double-charges. That cost/consequence disclosure is exactly the value structured fields cannot carry.

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

Conciseness5/5

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

Four sentences, front-loaded with the verb and return shape, then the polling contract and the failure semantics in descending order of importance. No filler; each sentence changes agent behavior.

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?

There is no output schema, but the description names the return fields and their enum-ish values (queued|running|done|error), progress, and the served media URL, so the agent knows what to expect. Combined with the polling and terminal-state rules, nothing needed to call this correctly is missing.

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

Parameters3/5

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

Only one parameter, and schema description coverage is 100% ('the job id, e.g. job_xxx'), so the schema already documents it. The description adds no format or sourcing detail beyond 'by id', which matches the baseline-3 case where the schema does the heavy lifting.

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 ('Poll a render job by id') and immediately scopes what it returns (status, progress, served media URL). This is clearly distinguishable from siblings like list_jobs (enumeration) and render_ad (creation) without opening 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?

Gives strong operating context: renders take 1–3 minutes, keep polling until done/error, don't ask the user, several calls are normal. The 'when not' case is also explicit (stop on not_found, don't re-fire). It does not name a sibling for discovering job ids, which is the only gap.

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

get_linkedin_leadRead one LinkedIn leadA
Read-onlyIdempotent
Inspect

One LinkedIn lead by id (from list_linkedin_leads), with every answer named by field. Personal data — show, never republish. Read-only, free.

ParametersJSON Schema
NameRequiredDescriptionDefault
leadIdYes
pageIdNothe company Page that owns the form — from list_linkedin_pages; omit when one Page is shared
adAccountIdNoread forms owned by an AD ACCOUNT instead of a Page

TDQS

A4.2/5.0
Behavior4/5

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

Annotations already supply read-only/idempotent/non-destructive hints; the description adds value by disclosing that results are named by field and that the lead contains personal data that may be shown but never republished. It repeats read-only/'free' status without contradicting any annotation.

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

Conciseness5/5

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

Three short sentences pack identity, return-shape, and handling policy with no filler. The key scoping phrase ('One LinkedIn lead by id') is front-loaded.

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 one-required-parameter read tool with strong annotations, the description is nearly complete: it explains where the ID comes from, what output to expect, and the sensitive-data policy. It doesn't specify error/not-found behavior, but that is a minor gap for a simple read by ID.

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

Parameters3/5

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

The schema already describes pageId and adAccountId in useful terms; the description mainly clarifies leadId's provenance ('by id (from list_linkedin_leads)'). With 67% schema coverage, this is adequate but not a major contribution beyond the schema.

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 operation — retrieving one LinkedIn lead by ID — and ties the ID to list_linkedin_leads, which distinguishes it from the list/collection siblings. It also communicates the output characteristic ('every answer named by field').

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 clearly locates the tool in a workflow: after listing leads, pass a lead ID to read that single lead in full. It doesn't name sibling alternatives or state when not to use it, but the contextual signal is strong and the privacy instruction gives a use restriction.

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

get_onedrive_fileGet a OneDrive file’s detailsA
Read-onlyIdempotent
Inspect

Fetch one OneDrive item’s metadata — name, type, size, modified time, a webViewLink to open it and a webContentLink to download it. Pass fileId (from list_onedrive_files). Read-only.

ParametersJSON Schema
NameRequiredDescriptionDefault
fileIdYesthe OneDrive item id (from list_onedrive_files)

TDQS

A4/5.0
Behavior4/5

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

Annotations already declare readOnlyHint/idempotentHint/destructiveHint, so safety is covered; the trailing 'Read-only' mostly repeats that. The genuine added value is the disclosure of the returned payload (webViewLink vs webContentLink), which the annotations do not convey. No error, pagination, or permission behavior is described.

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

Conciseness5/5

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

One sentence, front-loaded with the action and resource, using an em-dash list for the return fields. Every clause carries information; nothing is padded.

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?

With no output schema, the description usefully compensates by naming the returned fields, and it covers the single parameter's source. Complete for a simple read tool, though it omits any note on failure modes (e.g., missing/inaccessible fileId) or whether a webUrl-style direct open is guaranteed.

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

Parameters3/5

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

Schema description coverage is 100% and there is a single required parameter, so the schema already documents fileId. The description's 'Pass fileId (from list_onedrive_files)' matches the schema wording rather than adding format or constraint detail. Baseline 3 applies.

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?

Specific verb ('Fetch') plus resource ('one OneDrive item's metadata') with the returned fields enumerated (name, type, size, modified time, webViewLink, webContentLink). The 'OneDrive' qualifier distinguishes it from the sibling get_drive_file, which targets Google Drive.

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

Usage Guidelines3/5

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

It tells the agent where fileId comes from ('from list_onedrive_files'), which implies the natural workflow, but gives no explicit when-to-use/when-not guidance and does not name alternatives such as get_drive_file or list_onedrive_files as the discovery step.

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

get_post_refillAutopilot posting statusA
Read-onlyIdempotent
Inspect

Show AUTOPILOT POSTING (autoposting, the posting refill) for this profile — "is autopilot on?", "what is waiting for review?": whether it is on, its mode (review = each batch of fresh posts waits as drafts for approval; auto = fresh posts are scheduled straight away), WHICH CHANNELS it posts to with each one’s posts per day and posting times, roughly what it costs a day, when it next runs, how many posts are queued, the DRAFTS waiting for approval (id, time, channels, caption, the new image or video), and the notes it learned from reviews. It also names the channels that CANNOT be posted to and why. Read-only, free.

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

TDQS

A4.2/5.0
Behavior4/5

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

Annotations already cover the safety profile (readOnlyHint, idempotentHint, destructiveHint=false, openWorldHint), so the bar is lower, yet the description goes well beyond them by enumerating the payload: on/off state, mode semantics (review vs auto), per-channel cadence, daily cost, next run, queue count, draft fields, learned notes, and blocked channels. 'Read-only, free' partly restates readOnlyHint but adds the cost angle; the only gap is return format/pagination, which is minor here.

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 subject ('Show AUTOPILOT POSTING ... for this profile') and every clause maps to an actual return field, which is justified because there is no output schema. It is a single dense run-on with heavy ALL-CAPS and stacked parentheticals, which hurts scanability, but it is not padded.

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 and no parameters, the description carries the full burden of describing what comes back, and it does so comprehensively — state, mode, channels, cost, schedule, queue, drafts, learned notes, and blocked channels. Nothing an agent needs to decide whether to call it is missing.

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?

Zero parameters, so there is nothing for the description to disambiguate and the baseline is 4. The description correctly spends no words on inputs.

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 (Show) and resource (AUTOPILOT POSTING / the posting refill) and scopes it to 'this profile'. The read-only framing plus the alias list ('autoposting, the posting refill') lets an agent separate it from the set_post_refill and run_post_refill siblings without opening 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 Guidelines3/5

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

The enumerated questions ('is autopilot on?', 'what is waiting for review?') imply the use case clearly, but there is no explicit when-to-use/when-not, and the natural alternatives set_post_refill and run_post_refill are never named as the tools to reach for when the user wants to change or trigger autopilot. Usage is implied rather than routed.

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

get_settingsRead app settingsA
Read-onlyIdempotent
Inspect

Read this account's app settings — the LANGUAGE Hermoso writes ads, copy and answers in, the app appearance (theme), and whether the weekly competitor-watch email is on. Same settings as the web app's Settings pane. Read-only, free.

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

TDQS

A4.2/5.0
Behavior4/5

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

Annotations already declare readOnlyHint, idempotentHint, non-destructive, and closed-world, so the safety profile is covered. The description adds value the annotations do not: it declares the tool is 'free' (no credit cost) — non-obvious against a sibling set full of credit-consuming tools — and names the exact settings fields returned.

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

Conciseness3/5

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

The first sentence and the 'free' suffix pull their weight, but the middle clause listing three concrete settings ('LANGUAGE Hermoso writes ads, copy and answers in, the app appearance (theme), and whether the weekly competitor-watch email is on') is longer than the tool needs to differentiate itself and could be tightened to a single parenthetical. Not wasteful, but not maximally front-loaded.

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 0-param read-only tool with full annotation coverage and no output schema, the description supplies everything an agent needs: purpose, scope, the write-sibling relationship, and the novel 'free' cost caveat. The only missing piece is an explicit pointer to update_settings as the mutation path.

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?

Zero parameters, so the baseline is 4. The description gives no param syntax because none is needed; the schema is an empty object with no description required.

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+resource ('Read this account's app settings') and then enumerates exactly what settings are returned: LANGUAGE, app appearance/theme, and competitor-watch email state. The companion update_settings sibling is implicitly distinguished as the writer counterpart.

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?

Frames the tool as the read side of a read/write pair with update_settings and clarifies scope by equating it with 'the web app's Settings pane'. It does not, however, state when an agent should prefer this over list_connectors or other account-introspection tools, though the resource here is narrow enough that ambiguity is low.

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

get_skillGet skillA
Read-onlyIdempotent
Inspect

Load a bundled skill’s full SKILL.md workflow instructions by name (from list_skills). Follow the loaded instructions to run that workflow with the other tools. Read-only, free.

ParametersJSON Schema
NameRequiredDescriptionDefault
nameYesbundle name from list_skills, e.g. hermoso-generate

TDQS

A4.3/5.0
Behavior4/5

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

Annotations already cover readOnly, idempotent, non-destructive, and closed-world, so the safety profile is done. The description adds value beyond that by disclosing that the call itself costs nothing ('free') and that the returned content is instructions to be executed elsewhere rather than an action performed here.

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

Conciseness5/5

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

Three compact sentences, front-loaded with what is loaded, followed by the prerequisite and the follow-up action. No filler or repetition.

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?

For a one-parameter read tool with no output schema, the description covers source of input, return content, and intended next action. Nothing an agent needs in order to call and use it correctly is missing.

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

Parameters3/5

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

Single parameter with 100% schema description coverage; the schema already states 'bundle name from list_skills' with an example, and the description's 'by name (from list_skills)' merely echoes it. Baseline 3 for a fully documented single-param schema.

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+resource ('Load a bundled skill's full SKILL.md workflow instructions') and names the return artifact precisely, so it is unmistakably distinct from siblings save_skill and delete_skill. An agent knows exactly what it retrieves without opening the 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 clear usage context: fetch instructions by name obtained 'from list_skills', then 'Follow the loaded instructions to run that workflow with the other tools.' The prerequisite and next step are explicit, though there is no stated when-not-to-use case.

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

headline_variantsHeadline variants of a static adAInspect

Turn ONE finished static ad into several copies that differ ONLY in the headline, for an A/B test: same picture, product, layout, colours and every other line. Pass image, and either headlines (your own, up to 10) or count (default 5, max 10) to have distinct angles written for you in the saved brand's voice (never inventing numbers, prices, ratings or claims the ad or brand does not state); brief steers what to test. The ad's text is read first (3 credits), then one image edit per headline; each output is proofread and flagged (textCheck) if the rendered words do not match, never silently re-rendered. When the saved brand has a product photo and the ad shows that product, the photo rides with the edit and the product's label is read and checked against it: re-printed from the photo only when it came out wrong (the check alone is charged when it is right; one small extra charge finds the product first). fixLabel:false turns that off. The whole batch is priced before anything runs. Returns each headline, its angle and its image URL.

ParametersJSON Schema
NameRequiredDescriptionDefault
briefNowhat to test, e.g. "price-led vs outcome-led" or "speak to busy parents"
countNohow many headlines to write when `headlines` is omitted (default 5)
imageYesthe finished static ad: URL, Library item URL, upload_file URL or local path
fixLabelNofalse = leave the product label exactly as rendered: no product lookup, no label check, no re-print (default on when the ad shows the brand’s saved product)
headlinesNoyour own headlines to test (up to 10); omit to have them written

TDQS

A4.6/5.0
Behavior5/5

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

Goes well beyond the annotations (readOnly=false, openWorld=true, idempotent=false, destructive=false): it discloses credit costs (3 credits to read text, one edit per headline), that the batch is priced before running, that outputs are proofread and flagged via textCheck, and that failed renders are never silently retried. Product-photo/label re-print behavior and the fixLabel off-switch are also spelled out.

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-loads the purpose in the first clause before diving into parameters and billing mechanics, and every clause carries information. It is dense and semicolon-heavy, with some parenthetical asides (the label-lookup charge) that a reader must parse slowly, so it is efficient but not maximally tight.

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?

For a multi-step, credit-consuming generation tool with no output schema, the description covers cost, pricing-up-front, failure handling, brand/product constraints, and return values ('each headline, its angle and its image URL'). Nothing an agent needs to call it safely is missing.

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 100% schema coverage the baseline is 3, and the description genuinely adds meaning: it reinforces count's default of 5/max 10, headlines' cap of 10, the semantic difference between supplying headlines vs letting them be written, and that generated copy 'never invents numbers, prices, ratings or claims.' brief's role ('steers what to test') is also clarified.

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 ('Turn ONE finished static ad into several copies that differ ONLY in the headline') and scopes it tightly to an A/B-test use case. The 'ONLY in the headline' constraint distinguishes it from siblings like hook_variants, multiply_ad and remix_static at a glance.

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?

Clearly explains invocation context: pass `image` plus either `headlines` (your own) or `count`, with `brief` steering the test. However it never names a sibling alternative (e.g. hook_variants) or states when NOT to use this tool, so the routing guidance is contextual rather than explicit.

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

hermoso_capabilitiesStart here: what Hermoso can do and what it costsA
Read-onlyIdempotent
Inspect

Probe what this Hermoso account can do RIGHT NOW: available image/video model ids + their exact credit costs, aspect ratios, video durations, the recipe ids, and the canEdit/canAvatar flags. Call it when you need a specific model id, an exact cost, or a capability you are not sure of. It is NOT a prerequisite for rendering: generate_image, generate_video and render_ad all run with model omitted and route to the server’s own default. Read-only, free.

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

TDQS

A4.7/5.0
Behavior4/5

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

Annotations already declare readOnlyHint, idempotentHint, and destructiveHint: false, so the safety profile is covered. The description adds valuable behavioral context beyond annotations: it is free and read-only, it reveals the full set of capability data returned, and it clarifies the non-requirement for rendering. It could note whether the response is cached or real-time, but the annotation coverage makes this a minor gap.

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

Conciseness5/5

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

Extremely efficient: two sentences pack purpose, timing, exclusions, and behavioral traits without any filler. Front-loaded with the core action and result set.

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 zero parameters and no output schema, the description fully covers what the agent needs: what it returns, when to use it, when not to, and that it is free/read-only. No critical gaps remain for correct invocation.

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?

The schema has zero parameters, so the baseline is 4. The description adds meaningful context about what information the probe returns, which compensates for the lack of a formal output schema and helps the agent know what to expect. No additional parameter details are needed since none exist.

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?

State a specific verb (Probe) and resource (what this Hermoso account can do), and enumerates exactly what is returned: model ids, credit costs, aspect ratios, video durations, recipe ids, and canEdit/canAvatar flags. This distinguishes it from siblings like generate_image and hermoso_credits.

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?

Explicitly states when to call it ('when you need a specific model id, an exact cost, or a capability you are not sure of') and when NOT to call it ('It is NOT a prerequisite for rendering: generate_image, generate_video and render_ad all run with model omitted and route to the server’s own default'). This is exactly the kind of routing guidance that prevents wrong tool selection.

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

hermoso_creditsCredit balanceA
Read-onlyIdempotent
Inspect

Return the account credit balance, the credits this account has spent on the calls listed, those recent priced calls, and costModel — the one-sentence rule of what costs credits. THE RULE: only AI model runs and Ad Spy research spend credits; publishing, scheduling, ads management, analytics, comments, DMs and connectors are FREE on every plan (X is the single per-call exception). Check before kicking off paid generation; answer "does posting cost credits?" with NO.

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

TDQS

A4.6/5.0
Behavior4/5

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

Annotations already declare readOnly, idempotent, non-destructive, closed-world, so the safety profile is covered. The description adds genuine domain behavior beyond that: the exact spend rule (only AI model runs and Ad Spy research cost credits; X is the per-call exception) and the shape of the returned data.

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 return contents, then the pricing rule. It is longer than a minimal definition and the ALL-CAPS 'THE RULE' plus the canned Q&A is slightly verbose, but every sentence carries actionable information.

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 compensates by naming the four returned fields (balance, spent credits, recent priced calls, costModel) and explaining the cost rule. An agent has everything needed to call and interpret this tool.

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?

The tool takes zero parameters, so there is nothing for the description to disambiguate; baseline 4 applies. The description correctly does not waste space on nonexistent inputs.

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 ('Return the account credit balance') and enumerates the additional payload: spent credits, recent priced calls, and costModel. This clearly distinguishes it from siblings like billing_status and buy_credits.

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?

Explicitly says when to call it ('Check before kicking off paid generation') and gives a concrete decision rule ('answer "does posting cost credits?" with NO'). The free-vs-paid rule tells the agent exactly which downstream operations warrant a pre-check.

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

hook_variantsNew opening hooks for a videoAInspect

HOOK MULTIPLIER: give ONE finished video ad N NEW OPENING HOOKS, N complete versions to A/B test. A planned hook replaces the first ~1.5-4 s (ends at the source's first cut there, else 3 s; hookSeconds overrides) with a new silent shot on a DIFFERENT named mechanic (list_hooks); the rest of the footage and the WHOLE original soundtrack stay, so every version keeps the source's length, and nobody in the hook talks or shows text. hooks[] adds your own openings: {url} a FOUND viral hook (post link or file) joined in front with the approved bridge (cut before its payoff, its own payoff sound carried across; post_edit price), {prompt} an opening described in words, {mechanic} a named one; with hooks and no count only those are made. To make the found hook's subject your product or creator first: recast_hook. Pass the video's FILE URL (a render, job result, list_library or upload_file). 1-5 versions, default 3. Refused free before billing: a source over 120 s, too short for a 1.5 s hook plus 2 s after it, unreadable, or a social post as the SOURCE (clone_video remakes someone else's ad). COST: a small planning read, then each planned version is billed like fix_beat for the hook's seconds; the reply quotes credits, and dryRun:true returns the plan and quote without rendering (pass that plan back to render exactly those). Returns ONE JOB PER VERSION; call get_job on each until done, never describe a version before its URL arrives. Hooks that show the product use the brand's product photo (productImage overrides; useBrand:false sends none).

ParametersJSON Schema
NameRequiredDescriptionDefault
planNothe `plan` object a previous dryRun returned, to render exactly those hooks without planning again
countNohow many hook versions, 1-5 (default 3)
hooksNoyour own openings, one version each
notesNoanything the hooks must respect, e.g. "keep it calm", "show the product in every hook"
videoYesthe finished video to give new hooks: its served file URL
dryRunNotrue = return the plan and the quote, render nothing
useBrandNofalse = send no brand name or product photo (for a video that is not this workspace brand’s)
resolutionNorender tier for the new opening. Defaults to the source’s OWN tier so the hook matches the rest of the ad; a lower tier costs a lot less and is scaled into the source’s canvas (visibly softer for the first seconds). dryRun quotes whichever you pick.
hookSecondsNowhere the CURRENT hook ends, in seconds (1.5-4). Omit to use the first shot cut.
productImageNoproduct photo URL used as a reference in hooks that show the product (defaults to the workspace brand’s first product photo)

TDQS

A4.7/5.0
Behavior5/5

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

Annotations only state readOnlyHint=false/openWorldHint=true/idempotentHint=false/destructiveHint=false; the description carries far more: exactly what is replaced (first ~1.5-4 s up to the first cut), that footage and the whole soundtrack are preserved, that nobody talks or shows text, the billing model (small planning read + per-version like fix_beat), credit quoting, and the async contract (one job per version, poll get_job, never describe before the URL arrives).

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

Conciseness3/5

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

Front-loaded with the core action, but it is a single dense paragraph of ALL-CAPS interjections and parenthetical caveats that is harder to scan than it needs to be. Every clause is substantive, yet the packaging and some redundant restatement (e.g. repeated note that the whole soundtrack/length is preserved) cost readability.

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?

Complete for a 10-param, no-output-schema mutation tool: it explains the async job flow in lieu of a return schema, the cost path, refusal guards, defaults, and the dryRun round-trip. Nothing an agent needs to call this correctly is missing.

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 already 100%, so the baseline is 3, but the description adds real semantics: hookSeconds override behavior vs the default cut, resolution defaulting to the source's tier with a cost tradeoff, productImage/useBrand defaults, the {url}/{prompt}/{mechanic} entry meanings, and passing the dryRun `plan` back to render exactly those hooks.

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+resource and scope: 'give ONE finished video ad N NEW OPENING HOOKS, N complete versions to A/B test.' It explicitly distinguishes itself from siblings by naming list_hooks (mechanic source), recast_hook (changing the found hook's subject), clone_video (remaking someone else's ad), and fix_beat (billing analog).

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 when-to-use conditions and alternatives: pass a file URL from render/job result/list_library/upload_file; use recast_hook first to swap the found hook's subject; clone_video is the route for a social post source. It also enumerates refusal conditions (over 120 s, too short, unreadable, social post as source) and when to choose dryRun.

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

import_from_cloudImport a Drive / OneDrive folder into the LibraryAInspect

Pull the files in a Google Drive or OneDrive FOLDER into this profile's Library, so they can be used like anything rendered here — published, scheduled, cloned, used as a product photo or a reference. Hermoso downloads each file with the user's own connected account (a Drive/OneDrive file is not public, so this is the only way in) and stores a durable Hermoso url for each. Give folderId from list_drive_files / list_onedrive_files with onlyFolders — omit it for the root. GOOGLE DRIVE ONLY SHOWS WHAT THE USER HANDED OVER: our Drive scope is drive.file, so Hermoso can see the files and folders it created plus the ones the user picked with the Google picker in the app, and NEVER their whole Drive — if a folder comes back empty, that is the answer, and the user picks it in the app once to make it reachable. OneDrive has no such limit. SUBFOLDERS ARE NOT WALKED and Google-native docs (Docs/Sheets/Slides) have no file to download: both are reported back BY NAME rather than silently dropped, along with anything too large or unreadable, so you can tell the user exactly what did and did not come across.

ParametersJSON Schema
NameRequiredDescriptionDefault
limitNohow many files to bring across in this call (default 10, max 25). Anything over the limit is listed as skipped so you know what is left.
folderIdNothe folder to import, from list_drive_files / list_onedrive_files (onlyFolders:true). Omit for the root of the drive.
providerYeswhich cloud — `drive` is Google Drive, `onedrive` is Microsoft OneDrive

TDQS

A4.6/5.0
Behavior5/5

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

With annotations only covering readOnly/openWorld/idempotent/destructive flags, the description carries the real burden and does so richly: the drive.file scope limits visibility, downloads use the user's own connected account, subfolders are not walked, and Google-native docs / oversized / unreadable files are reported by name rather than dropped. This is exactly the non-obvious behavior an agent 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?

Purpose is front-loaded and every paragraph addresses a distinct concern (what it does, account/scope mechanics, folderId sourcing, platform limits, reporting of skipped items). It is dense and the all-caps emphasis is heavy, but little is wasted.

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?

No output schema exists, and the description compensates by explaining what comes across and what is reported back by name, including partial-import and skip behavior. For a non-idempotent import tool this is complete enough to call 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 adds real meaning beyond the schema: the cross-tool provenance of folderId, the root-omission semantics, and the fact that files over `limit` are listed as skipped. That linkage and consequence go past what the schema alone states.

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 specific verb and resource ('Pull the files in a Google Drive or OneDrive FOLDER into this profile's Library') and immediately scopes what the imported files can then be used for. It is clearly distinguishable from siblings like list_drive_files, get_drive_file, and save_to_drive.

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 tells the agent how to source the required folderId (from list_drive_files / list_onedrive_files with onlyFolders) and that omitting it targets the root, plus what an empty result means. It stops short of an explicit 'use this instead of X' routing statement, but the context is clear.

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

invite_memberInvite a teammateA
Destructive
Inspect

Invite someone to this profile by email (role: member = read-only on billing, admin = full). This SENDS a real email invite / share link — an account change. Confirm the exact email + role with the user, then call with confirm:true.

ParametersJSON Schema
NameRequiredDescriptionDefault
roleNodefault member
emailYesthe invitee’s email
confirmNoREQUIRED true — this invites a real person

TDQS

A4.7/5.0
Behavior5/5

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

Annotations only flag destructive/openWorld/idempotency; the description adds the crucial real-world side effect that an actual email invite or share link is SENT, that this is an account change, and that confirm:true is mandatory. That is exactly the kind of context annotations cannot convey.

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

Conciseness5/5

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

Two tight sentences: the action and role mapping come first, the irreversible side effect and confirmation step second. No filler, and the most important warning is front-loaded before the call instructions.

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?

Despite no output schema, nothing an agent needs is missing: it knows the target resource, the role semantics, the required confirmation flag, and the externally visible consequence of calling. For a mutation with clear annotations, this is complete.

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, but the description adds meaning the schema lacks: member = read-only on billing, admin = full, and it reinforces the confirm requirement in prose. Only the email parameter gets no extra explanation, which the name already covers.

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 ('Invite someone to this profile by email') and immediately disambiguates the role enum. An agent can tell this apart from siblings like remove_member or list_team without opening 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?

Gives a clear precondition sequence: confirm the exact email + role with the user, then call with confirm:true. It doesn't name alternative tools or exclusions, but for a single-purpose invite action the when-to-use guidance is essentially complete.

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

leave_connectorRemove my own account from a shared connectionA
DestructiveIdempotent
Inspect

On a connector that several teammates can contribute their OWN account to (see list_connectors — the row reports multiContributor), remove YOURS from this profile: your stored credential is dropped and the accounts you shared stop being shared. Your teammates' accounts on the same connection keep working, and nothing changes at the provider — reconnecting in a browser shares again. Use this instead of disconnect_connector when the connection is not yours to remove: disconnect_connector revokes the grant at the provider and only the person who created the connection may call it.

ParametersJSON Schema
NameRequiredDescriptionDefault
providerYesprovider id exactly as list_connectors reports it, e.g. "linkedin", "tiktok_ads", "meta"

TDQS

A4.6/5.0
Behavior5/5

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

Annotations declare destructive/idempotent/openWorld, and the description adds substantive context beyond them: what gets destroyed (your stored credential), collateral effect (your shared accounts stop being shared), what is preserved (teammates' accounts keep working), and reversibility (nothing changes at the provider; reconnect in a browser to share again). This is exactly the extra detail annotations cannot convey.

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 action and scope, then the side effects, then the alternative. Dense but every clause carries information (destruction, preservation, reversibility, routing). Slightly long, but nothing is padding.

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?

For a single-param mutation with no output schema, the description covers purpose, preconditions, side effects, reversibility, and sibling routing. Nothing an agent needs to invoke it correctly is missing.

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

Parameters3/5

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

Schema description coverage is 100%, so the single 'provider' parameter and its expected format are already documented in the schema. The description references list_connectors indirectly but adds no syntax or format detail beyond what the schema provides, so baseline 3 applies.

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 precise verb+resource ('remove YOURS from this profile') with the exact scope: your stored credential is dropped and your shared accounts stop being shared. It also distinguishes itself from the sibling disconnect_connector by naming the condition that selects each. An agent can tell the two apart without opening either 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 routing guidance: 'Use this instead of disconnect_connector when the connection is not yours to remove,' plus the inverse reason disconnect_connector exists (revokes the provider grant, creator-only). It also points to list_connectors/multiContributor as the precondition for eligibility.

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

list_brandsList profilesA
Read-onlyIdempotent
Inspect

List every profile on this account (id + name; a profile is a brand, a creator or a personal workspace) and which one this connection acts on, PLUS any profile another account shared with you. Call this, then use_brand to switch; a SHARED profile switches the same way (pass its name or the profile id printed here). If a profile looks empty when the app shows it full, you are acting on a different one: call this first. Read-only, free.

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

TDQS

A4.6/5.0
Behavior4/5

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

Annotations already declare readOnly/idempotent/non-destructive, so the safety bar is set. The description adds meaningful behavior beyond that: it says a shared profile switches the same way (by name or printed id), and notes the tool is 'free', which is a cost trait not covered by any annotation. It leaves the exact return shape only lightly sketched, keeping it short of a 5.

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-loads the core purpose, then layers switching workflow and the empty-profile diagnostic. It is dense with no filler, though the second sentence is a run-on that packs shared-profile switching, id passing, and the diagnostic into one breath.

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 compensates by describing what is returned (id + name and the active profile) and how to act on it. Combined with annotations covering the safety profile, everything an agent needs to call this zero-arg list tool and chain into use_brand is present.

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?

There are no input parameters, so there is nothing for the description to explain and the baseline is 4. The description still usefully clarifies the values the tool emits (id + name) that downstream calls like use_brand consume.

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+resource ('List every profile on this account') and defines the domain term ('a profile is a brand, a creator or a personal workspace'). It also scopes in shared profiles, so an agent can distinguish it from sibling listing tools like list_creators or get_brand 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 guidance: 'Call this, then use_brand to switch' names the alternative tool and order of operations. It also gives a concrete when-to-use trigger ('If a profile looks empty when the app shows it full... call this first'), which is exactly the kind of routing an agent needs.

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

list_business_locationsList Google business listingsA
Read-onlyIdempotent
Inspect

List the Google Business Profile listings SHARED WITH THIS PROFILE — id, title, address, website and Maps link. These are the only listings anything here can post to or read: one Google login often manages several businesses (an agency manages its clients’), and the user ticks which of them belong to this profile. Call this before posting whenever more than one is shared and let the USER pick: a Post on the wrong storefront is a public mistake Hermoso will not make for them. If nothing is shared, ask the user to choose — list_connector_accounts("google_business") then set_connector_accounts — and never name or guess a listing. Read-only, 0 credits. Needs Google Business Profile connected (Settings > Connectors > Google Business Profile).

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

TDQS

A4.6/5.0
Behavior4/5

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

Annotations already declare readOnlyHint/idempotentHint/destructiveHint:false, so the safety profile is covered; the description still adds real value with the auth prerequisite (Google Business Profile connected via Settings > Connectors) and the zero-credit cost. It stops short of describing output shape or pagination, but for a 0-arg list call in a rich-annotation tool this is solid.

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?

Purpose is front-loaded, then usage, then the failure path -- a sensible ordering, and the fallback instructions earn their space. The rhetoric about a wrong post being 'a public mistake Hermoso will not make for them' is stylistic padding that could be cut without losing instruction.

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 0 parameters, no output schema and safety already covered by annotations, the definition supplies everything an agent needs: scope, when to call, what to do when empty, the auth prerequisite, and the credit cost. No meaningful gap remains for a tool of this complexity.

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?

The tool takes no parameters, so there is nothing for the description to disambiguate; baseline 4 applies. The field list (id, title, address, website, Maps link) adds mild value about what is returned even though it is not a parameter concern.

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 ('List the Google Business Profile listings SHARED WITH THIS PROFILE') and enumerates the returned fields (id, title, address, website, Maps link). It also scopes the tool against everything else in the family by declaring these are the only listings that can be posted to or read, so an agent can distinguish it from post_to_google_business 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 when-to-use: 'Call this before posting whenever more than one is shared and let the USER pick.' It also covers the empty case with concrete alternatives -- list_connector_accounts("google_business") then set_connector_accounts -- and a hard exclusion ('never name or guess a listing').

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

list_connector_accountsList a connector’s accountsA
Read-onlyIdempotent
Inspect

Show every identity a connected account can act as, and which ones this PROFILE is currently allowed to use — Facebook Pages + Instagram + Meta ad accounts, Google Ads customers, LinkedIn company Pages (and the personal profile), Pinterest ad accounts, Microsoft Advertising accounts. One person often administers several; only the ticked ones can be posted to or spent from. Call this before set_connector_accounts, and let the USER pick — never guess. Providers: tiktok, x, youtube, threads, bluesky, telegram, reddit, pinterest, instagram, meta, google_ads, linkedin, pinterest_ads, linkedin_ads, reddit_ads, apple_ads, microsoft_ads, google_business, google_analytics, snapchat_ads, x_ads, tiktok_ads, google_tag_manager, google_search_console, bing_webmaster. Read-only, free.

ParametersJSON Schema
NameRequiredDescriptionDefault
providerYeswhich connector’s accounts to list

TDQS

A4.2/5.0
Behavior4/5

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

Annotations already declare readOnlyHint, idempotentHint, openWorldHint, and destructiveHint=false. The description adds that only ticked accounts can be posted to or spent from, and that the operation is 'free' (cost context), which goes beyond the safety profile. It does not describe return structure or pagination, but that is a minor gap given 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.

Conciseness3/5

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

The first sentence front-loads purpose effectively, but the description then lists all 25 provider values, duplicating the schema enum and consuming significant space without earning its place. The remaining sentences are concise and useful, but the redundancy reduces the overall structure score.

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?

No output schema exists, so the description must explain the return. It does so conceptually: every identity a connected account can act as and which are allowed, with concrete examples of account types. It could specify the return format (e.g., IDs, names, flags) more explicitly, but for a simple list tool this is adequate.

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

Parameters3/5

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

Schema description coverage is 100% with an enum for the single required provider parameter. The description repeats the full provider list, which is redundant with the schema and adds no semantic meaning beyond what the schema provides. Baseline 3 is appropriate.

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+resource: it shows every identity a connected account can act as, with examples of account types (Facebook Pages, Google Ads customers, etc.). It distinguishes itself from set_connector_accounts by instructing to call it first. An agent can identify its role without opening the 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?

Explicitly says 'Call this before set_connector_accounts, and let the USER pick — never guess,' naming the alternative and the required workflow. It tells the agent when to use it and what to do with the result, leaving little to inference.

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

list_connectorsList connectorsA
Read-onlyIdempotent
Inspect

List the third-party accounts connected to this workspace (Meta, Google Ads, Google Drive/Sheets/Docs, YouTube, LinkedIn, OneDrive, Slack, …) — provider, status and the connected account label — PLUS which providers are available to connect. IT ALSO FLAGS A CONNECTION WHOSE PERMISSIONS ARE OUT OF DATE: a provider writes its granted permission set into the token at consent time, so a connection authorized before a permission was approved does not carry it and never will — those calls are refused by the provider and no retry or wait can change it. Check this FIRST when a connected provider starts refusing things. The fix is a browser: the user reconnects under Workspace > Connectors. A paste-a-key account needs no browser at all: connect_connector connects it from here if the user prefers that to the app. Read-only, free.

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

TDQS

A4.5/5.0
Behavior5/5

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

Annotations already declare readOnlyHint/idempotentHint/destructiveHint=false, and the description adds genuinely non-obvious behavior: stale permission sets baked into tokens at consent time, that refusals are permanent and not retryable, and the browser-based reconnect remedy. This is real diagnostic context an agent could not infer from 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?

Front-loaded with the list purpose before the diagnostic digression, and each sentence carries information. It is a bit long with heavy ALL-CAPS emphasis, and 'Read-only, free' partly restates the readOnlyHint annotation, but nothing is gratuitous.

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 input parameters and no output schema, the description carries the burden of describing the return contents and the failure mode it helps diagnose, and it does both. An agent has everything needed to call it and act on the result.

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?

The tool takes no parameters, so there is nothing for the description to disambiguate; the baseline for zero-param tools applies. The description correctly explains the shape of what is enumerated rather than inventing input semantics.

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

Purpose4/5

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

It states a specific verb and resource and enumerates what is returned: provider, status, account label, plus available-to-connect providers. However, it never distinguishes itself from the sibling list_connector_accounts, so an agent choosing between those two must still open both schemas.

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 when-to-use: 'Check this FIRST when a connected provider starts refusing things.' It also names the fix workflow and points to connect_connector as the no-browser alternative for paste-a-key accounts, so the routing decision is fully specified.

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

list_creatorsList saved creatorsA
Read-onlyIdempotent
Inspect

List this workspace’s SAVED CREATORS — the reusable on-camera cast (AI creators made here, a person pulled from a social profile, a consented photo upload). Read-only, FREE. Each entry gives the name, the PORTRAIT URL, where the portrait came from and whether a real person’s likeness consent is on file, how many extra pose plates exist, and any chosen or cloned voice. TO PUT ONE IN A FINISHED AD, pass their id or name as render_ad’s creator — that casts them for the whole spot (and skips the character-portrait render, so it costs less than not casting anyone). THE PORTRAIT URL IS THE REUSE HANDLE for the raw lanes — pass it as generate_avatar’s image (a talking clip of them), generate_video’s refImage (they star in the scene), recast_motion’s image (they perform a reference clip’s motion), or generate_image’s refImages. CALL THIS BEFORE OFFERING TO GENERATE A NEW PERSON: re-casting somebody the workspace already has keeps the SAME face across every ad, while a fresh person costs credits and breaks that continuity. An empty answer means the workspace genuinely has no cast yet — say so and offer generate_avatar / save_creator, never invent a roster.

ParametersJSON Schema
NameRequiredDescriptionDefault
limitNomax creators to return (default 24)
genderNofilter the PRESET creators by gender (the saved cast is never filtered)

TDQS

A4.6/5.0
Behavior5/5

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

Annotations already declare read-only, idempotent, non-destructive behavior, but the description adds substantial context beyond them: the list is FREE, each entry includes consent status, pose plate count, and voice metadata, and the portrait URL is a reuse handle for multiple generation tools. It also explains that casting a saved creator skips the portrait render and costs less. This is rich behavioral context for a list tool.

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?

The description is front-loaded with purpose, then usage, then downstream integrations, and every major section adds value. It is longer than typical and uses emphatic all-caps formatting, but for a tool with many cross-tool handoffs and no output schema, the length is mostly earned. Slight reduction for density and caps-heavy style.

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 the absence of an output schema, the description explains what each returned entry contains: name, portrait URL, portrait source, likeness consent, extra pose plates, and voice. It also covers empty-result behavior and the main downstream use cases. Combined with annotations that already cover safety, this is complete for an agent to call and act on the result correctly.

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

Parameters3/5

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

Schema description coverage is 100%, so both parameters are already documented in the input schema. The description does not add parameter-level syntax or filtering semantics beyond what the schema provides. Baseline 3 is appropriate when the schema fully carries parameter meaning.

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 names a specific verb and resource: 'List this workspace’s SAVED CREATORS — the reusable on-camera cast.' It clearly distinguishes saved creators from preset/findable people and explains the exact resource type (AI creators, social profiles, consented uploads). An agent can tell this is not the same as save_creator, update_saved_creator, delete_creator, or find_creators.

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?

It gives explicit when-to-use guidance: call this before offering to generate a new person to preserve face continuity and avoid credits. It also names downstream alternatives and exact parameter handoffs: render_ad's `creator`, generate_avatar's `image`, generate_video's `refImage`, recast_motion's `image`, and generate_image's `refImages`. It even specifies what to do on an empty result.

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

list_drive_filesList Google Drive filesA
Read-onlyIdempotent
Inspect

List the Google Drive files & folders Hermoso can reach — the ones it created, plus any the user handed over with the Google file picker in the app (the drive.file scope exposes nothing else, never their entire Drive). This is how you find the id of a file the user picked. Filter by query (name contains …), folderId (contents of a folder), or onlyFolders:true. Paginate with pageToken. Read-only.

ParametersJSON Schema
NameRequiredDescriptionDefault
queryNoonly files whose name contains this
folderIdNolist the contents of this folder id
pageSizeNorows per page (1–200, default 50)
pageTokenNocursor from a previous call
onlyFoldersNolist folders only
includeTrashedNoinclude trashed files (default false)

TDQS

A4.1/5.0
Behavior4/5

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

Annotations already declare readOnlyHint=true, destructiveHint=false, and idempotentHint=true, so the safety profile is covered. The description adds valuable scope context beyond the annotations: the drive.file scope only exposes files it created or the user explicitly handed over, never the entire Drive. This is meaningful behavioral disclosure an agent cannot get from structured fields.

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-loads the purpose, then the scope caveat, then usage and filters in a compact paragraph. Every sentence carries weight, though the parenthetical scope clause is fairly dense and could be its own sentence for readability.

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 read-only list tool with annotations covering safety and full schema coverage, the description supplies the essential missing piece: the reachable-file scope and the id-discovery use case. With no output schema, it would be marginally better to describe the return shape, but this is not a serious gap.

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

Parameters3/5

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

Schema description coverage is 100%, so all six parameters are already documented in the schema. The description echoes query, folderId, onlyFolders, and pageToken but adds no syntax or format detail beyond what the schema provides, and omits pageSize and includeTrashed entirely. Baseline 3 is appropriate.

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 (List) and resource (Google Drive files & folders), and the scope explanation distinguishes it from siblings like list_onedrive_files, get_drive_file, and delete_drive_file. An agent can identify exactly what this returns without opening the 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?

Explicitly frames when this tool matters: 'This is how you find the id of a file the user picked,' which is a concrete usage trigger. It also names the filter modes, though it does not explicitly contrast with get_drive_file as an alternative for fetching a known id.

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

list_errorsList errors users hitA
Read-onlyIdempotent
Inspect

The errors actually recorded against this workspace, GROUPED by fingerprint — the same failure at the same call site is one row with a hit count and first/last seen, sorted defects-first. Each row says whose side it is: ours (a defect worth fixing), user (a refusal we deliberately authored, e.g. not-connected or out-of-credits), or unknown (a vendor 4xx we cannot attribute — never guessed). Free text, tokens, emails and creative are redacted before anything is stored, so an input echo shows shapes and lengths, not content. Filter by surface (http/mcp/agent/job/client) or kind. Read-only, 0 credits. Scoped to your own workspace; an operator whose client carries the admin key sees every account.

ParametersJSON Schema
NameRequiredDescriptionDefault
kindNo'ours' = a defect; 'user' = a refusal we authored; 'unknown' = we could not tell
limitNohow many groups to return (default 50, max 200)
sinceNoISO timestamp — only groups last seen at or after this
surfaceNowhere it happened: http (an API route), mcp (an agent tool), agent (the in-app Studio agent), job (an async render/publish), client (a browser crash)

TDQS

A4.2/5.0
Behavior5/5

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

Annotations already declare readOnly/idempotent/non-destructive, yet the description adds substantially more: redaction of free text, tokens, emails and creative before storage; the ours/user/unknown attribution rule including that unknown is 'never guessed'; zero credit cost; and the admin-key visibility boundary. These are real behavioral disclosures an agent needs to interpret rows correctly.

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?

Information-dense and front-loaded: purpose first, then row semantics, then redaction, then filters, then cost/scope. Every sentence carries content, though the dense parentheticals make it slightly heavier than necessary.

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?

No output schema exists, so the description must describe results — and it does: one row per fingerprint, hit count, first/last seen, defects-first ordering, and per-row attribution. Combined with 100% parameter coverage and safety annotations, an agent has everything needed to call and interpret this tool.

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

Parameters3/5

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

Schema description coverage is 100% and both enums are documented in the schema, so the baseline is 3. The description restates the kind and surface categories but adds little syntax or format meaning beyond what the schema already provides.

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?

Names a specific verb and resource ('list errors actually recorded against this workspace') and adds the distinguishing mechanic — grouping by fingerprint with hit counts and first/last seen, sorted defects-first. An agent can differentiate it from the sibling error_detail, which returns a single failure, without opening either schema.

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

Usage Guidelines3/5

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

It explains how to narrow results ('Filter by surface or kind') and states cost/scope, but never states when to reach for this over error_detail or what situation calls for it. Usage is implied rather than directed at an alternative.

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

list_hooksThe hook + setting libraries, and which hooks are workingA
Read-onlyIdempotent
Inspect

The curated menu of VISUAL scroll-stop HOOKS (how an ad opens) and SETTINGS (where it is staged) that plan_ad and render_ad accept, PLUS this brand's measured traction per hook. Call it before planning an ad to pick a hook deliberately instead of letting the model improvise one, and call it after publishing to see which ones are actually landing. Three things it will not do: it never recommends a hook from thin data — a verdict is SUPPRESSED below 5 measured posts and the reason is stated; it never compares across channels; and it reports hooks you have NEVER TRIED as a fact, not as advice, because 'you haven't tried this' is an observation and 'you should' would be a verdict drawn from zero data. A hook marked unusable in this brief says WHY (an on-screen-text hook cannot ride an authentic/UGC render, which carries zero on-screen text). Read-only, 0 credits.

ParametersJSON Schema
NameRequiredDescriptionDefault
tierNoproduct tier, used with category — changes the FINISH of the room, never the room. Default premium.
channelNorestrict the performance half to one channel (facebook, instagram, threads, x, linkedin, youtube, tiktok, reddit, pinterest)
categoryNothe product category (e.g. 'skincare serum', 'protein powder', 'sunglasses') — returns the setting our Location x Tier matrix puts that category in, with the reason
authenticNotrue if the planned ad is an authentic/UGC/creator-register render — on-screen-text hooks are then reported unusable, with the reason

TDQS

A4.6/5.0
Behavior5/5

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

Annotations already cover the safety profile (readOnlyHint, idempotentHint, destructiveHint false, openWorldHint false), yet the description adds substantial behavioral context: verdict suppression below 5 measured posts with a stated reason, no cross-channel comparison, never-tried hooks reported as fact rather than advice, and the reason unusable hooks are unusable. It closes with 'Read-only, 0 credits.'

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?

Purpose and the before/after call guidance are front-loaded, and each sentence conveys usable behavior. It runs long, with a slightly discursive passage explaining the observation-vs-verdict distinction, but nothing is truly wasted.

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 4 optional parameters, no output schema, and rich annotations already present, the description is complete for correct invocation: it explains what the tool returns conceptually (libraries + measured traction + suppression rules) and how to use it. No output schema means no need to describe return values.

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

Parameters3/5

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

Schema description coverage is 100%, so the schema already documents tier, channel, category, and authentic in detail. The description reinforces the authentic interaction (on-screen-text hooks unusable for UGC renders) but adds little param syntax beyond the schema. Baseline 3 is appropriate when the schema does the heavy lifting.

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: a curated library of hooks (how an ad opens) and settings (where it's staged) plus this brand's measured traction per hook. It names the consuming siblings (plan_ad, render_ad), so an agent can distinguish it from generic library or variant tools 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?

Gives explicit when-to-use on both sides of the workflow: call before planning an ad to pick a hook deliberately, and after publishing to see which are landing. It also states when-not via the 'three things it will not do' (no thin-data verdicts, no cross-channel comparison, no advice framing).

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

list_jobsList render jobsA
Read-onlyIdempotent
Inspect

List the most recent render jobs + how many are currently running, so you can report on or resume in-flight work.

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

TDQS

A3.7/5.0
Behavior3/5

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

Annotations already declare readOnlyHint, idempotentHint, destructiveHint=false, and openWorldHint=false, covering the safety profile. The description adds useful return-content context ('most recent' jobs plus a count of running jobs), but says nothing about how many jobs 'most recent' covers, ordering, or pagination.

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

Conciseness5/5

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

A single front-loaded sentence with no filler; the return content and the motivating use case are packed in without redundancy.

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?

With no output schema, the description carries the burden of describing return values and does so at a high level (recent jobs + running count). It is adequate for a parameterless list tool, though the exact shape and any limit on 'most recent' remain unspecified.

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?

The tool takes zero parameters, so per the rubric the baseline is 4. There is no parameter surface for the description to clarify.

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

Purpose4/5

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

States a specific verb ('List'), a clear resource ('render jobs'), and adds scope ('most recent' plus a running count). An agent can understand what it does immediately, though it never names the sibling get_job to distinguish the collection view from the single-job view.

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

Usage Guidelines3/5

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

The clause 'so you can report on or resume in-flight work' gives an implied use case, but there is no explicit when-not guidance or reference to get_job for drilling into a specific job. Usage is inferable rather than spelled out.

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

list_libraryList libraryA
Read-onlyIdempotent
Inspect

Browse this workspace's Library — every image/video generated in the Studio, newest first (the same Library the web app shows). Returns served URLs you can open directly or hand to fetch_asset for a download link, plus each asset's kind, model, and age. Free, read-only.

ParametersJSON Schema
NameRequiredDescriptionDefault
kindNofilter by asset kind (default 'all')
limitNomax assets to return (default 20, max 60)

TDQS

A4.3/5.0
Behavior4/5

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

Annotations already declare this as read-only, idempotent, and non-destructive. The description adds genuinely useful behavioral context: the result set is newest-first, matches the web app's Library, returns served URLs, and includes each asset's kind, model, and age. It also notes the operation is free, which is useful cost-related transparency.

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

Conciseness5/5

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

Two tightly written sentences, with the most important scope information ('workspace Library', 'newest first') front-loaded. Returns, downstream usage, and cost/read-only status are conveyed with no wasted words.

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?

Despite having no output schema, the description clearly explains what the tool returns: served URLs, asset kind, model, and age. It also covers ordering, scope, and how to connect to fetch_asset. For a simple list operation with well-covered parameters and rich annotations, nothing critical is missing.

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

Parameters3/5

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

Schema description coverage is 100%, so both parameters (kind and limit) are already documented in the input schema. The description adds light semantic context by mentioning images/videos and asset kinds, but it does not meaningfully extend beyond the schema. Baseline 3 is appropriate.

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 uses a specific verb ('Browse') and identifies the exact resource: this workspace's Library, containing every image/video generated in the Studio, newest first. It also clarifies the tool's scope with 'the same Library the web app shows,' making it easy to distinguish from generic list_* siblings.

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?

The description gives clear context for when to use the tool: browsing generated assets in the workspace Library. It also points the agent to a concrete follow-up path by saying served URLs can be handed to fetch_asset for a download link. It does not explicitly enumerate when not to use it versus other listing tools, but the context is strong enough.

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

list_linkedin_lead_eventsLinkedIn lead events received in real timeA
Read-onlyIdempotent
Inspect

The lead events LinkedIn has PUSHED to Hermoso for this brand (new lead / deleted lead, with the form and the lead id), newest first. Empty means none have arrived, not that none exist — list_linkedin_leads reads every lead regardless, and subscribe_linkedin_leads is what starts delivery. Read a lead’s answers with get_linkedin_lead. Read-only, free.

ParametersJSON Schema
NameRequiredDescriptionDefault
limitNo

TDQS

A4.4/5.0
Behavior5/5

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

Annotations already declare readOnlyHint, idempotentHint, and destructiveHint, and the description adds meaningful behavioral nuance: events are pushed, ordered newest first, and empty means no events have arrived rather than no leads exist. The read-only claim is consistent with 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.

Conciseness5/5

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

Three concise, front-loaded sentences cover the core purpose, the empty-result semantics, and the relevant sibling tools. Every sentence adds information without redundancy or filler.

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?

The description covers purpose, ordering, empty semantics, and related tools, and annotations cover safety and idempotency. However, it does not explain the 'limit' parameter or describe the exact event payload structure, which is a minor gap given no output schema exists.

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

Parameters2/5

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

Schema description coverage is 0%, so the description must compensate for parameter meaning, but it never mentions the 'limit' parameter or how it affects results. The single parameter is left entirely to the schema's bare name and type.

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 and resource: it lists lead events that LinkedIn pushed to Hermoso for the brand, including new/deleted leads with form and lead ID, newest first. It clearly distinguishes itself from related tools like list_linkedin_leads, subscribe_linkedin_leads, and get_linkedin_lead.

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?

The description gives explicit context for when this tool is appropriate: it reads pushed events only, and it clarifies that empty results mean no events arrived, not that no leads exist. It also names alternatives for related needs: list_linkedin_leads for all leads, subscribe_linkedin_leads to start delivery, and get_linkedin_lead to read answers.

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

list_linkedin_lead_formsList LinkedIn lead gen formsA
Read-onlyIdempotent
Inspect

The LEAD GEN FORMS a LinkedIn company Page or ad account owns — id, name, state, version and the fields each one asks for (firstName, email, company …). Forms are created in Campaign Manager or on the Page; this API reads them and cannot create one. If it answers that the connection must be reconnected, say exactly that: the lead-sync permission is granted at authorise time. Read-only, free.

ParametersJSON Schema
NameRequiredDescriptionDefault
pageIdNothe company Page that owns the form — from list_linkedin_pages; omit when one Page is shared
adAccountIdNoread forms owned by an AD ACCOUNT instead of a Page

TDQS

A4.4/5.0
Behavior5/5

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

The annotations already declare readOnlyHint, idempotentHint, and destructiveHint false, so the safety profile is known. The description adds meaningful behavioral context beyond that: the lead-sync permission is granted at authorization time, a specific reconnection error requires an exact user-facing response, and the operation is free. No contradiction with annotations exists.

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?

The description is front-loaded with the resource and return fields, and each subsequent sentence adds context: creation source, read-only nature, error handling, and cost. It is slightly dense and repeats the read-only point in more than one place, but it remains under ~65 words and earns its length.

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?

There is no output schema, so the description appropriately compensates by naming the return fields (id, name, state, version, form fields). It also covers ownership scope, how forms come to exist, authentication permission timing, and read-only/free behavior. For a list-style tool with two optional parameters, this is complete enough for an agent to invoke it correctly.

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

Parameters3/5

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

Schema description coverage is 100%, so both pageId and adAccountId are already well explained in the schema. The description's mention of 'Page or ad account' aligns with the parameters but does not add meaning beyond what the input schema already provides. The baseline of 3 is appropriate.

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 clearly identifies the verb and resource: it lists LinkedIn lead gen forms owned by a Page or ad account, and it enumerates what is returned (id, name, state, version, requested fields). It also distinguishes itself from creation by stating this API only reads and cannot create a form, which separates it from related lead-related sibling tools.

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?

The description gives a clear context for use: retrieving lead gen form definitions owned by a Page or ad account, and it explicitly notes that forms are created elsewhere (Campaign Manager or the Page), so this tool is not for creation. It lacks an explicit 'for actual leads, use list_linkedin_leads' style exclusion, but the context is strong enough for an agent to route correctly.

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

list_linkedin_leadsList LinkedIn leads (form responses)A
Read-onlyIdempotent
Inspect

The LEADS a LinkedIn lead gen form collected — every response with its answers keyed by field (firstName, lastName, email, company, …), the campaign and creative that produced it, the consents ticked, and whether it was a test lead. Newest first. Filter by formId, a since/until window (ISO date or epoch ms — LinkedIn takes epoch), or testLeadsOnly. THIS IS PERSONAL DATA: show it to the user, hand it to the CRM they name, never repeat it into a post or an unrelated tool. Pass start for the next page. Read-only, free.

ParametersJSON Schema
NameRequiredDescriptionDefault
limitNoper page, max 100
sinceNoISO date or epoch milliseconds
startNooffset for the next page
untilNo
formIdNoonly this form (from list_linkedin_lead_forms)
pageIdNothe company Page that owns the form — from list_linkedin_pages; omit when one Page is shared
leadTypeNodefaults by owner: SPONSORED for an ad account, COMPANY (organic Page form) for a Page; EVENT for event forms. LinkedIn refuses SPONSORED on a Page owner
adAccountIdNoread forms owned by an AD ACCOUNT instead of a Page
formVersionNodefault 1
testLeadsOnlyNotrue returns ONLY test submissions

TDQS

A4.5/5.0
Behavior5/5

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

Annotations already mark readOnlyHint=true and destructiveHint=false, which the description's 'Read-only' reinforces consistently. The description adds genuinely new behavioral context: a personal-data handling directive ('never repeat it into a post or an unrelated tool'), output shape, newest-first ordering, and the LinkedIn-epoch nuance for date filters.

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?

Single dense paragraph that front-loads the purpose before filtering details. The personal-data warning earns its capitalization, and the read-only/free note is a one-word add. Slightly long, but with 10 parameters and no output schema, every clause carries weight.

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?

With 10 optional parameters and no output schema, the description compensates well: it enumerates return contents (answers, campaign/creative, consents, test flag), ordering, filters, pagination, and data-handling constraints. The schema handles parameter documentation at 90% coverage. Minor gap: no pointer to get_linkedin_lead for single-lead lookups.

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 90%, so the baseline is 3. The description adds value on top: it flags that LinkedIn actually consumes epoch ms for since/until, explains that formId scopes to a specific form, and identifies start as the pagination parameter. Some params (leadType, adAccountId, pageId, formVersion) are left to the schema, which already documents 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?

States a specific verb and resource: 'The LEADS a LinkedIn lead gen form collected.' It enumerates what each response contains (answers keyed by field, campaign/creative, consents, test-lead flag), which distinguishes it from siblings like list_linkedin_lead_forms (forms, not responses) and get_linkedin_lead (single lead). Ordering ('Newest first') adds further precision.

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?

Provides clear context for when to use it: retrieving responses from a LinkedIn lead gen form, with concrete filter scenarios (formId, since/until, testLeadsOnly) and a pagination instruction ('Pass start for the next page'). It does not explicitly name alternatives or state when not to use it, so it falls short of full routing guidance.

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

list_linkedin_lead_subscriptionsList LinkedIn lead webhooksA
Read-onlyIdempotent
Inspect

The lead notification webhooks registered on a LinkedIn Page or ad account, with the id delete_linkedin_lead_subscription takes. Read-only, free.

ParametersJSON Schema
NameRequiredDescriptionDefault
pageIdNothe company Page that owns the form — from list_linkedin_pages; omit when one Page is shared
leadTypeNo
adAccountIdNoread forms owned by an AD ACCOUNT instead of a Page

TDQS

A4.1/5.0
Behavior4/5

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

Annotations already declare readOnlyHint, openWorldHint, idempotentHint, and destructiveHint false, so the safety profile is covered. The description adds value beyond annotations by stating the call is free and that the returned data contains the id that delete_linkedin_lead_subscription consumes. It does not mention pagination or auth details, but these are less critical given 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?

The description is compact: one sentence plus the 'Read-only, free' tag, with no filler. The clause 'with the id delete_linkedin_lead_subscription takes' is grammatically awkward, but the resource is front-loaded and the whole definition is appropriately sized for a simple list operation.

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 list tool with no required parameters and no output schema, the description tells the agent what is listed, the Page/ad-account scope options, that it is read-only and free, and that results contain the id needed for deletion. It omits leadType semantics and pagination, but openWorldHint and the absence of required parameters reduce the risk of incorrect invocation.

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

Parameters3/5

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

The schema documents pageId and adAccountId, and the description reinforces the Page-versus-ad-account axis with 'registered on a LinkedIn Page or ad account.' However, it adds no guidance for the undocumented leadType parameter, leaving 33% of parameters without semantic coverage. The description is helpful but largely redundant with the schema's existing parameter notes.

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 precisely identifies the resource as lead notification webhooks registered on a LinkedIn Page or ad account, clearly distinguishing it from sibling tools like list_linkedin_lead_forms, list_linkedin_leads, and list_linkedin_lead_events. It also notes the returned id feeds delete_linkedin_lead_subscription, reinforcing what the tool returns. The grammar is slightly awkward, but the purpose is unmistakable.

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?

The description makes clear this is for inspecting existing webhook registrations on either a Page or ad account, and the reference to delete_linkedin_lead_subscription signals it as the lookup step before deletion. It does not explicitly contrast with subscribe_linkedin_leads or list_linkedin_lead_events, but the registered-webhooks framing provides clear context without exclusions.

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

list_linkedin_pagesList the LinkedIn company Pages this account administersA
Read-onlyIdempotent
Inspect

List the LinkedIn COMPANY PAGES the connected account administers — id, name and the role held on each. ALWAYS call this before post_to_linkedin_page when there is more than one Page: publishing to the wrong company Page is a public mistake and Hermoso never chooses for the user. If it comes back empty, the account holds no Page admin role, or LinkedIn has not granted this app the organization scopes — say that plainly rather than guessing an id. Read-only, free.

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

TDQS

A4.6/5.0
Behavior4/5

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

Annotations already declare readOnlyHint, idempotentHint, and destructiveHint=false, so the safety profile is covered. The description adds genuine value beyond them: it discloses the empty-result causes (no admin role held, or organization scopes not granted to the app), the no-auto-selection policy, and that the call is free of credit cost.

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 purpose, then the routing rule, then the failure-mode advice. It is slightly dense with three distinct directives in one paragraph, but every sentence carries information an agent needs and none is 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 compensates by naming the returned fields and by explaining what an empty response means. Annotations cover safety and idempotency, so nothing an agent needs to call this correctly or interpret the result is missing.

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?

The tool takes zero parameters, so per the baseline this starts at 4. The description does not need to document inputs and instead describes the shape of the returned records, which is useful but outside this dimension's scope.

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?

Names a specific verb and resource (list LinkedIn company Pages administered by the connected account) and enumerates the returned fields (id, name, role). It is immediately distinguishable from siblings like list_meta_pages or list_pinterest_boards, which cover other platforms.

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?

Gives an explicit when-to-use rule ('ALWAYS call this before post_to_linkedin_page when there is more than one Page') plus the rationale (wrong-Page publishing is public). It also states the fallback behavior when the result is empty, covering the when-not case.

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

list_memoryList memoryA
Read-onlyIdempotent
Inspect

List the durable facts & preferences saved in this workspace’s Memory (what the studio remembers about the brand, audience, taste, and do/don’t rules) — the same Memory the web app shows. These shape every future ad. Read-only, free.

ParametersJSON Schema
NameRequiredDescriptionDefault
limitNomax items (default 50, max 200)
categoryNofilter to one bucket (Brand/Audience/Taste/Do/Don’t/Preference)

TDQS

A3.7/5.0
Behavior4/5

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

Annotations already declare readOnly/idempotent/non-destructive, so the bar is lowered, yet the description still adds value beyond them: 'free' communicates no credit cost, 'the same Memory the web app shows' tells the agent the data source is consistent with the UI, and 'these shape every future ad' conveys downstream impact. It does not describe return format or ordering, keeping it from a 5.

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?

Two tightly packed sentences with the resource front-loaded, followed by scope clarification and the read-only/free qualifier. The parenthetical is dense but earns its place by defining the resource contents; minor redundancy between the title and opening verb keeps it from a 5.

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 simple, annotation-backed read tool with fully documented parameters and no output schema, the description supplies what memory represents and its downstream use, which is enough to call it correctly. It could still note ordering or default behavior, but nothing critical is missing.

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

Parameters3/5

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

Schema coverage is 100%, with both limit (default 50, max 200) and category buckets fully documented in the schema, so the baseline is 3. The description's mention of the brand/audience/taste/do-don't buckets loosely echoes the category filter but adds no format, syntax, or semantics beyond what the schema already provides.

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

Purpose4/5

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

States a specific verb (List) and resource (durable facts & preferences in this workspace's Memory), and enriches it with the domain content (brand, audience, taste, do/don't rules). However, it never differentiates the action from the closely related siblings remember, forget, and tidy_memory that operate on the same resource, so it stops short of the 5 tier.

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

Usage Guidelines3/5

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

The 'these shape every future ad' framing implies you inspect memory before generating ad content, and 'Read-only, free' signals it is safe to call liberally. But there is no explicit when-to-use versus remember/forget guidance and no stated exclusions, leaving usage to inference.

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

list_meta_pagesList Meta pages & ad accountsA
Read-onlyIdempotent
Inspect

List the Facebook Pages (with any linked Instagram business account) and ad accounts on the connected Meta account — use before post_to_meta / create_meta_campaign to pick the target. Requires the user to have connected Meta (Settings > Connectors > Meta); returns a connect hint if not.

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

TDQS

A4.7/5.0
Behavior4/5

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

Annotations already declare readOnly, idempotent, non-destructive and open-world, so the stateless-read profile is covered structurally. The description adds genuinely non-derivable behavior: the connector prerequisite and the fact that the tool returns a connect hint rather than erroring when Meta is not connected. It does not cover pagination or result shape, keeping it short of a 5.

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

Conciseness5/5

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

Two sentences, front-loaded with what is listed, then the when-to-use routing, then the prerequisite. Every clause carries information an agent needs and nothing is repeated from the title or annotations.

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 and no parameters, this is exactly the scope an agent needs: what comes back, when to call it, what it feeds into, and what happens on the failure path when Meta is not connected. No material gap remains for a zero-arg enumeration tool.

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?

The tool takes zero parameters, so the baseline is 4 and there is no schema semantics for the description to supplement. It does usefully characterize the payload (Pages plus linked Instagram accounts, plus ad accounts), which is the closest analogue to parameter selection for a no-arg list tool.

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 precise verb+resource ('List the Facebook Pages ... and ad accounts on the connected Meta account') and disambiguates from near-neighbors in a crowded sibling list (list_meta_posts, list_linkedin_pages, list_pinterest_boards) by naming Meta specifically. It also goes beyond the title by clarifying that linked Instagram business accounts are folded into the Page entries.

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?

Explicitly says when to use it ('use before post_to_meta / create_meta_campaign to pick the target'), which names both the downstream alternatives and the decision it feeds. It also states the precondition the agent cannot infer: the user must have connected Meta via Settings > Connectors > Meta.

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

list_meta_postsList the Page’s / Instagram account’s own postsA
Read-onlyIdempotent
Inspect

List the connected Facebook Page's or Instagram account's OWN existing posts — id, caption, permalink, publish date and format. THIS IS THE TOOL THAT GETS YOU THE postId every other Meta read needs: meta_post_insights, list_meta_comments and manage_meta_post all require one, and until now the only way to have a postId was to have just published it yourself with post_to_meta. Use it for "how did our last few posts do", to find a post the user describes loosely, or before backfill_posts. Pass target:'instagram' for the linked IG account (Stories are excluded — Meta's media edge does not return them); Facebook hides unpublished drafts unless you ask for them. Only ever reads a Page the brand has connected. Read-only, 0 credits.

ParametersJSON Schema
NameRequiredDescriptionDefault
brandNoWHICH PROFILE the post lives in — id or exact name from list_brands (a shared workspace: its profile id). Needed when it was published in a profile this connection is not pinned to; applies to THIS CALL ONLY. A name that matches no profile, or two, is REFUSED and nothing is done.
limitNohow many posts (default 25, max 100)
cursorNopaging cursor returned by a previous call
pageIdNowhich connected Page — omit when the profile has only one
targetNodefault facebook; 'instagram' reads the Page's linked IG business account
accountNowhich Instagram account — an @handle or id from list_connector_accounts("instagram"). Needed when several are linked, and the way an Instagram Login (standalone) account is reached; omit for the one Page-linked account.
includeUnpublishedNoFacebook only — also return unpublished drafts (hidden by default)

TDQS

A4.6/5.0
Behavior5/5

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

Annotations already cover the safety profile (readOnly, idempotent, non-destructive), yet the description adds real behavioral context: Stories are excluded because Meta's media edge omits them, Facebook hides unpublished drafts unless requested, only connected Pages are readable, and it costs 0 credits. These are exactly the edge cases an agent needs before calling.

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 purpose and the postId value proposition before edge cases. It is dense but mostly earns its length; the trailing 'Read-only, 0 credits' slightly duplicates annotation-provided information.

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?

For a 7-parameter, zero-required list tool with no output schema, the description supplies the return fields, the postId dependency chain, target-specific caveats, and pricing. An agent has enough to call it correctly without opening the schema.

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

Parameters3/5

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

Schema description coverage is 100%, so the baseline is 3. The description restates the target:'instagram' behavior and the unpublished-drafts default, but this largely duplicates the schema's own field descriptions rather than adding new syntax or constraints.

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 specific verb+resource+scope: listing the connected Page's or IG account's OWN existing posts, and enumerates the returned fields (id, caption, permalink, publish date, format). It clearly distinguishes itself from sibling reads like search_posts and list_published_posts by emphasizing 'OWN' posts and the postId-gathering role.

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?

Explicitly states when to use it ('how did our last few posts do', finding a loosely described post, before backfill_posts) and names the downstream tools that depend on its output (meta_post_insights, list_meta_comments, manage_meta_post). It also contrasts with the prior alternative (post_to_meta) for obtaining a postId.

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

list_onedrive_filesList OneDrive filesA
Read-onlyIdempotent
Inspect

List files & folders in the user’s OneDrive — the root by default, a folder’s contents (folderId), or a name search (query). onlyFolders:true lists folders only. Paginate with pageToken (the cursor from a previous call). Read-only.

ParametersJSON Schema
NameRequiredDescriptionDefault
queryNosearch — only items whose name matches this
folderIdNolist the contents of this folder id
pageSizeNorows per page (1–200, default 50)
pageTokenNocursor from a previous call
onlyFoldersNolist folders only

TDQS

A4.2/5.0
Behavior3/5

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

Annotations already declare readOnlyHint, idempotentHint, openWorldHint and destructiveHint=false, so the safety profile is fully covered and the description's 'Read-only' restates it. The one real addition is the pagination workflow ('pageToken, the cursor from a previous call'), which is useful but modest given the bar is lower with annotations present.

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

Conciseness5/5

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

Four short clauses, each carrying distinct information, with the resource and default mode front-loaded and pagination last. The dash-separated structure is terse but every phrase earns its place; no filler or repetition.

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 read-only list tool with no output schema, the description covers modes, folders-only filtering, and pagination, which is enough to call it correctly. It omits any hint of the returned item shape or ordering, which an output-schema-free tool could reasonably clarify, so it is strong but not complete.

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 schema already documents all five parameters and a baseline of 3 applies. The description adds genuine meaning beyond the schema by stating the default behavior ('the root by default') and how folderId versus query select different modes, which the schema lists only in isolation.

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 names a specific verb and resource ('List files & folders in the user's OneDrive') and explicitly enumerates its three modes (root default, folderId contents, query name search). This distinguishes it from siblings like list_drive_files (Google Drive) and get_onedrive_file without needing the schemas.

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 clearly states which invocation to use in which case: root by default, folderId for a folder's contents, query for name search, and onlyFolders for folders-only. It does not name explicit alternatives (e.g., when to prefer get_onedrive_file) or exclusions, so it stops short of a 5.

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

list_pinterest_boardsList Pinterest boardsA
Read-onlyIdempotent
Inspect

List the boards on the user’s connected Pinterest account — id, name, privacy and pin count. ALWAYS call this before post_to_pinterest and let the USER pick: Pinterest requires a board and Hermoso never chooses one for them. Read-only, 0 credits. Needs Pinterest connected (Settings > Connectors > Pinterest).

ParametersJSON Schema
NameRequiredDescriptionDefault
privacyNofilter by board privacy; default is everything the connection can see

TDQS

A4.5/5.0
Behavior4/5

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

Annotations already declare readOnly, idempotent, and non-destructive, so the safety profile is covered. The description still adds non-obvious context the annotations cannot convey: zero credit cost and the mandatory connection prerequisite. It stops short of describing pagination or board-count limits, which keeps it from a 5.

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

Conciseness5/5

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

Three dense sentences, each carrying independent load: what is returned, when/why to call it with the user-pick rule, and the precondition plus cost. Front-loaded with the outcome, no 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?

No output schema exists, so the description correctly enumerates the returned fields. For a zero-required-parameter list tool embedded in a larger posting workflow, the workflow ordering, cost, and auth prerequisite are all the agent needs.

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

Parameters3/5

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

Schema description coverage is 100%, so the single privacy enum is fully documented in the schema itself (including its default). The description never mentions the privacy filter, so it adds nothing over the schema — the baseline of 3 is appropriate.

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 (List) and resource (Pinterest boards) plus the exact fields returned (id, name, privacy, pin count), so the agent knows what it gets back. It also implicitly distinguishes itself from the write-side sibling post_to_pinterest by being the read step that precedes it.

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?

Gives an explicit ordering rule ('ALWAYS call this before post_to_pinterest'), an explicit reason, and a hard behavioral constraint ('let the USER pick ... Hermoso never chooses one for them'). The prerequisite is also spelled out (Pinterest connected via Settings > Connectors > Pinterest), leaving nothing to inference.

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

list_playbooksList playbooksA
Read-onlyIdempotent
Inspect

List the PLAYBOOKS saved in this workspace — the reusable strategy cards (winning hooks, angles, formats and the concrete plays to run) kept from teardowns, angle mining and creatives worth repeating. The same Playbooks the web app's Playbooks tab lists. Read one before planning an ad so you re-run what already worked instead of starting cold. Read-only, free.

ParametersJSON Schema
NameRequiredDescriptionDefault
fullNotrue to return every hook/angle/play in the text, not just the headline counts
limitNomax playbooks to return (default 25, max 100)

TDQS

A3.9/5.0
Behavior3/5

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

Annotations already declare readOnlyHint, idempotentHint, and destructiveHint=false, so 'Read-only' largely restates structured data. It does add two useful non-annotation facts — the operation is 'free' (no credit cost) and the content is derived from prior teardowns/mining — but says nothing about return shape or pagination, leaving the bar at a solid 3.

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?

Purpose is front-loaded and each sentence has a job: what playbooks are, where they come from, when to read them, and the cost/safety note. The em-dash definitional clause is dense and slightly adjective-heavy, but nothing is wasteful.

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?

No output schema exists, so the description must convey what comes back — it does so conceptually (headline counts vs. full hooks/angles/plays via the schema's full flag). With annotations covering the safety profile and a fully documented 2-param schema, this is complete enough for an agent to call it correctly.

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

Parameters3/5

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

Schema description coverage is 100%, so both parameters (full, limit) are already documented in the schema; the description adds no syntax, defaults, or guidance beyond it. Baseline 3 applies since the schema carries the parameter burden.

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?

Specific verb+resource ('List the PLAYBOOKS saved in this workspace') with an on-topic definition of what a playbook is (reusable strategy cards: hooks, angles, formats, plays) and their provenance (teardowns, angle mining, creatives worth repeating). It also anchors to a familiar referent ('the same Playbooks the web app's Playbooks tab lists') and is clearly distinct from save_playbook/delete_playbook among siblings.

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 concrete when-to-use: read one before planning an ad so you re-run what worked rather than starting cold. No explicit when-not or named alternative (e.g., list_hooks, list_swipefile, list_skills), so the routing is implied rather than spelled out.

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

list_product_photosList product photosA
Idempotent
Inspect

List the product photos ALREADY saved in your workspace — the brand's product library plus any app-store screens (also surfaces photos locked in your OTHER creations, since a set product lands in the shared library). FREE — returns each photo's url + label. Call it before set_product_image to see the existing photos you can reuse. Reads YOUR saved brand (pass brandId to target a specific profile — that switches this key's active profile like use_brand).

ParametersJSON Schema
NameRequiredDescriptionDefault
brandIdNoa profile id/name from list_brands whose product library to list; omit to use the active profile

TDQS

A4.8/5.0
Behavior5/5

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

Adds substantial traits beyond annotations: the operation is 'FREE' (cost), the return payload is 'each photo's url + label', and critically that passing brandId 'switches this key's active profile like use_brand' — a state-mutating side effect that explains why readOnlyHint is false. This is exactly the kind of non-obvious behavior the description should disclose.

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 action and scope, then rationale and workflow in descending priority. The heavy parentheticals make it dense, but each clause conveys a distinct, useful fact 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?

For a single optional-parameter tool with no output schema, the description covers what it returns (url + label), cost, the upgrade/scope behavior, and how it fits with set_product_image. Nothing an agent needs to invoke it correctly is missing.

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 schema already documents the parameter, making 3 the baseline. The description goes further by documenting the side effect of brandId (profile switching) and the fallback when omitted, adding meaning the schema does not carry.

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?

Names a specific verb and resource (list product photos) and precisely bounds the scope: brand product library, app-store screens, and photos from other creations landing in the shared library. This distinguishes it from siblings like list_library and fetch_app_screens 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 Guidelines5/5

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

Gives explicit workflow guidance: 'Call it before set_product_image to see the existing photos you can reuse,' naming the alternative tool and the ordering condition. It also states the argument choice (omit brandId for active profile) with the consequence of passing one.

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

list_published_postsList what this brand has publishedA
Read-onlyIdempotent
Inspect

List every post Hermoso has recorded publishing for this brand, newest first, across all channels — channel, permalink, caption, format, the HOOK and SUBJECT it was written to, and its measured engagement. The WHOLE history, no cap: pass the reply's nextCursor as cursor for older posts. Each row says how it was recorded: 'captured' (written at publish time — the hook is what the author intended) or 'backfilled' (reconstructed from the platform afterwards). A dash for engagement means the platform reported no number — NOT zero. Read-only, 0 credits.

ParametersJSON Schema
NameRequiredDescriptionDefault
brandNoWHICH PROFILE the post lives in — id or exact name from list_brands (a shared workspace: its profile id). Needed when it was published in a profile this connection is not pinned to; applies to THIS CALL ONLY. A name that matches no profile, or two, is REFUSED and nothing is done.
limitNomax posts (default 50, max 200), newest first
cursorNonextCursor from a previous reply: the next, older page
channelNofilter to one channel: facebook, instagram, threads, x, linkedin, youtube, tiktok, reddit, pinterest, google_business

TDQS

A4.6/5.0
Behavior5/5

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

Annotations already cover the safety profile (readOnly, idempotent, non-destructive), yet the description adds substantial behavioral context beyond them: no result cap, pagination mechanics, the 'captured' vs 'backfilled' provenance distinction, and the critical warning that a dash for engagement means no number reported, NOT zero. That last point alone materially changes how an agent interprets results.

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?

Dense but purposeful prose with the core action front-loaded and secondary caveats (provenance, dash semantics, cost) following. Every sentence carries information, though the single long block could be segmented for faster scanning.

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?

No output schema exists, so the description carries the full burden of describing return values — and it does, enumerating channel, permalink, caption, format, hook, subject, and engagement. Combined with pagination and provenance handling, an agent has everything needed to call and interpret this tool 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 description coverage is 100%, so the baseline is 3. The description nonetheless adds the cursor workflow explicitly ('pass the reply's nextCursor as `cursor` for older posts'), linking the parameter to the prior response — meaning beyond what the schema's terse 'nextCursor from a previous reply' conveys.

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 ('List every post Hermoso has recorded publishing for this brand'), plus scope details: newest first, across all channels, whole history. An agent can distinguish this from siblings like list_meta_posts, search_posts, or post_performance without opening 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?

Gives clear usage context — retrieving the complete publishing history, paginating older results via the reply's nextCursor. It does not explicitly name alternative tools or state when NOT to use it (e.g., vs. search_posts for filtered lookups), which keeps it short of a 5.

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

list_scheduledList scheduled and past postsA
Read-onlyIdempotent
Inspect

Show what is queued to post and what already went out. Each fired item reports PER-CHANNEL outcomes, so you can see that (say) Instagram published and TikTok failed on the same item rather than a single misleading verdict. Past items are rebuilt from published-post records, one row per channel; they are not job runs (use list_jobs for those). THE LIST IS COMPACT so it fits in one reply: the next 25 queued and the last 15 fired, captions shortened. Pass id for ONE post in full (every caption and setting, which you need before reschedule_post replaces a caption map), channel to filter, or upcoming / fired for more rows. Read-only, 0 credits.

ParametersJSON Schema
NameRequiredDescriptionDefault
idNoone post id from this list: returns that post in full, every caption and setting included
brandNoWHICH PROFILE to list — the id or exact name from list_brands. Needed when the post lives in a profile this connection is not pinned to: a post you can CREATE in a profile must be manageable there too, without switching the whole connection. A name that matches no profile, or two, is REFUSED.
firedNohow many already-fired posts to list, most recent last (default 15, max 200)
channelNoonly posts that include this channel, e.g. "pinterest" or "x"
upcomingNohow many queued posts to list, soonest first (default 25, max 200)

TDQS

A4.8/5.0
Behavior5/5

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

Annotations already declare a safe, idempotent, open-world read, but the description adds substantive behavior the annotations cannot: per-channel outcomes (Instagram published / TikTok failed on one item), past rows being rebuilt from published-post records at one row per channel, the compact default window (next 25 queued, last 15 fired) with shortened captions, and the cost profile ('Read-only, 0 credits').

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?

The scoping constraint and the list_jobs disambiguation are front-loaded, and the compactness/cost facts arrive in the last third where they belong. It is dense and parenthetical-heavy ('(say)', '(use list_jobs for those)'), which slightly taxes readability, but every sentence carries distinct information.

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 carries the return-shape burden and does so: per-channel outcome rows, one row per channel for past items, and an explicit statement that the default list is truncated and captions are shortened. An agent knows both what it will get and when it must request the full record.

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 adds real meaning beyond the schema: why `id` matters (full captions and settings are needed before reschedule_post replaces a caption map) and why `brand` exists (managing a post in a profile the connection is not pinned to). It does not restate the numeric defaults/maxima that the schema already documents.

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 specific verb+resource and scope: 'Show what is queued to post and what already went out.' It explicitly differentiates itself from a closely-named sibling by stating past items 'are not job runs (use list_jobs for those),' so an agent can route correctly without opening 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 Guidelines5/5

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

Gives explicit routing rules: use list_jobs for job runs, pass `id` for a single post in full, `channel` to filter, and `upcoming`/`fired` to widen the window. It also names the downstream dependency ('which you need before reschedule_post replaces a caption map') and the upstream source for brand values (list_brands).

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

list_sheet_tabsList the tabs in a Google SheetA
Read-onlyIdempotent
Inspect

The tabs in a Google Spreadsheet, each with its name, numeric sheetId, row/column count and position. Call this BEFORE naming a tab in update_sheet / clear_sheet_range / manage_sheet_tabs / format_sheet, and before proposing to delete one — it is how you learn what the file actually contains instead of guessing at a name. Read-only, free.

ParametersJSON Schema
NameRequiredDescriptionDefault
sheetUrlNoa Google Sheets URL — the id is extracted from it
spreadsheetIdNothe spreadsheet id (from create_sheet, or list_drive_files for one the user picked)

TDQS

A4.3/5.0
Behavior4/5

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

Annotations already cover readOnlyHint, idempotentHint, destructiveHint and openWorldHint, so safety is declared structurally. The description adds cost information ('free') and enumerates the response fields, which the annotations do not. It does not restate pagination or failure behavior, keeping it short of a 5.

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?

Two sentences, front-loaded with the return payload before the routing advice. Efficient, though the four-tool callout plus the deletion clause makes the second sentence dense.

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?

With no output schema, the description compensates by naming the returned fields, and the usage routing is thorough. The remaining gap is that it never says which identifier parameter to prefer, leaving an agent to infer sheetUrl vs spreadsheetId.

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

Parameters3/5

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

Schema description coverage is 100%, so both parameters (sheetUrl, spreadsheetId) are already documented in the schema. The description adds nothing about them, notably no guidance on which of the two alternatives to supply when neither is required — baseline 3.

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+resource ('The tabs in a Google Spreadsheet') and enumerates the returned fields (name, sheetId, row/column count, position). An agent can distinguish this from read_sheet (content) and manage_sheet_tabs (mutation) without opening either 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?

Explicitly names the prerequisite condition and four sibling tools it must precede (update_sheet, clear_sheet_range, manage_sheet_tabs, format_sheet), plus the deletion-proposal case. It also gives the reason — learn what the file contains instead of guessing a tab name.

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

list_skillsList skillsA
Read-onlyIdempotent
Inspect

List the bundled Hermoso SKILLS — multi-step workflow instructions (SKILL.md) that orchestrate the other tools (research an ad space, plan+render a finished ad, product photoshoot, raw generation) — plus the in-app strategy skills and creative recipes. Call get_skill to load a bundle. Read-only, free.

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

TDQS

A4.1/5.0
Behavior3/5

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

Annotations already cover the safety profile (readOnlyHint=true, idempotentHint=true, destructiveHint=false), and the description's 'Read-only, free' restates that rather than adding new context. It does add one genuinely useful behavioral fact — that skills are SKILL.md bundles loaded by get_skill — but doesn't mention pagination, count, or ordering. With annotations carrying the safety burden, a 3 is appropriate: marginal value beyond structured fields.

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?

Two sentences, front-loaded with the verb and resource, with the parenthetical that defines a skill and the follow-up call both earning their place. Slightly dense with the em-dash aside, but no filler.

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 zero-param, read-only list tool with no output schema, the agent knows what it will get (skill bundles, strategy skills, creative recipes) and what to do next (get_skill). The only gap is the shape/count of returned items and how it relates to list_playbooks/list_library, which a sibling-confused agent might need.

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?

The tool takes zero parameters, so the baseline is 4. The description correctly implies no filtering or pagination arguments are needed. No parameter detail is required or expected, and none is incorrectly omitted.

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+resource ('List the bundled Hermoso SKILLS') and goes further by defining what a skill actually is (multi-step workflow instructions / SKILL.md that orchestrate other tools). This distinguishes it clearly from siblings like list_memory, list_brands, or list_playbooks, which are never defined or contrasted.

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?

The description routes the agent forward: 'Call get_skill to load a bundle', naming the natural follow-up sibling. It also enumerates what categories appear in the list (orchestration skills, in-app strategy skills, creative recipes), which implicitly tells the agent what to expect. However, it doesn't say when NOT to use this (e.g., vs list_playbooks or list_library) and gives no selection criteria among the returned skills.

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

list_swipefileList the swipefileA
Read-onlyIdempotent
Inspect

List this workspace's SWIPEFILE — the saved-ad research board: every named collection and the ads/creatives kept in it (advertiser, headline, body copy, media URL, platform, when it was saved, and any taste tags). The SAME board the web app's Swipefile tab shows. Use it to answer "what have we saved?", to mine the user's own taste before planning an ad, or to find a reference to remix. Read-only, free.

ParametersJSON Schema
NameRequiredDescriptionDefault
limitNomax ads to return (default 50, max 500)
collectionNoonly list ads in this collection (by name or id) — omit for every collection

TDQS

A4.2/5.0
Behavior4/5

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

Annotations already declare readOnlyHint, idempotentHint, openWorldHint=false and destructiveHint=false, so the safety profile is covered. The description adds value beyond that by enumerating the returned fields (since no output schema exists) and noting it is the same board the web app's Swipefile tab shows, though 'Read-only, free' largely repeats 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?

Front-loaded with the core purpose and resource, followed by the returned-field list and use cases. It is somewhat dense and the 'Read-only, free' tail is redundant with annotations, but overall each sentence contributes.

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 compensates by describing the return payload (collections and ad fields). Both optional params are fully documented in the schema, and usage contexts are covered, so 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.

Parameters3/5

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

Schema description coverage is 100%, so both limit and collection are fully documented in the schema and the baseline is 3. The description adds no extra syntax or default semantics for these params beyond what the schema already states.

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 (list) and resource (the workspace's swipefile/saved-ad board), and describes the returned contents in detail (collections, ads/creatives, advertiser, headline, etc.). It clearly distinguishes this read tool from siblings like save_to_swipefile and export_swipefile_deck.

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 concrete when-to-use guidance: answer 'what have we saved?', mine the user's taste before planning an ad, or find a reference to remix. It doesn't explicitly name an alternative sibling (e.g. export_swipefile_deck for exporting), but the usage context is unambiguous.

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

list_teamList team membersA
Read-onlyIdempotent
Inspect

List the members of the current profile — email, role (admin/member) and status. Read-only, free.

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

TDQS

A3.7/5.0
Behavior3/5

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

The annotations already declare readOnlyHint, destructiveHint=false, idempotentHint, and openWorldHint=false, so the safety profile is fully covered and 'Read-only' merely repeats it. The description does add one genuinely new fact not in annotations — that the call is free — plus the returned field set, but says nothing about pagination, size limits, or ordering.

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

Conciseness5/5

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

A single tight sentence with the resource front-loaded and the returned fields and cost caveat appended; nothing is padded or repeated.

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?

With no output schema, the description usefully enumerates the returned fields (email, role, status), which is exactly the gap it should fill for a simple zero-param read tool. Only ordering/pagination behavior is left unspecified, a minor omission at this complexity.

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?

The tool takes zero parameters, so the baseline is 4; the description correctly offers no parameter detail and instead describes output fields, which is the relevant information for a no-arg call.

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

Purpose4/5

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

States a specific verb and resource ('List the members of the current profile') and names the fields returned (email, role, status), so the agent knows exactly what comes back. However, it does not differentiate from the sibling team-mutation tools invite_member, remove_member, and set_role, which all act on the same resource.

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

Usage Guidelines3/5

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

'Read-only, free' implies this is the safe inspection call and hints at a when-to-use condition (no cost, no mutation), but it never names the alternatives or states an explicit trigger such as 'use this before set_role or remove_member to find member identities'.

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

list_telegram_chatsFind Telegram chat idsA
Read-onlyIdempotent
Inspect

Find the chat ids this Telegram bot can be addressed by. STATE THE LIMIT WHENEVER YOU USE IT: this is NOT the list of chats the bot belongs to — the Bot API publishes no such method — it is every chat that SENT the bot an update in the last 24 hours, which is as long as Telegram keeps an update. A channel the bot posts to every day but nobody messages will NOT appear here, and its absence means nothing at all: post to it by @username or numeric id anyway. If the bot has an outgoing WEBHOOK configured the list is empty for that reason alone (Telegram: getUpdates "will not work if an outgoing webhook is set up"), and the reply says so rather than reading as an empty account. Nothing is consumed — no update offset is confirmed, so this cannot eat the bot’s pending updates. Free, 0 credits.

ParametersJSON Schema
NameRequiredDescriptionDefault
limitNohow many recent updates to scan, 1–100 (default 100)

TDQS

A4.6/5.0
Behavior5/5

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

Annotations already declare readOnlyHint=true, idempotentHint=true, and destructiveHint=false, and the description deepens all of these: 'Nothing is consumed — no update offset is confirmed, so this cannot eat the bot's pending updates' explains the idempotency mechanism, and 'Free, 0 credits' adds cost behavior. The 24-hour retention window and webhook caveat are non-obvious behavioral traits disclosed beyond 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?

The description is longer than average, but nearly every clause earns its place by preventing a real misinterpretation: membership vs. senders, the 24-hour window, the webhook empty-list trap, and the no-consumption guarantee. It is front-loaded with purpose and scoping before caveats. Minor verbosity such as the inline Telegram quote and 'whose absence means nothing at all' could be trimmed without loss.

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 must convey return semantics, and it does: the result is chat ids addressable by the bot. It covers the data source (updates in last 24h), the edge case (webhook configured → empty with explanation), side effects (none), and cost (free). For a one-parameter list tool with tricky domain semantics, nothing needed for correct invocation is missing.

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

Parameters3/5

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

Schema coverage is 100% — the single 'limit' parameter is fully described as 'how many recent updates to scan, 1–100 (default 100)'. The description references the limit ('STATE THE LIMIT') and ties it to the 24-hour recency window, adding mild context, but the schema already carries the semantic load. Baseline 3 is appropriate.

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 opening sentence states a specific verb and resource: 'Find the chat ids this Telegram bot can be addressed by.' This clearly distinguishes the tool from siblings like post_to_telegram and the other list_* tools, and the title reinforces the same purpose without contradiction.

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?

The description gives explicit operational guidance: 'STATE THE LIMIT WHENEVER YOU USE IT,' explains what the tool is NOT ('NOT the list of chats the bot belongs to'), and tells the agent what to do in the channel case ('post to it by @username or numeric id anyway'). It also explains how to interpret the webhook empty-list case, which is direct when-to-trust guidance.

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

list_watch_findingsRead the competitor watchA
Read-onlyIdempotent
Inspect

Read what the standing COMPETITOR WATCH has found — the new ads each watched brand has launched since the last check, plus the watch's own state (who is watched, when it last ran, when it runs next, and whether the last run actually succeeded). The same board the web app's Ad Spy > Watching tab renders. Use it to answer "what are our competitors running that's new?", to feed a teardown, or to save something worth keeping with save_to_swipefile. Findings marked seed:true are NOT new launches — the first check of a brand has nothing to diff against, so it seeds the board with what that brand is running right now; only later runs surface genuine changes. Read-only and free — it returns the stored results of past runs and never triggers a check (set_competitor_watch({runNow:true}) is what runs one).

ParametersJSON Schema
NameRequiredDescriptionDefault
limitNomax findings to return (default 25, max 75 — the server keeps at most 75, and at most 15 per brand)
competitorNoonly findings for this watched brand (exact name as returned in `watching`) — omit for all of them

TDQS

A4.6/5.0
Behavior5/5

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

Annotations already declare readOnly/idempotent/non-destructive, but the description adds genuinely new behavioral context: results come from stored past runs, the call is free and never triggers a check, and — critically — findings marked seed:true are NOT new launches because the first check has nothing to diff against. That semantic caveat materially changes how results should be interpreted.

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 purpose and the board it mirrors, then uses and the seed caveat. Dense and mostly waste-free, though it runs long and the read-only/free note is slightly reiterated. Not bloated, but not maximally tight.

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 carries the burden of describing return content — new ads per brand plus watch state (who is watched, last run, next run, success) — and it does so fully, including the seed:true interpretation. Nothing an agent needs to call or interpret this tool is missing.

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

Parameters3/5

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

Schema description coverage is 100%, so the schema fully documents limit (default 25, max 75, server cap) and competitor (exact name, omit for all). The description adds no parameter-level detail beyond that, so the baseline of 3 is appropriate.

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+resource: it reads the standing competitor watch's findings (new ads per watched brand) plus the watch's own state. It explicitly distinguishes itself from set_competitor_watch (which runs a check) and names the exact board it mirrors (Ad Spy > Watching), so an agent can place it among siblings 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?

Gives concrete when-to-use cases ('what are our competitors running that's new?', feeding a teardown, saving via save_to_swipefile) and an explicit when-not: it never triggers a check, and set_competitor_watch({runNow:true}) is named as the alternative that does. Routing is unambiguous.

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

list_whatsapp_accountsWhatsApp Business accounts and numbersA
Read-onlyIdempotent
Inspect

The WhatsApp Business Accounts SHARED WITH THIS PROFILE and the phone numbers registered on each — the ids every other WhatsApp tool needs, plus each number’s QUALITY RATING, which is what decides how many messages Meta will let it send. Start here. A WABA with no number cannot send anything and the reply says so: Hermoso does not register or verify numbers, that is WhatsApp Manager. A business portfolio that could not be read is REPORTED rather than dropped — an account missing from this list would read as "the brand has no WhatsApp", which is a claim about their business and not about our read. TWO ACCOUNTS CAN CARRY THE SAME DISPLAY NAME — always quote the display field or the id when naming one to a user, never the bare name. ONLY THE ACCOUNTS SHARED WITH THIS PROFILE ARE REACHABLE. One Meta login often administers WhatsApp for several businesses, so a human ticks which belong to this profile under Settings > Connectors > Meta > Manage accounts (or set_connector_accounts(provider:"meta")); nothing else here can see or touch the rest. With none ticked every WhatsApp tool refuses and names that as the way out. With exactly ONE ticked, wabaId is optional — pass it only to disambiguate. Read-only, 0 credits (WhatsApp conversations are billed by META to the business directly, never in Hermoso credits). Needs Meta connected.

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

TDQS

A4.8/5.0
Behavior5/5

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

Annotations cover read-only/idempotent/non-destructive, but the description adds substantial non-annotation context: unreadable portfolios are reported rather than dropped, only accounts shared with this profile are reachable, WABAs without numbers cannot send, and billing is 0 Hermoso credits because Meta bills directly. That is real behavioral disclosure beyond the structured fields.

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?

Information-dense with each paragraph carrying distinct facts (quality rating, unreachable accounts, duplicate display names, sharing scope, credits, prerequisite), and the key 'Start here' guidance is near the front. However, the heavy ALL-CAPS emphasis makes it longer and noisier than it needs to be.

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 carries the return-value burden and does so: it explains that accounts, ids, display names and quality ratings come back, and that unreadable portfolios are reported. Nothing an agent needs to call it correctly is missing.

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?

Zero parameters, so the baseline is 4 and the schema fully covers the (empty) surface. The description mentions wabaId being optional, but that parameter belongs to other WhatsApp tools rather than this one, so it adds little meaning here.

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 precise verb+resource: the WhatsApp Business accounts shared with this profile and the phone numbers on each, plus the ids other WhatsApp tools need. It self-identifies as the entry point ('Start here') and is clearly separable from siblings like list_connector_accounts and set_connector_accounts, which it references.

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?

Gives explicit when-to-use ('Start here'), prerequisites ('Needs Meta connected'), the failure mode when nothing is ticked, the special case when exactly one account is ticked (wabaId optional), and names the alternative tool (set_connector_accounts) that controls which accounts are visible.

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

localize_adLocalize a static ad into other languagesAInspect

Translate the on-image text of ONE finished static ad into other languages and keep everything else: same picture, layout, typeface, colours, logo and product. Pass image and languages (up to 5, e.g. ["Spanish", "German", "French (Canada)"]). The ad's text is read (3 credits), translated the way a native copywriter in each market would write it (brand and product names, URLs and prices kept as written), then one image edit per language; each output is proofread and flagged (textCheck) if the words do not match, never silently re-rendered. When the saved brand has a product photo and the ad shows that product, the photo rides with the edit and the product's label is read and checked against it: re-printed from the photo only when it came out wrong (the check alone is charged when it is right; one small extra charge finds the product first). fixLabel:false turns that off. Priced before it runs. For a VIDEO use dub_video.

ParametersJSON Schema
NameRequiredDescriptionDefault
imageYesthe finished static ad: URL, Library item URL, upload_file URL or local path
fixLabelNofalse = leave the product label exactly as rendered: no product lookup, no label check, no re-print (default on when the ad shows the brand’s saved product)
languagesYestarget languages, by name

TDQS

A4.5/5.0
Behavior5/5

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

Goes well beyond the annotations (readOnlyHint=false, openWorldHint=true, non-idempotent, non-destructive) by disclosing the credit model (3 credits to read, extra charge per edit and for product lookup), the proofreading/textCheck flagging behavior, the promise never to silently re-render, and the product-photo/label re-print logic. Pricing is stated to occur before the run, which is material for an open-world, billable mutation.

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 action and scoping before the pricing and label-handling detail. It is dense and contains one long run-on sentence stacking credits, translation behavior and per-language edits, but essentially every clause carries operational information an agent needs.

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?

With no output schema, the description still conveys what comes back (one image edit per language, each proofread and flagged via textCheck) and the billing behavior. It stops short of describing the exact return shape or how textCheck flags are surfaced, but for a 3-parameter tool this is close to complete.

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 schema already carries most parameter documentation, but the description adds real meaning: concrete language-format examples showing locale variants (['Spanish', 'German', 'French (Canada)']) where the schema only says 'by name', and a plain-language restatement of what fixLabel:false disables (no product lookup, no label check, no re-print).

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 (translate/localize) and resource (the on-image text of ONE finished static ad) with an explicit scope constraint ('keep everything else: same picture, layout, typeface, colours, logo and product'). It also names the sibling it is not ('For a VIDEO use dub_video'), letting an agent separate it from dub_video, clone_static and remix_static 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 Guidelines4/5

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

Gives clear usage context (one finished static ad, up to 5 target languages) and an explicit alternative for the video case. It does not, however, contrast itself against the other static-ad siblings (clone_static, remix_static, multiply_ad), which an agent choosing between ad-creation tools would need.

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

make_explainerMake an explainer videoAInspect

AI HOST EPISODE: format 'host_episode' = one presenter talking to camera, the camera changing every piece (frontal, three-quarter, close), the same host and set throughout, native voice, 16:9. Host = creator (saved or preset) or hostImage; words = topic (written for you) or script (verbatim). It returns a 480p DRAFT; HD (720p) is a SEPARATE call with fromDraft (quote it with dryRun, run it only when the user asks). Otherwise: turn a TOPIC into a finished narrated explainer video, in one of TWO LANES (lane). 'blocks' (the default) = 10-second VIDEO blocks, one narrated line per block, hard cuts, a music bed under the voice: an EXPLAINER renders on Gemini Omni at 720p (9:16 by default, or 16:9), a FACELESS CHANNEL video (format:'faceless_channel', or channel history / kids / fairytale) on MiniMax H3 at 2K (16:9 by default) with five cuts per block. 'stills' = a picture film: writes a sectioned script, paints a BURST of pictures per section (about one every 1.5s — most of them one-detail edits of the frame before, so it reads as movement rather than a slideshow), narrates each section with TTS, holds each picture PERFECTLY STILL for its own slice of the narration (the motion is the CUT RATE — a slow move on a still shimmers), then composites the end card (and any on-screen text you asked for) with the Chrome+ffmpeg engine the ads use (text is never model-painted, so it never garbles). BURNED ON-SCREEN TEXT IS OFF BY DEFAULT — the narration carries the point and the pictures carry the story, so the film ships clean unless the user asks otherwise; captions:true adds held key points and subtitles:true adds narration-timed CAPS (see both). The stills lane is an image film WITH motion, not N video-model renders — that's what keeps it affordable. style picks the visual family: the default 'cinematic' is photoreal editorial; every other id is a STYLED, strictly non-photoreal look (illustrated / collage / clay / pixel …) that first renders ONE style-key image and then locks every scene to it, so the whole film holds one look. Cost at the default frame density: a ~130-credit hold for a 60s explainer on the default style, ~100 styled; frameDensity:'lean' roughly halves it and 'minimal' (one picture per section) is ~30. All settle to the exact per-frame image + narration spend (a longer target = more sections = more). Takes SEVERAL minutes — one image render per frame; independent frames are painted concurrently, so it is far faster than the frame count suggests. Needs the writing model and a narration voice engine connected. NOT the tool for a short product ad — use render_ad or generate_video for those, and make_template_ad for the deterministic native formats.

ParametersJSON Schema
NameRequiredDescriptionDefault
laneNo'blocks' (default) = 10-second video blocks, real motion, one narrated line per block; 'stills' = the picture film (a still about every 1.5s, narrated, no video model; cheaper). Quote either with dryRun.
musicNomusic bed under the narration, measured to sit about 14 dB under the voice and sidechain-ducked beneath it. Omit and the KIDS and FAIRYTALE channels get their recommended bed COMPOSED for this film — those two are the only channels a bed is due on unasked, and it costs a small flat fee; every other channel ships dry. 'off' forces silence. 'library' takes a free curated track only, and ships dry when none is on file. NAME A MOOD (upbeat / calm / warm / epic / tense / playful / elegant / hype / chill / dramatic) or DESCRIBE it in words to compose one on ANY channel, at the same fee. hermoso_capabilities reports the exact figure as explainerMusicCredits; quote it before you turn a bed on or pick a mood.
styleNovisual style: 'cinematic' (default, photoreal); styled shortcuts editorial_collage, flat_vector, stickman, whiteboard, ink_marker, silhouette, storybook, paper_diorama, isometric, claymation, pixel_art, watercolor, fluffy_toy, low_poly, stylized_3d, studio_3d (the Kids default), mannequin; or ANY look described in words ('80s anime cel animation'), locked across every frame. Ask rather than pick silently; a styled look costs more.
topicNowhat the explainer should teach or explain — a topic or a short brief (host_episode: or pass `script`)
voiceNonarration voice name — omit for the default warm read
dryRunNotrue = return the exact credits this explainer reserves (its own pricing, stopped at the hold) and render nothing. Quote it before running one; try frameDensity lean or minimal when the balance is short.
formatNoblocks lane: 'explainer' (default; Gemini Omni 720p, 16:9 or 9:16, runs exactly the length asked) or 'faceless_channel' (a YouTube/TikTok faceless channel video; MiniMax H3 at 2K, 16:9 by default, five hard cuts per 10s block, a whole number of blocks). Omit and a history / kids / fairytale channel is a faceless channel video. 'host_episode' = the AI host episode (see the top).
scriptNohost_episode: the exact words, said verbatim and split at natural breaks into 4-30s pieces
camerasNohost_episode: the rotation, ids frontal / three_quarter / close or framings in words; one entry = one fixed camera
channelNothe CHANNEL TYPE — it sets the pacing, the narration register and the default look, and is orthogonal to `style` (a named style always wins): explainer (casual second-person, fast cuts), history (witty chronological retelling / documentary), kids (fastest, question-first, warm teacher), fairytale (slow, atmospheric myth or folklore). Default 'explainer'.
creatorNohost_episode: the host, a saved creator or a preset by name or id (list_creators)
endCardNoappend the branded end card. DEFAULT FALSE — set true ONLY when the user asks for one
settingNohost_episode: the set in words (default: written to fit the topic)
upscaleNooptional FINAL upscale — 2 doubles each side, 4 quadruples. Captions and the end card are burned BEFORE it so they upscale with the frame. It is priced BY LENGTH and it is the expensive part — several times the cost of rendering the film itself. hermoso_capabilities reports the exact figures per length as explainerUpscaleCredits. Never turn it on unasked: quote the number and let the user choose.
captionsNoturn ON-SCREEN TEXT on. DEFAULT FALSE, and leave it false unless the user asks — the narration already says the point and the pictures carry it, so the clean film is the better default. `captions:true` on its own burns SUBTITLES (see below), because that is what a caption is for: showing what is being said when the phone is on mute. Slim white CAPS, thin black outline, bottom safe band, no plate, no box.
brandNameNobrand name for the end card — omit to leave it unbranded
fromDraftNohost_episode: a finished draft's job id, to render it in HD (720p) with the same script, cameras, set and host
hostImageNohost_episode: a photo URL of the host instead (upload_file for a local file); a real person's face needs a paid plan
subtitlesNowhich on-screen text, once `captions` is on. LEAVE IT UNSET (or true) for SUBTITLES — every spoken word, in order, timed to the narration; free, no extra render, no extra credits, and there is NO cue limit, so the whole film is subtitled however long it runs (at most 5 words / 32 characters a line). Set it FALSE only if the user explicitly wants section HEADINGS instead: one short summary label held over each ~7-15s section. That is NOT what is being said — it is a label about it — so it is the wrong answer to "add captions" and to anyone watching on mute. `subtitles:true` also implies `captions:true`. TIMING: each cue is anchored to that section’s REAL measured narration length and distributed inside the section by character count — exact at every section boundary, approximate to a few tenths of a second within one. It is not a word-level speech clock, so never promise frame-accurate sync.
thumbnailNohost_episode: one thumbnail of the host (default true)
aspectRatioNo'9:16' default (a faceless channel video and a host episode default to 16:9)
frameDensityNohow many pictures per second of narration, and therefore what it costs. 'standard' (default) is a frame about every 1.5s — the density a stills film needs to read as a film rather than a slideshow; 'lean' is one about every 2.5s (the longest hold that still reads as a film, ~40% of the frames and ~40% of the cost); 'minimal' is one picture about every 3.5s, the cheapest and the longest any still is ever held, and it reads close to a slideshow. Only drop below the default if the user asked for something cheaper.
durationSecondsNotarget length 20-120s (default 60); drives the section count — ~10s of narration each, 3-8 sections

TDQS

A4.8/5.0
Behavior5/5

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

Annotations only flag readOnly=false/openWorld=true/idempotent=false/destructive=false; the description adds far more: it produces a 480p draft with HD as a separate fromDraft call, takes several minutes, concurrency behavior, credit costs with exact figures, the music-bed fee, and that upscale is priced by length and expensive. Defaults for captions/endCard/music shipping dry are all disclosed.

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

Conciseness3/5

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

The content is valuable but it is a dense wall of text opening on a niche sub-format ('AI HOST EPISODE') before the general purpose, and several ideas (captions/subtitles, upscale cost) are restated in multiple places. It is front-loaded only after the reader wades through the host-episode preamble, which buries the primary use case.

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?

For a 23-parameter, zero-required, multi-format tool with no output schema, the description covers lane selection, formats, aspect defaults, cost bounds, timing expectations, prerequisites, and sibling routing. Nothing an agent needs to call it correctly is missing, and the draft-vs-HD flow is spelled out.

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

Parameters5/5

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

Schema coverage is already 100%, but the description still adds real meaning: cost and pacing implications of frameDensity, the mood vocabulary for music and when a bed is free vs charged, the subtitle-vs-headings distinction for subtitles, and the exact rule that captions:true alone means subtitles. This is well past the baseline-3 case.

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+resource: 'turn a TOPIC into a finished narrated explainer video,' then enumerates its concrete variants (host_episode, blocks/stills lanes, explainer vs faceless_channel). It explicitly separates itself from adjacent tools ('NOT the tool for a short product ad — use render_ad or generate_video'). An agent can place it against siblings without opening the 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?

Gives explicit when/when-not and named alternatives: host_episode vs blocks vs stills, which channel triggers faceless, and the direct exclusion routing to render_ad / generate_video / make_template_ad. Prerequisites are named too (writing model and narration voice engine must be connected), plus guidance to quote with dryRun before running.

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

make_insertMake an insert clipCInspect

A reaction picture (image, or video from videoStart) with its sound, cut to when the sound lands (or seconds): a 1080x1920 clip for post_edit join.

ParametersJSON Schema
NameRequiredDescriptionDefault
imageNo
soundYes
videoNo
secondsNo
soundEndNo
soundStartNo
videoStartNo

TDQS

C2.6/5.0
Behavior3/5

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

Annotations already declare readOnlyHint=false, openWorldHint=true, idempotentHint=false, and destructiveHint=false. The description adds the output dimensions (1080x1920) and the sound-sync behavior, which is useful context beyond the annotations, but says nothing about permissions, rate limits, or what happens to source assets.

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

Conciseness3/5

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

A single dense sentence with no filler, and the output-format detail is saved for the clause after the colon. However, it is a run-on construction that is hard to parse front-to-back for such an input-rich tool.

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

Completeness2/5

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

For a 7-parameter mutation tool with no output schema, the description is too thin: it omits the meaning of several params, gives no return expectations, and provides no usage boundaries. Only the output format and the post_edit hand-off are covered.

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

Parameters2/5

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

Schema description coverage is 0%, so the description must carry all parameter meaning. It clarifies image, sound, videoStart ('video from videoStart'), and seconds parenthetically, but leaves video, soundStart, and soundEnd completely unexplained, dropping three of seven parameters.

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

Purpose3/5

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

The description conveys that this produces a 1080x1920 reaction-picture clip synced to a sound, and names the consuming tool (post_edit join). But it never states a clean verb+resource, and the phrasing ('reaction picture ... cut to when the sound lands') requires effort to parse. It is distinguishable from siblings only indirectly through the post_edit reference.

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

Usage Guidelines2/5

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

The only routing hint is 'for post_edit join,' which implies a downstream step but does not say when to choose make_insert over alternatives like clip_video, stitch_video, or finish_video. No prerequisites or when-not conditions are given.

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

make_template_adMake template adAInspect

An ad or post rendered from HTML: no AI model, ~30s, a couple of credits. Presets are SHORTCUTS; 'custom' is YOUR OWN design as config.html (+ css), so no layout, type, colour or motion is 'unsupported'. custom: { html, css?, size? ('9:16' default | '4:5' | '1:1' | '16:9' | any 'W:H' | {w,h} px), durationSeconds? (1-60 = VIDEO; CSS/SVG animation and are frame-stepped, scripts stripped), slides?:[{html, css?}] (2-35 = carousel) }; {{logo}} {{brandName}} {{domain}} {{accent}} fill from the brand; images and fonts load by https URL; notes[] lists what failed to load. YOU author preset copy: short, casual, believable, finished phrases within budget. The preset ids — slideshow, imessage-chat, chatgpt-chat, apple-notes, value-prop, static-mockup, airdrop-carousel, app-ui-tour, imessage-cascade, photo-grid, vignette, kinetic-type, myth-vs-fact, carousel — and each one's fields are listed on config. config.music on a VIDEO: omit and the format gets a music bed from our library, matched to its mood and free, whenever the library is stocked (hermoso_capabilities hasMusic); with none on file the video carries only its own sound effects, and the reply says so. 'off' for silence, or any words (a mood or a description) to compose a bed to them (a flat music fee, in hermoso_capabilities). Image URLs may be any public URL.

ParametersJSON Schema
NameRequiredDescriptionDefault
configYesMUST include config.template: 'custom' or a preset id, plus its fields. PRESETS: 'slideshow' (IMAGES, TikTok photo mode / Reels 1080x1920, or size:'4:5' feed carousels; no branding): { slides:[{text, sub?, image?, blur?, background?, position?}] (2-35; words never rewritten), style? ('tiktok-classic'|'clean-minimal'|'note-style' or a look in words), textStyle?, video?:true (+ an MP4) }; 2 credits, +1 per slide past 5, +2 for the MP4. 'imessage-chat' (VIDEO ~15s): { thread:{contactName, messages:[{from:'them'|'me', text?, product?:{image,title,domain}}]}, theme?, endCard }. 'chatgpt-chat' (VIDEO): { question, answer (may **bold** the brand), productImage?, endCard }. 'apple-notes' (VIDEO): { title, lines[], theme?, endCard }. 'value-prop' (VIDEO ~17s): { hook ≤40ch, claims[3-5 ≤34ch], productImages[2-3], palette[], endCard }. 'static-mockup' (IMAGE): { style:'imessage'|'notes'|'card', size?:{w,h}, ...fields }. 'airdrop-carousel' (VIDEO): { brandName, products:[{image, title?}] (3-16), endCard }. 'app-ui-tour' (VIDEO): { hook?, appName, iconImage?, beats:[{screenImage, caption}] (2-6), endCard }. 'imessage-cascade' (VIDEO): { notifications:[{sender, text}] (4-8), backgroundImage?, endCard }. 'photo-grid' (VIDEO): { title?, photos:[{image, label?}] (4-9), endCard }. 'vignette' (VIDEO): { hook, lines[2-4 ≤40ch], heroImage, endCard }. 'kinetic-type' (VIDEO, own SFX): { phrases[3-6 ≤34ch], productImages?[≤4], endCard }. 'myth-vs-fact' (VIDEO with a real VOICEOVER, small extra charge): { pairs:[{myth ≤50ch, fact ≤60ch}] (2-4; [brackets] accent), endCard }, real truths only. 'carousel' (IMAGES, 5-10 branded 1080x1080): { cover:{hook?, title}, slides:[{headline, support?, stat?:{value, label}}] (3-8), cta:{headline, cta?, domain?}, productImage?, logo? }. endCard = { headline, cta, domain?, logo?, color? }; palette and fontStack optional.

TDQS

A3.5/5.0
Behavior4/5

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

Annotations already declare openWorld and non-idempotent, and the description adds meaningful context: ~30s render, a couple of credits, scripts stripped, CSS/SVG animation frame-stepped, 1-60s means VIDEO, 2-35 slides means carousel, failed loads reported in notes[]. That is strong behavioral disclosure beyond the annotations, though it stops short of full return-shape detail.

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

Conciseness3/5

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

Nearly information-free of padding, but delivered as one dense, semicolon-chained paragraph that is hard to parse. The purpose is front-loaded, yet the preset-id enumeration and music rules tumble together without structure, hurting scanability for an agent.

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 complex, highly configurable renderer with no output schema, this covers cost, timing, rendering constraints, failure reporting, branding tokens and music behavior. It is close to complete; the main omission is routing guidance against sibling ad tools.

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 adds genuinely new semantics the schema lacks: the {{logo}}, {{brandName}}, {{domain}}, {{accent}} placeholder vocabulary, the default '9:16' size and W:H/{w,h} forms, and the config.music omit/'off'/describe-mood rules. These are not restatements of the config schema.

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

Purpose4/5

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

States a specific verb+resource: an ad/post rendered from HTML, either from a named preset or your own config.html/css, with no AI model. That is a crisp capability statement. It does not, however, distinguish itself from close siblings like render_ad, multiply_ad, clone_static or remix_static, so the agent cannot route on purpose alone.

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

Usage Guidelines2/5

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

The bulk of the text is configuration/music mechanics, not tool selection. There is no statement of when to reach for make_template_ad versus render_ad or the other ad-rendering siblings, and no prerequisites or exclusions beyond the per-parameter music rules.

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

make_thumbnailMake video thumbnailAInspect

Render a click-driving YOUTUBE / Shorts / Instagram THUMBNAIL or video cover through the full production pipeline (concept, casting, scene, render, tweaks, text), not a bare image prompt. Use it for any "thumbnail", "video cover" or MrBeast-style packaging ask INSTEAD of generate_image. About 9 credits per variant; the headline overlay is free.

CONCEPT — open an INFORMATION GAP (the image raises a question the title answers) while staying truthful to the video. Brainstorm ≥5 concepts across the 16 frameworks (ids on framework; combining two is fine) before you pick; hermoso_capabilities has each one's 'realize it with' note and the emotion, overlay, font and rim-colour catalogs.

THREE GATES, all BEFORE you render:

  1. WHO IS IN FRAME — never assume or silently substitute a stranger. A framework with a person and no face photo is refused (nothing charged): ask the user once — themselves (a face photo, identity-locked), a generated person (castGenericPerson:true), or a people-free framework.

  2. TEXT — default is a CLEAN render with the headline TYPESET over it (free, legible, correctly spelled): pass headline. bakeText:true only on an explicit ask for words painted INTO the image. Never infer text intent from the topic.

  3. HOW MANY — ask once: one, or a SET (offer 4: one concept at different emotions / camera takes). Default 1; variants caps at 16.

emotion is the biggest CTR lever on a face (identity lock is automatic for every face photo). To fix a finished one, re-call with tweak + sourceImage for a surgical edit (emotion / background / background_color / rim_light) — tweaks chain. ALWAYS check the returned postRenderCheck against the image before presenting it.

PROMPT LANGUAGE — write every DESCRIPTIVE field (sceneBrief, keyElements, location, composition, background, topic, each person's describe, every reference) in ENGLISH, translating the user's words: the models render English better. headline, headlineLines and bakedUiText stay verbatim in the user's language.

ParametersJSON Schema
NameRequiredDescriptionDefault
fontNoheadline font: Anton (default) or any Google Fonts family
logoNoa brand logo URL or path to place into the composition
splitNosplit/panel LAYOUT — only when the user asks for one ("split", "before/after", "versus screen"). "X vs Y" as a SCENE stays one unified frame
takesNocamera takes per emotion, 1–4: designed framing / low-angle hero / extreme close-up / wide dutch tilt
topicNothe video's topic — used to pick the hero object when you don't name keyElements
tweakNosurgical pixel-faithful edit of a FINISHED thumbnail (needs sourceImage): kind emotion / background / background_color / rim_light, or any other kind with the edit in words as value
logo3dNofirst turn the flat logo into a volumetric 3D render (one extra billed image), then composite that
peopleNopeople described in prose instead of by photo (each still gets the chosen expression)
emotionNothe expression on the face (default 'shock') — shock · hype · fear · confusion · determination · smug · charisma · disgust · awe · rage · laugh, or your own phrase
bakeTextNodefault false. true paints the headline INTO the generation — only on an explicit user ask; it leaks garbled text elsewhere in the frame
emotionsNorender one variant per emotion (variants = emotions × takes, max 16)
headlineNo2–4 word headline. Typeset OVER the finished render by default (free, always legible); newlines split it into stacked lines
locationNoplace, time of day, weather, atmosphere
rimColorNocolored back+hair light — ONLY when the user names one: 'ice-blue' / 'neon-magenta' / 'toxic-lime' / 'amber-gold' / 'pure-white'
variantsNohow many thumbnails to render (default 1, max 16). Each is its own billed render — offer a set of 4 rather than assuming
frameworkNoconcept framework id (default 'posed_portrait') — before_after · social_ui · three_step · screenshot · posed_portrait · posed_action · specific_day · graphical · landscape · map_aerial · product · adding_text · repetition · size_difference · news_clip · amplified_reality — or your own concept in words
referenceNofields YOU extracted by eye from a reference thumbnail. Extract ALL of: brief (one dense sentence on the concept), subject (pose/action generically, NEVER a specific identity), elements, location, composition, background, split (boolean), split_count, person_count (0-3), emotion (one of the 11 presets or 'other'), emotion_detail (one vivid sentence covering eyes, brows, mouth, head angle). emotion + emotion_detail carry the reference's actual facial performance, which is the single biggest CTR lever on a face; split/split_count reproduce its panel structure. The reference image itself is never sent to the model
backgroundNooverride the default bold saturated colour-field background
faceImagesNoup to 3 face photos (URLs or local paths) — each becomes a locked CHARACTER identity, in order
sceneBriefNowhat the thumbnail depicts — the concept in one dense sentence, rendered exactly
aspectRatioNo'16:9' (YouTube, default) / '9:16' (Shorts) / '4:5' (Instagram) / '4:3' / '1:1'
bakedUiTextNoshort label for a text-carrying framework (a chat bubble, a DAY N badge, a news lower-third, a map callout) — needs frameworkRequested:true
compositionNooverride the default large-foreground-subject composition
keyElementsNosignature props / effects that make it pop — oversized, flying toward camera
sourceImageNothe finished thumbnail URL a `tweak` edits; tweaks chain, so feed each accepted output into the next
overlayStyleNoheadline style: beast (default), fire, neon-lime, clean-glass, marker, or your own CSS declarations
forceGenerateNorender the 'screenshot' framework anyway (it is normally a real video frame, not a generation)
headlineLinesNoexplicit headline lines (up to 3) — overrides splitting `headline` on newlines
headlinePlaceNobottom (default), top, center, or a 0-1 fraction from the top; never over the face
logoPlacementNooverlay (exact, flat) | in_scene (from the file, checked); auto: in_scene with logo3d
restrainedGradeNotrue for a calm / premium / muted look instead of the default punchy poster grade
castGenericPersonNopass true only after the user has explicitly chosen a generated stranger over their own face
frameworkRequestedNotrue ONLY when the USER named this framework — it is what authorizes a text-carrying framework (social_ui / news_clip / specific_day / map_aerial) to bake its short UI label

TDQS

A4.7/5.0
Behavior5/5

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

Annotations only declare the generic write/open-world profile. The description adds substantive behavior: ~9 credits per variant, free headline overlay, extra billed image for logo3d, refusal-with-no-charge when a face framework lacks a photo, tweak chaining, and the postRenderCheck verification step. This is rich context beyond 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?

Front-loaded with purpose and routing, then organized under CONCEPT / THREE GATES / PROMPT LANGUAGE headers. Dense but most sentences carry actionable guidance; a few procedural lines run long, keeping it from a 5 for a description this size.

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 33-parameter generative tool with no output schema, it covers the workflow, cost model, refusal behavior, and prompt-language rules thoroughly. It doesn't fully describe the return shape beyond referencing postRenderCheck, but nothing essential for correct invocation is missing.

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. The description adds meaning beyond the schema: it groups parameters into conceptual decisions (gates), explains when bakeText vs headline applies, that combining frameworks is allowed, and that variants is a billed render per unit. Marginal but real value over the schema text.

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+resource ('Render a ... THUMBNAIL or video cover through the full production pipeline') and explicitly contrasts with the sibling generate_image ('use it ... INSTEAD of generate_image'). An agent can distinguish it from generate_image and image-editing siblings without opening the 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 when-to-use (any thumbnail/video-cover/MrBeast packaging ask), when-not (bakeText only on explicit ask; never infer text intent; don't assume who's in frame), and a named alternative (generate_image). The THREE GATES lay out decision points and defaults clearly.

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

manage_sheet_tabsAdd, rename or delete a sheet tabA
Destructive
Inspect

Add, rename or delete a tab in a Google Spreadsheet. action:"add" + title · action:"rename" + tab + newTitle · action:"delete" + tab. Name the tab by its TITLE or its numeric sheetId (list_sheet_tabs gives both); an unknown tab is refused with the real list rather than a Google error nobody can map back. DELETING a tab destroys everything on it: call it without confirm first to get the filled-cell count, then confirm:true + confirmCells. Google does not allow removing the LAST remaining tab in a file, and that is refused by name with the way out (clear it, or delete the whole file with delete_drive_file). Every action is read back from the spreadsheet before it is reported as done.

ParametersJSON Schema
NameRequiredDescriptionDefault
tabNowhich tab — its title or numeric sheetId (rename / delete)
titleNothe name for the new tab (action:"add")
actionYes
confirmNo
newTitleNowhat to rename the tab to (action:"rename")
sheetUrlNo
confirmCellsNo
spreadsheetIdNo

TDQS

A4.9/5.0
Behavior5/5

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

Beyond the annotations (readOnlyHint=false, destructiveHint=true), the description discloses that deleting a tab destroys its contents, that an unknown tab returns the real list instead of an opaque Google error, that the last tab is refused, and that actions are verified by reading back the spreadsheet. This far exceeds the annotation baseline.

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

Conciseness5/5

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

The description is dense but every sentence earns its place: action recipes, tab identification, deletion safety, the last-tab edge case, and read-back verification. It is front-loaded with the purpose, and the compact '·' notation keeps the action recipes scannable.

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?

For a destructive, three-action tool with no output schema, the description is remarkably complete: it covers parameter usage, error behavior, confirmation flow, edge cases, and the fallback alternative. The only minor gap is spreadsheet identifier semantics, which is slight given the context signals.

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?

The description adds substantial meaning to the schema's under-documented parameters: it explains how action combines with title, tab, newTitle, confirm, and confirmCells. However, sheetUrl and spreadsheetId are left entirely implicit, and with schema coverage at only 38%, those two identifiers deserved at least a mention.

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 and resource — 'Add, rename or delete a tab in a Google Spreadsheet' — and then enumerates the exact action combinations. It clearly distinguishes the tool's scope from the many sibling file/sheet tools by naming the relevant helpers list_sheet_tabs and delete_drive_file.

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?

The description provides explicit when-to-use guidance: action-specific parameter recipes for add, rename, and delete, plus a two-step confirmation workflow for destructive deletes. It also tells the agent what to do when the last tab cannot be removed and points to delete_drive_file as the alternative.

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

mine_anglesMine customer anglesAInspect

Mine ad ANGLES from real customer language: gathers the customer's own words (Reddit, TikTok, the brand's review page + review-site results) and returns a RANKED angle bank — each angle tagged (pain / outcome / identity / fear / competitive-displacement / social-proof / contrast), 2-5 VERBATIM proof quotes, a 0-100 score with breakdown, and a ready-to-run hook in the customer's own voice. Reads YOUR saved brand (pass brandId to target a specific profile — that switches this key's active profile like use_brand). To tear down a COMPETITOR use competitor_teardown instead. YOUR OWN REVIEWS: pass reviews (a list of review texts, or one pasted block: one per line, numbered, blank-line separated, or a CSV with a review column) and/or reviewsUrl (a CSV, TXT or JSON file from upload_file, or a review page; on a local CLI a file path works too). They are first-class evidence: every quote from them is checked word for word against what you sent and labelled 'your reviews', and a quote that is not verbatim is dropped and counted. useOwnReviewsOnly:true mines only your reviews and gathers nothing public. Limits: 300 reviews, 2,000 characters each, 40,000 in total; over that it is refused at no cost, so send fewer or split into batches. Each angle comes back with next: the exact plan_variations and generate_image arguments that turn it into finished statics (one generate_image per angle = statics with distinct angles). Spends a few credits.

ParametersJSON Schema
NameRequiredDescriptionDefault
brandIdNoa profile id/name from list_brands to mine for; omit to use the active profile
reviewsNoyour own customer reviews: a list of review texts, or one pasted block (one per line, numbered, blank-line separated, or CSV with a review column)
reviewsUrlNoa URL of your reviews: an uploaded CSV, TXT or JSON file (from upload_file) or a review page. On a local CLI a file path also works.
useOwnReviewsOnlyNotrue = mine only your reviews, no public search (no search credits). Default false = merge with public customer language.

TDQS

A4.8/5.0
Behavior5/5

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

Annotations only declare it is a non-read-only, open-world, non-idempotent, non-destructive operation; the description adds the substantive traits: it reads YOUR saved brand, silently switches the active profile, spends credits, enforces review-size limits, and drops/counts non-verbatim quotes. This is far more than the annotations convey.

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?

Core purpose and the key alternative are front-loaded, and every sentence carries operational detail. It is dense and long with some repetition of the reviews/reviewsUrl mechanics, but nothing is purely filler, so it is efficient rather than padded.

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 fully covers the return shape (ranked angles, tags, 2-5 verbatim quotes, 0-100 score, hook, and the `next` plan_variations/generate_image arguments). Combined with the disclosed limits and cost behavior, an agent has everything needed to invoke and interpret 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 coverage is already 100%, so the baseline is 3, but the description adds real meaning: accepted review formats (list, numbered lines, blank-line separated, CSV column), accepted reviewsUrl formats (CSV/TXT/JSON from upload_file, page, or local path), and the behavioral effect of useOwnReviewsOnly. It does not add much on brandId beyond the schema, keeping it just above baseline.

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 ('Mine ad ANGLES from real customer language') and details the output artifact (a ranked angle bank with tagged angles, verbatim quotes, scores, and hooks). It explicitly distinguishes itself from the sibling competitor_teardown and references use_brand/list_brands, so an agent can route correctly without opening schemas.

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?

Gives explicit when-to-use (mine customer language from Reddit, TikTok, reviews), when-not (tear down a competitor -> competitor_teardown), and alternative mechanisms (pass brandId to switch the active profile like use_brand; useOwnReviewsOnly to skip public gathering). Hard limits are stated with the exact fallback (refused at no cost, so send fewer or split into batches).

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

multiply_adMultiply an adAInspect

MULTIPLY a winning video ad into N variants: each gets a NEW character, outfit, location and/or objects while the cut, the camera motion, the pacing and the ORIGINAL AUDIO stay exactly as they were (that is what made the ad work), and any burned-in captions are removed. Pass the source video URL (a previous render, a job result, list_library, or the top performer from post_performance / meta_insights). HOW: the source video itself DRIVES each variant (motion transfer from one image-edited opening frame), so every variant comes back the SAME LENGTH as the source with the same cut, the same performance and the original audio — only the person, outfit, set and props change. Sources up to 30 seconds work as they are; longer ones are refused for free with the way out (trim it first: post_edit with ops [{op:'trim', start:0, end:30}] — clip_video is the AI highlight clipper, not a trim). Returns the plan and ONE JOB PER VARIANT — call get_job on each until it reports done; do not describe a variant before its URL arrives. Cost is quoted per variant in the reply (use dryRun:true to see the plan and the quote without rendering). Regions: pass regions:['Berlin','Tokyo'] to restyle variants per market; translation is a separate, explicit step — dub_video on a finished variant. change is what should change, in the user's own words ('older women', 'winter streets', 'swap the mug for our bottle'): every variant applies it and the variants still differ around it; a part of it about the voice, words, language, music, length or captions cannot change here, and the reply says so and names the tool that can.

ParametersJSON Schema
NameRequiredDescriptionDefault
axesNowhat to vary: character, outfit, location, objects (default all four), or your own, e.g. "season"
countNohow many variants, 1-12 (default 6)
notesNoanything the variants must respect, e.g. "keep it women 25-40", "no gyms"
videoYesthe source video URL
changeNowhat should change, in the user's own words, e.g. 'older women, winter streets'; every variant applies it (omit to let the variants vary freely)
dryRunNotrue = return the plan and the quote, render nothing
regionsNomarkets to restyle for, one or more variants each, e.g. ["Berlin","Tokyo","São Paulo"] — visuals only; audio is never translated here

TDQS

A4.8/5.0
Behavior5/5

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

Adds substantial context beyond annotations: length/cut/audio preservation, caption removal, the 30-second limit with a free refusal, per-variant cost quoting, dryRun behavior, and the async job-per-variant pattern requiring get_job. Annotations (non-idempotent, open-world, not destructive) are consistent and supplemented.

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?

Long but front-loaded with the core behavior, and most sentences carry operational value (limits, cost, job flow, sibling routing). Some density and repeated framing ('that is what made the ad work', restated preservation) cost it a point.

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?

No output schema exists, and the description compensates fully by explaining the return ('the plan and ONE JOB PER VARIANT — call get_job on each until done') plus cost quoting and the dryRun preview path. Nothing an agent needs to invoke it correctly is missing.

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 adds real semantics: `change` applies to every variant while variants still differ, and content about voice/words/language/music/length/captions cannot change here; `regions` restyles visuals only with audio never translated. These go beyond the schema text.

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 ('MULTIPLY a winning video ad into N variants') and precisely defines the scope of change (character/outfit/location/objects) versus what is preserved (cut, camera motion, pacing, original audio). It explicitly distinguishes itself from siblings by naming clip_video and post_edit as different tools.

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 about when to use it, where the source URL comes from (previous render, job result, list_library, top performer from post_performance/meta_insights), and what to do when input exceeds 30s ('trim it first: post_edit... clip_video is the AI highlight clipper, not a trim'). It routes translation to dub_video.

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

plan_adPlan an ad conceptAInspect

Industry briefs: list_templates. Creative director: turn a brand + product/brief into a finished ad CONCEPT — copy variants (headline/primary/cta) plus an image_concept.prompt OR a video_storyboard, with the resolved recipe + the model ids to render with. Renders nothing; chain its output into generate_image / generate_video. THE USER’S EXPLICIT LENGTH IS SOVEREIGN: when they name a duration ("a 30 second ad", "make it 45s"), pass it as durationSeconds — the board is then AUTHORED to that length (its scenes sum to it) and render_ad renders it as one clip or stitched acts accordingly. Leaving it out lets the planner pick its own default, which is how an explicit ask silently becomes a 15s spot. Spends credits.

ParametersJSON Schema
NameRequiredDescriptionDefault
hookNoforce the VISUAL scroll-stop mechanic the opening beat is built on — a hook id from list_hooks (e.g. "direct_callout", "mid_problem", "macro_asmr"). Omit to let the planner pick. A hook that cannot be delivered in this brief is DROPPED with the reason rather than rendered wrongly — an on-screen-text hook on an authentic/UGC ad is the one that bites, because that register carries zero on-screen text.
brandNobrand name, or a brand profile object {name,domain,category,palette,products,…}. OMIT to use the workspace’s SAVED brand + memory automatically (see get_brand); use draft_brand to onboard a new one
draftNoONLY after a video refusal that offered a light draft: the {model, durationSeconds} it named. The plan is then authored to that length and priced on that model. Never invent one — a video the account cannot cover is refused BEFORE planning with the three options (image / add credits / this draft when one fits), and the user chooses.
formatNo'image', 'video', or 'auto' when unspecified
recipeNoa recipe id from hermoso_capabilities to force an archetype
talentNowho is on camera; omit to follow the format. Say 'someone new' in product to skip reusing a saved creator.
productYeswhat to advertise + any angle/offer the user specified
settingNoforce the WHERE — a setting id from list_hooks (e.g. "kitchen", "gym", or a surreal one like "volcano_rim" / "airplane_wing", which are played 100% straight and never acknowledged). Omit for a neutral setting.
languageNooutput language for the ad copy (e.g. Spanish) — default English
referenceNoa reference to clone: an ad-library link (Facebook Ad Library, LinkedIn Ad Library, Google Ads Transparency — its real copy/advertiser are fetched) OR a VIDEO link — a TikTok, Instagram Reel, Facebook video, X post, YouTube Short/video or a direct video file — which is WATCHED first (frames + voiceover/on-screen-text transcript) so the concept keeps its hook, structure and pacing. To remake one video for this brand at its own length, clone_video is the direct tool
durationSecondsNoVIDEO ONLY — the total spot length the user explicitly asked for, in seconds, copied verbatim (30 for "a 30 second ad"). The planner authors the storyboard TO it: the scenes’ seconds sum to it and the script is word-budgeted for it. Supported range 4–180; anything outside is CLAMPED to it (the reply says so). A length that fits ONE clip of the render model renders as a single continuous pass; anything longer is STITCHED from acts filled to that model’s clip maximum with the remainder last (on a 15s-clip model, 40 -> 15+15+10 and 17 -> 13+4) — never time-compressed. The maximum is the model’s own: 15s on most, 30s on the longest-clip model, so a 30s ad can be one unbroken take rather than two acts. Omit when the user named no length; do NOT pass a guess: an omitted value is the default, a 30s spot in one unbroken take (2026-09-29), and a length the user names always wins.

TDQS

A4.9/5.0
Behavior5/5

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

Adds substantial behavior beyond annotations: 'Spends credits', 'Renders nothing', explicit duration-sovereignty rules, clamping outside 4–180s, scene-sum authoring, stitching math (40 -> 15+15+10, 17 -> 13+4), the model-clip-maximum constraint, and the hook-drop behavior. Annotations (destructiveHint=false, readOnlyHint=false, idempotentHint=false) only declare it as a non-idempotent, credit-spending writer; the description carries the real behavioral burden no annotation provides.

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-loads the purpose (concept, not a render) and the chaining requirement before diving into depth. It's dense but heavily load-bearing — the length/stitching passage is long because it's the tool's main correctness trap. The repeated emphasis on 'THE USER'S EXPLICIT LENGTH IS SOVEREIGN' is slightly redundant, keeping it just below 5.

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 11 params (nested draft object, enums for format and talent), no output schema, and a credit-spending non-idempotent behavior, the description covers the essential traps: what it returns conceptually, why duration matters, when it refuses (video the account can't cover), and the chaining contract into render. No separate output schema means the description correctly stands in for the return-value shape.

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

Parameters5/5

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

Schema coverage is already 100%, but the description adds semantics the schema cannot: the duration itself is a sovereignty/consistency guarantee with clamping, sum-of-scenes authoring, and clip-stitching consequences, plus narrative on why omitting it silently yields a 15s spot. Other params equally get nuanced guidance (reference can be a link OR a watched video; hook is dropped with a reason if undeliverable).

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 and resource ('turn a brand + product/brief into a finished ad CONCEPT') and enumerates exactly what the output contains: copy variants (headline/primary/cta) plus an image_concept.prompt OR a video_storyboard, with the resolved recipe and model ids. It distinguishes itself from siblings immediately with 'Renders nothing; chain its output into generate_image / generate_video', separating it from render_ad, make_template_ad, generate_image, and generate_video.

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?

Explicitly states when to use this tool and when not: it renders nothing and must be chained into generate_image / generate_video. It also names clone_video (via the reference param) and list_templates/list_hooks as prior steps, and explains the duration-choice condition ('leave it out and the planner picks its own default'). Alternatives and conditions are named rather than left to inference.

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

plan_variationsPlan ad variationsAInspect

Fan a brief into N DISTINCT ad angles (different hooks/mechanics/audiences), each with its own headline + visual brief — then render each with generate_image and rank with score_ad. LLM planning only; renders nothing itself. To plan AND render a batch of finished static ads for one or several products in one call (up to 20 per product), use make_static_ads.

ParametersJSON Schema
NameRequiredDescriptionDefault
brandNobrand name or profile object; OMIT to use the workspace’s saved brand
countNohow many distinct variants (default 6)
productYeswhat to advertise
languageNooutput language for the variant copy (e.g. Spanish) — default English

TDQS

A4.3/5.0
Behavior4/5

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

Annotations already declare readOnly=false, openWorld=true, idempotent=false, destructive=false. The description adds the important boundary that this is a planning step only and renders nothing itself, which disambiguates the otherwise confusing mention of generate_image/score_ad. It does not cover whether the plan is persisted or whether downstream renders cost credits, so it stops short of full transparency.

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 action and output, then the workflow, then the exclusion. Two sentences plus a clause with little waste, though the generate_image/score_ad workflow mention adds slight density that then has to be walked back with 'renders nothing itself.'

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?

With no output schema, the description carries the return-value burden and does so by naming the per-angle artifacts (headline + visual brief). For a 4-param, low-complexity planning tool this is nearly complete; only the persistence/shape of the returned plan is left implicit.

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

Parameters3/5

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

Schema description coverage is 100%, so the schema already documents brand, count, product, and language. The description only echoes the 'N DISTINCT' notion of count and the brief concept; it adds no format or default guidance beyond what the schema provides. Baseline 3 is appropriate.

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 precise verb and output ('fan a brief into N DISTINCT ad angles ... each with its own headline + visual brief'), and explicitly names the siblings it works with (generate_image, score_ad) and the one it is not (make_static_ads). An agent can distinguish it from plan_ad, headline_variants, and make_static_ads without opening 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 Guidelines5/5

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

Explicitly routes the agent: 'To plan AND render a batch of finished static ads ... use make_static_ads', and clarifies its own boundary with 'LLM planning only; renders nothing itself.' Both the when-to-use and the when-to-use-something-else are stated.

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

post_editPost-production editAInspect

MECHANICAL post-production on an EXISTING video (URL): ordered primitives run by ffmpeg in seconds, ~2 credits flat, NO AI model, as a NEW video. Ops: a branded end card (adds its seconds), trim, speed (0.5-2x), mute (whole or a window), audio_gain (-20..+6 dB), fade_out, music (a bed UNDER the clip, its own sound kept and ducked under: omit track = a free library track picked by mood, no attribution; or track = an audio link: an upload_file URL, a find_sound link, a video post's sound; track 'generate' composes it, paid, ONLY when the user asks; start/end, db, replace), voiceover (text spoken OVER it, its sound ducked; ~2.5 words/s; voice female|male; +~1 credit), watermark (brand logo), grain (anti-AI), text (timed words in a native look: style 'tiktok-classic' default / 'clean-minimal' / 'note-style' or a textStyle; start/end), join (this video FOLLOWED BY clips[]: Library URLs, direct files or public post links, as one 1080x1920 video, loudness matched). Presets are shortcuts: text/watermark take any x/y, grain any amount, join any ffmpeg transition, a bridge any sound link. 'A viral hook, then our clip' = videoUrl: the hook's post link + [{op:'join', clips:[{url: ours}], bridge}], and it ALWAYS gets a bridge unless the user asks for a bare cut: {kind:'impact'} cuts the hook just before its payoff (found from the footage; cutAt overrides) and lands our clip on a punch-in and flash, with the payoff sound FROM THE HOOK ITSELF: its own audio carries across the cut, else one generated from its frames (up to 8 credits), else a neutral impact. {kind:'text', text, then?} only when the user asks for words over the cut. No voiceover bridge: have our clip's host say the connecting line. matchCut = where our clip starts. ANY OTHER EDIT, op or bridge (whip, zoom, freeze, wipe, split screen, generated shot) is edit_timeline: free keyframes, any size. NEVER generate_video/render_ad for these.

ParametersJSON Schema
NameRequiredDescriptionDefault
opsYesthe ordered edit plan (max 6 ops)
accentNooverride the brand accent hex
domainNooverride the brand website
dryRunNotrue = return the exact credits this edit reserves and run nothing
videoUrlYesthe video to edit: a render / Library URL, a direct file, or a public post link
brandNameNooverride the workspace brand name

TDQS

A4.7/5.0
Behavior5/5

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

Goes well beyond annotations (which only say non-readonly, non-destructive, open-world): it discloses that output is a NEW video, that cost is ~2 credits flat with specific surcharges (voiceover +~1, generated impact sound up to 8), that music ducks under the clip's own audio, and the full bridge/cut semantics. This is exactly the kind of context annotations cannot carry.

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 purpose, cost, and op list before the complex bridge rules, and for a 12-op tool with nested bridge/clip objects most sentences carry genuine routing information. However, the heavy ALL-CAPS and comma-spliced clauses make it dense and hard to parse, costing some readability.

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?

Covers cost, safety behavior, alternatives, and brand overrides thoroughly, which is strong for a complex tool. With no output schema, however, it never describes the return value or job/status shape an agent would receive, leaving that one gap.

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 baseline is 3, but the description adds real semantic value the schema lacks — the 'join' hook pattern, bridge auto-insertion rules, matchCut meaning, and preset shortcuts ('text/watermark take any x/y, grain any amount'). It clarifies how ops interact rather than merely restating field names.

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 precise verb and resource — 'MECHANICAL post-production on an EXISTING video (URL)' — and explicitly distinguishes itself from edit_timeline, generate_video, and render_ad by name. An agent can route between these siblings without opening 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 Guidelines5/5

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

Explicit when-to-use and when-not: 'ANY OTHER EDIT, op or bridge (whip, zoom, freeze...) is edit_timeline' and 'NEVER generate_video/render_ad for these.' It also gates sub-features by user intent (track 'generate' only when the user asks; text bridge only when words are requested).

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

post_performanceWhich hooks and subjects are getting tractionA
Read-onlyIdempotent
Inspect

Aggregate this brand's published posts to answer WHICH HOOKS AND SUBJECTS WORK. Groups by hook (default), subject, format (recipe), channel, media format or posting hour, reports the engagement RATE within each channel, and ranks the best and worst POSTS in each channel. Describe a post by the creative it carried (what it shows, its format, its link), not by its caption — the caption is the least important part of a post. THREE THINGS IT DELIBERATELY WILL NOT DO, and you should repeat them rather than paper over them: (1) it never sums metrics across channels — a LinkedIn impression and a TikTok view are different units, so every comparison is within one channel; (2) it SUPPRESSES a verdict below 5 measured posts and says so, because a confident recommendation from 3 posts is worse than none; (3) a post with no recorded hook (published outside Hermoso, or backfilled without a creation match) counts toward channel and format totals but never votes on which hook works. Present the finding verbatim if there is one, and the reason if there is not. Read-only, 0 credits.

ParametersJSON Schema
NameRequiredDescriptionDefault
axisNowhat to group by — default hook; recipe = the format of the creative
daysNolook back N days (1-730) over the whole history; omit for the recent posts only
brandNoWHICH PROFILE the post lives in — id or exact name from list_brands (a shared workspace: its profile id). Needed when it was published in a profile this connection is not pinned to; applies to THIS CALL ONLY. A name that matches no profile, or two, is REFUSED and nothing is done.
channelNorestrict to one channel

TDQS

A4.4/5.0
Behavior5/5

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

Beyond the annotations (readOnly/idempotent/non-destructive), it discloses rich non-obvious behavior: never summing metrics across channels, suppressing verdicts below 5 measured posts, treating hookless posts as non-voting, presenting the `finding` verbatim, and being read-only at 0 credits. These are exactly the constraints an agent would otherwise get wrong.

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 the length is earned: the outcome leads, the three deliberate non-goals are enumerated, and each sentence carries behavioral weight. Slightly dense, but nothing reads as filler or repetition of the name.

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 compensates by stating what is reported (engagement rate per channel, ranked best/worst posts, verbatim `finding`) and enumerating the three suppression/aggregation rules. An agent has enough to call it correctly and interpret results without further discovery.

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

Parameters3/5

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

Schema description coverage is 100%, so the schema already documents axis, days, brand, and channel (including the default and the 'recipe = format' mapping). The description restates the axis dimensions and adds marginal framing, but no syntax or semantics the schema lacks; baseline 3 is appropriate.

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+outcome: 'Aggregate this brand's published posts to answer WHICH HOOKS AND SUBJECTS WORK,' then names the exact grouping dimensions and the ranking output. This clearly separates it from siblings like collect_post_metrics (raw collection) or list_published_posts (listing), since the defining behavior is aggregation by axis with best/worst ranking.

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 usable guidance on how to reason about a post ('by the creative it carried, not by its caption') and implicitly scopes itself to hook/subject analysis via the axis enum. It does not, however, explicitly state when to choose this over sibling analytics tools such as diagnose_posts or search_posts, so routing is left partly to inference.

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

post_to_blueskyPost to BlueskyA
Destructive
Inspect

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.

ParametersJSON 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

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.

post_to_google_businessPost to Google Business ProfileA
Destructive
Inspect

Publish a Post to the brand’s Google Business Profile — the panel that appears on Google Search and Maps for the business. Text, optionally ONE PHOTO, and a call-to-action button. Google’s Posts API accepts NO VIDEO, so pass a still image. This PUBLISHES immediately and publicly on the business listing — show the user the exact text, photo and button and get an explicit yes BEFORE calling. If the account manages several listings, call list_business_locations first and pass locationId. EVENT and OFFER posts both REQUIRE a title and a start date (Google’s rule). On an OFFER, Google IGNORES the button’s link — pass redeemOnlineUrl instead. A CALL button dials the number on the listing and takes no link. Needs Google Business Profile connected (Settings > Connectors > Google Business Profile).

ParametersJSON Schema
NameRequiredDescriptionDefault
hookNoWHAT ANGLE this post is built on, recorded only at publish time. post_performance ranks hooks on it (a winner needs 5 posts sharing ONE hook), so pass a list_hooks id (e.g. "direct_callout", "before_after") or reuse your own wording EXACTLY across a campaign. Omit it and this post never votes on which hook works.
linkNothe URL the button opens — not for CALL, and ignored on an OFFER
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.
titleNoheadline — REQUIRED for EVENT and OFFER
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
endDateNoYYYY-MM-DD, defaults to startDate
subjectNoWHAT THIS POST IS ABOUT — product, feature, offer or theme (e.g. "winter coat", "free trial"). post_performance's second grouping axis: reuse the exact wording, as with hook.
summaryNothe body text of the Post
imageUrlNoa Hermoso render image URL (or an upload_file url) to show on the Post
startDateNoYYYY-MM-DD — REQUIRED for EVENT and OFFER
topicTypeNodefault STANDARD
actionTypeNothe button on the Post
couponCodeNoOFFER only
locationIdNowhich listing, e.g. 'locations/123' from list_business_locations — only needed when the account manages more than one
languageCodeNoBCP-47 language of the Post, default 'en'
redeemOnlineUrlNoOFFER only — this is the link Google actually uses on an offer
termsConditionsNoOFFER only

TDQS

A4.6/5.0
Behavior5/5

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

Annotations already declare destructiveHint=true, idempotentHint=false, openWorldHint=true, but the description goes well beyond them: it says the post publishes 'immediately and publicly on the business listing,' spells the required auth/connection, and discloses per-topic behavioral quirks (EVENT/OFFER require title+startDate; OFFER ignores the button link; CALL dials the listing number and takes no link). That is meaningful context the structured fields cannot carry.

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?

Dense but well front-loaded: the publish action and public/destructive consequence come first, then location, then per-topic-type rules. Nearly every sentence is an actionable constraint, though the run-on em-dash sentences and repeated EVENT/OFFER requirement cost it a perfect score.

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?

For a zero-required-param, non-idempotent, publicly-publishing mutation with no output schema, the description covers the operational essentials an agent needs — confirmation workflow, multi-location disambiguation, per-topicalType requirements, CTA/link interactions, and the connection prerequisite — leaving no obvious gap that would cause a bad call.

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

Parameters3/5

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

With 18 parameters at 100% schema description coverage, the schema already documents each field in depth (hook/recipe/subject grouping semantics, imageUrl sources, enum defaults). The description largely restates those rules (OFFER-only redeemOnlineUrl, CALL has no link) rather than adding net-new per-parameter syntax, so the baseline 3 applies despite a couple of cross-field conditions it clarifies.

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 (publish) and resource (Post to the brand's Google Business Profile), and immediately defines what that surface is ('the panel that appears on Google Search and Maps'). The media envelope (text, ONE PHOTO, no video) and CTA button are named up front, so an agent can distinguish this from post_to_linkedin/post_to_meta/post_to_x siblings 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?

Explicitly gates the call: 'show the user the exact text, photo and button and get an explicit yes BEFORE calling,' and 'If the account manages several listings, call list_business_locations first and pass locationId.' It also names the prerequisite connection path (Settings > Connectors > Google Business Profile), leaving nothing to inference.

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

post_to_linkedinPublish to LinkedInA
Destructive
Inspect

Publish a post to the user’s connected LinkedIn profile — text, and optionally an image (pass its served URL as imageUrl). The image does NOT have to be something Hermoso generated: LinkedIn is served the bytes from us, so the URL must be Hermoso-hosted, and upload_file turns ANY file the user already has into exactly that. This PUBLISHES immediately and PUBLICLY — ALWAYS show the user the exact text and get an explicit yes BEFORE calling. Needs a connected LinkedIn account (Settings > Connectors > LinkedIn).

ParametersJSON Schema
NameRequiredDescriptionDefault
hookNoWHAT ANGLE this post is built on, recorded only at publish time. post_performance ranks hooks on it (a winner needs 5 posts sharing ONE hook), so pass a list_hooks id (e.g. "direct_callout", "before_after") or reuse your own wording EXACTLY across a campaign. Omit it and this post never votes on which hook works.
textYesthe post text
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.
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
subjectNoWHAT THIS POST IS ABOUT — product, feature, offer or theme (e.g. "winter coat", "free trial"). post_performance's second grouping axis: reuse the exact wording, as with hook.
imageUrlNoa Hermoso-hosted image URL to attach (≤12MB) — a Hermoso render, or ANY file of the user’s own put through upload_file first. An arbitrary external host is refused (we fetch the bytes ourselves).
imageUrlsNoA CAROUSEL IS NOT AVAILABLE ON A PERSONAL PROFILE — LinkedIn's organic multi-image post publishes from a COMPANY PAGE. Passing several here is refused by name rather than posting slide 1; use post_to_linkedin_page instead.
visibilityNodefault PUBLIC
allowDuplicateNopost it even though an identical post was just made or attempted. Only pass this when the user genuinely wants the same thing posted twice, or when you have LOOKED at the account and confirmed a timed-out attempt did not land.
idempotencyKeyNoSAFE RETRIES — any stable string: a repeat of the SAME publish within 24h returns the ORIGINAL post id instead of posting again (an identical publish is also auto-recognised for 10 minutes). On a timeout or an ambiguous error, CALL AGAIN WITH THE SAME KEY: it reports the original post or publishes it once. It never posts twice.

TDQS

A4.3/5.0
Behavior4/5

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

Annotations mark it non-read-only, destructive, and open-world, and the description adds meaningful context beyond that: the post is immediate and PUBLIC by default, so explicit user confirmation is required first. It also surfaces the connection requirement (Settings > Connectors > LinkedIn). It doesn't detail return values or failure modes, but that's largely covered elsewhere.

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 action and scope, then the critical confirmation and auth constraints. Dense but every sentence serves a purpose, though the imageUrl/upload_file explanation is somewhat verbose and partially duplicated by the schema.

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 an 11-parameter mutation tool with no output schema, it covers the key operational context: public/immediate behavior, confirmation workflow, account prerequisite, and the carousel exclusion. Idempotency and duplicate handling are thoroughly handled in the schema descriptions, so the top-level description is essentially complete.

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

Parameters3/5

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

Schema description coverage is 100%, so the schema already documents all 11 parameters in depth. The description adds little parameter meaning beyond restating that imageUrl must be a Hermoso-hosted URL and pointing to upload_file, which the schema also states. Baseline 3 is appropriate when the schema carries the load.

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 (Publish) and resource (post to user's connected LinkedIn profile), including the text-only vs. text+image scope. It implicitly distinguishes itself from post_to_linkedin_page, which is named in the imageUrls parameter for the multi-image/company-page case.

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?

Explicitly mandates showing the user the exact text and getting an explicit yes before calling, states the LinkedIn account prerequisite, and routes multi-image carousel cases to post_to_linkedin_page. It also names upload_file as the required prior step for non-generated images.

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

post_to_linkedin_pagePublish to a LinkedIn company PageA
Destructive
Inspect

Publish a post to one of the user’s LinkedIn COMPANY PAGES — text, plus optionally an image, a video, a 2–20 image CAROUSEL (LinkedIn calls it a MultiImage post; pass the slides in order as imageUrls[]), or a LINK POST with a real preview card (linkUrl). USE linkUrl WHENEVER THE POINT OF THE POST IS A LINK: LinkedIn disables URL scraping for API partners, so a url sitting in the text renders as plain text with no card, and the card’s title, description and image only exist if you pass linkTitle / linkDescription / linkThumbnailUrl — read them off the page and supply them. The media need not be a Hermoso render — it must be Hermoso-HOSTED because we upload the bytes to LinkedIn ourselves, and upload_file turns ANY file the user already has into such a URL. ORGANIC CAROUSELS ARE COMPANY-PAGE ONLY — a personal profile cannot publish one and is refused by name, so send a deck here rather than to post_to_linkedin. This is a DIFFERENT thing from post_to_linkedin, which publishes to the person’s own profile: pick the one the user actually asked for and never substitute. organizationId comes from list_linkedin_pages; omit it only when the account administers exactly one Page. This PUBLISHES immediately and PUBLICLY — ALWAYS show the user the exact text and get an explicit yes BEFORE calling. A VIDEO POST CAN CARRY CAPTIONS AND ITS OWN COVER, and both are attached only during the upload: pass captionsSrt (SubRip content — LinkedIn is watched with the sound off) and videoThumbnailUrl (otherwise LinkedIn picks a frame for you). LinkedIn does NOT allow the image, video, captions or thumbnail of a published post to be swapped afterwards, so get all of that right first (the copy can still be edited with manage_linkedin_post).

ParametersJSON Schema
NameRequiredDescriptionDefault
hookNoWHAT ANGLE this post is built on, recorded only at publish time. post_performance ranks hooks on it (a winner needs 5 posts sharing ONE hook), so pass a list_hooks id (e.g. "direct_callout", "before_after") or reuse your own wording EXACTLY across a campaign. Omit it and this post never votes on which hook works.
textYesthe post text
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.
titleNovideo title
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
altTextNoaccessibility alt text (max 4086 characters, ~120 recommended). A STRING describes every image; an ARRAY describes each slide of a multi-image post separately, in slide order — LinkedIn stores altText per image, and their own sample request carries a different one on each. Not available on a PERSONAL-profile post: LinkedIn’s member posting API has no alt-text field at all.
linkUrlNopublish a LINK POST — LinkedIn renders a real preview card for this URL instead of leaving a bare link in the text. Mutually exclusive with imageUrl / videoUrl / imageUrls: LinkedIn’s content field is a union, so combining them is refused by name rather than one being dropped.
subjectNoWHAT THIS POST IS ABOUT — product, feature, offer or theme (e.g. "winter coat", "free trial"). post_performance's second grouping axis: reuse the exact wording, as with hook.
imageUrlNoa Hermoso-hosted image URL — a render (list_library), or ANY image of the user’s own passed through upload_file first. An arbitrary external host is refused.
videoUrlNoa Hermoso-hosted video URL — a render, or the user’s own footage via upload_file. LinkedIn processes it before publishing, which takes a minute.
coverAtMsNoTHE VIDEO COVER of a videoUrl post, as ONE frame: milliseconds from the start (7000 = the frame at 7s). That frame is uploaded as LinkedIn’s video thumbnail (only possible while the video uploads). videoThumbnailUrl wins.
imageUrlsNoCAROUSEL — an ORDERED list of image (and, where the channel allows, video) URLs published as ONE post the viewer swipes through. THIS IS NOT “post several” — it is a single post with several slides, which is what a multi-slide creative (a listicle, a “1/6 · SWIPE” deck) actually needs; publishing only its first slide tells the viewer to swipe at something that cannot. The ORDER is the product. Limits per channel: Instagram 2–10 (images, videos or a mix), Threads 2–20 (mix allowed), Facebook 2+ (Meta publishes no documented maximum; Hermoso caps the upload fan-out at 30 and says so), LinkedIn company Pages 2–20 (images only), Pinterest 2–5 (images only), TikTok up to 35. One url here is simply an ordinary single post. Anything a channel cannot do is REFUSED with the real reason — nothing is ever quietly downgraded to one slide.
linkTitleNothe headline ON the preview card. LINKEDIN NEVER SCRAPES THE PAGE — their Posts API disables URL scraping for API partners outright — so if you do not pass this the card renders UNLABELLED. Fetch the page’s own title and pass it.
visibilityNodefault PUBLIC
captionsSrtNoCLOSED CAPTIONS for a videoUrl post — the SubRip (.srt) CONTENT itself, cue numbers and `00:00:00,000 --> 00:00:02,000` timing lines included, NOT a URL and NOT the plain script (a file with no timings is refused, because LinkedIn would accept it and then silently never show it). Most of LinkedIn is watched with the sound off, so an uncaptioned video is one most of the feed never hears. LinkedIn allows ONE caption file per video and ENGLISH ONLY; it can be attached only WHILE the video is uploaded, never added to a published post; and it is processed asynchronously, so the reply confirms it was UPLOADED and never that it is visible yet. Requires videoUrl — passing it on an image, carousel or link post is refused by name.
platformCoverNoVIDEO COVER. Omit it (the default) and Hermoso sets the video’s best frame — the same frame as its Library thumbnail — as the cover (LinkedIn’s video thumbnail upload). true = send no cover and let the platform pick (usually the first frame). A cover you pass yourself always wins.
allowDuplicateNopost it even though an identical post was just made or attempted. Only pass this when the user genuinely wants the same thing posted twice, or when you have LOOKED at the account and confirmed a timed-out attempt did not land.
idempotencyKeyNoSAFE RETRIES — any stable string: a repeat of the SAME publish within 24h returns the ORIGINAL post id instead of posting again (an identical publish is also auto-recognised for 10 minutes). On a timeout or an ambiguous error, CALL AGAIN WITH THE SAME KEY: it reports the original post or publishes it once. It never posts twice.
organizationIdNonumeric Page id from list_linkedin_pages
targetAudienceNoLINKEDIN COMPANY PAGE POST ONLY — show the post only to Page followers matching these facets (URNs or bare numeric ids; search_linkedin_ads_targeting finds them). LinkedIn requires the matching audience to be over 300 followers and refuses a smaller one. Personal-profile posts cannot be targeted.
linkDescriptionNothe sub-line on the preview card. Same rule as linkTitle: absent means blank, because LinkedIn will not fetch it.
linkThumbnailUrlNoa Hermoso-hosted image used as the card’s picture (uploaded to LinkedIn for you). Without it the card has no image.
videoThumbnailUrlNothe COVER IMAGE for a videoUrl post — a Hermoso-hosted image (a render, or any picture of the user’s via upload_file). Without it LinkedIn adds a system-generated thumbnail, which on an ad is usually whatever the first frame happens to be. Like captions this can only be set WHILE the video is uploaded, never afterwards. Requires videoUrl. This is NOT linkThumbnailUrl, which is the picture on a link-preview card.

TDQS

A4.6/5.0
Behavior5/5

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

Annotations already declare destructive=true and openWorld=true, and the description adds crucial behavior: the post publishes immediately and publicly, media cannot be swapped after publishing, captions and thumbnails must be attached during upload, and idempotencyKey provides safe retries. It also discloses LinkedIn-specific constraints such as English-only captions and audience size requirements. The description is highly transparent and consistent with 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?

The description is long, but the tool has 24 parameters, multiple media types, and destructive public-publish behavior, so much of the length is justified. It is front-loaded with the core purpose, though the single-paragraph format, heavy all-caps emphasis, and minor repetition keep it from being maximally concise.

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 the tool's complexity, annotations, and lack of an output schema, the description covers the essential operational context: approval flow, publishing consequences, media limitations, idempotency behavior, organizationId sourcing, and targeting constraints. Nothing critical for correct invocation appears to be missing.

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

Parameters3/5

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

Schema coverage is 100%, and the schema descriptions are themselves extremely detailed, so the description's parameter-level content is largely redundant. It does add a few meaningful cross-parameter rules, such as omitting organizationId only when the account administers exactly one Page, but most parameter semantics already live in the schema. Baseline 3 is appropriate.

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 and resource: publishing a post to one of the user's LinkedIn COMPANY PAGES. It explicitly distinguishes this from post_to_linkedin, which targets a personal profile, and names the post types it supports. An agent can identify the tool's purpose without opening the 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?

The description gives explicit routing rules: use this for company Pages and post_to_linkedin for personal profiles, send carousels here because personal profiles cannot publish them, and use linkUrl whenever the point is a link. It also states prerequisites such as getting organizationId from list_linkedin_pages and showing the exact post for approval before calling. These are detailed when-to-use and when-not-to-use guidelines.

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

post_to_metaPost to Facebook, Instagram or ThreadsA
Destructive
Inspect

Publish to a connected Facebook Page, its linked Instagram, OR the brand’s Threads account — text/link/image/VIDEO/CAROUSEL. A MULTI-SLIDE creative is a CAROUSEL, not several posts: pass the slides in order as imageUrls[] and they publish as ONE swipeable post (Instagram album, Threads carousel, Facebook multi-photo post). Never publish slide 1 of a deck on its own — the creative tells the viewer to swipe. target:"facebook" (default) posts to the Page; target:"instagram" publishes a photo or Reel to the linked IG business account (needs an image or video); target:"threads" posts to the connected Threads account (text, image, or video). Works with ANY media — a finished Hermoso ad OR an arbitrary user file: imageUrl/videoUrl accept a public https URL, a data: URI, or a Hermoso /generated path; for a LOCAL file (e.g. on the user’s desktop) call upload_file first and pass the url it returns. INSTAGRAM COLLAB: pass collaborators (up to 3 usernames) to invite other accounts to CO-AUTHOR the post — it then shows on their profile too once they accept, which is the reach play behind every creator partnership. This PUBLISHES immediately — confirm the copy + media with the user first. Needs a connected Meta account (Settings > Connectors > Meta) with posting permission; Threads needs its own connection.

ParametersJSON Schema
NameRequiredDescriptionDefault
hookNoWHAT ANGLE this post is built on, recorded only at publish time. post_performance ranks hooks on it (a winner needs 5 posts sharing ONE hook), so pass a list_hooks id (e.g. "direct_callout", "before_after") or reuse your own wording EXACTLY across a campaign. Omit it and this post never votes on which hook works.
linkNoa URL to attach (FB text post only)
asyncNopublish in the BACKGROUND and return a job id to poll with get_job, instead of waiting. USE THIS FOR VIDEO: a Facebook or Instagram video publish routinely outlives an agent transport, and a timeout on the synchronous path leaves you unable to tell whether the post is live. With async:true nothing can time out — the job reports the post id and url when it lands.
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.
placeNoFACEBOOK — tag a location on the Page post. The NUMERIC ID OF THE FACEBOOK PAGE for that place, not a place name and not coordinates.
storyNoINSTAGRAM STORY — publish this as a 24-hour Story instead of a feed post. One image OR one video: Instagram has no carousel story, so a carousel is REFUSED BY NAME rather than quietly posted to the feed. A Story carries NO CAPTION (there is nowhere to show one), no collaborators, no product tags and no trialReel — passing any of those is refused by name and nothing is posted, because a story that silently drops the words is worse than one that never went. INSTAGRAM ONLY: a Facebook Page story is a different upload and is not built, so a Facebook channel with `story` set is refused rather than published to the feed.
ideaIdNoshort id of the content-plan idea this post came from
pageIdNotarget Page id (from list_meta_pages); omit = first Page
recipeNothe post's FORMAT id, e.g. "slideshow" or "imessage_chat" — post_performance groups by it, so reuse one id per format
targetNodefault facebook; instagram -> the Page’s linked IG; threads -> the brand’s connected Threads account
accountNoWHICH Instagram account when target is instagram and the brand has several — Page-linked and Instagram Login accounts alike; an @username or id from list_connector_accounts("instagram"). Several and none named is refused by name; omit when there is one.
altTextNoACCESSIBILITY — the description screen readers announce, and what the platform otherwise auto-generates badly or not at all. Describe what is actually IN the picture, never the caption. ONE STRING describes the picture; on a CAROUSEL it describes EVERY slide. Pass an ARRAY of strings instead to describe each slide separately, aligned to the slide order — that is strictly better on a multi-slide post, because one sentence read out over six different pictures is wrong for five of them. More descriptions than pictures is refused rather than dropped. WHERE IT LANDS, per Meta’s own docs: INSTAGRAM image posts and the IMAGE slides of an Instagram carousel (up to 1000 characters; dropped rather than sent on a Reel or a video slide, which Meta do not support); FACEBOOK Page photos including every photo of an album, via alt_text_custom (Meta publish no length for it, so nothing is truncated); THREADS on a single-image or single-video post ONLY — Meta document no way to attach alt text to a Threads CAROUSEL slide, so a Threads carousel publishes undescribed and the reply says so rather than risking the whole post on a guess. (Meta’s AI disclosure defaults from PROVENANCE: a Hermoso render is declared is_ai_generated; media that came through upload_file or an external URL, i.e. the user’s own photos or footage, is NOT. Pass `aiGenerated` to force it either way.)
audioIdNoINSTAGRAM REEL — put one of Instagram’s OWN licensed music tracks under the Reel: the `id` search_instagram_audio returns. Instagram mixes it in when the Reel is published; nothing is downloaded or re-rendered. REELS ONLY, and only on an Instagram account connected through Meta (a Facebook Page with a linked Instagram), which is Instagram’s own rule — the Instagram connector cannot take it and is refused by name.
messageNopost text / caption
subjectNoWHAT THIS POST IS ABOUT — product, feature, offer or theme (e.g. "winter coat", "free trial"). post_performance's second grouping axis: reuse the exact wording, as with hook.
audienceNoFACEBOOK PAGE POST ONLY — limit who can see this organic post to people in these places and/or over this age (Facebook allows 13, 15, 18, 21 or 25). Not on Instagram, Threads or a Facebook Reel (a vertical clip becomes a Reel): those are refused. Region and city keys come from the Meta ads location search.
coverUrlNoINSTAGRAM REEL COVER — a public image url Instagram fetches and uses as the cover in the Reels tab. REELS ONLY, and the alternative to `thumbOffset`: passing both is refused, since they are two answers to the same question. Run a local file through upload_file first.
imageUrlNopublic https URL, a data: URI, or a Hermoso /generated path (upload_file gives you one for a local file)
linkNameNoFACEBOOK — override the HEADLINE of the link preview. Without it the post shows whatever Open Graph title the destination carries, which on a bare landing page is often nothing. Needs `link`. MEASURED 2026-09-17: Facebook only honours these when the Page’s business has VERIFIED the domain (Business Manager > Brand safety > Domains) — without that Meta refuses the post outright with “Only owners of the URL…”, so leave them out unless the brand owns and has verified that link’s domain.
topicTagNoTHREADS ONLY — one topic tag for discovery, 1–50 characters. A leading # is stripped for you; Threads refuses "." and "&".
videoUrlNopublic https URL, data: URI, or /generated path — FB video post / IG Reel
audioNameNoINSTAGRAM REEL — the name of the Reel’s audio track, which is what viewers tap through to. REELS ONLY.
coverAtMsNoTHE VIDEO COVER for every target of this post, as ONE frame: milliseconds from the start (7000 = the frame at 7s). Instagram gets it as thumb_offset, Facebook as its uploaded cover (a Reel’s preferred thumbnail). Instagram’s own thumbOffset, or a Hermoso-hosted coverUrl, also becomes the Facebook cover.
imageUrlsNoCAROUSEL — an ORDERED list of image (and, where the channel allows, video) URLs published as ONE post the viewer swipes through. THIS IS NOT “post several” — it is a single post with several slides, which is what a multi-slide creative (a listicle, a “1/6 · SWIPE” deck) actually needs; publishing only its first slide tells the viewer to swipe at something that cannot. The ORDER is the product. Limits per channel: Instagram 2–10 (images, videos or a mix), Threads 2–20 (mix allowed), Facebook 2+ (Meta publishes no documented maximum; Hermoso caps the upload fan-out at 30 and says so), LinkedIn company Pages 2–20 (images only), Pinterest 2–5 (images only), TikTok up to 35. One url here is simply an ordinary single post. Anything a channel cannot do is REFUSED with the real reason — nothing is ever quietly downgraded to one slide.
trialReelNoINSTAGRAM TRIAL REEL — publish this Reel to NON-FOLLOWERS ONLY at first (Instagram allows trials only on accounts above its follower threshold — about 1,000 followers; an ineligible account is refused by name and nothing is posted), so a hook can be tested on a cold audience without spending it on the people who already follow the brand. Instagram then shows it to followers only if it graduates. MANUAL = the creator graduates it by hand in the Instagram app; SS_PERFORMANCE = Instagram graduates it automatically if it performs well. REELS ONLY and INSTAGRAM ONLY: an image, a carousel, a Facebook post or a Threads post is REFUSED BY NAME rather than quietly published as an ordinary post — a trial that silently goes to every follower is the exact opposite of what was asked for. Omit it for a normal Reel.
locationIdNoTAG A PLACE. THREADS: a place id from search_threads_locations. INSTAGRAM: the NUMERIC ID OF A FACEBOOK PAGE associated with that location, not a place name and not coordinates; a non-numeric value is refused rather than sent.
scheduleAtNoFACEBOOK ONLY — schedule instead of posting now. ISO timestamp (2026-08-01T09:00:00Z) or unix seconds; must be 10 minutes to 30 days ahead. Facebook holds the post and publishes it at that time, so nothing has to stay running on our side. Instagram and Threads have NO scheduling in Meta’s API — passing this for them is refused rather than silently posted immediately.
aiGeneratedNoINSTAGRAM / FACEBOOK REEL — Meta’s is_ai_generated self-disclosure. OMIT IT and Hermoso decides from provenance: a Hermoso render is declared, media that came through upload_file or from an external URL (the user’s own photographs or footage) is NOT — a real photo must never carry Instagram’s “AI info” label. Pass true or false only to override: false strips the label from something Hermoso would otherwise declare, true declares a render the user uploaded themselves.
audioVolumeNoINSTAGRAM REEL — how loud that track plays, 0–100 (Instagram’s default 100). Needs audioId.
linkPictureNoFACEBOOK — override the IMAGE of the link preview: a public http(s) url Facebook fetches itself. Needs `link`. MEASURED 2026-09-17: Facebook only honours these when the Page’s business has VERIFIED the domain (Business Manager > Brand safety > Domains) — without that Meta refuses the post outright with “Only owners of the URL…”, so leave them out unless the brand owns and has verified that link’s domain.
productTagsNoINSTAGRAM SHOPPING — make the post SHOPPABLE by tagging products from the brand’s own catalog; tapping a tag opens the product’s price sheet inside Instagram. Instagram only. ON A PHOTO each tag is {product_id, x, y}, where x and y are FRACTIONS of the image from 0.0 (left/top) to 1.0 (right/bottom) — 0.5,0.5 is the middle — and BOTH are required, max 20. ON A REEL it is {product_id} ALONE with no coordinates, max 30. ON A CAROUSEL it is an array PER SLIDE ([[{…}], [], [{…}]]) because Instagram tags each slide’s own container, max 5 per slide and 20 across the post. Ids come from search_instagram_shopping_products; call list_instagram_shopping_catalogs FIRST, because tagging needs an APPROVED Instagram Shop and without one this fails after the media is already uploaded. A tag whose product is not “approved” is stored and shown to nobody.
quotePostIdNoTHREADS ONLY — the id of a post to QUOTE. A quote post is a distinct post type, not a formatting option.
shareToFeedNoINSTAGRAM REEL — true puts the Reel in the Feed grid as well as the Reels tab. REELS ONLY. Left unset it follows Instagram’s own default; Hermoso does not flip it either way on the user’s behalf.
thumbOffsetNoINSTAGRAM REEL COVER, the other way — which frame becomes the cover, in MILLISECONDS from the start of the video. REELS ONLY. Use it instead of `coverUrl` when the right cover is already a frame of the clip.
videoVolumeNoINSTAGRAM REEL — how loud the video’s own sound plays under the track, 0–100 (Instagram’s default 100; 0 = the track alone). Needs audioId.
callToActionNoFACEBOOK — a real call-to-action BUTTON on the Page post. The types that open a destination (SHOP_NOW, LEARN_MORE, SIGN_UP, BUY_NOW, DOWNLOAD, OPEN_LINK, WATCH_MORE, BOOK_TRAVEL) need somewhere to go: the post’s own `link`, or `callToActionLink`. CALL_NOW, MESSAGE_PAGE, LIKE_PAGE and GET_DIRECTIONS act on the Page itself and take none.
countryCodesNoTHREADS ONLY — restrict the post to these ISO 3166-1 alpha-2 countries. Warning: This is an ALLOWLIST, so the post becomes INVISIBLE in every country not named — it narrows reach, it does not target.
replyControlNoTHREADS ONLY — who may reply. Default is everyone.
collaboratorsNoINSTAGRAM COLLAB — up to 3 Instagram usernames invited to CO-AUTHOR this post. Once one accepts, the post appears on THEIR profile too, with both handles in the header and the likes and comments shared — it is how a brand reaches a creator’s audience without paying for placement, and it is the single most-asked thing a scheduler normally cannot do. Pass handles only ("hermosoai"), not profile links; a leading @ is fine. INSTAGRAM ONLY — Facebook and Threads have no collab post at all and are refused BY NAME rather than silently dropping the co-authors — and never on a Story. AN INVITE IS NOT A CO-POST: publishing SENDS a request the other account must accept in their Instagram notifications, and until they do the post is on this brand’s profile ALONE; they may also decline, and Instagram sends no notification either way. So never report the post as live on both accounts — read the invite status back out of the reply, and use instagram_collaborators later to find out whether they accepted.
instagramPollNoINSTAGRAM — attach a POLL: 2 to 4 answers of 1–25 characters each, and the caption IS the question (so a caption is required). Feed photos, carousels and Reels only (never a story), one caption add-on per post (not with instagramCommentPrompt), and only on an account connected through Meta (a Facebook Page with a linked Instagram). WRITE-ONCE: Instagram cannot add, change or remove it after publishing, so show the user the answers first.
platformCoverNoVIDEO COVER. Omit it (the default) and Hermoso sets the video’s best frame — the same frame as its Library thumbnail — as the cover (Instagram Reel: thumb_offset; Facebook video/Reel: an uploaded cover image) — and on Threads, which has no cover setting, a blank first frame is replaced on a copy sent to Threads only. true = send no cover and let the platform pick (usually the first frame). A cover you pass yourself always wins.
allowDuplicateNopost it even though an identical post was just made or attempted. Only pass this when the user genuinely wants the same thing posted twice, or when you have LOOKED at the account and confirmed a timed-out attempt did not land.
idempotencyKeyNoSAFE RETRIES — any stable string: a repeat of the SAME publish within 24h returns the ORIGINAL post id instead of posting again (an identical publish is also auto-recognised for 10 minutes). On a timeout or an ambiguous error, CALL AGAIN WITH THE SAME KEY: it reports the original post or publishes it once. It never posts twice.
linkAttachmentNoTHREADS TEXT POSTS ONLY — a full http(s) URL rendered as a clickable link card. This is how a Threads post carries a destination at all; without it a link is just text. Meta allows at most 5 links per post and refuses a link attachment on a post that also carries an image or video, so Hermoso refuses that combination up front rather than letting Meta silently drop it.
linkDescriptionNoFACEBOOK — override the DESCRIPTION of the link preview. Needs `link`. MEASURED 2026-09-17: Facebook only honours these when the Page’s business has VERIFIED the domain (Business Manager > Brand safety > Domains) — without that Meta refuses the post outright with “Only owners of the URL…”, so leave them out unless the brand owns and has verified that link’s domain.
paidPartnershipNoINSTAGRAM — the PAID PARTNERSHIP label. A COMPLIANCE DECLARATION, the same kind Hermoso already carries for TikTok and X: set it whenever the post is sponsored, gifted or otherwise paid for. Opt-in and never inferred — it is the poster’s own statement about their commercial relationship.
callToActionLinkNoFACEBOOK — where the button goes, when that is not the post’s own `link`.
crossreshareToIgNoTHREADS ONLY — ALSO share this Threads post to the linked Instagram account AS A STORY (not a feed post), in the same publish. NOT available on a Threads CAROUSEL, which is refused by name rather than silently dropped. THERE IS NO CONFIRMATION: Threads returns no field saying whether the Story was created, so report it as REQUESTED and tell the user to check their Instagram Stories — never that it is live.
crossreshareDarkModeNoTHREADS ONLY — render that Instagram Story in dark mode. Only meaningful alongside crossreshareToIg; on its own it is refused rather than silently ignored, because a parameter that never reaches the wire must not look accepted.
instagramPollExtendedNoINSTAGRAM — give that poll Instagram’s extended voting duration instead of its default 3 days (Instagram’s changelog says it then stays open indefinitely). Only with instagramPoll.
instagramCommentPromptNoINSTAGRAM — make the caption a COMMENT PROMPT that people answer in the comments. Same limits as instagramPoll, and never together with it.
brandedContentSponsorIdsNoINSTAGRAM — the numeric Instagram USER IDS of the brands behind that label (at most 2, and ids rather than @handles). Naming sponsors IS asking for the label, so setting these with `paidPartnership:false` is refused instead of publishing brand credits with no disclosure.

TDQS

A4.6/5.0
Behavior5/5

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

Annotations cover destructive/openWorld/readOnly, and the description adds substantial context beyond them: it 'PUBLISHES immediately — confirm the copy + media with the user first,' states the Meta connector and posting-permission requirement (Threads needing its own), and explains refusal-by-name behavior instead of silent downgrades.

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

Conciseness5/5

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

Dense but front-loaded: the publish action, channels, and the carousel-vs-several-posts distinction come first, followed by media-source and connector prerequisites. For a 52-parameter tool, every sentence carries routing or behavioral weight.

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 52-param, no-output-schema tool spanning three platforms, the description covers destinations, media sourcing, async/timeout handling, and key constraints. It could say more about the reply shape (post id/url) and polling, though some parameter text hints at returning status.

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%, so the baseline is 3. The description still adds meaning: carousel slides go in imageUrls[] in order as one swipeable post, and collaborators invite co-authorship that appears on the other profile only after acceptance — nuance beyond the raw schema.

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+scope: 'Publish to a connected Facebook Page, its linked Instagram, OR the brand's Threads account — text/link/image/VIDEO/CAROUSEL.' It explicitly distinguishes the three destinations and clarifies that a multi-slide creative is ONE carousel post, not several, separating it from the sibling post_to_* family.

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 clear conditional routing: target values select the channel, local files require upload_file first, VIDEO should use async:true to avoid timeouts, and collaborators trigger a co-author invite. It stops short of naming non-Meta alternatives (schedule_post, post_to_linkedin) or stating when a different publishing tool is preferred.

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

post_to_pinterestCreate a PinA
Destructive
Inspect

Create a Pin on one of the user’s Pinterest boards from any finished visual they have — image, video, or a 2–5 slide CAROUSEL (pass the slides in order as imageUrls[] and Pinterest publishes one swipeable Pin). Title, description and destination link ride along. The link is what makes a Pin drive traffic, so ask for it rather than omitting it, and the description is the text Pinterest search actually reads. boardId is REQUIRED: call list_pinterest_boards first and let the user choose. This PUBLISHES to their public profile — confirm the board, title and link before calling. Video Pins take a minute or two while Pinterest ingests the file. Needs Pinterest connected (Settings > Connectors > Pinterest).

ParametersJSON Schema
NameRequiredDescriptionDefault
hookNoWHAT ANGLE this post is built on, recorded only at publish time. post_performance ranks hooks on it (a winner needs 5 posts sharing ONE hook), so pass a list_hooks id (e.g. "direct_callout", "before_after") or reuse your own wording EXACTLY across a campaign. Omit it and this post never votes on which hook works.
linkNodestination URL the Pin clicks through to
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.
titleNoPin title, max 100 characters
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 pinterest account connected (several and none named is refused by name, never guessed); omit when there is one.
altTextNoaccessibility alt text, max 500 characters. PIN-LEVEL: Pinterest’s API has no per-item alt text at all, so on a CAROUSEL the FIRST description is used for the whole Pin and the reply states that the others were not sent.
boardIdYesnumeric board id from list_pinterest_boards — the user picks it, never guess
subjectNoWHAT THIS POST IS ABOUT — product, feature, offer or theme (e.g. "winter coat", "free trial"). post_performance's second grouping axis: reuse the exact wording, as with hook.
imageUrlNoa Hermoso render image URL (or an upload_file url)
videoUrlNoa Hermoso render video URL — takes 1–2 minutes to ingest
coverAtMsNoTHE VIDEO COVER of a video Pin, as ONE frame: milliseconds from the start (7000 = the frame at 7s). That frame is cut and sent as the Pin cover image. coverImageUrl wins.
imageUrlsNoCAROUSEL — an ORDERED list of image (and, where the channel allows, video) URLs published as ONE post the viewer swipes through. THIS IS NOT “post several” — it is a single post with several slides, which is what a multi-slide creative (a listicle, a “1/6 · SWIPE” deck) actually needs; publishing only its first slide tells the viewer to swipe at something that cannot. The ORDER is the product. Limits per channel: Instagram 2–10 (images, videos or a mix), Threads 2–20 (mix allowed), Facebook 2+ (Meta publishes no documented maximum; Hermoso caps the upload fan-out at 30 and says so), LinkedIn company Pages 2–20 (images only), Pinterest 2–5 (images only), TikTok up to 35. One url here is simply an ordinary single post. Anything a channel cannot do is REFUSED with the real reason — nothing is ever quietly downgraded to one slide.
slideTextNoPINTEREST CAROUSEL ONLY — per-slide title, description and destination LINK, one object per slide in slide order. This is the ONLY genuine per-slide caption on any channel Hermoso publishes to: slide 4 can send people to the product ON slide 4, where every other platform gives a carousel one shared caption. Omit any field to leave it unset; the Pin’s own title/description/link still describe the Pin as a whole. Pinterest publishes no length limit on these, so nothing is truncated. More entries than slides is refused rather than dropped.
descriptionNoPin description, max 800 characters — this is what Pinterest search reads
coverImageUrlNovideo Pins only — a render to use as the cover frame
platformCoverNoVIDEO COVER. Omit it (the default) and Hermoso sets the video’s best frame — the same frame as its Library thumbnail — as the cover (on Pinterest the frame rides as the cover image). true = send no cover and let the platform pick (usually the first frame). A cover you pass yourself always wins.
allowDuplicateNopost it even though an identical post was just made or attempted. Only pass this when the user genuinely wants the same thing posted twice, or when you have LOOKED at the account and confirmed a timed-out attempt did not land.
boardSectionIdNooptional section within the board
idempotencyKeyNoSAFE RETRIES — any stable string: a repeat of the SAME publish within 24h returns the ORIGINAL post id instead of posting again (an identical publish is also auto-recognised for 10 minutes). On a timeout or an ambiguous error, CALL AGAIN WITH THE SAME KEY: it reports the original post or publishes it once. It never posts twice.

TDQS

A4.8/5.0
Behavior5/5

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

Annotations already declare destructive=true and openWorld=true, but the description adds real context beyond them: this PUBLISHES to the user's public profile, video Pins take 1–2 minutes to ingest, Pinterest must be connected, and a carousel is one swipeable Pin rather than several posts (with the API's pin-level alt-text limitation disclosed).

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 action and the carousel distinction, and every sentence carries operational information. It is dense and runs long as a single block, but nothing is pure 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?

For a 21-parameter mutation tool with no output schema, the description covers the critical decision points an agent needs: required boardId and its lookup, public-profile consequences, confirmation protocol, video latency, connector requirement, and idempotency-key retry behavior.

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 baseline is 3, but the description adds genuine framing beyond the schema: it explains imageUrls[] ordering as the product for carousels, elevates boardId to a required user-chosen value, and stresses that link and description are the traffic- and search-bearing fields.

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+resource (create a Pin on one of the user's Pinterest boards) and immediately differentiates scope by content type (image, video, or 2–5 slide carousel), which cleanly separates it from the many sibling post_to_* tools.

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 when-and-how: boardId is REQUIRED and the agent must call list_pinterest_boards first and let the user choose; it tells the agent to ask for the link rather than omit it, and to confirm board/title/link before publishing. It also names the connector prerequisite and the account/brand resolution path via siblings.

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

post_to_telegramPost to TelegramA
Destructive
Inspect

Publish to a Telegram channel, group or chat as the brand's own bot. WHICH CHAT IS ALWAYS REQUIRED AND IS NEVER GUESSED: pass chatId as the public channel's @username (e.g. @hermosoai) or its numeric id (a group is negative; a supergroup or channel starts with -100). There is no default and there cannot be one — the Telegram Bot API publishes no method that lists the chats a bot belongs to, so Hermoso genuinely cannot know them. list_telegram_chats reports the chats that have MESSAGED the bot in the last 24 hours, which is a shortcut for finding an id and is NOT a roster: a chat missing from it can still be posted to. TEXT: up to 4096 characters on a text-only message, but only 1024 the moment ANY photo or video is attached — a caption is not a message, and Hermoso refuses the over-long one before spending the round trip and says which budget applied. MEDIA: one image (imageUrl), one video (videoUrl), or an ALBUM of 2–10 (imageUrls, in order) in which photos and videos may be MIXED — pass a videoUrl alongside imageUrls and it joins the album as one more item. Hermoso uploads the bytes rather than handing Telegram a link, which is what buys the larger ceilings: 10MB per photo and 50MB per video, where a link would be 5MB and 20MB. THE BOT MUST BE IN THE CHAT — an administrator with Post Messages for a channel, an unrestricted member for a group; if it is not, Telegram refuses and the error says to add it rather than to reconnect. Returns the message id and, for a PUBLIC chat, its t.me link — a private group has no public web link, so url comes back null rather than as a link that would 404 for whoever you hand it to. 0 credits. Connect at Settings > Connectors > Telegram, or here with connect_connector, by pasting a bot token from @BotFather.

ParametersJSON Schema
NameRequiredDescriptionDefault
hookNothe post's angle — a list_hooks id or your own wording, reused exactly
textNothe message. ≤4096 characters on its own; ≤1024 once any image or video is attached.
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.
chatIdYesREQUIRED — the destination: a public channel's @username, or the numeric chat id. Never guessed; ask the user, or use list_telegram_chats.
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
silentNodeliver without a notification sound (Telegram’s disable_notification). This is NOT a visibility setting — the message is just as visible.
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 telegram account connected (several and none named is refused by name, never guessed); omit when there is one.
subjectNowhat the post is about
imageUrlNoone image (≤10MB after upload)
videoUrlNoone video (≤50MB). Passed alongside imageUrls it joins the album as one more item.
coverAtMsNoTHE VIDEO COVER in the chat, as ONE frame: milliseconds from the start (7000 = the frame at 7s). Sent as Telegram’s cover image; beats platformCover.
imageUrlsNoan ALBUM of 2–10 media, in order. Photos and videos may be mixed — Telegram allows it.
platformCoverNoVIDEO COVER. Omit it (the default) and Hermoso sets the video’s best frame — the same frame as its Library thumbnail — as the cover (Telegram’s in-chat video cover). true = send no cover and let the platform pick (usually the first frame). A cover you pass yourself always wins.
allowDuplicateNopost it even though an identical post was just made
disablePreviewNosuppress the link-preview card on a text-only post. Default is Telegram’s own behaviour (previews on).
idempotencyKeyNoany stable string: a repeat within 24h returns the original post instead of posting again

TDQS

A4.8/5.0
Behavior5/5

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

Despite annotations already covering safety (readOnlyHint=false, destructiveHint=true, openWorldHint=true), the description adds substantial context: the 4096/1024 character budget switch, the 10MB/50MB upload ceilings vs lower link limits, the bot-membership requirement and resulting error semantics, the returned message id and null url for private chats, and the 0-credit cost plus connect_connector setup path.

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?

Dense but well front-loaded: the never-guess chat rule precedes the media and setup details, and each section is capitalized as a scannable heading. It runs long and has a few redundant restatements (e.g. repeated explanations of why there is no default chat), which keeps it just short of 5.

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?

For a 17-parameter mutation tool with no output schema, the description fills the important gaps: it describes the return values, the failure modes (bot not in chat, over-long caption), the credit cost, and the prerequisite connection setup. Nothing essential is left to inference.

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 100% schema coverage the baseline is 3, but the description adds genuine meaning beyond the schema for chatId (public @username, numeric id, negative for groups, -100 for supergroups/channels) and for media (album of 2–10 mixed photos/videos, videoUrl joining an album). Most other parameter guidance duplicates the already-thorough schema.

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 up front: 'Publish to a Telegram channel, group or chat as the brand's own bot.' It is immediately distinguishable from the sibling post_to_linkedin/post_to_x/post_to_meta tools by naming the exact platform and the acting identity (the brand's own bot).

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?

Explicitly states that chatId is always required and never guessed, explains why no default is possible, and routes the agent to list_telegram_chats for discovery while warning it is not a roster. Adds a hard prerequisite: the bot must be in the chat with the right role before posting.

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

post_to_tiktokPost a video or photo post to TikTokA
Destructive
Inspect

Publish to the user’s connected TikTok account — a finished VIDEO, or a PHOTO POST (TikTok’s photo/slideshow format). A photo post carries 1 to 35 images and ONE image is simply a one-slide photo post, so there is nothing special to do for a single picture: pass imageUrls, in the order the slides should appear, and optionally coverIndex. Pass videoUrl for a video. Never pass both — TikTok has no mixed post. TWO destinations. destination:"post" (THE DEFAULT) publishes it LIVE on their profile: TikTok requires the user to CHOOSE the privacy themselves (no default is allowed), so call tiktok_creator_info, show them their real privacy options, and get their choice and an explicit yes before calling. destination:"draft" is ONLY for when the user asks for a draft, or wants to add a TikTok sound or trending audio (TikTok’s API takes no sound for a VIDEO): BEFORE sending, tell them plainly it lands in their TikTok inbox as a DRAFT, that they add the sound in TikTok’s editor, and that THEY must publish it from the TikTok app — nothing goes live until they do. Never pick draft on your own. A photo post published with destination:"post" gets a TikTok-recommended track automatically (autoAddMusic, default on) that they can change in the app. Pass Hermoso render URLs (or upload_file urls for local/external files). Needs TikTok connected (Settings > Connectors > TikTok).

ParametersJSON Schema
NameRequiredDescriptionDefault
hookNoWHAT ANGLE this post is built on, recorded only at publish time. post_performance ranks hooks on it (a winner needs 5 posts sharing ONE hook), so pass a list_hooks id (e.g. "direct_callout", "before_after") or reuse your own wording EXACTLY across a campaign. Omit it and this post never votes on which hook works.
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.
titleNothe caption — hashtags go here (video ≤2200 chars, photo post ≤4000)
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 tiktok account connected (several and none named is refused by name, never guessed); omit when there is one.
privacyNoREQUIRED for destination:"post", for photos and video alike. Must be one the creator actually allows — read them from tiktok_creator_info, never guess.
subjectNoWHAT THIS POST IS ABOUT — product, feature, offer or theme (e.g. "winter coat", "free trial"). post_performance's second grouping axis: reuse the exact wording, as with hook.
videoUrlNothe video to post — a Hermoso render URL or an upload_file url. Omit for a photo post.
imageUrlsNoa PHOTO POST: 1–35 image URLs in slide order. One url = a single-image photo post. Do not combine with videoUrl.
yourBrandNodiscloses that this promotes the creator’s own brand
coverIndexNophoto posts: which slide is the cover, 0-based. Default 0 (the first slide).
photoTitleNophoto posts only: a short title above the caption (≤90 chars). Defaults to the caption’s first line.
aiGeneratedNoTikTok’s is_aigc AI-generated-content label (video posts). OMIT IT and Hermoso decides from provenance: a Hermoso render is declared, a video that came through upload_file or an external URL (the user’s own footage) is NOT. true/false overrides.
destinationNo"post" (default) = live on the profile now (needs the privacy the user chose + their explicit yes); "draft" = to their TikTok inbox for them to finish and publish in the app — only when they ask for a draft or want to add a TikTok sound, and only after telling them so.
disableDuetNovideo only — TikTok has no duet on a photo post
autoAddMusicNophoto posts only: let TikTok add a recommended track (default true — a silent slideshow reads as broken)
disableStitchNovideo only — TikTok has no stitch on a photo post
platformCoverNoVIDEO COVER. Omit it (the default) and Hermoso sets the video’s best frame — the same frame as its Library thumbnail — as the cover (TikTok video_cover_timestamp_ms, on a direct post — a draft takes no cover, you pick it in the TikTok app). true = send no cover and let the platform pick (usually the first frame). A cover you pass yourself always wins.
brandedContentNodiscloses a paid partnership — cannot be combined with SELF_ONLY privacy
disableCommentNo
coverTimestampMsNovideo only: which frame to use as the cover, in ms

TDQS

A4.7/5.0
Behavior5/5

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

Annotations already declare a non-read-only, destructive, non-idempotent, open-world write, and the description adds substantial context on top: privacy must be user-chosen (no default allowed), drafts land in the TikTok inbox with nothing live until the user publishes in-app, autoAddMusic defaults on, cover behavior differs for direct post vs draft, and aiGenerated provenance rules. This is behavior beyond structured fields.

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?

The key distinction (video vs photo, post vs draft) is front-loaded and every sentence carries needed information. It is dense and heavy on all-caps emphasis, which borders on over-packed for a description, but nothing is filler.

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 22-parameter mutation tool with no output schema, the description covers prerequisites, the format split, the destination semantics, and the privacy gate thoroughly. It doesn't say what a successful response returns or how partial failures are handled, a minor gap given the annotations and schema carry the rest.

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 95%, so the baseline is 3, but the description adds cross-parameter meaning the schema alone doesn't convey: videoUrl and imageUrls are mutually exclusive ('Never pass both'), imageUrls ordering maps to slide order, coverIndex is 0-based, and autoAddMusic defaults on. Marginal but real added value.

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 (publish a video or photo post to TikTok), immediately splits the two formats, and is unambiguous against siblings like schedule_post or tiktok_post_status. An agent can tell exactly what this does without opening the 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 rules for when to use each destination: destination:"post" is the default, destination:"draft" is only when the user asks or wants a TikTok sound, and 'Never pick draft on your own.' It names the prerequisite (TikTok connected and the tiktok_creator_info call) and the needed alternatives for account/brand/hook selection.

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

post_to_xPublish a post to X (Twitter)A
Destructive
Inspect

Publish to the user’s connected X (Twitter) account — a single post, a post with an image or video render attached, a reply to an existing post, or a whole THREAD (pass thread as an array and each part is posted as a reply to the one before). This PUBLISHES immediately and PUBLICLY — ALWAYS show the user the exact text and get an explicit yes BEFORE calling. Can also run a POLL (2-4 options) instead of media, and restrict who may reply. LENGTH IS THE POSTING ACCOUNT’S, NOT A FLAT 280: 280 characters on an ordinary account, but UP TO 25,000 if that account has X Premium. Hermoso reads the account’s own subscription from X and sends the post rather than refusing something it may be entitled to; if X declines it on length, the reply says so and names the subscription X reported. Over 25,000 is refused for free — that is X’s own ceiling on every account. Text is NEVER truncated. AND A LONG POST IS CHEAPER THAN A THREAD: it is ONE billed X call where the same words split across five posts are five, so prefer one long post over threading when the account has Premium. Only the first ~280 characters show in the timeline; the rest sits behind “Show more”. Write altText whenever you attach a render. COSTS CREDITS: X charges per API request, so every post in a thread is billed, and a post containing a LINK costs roughly 13× one without — mention the cost before publishing a long thread. Each profile also has a rolling 24-hour ceiling on what it can spend at X, and a request that would cross it is refused WHOLE before anything publishes, so a batch of posts is bounded rather than open-ended. THIS TOOL POSTS ORGANICALLY — it does not create an ad campaign. X ads are built with create_x_ads_campaign -> create_x_ads_line_item -> create_x_ads_promoted_tweet, and a promoted post needs a post id, so publish here first and promote that post. Needs X connected (Settings > Connectors > X).

ParametersJSON Schema
NameRequiredDescriptionDefault
hookNoWHAT ANGLE this post is built on, recorded only at publish time. post_performance ranks hooks on it (a winner needs 5 posts sharing ONE hook), so pass a list_hooks id (e.g. "direct_callout", "before_after") or reuse your own wording EXACTLY across a campaign. Omit it and this post never votes on which hook works.
pollNorun a poll on the post. X does not allow a poll and media on the same post, and a poll cannot ride on a thread.
textNothe post text. 280 characters without X Premium, up to 25,000 with it — write the full thing, it is never truncated. Use this OR thread, not both.
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.
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
threadNoa thread: each string is one post, published in order, each replying to the previous. Max 25. Each part follows the same length rule as `text`, and on an X Premium account ONE long post is usually both better reading and cheaper than a thread.
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 x account connected (several and none named is refused by name, never guessed); omit when there is one.
altTextNoaccessibility description of the attached media, max 1000 characters — write one whenever you attach a render. Costs a small extra amount: X bills one metadata write PER media. With SEVERAL media, pass an ARRAY aligned to their order — X attaches alt text per media id, and a single string describes only the FIRST one (X renders up to four media as a GRID, not a carousel, so one sentence would be wrong for the other three).
subjectNoWHAT THIS POST IS ABOUT — product, feature, offer or theme (e.g. "winter coat", "free trial"). post_performance's second grouping axis: reuse the exact wording, as with hook.
imageUrlNoalias of mediaUrl for an IMAGE — same as passing it as mediaUrl
mediaUrlNoa Hermoso render (image or video) to attach to the first post — pass its served URL, or an upload_file url for external media
videoUrlNoalias of mediaUrl for a VIDEO — same as passing it as mediaUrl
mediaUrlsNoUP TO FOUR Hermoso-hosted media attached to ONE post — X’s own schema caps media_ids at 4. X renders them as a GRID: every image visible at once, nothing to swipe to. That is NOT a carousel, and a numbered "1/6 · SWIPE" slide deck must still not be sent here — it would publish as a grid and the "swipe" instruction would make no sense. Order decides the layout. On a thread the media rides the FIRST post; give each later part its own post to attach more. Mutually exclusive with mediaUrl, and more than 4 is refused before anything is uploaded.
replyToIdNonumeric id of an existing X post to reply to. X ONLY ACCEPTS AN API REPLY TO A POST THAT MENTIONS THIS ACCOUNT OR THAT THIS ACCOUNT WROTE (X policy since Feb 2026, every tier below Enterprise): a cold reply under a stranger's post is refused by X with "You can only reply to or quote posts where you are mentioned or are the author" and nothing is posted. Reply to those from x.com itself; use this for replies in your own threads and to people who tagged you.
communityIdNopublish into an X COMMUNITY instead of the main timeline — the number in the community’s own URL (x.com/i/communities/<id>). The connected account must be a MEMBER of it; X answers a non-member and a non-existent id with the same refusal and does not separate them.
quotePostIdNonumeric id of a post to QUOTE — X renders the quoted post inside yours. This is NOT replyToId: a reply sits under the original in its thread, a quote stands alone on your own timeline with the original embedded, which is the one you want for commentary. X makes a quote mutually exclusive with media and with a poll (their own schema), and a quote is billed at the higher LINK rate because X appends the quoted post’s t.co URL whatever your text says. Same X rule as replyToId: a quote of a post that does not mention this account is refused by X.
platformCoverNoVIDEO COVER. X 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 X 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.
replySettingsNorestrict who can reply — omit for everyone, which is the right default for a brand post
paidPartnershipNolabel the post a PAID PARTNERSHIP on X — the same disclosure Hermoso already ships for TikTok. OPT-IN ONLY: set it when the post is sponsored, gifted or otherwise paid for, and never assume it on the user’s behalf.

TDQS

A4.7/5.0
Behavior5/5

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

Annotations declare destructiveHint=true and openWorldHint=true, and the description adds substantial behavioral context beyond them: immediate public publication, credit costs, the ~13x link surcharge, a rolling 24-hour spend ceiling that refuses whole requests, per-media alt-text billing, length limits driven by the account's actual subscription, and how X's refusal is surfaced. This is exactly the extra context annotations cannot carry.

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?

The description is long, but nearly every sentence carries a distinct, actionable fact (confirmation requirement, cost model, length rule, thread-vs-post economics, ad-flow pointer). It is dense and information-rich rather than padded, though the heavy ALL-CAPS emphasis makes it harder to scan and could be trimmed slightly.

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 20-parameter mutation tool with nested objects and no output schema, the description covers the operationally critical dimensions: consent, cost, length, threading strategy, reply/quote constraints, and account requirements. Parameter-specific semantics like hook/subject/recipe grouping are left to the (rich) schema, which is reasonable, so coverage is effectively complete.

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 goes beyond the schema by explaining strategy that shapes parameter choice: thread vs single long post cost tradeoffs, quote vs reply semantics, poll exclusivity with media and threads, and the mediaUrls grid-not-carousel caveat. It adds real decision value rather than restating field descriptions.

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 ('Publish to the user's connected X (Twitter) account') and immediately enumerates the modes it covers: single post, media post, reply, thread, poll. It also explicitly separates itself from the sibling ad flow (create_x_ads_campaign etc.) and from quote-vs-reply behavior, so an agent can route correctly without opening schemas.

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?

Gives explicit when-to-use and when-not-to-use guidance: always get an explicit yes before publishing, prefer one long post over a thread on Premium accounts, use replyToId only for posts that mention the account, use x.com itself for cold replies, and use this tool before promoting a post. Alternatives and prerequisites (X connected via Settings > Connectors) are named.

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

post_to_youtubePost a video to YouTubeA
Destructive
Inspect

Publish a finished video to the brand’s connected YouTube channel. Pass a Hermoso render URL (or an upload_file url for a local/external file). PUBLISHES PUBLICLY BY DEFAULT: a plain "post this to YouTube" puts it ON the channel (confirm the title with the user, as for any publish) and notifies subscribers as YouTube does. Pass the privacy the user states instead: "unlisted" (link-only) or "private" (eyes-only). A video meant to run as a YouTube/Google AD should go up "unlisted" — private videos CANNOT be used as ads. SCHEDULE it with publishAt, FILE it under the right categoryId (the default 22 "People & Blogs" is wrong for most ads), SUBSCRIBER NOTIFICATIONS FOLLOW PRIVACY — a public publish announces the video to the channel’s subscribers (YouTube’s own default), while unlisted/private uploads stay quiet; pass notifySubscribers explicitly to override either way. THUMBNAIL: pass thumbnailUrl, or a frame of the video is set for free; a thumbnail YouTube refuses never fails the upload, and thumbnailNote says why. YOUTUBE MUSIC: YouTube’s API takes no music track. Only when the user wants a track from YouTube’s own Audio Library: BEFORE uploading, tell them plainly it will go up UNLISTED (not on their channel, nobody sees it) so they can add the track in YouTube Studio on desktop (Content > the video > Editor > Audio), and that THEY must then switch it to Public there themselves; get their yes, then pass privacy:"unlisted". Never choose unlisted for music on your own. Needs a connected YouTube channel (Settings > Connectors > YouTube).

ParametersJSON Schema
NameRequiredDescriptionDefault
hookNoWHAT ANGLE this post is built on, recorded only at publish time. post_performance ranks hooks on it (a winner needs 5 posts sharing ONE hook), so pass a list_hooks id (e.g. "direct_callout", "before_after") or reuse your own wording EXACTLY across a campaign. Omit it and this post never votes on which hook works.
tagsNoup to 30 tags
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.
titleNoREQUIRED in practice — the headline every search result and the Shorts feed shows (≤100 chars); an upload with no title is refused
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 youtube account connected (several and none named is refused by name, never guessed); omit when there is one.
privacyNodefault public (live + searchable on the channel); unlisted = link-only (the ad-ready setting, or to add YouTube music in Studio first — only when the user asks); private = eyes-only (cannot run as an ad)
subjectNoWHAT THIS POST IS ABOUT — product, feature, offer or theme (e.g. "winter coat", "free trial"). post_performance's second grouping axis: reuse the exact wording, as with hook.
videoUrlYesthe video to post — a Hermoso render URL or an upload_file url
coverAtMsNoTHE VIDEO COVER (the custom thumbnail), as ONE frame: milliseconds from the start (7000 = the frame at 7s). That frame is cut from the upload and set with thumbnails.set. thumbnailUrl wins; this beats platformCover.
publishAtNoSCHEDULE the publish — ISO 8601, e.g. "2026-09-01T15:00:00Z", and it must be in the future. YouTube only allows this on a PRIVATE video and makes it PUBLIC at that moment, so pass privacy:"private" (or leave privacy unset) — asking for a scheduled "unlisted" or "public" post is refused rather than half-honoured.
categoryIdNoYouTube category id, NUMERIC — default "22" (People & Blogs), which is wrong for most ads. 1 Film & Animation · 2 Autos & Vehicles · 10 Music · 15 Pets & Animals · 17 Sports · 19 Travel & Events · 20 Gaming · 22 People & Blogs · 23 Comedy · 24 Entertainment · 25 News & Politics · 26 Howto & Style · 27 Education · 28 Science & Technology · 29 Nonprofits & Activism. The assignable set is region-specific, so the value is forwarded as given and YouTube has the last word.
aiGeneratedNoYouTube’s “altered or synthetic content” declaration (containsSyntheticMedia). OMIT IT and Hermoso decides from provenance: a Hermoso render is declared, a video the user uploaded through upload_file or from an external URL is NOT — real footage must not carry the label. true/false overrides.
descriptionNoREQUIRED in practice — what the video shows, a line about the brand, a link and a few #hashtags (≤5000 chars); YouTube search, suggested videos and the Shorts feed rank on it, so an upload with no description (and no caption) is refused
thumbnailUrlNothe custom thumbnail: a Hermoso-hosted image (make_thumbnail, or upload_file for the user’s own). Omit it and a representative frame of the video is set, free; "auto" keeps YouTube’s pick. Custom thumbnails need a verified channel.
platformCoverNoVIDEO COVER. Omit it (the default) and Hermoso sets the video’s best frame — the same frame as its Library thumbnail — as the cover (YouTube custom thumbnail; the same as thumbnailUrl:"auto" when true). true = send no cover and let the platform pick (usually the first frame). A cover you pass yourself always wins.
notifySubscribersNoTHE DEFAULT FOLLOWS PRIVACY. privacy:"public" NOTIFIES the channel's subscribers — that is YouTube's own default and normally what someone publishing publicly wants. privacy:"unlisted" and "private" do NOT: the video is not on the channel, so announcing it is nonsense, and a blast to somebody's whole subscriber list cannot be undone. A scheduled publish (publishAt) is PRIVATE at upload, so it does not notify either — pass true to announce one. An explicit value ALWAYS wins in both directions: true announces an unlisted/private upload, false publishes publicly and quietly. The reply reports which way it went and why.

TDQS

A4.5/5.0
Behavior5/5

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

Annotations only flag destructive/openWorld/non-idempotent; the description goes far beyond them, disclosing that publishing is public by default and notifies subscribers, that thumbnail rejection never fails the upload (with thumbnailNote explaining why), that music cannot be added via API, and the exact privacy/publishAt/notifySubscribers interaction rules.

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?

The safety-critical default (publishes publicly) is front-loaded and the structure follows parameters logically. It is long, and the YouTube Music paragraph and some privacy/notify restatements run past what is needed, though given 18 parameters most content earns its place.

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?

With 18 parameters, a destructive publish and no output schema, the description covers prerequisites, privacy semantics, scheduling constraints, thumbnail fallbacks and even reports what the reply will say about notifications. Only minor gaps remain, such as what the successful response contains.

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

Parameters3/5

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

Schema description coverage is 100% and each parameter already carries a detailed description, so the baseline is 3. The description reinforces cross-parameter interactions (privacy vs publishAt vs notifySubscribers, categoryId defaults for ads, subtitle defaults, aiGenerated provenance), but most of that repeats what the schema states, so it does not lift beyond baseline.

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 and resource — 'Publish a finished video to the brand's connected YouTube channel' — and the platform is unmistakable against siblings like post_to_tiktok or post_to_meta. An agent can select this tool without opening any other 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?

It gives explicit when-to-use routing: public by default, 'unlisted' for ads (with the reason private cannot run as an ad), 'private' for eyes-only, and a conditional path for YouTube Audio Library music that requires user confirmation before uploading unlisted. It also names the prerequisite connector setup.

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

post_x_articlePublish a long-form Article to XA
Destructive
Inspect

Publish a long-form ARTICLE to the user’s connected X (Twitter) account — X’s own long-form format, which is a different thing from a long POST. Give it a title and a body written in MARKDOWN (or plain prose) and Hermoso converts it into the DraftJS content_state structure X requires: headings, paragraphs, bulleted and numbered lists, blockquotes, bold / italic / strikethrough, links, horizontal rules, fenced code blocks and pipe tables all carry across. FORMATTING IS NEVER SILENTLY DROPPED — anything X Articles cannot represent (inline code, an inline image) REFUSES the article for free and names exactly what and why, and allowLossy: true is the explicit way to publish it as plain text anyway. THE HARD LIMIT TO PLAN AROUND: X allows only about 10 Article DRAFTS and 5 Article PUBLISHES per account per DAY, it publishes that cap nowhere, and it counts a FAILED attempt against them — so never iterate on an article by republishing it, and use publish: false to save a draft for the user to read in X’s own composer when they want to review before it goes out. This PUBLISHES immediately and PUBLICLY: show the user the full text and get an explicit yes first, because a published Article can NEVER be edited (X’s own rule — edit_x_post will refuse it) and the only remedy is to delete and republish, which costs another of the five. An Article appears in the timeline as a title card, not as body text; readers open it. Costs credits (X bills per API request, and this is three of them). Needs X connected (Settings > Connectors > X).

ParametersJSON Schema
NameRequiredDescriptionDefault
bodyYesthe article body, as markdown or plain prose. Markdown headings, lists, quotes, links, emphasis, ``` code fences and | pipe | tables | are all converted to X’s own Article structure.
hookNoWHAT ANGLE this post is built on, recorded only at publish time. post_performance ranks hooks on it (a winner needs 5 posts sharing ONE hook), so pass a list_hooks id (e.g. "direct_callout", "before_after") or reuse your own wording EXACTLY across a campaign. Omit it and this post never votes on which hook works.
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.
titleYesthe Article title — X requires one and refuses a draft without it. This is what shows on the timeline card.
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
publishNodefault true. Pass false to save it as a DRAFT in the account’s X Articles composer instead — nothing becomes public, the user can review and publish it from X, and it does not spend one of the five daily publishes.
subjectNoWHAT THIS POST IS ABOUT — product, feature, offer or theme (e.g. "winter coat", "free trial"). post_performance's second grouping axis: reuse the exact wording, as with hook.
headingsNohow headings are rendered. “blocks” (default) uses X’s own heading block types for # and ## headings; ### and deeper become bold lines, because X Articles refuse a third-level heading (the reply says so). “text” renders every heading as a BOLD standalone paragraph instead: the words survive and the heading structure does not, and the reply says so.
allowLossyNopublish even though part of the source cannot be represented on X, rendering those parts as plain text. OFF by default and it should usually stay off — silently publishing a user’s copy with formatting missing is worse than refusing and telling them.
coverImageUrlNooptional cover picture for the Article — a Hermoso render URL or an upload_file url. Must be a STILL image; X Article covers are not videos.

TDQS

A4.5/5.0
Behavior5/5

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

Far exceeds annotations (destructiveHint=true, openWorldHint=true). Discloses uneditable-after-publish rule (edit_x_post refuses), daily draft/publish caps (~10/5) that are undocumented by X, failed attempts counting against the cap, credit cost (three API requests), lossy-format refusal behavior, and connector prerequisite. This is exactly the behavioral context annotations cannot carry.

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

Conciseness3/5

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

Rich and front-loaded, but heavily oversized with ALL-CAPS emphasis and multi-clause sentences that pile on caveats. The formatting-conversion list and cap warnings are valuable, yet the draft reads more like a policy page than a tool description, and some redundancy (X's rules restated twice) could be trimmed.

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?

For a destructive, non-idempotent, open-world publish tool with no output schema, the description covers approval workflow, irreversibility, caps, cost, formatting loss modes, draft escape hatch, and connector requirement. An agent has everything needed to call it safely.

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

Parameters3/5

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

Schema description coverage is 100%, so the schema already documents all 11 parameters thoroughly (including hook, brand, recipe, headings enum semantics). The description reiterates the title/body contract and allowLossy/publish behavior, adding framing but little beyond the schema. Baseline 3 is correct when the schema does the heavy lifting.

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 (publish) and resource (long-form Article on X), and explicitly distinguishes it from the sibling post_to_x by clarifying it is X's Article format, 'a different thing from a long POST'. An agent can route correctly without inspecting either 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?

Provides explicit when-to-use guidance ('show the user the full text and get an explicit yes first'), when to use publish:false (review drafts), and warns against iterating by republishing. The user-approval precondition is spelled out, not implied.

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

product_sizzleProduct sizzle (music-led)AInspect

Render an 18-30s music-led PRODUCT SIZZLE: ONE 15s Seedance 2.0 hero clip of the product, diced into fast cuts and intercut with typeset spec/CTA cards on a brand-coloured grain background, mixed to a music bed. Faceless by design — no people, no voiceover, no spoken lines; the cards carry every word, so nothing is left to a video model's spelling. Pass a real packshot as refImage or the label will not be yours. EXPENSIVE — the hero clip is the only paid leg and it is a full 15s Seedance render: ≈1,040 credits at the DEFAULT 1080p, ≈470 at 720p, ≈220 at 480p, ≈4,130 at 4k (call hermoso_capabilities for the live seedance-2 per-duration numbers; the dicing and the cards are free, and the music bed is already included in the quoted figure). Confirm the spend with the user before calling. For a talking/UGC ad use render_ad or generate_avatar; for a cheap deterministic format use make_template_ad.

ParametersJSON Schema
NameRequiredDescriptionDefault
ctaNoclosing CTA line, ≤30 chars
specsNoup to 4 spec lines for the typeset cards, ≤26 chars each
promptYeswhat the sizzle should show — the product, the setting, the look
secondsNofinished length, clamped to 18-30s (default 25). The PAID hero render is always 15s regardless — this only changes how the cuts and cards are packed
refImageNoproduct packshot URL that anchors the real label — strongly recommended
brandNameNobrand name on the cards — defaults to the workspace brand
musicMoodNomusic-bed mood, e.g. driving / cinematic / upbeat
resolutionNohero-clip resolution and therefore the whole cost — DEFAULT '1080p' (≈1,040 credits); '720p' ≈470, '480p' ≈220, '4k' ≈4,130
aspectRatioNo'9:16' default; anything the seedance-2 catalog entry does not list falls back to 9:16

TDQS

A4.7/5.0
Behavior5/5

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

Annotations only give generic hints (readOnlyHint false, openWorldHint true, not idempotent, not destructive); the description supplies the consequential behavior an agent actually needs: per-resolution credit costs, that the paid leg is always 15s, that dicing and cards are free, that the music bed is included, and that spend must be confirmed. It also warns that omitting refImage forfeits the real label.

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-loads what the tool produces before diving into cost and routing, which is the right order. The cost sentence is dense with figures, but each element (resolution tiers, confirmation requirement, sibling routing) earns its place.

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 no-output-schema tool it covers output intent, cost, prerequisites, and alternatives thoroughly. The one omission is return/workflow behavior — nothing says whether this returns a job id to poll via get_job, which matters given the sibling job tools.

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 adds real meaning beyond the schema: it explains the cost/quality tradeoff across resolutions and that only the fixed 15s hero render is billed regardless of the requested finished length.

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 ('Render an 18-30s music-led PRODUCT SIZZLE') and immediately enumerates the construction: one 15s Seedance hero clip, fast cuts, typeset cards, music bed. Clearly separable from the named siblings render_ad, make_template_ad, and generate_avatar.

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 routing rules: use render_ad or generate_avatar for talking/UGC ads, make_template_ad for a cheap deterministic format. Also names the refImage prerequisite and requires the agent to confirm spend with the user before calling.

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

publish_to_shopify_productPublish an image onto a Shopify productA
Destructive
Inspect

Attach a finished image to one of the merchant's Shopify product listings, as product media. Pass productId (from list_shopify_products — the gid://shopify/Product/… form) and a PUBLIC https imageUrl, which is what every Hermoso render returns. Shopify fetches the image server-side and processes it asynchronously, so the media can come back status PROCESSING and appear on the listing a moment later — that is success, not a failure. Only works for accounts created by installing Hermoso from the Shopify App Store. Free — the render was already paid for.

ParametersJSON Schema
NameRequiredDescriptionDefault
altNoalt text for accessibility and SEO; defaults to a generic credit
imageUrlYesany public https image URL — a Hermoso render works, and so does ANY file of your own brought in with upload_file
productIdYesgid://shopify/Product/… from list_shopify_products

TDQS

A4.4/5.0
Behavior4/5

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

Adds genuinely valuable context not present in annotations: server-side fetch, asynchronous processing, and the crucial clarification that a PROCESSING media status is success rather than failure. It does not address the annotation's destructiveHint=true or idempotentHint=false, so an agent gets no warning that repeated calls may create duplicate media entries.

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

Conciseness5/5

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

Front-loaded with the action and required inputs, then the async-status caveat, then eligibility and cost. Dense but every sentence carries operational information; nothing is padding.

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 3-param mutation with no output schema, the description covers inputs, eligibility, cost, and the async result behavior that would otherwise look like an error. The missing piece is any statement about what a repeat call does, given idempotentHint=false.

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 meaning: imageUrl must be a PUBLIC https URL, and productId must be in gid://shopify/Product/ form. The optional alt parameter is left to the schema.

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 precise verb and resource: attach a finished image to a Shopify product listing as product media. It names the exact source of the ID and the required input forms, so it is unmistakable against siblings like list_shopify_products or set_product_image.

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 clear context for use (productId comes from list_shopify_products, imageUrl from any Hermoso render) and an explicit eligibility exclusion: only accounts created by installing Hermoso from the Shopify App Store. It does not name a competing sibling tool to route away from, so it stops short of 5.

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

pull_competitor_adsPull competitor adsAInspect

Pull one named brand’s live ads from the Meta (Facebook and Instagram) Ad Library: deduplicated, sorted, with the brand’s own page resolved. One call, back in a few seconds. Use it when the user names a brand and asks what ads it is running ("show me the ads is running"). Covers the Meta Ad Library only; for a broader search across brands or platforms use research_ads. Spends credits.

ParametersJSON Schema
NameRequiredDescriptionDefault
urlNosame as domain
nameNosame as companyName
sortNo'longest_running' (default) etc.
brandNosame as companyName
limitNomax ads per platform (default 30)
domainNothe advertiser domain, e.g. liquiddeath.com — a full website URL works (url / website are read as this too). Pass companyName OR domain
companyNosame as companyName
countryNo2-letter, default 'US'
websiteNosame as domain
companyNameNothe advertiser name, e.g. "Liquid Death" (company / brand / name are read as this too)

TDQS

A4.1/5.0
Behavior3/5

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

Annotations already declare readOnlyHint=false, openWorldHint=true, idempotentHint=false, destructiveHint=false. The description adds credit consumption and latency ('back in a few seconds'), which explains why the tool is not read-only. But it omits how many credits, whether repeated calls are charged again (idempotency implications), and failure/auth behavior.

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 capability, then the trigger, then the boundary/alternative, then the cost warning. No wasted clauses, though 'One call, back in a few seconds' is mildly promotional filler.

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?

With no output schema, the description usefully characterizes the result ('deduplicated, sorted, with the brand's own page resolved'), so an agent knows what comes back. Missing credit amount and pagination/limits detail keeps it short of a 5.

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

Parameters3/5

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

Schema description coverage is 100% across 10 params, so the schema carries parameter meaning (including aliases like 'company / brand / name'). The description adds no parameter-level guidance such as default limit behavior or sort semantics beyond the schema. Baseline 3 is appropriate.

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?

Specific verb+resource ('Pull one named brand's live ads from the Meta Ad Library') with scope, and it names a sibling ('research_ads') for the broader case. An agent can distinguish it from search_meta_ads and research_ads 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 triggering condition with a quoted user phrasing ('show me the ads <brand> is running'), an explicit boundary ('Covers the Meta Ad Library only'), and a named alternative ('for a broader search across brands or platforms use research_ads'). Also warns about cost ('Spends credits').

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

read_docRead a Google DocA
Read-onlyIdempotent
Inspect

Read the text of a Google Doc Hermoso can reach — one it created, or one the user handed over with the Google file picker in the app (that is how an EXISTING doc becomes readable; find its id with list_drive_files). Pass documentId (from create_doc) OR paste a Google Docs URL as docUrl. Under the drive.file scope it reaches nothing else in the user’s Drive; if Google answers that the file was not found, the user has not picked it yet — ask them to pick it in the app rather than retrying. Returns the plain text of EVERY tab (each headed by its tab name when there are several), with smart chips (dropdowns, people, dates, rich links) shown as the text Docs displays, plus dropdowns[] — each chip with its title, current value and options, which is what update_doc dropdowns sets. Read-only, free.

ParametersJSON Schema
NameRequiredDescriptionDefault
docUrlNoa Google Docs URL to read — the document id is extracted from it
documentIdNothe document id (from create_doc)

TDQS

A4.8/5.0
Behavior5/5

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

Annotations already cover readOnly/idempotent/non-destructive, but the description adds meaningful context: the drive.file scope's reach limits, the not-found failure mode and correct recovery, and exactly what the read yields (every tab, chip text as Docs renders it, dropdowns[] feeding update_doc). This is rich disclosure beyond the annotation set.

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?

Dense and somewhat long, but front-loaded with the core action and its scope, and every clause carries a distinct operational fact (id sources, scope limits, failure handling, return shape). Minor trimming around the chip/dropdown explanation would help but nothing is 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 carries the return-value burden and does so precisely (plain text of every tab, tab headings, chip rendering, dropdowns[]). Combined with scope and error guidance, an agent has everything needed to call and interpret this tool 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 adds provenance and alternation semantics the schema cannot express: documentId comes from create_doc, and the two params are an OR ('Pass documentId OR paste a Google Docs URL as docUrl').

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 ('Read the text of a Google Doc') and immediately bounds it: only docs Hermoso created or the user picked via the Google file picker. Names sibling tools (create_doc, list_drive_files) for the id path, so an agent can distinguish it from list_drive_files or read_sheet without opening the 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?

Gives explicit when-to-use (doc created by Hermoso or handed over via the file picker), how to obtain the id (list_drive_files), and the two acceptable input routes (documentId from create_doc or a pasted docUrl). It also states the failure branch: if Google reports file-not-found, the user hasn't picked it, so ask rather than retry — that is actionable when-not guidance.

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

read_sheetRead a Google Sheet rangeA
Read-onlyIdempotent
Inspect

Read cells from a Google Sheet Hermoso can reach — one it created, or one the user handed over with the Google file picker in the app (that is how an EXISTING spreadsheet becomes readable; find its id with list_drive_files). Pass the spreadsheetId (from create_sheet) OR paste a Google Sheets URL as sheetUrl. If Google answers that the file was not found, the user has not picked it yet — ask them to pick it in the app rather than retrying. Returns a 2-D array of values.

ParametersJSON Schema
NameRequiredDescriptionDefault
rangeNoA1 range, e.g. "A1:D50" (default A1:Z1000)
sheetUrlNoa Google Sheets URL to read — the spreadsheet id is extracted from it
spreadsheetIdNothe spreadsheet id (from create_sheet)

TDQS

A4.7/5.0
Behavior5/5

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

Annotations already cover readOnly/idempotent/openWorld, but the description goes further by disclosing the authorization model (file-picker hand-off required for pre-existing spreadsheets) and a concrete error-recovery path for the not-found case, plus the return shape (2-D array of values).

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 action and scoping rule, then the parameter alternatives, then error handling. Dense with em-dashes and parentheticals, which makes it slightly harder to scan, but every sentence carries information.

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?

Return format is stated and the access model is thorough. The one gap is tab selection: for a multi-tab spreadsheet it never says which tab is read or that the agent may need list_sheet_tabs first.

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 adds meaning the schema does not: spreadsheetId and sheetUrl are alternatives ('OR'), and it states where each value originates (create_sheet for the id, a pasted URL for sheetUrl).

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+resource ('Read cells from a Google Sheet') and scopes it to reachable files. An agent can distinguish this from update_sheet, append_to_sheet, and list_drive_files without opening 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 Guidelines5/5

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

Explicitly covers when the tool works: sheets Hermoso created or ones the user handed over via the file picker, and directs the agent to list_drive_files to obtain the id. It also names the failure condition ('file not found' means the user hasn't picked it) and prescribes the correct next action instead of retrying.

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

recast_motionRecast motionAInspect

Motion transfer: re-perform a clip's motion, camera and timing with YOUR character, product, clothes or place; sound kept. image (+ images, @Image1, @Image2… in prompt) = who and what. engine auto (default), a person clip recast with people: ≤15s carried by one face (talking/close-up, one person, one photo) or an AI person made from words -> wan (follows every blink and mouth shape); dance, full-body, a group or >15s -> h3 (H3 Max Recast: ≤4 photos, one per person, 5-30s, no shot over 15s, keeps set, light, cuts); otherwise seedance (Seedance 2.5, ≤9 pictures, 4-30s, keeps set and beats; a person clip only if our render or faceRoute, else wan/kling). kling = Kling Motion Control (ONE picture, its background becomes the set, 3-30s); wan = Wan 3.0 Prime (≤9 pictures, keeps the set, ≤15s). Billed per output second, ~5 min per 5s; dryRun quotes.

ParametersJSON Schema
NameRequiredDescriptionDefault
tierNokling only: 'pro' (default): 1080p and real hand-object interaction. 'standard': about 25% fewer credits and faster, but 720p, and it tends to mime a held object with empty hands. hermoso_capabilities lists the exact credits for both
imageNowho performs it: the actor/character image URL (@Image1). Required on kling. No one yet: generate_image a portrait of an AI person from words (no reference photo) and pass its url, or a saved / preset creator’s portrait (list_creators). Using a real person’s face, likeness or voice confirms you are them or have their consent, take full, unlimited responsibility for its use and will cover any claim against Hermoso (hermoso.ai/terms). Paid plan only; recorded.
videoYesthe reference video whose motion to re-perform
engineNodefault auto
imagesNoseedance/wan: more pictures, @Image2…: a character, product, outfit or place
promptNooptional, e.g. 'same moves, new location: Tokyo at night'
faceRouteNo'face_lane' = the user's "I own the rights to this face" for a real person in the source clip (paid plans). Only on the user's say-so, never on your own.
resolutionNoseedance and wan default 1080p; h3 takes 720p or 1080p, default the clip’s own
orientationNokling only: which aspect to keep: the video's (default) or the image's

TDQS

A4.1/5.0
Behavior5/5

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

Goes well beyond the annotations (readOnly=false, openWorld=true, idempotent=false): it discloses per-output-second billing, ~5 min per 5s latency, dryRun quoting, paid-plan-only gating, and consent/terms responsibility for real-person likeness. It is also consistent with the non-readOnly, non-destructive annotation profile.

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

Conciseness3/5

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

The purpose is front-loaded, but the body is a single run-on sentence packed with arrows, abbreviations and routing branches that are hard to parse. Every clause is arguably useful, yet the density hurts scannability.

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 9-parameter, no-output-schema generation tool with high schema coverage, the description supplies the missing operational context: engine selection, duration limits, photo counts, billing, and consent. Only minor gaps remain, such as what a dryRun response returns or how to handle failures.

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 baseline is 3, but the description adds real meaning: the engine enum's routing logic (schema only says 'default auto'), image as 'who performs it', and images/@Image2 as additional character/product/outfit/place references. It meaningfully enriches the highest-stakes parameters.

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

Purpose4/5

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

States a specific verb and resource — 'Motion transfer: re-perform a clip's motion, camera and timing with YOUR character... sound kept' — which is a distinctive capability an agent can separate from generic siblings like edit_video or restyle_video. It does not explicitly name a sibling it is not, so it falls short of a 5.

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 dense conditional guidance on which engine fits which input (one-face talking clips -> wan, dance/group/>15s -> h3, else seedance, kling = one picture), effectively telling the agent when each mode applies. It never states when to prefer recast_motion over sibling tools like clone_video or edit_video, so no explicit exclusions.

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

reframe_videoReframe videoAInspect

Reframe a video to a different aspect ratio (e.g. 16:9 master -> 9:16 vertical) with smart subject tracking. Paid render; returns the served URL of the reframed video.

ParametersJSON Schema
NameRequiredDescriptionDefault
videoYesthe source video URL
aspectRatioYesthe target aspect ratio

TDQS

A4/5.0
Behavior4/5

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

Annotations already declare the safety profile (readOnly=false, idempotent=false, destructive=false, openWorld=true). The description adds genuinely new context beyond them: it is a 'Paid render' (cost implication) and it 'returns the served URL of the reframed video' (return value, important since there is no output schema). It stops short of clarifying async/job behavior, which matters given the sibling get_job pattern.

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

Conciseness5/5

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

Two tight sentences, front-loaded with the core action followed by the cost/return note. No filler; every clause carries information.

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 simple two-parameter tool with no output schema, the description covers what it does and what it returns. It is nearly complete, though it omits whether the render is synchronous or requires polling and gives no cost magnitude for the 'Paid render' warning.

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

Parameters3/5

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

Schema description coverage is 100% and both parameters (video URL, aspectRatio enum) are documented in the schema itself, so the baseline is 3. The description reinforces semantics with the 16:9->9:16 example but adds no format or constraint detail beyond the schema.

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 (reframe) and resource (video), names the transformation (different aspect ratio) and the mechanism (smart subject tracking), plus a concrete 16:9->9:16 example. This distinguishes it from siblings like resize_ad, edit_video, and upscale_video without needing to open their schemas.

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

Usage Guidelines3/5

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

Usage context is implied by the aspect-ratio transformation and the 'Paid render' cost note, but there is no explicit when-to-use guidance, no when-not-to-use, and no naming of alternatives such as upscale_video or edit_video. Adequate but leaves routing to inference.

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

rememberRemember a factAInspect

Save a durable fact or PREFERENCE about the brand, audience, or the user’s creative TASTE (e.g. “audience is first-time homebuyers”, “prefers bold lime accents”, “always captions off”) into the workspace Memory so it shapes FUTURE ads. For lasting things, not one-off requests. Merges into the existing Memory (never overwrites); de-dupes on identical text. NEVER how Hermoso, a tool, a connector or a platform API behaves (what a call returns, errors, permissions, limits, ids) and never a phone number or email — those are refused.

ParametersJSON Schema
NameRequiredDescriptionDefault
textYesthe fact/preference, concise
brandNoprofile id/name from list_brands, this call only
categoryNoshort bucket: Brand, Audience, Taste, Do, Don’t, or Preference (default General)

TDQS

A4.6/5.0
Behavior5/5

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

Annotations already declare non-readonly, non-destructive, and non-idempotent. The description adds critical behavioral detail: it merges into existing Memory without overwriting, de-dupes identical text, and refuses certain content categories. This goes well beyond 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?

The description is front-loaded with purpose and includes necessary constraints in a compact paragraph. No sentences are wasted, though the all-caps emphasis is slightly heavy and the refusal clause could be tighter.

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 no output schema and clear annotations, the description covers purpose, usage boundaries, merge semantics, and content restrictions. An agent has enough to call it correctly without further inference.

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

Parameters3/5

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

Schema description coverage is 100%, so parameter meanings are fully documented in the schema. The description does not add new syntax or constraints for the three parameters; its examples of facts illustrate the 'text' field but don't provide semantic detail beyond the schema.

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 (Save) and resource (durable fact or preference about brand, audience, or creative taste) and explains the outcome (shapes future ads). It clearly distinguishes from siblings like forget, list_memory, and tidy_memory by focusing on persistence of taste/audience facts.

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?

Explicitly says when to use (durable facts, not one-off requests) and when not to use (never how Hermoso, a tool, connector, or platform API behaves; never phone numbers or emails). It also notes merge and dedupe behavior, giving strong context for appropriate invocation.

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

remix_staticClone a static ad (old name)AInspect

The OLD NAME of clone_static, kept so agents that already call it keep working. It is the same tool with the same inputs, result and cost; prefer clone_static.

ParametersJSON Schema
NameRequiredDescriptionDefault
brandIdNoa profile id/name from list_brands to clone for; omit to use the active profile
imageUrlYesthe URL of the static ad image to clone. Using a real person’s face, likeness or voice confirms you are them or have their consent, take full, unlimited responsibility for its use and will cover any claim against Hermoso (hermoso.ai/terms). Paid plan only; recorded.

TDQS

A4.3/5.0
Behavior3/5

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

Annotations already declare readOnlyHint=false, openWorldHint=true, idempotentHint=false, destructiveHint=false. The description usefully adds that behavior and cost are identical to clone_static, but it does not describe the actual operation's side effects (it clones an ad image, paid plan, recorded) beyond what the schema text already carries.

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

Conciseness5/5

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

Two tight sentences, zero filler. The identity and the routing recommendation are both front-loaded, which is exactly right for a deprecation shim.

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?

For a deprecated alias with a complete schema and no output schema, this covers everything an agent needs: what it is, that it is equivalent to clone_static, and that clone_static should be preferred.

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

Parameters3/5

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

Schema description coverage is 100%, so brandId and imageUrl are fully documented in the schema, including the paid-plan/consent note. The description only asserts inputs are the same as clone_static, adding no new parameter meaning — baseline 3 is appropriate.

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 identity: this is the OLD NAME of clone_static, same tool, same inputs, result and cost. It distinguishes itself from the sibling clone_static by name and explains the only reason it still exists (backward compatibility).

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?

It gives an explicit routing rule — 'prefer clone_static' — and names the exact condition under which remix_static is still the right call: agents that already call it. No inference required.

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

remove_memberRemove a teammateA
DestructiveIdempotent
Inspect

Remove a member from this profile by email — they lose access (you can re-invite them later), AND every connection THEY made on this profile is disconnected with them: their own X, LinkedIn, TikTok, YouTube, Pinterest, Threads… connections (on a channel holding several accounts, only the accounts they added), and any extra login or authorization they added. Connections the owner or anyone else made are never touched, and one with no record of who connected it is treated as the owner's. Queued posts that would publish through their accounts, or that they scheduled themselves, will not go out. The unconfirmed call reports exactly which connections and how many scheduled posts — relay that to the user, then call with confirm:true.

ParametersJSON Schema
NameRequiredDescriptionDefault
emailYesthe member’s email
confirmNoREQUIRED true

TDQS

A4.8/5.0
Behavior5/5

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

Annotations only declare destructive/idempotent/openWorld, but the description adds the full blast radius: the member's own connections are disconnected, extra logins/authorizations removed, queued and self-scheduled posts suppressed, while the owner's connections are never touched. It even resolves an edge case (connections with no recorded owner are treated as the owner's).

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?

The core action and its consequence are front-loaded, and every clause carries real information about the cascade. It is a dense single block, however, and could be easier to scan if broken into the destruction scope vs. the confirm flow.

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?

For a destructive, cascading mutation with no output schema, the description covers what is destroyed, what is preserved, the edge case, the confirmation protocol, and what the dry-run returns. Nothing an agent needs to call it safely is missing.

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 goes further by explaining the purpose and required sequencing of the 'confirm' parameter (dry-run first, then confirm:true), which the schema only labels 'REQUIRED true'.

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 ('Remove a member from this profile by email') and immediately names the primary consequence (loss of access, re-invitable). An agent can distinguish it from invite_member and leave_connector without opening 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 Guidelines5/5

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

Explicitly prescribes the two-step workflow: the unconfirmed call reports affected connections and scheduled posts, which the agent must relay, then call again with confirm:true. This is concrete when-to-call guidance rather than implied usage.

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

render_adRender ad videoAInspect

RECOMMENDED for finished video ADS: render a plan_ad concept through the SAME quality pipeline as the Hermoso web Studio — timed shot list, exact/clean speech (no garbled words), text composited in post (never model-painted), an optional brand end card (only when the user asks), no music unless asked, real product references. UGC and cinematic plans are rendered from a painted STORYBOARD board (cleaned, then bound first, ahead of the creator and the product); a cinematic spot paints a location anchor first (and a hero when someone is on camera). A product-only commercial renders from the real product photo by default; board:true or foundations:'choose' adds a product identity sheet, a four-up moodboard and a board. Pass plan_ad’s full structured output as creative. Honors the plan’s render_plan structure/duration: a storyboard that FITS ONE CLIP OF THE RENDER MODEL renders as a single continuous pass; anything longer automatically renders as STITCHED ACTS (the fewest balanced clips, each at most one model clip) — never time-compressed into one clip. That threshold is the render model’s own maximum, not a fixed number: most models cap a clip at 15s and the longest-clip one goes to 30s, so use dryRun:true to see the act split this plan will actually get, for free, before spending. CAST A SAVED CREATOR with creator so the SAME person stars in this ad as in the last one (list_creators is the roster) — otherwise every render invents a new face. Renders take 1–3 min; keep polling get_job if it returns still-rendering. Spends credits.

ParametersJSON Schema
NameRequiredDescriptionDefault
boardNopaint the storyboard board first (default: on for UGC and cinematic spots, off for a product-only commercial); false = render from the text shot list
modelNovideo model id from hermoso_capabilities (default: the plan’s pick). Naming one is a DELIBERATE pick — the server asks before ever swapping it (no silent fallback)
musicNomusic bed: OFF unless asked. true = the plan's own music line; or the bed in words ('lo-fi jazz, brushed drums'); false = none
dryRunNoreturn the routing decision (single pass vs stitched acts, resolved model + act lengths) and the exact credits the real render reserves, WITHOUT submitting a render — free, nothing charged
lockupNobrand wordmark + tagline composited over the closing seconds. DEFAULT FALSE — set true ONLY when the user asks for branding on the close
creatorNoCAST A SAVED CREATOR in this ad — their id from list_creators, or the name you know them by (“Sarah”), or a PRESET AI creator from list_creators presets by exact name or id (free, no generation). Their saved portrait becomes the on-camera identity for the whole spot, so the same face carries across every act and across every ad you render for this brand — and because we already have their picture, the character portrait this pipeline would otherwise generate is skipped, so casting somebody costs LESS than not casting them. Omit to let the ad cast a fresh person — EXCEPT for a CREATOR account (onboarded from their own @handle): their own saved likeness is cast by default when the plan has a person on camera and the account is on a paid Hermoso plan (a free account gets a fresh AI person), and the read-back says `default:true`; pass "none" to render without them. Refused for free, with nothing rendered, if the name matches nobody or more than one creator, if an explicitly named creator is cast on a plan with nobody on camera, or if a REAL person is cast on a free plan. Using a real person’s face, likeness or voice confirms you are them or have their consent, take full, unlimited responsibility for its use and will cover any claim against Hermoso (hermoso.ai/terms). Paid plan only; recorded.
endCardNoappend the branded end card. DEFAULT FALSE on every recipe — set true ONLY when the user asks for an end card (a clone of a video that had none should not grow one)
captionsNoburn the plan's per-scene on-screen words as caption pills. DEFAULT FALSE on every recipe — set true ONLY when the user asks for on-screen text or captions; no recipe turns them on by itself
creativeYesthe FULL structured output of plan_ad (must contain video_storyboard)
ttsVoiceNovoiceover voice name (e.g. Rachel / George) when the plan voices over
faceRouteNoONLY after a render came back saying the video model's safety check flagged a person's face: 'face_lane' is the user's choice "I own the rights to this face". Send the SAME request again with it and the same face is rendered on Seedance through the face library, at the normal price. Sending it is the user's confirmation that they have the rights to that face (paid plans, like every real face). Never set it on your own; the other choices that refusal names are a different model (`model`) or another creator (`creator`).
textStyleNoTHE LOOK of captions and the end card, only with captions or endCard and only when the user described one: the look in WORDS ("chunky yellow comic letters, purple outline"), a preset (editorial: big serif title + small italic line; bold: condensed caps, outline; minimal; handwritten; boxed; pill, the default), or fields. "TITLE · small line" puts the part after the dot on a second line.
resolutionNo'1080p' default (what we ship and bill for); '480p'/'720p' = cheaper draft passes, '4k' = premium final delivery (more credits). NOT EVERY MODEL OFFERS EVERY TIER — this enum is what the tool accepts, and each model's OWN `resolutions` list in hermoso_capabilities is what it can actually render (the longest-clip 30s model, for one, tops out at 720p). Ask for a tier the chosen model does not list and it is rendered at that model's best available tier instead, with nothing in the reply saying so — so check `resolutions` before promising anyone 1080p or 4k.
aspectRatioNooutput aspect ratio, e.g. 9:16 (default) / 1:1 / 16:9
foundationsNoproduct commercial / cinematic: 'choose' paints ONLY the foundations (identity sheet + four-up moodboard, or hero + location) and returns them, no video — then call again with moodboardPick + foundationImages
moodboardPickNowhich moodboard panel (1-4, left to right, top to bottom) the storyboard follows; default 1
durationSecondsNototal ad length in seconds (supported range 4–180; outside that it is clamped). Omit to honor the plan’s own duration — that is almost always right. This only RE-TIMES an already-authored board (its scenes are scaled to fit), it does NOT re-write it, so to change the length of the ad the user asked for, re-run plan_ad with durationSeconds instead. A length that fits ONE clip of the render model renders as one continuous pass; longer is stitched from acts filled to that model’s clip maximum with the remainder last — the maximum is 15s on most models and 30s on the longest-clip one, so use dryRun:true to see the exact act split for free before spending.
foundationImagesNoimages a foundations:'choose' call returned, reused as-is (not repainted)
allowGenericProductNoproceed even though this brand has NO product photo on file and the ad features a product — the packaging will be INVENTED. Only pass true after telling the user that and hearing they are fine with a generic stand-in

TDQS

A4.5/5.0
Behavior5/5

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

Annotations already declare write/open-world/non-idempotent behavior, but the description adds critical operational detail: renders take 1-3 minutes and require polling get_job, credits are spent, dryRun is free, unsupported resolution tiers silently fall back, faceRoute requires explicit user confirmation, and creator casting can change cost/availability. These are meaningful behavioral disclosures beyond 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.

Conciseness3/5

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

The description is front-loaded with the key recommendation, but it is extremely long, dense, and repetitive, especially around act splitting, dryRun behavior, and default flags. Some repetition is justified by tool complexity, but the paragraph could be structured more cleanly.

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 the tool's complexity, 19 parameters, and lack of an output schema, the description is highly complete. It covers routing decisions, timing, polling, credits, defaults, safety refusals, and casting behavior, giving an agent enough context to call the tool correctly.

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

Parameters3/5

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

Schema description coverage is 100%, so the schema already documents all 19 parameters in detail. The description reinforces some cross-parameter workflows, such as passing plan_ad output as creative or using dryRun to preview act splits, but much of its parameter detail duplicates rather than extends the schema.

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 recommendation and verb-resource pair: 'RECOMMENDED for finished video ADS: render a plan_ad concept through the SAME quality pipeline as the Hermoso web Studio.' It clearly distinguishes this from planning (plan_ad) and generic video generation by describing the finished-ad render pipeline and its output types.

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?

The description gives explicit when-to-use guidance, including 'RECOMMENDED for finished video ADS,' how to pass plan_ad's structured output, when to use dryRun before spending, when to cast a creator via list_creators, and when each rendering path applies. It also names related tools and conditions, such as using plan_ad to change duration rather than this tool.

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

report_bugReport a bugAInspect

Report a bug in Hermoso to the team. Use this when something in Hermoso genuinely misbehaves — a tool errors unexpectedly, returns a wrong or malformed result, a render comes back broken, or documented behaviour doesn't match what happened. Include what you were trying to do, the exact tool call and arguments, and what came back. Do NOT use it for out-of-credits, a policy refusal, or a missing capability (use request_feature for that). Free, no credits.

ParametersJSON Schema
NameRequiredDescriptionDefault
detailsYeswhat you were doing, the tool + arguments you called, what you expected, and what actually happened (paste the exact error)
summaryYesone-line summary of the bug
severityNohigh = blocks the task or loses paid work; medium = wrong output but workable; low = cosmetic

TDQS

A4.5/5.0
Behavior4/5

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

The description adds useful behavioral context beyond the annotations: it explicitly states the action has no credit cost ('Free, no credits') and clarifies that it reports to the team rather than fixing the issue. The annotations already indicate non-read-only, non-destructive, non-idempotent behavior, and the description does not contradict them.

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

Conciseness5/5

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

The description is compact and every sentence earns its place: a clear opening statement, concrete use-case examples, explicit submission guidance, exclusions with the alternative, and a cost note. It is front-loaded with the core purpose and then narrows to conditions and 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?

For a simple reporting tool with two required string parameters and one optional enum, the description covers when to use it, what to include, what not to include, and the cost implication. No output schema is needed for a submit-style tool, and the annotations plus schema fully cover the remaining semantics.

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

Parameters3/5

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

Schema description coverage is 100%, so the parameters are already well documented. The description's guidance about including what you were trying to do, the exact tool call, and what came back largely paraphrases the existing 'details' parameter description, so it adds limited new semantic value. It also does not explain the summary or severity parameters beyond what the schema already provides.

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 clearly states the action ('report a bug in Hermoso to the team') and gives a specific definition of what counts as a genuine bug: unexpected errors, wrong or malformed results, broken renders, or behavior that contradicts documentation. It also names its key sibling, request_feature, when describing what the tool is not for, helping an agent distinguish it from nearby capabilities.

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?

It explicitly tells agents when to use the tool ('when something in Hermoso genuinely misbehaves') and when NOT to use it ('out-of-credits, a policy refusal, or a missing capability'), pointing to request_feature as the alternative. This is exactly the level of routing guidance an agent needs.

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

request_featureRequest a featureAInspect

Ask the Hermoso team for a capability that doesn't exist yet. Use this when you need something Hermoso genuinely can't do — an unsupported platform or channel, a missing model, an export format, a tool that would have completed the user's task but isn't available. Say what the user was trying to achieve, not just the feature name — the use case is what gets built. Free, no credits.

ParametersJSON Schema
NameRequiredDescriptionDefault
detailsYeswhat the user was actually trying to achieve, why the current tools couldn't do it, and what you'd expect the capability to do
summaryYesone line: the capability you need

TDQS

A4.3/5.0
Behavior4/5

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

The annotations provide a baseline (readOnlyHint=false, openWorldHint=true, idempotentHint=false) but the description adds meaningful behavioral context: submitting a request to the Hermoso team, requiring the user's goal rather than just a feature name, and being 'Free, no credits.' No contradiction with annotations exists.

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

Conciseness5/5

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

The description is three compact sentences, front-loaded with purpose, then usage signals, then guidance on writing the request, then the cost implication. Every sentence earns its place and there is no redundant restating of the tool name or title.

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?

For a simple two-parameter request tool with no output schema, the description fully covers what the tool does, when to use it, what to include in the request, and the fact that it costs nothing. Nothing needed for correct invocation is missing.

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

Parameters3/5

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

Schema coverage is 100%, so both 'summary' and 'details' are already documented in the input schema. The description reinforces the important semantic of 'details' by explaining what the user was trying to achieve rather than just a feature name, but it does not go far beyond the schema, so the baseline 3 is appropriate.

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 uses a specific verb ('Ask the Hermoso team for a capability that doesn't exist yet') with a clear resource and scope. It identifies the exact kinds of missing capabilities (unsupported platform, missing model, export format, unavailable tool), making it easy for an agent to understand what this tool is for and distinguish it from operational siblings like report_bug or enable_tools.

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?

The description explicitly tells the agent when to use this tool: 'when you need something Hermoso genuinely can't do' and gives concrete examples. It does not explicitly name alternatives or state when not to use it (e.g., 'for bugs use report_bug'), but the usage context is clear enough that an agent can route to this tool confidently.

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

reschedule_postChange a scheduled postA
DestructiveIdempotent
Inspect

Change a post that is still QUEUED — move it to a different time, rewrite the caption, swap the media, add or drop a channel, or change which board / Page / company Page / listing it goes to. PASS ONLY WHAT CHANGES: an omitted field is left exactly as it was, and an explicit empty string CLEARS one (linkedinOrganizationId:"" moves a company-Page post back to the person’s own profile). The edited item is re-checked against the identical rules its create passed — visibility the channel can honour, per-channel length, media the channel can carry — so an edit can never slip past a refusal that a create would have caught. Get the id from list_scheduled. Something that already went out cannot be changed: a published post is edited or removed with manage_meta_post / manage_linkedin_post / delete_x_post, not rescheduled.

ParametersJSON Schema
NameRequiredDescriptionDefault
atNothe new time — ISO timestamp (2026-08-05T09:00:00Z) or epoch milliseconds. Must be in the future, at most 365 days out.
idYesthe scheduled post id from list_scheduled
linkNo
pollNoX — replaces the poll; an empty options list removes it.
tagsNoYOUTUBE — replaces the WHOLE tag list; an empty array [] clears the tags.
brandNoWHICH PROFILE this post is in — id or exact name from list_brands; needed when it is not the profile this connection is pinned to. A name that matches no profile, or two, is REFUSED.
eventNoGOOGLE BUSINESS — replaces the whole event record {title, startDate, startTime, endDate, endTime}.
offerNoGOOGLE BUSINESS — replaces the whole offer record {couponCode, redeemOnlineUrl, termsConditions}.
placeNoFACEBOOK — replaces the tagged place (the numeric id of its Facebook Page); an empty string removes it.
storyNoINSTAGRAM — true makes it a 24-hour Story, false an ordinary feed post. One image or one video, no carousel.
titleNoPINTEREST / YOUTUBE — replace the headline; "" clears it and goes back to deriving one from the caption
chatIdNoTELEGRAM — send it to a different chat, group or channel (@username or numeric id). It can be changed but never cleared: telegram cannot publish without one.
ideaIdNoshort id of the content-plan idea this post came from
pageIdNoFACEBOOK / INSTAGRAM / THREADS — publish from a different connected Page (list_meta_pages)
recipeNothe post's FORMAT id, e.g. "slideshow" or "imessage_chat" — post_performance groups by it, so reuse one id per format
threadNoX — replaces the WHOLE thread; an explicit [] drops back to a single post using the caption.
altTextNoACCESSIBILITY — replace the screen-reader description(s). A STRING describes every slide; an ARRAY describes them one at a time in slide order and REPLACES the whole list. "" clears it.
audioIdNoINSTAGRAM REEL — put one of Instagram’s OWN licensed music tracks under the Reel: the `id` search_instagram_audio returns. Instagram mixes it in when the Reel is published; nothing is downloaded or re-rendered. REELS ONLY; an empty string removes it, and only on an Instagram account connected through Meta (a Facebook Page with a linked Instagram), which is Instagram’s own rule — the Instagram connector cannot take it and is refused by name.
boardIdNoPINTEREST — move the Pin to a different board (list_pinterest_boards)
messageNoreplace the caption used for every channel that has no override
audienceNoFACEBOOK — replaces who can see the Page post {countries, regions, cities, minAge}; {} removes the limit.
captionsNoreplaces the WHOLE per-channel caption map — send every override you want to keep, not just the new one
channelsNoreplaces the channel list
coverUrlNoINSTAGRAM REEL — replaces the cover image url; an empty string removes it.
imageUrlNoswap the image; "" removes it
linkNameNoFACEBOOK — replaces the link preview headline; an empty string removes the override.
topicTagNoTHREADS ONLY — one topic tag, without the leading #.
videoUrlNoswap the video; "" removes it
xArticleNoX: replaces the X Article (title, headings); {} makes it an ordinary X post again.
audioNameNoINSTAGRAM REEL — replaces the audio track name; an empty string removes it.
coverAtMsNoTHE VIDEO COVER on every channel, as one frame in milliseconds (see schedule_post).
imageUrlsNoreplace the CAROUSEL slides, in order; an empty array [] drops the carousel and goes back to a single image. Omit to leave the slides exactly as they are. The edited item is re-checked against the same carousel rules the create passed, so adding a channel that cannot swipe is refused now rather than posting slide 1 later.
slideTextNoPINTEREST CAROUSEL ONLY — per-slide title / description / link, aligned to slide order.
topicTypeNoGOOGLE BUSINESS — the kind of Post; EVENT and OFFER both require `event`.
trialReelNoINSTAGRAM — replaces the trial-reel setting on a queued Reel (MANUAL or SS_PERFORMANCE); an explicit "" turns the trial off and it goes out as an ordinary Reel. Only takes effect while the post is still queued — a Reel already published cannot be converted into a trial.
yourBrandNoTIKTOK — the own-brand disclosure; false turns it off.
actionTypeNoGOOGLE BUSINESS — the call-to-action button; "" clears it.
locationIdNoGOOGLE BUSINESS — a different listing (list_business_locations)
madeWithAiNoX — the AI-media label; false turns it off.
visibilityNoNOTE: changing this without also naming visibilityByChannel clears any per-channel overrides, so "make it all draft" is not a no-op
aiGeneratedNoAI-CONTENT DISCLOSURE (Instagram / Facebook Reel is_ai_generated, TikTok is_aigc, YouTube containsSyntheticMedia). OMIT IT and the value already on the post stays; a post that never had one is decided from provenance at publish: a Hermoso render is declared AI-generated, media that came through upload_file or from an external URL (the user’s own photos or footage) is NOT. Pass true or false only to override.
audioVolumeNoINSTAGRAM REEL — how loud that track plays, 0–100 (Instagram’s default 100). Needs audioId.
communityIdNoX — the community to publish into; an empty string goes back to the main timeline.
descriptionNoYOUTUBE — replace the video description; "" clears it and the caption is used.
disableDuetNoTIKTOK VIDEO ONLY — block Duets.
linkPictureNoFACEBOOK — replaces the link preview image url; an empty string removes the override.
quotePostIdNoTHREADS ONLY — the id of the Threads post this one quotes.
shareToFeedNoINSTAGRAM REEL — whether the Reel also shows in the Feed grid.
thumbOffsetNoINSTAGRAM REEL — replaces the cover frame, in milliseconds; 0 removes it. Never together with coverUrl.
videoVolumeNoINSTAGRAM REEL — how loud the video’s own sound plays under the track, 0–100 (Instagram’s default 100; 0 = the track alone). Needs audioId.
callToActionNoFACEBOOK — replaces the button on the Page post; "" removes it.
countryCodesNoTHREADS ONLY — two-letter country codes limiting who can see the post.
optimizeCopyNofit the shared caption to each channel’s own rules when you schedule it (the fitted caption is stored on the scheduled post, so what you scheduled is what publishes) wherever no per-channel caption was written; a channel with its own caption is left exactly as written. Send false to switch it off on this item.
privacyLevelNoTIKTOK — WHICH PRIVACY LEVEL the post goes out at, in TikTok’s own vocabulary. TikTok requires the USER to choose this from the levels their own account allows: call tiktok_creator_info, show them the real options, and pass back the one they picked — never a default and never a guess, which TikTok refuses at init. Omit it and the post falls back to the coarse `visibility` (private -> SELF_ONLY, otherwise PUBLIC_TO_EVERYONE), which cannot express MUTUAL_FOLLOW_FRIENDS or FOLLOWER_OF_CREATOR at all. It must agree with the TikTok visibility (SELF_ONLY is the private one) and it is refused on a TikTok DRAFT, which carries no post info.
replyControlNoTHREADS ONLY — who may reply.
thumbnailUrlNoYOUTUBE: replace the custom thumbnail; "" goes back to a frame of the video, "auto" to YouTube’s pick.
xQuotePostIdNoX — the post this one QUOTES; an empty string removes the quote. Named apart from the Threads `quotePostId` on this same schedule.
collaboratorsNoINSTAGRAM — replaces the WHOLE collab list (up to 3 usernames); an explicit [] removes the co-authors and the post goes out as an ordinary single-author post. Only takes effect if the post has not fired yet — an invite already sent cannot be withdrawn from here.
coverImageUrlNoTHE VIDEO COVER on every channel, as a Hermoso-hosted picture (see schedule_post).
disableStitchNoTIKTOK VIDEO ONLY — block Stitches.
instagramPollNoINSTAGRAM — attach a POLL: 2 to 4 answers of 1–25 characters each, and the caption IS the question (so a caption is required). Feed photos, carousels and Reels only (never a story), one caption add-on per post (not with instagramCommentPrompt), and only on an account connected through Meta (a Facebook Page with a linked Instagram). WRITE-ONCE: Instagram cannot add, change or remove it after publishing, so show the user the answers first.
platformCoverNoVIDEO COVER. Omit it (the default) and Hermoso sets the video’s best frame — the same frame as its Library thumbnail — as the cover on every channel that allows one — and on X, Threads and Bluesky, which have none, a blank first frame is replaced on a copy sent there only. true = send no cover and let the platform pick (usually the first frame). A cover you pass yourself always wins.
replySettingsNoX — who may reply; "" goes back to everyone.
brandedContentNoTIKTOK — the paid-partnership disclosure; false turns it off.
disableCommentNoTIKTOK — comments off on this post.
linkAttachmentNoTHREADS ONLY — a full http(s) URL rendered as a link card on a TEXT-ONLY post. The only way a Threads post carries a destination.
targetAudienceNoLINKEDIN COMPANY PAGE — replaces who sees the post; {} removes the limit. The matching audience must be over 300 followers.
linkDescriptionNoFACEBOOK — replaces the link preview description; an empty string removes the override.
paidPartnershipNoINSTAGRAM AND X — the paid-partnership label; false turns it off.
callToActionLinkNoFACEBOOK — replaces where the button goes; an empty string falls back to the post link.
coverTimestampMsNoTIKTOK VIDEO ONLY — cover frame in milliseconds.
crossreshareToIgNoTHREADS ONLY — also share to Instagram as a Story when it fires; false turns it off. Refused on a carousel.
commercialContentNoTIKTOK — the COMMERCIAL CONTENT DISCLOSURE toggle: true when this post promotes a brand, product or service at all. TikTok requires at least one of yourBrand / brandedContent once it is on, and refuses a post that declares itself commercial without naming which kind. Either disclosure already implies it.
instagramLocationIdNoINSTAGRAM — replaces the tagged place (the numeric id of its Facebook Page); an empty string removes it. Not locationId, which is the Google Business listing.
visibilityByChannelNo
crossreshareDarkModeNoTHREADS ONLY — dark-mode that Instagram Story. Needs crossreshareToIg.
instagramPollExtendedNoINSTAGRAM — give that poll Instagram’s extended voting duration instead of its default 3 days (Instagram’s changelog says it then stays open indefinitely). Only with instagramPoll.
instagramCommentPromptNoINSTAGRAM — make the caption a COMMENT PROMPT that people answer in the comments. Same limits as instagramPoll, and never together with it.
linkedinOrganizationIdNoLINKEDIN — target a different company Page, or "" to post as the connected person instead
brandedContentSponsorIdsNoINSTAGRAM — replaces the sponsor user ids behind the paid-partnership label (at most 2); [] removes them.

TDQS

A4.8/5.0
Behavior5/5

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

Annotations already declare destructiveHint, idempotentHint and openWorldHint, but the description adds substantial behavioral context beyond them: patch semantics (omitted = unchanged, explicit empty string clears, with a concrete linkedinOrganizationId example) and the guarantee that the edit is re-validated against the same create-time rules so it cannot bypass a refusal. This is real disclosure the annotations do not carry.

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 action, then patch semantics, then the validation guarantee, then the exclusion. Dense and nearly waste-free, though the create-time re-validation point is restated in the imageUrls schema description.

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?

For an 80-parameter mutation tool with no output schema, the description covers the things an agent must know: source of the id, patch/clear conventions, per-channel constraints being re-checked, and the boundary against published-post tooling. Nothing critical to calling it correctly is missing.

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 98% across 80 parameters, so per-field meaning is already documented in the schema and the baseline is 3. The description nonetheless adds a cross-cutting semantic rule (PASS ONLY WHAT CHANGES; empty string/empty array clears) plus examples that govern how every optional field should be interpreted, which is genuine value beyond the schema.

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 ('change a post that is still QUEUED') and enumerates the dimensions it edits: time, caption, media, channels, board/Page/listing. It explicitly distinguishes itself from post_edit, cancel_scheduled and schedule_post by naming the lifecycle stage it operates on.

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?

Gives explicit when-to-use ('still QUEUED', id comes from list_scheduled) and when-not-to-use ('a published post is edited or removed with manage_meta_post / manage_linkedin_post / delete_x_post, not rescheduled'). The alternative tools are named, so the agent can route without inference.

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

research_adsResearch adsAInspect

Open-ended ad research that needs JUDGMENT across platforms — comparisons, "what angle is working", "who else is doing this", anything where the right sources are not known up front. It is an agentic loop (several rounds of library pulls plus a written synthesis) and typically takes 30-60 seconds, so it is the WRONG tool for a question that names its own answer. For one named brand’s live ads use pull_competitor_ads; for one keyword or one advertiser on Meta use search_meta_ads — both are a single call and return in a few seconds. Spends credits — an agentic loop, so a handful rather than the one-call cost of a targeted search.

ParametersJSON Schema
NameRequiredDescriptionDefault
brandNobrand name or profile object to tailor the research to; omit to use the workspace’s saved brand
queryYeswhat to research, e.g. "the longest-running protein-pancake ads on Meta"

TDQS

A4.5/5.0
Behavior5/5

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

Annotations declare readOnlyHint=false, openWorldHint=true and idempotentHint=false, but do not explain why. The description supplies the missing behavior: it is an agentic loop of several library pulls plus a written synthesis, runs 30-60 seconds, and spends credits (a handful rather than a single-call cost) — which is exactly what justifies the non-read-only, non-idempotent profile. Return shape ('written synthesis') is also disclosed.

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 use case, then tradeoffs and routing, with zero filler sentences. It is dense and slightly long with nested quoted examples, but each sentence contributes either a routing rule or a behavioral fact.

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 an open-world agentic research tool with no output schema, the description covers cost, runtime, loop structure, and what comes back, plus sibling routing. Only minor gaps remain, such as how results are scoped or paginated, which are not essential to calling it correctly.

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

Parameters3/5

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

Schema description coverage is 100%, so both parameters are already documented in the schema, and the description adds no format, syntax, or defaulting detail for query or brand. Baseline 3 is appropriate when the schema carries the parameter burden.

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 ('open-ended ad research') and immediately scopes it with the judgment-based case it serves ('what angle is working', 'who else is doing this'). It positively distinguishes itself from the two nearest siblings, pull_competitor_ads and search_meta_ads, so an agent can route without opening 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 Guidelines5/5

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

Gives explicit when-to-use ('needs JUDGMENT across platforms', right sources unknown up front) and when-not ('the WRONG tool for a question that names its own answer'), then names the two alternative tools with the exact condition that selects each. The latency and cost tradeoff is stated as the selection axis, leaving nothing to inference.

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

resize_adResize a static ad for other placementsAInspect

Re-lay out ONE finished static ad for other placements: the same ad, product, copy (word for word), logo and style, recomposed natively for each canvas rather than cropped. Pass image and optionally aspectRatios from 1:1, 4:5, 9:16, 16:9, 3:4, 4:3 (default 1:1, 4:5 and 9:16; the ad's own ratio is skipped). Reads the ad's text first (3 credits) so every line survives, then one image edit per canvas. When the saved brand has a product photo and the ad shows that product, the photo rides with the edit and the product's label is read and checked against it: re-printed from the photo only when it came out wrong (the check alone is charged when it is right; one small extra charge finds the product first). fixLabel:false turns that off. Priced before it runs. For VIDEO use reframe_video.

ParametersJSON Schema
NameRequiredDescriptionDefault
imageYesthe finished static ad: URL, Library item URL, upload_file URL or local path
fixLabelNofalse = leave the product label exactly as rendered: no product lookup, no label check, no re-print (default on when the ad shows the brand’s saved product)
aspectRatiosNotarget canvases (default 1:1, 4:5, 9:16)

TDQS

A4.7/5.0
Behavior5/5

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

Annotations declare non-read-only, open-world, non-idempotent, non-destructive, and the description goes well beyond them: credit costs (3 credits for text read, one image edit per canvas, label-check and product-lookup charges), the fact it is priced before running, the sequencing of text extraction before edits, and the conditional product-photo behavior. This is unusually rich disclosure of side effects and cost.

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 purpose and the video exclusion, and every clause carries information. Some sentences are heavily parenthetical and dense ('the check alone is charged when it is right; one small extra charge finds the product first'), which costs readability but not correctness.

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 3-parameter, no-output-schema tool, the description covers cost, defaults, conditional behavior, and the video alternative. The one gap is that with no output schema it does not state what is returned (e.g., rendered image assets), leaving the agent to infer the result shape.

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 baseline is 3, but the description adds real semantics beyond the schema: the default set of ratios, that the source ratio is skipped, and the conditional default-on behavior of the label fix tied to the saved brand product. It does not, however, add format detail like allowed input image types beyond what the schema lists.

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 precise verb and resource ('re-lay out ONE finished static ad for other placements') and immediately disambiguates the operation from cropping by describing native recomposition. It also separates itself from the video sibling by naming reframe_video. An agent can identify what this does without opening the 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?

Explicitly routes the agent away from this tool for video ('For VIDEO use reframe_video') and states the alternative-ratio condition (the ad's own ratio is skipped). It also gives the opt-out condition for the label workflow via fixLabel:false, covering both when-to-use and when-to-disable.

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

restyle_videoRestyle a videoAInspect

RESTYLE a clip into a new LOOK: every frame redrawn, shots, motion, camera, framing, timing and sound kept. Looks: Restyle looks in hermoso_capabilities (claymation, knitted yarn, cel-shaded CG anime…), your own words, or a look BUILT FROM YOUR IMAGES (styleImages); characters draws your own characters in. engine auto (default) picks by what is in the clip: a real-looking person -> kling (Kling O3 Edit, 3-15s, the strongest look on a face); none -> wan (Wan 3.0 Prime, ≤15s) else seedance (Seedance 2.5, 4-30s). Paid; dryRun quotes it. One change only: edit_video.

ParametersJSON Schema
NameRequiredDescriptionDefault
styleNoa look id from hermoso_capabilities (e.g. claymation, knitted-yarn, cel-shaded-anime-cg)
videoYesthe source video URL (a render, job result or list_library)
engineNodefault auto
describeNoa look in the user’s own words, when no preset fits, or extra detail on top of a preset
faceRouteNo'face_lane' = the user's "I own the rights to this face" for a real person in the source clip (paid plans). Only on the user's say-so, never on your own.
charactersNocharacters to draw the people as. Using a real person’s face, likeness or voice confirms you are them or have their consent, take full, unlimited responsibility for its use and will cover any claim against Hermoso (hermoso.ai/terms). Paid plan only; recorded.
resolutionNoSeedance and Wan; default 1080p
styleImagesNopictures that ARE the look (their style, never their subject)

TDQS

A4.7/5.0
Behavior5/5

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

Annotations only give the generic readOnly/idempotent/destructive profile; the description adds the operation is Paid with dryRun quoting, the deterministic auto engine-selection rules with duration limits per engine, that faceRoute requires explicit user consent and paid plans, and that characters carries a recorded legal-liability declaration. This is meaningful behavioral context not derivable from structured fields.

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?

Dense and front-loaded: the core verb and preservation rule come first, then look options, then engine routing, then cost and the sibling. Heavy caps and slash-shorthand make it slightly cryptic, but nearly every clause carries actionable information.

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 an 8-parameter, no-output-schema mutation tool, it covers cost, consent gating, engine selection, and look inputs well. It does not say what the call returns (job handle vs. final video) or whether the operation is asynchronous, which would help an agent chain follow-up calls such as get_job.

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 baseline is 3, but the description adds real meaning: it disambiguates the three look-specifying paths (style, describe, styleImages), explains what auto resolves to and why, and encodes the consent precondition on faceRoute. It omits resolution defaults and how describe composes with a preset, so it falls short of a 5.

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+resource ('RESTYLE a clip into a new LOOK') and immediately bounds what is preserved (shots, motion, camera, framing, timing, sound) versus what changes (every frame redrawn). It also explicitly separates itself from the nearest sibling, edit_video, so an agent can route without opening either 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 selection rules: three ways to specify a look (a preset id via hermoso_capabilities, own words via describe, or styleImages), engine auto-routing logic keyed to clip content, and the hard exclusion 'One change only: edit_video.' When-not-to-use is stated, not implied.

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

retry_scheduledRetry a failed scheduled postA
Destructive
Inspect

Send a post that FAILED again. A scheduled post fans out across its channels INDEPENDENTLY, so a failure is usually PARTIAL — LinkedIn 401s while Instagram published fine — and this re-fires ONLY the channels that did not succeed by default (list_scheduled reports them as retryable). It re-queues the same content as a NEW post that goes out RIGHT AWAY — the queue picks it up on its next pass, within seconds — and the original keeps its failure record so the history still shows what went wrong. Naming a channel that already published is REFUSED rather than quietly posting a second time. Two independent belts stop a double-post: a channel that genuinely published can only REPLAY (nothing is posted), and a channel whose outcome is UNRESOLVED — the platform timed out and may be holding the post — refuses with that reason instead of guessing. Retry after fixing the cause — and you can fix it IN THIS CALL: pass boardId, pageId, linkedinOrganizationId, locationId, message or captions to correct the value that failed, and the corrected post is re-validated exactly like a fresh schedule. That matters because the commonest cause is a field, not an outage: a Pin aimed at the wrong board fails identically however many times it is re-sent. Anything you do not name is copied from the original. To send the same thing again ON PURPOSE, use duplicate_scheduled.

ParametersJSON Schema
NameRequiredDescriptionDefault
atNohold the retry until a later time — ISO timestamp or epoch milliseconds. Leave it out to retry immediately, which is almost always what you want. A time you name here must be at least a minute from now, exactly like any other scheduled post.
idYesthe scheduled post id from list_scheduled
brandNoWHICH PROFILE this post is in — id or exact name from list_brands; needed when it is not the profile this connection is pinned to. A name that matches no profile, or two, is REFUSED.
chatIdNoCORRECT THE TELEGRAM DESTINATION on retry — the @username or numeric id of the chat. A post aimed at a chat the bot is not in fails every time it is retried until this changes.
pageIdNoCORRECT THE PAGE on retry — which connected Facebook Page (and its linked Instagram/Threads) publishes, from list_meta_pages.
boardIdNoCORRECT THE BOARD on retry — the Pinterest board the Pin goes on, from list_pinterest_boards. A Pin aimed at a board Pinterest refuses fails the same way on every retry until this is changed.
messageNoCORRECT THE CAPTION on retry — use this when the original was refused for length or content. Anything not named here is copied from the original post.
captionsNoCORRECT ONE CHANNEL’S CAPTION on retry, e.g. { "x": "..." } when only that channel refused the text.
channelsNoretry only these channels (default: every channel that did not publish)
locationIdNoCORRECT THE LISTING on retry — which Google Business Profile location, e.g. "locations/123" from list_business_locations.
allowDuplicateNoONLY for a channel you have checked by hand and confirmed the post is genuinely NOT there. It bypasses the double-post protection and can publish a second public copy, so never set it to work around a refusal you have not investigated.
linkedinOrganizationIdNoCORRECT THE LINKEDIN AUTHOR on retry — the company Page id from list_linkedin_pages. Set it to an empty string to fall back to the personal profile.

TDQS

A4.8/5.0
Behavior5/5

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

Annotations only give destructiveHint=true, idempotentHint=false, openWorldHint=true; the description goes far beyond by disclosing partial fan-out, that only non-succeeded channels re-fire by default, that it creates a NEW post sent immediately, that the original keeps its failure record, and the two independent double-post protections (replay vs unresolved) plus the allowDuplicate bypass.

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 action and the partial-failure model, and nearly every sentence carries behavioral information. However it is a dense ~250-word block with heavy ALL-CAPS emphasis, which costs some scannability even though little is strictly redundant.

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?

For a 12-parameter, non-idempotent, destructive mutation tool with no output schema, the description covers outcome semantics (new post, picked up within seconds), history behavior, refusal reasons, and the duplicate alternative, leaving nothing an agent needs to call it correctly unstated.

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 adds real value: it frames boardId/pageId/linkedinOrganizationId/locationId/message/captions as in-call corrections, explains that unnamed values are copied from the original, that corrections are re-validated like a fresh schedule, and that the commonest failure is a bad field rather than an outage.

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 specific verb+resource ('Send a post that FAILED again') and immediately scopes it as retrying a failed scheduled post, distinguishing it from schedule_post, reschedule_post, cancel_scheduled and duplicate_scheduled without needing the 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?

Explicitly states when to use it ('Retry after fixing the cause'), when not to ('To send the same thing again ON PURPOSE, use duplicate_scheduled'), and points to list_scheduled as the source of `retryable` channels. Alternatives and selection conditions are spelled out.

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

run_post_refillRun autopilot now / approve its draftsA
Destructive
Inspect

Run AUTOPILOT POSTING (autoposting) NOW instead of waiting for its daily turn, or REVIEW the drafts it made — "approve them all", "delete that one", "change the caption", "too salesy, remember that". PREVIEW BY DEFAULT: it returns the posts it WOULD make — each new picture’s scene, the caption, the channels and the price — rendering nothing and queueing nothing. The preview still WRITES the scenes and copy with a model, which bills a few credits (the reply says how many). dryRun:false actually renders a fresh image or video for each post within the budget (credits are spent) and then, by mode, schedules them ("auto") or keeps them as drafts ("review"). It never re-posts the Library. REVIEWING A BATCH: pass approve (draft ids, or ["all"]) to schedule drafts, discard to delete them, edit ([{id, message?, captions?, title?, at?, channels?}]) to change one first (a new at reschedules it); get_post_refill lists them. learn saves notes future batches follow (free). redo remakes ONE draft from a note: PAID, so without confirm:true it returns only the price; confirm only on the user’s yes. A scheduled post can still be pulled before it goes out with cancel_scheduled. Every caption is screened against the voice rules and a failing one is dropped, so a plan can come back shorter than the cadence — the reason is in the notes.

ParametersJSON Schema
NameRequiredDescriptionDefault
editNoREVIEW: change drafts before approving them. Edited copy is screened by the voice rules.
redoNo
forceNoplan a preview even while autoposting is switched off — useful for showing someone what it would do before they turn it on.
learnNonotes for future batches, e.g. "too salesy". Free.
dryRunNodefault TRUE (a preview: nothing rendered, nothing queued; the copy it writes bills a few credits). false renders fresh creative and schedules or drafts it per the mode.
approveNoREVIEW: draft ids to schedule, or ["all"]. A draft whose time has passed moves to the next free posting slot.
discardNoREVIEW: draft ids to delete, or ["all"]. Nothing is posted for them.

TDQS

A4.8/5.0
Behavior5/5

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

Goes well beyond the annotations (which only flag destructive/openWorld/non-idempotent). It discloses that preview is the default, that even a preview bills a few credits via the model, that dryRun:false actually spends credits to render, that redo is PAID and gated by confirm:true, that learn is free, and that captions failing voice rules are dropped — exactly the credit/auth/destruction context annotations cannot convey.

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?

The core action is front-loaded and the batch-review verbs are grouped logically. It is dense and somewhat long with repeated credit/draft phrasing, but nearly every clause carries operational information, so little is waste.

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?

For a 7-param, nested-object, no-output-schema tool, the description covers what comes back (scene, caption, channels, price, credit count), the default preview vs real render, scheduling/draft modes, and the caption-screening edge case — nothing an agent needs to call it correctly is missing.

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 86%, so the schema carries most param meaning. The description still adds value by explaining mode-dependent behavior of dryRun, the ["all"] shorthand for approve/discard, the paid vs free distinction between redo and learn, and that a new `at` reschedules a draft.

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 specific verbs and resources: run autoposting now, or review/approve/discard/edit its drafts. It explicitly distinguishes itself from siblings (get_post_refill lists drafts, cancel_scheduled pulls a scheduled post), so an agent can route correctly without opening other schemas.

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 when-to-use (skip the daily turn, review a batch) plus named alternatives for adjacent needs (get_post_refill for listing, cancel_scheduled for pulling). It even says force is for previewing while autoposting is off, which is a precise conditional.

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

save_creatorSave a creatorAInspect

Add a portrait to this workspace’s reusable CAST so the SAME person can star in future ads — the headless twin of the app’s + > Pick a creator > save. Pass the portrait’s public url (a generate_image render of an AI person, or a photo of a real person you have permission to use, or of yourself; never a photo just because it is public) plus a name to call them by; from then on list_creators returns them and their url can be re-passed to generate_avatar / generate_video / recast_motion. Saving is FREE and renders nothing. LIKENESS: leave source "generated" for an AI-made person (free on every plan) and use "upload"/"social" for a REAL person. Using a real person’s face, likeness or voice confirms you are them or have their consent, take full, unlimited responsibility for its use and will cover any claim against Hermoso (hermoso.ai/terms). Paid plan only; recorded.

ParametersJSON Schema
NameRequiredDescriptionDefault
lookNotheir canonical wardrobe/appearance in words — reused to hold the look steady across ads
nameYeswhat to call this creator (e.g. “Sarah”) — list_creators and the app’s picker match on it
brandNoprofile id/name from list_brands, this call only
imageNoREQUIRED except with useAnyway. public https url of the portrait (an existing render’s url, or any public photo). Not a local file path — upload it with upload_file first and save the url that returns
posesNoup to 4 extra full-body / angle plates of the SAME person (public urls) — they make a wider shot hold the identity
voiceNoa default voice name for this persona (engines + voices are in hermoso_capabilities)
sourceNo"generated" (default) = an AI-made person; "upload" / "social" = a REAL person
useAnywayNoonly for a creator whose saved photo was flagged too unclear to cast (render_ad says so): true casts the current photo as it is, no new image needed

TDQS

A4.5/5.0
Behavior5/5

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

Beyond the annotations, it discloses that saving is FREE and renders nothing, that it is paid-plan only, that the action is recorded, and it carries the likeness/consent legal terms and responsibility. That is substantial behavioral context an agent would not get from readOnlyHint/destructiveHint alone.

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 front-loaded with purpose before the mechanics, and most clauses earn their place (cost, source semantics, consent). However it is delivered as one dense run-on paragraph with heavy em-dashes, which reduces scannability for an 8-parameter tool.

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 mutation tool with no output schema, it covers cost, plan gating, downstream consumption via list_creators, and the consent obligation, which is close to complete. It does not describe the failure/flag path in detail (only useAnyway references render_ad flagging), leaving a small gap.

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 adds real meaning: the public-https-url constraint and 'not a local file path' rule for image, the source enum's real-vs-AI distinction tied to consent, and how name/look are reused. It stops short of clarifying every field (brand, voice, poses beyond a mention), so it is above baseline but not exhaustive.

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 names a concrete verb+resource ('Add a portrait to this workspace's reusable CAST') and states the scope precisely, including that it is the headless twin of a specific app action. It implicitly separates itself from list_creators (downstream read) and generate_avatar/generate_video/recast_motion (consumers of the saved url), so an agent can place it without opening schemas.

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 condition to use it (so the SAME person can star in future ads), an explicit when-not ('never a photo just because it is public'), and routes local files through upload_file first. It does not contrast with the sibling update_saved_creator or find_creators, so the alternative-selection guidance is strong but not exhaustive.

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

save_playbookSave a playbookAInspect

Save a reusable PLAYBOOK — the strategy takeaways worth re-running: the hooks that work, the angles, the formats, and the concrete plays. Use it to keep what a competitor_teardown or mine_angles just found, or to bank a creative you want to repeat. Lands in the same Playbooks library the web app lists, runs and manages. Distinct from save_skill (a directive applied to every ad) and from the swipefile (raw saved creative). Free.

ParametersJSON Schema
NameRequiredDescriptionDefault
nameYesthe playbook headline — what it is, in a few words
brandNoprofile id/name from list_brands, this call only
hooksNothe opening hooks worth reusing, verbatim
playsNothe concrete plays to run ({title, detail}) — the actionable half
anglesNothe persuasion angles ({title, detail})
sourceNowhere it came from, e.g. “teardown · Ridge”
formatsNothe formats/recipes this plays best in (e.g. ugc_selfie, cinematic, static)

TDQS

A4.4/5.0
Behavior4/5

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

Annotations already declare the write operation is non-destructive, non-idempotent, and closed-world. The description adds value beyond that by disclosing that the playbook lands in the same Playbooks library shown in the web app, that it is reusable, and that it is free — useful post-save and cost context. It stops short of describing failure modes or auth needs, but the annotation bar is lower here.

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 purpose and content, followed by usage, landing location, sibling distinctions, and cost. Every sentence earns its place, though the opening list is slightly dense and the overall description is longer than strictly necessary.

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?

For a 7-parameter write tool with full schema coverage and annotations covering the safety profile, the description supplies the missing context: what a playbook is, when to save one, where it goes, how it differs from siblings, and that it is free. No output schema exists, but the description tells the agent what happens after saving, which is sufficient.

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

Parameters3/5

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

Schema description coverage is 100%, so the schema already documents every parameter thoroughly, including hooks, plays, angles, and formats. The description adds conceptual framing by grouping these as the elements of a playbook, but it does not add syntax, format, or validation detail beyond what the schema provides. Baseline 3 is appropriate.

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 (save) and resource (playbook), then defines the content in concrete terms: hooks, angles, formats, and plays. It explicitly distinguishes itself from siblings save_skill and the swipefile, so an agent can tell it apart without opening 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 Guidelines5/5

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

Gives explicit when-to-use guidance: 'Use it to keep what a competitor_teardown or mine_angles just found, or to bank a creative you want to repeat.' It also names the alternatives (save_skill, swipefile) and the condition that separates them, leaving nothing to inference.

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

save_skillSave a skillAInspect

Save a reusable custom SKILL — a named creative directive/playbook applied to future ads (a hook formula, a UGC recipe, a compliance rule, a named specialist persona like “our founder-story style” or “short-form ad strategist”). Distill an imperative, self-contained directive. Merges into the workspace Skills library (list_skills shows built-ins + your custom skills).

ParametersJSON Schema
NameRequiredDescriptionDefault
nameYesshort skill name, e.g. “Founder-story hook”
brandNoprofile id/name from list_brands, this call only
directiveYesthe full instruction the skill applies when used (1–6 sentences, imperative)

TDQS

A3.9/5.0
Behavior4/5

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

Annotations declare the write profile (readOnlyHint=false, destructiveHint=false, idempotentHint=false), so the safety burden is partly lifted. The description adds real context beyond that: it explains the effect is a merge into the workspace Skills library (additive, not a wholesale overwrite) and routes the agent to list_skills. It does not clarify what happens on a same-name repeat call, which idempotentHint=false makes relevant.

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?

Purpose is front-loaded in the first clause with the parenthetical examples earning their place by disambiguating the concept. It is slightly dense with em-dashes but contains no filler sentences.

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 3-parameter save tool with full schema coverage, no output schema (and none needed), and annotations covering safety, the description supplies the concept, the effect, and a related sibling. The only gap is same-name/idempotency behavior, which is minor given the overall completeness.

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

Parameters3/5

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

Schema coverage is 100%, so the schema already documents all three parameters (name, brand, directive) with examples. The description reinforces the expected form of the directive (imperative, self-contained) but adds little syntax or format detail beyond what the schema states, so baseline 3 applies.

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+resource ("Save a reusable custom SKILL") and defines what a skill is with concrete examples (hook formula, UGC recipe, compliance rule, persona). This clearly distinguishes it from siblings like get_skill, list_skills, and delete_skill without opening 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 Guidelines3/5

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

Implies usage (creating a reusable directive for future ads) and gives a formulation hint ("Distill an imperative, self-contained directive"), and references list_skills as the inspection counterpart. However, it never explicitly states when to prefer this over siblings like save_playbook or remember, nor any prerequisites or exclusions.

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

save_to_driveSave file(s) to Google DriveAInspect

Save a Hermoso render — or ANY file — into the user’s connected Google Drive. Pass a Hermoso render URL as url (or urls[] for several); for a local/external file, call upload_file first and pass the url it returns. Optional folder (created if new) + name. Returns the Drive file(s) with a webViewLink. Needs Google Drive connected (Settings > Connectors > Google Drive — one connection covers Drive, Sheets and Docs). NOTE: Hermoso uses the drive.file scope, so it reaches ONLY the files it created plus any the user explicitly handed over with the Google file picker in the app — never their whole Drive.

ParametersJSON Schema
NameRequiredDescriptionDefault
urlNoa single Hermoso render URL to save
nameNofile name (single save)
urlsNoseveral render URLs (up to 20) to save in one call
folderNoDrive folder name to save into (created if new)

TDQS

A4.7/5.0
Behavior5/5

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

Annotations indicate a non-read-only, non-idempotent, non-destructive operation, and the description adds substantial context beyond that: the return value is a Drive file with a webViewLink, Google Drive must be connected, and the drive.file scope restricts access to only files Hermoso created or the user explicitly shared. This is exactly the kind of behavioral disclosure that prevents misuse.

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

Conciseness5/5

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

The description is front-loaded with the core purpose, then proceeds through input modes, optional parameters, return value, and prerequisites. Every sentence adds operational value, including the scope warning, and none is redundant with the schema or annotations.

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?

For a mutation tool with no output schema, the description covers inputs, parameter roles, return information, setup prerequisites, and access limitations. An agent has enough context to call it correctly and to avoid the common mistake of assuming full Drive access.

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?

The input schema already documents all four parameters and covers them 100%, so the baseline is strong. The description adds meaningful semantics by explaining how url and urls[] relate to Hermoso render URLs, how to handle local/external files via upload_file, and that folder is created if new. This enhances, rather than merely repeats, the schema descriptions.

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 action and resource: 'Save a Hermoso render — or ANY file — into the user's connected Google Drive.' It clearly explains both the single and batch modes and explicitly names the alternative workflow for local files. The destination (Google Drive) distinguishes it from sibling tools like save_to_onedrive and save_to_swipefile.

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?

The description gives concrete usage context: when the input is a Hermoso render URL, pass it directly; for other files, call upload_file first and use its returned URL. It also spells out a required prerequisite (Google Drive connection) and a scope limitation. It does not explicitly contrast with OneDrive or Swipefile siblings, so it stops short of full when-not guidance.

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

save_to_onedriveSave file(s) to OneDriveAInspect

Save a Hermoso render — or ANY file — into the user’s connected Microsoft OneDrive. Pass a Hermoso render URL as url (or urls[] for several); for a local/external file, call upload_file first and pass the url it returns. Optional folder (created if new) + name. Returns the OneDrive file(s) with a webViewLink. Needs OneDrive connected (Settings > Connectors > OneDrive).

ParametersJSON Schema
NameRequiredDescriptionDefault
urlNoa single Hermoso render URL to save
nameNofile name (single save)
urlsNoseveral render URLs (up to 20) to save in one call
folderNoOneDrive folder name to save into (created if new)

TDQS

A4.3/5.0
Behavior4/5

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

Annotations already indicate a mutating operation (readOnlyHint=false), and the description adds non-obvious behaviors: folder creation if new, the return of webViewLink, and the OneDrive connector requirement. It does not contradict annotations and provides useful side-effecting context beyond the schema.

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?

Four sentences cover the core purpose, parameter usage, return value, and prerequisite without redundancy. The structure is logical, though slightly longer than strictly necessary. Every sentence contributes, earning a high score.

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?

The description covers key usage flows, prerequisites, and the return value despite lacking an output schema. It handles batch saves via urls[] and the local-file path via upload_file. Minor gaps like behavior on duplicate filenames keep it from being fully complete.

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?

Since schema coverage is 100%, the baseline is 3. The description adds meaningful semantics by explaining that url can be a Hermoso render URL or the result of upload_file for local/external files, and clarifies that folder is created if new. This goes beyond the schema's descriptions.

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 leads with a specific verb and resource: "Save a Hermoso render — or ANY file — into the user's connected Microsoft OneDrive." It distinguishes this tool from siblings like save_to_drive (Google Drive) and upload_file by explicitly naming the destination and the prerequisite flow for local/external files.

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 clear context on when to use this tool: for saving render URLs or any file to OneDrive, and explicitly instructs to call upload_file first for local/external files. It also states a prerequisite (OneDrive connected), though it does not explicitly list exclusions or alternatives such as save_to_drive for Google Drive.

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

save_to_swipefileSave ads to the swipefileA
Idempotent
Inspect

Save one or more ads/creatives to a named SWIPEFILE collection, creating the collection if it does not exist — the headless twin of the heart on every ad card in the web app. Use it whenever research turns up something worth keeping: a competitor ad from search_meta_ads / pull_competitor_ads, an organic post, or one of your own renders. Saved ads persist to the workspace board the web Swipefile tab shows, and feed the taste signal every future ad is planned against. De-dupes: re-saving the same ad (same key/link/media) MOVES it into the named collection instead of duplicating it. Free.

ParametersJSON Schema
NameRequiredDescriptionDefault
brandNoprofile id/name from list_brands, this call only
itemsYesthe ads to save
collectionYesthe collection name — an existing one, or a new one to create

TDQS

A4.4/5.0
Behavior5/5

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

Goes well beyond the annotations: discloses that the collection is auto-created if missing, that de-dup re-saving MOVES the ad rather than duplicating it, that saves persist to the workspace board the web tab shows, and that it feeds the taste signal. These are meaningful behavioral traits not derivable from readOnlyHint/idempotentHint alone.

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 action and the de-dup caveat, and every functional sentence earns its place. Some marketing phrasing ('headless twin of the heart', 'feeds the taste signal') adds flavor at modest length cost, keeping it just below maximally tight.

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?

For a 3-parameter, non-destructive write tool with annotations and no output schema, the description covers creation, de-duplication, persistence, and cost. Nothing an agent needs to invoke it correctly is missing.

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

Parameters3/5

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

Schema coverage is 100%, so the schema already documents brand, items, and collection thoroughly, including the key/link/media de-dup fields. The description only restates the collection-creation semantics, adding no syntax or format detail beyond the schema. Baseline 3 is appropriate.

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 (save) and resource (ads/creatives to a named swipefile collection) with clear scope, and the 'headless twin of the heart' framing distinguishes it from siblings like list_swipefile or export_swipefile_deck. An agent can identify it without opening the 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?

Explicitly says when to use it ('whenever research turns up something worth keeping') and names the source tools that feed it (search_meta_ads / pull_competitor_ads). It stops short of naming when NOT to use it or pointing to sibling alternatives like list_swipefile, so it is strong but not complete routing guidance.

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

schedule_postSchedule a post for laterA
Destructive
Inspect

Queue a post for a future time on one or more connected channels at once: facebook, instagram, threads, tiktok, youtube, linkedin, x, pinterest, bluesky, telegram — TEN channels, every one live. google_business is in the schema but HELD BACK (Google’s API allowlist) and is refused at enqueue. A MULTI-SLIDE creative goes in imageUrls[] as a CAROUSEL, in order — never schedule just its first slide. Name the time in at, or pass useQueue:true for the brand’s next free POSTING SLOT (what “just queue it” means). imageUrl/videoUrl take a Hermoso render URL or an upload_file URL. captions gives a channel its own wording; the rest use message. PINTEREST AND YOUTUBE SHOW A TITLE: title (max 100 chars), derived from the caption when omitted; YOUTUBE also takes description, tags, thumbnailUrl. Every PER-CHANNEL SETTING is a parameter below, carried straight to the real publisher — TikTok’s brandedContent / yourBrand disclosures (set them whenever the post is commercial), Google Business topicType / event / offer / actionType, an X thread / poll, Instagram collaborators, and the rest. Channels are attempted INDEPENDENTLY: one failing never blocks the others. SOME CHANNELS MUST BE TOLD WHICH ACCOUNT, and Hermoso never guesses: Pinterest needs boardId (list_pinterest_boards) or is refused; a LinkedIn COMPANY PAGE post needs linkedinOrganizationId (list_linkedin_pages) or it goes to the person’s own profile; several Facebook Pages need pageId (list_meta_pages), several Google Business listings locationId (list_business_locations) — resolve those FIRST and let the user pick, or the post is refused when it fires. A scheduled post GOES LIVE PUBLICLY by default on every channel, never quietly downgraded. Only if the user asks, set visibility (or visibilityByChannel): ‘unlisted’ (YouTube) · ‘private’ (YouTube, or TikTok SELF_ONLY) · ‘draft’ (TikTok, or an unpublished Facebook Page post). A visibility a channel cannot do is REFUSED now, never posted weaker later.

ParametersJSON Schema
NameRequiredDescriptionDefault
atNowhen to post — ISO timestamp (2026-08-01T09:00:00Z) or epoch milliseconds. Must be in the future, at most 365 days out. Give this OR useQueue, never both.
hookNoWHAT ANGLE this post is built on, recorded only at publish time. post_performance ranks hooks on it (a winner needs 5 posts sharing ONE hook), so pass a list_hooks id (e.g. "direct_callout", "before_after") or reuse your own wording EXACTLY across a campaign. Omit it and this post never votes on which hook works.
linkNoa link to attach (Facebook)
pollNoX — attach a poll: {options:["…","…"], durationMinutes}. 2–4 options of at most 25 characters each; voting runs 5–10080 minutes (7 days), default 1440. X makes a poll MUTUALLY EXCLUSIVE with media, so an item carrying an image or video is refused — schedule the poll as its own X-only post.
tagsNoYOUTUBE — up to 30 search tags for the video (plain words, no #).
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.
eventNoGOOGLE BUSINESS — required for an EVENT or OFFER post: {title, startDate:"YYYY-MM-DD", endDate, startTime:"HH:MM", endTime}. `title` is the EVENT’s headline, a different thing from the post `title` (which is the Pinterest/YouTube one). Google documents its TimeInterval as needing all four date/time parts to be valid, so send the times whenever you know them.
offerNoGOOGLE BUSINESS — OFFER posts only: {couponCode, redeemOnlineUrl, termsConditions}. redeemOnlineUrl is where an offer actually sends people, since the button link is ignored on an Offer.
placeNoFACEBOOK — tag a location on the Page post. The NUMERIC ID OF THE FACEBOOK PAGE for that place, not a place name and not coordinates.
storyNoINSTAGRAM STORY — publish this as a 24-hour Story instead of a feed post. One image OR one video: Instagram has no carousel story, so a carousel is REFUSED BY NAME rather than quietly posted to the feed. A Story carries NO CAPTION (there is nowhere to show one), no collaborators, no product tags and no trialReel — passing any of those is refused by name and nothing is posted, because a story that silently drops the words is worse than one that never went. INSTAGRAM ONLY: a Facebook Page story is a different upload and is not built, so a Facebook channel with `story` set is refused rather than published to the feed.
titleNoPINTEREST / YOUTUBE — the headline, max 100 characters. Pinterest shows it in search and under the pin; YouTube requires one. Leave it out and Hermoso derives one from that channel’s caption (first sentence, cut on a word boundary, trailing hashtags dropped) — set a real one whenever the caption does not open with a usable headline.
chatIdNoTELEGRAM — REQUIRED whenever telegram is a channel: WHICH chat, group or channel the bot posts to. A public channel’s @username (@hermosoai) or the numeric id (a group is negative; a supergroup or channel starts with -100). There is no default and there cannot be one — the Telegram Bot API publishes no method that lists a bot’s chats — so scheduling telegram without one is refused up front. list_telegram_chats finds ids for chats that have messaged the bot in the last 24 hours.
ideaIdNoshort id of the content-plan idea this post came from
pageIdNoFACEBOOK / INSTAGRAM / THREADS — which connected Facebook Page (and its linked Instagram) publishes, from list_meta_pages. Needed when the profile has more than one Page connected; with several and no id the post is refused at fire time rather than sent from the wrong brand.
recipeNothe post's FORMAT id, e.g. "slideshow" or "imessage_chat" — post_performance groups by it, so reuse one id per format
threadNoX — publish a THREAD, one entry per post, each replying to the one before (at most 25). 280 characters per part without X Premium, up to 25,000 with it; nothing is truncated. It REPLACES the X caption: with a thread set, `message`/`captions.x` is not sent to X at all. A thread cannot carry a poll.
altTextNoACCESSIBILITY — the screen-reader description of the attached image. ONE STRING describes the picture; on a CAROUSEL it describes EVERY slide. Pass an ARRAY of strings instead to describe each slide separately, aligned to the slide order — that is strictly better on a multi-slide post, because one sentence read out over six different pictures is wrong for five of them. More descriptions than pictures is refused rather than dropped. Write one whenever the post carries an image: describe what is IN the picture, never a repeat of the caption, which a screen reader already reads. CARRIED BY: X (max 1000, one per media), Pinterest (max 500 — PIN-LEVEL only, since its API has no per-item alt text, so slide 1’s description is used for the whole Pin and the result says the others were not sent), LinkedIn COMPANY PAGES (max 4086, one per slide), INSTAGRAM image posts and image slides (max 1000), FACEBOOK photos and albums, and BLUESKY, whose lexicon makes it REQUIRED on every image. The schedule is REFUSED if the LONGEST description exceeds the tightest of the channels on it, rather than truncated on the way out. NOT CARRIED, and none of these is a refusal — the post still publishes, just undescribed there, and the per-channel result says which: TikTok (its photo post has no alt field at any level), a THREADS CAROUSEL, an INSTAGRAM Reel or video slide, and a LinkedIn PERSONAL-profile post.
audioIdNoINSTAGRAM REEL — put one of Instagram’s OWN licensed music tracks under the Reel: the `id` search_instagram_audio returns. Instagram mixes it in when the Reel is published; nothing is downloaded or re-rendered. REELS ONLY, and only on an Instagram account connected through Meta (a Facebook Page with a linked Instagram), which is Instagram’s own rule — the Instagram connector cannot take it and is refused by name.
boardIdNoPINTEREST — REQUIRED whenever pinterest is a channel: the board the Pin goes on, from list_pinterest_boards. The user picks it; a Pin on the wrong board is a public mistake. Scheduling pinterest without one is refused immediately.
messageNothe caption/text used for every channel unless overridden in captions
subjectNoWHAT THIS POST IS ABOUT — product, feature, offer or theme (e.g. "winter coat", "free trial"). post_performance's second grouping axis: reuse the exact wording, as with hook.
accountsNoWHICH accounts of a multi-account channel to post to, e.g. { "tiktok": ["@a", "@b"] } or { "tiktok": "all" } — one row per account at fire time, each with its own result. Omit for channels with one account (several and none named is refused by name).
audienceNoFACEBOOK PAGE POST ONLY — limit who can see this organic post to people in these places and/or over this age (Facebook allows 13, 15, 18, 21 or 25). Not on Instagram, Threads or a Facebook Reel (a vertical clip becomes a Reel): those are refused. Region and city keys come from the Meta ads location search.
captionsNoper-channel caption overrides, e.g. { "instagram": "…", "threads": "…" } — platforms want different lengths and hashtag conventions
channelsYesone or more channels to post to at that time
coverUrlNoINSTAGRAM REEL COVER — a public image url Instagram fetches and uses as the cover in the Reels tab. REELS ONLY, and the alternative to `thumbOffset`: passing both is refused, since they are two answers to the same question. Run a local file through upload_file first.
imageUrlNoa Hermoso render URL (a /generated path, or what upload_file returned), a data: URI, or a public https URL. NOTE: only Facebook/Instagram/Threads accept an arbitrary public URL — X, TikTok, YouTube, LinkedIn, Pinterest and Google Business re-host the bytes and REFUSE anything that is not a Hermoso render, so run an external file through upload_file first and schedule that url.
linkNameNoFACEBOOK — override the HEADLINE of the link preview. Without it the post shows whatever Open Graph title the destination carries, which on a bare landing page is often nothing. Needs `link`. MEASURED 2026-09-17: Facebook only honours these when the Page’s business has VERIFIED the domain (Business Manager > Brand safety > Domains) — without that Meta refuses the post outright with “Only owners of the URL…”, so leave them out unless the brand owns and has verified that link’s domain.
timezoneNoIANA zone for the queue, e.g. "America/New_York" — only meaningful with useQueue, and it overrides the brand’s saved zone for this one post. A saved slot of "09:00" is a WALL-CLOCK time, so the zone is what turns it into an instant; without either the brand’s saved zone or this, the queue resolves in UTC.
topicTagNoTHREADS ONLY — one topic tag for the post, without the leading #.
useQueueNoinstead of naming a minute, drop this into the brand’s POSTING QUEUE: Hermoso takes the earliest of its saved posting times that is still free (skipping any slot another queued post already holds). This is the natural answer to “just queue it” / “post it at my next opening”. Mutually exclusive with `at` — passing both is refused rather than one being silently preferred. If the profile has no posting times set, or every slot for the next 90 days is taken, it is refused by name and nothing is scheduled.
videoUrlNoa Hermoso render URL (a /generated path, or what upload_file returned), a data: URI, or a public https URL — required for youtube, and for tiktok unless you pass an imageUrl (TikTok takes a photo post too). Same origin rule as imageUrl: everything except Facebook/Instagram/Threads REFUSES a non-Hermoso URL, so pass external video through upload_file first.
xArticleNoX: publish the X item as a long-form X ARTICLE. title is its headline, the X text (message or captions.x) its markdown body, the image its cover. Refused now if the markdown has formatting X cannot hold. X allows about 5 Articles a day; one that fires into that cap fails with the reset time and can be retried.
audioNameNoINSTAGRAM REEL — the name of the Reel’s audio track, which is what viewers tap through to. REELS ONLY.
coverAtMsNoTHE VIDEO COVER on EVERY channel of this post, as ONE frame: milliseconds from the start (7000 = the frame at 7s). Instagram, TikTok, Facebook, LinkedIn Page, Pinterest, Telegram and YouTube all get that frame; X, Threads and Bluesky have no cover setting. A channel’s own field (thumbOffset, coverTimestampMs, coverUrl) wins there and otherwise counts as this.
imageUrlsNoCAROUSEL — an ORDERED list of image URLs to publish as ONE swipeable post on every channel that supports it (Instagram 2–10, Threads 2–20, Facebook, LinkedIn company Pages 2–20, Pinterest 2–5, TikTok up to 35 as a photo post). Use this whenever the creative is a multi-slide deck: a scheduled post carrying only slide 1 of a “1/6 · SWIPE” set is a broken ad that nobody is watching when it fires. THE ORDER IS THE PRODUCT. A channel on this schedule that cannot do carousels — X, YouTube, Google Business Profile — is REFUSED NOW, with the reason, so you can drop it or give it its own single image; it is never quietly downgraded hours later.
slideTextNoPINTEREST CAROUSEL ONLY — per-slide title / description / link, aligned to slide order. Every other platform takes ONE caption for the whole carousel.
topicTypeNoGOOGLE BUSINESS — the KIND of Post. STANDARD is the default; EVENT and OFFER both REQUIRE `event` (title + start date), and OFFER also takes `offer`.
trialReelNoINSTAGRAM TRIAL REEL — publish this Reel to NON-FOLLOWERS ONLY at first (Instagram allows trials only on accounts above its follower threshold — about 1,000 followers; an ineligible account is refused by name and nothing is posted), so a hook can be tested on a cold audience without spending it on the people who already follow the brand; Instagram shows it to followers only if it graduates. MANUAL = the creator graduates it by hand in the Instagram app; SS_PERFORMANCE = Instagram graduates it automatically if it performs. REELS ONLY and INSTAGRAM ONLY: an image, a carousel, or a Facebook/Threads channel is REFUSED BY NAME rather than quietly published as an ordinary post — a trial that silently goes to every follower is the exact opposite of what was asked for, so Instagram must be one of the `channels` and the item must carry a video. Omit it for a normal Reel.
yourBrandNoTIKTOK — the OWN-BRAND disclosure (brand_organic_toggle): true when the post promotes the creator’s own business. TikTok asks for at least one of this and brandedContent once a post is commercial.
actionTypeNoGOOGLE BUSINESS — the call-to-action button. Every button except CALL needs `link` (CALL dials the number on the listing and takes none). Google IGNORES the button link on an OFFER post — put the destination in offer.redeemOnlineUrl. Omit and a post carrying a link gets LEARN_MORE.
locationIdNoGOOGLE BUSINESS PROFILE — which listing, e.g. 'locations/123' from list_business_locations. Needed when the account manages more than one storefront; it is never chosen for the user.
madeWithAiNoX — X’s AI-media label on this post. Opt-in: X treats it as the poster’s own claim about their media, so it is never set on the user’s behalf.
visibilityNohow it should be published — DEFAULT 'public' (live). Only pass something else if the user explicitly asked to stage/hide it. Not every channel supports every value; an impossible combination is refused when you schedule it, with the reason.
aiGeneratedNoAI-CONTENT DISCLOSURE (Instagram / Facebook Reel is_ai_generated, TikTok is_aigc, YouTube containsSyntheticMedia). OMIT IT and Hermoso decides from provenance: a Hermoso render is declared AI-generated, media that came through upload_file or from an external URL (the user’s own photos or footage) is NOT. Pass true or false only to override.
audioVolumeNoINSTAGRAM REEL — how loud that track plays, 0–100 (Instagram’s default 100). Needs audioId.
communityIdNoX — publish into an X COMMUNITY instead of the main timeline: the number in the community’s own URL (x.com/i/communities/<id>). The connected account must be a MEMBER of it.
descriptionNoYOUTUBE: the video DESCRIPTION, max 5000 characters, carrying the links, the CTA and what YouTube search reads. Omit it and the caption is used.
disableDuetNoTIKTOK VIDEO ONLY — block Duets. TikTok’s photo-post API has no Duets, so this is refused on a photo/slideshow item rather than silently dropped.
linkPictureNoFACEBOOK — override the IMAGE of the link preview: a public http(s) url Facebook fetches itself. Needs `link`. MEASURED 2026-09-17: Facebook only honours these when the Page’s business has VERIFIED the domain (Business Manager > Brand safety > Domains) — without that Meta refuses the post outright with “Only owners of the URL…”, so leave them out unless the brand owns and has verified that link’s domain.
quotePostIdNoTHREADS ONLY — the id of the Threads post this one quotes.
shareToFeedNoINSTAGRAM REEL — true puts the Reel in the Feed grid as well as the Reels tab. REELS ONLY. Left unset it follows Instagram’s own default; Hermoso does not flip it either way on the user’s behalf.
thumbOffsetNoINSTAGRAM REEL COVER, the other way — which frame becomes the cover, in MILLISECONDS from the start of the video. REELS ONLY. Use it instead of `coverUrl` when the right cover is already a frame of the clip.
videoVolumeNoINSTAGRAM REEL — how loud the video’s own sound plays under the track, 0–100 (Instagram’s default 100; 0 = the track alone). Needs audioId.
callToActionNoFACEBOOK — a real call-to-action BUTTON on the Page post. The types that open a destination (SHOP_NOW, LEARN_MORE, SIGN_UP, BUY_NOW, DOWNLOAD, OPEN_LINK, WATCH_MORE, BOOK_TRAVEL) need somewhere to go: the post’s own `link`, or `callToActionLink`. CALL_NOW, MESSAGE_PAGE, LIKE_PAGE and GET_DIRECTIONS act on the Page itself and take none.
countryCodesNoTHREADS ONLY — two-letter country codes to limit who can see the post. Omit to show it everywhere.
optimizeCopyNoRECOMMENDED when one caption goes to several channels: fit the shared caption to each channel’s own rules when you schedule it (the fitted caption is stored on the scheduled post, so what you scheduled is what publishes) wherever no per-channel caption was written. The angle and every claim stay the author’s; a channel with its own caption is left exactly as written. Off by default so nobody’s words are rewritten unasked.
privacyLevelNoTIKTOK — WHICH PRIVACY LEVEL the post goes out at, in TikTok’s own vocabulary. TikTok requires the USER to choose this from the levels their own account allows: call tiktok_creator_info, show them the real options, and pass back the one they picked — never a default and never a guess, which TikTok refuses at init. Omit it and the post falls back to the coarse `visibility` (private -> SELF_ONLY, otherwise PUBLIC_TO_EVERYONE), which cannot express MUTUAL_FOLLOW_FRIENDS or FOLLOWER_OF_CREATOR at all. It must agree with the TikTok visibility (SELF_ONLY is the private one) and it is refused on a TikTok DRAFT, which carries no post info.
replyControlNoTHREADS ONLY — who may reply. Omit for Threads' own default (everyone).
thumbnailUrlNoYOUTUBE: the custom thumbnail, a Hermoso-hosted image (make_thumbnail, or upload_file for the user’s own). Omit it and a frame of the video is used; "auto" keeps YouTube’s pick.
xQuotePostIdNoX — the numeric id of an X post this one QUOTES: the last part of its URL. NAMED xQuotePostId, NOT quotePostId, because `quotePostId` on this same schedule belongs to THREADS. Billed at X’s higher LINK rate.
collaboratorsNoINSTAGRAM — a COLLAB post: up to 3 Instagram usernames invited to CO-AUTHOR it, so it appears on their profile too once they accept, with both handles in the header and the engagement shared. Handles only ("hermosoai"); a leading @ is fine. Instagram must be one of the `channels` — asking for collaborators on a schedule Instagram is not on is REFUSED now rather than discovered when it fires, and the other channels in a mixed schedule simply publish without co-authors. THE INVITE IS SENT WHEN THE POST FIRES, not when you schedule it, and it is PENDING until the other account accepts in their notifications; check with instagram_collaborators afterwards rather than telling the user it is live on both profiles.
coverImageUrlNoTHE VIDEO COVER as a picture instead of a frame — a Hermoso-hosted image (upload_file). Every channel that takes a cover image gets it (Instagram, Facebook, LinkedIn Page, Pinterest, Telegram, YouTube); TikTok takes only a frame. Never together with coverAtMs.
disableStitchNoTIKTOK VIDEO ONLY — block Stitches. Same photo-post rule as disableDuet.
instagramPollNoINSTAGRAM — attach a POLL: 2 to 4 answers of 1–25 characters each, and the caption IS the question (so a caption is required). Feed photos, carousels and Reels only (never a story), one caption add-on per post (not with instagramCommentPrompt), and only on an account connected through Meta (a Facebook Page with a linked Instagram). WRITE-ONCE: Instagram cannot add, change or remove it after publishing, so show the user the answers first.
platformCoverNoVIDEO COVER. Omit it (the default) and Hermoso sets the video’s best frame — the same frame as its Library thumbnail — as the cover on every channel that allows one (Instagram, Facebook, TikTok direct posts, LinkedIn Pages, Pinterest, Telegram, YouTube) — and on X, Threads and Bluesky, which have none, a blank first frame is replaced on a copy sent there only. true = send no cover and let the platform pick (usually the first frame). A cover you pass yourself always wins.
replySettingsNoX — who may reply. Omit for everyone, which is the right default for a brand post.
brandedContentNoTIKTOK — the PAID-PARTNERSHIP disclosure (TikTok’s brand_content_toggle): true when this post promotes a THIRD-PARTY business. It is a compliance declaration, not a preference — set it whenever the post is sponsored. TikTok only accepts branded content on a public or friends-only post, so it cannot ride a private/SELF_ONLY or draft TikTok item and the schedule is refused with the reason.
disableCommentNoTIKTOK — turn comments off on this post.
linkAttachmentNoTHREADS ONLY — a full http(s) URL rendered as a link card. This is the ONLY way a Threads post carries a destination, and Threads attaches it to TEXT-ONLY posts (a post with media cannot also carry a card).
targetAudienceNoLINKEDIN COMPANY PAGE POST ONLY — show the post only to Page followers matching these facets (URNs or bare numeric ids; search_linkedin_ads_targeting finds them). LinkedIn requires the matching audience to be over 300 followers and refuses a smaller one. Personal-profile posts cannot be targeted.
linkDescriptionNoFACEBOOK — override the DESCRIPTION of the link preview. Needs `link`. MEASURED 2026-09-17: Facebook only honours these when the Page’s business has VERIFIED the domain (Business Manager > Brand safety > Domains) — without that Meta refuses the post outright with “Only owners of the URL…”, so leave them out unless the brand owns and has verified that link’s domain.
paidPartnershipNoINSTAGRAM AND X — the PAID PARTNERSHIP label, a compliance declaration: set it when the post is sponsored, gifted or otherwise paid for. OPT-IN ONLY, never assume it on the user’s behalf. On Instagram, brandedContentSponsorIds names the brands behind it.
callToActionLinkNoFACEBOOK — where the button goes, when that is not the post’s own `link`.
coverTimestampMsNoTIKTOK VIDEO ONLY — which frame TikTok uses as the cover, in milliseconds from the start. Omit and Hermoso uses the video’s best frame (platformCover:true leaves it to TikTok, which uses the first frame).
crossreshareToIgNoTHREADS ONLY — when this fires, ALSO share it to the linked Instagram account as a STORY. Refused on a Threads carousel. No confirmation exists that the Story was created, so the result says it was requested.
commercialContentNoTIKTOK — the COMMERCIAL CONTENT DISCLOSURE toggle: true when the post promotes a brand, product or service at all. TikTok requires at least one of yourBrand / brandedContent alongside it, and a post declaring itself commercial without naming which kind is refused. Setting either disclosure already implies this, so it is only needed to be explicit.
instagramLocationIdNoINSTAGRAM — tag a place (called locationId on post_to_meta; locationId here is the Google Business listing). It is the NUMERIC ID OF A FACEBOOK PAGE associated with that location, not a place name and not coordinates; a non-numeric value is refused rather than sent.
visibilityByChannelNooverride visibility for one channel, e.g. { "tiktok": "draft" } to go live everywhere but stage TikTok for review
crossreshareDarkModeNoTHREADS ONLY — render that Instagram Story in dark mode. Needs crossreshareToIg.
instagramPollExtendedNoINSTAGRAM — give that poll Instagram’s extended voting duration instead of its default 3 days (Instagram’s changelog says it then stays open indefinitely). Only with instagramPoll.
instagramCommentPromptNoINSTAGRAM — make the caption a COMMENT PROMPT that people answer in the comments. Same limits as instagramPoll, and never together with it.
linkedinOrganizationIdNoLINKEDIN — publish as a COMPANY PAGE instead of the connected personal profile. The organization id from list_linkedin_pages. Omit and it posts as the person: an unset id means the profile, never “probably the company”. A Page can also carry VIDEO, which a personal profile cannot.
brandedContentSponsorIdsNoINSTAGRAM — the numeric Instagram USER IDS of the brands behind that label (at most 2, and ids rather than @handles). Naming sponsors IS asking for the label, so setting these with `paidPartnership:false` is refused instead of publishing brand credits with no disclosure.

TDQS

A4.4/5.0
Behavior5/5

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

Annotations only give destructive=false/readOnly=false/openWorld=true and no idempotency; the description adds the crucial behaviors: posts go LIVE PUBLICLY by default, channels are attempted independently so one failure never blocks others, impossible visibility combos are refused up front, and multi-slide creatives must be sent complete. This is exactly the context annotations cannot carry.

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

Conciseness3/5

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

Front-loaded with the core purpose, which is good, but the body is a very long run of capitalized emphasis and nested asides that is heavy to parse. For an 84-parameter tool some length is warranted, yet the density of ALL-CAPS clauses pushes past efficient.

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?

Covers behavior, defaults, refusals and prerequisites thoroughly, which is what a complex scheduling tool needs. No output schema exists, so it can't explain returns, and it omits scheduling-failure/retry behavior, but for its complexity it is largely complete.

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 adds cross-parameter semantics the schema does not: at/useQueue mutual exclusivity, imageUrl vs videoUrl origin rules, captions overriding message per channel, and the requirement-list of account-scoping ids. It meaningfully enriches understanding beyond individual field docs.

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+resource+scope ('Queue a post for a future time on one or more connected channels at once') and enumerates the exact ten live channels plus the held-back one. An agent can immediately see this differs from the single-channel post_to_* siblings without opening the 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 strong conditional routing: name `at` OR pass `useQueue:true`, resolve boardId/linkedinOrganizationId/pageId/locationId FIRST via the named list_* helpers, and only set `visibility` if the user explicitly asks. It does not explicitly contrast with the immediate-publish siblings (post_to_x, post_to_meta, etc.), which a 'schedule' tool ideally would.

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

score_adScore adAInspect

Virality/performance prediction for a finished ad (image or video URL): overall score, per-dimension breakdown (scroll-stop, hook, clarity, brand/product, CTA, retention, goal fit), strengths, and the single biggest fix. Use BEFORE spending on distribution, or to rank variants.

ParametersJSON Schema
NameRequiredDescriptionDefault
urlYesthe ad asset URL (a /generated/ path or public URL)
kindNo'image' (default) or 'video'
intentNowhat the ad is trying to achieve, for goal-fit scoring

TDQS

A3.8/5.0
Behavior3/5

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

Annotations declare openWorldHint=true and destructiveHint=false, so the safety profile is partly covered. The description adds useful context that the input must be a finished asset and what the return contains, but says nothing about cost/credits, auth requirements, or timing despite readOnlyHint=false implying a non-pure-read operation.

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

Conciseness5/5

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

Two tight sentences: purpose and output fronts the text, usage trails it. Zero filler and every clause carries information.

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?

With no output schema, the description correctly compensates by enumerating the returned dimensions, strengths, and fix. The only gap is operational detail (cost, latency, error cases), which keeps it just short of complete.

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

Parameters3/5

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

Schema description coverage is 100%, so the schema already documents url, kind, and intent. The description only restates that the asset is an image/video URL and mentions goal fit, adding almost nothing beyond the structured field docs. Baseline 3 applies.

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

Purpose4/5

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

States a specific verb (predict/score) and resource (a finished ad, image or video URL) and even enumerates the output dimensions. Scope is unmistakable, though it never contrasts itself against nearby siblings like check_ad_policy, analyze_video, or competitor_teardown.

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 clear usage context: 'Use BEFORE spending on distribution, or to rank variants.' That tells the agent when it is appropriate, but offers no explicit when-not or alternative tool for adjacent needs (e.g., policy checking).

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

search_google_adsSearch Google adsAInspect

Structured Google Ads Transparency pull for ONE advertiser (by domain or advertiserId) — use when you know the brand; use research_ads for open-ended research. Deliberately fetches the cheap BASIC listing (get_ad_details=false, ~1 credit — the detailed variant with per-ad headlines costs 25 credits/call and is not exposed here). Returns compact JSON {advertiser, format, adUrl, image, firstShown, lastShown} per ad.

ParametersJSON Schema
NameRequiredDescriptionDefault
limitNomax ads returned (1–25, default 8)
domainNothe advertiser's domain, e.g. nike.com
regionNo2-letter region, default US
advertiserIdNoGoogle advertiser id (AR…) when the domain is ambiguous

TDQS

A3.8/5.0
Behavior1/5

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

The description frames the tool as a retrieval operation ('pull', 'fetches', 'Returns compact JSON'), but the annotations declare readOnlyHint=false. That structured signal says the tool is not read-only, which contradicts the description's fetch/search framing. The useful cost disclosure (~1 credit, no 25-credit detailed variant) does not resolve this contradiction.

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

Conciseness5/5

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

The description is dense but front-loaded: purpose and alternative first, then cost behavior, then return shape. Every sentence carries useful selection or invocation information, with no filler.

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?

It gives the return fields, the cost model, and the sibling alternative, which is strong for a search tool with no output schema. The remaining gap is that it does not reconcile the read-only framing with the annotation readOnlyHint=false.

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

Parameters3/5

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

Schema description coverage is 100%, so domain, advertiserId, limit, and region are already documented. The description only repeats the domain/advertiserId distinction and adds no syntax or default behavior beyond the schema, so baseline 3 is appropriate.

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 ('Structured Google Ads Transparency pull for ONE advertiser') and scopes it to domain or advertiserId. It also explicitly distinguishes itself from research_ads for open-ended research, so an agent can select it without opening another 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?

Explicitly says when to use it ('when you know the brand') and names the alternative for the opposite case ('use research_ads for open-ended research'). This is exactly the when/when-not routing guidance an agent needs.

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

search_instagramSearch InstagramAInspect

Organic Instagram REELS keyword search (/v2/instagram/reels/search — our only IG keyword surface; profile/hashtag pulls go through fetch_social_data with a handle). Returns compact JSON {desc, author, handle, plays, likes, link, cover} per reel, ranked by plays. Spends about a credit.

ParametersJSON Schema
NameRequiredDescriptionDefault
limitNomax reels returned (1–25, default 8)
queryYeskeyword to search reels for

TDQS

A4.5/5.0
Behavior4/5

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

Annotations already declare openWorldHint=true and readOnlyHint=false, and the description usefully explains the non-read-only nature by stating 'Spends about a credit'. It also discloses the compact return shape and that results are ranked by plays. It does not mention rate limits or search syntax limits, so it stops short of a 5.

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

Conciseness5/5

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

A single dense passage with zero filler — purpose first, then the disambiguation rule, return shape, and cost. Every clause earns its place and nothing is front-loaded incorrectly.

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?

No output schema exists, and the description compensates by listing the returned fields ({desc, author, handle, plays, likes, link, cover}) and the ranking. Combined with the credit cost and alternative routing, 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.

Parameters3/5

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

Schema description coverage is 100% (both query and limit documented, including the 1-25 range and default 8), so the schema carries parameter semantics. The description adds no syntax, format, or operator guidance beyond what the schema already provides, making the baseline 3 appropriate.

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+resource+scope: 'Organic Instagram REELS keyword search'. It further differentiates by naming the endpoint surface and clarifying that profile/hashtag pulls are a different operation, so an agent can separate it from fetch_social_data and the other search_* tools 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?

Explicitly states the when: 'our only IG keyword surface', and the when-not plus alternative: 'profile/hashtag pulls go through fetch_social_data with a handle'. This is exactly the routing guidance an agent needs versus siblings.

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

search_linkedin_adsSearch LinkedIn adsAInspect

Structured LinkedIn Ad Library search by company name, keyword, or companyId — use for a targeted B2B pull; use research_ads for open-ended research. Returns compact JSON {advertiser, headline, description, cta, link, media, dates, impressions} per ad — LinkedIn is the one library exposing real impression counts. Spends about a credit.

ParametersJSON Schema
NameRequiredDescriptionDefault
limitNomax ads returned (1–25, default 8)
companyNoadvertiser company name
keywordNokeyword across all advertisers
companyIdNoLinkedIn company id (numeric) when the name is ambiguous
countriesNoCSV of 2-letter codes like 'US,CA'; omit or 'ALL' = worldwide

TDQS

A4.4/5.0
Behavior4/5

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

Goes beyond the annotations by disclosing a cost ('Spends about a credit') and a notable capability (real impression counts, unique to LinkedIn). It does not explain why readOnlyHint=false / idempotentHint=false for what looks like a search (presumably the credit spend), but there is no contradiction and the credit disclosure is genuinely useful.

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?

Three tight sentences, front-loaded with the purpose and routing before the return shape and cost. Dense but every clause carries information; minor cost is that the return-field list is a long inline enumeration.

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 shape ({advertiser, headline, description, cta, link, media, dates, impressions}), the cost, and the routing alternative — everything needed to call and interpret the tool correctly.

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

Parameters3/5

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

Schema description coverage is 100%, so all five parameters are already documented (including the countries CSV and companyId ambiguity hint). The description only restates the targeting options at a high level, adding no syntax or format detail beyond the schema. Baseline 3.

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 ('Structured LinkedIn Ad Library search') plus the three targeting axes (company name, keyword, companyId), which lets an agent distinguish it from the ad-search siblings at a glance.

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?

Explicitly names the alternative ('use research_ads for open-ended research') and the condition that selects this tool ('a targeted B2B pull'). This is exactly the when/when-not routing an agent needs.

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

search_meta_adsSearch Meta adsAInspect

Structured Meta (Facebook/Instagram) Ad Library pull — use when you know exactly WHAT to fetch: a keyword (query) OR one advertiser (companyName / pageId). Returns compact JSON {page_name, body, cta, link, dates, media} per ad. For open-ended research that needs judgment across platforms, use research_ads instead. Spends a credit or two.

ParametersJSON Schema
NameRequiredDescriptionDefault
limitNomax ads returned (1–25, default 8)
queryNokeyword search across ALL advertisers (use INSTEAD of companyName/pageId)
pageIdNoone advertiser’s ads by Facebook page id (most precise)
statusNoACTIVE = currently running; default ALL (includes proven past winners)
countryNo2-letter code or 'ALL' (default ALL)
mediaTypeNofilter by creative type (default ALL)
companyNameNoone advertiser’s ads by brand name

TDQS

A4.3/5.0
Behavior4/5

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

Annotations cover the safety profile (destructiveHint=false, openWorldHint=true), so the bar is lower. The description adds two things annotations cannot: cost ('Spends a credit or two') and the return shape ({page_name, body, cta, link, dates, media}). It does not mention rate limits or result freshness, keeping it short of a 5.

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?

Three tightly packed clauses with zero filler and the core scoping rule front-loaded. The em-dash constructions and stacked alternatives make it dense, though nothing is redundant.

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?

With no output schema, the description helpfully names the returned fields, and it covers the alternative-tool routing and credit cost. It leaves the credit amount and result cap/freshness unstated, but those are minor for a filtered search tool.

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

Parameters3/5

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

Schema coverage is 100%, so every parameter is already documented, including the query-vs-companyName/pageId mutual exclusivity. The description's 'OR' phrasing mirrors what the schema says without adding format, syntax, or edge-case guidance. Baseline 3 applies.

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+resource ('Structured Meta (Facebook/Instagram) Ad Library pull') and immediately scopes it: a keyword query OR one advertiser. It names the sibling it is not (research_ads) for open-ended work, so an agent can disambiguate without opening either 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 when-to-use ('use when you know exactly WHAT to fetch: a keyword (query) OR one advertiser (companyName / pageId)') and explicit when-not ('For open-ended research that needs judgment across platforms, use research_ads instead'). The condition selecting the alternative is spelled out.

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

search_postsTop posts about any topic, brand or productAInspect

The POSTS people make ABOUT a subject — a brand ("liquid death"), a product, a hobby ("coffee"), a hashtag ("#homecafe") — from whoever posted them, across organic TikTok, Instagram Reels and YouTube in ONE call, ranked by views. Not the brand's own ads (search_meta_ads / research_ads) and not the people (find_creators folds these same posts into creators): use it to see what is actually being posted and watched about a subject, to find clips worth cloning (clone_video), and to read the hooks and angles an audience already responds to. About one credit per platform searched (one query each by default; queries adds "best X" / "X review" / #tag variants, each a paid call); repeats inside 20 minutes are free.

ParametersJSON Schema
NameRequiredDescriptionDefault
limitNoposts per platform, 1–60 (default 24)
topicYessubject, brand, product or hashtag — "liquid death", "coffee", "#homecafe"
queriesNoquery variants per platform, 1–4 (default 1); each is a paid search call
platformsNodefault all three

TDQS

A4.5/5.0
Behavior4/5

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

Annotations cover open-world/idempotency but not cost, and the description adds real behavioral context: 'About one credit per platform searched', each `queries` variant is a paid call, and 'repeats inside 20 minutes are free'. It also states the ranking (by views). It stops short of describing pagination or result shape, so a 4 rather than 5.

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?

The core purpose and scope are front-loaded, and every clause carries routing, cost, or return-value information. It is a dense single paragraph, but with little genuine filler; the density borders on overloaded rather than wasteful.

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?

With no output schema, the description still tells the agent what comes back (posts ranked by views) and covers cost, scope, and alternatives. It leaves result shape and pagination unspecified, a minor gap for a search tool with all params already documented.

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 baseline is 3, but the description adds meaning to `queries` (adds 'best X' / 'X review' / #tag variants, each a paid call) and clarifies that the default is one query per platform across all three platforms. This enriches the schema rather than restating it.

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 (post search) and resource (posts about a subject) with concrete scope: organic TikTok, Instagram Reels and YouTube in one call, ranked by views. It explicitly differentiates from siblings by saying it is 'Not the brand's own ads (search_meta_ads / research_ads) and not the people (find_creators)', so an agent can route 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?

Gives explicit when-to-use conditions ('see what is actually being posted and watched about a subject', 'find clips worth cloning', 'read the hooks and angles an audience already responds to') and names the adjacent tools it is not (search_meta_ads, research_ads, find_creators). Nothing about selection is left to inference.

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

search_redditSearch RedditAInspect

Reddit keyword search (/v1/reddit/search, top-ranked) — a goldmine for the customer's OWN words (pain points, objections, language) to mine into ad hooks and copy. Returns compact JSON {desc (title+selftext), subreddit, upvotes, comments, link} per post. Spends about a credit.

ParametersJSON Schema
NameRequiredDescriptionDefault
limitNomax posts returned (1–25, default 8)
queryYeswhat to search Reddit for

TDQS

A4.3/5.0
Behavior4/5

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

Annotations already declare openWorldHint=true and non-destructive behavior. The description adds important context beyond annotations by stating the credit cost and the exact return shape, though it does not mention auth requirements, rate limits, or pagination.

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

Conciseness5/5

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

The description is compact and front-loaded: it names the endpoint, purpose, return shape, and cost in a few dense sentences. Every sentence adds useful information with no repetition or 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?

For a simple two-parameter search tool with no output schema, the description supplies the missing return structure and credit cost. Together with the complete input schema and annotations, an agent has enough information to invoke it correctly.

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

Parameters3/5

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

Schema description coverage is 100%, so the schema already documents both the required query and optional limit parameters. The description adds no parameter-level semantics beyond what the schema provides, making the baseline score of 3 appropriate.

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 and resource ('Reddit keyword search'), gives the endpoint, and scopes it as top-ranked. It also distinguishes the tool from generic social searches by tying it to Reddit and to mining the customer's own words for ad copy.

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 clearly describes the intended use case: mining customer pain points, objections, and language into ad hooks and copy. However, it does not name alternatives such as search_posts, search_threads, or other platform-specific searches, so the agent must infer when Reddit is preferable.

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

search_threadsSearch ThreadsAInspect

Organic Threads keyword search (/v1/threads/search) — short-form text/social posts for trend + voice research. Returns compact JSON {desc, author, handle, likes, link, cover} per post. Spends about a credit.

ParametersJSON Schema
NameRequiredDescriptionDefault
limitNomax posts returned (1–25, default 8)
queryYeskeyword to search Threads for

TDQS

A4.2/5.0
Behavior4/5

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

Annotations cover the safety profile (destructiveHint=false, openWorldHint=true), and the description adds genuinely useful context beyond them: the response shape and the cost ("Spends about a credit"), which matters for a metered open-world search. It stops short of describing rate limits or result ordering.

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

Conciseness5/5

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

Two tight sentences with zero filler; endpoint, corpus, return fields, and cost are front-loaded in that order. Every clause carries information an agent can act on.

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?

With no output schema, the description usefully enumerates the returned fields (desc, author, handle, likes, link, cover), and the cost note rounds out the picture for a paid, non-idempotent call. Minor gaps remain around result ordering or pagination, but nothing critical is missing.

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

Parameters3/5

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

Schema description coverage is 100%, so both query and limit (including the 1–25 range and default 8) are already fully documented in the schema. The description adds no syntax or formatting guidance beyond that, so the baseline 3 applies.

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 (Threads keyword search), names the exact endpoint /v1/threads/search, and characterizes the corpus as short-form text/social posts. This clearly distinguishes it from siblings like search_reddit, search_posts, and search_tiktok.

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 concrete purpose context (trend + voice research), which tells the agent when this tool is relevant. It does not, however, name alternatives or exclusion conditions against the many other search_* siblings, so routing still requires inference.

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

search_tiktokSearch TikTokAInspect

Organic TikTok keyword search (there is NO TikTok ad library) — top-performing videos to mine for hooks/trends/remixable creative. Returns compact JSON {desc, author, handle, plays, likes, link, cover} per video, ranked by plays. Use research_ads for open-ended research. Spends about a credit.

ParametersJSON Schema
NameRequiredDescriptionDefault
limitNomax videos returned (1–25, default 8)
queryYeskeyword or hashtag (no # needed)

TDQS

A4.7/5.0
Behavior5/5

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

Adds substantial context beyond the annotations (readOnlyHint=false, openWorldHint=true, idempotent=false): it discloses a real cost ('Spends about a credit'), the ranking behavior ('ranked by plays'), and the exact return shape (compact JSON with desc/author/handle/plays/likes/link/cover). Cost and return-format disclosure are exactly what annotations cannot convey.

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

Conciseness5/5

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

Front-loaded with the core action and scope, then the return payload, then the alternative, then cost. Every clause carries a distinct fact (negative scope, output fields, ranking, alternative tool, credit cost) with zero 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?

No output schema exists, and the description compensates by enumerating the returned fields. Combined with the stated ranking, cost, and the explicit absence of an ad library, an agent has everything needed to call it correctly and set expectations.

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

Parameters3/5

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

Schema description coverage is 100%, so the schema already documents both query ('keyword or hashtag, no # needed') and limit (1–25, default 8). The description adds no syntax or format detail on top of that, so the baseline 3 applies.

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?

Names a specific verb+resource (organic TikTok keyword search) and immediately bounds it with 'there is NO TikTok ad library', which separates it from the ad-search siblings like search_meta_ads and search_google_ads. The stated output (top-performing videos for mining hooks/trends) tells the agent exactly what it gets back, distinguishing it from post_to_tiktok and tiktok_post_status.

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?

Gives the use case (mine hooks/trends/remixable creative) and names a concrete alternative with its selecting condition: 'Use research_ads for open-ended research.' The negative scope statement about the absent ad library further routes the agent away from a wrong expectation. Nothing is left to inference about when this tool applies.

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

search_youtubeSearch YouTubeAInspect

Organic YouTube keyword search (/v1/youtube/search) — videos to mine for hooks/angles/long-form structure. Returns compact JSON {desc (title), author, handle, plays, link, cover} per video, ranked by views. Spends about a credit.

ParametersJSON Schema
NameRequiredDescriptionDefault
limitNomax videos returned (1–25, default 8)
queryYeskeyword to search videos for

TDQS

A4.3/5.0
Behavior4/5

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

Annotations already declare openWorldHint=true and readOnlyHint=false, but the description adds genuine behavioral value beyond them: the result ordering ('ranked by views'), the exact credit cost ('spends about a credit'), and the returned field shape. The credit-cost disclosure is especially useful for an agent managing a budget.

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

Conciseness5/5

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

A single front-loaded sentence with zero filler; the core purpose leads, followed by use case, return shape, and cost. Every clause earns its place.

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 compensates by enumerating the returned fields ({desc, author, handle, plays, link, cover}) and the ordering. Combined with the cost note, an agent has everything needed to call and interpret it.

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

Parameters3/5

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

Schema coverage is 100% with only two params, so the schema fully documents query and limit. The description adds nothing param-specific (e.g. no note on how limit interacts with the credit cost or ranking). Baseline 3 is appropriate.

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 ('Organic YouTube keyword search') plus the backing endpoint (/v1/youtube/search), which cleanly distinguishes it from siblings like search_tiktok, search_instagram and search_reddit. An agent knows exactly which corpus this hits.

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?

Provides a use case ('videos to mine for hooks/angles/long-form structure'), giving clear context for when to reach for it. It stops short of naming alternatives or stating when NOT to use it (e.g. vs find_creators or search_reddit), so it doesn't reach 5.

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

set_auto_reloadSet auto-reloadA
Idempotent
Inspect

Turn automatic credit reloads on or off (admin only): when the balance drops below a threshold, the card on file is charged for a top-up pack — SERVER-SIDE, even with no app open. Requires a saved card, added once in the app at first checkout/top-up; if there's none the tool tells you exactly where to add it. After that one-time card setup, agents can manage auto-reload, top-ups and plan links fully. Members (read-only billing) get an 'ask an admin' message.

ParametersJSON Schema
NameRequiredDescriptionDefault
enabledYestrue to turn auto-reload on, false to turn it off
reloadCreditsNohow many credits to add each reload — must match a credit pack size (see buy_credits)
thresholdCreditsNoreload when the balance drops below this many credits

TDQS

A4.6/5.0
Behavior5/5

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

Annotations already indicate readOnlyHint=false and destructiveHint=false, but the description adds substantial behavioral detail: the action is SERVER-SIDE and executes even with no app open, charges the card on file, requires a saved card, and gives specific guidance when no card exists. This goes far beyond structured annotation fields and matches the idempotentHint.

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?

The description is longer than average but every sentence carries meaningful information: purpose, permission, server-side behavior, card requirement, and member handling. It is front-loaded with the core action and does not contain filler or repetition.

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?

For a configuration tool with 3 params, no output schema, and no nested objects, the description is complete: it details prerequisites, permissions, execution behavior, and even what the tool tells the user when a card is missing. An agent has enough context to invoke it correctly and know what to expect.

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 covers all three parameters with descriptions, including 'must match a credit pack size (see buy_credits)' and 'reload when the balance drops below this many credits'. The description reinforces the parameter semantics by explaining the threshold-to-charge behavior and top-up pack concept, adding value beyond the schema alone.

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?

Description states a specific verb and resource: 'Turn automatic credit reloads on or off' with '(admin only)' scope. It clearly distinguishes this from billing-related siblings by focusing on the auto-reload mechanism, and is not a tautology of the name.

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?

The description gives clear context on when the tool applies: requires admin role, requires a saved card, and after card setup agents can manage auto-reload. It explains members will receive an 'ask an admin' message, implying a when-not-to-use condition, though it doesn't explicitly name alternative tools like buy_credits for manual top-ups.

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

set_competitor_watchSet the competitor watchA
DestructiveIdempotent
Inspect

Set (or STOP) this workspace's standing COMPETITOR WATCH — the weekly job that re-checks each named brand's ad libraries and reports what is NEW since last time. The same watch the web app's Ad Spy > Watching tab manages, and the same one the weekly digest email is sent from (turn that email on/off with update_settings({watchEmail})). This REPLACES the whole watched list, it does not add to it — pass every brand you want watched, every time. Max 5 brands (the server trims past that). Pass an EMPTY list to stop the watch entirely, which also clears the findings. Give a domain wherever you know one: Google Ads Transparency is looked up BY DOMAIN and is skipped for a brand without one, and the domain is what resolves the right Meta page for a brand with an ambiguous name. The run itself spends credits against the ad libraries (roughly 3 per brand on Meta, 1 each on Google and LinkedIn) and is hard-capped per run server-side, so an oversized watch is trimmed rather than allowed to run away. Setting the list is free; only a run spends. runNow:true runs it once IMMEDIATELY (a background job — it spends now) and then keeps the weekly cadence; leave it off and the first check is a week out. The country and the platform mix are NOT settable here — a re-set inherits whatever the pending run already carried (US / Meta for a watch that has never been configured otherwise). Read the findings back with list_watch_findings.

ParametersJSON Schema
NameRequiredDescriptionDefault
runNowNotrue to run one check immediately (spends credits now) instead of waiting a week for the first one
competitorsYesthe brands to watch — the COMPLETE list, replacing whatever was set before. Empty array = stop watching.

TDQS

A4.8/5.0
Behavior5/5

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

Annotations already mark it destructive and idempotent, but the description adds substantial context beyond that: max 5 brands, server-side trimming, credit costs (~3 per brand on Meta, 1 on Google/LinkedIn), hard-capped runs, immediate run via runNow, and that a re-set inherits prior country/platform. This is rich behavioral disclosure.

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-loads the core purpose and the critical REPLACE-vs-ADD distinction. Some sentences are dense with parenthetical asides, but every sentence carries operational value (costs, limits, alternatives). Slightly long but justified by complexity.

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?

For a tool with no output schema, nested objects, and multiple behavioral facets (destructive replace, credit spend, max limits, immediate run option, inherited settings), the description is remarkably complete. An agent would need no further investigation to call this 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 schema itself documents parameters. The description adds meaning beyond the schema: the REPLACE semantics of the competitors list, the empty-list stops behavior, the domain disambiguation value, and the runNow immediate-run behavior. This goes beyond the schema's mechanical descriptions.

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 (Set/STOP) and resource (the competitor watch) with the exact mechanism: a weekly job that re-checks named brands' ad libraries. Clearly distinguishes itself from siblings like list_watch_findings and pull_competitor_ads by naming them or describing its distinct role.

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?

Explicitly states when/why to use it, when not to (via alternative tools), and the critical semantic that it REPLACES the entire list rather than appends. Names update_settings({watchEmail}) for email control and list_watch_findings for reading results.

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

set_connector_accountsChoose which accounts a brand may useA
DestructiveIdempotent
Inspect

Set WHICH of a connector's accounts this profile is allowed to post to and spend from — Facebook Pages / Instagram / Meta ad accounts, Google Ads customers, LinkedIn company Pages (and the personal profile), Pinterest or Microsoft Advertising ad accounts. Pass ids from list_connector_accounts. This REPLACES the current selection: anything you leave out is un-shared, and an EMPTY list shares nothing (publishing then refuses — it fails closed by design, and the server re-verifies every id against the live connection, so an id the account cannot actually reach is rejected rather than saved). Ask the user which accounts they mean; posting as the wrong Page is a public mistake. Providers: tiktok, x, youtube, threads, bluesky, telegram, reddit, pinterest, instagram, meta, google_ads, linkedin, pinterest_ads, linkedin_ads, reddit_ads, apple_ads, microsoft_ads, google_business, google_analytics, snapchat_ads, x_ads, tiktok_ads, google_tag_manager, google_search_console, bing_webmaster. Free.

ParametersJSON Schema
NameRequiredDescriptionDefault
providerYeswhich connector to scope
accountIdsYesthe ids (from list_connector_accounts) this profile may use — an empty array shares nothing

TDQS

A4.3/5.0
Behavior5/5

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

Annotations declare destructiveHint=true and idempotentHint=true, but the description adds real substance: the operation REPLACES the selection, omitted ids are un-shared, an empty list shares nothing and publishing then fails closed, and the server re-verifies every id against the live connection so unreachable ids are rejected. That is precisely the behavioral detail an agent needs for a destructive setter.

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

Conciseness3/5

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

The core content is front-loaded and dense, but the trailing list of all 25 providers duplicates the enum already present in the input schema verbatim, and the lone 'Free' token adds little. Redundancy with structured data costs it here.

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 needed and annotations covering safety hints, the description still supplies replacement semantics, fail-closed behavior, id validation, and account-type examples. An agent has everything required to call it correctly and safely.

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

Parameters3/5

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

Schema description coverage is 100%, so both parameters are already documented, including the empty-array semantics. The description only adds provenance ('ids from list_connector_accounts') and restates the empty-list rule, so the baseline of 3 applies.

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 (set) plus resource (connector accounts) and scope (which accounts a profile may post to and spend from), then enumerates concrete account types like Facebook Pages, Google Ads customers, and LinkedIn company Pages. An agent can distinguish it from siblings like list_connector_accounts and connect_connector without opening 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?

Points the agent to list_connector_accounts as the id source and instructs 'Ask the user which accounts they mean; posting as the wrong Page is a public mistake.' It gives clear usage context but names no explicit alternative for the setting operation or a when-not-to-use condition.

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

set_post_refillSet up, change or pause autopilot postingA
DestructiveIdempotent
Inspect

Turn AUTOPILOT POSTING (autoposting, the posting refill) on, change it or pause it: "post 2 times a day on Instagram and Bluesky about …, let me review first", "make it 1 a day", "switch to auto-publish", "pause autopilot". PASS ONLY WHAT CHANGES. It always makes NEW posts (a fresh image, carousel or video for each) and never re-posts the Library. WHERE: every connected channel by default (channels narrows it); HOW OFTEN: the brand’s posting times, and channelPostsPerDay sets fewer on a channel (e.g. {"x":1}). There are no credit limits to set — get_post_refill shows roughly what it costs a day. Switching it on is refused when no channel is connected or nothing a day would be made. SWITCHING IT ON ASKS THE BRIEF ONCE: a goal (followers / sales / launch / other) is required; content pillars (get_post_refill suggests some from the brand), formats (mix / video / carousel / image) and an avoid list are optional. Posts are written from the brief, the brand profile, what has worked on each channel (compared only within a channel), the brand’s skills and playbooks and the ads it saved. Ask the user for anything missing (goal, mode, channels) rather than guessing. mode picks what happens to each batch: "review" (the default) keeps the fresh posts as drafts until a human approves them (run_post_refill approve), "auto" schedules them straight away. enabled:false is the PAUSE — it removes the recurring job outright, and posts already queued are left alone (cancel those with cancel_scheduled). Three posting times means up to three posts a day; postsPerDay sets fewer, and to post MORE per day pass postingTimes (one time per post a day) — e.g. "2 a day" on a brand with 3 posting times is postsPerDay:2, "4 a day" needs four postingTimes.

ParametersJSON Schema
NameRequiredDescriptionDefault
goalNoTHE BRIEF: what the posts are for — grow followers, drive sales, promote a launch, or other (say what in goalNote). Required to switch it on.
modeNo"review" (default): each batch of fresh posts waits as drafts for approval. "auto": fresh posts are scheduled to publish automatically.
avoidNoTHE BRIEF: never mention or show these — topics, claims, competitors, faces.
chatIdNoTELEGRAM — which chat, group or channel posts go to (@username or numeric id). Without one, telegram is skipped: there is no default chat and posting to the wrong one is a public mistake.
pageIdNoFACEBOOK / INSTAGRAM / THREADS — which connected Page to publish from (list_meta_pages). Omit for the brand’s only Page.
boardIdNoPINTEREST — which board Pins go on (list_pinterest_boards). Without one, Pinterest is skipped: a Pin on the wrong board is a public mistake, so it is never guessed.
enabledNoon/off. true needs a connected channel and a goal (refused otherwise). false PAUSES it: the recurring job is deleted and nothing new is made. Already-queued posts are untouched.
formatsNoTHE BRIEF: what to make — mix (images with a short video now and then, the default), video (short videos only), carousel (3 new images per post), image.
pillarsNoTHE BRIEF: content pillars / topics, up to 8 short lines. Every post belongs to one. get_post_refill suggests some from the brand profile.
channelsNorestrict it to these channels. Omit (or send an empty list) to use every connected channel that can carry each post.
goalNoteNoTHE BRIEF: a line about the goal (e.g. the launch date and what is launching).
timezoneNoIANA timezone the posting times are in, e.g. "America/New_York" (default: the brand's, else UTC)
daysAheadNohow far ahead to keep the queue full, 1–30 (default 7)
learningsNonotes learned from reviews; REPLACES the list
postsPerDayNoposts a day, up to the number of posting times ("2 times a day" = 2). 0 (default) = one per posting time. To post MORE per day than there are posting times, also pass postingTimes.
postingTimesNothe brand's daily posting times, 24-hour "HH:MM" (e.g. ["09:00","13:00","18:00"]); REPLACES the list. Autopilot makes one post per posting time, and scheduled posts use the same times. get_post_refill shows the current ones.
channelPostsPerDayNoper channel, how many posts a day (the first N of the posting times), e.g. {"x":1,"instagram":3}. 0 turns a channel off; null resets it to every posting time. Merged into what is saved.
forgetCaptionEditsNo
linkedinOrganizationIdNoLINKEDIN — which company Page to post as (list_linkedin_pages). A single shared Page is used automatically; a Page that is not shared with this profile is ignored rather than failing the whole post.

TDQS

A4.8/5.0
Behavior5/5

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

Goes well beyond the annotations (readOnly=false, destructive=true, idempotent=true, openWorld=true) by disclosing what the pause actually destroys (the recurring job is deleted), what is left untouched (queued posts), and that switching on always creates NEW assets and never re-posts the Library. Refusal preconditions and the once-only brief requirement are behavior an agent could not infer from structured fields.

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 action and example phrasings, and most sentences earn their place given 19 parameters. It is dense and repeats the postsPerDay/postingTimes relationship that the schema already spells out, which costs some crispness.

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?

No output schema exists, and for a 19-parameter, nested-object mutation tool the description covers defaults, refusal conditions, pause semantics and cross-parameter interactions thoroughly. An agent has enough to invoke it correctly without external context.

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 95%, so the baseline is 3, but the description adds real meaning on top: the interaction between postsPerDay, postingTimes and channelPostsPerDay (e.g. "4 a day" requires four postingTimes) and the PASS ONLY WHAT CHANGES convention. It stops short of documenting a few params (e.g. forgetCaptionEdits, daysAhead defaults) in prose.

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 set (turn on / change / pause) and the exact resource (autopilot posting, the posting refill), and immediately distinguishes itself from siblings get_post_refill, run_post_refill and cancel_scheduled. An agent can tell what this does and what it is not without opening the 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?

Explicitly routes to alternatives: get_post_refill for cost/current times, run_post_refill to approve drafts, cancel_scheduled for already-queued posts. It also states refusal conditions (no channel connected, nothing a day would be made) and instructs the agent to ask the user for missing goal/mode/channels rather than guessing.

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

set_product_imageSet product photoA
DestructiveIdempotent
Inspect

Lock an image as the ad's real PRODUCT photo and SAVE it as this brand's default product, so every later plan_ad / render_ad / generate_image grounds on the true packaging without being told again. Pass imageUrl = a product shot's URL — an image from a prior research result (an organic Instagram/TikTok post, a scraped page image), a workspace / list_product_photos url, or any public product photo. The server downloads it and runs a product+safety check: a lifestyle/scene shot with no clear product, or an off-category / unsafe image, is REJECTED and NOTHING is locked or saved (the summary says why). On PASS it persists the photo to a DURABLE url, writes it to the brand's product library as the DEFAULT, and READS THE BRAND BACK to confirm — savedToBrand and the summary report what the brand ACTUALLY holds now, never what was asked for, so if it did not become the default you are told instead of finding out from a paid render. Bills one vision check. Reads YOUR saved brand for the category match (pass brandId to target a specific profile — switches this key's active profile like use_brand).

ParametersJSON Schema
NameRequiredDescriptionDefault
brandIdNoa profile id/name from list_brands to lock the product for; omit to use the active profile
imageUrlYesthe image URL to lock as the product (from a research result, a workspace / list_product_photos url, or any public product photo)
source_noteNoa short note on where it came from, e.g. "from their IG post"

TDQS

A4.6/5.0
Behavior5/5

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

Far exceeds the annotations: discloses the product+safety vision check, the rejection path (lifestyle/off-category/unsafe images lock nothing), durable-URL persistence, the default-write to the brand library, the read-back confirmation via savedToBrand, and the one-vision-check billing. This is exactly the behavioral context an agent needs for a destructive mutation.

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-loads the lock/save purpose and its downstream effect, then layers the check, rejection, and confirmation semantics. Dense and heavily emphasized with caps, but nearly every sentence carries information an agent must not miss.

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?

For a destructive, no-output-schema mutation tool, the description covers the happy path, the failure/rejection path, the returned fields (savedToBrand, summary), billing, and profile targeting. Nothing an agent needs to call it correctly is missing.

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 adds real value beyond the schema: concrete source examples for imageUrl and the note that brandId switches this key's active profile like use_brand. source_note is left to the schema.

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+resource ('Lock an image as the ad's real PRODUCT photo and SAVE it as this brand's default product') and immediately ties it to downstream effects on plan_ad/render_ad/generate_image, distinguishing it from siblings like list_product_photos and generate_image.

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?

Explains clearly what imageUrl should be (a research result, a workspace/list_product_photos URL, or any public product photo) and how brandId targets a profile like use_brand. It stops short of an explicit when-not-to-use rule, but the context is strong and actionable.

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

set_roleChange a teammate’s roleA
DestructiveIdempotent
Inspect

Change a workspace member’s role — admin (full access incl. billing) or member (read-only on billing). A privilege change: confirm the exact person + new role with the user, then call with confirm:true.

ParametersJSON Schema
NameRequiredDescriptionDefault
roleYesthe new role
emailYesthe member’s email
confirmNoREQUIRED true

TDQS

A4.5/5.0
Behavior4/5

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

Annotations already declare destructiveHint=true and idempotentHint=true, so the safety profile is covered. The description adds real value beyond them: it explains what the roles grant (admin = full access incl. billing, member = read-only on billing) and frames this as a privilege change requiring confirmation, which is not in 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.

Conciseness5/5

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

Two tight sentences with the role semantics front-loaded and the confirmation requirement second. No filler; every clause earns its place.

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 rich annotations, full schema coverage, and no output schema to explain, the description supplies everything an agent needs: what changes, the two role meanings, and the required confirmation step. Nothing material is missing.

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. The description goes further by explaining the semantic consequences of each enum value (admin vs member billing access) and clarifying the confirm workflow, adding meaning beyond the schema's terse 'the new role'.

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 (Change) and resource (a workspace member's role) and enumerates the two allowed outcomes with their meaning. An agent can distinguish it from invite_member and remove_member without opening 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?

Gives clear procedural guidance: confirm the exact person + new role with the user before calling, and call with confirm:true. It lacks explicit routing to siblings (e.g. when to use remove_member vs set_role), which keeps it below a 5.

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

stitch_videoStitch multi-scene videoAInspect

Render a multi-scene STITCHED video (≥2 scenes) — ONLY for spots LONGER than ONE clip of the chosen model. A multi-beat ad that FITS one clip renders better and cheaper as ONE single-pass generate_video/render_ad (a single generation carries the whole hook->demo->payoff arc) — never stitch those. What fits is the model’s own maximum from hermoso_capabilities, not a fixed number: 15s on most models, 30s on the longest-clip one, so a 30s spot need not be stitched at all if you name that model. Blocks until done. Spends credits.

ParametersJSON Schema
NameRequiredDescriptionDefault
modelNovideo model id from hermoso_capabilities — omit to let the router pick
voiceNovoiceover voice name, e.g. Rachel / George
scenesYesarray of scene objects (visual + optional voiceover/seconds)
voiceoverNofull voiceover script spoken across the scenes
resolutionNo720p (default), 1080p for full detail, or 480p for a cheaper draft
aspectRatioNooutput aspect ratio, e.g. 9:16 (default) / 1:1 / 16:9
durationSecondsNototal spot length in seconds (defaults to the sum of the scenes’ seconds)

TDQS

A4.3/5.0
Behavior4/5

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

Annotations disclose write/openWorld/non-idempotent, and the description adds two behaviors annotations cannot express: 'Blocks until done' (synchronous long-running call) and 'Spends credits' (cost). These materially affect invocation, though failure modes and partial-render behavior remain unstated.

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?

The governing rule (only stitch when longer than one clip) is front-loaded, with rationale following. It is dense and em-dash-heavy, leaning on caps-lock emphasis, but nearly every clause carries routing or cost information rather than filler.

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 non-read-only tool with no output schema, the description covers the decision logic, the blocking behavior, and the credit cost. It stops short of describing the returned result or how to inspect an in-progress render, so it is strong but not exhaustive.

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

Parameters3/5

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

Schema description coverage is 100%, so all 7 parameters are already documented in the schema; per the rubric the baseline is 3. The description reinforces a few (scenes ≥2, model from hermoso_capabilities, default duration as the sum of scene seconds) but adds little beyond what the schema already conveys.

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+resource ('Render a multi-scene STITCHED video') with the defining constraint (≥2 scenes), and names the sibling tools (generate_video/render_ad) it must not be confused with. An agent can distinguish it from its closest neighbors without opening either 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?

Explicitly states when to use (spots longer than one clip of the chosen model), when not to use (any multi-beat ad that fits a single clip), and the alternative to pick instead. It even corrects the fixed-number assumption by pointing to hermoso_capabilities for the model's own max, so the routing decision is fully specified.

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

store_getRead a workspace storeA
Read-onlyIdempotent
Inspect

Read one of this workspace’s data stores by key, for visibility into what the app holds — playbooks, swipefile, saved locations, avatars, creations, chats, brand, memory, skills. Read-only, free. Allowed keys: heist.memory.v1, heist.skills.v1, heist.playbooks.v1, heist.avatars.v1, heist.locations.v1, heist.chats.v1, heist.creations.v1, heist.assets.v1, heist.brand.v1, adInspo.swipefile.v1. (The typed tools — list_memory / list_skills / get_brand — are friendlier for those; use store_get for the rest.)

ParametersJSON Schema
NameRequiredDescriptionDefault
keyYesthe store key to read (one of the allowlisted keys)
limitNomax array items to return (default 50)

TDQS

A4.5/5.0
Behavior4/5

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

Annotations already declare readOnlyHint, idempotentHint, destructiveHint=false, openWorldHint=false, so the safety profile is covered. The description adds value beyond them by stating 'free' (cost) and, critically, disclosing the allowlisted keys — an invocation constraint not expressible in the schema. Return format is still unspecified, keeping it short of a 5.

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 purpose, then the essential allowlist, then the routing parenthetical. Dense but every clause carries signal; the long key list is necessary because the schema lacks enums.

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?

Given read-only annotations, no output schema, and simple two-param input, the description covers purpose, valid keys, cost, and sibling routing. It stops short of describing the returned shape (the fact that limit caps array items hints but does not explain).

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% (baseline 3), but the schema does not enumerate valid keys (parameters with enums: 0). The description compensates by listing all valid heist.* / adInspo.* key values, adding meaning the schema lacks. Two sentences noting the limit is about max array items would push it higher.

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 (Read) and resource (one of this workspace's data stores by key), then enumerates the concrete contents (playbooks, swipefile, locations, avatars, etc.). An agent can immediately distinguish it from typed siblings like list_memory and get_brand.

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?

Explicitly routes the agent: names the typed alternatives (list_memory / list_skills / get_brand) as friendlier for those domains and instructs to 'use store_get for the rest.' The allowlist of keys bounds when this tool is valid at all.

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

subscribe_linkedin_leadsTurn on real-time LinkedIn lead deliveryA
Idempotent
Inspect

Have LinkedIn push every new lead to Hermoso the moment it is submitted, and optionally relay each event on to the user’s own CRM. THE WEBHOOK LINKEDIN VALIDATES IS ALWAYS HERMOSO’S OWN: LinkedIn challenges it with our app secret (and re-challenges every ~2 hours), which no CRM, Zapier or Make endpoint can answer — so never promise a customer URL as the LinkedIn webhook. Pass forwardTo (public HTTPS) to have Hermoso relay each lead event there; leave it off to keep events in Hermoso only (list_linkedin_lead_events). The reply is read back from LinkedIn, not from the 201. Leads stay readable with list_linkedin_leads either way. Free.

ParametersJSON Schema
NameRequiredDescriptionDefault
pageIdNothe company Page that owns the form — from list_linkedin_pages; omit when one Page is shared
leadTypeNodefaults by owner: SPONSORED for an ad account, COMPANY for a Page
forwardToNooptional public HTTPS URL Hermoso relays each lead event to (a CRM, Zapier, Make)
adAccountIdNoread forms owned by an AD ACCOUNT instead of a Page

TDQS

A4.5/5.0
Behavior5/5

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

Beyond what annotations state, it discloses the webhook validation mechanism: LinkedIn challenges Hermoso's own webhook with the app secret every ~2 hours, which no CRM/Zapier/Make endpoint can answer — the key reason a customer URL must never be promised. It also discloses reply semantics ('The reply is read back from LinkedIn, not from the 201') and post-conditions (events stay in Hermoso when forwardTo is omitted; leads remain readable either way). None of this contradicts the annotations; idempotentHint=true and destructiveHint=false are consistent with a non-destructive subscription action.

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?

The core purpose is front-loaded, and the critical webhook constraint is CAPS-flagged so an agent cannot miss it. Each subsequent sentence covers one distinct fact: forwardTo behavior, reply semantics, persistence of leads, and cost. It is dense — the webhook sentence packs substantial information — but no sentence is wasted.

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?

With no output schema, the description compensates by covering the call's effect, the hard webhook constraint, forwardTo behavior, reply semantics, and post-conditions. Minor gaps: it does not point to list_linkedin_lead_subscriptions or delete_linkedin_lead_subscription for managing the subscription afterward, nor does it describe failure/validation scenarios.

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%, so the baseline is 3; the description adds meaning by explaining forwardTo's behavioral consequence — 'leave it off to keep events in Hermoso only' — and clarifying that forwardTo is a relay target, not the LinkedIn webhook. pageId, leadType, and adAccountId gain no new semantics beyond their schema descriptions, which already document defaults and source ownership.

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 opening sentence states a specific action and resource: 'Have LinkedIn push every new lead to Hermoso the moment it is submitted' — a clear verb+resource+trigger semantics. The title 'Turn on real-time LinkedIn lead delivery' matches, and the description distinguishes this creation action from siblings like list_linkedin_lead_events and list_linkedin_lead_subscriptions, which read or manage events/subscriptions rather than create the push.

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?

The description gives concrete context: forwardTo on/off selects relay-to-CRM versus Hermoso-only, and it cross-references list_linkedin_lead_events for the Hermoso-only case and list_linkedin_leads as the persistent read path 'either way.' It also contains an explicit when-not ('never promise a customer URL as the LinkedIn webhook'). It stops short of fully enumerating sibling selection — e.g., when to use list_linkedin_lead_subscriptions or delete_linkedin_lead_subscription to manage an existing subscription.

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

tidy_memoryTidy the Memory listA
DestructiveIdempotent
Inspect

Clean up and consolidate the workspace Memory: drops entries that are about how Hermoso, a tool, a connector or a platform API behaves (product behaviour, not the brand), drops phone numbers and emails, and merges near-duplicate facts into one sentence each. Call with no argument to get the PROPOSAL (what would be removed and merged, with reasons) — nothing changes. Call again with confirm:true to apply it through the same typed writers the app uses (deletes carry tombstones so they stay deleted on every device). One small model call; a few credits.

ParametersJSON Schema
NameRequiredDescriptionDefault
brandNoprofile id/name from list_brands, this call only
confirmNotrue to APPLY the proposal; omit to only see it

TDQS

A4.3/5.0
Behavior4/5

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

Adds substantial context beyond annotations: the two-phase proposal/apply flow (nothing changes first call), application through 'the same typed writers the app uses', tombstoned deletes that stay deleted across devices, and a cost note (one small model call, a few credits). Annotations cover write/destructive/idempotent hints, but the description uniquely explains reversibility stance and cross-device persistence. Minor gap: doesn't describe the proposal's return format, though no output schema exists.

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?

Three tight sentences, each carrying distinct payload (scope, dry-run workflow, apply semantics and cost). Front-loaded with the core action. Slightly dense but no filler.

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?

Given a destructive, two-phase mutation with zero-param defaults and no output schema, the description covers what gets removed, what gets merged, how to preview, how to apply, safety of deletes, and cost. It doesn't specify the proposal's field structure or how brand scoping interacts with workspace-level Memory, which is a small residual gap.

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

Parameters3/5

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

Schema coverage is 100%, so both parameters are already documented in the schema. The description adds the practical meaning of confirm ('nothing changes' vs 'apply') and the ephemeral scope of brand ('this call only' is in the schema description too), which is a modest clarification rather than new syntax or format detail. Baseline 3 is appropriate when the schema does the heavy lifting.

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 specific verbs and resource: 'Clean up and consolidate the workspace Memory', then enumerates the exact operations (drops product-behaviour entries, drops phone/email, merges near-duplicates). This is unmistakably distinct from siblings list_memory, remember, and forget.

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?

Explicitly states the dry-run-first workflow: call with no argument to get the PROPOSAL with reasons, then call again with confirm:true to apply. The condition selecting each mode is spelled out, which is exactly what an agent needs.

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

tiktok_post_statusCheck whether a TikTok post finished publishingA
Read-onlyIdempotent
Inspect

Follow a TikTok post that post_to_tiktok sent but TikTok was still processing (it came back pending:true). Pass the publishId it returned. Answers one of three states: done (PUBLISH_COMPLETE, with the public postId when TikTok gives one), failed (with TikTok’s reason), or still processing, which is normal for a few minutes and is NOT a failure. Never post the video again while it is processing. Free, read-only. Needs TikTok connected (Settings > Connectors > TikTok).

ParametersJSON Schema
NameRequiredDescriptionDefault
accountNowhich connected TikTok account the post went out on (@handle or id from list_connector_accounts) when the profile has more than one
publishIdYesthe publishId post_to_tiktok returned

TDQS

A4.7/5.0
Behavior5/5

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

Annotations already cover the safety profile (read-only, idempotent, non-destructive), and the description adds substantial value beyond them: the three possible outcomes and their meaning, the key caveat that 'still processing' is normal and NOT a failure, and the auth prerequisite (TikTok connected via Settings > Connectors).

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

Conciseness5/5

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

Front-loaded with the trigger and the action, then the outcome states, then the critical warning and auth note. Every sentence earns its place; no 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?

No output schema exists, so the description carries the return-value burden and does so fully by enumerating done/failed/processing and clarifying what each means. An agent has everything needed to call and interpret the result.

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

Parameters3/5

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

Schema description coverage is 100%, so both parameters are already documented. The description reinforces that the publishId is the value post_to_tiktok returned but adds no format or syntax detail beyond the schema, so the baseline 3 is appropriate.

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 (follow/check) and resource (a TikTok post) and ties itself precisely to its origin tool (post_to_tiktok returned pending:true). An agent can distinguish this from post_to_tiktok or search_tiktok immediately.

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 trigger condition (post came back pending:true), explicit input (pass the publishId), and an explicit exclusion/anti-pattern ('Never post the video again while it is processing'). Nothing about when-to-use is left to inference.

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

update_brandUpdate profile fieldsA
Destructive
Inspect

Patch SPECIFIC fields of the active profile (name, domain, sells, summary, category, audience, positioning, voice, style, goal, logos, and how the brand and product names are pronounced) WITHOUT overwriting the rest — a read-modify-write on the saved brand. Use for “change our voice to playful”, “we sell to dentists now”. To onboard a brand from scratch, use draft_brand. If no brand is saved yet and you only INFERRED one from what the user is making, ask them to confirm it is their brand before saving it (they may be working for a client or just trying things). Only pass the fields you’re changing.

ParametersJSON Schema
NameRequiredDescriptionDefault
goalNocurrent marketing goal
logoNo
nameNo
brandNoprofile id/name from list_brands, this call only
sellsNowhat the brand sells
styleNovisual style — palette, typography, aesthetic
voiceNobrand voice/tone
domainNowebsite domain
summaryNoone-line description
audienceNo
categoryNo
logoDarkNofor light pictures
logoLightNofor dark pictures; "none" removes
pronounceNohow the brand NAME is said aloud, as a simple respelling with the stressed syllable in capitals (e.g. "KOH-dee-ak"). Videos use it as a delivery note beside the spoken line; set it when a render mispronounced the name.
positioningNo
pronunciationsNohow PRODUCT names are said aloud, e.g. {"Power Cakes": "POW-er cakes"}. Merged into the saved ones; an empty string removes one.

TDQS

A4.5/5.0
Behavior4/5

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

Annotations already declare destructiveHint=true and idempotentHint=false; the description adds real behavioral context on top by explaining the read-modify-write semantics and that omitted fields are preserved. It also warns that inferred brands must be confirmed before saving. The only minor gap is not stating what the destructive aspect (field removal via "none"/empty string) means at the tool level, though that is covered per-parameter in the schema.

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-loads the core patch/partial-update behavior, then examples, then the alternative and the confirmation caveat. The long parenthetical field list is somewhat redundant with the schema properties, but every sentence otherwise earns its place and the ordering is sensible.

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 16-param mutation tool with no output schema, the description covers mutation semantics, partial-update safety, sibling routing, and the inferred-brand precondition, while annotations cover the safety profile. Return-value detail is not needed without an output schema; the definition is essentially complete.

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 69%, so the description must carry some weight: it enumerates the fields being patched and adds the crucial partial-update rule ("Only pass the fields you're changing"). The per-parameter removal semantics live in the schema rather than the description, which keeps this below 5, but the description meaningfully compensates for the coverage gap.

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 (patch), resource (active brand profile), and precise scope (specific fields only, not the rest) with an explicit read-modify-write framing. It enumerates the updatable fields and names draft_brand as the sibling it is not, so an agent can distinguish it from onboarding tools 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?

Gives concrete triggers ("change our voice to playful", "we sell to dentists now"), an explicit alternative for the from-scratch case (draft_brand), and a nuanced precondition about inferred brands requiring user confirmation. When-to-use, when-not-to-use, and the edge case are all spelled out.

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

update_docEdit a Google Doc in placeA
DestructiveIdempotent
Inspect

EDIT a Google Doc — the correction append_to_doc cannot make, which until now meant a doc could only ever grow and a wrong line stayed in it forever. Two shapes: replacements:[{find, replace}] rewrites specific text wherever it appears (call read_doc first and match the text EXACTLY; matchCase:false ignores case), or rewrite:"…" replaces the ENTIRE body (rewrite:"" empties it), or dropdowns:[{title, value}] sets a dropdown chip (e.g. Status -> Approved — read_doc lists every chip with its options; pass dropdownId when two share a title; an unknown title or option is refused with the real list and nothing changes); the same entry with options:[…] (the COMPLETE new list, 2–50) and/or newTitle changes the dropdown itself for every chip built from it, and an option you drop that a chip shows needs replace:{"Old":"New"}. Find/replace runs immediately and REPORTS how many occurrences changed — zero matches is reported as a FAILURE to match, never as a quiet success, because a text edit that silently does nothing is worse than one that visibly fails. A whole-body rewrite is destructive: call it without confirm first to get the character count, then confirm:true + confirmCells. Both are index-free by design — an agent cannot reliably compute Google’s character offsets, and a wrong offset deletes the wrong sentence.

ParametersJSON Schema
NameRequiredDescriptionDefault
docUrlNoa Google Docs URL — the id is extracted from it
confirmNo
rewriteNoreplace the WHOLE body with this text ("" empties the doc)
dropdownsNodropdown chips to set, or dropdowns to change
documentIdNothe document id (from create_doc, or list_drive_files for one the user picked)
confirmCellsNoecho back the character count the unconfirmed call reported (rewrite only)
replacementsNofind/replace pairs, applied in order

TDQS

A4.8/5.0
Behavior5/5

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

Annotations already declare destructiveHint=true, but the description goes well beyond them: it spells out the two-step destructive flow (unconfirmed call returns a character count, then confirm:true + confirmCells), states that zero find/replace matches are surfaced as a failure rather than silent success, and notes that an unknown dropdown title/option is refused with the real list and leaves state unchanged. It also explains the index-free design rationale. No contradiction with 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?

Front-loaded with the verb and scope, and every sentence carries operational content (shapes, prerequisites, confirmation flow). It is dense to the point of being a wall of text, with a few rhetorical asides ("a wrong line stayed in it forever") that add flavor rather than instruction, but there is no truly wasted sentence.

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?

There is no output schema, so the description must cover returns, and it does: find/replace reports the number of occurrences changed, an unconfirmed rewrite reports the character count, and a rejected dropdown edit reports the real option list. Combined with the confirmation workflow and the read_doc prerequisite, 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 already 86%, so the schema carries most parameter meaning, but the description adds cross-parameter semantics the schema does not: matchCase:false ignores case, dropdownId is needed only when two dropdowns share a title, replace:{Old:New} is required for a dropped option a chip shows, and confirmCells must echo the count from the unconfirmed call. These are genuine additions above the baseline 3.

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 ("EDIT a Google Doc") and immediately distinguishes it from the sibling it is not ("the correction append_to_doc cannot make"). An agent can tell what this does and why it exists next to append_to_doc without opening either 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?

It enumerates the three call shapes (replacements, rewrite, dropdowns), states the prerequisite for each ("call read_doc first and match the text EXACTLY" for replacements; "read_doc lists every chip with its options" for dropdowns), and routes the agent to the sibling read_doc as the discovery step. The when-to-use and when-not-to-use conditions are explicit rather than inferred.

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

update_drive_fileRename / move / trash a Drive fileA
DestructiveIdempotent
Inspect

Update a Drive file: rename (name), move it into a folder (moveToFolderId, optionally removeFromFolderId to move OUT of the old one), or trash / untrash it (trash:true|false). Pass fileId (from list_drive_files). To delete permanently, use delete_drive_file.

ParametersJSON Schema
NameRequiredDescriptionDefault
nameNonew name
trashNotrue -> move to Trash; false -> restore from Trash
fileIdYesthe Drive file id
moveToFolderIdNofolder id to move the file into (from create_drive_folder / list_drive_files)
removeFromFolderIdNothe old parent folder id to remove (when moving)

TDQS

A4.4/5.0
Behavior4/5

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

Annotations already declare destructiveHint=true and idempotentHint=true, but the description adds non-obvious behavior: that trash:false restores, that moving OUT of the old folder requires removeFromFolderId, and that permanent deletion lives elsewhere. This is genuine value beyond the annotations, though it does not describe the response shape.

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

Conciseness5/5

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

Front-loads the core purpose and packs the three modes and the sibling pointer into three tight sentences; every parenthetical earns its place with no redundant restatement of the schema.

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 5-param mutation tool with no output schema, the description covers all operations, parameter roles, reversibility, and the delete alternative. Full marks would require a note on what the updated file response contains, but that is minor here.

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 adds relational meaning the schema does not: name implies rename, moveToFolderId pairs with removeFromFolderId to fully relocate, and trash is a tri-state-ish toggle. This extra semantic framing lifts it above baseline.

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 (update) and resource (Drive file) and enumerates the three distinct modes: rename, move, trash/untrash. It names the sibling delete_drive_file as the permanent-delete alternative, so an agent can distinguish them without opening schemas.

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?

Explains when each mode applies (rename via name, move via moveToFolderId/removeFromFolderId, trash boolean) and explicitly routes permanent deletion to delete_drive_file. It lacks an explicit note on when to prefer update_onedrive_file or how this differs from save_to_drive, which is the only gap.

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

update_onedrive_fileRename / move a OneDrive fileA
DestructiveIdempotent
Inspect

Update a OneDrive item: rename (name) and/or move it into a folder (moveToFolderId). Pass fileId (from list_onedrive_files). To remove an item, use delete_onedrive_file.

ParametersJSON Schema
NameRequiredDescriptionDefault
nameNonew name
fileIdYesthe OneDrive item id
moveToFolderIdNofolder id to move the item into (from create_onedrive_folder / list_onedrive_files)

TDQS

A4/5.0
Behavior3/5

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

Annotations already declare destructiveHint=true, idempotentHint=true, and readOnlyHint=false, so the mutation/safety profile is covered by structured data. The description adds marginal value by naming parameter sources and redirecting deletes, but says nothing about reversibility, permission requirements, or what happens to the item's other properties on rename.

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

Conciseness5/5

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

Two tight sentences, front-loaded with the core action and scoping, with the alternative-tool pointer and parameter source packed in without waste. Every clause earns its place.

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 simple 3-parameter tool with full schema coverage and annotations covering the safety profile, the description is nearly complete: it covers what changes, what's required, and the delete alternative. Minor gap is it doesn't clarify behavior when only one of name/moveToFolderId is supplied, though 'and/or' implies either.

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

Parameters3/5

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

Schema description coverage is 100%, so all three parameters are already documented, and the description largely maps to them (rename=name, move=moveToFolderId). It adds the useful hint that fileId originates from list_onedrive_files, but for a fully-covered schema the baseline of 3 is appropriate.

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 (update) and resource (OneDrive item) and immediately enumerates the two operations it can perform: rename via `name` and move via `moveToFolderId`. It explicitly names and routes away from the sibling `delete_onedrive_file`, so an agent can distinguish it 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 Guidelines4/5

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

It gives clear context ('rename and/or move') and an explicit exclusion routing deletion to `delete_onedrive_file`, plus provenance for the required id ('from list_onedrive_files'). It stops short of a full when/when-not matrix (e.g. when to prefer this over `save_to_onedrive` or `convert_onedrive_file`), but the routing is actionable.

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

update_saved_creatorUpdate a saved creator (outreach status, note)A
DestructiveIdempotent
Inspect

Set the OUTREACH STATUS and/or a NOTE on a creator already saved in the swipefile (find_creators -> save_to_swipefile, or the heart on a creator card). Status is one of new | contacted | replied | booked | passed. The note is free text (deal terms, rate, what was sent). Reads back the updated row. Use list_swipefile to find the key. Free.

ParametersJSON Schema
NameRequiredDescriptionDefault
keyYesthe saved row's key from list_swipefile, e.g. tiktok:handle
noteNoreplaces the existing note; pass "" to clear it
brandNoprofile id/name from list_brands, this call only
statusNo

TDQS

A4.3/5.0
Behavior4/5

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

Annotations already declare the mutation/destructive/idempotent profile, so the bar is lower; the description adds real value beyond them by stating 'Reads back the updated row' (return behavior) and 'Free' (no cost). It does not spell out that overwriting a note is the destructive action, but that is covered in the schema text.

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-loads the core action and status enum, then adds the key-finding tip and cost note. Every sentence carries information, though the workflow parenthetical and the 'Free' note make it slightly denser than strictly necessary.

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 4-param mutation tool with no output schema, the description covers the action, the status vocabulary, note purpose, key provenance, and the return behavior ('reads back the updated row'). Coverage is solid; only the brand parameter and the destructive effect of note replacement are left implicit.

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 75%, and the description still enriches it: it enumerates the valid status values and clarifies the note's intended use ('deal terms, rate, what was sent'). It omits the 'brand' parameter entirely, leaving only the schema description to cover that last quarter.

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 ('Set') and resource ('outreach status and/or note on a creator already saved in the swipefile'), with a parenthetical workflow hint (find_creators -> save_to_swipefile, or the heart). An agent can distinguish it from siblings like save_creator, save_to_swipefile, or update_brand without opening 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?

Gives a clear prerequisite/routing instruction: 'Use list_swipefile to find the key', and ties the tool to the save flow. It lacks explicit when-not guidance (e.g. how it differs from save_creator for brand-new creators), but the context needed to use it is present.

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

update_settingsChange app settingsA
DestructiveIdempotent
Inspect

Change this account's app settings. language = the language EVERY ad, script, plan and answer is written in from now on (say the language in plain English, e.g. "German", "Japanese", "Brazilian Portuguese") — it applies to renders made over MCP as well as in the app. theme = the app's appearance, "dark" or "light". watchEmail = the weekly competitor-watch email on/off. Only pass what you are changing. Account-wide (every profile), and it takes effect on the next call.

ParametersJSON Schema
NameRequiredDescriptionDefault
themeNoapp appearance
languageNolanguage for generated ads, copy and answers — e.g. "English", "German", "Japanese"
watchEmailNoweekly competitor-watch email on/off

TDQS

A3.8/5.0
Behavior4/5

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

Goes beyond the annotations by disclosing that the language change governs EVERY future ad, script, plan and answer, applies to MCP renders as well as the in-app UI, spans all profiles, and takes effect on the next call — context that justifies the destructiveHint. It stops short of stating reversibility or any permission requirements.

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-loads the core action, then documents each of the three parameters in order before the closing scope/timing notes. Dense and mostly waste-free, though the language parenthetical is long relative to the simple enum-style values.

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?

With no output schema and annotations already covering idempotency and safety, the description supplies the parameter meanings, the global scope, and the activation timing an agent needs. Minor gaps remain around reversibility and required permissions, but nothing essential is missing.

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 already 100%, so baseline is 3, but the description adds real meaning: the language must be given as a plain-English name (with examples like "Brazilian Portuguese") and its forward-looking scope, plus the purpose of watchEmail and the allowed theme values.

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

Purpose4/5

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

States a specific verb (change) and resource (this account's app settings) plus scope (account-wide, every profile). It is clearly distinct from get_settings by the mutation verb, but it never names the sibling explicitly, so sibling differentiation is left to inference.

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

Usage Guidelines3/5

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

'Only pass what you are changing' is a useful partial-update instruction, and the account-wide/next-call notes give context. However, there is no explicit when-to-use guidance or pointer to get_settings as the read counterpart, so usage is only implied.

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

update_sheetWrite to a range in a Google SheetA
DestructiveIdempotent
Inspect

CORRECT cells in a Google Sheet — write values to an exact range, overwriting whatever is there. This is the fix append_to_sheet cannot make: appending only ever adds rows at the bottom, so without this a wrong number stays wrong forever and the only "correction" is a second row contradicting the first. Pass range (e.g. "B2:C5", or "Q3 Report!B2" to name a tab — list_sheet_tabs gives the names) and values as an array of row arrays; an anchor cell like "B2" is fine and the block is written down and right from it. Writing into EMPTY cells goes straight through. Writing OVER cells that already hold values is REFUSED first, naming exactly how many filled cells would be overwritten — show the user that, get a yes, then call again with confirm:true. The result is READ BACK from the sheet, so what you report is what the sheet now holds rather than what Google accepted.

ParametersJSON Schema
NameRequiredDescriptionDefault
rangeNoA1 range or anchor cell, e.g. "B2:C5", "B2", or "Q3 Report!B2" (default A1)
valuesNoarray of row arrays to write
confirmNorequired only when the target range already holds values
updatesNowrite SEVERAL disjoint ranges in one call, instead of range+values
sheetUrlNo
spreadsheetIdNo
valueInputOptionNoUSER_ENTERED (default) parses formulas, dates and numbers the way typing them would; RAW stores every value as literal text

TDQS

A4.8/5.0
Behavior5/5

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

The description discloses the destructive overwrite behavior and the guarded confirmation flow: writing over filled cells is refused until the agent shows the count and calls again with confirm:true. It also reveals that the result is read back from the sheet, extending the annotations with operationally important detail.

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

Conciseness5/5

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

The description is longer than average but every sentence earns its place: purpose, alternative comparison, input format, safety flow, and read-back guarantee are presented in a logical order. The rhetorical explanation of append_to_sheet's limitation directly supports tool selection rather than padding.

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?

It thoroughly covers purpose, alternatives, confirmation safety, and result verification, and it compensates for the missing output schema by explaining that values are read back from the sheet. The remaining gaps are the exact return shape and the sheetUrl/spreadsheetId target identifiers, which are conventional and partially covered by the schema.

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?

The schema already documents most parameters, and the description adds concrete examples for range formats, anchor-cell expansion, and row-array shape. However, it does not add detail for updates, sheetUrl, or spreadsheetId, so it slightly misses full semantic coverage.

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 and object ('CORRECT cells in a Google Sheet') and immediately defines the action as writing to an exact range and overwriting whatever is there. It explicitly contrasts itself with append_to_sheet, making its role unambiguous within a large sibling list.

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?

It explicitly says this is the fix append_to_sheet cannot make and explains why appending is insufficient for corrections. It also points to list_sheet_tabs for getting tab names, giving the agent concrete routing guidance.

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

upgrade_planUpgrade planAInspect

Change this account's SUBSCRIPTION plan (admin only). Call with no argument to list the plans (id · monthly price · monthly credits); call again with plan set to a plan id. A NEW subscriber gets a ready-to-pay Stripe Checkout URL to hand your human — THEY pay on Stripe (agents never spend money directly). If the account already has a paid plan, or you're DOWNGRADING, the change is made by a person in the app (Settings -> Billing) and the tool returns exactly what to do. Members (read-only billing) get an honest 'ask an admin' message. Nothing is charged until your human pays.

ParametersJSON Schema
NameRequiredDescriptionDefault
planNothe plan id to move to (e.g. pro) — omit to list the available plans first
periodNobilling cadence — monthly (default) or yearly (2 months free)

TDQS

A4.6/5.0
Behavior5/5

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

The description adds significant behavioral context beyond the annotations: agents never spend money directly, nothing is charged until a human pays, new subscribers receive a Stripe Checkout URL, downgrades are handled by a person, and members get a specific message. These are non-obvious consequences not inferable from readOnlyHint/destructiveHint flags.

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?

The description is front-loaded with the core purpose and then walks through each usage scenario. It is longer than strictly minimal, but every sentence carries distinct information about plan listing, payment, downgrades, or member behavior. The only minor issue is the density of capitalized emphasis, which slows reading slightly.

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?

For a tool with no output schema, the description explains the return outcomes for every branch: plan list, Stripe Checkout URL, exact human-step instructions for downgrades, and an ask-admin message. Combined with full parameter schema coverageuj, an agent has everything needed to invoke it correctly across all cases.

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

Parameters3/5

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

Schema description coverage is 100%, and the schema already documents that `plan` can be omitted to list plans and that `period` defaults to monthly with yearly offering 2 months free. The description reinforces the plan-omission flow but doesn't add meaning beyond the schema for either parameter.

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 precise verb and resource: 'Change this account's SUBSCRIPTION plan (admin only).' It distinguishes itself from related siblings like billing_status and buy_credits by focusing on subscription plan changes and explicitly covering admin/member access rules.

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?

It gives explicit call patterns: call with no argument to list plans, then call again with `plan` set. It also specifies the alternative path for existing paid plans or downgrades (human action in Settings -> Billing), and tells members to ask an admin. This is thorough, actionable guidance.

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

upload_fileUpload a local file -> durable public URLAInspect

Persist an ARBITRARY user file (image, video, audio, PDF or document, up to 150MB) into Hermoso and get back a durable public URL that EVERY publish, schedule and ad-build tool accepts — post_to_meta / post_to_linkedin / post_to_linkedin_page / post_to_youtube / post_to_tiktok / post_to_pinterest / post_to_x / post_to_google_business / schedule_post / upload_meta_asset / upload_google_ads_asset / create_meta_ad / create_linkedin_ads_creative / set_youtube_thumbnail / save_to_drive / save_to_onedrive. THIS IS THE BRING-YOUR-OWN-CREATIVE PATH: it is for files that have NOTHING to do with a Hermoso render (media on the user's desktop, an agency's finished ad, a photo they shot), and it means you can publish, schedule and run ads through Hermoso without generating anything here. Provide exactly ONE source — passing two is an error, never a silent preference: url (ANY public http(s) link — Hermoso fetches it server-side, so nothing crosses this connection and there is no practical size limit; THIS IS THE ONE THAT ALWAYS WORKS, including on the hosted connector), path (a local file, ONLY when Hermoso runs on the user's own machine over stdio/CLI; the hosted connector cannot see their disk), or dataUri (a base64 data: URI — keep it under ~15MB, since the bytes travel over this connection). If the file is already at a public https URL, the Meta, Reddit and ChatGPT-Ads tools take it directly and re-host it safely — but LinkedIn (posts and ad creatives), Pinterest, the YouTube thumbnail and upload_google_ads_asset upload the BYTES themselves and therefore refuse an external host, so run it through here first and pass the URL this returns. When in doubt, use this: its URL works everywhere. (Calling the underlying HTTP route directly? POST /api/upload takes the file's RAW BYTES as the request body with its own content-type — NOT multipart/form-data — or ?url= with no body.) Returns {url, kind, bytes}.

ParametersJSON Schema
NameRequiredDescriptionDefault
urlNoa PUBLIC http(s) URL Hermoso fetches server-side (private/internal addresses are refused, and every redirect hop is re-checked). Works on every surface including the hosted connector, and the bytes never cross this connection — prefer this whenever the file is reachable on the web.
nameNooriginal file name — helps pick the right extension
pathNolocal filesystem path (stdio/CLI only — refused on the hosted connector)
dataUriNobase64 data: URI of the file bytes (data:<mime>;base64,<…>) — bytes travel over this connection, so keep it small
getUploadUrlNoASK FOR A ONE-TIME UPLOAD URL instead of uploading now — use this whenever the file is on the user’s machine and you can run a shell or an HTTP request. Returns a uploadUrl you PUT the raw bytes to (any HTTP client), which answers with the durable Hermoso url. It beats `dataUri` for anything but a small image: a data: URI spends the whole file as tokens in this conversation. One file per url, and it expires.

TDQS

A4.9/5.0
Behavior5/5

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

Annotations only signal write, non-idempotent, non-destructive. The description adds far more: the 150MB limit, the fact that passing two sources is an explicit error, the server-side fetch vs local disk vs connection-traveling bytes distinctions, the 'always works' reliability cue for url, and the return shape {url, kind, bytes}. It also documents the direct HTTP route behavior. No contradiction with annotations.

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

Conciseness5/5

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

The description is long but every sentence carries distinct operational information. It is front-loaded with the core purpose, then systematically addresses source selection, per-channel requirements, and finally the HTTP route and return format. There is no redundant filler; the length is justified by the tool's complexity.

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 5 parameters, no output schema, and a potentially confusing role among many sibling tools, the description covers all bases: when to use, which parameter to pick, size constraints, connector-specific behavior, and return format. An agent has everything needed to call this tool correctly without additional lookups.

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 descriptions already cover all 5 parameters (100% coverage), so the baseline is 3. The description adds extra semantics beyond the schema: it clarifies that 'two is an error', names url as the always-works option, explains the stdio/CLI restriction for path, and positions getUploadUrl as the preferred path for local files over dataUri. This pushes it above baseline.

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 clearly states a specific verb ('Persist... get back a durable public URL') and resource ('ARBITRARY user file') while distinguishing itself from render-related workflows: 'THIS IS THE BRING-YOUR-OWN-CREATIVE PATH'. It explicitly lists the sibling tools that accept its output, making its role unmistakable.

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?

Provides explicit when-to-use and when-not-to-use guidance: it's for files unrelated to Hermoso renders, and it names the alternative paths (post_to_meta etc. take the URL directly, while LinkedIn, Pinterest, YouTube thumbnail, and google ads asset require uploading through here first). It even says 'When in doubt, use this', leaving nothing to inference.

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

upscale_videoUpscale videoAInspect

Upscale a video to higher resolution (2x) for final delivery. Paid render; returns the served URL. A 480p Seedance 2.5 draft: finish_draft (same take, native 1080p). Two engines: the default ('standard') is the safe precision upscaler; engine:'flux' is the FLUX 3 video upscaler (1080p/2K/4K) with an optional mode:'creative' detail-enhancement pass — pick it when the user asks for the FLUX upscaler or wants added detail rather than a faithful enlargement.

ParametersJSON Schema
NameRequiredDescriptionDefault
modeNoFLUX only — 'creative' turns on its detail-enhancement pass; default precise
videoYesthe source video URL
engineNodefault 'standard', the precision upscaler. 'flux' = the FLUX 3 video upscaler

TDQS

A4.5/5.0
Behavior4/5

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

Annotations already declare this is not read-only, is open-world, not idempotent, and not destructive. The description adds that it is a paid render, returns the served URL, and explains the two engine behaviors, though it does not cover auth or rate-limit details.

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?

The purpose is front-loaded and most sentences earn their place. The fragment about the 480p Seedance draft is slightly awkward, but the overall description remains appropriately sized for a tool with multiple engines and modes.

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 paid, mutating video-render tool with no output schema, the description covers purpose, payment, return URL, alternatives, and engine/mode behavior. It is largely complete, though async behavior and result expiration are not addressed.

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 schema already documents all three parameters. The description adds meaningful guidance beyond the schema by clarifying that 'standard' is the safe precision upscaler, 'flux' is the FLUX 3 upscaler, and mode:'creative' enables a detail-enhancement pass.

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 and resource: upscaling a video to 2x resolution for final delivery. It also distinguishes the default engine from the FLUX engine and points to finish_draft as the alternative for 480p Seedance drafts.

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?

It gives explicit usage conditions: use for final delivery, use finish_draft for a 480p Seedance draft, and choose engine:'flux' when the user asks for the FLUX upscaler or wants added detail rather than a faithful enlargement.

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

use_brandSwitch profileA
Idempotent
Inspect

Pin which profile this connection acts on. Pass the profile id or exact name from list_brands. Works for your own profiles AND one another account SHARED with you (pass its name or the profile id list_brands prints); access is verified against your real membership before it is pinned. Persists for this API key until changed, on every surface (hosted connector included — no environment variables, no restart).

ParametersJSON Schema
NameRequiredDescriptionDefault
brandYesprofile id (e.g. default / p_xxx), its exact name from list_brands, or the profile id of one shared with you

TDQS

A4.6/5.0
Behavior5/5

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

Annotations only say it is a non-destructive, idempotent mutation. The description adds real behavioral context: membership is verified before pinning, the pin persists for this API key until changed, it applies on every surface including the hosted connector, and no env vars or restart are needed. This is exactly the extra disclosure annotations can't carry.

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 action and the argument format. Mild redundancy: the 'name or profile id from list_brands' instruction is repeated in the second clause, and the parenthetical asides add length, though each conveys distinct behavior.

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?

For a single-parameter, no-output-schema tool, the description covers everything needed: what is mutated (the pinned profile), accepted argument forms, the source for valid values, permission verification, and persistence scope. Nothing an agent needs to call it correctly is missing.

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 already 100%, and the description goes further by enumerating accepted forms (profile id like default/p_xxx, exact name, or the id of a profile shared with you) and pointing to list_brands as the source of valid values, which the schema alone does not.

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 precise verb+resource: 'Pin which profile this connection acts on.' An agent can immediately tell this is a context-switch operation, distinct from list_brands / create_brand / update_brand / delete_brand / get_brand siblings.

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?

Names the source for the argument ('the profile id or exact name from list_brands') and explicitly extends use to profiles shared with the account, so the agent knows both valid scenarios. It stops short of stating when *not* to call this (e.g. relative to get_brand or per-call brand args).

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

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

  1. 8 tool updatesv0.1.398
    • Changedgenerate_image1 field changed
      • addedInput schema / properties / logoPlacement
        Added value: +{
        +  "description": "overlay: real logo file laid flat. in_scene: on something in the scene, from the file, checked. auto (default): from the prompt",
        +  "enum": [
        +    "auto",
        +    "overlay",
        +    "in_scene",
        +    "none"
        +  ],
        +  "type": "string"
        +}
    • Changedgenerate_video4 fields changed
      • changedInput schema / properties / prompt / description
        Previous value: -"the video prompt / shot description (for a refVideo edit, this is the transformation instruction)"New value: +"the video prompt / shot description (for a refVideo edit, this is the transformation instruction); optional with `shots`"
      • changedInput schema / properties / raw / description
        Previous value: -"RAW MODEL ACCESS: dispatch this prompt to the model BYTE-IDENTICAL — no appended packaging/label guidance, no negative prompt, no reference-binding lines, no hex-to-colour-name rewrite. Use it when you want the model itself rather than Hermoso's render craft. Two vendor-required fixes still apply: extra @ImageN tokens are dropped and an over-long prompt is trimmed at a sentence. Billing, durable delivery and per-model validation are unchanged."New value: +"RAW MODEL ACCESS: dispatch this prompt to the model BYTE-IDENTICAL — no appended packaging/label guidance, no negative prompt, no reference-binding lines, no hex-to-colour-name rewrite. Use it when you want the model itself rather than Hermoso's render craft. Two vendor-required fixes still apply: extra @ImageN tokens are dropped and an over-long prompt is trimmed at a sentence (on Veo it is refused with the length and the cap instead). Billing, durable delivery and per-model validation are unchanged."
      • changedInput schema / properties / shots / description
        Previous value: -"MULTI-SHOT: one clip cut into these shots, in order. The seconds must add up to a length the model renders; replaces `prompt` (send either). Only models with multiShot true in hermoso_capabilities."New value: +"MULTI-SHOT: one clip cut into these shots, in order. The seconds must add up to a length the model renders; replaces `prompt` (send either). Only models with multiShot true in hermoso_capabilities. Each shot prompt is capped per model (shotPromptMaxChars there; Kling 3.0: 512 characters); a longer one is refused free, never cut."
      • removedInput schema / required
        Removed value: -[
        -  "prompt"
        -]
    • Changedmake_thumbnail1 field changed
      • addedInput schema / properties / logoPlacement
        Added value: +{
        +  "description": "overlay (exact, flat) | in_scene (from the file, checked); auto: in_scene with logo3d",
        +  "enum": [
        +    "auto",
        +    "overlay",
        +    "in_scene"
        +  ],
        +  "type": "string"
        +}
    • Changedrecast_motion3 fields changed
      • changedInput schema / properties / engine / enum
        Previous value: -[
        -  "auto",
        -  "seedance",
        -  "wan",
        -  "kling"
        -]New value: +[
        +  "auto",
        +  "h3",
        +  "seedance",
        +  "wan",
        +  "kling"
        +]
      • changedInput schema / properties / image / description
        Previous value: -"who performs it: the actor/character image URL (@Image1). Required on kling. Using a real person’s face, likeness or voice confirms you are them or have their consent, take full, unlimited responsibility for its use and will cover any claim against Hermoso (hermoso.ai/terms). Paid plan only; recorded."New value: +"who performs it: the actor/character image URL (@Image1). Required on kling. No one yet: generate_image a portrait of an AI person from words (no reference photo) and pass its url, or a saved / preset creator’s portrait (list_creators). Using a real person’s face, likeness or voice confirms you are them or have their consent, take full, unlimited responsibility for its use and will cover any claim against Hermoso (hermoso.ai/terms). Paid plan only; recorded."
      • changedInput schema / properties / resolution / description
        Previous value: -"seedance and wan; default 1080p"New value: +"seedance and wan default 1080p; h3 takes 720p or 1080p, default the clip’s own"
    • Changedrun_post_refill7 fields changed
      • addedInput schema / properties / approve
        Added value: +{
        +  "description": "REVIEW: draft ids to schedule, or [\"all\"]. A draft whose time has passed moves to the next free posting slot.",
        +  "items": {
        +    "type": "string"
        +  },
        +  "type": "array"
        +}
      • addedInput schema / properties / discard
        Added value: +{
        +  "description": "REVIEW: draft ids to delete, or [\"all\"]. Nothing is posted for them.",
        +  "items": {
        +    "type": "string"
        +  },
        +  "type": "array"
        +}
      • changedInput schema / properties / dryRun / description
        Previous value: -"default TRUE (preview only). false actually queues the posts."New value: +"default TRUE (a preview: nothing rendered, nothing queued; the copy it writes bills a few credits). false renders fresh creative and schedules or drafts it per the mode."
      • addedInput schema / properties / edit
        Added value: +{
        +  "description": "REVIEW: change drafts before approving them. Edited copy is screened by the voice rules.",
        +  "items": {
        +    "properties": {
        +      "at": {
        +        "description": "ISO time, at least 5 minutes ahead",
        +        "type": "string"
        +      },
        +      "captions": {
        +        "additionalProperties": {
        +          "type": "string"
        +        },
        +        "propertyNames": {
        +          "type": "string"
        +        },
        +        "type": "object"
        +      },
        +      "channels": {
        +        "description": "narrow to some of the draft’s own channels",
        +        "items": {
        +          "type": "string"
        +        },
        +        "type": "array"
        +      },
        +      "id": {
        +        "type": "string"
        +      },
        +      "message": {
        +        "type": "string"
        +      },
        +      "title": {
        +        "type": "string"
        +      }
        +    },
        +    "required": [
        +      "id"
        +    ],
        +    "type": "object"
        +  },
        +  "type": "array"
        +}
      • changedInput schema / properties / force / description
        Previous value: -"plan even while the refill is switched off — useful for showing someone what it would do before they turn it on. Combined with dryRun:false it still respects a stored dryRun."New value: +"plan a preview even while autoposting is switched off — useful for showing someone what it would do before they turn it on."
      • addedInput schema / properties / learn
        Added value: +{
        +  "description": "notes for future batches, e.g. \"too salesy\". Free.",
        +  "items": {
        +    "type": "string"
        +  },
        +  "type": "array"
        +}
      • addedInput schema / properties / redo
        Added value: +{
        +  "properties": {
        +    "change": {
        +      "description": "caption keeps the picture",
        +      "enum": [
        +        "both",
        +        "caption",
        +        "picture"
        +      ],
        +      "type": "string"
        +    },
        +    "confirm": {
        +      "description": "omit for the price only",
        +      "type": "boolean"
        +    },
        +    "feedback": {
        +      "type": "string"
        +    },
        +    "id": {
        +      "type": "string"
        +    },
        +    "remember": {
        +      "description": "save the note too (default true)",
        +      "type": "boolean"
        +    }
        +  },
        +  "required": [
        +    "id"
        +  ],
        +  "type": "object"
        +}
    • Changedset_post_refill18 fields changed
      • removedInput schema / properties / assetCooldownDays
        Removed value: -{
        -  "description": "how long before a Library render may be posted again (default 30). It never repeats one inside this window — it queues fewer posts and says so.",
        -  "type": "number"
        -}
      • addedInput schema / properties / avoid
        Added value: +{
        +  "description": "THE BRIEF: never mention or show these — topics, claims, competitors, faces.",
        +  "items": {
        +    "type": "string"
        +  },
        +  "type": "array"
        +}
      • addedInput schema / properties / channelPostsPerDay
        Added value: +{
        +  "additionalProperties": {
        +    "type": "number"
        +  },
        +  "description": "per channel, how many posts a day (the first N of the posting times), e.g. {\"x\":1,\"instagram\":3}. 0 turns a channel off; null resets it to every posting time. Merged into what is saved.",
        +  "propertyNames": {
        +    "type": "string"
        +  },
        +  "type": "object"
        +}
      • removedInput schema / properties / dryRun
        Removed value: -{
        -  "description": "true (the default) = plan and preview only, queue nothing. Set false ONLY after a human has read a preview.",
        -  "type": "boolean"
        -}
      • changedInput schema / properties / enabled / description
        Previous value: -"on/off. false PAUSES it: the recurring job is deleted and nothing new is queued. Already-queued posts are untouched."New value: +"on/off. true needs a connected channel and a goal (refused otherwise). false PAUSES it: the recurring job is deleted and nothing new is made. Already-queued posts are untouched."
      • addedInput schema / properties / forgetCaptionEdits
        Added value: +{
        +  "type": "boolean"
        +}
      • addedInput schema / properties / formats
        Added value: +{
        +  "description": "THE BRIEF: what to make — mix (images with a short video now and then, the default), video (short videos only), carousel (3 new images per post), image.",
        +  "enum": [
        +    "mix",
        +    "video",
        +    "carousel",
        +    "image"
        +  ],
        +  "type": "string"
        +}
      • addedInput schema / properties / goal
        Added value: +{
        +  "description": "THE BRIEF: what the posts are for — grow followers, drive sales, promote a launch, or other (say what in goalNote). Required to switch it on.",
        +  "enum": [
        +    "followers",
        +    "sales",
        +    "launch",
        +    "other"
        +  ],
        +  "type": "string"
        +}
      • addedInput schema / properties / goalNote
        Added value: +{
        +  "description": "THE BRIEF: a line about the goal (e.g. the launch date and what is launching).",
        +  "type": "string"
        +}
      • addedInput schema / properties / learnings
        Added value: +{
        +  "description": "notes learned from reviews; REPLACES the list",
        +  "items": {
        +    "type": "string"
        +  },
        +  "type": "array"
        +}
      • removedInput schema / properties / maxCreditsPerDay
        Removed value: -{
        -  "description": "a hard credit ceiling per day, checked BEFORE any render starts. It binds independently of the counts above.",
        -  "type": "number"
        -}
      • removedInput schema / properties / maxImagesPerDay
        Removed value: -{
        -  "description": "how many NEW images a day it may render when the Library runs dry. 0 (default) = none, spend nothing.",
        -  "type": "number"
        -}
      • removedInput schema / properties / maxVideosPerDay
        Removed value: -{
        -  "description": "how many NEW videos a day it may render. 0 (default) = none. Video is the expensive one — hundreds of credits each.",
        -  "type": "number"
        -}
      • addedInput schema / properties / mode
        Added value: +{
        +  "description": "\"review\" (default): each batch of fresh posts waits as drafts for approval. \"auto\": fresh posts are scheduled to publish automatically.",
        +  "enum": [
        +    "review",
        +    "auto"
        +  ],
        +  "type": "string"
        +}
      • addedInput schema / properties / pillars
        Added value: +{
        +  "description": "THE BRIEF: content pillars / topics, up to 8 short lines. Every post belongs to one. get_post_refill suggests some from the brand profile.",
        +  "items": {
        +    "type": "string"
        +  },
        +  "type": "array"
        +}
      • addedInput schema / properties / postingTimes
        Added value: +{
        +  "description": "the brand's daily posting times, 24-hour \"HH:MM\" (e.g. [\"09:00\",\"13:00\",\"18:00\"]); REPLACES the list. Autopilot makes one post per posting time, and scheduled posts use the same times. get_post_refill shows the current ones.",
        +  "items": {
        +    "type": "string"
        +  },
        +  "type": "array"
        +}
      • changedInput schema / properties / postsPerDay / description
        Previous value: -"cap the posts per day BELOW the number of posting times. 0 (default) = use every posting time, which is where \"3 a day\" comes from. To post MORE per day, add posting times instead."New value: +"posts a day, up to the number of posting times (\"2 times a day\" = 2). 0 (default) = one per posting time. To post MORE per day than there are posting times, also pass postingTimes."
      • addedInput schema / properties / timezone
        Added value: +{
        +  "description": "IANA timezone the posting times are in, e.g. \"America/New_York\" (default: the brand's, else UTC)",
        +  "type": "string"
        +}
    • Changedupdate_brand3 fields changed
      • addedInput schema / properties / logo
        Added value: +{
        +  "type": "string"
        +}
      • addedInput schema / properties / logoDark
        Added value: +{
        +  "description": "for light pictures",
        +  "type": "string"
        +}
      • addedInput schema / properties / logoLight
        Added value: +{
        +  "description": "for dark pictures; \"none\" removes",
        +  "type": "string"
        +}
    • Changedupdate_doc5 fields changed
      • changedInput schema / properties / dropdowns / description
        Previous value: -"dropdown chips to set"New value: +"dropdown chips to set, or dropdowns to change"
      • addedInput schema / properties / dropdowns / items / properties / newTitle
        Added value: +{
        +  "description": "rename the dropdown",
        +  "type": "string"
        +}
      • addedInput schema / properties / dropdowns / items / properties / options
        Added value: +{
        +  "description": "change the dropdown itself: the COMPLETE new option list (2–50) by display text; existing options keep their colour; shared by every chip built from it",
        +  "items": {
        +    "type": "string"
        +  },
        +  "type": "array"
        +}
      • addedInput schema / properties / dropdowns / items / properties / replace
        Added value: +{
        +  "additionalProperties": {
        +    "type": "string"
        +  },
        +  "description": "for an option you remove that a chip shows: {\"Old\": \"New\"}",
        +  "propertyNames": {
        +    "type": "string"
        +  },
        +  "type": "object"
        +}
      • removedInput schema / properties / dropdowns / items / required
        Removed value: -[
        -  "value"
        -]
  2. 53 tool updatesv0.1.374
    • Changedbackfill_posts1 field changed
      • changedInput schema / properties / accountRef / description
        Previous value: -"which Page / account, when the brand has more than one"New value: +"which Page / account, when the profile has more than one"
    • Changedcancel_scheduled1 field changed
      • changedInput schema / properties / brand / description
        Previous value: -"WHICH BRAND this post is in — id or exact name from list_brands; needed when it is not the brand this connection is pinned to. A name that matches no brand, or two, is REFUSED."New value: +"WHICH PROFILE this post is in — id or exact name from list_brands; needed when it is not the profile this connection is pinned to. A name that matches no profile, or two, is REFUSED."
    • Changedclone_static1 field changed
      • changedInput schema / properties / brandId / description
        Previous value: -"a brand id/name from list_brands to clone for; omit to use the active brand"New value: +"a profile id/name from list_brands to clone for; omit to use the active profile"
    • Changedcollect_post_metrics1 field changed
      • changedInput schema / properties / brand / description
        Previous value: -"WHICH BRAND the post lives in — id or exact name from list_brands (a shared workspace: its profile id). Needed when it was published in a brand this connection is not pinned to; applies to THIS CALL ONLY. A name that matches no brand, or two, is REFUSED and nothing is done."New value: +"WHICH PROFILE the post lives in — id or exact name from list_brands (a shared workspace: its profile id). Needed when it was published in a profile this connection is not pinned to; applies to THIS CALL ONLY. A name that matches no profile, or two, is REFUSED and nothing is done."
    • Changedconnect_connector1 field changed
      • changedInput schema / properties / provider / description
        Previous value: -"the connector id: stripe, openai_ads, apple_ads, bluesky, telegram, bing_webmaster, posthog, mixpanel, amplitude, slack, discord, webhook"New value: +"the connector id: applovin_ads, stripe, openai_ads, apple_ads, bluesky, telegram, bing_webmaster, posthog, mixpanel, amplitude, slack, discord, webhook"
    • Changedcreate_brand2 fields changed
      • changedInput schema / properties / activate / description
        Previous value: -"switch this connection to the new brand (default true) — everything you do next scopes to it"New value: +"switch this connection to the new profile (default true); everything you do next scopes to it"
      • changedInput schema / properties / name / description
        Previous value: -"the brand / client name for the new workspace"New value: +"the name for the new profile (a brand, client or creator name)"
    • Changeddelete_brand2 fields changed
      • changedInput schema / properties / brand / description
        Previous value: -"brand id or exact name from list_brands"New value: +"profile id or exact name from list_brands"
      • changedInput schema / properties / confirm / description
        Previous value: -"REQUIRED true — this destroys the whole workspace and cannot be undone"New value: +"REQUIRED true — this destroys the whole profile and cannot be undone"
    • Changeddelete_creator1 field changed
      • changedInput schema / properties / brand / description
        Previous value: -"brand id/name from list_brands, this call only"New value: +"profile id/name from list_brands, this call only"
    • Changeddelete_playbook1 field changed
      • changedInput schema / properties / brand / description
        Previous value: -"brand id/name from list_brands, this call only"New value: +"profile id/name from list_brands, this call only"
    • Changeddelete_skill1 field changed
      • changedInput schema / properties / brand / description
        Previous value: -"brand id/name from list_brands, this call only"New value: +"profile id/name from list_brands, this call only"
    • Changeddraft_brand1 field changed
      • changedInput schema / properties / save / description
        Previous value: -"save as the workspace’s brand (like Studio onboarding) so plan_ad/create use it automatically. Default: saves only when NO brand is saved yet; pass true to REPLACE the saved brand profile (the drafted fields overwrite the saved ones), false to never save"New value: +"save onto the active profile (like Studio onboarding) so plan_ad/create use it automatically. Default: saves only when NO brand is saved yet; pass true to REPLACE the saved profile (the drafted fields overwrite the saved ones), false to never save"
    • Changedduplicate_scheduled1 field changed
      • changedInput schema / properties / brand / description
        Previous value: -"WHICH BRAND this post is in — id or exact name from list_brands; needed when it is not the brand this connection is pinned to. A name that matches no brand, or two, is REFUSED."New value: +"WHICH PROFILE this post is in — id or exact name from list_brands; needed when it is not the profile this connection is pinned to. A name that matches no profile, or two, is REFUSED."
    • Changededit_video2 fields changed
      • addedInput schema / properties / engine
        Added value: +{
        +  "description": "default auto: Seedance 2.5 Edit without a real-looking person, else Kling O3 Edit",
        +  "enum": [
        +    "auto",
        +    "seedance",
        +    "kling"
        +  ],
        +  "type": "string"
        +}
      • addedInput schema / properties / faceRoute
        Added value: +{
        +  "description": "'face_lane' = the user's \"I own the rights to this face\" for a real person in the source clip (paid plans). Only on the user's say-so, never on your own.",
        +  "enum": [
        +    "face_lane"
        +  ],
        +  "type": "string"
        +}
    • Changedfetch_app_screens1 field changed
      • changedInput schema / properties / brandId / description
        Previous value: -"a brand id/name from list_brands to save the screens onto; omit to use the active brand"New value: +"a profile id/name from list_brands to save the screens onto; omit to use the active profile"
    • Changedforget1 field changed
      • changedInput schema / properties / brand / description
        Previous value: -"brand id/name from list_brands, this call only"New value: +"profile id/name from list_brands, this call only"
    • Changedget_brand1 field changed
      • changedInput schema / properties / brand / description
        Previous value: -"brand id/name from list_brands, this call only"New value: +"profile id/name from list_brands, this call only"
    • Changedlist_meta_posts2 fields changed
      • changedInput schema / properties / brand / description
        Previous value: -"WHICH BRAND the post lives in — id or exact name from list_brands (a shared workspace: its profile id). Needed when it was published in a brand this connection is not pinned to; applies to THIS CALL ONLY. A name that matches no brand, or two, is REFUSED and nothing is done."New value: +"WHICH PROFILE the post lives in — id or exact name from list_brands (a shared workspace: its profile id). Needed when it was published in a profile this connection is not pinned to; applies to THIS CALL ONLY. A name that matches no profile, or two, is REFUSED and nothing is done."
      • changedInput schema / properties / pageId / description
        Previous value: -"which connected Page — omit when the brand has only one"New value: +"which connected Page — omit when the profile has only one"
    • Changedlist_product_photos1 field changed
      • changedInput schema / properties / brandId / description
        Previous value: -"a brand id/name from list_brands whose product library to list; omit to use the active brand"New value: +"a profile id/name from list_brands whose product library to list; omit to use the active profile"
    • Changedlist_published_posts1 field changed
      • changedInput schema / properties / brand / description
        Previous value: -"WHICH BRAND the post lives in — id or exact name from list_brands (a shared workspace: its profile id). Needed when it was published in a brand this connection is not pinned to; applies to THIS CALL ONLY. A name that matches no brand, or two, is REFUSED and nothing is done."New value: +"WHICH PROFILE the post lives in — id or exact name from list_brands (a shared workspace: its profile id). Needed when it was published in a profile this connection is not pinned to; applies to THIS CALL ONLY. A name that matches no profile, or two, is REFUSED and nothing is done."
    • Changedlist_scheduled1 field changed
      • changedInput schema / properties / brand / description
        Previous value: -"WHICH BRAND to list — the id or exact name from list_brands. Needed when the post lives in a brand this connection is not pinned to: a post you can CREATE in a brand must be manageable there too, without switching the whole connection. A name that matches no brand, or two, is REFUSED."New value: +"WHICH PROFILE to list — the id or exact name from list_brands. Needed when the post lives in a profile this connection is not pinned to: a post you can CREATE in a profile must be manageable there too, without switching the whole connection. A name that matches no profile, or two, is REFUSED."
    • Changedmine_angles1 field changed
      • changedInput schema / properties / brandId / description
        Previous value: -"a brand id/name from list_brands to mine for; omit to use the active brand"New value: +"a profile id/name from list_brands to mine for; omit to use the active profile"
    • Changedpost_edit4 fields changed
      • addedInput schema / properties / ops / items / properties / keepSound
        Added value: +{
        +  "type": "boolean"
        +}
      • changedInput schema / properties / ops / items / properties / op / enum
        Previous value: -[
        -  "trim",
        -  "speed",
        -  "mute",
        -  "audio_gain",
        -  "fade_out",
        -  "music",
        -  "append_card",
        -  "watermark",
        -  "grain",
        -  "text",
        -  "join"
        -]New value: +[
        +  "trim",
        +  "speed",
        +  "mute",
        +  "audio_gain",
        +  "fade_out",
        +  "music",
        +  "voiceover",
        +  "append_card",
        +  "watermark",
        +  "grain",
        +  "text",
        +  "join"
        +]
      • changedInput schema / properties / ops / items / properties / text / description
        Previous value: -"text: the words, verbatim"New value: +"text/voiceover: the words, verbatim"
      • addedInput schema / properties / ops / items / properties / voice
        Added value: +{
        +  "type": "string"
        +}
    • Changedpost_performance1 field changed
      • changedInput schema / properties / brand / description
        Previous value: -"WHICH BRAND the post lives in — id or exact name from list_brands (a shared workspace: its profile id). Needed when it was published in a brand this connection is not pinned to; applies to THIS CALL ONLY. A name that matches no brand, or two, is REFUSED and nothing is done."New value: +"WHICH PROFILE the post lives in — id or exact name from list_brands (a shared workspace: its profile id). Needed when it was published in a profile this connection is not pinned to; applies to THIS CALL ONLY. A name that matches no profile, or two, is REFUSED and nothing is done."
    • Changedpost_to_bluesky2 fields changed
      • 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."
    • Changedpost_to_google_business1 field changed
      • 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."
    • Changedpost_to_linkedin1 field changed
      • 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."
    • Changedpost_to_linkedin_page1 field changed
      • 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."
    • Changedpost_to_meta1 field changed
      • 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."
    • Changedpost_to_pinterest2 fields changed
      • 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 pinterest 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 pinterest 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."
    • Changedpost_to_telegram2 fields changed
      • 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 telegram 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 telegram 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."
    • Changedpost_to_tiktok2 fields changed
      • 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 tiktok 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 tiktok 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."
    • Changedpost_to_x2 fields changed
      • 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 x 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 x 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."
    • Changedpost_to_youtube2 fields changed
      • 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 youtube 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 youtube 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."
    • Changedpost_x_article1 field changed
      • 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."
    • Changedrecast_motion9 fields changed
      • addedInput schema / properties / engine
        Added value: +{
        +  "description": "default auto",
        +  "enum": [
        +    "auto",
        +    "seedance",
        +    "wan",
        +    "kling"
        +  ],
        +  "type": "string"
        +}
      • addedInput schema / properties / faceRoute
        Added value: +{
        +  "description": "'face_lane' = the user's \"I own the rights to this face\" for a real person in the source clip (paid plans). Only on the user's say-so, never on your own.",
        +  "enum": [
        +    "face_lane"
        +  ],
        +  "type": "string"
        +}
      • changedInput schema / properties / image / description
        Previous value: -"the actor/character image URL (who should appear). Using a real person’s face, likeness or voice confirms you are them or have their consent, take full, unlimited responsibility for its use and will cover any claim against Hermoso (hermoso.ai/terms). Paid plan only; recorded."New value: +"who performs it: the actor/character image URL (@Image1). Required on kling. Using a real person’s face, likeness or voice confirms you are them or have their consent, take full, unlimited responsibility for its use and will cover any claim against Hermoso (hermoso.ai/terms). Paid plan only; recorded."
      • addedInput schema / properties / images
        Added value: +{
        +  "description": "seedance/wan: more pictures, @Image2…: a character, product, outfit or place",
        +  "items": {
        +    "type": "string"
        +  },
        +  "maxItems": 8,
        +  "type": "array"
        +}
      • changedInput schema / properties / orientation / description
        Previous value: -"which aspect to keep: the video's (default) or the image's"New value: +"kling only: which aspect to keep: the video's (default) or the image's"
      • changedInput schema / properties / prompt / description
        Previous value: -"optional scene/style guidance"New value: +"optional, e.g. 'same moves, new location: Tokyo at night'"
      • addedInput schema / properties / resolution
        Added value: +{
        +  "description": "seedance and wan; default 1080p",
        +  "enum": [
        +    "480p",
        +    "720p",
        +    "1080p"
        +  ],
        +  "type": "string"
        +}
      • changedInput schema / properties / tier / description
        Previous value: -"'pro' (default): 1080p and real hand-object interaction. 'standard': about 25% fewer credits and faster, but 720p, and it tends to mime a held object with empty hands. hermoso_capabilities lists the exact credits for both"New value: +"kling only: 'pro' (default): 1080p and real hand-object interaction. 'standard': about 25% fewer credits and faster, but 720p, and it tends to mime a held object with empty hands. hermoso_capabilities lists the exact credits for both"
      • changedInput schema / required
        Previous value: -[
        -  "image",
        -  "video"
        -]New value: +[
        +  "video"
        +]
    • Changedremember1 field changed
      • changedInput schema / properties / brand / description
        Previous value: -"brand id/name from list_brands, this call only"New value: +"profile id/name from list_brands, this call only"
    • Changedremix_static1 field changed
      • changedInput schema / properties / brandId / description
        Previous value: -"a brand id/name from list_brands to clone for; omit to use the active brand"New value: +"a profile id/name from list_brands to clone for; omit to use the active profile"
    • Changedreschedule_post1 field changed
      • changedInput schema / properties / brand / description
        Previous value: -"WHICH BRAND this post is in — id or exact name from list_brands; needed when it is not the brand this connection is pinned to. A name that matches no brand, or two, is REFUSED."New value: +"WHICH PROFILE this post is in — id or exact name from list_brands; needed when it is not the profile this connection is pinned to. A name that matches no profile, or two, is REFUSED."
    • Changedrestyle_video6 fields changed
      • addedInput schema / properties / characters
        Added value: +{
        +  "description": "characters to draw the people as. Using a real person’s face, likeness or voice confirms you are them or have their consent, take full, unlimited responsibility for its use and will cover any claim against Hermoso (hermoso.ai/terms). Paid plan only; recorded.",
        +  "items": {
        +    "type": "string"
        +  },
        +  "maxItems": 4,
        +  "type": "array"
        +}
      • addedInput schema / properties / engine
        Added value: +{
        +  "description": "default auto",
        +  "enum": [
        +    "auto",
        +    "seedance",
        +    "wan",
        +    "kling"
        +  ],
        +  "type": "string"
        +}
      • addedInput schema / properties / faceRoute
        Added value: +{
        +  "description": "'face_lane' = the user's \"I own the rights to this face\" for a real person in the source clip (paid plans). Only on the user's say-so, never on your own.",
        +  "enum": [
        +    "face_lane"
        +  ],
        +  "type": "string"
        +}
      • addedInput schema / properties / resolution
        Added value: +{
        +  "description": "Seedance and Wan; default 1080p",
        +  "enum": [
        +    "480p",
        +    "720p",
        +    "1080p"
        +  ],
        +  "type": "string"
        +}
      • changedInput schema / properties / style / description
        Previous value: -"a look id from hermoso_capabilities (e.g. claymation, vhs-80s, film-noir)"New value: +"a look id from hermoso_capabilities (e.g. claymation, knitted-yarn, cel-shaded-anime-cg)"
      • addedInput schema / properties / styleImages
        Added value: +{
        +  "description": "pictures that ARE the look (their style, never their subject)",
        +  "items": {
        +    "type": "string"
        +  },
        +  "maxItems": 9,
        +  "type": "array"
        +}
    • Changedretry_scheduled1 field changed
      • changedInput schema / properties / brand / description
        Previous value: -"WHICH BRAND this post is in — id or exact name from list_brands; needed when it is not the brand this connection is pinned to. A name that matches no brand, or two, is REFUSED."New value: +"WHICH PROFILE this post is in — id or exact name from list_brands; needed when it is not the profile this connection is pinned to. A name that matches no profile, or two, is REFUSED."
    • Changedsave_creator1 field changed
      • changedInput schema / properties / brand / description
        Previous value: -"brand id/name from list_brands, this call only"New value: +"profile id/name from list_brands, this call only"
    • Changedsave_playbook1 field changed
      • changedInput schema / properties / brand / description
        Previous value: -"brand id/name from list_brands, this call only"New value: +"profile id/name from list_brands, this call only"
    • Changedsave_skill1 field changed
      • changedInput schema / properties / brand / description
        Previous value: -"brand id/name from list_brands, this call only"New value: +"profile id/name from list_brands, this call only"
    • Changedsave_to_swipefile1 field changed
      • changedInput schema / properties / brand / description
        Previous value: -"brand id/name from list_brands, this call only"New value: +"profile id/name from list_brands, this call only"
    • Changedschedule_post3 fields changed
      • 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."
      • changedInput schema / properties / pageId / description
        Previous value: -"FACEBOOK / INSTAGRAM / THREADS — which connected Facebook Page (and its linked Instagram) publishes, from list_meta_pages. Needed when the brand has more than one Page connected; with several and no id the post is refused at fire time rather than sent from the wrong brand."New value: +"FACEBOOK / INSTAGRAM / THREADS — which connected Facebook Page (and its linked Instagram) publishes, from list_meta_pages. Needed when the profile has more than one Page connected; with several and no id the post is refused at fire time rather than sent from the wrong brand."
      • changedInput schema / properties / useQueue / description
        Previous value: -"instead of naming a minute, drop this into the brand’s POSTING QUEUE: Hermoso takes the earliest of its saved posting times that is still free (skipping any slot another queued post already holds). This is the natural answer to “just queue it” / “post it at my next opening”. Mutually exclusive with `at` — passing both is refused rather than one being silently preferred. If the brand has no posting times set, or every slot for the next 90 days is taken, it is refused by name and nothing is scheduled."New value: +"instead of naming a minute, drop this into the brand’s POSTING QUEUE: Hermoso takes the earliest of its saved posting times that is still free (skipping any slot another queued post already holds). This is the natural answer to “just queue it” / “post it at my next opening”. Mutually exclusive with `at` — passing both is refused rather than one being silently preferred. If the profile has no posting times set, or every slot for the next 90 days is taken, it is refused by name and nothing is scheduled."
    • Changedset_connector_accounts1 field changed
      • changedInput schema / properties / accountIds / description
        Previous value: -"the ids (from list_connector_accounts) this brand may use — an empty array shares nothing"New value: +"the ids (from list_connector_accounts) this profile may use — an empty array shares nothing"
    • Changedset_post_refill1 field changed
      • changedInput schema / properties / linkedinOrganizationId / description
        Previous value: -"LINKEDIN — which company Page to post as (list_linkedin_pages). A single shared Page is used automatically; a Page that is not shared with this brand is ignored rather than failing the whole post."New value: +"LINKEDIN — which company Page to post as (list_linkedin_pages). A single shared Page is used automatically; a Page that is not shared with this profile is ignored rather than failing the whole post."
    • Changedset_product_image1 field changed
      • changedInput schema / properties / brandId / description
        Previous value: -"a brand id/name from list_brands to lock the product for; omit to use the active brand"New value: +"a profile id/name from list_brands to lock the product for; omit to use the active profile"
    • Changedtidy_memory1 field changed
      • changedInput schema / properties / brand / description
        Previous value: -"brand id/name from list_brands, this call only"New value: +"profile id/name from list_brands, this call only"
    • Changedtiktok_post_status1 field changed
      • changedInput schema / properties / account / description
        Previous value: -"which connected TikTok account the post went out on (@handle or id from list_connector_accounts) when the brand has more than one"New value: +"which connected TikTok account the post went out on (@handle or id from list_connector_accounts) when the profile has more than one"
    • Changedupdate_brand1 field changed
      • changedInput schema / properties / brand / description
        Previous value: -"brand id/name from list_brands, this call only"New value: +"profile id/name from list_brands, this call only"
    • Changedupdate_saved_creator1 field changed
      • changedInput schema / properties / brand / description
        Previous value: -"brand id/name from list_brands, this call only"New value: +"profile id/name from list_brands, this call only"
    • Changeduse_brand1 field changed
      • changedInput schema / properties / brand / description
        Previous value: -"brand id (e.g. default / p_xxx), its exact name from list_brands, or the profile id of a workspace shared with you"New value: +"profile id (e.g. default / p_xxx), its exact name from list_brands, or the profile id of one shared with you"
  3. 6 tool updatesv0.1.372
    • Changedappend_to_doc3 fields changed
      • addedInput schema / properties / dropdown
        Added value: +{
        +  "description": "a dropdown chip to append after the text",
        +  "properties": {
        +    "options": {
        +      "description": "2 to 50 distinct options",
        +      "items": {
        +        "type": "string"
        +      },
        +      "type": "array"
        +    },
        +    "selected": {
        +      "description": "the option it starts on (default: the first)",
        +      "type": "string"
        +    },
        +    "title": {
        +      "description": "the dropdown title, e.g. \"Status\"",
        +      "type": "string"
        +    }
        +  },
        +  "required": [
        +    "options"
        +  ],
        +  "type": "object"
        +}
      • changedInput schema / properties / text / description
        Previous value: -"text to append at the end of the doc"New value: +"text to append at the end of the doc (optional when a dropdown is passed)"
      • changedInput schema / required
        Previous value: -[
        -  "documentId",
        -  "text"
        -]New value: +[
        +  "documentId"
        +]
    • Changedpost_to_meta3 fields changed
      • addedInput schema / properties / instagramCommentPrompt
        Added value: +{
        +  "description": "INSTAGRAM — make the caption a COMMENT PROMPT that people answer in the comments. Same limits as instagramPoll, and never together with it.",
        +  "type": "boolean"
        +}
      • addedInput schema / properties / instagramPoll
        Added value: +{
        +  "description": "INSTAGRAM — attach a POLL: 2 to 4 answers of 1–25 characters each, and the caption IS the question (so a caption is required). Feed photos, carousels and Reels only (never a story), one caption add-on per post (not with instagramCommentPrompt), and only on an account connected through Meta (a Facebook Page with a linked Instagram). WRITE-ONCE: Instagram cannot add, change or remove it after publishing, so show the user the answers first.",
        +  "items": {
        +    "type": "string"
        +  },
        +  "type": "array"
        +}
      • addedInput schema / properties / instagramPollExtended
        Added value: +{
        +  "description": "INSTAGRAM — give that poll Instagram’s extended voting duration instead of its default 3 days (Instagram’s changelog says it then stays open indefinitely). Only with instagramPoll.",
        +  "type": "boolean"
        +}
    • Changedreschedule_post3 fields changed
      • addedInput schema / properties / instagramCommentPrompt
        Added value: +{
        +  "description": "INSTAGRAM — make the caption a COMMENT PROMPT that people answer in the comments. Same limits as instagramPoll, and never together with it.",
        +  "type": "boolean"
        +}
      • addedInput schema / properties / instagramPoll
        Added value: +{
        +  "description": "INSTAGRAM — attach a POLL: 2 to 4 answers of 1–25 characters each, and the caption IS the question (so a caption is required). Feed photos, carousels and Reels only (never a story), one caption add-on per post (not with instagramCommentPrompt), and only on an account connected through Meta (a Facebook Page with a linked Instagram). WRITE-ONCE: Instagram cannot add, change or remove it after publishing, so show the user the answers first.",
        +  "items": {
        +    "type": "string"
        +  },
        +  "type": "array"
        +}
      • addedInput schema / properties / instagramPollExtended
        Added value: +{
        +  "description": "INSTAGRAM — give that poll Instagram’s extended voting duration instead of its default 3 days (Instagram’s changelog says it then stays open indefinitely). Only with instagramPoll.",
        +  "type": "boolean"
        +}
    • Addedrestyle_video
    • Changedschedule_post3 fields changed
      • addedInput schema / properties / instagramCommentPrompt
        Added value: +{
        +  "description": "INSTAGRAM — make the caption a COMMENT PROMPT that people answer in the comments. Same limits as instagramPoll, and never together with it.",
        +  "type": "boolean"
        +}
      • addedInput schema / properties / instagramPoll
        Added value: +{
        +  "description": "INSTAGRAM — attach a POLL: 2 to 4 answers of 1–25 characters each, and the caption IS the question (so a caption is required). Feed photos, carousels and Reels only (never a story), one caption add-on per post (not with instagramCommentPrompt), and only on an account connected through Meta (a Facebook Page with a linked Instagram). WRITE-ONCE: Instagram cannot add, change or remove it after publishing, so show the user the answers first.",
        +  "items": {
        +    "type": "string"
        +  },
        +  "type": "array"
        +}
      • addedInput schema / properties / instagramPollExtended
        Added value: +{
        +  "description": "INSTAGRAM — give that poll Instagram’s extended voting duration instead of its default 3 days (Instagram’s changelog says it then stays open indefinitely). Only with instagramPoll.",
        +  "type": "boolean"
        +}
    • Changedupdate_doc1 field changed
      • addedInput schema / properties / dropdowns
        Added value: +{
        +  "description": "dropdown chips to set",
        +  "items": {
        +    "properties": {
        +      "dropdownId": {
        +        "description": "when two dropdowns share a title",
        +        "type": "string"
        +      },
        +      "tabId": {
        +        "type": "string"
        +      },
        +      "title": {
        +        "description": "the dropdown title (from read_doc)",
        +        "type": "string"
        +      },
        +      "value": {
        +        "description": "the option to select, by its display text",
        +        "type": "string"
        +      }
        +    },
        +    "required": [
        +      "value"
        +    ],
        +    "type": "object"
        +  },
        +  "type": "array"
        +}
  4. 17 tool updatesv0.1.371
    • Changeddelete_creator1 field changed
      • addedInput schema / properties / brand
        Added value: +{
        +  "description": "brand id/name from list_brands, this call only",
        +  "type": "string"
        +}
    • Changeddelete_playbook1 field changed
      • addedInput schema / properties / brand
        Added value: +{
        +  "description": "brand id/name from list_brands, this call only",
        +  "type": "string"
        +}
    • Changeddelete_skill1 field changed
      • addedInput schema / properties / brand
        Added value: +{
        +  "description": "brand id/name from list_brands, this call only",
        +  "type": "string"
        +}
    • Changedforget1 field changed
      • addedInput schema / properties / brand
        Added value: +{
        +  "description": "brand id/name from list_brands, this call only",
        +  "type": "string"
        +}
    • Changedget_brand1 field changed
      • addedInput schema / properties / brand
        Added value: +{
        +  "description": "brand id/name from list_brands, this call only",
        +  "type": "string"
        +}
    • Changedpost_to_meta1 field changed
      • addedInput schema / additionalProperties
        Added value: +{}
    • Changedremember1 field changed
      • addedInput schema / properties / brand
        Added value: +{
        +  "description": "brand id/name from list_brands, this call only",
        +  "type": "string"
        +}
    • Changedreschedule_post1 field changed
      • changedInput schema / properties / aiGenerated / description
        Previous value: -"INSTAGRAM / FACEBOOK REEL — Meta’s is_ai_generated self-disclosure. OMIT IT and Hermoso decides from provenance: a Hermoso render is declared, media that came through upload_file or from an external URL (the user’s own photographs or footage) is NOT — a real photo must never carry Instagram’s “AI info” label. Pass true or false only to override: false strips the label from something Hermoso would otherwise declare, true declares a render the user uploaded themselves."New value: +"AI-CONTENT DISCLOSURE (Instagram / Facebook Reel is_ai_generated, TikTok is_aigc, YouTube containsSyntheticMedia). OMIT IT and the value already on the post stays; a post that never had one is decided from provenance at publish: a Hermoso render is declared AI-generated, media that came through upload_file or from an external URL (the user’s own photos or footage) is NOT. Pass true or false only to override."
    • Changedsave_creator1 field changed
      • addedInput schema / properties / brand
        Added value: +{
        +  "description": "brand id/name from list_brands, this call only",
        +  "type": "string"
        +}
    • Changedsave_playbook1 field changed
      • changedInput schema / properties / brand / description
        Previous value: -"which brand this is for (defaults to the workspace brand)"New value: +"brand id/name from list_brands, this call only"
    • Changedsave_skill1 field changed
      • addedInput schema / properties / brand
        Added value: +{
        +  "description": "brand id/name from list_brands, this call only",
        +  "type": "string"
        +}
    • Changedsave_to_swipefile1 field changed
      • addedInput schema / properties / brand
        Added value: +{
        +  "description": "brand id/name from list_brands, this call only",
        +  "type": "string"
        +}
    • Changedschedule_post1 field changed
      • changedInput schema / properties / aiGenerated / description
        Previous value: -"INSTAGRAM / FACEBOOK REEL — Meta’s is_ai_generated self-disclosure. OMIT IT and Hermoso decides from provenance: a Hermoso render is declared, media that came through upload_file or from an external URL (the user’s own photographs or footage) is NOT — a real photo must never carry Instagram’s “AI info” label. Pass true or false only to override: false strips the label from something Hermoso would otherwise declare, true declares a render the user uploaded themselves."New value: +"AI-CONTENT DISCLOSURE (Instagram / Facebook Reel is_ai_generated, TikTok is_aigc, YouTube containsSyntheticMedia). OMIT IT and Hermoso decides from provenance: a Hermoso render is declared AI-generated, media that came through upload_file or from an external URL (the user’s own photos or footage) is NOT. Pass true or false only to override."
    • Changedtidy_memory1 field changed
      • addedInput schema / properties / brand
        Added value: +{
        +  "description": "brand id/name from list_brands, this call only",
        +  "type": "string"
        +}
    • Addedtiktok_post_status
    • Changedupdate_brand1 field changed
      • addedInput schema / properties / brand
        Added value: +{
        +  "description": "brand id/name from list_brands, this call only",
        +  "type": "string"
        +}
    • Changedupdate_saved_creator1 field changed
      • addedInput schema / properties / brand
        Added value: +{
        +  "description": "brand id/name from list_brands, this call only",
        +  "type": "string"
        +}
  5. 18 tool updatesv0.1.366
    • Changedclone_static1 field changed
      • changedInput schema / properties / imageUrl / description
        Previous value: -"the URL of the static ad image to clone; a real person in it confirms their likeness consent"New value: +"the URL of the static ad image to clone. Using a real person’s face, likeness or voice confirms you are them or have their consent, take full, unlimited responsibility for its use and will cover any claim against Hermoso (hermoso.ai/terms). Paid plan only; recorded."
    • Changeddraft_brand1 field changed
      • changedInput schema / properties / save / description
        Previous value: -"save as the workspace’s brand (like Studio onboarding) so plan_ad/create use it automatically. Default: saves only when NO brand is saved yet; pass true to overwrite, false to never save"New value: +"save as the workspace’s brand (like Studio onboarding) so plan_ad/create use it automatically. Default: saves only when NO brand is saved yet; pass true to REPLACE the saved brand profile (the drafted fields overwrite the saved ones), false to never save"
    • Changededit_image1 field changed
      • addedInput schema / properties / fixLabel
        Added value: +{
        +  "description": "false = leave the product label exactly as rendered: no product lookup, no label check, no re-print (default on when the ad shows the brand’s saved product)",
        +  "type": "boolean"
        +}
    • Changedgenerate_avatar8 fields changed
      • addedInput schema / properties / acceptQueue
        Added value: +{
        +  "description": "only for an engine hermoso_capabilities marks oneAtATime: wait in line",
        +  "type": "boolean"
        +}
      • addedInput schema / properties / dryRun
        Added value: +{
        +  "description": "return the credits this exact job would hold, without rendering",
        +  "type": "boolean"
        +}
      • addedInput schema / properties / engine
        Added value: +{
        +  "description": "leave out for the standard engine; only an engine listed in hermoso_capabilities avatarEngines is accepted",
        +  "type": "string"
        +}
      • changedInput schema / properties / image / description
        Previous value: -"local path or URL of the presenter portrait; a real person’s photo confirms their likeness consent"New value: +"local path or URL of the presenter portrait. Using a real person’s face, likeness or voice confirms you are them or have their consent, take full, unlimited responsibility for its use and will cover any claim against Hermoso (hermoso.ai/terms). Paid plan only; recorded."
      • addedInput schema / properties / prompt
        Added value: +{
        +  "description": "motion direction, only for an engine other than standard that hermoso_capabilities lists",
        +  "type": "string"
        +}
      • changedInput schema / properties / resolution / description
        Previous value: -"'1080p' (default) or '480p'/'720p' draft"New value: +"'720p' (default) or '480p'"
      • addedInput schema / properties / seed
        Added value: +{
        +  "description": "fixed seed, only for an engine other than standard",
        +  "maximum": 9007199254740991,
        +  "minimum": -9007199254740991,
        +  "type": "integer"
        +}
      • changedInput schema / properties / voice / description
        Previous value: -"voice name (Rachel/Sarah/George/Adam)"New value: +"voice name (Aria / Sarah / George / Adam). Leave it out and the voice matches the person in `image`; if nobody can be read, the call is refused free asking for one"
    • Changedgenerate_image2 fields changed
      • addedInput schema / properties / fixLabel
        Added value: +{
        +  "description": "default true: when the saved brand's product photo rides in this render, the product's label on the finished image is READ and compared with the photo, and re-printed from the photo at close range ONLY if it came out wrong (a label that is already right costs only the check, a credit or two; a re-print adds about ten). The reply says whether the label was checked, fixed or left as rendered (`labelPass`). Pass false when the user wants the packaging left exactly as generated: nothing is checked or re-printed.",
        +  "type": "boolean"
        +}
      • changedInput schema / properties / refImages / description
        Previous value: -"local file paths or URLs of product/logo references to composite in. ON THE POSE MODELS — any row hermoso_capabilities marks `needsRefs` with a `refsMax`, such as putting your product in someone’s hands or a virtual try-on — THE ORDER IS THE CONTRACT AND IT IS NOT A COMPOSITE: refImages[0] is the PERSON photo, and the rest (up to `refsMax` minus one) are the product or garment photos. Reversed, you get the product wearing the person. A 4th product is dropped and the reply says so. A real person’s photo confirms their likeness consent."New value: +"local file paths or URLs of product/logo references to composite in. ON THE POSE MODELS — any row hermoso_capabilities marks `needsRefs` with a `refsMax`, such as putting your product in someone’s hands or a virtual try-on — THE ORDER IS THE CONTRACT AND IT IS NOT A COMPOSITE: refImages[0] is the PERSON photo, and the rest (up to `refsMax` minus one) are the product or garment photos. Reversed, you get the product wearing the person. A 4th product is dropped and the reply says so. Using a real person’s face, likeness or voice confirms you are them or have their consent, take full, unlimited responsibility for its use and will cover any claim against Hermoso (hermoso.ai/terms). Paid plan only; recorded."
    • Changedgenerate_video5 fields changed
      • addedInput schema / properties / angles
        Added value: +{
        +  "description": "OPTIONAL, OFF BY DEFAULT: with `creator`, also send the extra views that creator already has saved (their pose plates, up to 2) beside their portrait. A test showed no visible improvement over the portrait alone, and each extra view adds about 25 s before the render starts, so leave it off unless asked. Views are skipped when the face library is busy, and the render always goes ahead on the portrait.",
        +  "type": "boolean"
        +}
      • addedInput schema / properties / creator
        Added value: +{
        +  "description": "STAR A SAVED CREATOR in this clip — their id from list_creators, or the name you know them by, or a PRESET AI creator from list_creators presets (exact name or id; free, no generation). Their saved portrait rides first among the references as the on-camera person, with their saved consent, exactly as render_ad casts them; a real person saved from a photo keeps their real face on camera. An unknown name is refused by name, nothing charged.",
        +  "type": "string"
        +}
      • addedInput schema / properties / faceRoute
        Added value: +{
        +  "description": "ONLY after a render came back saying the video model's safety check flagged a person's face: 'face_lane' is the user's choice \"I own the rights to this face\". Send the SAME request again with it and the same face is rendered on Seedance through the face library, at the normal price. Sending it is the user's confirmation that they have the rights to that face (paid plans, like every real face). Never set it on your own; the other choices that refusal names are a different model (`model`) or another creator (`creator`).",
        +  "enum": [
        +    "face_lane"
        +  ],
        +  "type": "string"
        +}
      • changedInput schema / properties / refImage / description
        Previous value: -"local path or URL to anchor the first frame; a real person’s photo here or in refImages confirms their likeness consent"New value: +"local path or URL to anchor the first frame. Using a real person’s face, likeness or voice confirms you are them or have their consent, take full, unlimited responsibility for its use and will cover any claim against Hermoso (hermoso.ai/terms). Paid plan only; recorded."
      • addedInput schema / properties / speak
        Added value: +{
        +  "description": "A PERSON SAYING THESE EXACT WORDS TO CAMERA — the default for any \"make my photo talk\" / spokesperson / talk-to-camera ask. Pass with `creator` or a portrait as `refImage`: the video model films them saying it in their own voice, matched to who they are, and the length follows the words (leave durationSeconds out). `prompt` is then the staging (e.g. \"natural\", \"walking in a park\"). Real footage, not an animated photo; generate_avatar is the animated-photo look, only when asked for by name",
        +  "type": "string"
        +}
    • Changedheadline_variants1 field changed
      • addedInput schema / properties / fixLabel
        Added value: +{
        +  "description": "false = leave the product label exactly as rendered: no product lookup, no label check, no re-print (default on when the ad shows the brand’s saved product)",
        +  "type": "boolean"
        +}
    • Changedlist_creators1 field changed
      • addedInput schema / properties / gender
        Added value: +{
        +  "description": "filter the PRESET creators by gender (the saved cast is never filtered)",
        +  "enum": [
        +    "female",
        +    "male"
        +  ],
        +  "type": "string"
        +}
    • Changedlocalize_ad1 field changed
      • addedInput schema / properties / fixLabel
        Added value: +{
        +  "description": "false = leave the product label exactly as rendered: no product lookup, no label check, no re-print (default on when the ad shows the brand’s saved product)",
        +  "type": "boolean"
        +}
    • Changedmake_explainer14 fields changed
      • changedInput schema / properties / aspectRatio / description
        Previous value: -"'9:16' default"New value: +"'9:16' default (a faceless channel video and a host episode default to 16:9)"
      • addedInput schema / properties / cameras
        Added value: +{
        +  "description": "host_episode: the rotation, ids frontal / three_quarter / close or framings in words; one entry = one fixed camera",
        +  "items": {
        +    "type": "string"
        +  },
        +  "type": "array"
        +}
      • addedInput schema / properties / creator
        Added value: +{
        +  "description": "host_episode: the host, a saved creator or a preset by name or id (list_creators)",
        +  "type": "string"
        +}
      • addedInput schema / properties / dryRun
        Added value: +{
        +  "description": "true = return the exact credits this explainer reserves (its own pricing, stopped at the hold) and render nothing. Quote it before running one; try frameDensity lean or minimal when the balance is short.",
        +  "type": "boolean"
        +}
      • addedInput schema / properties / format
        Added value: +{
        +  "description": "blocks lane: 'explainer' (default; Gemini Omni 720p, 16:9 or 9:16, runs exactly the length asked) or 'faceless_channel' (a YouTube/TikTok faceless channel video; MiniMax H3 at 2K, 16:9 by default, five hard cuts per 10s block, a whole number of blocks). Omit and a history / kids / fairytale channel is a faceless channel video. 'host_episode' = the AI host episode (see the top).",
        +  "enum": [
        +    "explainer",
        +    "faceless_channel",
        +    "host_episode"
        +  ],
        +  "type": "string"
        +}
      • changedInput schema / properties / frameDensity / description
        Previous value: -"how many pictures per second of narration, and therefore what it costs. 'standard' (default) is a frame about every 1.5s — the density a stills film needs to read as a film rather than a slideshow; 'lean' is one about every 2.5s (the longest hold that still reads as a film, ~40% of the frames and ~40% of the cost); 'minimal' is ONE picture per narration section, which is cheapest and is frankly a slideshow. Only drop below the default if the user asked for something cheaper."New value: +"how many pictures per second of narration, and therefore what it costs. 'standard' (default) is a frame about every 1.5s — the density a stills film needs to read as a film rather than a slideshow; 'lean' is one about every 2.5s (the longest hold that still reads as a film, ~40% of the frames and ~40% of the cost); 'minimal' is one picture about every 3.5s, the cheapest and the longest any still is ever held, and it reads close to a slideshow. Only drop below the default if the user asked for something cheaper."
      • addedInput schema / properties / fromDraft
        Added value: +{
        +  "description": "host_episode: a finished draft's job id, to render it in HD (720p) with the same script, cameras, set and host",
        +  "type": "string"
        +}
      • addedInput schema / properties / hostImage
        Added value: +{
        +  "description": "host_episode: a photo URL of the host instead (upload_file for a local file); a real person's face needs a paid plan",
        +  "type": "string"
        +}
      • addedInput schema / properties / lane
        Added value: +{
        +  "description": "'blocks' (default) = 10-second video blocks, real motion, one narrated line per block; 'stills' = the picture film (a still about every 1.5s, narrated, no video model; cheaper). Quote either with dryRun.",
        +  "enum": [
        +    "blocks",
        +    "stills"
        +  ],
        +  "type": "string"
        +}
      • addedInput schema / properties / script
        Added value: +{
        +  "description": "host_episode: the exact words, said verbatim and split at natural breaks into 4-30s pieces",
        +  "type": "string"
        +}
      • addedInput schema / properties / setting
        Added value: +{
        +  "description": "host_episode: the set in words (default: written to fit the topic)",
        +  "type": "string"
        +}
      • addedInput schema / properties / thumbnail
        Added value: +{
        +  "description": "host_episode: one thumbnail of the host (default true)",
        +  "type": "boolean"
        +}
      • changedInput schema / properties / topic / description
        Previous value: -"what the explainer should teach or explain — a topic or a short brief"New value: +"what the explainer should teach or explain — a topic or a short brief (host_episode: or pass `script`)"
      • removedInput schema / required
        Removed value: -[
        -  "topic"
        -]
    • Changedmultiply_ad1 field changed
      • addedInput schema / properties / change
        Added value: +{
        +  "description": "what should change, in the user's own words, e.g. 'older women, winter streets'; every variant applies it (omit to let the variants vary freely)",
        +  "type": "string"
        +}
    • Changedplan_ad2 fields changed
      • changedInput schema / properties / durationSeconds / description
        Previous value: -"VIDEO ONLY — the total spot length the user explicitly asked for, in seconds, copied verbatim (30 for \"a 30 second ad\"). The planner authors the storyboard TO it: the scenes’ seconds sum to it and the script is word-budgeted for it. Supported range 4–180; anything outside is CLAMPED to it (the reply says so). A length that fits ONE clip of the render model renders as a single continuous pass; anything longer is STITCHED from acts filled to that model’s clip maximum with the remainder last (on a 15s-clip model, 40 -> 15+15+10 and 17 -> 13+4) — never time-compressed. The maximum is the model’s own: 15s on most, 30s on the longest-clip model, so a 30s ad can be one unbroken take rather than two acts. Omit when the user named no length; do NOT pass a guess, an omitted value keeps the recipe-aware default."New value: +"VIDEO ONLY — the total spot length the user explicitly asked for, in seconds, copied verbatim (30 for \"a 30 second ad\"). The planner authors the storyboard TO it: the scenes’ seconds sum to it and the script is word-budgeted for it. Supported range 4–180; anything outside is CLAMPED to it (the reply says so). A length that fits ONE clip of the render model renders as a single continuous pass; anything longer is STITCHED from acts filled to that model’s clip maximum with the remainder last (on a 15s-clip model, 40 -> 15+15+10 and 17 -> 13+4) — never time-compressed. The maximum is the model’s own: 15s on most, 30s on the longest-clip model, so a 30s ad can be one unbroken take rather than two acts. Omit when the user named no length; do NOT pass a guess: an omitted value is the default, a 30s spot in one unbroken take (2026-09-29), and a length the user names always wins."
      • addedInput schema / properties / talent
        Added value: +{
        +  "description": "who is on camera; omit to follow the format. Say 'someone new' in product to skip reusing a saved creator.",
        +  "enum": [
        +    "auto",
        +    "creator",
        +    "product_only"
        +  ],
        +  "type": "string"
        +}
    • Changedpost_edit8 fields changed
      • changedInput schema / properties / ops / items / properties / db / description
        Previous value: -"audio_gain -20..+6 dB"New value: +"audio_gain -20..+6 dB / music: trim on its automatic level, -20..+12"
      • addedInput schema / properties / ops / items / properties / duck
        Added value: +{
        +  "anyOf": [
        +    {
        +      "const": "auto",
        +      "type": "string"
        +    },
        +    {
        +      "type": "boolean"
        +    }
        +  ],
        +  "description": "music: 'auto' default = dips while the clip's own sound plays"
        +}
      • addedInput schema / properties / ops / items / properties / fadeOut
        Added value: +{
        +  "description": "music: fade-out seconds 0-5",
        +  "type": "number"
        +}
      • addedInput schema / properties / ops / items / properties / from
        Added value: +{
        +  "description": "music: the second of the track to start from",
        +  "type": "number"
        +}
      • addedInput schema / properties / ops / items / properties / mood
        Added value: +{
        +  "description": "music: mood words for the library pick, or the brief for generated music",
        +  "type": "string"
        +}
      • changedInput schema / properties / ops / items / properties / op / enum
        Previous value: -[
        -  "trim",
        -  "speed",
        -  "mute",
        -  "audio_gain",
        -  "fade_out",
        -  "append_card",
        -  "watermark",
        -  "grain",
        -  "text",
        -  "join"
        -]New value: +[
        +  "trim",
        +  "speed",
        +  "mute",
        +  "audio_gain",
        +  "fade_out",
        +  "music",
        +  "append_card",
        +  "watermark",
        +  "grain",
        +  "text",
        +  "join"
        +]
      • addedInput schema / properties / ops / items / properties / replace
        Added value: +{
        +  "description": "music: true = replaces the clip's own sound",
        +  "type": "boolean"
        +}
      • addedInput schema / properties / ops / items / properties / track
        Added value: +{
        +  "description": "music: omit = a library track by mood; a library track name; an audio link; 'generate' (paid, only on the user's ask)",
        +  "type": "string"
        +}
    • Changedrecast_motion1 field changed
      • changedInput schema / properties / image / description
        Previous value: -"the actor/character image URL (who should appear); a real person’s photo confirms their likeness consent"New value: +"the actor/character image URL (who should appear). Using a real person’s face, likeness or voice confirms you are them or have their consent, take full, unlimited responsibility for its use and will cover any claim against Hermoso (hermoso.ai/terms). Paid plan only; recorded."
    • Changedremix_static1 field changed
      • changedInput schema / properties / imageUrl / description
        Previous value: -"the URL of the static ad image to clone; a real person in it confirms their likeness consent"New value: +"the URL of the static ad image to clone. Using a real person’s face, likeness or voice confirms you are them or have their consent, take full, unlimited responsibility for its use and will cover any claim against Hermoso (hermoso.ai/terms). Paid plan only; recorded."
    • Changedrender_ad7 fields changed
      • addedInput schema / properties / board
        Added value: +{
        +  "description": "paint the storyboard board first (default: on for UGC and cinematic spots, off for a product-only commercial); false = render from the text shot list",
        +  "type": "boolean"
        +}
      • changedInput schema / properties / creator / description
        Previous value: -"CAST A SAVED CREATOR in this ad — their id from list_creators, or the name you know them by (“Sarah”). Their saved portrait becomes the on-camera identity for the whole spot, so the same face carries across every act and across every ad you render for this brand — and because we already have their picture, the character portrait this pipeline would otherwise generate is skipped, so casting somebody costs LESS than not casting them. Omit to let the ad cast a fresh person — EXCEPT for a CREATOR account (onboarded from their own @handle): their own saved likeness is cast by default on any plan with a person on camera, and the read-back says `default:true`; pass \"none\" to render without them. Refused for free, with nothing rendered, if the name matches nobody or more than one creator, or if an explicitly named creator is cast on a plan with nobody on camera. Casting a REAL person confirms their likeness consent."New value: +"CAST A SAVED CREATOR in this ad — their id from list_creators, or the name you know them by (“Sarah”), or a PRESET AI creator from list_creators presets by exact name or id (free, no generation). Their saved portrait becomes the on-camera identity for the whole spot, so the same face carries across every act and across every ad you render for this brand — and because we already have their picture, the character portrait this pipeline would otherwise generate is skipped, so casting somebody costs LESS than not casting them. Omit to let the ad cast a fresh person — EXCEPT for a CREATOR account (onboarded from their own @handle): their own saved likeness is cast by default when the plan has a person on camera and the account is on a paid Hermoso plan (a free account gets a fresh AI person), and the read-back says `default:true`; pass \"none\" to render without them. Refused for free, with nothing rendered, if the name matches nobody or more than one creator, if an explicitly named creator is cast on a plan with nobody on camera, or if a REAL person is cast on a free plan. Using a real person’s face, likeness or voice confirms you are them or have their consent, take full, unlimited responsibility for its use and will cover any claim against Hermoso (hermoso.ai/terms). Paid plan only; recorded."
      • addedInput schema / properties / faceRoute
        Added value: +{
        +  "description": "ONLY after a render came back saying the video model's safety check flagged a person's face: 'face_lane' is the user's choice \"I own the rights to this face\". Send the SAME request again with it and the same face is rendered on Seedance through the face library, at the normal price. Sending it is the user's confirmation that they have the rights to that face (paid plans, like every real face). Never set it on your own; the other choices that refusal names are a different model (`model`) or another creator (`creator`).",
        +  "enum": [
        +    "face_lane"
        +  ],
        +  "type": "string"
        +}
      • addedInput schema / properties / foundationImages
        Added value: +{
        +  "description": "images a foundations:'choose' call returned, reused as-is (not repainted)",
        +  "properties": {
        +    "hero": {
        +      "type": "string"
        +    },
        +    "identitySheet": {
        +      "type": "string"
        +    },
        +    "location": {
        +      "type": "string"
        +    },
        +    "moodboard": {
        +      "type": "string"
        +    }
        +  },
        +  "type": "object"
        +}
      • addedInput schema / properties / foundations
        Added value: +{
        +  "description": "product commercial / cinematic: 'choose' paints ONLY the foundations (identity sheet + four-up moodboard, or hero + location) and returns them, no video — then call again with moodboardPick + foundationImages",
        +  "enum": [
        +    "choose"
        +  ],
        +  "type": "string"
        +}
      • addedInput schema / properties / moodboardPick
        Added value: +{
        +  "description": "which moodboard panel (1-4, left to right, top to bottom) the storyboard follows; default 1",
        +  "maximum": 4,
        +  "minimum": 1,
        +  "type": "integer"
        +}
      • changedInput schema / properties / music / description
        Previous value: -"music bed: on (default), false, or the bed described in words ('lo-fi jazz, brushed drums')"New value: +"music bed: OFF unless asked. true = the plan's own music line; or the bed in words ('lo-fi jazz, brushed drums'); false = none"
    • Changedresize_ad1 field changed
      • addedInput schema / properties / fixLabel
        Added value: +{
        +  "description": "false = leave the product label exactly as rendered: no product lookup, no label check, no re-print (default on when the ad shows the brand’s saved product)",
        +  "type": "boolean"
        +}
    • Changedupdate_brand2 fields changed
      • addedInput schema / properties / pronounce
        Added value: +{
        +  "description": "how the brand NAME is said aloud, as a simple respelling with the stressed syllable in capitals (e.g. \"KOH-dee-ak\"). Videos use it as a delivery note beside the spoken line; set it when a render mispronounced the name.",
        +  "type": "string"
        +}
      • addedInput schema / properties / pronunciations
        Added value: +{
        +  "additionalProperties": {
        +    "type": "string"
        +  },
        +  "description": "how PRODUCT names are said aloud, e.g. {\"Power Cakes\": \"POW-er cakes\"}. Merged into the saved ones; an empty string removes one.",
        +  "propertyNames": {
        +    "type": "string"
        +  },
        +  "type": "object"
        +}
  6. 29 tool updatesv0.1.320
    • Changedcancel_scheduled1 field changed
      • changedInput schema / properties / brand / description
        Previous value: -"WHICH BRAND this post is in — the id or exact name from list_brands. Needed when the post lives in a brand this connection is not pinned to: a post you can CREATE in a brand must be manageable there too, without switching the whole connection. A name that matches no brand, or two, is REFUSED."New value: +"WHICH BRAND this post is in — id or exact name from list_brands; needed when it is not the brand this connection is pinned to. A name that matches no brand, or two, is REFUSED."
    • Changedcollect_post_metrics1 field changed
      • changedInput schema / properties / brand / description
        Previous value: -"WHICH BRAND the post lives in — the id or exact name from list_brands (a workspace shared with you: its profile id). Needed when it was published in a brand this connection is not pinned to: a post you can CREATE in a brand is manageable there too, for THIS CALL ONLY, without switching the connection. A name that matches no brand, or two, is REFUSED and nothing is done."New value: +"WHICH BRAND the post lives in — id or exact name from list_brands (a shared workspace: its profile id). Needed when it was published in a brand this connection is not pinned to; applies to THIS CALL ONLY. A name that matches no brand, or two, is REFUSED and nothing is done."
    • Changedduplicate_scheduled1 field changed
      • changedInput schema / properties / brand / description
        Previous value: -"WHICH BRAND this post is in — the id or exact name from list_brands. Needed when the post lives in a brand this connection is not pinned to: a post you can CREATE in a brand must be manageable there too, without switching the whole connection. A name that matches no brand, or two, is REFUSED."New value: +"WHICH BRAND this post is in — id or exact name from list_brands; needed when it is not the brand this connection is pinned to. A name that matches no brand, or two, is REFUSED."
    • Changededit_image1 field changed
      • addedInput schema / properties / dryRun
        Added value: +{
        +  "description": "true = return the exact credits this edit reserves and render nothing",
        +  "type": "boolean"
        +}
    • Changededit_video11 fields changed
      • changedInput schema / properties / elements / description
        Previous value: -"OPTIONAL identity/product grounding (≤4): a creator portrait or the real product photo, so the edit restores the REAL thing instead of re-inventing it. Describe each one in the instruction. Leave out for a plain restyle"New value: +"OPTIONAL identity/product grounding (≤4): a creator portrait or the real product photo, so the edit restores the REAL thing. Describe each one in the instruction"
      • changedInput schema / properties / instruction / description
        Previous value: -"the exact transformation to apply, in the user’s own words"New value: +"the change, in the user’s own words"
      • changedInput schema / properties / interactionId / description
        Previous value: -"OPTIONAL: the interactionId an earlier Gemini Omni render or edit returned. The edit then continues that clip on the SAME Omni model from its own stored context (identity-true, no re-upload, usually cheaper). If that edit cannot run, the clip is edited by the video editor instead and the reply says so."New value: +"OPTIONAL: the interactionId an earlier Gemini Omni render or edit returned; the edit continues that clip on the same Omni model. If it cannot run, the video editor edits it and the reply says so."
      • changedInput schema / properties / keepAudio / description
        Previous value: -"default true — keep the source clip’s audio track. Set false to return the edit silent"New value: +"default true: keep the source audio; false = silent"
      • addedInput schema / properties / lighting
        Added value: +{
        +  "description": "default auto: keep the source light unless the change needs new light (night, a lamp, fire). 'preserve' or 'relight' forces it",
        +  "enum": [
        +    "auto",
        +    "preserve",
        +    "relight"
        +  ],
        +  "type": "string"
        +}
      • addedInput schema / properties / literal
        Added value: +{
        +  "description": "true = send the instruction exactly as written, with no preserve/lock wrapper",
        +  "type": "boolean"
        +}
      • addedInput schema / properties / previewAt
        Added value: +{
        +  "description": "second to preview (default 0); resend with previewStill",
        +  "type": "number"
        +}
      • addedInput schema / properties / previewFirstFrame
        Added value: +{
        +  "description": "true = edit ONE still of the first frame first and quote the clip; the paid clip does not run",
        +  "type": "boolean"
        +}
      • addedInput schema / properties / previewStill
        Added value: +{
        +  "description": "the still a previewFirstFrame call returned; the clip then matches the changed thing to it",
        +  "type": "string"
        +}
      • addedInput schema / properties / reference
        Added value: +{
        +  "description": "OPTIONAL image URL that anchors the MATERIAL of what changes (a fabric, a finish, a colour swatch, the real product). Only its surface is used, never its framing or light",
        +  "type": "string"
        +}
      • changedInput schema / properties / video / description
        Previous value: -"the source video URL (from a previous render, a job result, or list_library)"New value: +"the source video URL (a render, job result or list_library)"
    • Changedfix_beat1 field changed
      • addedInput schema / properties / dryRun
        Added value: +{
        +  "description": "true = return the exact credits this fix reserves and render nothing",
        +  "type": "boolean"
        +}
    • Changedgenerate_image2 fields changed
      • changedInput schema / properties / prompt / description
        Previous value: -"the full image prompt — subject, composition, lighting, and any on-image ad text. ON A POSE MODEL (product-in-hand / try-on) THIS IS EXTRA DIRECTION AND IT IS OPTIONAL — leave it out and the pose is built for you. If you do write one, DESCRIBE THE POSE (\"she holds the bottle upright in her right hand at chest height, label to camera\"); do NOT phrase it as a swap (\"replace the mug with the bottle\"), which is REFUSED for free, because the product then comes out the size of whatever it replaced — a 30ml bottle rendered mug-sized in testing."New value: +"REQUIRED on every model EXCEPT the pose rows below. the full image prompt — subject, composition, lighting, and any on-image ad text. ON A POSE MODEL (product-in-hand / try-on) THIS IS EXTRA DIRECTION AND IT IS OPTIONAL — leave it out and the pose is built for you. If you do write one, DESCRIBE THE POSE (\"she holds the bottle upright in her right hand at chest height, label to camera\"); do NOT phrase it as a swap (\"replace the mug with the bottle\"), which is REFUSED for free, because the product then comes out the size of whatever it replaced — a 30ml bottle rendered mug-sized in testing."
      • removedInput schema / required
        Removed value: -[
        -  "prompt"
        -]
    • Changedlist_meta_posts1 field changed
      • changedInput schema / properties / brand / description
        Previous value: -"WHICH BRAND the post lives in — the id or exact name from list_brands (a workspace shared with you: its profile id). Needed when it was published in a brand this connection is not pinned to: a post you can CREATE in a brand is manageable there too, for THIS CALL ONLY, without switching the connection. A name that matches no brand, or two, is REFUSED and nothing is done."New value: +"WHICH BRAND the post lives in — id or exact name from list_brands (a shared workspace: its profile id). Needed when it was published in a brand this connection is not pinned to; applies to THIS CALL ONLY. A name that matches no brand, or two, is REFUSED and nothing is done."
    • Changedlist_published_posts1 field changed
      • changedInput schema / properties / brand / description
        Previous value: -"WHICH BRAND the post lives in — the id or exact name from list_brands (a workspace shared with you: its profile id). Needed when it was published in a brand this connection is not pinned to: a post you can CREATE in a brand is manageable there too, for THIS CALL ONLY, without switching the connection. A name that matches no brand, or two, is REFUSED and nothing is done."New value: +"WHICH BRAND the post lives in — id or exact name from list_brands (a shared workspace: its profile id). Needed when it was published in a brand this connection is not pinned to; applies to THIS CALL ONLY. A name that matches no brand, or two, is REFUSED and nothing is done."
    • Changedmake_template_ad1 field changed
      • changedInput schema / properties / config / description
        Previous value: -"MUST include config.template: 'custom' or a preset id above, plus its fields"New value: +"MUST include config.template: 'custom' or a preset id, plus its fields. PRESETS: 'slideshow' (IMAGES, TikTok photo mode / Reels 1080x1920, or size:'4:5' feed carousels; no branding): { slides:[{text, sub?, image?, blur?, background?, position?}] (2-35; words never rewritten), style? ('tiktok-classic'|'clean-minimal'|'note-style' or a look in words), textStyle?, video?:true (+ an MP4) }; 2 credits, +1 per slide past 5, +2 for the MP4. 'imessage-chat' (VIDEO ~15s): { thread:{contactName, messages:[{from:'them'|'me', text?, product?:{image,title,domain}}]}, theme?, endCard }. 'chatgpt-chat' (VIDEO): { question, answer (may **bold** the brand), productImage?, endCard }. 'apple-notes' (VIDEO): { title, lines[], theme?, endCard }. 'value-prop' (VIDEO ~17s): { hook ≤40ch, claims[3-5 ≤34ch], productImages[2-3], palette[], endCard }. 'static-mockup' (IMAGE): { style:'imessage'|'notes'|'card', size?:{w,h}, ...fields }. 'airdrop-carousel' (VIDEO): { brandName, products:[{image, title?}] (3-16), endCard }. 'app-ui-tour' (VIDEO): { hook?, appName, iconImage?, beats:[{screenImage, caption}] (2-6), endCard }. 'imessage-cascade' (VIDEO): { notifications:[{sender, text}] (4-8), backgroundImage?, endCard }. 'photo-grid' (VIDEO): { title?, photos:[{image, label?}] (4-9), endCard }. 'vignette' (VIDEO): { hook, lines[2-4 ≤40ch], heroImage, endCard }. 'kinetic-type' (VIDEO, own SFX): { phrases[3-6 ≤34ch], productImages?[≤4], endCard }. 'myth-vs-fact' (VIDEO with a real VOICEOVER, small extra charge): { pairs:[{myth ≤50ch, fact ≤60ch}] (2-4; [brackets] accent), endCard }, real truths only. 'carousel' (IMAGES, 5-10 branded 1080x1080): { cover:{hook?, title}, slides:[{headline, support?, stat?:{value, label}}] (3-8), cta:{headline, cta?, domain?}, productImage?, logo? }. endCard = { headline, cta, domain?, logo?, color? }; palette and fontStack optional."
    • Changedmake_thumbnail2 fields changed
      • changedInput schema / properties / emotion / description
        Previous value: -"the expression on the face (default 'shock') — a preset id or your own phrase"New value: +"the expression on the face (default 'shock') — shock · hype · fear · confusion · determination · smug · charisma · disgust · awe · rage · laugh, or your own phrase"
      • changedInput schema / properties / framework / description
        Previous value: -"concept framework id (default 'posed_portrait'), or your own concept in words"New value: +"concept framework id (default 'posed_portrait') — before_after · social_ui · three_step · screenshot · posed_portrait · posed_action · specific_day · graphical · landscape · map_aerial · product · adding_text · repetition · size_difference · news_clip · amplified_reality — or your own concept in words"
    • Changedpost_edit22 fields changed
      • addedInput schema / properties / dryRun
        Added value: +{
        +  "description": "true = return the exact credits this edit reserves and run nothing",
        +  "type": "boolean"
        +}
      • changedInput schema / properties / ops / items / properties / background / description
        Previous value: -"append_card: card background — hex or a color name ('red', 'navy'…); the user's stated color always wins over the brand palette"New value: +"append_card: hex or a colour name; the user's colour beats the brand palette"
      • addedInput schema / properties / ops / items / properties / bridge / properties / sound / description
        Added value: +"'auto' default, 'own', 'impact', 'whoosh', 'none', or an audio URL (find_sound)"
      • removedInput schema / properties / ops / items / properties / bridge / properties / sound / enum
        Removed value: -[
        -  "auto",
        -  "own",
        -  "impact",
        -  "whoosh",
        -  "none"
        -]
      • changedInput schema / properties / ops / items / properties / card_html / description
        Previous value: -"append_card: your OWN full-frame card design as inline-styled HTML ({{logo}} inserts the real brand logo) — use when the standard layout cannot honor the request"New value: +"append_card: your OWN full-frame card as inline-styled HTML ({{logo}} = the brand logo)"
      • addedInput schema / properties / ops / items / properties / clips / items / properties / match
        Added value: +{}
      • addedInput schema / properties / ops / items / properties / clips / items / properties / reframe
        Added value: +{}
      • changedInput schema / properties / ops / items / properties / corner / description
        Previous value: -"watermark corner (default br)"New value: +"watermark corner (default br), or x/y"
      • changedInput schema / properties / ops / items / properties / headline / description
        Previous value: -"append_card: big line (defaults to the brand name)"New value: +"append_card: big line (default: brand name)"
      • addedInput schema / properties / ops / items / properties / intensity / anyOf
        Added value: +[
        +  {
        +    "enum": [
        +      "default",
        +      "strong"
        +    ],
        +    "type": "string"
        +  },
        +  {
        +    "type": "number"
        +  }
        +]
      • changedInput schema / properties / ops / items / properties / intensity / description
        Previous value: -"grain look"New value: +"grain: or 0-1 (default 0.25)"
      • removedInput schema / properties / ops / items / properties / intensity / enum
        Removed value: -[
        -  "default",
        -  "strong"
        -]
      • removedInput schema / properties / ops / items / properties / intensity / type
        Removed value: -"string"
      • addedInput schema / properties / ops / items / properties / match
        Added value: +{
        +  "description": "join: seam match, 'auto' default | 'off' | {grade,level,grain,blur,strength}"
        +}
      • changedInput schema / properties / ops / items / properties / position / description
        Previous value: -"text: where"New value: +"text: where, or x/y"
      • addedInput schema / properties / ops / items / properties / reframe
        Added value: +{
        +  "description": "join: shot change at a same-framing stitch, 'auto' | 'off' | step 1.1-1.5"
        +}
      • changedInput schema / properties / ops / items / properties / seconds / description
        Previous value: -"fade_out 0.3-3s / append_card 2-5s / crossfade 0.2-1.5s"New value: +"fade_out 0.3-3s / append_card 2-5s / transition 0.2-1.5s"
      • changedInput schema / properties / ops / items / properties / sub / description
        Previous value: -"append_card: the pill line (defaults to the website) / text: a smaller second line"New value: +"append_card: pill line (default: website) / text: a second, smaller line"
      • changedInput schema / properties / ops / items / properties / transition / description
        Previous value: -"join"New value: +"join: 'cut' default, 'crossfade', or any ffmpeg xfade name (wipeleft…)"
      • removedInput schema / properties / ops / items / properties / transition / enum
        Removed value: -[
        -  "cut",
        -  "crossfade"
        -]
      • addedInput schema / properties / ops / items / properties / x
        Added value: +{
        +  "description": "text/watermark centre: 0-1 of frame, or px",
        +  "type": "number"
        +}
      • addedInput schema / properties / ops / items / properties / y
        Added value: +{
        +  "type": "number"
        +}
    • Changedpost_performance1 field changed
      • changedInput schema / properties / brand / description
        Previous value: -"WHICH BRAND the post lives in — the id or exact name from list_brands (a workspace shared with you: its profile id). Needed when it was published in a brand this connection is not pinned to: a post you can CREATE in a brand is manageable there too, for THIS CALL ONLY, without switching the connection. A name that matches no brand, or two, is REFUSED and nothing is done."New value: +"WHICH BRAND the post lives in — id or exact name from list_brands (a shared workspace: its profile id). Needed when it was published in a brand this connection is not pinned to; applies to THIS CALL ONLY. A name that matches no brand, or two, is REFUSED and nothing is done."
    • Changedpost_to_bluesky1 field changed
      • 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."
    • Changedpost_to_google_business3 fields changed
      • 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."
      • changedInput schema / properties / hook / description
        Previous value: -"WHAT ANGLE THIS POST IS BUILT ON — the single most valuable field here, and the only moment it can ever be recorded. post_performance groups on it to answer \"which hooks work\", and it needs 5 posts sharing ONE hook before it will call anything a winner, so REUSE THE SAME WORDING across a campaign instead of rephrasing it every time. Best of all, pass a hook id from list_hooks (e.g. \"direct_callout\", \"mid_problem\", \"before_after\") — those fold onto a stable key however they are spelled, so a whole brand accumulates evidence on one row. Your own wording is fine too; it just only groups when you repeat it exactly. Omitting it means this post can never vote on which hook works."New value: +"WHAT ANGLE this post is built on, recorded only at publish time. post_performance ranks hooks on it (a winner needs 5 posts sharing ONE hook), so pass a list_hooks id (e.g. \"direct_callout\", \"before_after\") or reuse your own wording EXACTLY across a campaign. Omit it and this post never votes on which hook works."
      • changedInput schema / properties / subject / description
        Previous value: -"WHAT THIS POST IS ABOUT — the product, feature, offer or theme (e.g. \"winter coat\", \"free trial\", \"founder story\"). The second grouping axis in post_performance. Same rule as hook: reuse the exact wording so posts about one subject land in one group."New value: +"WHAT THIS POST IS ABOUT — product, feature, offer or theme (e.g. \"winter coat\", \"free trial\"). post_performance's second grouping axis: reuse the exact wording, as with hook."
    • Changedpost_to_linkedin4 fields changed
      • 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."
      • changedInput schema / properties / hook / description
        Previous value: -"WHAT ANGLE THIS POST IS BUILT ON — the single most valuable field here, and the only moment it can ever be recorded. post_performance groups on it to answer \"which hooks work\", and it needs 5 posts sharing ONE hook before it will call anything a winner, so REUSE THE SAME WORDING across a campaign instead of rephrasing it every time. Best of all, pass a hook id from list_hooks (e.g. \"direct_callout\", \"mid_problem\", \"before_after\") — those fold onto a stable key however they are spelled, so a whole brand accumulates evidence on one row. Your own wording is fine too; it just only groups when you repeat it exactly. Omitting it means this post can never vote on which hook works."New value: +"WHAT ANGLE this post is built on, recorded only at publish time. post_performance ranks hooks on it (a winner needs 5 posts sharing ONE hook), so pass a list_hooks id (e.g. \"direct_callout\", \"before_after\") or reuse your own wording EXACTLY across a campaign. Omit it and this post never votes on which hook works."
      • changedInput schema / properties / idempotencyKey / description
        Previous value: -"SAFE RETRIES. Publishing can take minutes (a video upload, a carousel of ten slides) and a transport can time out while the post SUCCEEDS — retrying blind is how the same thing gets posted twice. Pass any stable string here and a repeat of the SAME publish returns the ORIGINAL post id instead of posting again (24h). You do not have to: an identical publish is auto-recognised for 10 minutes anyway. If a call times out or errors ambiguously, CALL AGAIN WITH THE SAME KEY — that is the safe move, and it will either report the original post or publish it for the first time. It never posts twice."New value: +"SAFE RETRIES — any stable string: a repeat of the SAME publish within 24h returns the ORIGINAL post id instead of posting again (an identical publish is also auto-recognised for 10 minutes). On a timeout or an ambiguous error, CALL AGAIN WITH THE SAME KEY: it reports the original post or publishes it once. It never posts twice."
      • changedInput schema / properties / subject / description
        Previous value: -"WHAT THIS POST IS ABOUT — the product, feature, offer or theme (e.g. \"winter coat\", \"free trial\", \"founder story\"). The second grouping axis in post_performance. Same rule as hook: reuse the exact wording so posts about one subject land in one group."New value: +"WHAT THIS POST IS ABOUT — product, feature, offer or theme (e.g. \"winter coat\", \"free trial\"). post_performance's second grouping axis: reuse the exact wording, as with hook."
    • Changedpost_to_linkedin_page5 fields changed
      • 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."
      • addedInput schema / properties / coverAtMs
        Added value: +{
        +  "description": "THE VIDEO COVER of a videoUrl post, as ONE frame: milliseconds from the start (7000 = the frame at 7s). That frame is uploaded as LinkedIn’s video thumbnail (only possible while the video uploads). videoThumbnailUrl wins.",
        +  "type": "number"
        +}
      • changedInput schema / properties / hook / description
        Previous value: -"WHAT ANGLE THIS POST IS BUILT ON — the single most valuable field here, and the only moment it can ever be recorded. post_performance groups on it to answer \"which hooks work\", and it needs 5 posts sharing ONE hook before it will call anything a winner, so REUSE THE SAME WORDING across a campaign instead of rephrasing it every time. Best of all, pass a hook id from list_hooks (e.g. \"direct_callout\", \"mid_problem\", \"before_after\") — those fold onto a stable key however they are spelled, so a whole brand accumulates evidence on one row. Your own wording is fine too; it just only groups when you repeat it exactly. Omitting it means this post can never vote on which hook works."New value: +"WHAT ANGLE this post is built on, recorded only at publish time. post_performance ranks hooks on it (a winner needs 5 posts sharing ONE hook), so pass a list_hooks id (e.g. \"direct_callout\", \"before_after\") or reuse your own wording EXACTLY across a campaign. Omit it and this post never votes on which hook works."
      • changedInput schema / properties / idempotencyKey / description
        Previous value: -"SAFE RETRIES. Publishing can take minutes (a video upload, a carousel of ten slides) and a transport can time out while the post SUCCEEDS — retrying blind is how the same thing gets posted twice. Pass any stable string here and a repeat of the SAME publish returns the ORIGINAL post id instead of posting again (24h). You do not have to: an identical publish is auto-recognised for 10 minutes anyway. If a call times out or errors ambiguously, CALL AGAIN WITH THE SAME KEY — that is the safe move, and it will either report the original post or publish it for the first time. It never posts twice."New value: +"SAFE RETRIES — any stable string: a repeat of the SAME publish within 24h returns the ORIGINAL post id instead of posting again (an identical publish is also auto-recognised for 10 minutes). On a timeout or an ambiguous error, CALL AGAIN WITH THE SAME KEY: it reports the original post or publishes it once. It never posts twice."
      • changedInput schema / properties / subject / description
        Previous value: -"WHAT THIS POST IS ABOUT — the product, feature, offer or theme (e.g. \"winter coat\", \"free trial\", \"founder story\"). The second grouping axis in post_performance. Same rule as hook: reuse the exact wording so posts about one subject land in one group."New value: +"WHAT THIS POST IS ABOUT — product, feature, offer or theme (e.g. \"winter coat\", \"free trial\"). post_performance's second grouping axis: reuse the exact wording, as with hook."
    • Changedpost_to_meta8 fields changed
      • addedInput schema / properties / audioId
        Added value: +{
        +  "description": "INSTAGRAM REEL — put one of Instagram’s OWN licensed music tracks under the Reel: the `id` search_instagram_audio returns. Instagram mixes it in when the Reel is published; nothing is downloaded or re-rendered. REELS ONLY, and only on an Instagram account connected through Meta (a Facebook Page with a linked Instagram), which is Instagram’s own rule — the Instagram connector cannot take it and is refused by name.",
        +  "type": "string"
        +}
      • addedInput schema / properties / audioVolume
        Added value: +{
        +  "description": "INSTAGRAM REEL — how loud that track plays, 0–100 (Instagram’s default 100). Needs audioId.",
        +  "maximum": 100,
        +  "minimum": 0,
        +  "type": "integer"
        +}
      • 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."
      • addedInput schema / properties / coverAtMs
        Added value: +{
        +  "description": "THE VIDEO COVER for every target of this post, as ONE frame: milliseconds from the start (7000 = the frame at 7s). Instagram gets it as thumb_offset, Facebook as its uploaded cover (a Reel’s preferred thumbnail). Instagram’s own thumbOffset, or a Hermoso-hosted coverUrl, also becomes the Facebook cover.",
        +  "type": "number"
        +}
      • changedInput schema / properties / hook / description
        Previous value: -"WHAT ANGLE THIS POST IS BUILT ON — the single most valuable field here, and the only moment it can ever be recorded. post_performance groups on it to answer \"which hooks work\", and it needs 5 posts sharing ONE hook before it will call anything a winner, so REUSE THE SAME WORDING across a campaign instead of rephrasing it every time. Best of all, pass a hook id from list_hooks (e.g. \"direct_callout\", \"mid_problem\", \"before_after\") — those fold onto a stable key however they are spelled, so a whole brand accumulates evidence on one row. Your own wording is fine too; it just only groups when you repeat it exactly. Omitting it means this post can never vote on which hook works."New value: +"WHAT ANGLE this post is built on, recorded only at publish time. post_performance ranks hooks on it (a winner needs 5 posts sharing ONE hook), so pass a list_hooks id (e.g. \"direct_callout\", \"before_after\") or reuse your own wording EXACTLY across a campaign. Omit it and this post never votes on which hook works."
      • changedInput schema / properties / idempotencyKey / description
        Previous value: -"SAFE RETRIES. Publishing can take minutes (a video upload, a carousel of ten slides) and a transport can time out while the post SUCCEEDS — retrying blind is how the same thing gets posted twice. Pass any stable string here and a repeat of the SAME publish returns the ORIGINAL post id instead of posting again (24h). You do not have to: an identical publish is auto-recognised for 10 minutes anyway. If a call times out or errors ambiguously, CALL AGAIN WITH THE SAME KEY — that is the safe move, and it will either report the original post or publish it for the first time. It never posts twice."New value: +"SAFE RETRIES — any stable string: a repeat of the SAME publish within 24h returns the ORIGINAL post id instead of posting again (an identical publish is also auto-recognised for 10 minutes). On a timeout or an ambiguous error, CALL AGAIN WITH THE SAME KEY: it reports the original post or publishes it once. It never posts twice."
      • changedInput schema / properties / subject / description
        Previous value: -"WHAT THIS POST IS ABOUT — the product, feature, offer or theme (e.g. \"winter coat\", \"free trial\", \"founder story\"). The second grouping axis in post_performance. Same rule as hook: reuse the exact wording so posts about one subject land in one group."New value: +"WHAT THIS POST IS ABOUT — product, feature, offer or theme (e.g. \"winter coat\", \"free trial\"). post_performance's second grouping axis: reuse the exact wording, as with hook."
      • addedInput schema / properties / videoVolume
        Added value: +{
        +  "description": "INSTAGRAM REEL — how loud the video’s own sound plays under the track, 0–100 (Instagram’s default 100; 0 = the track alone). Needs audioId.",
        +  "maximum": 100,
        +  "minimum": 0,
        +  "type": "integer"
        +}
    • Changedpost_to_pinterest5 fields changed
      • 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."
      • addedInput schema / properties / coverAtMs
        Added value: +{
        +  "description": "THE VIDEO COVER of a video Pin, as ONE frame: milliseconds from the start (7000 = the frame at 7s). That frame is cut and sent as the Pin cover image. coverImageUrl wins.",
        +  "type": "number"
        +}
      • changedInput schema / properties / hook / description
        Previous value: -"WHAT ANGLE THIS POST IS BUILT ON — the single most valuable field here, and the only moment it can ever be recorded. post_performance groups on it to answer \"which hooks work\", and it needs 5 posts sharing ONE hook before it will call anything a winner, so REUSE THE SAME WORDING across a campaign instead of rephrasing it every time. Best of all, pass a hook id from list_hooks (e.g. \"direct_callout\", \"mid_problem\", \"before_after\") — those fold onto a stable key however they are spelled, so a whole brand accumulates evidence on one row. Your own wording is fine too; it just only groups when you repeat it exactly. Omitting it means this post can never vote on which hook works."New value: +"WHAT ANGLE this post is built on, recorded only at publish time. post_performance ranks hooks on it (a winner needs 5 posts sharing ONE hook), so pass a list_hooks id (e.g. \"direct_callout\", \"before_after\") or reuse your own wording EXACTLY across a campaign. Omit it and this post never votes on which hook works."
      • changedInput schema / properties / idempotencyKey / description
        Previous value: -"SAFE RETRIES. Publishing can take minutes (a video upload, a carousel of ten slides) and a transport can time out while the post SUCCEEDS — retrying blind is how the same thing gets posted twice. Pass any stable string here and a repeat of the SAME publish returns the ORIGINAL post id instead of posting again (24h). You do not have to: an identical publish is auto-recognised for 10 minutes anyway. If a call times out or errors ambiguously, CALL AGAIN WITH THE SAME KEY — that is the safe move, and it will either report the original post or publish it for the first time. It never posts twice."New value: +"SAFE RETRIES — any stable string: a repeat of the SAME publish within 24h returns the ORIGINAL post id instead of posting again (an identical publish is also auto-recognised for 10 minutes). On a timeout or an ambiguous error, CALL AGAIN WITH THE SAME KEY: it reports the original post or publishes it once. It never posts twice."
      • changedInput schema / properties / subject / description
        Previous value: -"WHAT THIS POST IS ABOUT — the product, feature, offer or theme (e.g. \"winter coat\", \"free trial\", \"founder story\"). The second grouping axis in post_performance. Same rule as hook: reuse the exact wording so posts about one subject land in one group."New value: +"WHAT THIS POST IS ABOUT — product, feature, offer or theme (e.g. \"winter coat\", \"free trial\"). post_performance's second grouping axis: reuse the exact wording, as with hook."
    • Changedpost_to_telegram2 fields changed
      • 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."
      • addedInput schema / properties / coverAtMs
        Added value: +{
        +  "description": "THE VIDEO COVER in the chat, as ONE frame: milliseconds from the start (7000 = the frame at 7s). Sent as Telegram’s cover image; beats platformCover.",
        +  "type": "number"
        +}
    • Changedpost_to_tiktok4 fields changed
      • 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."
      • changedInput schema / properties / destination / description
        Previous value: -"\"post\" = live on the profile now (needs privacy + an explicit user yes); \"draft\" = to TikTok for the user to review and post themselves. Default \"draft\"."New value: +"\"post\" (default) = live on the profile now (needs the privacy the user chose + their explicit yes); \"draft\" = to their TikTok inbox for them to finish and publish in the app — only when they ask for a draft or want to add a TikTok sound, and only after telling them so."
      • changedInput schema / properties / hook / description
        Previous value: -"WHAT ANGLE THIS POST IS BUILT ON — the single most valuable field here, and the only moment it can ever be recorded. post_performance groups on it to answer \"which hooks work\", and it needs 5 posts sharing ONE hook before it will call anything a winner, so REUSE THE SAME WORDING across a campaign instead of rephrasing it every time. Best of all, pass a hook id from list_hooks (e.g. \"direct_callout\", \"mid_problem\", \"before_after\") — those fold onto a stable key however they are spelled, so a whole brand accumulates evidence on one row. Your own wording is fine too; it just only groups when you repeat it exactly. Omitting it means this post can never vote on which hook works."New value: +"WHAT ANGLE this post is built on, recorded only at publish time. post_performance ranks hooks on it (a winner needs 5 posts sharing ONE hook), so pass a list_hooks id (e.g. \"direct_callout\", \"before_after\") or reuse your own wording EXACTLY across a campaign. Omit it and this post never votes on which hook works."
      • changedInput schema / properties / subject / description
        Previous value: -"WHAT THIS POST IS ABOUT — the product, feature, offer or theme (e.g. \"winter coat\", \"free trial\", \"founder story\"). The second grouping axis in post_performance. Same rule as hook: reuse the exact wording so posts about one subject land in one group."New value: +"WHAT THIS POST IS ABOUT — product, feature, offer or theme (e.g. \"winter coat\", \"free trial\"). post_performance's second grouping axis: reuse the exact wording, as with hook."
    • Changedpost_to_x3 fields changed
      • 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."
      • changedInput schema / properties / hook / description
        Previous value: -"WHAT ANGLE THIS POST IS BUILT ON — the single most valuable field here, and the only moment it can ever be recorded. post_performance groups on it to answer \"which hooks work\", and it needs 5 posts sharing ONE hook before it will call anything a winner, so REUSE THE SAME WORDING across a campaign instead of rephrasing it every time. Best of all, pass a hook id from list_hooks (e.g. \"direct_callout\", \"mid_problem\", \"before_after\") — those fold onto a stable key however they are spelled, so a whole brand accumulates evidence on one row. Your own wording is fine too; it just only groups when you repeat it exactly. Omitting it means this post can never vote on which hook works."New value: +"WHAT ANGLE this post is built on, recorded only at publish time. post_performance ranks hooks on it (a winner needs 5 posts sharing ONE hook), so pass a list_hooks id (e.g. \"direct_callout\", \"before_after\") or reuse your own wording EXACTLY across a campaign. Omit it and this post never votes on which hook works."
      • changedInput schema / properties / subject / description
        Previous value: -"WHAT THIS POST IS ABOUT — the product, feature, offer or theme (e.g. \"winter coat\", \"free trial\", \"founder story\"). The second grouping axis in post_performance. Same rule as hook: reuse the exact wording so posts about one subject land in one group."New value: +"WHAT THIS POST IS ABOUT — product, feature, offer or theme (e.g. \"winter coat\", \"free trial\"). post_performance's second grouping axis: reuse the exact wording, as with hook."
    • Changedpost_to_youtube5 fields changed
      • 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."
      • addedInput schema / properties / coverAtMs
        Added value: +{
        +  "description": "THE VIDEO COVER (the custom thumbnail), as ONE frame: milliseconds from the start (7000 = the frame at 7s). That frame is cut from the upload and set with thumbnails.set. thumbnailUrl wins; this beats platformCover.",
        +  "type": "number"
        +}
      • changedInput schema / properties / hook / description
        Previous value: -"WHAT ANGLE THIS POST IS BUILT ON — the single most valuable field here, and the only moment it can ever be recorded. post_performance groups on it to answer \"which hooks work\", and it needs 5 posts sharing ONE hook before it will call anything a winner, so REUSE THE SAME WORDING across a campaign instead of rephrasing it every time. Best of all, pass a hook id from list_hooks (e.g. \"direct_callout\", \"mid_problem\", \"before_after\") — those fold onto a stable key however they are spelled, so a whole brand accumulates evidence on one row. Your own wording is fine too; it just only groups when you repeat it exactly. Omitting it means this post can never vote on which hook works."New value: +"WHAT ANGLE this post is built on, recorded only at publish time. post_performance ranks hooks on it (a winner needs 5 posts sharing ONE hook), so pass a list_hooks id (e.g. \"direct_callout\", \"before_after\") or reuse your own wording EXACTLY across a campaign. Omit it and this post never votes on which hook works."
      • changedInput schema / properties / privacy / description
        Previous value: -"default unlisted (anyone-with-link, ad-ready); public = live + searchable on the channel (confirm first); private = eyes-only (cannot run as an ad)"New value: +"default public (live + searchable on the channel); unlisted = link-only (the ad-ready setting, or to add YouTube music in Studio first — only when the user asks); private = eyes-only (cannot run as an ad)"
      • changedInput schema / properties / subject / description
        Previous value: -"WHAT THIS POST IS ABOUT — the product, feature, offer or theme (e.g. \"winter coat\", \"free trial\", \"founder story\"). The second grouping axis in post_performance. Same rule as hook: reuse the exact wording so posts about one subject land in one group."New value: +"WHAT THIS POST IS ABOUT — product, feature, offer or theme (e.g. \"winter coat\", \"free trial\"). post_performance's second grouping axis: reuse the exact wording, as with hook."
    • Changedpost_x_article3 fields changed
      • 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."
      • changedInput schema / properties / hook / description
        Previous value: -"WHAT ANGLE THIS POST IS BUILT ON — the single most valuable field here, and the only moment it can ever be recorded. post_performance groups on it to answer \"which hooks work\", and it needs 5 posts sharing ONE hook before it will call anything a winner, so REUSE THE SAME WORDING across a campaign instead of rephrasing it every time. Best of all, pass a hook id from list_hooks (e.g. \"direct_callout\", \"mid_problem\", \"before_after\") — those fold onto a stable key however they are spelled, so a whole brand accumulates evidence on one row. Your own wording is fine too; it just only groups when you repeat it exactly. Omitting it means this post can never vote on which hook works."New value: +"WHAT ANGLE this post is built on, recorded only at publish time. post_performance ranks hooks on it (a winner needs 5 posts sharing ONE hook), so pass a list_hooks id (e.g. \"direct_callout\", \"before_after\") or reuse your own wording EXACTLY across a campaign. Omit it and this post never votes on which hook works."
      • changedInput schema / properties / subject / description
        Previous value: -"WHAT THIS POST IS ABOUT — the product, feature, offer or theme (e.g. \"winter coat\", \"free trial\", \"founder story\"). The second grouping axis in post_performance. Same rule as hook: reuse the exact wording so posts about one subject land in one group."New value: +"WHAT THIS POST IS ABOUT — product, feature, offer or theme (e.g. \"winter coat\", \"free trial\"). post_performance's second grouping axis: reuse the exact wording, as with hook."
    • Changedpull_competitor_ads7 fields changed
      • addedInput schema / properties / brand
        Added value: +{
        +  "description": "same as companyName",
        +  "type": "string"
        +}
      • addedInput schema / properties / company
        Added value: +{
        +  "description": "same as companyName",
        +  "type": "string"
        +}
      • changedInput schema / properties / companyName / description
        Previous value: -"the advertiser name"New value: +"the advertiser name, e.g. \"Liquid Death\" (company / brand / name are read as this too)"
      • changedInput schema / properties / domain / description
        Previous value: -"the advertiser domain"New value: +"the advertiser domain, e.g. liquiddeath.com — a full website URL works (url / website are read as this too). Pass companyName OR domain"
      • addedInput schema / properties / name
        Added value: +{
        +  "description": "same as companyName",
        +  "type": "string"
        +}
      • addedInput schema / properties / url
        Added value: +{
        +  "description": "same as domain",
        +  "type": "string"
        +}
      • addedInput schema / properties / website
        Added value: +{
        +  "description": "same as domain",
        +  "type": "string"
        +}
    • Changedrender_ad1 field changed
      • changedInput schema / properties / dryRun / description
        Previous value: -"return the routing decision (single pass vs stitched acts, resolved model + act lengths) WITHOUT submitting a render — free, nothing charged"New value: +"return the routing decision (single pass vs stitched acts, resolved model + act lengths) and the exact credits the real render reserves, WITHOUT submitting a render — free, nothing charged"
    • Changedreschedule_post6 fields changed
      • addedInput schema / properties / audioId
        Added value: +{
        +  "description": "INSTAGRAM REEL — put one of Instagram’s OWN licensed music tracks under the Reel: the `id` search_instagram_audio returns. Instagram mixes it in when the Reel is published; nothing is downloaded or re-rendered. REELS ONLY; an empty string removes it, and only on an Instagram account connected through Meta (a Facebook Page with a linked Instagram), which is Instagram’s own rule — the Instagram connector cannot take it and is refused by name.",
        +  "type": "string"
        +}
      • addedInput schema / properties / audioVolume
        Added value: +{
        +  "description": "INSTAGRAM REEL — how loud that track plays, 0–100 (Instagram’s default 100). Needs audioId.",
        +  "maximum": 100,
        +  "minimum": 0,
        +  "type": "integer"
        +}
      • changedInput schema / properties / brand / description
        Previous value: -"WHICH BRAND this post is in — the id or exact name from list_brands. Needed when the post lives in a brand this connection is not pinned to: a post you can CREATE in a brand must be manageable there too, without switching the whole connection. A name that matches no brand, or two, is REFUSED."New value: +"WHICH BRAND this post is in — id or exact name from list_brands; needed when it is not the brand this connection is pinned to. A name that matches no brand, or two, is REFUSED."
      • addedInput schema / properties / coverAtMs
        Added value: +{
        +  "description": "THE VIDEO COVER on every channel, as one frame in milliseconds (see schedule_post).",
        +  "type": "number"
        +}
      • addedInput schema / properties / coverImageUrl
        Added value: +{
        +  "description": "THE VIDEO COVER on every channel, as a Hermoso-hosted picture (see schedule_post).",
        +  "type": "string"
        +}
      • addedInput schema / properties / videoVolume
        Added value: +{
        +  "description": "INSTAGRAM REEL — how loud the video’s own sound plays under the track, 0–100 (Instagram’s default 100; 0 = the track alone). Needs audioId.",
        +  "maximum": 100,
        +  "minimum": 0,
        +  "type": "integer"
        +}
    • Changedretry_scheduled1 field changed
      • changedInput schema / properties / brand / description
        Previous value: -"WHICH BRAND this post is in — the id or exact name from list_brands. Needed when the post lives in a brand this connection is not pinned to: a post you can CREATE in a brand must be manageable there too, without switching the whole connection. A name that matches no brand, or two, is REFUSED."New value: +"WHICH BRAND this post is in — id or exact name from list_brands; needed when it is not the brand this connection is pinned to. A name that matches no brand, or two, is REFUSED."
    • Changedschedule_post8 fields changed
      • addedInput schema / properties / audioId
        Added value: +{
        +  "description": "INSTAGRAM REEL — put one of Instagram’s OWN licensed music tracks under the Reel: the `id` search_instagram_audio returns. Instagram mixes it in when the Reel is published; nothing is downloaded or re-rendered. REELS ONLY, and only on an Instagram account connected through Meta (a Facebook Page with a linked Instagram), which is Instagram’s own rule — the Instagram connector cannot take it and is refused by name.",
        +  "type": "string"
        +}
      • addedInput schema / properties / audioVolume
        Added value: +{
        +  "description": "INSTAGRAM REEL — how loud that track plays, 0–100 (Instagram’s default 100). Needs audioId.",
        +  "maximum": 100,
        +  "minimum": 0,
        +  "type": "integer"
        +}
      • 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."
      • addedInput schema / properties / coverAtMs
        Added value: +{
        +  "description": "THE VIDEO COVER on EVERY channel of this post, as ONE frame: milliseconds from the start (7000 = the frame at 7s). Instagram, TikTok, Facebook, LinkedIn Page, Pinterest, Telegram and YouTube all get that frame; X, Threads and Bluesky have no cover setting. A channel’s own field (thumbOffset, coverTimestampMs, coverUrl) wins there and otherwise counts as this.",
        +  "type": "number"
        +}
      • addedInput schema / properties / coverImageUrl
        Added value: +{
        +  "description": "THE VIDEO COVER as a picture instead of a frame — a Hermoso-hosted image (upload_file). Every channel that takes a cover image gets it (Instagram, Facebook, LinkedIn Page, Pinterest, Telegram, YouTube); TikTok takes only a frame. Never together with coverAtMs.",
        +  "type": "string"
        +}
      • changedInput schema / properties / hook / description
        Previous value: -"WHAT ANGLE THIS POST IS BUILT ON — the single most valuable field here, and the only moment it can ever be recorded. post_performance groups on it to answer \"which hooks work\", and it needs 5 posts sharing ONE hook before it will call anything a winner, so REUSE THE SAME WORDING across a campaign instead of rephrasing it every time. Best of all, pass a hook id from list_hooks (e.g. \"direct_callout\", \"mid_problem\", \"before_after\") — those fold onto a stable key however they are spelled, so a whole brand accumulates evidence on one row. Your own wording is fine too; it just only groups when you repeat it exactly. Omitting it means this post can never vote on which hook works."New value: +"WHAT ANGLE this post is built on, recorded only at publish time. post_performance ranks hooks on it (a winner needs 5 posts sharing ONE hook), so pass a list_hooks id (e.g. \"direct_callout\", \"before_after\") or reuse your own wording EXACTLY across a campaign. Omit it and this post never votes on which hook works."
      • changedInput schema / properties / subject / description
        Previous value: -"WHAT THIS POST IS ABOUT — the product, feature, offer or theme (e.g. \"winter coat\", \"free trial\", \"founder story\"). The second grouping axis in post_performance. Same rule as hook: reuse the exact wording so posts about one subject land in one group."New value: +"WHAT THIS POST IS ABOUT — product, feature, offer or theme (e.g. \"winter coat\", \"free trial\"). post_performance's second grouping axis: reuse the exact wording, as with hook."
      • addedInput schema / properties / videoVolume
        Added value: +{
        +  "description": "INSTAGRAM REEL — how loud the video’s own sound plays under the track, 0–100 (Instagram’s default 100; 0 = the track alone). Needs audioId.",
        +  "maximum": 100,
        +  "minimum": 0,
        +  "type": "integer"
        +}
  7. 20 tool updatesv0.1.285
    • Changedadd_subtitles5 fields changed
      • addedInput schema / properties / auto
        Added value: +{
        +  "enum": [
        +    "sentences",
        +    "words"
        +  ],
        +  "type": "string"
        +}
      • addedInput schema / properties / cues
        Added value: +{
        +  "description": "your own lines in seconds, no overlap, max 90 chars, no emoji",
        +  "items": {
        +    "properties": {
        +      "end": {
        +        "type": "number"
        +      },
        +      "start": {
        +        "type": "number"
        +      },
        +      "text": {
        +        "type": "string"
        +      }
        +    },
        +    "required": [
        +      "text",
        +      "start",
        +      "end"
        +    ],
        +    "type": "object"
        +  },
        +  "maxItems": 400,
        +  "type": "array"
        +}
      • changedInput schema / properties / textStyle / anyOf
        Previous value: -[
        -  {
        -    "enum": [
        -      "pill",
        -      "editorial",
        -      "bold",
        -      "minimal",
        -      "handwritten",
        -      "boxed"
        -    ],
        -    "type": "string"
        -  },
        -  {
        -    "properties": {
        -      "background": {
        -        "description": "none | pill | #hex box",
        -        "type": "string"
        -      },
        -      "color": {
        -        "description": "#hex",
        -        "type": "string"
        -      },
        -      "font": {
        -        "enum": [
        -          "sans",
        -          "serif",
        -          "elegant",
        -          "condensed",
        -          "hand"
        -        ],
        -        "type": "string"
        -      },
        -      "italic": {
        -        "type": "boolean"
        -      },
        -      "outline": {
        -        "type": "boolean"
        -      },
        -      "position": {
        -        "enum": [
        -          "top",
        -          "center",
        -          "lower",
        -          "bottom"
        -        ],
        -        "type": "string"
        -      },
        -      "preset": {
        -        "enum": [
        -          "pill",
        -          "editorial",
        -          "bold",
        -          "minimal",
        -          "handwritten",
        -          "boxed"
        -        ],
        -        "type": "string"
        -      },
        -      "shadow": {
        -        "type": "boolean"
        -      },
        -      "size": {
        -        "anyOf": [
        -          {
        -            "enum": [
        -              "s",
        -              "m",
        -              "l",
        -              "xl"
        -            ],
        -            "type": "string"
        -          },
        -          {
        -            "type": "number"
        -          }
        -        ]
        -      },
        -      "textCase": {
        -        "enum": [
        -          "as-is",
        -          "upper",
        -          "lower",
        -          "title"
        -        ],
        -        "type": "string"
        -      },
        -      "tilt": {
        -        "description": "degrees, ±12",
        -        "type": "number"
        -      },
        -      "weight": {
        -        "type": "number"
        -      }
        -    },
        -    "type": "object"
        -  }
        -]New value: +[
        +  {
        +    "type": "string"
        +  },
        +  {
        +    "properties": {
        +      "background": {
        +        "description": "none|pill|a colour",
        +        "type": "string"
        +      },
        +      "cardColor": {
        +        "type": "string"
        +      },
        +      "color": {
        +        "description": "any CSS colour",
        +        "type": "string"
        +      },
        +      "describe": {
        +        "description": "the look in words",
        +        "type": "string"
        +      },
        +      "font": {
        +        "description": "sans|serif|elegant|condensed|hand or any Google Fonts family",
        +        "type": "string"
        +      },
        +      "italic": {
        +        "type": "boolean"
        +      },
        +      "outline": {
        +        "type": "boolean"
        +      },
        +      "outlineColor": {
        +        "type": "string"
        +      },
        +      "position": {
        +        "anyOf": [
        +          {
        +            "type": "string"
        +          },
        +          {
        +            "type": "number"
        +          }
        +        ],
        +        "description": "top|center|lower|bottom or 0.05-0.95 from the top"
        +      },
        +      "preset": {
        +        "type": "string"
        +      },
        +      "shadow": {
        +        "type": "boolean"
        +      },
        +      "size": {
        +        "anyOf": [
        +          {
        +            "type": "string"
        +          },
        +          {
        +            "type": "number"
        +          }
        +        ],
        +        "description": "s|m|l|xl, '120px', or a 0.015-0.15 frame fraction"
        +      },
        +      "subFont": {
        +        "type": "string"
        +      },
        +      "subItalic": {
        +        "type": "boolean"
        +      },
        +      "textCase": {
        +        "enum": [
        +          "as-is",
        +          "upper",
        +          "lower",
        +          "title"
        +        ],
        +        "type": "string"
        +      },
        +      "tilt": {
        +        "description": "degrees, ±45",
        +        "type": "number"
        +      },
        +      "weight": {
        +        "type": "number"
        +      }
        +    },
        +    "type": "object"
        +  }
        +]
      • changedInput schema / properties / textStyle / description
        Previous value: -"the look: a preset or overrides; omit for the default"New value: +"the look: words, a preset or fields; omit for the default"
      • addedInput schema / properties / wordsPerCue
        Added value: +{
        +  "type": "number"
        +}
    • Changedanalyze_video3 fields changed
      • addedInput schema / properties / anchors
        Added value: +{
        +  "description": "{afterWord|beforeWord|atWord, occurrence?, offset?} -> s",
        +  "items": {},
        +  "type": "array"
        +}
      • addedInput schema / properties / frames
        Added value: +{
        +  "description": "also return the frames as images",
        +  "type": "boolean"
        +}
      • addedInput schema / properties / words
        Added value: +{
        +  "description": "true | 'only': each spoken word's start/end"
        +}
    • Changedclip_video2 fields changed
      • changedInput schema / properties / aspectRatio / description
        Previous value: -"clip shape — '9:16' (default) vertical for Reels/Shorts/TikTok; 'keep' leaves the source framing untouched"New value: +"clip shape: '9:16' (default), '1:1', '16:9', any 'W:H', or 'keep' for the source framing"
      • removedInput schema / properties / aspectRatio / enum
        Removed value: -[
        -  "9:16",
        -  "1:1",
        -  "16:9",
        -  "keep"
        -]
    • Changedclone_static1 field changed
      • changedInput schema / properties / imageUrl / description
        Previous value: -"the URL of the static ad image to clone"New value: +"the URL of the static ad image to clone; a real person in it confirms their likeness consent"
    • Addedfind_sound
    • Changedgenerate_avatar1 field changed
      • changedInput schema / properties / image / description
        Previous value: -"local path or URL of the presenter portrait"New value: +"local path or URL of the presenter portrait; a real person’s photo confirms their likeness consent"
    • Changedgenerate_image1 field changed
      • changedInput schema / properties / refImages / description
        Previous value: -"local file paths or URLs of product/logo references to composite in. ON THE POSE MODELS — any row hermoso_capabilities marks `needsRefs` with a `refsMax`, such as putting your product in someone’s hands or a virtual try-on — THE ORDER IS THE CONTRACT AND IT IS NOT A COMPOSITE: refImages[0] is the PERSON photo, and the rest (up to `refsMax` minus one) are the product or garment photos. Reversed, you get the product wearing the person. A 4th product is dropped and the reply says so."New value: +"local file paths or URLs of product/logo references to composite in. ON THE POSE MODELS — any row hermoso_capabilities marks `needsRefs` with a `refsMax`, such as putting your product in someone’s hands or a virtual try-on — THE ORDER IS THE CONTRACT AND IT IS NOT A COMPOSITE: refImages[0] is the PERSON photo, and the rest (up to `refsMax` minus one) are the product or garment photos. Reversed, you get the product wearing the person. A 4th product is dropped and the reply says so. A real person’s photo confirms their likeness consent."
    • Addedgenerate_music
    • Changedgenerate_video1 field changed
      • changedInput schema / properties / refImage / description
        Previous value: -"local path or URL to anchor the first frame"New value: +"local path or URL to anchor the first frame; a real person’s photo here or in refImages confirms their likeness consent"
    • Changedhook_variants1 field changed
      • addedInput schema / properties / hooks
        Added value: +{
        +  "description": "your own openings, one version each",
        +  "items": {
        +    "properties": {
        +      "cutAt": {
        +        "description": "url: override the found payoff cut",
        +        "type": "number"
        +      },
        +      "mechanic": {
        +        "type": "string"
        +      },
        +      "prompt": {
        +        "type": "string"
        +      },
        +      "start": {
        +        "description": "url: skip the video’s own first seconds",
        +        "type": "number"
        +      },
        +      "url": {
        +        "type": "string"
        +      }
        +    },
        +    "type": "object"
        +  },
        +  "type": "array"
        +}
    • Changedmake_explainer3 fields changed
      • changedInput schema / properties / music / description
        Previous value: -"music bed under the narration, measured to sit about 14 dB under the voice and sidechain-ducked beneath it. Omit and the KIDS and FAIRYTALE channels get their recommended bed COMPOSED for this film — those two are the only channels a bed is due on unasked, and it costs a small flat fee; every other channel ships dry. 'off' forces silence. 'library' takes a free curated track only, and ships dry when none is on file. NAME A MOOD — upbeat / calm / warm / epic / tense / playful / elegant / hype / chill / dramatic — to compose one on ANY channel, at the same fee. hermoso_capabilities reports the exact figure as explainerMusicCredits; quote it before you turn a bed on or pick a mood."New value: +"music bed under the narration, measured to sit about 14 dB under the voice and sidechain-ducked beneath it. Omit and the KIDS and FAIRYTALE channels get their recommended bed COMPOSED for this film — those two are the only channels a bed is due on unasked, and it costs a small flat fee; every other channel ships dry. 'off' forces silence. 'library' takes a free curated track only, and ships dry when none is on file. NAME A MOOD (upbeat / calm / warm / epic / tense / playful / elegant / hype / chill / dramatic) or DESCRIBE it in words to compose one on ANY channel, at the same fee. hermoso_capabilities reports the exact figure as explainerMusicCredits; quote it before you turn a bed on or pick a mood."
      • changedInput schema / properties / style / description
        Previous value: -"visual style. 'cinematic' (default) is photoreal; the rest are non-photoreal styled looks — editorial_collage (halftone cutouts + marker accents), flat_vector, stickman, whiteboard, ink_marker, silhouette, storybook (gouache), paper_diorama, isometric, claymation, pixel_art, watercolor, fluffy_toy (felted plush), low_poly, stylized_3d (matte clay render), studio_3d (preschool toy 3D on a white sweep — the Kids default), mannequin (clay-render reenactment figures — a History alternate). Ask the user which they want rather than picking silently; a styled pick costs more (see the cost note)."New value: +"visual style: 'cinematic' (default, photoreal); styled shortcuts editorial_collage, flat_vector, stickman, whiteboard, ink_marker, silhouette, storybook, paper_diorama, isometric, claymation, pixel_art, watercolor, fluffy_toy, low_poly, stylized_3d, studio_3d (the Kids default), mannequin; or ANY look described in words ('80s anime cel animation'), locked across every frame. Ask rather than pick silently; a styled look costs more."
      • removedInput schema / properties / style / enum
        Removed value: -[
        -  "cinematic",
        -  "editorial_collage",
        -  "flat_vector",
        -  "stickman",
        -  "whiteboard",
        -  "ink_marker",
        -  "silhouette",
        -  "storybook",
        -  "paper_diorama",
        -  "isometric",
        -  "claymation",
        -  "pixel_art",
        -  "watercolor",
        -  "fluffy_toy",
        -  "low_poly",
        -  "stylized_3d",
        -  "studio_3d",
        -  "mannequin"
        -]
    • Addedmake_insert
    • Changedmake_template_ad1 field changed
      • changedInput schema / properties / config / description
        Previous value: -"the template config — MUST include config.template (one of the template ids above) plus that template's fields"New value: +"MUST include config.template: 'custom' or a preset id above, plus its fields"
    • Changedmake_thumbnail7 fields changed
      • changedInput schema / properties / font / description
        Previous value: -"headline font (default Anton). Alternatives incl. Bebas Neue, Oswald, Archivo Black, Montserrat, Inter, Playfair Display"New value: +"headline font: Anton (default) or any Google Fonts family"
      • changedInput schema / properties / framework / description
        Previous value: -"concept framework id (default 'posed_portrait'); see the list in this description / hermoso_capabilities"New value: +"concept framework id (default 'posed_portrait'), or your own concept in words"
      • changedInput schema / properties / headlinePlace / description
        Previous value: -"where the headline sits — never over the face (default 'bottom')"New value: +"bottom (default), top, center, or a 0-1 fraction from the top; never over the face"
      • removedInput schema / properties / headlinePlace / enum
        Removed value: -[
        -  "bottom",
        -  "top",
        -  "center"
        -]
      • changedInput schema / properties / overlayStyle / description
        Previous value: -"headline style: 'beast' (default, white + heavy black stroke) / 'fire' / 'neon-lime' / 'clean-glass' / 'marker'"New value: +"headline style: beast (default), fire, neon-lime, clean-glass, marker, or your own CSS declarations"
      • changedInput schema / properties / tweak / description
        Previous value: -"surgical pixel-faithful edit of a FINISHED thumbnail — needs sourceImage"New value: +"surgical pixel-faithful edit of a FINISHED thumbnail (needs sourceImage): kind emotion / background / background_color / rim_light, or any other kind with the edit in words as value"
      • removedInput schema / properties / tweak / properties / kind / enum
        Removed value: -[
        -  "emotion",
        -  "background",
        -  "background_color",
        -  "rim_light"
        -]
    • Changedmultiply_ad2 fields changed
      • changedInput schema / properties / axes / description
        Previous value: -"which axes to vary (default: all four)"New value: +"what to vary: character, outfit, location, objects (default all four), or your own, e.g. \"season\""
      • removedInput schema / properties / axes / items / enum
        Removed value: -[
        -  "character",
        -  "outfit",
        -  "location",
        -  "objects"
        -]
    • Changedpost_edit2 fields changed
      • addedInput schema / properties / ops / items / properties / bridge
        Added value: +{
        +  "description": "join: connects the hook to the first clip",
        +  "properties": {
        +    "cutAt": {
        +      "description": "omit: found from the footage",
        +      "type": "number"
        +    },
        +    "flash": {
        +      "type": "boolean"
        +    },
        +    "kind": {
        +      "enum": [
        +        "impact",
        +        "text"
        +      ],
        +      "type": "string"
        +    },
        +    "matchCut": {
        +      "type": "number"
        +    },
        +    "shake": {
        +      "type": "boolean"
        +    },
        +    "sound": {
        +      "enum": [
        +        "auto",
        +        "own",
        +        "impact",
        +        "whoosh",
        +        "none"
        +      ],
        +      "type": "string"
        +    },
        +    "text": {
        +      "type": "string"
        +    },
        +    "then": {
        +      "type": "string"
        +    }
        +  },
        +  "required": [
        +    "kind"
        +  ],
        +  "type": "object"
        +}
      • addedInput schema / properties / ops / items / properties / textStyle
        Added value: +{
        +  "anyOf": [
        +    {
        +      "type": "string"
        +    },
        +    {
        +      "additionalProperties": {},
        +      "properties": {},
        +      "type": "object"
        +    }
        +  ]
        +}
    • Changedrecast_motion1 field changed
      • changedInput schema / properties / image / description
        Previous value: -"the actor/character image URL (who should appear)"New value: +"the actor/character image URL (who should appear); a real person’s photo confirms their likeness consent"
    • Changedremix_static1 field changed
      • changedInput schema / properties / imageUrl / description
        Previous value: -"the URL of the static ad image to clone"New value: +"the URL of the static ad image to clone; a real person in it confirms their likeness consent"
    • Changedrender_ad6 fields changed
      • changedInput schema / properties / creator / description
        Previous value: -"CAST A SAVED CREATOR in this ad — their id from list_creators, or the name you know them by (“Sarah”). Their saved portrait becomes the on-camera identity for the whole spot, so the same face carries across every act and across every ad you render for this brand — and because we already have their picture, the character portrait this pipeline would otherwise generate is skipped, so casting somebody costs LESS than not casting them. Omit to let the ad cast a fresh person — EXCEPT for a CREATOR account (onboarded from their own @handle): their own saved likeness is cast by default on any plan with a person on camera, and the read-back says `default:true`; pass \"none\" to render without them. Refused for free, with nothing rendered, if the name matches nobody or more than one creator, if an explicitly named creator is cast on a plan with nobody on camera, or if they are a REAL person with no likeness consent on file."New value: +"CAST A SAVED CREATOR in this ad — their id from list_creators, or the name you know them by (“Sarah”). Their saved portrait becomes the on-camera identity for the whole spot, so the same face carries across every act and across every ad you render for this brand — and because we already have their picture, the character portrait this pipeline would otherwise generate is skipped, so casting somebody costs LESS than not casting them. Omit to let the ad cast a fresh person — EXCEPT for a CREATOR account (onboarded from their own @handle): their own saved likeness is cast by default on any plan with a person on camera, and the read-back says `default:true`; pass \"none\" to render without them. Refused for free, with nothing rendered, if the name matches nobody or more than one creator, or if an explicitly named creator is cast on a plan with nobody on camera. Casting a REAL person confirms their likeness consent."
      • addedInput schema / properties / music / anyOf
        Added value: +[
        +  {
        +    "type": "boolean"
        +  },
        +  {
        +    "type": "string"
        +  }
        +]
      • changedInput schema / properties / music / description
        Previous value: -"licensed music bed on/off (default on)"New value: +"music bed: on (default), false, or the bed described in words ('lo-fi jazz, brushed drums')"
      • removedInput schema / properties / music / type
        Removed value: -"boolean"
      • changedInput schema / properties / textStyle / anyOf
        Previous value: -[
        -  {
        -    "enum": [
        -      "pill",
        -      "editorial",
        -      "bold",
        -      "minimal",
        -      "handwritten",
        -      "boxed"
        -    ],
        -    "type": "string"
        -  },
        -  {
        -    "properties": {
        -      "background": {
        -        "description": "\"none\", \"pill\", or a #hex box",
        -        "type": "string"
        -      },
        -      "cardColor": {
        -        "description": "#hex end card background",
        -        "type": "string"
        -      },
        -      "color": {
        -        "description": "#hex",
        -        "type": "string"
        -      },
        -      "font": {
        -        "enum": [
        -          "sans",
        -          "serif",
        -          "elegant",
        -          "condensed",
        -          "hand"
        -        ],
        -        "type": "string"
        -      },
        -      "italic": {
        -        "type": "boolean"
        -      },
        -      "outline": {
        -        "type": "boolean"
        -      },
        -      "position": {
        -        "enum": [
        -          "top",
        -          "center",
        -          "lower",
        -          "bottom"
        -        ],
        -        "type": "string"
        -      },
        -      "preset": {
        -        "enum": [
        -          "pill",
        -          "editorial",
        -          "bold",
        -          "minimal",
        -          "handwritten",
        -          "boxed"
        -        ],
        -        "type": "string"
        -      },
        -      "shadow": {
        -        "type": "boolean"
        -      },
        -      "size": {
        -        "anyOf": [
        -          {
        -            "enum": [
        -              "s",
        -              "m",
        -              "l",
        -              "xl"
        -            ],
        -            "type": "string"
        -          },
        -          {
        -            "type": "number"
        -          }
        -        ]
        -      },
        -      "subFont": {
        -        "enum": [
        -          "sans",
        -          "serif",
        -          "elegant",
        -          "condensed",
        -          "hand"
        -        ],
        -        "type": "string"
        -      },
        -      "subItalic": {
        -        "type": "boolean"
        -      },
        -      "textCase": {
        -        "enum": [
        -          "as-is",
        -          "upper",
        -          "lower",
        -          "title"
        -        ],
        -        "type": "string"
        -      },
        -      "tilt": {
        -        "description": "degrees, ±12",
        -        "type": "number"
        -      },
        -      "weight": {
        -        "type": "number"
        -      }
        -    },
        -    "type": "object"
        -  }
        -]New value: +[
        +  {
        +    "type": "string"
        +  },
        +  {
        +    "properties": {
        +      "background": {
        +        "description": "none|pill|a colour",
        +        "type": "string"
        +      },
        +      "cardColor": {
        +        "type": "string"
        +      },
        +      "color": {
        +        "description": "any CSS colour",
        +        "type": "string"
        +      },
        +      "describe": {
        +        "description": "the look in words",
        +        "type": "string"
        +      },
        +      "font": {
        +        "description": "sans|serif|elegant|condensed|hand or any Google Fonts family",
        +        "type": "string"
        +      },
        +      "italic": {
        +        "type": "boolean"
        +      },
        +      "outline": {
        +        "type": "boolean"
        +      },
        +      "outlineColor": {
        +        "type": "string"
        +      },
        +      "position": {
        +        "anyOf": [
        +          {
        +            "type": "string"
        +          },
        +          {
        +            "type": "number"
        +          }
        +        ],
        +        "description": "top|center|lower|bottom or 0.05-0.95 from the top"
        +      },
        +      "preset": {
        +        "type": "string"
        +      },
        +      "shadow": {
        +        "type": "boolean"
        +      },
        +      "size": {
        +        "anyOf": [
        +          {
        +            "type": "string"
        +          },
        +          {
        +            "type": "number"
        +          }
        +        ],
        +        "description": "s|m|l|xl, '120px', or a 0.015-0.15 frame fraction"
        +      },
        +      "subFont": {
        +        "type": "string"
        +      },
        +      "subItalic": {
        +        "type": "boolean"
        +      },
        +      "textCase": {
        +        "enum": [
        +          "as-is",
        +          "upper",
        +          "lower",
        +          "title"
        +        ],
        +        "type": "string"
        +      },
        +      "tilt": {
        +        "description": "degrees, ±45",
        +        "type": "number"
        +      },
        +      "weight": {
        +        "type": "number"
        +      }
        +    },
        +    "type": "object"
        +  }
        +]
      • changedInput schema / properties / textStyle / description
        Previous value: -"THE LOOK of captions and the end card — only meaningful with captions:true or endCard:true, and only when the user described a look. Presets: editorial (a large elegant serif title mid-frame with a small italic line under it, no box), bold (tall condensed caps with a black outline), minimal (small lowercase near the bottom), handwritten (tilted marker), boxed (dark words on a white box), pill (the plain default). Pass a preset name, or an object with a preset plus overrides. A caption written \"TITLE · small line\" puts the part after the middle dot on a second line. An invalid field is refused by name before anything renders."New value: +"THE LOOK of captions and the end card, only with captions or endCard and only when the user described one: the look in WORDS (\"chunky yellow comic letters, purple outline\"), a preset (editorial: big serif title + small italic line; bold: condensed caps, outline; minimal; handwritten; boxed; pill, the default), or fields. \"TITLE · small line\" puts the part after the dot on a second line."
    • Changedsave_creator1 field changed
      • removedInput schema / properties / consented
        Removed value: -{
        -  "description": "REAL people only: the user has confirmed that person consented to their likeness being used in ads",
        -  "type": "boolean"
        -}
  8. 18 tool updatesv0.1.281
    • Changedduplicate_scheduled2 fields changed
      • addedInput schema / properties / ideaId
        Added value: +{
        +  "description": "short id of the content-plan idea this post came from",
        +  "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"
        +}
    • Changedgenerate_image1 field changed
      • changedInput schema / properties / aspectRatio / description
        Previous value: -"e.g. '1:1', '9:16', '16:9'"New value: +"e.g. '1:1', '9:16', '16:9', '4:5'. Each model draws its own list (hermoso_capabilities prints it per model, e.g. Nano Banana 2 goes to 1:8 and 8:1); a ratio the chosen model cannot draw is refused before anything is charged"
    • Changedlist_published_posts1 field changed
      • addedInput schema / properties / cursor
        Added value: +{
        +  "description": "nextCursor from a previous reply: the next, older page",
        +  "type": "string"
        +}
    • Changedpost_edit11 fields changed
      • addedInput schema / properties / ops / items / properties / clips
        Added value: +{
        +  "description": "join: the clips after this video",
        +  "items": {
        +    "properties": {
        +      "end": {
        +        "type": "number"
        +      },
        +      "start": {
        +        "type": "number"
        +      },
        +      "url": {
        +        "type": "string"
        +      }
        +    },
        +    "required": [
        +      "url"
        +    ],
        +    "type": "object"
        +  },
        +  "type": "array"
        +}
      • changedInput schema / properties / ops / items / properties / end / description
        Previous value: -"trim/mute window end (s)"New value: +"trim/mute/text window end (s)"
      • changedInput schema / properties / ops / items / properties / op / enum
        Previous value: -[
        -  "trim",
        -  "speed",
        -  "mute",
        -  "audio_gain",
        -  "fade_out",
        -  "append_card",
        -  "watermark",
        -  "grain"
        -]New value: +[
        +  "trim",
        +  "speed",
        +  "mute",
        +  "audio_gain",
        +  "fade_out",
        +  "append_card",
        +  "watermark",
        +  "grain",
        +  "text",
        +  "join"
        +]
      • addedInput schema / properties / ops / items / properties / position
        Added value: +{
        +  "description": "text: where",
        +  "enum": [
        +    "top",
        +    "center",
        +    "lower",
        +    "bottom"
        +  ],
        +  "type": "string"
        +}
      • changedInput schema / properties / ops / items / properties / seconds / description
        Previous value: -"fade_out 0.3-3s / append_card 2-5s"New value: +"fade_out 0.3-3s / append_card 2-5s / crossfade 0.2-1.5s"
      • changedInput schema / properties / ops / items / properties / start / description
        Previous value: -"trim/mute window start (s)"New value: +"trim/mute/text window start (s)"
      • addedInput schema / properties / ops / items / properties / style
        Added value: +{
        +  "anyOf": [
        +    {
        +      "type": "string"
        +    },
        +    {
        +      "additionalProperties": {},
        +      "properties": {},
        +      "type": "object"
        +    }
        +  ],
        +  "description": "text: a look name or a textStyle"
        +}
      • changedInput schema / properties / ops / items / properties / sub / description
        Previous value: -"append_card: the pill line (defaults to the brand website)"New value: +"append_card: the pill line (defaults to the website) / text: a smaller second line"
      • addedInput schema / properties / ops / items / properties / text
        Added value: +{
        +  "description": "text: the words, verbatim",
        +  "type": "string"
        +}
      • addedInput schema / properties / ops / items / properties / transition
        Added value: +{
        +  "description": "join",
        +  "enum": [
        +    "cut",
        +    "crossfade"
        +  ],
        +  "type": "string"
        +}
      • changedInput schema / properties / videoUrl / description
        Previous value: -"the served URL of the video to edit"New value: +"the video to edit: a render / Library URL, a direct file, or a public post link"
    • Changedpost_performance1 field changed
      • addedInput schema / properties / days
        Added value: +{
        +  "description": "look back N days (1-730) over the whole history; omit for the recent posts only",
        +  "type": "number"
        +}
    • Changedpost_to_bluesky6 fields changed
      • 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"
        +}
    • Changedpost_to_google_business2 fields changed
      • addedInput schema / properties / ideaId
        Added value: +{
        +  "description": "short id of the content-plan idea this post came from",
        +  "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"
        +}
    • Changedpost_to_linkedin2 fields changed
      • addedInput schema / properties / ideaId
        Added value: +{
        +  "description": "short id of the content-plan idea this post came from",
        +  "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"
        +}
    • Changedpost_to_linkedin_page2 fields changed
      • addedInput schema / properties / ideaId
        Added value: +{
        +  "description": "short id of the content-plan idea this post came from",
        +  "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"
        +}
    • Changedpost_to_meta2 fields changed
      • addedInput schema / properties / ideaId
        Added value: +{
        +  "description": "short id of the content-plan idea this post came from",
        +  "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"
        +}
    • Changedpost_to_pinterest2 fields changed
      • addedInput schema / properties / ideaId
        Added value: +{
        +  "description": "short id of the content-plan idea this post came from",
        +  "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"
        +}
    • Changedpost_to_telegram6 fields changed
      • 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"
        +}
    • Changedpost_to_tiktok2 fields changed
      • addedInput schema / properties / ideaId
        Added value: +{
        +  "description": "short id of the content-plan idea this post came from",
        +  "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"
        +}
    • Changedpost_to_x2 fields changed
      • addedInput schema / properties / ideaId
        Added value: +{
        +  "description": "short id of the content-plan idea this post came from",
        +  "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"
        +}
    • Changedpost_to_youtube2 fields changed
      • addedInput schema / properties / ideaId
        Added value: +{
        +  "description": "short id of the content-plan idea this post came from",
        +  "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"
        +}
    • Changedpost_x_article2 fields changed
      • addedInput schema / properties / ideaId
        Added value: +{
        +  "description": "short id of the content-plan idea this post came from",
        +  "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"
        +}
    • Changedreschedule_post2 fields changed
      • addedInput schema / properties / ideaId
        Added value: +{
        +  "description": "short id of the content-plan idea this post came from",
        +  "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"
        +}
    • Changedschedule_post2 fields changed
      • addedInput schema / properties / ideaId
        Added value: +{
        +  "description": "short id of the content-plan idea this post came from",
        +  "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"
        +}
  9. 21 tool updatesv0.1.272
    • Changedcollect_post_metrics1 field changed
      • addedInput schema / properties / brand
        Added value: +{
        +  "description": "WHICH BRAND the post lives in — the id or exact name from list_brands (a workspace shared with you: its profile id). Needed when it was published in a brand this connection is not pinned to: a post you can CREATE in a brand is manageable there too, for THIS CALL ONLY, without switching the connection. A name that matches no brand, or two, is REFUSED and nothing is done.",
        +  "type": "string"
        +}
    • Addededit_image
    • Changedfind_creators1 field changed
      • addedInput schema / properties / marketplace
        Added value: +{
        +  "description": "also search Instagram’s creator marketplace (Meta’s own creator directory, free) for the same niche; needs the Meta connector",
        +  "type": "boolean"
        +}
    • Addedheadline_variants
    • Addedhook_variants
    • Changedlist_meta_posts1 field changed
      • addedInput schema / properties / brand
        Added value: +{
        +  "description": "WHICH BRAND the post lives in — the id or exact name from list_brands (a workspace shared with you: its profile id). Needed when it was published in a brand this connection is not pinned to: a post you can CREATE in a brand is manageable there too, for THIS CALL ONLY, without switching the connection. A name that matches no brand, or two, is REFUSED and nothing is done.",
        +  "type": "string"
        +}
    • Changedlist_published_posts1 field changed
      • addedInput schema / properties / brand
        Added value: +{
        +  "description": "WHICH BRAND the post lives in — the id or exact name from list_brands (a workspace shared with you: its profile id). Needed when it was published in a brand this connection is not pinned to: a post you can CREATE in a brand is manageable there too, for THIS CALL ONLY, without switching the connection. A name that matches no brand, or two, is REFUSED and nothing is done.",
        +  "type": "string"
        +}
    • Addedlocalize_ad
    • Changedmine_angles3 fields changed
      • addedInput schema / properties / reviews
        Added value: +{
        +  "anyOf": [
        +    {
        +      "items": {
        +        "type": "string"
        +      },
        +      "type": "array"
        +    },
        +    {
        +      "type": "string"
        +    }
        +  ],
        +  "description": "your own customer reviews: a list of review texts, or one pasted block (one per line, numbered, blank-line separated, or CSV with a review column)"
        +}
      • addedInput schema / properties / reviewsUrl
        Added value: +{
        +  "description": "a URL of your reviews: an uploaded CSV, TXT or JSON file (from upload_file) or a review page. On a local CLI a file path also works.",
        +  "type": "string"
        +}
      • addedInput schema / properties / useOwnReviewsOnly
        Added value: +{
        +  "description": "true = mine only your reviews, no public search (no search credits). Default false = merge with public customer language.",
        +  "type": "boolean"
        +}
    • Changedplan_ad2 fields changed
      • addedInput schema / properties / draft
        Added value: +{
        +  "description": "ONLY after a video refusal that offered a light draft: the {model, durationSeconds} it named. The plan is then authored to that length and priced on that model. Never invent one — a video the account cannot cover is refused BEFORE planning with the three options (image / add credits / this draft when one fits), and the user chooses.",
        +  "properties": {
        +    "durationSeconds": {
        +      "type": "number"
        +    },
        +    "model": {
        +      "type": "string"
        +    }
        +  },
        +  "required": [
        +    "model",
        +    "durationSeconds"
        +  ],
        +  "type": "object"
        +}
      • changedInput schema / properties / durationSeconds / description
        Previous value: -"VIDEO ONLY — the total spot length the user explicitly asked for, in seconds, copied verbatim (30 for \"a 30 second ad\"). The planner authors the storyboard TO it: the scenes’ seconds sum to it and the script is word-budgeted for it. Supported range 4–180; anything outside is CLAMPED to it (the reply says so). A length that fits ONE clip of the render model renders as a single continuous pass; anything longer is STITCHED from acts filled to that model’s clip maximum with the remainder last (on a 15s-clip model, 40 → 15+15+10 and 17 → 13+4) — never time-compressed. The maximum is the model’s own: 15s on most, 30s on the longest-clip model, so a 30s ad can be one unbroken take rather than two acts. Omit when the user named no length; do NOT pass a guess, an omitted value keeps the recipe-aware default."New value: +"VIDEO ONLY — the total spot length the user explicitly asked for, in seconds, copied verbatim (30 for \"a 30 second ad\"). The planner authors the storyboard TO it: the scenes’ seconds sum to it and the script is word-budgeted for it. Supported range 4–180; anything outside is CLAMPED to it (the reply says so). A length that fits ONE clip of the render model renders as a single continuous pass; anything longer is STITCHED from acts filled to that model’s clip maximum with the remainder last (on a 15s-clip model, 40 -> 15+15+10 and 17 -> 13+4) — never time-compressed. The maximum is the model’s own: 15s on most, 30s on the longest-clip model, so a 30s ad can be one unbroken take rather than two acts. Omit when the user named no length; do NOT pass a guess, an omitted value keeps the recipe-aware default."
    • Changedpost_performance1 field changed
      • addedInput schema / properties / brand
        Added value: +{
        +  "description": "WHICH BRAND the post lives in — the id or exact name from list_brands (a workspace shared with you: its profile id). Needed when it was published in a brand this connection is not pinned to: a post you can CREATE in a brand is manageable there too, for THIS CALL ONLY, without switching the connection. A name that matches no brand, or two, is REFUSED and nothing is done.",
        +  "type": "string"
        +}
    • Changedpost_to_bluesky1 field changed
      • 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"
        +}
    • Changedpost_to_meta5 fields changed
      • changedInput schema / properties / countryCodes / description
        Previous value: -"THREADS ONLY — restrict the post to these ISO 3166-1 alpha-2 countries. ⚠ This is an ALLOWLIST, so the post becomes INVISIBLE in every country not named — it narrows reach, it does not target."New value: +"THREADS ONLY — restrict the post to these ISO 3166-1 alpha-2 countries. Warning: This is an ALLOWLIST, so the post becomes INVISIBLE in every country not named — it narrows reach, it does not target."
      • changedInput schema / properties / linkDescription / description
        Previous value: -"FACEBOOK — override the DESCRIPTION of the link preview. Needs `link`. MEASURED 2026-09-17: Facebook only honours these when the Page’s business has VERIFIED the domain (Business Manager ▸ Brand safety ▸ Domains) — without that Meta refuses the post outright with “Only owners of the URL…”, so leave them out unless the brand owns and has verified that link’s domain."New value: +"FACEBOOK — override the DESCRIPTION of the link preview. Needs `link`. MEASURED 2026-09-17: Facebook only honours these when the Page’s business has VERIFIED the domain (Business Manager > Brand safety > Domains) — without that Meta refuses the post outright with “Only owners of the URL…”, so leave them out unless the brand owns and has verified that link’s domain."
      • changedInput schema / properties / linkName / description
        Previous value: -"FACEBOOK — override the HEADLINE of the link preview. Without it the post shows whatever Open Graph title the destination carries, which on a bare landing page is often nothing. Needs `link`. MEASURED 2026-09-17: Facebook only honours these when the Page’s business has VERIFIED the domain (Business Manager ▸ Brand safety ▸ Domains) — without that Meta refuses the post outright with “Only owners of the URL…”, so leave them out unless the brand owns and has verified that link’s domain."New value: +"FACEBOOK — override the HEADLINE of the link preview. Without it the post shows whatever Open Graph title the destination carries, which on a bare landing page is often nothing. Needs `link`. MEASURED 2026-09-17: Facebook only honours these when the Page’s business has VERIFIED the domain (Business Manager > Brand safety > Domains) — without that Meta refuses the post outright with “Only owners of the URL…”, so leave them out unless the brand owns and has verified that link’s domain."
      • changedInput schema / properties / linkPicture / description
        Previous value: -"FACEBOOK — override the IMAGE of the link preview: a public http(s) url Facebook fetches itself. Needs `link`. MEASURED 2026-09-17: Facebook only honours these when the Page’s business has VERIFIED the domain (Business Manager ▸ Brand safety ▸ Domains) — without that Meta refuses the post outright with “Only owners of the URL…”, so leave them out unless the brand owns and has verified that link’s domain."New value: +"FACEBOOK — override the IMAGE of the link preview: a public http(s) url Facebook fetches itself. Needs `link`. MEASURED 2026-09-17: Facebook only honours these when the Page’s business has VERIFIED the domain (Business Manager > Brand safety > Domains) — without that Meta refuses the post outright with “Only owners of the URL…”, so leave them out unless the brand owns and has verified that link’s domain."
      • changedInput schema / properties / target / description
        Previous value: -"default facebook; instagram → the Page’s linked IG; threads → the brand’s connected Threads account"New value: +"default facebook; instagram -> the Page’s linked IG; threads -> the brand’s connected Threads account"
    • Changedpost_to_pinterest1 field changed
      • changedInput schema / properties / platformCover / description
        Previous value: -"VIDEO COVER. Omit it (the default) and Hermoso sets the video’s best frame — the same frame as its Library thumbnail — as the cover (Pinterest’s cover key frame, to the whole second). true = send no cover and let the platform pick (usually the first frame). A cover you pass yourself always wins."New value: +"VIDEO COVER. Omit it (the default) and Hermoso sets the video’s best frame — the same frame as its Library thumbnail — as the cover (on Pinterest the frame rides as the cover image). true = send no cover and let the platform pick (usually the first frame). A cover you pass yourself always wins."
    • Changedpost_to_telegram1 field changed
      • 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"
        +}
    • Changedrender_ad1 field changed
      • changedInput schema / properties / creator / description
        Previous value: -"CAST A SAVED CREATOR in this ad — their id from list_creators, or the name you know them by (“Sarah”). Their saved portrait becomes the on-camera identity for the whole spot, so the same face carries across every act and across every ad you render for this brand — and because we already have their picture, the character portrait this pipeline would otherwise generate is skipped, so casting somebody costs LESS than not casting them. Omit to let the ad cast a fresh person. Refused for free, with nothing rendered, if the name matches nobody or more than one creator, if the plan has nobody on camera, or if they are a REAL person with no likeness consent on file."New value: +"CAST A SAVED CREATOR in this ad — their id from list_creators, or the name you know them by (“Sarah”). Their saved portrait becomes the on-camera identity for the whole spot, so the same face carries across every act and across every ad you render for this brand — and because we already have their picture, the character portrait this pipeline would otherwise generate is skipped, so casting somebody costs LESS than not casting them. Omit to let the ad cast a fresh person — EXCEPT for a CREATOR account (onboarded from their own @handle): their own saved likeness is cast by default on any plan with a person on camera, and the read-back says `default:true`; pass \"none\" to render without them. Refused for free, with nothing rendered, if the name matches nobody or more than one creator, if an explicitly named creator is cast on a plan with nobody on camera, or if they are a REAL person with no likeness consent on file."
    • Changedreschedule_post1 field changed
      • changedInput schema / properties / privacyLevel / description
        Previous value: -"TIKTOK — WHICH PRIVACY LEVEL the post goes out at, in TikTok’s own vocabulary. TikTok requires the USER to choose this from the levels their own account allows: call tiktok_creator_info, show them the real options, and pass back the one they picked — never a default and never a guess, which TikTok refuses at init. Omit it and the post falls back to the coarse `visibility` (private → SELF_ONLY, otherwise PUBLIC_TO_EVERYONE), which cannot express MUTUAL_FOLLOW_FRIENDS or FOLLOWER_OF_CREATOR at all. It must agree with the TikTok visibility (SELF_ONLY is the private one) and it is refused on a TikTok DRAFT, which carries no post info."New value: +"TIKTOK — WHICH PRIVACY LEVEL the post goes out at, in TikTok’s own vocabulary. TikTok requires the USER to choose this from the levels their own account allows: call tiktok_creator_info, show them the real options, and pass back the one they picked — never a default and never a guess, which TikTok refuses at init. Omit it and the post falls back to the coarse `visibility` (private -> SELF_ONLY, otherwise PUBLIC_TO_EVERYONE), which cannot express MUTUAL_FOLLOW_FRIENDS or FOLLOWER_OF_CREATOR at all. It must agree with the TikTok visibility (SELF_ONLY is the private one) and it is refused on a TikTok DRAFT, which carries no post info."
    • Addedresize_ad
    • Changedsave_creator3 fields changed
      • changedInput schema / properties / image / description
        Previous value: -"public https url of the portrait (an existing render’s url, or any public photo). Not a local file path — upload it with upload_file first and save the url that returns"New value: +"REQUIRED except with useAnyway. public https url of the portrait (an existing render’s url, or any public photo). Not a local file path — upload it with upload_file first and save the url that returns"
      • addedInput schema / properties / useAnyway
        Added value: +{
        +  "description": "only for a creator whose saved photo was flagged too unclear to cast (render_ad says so): true casts the current photo as it is, no new image needed",
        +  "type": "boolean"
        +}
      • changedInput schema / required
        Previous value: -[
        -  "name",
        -  "image"
        -]New value: +[
        +  "name"
        +]
    • Changedschedule_post4 fields changed
      • changedInput schema / properties / linkDescription / description
        Previous value: -"FACEBOOK — override the DESCRIPTION of the link preview. Needs `link`. MEASURED 2026-09-17: Facebook only honours these when the Page’s business has VERIFIED the domain (Business Manager ▸ Brand safety ▸ Domains) — without that Meta refuses the post outright with “Only owners of the URL…”, so leave them out unless the brand owns and has verified that link’s domain."New value: +"FACEBOOK — override the DESCRIPTION of the link preview. Needs `link`. MEASURED 2026-09-17: Facebook only honours these when the Page’s business has VERIFIED the domain (Business Manager > Brand safety > Domains) — without that Meta refuses the post outright with “Only owners of the URL…”, so leave them out unless the brand owns and has verified that link’s domain."
      • changedInput schema / properties / linkName / description
        Previous value: -"FACEBOOK — override the HEADLINE of the link preview. Without it the post shows whatever Open Graph title the destination carries, which on a bare landing page is often nothing. Needs `link`. MEASURED 2026-09-17: Facebook only honours these when the Page’s business has VERIFIED the domain (Business Manager ▸ Brand safety ▸ Domains) — without that Meta refuses the post outright with “Only owners of the URL…”, so leave them out unless the brand owns and has verified that link’s domain."New value: +"FACEBOOK — override the HEADLINE of the link preview. Without it the post shows whatever Open Graph title the destination carries, which on a bare landing page is often nothing. Needs `link`. MEASURED 2026-09-17: Facebook only honours these when the Page’s business has VERIFIED the domain (Business Manager > Brand safety > Domains) — without that Meta refuses the post outright with “Only owners of the URL…”, so leave them out unless the brand owns and has verified that link’s domain."
      • changedInput schema / properties / linkPicture / description
        Previous value: -"FACEBOOK — override the IMAGE of the link preview: a public http(s) url Facebook fetches itself. Needs `link`. MEASURED 2026-09-17: Facebook only honours these when the Page’s business has VERIFIED the domain (Business Manager ▸ Brand safety ▸ Domains) — without that Meta refuses the post outright with “Only owners of the URL…”, so leave them out unless the brand owns and has verified that link’s domain."New value: +"FACEBOOK — override the IMAGE of the link preview: a public http(s) url Facebook fetches itself. Needs `link`. MEASURED 2026-09-17: Facebook only honours these when the Page’s business has VERIFIED the domain (Business Manager > Brand safety > Domains) — without that Meta refuses the post outright with “Only owners of the URL…”, so leave them out unless the brand owns and has verified that link’s domain."
      • changedInput schema / properties / privacyLevel / description
        Previous value: -"TIKTOK — WHICH PRIVACY LEVEL the post goes out at, in TikTok’s own vocabulary. TikTok requires the USER to choose this from the levels their own account allows: call tiktok_creator_info, show them the real options, and pass back the one they picked — never a default and never a guess, which TikTok refuses at init. Omit it and the post falls back to the coarse `visibility` (private → SELF_ONLY, otherwise PUBLIC_TO_EVERYONE), which cannot express MUTUAL_FOLLOW_FRIENDS or FOLLOWER_OF_CREATOR at all. It must agree with the TikTok visibility (SELF_ONLY is the private one) and it is refused on a TikTok DRAFT, which carries no post info."New value: +"TIKTOK — WHICH PRIVACY LEVEL the post goes out at, in TikTok’s own vocabulary. TikTok requires the USER to choose this from the levels their own account allows: call tiktok_creator_info, show them the real options, and pass back the one they picked — never a default and never a guess, which TikTok refuses at init. Omit it and the post falls back to the coarse `visibility` (private -> SELF_ONLY, otherwise PUBLIC_TO_EVERYONE), which cannot express MUTUAL_FOLLOW_FRIENDS or FOLLOWER_OF_CREATOR at all. It must agree with the TikTok visibility (SELF_ONLY is the private one) and it is refused on a TikTok DRAFT, which carries no post info."
    • Changedupdate_drive_file1 field changed
      • changedInput schema / properties / trash / description
        Previous value: -"true → move to Trash; false → restore from Trash"New value: +"true -> move to Trash; false -> restore from Trash"
  10. 15 tool updatesv0.1.256
    • Changedgenerate_image4 fields changed
      • addedInput schema / properties / mask
        Added value: +{
        +  "description": "MASKED EDIT — change ONE region of an image and keep the rest: a local path or URL of a mask image for refImages[0] (the image being edited). Either convention works and the reply says which it read: TRANSPARENT pixels = change, or, on a mask with no transparency, WHITE = change and black = keep. Any size; it is scaled to the image. The mask GUIDES the edit rather than stencilling it: the new content can blend a little past its edge. Runs on the model hermoso_capabilities marks `refs.mask` (gpt-image-2.5): leave `model` empty or name that one — any other named model is refused, free. Needs refImages; the result keeps the source image's own frame, so aspectRatio is not applied.",
        +  "type": "string"
        +}
      • changedInput schema / properties / model / description
        Previous value: -"image model id from hermoso_capabilities"New value: +"image model id from hermoso_capabilities. A model whose `refs.mode` is \"edit\" there (gpt-image-2.5) takes your refImages on ITS OWN editor, up to its `refs.max`, instead of the default compositor"
      • changedInput schema / properties / prompt / description
        Previous value: -"the full image prompt — subject, composition, lighting, and any on-image ad text"New value: +"the full image prompt — subject, composition, lighting, and any on-image ad text. ON A POSE MODEL (product-in-hand / try-on) THIS IS EXTRA DIRECTION AND IT IS OPTIONAL — leave it out and the pose is built for you. If you do write one, DESCRIBE THE POSE (\"she holds the bottle upright in her right hand at chest height, label to camera\"); do NOT phrase it as a swap (\"replace the mug with the bottle\"), which is REFUSED for free, because the product then comes out the size of whatever it replaced — a 30ml bottle rendered mug-sized in testing."
      • changedInput schema / properties / refImages / description
        Previous value: -"local file paths or URLs of product/logo references to composite in"New value: +"local file paths or URLs of product/logo references to composite in. ON THE POSE MODELS — any row hermoso_capabilities marks `needsRefs` with a `refsMax`, such as putting your product in someone’s hands or a virtual try-on — THE ORDER IS THE CONTRACT AND IT IS NOT A COMPOSITE: refImages[0] is the PERSON photo, and the rest (up to `refsMax` minus one) are the product or garment photos. Reversed, you get the product wearing the person. A 4th product is dropped and the reply says so."
    • Changedgenerate_video2 fields changed
      • addedInput schema / properties / refImages
        Added value: +{
        +  "description": "SEVERAL reference images (local paths or URLs) — a person, products, a place — that must all appear in the clip. Only models whose `refs.max` in hermoso_capabilities is above 1 use more than one, and each uses at most that many; with `refs.promptAddressed` true, name them in your prompt as Image 1, Image 2… in this order. minimax-h3-max-ref takes up to 9 and keeps each one as a reference rather than a first frame. On a model that takes one image, only the first is used.",
        +  "items": {
        +    "type": "string"
        +  },
        +  "type": "array"
        +}
      • changedInput schema / properties / refVideo / description
        Previous value: -"URL of an existing video to EDIT rather than generate from scratch — the clip is transformed per your prompt, inheriting the SOURCE clip’s canvas (aspect ratio) and length (aspectRatio/durationSeconds are ignored for an edit). Omit `model` for the default editor, or name a model whose videoEdit is true in hermoso_capabilities (a named model that cannot edit is refused, nothing charged); refImage rides along as the look of what the edit adds. With extend:true this is instead the clip to EXTEND. Omit to generate a fresh clip."New value: +"URL of an existing video to EDIT rather than generate from scratch — the clip is transformed per your prompt, inheriting the SOURCE clip’s canvas (aspect ratio) and, on every model hermoso_capabilities marks `sourceLength`, its LENGTH too: those endpoints have no duration parameter, their listed `durations` are the per-second price ladder, and a durationSeconds you send is reported back as unused rather than silently dropped. Trim the source to change the length. Omit `model` for the default editor, or name a model whose videoEdit is true in hermoso_capabilities (a named model that cannot edit is refused, nothing charged); refImage rides along as the look of what the edit adds. With extend:true this is instead the clip to EXTEND. Omit to generate a fresh clip."
    • Changedpost_to_bluesky1 field changed
      • 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"
        +}
    • Changedpost_to_linkedin_page1 field changed
      • addedInput schema / properties / platformCover
        Added value: +{
        +  "description": "VIDEO COVER. Omit it (the default) and Hermoso sets the video’s best frame — the same frame as its Library thumbnail — as the cover (LinkedIn’s video thumbnail upload). true = send no cover and let the platform pick (usually the first frame). A cover you pass yourself always wins.",
        +  "type": "boolean"
        +}
    • Changedpost_to_meta1 field changed
      • addedInput schema / properties / platformCover
        Added value: +{
        +  "description": "VIDEO COVER. Omit it (the default) and Hermoso sets the video’s best frame — the same frame as its Library thumbnail — as the cover (Instagram Reel: thumb_offset; Facebook video/Reel: an uploaded cover image) — and on Threads, which has no cover setting, a blank first frame is replaced on a copy sent to Threads only. true = send no cover and let the platform pick (usually the first frame). A cover you pass yourself always wins.",
        +  "type": "boolean"
        +}
    • Changedpost_to_pinterest1 field changed
      • addedInput schema / properties / platformCover
        Added value: +{
        +  "description": "VIDEO COVER. Omit it (the default) and Hermoso sets the video’s best frame — the same frame as its Library thumbnail — as the cover (Pinterest’s cover key frame, to the whole second). true = send no cover and let the platform pick (usually the first frame). A cover you pass yourself always wins.",
        +  "type": "boolean"
        +}
    • Changedpost_to_telegram1 field changed
      • addedInput schema / properties / platformCover
        Added value: +{
        +  "description": "VIDEO COVER. Omit it (the default) and Hermoso sets the video’s best frame — the same frame as its Library thumbnail — as the cover (Telegram’s in-chat video cover). true = send no cover and let the platform pick (usually the first frame). A cover you pass yourself always wins.",
        +  "type": "boolean"
        +}
    • Changedpost_to_tiktok1 field changed
      • addedInput schema / properties / platformCover
        Added value: +{
        +  "description": "VIDEO COVER. Omit it (the default) and Hermoso sets the video’s best frame — the same frame as its Library thumbnail — as the cover (TikTok video_cover_timestamp_ms, on a direct post — a draft takes no cover, you pick it in the TikTok app). true = send no cover and let the platform pick (usually the first frame). A cover you pass yourself always wins.",
        +  "type": "boolean"
        +}
    • Changedpost_to_x1 field changed
      • addedInput schema / properties / platformCover
        Added value: +{
        +  "description": "VIDEO COVER. X 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 X 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"
        +}
    • Changedpost_to_youtube1 field changed
      • addedInput schema / properties / platformCover
        Added value: +{
        +  "description": "VIDEO COVER. Omit it (the default) and Hermoso sets the video’s best frame — the same frame as its Library thumbnail — as the cover (YouTube custom thumbnail; the same as thumbnailUrl:\"auto\" when true). true = send no cover and let the platform pick (usually the first frame). A cover you pass yourself always wins.",
        +  "type": "boolean"
        +}
    • Changedrecast_motion1 field changed
      • addedInput schema / properties / tier
        Added value: +{
        +  "description": "'pro' (default): 1080p and real hand-object interaction. 'standard': about 25% fewer credits and faster, but 720p, and it tends to mime a held object with empty hands. hermoso_capabilities lists the exact credits for both",
        +  "enum": [
        +    "pro",
        +    "standard"
        +  ],
        +  "type": "string"
        +}
    • Changedreschedule_post1 field changed
      • addedInput schema / properties / platformCover
        Added value: +{
        +  "description": "VIDEO COVER. Omit it (the default) and Hermoso sets the video’s best frame — the same frame as its Library thumbnail — as the cover on every channel that allows one — and on X, Threads and Bluesky, which have none, a blank first frame is replaced on a copy sent there only. true = send no cover and let the platform pick (usually the first frame). A cover you pass yourself always wins.",
        +  "type": "boolean"
        +}
    • Changedschedule_post2 fields changed
      • changedInput schema / properties / coverTimestampMs / description
        Previous value: -"TIKTOK VIDEO ONLY — which frame TikTok uses as the cover, in milliseconds from the start. Omit and TikTok uses the first frame."New value: +"TIKTOK VIDEO ONLY — which frame TikTok uses as the cover, in milliseconds from the start. Omit and Hermoso uses the video’s best frame (platformCover:true leaves it to TikTok, which uses the first frame)."
      • addedInput schema / properties / platformCover
        Added value: +{
        +  "description": "VIDEO COVER. Omit it (the default) and Hermoso sets the video’s best frame — the same frame as its Library thumbnail — as the cover on every channel that allows one (Instagram, Facebook, TikTok direct posts, LinkedIn Pages, Pinterest, Telegram, YouTube) — and on X, Threads and Bluesky, which have none, a blank first frame is replaced on a copy sent there only. true = send no cover and let the platform pick (usually the first frame). A cover you pass yourself always wins.",
        +  "type": "boolean"
        +}
    • Changedsearch_posts1 field changed
      • changedInput schema / properties / topic / description
        Previous value: -"subject, brand, product or hashtag — \"higgsfield\", \"coffee\", \"#homecafe\""New value: +"subject, brand, product or hashtag — \"liquid death\", \"coffee\", \"#homecafe\""
    • Changedupscale_video2 fields changed
      • changedInput schema / properties / engine / description
        Previous value: -"default topaz. 'flux' = the FLUX 3 video upscaler"New value: +"default 'standard', the precision upscaler. 'flux' = the FLUX 3 video upscaler"
      • changedInput schema / properties / engine / enum
        Previous value: -[
        -  "topaz",
        -  "flux"
        -]New value: +[
        +  "standard",
        +  "flux"
        +]
  11. 2 tool updatesv0.1.252
    • Changededit_video1 field changed
      • addedInput schema / properties / interactionId
        Added value: +{
        +  "description": "OPTIONAL: the interactionId an earlier Gemini Omni render or edit returned. The edit then continues that clip on the SAME Omni model from its own stored context (identity-true, no re-upload, usually cheaper). If that edit cannot run, the clip is edited by the video editor instead and the reply says so.",
        +  "type": "string"
        +}
    • Changedfind_tools1 field changed
      • addedInput schema / properties / onlyHealthy
        Added value: +{
        +  "description": "leave out tools that are failing their recent calls or that need a connector this workspace has not made. Default false — nothing is hidden unless you ask, because a missing row reads as a missing capability.",
        +  "type": "boolean"
        +}
  12. 20 tool updatesv0.1.251
    • Changedcancel_scheduled1 field changed
      • addedInput schema / properties / brand
        Added value: +{
        +  "description": "WHICH BRAND this post is in — the id or exact name from list_brands. Needed when the post lives in a brand this connection is not pinned to: a post you can CREATE in a brand must be manageable there too, without switching the whole connection. A name that matches no brand, or two, is REFUSED.",
        +  "type": "string"
        +}
    • Changedduplicate_scheduled1 field changed
      • addedInput schema / properties / brand
        Added value: +{
        +  "description": "WHICH BRAND this post is in — the id or exact name from list_brands. Needed when the post lives in a brand this connection is not pinned to: a post you can CREATE in a brand must be manageable there too, without switching the whole connection. A name that matches no brand, or two, is REFUSED.",
        +  "type": "string"
        +}
    • Changedgenerate_image1 field changed
      • changedInput schema / properties / imageSize / description
        Previous value: -"pixel-size preset for models that support it (e.g. 1K/2K) — omit for the default"New value: +"pixel-size preset for models that support it: 1K/2K, and 4K on the models hermoso_capabilities lists with a 4K imageSize price (a 4K ask on any other model is refused, free) — omit for the default"
    • Changedgenerate_video7 fields changed
      • changedInput schema / properties / cameraMove / description
        Previous value: -"A named camera move around the still in refImage — orbit (quarter turn, the default), orbit_left, orbit_half, orbit_full (turntable), rise, crane_up, push_in, pull_back, reveal. Only the camera-controls model renders one (minimax-h3-max-camera, listed with its moves in hermoso_capabilities): pass it with model omitted and that model is picked, or with that model named; any other named model is refused by name with nothing charged. Needs refImage."New value: +"A named camera move around the still in refImage, spelled exactly as the enum gives it: an orbit (a quarter turn, the default), orbiting left, a half turn or a full turntable, a rise, a crane up, a push in, a pull back, or a reveal. Only the camera-controls model renders one (minimax-h3-max-camera, listed with its moves in hermoso_capabilities): pass it with model omitted and that model is picked, or with that model named; any other named model is refused by name with nothing charged. Needs refImage."
      • addedInput schema / properties / endImage
        Added value: +{
        +  "description": "local path or URL of the LAST frame: the clip travels from refImage (required with it) to this image. Only models with endFrame true in hermoso_capabilities take it; any other named model is refused by name, nothing charged.",
        +  "type": "string"
        +}
      • addedInput schema / properties / extend
        Added value: +{
        +  "description": "true = EXTEND refVideo: the same clip continues per your prompt for durationSeconds more (each model’s extend.minSeconds..maxSeconds in hermoso_capabilities), delivered as ONE clip, source then continuation. Needs model named (a model with extend in hermoso_capabilities).",
        +  "type": "boolean"
        +}
      • addedInput schema / properties / interactionId
        Added value: +{
        +  "description": "with extend:true on an Omni model: the interactionId returned by an earlier render on that model — continues it from its own stored context instead of re-uploading refVideo.",
        +  "type": "string"
        +}
      • addedInput schema / properties / loop
        Added value: +{
        +  "description": "true = a seamless loop whose last frame flows back into its first. Only models with loop true in hermoso_capabilities; needs refImage and cannot be combined with endImage.",
        +  "type": "boolean"
        +}
      • changedInput schema / properties / refVideo / description
        Previous value: -"URL of an existing video to EDIT rather than generate from scratch — the omni engine accepts a raw clip and transforms it per your prompt, inheriting the SOURCE clip’s canvas (aspect ratio) and length (aspectRatio/durationSeconds are ignored for an edit). Omit to generate a fresh clip."New value: +"URL of an existing video to EDIT rather than generate from scratch — the clip is transformed per your prompt, inheriting the SOURCE clip’s canvas (aspect ratio) and length (aspectRatio/durationSeconds are ignored for an edit). Omit `model` for the default editor, or name a model whose videoEdit is true in hermoso_capabilities (a named model that cannot edit is refused, nothing charged); refImage rides along as the look of what the edit adds. With extend:true this is instead the clip to EXTEND. Omit to generate a fresh clip."
      • addedInput schema / properties / shots
        Added value: +{
        +  "description": "MULTI-SHOT: one clip cut into these shots, in order. The seconds must add up to a length the model renders; replaces `prompt` (send either). Only models with multiShot true in hermoso_capabilities.",
        +  "items": {
        +    "properties": {
        +      "prompt": {
        +        "description": "what happens in this shot",
        +        "type": "string"
        +      },
        +      "seconds": {
        +        "description": "this shot’s length in whole seconds",
        +        "maximum": 15,
        +        "minimum": 1,
        +        "type": "integer"
        +      }
        +    },
        +    "required": [
        +      "prompt",
        +      "seconds"
        +    ],
        +    "type": "object"
        +  },
        +  "type": "array"
        +}
    • Addedimport_from_cloud
    • Changedlist_scheduled1 field changed
      • addedInput schema / properties / brand
        Added value: +{
        +  "description": "WHICH BRAND to list — the id or exact name from list_brands. Needed when the post lives in a brand this connection is not pinned to: a post you can CREATE in a brand must be manageable there too, without switching the whole connection. A name that matches no brand, or two, is REFUSED.",
        +  "type": "string"
        +}
    • Changedpost_to_google_business1 field changed
      • 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"
        +}
    • Changedpost_to_linkedin1 field changed
      • 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"
        +}
    • Changedpost_to_linkedin_page2 fields changed
      • 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"
        +}
      • addedInput schema / properties / targetAudience
        Added value: +{
        +  "description": "LINKEDIN COMPANY PAGE POST ONLY — show the post only to Page followers matching these facets (URNs or bare numeric ids; search_linkedin_ads_targeting finds them). LinkedIn requires the matching audience to be over 300 followers and refuses a smaller one. Personal-profile posts cannot be targeted.",
        +  "properties": {
        +    "degrees": {
        +      "items": {
        +        "type": "string"
        +      },
        +      "type": "array"
        +    },
        +    "fieldsOfStudy": {
        +      "items": {
        +        "type": "string"
        +      },
        +      "type": "array"
        +    },
        +    "geoLocations": {
        +      "items": {
        +        "type": "string"
        +      },
        +      "type": "array"
        +    },
        +    "industries": {
        +      "items": {
        +        "type": "string"
        +      },
        +      "type": "array"
        +    },
        +    "jobFunctions": {
        +      "items": {
        +        "type": "string"
        +      },
        +      "type": "array"
        +    },
        +    "organizations": {
        +      "items": {
        +        "type": "string"
        +      },
        +      "type": "array"
        +    },
        +    "seniorities": {
        +      "items": {
        +        "type": "string"
        +      },
        +      "type": "array"
        +    },
        +    "staffCountRanges": {
        +      "items": {
        +        "enum": [
        +          "SIZE_1",
        +          "SIZE_2_TO_10",
        +          "SIZE_11_TO_50",
        +          "SIZE_51_TO_200",
        +          "SIZE_201_TO_500",
        +          "SIZE_501_TO_1000",
        +          "SIZE_1001_TO_5000",
        +          "SIZE_5001_TO_10000",
        +          "SIZE_10001_OR_MORE"
        +        ],
        +        "type": "string"
        +      },
        +      "type": "array"
        +    }
        +  },
        +  "type": "object"
        +}
    • Changedpost_to_meta16 fields changed
      • addedInput schema / properties / audience
        Added value: +{
        +  "description": "FACEBOOK PAGE POST ONLY — limit who can see this organic post to people in these places and/or over this age (Facebook allows 13, 15, 18, 21 or 25). Not on Instagram, Threads or a Facebook Reel (a vertical clip becomes a Reel): those are refused. Region and city keys come from the Meta ads location search.",
        +  "properties": {
        +    "cities": {
        +      "description": "Meta location keys for cities",
        +      "items": {
        +        "type": "string"
        +      },
        +      "type": "array"
        +    },
        +    "countries": {
        +      "description": "two-letter codes, e.g. [\"CA\",\"US\"]",
        +      "items": {
        +        "type": "string"
        +      },
        +      "type": "array"
        +    },
        +    "minAge": {
        +      "anyOf": [
        +        {
        +          "const": 13,
        +          "type": "number"
        +        },
        +        {
        +          "const": 15,
        +          "type": "number"
        +        },
        +        {
        +          "const": 18,
        +          "type": "number"
        +        },
        +        {
        +          "const": 21,
        +          "type": "number"
        +        },
        +        {
        +          "const": 25,
        +          "type": "number"
        +        }
        +      ]
        +    },
        +    "regions": {
        +      "description": "Meta location keys for regions/states",
        +      "items": {
        +        "type": "string"
        +      },
        +      "type": "array"
        +    }
        +  },
        +  "type": "object"
        +}
      • addedInput schema / properties / audioName
        Added value: +{
        +  "description": "INSTAGRAM REEL — the name of the Reel’s audio track, which is what viewers tap through to. REELS ONLY.",
        +  "type": "string"
        +}
      • 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"
        +}
      • addedInput schema / properties / brandedContentSponsorIds
        Added value: +{
        +  "description": "INSTAGRAM — the numeric Instagram USER IDS of the brands behind that label (at most 2, and ids rather than @handles). Naming sponsors IS asking for the label, so setting these with `paidPartnership:false` is refused instead of publishing brand credits with no disclosure.",
        +  "items": {
        +    "type": "string"
        +  },
        +  "type": "array"
        +}
      • addedInput schema / properties / callToAction
        Added value: +{
        +  "description": "FACEBOOK — a real call-to-action BUTTON on the Page post. The types that open a destination (SHOP_NOW, LEARN_MORE, SIGN_UP, BUY_NOW, DOWNLOAD, OPEN_LINK, WATCH_MORE, BOOK_TRAVEL) need somewhere to go: the post’s own `link`, or `callToActionLink`. CALL_NOW, MESSAGE_PAGE, LIKE_PAGE and GET_DIRECTIONS act on the Page itself and take none.",
        +  "enum": [
        +    "BOOK_TRAVEL",
        +    "BUY_NOW",
        +    "CALL_NOW",
        +    "DOWNLOAD",
        +    "GET_DIRECTIONS",
        +    "LEARN_MORE",
        +    "LIKE_PAGE",
        +    "MESSAGE_PAGE",
        +    "NO_BUTTON",
        +    "OPEN_LINK",
        +    "SHOP_NOW",
        +    "SIGN_UP",
        +    "WATCH_MORE"
        +  ],
        +  "type": "string"
        +}
      • addedInput schema / properties / callToActionLink
        Added value: +{
        +  "description": "FACEBOOK — where the button goes, when that is not the post’s own `link`.",
        +  "type": "string"
        +}
      • addedInput schema / properties / coverUrl
        Added value: +{
        +  "description": "INSTAGRAM REEL COVER — a public image url Instagram fetches and uses as the cover in the Reels tab. REELS ONLY, and the alternative to `thumbOffset`: passing both is refused, since they are two answers to the same question. Run a local file through upload_file first.",
        +  "type": "string"
        +}
      • addedInput schema / properties / linkDescription
        Added value: +{
        +  "description": "FACEBOOK — override the DESCRIPTION of the link preview. Needs `link`. MEASURED 2026-09-17: Facebook only honours these when the Page’s business has VERIFIED the domain (Business Manager ▸ Brand safety ▸ Domains) — without that Meta refuses the post outright with “Only owners of the URL…”, so leave them out unless the brand owns and has verified that link’s domain.",
        +  "type": "string"
        +}
      • addedInput schema / properties / linkName
        Added value: +{
        +  "description": "FACEBOOK — override the HEADLINE of the link preview. Without it the post shows whatever Open Graph title the destination carries, which on a bare landing page is often nothing. Needs `link`. MEASURED 2026-09-17: Facebook only honours these when the Page’s business has VERIFIED the domain (Business Manager ▸ Brand safety ▸ Domains) — without that Meta refuses the post outright with “Only owners of the URL…”, so leave them out unless the brand owns and has verified that link’s domain.",
        +  "type": "string"
        +}
      • addedInput schema / properties / linkPicture
        Added value: +{
        +  "description": "FACEBOOK — override the IMAGE of the link preview: a public http(s) url Facebook fetches itself. Needs `link`. MEASURED 2026-09-17: Facebook only honours these when the Page’s business has VERIFIED the domain (Business Manager ▸ Brand safety ▸ Domains) — without that Meta refuses the post outright with “Only owners of the URL…”, so leave them out unless the brand owns and has verified that link’s domain.",
        +  "type": "string"
        +}
      • changedInput schema / properties / locationId / description
        Previous value: -"Threads only — a place id from search_threads_locations, to geotag the post to a physical location (restaurant, storefront)"New value: +"TAG A PLACE. THREADS: a place id from search_threads_locations. INSTAGRAM: the NUMERIC ID OF A FACEBOOK PAGE associated with that location, not a place name and not coordinates; a non-numeric value is refused rather than sent."
      • addedInput schema / properties / paidPartnership
        Added value: +{
        +  "description": "INSTAGRAM — the PAID PARTNERSHIP label. A COMPLIANCE DECLARATION, the same kind Hermoso already carries for TikTok and X: set it whenever the post is sponsored, gifted or otherwise paid for. Opt-in and never inferred — it is the poster’s own statement about their commercial relationship.",
        +  "type": "boolean"
        +}
      • addedInput schema / properties / place
        Added value: +{
        +  "description": "FACEBOOK — tag a location on the Page post. The NUMERIC ID OF THE FACEBOOK PAGE for that place, not a place name and not coordinates.",
        +  "type": "string"
        +}
      • addedInput schema / properties / shareToFeed
        Added value: +{
        +  "description": "INSTAGRAM REEL — true puts the Reel in the Feed grid as well as the Reels tab. REELS ONLY. Left unset it follows Instagram’s own default; Hermoso does not flip it either way on the user’s behalf.",
        +  "type": "boolean"
        +}
      • addedInput schema / properties / story
        Added value: +{
        +  "description": "INSTAGRAM STORY — publish this as a 24-hour Story instead of a feed post. One image OR one video: Instagram has no carousel story, so a carousel is REFUSED BY NAME rather than quietly posted to the feed. A Story carries NO CAPTION (there is nowhere to show one), no collaborators, no product tags and no trialReel — passing any of those is refused by name and nothing is posted, because a story that silently drops the words is worse than one that never went. INSTAGRAM ONLY: a Facebook Page story is a different upload and is not built, so a Facebook channel with `story` set is refused rather than published to the feed.",
        +  "type": "boolean"
        +}
      • addedInput schema / properties / thumbOffset
        Added value: +{
        +  "description": "INSTAGRAM REEL COVER, the other way — which frame becomes the cover, in MILLISECONDS from the start of the video. REELS ONLY. Use it instead of `coverUrl` when the right cover is already a frame of the clip.",
        +  "type": "number"
        +}
    • Changedpost_to_pinterest1 field changed
      • 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"
        +}
    • Changedpost_to_tiktok1 field changed
      • 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"
        +}
    • Changedpost_to_x1 field changed
      • 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"
        +}
    • Changedpost_to_youtube1 field changed
      • 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"
        +}
    • Changedpost_x_article1 field changed
      • 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"
        +}
    • Changedreschedule_post17 fields changed
      • addedInput schema / properties / audience
        Added value: +{
        +  "description": "FACEBOOK — replaces who can see the Page post {countries, regions, cities, minAge}; {} removes the limit.",
        +  "properties": {
        +    "cities": {
        +      "items": {
        +        "type": "string"
        +      },
        +      "type": "array"
        +    },
        +    "countries": {
        +      "items": {
        +        "type": "string"
        +      },
        +      "type": "array"
        +    },
        +    "minAge": {
        +      "type": "number"
        +    },
        +    "regions": {
        +      "items": {
        +        "type": "string"
        +      },
        +      "type": "array"
        +    }
        +  },
        +  "type": "object"
        +}
      • addedInput schema / properties / audioName
        Added value: +{
        +  "description": "INSTAGRAM REEL — replaces the audio track name; an empty string removes it.",
        +  "type": "string"
        +}
      • addedInput schema / properties / brand
        Added value: +{
        +  "description": "WHICH BRAND this post is in — the id or exact name from list_brands. Needed when the post lives in a brand this connection is not pinned to: a post you can CREATE in a brand must be manageable there too, without switching the whole connection. A name that matches no brand, or two, is REFUSED.",
        +  "type": "string"
        +}
      • addedInput schema / properties / brandedContentSponsorIds
        Added value: +{
        +  "description": "INSTAGRAM — replaces the sponsor user ids behind the paid-partnership label (at most 2); [] removes them.",
        +  "items": {
        +    "type": "string"
        +  },
        +  "type": "array"
        +}
      • addedInput schema / properties / callToAction
        Added value: +{
        +  "description": "FACEBOOK — replaces the button on the Page post; \"\" removes it.",
        +  "enum": [
        +    "BOOK_TRAVEL",
        +    "BUY_NOW",
        +    "CALL_NOW",
        +    "DOWNLOAD",
        +    "GET_DIRECTIONS",
        +    "LEARN_MORE",
        +    "LIKE_PAGE",
        +    "MESSAGE_PAGE",
        +    "NO_BUTTON",
        +    "OPEN_LINK",
        +    "SHOP_NOW",
        +    "SIGN_UP",
        +    "WATCH_MORE",
        +    ""
        +  ],
        +  "type": "string"
        +}
      • addedInput schema / properties / callToActionLink
        Added value: +{
        +  "description": "FACEBOOK — replaces where the button goes; an empty string falls back to the post link.",
        +  "type": "string"
        +}
      • addedInput schema / properties / coverUrl
        Added value: +{
        +  "description": "INSTAGRAM REEL — replaces the cover image url; an empty string removes it.",
        +  "type": "string"
        +}
      • addedInput schema / properties / instagramLocationId
        Added value: +{
        +  "description": "INSTAGRAM — replaces the tagged place (the numeric id of its Facebook Page); an empty string removes it. Not locationId, which is the Google Business listing.",
        +  "type": "string"
        +}
      • addedInput schema / properties / linkDescription
        Added value: +{
        +  "description": "FACEBOOK — replaces the link preview description; an empty string removes the override.",
        +  "type": "string"
        +}
      • addedInput schema / properties / linkName
        Added value: +{
        +  "description": "FACEBOOK — replaces the link preview headline; an empty string removes the override.",
        +  "type": "string"
        +}
      • addedInput schema / properties / linkPicture
        Added value: +{
        +  "description": "FACEBOOK — replaces the link preview image url; an empty string removes the override.",
        +  "type": "string"
        +}
      • changedInput schema / properties / paidPartnership / description
        Previous value: -"X — the paid-partnership label; false turns it off."New value: +"INSTAGRAM AND X — the paid-partnership label; false turns it off."
      • addedInput schema / properties / place
        Added value: +{
        +  "description": "FACEBOOK — replaces the tagged place (the numeric id of its Facebook Page); an empty string removes it.",
        +  "type": "string"
        +}
      • addedInput schema / properties / shareToFeed
        Added value: +{
        +  "description": "INSTAGRAM REEL — whether the Reel also shows in the Feed grid.",
        +  "type": "boolean"
        +}
      • addedInput schema / properties / story
        Added value: +{
        +  "description": "INSTAGRAM — true makes it a 24-hour Story, false an ordinary feed post. One image or one video, no carousel.",
        +  "type": "boolean"
        +}
      • addedInput schema / properties / targetAudience
        Added value: +{
        +  "description": "LINKEDIN COMPANY PAGE — replaces who sees the post; {} removes the limit. The matching audience must be over 300 followers.",
        +  "properties": {
        +    "degrees": {
        +      "items": {
        +        "type": "string"
        +      },
        +      "type": "array"
        +    },
        +    "fieldsOfStudy": {
        +      "items": {
        +        "type": "string"
        +      },
        +      "type": "array"
        +    },
        +    "geoLocations": {
        +      "items": {
        +        "type": "string"
        +      },
        +      "type": "array"
        +    },
        +    "industries": {
        +      "items": {
        +        "type": "string"
        +      },
        +      "type": "array"
        +    },
        +    "jobFunctions": {
        +      "items": {
        +        "type": "string"
        +      },
        +      "type": "array"
        +    },
        +    "organizations": {
        +      "items": {
        +        "type": "string"
        +      },
        +      "type": "array"
        +    },
        +    "seniorities": {
        +      "items": {
        +        "type": "string"
        +      },
        +      "type": "array"
        +    },
        +    "staffCountRanges": {
        +      "items": {
        +        "type": "string"
        +      },
        +      "type": "array"
        +    }
        +  },
        +  "type": "object"
        +}
      • addedInput schema / properties / thumbOffset
        Added value: +{
        +  "description": "INSTAGRAM REEL — replaces the cover frame, in milliseconds; 0 removes it. Never together with coverUrl.",
        +  "type": "number"
        +}
    • Changedretry_scheduled1 field changed
      • addedInput schema / properties / brand
        Added value: +{
        +  "description": "WHICH BRAND this post is in — the id or exact name from list_brands. Needed when the post lives in a brand this connection is not pinned to: a post you can CREATE in a brand must be manageable there too, without switching the whole connection. A name that matches no brand, or two, is REFUSED.",
        +  "type": "string"
        +}
    • Changedschedule_post17 fields changed
      • addedInput schema / properties / audience
        Added value: +{
        +  "description": "FACEBOOK PAGE POST ONLY — limit who can see this organic post to people in these places and/or over this age (Facebook allows 13, 15, 18, 21 or 25). Not on Instagram, Threads or a Facebook Reel (a vertical clip becomes a Reel): those are refused. Region and city keys come from the Meta ads location search.",
        +  "properties": {
        +    "cities": {
        +      "description": "Meta location keys for cities",
        +      "items": {
        +        "type": "string"
        +      },
        +      "type": "array"
        +    },
        +    "countries": {
        +      "description": "two-letter codes, e.g. [\"CA\",\"US\"]",
        +      "items": {
        +        "type": "string"
        +      },
        +      "type": "array"
        +    },
        +    "minAge": {
        +      "anyOf": [
        +        {
        +          "const": 13,
        +          "type": "number"
        +        },
        +        {
        +          "const": 15,
        +          "type": "number"
        +        },
        +        {
        +          "const": 18,
        +          "type": "number"
        +        },
        +        {
        +          "const": 21,
        +          "type": "number"
        +        },
        +        {
        +          "const": 25,
        +          "type": "number"
        +        }
        +      ]
        +    },
        +    "regions": {
        +      "description": "Meta location keys for regions/states",
        +      "items": {
        +        "type": "string"
        +      },
        +      "type": "array"
        +    }
        +  },
        +  "type": "object"
        +}
      • addedInput schema / properties / audioName
        Added value: +{
        +  "description": "INSTAGRAM REEL — the name of the Reel’s audio track, which is what viewers tap through to. REELS ONLY.",
        +  "type": "string"
        +}
      • 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"
        +}
      • addedInput schema / properties / brandedContentSponsorIds
        Added value: +{
        +  "description": "INSTAGRAM — the numeric Instagram USER IDS of the brands behind that label (at most 2, and ids rather than @handles). Naming sponsors IS asking for the label, so setting these with `paidPartnership:false` is refused instead of publishing brand credits with no disclosure.",
        +  "items": {
        +    "type": "string"
        +  },
        +  "type": "array"
        +}
      • addedInput schema / properties / callToAction
        Added value: +{
        +  "description": "FACEBOOK — a real call-to-action BUTTON on the Page post. The types that open a destination (SHOP_NOW, LEARN_MORE, SIGN_UP, BUY_NOW, DOWNLOAD, OPEN_LINK, WATCH_MORE, BOOK_TRAVEL) need somewhere to go: the post’s own `link`, or `callToActionLink`. CALL_NOW, MESSAGE_PAGE, LIKE_PAGE and GET_DIRECTIONS act on the Page itself and take none.",
        +  "enum": [
        +    "BOOK_TRAVEL",
        +    "BUY_NOW",
        +    "CALL_NOW",
        +    "DOWNLOAD",
        +    "GET_DIRECTIONS",
        +    "LEARN_MORE",
        +    "LIKE_PAGE",
        +    "MESSAGE_PAGE",
        +    "NO_BUTTON",
        +    "OPEN_LINK",
        +    "SHOP_NOW",
        +    "SIGN_UP",
        +    "WATCH_MORE"
        +  ],
        +  "type": "string"
        +}
      • addedInput schema / properties / callToActionLink
        Added value: +{
        +  "description": "FACEBOOK — where the button goes, when that is not the post’s own `link`.",
        +  "type": "string"
        +}
      • addedInput schema / properties / coverUrl
        Added value: +{
        +  "description": "INSTAGRAM REEL COVER — a public image url Instagram fetches and uses as the cover in the Reels tab. REELS ONLY, and the alternative to `thumbOffset`: passing both is refused, since they are two answers to the same question. Run a local file through upload_file first.",
        +  "type": "string"
        +}
      • addedInput schema / properties / instagramLocationId
        Added value: +{
        +  "description": "INSTAGRAM — tag a place (called locationId on post_to_meta; locationId here is the Google Business listing). It is the NUMERIC ID OF A FACEBOOK PAGE associated with that location, not a place name and not coordinates; a non-numeric value is refused rather than sent.",
        +  "type": "string"
        +}
      • addedInput schema / properties / linkDescription
        Added value: +{
        +  "description": "FACEBOOK — override the DESCRIPTION of the link preview. Needs `link`. MEASURED 2026-09-17: Facebook only honours these when the Page’s business has VERIFIED the domain (Business Manager ▸ Brand safety ▸ Domains) — without that Meta refuses the post outright with “Only owners of the URL…”, so leave them out unless the brand owns and has verified that link’s domain.",
        +  "type": "string"
        +}
      • addedInput schema / properties / linkName
        Added value: +{
        +  "description": "FACEBOOK — override the HEADLINE of the link preview. Without it the post shows whatever Open Graph title the destination carries, which on a bare landing page is often nothing. Needs `link`. MEASURED 2026-09-17: Facebook only honours these when the Page’s business has VERIFIED the domain (Business Manager ▸ Brand safety ▸ Domains) — without that Meta refuses the post outright with “Only owners of the URL…”, so leave them out unless the brand owns and has verified that link’s domain.",
        +  "type": "string"
        +}
      • addedInput schema / properties / linkPicture
        Added value: +{
        +  "description": "FACEBOOK — override the IMAGE of the link preview: a public http(s) url Facebook fetches itself. Needs `link`. MEASURED 2026-09-17: Facebook only honours these when the Page’s business has VERIFIED the domain (Business Manager ▸ Brand safety ▸ Domains) — without that Meta refuses the post outright with “Only owners of the URL…”, so leave them out unless the brand owns and has verified that link’s domain.",
        +  "type": "string"
        +}
      • changedInput schema / properties / paidPartnership / description
        Previous value: -"X — label the post a PAID PARTNERSHIP. OPT-IN ONLY: set it when the post is sponsored, gifted or otherwise paid for, and never assume it on the user’s behalf."New value: +"INSTAGRAM AND X — the PAID PARTNERSHIP label, a compliance declaration: set it when the post is sponsored, gifted or otherwise paid for. OPT-IN ONLY, never assume it on the user’s behalf. On Instagram, brandedContentSponsorIds names the brands behind it."
      • addedInput schema / properties / place
        Added value: +{
        +  "description": "FACEBOOK — tag a location on the Page post. The NUMERIC ID OF THE FACEBOOK PAGE for that place, not a place name and not coordinates.",
        +  "type": "string"
        +}
      • addedInput schema / properties / shareToFeed
        Added value: +{
        +  "description": "INSTAGRAM REEL — true puts the Reel in the Feed grid as well as the Reels tab. REELS ONLY. Left unset it follows Instagram’s own default; Hermoso does not flip it either way on the user’s behalf.",
        +  "type": "boolean"
        +}
      • addedInput schema / properties / story
        Added value: +{
        +  "description": "INSTAGRAM STORY — publish this as a 24-hour Story instead of a feed post. One image OR one video: Instagram has no carousel story, so a carousel is REFUSED BY NAME rather than quietly posted to the feed. A Story carries NO CAPTION (there is nowhere to show one), no collaborators, no product tags and no trialReel — passing any of those is refused by name and nothing is posted, because a story that silently drops the words is worse than one that never went. INSTAGRAM ONLY: a Facebook Page story is a different upload and is not built, so a Facebook channel with `story` set is refused rather than published to the feed.",
        +  "type": "boolean"
        +}
      • addedInput schema / properties / targetAudience
        Added value: +{
        +  "description": "LINKEDIN COMPANY PAGE POST ONLY — show the post only to Page followers matching these facets (URNs or bare numeric ids; search_linkedin_ads_targeting finds them). LinkedIn requires the matching audience to be over 300 followers and refuses a smaller one. Personal-profile posts cannot be targeted.",
        +  "properties": {
        +    "degrees": {
        +      "items": {
        +        "type": "string"
        +      },
        +      "type": "array"
        +    },
        +    "fieldsOfStudy": {
        +      "items": {
        +        "type": "string"
        +      },
        +      "type": "array"
        +    },
        +    "geoLocations": {
        +      "items": {
        +        "type": "string"
        +      },
        +      "type": "array"
        +    },
        +    "industries": {
        +      "items": {
        +        "type": "string"
        +      },
        +      "type": "array"
        +    },
        +    "jobFunctions": {
        +      "items": {
        +        "type": "string"
        +      },
        +      "type": "array"
        +    },
        +    "organizations": {
        +      "items": {
        +        "type": "string"
        +      },
        +      "type": "array"
        +    },
        +    "seniorities": {
        +      "items": {
        +        "type": "string"
        +      },
        +      "type": "array"
        +    },
        +    "staffCountRanges": {
        +      "items": {
        +        "enum": [
        +          "SIZE_1",
        +          "SIZE_2_TO_10",
        +          "SIZE_11_TO_50",
        +          "SIZE_51_TO_200",
        +          "SIZE_201_TO_500",
        +          "SIZE_501_TO_1000",
        +          "SIZE_1001_TO_5000",
        +          "SIZE_5001_TO_10000",
        +          "SIZE_10001_OR_MORE"
        +        ],
        +        "type": "string"
        +      },
        +      "type": "array"
        +    }
        +  },
        +  "type": "object"
        +}
      • addedInput schema / properties / thumbOffset
        Added value: +{
        +  "description": "INSTAGRAM REEL COVER, the other way — which frame becomes the cover, in MILLISECONDS from the start of the video. REELS ONLY. Use it instead of `coverUrl` when the right cover is already a frame of the clip.",
        +  "type": "number"
        +}
    • Changedstitch_video1 field changed
      • changedInput schema / properties / resolution / description
        Previous value: -"1080p (default), or 480p/720p for a cheaper draft"New value: +"720p (default), 1080p for full detail, or 480p for a cheaper draft"
    • Changedupload_file1 field changed
      • addedInput schema / properties / getUploadUrl
        Added value: +{
        +  "description": "ASK FOR A ONE-TIME UPLOAD URL instead of uploading now — use this whenever the file is on the user’s machine and you can run a shell or an HTTP request. Returns a uploadUrl you PUT the raw bytes to (any HTTP client), which answers with the durable Hermoso url. It beats `dataUri` for anything but a small image: a data: URI spends the whole file as tokens in this conversation. One file per url, and it expires.",
        +  "type": "boolean"
        +}
  13. 3 tool updatesv0.1.249
    • Changedpost_to_x4 fields changed
      • addedInput schema / properties / imageUrl
        Added value: +{
        +  "description": "alias of mediaUrl for an IMAGE — same as passing it as mediaUrl",
        +  "type": "string"
        +}
      • changedInput schema / properties / quotePostId / description
        Previous value: -"numeric id of a post to QUOTE — X renders the quoted post inside yours. This is NOT replyToId: a reply sits under the original in its thread, a quote stands alone on your own timeline with the original embedded, which is the one you want for commentary. X makes a quote mutually exclusive with media and with a poll (their own schema), and a quote is billed at the higher LINK rate because X appends the quoted post’s t.co URL whatever your text says."New value: +"numeric id of a post to QUOTE — X renders the quoted post inside yours. This is NOT replyToId: a reply sits under the original in its thread, a quote stands alone on your own timeline with the original embedded, which is the one you want for commentary. X makes a quote mutually exclusive with media and with a poll (their own schema), and a quote is billed at the higher LINK rate because X appends the quoted post’s t.co URL whatever your text says. Same X rule as replyToId: a quote of a post that does not mention this account is refused by X."
      • changedInput schema / properties / replyToId / description
        Previous value: -"numeric id of an existing X post to reply to"New value: +"numeric id of an existing X post to reply to. X ONLY ACCEPTS AN API REPLY TO A POST THAT MENTIONS THIS ACCOUNT OR THAT THIS ACCOUNT WROTE (X policy since Feb 2026, every tier below Enterprise): a cold reply under a stranger's post is refused by X with \"You can only reply to or quote posts where you are mentioned or are the author\" and nothing is posted. Reply to those from x.com itself; use this for replies in your own threads and to people who tagged you."
      • addedInput schema / properties / videoUrl
        Added value: +{
        +  "description": "alias of mediaUrl for a VIDEO — same as passing it as mediaUrl",
        +  "type": "string"
        +}
    • Addedsearch_posts
    • Changedupscale_video2 fields changed
      • addedInput schema / properties / engine
        Added value: +{
        +  "description": "default topaz. 'flux' = the FLUX 3 video upscaler",
        +  "enum": [
        +    "topaz",
        +    "flux"
        +  ],
        +  "type": "string"
        +}
      • addedInput schema / properties / mode
        Added value: +{
        +  "description": "FLUX only — 'creative' turns on its detail-enhancement pass; default precise",
        +  "enum": [
        +    "precise",
        +    "creative"
        +  ],
        +  "type": "string"
        +}
  14. 8 tool updatesv0.1.243
    • Addedconnect_connector
    • Changeddisconnect_connector1 field changed
      • changedInput schema / properties / confirm / description
        Previous value: -"REQUIRED true — reconnecting needs the user's browser"New value: +"REQUIRED true — reconnecting a sign-in account afterwards needs the user's browser; a paste-a-key account is reconnected with connect_connector"
    • Changedgenerate_video3 fields changed
      • changedInput schema / properties / cameraMove / description
        Previous value: -"H3 Max Multi Angle only: camera move (default orbit)"New value: +"A named camera move around the still in refImage — orbit (quarter turn, the default), orbit_left, orbit_half, orbit_full (turntable), rise, crane_up, push_in, pull_back, reveal. Only the camera-controls model renders one (minimax-h3-max-camera, listed with its moves in hermoso_capabilities): pass it with model omitted and that model is picked, or with that model named; any other named model is refused by name with nothing charged. Needs refImage."
      • changedInput schema / properties / cameraMove / enum
        Previous value: -[
        -  "orbit",
        -  "orbit_half",
        -  "orbit_full",
        -  "rise",
        -  "push_in",
        -  "pull_back"
        -]New value: +[
        +  "orbit",
        +  "orbit_left",
        +  "orbit_half",
        +  "orbit_full",
        +  "rise",
        +  "crane_up",
        +  "push_in",
        +  "pull_back",
        +  "reveal"
        +]
      • addedInput schema / properties / cameraTrajectory
        Added value: +{
        +  "description": "Your own ordered camera path, 2 to 12 keyframes, for the camera-controls model only (same rule as cameraMove; overrides it). The first pose is held until its time and the last pose is held to the end. A value outside these bounds is refused by name, nothing charged.",
        +  "items": {
        +    "properties": {
        +      "azimuth": {
        +        "description": "horizontal angle around the subject in degrees (0 = where the still was taken; the sign turns the camera the other way; at most 32 full turns of total travel)",
        +        "type": "number"
        +      },
        +      "distance": {
        +        "description": "distance from the subject in scene units, 1 = the distance of the still; smaller is closer",
        +        "exclusiveMinimum": 0,
        +        "type": "number"
        +      },
        +      "elevation": {
        +        "description": "vertical angle in degrees, -90 (below) to 90 (straight above)",
        +        "maximum": 90,
        +        "minimum": -90,
        +        "type": "number"
        +      },
        +      "time": {
        +        "description": "when this pose is reached, 0 = start of the clip, 1 = end",
        +        "maximum": 1,
        +        "minimum": 0,
        +        "type": "number"
        +      }
        +    },
        +    "required": [
        +      "time",
        +      "azimuth",
        +      "elevation",
        +      "distance"
        +    ],
        +    "type": "object"
        +  },
        +  "maxItems": 12,
        +  "minItems": 2,
        +  "type": "array"
        +}
    • Changedlist_scheduled4 fields changed
      • addedInput schema / properties / channel
        Added value: +{
        +  "description": "only posts that include this channel, e.g. \"pinterest\" or \"x\"",
        +  "type": "string"
        +}
      • addedInput schema / properties / fired
        Added value: +{
        +  "description": "how many already-fired posts to list, most recent last (default 15, max 200)",
        +  "type": "number"
        +}
      • addedInput schema / properties / id
        Added value: +{
        +  "description": "one post id from this list: returns that post in full, every caption and setting included",
        +  "type": "string"
        +}
      • addedInput schema / properties / upcoming
        Added value: +{
        +  "description": "how many queued posts to list, soonest first (default 25, max 200)",
        +  "type": "number"
        +}
    • Changedpost_to_youtube1 field changed
      • addedInput schema / properties / thumbnailUrl
        Added value: +{
        +  "description": "the custom thumbnail: a Hermoso-hosted image (make_thumbnail, or upload_file for the user’s own). Omit it and a representative frame of the video is set, free; \"auto\" keeps YouTube’s pick. Custom thumbnails need a verified channel.",
        +  "type": "string"
        +}
    • Changedreschedule_post2 fields changed
      • changedInput schema / properties / description / description
        Previous value: -"YOUTUBE — replace the video description; \"\" clears it. Remember the caption is the TITLE, not the description."New value: +"YOUTUBE — replace the video description; \"\" clears it and the caption is used."
      • addedInput schema / properties / thumbnailUrl
        Added value: +{
        +  "description": "YOUTUBE: replace the custom thumbnail; \"\" goes back to a frame of the video, \"auto\" to YouTube’s pick.",
        +  "type": "string"
        +}
    • Changedsave_to_swipefile2 fields changed
      • addedInput schema / properties / items / items / properties / pageName
        Added value: +{
        +  "description": "alias of advertiser",
        +  "type": "string"
        +}
      • addedInput schema / properties / items / items / properties / page_name
        Added value: +{
        +  "description": "alias of advertiser: the field search_meta_ads returns, accepted as-is",
        +  "type": "string"
        +}
    • Changedschedule_post2 fields changed
      • changedInput schema / properties / description / description
        Previous value: -"YOUTUBE — the video DESCRIPTION, max 5000 characters: the box under the video carrying the links, the CTA and everything YouTube search reads. It is NOT the caption — a scheduled YouTube item’s text becomes its TITLE — so omitting this publishes the video with an empty description."New value: +"YOUTUBE: the video DESCRIPTION, max 5000 characters, carrying the links, the CTA and what YouTube search reads. Omit it and the caption is used."
      • addedInput schema / properties / thumbnailUrl
        Added value: +{
        +  "description": "YOUTUBE: the custom thumbnail, a Hermoso-hosted image (make_thumbnail, or upload_file for the user’s own). Omit it and a frame of the video is used; \"auto\" keeps YouTube’s pick.",
        +  "type": "string"
        +}
  15. 4 tool updatesv0.1.230
    • Changedgenerate_video4 fields changed
      • addedInput schema / properties / cameraMove
        Added value: +{
        +  "description": "H3 Max Multi Angle only: camera move (default orbit)",
        +  "enum": [
        +    "orbit",
        +    "orbit_half",
        +    "orbit_full",
        +    "rise",
        +    "push_in",
        +    "pull_back"
        +  ],
        +  "type": "string"
        +}
      • changedInput schema / properties / model / description
        Previous value: -"video model id from hermoso_capabilities. Naming one is a DELIBERATE pick — the server asks before ever swapping it (no silent fallback); omit it to let the router pick"New value: +"video model id from hermoso_capabilities; a named model is never swapped without asking. Omit to let the router pick"
      • changedInput schema / properties / raw / description
        Previous value: -"RAW MODEL ACCESS: dispatch this prompt to the model BYTE-IDENTICAL — no appended packaging/label guidance, no negative prompt, no reference-binding lines, no hex-to-colour-name rewrite. Use it when you want the model itself rather than Hermoso's render craft. Two provider-mandated corrections still apply, because the vendor hard-fails without them: an @ImageN token that outnumbers the references actually shipped is dropped, and a prompt past the endpoint's published character cap is trimmed at a sentence boundary. Billing, durable delivery and per-model validation are unchanged."New value: +"RAW MODEL ACCESS: dispatch this prompt to the model BYTE-IDENTICAL — no appended packaging/label guidance, no negative prompt, no reference-binding lines, no hex-to-colour-name rewrite. Use it when you want the model itself rather than Hermoso's render craft. Two vendor-required fixes still apply: extra @ImageN tokens are dropped and an over-long prompt is trimmed at a sentence. Billing, durable delivery and per-model validation are unchanged."
      • changedInput schema / properties / resolution / description
        Previous value: -"'1080p' default (what we ship and bill for); '480p'/'720p' = cheaper draft passes, '4k' = premium final delivery (more credits). NOT EVERY MODEL OFFERS EVERY TIER — this enum is what the tool accepts, and each model's OWN `resolutions` list in hermoso_capabilities is what it can actually render (the longest-clip 30s model, for one, tops out at 720p). Ask for a tier the chosen model does not list and it is rendered at that model's best available tier instead, with nothing in the reply saying so — so check `resolutions` before promising anyone 1080p or 4k."New value: +"'1080p' default (what we ship and bill for); '480p'/'720p' = cheaper draft passes, '4k' = premium final delivery (more credits). NOT EVERY MODEL OFFERS EVERY TIER — this enum is what the tool accepts, and each model's OWN `resolutions` list in hermoso_capabilities is what it can actually render. Ask for a tier the chosen model does not list and it is rendered at that model's best available tier instead, with nothing in the reply saying so — so check `resolutions` before promising anyone 1080p or 4k."
    • Changedpost_x_article1 field changed
      • changedInput schema / properties / headings / description
        Previous value: -"how headings are rendered. “blocks” (default) uses X’s own heading block types, which is the faithful conversion. “text” renders each heading as a BOLD standalone paragraph instead: the words survive and the heading structure does not, the reply says so, and it is the documented fallback if X’s Articles service rejects heading blocks."New value: +"how headings are rendered. “blocks” (default) uses X’s own heading block types for # and ## headings; ### and deeper become bold lines, because X Articles refuse a third-level heading (the reply says so). “text” renders every heading as a BOLD standalone paragraph instead: the words survive and the heading structure does not, and the reply says so."
    • Changedreschedule_post2 fields changed
      • changedInput schema / properties / optimizeCopy / description
        Previous value: -"fit the shared caption to each channel’s own rules when you schedule it (the fitted caption is stored on the scheduled post, so what you scheduled is what publishes) wherever no per-channel caption was written (YouTube keyword title + structured description + tags, Instagram/TikTok hashtags, LinkedIn longer, X/Bluesky short, Pinterest keyword-rich); a channel with its own caption is left exactly as written. Send false to switch it off on this item."New value: +"fit the shared caption to each channel’s own rules when you schedule it (the fitted caption is stored on the scheduled post, so what you scheduled is what publishes) wherever no per-channel caption was written; a channel with its own caption is left exactly as written. Send false to switch it off on this item."
      • addedInput schema / properties / xArticle
        Added value: +{
        +  "description": "X: replaces the X Article (title, headings); {} makes it an ordinary X post again.",
        +  "properties": {
        +    "headings": {
        +      "enum": [
        +        "blocks",
        +        "text"
        +      ],
        +      "type": "string"
        +    },
        +    "title": {
        +      "type": "string"
        +    }
        +  },
        +  "type": "object"
        +}
    • Changedschedule_post6 fields changed
      • changedInput schema / properties / communityId / description
        Previous value: -"X — publish into an X COMMUNITY instead of the main timeline: the number in the community’s own URL (x.com/i/communities/<id>). The connected account must be a MEMBER of it; X answers a non-member and a non-existent id with the same refusal and does not separate them."New value: +"X — publish into an X COMMUNITY instead of the main timeline: the number in the community’s own URL (x.com/i/communities/<id>). The connected account must be a MEMBER of it."
      • changedInput schema / properties / optimizeCopy / description
        Previous value: -"RECOMMENDED when one caption goes to several channels: fit the shared caption to each channel’s own rules when you schedule it (the fitted caption is stored on the scheduled post, so what you scheduled is what publishes) wherever no per-channel caption was written — YouTube gets a keyword title, a structured multi-paragraph description and search tags; Instagram/TikTok hashtags; LinkedIn longer; X/Bluesky short; Pinterest keyword-rich. The angle and every claim stay the author’s; a channel with its own caption is left exactly as written. Off by default so nobody’s words are rewritten unasked."New value: +"RECOMMENDED when one caption goes to several channels: fit the shared caption to each channel’s own rules when you schedule it (the fitted caption is stored on the scheduled post, so what you scheduled is what publishes) wherever no per-channel caption was written. The angle and every claim stay the author’s; a channel with its own caption is left exactly as written. Off by default so nobody’s words are rewritten unasked."
      • changedInput schema / properties / paidPartnership / description
        Previous value: -"X — label the post a PAID PARTNERSHIP, the same disclosure Hermoso already ships for TikTok. OPT-IN ONLY: set it when the post is sponsored, gifted or otherwise paid for, and never assume it on the user’s behalf."New value: +"X — label the post a PAID PARTNERSHIP. OPT-IN ONLY: set it when the post is sponsored, gifted or otherwise paid for, and never assume it on the user’s behalf."
      • changedInput schema / properties / thread / description
        Previous value: -"X — publish a THREAD, one entry per post, each replying to the one before (at most 25). 280 characters per part without X Premium, up to 25,000 with it — nothing is truncated, and on a Premium account one long post is usually better AND cheaper than a thread. It REPLACES the X caption: with a thread set, `message`/`captions.x` is not sent to X at all. A thread cannot carry a poll."New value: +"X — publish a THREAD, one entry per post, each replying to the one before (at most 25). 280 characters per part without X Premium, up to 25,000 with it; nothing is truncated. It REPLACES the X caption: with a thread set, `message`/`captions.x` is not sent to X at all. A thread cannot carry a poll."
      • addedInput schema / properties / xArticle
        Added value: +{
        +  "description": "X: publish the X item as a long-form X ARTICLE. title is its headline, the X text (message or captions.x) its markdown body, the image its cover. Refused now if the markdown has formatting X cannot hold. X allows about 5 Articles a day; one that fires into that cap fails with the reset time and can be retried.",
        +  "properties": {
        +    "headings": {
        +      "enum": [
        +        "blocks",
        +        "text"
        +      ],
        +      "type": "string"
        +    },
        +    "title": {
        +      "type": "string"
        +    }
        +  },
        +  "required": [
        +    "title"
        +  ],
        +  "type": "object"
        +}
      • changedInput schema / properties / xQuotePostId / description
        Previous value: -"X — the numeric id of an X post this one QUOTES: the last part of its URL. X renders that post inside yours and it stands alone on your own timeline, which is what makes a quote different from a reply. NAMED xQuotePostId, NOT quotePostId, because `quotePostId` on this same schedule belongs to THREADS — a schedule going to both channels would otherwise be silently ambiguous. Billed at X’s higher LINK rate, because X appends the quoted post’s t.co URL to yours."New value: +"X — the numeric id of an X post this one QUOTES: the last part of its URL. NAMED xQuotePostId, NOT quotePostId, because `quotePostId` on this same schedule belongs to THREADS. Billed at X’s higher LINK rate."
  16. 5 tool updatesv0.1.227
    • Addedadd_subtitles
    • Addedclone_static
    • Changedplan_ad1 field changed
      • changedInput schema / properties / reference / description
        Previous value: -"a reference to remix: an ad-library link (Facebook Ad Library, LinkedIn Ad Library, Google Ads Transparency — its real copy/advertiser are fetched) OR a VIDEO link — a TikTok, Instagram Reel, Facebook video, X post, YouTube Short/video or a direct video file — which is WATCHED first (frames + voiceover/on-screen-text transcript) so the concept keeps its hook, structure and pacing. To remake one video for this brand at its own length, clone_video is the direct tool"New value: +"a reference to clone: an ad-library link (Facebook Ad Library, LinkedIn Ad Library, Google Ads Transparency — its real copy/advertiser are fetched) OR a VIDEO link — a TikTok, Instagram Reel, Facebook video, X post, YouTube Short/video or a direct video file — which is WATCHED first (frames + voiceover/on-screen-text transcript) so the concept keeps its hook, structure and pacing. To remake one video for this brand at its own length, clone_video is the direct tool"
    • Changedpost_performance2 fields changed
      • changedInput schema / properties / axis / description
        Previous value: -"what to group by — default hook"New value: +"what to group by — default hook; recipe = the format of the creative"
      • changedInput schema / properties / axis / enum
        Previous value: -[
        -  "hook",
        -  "subject",
        -  "channel",
        -  "media",
        -  "hour"
        -]New value: +[
        +  "hook",
        +  "subject",
        +  "recipe",
        +  "channel",
        +  "media",
        +  "hour"
        +]
    • Changedremix_static2 fields changed
      • changedInput schema / properties / brandId / description
        Previous value: -"a brand id/name from list_brands to remix for; omit to use the active brand"New value: +"a brand id/name from list_brands to clone for; omit to use the active brand"
      • changedInput schema / properties / imageUrl / description
        Previous value: -"the URL of the static ad image to remix"New value: +"the URL of the static ad image to clone"
  17. 7 tool updatesv0.1.225
    • Addedclone_video
    • Changedcollect_post_metrics1 field changed
      • addedInput schema / properties / remeasure
        Added value: +{
        +  "description": "ALSO re-read posts older than 7 days whose every reading came back empty or failed — use after post_performance reports posts \"read but empty\", or once a channel's reader has been fixed. Otherwise those windows stay closed.",
        +  "type": "boolean"
        +}
    • Changedmake_explainer1 field changed
      • changedInput schema / properties / endCard / description
        Previous value: -"append the branded end card (default true)"New value: +"append the branded end card. DEFAULT FALSE — set true ONLY when the user asks for one"
    • Changedplan_ad1 field changed
      • changedInput schema / properties / reference / description
        Previous value: -"a reference ad URL to remix the angle from — Facebook Ad Library, LinkedIn Ad Library or Google Ads Transparency links (the real ad’s copy/advertiser are fetched and fed into the concept)"New value: +"a reference to remix: an ad-library link (Facebook Ad Library, LinkedIn Ad Library, Google Ads Transparency — its real copy/advertiser are fetched) OR a VIDEO link — a TikTok, Instagram Reel, Facebook video, X post, YouTube Short/video or a direct video file — which is WATCHED first (frames + voiceover/on-screen-text transcript) so the concept keeps its hook, structure and pacing. To remake one video for this brand at its own length, clone_video is the direct tool"
    • Changedrender_ad4 fields changed
      • changedInput schema / properties / captions / description
        Previous value: -"burn the plan's per-scene on-screen words as caption pills. DEFAULT FALSE — leave it off unless the user asks for on-screen text (no captions, or true subtitles of what is said; never scene or emphasis labels); a recipe whose format IS on-screen text keeps its text either way"New value: +"burn the plan's per-scene on-screen words as caption pills. DEFAULT FALSE on every recipe — set true ONLY when the user asks for on-screen text or captions; no recipe turns them on by itself"
      • changedInput schema / properties / endCard / description
        Previous value: -"branded end card on/off (default: on, except organic recipes)"New value: +"append the branded end card. DEFAULT FALSE on every recipe — set true ONLY when the user asks for an end card (a clone of a video that had none should not grow one)"
      • changedInput schema / properties / lockup / description
        Previous value: -"persistent brand-logo lockup overlay on/off"New value: +"brand wordmark + tagline composited over the closing seconds. DEFAULT FALSE — set true ONLY when the user asks for branding on the close"
      • addedInput schema / properties / textStyle
        Added value: +{
        +  "anyOf": [
        +    {
        +      "enum": [
        +        "pill",
        +        "editorial",
        +        "bold",
        +        "minimal",
        +        "handwritten",
        +        "boxed"
        +      ],
        +      "type": "string"
        +    },
        +    {
        +      "properties": {
        +        "background": {
        +          "description": "\"none\", \"pill\", or a #hex box",
        +          "type": "string"
        +        },
        +        "cardColor": {
        +          "description": "#hex end card background",
        +          "type": "string"
        +        },
        +        "color": {
        +          "description": "#hex",
        +          "type": "string"
        +        },
        +        "font": {
        +          "enum": [
        +            "sans",
        +            "serif",
        +            "elegant",
        +            "condensed",
        +            "hand"
        +          ],
        +          "type": "string"
        +        },
        +        "italic": {
        +          "type": "boolean"
        +        },
        +        "outline": {
        +          "type": "boolean"
        +        },
        +        "position": {
        +          "enum": [
        +            "top",
        +            "center",
        +            "lower",
        +            "bottom"
        +          ],
        +          "type": "string"
        +        },
        +        "preset": {
        +          "enum": [
        +            "pill",
        +            "editorial",
        +            "bold",
        +            "minimal",
        +            "handwritten",
        +            "boxed"
        +          ],
        +          "type": "string"
        +        },
        +        "shadow": {
        +          "type": "boolean"
        +        },
        +        "size": {
        +          "anyOf": [
        +            {
        +              "enum": [
        +                "s",
        +                "m",
        +                "l",
        +                "xl"
        +              ],
        +              "type": "string"
        +            },
        +            {
        +              "type": "number"
        +            }
        +          ]
        +        },
        +        "subFont": {
        +          "enum": [
        +            "sans",
        +            "serif",
        +            "elegant",
        +            "condensed",
        +            "hand"
        +          ],
        +          "type": "string"
        +        },
        +        "subItalic": {
        +          "type": "boolean"
        +        },
        +        "textCase": {
        +          "enum": [
        +            "as-is",
        +            "upper",
        +            "lower",
        +            "title"
        +          ],
        +          "type": "string"
        +        },
        +        "tilt": {
        +          "description": "degrees, ±12",
        +          "type": "number"
        +        },
        +        "weight": {
        +          "type": "number"
        +        }
        +      },
        +      "type": "object"
        +    }
        +  ],
        +  "description": "THE LOOK of captions and the end card — only meaningful with captions:true or endCard:true, and only when the user described a look. Presets: editorial (a large elegant serif title mid-frame with a small italic line under it, no box), bold (tall condensed caps with a black outline), minimal (small lowercase near the bottom), handwritten (tilted marker), boxed (dark words on a white box), pill (the plain default). Pass a preset name, or an object with a preset plus overrides. A caption written \"TITLE · small line\" puts the part after the middle dot on a second line. An invalid field is refused by name before anything renders."
        +}
    • Changedreschedule_post1 field changed
      • changedInput schema / properties / optimizeCopy / description
        Previous value: -"fit the shared caption to each channel’s own rules at publish time wherever no per-channel caption was written (YouTube keyword title + structured description + tags, Instagram/TikTok hashtags, LinkedIn longer, X/Bluesky short, Pinterest keyword-rich); a channel with its own caption is left exactly as written. Send false to switch it off on this item."New value: +"fit the shared caption to each channel’s own rules when you schedule it (the fitted caption is stored on the scheduled post, so what you scheduled is what publishes) wherever no per-channel caption was written (YouTube keyword title + structured description + tags, Instagram/TikTok hashtags, LinkedIn longer, X/Bluesky short, Pinterest keyword-rich); a channel with its own caption is left exactly as written. Send false to switch it off on this item."
    • Changedschedule_post1 field changed
      • changedInput schema / properties / optimizeCopy / description
        Previous value: -"RECOMMENDED when one caption goes to several channels: fit the shared caption to each channel’s own rules at publish time wherever no per-channel caption was written — YouTube gets a keyword title, a structured multi-paragraph description and search tags; Instagram/TikTok hashtags; LinkedIn longer; X/Bluesky short; Pinterest keyword-rich. The angle and every claim stay the author’s; a channel with its own caption is left exactly as written. Off by default so nobody’s words are rewritten unasked."New value: +"RECOMMENDED when one caption goes to several channels: fit the shared caption to each channel’s own rules when you schedule it (the fitted caption is stored on the scheduled post, so what you scheduled is what publishes) wherever no per-channel caption was written — YouTube gets a keyword title, a structured multi-paragraph description and search tags; Instagram/TikTok hashtags; LinkedIn longer; X/Bluesky short; Pinterest keyword-rich. The angle and every claim stay the author’s; a channel with its own caption is left exactly as written. Off by default so nobody’s words are rewritten unasked."
  18. 3 tool updatesv0.1.217
    • Changedpost_to_meta1 field changed
      • changedInput schema / properties / trialReel / description
        Previous value: -"INSTAGRAM TRIAL REEL — publish this Reel to NON-FOLLOWERS ONLY at first, so a hook can be tested on a cold audience without spending it on the people who already follow the brand. Instagram then shows it to followers only if it graduates. MANUAL = the creator graduates it by hand in the Instagram app; SS_PERFORMANCE = Instagram graduates it automatically if it performs well. REELS ONLY and INSTAGRAM ONLY: an image, a carousel, a Facebook post or a Threads post is REFUSED BY NAME rather than quietly published as an ordinary post — a trial that silently goes to every follower is the exact opposite of what was asked for. Omit it for a normal Reel."New value: +"INSTAGRAM TRIAL REEL — publish this Reel to NON-FOLLOWERS ONLY at first (Instagram allows trials only on accounts above its follower threshold — about 1,000 followers; an ineligible account is refused by name and nothing is posted), so a hook can be tested on a cold audience without spending it on the people who already follow the brand. Instagram then shows it to followers only if it graduates. MANUAL = the creator graduates it by hand in the Instagram app; SS_PERFORMANCE = Instagram graduates it automatically if it performs well. REELS ONLY and INSTAGRAM ONLY: an image, a carousel, a Facebook post or a Threads post is REFUSED BY NAME rather than quietly published as an ordinary post — a trial that silently goes to every follower is the exact opposite of what was asked for. Omit it for a normal Reel."
    • Changedreschedule_post1 field changed
      • addedInput schema / properties / optimizeCopy
        Added value: +{
        +  "description": "fit the shared caption to each channel’s own rules at publish time wherever no per-channel caption was written (YouTube keyword title + structured description + tags, Instagram/TikTok hashtags, LinkedIn longer, X/Bluesky short, Pinterest keyword-rich); a channel with its own caption is left exactly as written. Send false to switch it off on this item.",
        +  "type": "boolean"
        +}
    • Changedschedule_post1 field changed
      • changedInput schema / properties / trialReel / description
        Previous value: -"INSTAGRAM TRIAL REEL — publish this Reel to NON-FOLLOWERS ONLY at first, so a hook can be tested on a cold audience without spending it on the people who already follow the brand; Instagram shows it to followers only if it graduates. MANUAL = the creator graduates it by hand in the Instagram app; SS_PERFORMANCE = Instagram graduates it automatically if it performs. REELS ONLY and INSTAGRAM ONLY: an image, a carousel, or a Facebook/Threads channel is REFUSED BY NAME rather than quietly published as an ordinary post — a trial that silently goes to every follower is the exact opposite of what was asked for, so Instagram must be one of the `channels` and the item must carry a video. Omit it for a normal Reel."New value: +"INSTAGRAM TRIAL REEL — publish this Reel to NON-FOLLOWERS ONLY at first (Instagram allows trials only on accounts above its follower threshold — about 1,000 followers; an ineligible account is refused by name and nothing is posted), so a hook can be tested on a cold audience without spending it on the people who already follow the brand; Instagram shows it to followers only if it graduates. MANUAL = the creator graduates it by hand in the Instagram app; SS_PERFORMANCE = Instagram graduates it automatically if it performs. REELS ONLY and INSTAGRAM ONLY: an image, a carousel, or a Facebook/Threads channel is REFUSED BY NAME rather than quietly published as an ordinary post — a trial that silently goes to every follower is the exact opposite of what was asked for, so Instagram must be one of the `channels` and the item must carry a video. Omit it for a normal Reel."
  19. 18 tool updatesv0.1.209
    • Addedcall_tool
    • Changedchange_voice1 field changed
      • changedInput schema / properties / voice / description
        Previous value: -"target narrator voice preset name, e.g. 'Aria', 'George', 'Rachel', 'Sarah', 'Brian', 'Charlotte' (defaults to a warm female read)"New value: +"target narrator voice preset name, e.g. 'Aria', 'George', 'Rachel', 'Sarah', 'Brian', 'Charlotte' (defaults to a warm female read). A saved VOICE CLONE of the user's own voice counts as a preset here — name it the way it is saved on their cast"
    • Addeddelete_linkedin_lead_subscription
    • Addedfind_creators
    • Addedfind_tools
    • Addedget_linkedin_lead
    • Addedlist_linkedin_lead_events
    • Addedlist_linkedin_lead_forms
    • Addedlist_linkedin_lead_subscriptions
    • Addedlist_linkedin_leads
    • Changedpost_to_meta2 fields changed
      • addedInput schema / properties / aiGenerated
        Added value: +{
        +  "description": "INSTAGRAM / FACEBOOK REEL — Meta’s is_ai_generated self-disclosure. OMIT IT and Hermoso decides from provenance: a Hermoso render is declared, media that came through upload_file or from an external URL (the user’s own photographs or footage) is NOT — a real photo must never carry Instagram’s “AI info” label. Pass true or false only to override: false strips the label from something Hermoso would otherwise declare, true declares a render the user uploaded themselves.",
        +  "type": "boolean"
        +}
      • changedInput schema / properties / altText / description
        Previous value: -"ACCESSIBILITY — the description screen readers announce, and what the platform otherwise auto-generates badly or not at all. Describe what is actually IN the picture, never the caption. ONE STRING describes the picture; on a CAROUSEL it describes EVERY slide. Pass an ARRAY of strings instead to describe each slide separately, aligned to the slide order — that is strictly better on a multi-slide post, because one sentence read out over six different pictures is wrong for five of them. More descriptions than pictures is refused rather than dropped. WHERE IT LANDS, per Meta’s own docs: INSTAGRAM image posts and the IMAGE slides of an Instagram carousel (up to 1000 characters; dropped rather than sent on a Reel or a video slide, which Meta do not support); FACEBOOK Page photos including every photo of an album, via alt_text_custom (Meta publish no length for it, so nothing is truncated); THREADS on a single-image or single-video post ONLY — Meta document no way to attach alt text to a Threads CAROUSEL slide, so a Threads carousel publishes undescribed and the reply says so rather than risking the whole post on a guess. (AI disclosure is separate and automatic — every Instagram post Hermoso publishes is flagged is_ai_generated, which is not a caller setting.)"New value: +"ACCESSIBILITY — the description screen readers announce, and what the platform otherwise auto-generates badly or not at all. Describe what is actually IN the picture, never the caption. ONE STRING describes the picture; on a CAROUSEL it describes EVERY slide. Pass an ARRAY of strings instead to describe each slide separately, aligned to the slide order — that is strictly better on a multi-slide post, because one sentence read out over six different pictures is wrong for five of them. More descriptions than pictures is refused rather than dropped. WHERE IT LANDS, per Meta’s own docs: INSTAGRAM image posts and the IMAGE slides of an Instagram carousel (up to 1000 characters; dropped rather than sent on a Reel or a video slide, which Meta do not support); FACEBOOK Page photos including every photo of an album, via alt_text_custom (Meta publish no length for it, so nothing is truncated); THREADS on a single-image or single-video post ONLY — Meta document no way to attach alt text to a Threads CAROUSEL slide, so a Threads carousel publishes undescribed and the reply says so rather than risking the whole post on a guess. (Meta’s AI disclosure defaults from PROVENANCE: a Hermoso render is declared is_ai_generated; media that came through upload_file or an external URL, i.e. the user’s own photos or footage, is NOT. Pass `aiGenerated` to force it either way.)"
    • Removedpost_to_reddit
    • Changedpost_to_tiktok1 field changed
      • addedInput schema / properties / aiGenerated
        Added value: +{
        +  "description": "TikTok’s is_aigc AI-generated-content label (video posts). OMIT IT and Hermoso decides from provenance: a Hermoso render is declared, a video that came through upload_file or an external URL (the user’s own footage) is NOT. true/false overrides.",
        +  "type": "boolean"
        +}
    • Changedpost_to_youtube3 fields changed
      • addedInput schema / properties / aiGenerated
        Added value: +{
        +  "description": "YouTube’s “altered or synthetic content” declaration (containsSyntheticMedia). OMIT IT and Hermoso decides from provenance: a Hermoso render is declared, a video the user uploaded through upload_file or from an external URL is NOT — real footage must not carry the label. true/false overrides.",
        +  "type": "boolean"
        +}
      • changedInput schema / properties / description / description
        Previous value: -"video description (≤5000 chars)"New value: +"REQUIRED in practice — what the video shows, a line about the brand, a link and a few #hashtags (≤5000 chars); YouTube search, suggested videos and the Shorts feed rank on it, so an upload with no description (and no caption) is refused"
      • changedInput schema / properties / title / description
        Previous value: -"video title (≤100 chars)"New value: +"REQUIRED in practice — the headline every search result and the Shorts feed shows (≤100 chars); an upload with no title is refused"
    • Changedreschedule_post1 field changed
      • addedInput schema / properties / aiGenerated
        Added value: +{
        +  "description": "INSTAGRAM / FACEBOOK REEL — Meta’s is_ai_generated self-disclosure. OMIT IT and Hermoso decides from provenance: a Hermoso render is declared, media that came through upload_file or from an external URL (the user’s own photographs or footage) is NOT — a real photo must never carry Instagram’s “AI info” label. Pass true or false only to override: false strips the label from something Hermoso would otherwise declare, true declares a render the user uploaded themselves.",
        +  "type": "boolean"
        +}
    • Changedschedule_post2 fields changed
      • addedInput schema / properties / aiGenerated
        Added value: +{
        +  "description": "INSTAGRAM / FACEBOOK REEL — Meta’s is_ai_generated self-disclosure. OMIT IT and Hermoso decides from provenance: a Hermoso render is declared, media that came through upload_file or from an external URL (the user’s own photographs or footage) is NOT — a real photo must never carry Instagram’s “AI info” label. Pass true or false only to override: false strips the label from something Hermoso would otherwise declare, true declares a render the user uploaded themselves.",
        +  "type": "boolean"
        +}
      • addedInput schema / properties / optimizeCopy
        Added value: +{
        +  "description": "RECOMMENDED when one caption goes to several channels: fit the shared caption to each channel’s own rules at publish time wherever no per-channel caption was written — YouTube gets a keyword title, a structured multi-paragraph description and search tags; Instagram/TikTok hashtags; LinkedIn longer; X/Bluesky short; Pinterest keyword-rich. The angle and every claim stay the author’s; a channel with its own caption is left exactly as written. Off by default so nobody’s words are rewritten unasked.",
        +  "type": "boolean"
        +}
    • Addedsubscribe_linkedin_leads
    • Addedupdate_saved_creator

TDQS

B3.3/5.0

Scored across 185 tools

Disambiguation2/5

The set contains many near-overlapping tools despite detailed descriptions: publish tools such as post_to_x vs post_x_article and post_to_linkedin vs post_to_linkedin_page, plus a large creation cluster (generate_video, render_ad, make_explainer, product_sizzle, stitch_video) and editing cluster (edit_video, restyle_video, recast_motion, multiply_ad, hook_variants, fix_beat). Aliases like clone_static/remix_static and generic access paths like store_get vs the typed getters add avoidable ambiguity. Descriptions help, but at 185 tools many boundaries are still easy to misselect.

Naming Consistency3/5

Names are overwhelmingly snake_case, so the mechanical convention is stable. However verb patterns are mixed across large families: post_to_, generate_, make_, render_, plan_, create_, list_, get_, update_, delete_, and noun-first/status names such as error_detail, billing_status, hook_variants, and hermoso_capabilities. It remains readable, but not a predictable verb_noun set.

Tool Count1/5

185 tools is an extreme mismatch for a coherent tool set and far beyond the 30-50 tool range where selection quality tends to hold up. The enable_tools/find_tools/call_tool routing is a thoughtful mitigation, but it does not change the fact that the surface itself is enormous. This is the dominant coherence problem.

Completeness4/5

The surface is extraordinarily broad, covering brand setup, research, image/video generation, publishing, scheduling, analytics, billing, files, connectors, memory, skills, playbooks, and creator/lead workflows. Minor gaps remain, such as direct published-post management/deletion tools referenced in descriptions but not present in the listed surface. Overall it is close to complete for a marketing/ads automation domain.

Maintenance

ActivityActive
ResponsivenessUnresponsive

Related MCP Connectors

Related MCP Servers

  • A
    license
    A
    quality
    D
    maintenance
    Provides Meta and Google Ads intelligence for AI assistants, enabling users to analyze performance, track competitors, and manage ad campaigns through natural language. It features 17 tools for generating creative concepts, scraping competitor ads, and performing deep account-level analysis.
    17
    MIT
  • A
    license
    Not graded
    quality
    A
    maintenance
    Manage ad campaigns across Meta, Google, and TikTok, create campaigns, analyze performance, spy on competitors, and generate AI creatives.
    14
    MIT
  • A
    license
    A
    quality
    A
    maintenance
    50 tools for Meta Ads campaign management, creative analysis, audience building, and conversion tracking, accessible to any MCP-compatible AI agent.
    69
    191 npm
    13
    MIT