Hermoso
OfficialHermoso — MCP, CLI y Skills
Dirige toda tu operación de marketing desde cualquier agente de IA: Claude Code, Claude.ai, Cursor, Codex, o tus propios scripts. Investiga los anuncios que ya están ganando en un mercado, genera anuncios de imagen y vídeo terminados (tu producto real compuesto, copia + CTA incluidos), publícalos en tus propios canales sociales y crea y gestiona las campañas publicitarias detrás de ellos — todo a través de herramientas MCP, una CLI o skills de Claude instalables.
718 herramientas. tools/list es siempre el conjunto autoritativo; hermoso_capabilities (gratis) devuelve el catálogo
de modelos en vivo con los costes exactos de crédito por renderizado, además del mapa completo de capacidades.
A qué se conecta. Plataformas publicitarias: Meta, Google Ads, TikTok Ads, LinkedIn Ads, Reddit Ads, X Ads, Pinterest Ads, Snapchat Ads, Microsoft Advertising, Apple Search Ads y ChatGPT Ads, además de feeds de productos en Google Merchant Center. Publicación y programación — diez canales: Facebook, Instagram, Threads, TikTok, YouTube, X, LinkedIn, Pinterest, Bluesky y Telegram. Mensajería: WhatsApp (le envías un mensaje a una persona, así que no es un undécimo canal de publicación). Investigación de anuncios: las bibliotecas de anuncios de Meta, Google y LinkedIn, además de TikTok, Instagram, YouTube, Threads y Reddit orgánicos. Analítica: Google Analytics 4, Google Search Console y los insights de publicaciones y campañas de cada plataforma conectada. Archivos: Google Drive, Sheets, Docs y OneDrive.
No es todo o nada. Investigación, creación, publicación/programación y gestión de anuncios son cuatro áreas independientes
— ninguna herramienta requiere que hayas usado otra primero. Publica o programa creatividades que ya tengas y no generes
nada aquí (upload_file convierte cualquier archivo local o externo en una URL que toda herramienta de publicación, programación y
construcción de anuncios acepta); crea y lee campañas en tus propias cuentas publicitarias con tu propia creatividad; investiga
competidores sin una marca redactada ni un canal conectado; o genera un archivo sin nada conectado y simplemente
descárgalo. Usa la pieza que necesites, o todo junto.
¿Qué superficie debería usar tu agente?
Dos formas, y la correcta se decide por lo que tu cliente puede hacer, no por cuál preferimos.
Tu cliente | Usa | Por qué |
Se ejecuta en un navegador — Claude.ai, ChatGPT, Claude Desktop | el conector alojado | No puede lanzar un proceso local, así que una URL es la única forma que tiene. Nada que instalar, ninguna clave que pegar, y el conjunto completo de herramientas llega con tu contexto de marca guardado. Esta es la respuesta correcta para estos clientes, no una inferior. |
Puede ejecutar un shell — Claude Code, Cursor, Codex, Cline, OpenClaw, Hermes, tus propios scripts | la CLI, | Un manifiesto de herramientas se carga en cada sesión, se llame o no a una herramienta. Un comando de shell no cuesta nada hasta que se ejecuta, y llega a todas las herramientas en lugar de al repertorio predeterminado. |
La diferencia medida (2026-08-27, contada como definiciones reales de herramientas en lugar de estimada por bytes):
herramientas en alcance | cargadas por sesión | |
Conector alojado, repertorio predeterminado | 306 | 181 713 tokens |
Conector alojado, | 718 | 472 062 tokens |
Servidor stdio ( | 306 | 181 713 tokens |
CLI | las 718 | 0 |
La CLI responde las mismas preguntas bajo demanda en su lugar, y solo cuando se le pregunta:
npx -y hermoso tools --search reddit # every matching tool, name + one line 2,459 tokens
npx -y hermoso tools plan_ad # one tool's full argument schema 633 tokens
npx -y hermoso call plan_ad --json '{"product":"…"}' # run itAsí que un agente de terminal llega a su primera llamada en aproximadamente 3,4K tokens con todo el repertorio en alcance, frente a
182K por una fracción del mismo. tools y tools <name> leen un registro incluido en el paquete — sin clave, sin
red, sin inicio de sesión — así que un agente puede explorar todo el producto antes de que nadie inicie sesión. Solo call gasta, y
solo eso necesita hermoso auth login una vez.
Ambos a la vez está bien, y es lo que sugerimos para Claude Code. Un hermoso auth login cubre la CLI y
permite que claude mcp add hermoso -- npx -y hermoso mcp recoja la clave sin bloque env, así que el agente puede recurrir a
una herramienta nativa cuando quiera resultados estructurados y al shell cuando quiera amplitud. Si solo quieres uno, elige la
CLI: cubre estrictamente más.
Cuando el conector sigue siendo el mejor intercambio en un cliente con shell: una sesión que va a hacer muchas
llamadas a una sola área. enable_tools({groups:['ads']}) activa la gestión de campañas en una sola llamada gratuita y las
herramientas pasan a ser nativas — sin comillas de shell, resultados estructurados. Un viaje de ida y vuelta de shell supera a cargar un grupo de 221K tokens
para una sola herramienta; lo contrario es cierto una vez que una sesión se asienta en esa área.
Related MCP server: Ad Library MCP
Tu agente puede registrarse solo
Un agente sin cuenta de Hermoso puede aprovisionar una, obtener su propia clave y estar renderizando anuncios en la misma sesión. Sin un humano en un navegador, sin ticket, sin esperas.
# 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"}'
# -> { "id": "cs_...", "checkout_url": "https://checkout.stripe.com/...", "claim_token": "hsc_..." }
# 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 }Esa clave hmk_ es la misma credencial que acepta todo lo demás en esta página: /v1, el servidor MCP, la CLI. Apunta
tu cliente a ella y toda la superficie está abierta.
Pagar es algo que un agente con capacidad de navegador ya puede hacer por sí mismo. El checkout es la página alojada de Stripe, así que
Claude en Chrome y clientes similares lo completan sin supervisión hoy. Todo lo demás es una transferencia de un clic: envía
checkout_url a quien tenga la tarjeta. La misma forma te cubre más tarde, una vez que estés en marcha: buy_credits
y upgrade_plan generan un enlace listo para pagar por más créditos o un plan más grande, y billing_status lee el
saldo en cualquier momento.
El camino agéntico requiere un plan de pago. Cualquiera de ellos. El plan gratuito está ahí para una persona que se registra en app.hermoso.ai, y pedirlo aquí devuelve una negativa que lo dice. Nada se crea hasta que el pago se completa, así que un registro no pagado no deja ninguna cuenta atrás y no cobra nada.
Una cosa todavía quiere a una persona, y vale la pena saberlo de antemano. Conectar una cuenta social o publicitaria significa una
pantalla de consentimiento OAuth, y una pantalla de consentimiento no se puede completar sin cabeza en ninguna plataforma. list_connectors
muestra lo que ya está conectado y lo que no. Todo lo demás se ejecuta sin navegador: investigación,
generación, publicación en un canal que ya está conectado, construcción de campañas, informes.
Las formas completas de solicitud y respuesta, además de todos los demás endpoints, están en el documento OpenAPI en app.hermoso.ai/openapi.json, servido en vivo desde la misma tabla que monta las rutas.
Instantáneo: el conector alojado de Claude.ai
Pega https://app.hermoso.ai/mcp en Claude → Configuración → Conectores → Añadir conector personalizado, aprueba con
tu cuenta de Hermoso, listo — el conjunto completo de herramientas con tu contexto de marca guardado, facturado a tu plan.
Inicio rápido para Claude Code (una línea)
Obtén una cuenta en app.hermoso.ai — incluye el nivel gratuito; los planes y créditos son los mismos que usa el Studio web. O salta el navegador por completo y deja que tu agente se registre solo en un plan de pago con
POST /v1/signup(arriba).Ejecuta una línea. Tu navegador se abre una vez para iniciar sesión. Nada que pegar, y ninguna clave termina en
.claude.json:
npm install -g hermoso && hermoso auth login && claude mcp add hermoso -- npx -y hermoso mcpPide lo que quieras, en tus indicaciones normales. Claude Code recurre a una herramienta, o ejecuta el comando
hermosoen tu terminal, lo que el trabajo necesite. Tú no escribes ninguno.
Las herramientas de campañas publicitarias y analítica permanecen fuera de la lista de herramientas hasta que las actives con enable_tools,
lo que la mantiene pequeña. En una máquina sin navegador, inicia sesión con hermoso auth login --token hmk_… usando una clave de
Configuración → Agentes y API, o salta el inicio de sesión y pasa la clave al cliente en su lugar:
claude mcp add hermoso -e HERMOSO_TOKEN=hmk_… -- npx -y hermoso mcpLa URL alojada también funciona en Claude Code, pero es el peor camino allí y vale la pena saber por qué:
claude mcp add --transport http hermoso https://app.hermoso.ai/mcp se acepta, y luego claude mcp list
informa ! Needs authentication porque el cliente no iniciará el flujo OAuth por sí mismo — tienes que abrir una
sesión, ejecutar /mcp, encontrar el servidor y pulsar Autenticar. Medido contra Claude Code 2.1.241 el 2026-08-23.
Tu agente ahora tiene el estudio completo con el contexto de tu espacio de trabajo: el perfil de marca, productos, logotipos y
memoria aprendida que configuraste en la aplicación web se aplican automáticamente (get_brand muestra lo que está guardado; omite brand en
plan_ad/plan_variations para usarlo). Los renderizados se facturan a tus créditos de Hermoso — los mismos precios que el Studio.
1. Servidor MCP (stdio) — Claude Code / Cursor / Codex
hermoso mcp ejecuta un servidor MCP stdio que expone el conjunto completo de herramientas. El paquete hermoso publicado significa que no hay clon —
npx -y hermoso mcp lo descarga y lo ejecuta. Inicia sesión una vez con la CLI y ninguna clave entra en ninguna configuración de cliente,
porque hermoso mcp lee el portador que hermoso auth login almacena:
npm install -g hermoso && hermoso auth login && claude mcp add hermoso -- npx -y hermoso mcpCursor / Codex — inicia sesión de la misma manera, luego añade a mcp.json (Codex usa el equivalente TOML). Elimina el bloque env
por completo si iniciaste sesión arriba; está ahí para CI, donde el proceso no puede leer tu directorio personal:
{ "mcpServers": { "hermoso": { "command": "npx", "args": ["-y", "hermoso", "mcp"],
"env": { "HERMOSO_API_BASE": "https://app.hermoso.ai", "HERMOSO_TOKEN": "<your token>" } } } }Luego pídele a tu agente: “Genera un anuncio de imagen con Hermoso.”
Qué cubren las 718 herramientas
Espionaje de anuncios / investigación — find_competitors, competitor_teardown, pull_competitor_ads, research_ads; las
bibliotecas de anuncios de Meta / Google / LinkedIn (search_meta_ads, search_google_ads, search_linkedin_ads); social
orgánico (search_tiktok, search_instagram, search_youtube, search_reddit, search_threads);
fetch_social_data, mine_angles, analyze_video, check_ad_policy, list_skills / get_skill.
Crear — draft_brand → plan_ad → render_ad (el pipeline de calidad del Studio: texto compuesto, voz limpia,
música, tarjeta final de marca), o generate_image / generate_video / generate_avatar (creadores UGC + sincronización de labios).
El elenco guardado del espacio de trabajo es reutilizable: list_creators devuelve cada creador guardado con su URL de retrato,
save_creator añade uno, delete_creator elimina uno — vuelve a pasar un retrato a generate_avatar / generate_video /
recast_motion y la MISMA persona protagoniza cada anuncio, en lugar de una cara nueva en cada renderizado.
También make_template_ad (formatos de anuncio HTML nativos), make_explainer, product_sizzle, make_thumbnail,
remix_static, recast_motion, reframe_video, upscale_video, dub_video, change_voice, finish_video,
fix_beat, stitch_video, clip_video, post_edit, además de plan_variations + score_ad para expandir y clasificar.
La duración es tuya para establecer: pasa durationSeconds a plan_ad y el guion gráfico se escribe para esa duración — una duración
que cabe en un clip del modelo de renderizado se renderiza como una sola toma continua, más larga se une a partir de actos (en un
modelo de clips de 15s, 40s = 15+15+10), nunca comprimida en el tiempo. Lo que cabe en un clip es el máximo del propio modelo, no un número
fijo: la mayoría de los modelos de vídeo limitan un clip a 15 segundos y el modelo de clip más largo toma 30 segundos en una sola toma
ininterrumpida con audio sincronizado nativo. hermoso_capabilities es la lista en vivo — duraciones, resoluciones y el
coste exacto de crédito de cada nivel — y nombrar ese modelo en model es cómo lo obtienes, ya que un renderizado sin nombre se
enruta por un grupo automático más estrecho.
Playground de modelos en bruto — el catálogo completo (más de 30 modelos de imagen / vídeo / voz / escritura, cada uno con su coste exacto en créditos por render) sin ningún marco publicitario: generate_image / generate_video con useBrand:false, generate_voice, generate_text.
Publica en tus propios canales — diez de ellos: Facebook, Instagram y Threads (post_to_meta), TikTok (post_to_tiktok), YouTube (post_to_youtube + update_youtube_video, youtube_video_insights, lectura/respuesta de comentarios), X (post_to_x, x_post_metrics, x_post_insights, x_mentions, list_x_dms, send_x_dm), perfil de LinkedIn y páginas de empresa (post_to_linkedin, post_to_linkedin_page), Pinterest (post_to_pinterest + tableros), Bluesky (post_to_bluesky, delete_bluesky_post, bluesky_post_metrics, además de list_bluesky_convos / read_bluesky_dm / send_bluesky_dm) y Telegram (post_to_telegram, delete_telegram_message, list_telegram_chats). schedule_post / list_scheduled / cancel_scheduled te dan un único calendario de contenidos sobre exactamente ese conjunto. upload_file permite importar cualquier medio externo o local, no solo los renders de Hermoso.
Publicar en X factura créditos por llamada a la API (X cobra por solicitud); una publicación con enlace cuesta 13× más que una sin él.
Retenido, y nombrado en lugar de ocultado: Google Business Profile está construido (post_to_google_business, reseñas, Q&A, insights) y no se ofrece — Google permite esa API por proyecto y la nuestra lee 0 QPM, así que cada llamada daría 403 para todos los usuarios. Está en el enum de canales de schedule_post y se rechaza al encolar.
Mensajes a clientes por WhatsApp — mensajería, no un undécimo canal de publicación: escribes a una persona, y nada de esto publica en un feed. list_whatsapp_accounts encuentra la Cuenta Comercial y sus números, list_whatsapp_templates / create_whatsapp_template / delete_whatsapp_template gestionan las plantillas que Meta revisa, y send_whatsapp_message envía una — con confirmación obligatoria, porque llega a un teléfono real y Meta factura a la empresa por la conversación. Dos límites que son hechos permanentes de la API de Meta y no algo pendiente: Hermoso no recibe webhooks de WhatsApp, así que no hay historial de mensajes que leer — no es una superficie de bandeja de entrada y list_inbox no lo cubre — y fuera de la ventana de 24 horas que se abre cuando el cliente escribe primero, WhatsApp acepta una plantilla APROBADA y nada más.
Gestiona los anuncios — árboles de campaña completos, creados en pausa y leídos antes de que se informe de nada, con cada cambio de gasto sujeto a confirmación, en once plataformas: Meta, Google Ads, LinkedIn Ads, Reddit Ads, Pinterest Ads, Microsoft Advertising, ChatGPT Ads (API de anunciantes de OpenAI), X Ads, TikTok Ads, Snapchat Ads y Apple Ads (Apple Search Ads en la App Store). Cada una tiene herramientas de listar + informar + crear + presupuesto/estado (p. ej. list_google_ads_campaigns, google_ads_report, create_google_ads_campaign, set_google_ads_budget, set_google_ads_status). Snapchat requiere un paso extra que las demás no: un anuncio apunta a un CREATIVE, y cada creative de Snapchat debe llevar un id de Perfil Público — constrúyelo con upload_snapchat_ads_creative.
Alimenta las superficies de compra — Google Merchant Center es el catálogo que anuncia una campaña minorista de Performance Max o Shopping (create_google_ads_performance_max_campaign acepta un merchantCenterId), y lo gestionas desde aquí: cuentas y estado de cuentas, fuentes de datos, alta / actualización / baja de productos, inventario por región, cuota, merchant_report para rendimiento a nivel de producto, notificaciones y fuentes de conversión, además del bucle de desaprobación — list_merchant_issues dice qué está mal y merchant_issue_help devuelve la solución documentada por el propio Google. Las promociones requieren que el comerciante se haya dado de alta en el programa de promociones de Google; sin ello Google rechaza esa sub-API directamente. Microsoft Merchant Center está cubierto con la misma forma (tiendas, catálogos, productos, incidencias) para Bing Shopping.
Mide lo que lograron los anuncios — Google Analytics 4 cierra el círculo. Todos los demás conectores de aquí informan de lo que un anuncio costó; este es el que informa de lo que hizo. analytics_report desglosa sesiones, usuarios, conversiones e ingresos por canal, fuente/medio, campaña, página de destino, país, dispositivo o fecha, de modo que la campaña que Hermoso construyó y los ingresos que generó están en una misma conversación. analytics_realtime muestra quién está en el sitio ahora mismo. Empieza en list_analytics_properties — las herramientas aceptan un id de propiedad numérico, no el ID de medición G-XXXXXXXXX de tu fragmento de seguimiento, y esto es lo que resuelve uno a partir del otro. También escribe, no solo lee: create_analytics_key_event marca un evento que GA4 ya recopila como evento clave — que es lo que lo hace importable a Google Ads como conversión — y create_analytics_custom_dimension registra un parámetro de evento para que los informes puedan desglosar por él, con list_analytics_definitions mostrando lo que la propiedad ya mide. Inicia sesión con la misma cuenta de Google que Google Ads, YouTube y Drive, pero es su propia conexión.
Solo GA4 — la API no tiene superficie de Universal Analytics. Una dimensión personalizada se puede archivar pero nunca eliminar, y una propiedad admite 50 de ámbito de evento.
Archivos — CRUD de Google Drive (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) y OneDrive (save_to_onedrive + CRUD completo).
Espacio de trabajo y cuenta — espacios de trabajo de marca (list_brands, create_brand, use_brand, update_brand, delete_brand — una cuenta alberga muchas marcas, así que una agencia gestiona a cada cliente desde aquí), memoria (remember, forget, list_memory), habilidades personalizadas (save_skill, get_skill, list_skills, delete_skill — la biblioteca única, que absorbió los antiguos personajes de AI-Employee), equipo (list_team, invite_member, remove_member, set_role), ajustes (get_settings, update_settings — incluido el idioma en el que se escriben todos los anuncios, guiones y planes), conectores (list_connectors, list_connector_accounts, set_connector_accounts, disconnect_connector) y facturación (hermoso_credits, billing_status, buy_credits, upgrade_plan, set_auto_reload), además de list_jobs / get_job para renders asíncronos.
Las cuentas de conector se eligen, no se adivinan. Una misma persona administra a menudo varias páginas de Facebook, clientes de Google Ads o páginas de empresa de LinkedIn. Solo las cuentas marcadas para una marca son utilizables — aplicado en el servidor, y una selección vacía no comparte nada. Vincular una cuenta nueva es el único paso que no es headless (es una pantalla de consentimiento OAuth, así que el usuario lo hace en la aplicación).
Los trabajos de render se ponen en cola en el servidor y se consultan hasta completarse, devolviendo una URL servida.
2. CLI — la vía de bajo coste en tokens para agentes de terminal
bin/hermoso.mjs expone todo el conjunto de herramientas MCP como comandos de subproceso, de modo que un agente puede invocar el shell en lugar de cargar un manifiesto de herramientas pesado.
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"Añade --json a cualquier comando para obtener salida en máquina.
Esos atajos son la vía común, no el límite. Todas las herramientas que tiene el servidor MCP también son accesibles aquí, incluidos los grupos de campañas publicitarias y de analítica que un conector deja fuera de su lista predeterminada:
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, shortercall pasa por el mismo handler, la misma validación de argumentos y las mismas puertas de confirmación/gasto que usa el servidor MCP — no hay una segunda implementación que pueda divergir. tools y tools <name> leen un registro incluido en el paquete, así que no necesitan clave, ni red, ni inicio de sesión.
3. Habilidades de Claude — comandos de barra que envuelven la CLI
skills/ contiene cuatro habilidades instalables: hermoso-generate, hermoso-ad-from-brand, hermoso-product-photoshoot, hermoso-research.
cp -r skills/* ~/.claude/skills/Después invoca /hermoso-ad-from-brand an ad for yourbrand.com — our hero product.
Configuración
Env | Significado |
| El origen de la API de Hermoso (por defecto |
| Clave de agente Bearer ( |
| Id del espacio de trabajo de marca, para cuentas con varios perfiles de marca |
| Solo para una marca que otra cuenta ha compartido contigo (un espacio de trabajo de equipo): el id de la cuenta propietaria. Defínelo junto con |
mcp/http.mjs es el transporte de conector remoto alojado (pega una URL en Claude.ai → Conectores). Se incluye en este repositorio por transparencia y se niega a montarse sin identidad autenticada — nunca gasto anónimo.
Licencia
MIT © Hermoso
Available Tools
204 toolsadd_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.
| Name | Required | Description | Default |
|---|---|---|---|
| auto | No | sentences (default) or words: timing unit for transcribed subtitles | |
| burn | No | false = only the .srt | |
| cues | No | your own lines in seconds, no overlap, max 90 chars, no emoji | |
| video | Yes | the video to subtitle | |
| textStyle | No | the look: words, a preset or fields; omit for the default | |
| wordsPerCue | No | words per subtitle line when auto is words |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already disclose that this is a non-read-only, open-world, non-idempotent but non-destructive operation. The description adds substantial behavioral context beyond annotations: no source video is cut or re-rendered, accepted inputs include files, URLs, platform pages, and social post links, no-speech cases are refunded, and work runs in the background and lands in the Library.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is dense but front-loaded, starting with the core action and output before moving into mode-specific details. Every sentence carries useful operational information, with no filler or repetition.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a six-parameter tool with no output schema, the description covers the essential behavior: outputs, styling defaults, cue mode, source types, refund behavior, and background/Library handling. It does not fully explain the asynchronous job result shape or how to retrieve the finished output, which keeps it short of complete.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so parameter syntax is already well documented. The description still adds meaningful semantics by explaining default textStyle behavior, when to set textStyle, the distinction between sentence timing and auto:'words', how cues override transcription, and that burn:false returns only the .srt.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific verb and resource: burn subtitles into a video and also get the .srt. It distinguishes the operation from sibling video tools by emphasizing that nothing is cut or re-rendered and by describing subtitles as the output rather than a general edit.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It gives clear conditional guidance for the main modes: set textStyle only when the user describes a look, use auto:'words' for phrase-level timing, pass cues for exact untranscribed lines, and use burn:false for only the .srt. It does not explicitly compare this tool with sibling alternatives such as edit_video or dub_video, but the internal usage guidance is strong.
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.
| Name | Required | Description | Default |
|---|---|---|---|
| url | Yes | the video URL (a served /generated/ path or a public http(s) video) | |
| words | No | true | 'only': each spoken word's start/end | |
| frames | No | also return the frames as images | |
| anchors | No | {afterWord|beforeWord|atWord, occurrence?, offset?} -> s |
TDQS
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.
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.
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.
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.
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.
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.
| Name | Required | Description | Default |
|---|---|---|---|
| text | No | text to append at the end of the doc (optional when a dropdown is passed) | |
| dropdown | No | a dropdown chip to append after the text | |
| documentId | Yes | the document id from create_doc |
TDQS
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.
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.
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.
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.
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.
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_notion_pageAdd to a Notion pageAInspect
Add to the END of an existing Notion page (pageId or its link, from list_notion_pages): markdown content, a swipefileCollection, or includeSchedule:true. Nothing already on the page changes. Free.
| Name | Required | Description | Default |
|---|---|---|---|
| pageId | Yes | page id or link | |
| content | No | markdown to add | |
| includeSchedule | No | true appends the scheduled posts too | |
| swipefileCollection | No | swipefile collection name or id to append, with each ad's creative, copy, dates and link |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare destructiveHint=false and openWorldHint=true. The description reinforces these with 'Nothing already on the page changes' and adds the cost signal ('Free'), which are useful beyond the annotations. Return behavior isn't described, but with no output schema that omission is minor.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loads the core action, then the modes, then the non-destructiveness and cost. Dense with zero wasted words.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 4-param write tool with a fully covered schema and clear annotations, the description conveys scope, non-destructiveness, source of pageId, and cost. It doesn't describe the response, but with no output schema that's an acceptable gap.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
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 real value by indicating pageId's source ('from list_notion_pages') and framing the three optional modes; the swipefile detail largely restates the schema, so it doesn't rise to a 5.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb (Add/append), resource (existing Notion page), and scope (to the END). The 'Nothing already on the page changes' clause distinguishes it from a save/overwrite operation like save_to_notion, and the three append modes are enumerated.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Tells the agent where pageId comes from (from list_notion_pages) and what can be appended (markdown, swipefileCollection, includeSchedule). Clear usage context, but it doesn't explicitly name alternatives like save_to_notion or state when not to use it.
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.
| Name | Required | Description | Default |
|---|---|---|---|
| rows | Yes | rows to append — array of row arrays | |
| range | No | range to append at (default A1 / first sheet) | |
| spreadsheetId | Yes | the spreadsheet id from create_sheet |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=false, destructiveHint=false, openWorldHint=true, and idempotentHint=false. The description adds useful access constraints and id-discovery paths, but it does not describe append semantics such as where rows land or what happens on repeat calls.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is compact and front-loads the core action before adding access and id-source guidance. The sentence is dense but every clause supports correct invocation.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a simple append mutation, the description covers reachability, spreadsheetId sourcing, and row shape while annotations cover safety and schema covers parameters. It does not explain return behavior, but no output schema exists and this is a minor omission.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so all three parameters are already documented. The description reinforces rows as an array of row arrays and clarifies that spreadsheetId can come from create_sheet or list_drive_files, but it omits the optional range parameter.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific verb and resource: appending rows to a Google Sheet. It also clarifies reachable-sheet scope, though it does not explicitly distinguish append behavior from update_sheet or other sheet-writing siblings.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It gives clear usage context: the sheet must be one Hermoso created or one the user handed over via the Google file picker, and it names create_sheet and list_drive_files as sources for spreadsheetId. It lacks explicit when-not guidance against update_sheet or clear_sheet_range.
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 postsAIdempotentInspect
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 has 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.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | how many posts this page (default 50, max 200) | |
| cursor | No | resume from a previous run | |
| channel | Yes | which channel to import from | |
| confirm | No | actually import — omit for a dry run that only quotes the cost | |
| accountRef | No | which Page / account, when the profile has more than one |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Beyond the annotations (readOnlyHint false, idempotent, openWorld, non-destructive), the description discloses the DRY-RUN default, that a cost quote is returned, that runs are bounded and resumable via a cursor, and that the operation is free. It also explains the downstream data-model consequence — imported posts are labelled 'backfilled', weaker evidence, and stay UNATTRIBUTED unless matched by asset or caption. That 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.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with purpose and a bolded 'BOUNDED, RESUMABLE AND QUOTED' section, then caveats. Dense rather than padded, though the long parenthetical cataloguing LinkedIn, X, Google and Telegram costs some length for information that only matters if an agent tries an unsupported channel.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
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 the essentials an agent needs to call it safely: default safe mode, the confirm gate, pagination/resume path, which channels work, and what the imported data means downstream. Remaining details (return shape beyond counts and cost) are adequately implied by the quote step.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
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 on top: confirm:true is the switch from quote to import, and the cursor is described as coming back from a prior run and being passed in to continue — a usage pattern the schema only hints at. The limit and accountRef parameters are left entirely to the schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific verb and resource — importing a brand's past posts from a named channel into the performance record — and frames the outcome ('so which hook works can draw on history'). It implicitly contrasts with recorded posts but never names a sibling tool such as list_published_posts, so differentiation is by concept rather than by routing.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
When-to-use is clear (to give hook analysis historical depth) and when-not is unusually thorough: each unsupported channel is listed with the concrete reason (LinkedIn grant, X per-read billing, Google Business insights discontinued, Telegram Bot API limits). It also states the dry-run default and confirm:true to actually import. What's missing is explicit direction to an alternative tool for viewing already-recorded posts.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
billing_statusBilling statusARead-onlyIdempotentInspect
Show this account's billing at a glance: current plan (quote plan.priceUsd per plan.period), 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. plan.memberRate: media models at cost + 10%; plan.legacy: an older plan kept at its price. posting: this month's posting allowance ("N of 10 posts used this month" on Free, unlimited on every paid plan).
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare read-only, idempotent, and non-destructive behavior, but the description adds substantial context beyond them: billing permission visibility, member-scope limitations, the risk of falsely telling a member no card is on file, and how memberRate, legacy plans, and posting allowances should be interpreted. This is exactly the kind of behavioral caveat an agent needs for a billing-status tool.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is front-loaded with the purpose and permission-critical caveats, and every clause carries meaning. It is dense and long, with multiple parenthetical clarifications, but the length is justified by the breadth of billing states and the member-scope warning.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no input schema and no output schema, the description must explain what the tool returns and what the agent should infer from it, and it does so thoroughly. It covers plan pricing interpretation, credit balance, auto-reload/card visibility, admin rights, and posting allowances, which is complete for this read-only status tool.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
There are zero input parameters, so the baseline is 4; no parameter semantics are needed. The description instead spends its space clarifying return semantics, which is useful but does not change the input-parameter score.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
It names the specific resource (this account's billing) and enumerates the exact fields returned: plan, credit balance, auto-reload, card on file, and admin rights. This clearly distinguishes it from sibling tools like upgrade_plan, set_auto_reload, 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.
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 or set_auto_reload to know what is possible, and explains that members have read-only billing. The shared-workspace member limitation is called out with concrete guidance on what to say and not say.
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 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.
| Name | Required | Description | Default |
|---|---|---|---|
| pack | No | the pack id to buy (e.g. pack-2k) — omit to list the available packs first | |
| confirm | No | set true to actually charge the saved card for `pack` (required for the one-click charge; ignored on the checkout-link path) | |
| quote_token | No | the 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
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Goes well beyond the annotations: discloses that the charge is instant with no redirect, that the card is persisted for one-click after the first purchase, that quote_token has a 10-minute validity window, and that money is actually moved. The note that a same-token retry is idempotent is a refinement of idempotentHint:false rather than a contradiction — fresh quotes can still double-charge.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded and logically ordered through the three paths, but the long all-caps paragraph about POSTing to /api/billing/machine-payment describes a raw HTTP lane this tool cannot invoke, so it is largely unusable noise inside a tool description. The core flow could be stated in half the words.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
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 comes back on each path — the pack list (id · credits · price), the quoted charge amount, or a checkout URL. Combined with the annotations and the fully described schema, an agent has everything needed to call correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the individual fields are already documented; the description earns extra credit by explaining the two-step sequencing (quote → confirm with the binding token) that the flat schema cannot express. It does not, however, add format or validation detail beyond the schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource — topping up credit packs — and explicitly bounds the scope ('Packs only; subscriptions are managed by a person in Billing'), which separates it from upgrade_plan and set_auto_reload. An agent knows exactly what this tool transacts and what it does not.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives a complete decision tree: no argument lists packs, `pack` alone quotes, `pack` + confirm + quote_token charges, and a missing saved card falls back to a Stripe checkout URL. It also names the prerequisites (saved card, billing-admin rights) and points to set_auto_reload as the preventive alternative.
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 nameADestructiveInspect
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.
| Name | Required | Description | Default |
|---|---|---|---|
| args | No | the tool's arguments as an object, exactly as its own schema takes them | |
| name | Yes | the tool name exactly as find_tools returned it, e.g. create_meta_lead_form |
TDQS
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.
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.
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.
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.
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.
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 postADestructiveIdempotentInspect
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).
| Name | Required | Description | Default |
|---|---|---|---|
| id | Yes | the scheduled post id from list_scheduled | |
| brand | No | 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. |
TDQS
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.
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.
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.
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.
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.
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.
| Name | Required | Description | Default |
|---|---|---|---|
| video | Yes | the source video URL | |
| voice | No | 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 |
TDQS
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.
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.
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.
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.
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.
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.
| Name | Required | Description | Default |
|---|---|---|---|
| copy | Yes | the ad copy / script / on-screen text to check | |
| claims | No | the claims / proof points the ad makes | |
| category | No | the product category — helps pick the relevant policy pages | |
| imageDescription | No | a description of the creative / image when relevant |
TDQS
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.
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.
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.
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.
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.
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 SheetADestructiveIdempotentInspect
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.
| Name | Required | Description | Default |
|---|---|---|---|
| range | Yes | the range to clear, e.g. "A2:D50" or "Sheet1!A2:D50" | |
| confirm | No | true clears the range; call without it first to get the filled-cell count | |
| sheetUrl | No | Google Sheets link, instead of spreadsheetId | |
| confirmCells | No | echo back the filled-cell count the unconfirmed call reported | |
| spreadsheetId | No | Google spreadsheet id; use this or sheetUrl |
TDQS
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 substantial context beyond them: a two-phase dry-run/confirm flow, the fact that no default range exists, that the clear is read back and only reported confirmed if the range is genuinely empty, and the rationale for the count echo as a guard against mis-typed ranges. This is exactly the additive behavioral disclosure the lower bar asks for.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The critical information — destructive two-phase confirm flow — is front-loaded and the sentences are largely purposeful. It is slightly padded by editorial lines ('The echo is not ceremony: it is what catches naming A1:Z1000...'), which are persuasive but not strictly operational, keeping it short of a 5.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
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 supplies the safety flow, the expected return of the unconfirmed call (filled-cell count), the confirmation semantics, and the alternative for tab removal. An agent has everything needed to invoke it correctly and safely; no material gap remains.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
With 100% schema description coverage the baseline is 3, and the description adds real meaning on top: it explains the two-step interaction between confirm and confirmCells (the count must be echoed back from the first call). It does not separately explain sheetUrl vs spreadsheetId, but the schema covers those adequately, so this is a modest value-add rather than a full parameter walkthrough.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('Empty a range of cells in a Google Sheet') plus a key scope constraint ('leaving the rows themselves in place') that separates it from tab deletion. It explicitly routes the remove-a-whole-tab case to manage_sheet_tabs, so an agent can distinguish it from that sibling 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.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description gives a complete call sequence: first call without confirm to obtain the filled-cell count, show it to the user, get an unambiguous yes, then call again with confirm:true and confirmCells. It also names the alternative (manage_sheet_tabs) for a whole-tab removal. Nothing is left to inference about when and how to use it.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
click_to_adPlan a video ad from a product linkAInspect
Paste a product page link (Shopify, Amazon or any store page) and get a creator-style video ad planned for that product. Hermoso reads the page (name, photos, what it says about the product, price), saves the product to the brand's catalog, checks the page's real product photo and pins it to this ad (the brand's saved default photo is not changed), then plans the ad. A store that hides its page from servers is read in a real browser; failing that the name comes from the link and the photo from a search, and the reply says so. Nothing renders here: show the user the concept, then call render_ad with the returned creative (dryRun:true first for the exact price). Works with or without a saved brand.
| Name | Required | Description | Default |
|---|---|---|---|
| say | No | what the creator should say, or the angle, in the user's words | |
| url | Yes | the product page link, https://… | |
| style | No | ugc_selfie (talking to camera, default), ugc_unboxing, ugc_demo (how-to), ugc_tryon (try-on or haul), ugc_product (product close-ups, no face), ugc_shopping (in-store find), ugc_delivery, transformation (before and after), day_in_life (at home), grwm (get ready with me), ugc_addiction (obsessed with it) | |
| language | No | language for the script and copy, default English | |
| durationSeconds | No | length in seconds, 4-180; default 15 |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Beyond annotations, it discloses meaningful behavior: it reads the page, saves the product to the brand catalog, pins the real product photo to this ad without changing the brand's default, falls back to a real browser and then to link/search data, and says the reply notes that fallback. This adds substantial context that annotations alone (readOnlyHint=false, destructiveHint=false, openWorldHint=true) do not provide.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The core action is front-loaded in the first sentence, followed by side effects and the render_ad handoff. It is somewhat dense with run-on clauses, but nearly every sentence carries useful behavioral detail rather than filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For an open-world mutation tool with no output schema, the description covers side effects (catalog save, photo pinning), fallback behavior for hidden pages, and the required next step of calling render_ad. It does not describe the shape of the returned creative, but it does enough to let an agent use the tool correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
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, say, style, language, and durationSeconds, including the style enum. The description reinforces the url concept but does not add meaning to the other parameters, so the baseline of 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific verb and resource: paste a product page link and get a creator-style video ad planned for that product. It distinguishes itself from siblings by explicitly saying 'Nothing renders here' and routing rendering to render_ad, so an agent can tell it apart from generate_video or render_ad 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.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It gives clear usage context (paste a product link, works with or without a saved brand) and a concrete next step: show the concept, then call render_ad with the returned creative, using dryRun:true first for the exact price. It does not name alternatives such as plan_ad or product_sizzle, so the routing is strong on the follow-up but incomplete on sibling selection.
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.
| Name | Required | Description | Default |
|---|---|---|---|
| count | No | how many clips to cut, 1-8 (default 4) | |
| video | Yes | the 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 | |
| captions | No | burn 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. | |
| aspectRatio | No | clip shape: '9:16' (default), '1:1', '16:9', any 'W:H', or 'keep' for the source framing |
TDQS
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.
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.
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.
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.
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.
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.
| Name | Required | Description | Default |
|---|---|---|---|
| brandId | No | a profile id/name from list_brands to clone for; omit to use the active profile | |
| imageUrl | Yes | 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. |
TDQS
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.
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.
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.
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.
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.
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.
| Name | Required | Description | Default |
|---|---|---|---|
| url | Yes | the video to clone — a TikTok / Instagram Reel / Facebook / X / YouTube link, or a direct https video file URL | |
| brand | No | brand name or profile object; OMIT to use the workspace's saved brand (see get_brand) | |
| changes | No | what 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") | |
| product | No | what the new ad sells, plus any angle or offer; omit to use the saved brand's product | |
| language | No | language for the new ad's script and copy — default English | |
| durationSeconds | No | override the length in seconds; omit to match the original |
TDQS
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.
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.
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.
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.
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.
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 performedAIdempotentInspect
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.
| Name | Required | Description | Default |
|---|---|---|---|
| max | No | cap how many posts to read in this run (default 40) | |
| brand | No | 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. | |
| remeasure | No | 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. | |
| includeMetered | No | also read X, which BILLS CREDITS per post read — ask the user first |
TDQS
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.
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.
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.
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.
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.
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 the same ad-library reads search_meta_ads does, longest-running = proven winners). Auto-tailors the white space + counter-plays to YOUR saved brand. Spends credits (free when you pass ads).
| Name | Required | Description | Default |
|---|---|---|---|
| ads | No | ad objects to tear down (from pull_competitor_ads / search_meta_ads). Omit to auto-pull their Meta ads first. | |
| language | No | output language (default English) | |
| competitor | Yes | the competitor to tear down |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only declare readOnlyHint=false and openWorldHint=true; the description goes far beyond by disclosing the credit cost model ('Spends credits', 'free when you pass ads') and that the auto-pull consumes the same ad-library reads as search_meta_ads. It also discloses the personalization behavior (auto-tailors white space and counter-plays to the saved brand), which materially affects output.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with the deliverable list before the operational contract, and every clause carries information (outputs, input contract, cost, personalization). It is a single dense paragraph with heavy ALL-CAPS emphasis, which aids scanning but makes it slightly harder to parse than a two- or three-sentence layout.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
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 report sections the agent should expect back. For a 3-parameter tool with one nested object, the paid-vs-free branch, data provenance, and per-brand personalization are all covered — 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.
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 non-obvious semantics: that the `ads` array must be raw objects from a prior call and that its absence triggers a paid auto-pull, plus that longest-running ads are treated as proven winners. The `domain` sharpening hint is already in the schema, so the gain is real but partial.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Opens with a specific verb+resource ('Tear a competitor's ad strategy down') and enumerates the concrete deliverables (hook mix, longest-running themes, white space, counter-plays, avoid-list). This clearly separates it from sibling ad-search tools like search_meta_ads and pull_competitor_ads, which only retrieve ads rather than produce a playbook.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The CONTRACT sentence states the branch condition explicitly: supply `ads` to analyze exactly those, or omit it to auto-pull. It also names the sibling tools that produce compatible input (pull_competitor_ads / search_meta_ads), so the agent knows both when to use this and where upstream data comes from.
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 accountADestructiveIdempotentInspect
Connect an account that links with a KEY rather than a sign-in screen, from here, with no browser: AppLovin Ads, Stripe, Intercom, Mailchimp, Klaviyo, Vercel, ChatGPT Ads, Apple Ads, Bluesky, Telegram, Bing Webmaster Tools, PostHog, Mixpanel, Amplitude, WordPress, 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*} · intercom {accessToken*, region} · mailchimp {apiKey*, server} · klaviyo {apiKey*} · vercel {token*, team} · 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} · wordpress {siteUrl*, username*, appPassword*} · slack {webhookUrl, announceWebhookUrl} · discord {webhookUrl, announceWebhookUrl} · webhook {webhookUrl*}.
| Name | Required | Description | Default |
|---|---|---|---|
| fields | No | that provider's own field names and values, e.g. {"apiKey":"…"}; the names for each provider are in the description | |
| provider | Yes | the connector id: applovin_ads, stripe, intercom, mailchimp, klaviyo, vercel, openai_ads, apple_ads, bluesky, telegram, bing_webmaster, posthog, mixpanel, amplitude, wordpress, slack, discord, webhook |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already flag a destructive, open-world, idempotent write, but the description adds substantive behavior they cannot convey: the key is validated live with the provider before anything is persisted, nothing is saved on failure, the reply is read back from the saved connection, and Hermoso never echoes a submitted key. It also documents the non-obvious Apple Ads branch where a signing key pair and setupToken are generated first.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Well front-loaded — purpose first, then the security/alternative guidance, then field specs. But the body is a single run-on paragraph with the provider roster enumerated twice (display names in prose, ids in the field list), and the key-handling warning interleaved with setup instructions; bullets or a short structured layout would read faster without losing content.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
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 what comes back (the saved connection read back from the provider) and the Apple Ads setupToken round-trip. For a mutating, open-world tool with nested parameters, an agent has everything needed to call it correctly and to explain the privacy consequence to a user.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, but the schema's 'fields' object is generic (string→string), so the per-provider field lists with '*' required markers add real meaning that the schema cannot express. Small gap: the display names in the prose (AppLovin Ads, ChatGPT Ads, Bing Webmaster Tools) are only implicitly mapped to their ids (applovin_ads, openai_ads, bing_webmaster) via the later field dump.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('Connect an account that links with a KEY rather than a sign-in screen') and enumerates the exact provider set covered. It also draws the boundary that matters most: OAuth-based accounts 'cannot be connected here', which the agent cannot infer from the name alone.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives explicit routing instructions: 'OFFER BOTH WAYS AND LET THE USER CHOOSE', naming the in-app alternative (Workspace > Connectors, or the one-click link) and the privacy tradeoff that selects it. It also states the when-not case (OAuth providers) and prescribes the fallback — answering with the sign-in link to hand the user.
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 JPGAInspect
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.
| Name | Required | Description | Default |
|---|---|---|---|
| width | No | REQUIRED for jpg — output width in pixels | |
| fileId | Yes | the OneDrive item id, from list_onedrive_files | |
| format | No | default pdf | |
| height | No | REQUIRED for jpg — output height in pixels |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Adds substantial context beyond the annotations: conversion happens on Microsoft's servers with no local re-encoding, JPG requires both width and height or Microsoft rejects the call, and the returned URL is durable while Microsoft's own link expires in minutes. This is exactly the operational detail an agent needs and cannot get from readOnlyHint/destructiveHint alone; nothing contradicts the annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loads the core action and the JPG constraint is capitalized and easy to spot. Some enumeration of formats and ad-copy flourishes ('hand someone a PDF of a spreadsheet') run long, but they carry real capability information rather than filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
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 fills the gap by describing the return value (a durable Hermoso URL usable in a render or post) and warns against the expiring Microsoft link. Prerequisites, the conditional parameter rule, and format support are all covered.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so baseline is 3; the description goes one step further by explaining *why* width and height matter (Microsoft refuses the call without both), which reinforces the hard conditional requirement rather than merely restating it. It says little extra about fileId or the format default beyond the schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource (turn a OneDrive file into PDF or JPG) and immediately scopes it to files already in OneDrive, which distinguishes it from upload/import siblings like save_to_onedrive and get_onedrive_file. The list of supported source formats makes the tool's capability unmistakable.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives concrete when-to-use scenarios (client deck to ad images, PSD/raw to JPG, spreadsheet to PDF) and names the prerequisite (OneDrive connected, no new permission). The 'nothing else in this product can open' framing implicitly routes raw/PSD/AI cases here rather than to generate_image or edit_image.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
create_brandCreate a profileAIdempotentInspect
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. Creating it is free. Drafting a NEW brand into it (draft_brand) needs the credits for its research on the balance, about 100; with fewer, draft_brand refuses before anything runs or is charged and says how many it needs and how many the account has.
| Name | Required | Description | Default |
|---|---|---|---|
| name | Yes | the name for the new profile (a brand, client or creator name) | |
| activate | No | switch this connection to the new profile (default true); everything you do next scopes to it |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations cover the safety profile (readOnlyHint=false, idempotentHint=true, destructiveHint=false), and the description adds real behavioral context beyond them: profile-level isolation ('nothing leaks between them'), the dedupe rule that re-running with the same name returns the existing profile, and the fact that creation is free while a following draft costs credits and refuses before charging.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with the core action and alternatives, and the idempotency and cost notes are valuable. It slips slightly: the long inventory of profile-scoped resources (memory, swipefile, Library, avatars, skills, playbooks, connectors) is expendable, and the credit/refusal logic for draft_brand is fairly verbose for a create tool.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
No output schema, so the description carries the return semantics and does so adequately (returns the existing profile when the name repeats). With safety annotations present, the description fully covers what an agent needs: purpose, alternatives, isolation, idempotency, and cost prerequisites.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
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 to both parameters: 'switch to it' clarifies that activate defaults to making the new profile the active scope, and the name-based dedupe semantics change how 'name' behaves on repeats (returns an existing profile rather than erroring or duplicating).
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a precise verb + resource ('Add a NEW profile') and enumerates what counts as a profile (brand, client, creator, personal workspace), which no sibling tool does. It explicitly distinguishes itself from draft_brand and update_brand, so an agent can pick it 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.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives explicit when-to-use routing: use draft_brand to FILL the profile and update_brand to EDIT it, with this tool only creating. It also states the prerequisite (credits for the subsequent draft, ~100) and the failure mode when the requirement is unmet, which is the kind of guidance agents need before invoking.
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).
| Name | Required | Description | Default |
|---|---|---|---|
| text | No | body text to insert | |
| title | No | document title |
TDQS
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.
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.
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.
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.
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.
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).
| Name | Required | Description | Default |
|---|---|---|---|
| name | Yes | folder name | |
| parentId | No | parent folder id for a nested folder (default: Drive root) |
TDQS
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.
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.
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.
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.
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.
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.
| Name | Required | Description | Default |
|---|---|---|---|
| name | Yes | folder name | |
| parentId | No | parent folder id for a nested folder (default: OneDrive root) |
TDQS
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.
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.
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.
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.
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.
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).
| Name | Required | Description | Default |
|---|---|---|---|
| rows | No | rows to write — array of row arrays; first row = headers | |
| title | No | spreadsheet title |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The annotations already declare that this is a non-read-only, non-idempotent, open-world creation tool. The description adds useful context beyond that: it returns the spreadsheet id and URL, rows can optionally be filled, and Google Drive must be connected. It still does not cover failure modes such as duplicate titles or permission errors.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is front-loaded with the core action, then efficiently covers row formatting, return values, and the connection prerequisite. Every sentence adds practical value with no filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a simple two-parameter creation tool with annotations and no output schema, the description is complete: it explains the output, optional row filling, and the required Google Drive connection. An agent has enough information to invoke the tool correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
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 repeats the rows format and header convention without adding meaningful semantics 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.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific verb and resource: create a new Google Spreadsheet in the user’s Drive. It is clear what the tool does, but it does not explicitly differentiate itself from related siblings such as create_doc, save_to_drive, or append_to_sheet.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The examples — exporting a swipefile, ad list, or performance report — imply when the tool is useful, and the Google Drive connection prerequisite is stated. However, it does not explicitly say when to use this instead of append_to_sheet, update_sheet, create_doc, or save_to_drive.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
delete_brandDelete a profileADestructiveIdempotentInspect
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.
| Name | Required | Description | Default |
|---|---|---|---|
| brand | Yes | profile id or exact name from list_brands | |
| confirm | No | REQUIRED true — this destroys the whole profile and cannot be undone | |
| confirmName | No | the 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 | |
| confirmConnectors | No | the 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
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.
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.
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.
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.
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.
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 creatorADestructiveIdempotentInspect
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.
| Name | Required | Description | Default |
|---|---|---|---|
| id | Yes | the creator id (from list_creators) | |
| brand | No | profile id/name from list_brands, this call only |
TDQS
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.
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.
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.
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.
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.
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 fileADestructiveIdempotentInspect
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.
| Name | Required | Description | Default |
|---|---|---|---|
| fileId | Yes | the Drive file id | |
| confirm | No | REQUIRED true | |
| permanent | No | true = delete forever; default trashes (recoverable) |
TDQS
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.
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.
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.
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.
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.
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_library_itemDelete from LibraryADestructiveIdempotentInspect
PERMANENTLY delete images/videos from this brand's Library: the entry and the stored file. Pass ids (L0… from list_library) or urls. Without confirm it lists what would be deleted; ask the user, then call again with confirm:true. Free.
| Name | Required | Description | Default |
|---|---|---|---|
| ids | No | Library ids from list_library | |
| urls | No | file links | |
| confirm | No | true deletes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already flag destructiveHint=true and idempotentHint=true, so the description doesn't need to restate danger. It adds the non-obvious detail that the underlying stored file is deleted too, not just the entry, and that unconfirmed calls are safe previews — behavior annotations don't convey. Lacks mention of irreversibility/restore options, 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.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three tight sentences: destruction scope first, accepted inputs second, the confirm workflow third, plus a one-word cost note. Nothing is redundant with the title or schema, and the most consequential fact leads.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 3-param destructive tool with no output schema, it covers what is destroyed, how to preview, how to commit, which sibling supplies ids, and that it costs nothing. The preview return value is described informally, which is sufficient given no output schema exists.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so baseline is 3. The description exceeds it by explaining the tri-state meaning of confirm: absent = preview of what would be deleted, true = actual deletion, which the schema's 'true deletes' barely implies. It also notes ids/urls are alternatives rather than both required (consistent with 0 required params).
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb (PERMANENTLY delete) and a specific resource scope (images/videos from this brand's Library), and clarifies that both the library entry and the stored file are removed. This distinguishes it cleanly from siblings like delete_drive_file, delete_onedrive_file, and delete_brand.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives the exact workflow: call without confirm to preview, ask the user, then call again with confirm:true. It also names list_library as the source of ids, so the agent knows the prerequisite call and the safe sequence rather than just the tool's existence.
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 webhookADestructiveIdempotentInspect
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.
| Name | Required | Description | Default |
|---|---|---|---|
| pageId | No | the company Page that owns the form — from list_linkedin_pages; omit when one Page is shared | |
| adAccountId | No | read forms owned by an AD ACCOUNT instead of a Page | |
| subscriptionId | Yes | webhook id from list_linkedin_lead_subscriptions |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare destructiveHint=true, idempotentHint=true, readOnlyHint=false, and openWorldHint=true. The description usefully adds that leads remain unaffected and readable, only delivery stops, and notes the action is read back from LinkedIn and free, which clarifies blast radius 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.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is front-loaded with the core action and then adds only the effect scope and cost context. Every sentence carries useful information for deciding whether to invoke the tool.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a destructive webhook-removal tool with no output schema, the description covers what is removed, what is left intact, where the ID comes from, and cost. It could be slightly stronger by noting reversibility or prerequisites, but the annotations and schema cover most remaining behavioral needs.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the schema already fully documents subscriptionId, pageId, and adAccountId. The description repeats that subscriptionId comes from list_linkedin_lead_subscriptions but does not add new meaning for the optional pageId or adAccountId parameters.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
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 lead notification webhook. It also immediately distinguishes the effect from deleting leads themselves, which is critical next to sibling tools like list_linkedin_lead_subscriptions and subscribe_linkedin_leads.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Usage is clear: use this to stop real-time lead delivery for a subscription, with subscriptionId sourced from list_linkedin_lead_subscriptions. It does not explicitly state when not to use it or name subscribe_linkedin_leads as the inverse operation, though the context is strong.
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 fileADestructiveIdempotentInspect
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.
| Name | Required | Description | Default |
|---|---|---|---|
| fileId | Yes | the OneDrive item id | |
| confirm | No | REQUIRED true |
TDQS
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.
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.
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.
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.
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.
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 playbookADestructiveIdempotentInspect
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.
| Name | Required | Description | Default |
|---|---|---|---|
| id | Yes | the playbook id (from list_playbooks) | |
| brand | No | profile id/name from list_brands, this call only |
TDQS
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.
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.
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.
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.
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.
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 skillADestructiveIdempotentInspect
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.
| Name | Required | Description | Default |
|---|---|---|---|
| id | Yes | the custom skill id (from list_skills) | |
| brand | No | profile id/name from list_brands, this call only |
TDQS
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.
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.
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.
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.
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.
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 postARead-onlyIdempotentInspect
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.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | how 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. | |
| channel | No | restrict to one channel: facebook, instagram, threads, x, linkedin, youtube, tiktok, pinterest, bluesky | |
| converting | No | pass 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
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already cover read-only/idempotent/non-destructive, but the description adds substantive behavior: 0 credits, unmeasured is never zero with dated platform examples (Facebook post reach since 2026-06-15, Reddit impressions, Google Business), a 5-post baseline floor with an attributed external fallback, and the fact that conversions are invisible for organic posts. It also flags which output field to print verbatim.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with the purpose and the routing problem types, and the numbered refusals are scannable. It is dense and long, with some restatement of the title phrase and of the converting rules already in the schema, but nearly every sentence carries decision-relevant content.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a diagnostic tool with no output schema, the description covers the interpretive rules an agent needs (unmeasured vs zero, baseline insufficiency, attribution of the external hook floor, and the `summary` field to print verbatim). It stops short of describing the rest of the response shape, which an agent would otherwise have to discover at call time.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
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 with their defaults, limits, and the converting rationale — the description mostly restates the converting gate rather than adding new meaning. Baseline 3 applies 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.
Does the description clearly state what the tool does and how it differs from similar tools?
Opens with a specific verb-and-resource framing ('what is WRONG with a given post and where the next edit goes') and explicitly contrasts with the sibling post_performance ('tells you which hook is AHEAD'). 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.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives an explicit decision tree: under-distributed → mine_angles/list_hooks/plan_variations, seen-but-not-held → plan_variations, seen-held-not-converting → offer rung with converting:false. It also states the exclusions in the four refusals, including the 24h too-early rule and that 'nothing here needs fixing' is a legitimate outcome.
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 accountADestructiveIdempotentInspect
Disconnect a third-party account from this workspace (Meta, Google Ads, Google Drive/Sheets/Docs, YouTube, TikTok, LinkedIn, X, Reddit Ads, 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. IT NEVER REVOKES THE GRANT AT THE PROVIDER: the authorisation stays in place, so the same account keeps working in any other brand it is connected to, until the user removes Hermoso in that provider's own settings. The unconfirmed call names where to do that (list_connectors carries revokesAtProvider:"local") — relay it 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.
| Name | Required | Description | Default |
|---|---|---|---|
| account | No | on a channel with several connected accounts (TikTok, X, YouTube, Threads, Bluesky, Telegram, Pinterest): remove ONLY this account (@handle or id from list_connector_accounts) and keep the others | |
| confirm | No | REQUIRED true — reconnecting a sign-in account afterwards needs the user's browser; a paste-a-key account is reconnected with connect_connector | |
| provider | Yes | provider id exactly as list_connectors reports it, e.g. "meta", "google_ads", "youtube", "linkedin" |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Goes well past the destructiveHint annotation by spelling out what is actually destroyed (stored credentials, all provider tools stop working) versus what survives (already-published posts/campaigns). It discloses the non-obvious remote-side effect that the provider grant is NOT revoked and the account keeps working in other brands, and instructs the agent to relay the location for manual revocation from the unconfirmed response. That is exactly the kind of irreversible-side-effect disclosure a destructive 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.
Is the description appropriately sized, front-loaded, and free of redundancy?
Purpose is front-loaded in sentence one, followed by consequences and then prerequisites in a sensible order. Slightly over-signalled with all-caps warnings and a partial restatement ("IT NEVER REVOKES THE GRANT" is then re-explained as "rather than promising a revoke"), but almost every clause carries real information.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a destructive, non-idempotent-looking-with-no-output-schema operation, the description covers the safety profile, the side effects, the provider-side persistence, the reconnect barrier, and the required confirm loop. Nothing an agent needs in order to call this safely and explain it to a user is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
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 provider, account and confirm in detail, including the multi-account semantics. The description largely restates those constraints ("Name the provider", "call with confirm:true") rather than adding format or edge-case meaning 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.
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 provider families covered, so the agent knows exactly what domain it operates on. It stops short of distinguishing itself from the sibling leave_connector, which is the one adjacent tool an agent could easily confuse it with.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives a concrete pre-call workflow: look up the exact provider id via list_connectors, name the provider to the user, then call with confirm:true. It also warns that reconnecting requires a browser so the agent cannot undo it. It never says when NOT to use this instead of leave_connector, leaving a real ambiguity between siblings unresolved.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
draft_brandDraft profileADestructiveInspect
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.
| Name | Required | Description | Default |
|---|---|---|---|
| save | No | 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 | |
| domain | No | a website to scrape, or a social link (profile, YouTube channel or one post) to draft from the account it names | |
| platform | No | socialHandle platform: instagram/tiktok/youtube/twitter/threads | |
| description | No | a free-text brand description (no website) | |
| socialHandle | No | a social handle (creators); needs platform, never guessed |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Adds substantial context beyond the annotations: 0 credits, save=true overwrites the saved profile while default only saves when none exists, the tool cannot ask the user so the caller owns verification, and domain-based resolution can silently hit the wrong company. This meaningfully elaborates the destructiveHint=true / idempotentHint=false profile the annotations only imply.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with purpose, cost, and the verification warning in that order, with no filler sentences. The bala.com example costs a clause but is concrete and earns its space; slightly denser than it needs to be with parenthetical asides.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
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 sketching the returned object ({name, products, logo, …}) and naming the two fields to verify. Field-level detail of the full returned profile is missing, but the essential decision-relevant output information is present for a 5-param, all-optional tool.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so baseline is 3; the description still adds operational meaning to save (pass false until confirmed) and to the domain-vs-description fallback path. It also clarifies that the returned name/summary are the artifacts to inspect, which the schema does not express.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource: drafts a profile object from a domain, free-text description, or social handle, and names the downstream consumers (plan_ad / generate). It does not explicitly distinguish itself from near-siblings like create_brand, get_brand, or use_brand, which an agent may confuse with 'draft' when a profile is then saved.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives concrete when-to guidance: use before spending credits on research or renders, and pass save:false until the returned identity is confirmed. It does not name alternative tools (e.g. get_brand to fetch an existing profile, create_brand to start from scratch), leaving that comparison to inference.
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: each spoken line is said where it was said, in a native voice, lip-synced, background kept. Pass the video and the language; the script is read off the source (script only overrides it). Paid by length; dryRun quotes. Returns the localized video URL.
| Name | Required | Description | Default |
|---|---|---|---|
| video | Yes | the source video URL | |
| voice | No | optional, e.g. 'Aria' (woman) or 'George' (man): a native voice of that sex speaks. Default: the source speaker's sex. | |
| dryRun | No | quote only | |
| script | No | OPTIONAL 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. | |
| language | Yes | target language, e.g. 'Spanish', 'de', 'French (Canada)' |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only state the safety profile (openWorld, non-idempotent, non-destructive); the description adds substantial behavior beyond that: no re-render, lip-sync, background preservation, cost model ('Paid by length'), a dryRun quoting path, and the return value (localized video URL). That is exactly the context an agent needs for a paid, non-idempotent operation.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three sentences, front-loaded with the differentiating capability ('WITHOUT re-rendering it'), followed by call essentials and cost/output facts. 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.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 5-param, 100%-covered schema with a full annotation set and no output schema, the description supplies the remaining needed pieces: the cost/quote path and the returned URL. An agent has everything required to call it correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
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 reinforces that the script is read off the source and that `script` only overrides it, which is already conveyed by the schema's detailed property text, so it adds little beyond the structured fields.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description gives a precise verb and resource ('Localize a finished video into another language') and even sharpens the scope with 'WITHOUT re-rendering it.' It does not name or explicitly distinguish itself from siblings like localize_ad or edit_video, so a 4 rather than a 5.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Usage context is only implied: 'Pass the video and the language' explains the call, not when to choose this over localize_ad or another localization/edit tool. There is no when-not guidance or named alternative. The only real routing hint is the param-level note that script should be omitted unless the auto-transcript is wrong.
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 postADestructiveInspect
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.
| Name | Required | Description | Default |
|---|---|---|---|
| at | No | when the copy goes out: local wall clock (2026-10-30T10:00), UTC or epoch ms (default: an hour from now) | |
| id | Yes | the post to copy, from list_scheduled | |
| link | No | destination URL for the copy (Pinterest, Facebook text post or Google Business post) | |
| brand | No | 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. | |
| title | No | Pinterest or YouTube headline for the copy, max 100 characters | |
| chatId | No | TELEGRAM — which chat, group or channel the copy goes to (@username or numeric id) | |
| ideaId | No | short id of the content-plan idea this post came from | |
| pageId | No | FACEBOOK / INSTAGRAM / THREADS — which connected Page (list_meta_pages) | |
| recipe | No | the post's FORMAT id, e.g. "slideshow" or "imessage_chat" — post_performance groups by it, so reuse one id per format | |
| boardId | No | PINTEREST — the board for the copy (list_pinterest_boards) | |
| message | No | a different caption for the copy | |
| captions | No | per-channel caption overrides for the copy | |
| channels | No | post the copy to these channels instead of the original’s | |
| imageUrl | No | replacement image for the copy: a Hermoso render URL or an upload_file URL | |
| timezone | No | IANA zone for a wall-clock `at` or the queue | |
| useQueue | No | instead of naming a time, take the brand’s next free posting slot | |
| videoUrl | No | replacement video for the copy: a Hermoso render URL or an upload_file URL | |
| imageUrls | No | CAROUSEL — an ORDERED list of image URLs published as ONE swipeable post | |
| locationId | No | GOOGLE BUSINESS — which listing (list_business_locations) | |
| visibility | No | public (default), unlisted or private (YouTube), or draft (TikTok or an unpublished Facebook Page post) | |
| whopExperienceId | No | WHOP — which forum or chat the copy goes to (exp_…, from list_whop_destinations). | |
| linkedinOrganizationId | No | LINKEDIN — publish the copy as this company Page (list_linkedin_pages) |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=false and destructiveHint=true, so the safety bar is lower, yet the description still adds real behavior: the copy is independent so editing/cancelling never touches the original, it publishes even on channels where the original already went out, and defaults to one hour from now or the brand's next free slot. This materially qualifies the destructive/open-world hints rather than restating them.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Purpose is front-loaded and the size is justified for a 22-parameter tool, but the ALL-CAPS emphasis (NEW, ANY, INDEPENDENT) and the long em-dash clause in the first sentence add rhetorical bulk. Dense and mostly waste-free, yet it could be tightened by a line or two.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 22-param mutation with no output schema, the description covers what gets copied, the override rules, the two scheduling modes, and the independence guarantee. The notable gap is what comes back from the call — presumably the new post's id for later reschedule/cancel — which an agent would need to chain into follow-up tools.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
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 earns an extra point by explaining the override model — caption, media, per-channel captions, title, description, tags and target are all copied but "ANY of those can be overridden in the same call", which tells the agent these params are optional deltas rather than requirements. It also frames the at-vs-useQueue duality, though it does not add detail beyond the schema's own wording for either.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
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 near-miss semantics: a genuinely new post rather than a re-send. An agent can tell this apart from retry_scheduled and reschedule_post 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.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives three concrete motivating scenarios (run a creative again, reuse a post as a starting point, re-send after it went out) and an explicit exclusion with the correct alternative: "To re-fire only the channels that FAILED, use retry_scheduled instead." That is the when/when-not/alternative triad.
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 FILE is put on when the instruction asks for the logo (never redrawn by the model): an ask only about the logo ('the logo is wrong', 'make the logo bigger'; or logo:true, any language) runs no image model. 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.
| Name | Required | Description | Default |
|---|---|---|---|
| logo | No | ||
| mask | No | optional mask image (URL or local path) marking the region to change: transparent = change, or white = change on an opaque mask | |
| image | Yes | the image to edit: URL, Library item URL, upload_file URL or local path | |
| dryRun | No | true = return what this edit usually costs in credits and render nothing | |
| removal | No | true 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 | |
| fixLabel | No | 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) | |
| instruction | Yes | the change to make, in plain words (pass the user’s own words for a removal or plain photo edit) |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Rich behavioral context beyond annotations: brand name/website pinning, real logo file placement, no image model for logo-only asks, product label check and its charges, and that it returns the new image URL. However, the annotations declare destructiveHint=false while the description advertises a removal mode that 'STRIPS text, branding or an object' — the tool only edits a copy, so this is not a direct contradiction, but the erosion of the safety signal caps it at 3.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with the core action and an example, but the body is a dense ~200-word run-on of semicolons and parentheticals covering billing, brand pinning, product label checks and edge cases. Every clause is informative, yet several could be tightened or moved to schema fields.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 7-parameter generative mutation with no output schema, the description covers cost ('One image edit's credits'), return value ('returns the new image URL'), masking, brand integrity, and failure-adjacent modes. Nothing essential to invoking it correctly is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 86%, so the baseline is 3, and the description adds real meaning: logo:true triggers a no-model logo-file placement, removal:true suppresses brand re-adding, fixLabel:false disables lookup/check/re-print, mask is cross-referenced to generate_image. Only dryRun goes undiscussed, but the schema covers it.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('EDIT an existing image in place') plus the core constraint ('keep everything else'), and explicitly names the boundaries against generate_image and clone_static. An agent can tell 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.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly routes alternatives: 'For a new image from a prompt use generate_image; to rebuild a competitor's ad for your brand use clone_static.' Also gives conditional rules for logo-only asks, removal:true, fixLabel:false, and mask usage, so the agent knows when each mode applies.
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
SWAP AN OBJECT IN A VIDEO ('replace the cup with our can' + the product photo as elements) or 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), translating what is SPOKEN (dub_video) or a saved creator's face (recast_motion). Translating or rewriting the on-screen words of a video laid out from a design (a template ad, a custom design) is textChanges with every line at once: one re-make of the design, about 2 credits. Best on 3-15s clips.
| Name | Required | Description | Default |
|---|---|---|---|
| video | Yes | the source video URL (a render, job result or list_library) | |
| engine | No | default auto: Seedance 2.5 Edit without a real-looking person, else Kling 4.0 Edit | |
| literal | No | true = send the instruction exactly as written, with no preserve/lock wrapper | |
| elements | No | 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 | |
| lighting | No | default auto: keep the source light unless the change needs new light (night, a lamp, fire). 'preserve' or 'relight' forces it | |
| faceRoute | No | '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. | |
| keepAudio | No | default true: keep the source audio; false = silent | |
| previewAt | No | second to preview (default 0); resend with previewStill | |
| reference | No | 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 | |
| instruction | Yes | the change, in the user’s own words | |
| textChanges | No | EVERY change of on-screen words at once ([{from, to}], e.g. each line and its translation). A video laid out from a design is made again with all of them in ONE render, about 2 credits, never a video edit | |
| previewStill | No | the still a previewFirstFrame call returned; the clip then matches the changed thing to it | |
| interactionId | No | 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. | |
| previewFirstFrame | No | true = edit ONE still of the first frame first and quote the clip; the paid clip does not run |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Well beyond the annotations (which only say non-read-only, non-idempotent, open-world, non-destructive), the description discloses cost (about 2 credits for the textChanges re-make), the free quality-score and over-edit flag in the reply, the preserve/relight lighting policy and how to force it, the faceRoute ownership requirement with the explicit "only on the user's say-so, never on your own" guardrail, and the previewFirstFrame safety valve that quotes without running the paid clip. It also sets a success heuristic ("one change per call holds best"), which is behaviourally useful and absent 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.
Is the description appropriately sized, front-loaded, and free of redundancy?
Information-dense but poorly structured: one unbroken block with run-on sentences, abrupt fragments ("previewAt picks the second"), and redundant framing ("SWAP AN OBJECT ... or EDIT an existing clip from a plain instruction"). The illustrative quotes ('change only the mug to red', 'make it nighttime', 'restyle it as claymation') repeat the same point three times. Most sentences do earn their place, but the packing makes key routing facts hard to locate.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 14-parameter, 3-enum, high-consequence generation tool with no output schema, the description covers what an agent needs: when to use it, which sibling to use instead, the cost model, the preview pathway to avoid paying for a bad edit, the ownership guardrail on real faces, the reply's scoring/flagging behaviour, and the clip-length sweet spot. Remaining details (job polling, per-engine differences) live in the schema or in get_job, so nothing critical is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
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, and the description genuinely adds above that: it explains the two-step previewFirstFrame → previewStill workflow, what literal:true disables (the preserve/lock wrapper), and that textChanges must carry every line at once in a single paid render. It leaves interactionId, engine routing and reference-material semantics to the schema, so it is additive rather than exhaustive.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The opening states a precise verb+resource pair (swap an object in a video / edit an existing clip, video-to-video) and immediately scopes what stays fixed: "the motion, timing, framing and cut stay, the named thing changes." It also names the siblings it is NOT (post_edit, generate_video, render_ad, dub_video, recast_motion), so an agent can separate it from every near-neighbour in one read.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicit routing in both directions: use for object swaps and instruction-driven video-to-video edits, do not use for cuts/trims/end cards, new generation, spoken-language translation, or creator face replacement, each with the correct alternative named. It adds the special case where on-screen text changes in a template/design video should go through textChanges as one re-make rather than an edit, plus the practical constraint "best on 3-15s clips."
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 toolsAIdempotentInspect
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.
| Name | Required | Description | Default |
|---|---|---|---|
| groups | Yes | Groups to switch on, e.g. ['ads']. Unknown names are refused by name rather than ignored. |
TDQS
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.
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.
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.
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.
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.
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 detailARead-onlyIdempotentInspect
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.
| Name | Required | Description | Default |
|---|---|---|---|
| fingerprint | Yes | the `fp` value from list_errors |
TDQS
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.
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.
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.
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.
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.
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_schedule_to_notionExport the schedule to NotionAInspect
Export the posting schedule to a Notion database, one row per post: date, channels, caption, media link and status. Creates a new database (under parentPageId, or private in the workspace) or writes into an existing one (databaseId), matching its columns by name. historyDays adds posts already published. Up to 100 rows per run; the answer says if more were left. Free.
| Name | Required | Description | Default |
|---|---|---|---|
| title | No | name for a new database | |
| databaseId | No | an existing database id or link | |
| historyDays | No | 0-90 days of published posts to include | |
| dataSourceId | No | data source id of an existing database to write rows into, matched to columns by name | |
| parentPageId | No | Notion page to create the new database under; omit for a private page in the workspace |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Adds material context beyond the annotations: column matching by name, the 100-row cap, a signal that the response says if more rows were left, and cost ('Free'). Those are real behavioral and operational facts an agent needs, and they align with readOnlyHint=false and openWorldHint=true.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loads what the export produces, then covers the create/write modes, history scope, row cap, and cost in densely packed sentences. No filler; every sentence carries an operational fact.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 5-param, open-world, non-read-only export with no output schema, the description covers modes, limits, output fields, and cost. An agent could invoke it correctly without reading further.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
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 clarifies the create-vs-write distinction for parentPageId/databaseId and the meaning of historyDays. dataSourceId is only indirectly covered via 'matching its columns by name', so it isn't fully fleshed out beyond the schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb ('Export') and resource ('the posting schedule to a Notion database'), plus the row shape (date, channels, caption, media link, status). That makes it distinguishable from generic siblings like save_to_notion or append_to_notion_page by scope, though it never names an alternative explicitly.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explains the two operating modes clearly: create a new database under parentPageId (or private), or write into an existing one via databaseId. Also clarifies that historyDays includes already-published posts and that only 100 rows run at a time. No explicit when-not or sibling routing, which keeps it from a 5.
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. 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).
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | max ads to include, 1-60 (default 30) | |
| title | No | deck title (default: the collection name) | |
| collection | No | the swipefile collection to export, by name or id (default: the first collection) |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The description adds substantial behavioral context beyond the annotations: it creates a new deck every time, cannot append to existing user decks due to the drive.file scope, handles expired ad-library links by preserving copy and dates while noting the issue, and reports how many slides were affected. It also specifies the required Google Drive connection. This goes well beyond what the annotations alone disclose and contains no contradictions.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is front-loaded with the primary action and outcome, and each sentence generally adds useful operational detail. It is somewhat long and includes a few extended parenthetical explanations, but it remains readable and information-dense rather than wasteful.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
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 and the external side effect of creating a Google Slides deck, the description is complete enough for correct invocation. It covers the return value (presentation id and URL), prerequisites, creation behavior, scope limitations, and an important edge case involving expired creative links.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
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, title, and collection with defaults and formats. The description does not add meaningfully richer parameter semantics beyond what the schema provides, so the baseline score of 3 is appropriate.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific verb and resource: turning a swipefile collection into a Google Slides deck with one slide per saved ad. It clearly explains what is produced and what each slide carries, but does not explicitly differentiate this tool from sibling tools such as create_doc, save_to_drive, or list_swipefile.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It gives clear context for use ('the thing a marketer actually presents to a client or a team'), states prerequisites ('Needs Google Drive connected'), and discloses an important constraint ('Creates a NEW deck every time' and cannot add slides to an existing deck). However, it does not explicitly tell the agent when to choose this over alternative tools or name a specific sibling to use instead.
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 screensAIdempotentInspect
Pull an APP brand's REAL store screenshots (Google Play or App Store) 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'. A store link or package id goes in storeUrl and is read exactly; else pass appName (defaults to the saved brand's name). A web app with no store listing gets the screenshots of the product its OWN website shows (pictures the site labels as its interface). Call it before telling the user you have no screenshots of their app. FREE — a keyless store lookup or one read of the brand's homepage. 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.
| Name | Required | Description | Default |
|---|---|---|---|
| appName | No | the app's name to look up on the App Store — defaults to the saved brand's name | |
| brandId | No | a profile id/name from list_brands to save the screens onto; omit to use the active profile | |
| storeUrl | No | a Google Play / App Store link or package id |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=false, openWorldHint=true, idempotentHint=true and destructiveHint=false, and the description is consistent with a write-to-brand operation. Beyond that it discloses cost ('FREE — a keyless store lookup or one read of the homepage'), the failure mode (ambiguous/unknown app returns 0 screens and saves nothing), and the success side effect (screens saved to the brand as durable URLs, immediately usable). Only the exact return payload shape is left implicit.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
It is front-loaded with the purpose and trigger, then stacks preconditions, fallbacks, cost, and failure handling in a logical order with zero filler sentences. It is on the dense/long side for a three-parameter tool and could compress the web-app fallback clause, but nothing is redundant.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
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 and an external-dependency (open-world) lookup, the description covers inputs, the no-store fallback, the zero-result failure behavior with guidance not to retry, cost, and where results land. An agent has everything needed to call it once correctly and interpret the outcome.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100% and the schema already documents appName defaults to the saved brand's name and that storeUrl takes a link or package id, so the description largely restates it ('storeUrl ... is read exactly; else pass appName'). It does add genuinely new meaning for storeUrl-less web apps — falling back to pictures the product's own website labels as its interface. With the schema doing the heavy lifting, this sits at the baseline plus one useful addition.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description names a specific verb and resource — pulling real Google Play / App Store screenshots into the workspace brand — and distinguishes itself from siblings like fetch_asset or list_product_photos by scoping it to app-store screens used by screen-hungry native formats. An agent can tell exactly what it produces 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.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It gives an explicit trigger condition ('brand has 0–1 app screens on file'), a concrete downstream use (make_template_ad with template:'app-ui-tour'), a natural-language user phrasing, and a pre-emption rule ('Call it before telling the user you have no screenshots'). It also steers the agent away from retrying with guessed names, which is an explicit when-not.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
fetch_assetFetch assetARead-onlyIdempotentInspect
Resolve a generated asset reference (a /generated/… path or any URL) to a clickable absolute URL + a direct download URL.
| Name | Required | Description | Default |
|---|---|---|---|
| url | Yes | the asset url or /generated/ path | |
| name | No | optional filename for the download |
TDQS
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.
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.
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.
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.
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.
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.
| Name | Required | Description | Default |
|---|---|---|---|
| path | Yes | exact endpoint path, e.g. '/v1/tiktok/profile' — non-allowlisted paths are rejected | |
| params | No | endpoint query params, e.g. {handle:'nike'} |
TDQS
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.
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.
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.
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.
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.
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. Bills the discovery model's token cost (no ad-data charge); free inside a new account's first-brand setup.
| Name | Required | Description | Default |
|---|---|---|---|
| mode | No | 'competitors' (default, excludes the searched company), 'inspiration' (best relevant ads incl. it), or 'company' | |
| domain | Yes | the brand domain, e.g. flourish.com |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Adds genuinely useful non-schema context: it bills the discovery model's token cost, carries no ad-data charge, and is free during a new account's first-brand setup. That complements the annotations (readOnly=false, openWorld=true, non-idempotent), while the web-search grounding signals nondeterminism. It stops short of describing result size or latency.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two dense sentences with the core action front-loaded, followed by mode semantics and then cost implications. No redundant or filler text.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 2-parameter tool with annotations and no output schema, the description covers action, modes, and billing, which is most of what an agent needs. The notable gap is that it never says what comes back (a list of brands, ads, etc.), which matters since no output schema exists.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100% and the enum already documents each mode with the same wording used in the description, so the description adds no meaning beyond the schema. The domain parameter is only illustrated in the schema, not elaborated in the description.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb ('Discover') plus the resource ('competitor / similar / adjacent brands from its domain') and names the grounding mechanism (Claude + web search). An agent can distinguish it from sibling discovery/analysis tools like competitor_teardown or pull_competitor_ads, which imply deeper analysis or ad retrieval.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explains the three modes and their differing behavior (default excludes the searched company; inspiration includes it and surfaces best relevant ads), which tells the agent which mode to pick. It does not explicitly contrast with sibling tools such as pull_competitor_ads or set_competitor_watch, so routing is clear but not fully exclusionary.
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.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | creators to return, 1–30 (default 12) | |
| niche | Yes | product category, topic or hashtag — "calorie tracker app", "matcha", "#cleanbeauty" | |
| enrich | No | followers + public contact for the top 3 on TikTok and on Instagram (default true, 1 credit each) | |
| queries | No | query variants per platform, 1–4 (default 3); each is a paid search call | |
| platforms | No | default all three | |
| marketplace | No | also search Instagram’s creator marketplace (Meta’s own creator directory, free) for the same niche; needs the Meta connector | |
| minAvgViews | No | minimum average views per post; creators below this floor are dropped | |
| minEngagement | No | interactions per view, 0–1 (0.05 = 5%) |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Discloses the cost model in detail (one credit per platform × query call with defaults, one per enriched profile, free repeats inside 20 minutes), what enrichment actually returns, and that marketplace:true depends on the Meta connector. That is substantive context beyond the annotations, which only mark it non-read-only and open-world.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loads what it scans and how it ranks before the cost and follow-on detail; dense and slightly run-on in the middle, but nearly every sentence carries invocation-relevant information.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
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 comes back — ranked creators scored on median views, engagement rate and niche frequency, with follower counts and public contact fields on the top rows. Combined with cost, connector requirement and next steps, an agent has everything needed to call it and use results.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
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 meaning for enrich (top 3 per platform, 1 credit each), marketplace (also queries Meta's creator directory and appends those rows), and the cost implication of queries — more than the schema alone conveys.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb chain (scan, fold into creators, rank) over a named resource (TikTok/Reels/YouTube posts) scoped to a niche, and the 'real people, not AI actors' framing plus named ranking signals make it unmistakable next to search_posts or list_creators.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Names concrete use cases (influencer sourcing, UGC casting, partnership prospecting) and then routes the agent to the exact follow-on tools — save_to_swipefile for shortlisting, fetch_social_data / instagram_profile for checking, generate_text for outreach, manage_meta_partnership_creator for Partnership Ads.
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. A sound that does not exist yet is generate_sfx.
| Name | Required | Description | Default |
|---|---|---|---|
| url | No | link to a TikTok sound, a post, a meme-sound page or an audio file | |
| pick | No | candidate number from the previous list to take instead of the first | |
| query | No | sound by name, moment or meaning, e.g. 'bad news reaction' |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations declare readOnlyHint=false and openWorldHint=true, and the description is consistent with that — it fetches from TikTok's current catalog and returns a durable mp3 (implying materialization/side effects). However, it never explains why the call is not read-only: whether the mp3 is saved to a library, whether repeated calls are non-idempotent, or what happens when nothing matches. With annotations covering safety and idempotency, that residual gap justifies a 3 rather than higher.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two compact sentences plus a closing sentence, no filler, and the input modes and return shape are front-loaded. The phrasing is dense and slightly elliptical ('a durable mp3 and where it starts and lands', 'pick takes another candidate'), which costs a small amount of immediate parseability but not enough to waste space.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 3-parameter, all-optional lookup tool with no output schema, the description covers the input modes, the return payload shape, and the fallback to generate_sfx — enough to call it correctly. What is missing is the lifecycle around the returned mp3 (is it persisted, where, and what does an empty result look like), which matters given the non-read-only annotation.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so all three parameters are already documented, and the description largely restates them (name/moment/link, pick another candidate). It adds only marginal meaning: that 'named/described' queries resolve against what TikTok uses now, and that pick is a follow-up to a previously returned candidate list. Baseline 3 applies 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.
Does the description clearly state what the tool does and how it differs from similar tools?
The description names the resource (a sound) and the three lookup modes — by name ('the FAAAA sound'), by moment ('bad news reaction'), or by link — and states the output ('a durable mp3 and where it starts and lands'). It also names the sibling it is not (generate_sfx) for sounds that don't exist, so an agent can distinguish it without opening the schema. The only weakness is the telegraphic opening fragment, which reads more like a caption than a verb-led statement.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Clear routing is given: this tool when the sound exists in some form (name, moment, link), generate_sfx when it doesn't. It also explains the candidate-picking path ('pick takes another candidate' after a prior list), which is an implied retry/selection workflow. It gives no guidance relative to other audio siblings like generate_music or search_tiktok, so context is clear but not exhaustive.
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 taskARead-onlyIdempotentInspect
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.
| Name | Required | Description | Default |
|---|---|---|---|
| group | No | limit to one group: core, research, create, channels, channel_admin, analytics, ads, files, workspace | |
| limit | No | how many to return (default 12, max 40) | |
| query | No | words from the task or the tool name, e.g. "lead form", "whatsapp", "google ads keyword", "meta insights" | |
| onlyHealthy | No | 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. |
TDQS
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.
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.
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.
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.
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.
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, billed for its compute only. 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.
| Name | Required | Description | Default |
|---|---|---|---|
| sub | No | accent sub-pill copy, ≤34 chars (usually the product/brand) | |
| grain | No | default false — anti-AI film-grain finish | |
| pills | No | default true — set false for a grain-only pass | |
| accent | No | brand accent hex for the sub-pill | |
| header | No | header pill copy, ≤40 chars (required when pills is on) | |
| points | No | 3-4 proof points, ≤44 chars each | |
| videoUrl | Yes | the served URL of the video to finish (from a previous render/job) |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Goes well beyond the annotations: no AI model, ~30s runtime, billed for compute only, returns a NEW video and leaves the original untouched — this materially clarifies the non-destructive, non-idempotent profile. It also warns that copy is authored by the caller and stats must never be fabricated.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with the core action and the key constraint (EXISTING video), then behavior, then cost/runtime, then return semantics. Dense — one long paragraph with parentheticals — but every clause carries information; no filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
No output schema, but the description covers return semantics (new video, original untouched), cost and runtime, and the toggles. Minor gap: it doesn't say whether the result is returned inline as a URL or as a job to poll, which matters for chaining.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
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 real semantic value: how pills and grain interact (works alone or together), that pills defaults on, and the authoring intent for header/sub/points (concrete real benefits, never fabricated stats). It stops short of explaining 'accent' or the videoUrl provenance details beyond 'from a previous render/job'.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb+resource+scope: post-process an EXISTING rendered video (served mp4 URL) with the 'reviewer' pill finish and/or film grain. Explicitly distinguishes itself from generation siblings by capitalizing EXISTING and noting 'no AI model', so an agent won't confuse it with render_ad/edit_video.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Clear context for use (you already have a rendered mp4 URL; grain makes photoreal AI renders look phone-shot; pills works with grain or alone). No explicit when-not or named alternatives such as edit_video or upscale_video, which would push this to a 5.
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. A FINISHED VIDEO-BLOCKS FILM (make_explainer's explainer or faceless channel; get_job says it is editable) is edited ONE BLOCK at a time instead: pass job (its job id) and block (1-based) or atSeconds, with prompt for a different shot and/or line for new narration words. Only that block is re-rendered and/or re-read; every other block, take, the music and the captions are the film's own and stay aligned. It is priced as that block alone and charged exactly what it quotes (dryRun). The original film is kept; a new film comes back, and it can be edited again.
| Name | Required | Description | Default |
|---|---|---|---|
| job | No | a finished video-blocks film (explainer / faceless channel): its job id. Edits one block of it instead of splicing a window | |
| line | No | with `job`: the new narration for that block, the exact words to say (said as written, never rewritten; refused for free when it cannot fit the block) | |
| block | No | with `job`: which block, 1-based (block 3 = 20-30s of a film of 10s blocks) | |
| dryRun | No | true = return the estimated credits this fix reserves and render nothing | |
| prompt | No | what the replacement footage should show — describe the shot, matching the master's style. With `job`: the change to that block's picture, in words | |
| refImage | No | optional product/style anchor image URL | |
| videoUrl | No | the served URL of the master video to fix (not needed with `job`) | |
| atSeconds | No | with `job`: the moment in the film instead of `block` ("the line at 0:12" = 12) | |
| endSeconds | No | window end in seconds (window 1.5-8s; not needed with `job`) | |
| startSeconds | No | window start in seconds (not needed with `job`) | |
| speechWindows | No | [[start,end],...] windows with spoken lines — the fix window must not overlap these |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Beyond the annotations it discloses that all audio stays byte-identical, that a video-only splice under speech breaks lip-sync, that billing is only the replacement clip (~1/3 of a full render) and is charged exactly what dryRun quotes, and that the original film is preserved with a new editable film returned. These match destructiveHint=false and idempotentHint=false rather than contradicting them.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The primary window-splice behavior is front-loaded and the second mode is clearly separated, with every sentence carrying information (scope, pricing, lip-sync risk, return value). The dense run-on sentence introducing the video-blocks film mode is harder to parse than the rest, but nothing is padding.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With 11 optional parameters, two operating modes, and no output schema, the description covers mode selection, the key constraint, cost behavior, and what comes back ('a new film comes back, and it can be edited again'). It stops short of saying how to poll the returned film, though its mention of get_job partially covers that.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
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 cross-parameter semantics the schema cannot express: job/block/atSeconds/prompt/line form an alternative mode to videoUrl+startSeconds+endSeconds, speechWindows must not overlap the fix window, and `line` is spoken verbatim and refused for free if it does not fit. Minor gap: refImage's role is left to the schema alone.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The first clause gives a specific verb and resource with scope: 'Surgically re-render ONE time window (1.5-8s) of an existing rendered video and splice it back on the VIDEO TRACK ONLY'. It then explicitly separates itself from a full render (generate_video-style) and from whole-film regeneration, and names two sibling tools (make_explainer, get_job) by role.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It states the triggering condition ('Use when one beat/shot is broken... and a full re-render would waste the parts that worked') and an explicit prohibition with remedy ('Do NOT pick a window covering spoken dialogue... pass speechWindows'). It also routes between its two modes: videoUrl+start/endSeconds for a rendered video, versus job+block/atSeconds for a finished video-blocks film.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
forgetForget a memoryADestructiveIdempotentInspect
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.
| Name | Required | Description | Default |
|---|---|---|---|
| id | Yes | the memory item id (from list_memory) | |
| brand | No | profile id/name from list_brands, this call only |
TDQS
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.
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.
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.
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.
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.
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 SheetADestructiveIdempotentInspect
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.
| Name | Required | Description | Default |
|---|---|---|---|
| tab | No | tab title or numeric sheetId (default: the first tab) | |
| sheetUrl | No | Google Sheets link, instead of spreadsheetId | |
| autoResize | No | auto-size columns to fit their content; default true, false skips it | |
| boldHeader | No | bold the header row; default true, false skips it | |
| freezeRows | No | how many top rows to freeze (default 1, 0 = none) | |
| spreadsheetId | No | Google spreadsheet id; use this or sheetUrl |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Adds context beyond the annotations: it discloses that no cell VALUE is modified, only presentation, which scopes the meaning of destructiveHint=true (existing formatting may be overwritten, but data is safe). It also asserts the call 'is never gated', a useful operational fact not encoded in annotations. It could go further by stating explicitly that pre-existing formatting on the target tab is replaced.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with the three actions, then the usage rationale, then the parameter opt-outs — a natural decision order. Every sentence carries information; nothing is filler despite the length.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 6-parameter mutation tool with no output schema, it covers what changes, what does not change, defaults, and when to call it. It leaves unstated whether existing formatting on the tab is overwritten and what the call returns or costs, which is the remaining gap.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
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 semantic value by explaining the default behavior ('the defaults do all three on the first tab') and the exact opt-out idioms for tab, freezeRows, boldHeader, and autoResize. It ties parameters to user-visible outcomes rather than just restating types.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('make an exported sheet readable') and enumerates the three concrete operations performed: bold header, freeze header, auto-size columns. This clearly separates it from siblings like update_sheet (which edits values), read_sheet, and create_sheet.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives explicit timing and rationale — 'Worth calling right after create_sheet' — plus per-flag opt-outs (freezeRows:0, boldHeader:false, autoResize:false) so an agent knows how to narrow the behavior. It stops short of naming a competing tool to use instead, but no sibling overlaps in function, so the gap is minor.
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.
| Name | Required | Description | Default |
|---|---|---|---|
| seed | No | fixed seed, only for an engine other than standard | |
| image | Yes | 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. | |
| voice | No | 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 | |
| dryRun | No | return the credits this exact job would hold, without rendering | |
| engine | No | leave out for the standard engine; only an engine listed in hermoso_capabilities avatarEngines is accepted | |
| prompt | No | motion direction, only for an engine other than standard that hermoso_capabilities lists | |
| script | Yes | the words the avatar speaks | |
| resolution | No | '720p' (default) or '480p' | |
| acceptQueue | No | only for an engine hermoso_capabilities marks oneAtATime: wait in line |
TDQS
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.
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.
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.
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.
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.
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
Use this when the user asks for an image ad, ad creative, product photo or social post graphic for their business or product. It renders a finished image on a real image model, on their saved brand (product photos, logo), and returns a URL ready to post. For a video, use render_ad. refImages (paths or URLs) composite a real product into the scene. MULTI-BRAND: hydration uses the SAVED brand; for a different brand (a fresh draft_brand) pass its own productImages/logo as refImages with useBrand:false, or the wrong product lands in the shot. Product photos also pick the ENGINE (the compositing model), so a named model is honoured only when no references ride; raw:true or useBrand:false keeps it. model = a catalog id from hermoso_capabilities (omit for the default). A PRODUCT IN A PERSON’S HANDS, or a garment on them: name an id marked needsRefs with a refsMax; person photo first, then up to three product/garment photos. It EDITS THE PHOTOGRAPH: the person is RE-POSED (stance and hands change; face, clothing, setting and light are kept), so it is the wrong tool when the rest must stay untouched. 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. Seconds. Spends credits.
| Name | Required | Description | Default |
|---|---|---|---|
| job | No | what the picture is for. With no `model` named it renders on the engine judged best for that job (hermoso_capabilities `jobEngines.image` lists them): static_ad (the default when neither `model` nor `job` is given) = a finished image ad with its headline, copy and button on the picture; product_photo = a studio, lifestyle, flat-lay or spotlight photo of the real product; marketplace = a listing card; thumbnail = a video cover (make_thumbnail is the full thumbnail pipeline); character_sheet = a multi-view sheet of one person; vector = a logo, icon or flat illustration as an editable SVG (it takes no reference pictures, so the saved brand's photos stay off it). A named `model` always wins | |
| raw | No | RAW 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. | |
| mask | No | MASKED EDIT — change ONE region and keep the rest: a mask (path or URL, any size) for refImages[0]. TRANSPARENT = change, or on a mask with no transparency WHITE = change, black = keep; the reply says which it read. The mask guides rather than stencils, so the change can blend a little past its edge. Runs on gpt-image-2.5 (`refs.mask` in hermoso_capabilities): leave `model` empty or name it; another named model is refused, free. Needs refImages; keeps the source image's frame, so aspectRatio is not applied. | |
| model | No | 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 | |
| prompt | No | 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. | |
| fixLabel | No | 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 re-print costs more, and dryRun:true returns the up-to price). 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. | |
| useBrand | No | default 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 | |
| imageSize | No | 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 | |
| refImages | No | 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. | |
| aspectRatio | No | 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 | |
| logoPlacement | No | overlay: real logo file laid flat. in_scene: the scene leaves a blank patch on the object and the real file is printed onto it (perspective, light). Never drawn by the model. auto (default): from the prompt |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only say it is a non-readonly, non-idempotent, open-world call; the description adds the behavior that matters: it spends credits, takes seconds, returns a durable URL, applies the saved brand automatically, auto-checks and re-prints product labels at extra cost, and that pose runs re-pose the person rather than preserve the image. It also discloses what raw:true suppresses (prompt rewriting, appended guidance, negative prompt, brand refs) and which model each reference path forces 'or the wrong product lands in the shot'. This is well past what the annotations supply.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
It is front-loaded correctly — the first two sentences establish purpose and the render/return contract before any caveats. However, at roughly 300 words with heavy ALL-CAPS headers, em-dash digressions and repeated restatements of schema text (raw, model, mask), it reads as an internal spec rather than a tool description, and several clauses could be dropped without losing decision-relevant information.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For an 11-parameter, no-output-schema, credit-spending generation tool it covers the things an agent must know: cost, latency, delivery, brand hydration, reference ordering, refusals that are free, and per-model capability lookup via hermoso_capabilities. The remaining gap is job lifecycle — it never says whether the call returns synchronously or hands back an id for get_job/resume_job, which the sibling list implies exists.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
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 interaction rules the schema states only in isolation: named `model` is honoured only when no references ride, product photos select the engine, refImages order is a contract on `needsRefs` models and a composite elsewhere, and useBrand:false is the escape hatch for foreign brands. Those are constraints an agent cannot derive from reading each property description separately.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The opening sentence names a specific verb and resource ('renders a finished image on a real image model') and enumerates the request types it answers (image ad, ad creative, product photo, social graphic). It explicitly routes video requests to render_ad and marks when it is the wrong tool versus an in-place edit, so an agent can separate it from edit_image, render_ad, make_thumbnail and resize_ad 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.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It gives explicit when-to-use triggers ('when the user asks for an image ad...'), explicit exclusions ('it is the wrong tool when the rest must stay untouched', 'For a video, use render_ad'), and a multi-brand branch telling the caller exactly what to pass for a draft_brand. The pose-model paragraph states which input order is mandatory and that phrasing a prompt as a swap is refused, which is decision-grade guidance rather than description.
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. No length: a full track at a flat fee (explainerMusicCredits in hermoso_capabilities). With seconds: by the second up to about 38 s. A named model always runs.
| Name | Required | Description | Default |
|---|---|---|---|
| model | No | minimax-music-3 or lyria-3.5 | |
| prompt | Yes | the music in words, e.g. 'lo-fi jazz, brushed drums, 80 bpm' | |
| seconds | No | track length, up to 300 |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only mark this as a non-read-only, non-idempotent, open-world write, so the description's billing disclosure is genuinely additive: flat fee for a full track versus per-second charging for short clips. It also states the output is an instrumental MP3. It does not say whether generation is synchronous or returns a job to poll with get_job, which is a meaningful omission for a costly generation call.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three tight sentences with no filler and the key facts front-loaded. However, the telegraphic fragments ('RAW music:', 'No length:', 'With seconds:') and internal references ('explainerMusicCredits in hermoso_capabilities') read as shorthand notes rather than a clean specification, costing clarity at the same length.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Covers purpose, output format and cost model, which is more than most. But with no output schema, the description should say what comes back (URL? job id?) and whether the call is async, and it should reconcile the 38 s vs 300 s limit conflict that an agent will hit directly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
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 does add pricing semantics for 'seconds' and clarifies that 'model' is always resolved to a named model, but it conflicts with the schema: the description caps length at 'about 38 s' while the schema says 'up to 300'. That contradiction undermines rather than improves parameter understanding.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a concrete verb+resource and output artifact: describe the music in words (genre, mood, instruments, tempo) and get an instrumental MP3. The tagline 'RAW music' is unexplained jargon, and it never names a sibling (e.g. generate_voice, find_sound) that an agent might confuse it with, but the core action is unambiguous.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives conditional guidance for the two modes (omit seconds for a full track at a flat fee; include seconds for per-second billing) and points to hermoso_capabilities for credit details. It never says when NOT to use it or which alternative tool to pick instead, so usage is implied rather than directed.
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 (billed at the writing model's token cost, by length).
| Name | Required | Description | Default |
|---|---|---|---|
| raw | No | RAW 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. | |
| model | No | a writing-model id from hermoso_capabilities (a Claude / Gemini / GPT / Llama / DeepSeek id) — omit for the default | |
| prompt | Yes | the writing task / question |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already flag openWorldHint=true, non-readonly and non-idempotent, so safety is covered structurally; the description adds real value beyond that by disclosing the paid billing model (token cost, by length) and the non-obvious default behavior that a house copywriter system prompt is injected unless raw:true. It does not describe the response shape or latency, 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.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with the capability and scope, then alternatives, then the raw/model defaults and pricing. It earns most of its length, but the dense parenthetical stacking ("(a short house system prompt is applied, which is what you want for ad copy)") costs readability against a single-sentence ideal.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 3-param, no-output-schema generation tool, the description covers scope, defaults, model sourcing, and cost. The main omission is what the tool actually returns (a text string vs structured variants), which an agent might reasonably want to know despite the absence of an output schema.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
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 slightly beyond by tying model to the hermoso_capabilities catalog and stating the default (Claude orchestrator), and by reinforcing that raw:true means no system prompt at all. Parameter behavior is well grounded, though it largely echoes the schema text.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb+resource (text generation against the writing-model catalog) and enumerates the concrete use cases (ad copy, hooks, scripts, rewrites, brainstorms). It explicitly distinguishes itself from plan_ad/render_ad for finished creative, so an agent can pick it out of a large sibling list 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.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly says "prompt-only, no ad assembly" and routes to plan_ad -> render_ad for a finished creative, which is a named alternative plus the condition that selects it. It also gives a clear when-to-use rule for the raw flag and where to source model ids (hermoso_capabilities).
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
generate_videoGenerate videoAInspect
Use this when the user asks for a video clip, b-roll or a shot from a description, or names a video model. For a finished video ad, Reel, TikTok ad or UGC video for their business, use render_ad. It renders one continuous clip on a real video model and returns its mp4 URL. A clip runs up to the model’s longest listed duration (the longest takes a multi-beat 30s spot in ONE take with native audio), so never assume an 8–10s cap or stitch what fits one clip; durationSeconds must be one of the model’s durations in hermoso_capabilities. NAME A MODEL in model to get it: an unnamed render uses a narrower auto-pool. 1–3 min. refImage anchors the first frame; ttsScript adds a voiceover. AUDIO IS NOT FREE BY DEFAULT: a clip with no audio of its own gets a music bed composed and CHARGED on top (see musicMood and audio), which can cost as much as a cheap draft clip. refVideo (a clip URL) EDITS that clip instead, keeping its canvas and length. RAW MODEL ACCESS: by default small guards are added (label safety with no reference image, a negative prompt where the model takes one, reference lines) and hex colours become 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 (blocked on the Starter plan).
| Name | Required | Description | Default |
|---|---|---|---|
| raw | No | Send the prompt to the model exactly as written, without the wording Hermoso usually adds (packaging and label guidance, reference lines, colour names for hex codes, quality hints). Use it when you want the model's own interpretation 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. | |
| loop | No | 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. | |
| audio | No | default 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. | |
| model | No | video model id from hermoso_capabilities; a named model is never swapped without asking. Omit to let the router pick; its default per job (jobEngines in hermoso_capabilities, from watched side-by-side tests) is seedance-2.5 for product, lifestyle, a person talking to camera and 30s single takes (cheaper engines broke the product there). A model the user picks is kept; never switch it | |
| shots | No | MULTI-SHOT: one clip cut into these shots, in order; the seconds add up to a length the model renders. Replaces `prompt`. Only models with multiShot true in hermoso_capabilities; a shot prompt over that model’s shotPromptMaxChars (Kling 3.0: 512) is refused free, never cut. | |
| speak | No | 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 | |
| angles | No | 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. | |
| dryRun | No | true = render NOTHING and return the credits this exact clip would hold (an estimate; the final charge is its actual cost). Free. Call this to price a clip before running it: without it the call renders and bills. | |
| extend | No | true = EXTEND refVideo by durationSeconds (default 5) with what your prompt says happens next, as ONE clip. By default it CUTS to a NEW SHOT: new angle, same scene, room sound carried, no music at the cut. A named model (any i2v) renders it. | |
| prompt | No | the video prompt / shot description (for a refVideo edit, this is the transformation instruction); optional with `shots` | |
| creator | No | 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. | |
| endImage | No | 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. | |
| refImage | No | 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. | |
| refVideo | No | URL of an existing video to EDIT rather than generate from scratch — the clip is transformed per your prompt, keeping the SOURCE clip’s aspect ratio and, on models hermoso_capabilities marks `sourceLength`, its LENGTH (their `durations` are the per-second price ladder; a durationSeconds sent is reported as unused). 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. | |
| ttsVoice | No | voice name, e.g. Rachel / George | |
| faceRoute | No | 'face_lane' records the user's statement "I own the rights to this face" for the real person in their reference (paid plans; the likeness terms apply). Set it only when the user picked that option after a render said it could not use that face, and send the same request again; it then renders through Hermoso's face library at the normal price. Never set it on your own. The other options are a different `model` or one of the user's saved `creator`s. | |
| musicMood | No | the mood of the music bed (upbeat / calm / warm / epic / tense / playful / elegant / hype / chill / dramatic; default warm). It does not decide WHETHER there is one: a clip with no audio track (a "silent" model in hermoso_capabilities, or one that came back mute) gets a bed composed and CHARGED automatically at explainerMusicCredits. audio:false = silent, no bed, no charge. | |
| refImages | No | SEVERAL reference images (paths or URLs: a person, products, a place) that must all appear. A model uses up to its `refs.max` in hermoso_capabilities (one-image models take the first); with `refs.promptAddressed`, name them Image 1, Image 2… in order. minimax-h3-max-ref takes 9 and keeps each as a reference, not a first frame. | |
| ttsScript | No | voiceover script to speak | |
| cameraMove | No | A named camera move around the still in refImage (orbit = a quarter turn, the default; orbit_half / orbit_full = a half turn / a turntable). Only minimax-h3-max-camera renders one: with model omitted it is picked; any other named model is refused, nothing charged. Needs refImage. | |
| extendMode | No | with extend: 'continue' = the SAME take past its last frame, only when asked seamless (models with extend in hermoso_capabilities). | |
| musicModel | No | compose the bed on minimax-music-3 or lyria-3.5, skipping the free library | |
| resolution | No | Omit = the default tier, hermoso_capabilities defaultResolution (720p), the tier its `credits` prices are for. 1080p is ~2.25x, 4k more, 480p less: quote creditsByRes and pass the tier you quoted. A tier a model's `resolutions` lacks renders at its best one. | |
| aspectRatio | No | default '9:16' | |
| interactionId | No | 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. | |
| durationSeconds | No | length of THIS ONE clip in seconds: one of the CHOSEN model’s listed durations in hermoso_capabilities (they differ per model, 4–8s on the short ones up to 30s). One continuous generation, so a longer ask than the model’s longest clip is REFUSED, nothing charged (never quietly truncated). Past ~15s NAME a long-clip model in `model`. For a longer spot use plan_ad with durationSeconds then render_ad, which stitches acts (on a 15s model, 40s = 15+15+10). | |
| cameraTrajectory | No | 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. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The description adds substantial behavior beyond the annotations: it spends credits, is blocked on Starter, may charge extra for automatic music beds, supports dry runs, and has durable delivery and per-model validation. The annotations already declare non-read-only, open-world and non-idempotent behavior, and the prose is consistent with them.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is front-loaded with usage and alternatives, and most sentences carry substantive constraints. However, it is a dense single paragraph with all-caps warnings, fragments such as '1–3 min.', and repeated schema detail, so it is informative but not concise or well-structured.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a complex 27-parameter generation tool with no output schema, the description covers the missing essentials: it returns an mp4 URL, spends credits, may add charged audio, supports raw dispatch, and has model-specific constraints. An agent has enough context to decide whether to call it and what the major side effects are.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, and the schema already explains nearly every parameter in detail, including raw, audio, model, refVideo, refImage and durationSeconds. The description adds useful high-level synthesis such as the unnamed-model auto-pool and audio charge warning, but much of its parameter guidance repeats the schema rather than adding new semantics.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific verb and resource, naming video clip, b-roll and shot generation, and explicitly distinguishes this from render_ad for finished ads. It also differentiates from generate_avatar and plan_ad/render_ad, so an agent can route correctly without opening sibling schemas.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It gives explicit when-to-use guidance for video clips and b-roll, and names alternatives with their conditions: render_ad for finished video ads, Reels, TikTok ads or UGC, generate_avatar for the animated-photo look, and plan_ad plus render_ad for longer spots. This is close to a routing rule, not just a purpose statement.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
generate_voiceGenerate voiceover or dialogueAInspect
RAW speech as an MP3: one voice reading a script (text), or a DIALOGUE of two or more people (lines), each in their own voice. Not for adding audio to a video (render_ad and generate_video voice their own spots; change_voice re-voices a finished clip). ONE VOICE: text + engine (default 'seed-audio'; also 'eleven-v4', 'minimax-speech', 'kokoro'…) + voice, a preset from that engine (hermoso_capabilities -> voice engines); a name the engine does not have is REFUSED for free with its real list, and a few engines make their own voice (the reply says which voice spoke). DIALOGUE: lines [{speaker, voice, text}] in speaking order. Each speaker keeps ONE voice, no two speakers share one, a speaker with no voice gets one nobody has; up to 10 voices and 10,000 characters, refused for free past that. Default engine: the dialogue model (Eleven v3 Dialogue, voices Aria, George, Sarah, Roger, Laura, Charlie, Callum, River, Liam, Charlotte, Alice, Matilda, Will, Jessica, Eric, Chris, Brian, Daniel, Lily, Bill, or an ElevenLabs voice id; '[laughs]'-style cues work); or name any voice engine (e.g. 'eleven-v4') to read each line on it and join them in order. dryRun:true returns the exact credits and makes nothing; the price quoted is the charge. Paid, priced by characters (hermoso_capabilities lists credits per 1,000).
| Name | Required | Description | Default |
|---|---|---|---|
| text | No | ONE VOICE: the script to speak (≤900 characters) | |
| lines | No | DIALOGUE: the turns in speaking order | |
| voice | No | ONE VOICE: a preset from the chosen engine (e.g. 'Aria'/'George' on eleven-v4, 'stokie_en' on seed-audio); omit for the engine default | |
| dryRun | No | true = the exact credits only, nothing made or charged | |
| engine | No | voice-engine id: one voice defaults to 'seed-audio'; a dialogue defaults to the dialogue model ('eleven-dialogue'); or 'eleven-v4', 'minimax-speech', 'kokoro'… (hermoso_capabilities) |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only cover safety profile (readOnlyHint=false, destructiveHint=false, openWorldHint=true), so the description carries real weight here and delivers it: paid per character, dryRun returns exact credits and makes nothing, invalid voices are refused without charge, and hard caps of 10 voices / 10,000 characters. What it does not state is what the actual response payload looks like or how async job retrieval works, which matters for a non-idempotent generation tool. Not contradicted by annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Purpose and the main exclusion are front-loaded in the first two sentences, then structure splits cleanly into ONE VOICE and DIALOGUE blocks. It is dense and heavy on capitalized emphasis, and a few limits restate schema constraints, but almost every sentence carries operational information for a tool with two quite different calling modes.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
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 covers modality, pricing, free refusals, character/voice caps, per-mode engine defaults, and dryRun — enough to call the tool correctly on the first try. Leaving out the returned shape (job id? URL?) and job polling is the one remaining gap for a paid, non-idempotent generator.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so baseline would be 3, but the description adds semantics the schema lacks: engine defaults differ by mode ('seed-audio' for one voice, the dialogue model for lines), voice must be a preset from the chosen engine and unknown names are refused with the real list, and speakers get distinct auto-assigned voices. This meaningfully changes how an agent fills the parameters.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and artifact ('RAW speech as an MP3') with two concrete modes (one voice reading text vs. a dialogue of lines), and explicitly names the siblings it is not (render_ad, generate_video, change_voice) with the reason. 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.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives explicit when-not-to-use guidance with named alternatives ('render_ad and generate_video voice their own spots; change_voice re-voices a finished clip') and explains how to choose between the text and lines modes. The dryRun path is also framed as the pre-flight option, which is an actionable selection rule.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_brandGet saved profileARead-onlyIdempotentInspect
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.
| Name | Required | Description | Default |
|---|---|---|---|
| brand | No | profile id/name from list_brands, this call only |
TDQS
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.
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.
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.
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.
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.
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 detailsARead-onlyIdempotentInspect
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.
| Name | Required | Description | Default |
|---|---|---|---|
| fileId | Yes | the Drive file id (from list_drive_files) |
TDQS
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.
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.
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.
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.
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.
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 jobARead-onlyIdempotentInspect
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).
| Name | Required | Description | Default |
|---|---|---|---|
| id | Yes | the job id: starts with job_, from a render tool's jobId or from list_jobs (generate_voice and generate_sfx return their file directly and have no job) |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already establish readOnly/idempotent, so the description correctly spends its budget on traits annotations cannot express: latency window, expected polling cadence, the terminal nature of not_found, and the billing consequence of re-invoking. No contradictions with the declared read-only, idempotent profile.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loads purpose and return shape, then the polling protocol, then the terminal-failure warning, in three tight sentences with no filler. Nothing could be cut without losing agent-relevant information.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
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 carries the return contract itself and does so fully: status values, progress, and the URL on completion. Combined with the polling and not_found rules, an agent has everything needed to drive this tool correctly unattended.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100% and the schema description already documents the id format ('job_'), its origin (render tool jobId or list_jobs), and the generate_voice/generate_sfx exception. The description adds no id semantics beyond that, so the baseline of 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
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 enumerates the exact return payload (status enum, progress, served media URL). An agent can distinguish it from list_jobs, resume_job, and the generate_* family 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.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives explicit operational guidance: keep calling until done/error, expect 1–3 minutes, several calls are normal, and stop immediately on not_found. It also names the concrete failure mode (re-firing double-charges), which is stronger than a generic when-to-use note.
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 leadARead-onlyIdempotentInspect
One LinkedIn lead by id (from list_linkedin_leads), with every answer named by field. Personal data — show, never republish. Read-only, free.
| Name | Required | Description | Default |
|---|---|---|---|
| leadId | Yes | lead id from list_linkedin_leads | |
| pageId | No | the company Page that owns the form — from list_linkedin_pages; omit when one Page is shared | |
| adAccountId | No | read forms owned by an AD ACCOUNT instead of a Page |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already cover the safety profile (readOnly, idempotent, non-destructive), and the description adds two things they cannot: a data-handling constraint ('Personal data — show, never republish') and cost ('free'). It stops short of describing what happens if the lead id doesn't exist or belongs to another Page, but the key behavioral caveats are present.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two short sentences with zero waste; the resource and scope are front-loaded and the handling/cost caveats follow. The clipped phrasing ('with every answer named by field') is slightly odd but still parses as a response-shape hint rather than filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
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 noting that answers are returned as named fields. Combined with the privacy constraint and the schema's complete parameter documentation, an agent has enough to call it correctly, though error behavior for an unknown or cross-Page lead id is unaddressed.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so all three parameters including the Page-vs-ad-account distinction are already fully documented. The description only restates leadId's provenance, adding no extra meaning beyond the schema's own 'lead id from list_linkedin_leads'. Baseline 3 applies 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.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and scope ('One LinkedIn lead by id') and anchors the id's origin to the sibling list_linkedin_leads, so an agent can distinguish it from list_linkedin_leads and list_linkedin_lead_events without opening a schema. The 'every answer named by field' clause signals the response is labeled rather than positional.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The parenthetical '(from list_linkedin_leads)' tells the agent where the required id comes from, which effectively describes the correct call sequence. It does not explicitly say when to prefer list_linkedin_leads (e.g., over many records) or when pageId/adAccountId disambiguation is needed, leaving that to the schema.
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 detailsARead-onlyIdempotentInspect
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.
| Name | Required | Description | Default |
|---|---|---|---|
| fileId | Yes | the OneDrive item id (from list_onedrive_files) |
TDQS
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.
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.
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.
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.
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.
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 statusARead-onlyIdempotentInspect
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. On the Free plan, paidOnly is true and note explains the status. Read-only, free.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
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 contributes real additional behavior – the review vs auto mode semantics, that unpostable channels are named with reasons, and that on the Free plan paidOnly is true with an explanatory note. It does not cover failure modes (e.g. autopilot never configured) or response shape as JSON, which keeps it off 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.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is front-loaded with the core purpose, which is good, but it then unspools into one very long sentence enumerating every returned field, with ALL-CAPS emphasis and parenthetical asides. The content is useful given there is no output schema, but a short structured list would communicate the same information far more scannably.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
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 return-value burden and does so thoroughly: mode, per-channel cadence, cost, next run, queue depth, draft contents, learned notes, and blocked channels. What is missing is only marginal – error/empty states and how the profile is bound.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
There are zero parameters, so per the rubric this takes the baseline 4. The only semantic hint is that it operates implicitly on "this profile," which is all that is needed.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
"Show AUTOPILOT POSTING ... for this profile" gives a specific verb and resource, and the enumerated contents (mode, channels, cost, queue, drafts, notes) make the scope unmistakable. It stops short of naming the siblings set_post_refill and run_post_refill, so the agent must infer that this is the read/status counterpart rather than being told directly.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The quoted user questions ("is autopilot on?", "what is waiting for review?") are concrete usage triggers that map well to real intents. However, there are no exclusions and no explicit pointer to set_post_refill (to change it) or run_post_refill (to trigger it), which are the obvious alternatives for an agent that lands on the wrong tool.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_settingsRead app settingsARead-onlyIdempotentInspect
Read this account's app settings — the LANGUAGE Hermoso writes ads and copy in (chat replies follow the language the user writes 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.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnly, idempotent, non-destructive, so safety is covered; the description adds the non-obvious facts that the call consumes no credits ('free') and that the language setting governs ad/copy output while chat replies follow the user's own language. That language-behavior nuance is real context an agent could not 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.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three short sentences: what it returns first, then the semantics of the most confusing field, then the cost/safety claim. No filler and nothing buried.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
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 three settings returned and clarifying the one that would otherwise be ambiguous (write-language vs. chat language). For a trivial zero-arg read, nothing material is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Zero parameters, so there is nothing to document; the description correctly spends no words on inputs. Baseline for a no-param tool applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('Read this account's app settings') and then enumerates exactly what those settings are — ad/copy language, theme, weekly competitor-watch email. An agent can tell instantly this is the read side of configuration, distinct from update_settings or set_competitor_watch.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
'Read-only, free' signals it is safe and costless to call at any time, and 'Same settings as the web app's Settings pane' grounds it in a familiar surface. However, it never explicitly says when to prefer this over update_settings or how it relates to set_competitor_watch / list_watch_findings, so the routing is only implied.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_skillGet skillARead-onlyIdempotentInspect
Load a bundled skill’s full SKILL.md workflow instructions by name (from list_skills), or a built-in in-app skill by its id (e.g. youtube_script, retention_skeleton). Follow the loaded instructions to run that workflow with the other tools. Read-only, free.
| Name | Required | Description | Default |
|---|---|---|---|
| name | Yes | bundle name from list_skills, e.g. hermoso-generate |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already cover readOnly, idempotent, non-destructive, and closed-world behavior. The description adds value beyond them by stating 'free' (billing context) and explaining what the loaded content is and what to do with it (follow instructions to run the workflow), which is genuinely useful next-step guidance.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with the action and kept to a couple of sentences. Minor redundancy: 'Read-only' restates readOnlyHint, though 'free' adds new billing information, so the trailing phrase mostly earns its place.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a single-parameter, non-destructive tool with no output schema, the description explains what is loaded, where the valid name comes from, and what to do with the result. It does not describe error behavior for an invalid name, but otherwise it is complete.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, but the schema only calls the param a 'bundle name from list_skills,' whereas the description reveals the param can also be a built-in in-app skill id and gives examples (youtube_script, retention_skeleton). This adds real meaning beyond the schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Specific verb+resource: 'Load a bundled skill's full SKILL.md workflow instructions.' It clearly distinguishes the resource (a skill's instructions) from siblings like list_skills, save_skill, and delete_skill, and explains both the bundled-name and built-in-id forms.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It routes the agent to list_skills for obtaining a valid name and says to follow the loaded instructions to run the workflow with other tools. It gives clear context but does not name an explicit 'use X instead' alternative or when-not 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.
| Name | Required | Description | Default |
|---|---|---|---|
| brief | No | what to test, e.g. "price-led vs outcome-led" or "speak to busy parents" | |
| count | No | how many headlines to write when `headlines` is omitted (default 5) | |
| image | Yes | the finished static ad: URL, Library item URL, upload_file URL or local path | |
| fixLabel | No | 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) | |
| headlines | No | your own headlines to test (up to 10); omit to have them written |
TDQS
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.
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.
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.
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.
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.
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_capabilitiesHermoso models, options and credit costsARead-onlyIdempotentInspect
Probe what this Hermoso account can do RIGHT NOW: available image/video model ids + their credit costs (estimates: a render is charged its actual cost), aspect ratios, video durations, the recipe ids, and the canEdit/canAvatar flags. Call it when you need a specific model id, a credit 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.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnly/idempotent/non-destructive, but the description adds real context: the snapshot is live ('RIGHT NOW'), costs are estimates while renders are charged actual cost, and the call is free. It also signals no permissions or side effects are involved. Only the redundancy of 'Read-only' with readOnlyHint 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.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with the capability payload, then the usage trigger, then the negative constraint. Every clause carries information — the field enumeration and the 'not a prerequisite' caveat both earn their space; no filler or restated title.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
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 compensates by listing the returned capability fields and clarifying the estimate-vs-actual charge semantics, which is the one thing an agent could misread when budgeting credits. 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.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The tool takes zero parameters, so there is nothing to disambiguate and the schema is trivially complete. The description correctly spends no space on parameter explanation and instead describes the result shape.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Names a specific verb and resource ('Probe what this Hermoso account can do') and enumerates exactly what is returned: model ids, credit costs, aspect ratios, video durations, recipe ids, canEdit/canAvatar flags. This lets an agent distinguish it from hermoso_credits (balance), get_settings (config) and find_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.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicit trigger conditions ('when you need a specific model id, a credit cost, or a capability you are not sure of') plus an explicit exclusion: it is NOT a prerequisite for rendering because generate_image, generate_video and render_ad route to a server default when `model` is omitted. That prevents the most likely misuse.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
hermoso_creditsCredit balanceARead-onlyIdempotentInspect
Return the credit balance, recent priced calls with their credits, 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 cost no credits on any plan (X is the single per-call exception; xCost gives its exact credits per post, with and without a link). Check before kicking off paid generation; answer "does posting cost credits?" with NO. HOW MANY posts is set by the plan, not credits: the Free plan includes 10 posts a month on up to 3 channels, and a post counts once however many channels it goes to (posting says how many are used); every paid plan posts without a limit and runs autopilot (Basic $9.99/mo: unlimited posting and Autopilot, no monthly credits; it includes the same 250+ free credits as the Free plan, monthly credits start on Starter, and Autopilot spends credits). Starter, Pro and Studio: media models at cost + 10% (memberRate); Basic and Free pay standard prices.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already cover read-only, idempotent, closed-world and non-destructive, so the bar is low; the description still adds substantial meaning by spelling out what consumes credits (AI model runs and Ad Spy), the X per-post exception with xCost, and the memberRate markup. It also discloses that posting volume is plan-governed, not credit-governed, which prevents a common misread of the balance.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Purpose and 'THE RULE' are front-loaded, which is good structure, but the second half drifts into plan marketing (Basic $9.99/mo, 250+ free credits, Free plan post limits, Autopilot behavior) that does not help invoke this tool. Several sentences would be better served by billing_status or upgrade_plan; the description is roughly twice the length its job requires.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
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 names the return fields and their meaning, and the credit rule is fully explained. What remains unaddressed is minor for a zero-param read call, though it never states the shape of the balance itself or what a 'recent priced call' entry contains.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The tool takes zero parameters, so there is nothing to disambiguate and the baseline is 4. The description instead spends its words on output keys (costModel, xCost, posting, memberRate), which is the right emphasis for a no-arg tool.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Opens with a specific verb and enumerated return payload: 'Return the credit balance, recent priced calls with their credits, and costModel'. That is far more specific than the title 'Credit balance' alone. It never distinguishes itself from siblings like billing_status or buy_credits, so an agent must infer the boundary, which keeps it below a 5.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives clear situational guidance: 'Check before kicking off paid generation' and 'answer "does posting cost credits?" with NO'. That covers both the pre-flight check and the support-question use case. It stops short of naming alternatives (billing_status for invoices, buy_credits for top-ups), so no explicit when-not or sibling routing.
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: quoteOnly:true returns the exact credits free: state that first. A paid planning read (planCredits) runs next, then each planned version bills like fix_beat for the hook's seconds. dryRun:true pays for that read; pass its 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).
| Name | Required | Description | Default |
|---|---|---|---|
| plan | No | the `plan` object a previous dryRun returned, to render exactly those hooks without planning again | |
| count | No | how many hook versions, 1-5 (default 3) | |
| hooks | No | your own openings, one version each | |
| notes | No | anything the hooks must respect, e.g. "keep it calm", "show the product in every hook" | |
| video | Yes | the finished video to give new hooks: its served file URL | |
| dryRun | No | true = the paid plan and the price, no render | |
| useBrand | No | false = send no brand name or product photo (for a video that is not this workspace brand’s) | |
| quoteOnly | No | true = the exact credits, free; nothing runs | |
| resolution | No | render 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. | |
| hookSeconds | No | where the CURRENT hook ends, in seconds (1.5-4). Omit to use the first shot cut. | |
| productImage | No | product photo URL used as a reference in hooks that show the product (defaults to the workspace brand’s first product photo) |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Adds substantial behavior beyond annotations: billing sequence, job-per-version flow, source constraints, soundtrack preservation, silent no-text hook rule, and get_job polling requirement. No contradiction with readOnlyHint=false, openWorldHint=true, idempotentHint=false, or destructiveHint=false.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loads purpose and mechanics effectively, and nearly every clause carries useful operational detail for a complex 11-parameter tool. However, it is very dense and run-on, with several concepts packed into long semicolon-linked sentences.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Complete for a complex generation tool with no output schema: it explains cost modes, refusal conditions, plan handling, per-version job returns, and how to poll get_job before describing a version. 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.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, but the description adds operational meaning for key parameters: hooks[] url/prompt/mechanic behavior, count default, quoteOnly free pricing, dryRun paid planning, plan passback, hookSeconds override, resolution tier, and productImage/useBrand implications.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific action and resource: give one finished video ad N new opening hooks, each a complete A/B-testable version. It distinguishes itself from nearby tools such as list_hooks, recast_hook, clone_video, and fix_beat by naming those alternatives in context.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly covers when to use each mode: quoteOnly for free exact pricing, dryRun for paid planning, hooks[] for custom openings, and recast_hook when the found hook's subject must first become the product or creator. It also names refusal cases and the clone_video alternative for social-post sources.
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) 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. Picking a FOLDER hands over the folder alone, not the files in it, so if a folder comes back empty the reply carries a link that opens the picker inside it, where the user selects every file at once (Cmd+A / Ctrl+A); give them that link. 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.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | how 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. | |
| folderId | No | the folder to import, from list_drive_files / list_onedrive_files (onlyFolders:true). Omit for the root of the drive. | |
| provider | Yes | which cloud — `drive` is Google Drive, `onedrive` is Microsoft OneDrive |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only give a generic write/open-world/non-idempotent/non-destructive profile; the description adds substantial context beyond that: it downloads via the user's connected account, stores a durable Hermoso URL per file, and is bounded by the drive.file OAuth scope. It discloses real behavioral limits an agent must relay to a user — subfolders are not walked, Google-native docs have no file to download, oversized/unreadable files are returned by name rather than silently dropped, and OneDrive has no picker scope limit.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Purpose is front-loaded in the first sentence and the escalating caveats follow in a sensible order. The folderId instruction is duplicated from the schema and the all-caps emphasis is heavy-handed, but nearly every sentence carries information an agent needs to explain outcomes to a user.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With only three parameters and no output schema, the description covers everything an agent needs: source discovery, auth precondition, scope limitation, partial-failure reporting semantics, and what to do when nothing is returned. There is no output schema, so the description's explanation of skipped-item reporting and the picker link fills the return-value gap well.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the schema already explains provider, folderId sourcing, and the limit/skip behavior; baseline 3 applies. The description largely restates what the schema says about folderId (source tool, omit for root) rather than adding new semantics, though the drive.file explanation meaningfully explains why folderId may yield nothing.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The opening sentence names a specific verb (pull/import), a specific source (a Google Drive or OneDrive folder), and a specific destination (this profile's Library), then lists concrete downstream uses (publish, schedule, clone, product photo, reference). It is clearly distinguishable from the discovery siblings (list_drive_files / list_onedrive_files) and from the save_to_* tools by direction of data flow.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It explicitly routes the agent to list_drive_files / list_onedrive_files with onlyFolders to obtain folderId, says to omit folderId for the root, and gives concrete follow-up instructions when a folder comes back empty. It also states the key precondition (user's own connected account is required because the file is not public). What it lacks is an explicit exclusion, e.g. when to prefer upload_file or get_drive_file instead of importing.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
insert_sceneInsert a new scene into a videoAInspect
INSERT a newly generated scene into a clip you already have: the video plays up to startTime, the new scene (5 to 13 seconds) plays, then the original picks up again at resumeTime. Use for a product close-up, a reaction, a reveal or a missing beat in the middle of an ad without re-rendering the rest. resumeTime = startTime keeps every second of the original; a later resumeTime also cuts the footage in between. Describe the scene in prompt and/or attach up to 4 reference pictures (refImages) or 1 reference clip (refVideos). Clips up to 60 seconds; both times between 1.625 and 60 seconds. Paid render (billed per second of NEW scene); returns the whole video's URL.
| Name | Required | Description | Default |
|---|---|---|---|
| video | Yes | the source video URL (a render, job result or list_library item) | |
| prompt | No | what happens in the new scene: the action, the subject, the camera | |
| seconds | No | length of the new scene, 5-13 seconds (default 5) | |
| refImages | No | up to 4 picture URLs of what should appear (the product, a person, a place) | |
| refVideos | No | up to 1 clip URL whose look or motion the scene follows | |
| startTime | Yes | seconds into the clip where the new scene starts (1.625-60) | |
| resolution | No | default 720p (rendered at 768p); '480p' is cheaper | |
| resumeTime | Yes | seconds into the ORIGINAL clip where it picks up again after the new scene (2.625-60, at or after startTime) |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Beyond the annotations (non-read-only, open-world, non-idempotent), the description discloses cost ('paid render, billed per second of NEW scene'), the return value ('returns the whole video's URL'), clip-length limits, and the crucial resumeTime behavior where a later time discards intervening footage. This is exactly the behavioral 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.
Is the description appropriately sized, front-loaded, and free of redundancy?
The core mechanism is front-loaded in the first sentence and every subsequent sentence adds distinct value: use cases, resumeTime semantics, reference limits, duration limits, and cost/return. No filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a paid mutation tool with no output schema, the description covers cost model, return value, time-range constraints, and the startTime/resumeTime interaction. 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.
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 genuinely adds meaning: it explains that resumeTime = startTime preserves every original second while a later resumeTime cuts footage in between, and clarifies refImages/refVideos limits. It does not elaborate on the 'video' source parameter beyond 'a clip you already have'.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description uses a specific verb and resource ('INSERT a newly generated scene into a clip') and explains the exact mechanism (plays to startTime, new scene, resumes at resumeTime). It is highly clear but never names or distinguishes itself against closely related siblings such as make_insert, edit_video, or stitch_video, which is the only thing keeping it from a 5.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It gives concrete use cases ('product close-up, a reaction, a reveal or a missing beat in the middle of an ad without re-rendering the rest') and explains the resumeTime tradeoff, which tells the agent when this tool fits. It does not explicitly state when not to use it or which sibling to choose instead.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
invite_memberInvite a teammateADestructiveInspect
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.
| Name | Required | Description | Default |
|---|---|---|---|
| role | No | default member | |
| Yes | the invitee’s email | ||
| confirm | No | REQUIRED true — this invites a real person |
TDQS
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.
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.
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.
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.
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.
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 connectionADestructiveIdempotentInspect
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.
| Name | Required | Description | Default |
|---|---|---|---|
| provider | Yes | provider id exactly as list_connectors reports it, e.g. "linkedin", "tiktok_ads", "meta" |
TDQS
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.
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.
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.
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.
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.
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 profilesARead-onlyIdempotentInspect
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.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
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.
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.
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.
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.
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.
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 listingsARead-onlyIdempotentInspect
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).
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
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.
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.
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.
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.
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.
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 accountsARead-onlyIdempotentInspect
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, 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.
| Name | Required | Description | Default |
|---|---|---|---|
| provider | Yes | which connector’s accounts to list |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint, idempotentHint, non-destructive, and openWorld, and the description reinforces this with "Read-only, free," adding cost information the annotations do not carry. It also discloses the crucial state distinction between available identities and currently permitted ones, though it does not describe response shape or behavior when a provider is not connected.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The core purpose and the set_connector_accounts sequencing are front-loaded and useful, but the 24-item provider enumeration duplicates the schema enum almost exactly and consumes roughly a third of the text. That block is redundant padding rather than earned content.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a single-parameter read tool with full annotations and no output schema, the description covers purpose, prerequisite sequencing, and the semantics of the returned permission state. It stops short of describing the return fields (e.g., account IDs, names, selected flag), which would matter since there is no output schema.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100% and the single provider parameter has a full enum with its own description, so the schema does the heavy lifting. The description repeats the provider list verbatim without adding selection guidance (e.g., which provider maps to which account type, or what happens for an unconnected provider), so it adds little beyond the schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ("Show every identity a connected account can act as") and enumerates the concrete account types returned (Facebook Pages, Instagram, Meta ad accounts, Google Ads customers, LinkedIn pages, Pinterest ad accounts, Microsoft Advertising). It is clearly distinguishable from siblings like list_connectors (which lists connectors) and list_meta_pages (single-provider).
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly says to call this before set_connector_accounts and to let the USER pick rather than guessing, which is a concrete sequencing and behavior rule. The "only the ticked ones can be posted to or spent from" line explains the consequence of the selection, giving the agent a reason to use it.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_connectorsList connectorsARead-onlyIdempotentInspect
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. Read-only, free.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare read-only, idempotent, non-destructive, closed-world behavior, so the bar is lower. The description adds valuable behavioral context beyond that: it explains why permissions can become stale (granted set is frozen into the token at consent time), that retries or waiting cannot fix it, and that the remedy is a browser reconnection under Workspace > Connectors.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loads the main purpose, then adds the diagnostic flag and remediation detail. Every sentence carries useful context, but the description is longer than necessary for a zero-parameter list tool and uses emphatic all-caps phrasing that could be tightened without losing meaning.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no output schema and only safety annotations, the description carries the burden of explaining return contents and behavior. It lists what the caller sees (provider, status, connected account label, available providers) and explains the stale-permission flag, its cause, and the fix, so an agent has enough context 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.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The tool has zero input parameters, so the baseline is 4 and there is no parameter semantics to explain. The schema is empty and fully covered, and the description does not need to compensate for any undocumented inputs.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource — listing third-party accounts connected to the workspace — plus available providers and an out-of-date permission flag. Clear enough to distinguish from most siblings, but it never names or contrasts with the closely related list_connector_accounts tool, so an agent still has to infer the boundary.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Provides a concrete diagnostic trigger: 'Check this FIRST when a connected provider starts refusing things.' This tells the agent when the tool is especially useful. However, it does not state when not to use it or explicitly route to an alternative such as list_connector_accounts or connect_connector.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_creatorsList saved creatorsARead-onlyIdempotentInspect
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.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | max creators to return (default 24) | |
| gender | No | filter the PRESET creators by gender (the saved cast is never filtered) |
TDQS
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.
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.
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.
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.
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.
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 filesARead-onlyIdempotentInspect
List the Google Drive files & folders Hermoso can reach: the ones it created plus any the user picked with the Google file picker in the app (drive.file, never their whole Drive). This is how you find a picked file's id. Filter by query (name contains …), folderId, or onlyFolders:true; paginate with pageToken. Read-only. A FOLDER: picking a folder hands over the folder alone, not its files, so a folder can list empty or partly; the reply then carries pickUrl, an app link that opens the picker inside that folder (Cmd+A / Ctrl+A selects every file and subfolder). Give the user that exact link, then list again. recursive:true walks every subfolder Hermoso can see (each row has its path) and reports picked files whose folder it cannot see.
| Name | Required | Description | Default |
|---|---|---|---|
| query | No | only files whose name contains this | |
| folderId | No | list the contents of this folder id | |
| pageSize | No | rows per page (1–200, default 50) | |
| pageToken | No | cursor from a previous call | |
| recursive | No | with folderId: the whole tree | |
| onlyFolders | No | list folders only | |
| includeTrashed | No | include trashed files (default false) |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already cover read-only/idempotent/non-destructive, yet the description adds substantial non-obvious behavior: the drive.file scope limit, the folder-pick gotcha ('a folder can list empty or partly'), the pickUrl round-trip, the Cmd+A/Ctrl+A trick, and that recursive lists picked files whose folder it cannot see. This is well beyond the annotation surface.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loads what it does before the workflow detail, and the folder-picker paragraph is dense but each clause carries operational weight. It is fairly long and somewhat run-on, which keeps it off a 5.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 7-param, no-output-schema tool, it explains return content (pickUrl, per-row path) and the edge cases (empty/partial folders, invisible parents) that an agent needs to call it correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
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 on top: recursive 'walks every subfolder Hermoso can see' with a `path` per row, and clarifies query/folderId/onlyFolders/pageToken behavior. pageSize and includeTrashed are left to the schema, so not a full 5.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
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 immediately bounds the scope: 'the ones it created plus any the user picked ... (drive.file, never their whole Drive).' This scope statement alone distinguishes it from list_onedrive_files and from the broader Drive siblings.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives clear operating context: 'This is how you find a picked file's id,' when to use folderId/onlyFolders/recursive, and the Cmd+A picker workflow for empty folders. It never names a sibling alternative explicitly (e.g. get_drive_file for reading a known id), 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.
list_errorsList errors users hitARead-onlyIdempotentInspect
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.
| Name | Required | Description | Default |
|---|---|---|---|
| kind | No | 'ours' = a defect; 'user' = a refusal we authored; 'unknown' = we could not tell | |
| limit | No | how many groups to return (default 50, max 200) | |
| since | No | ISO timestamp — only groups last seen at or after this | |
| surface | No | where 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
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.
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.
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.
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.
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.
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 workingARead-onlyIdempotentInspect
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.
| Name | Required | Description | Default |
|---|---|---|---|
| tier | No | product tier, used with category — changes the FINISH of the room, never the room. Default premium. | |
| channel | No | restrict the performance half to one channel (facebook, instagram, threads, x, linkedin, youtube, tiktok, pinterest, bluesky) | |
| category | No | the product category (e.g. 'skincare serum', 'protein powder', 'sunglasses') — returns the setting our Location x Tier matrix puts that category in, with the reason | |
| authentic | No | true if the planned ad is an authentic/UGC/creator-register render — on-screen-text hooks are then reported unusable, with the reason |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnly/idempotent/non-destructive, so the description earns credit for going further: a hard suppression rule below 5 measured posts with the reason stated, a no-cross-channel-comparison limit, untried hooks reported as observation rather than advice, and explicit reasons when a hook is marked unusable for an authentic/UGC render. The '0 credits' note adds cost information 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.
Is the description appropriately sized, front-loaded, and free of redundancy?
Core information is front-loaded in the first sentence, but the 'three things it will not do' section drifts into justification prose ('an observation and "you should" would be a verdict drawn from zero data') that restates the same point rather than adding usable detail. Tighter phrasing would preserve the constraints without the digression.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With four optional parameters, full schema coverage, and no output schema, the description is nearly complete: it says what is returned (library plus per-hook traction, with suppressions and unusable flags) and how to use it in the workflow. It stops short of describing the response shape or library size, which would help given there is no output schema.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so each parameter's meaning is already documented, including the authentic flag's effect on on-screen-text hooks. The description reinforces the channel scoping and the authentic-render consequence, but adds little syntax or semantics the schema does not already state — baseline 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description names both resources it returns — the hook/setting library that plan_ad and render_ad consume, plus this brand's measured traction per hook — and explicitly frames itself as the reference consulted before planning. An agent can distinguish it from plan_ad, render_ad, and hook_variants 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.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It gives two explicit invocation points: before planning an ad (to choose a hook deliberately rather than improvise) and after publishing (to see which hooks land). It also carves out boundaries — no cross-channel comparison, no recommendation from thin data — which functions as when-not guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_jobsList render jobsARead-onlyIdempotentInspect
List the most recent render jobs + how many are currently running, so you can report on or resume in-flight work.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
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.
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.
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.
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.
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.
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 libraryARead-onlyIdempotentInspect
Browse this workspace's Library — every image/video generated in the Studio, and uploads (source:'upload'), 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 id (L0, L1…, which plan_ad and render_ad inserts take), kind, model, and age. Free, read-only.
| Name | Required | Description | Default |
|---|---|---|---|
| kind | No | filter by asset kind (default 'all') | |
| limit | No | max assets to return (default 20, max 60) | |
| uploads | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare read-only, idempotent, non-destructive, closed-world, but the description adds real context beyond them: zero credit cost, newest-first ordering, and a precise inventory of returned fields (URLs, id, kind, model, age). It doesn't say what happens when the library is empty or how the limit cutoff behaves, keeping it below 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.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two dense sentences, front-loaded with the resource and ordering, then the return contract, then the cost/safety tag. Parenthetical asides all carry load (id format, consumption by siblings) rather than padding.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
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 well: URLs, ids, kind, model, age. The one gap is the `uploads` parameter's exact semantics, which neither schema nor description pins down for a 3-parameter listing tool.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 67% and the kind/limit parameters are already documented in the schema; the description adds nothing about defaults or enum behaviour. The undocumented `uploads` boolean gets only an oblique hint via 'uploads (source:'upload')', which does not clearly explain whether it filters to uploads-only or includes them, so compensation for the coverage gap is partial.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a concrete verb and resource ('Browse this workspace's Library'), scopes the contents (Studio generations plus uploads, newest first), and ties it to the web app view. It also names sibling relationships: fetch_asset for downloads, plan_ad/render_ad consuming the L-id values, so an agent can place the tool in the workflow without reading schemas.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives clear usage context: browse here, then hand a served URL to fetch_asset if a download link is needed, and use the id in plan_ad/render_ad inserts. 'Free, read-only' signals cost and safety. It stops short of explicit exclusions (e.g. vs. list_product_photos or list_swipefile), so it is strong but not fully routing.
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 timeARead-onlyIdempotentInspect
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.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | events to return, newest first; default 50, max 200 |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Beyond the annotations (readOnly/idempotent/non-destructive), the description discloses that data arrives via push rather than being a full store, that an empty response has a specific meaning, and that the call is free. These are exactly the nuances that prevent an agent from misreading an empty list as 'no leads exist'.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three sentences, front-loaded with what the tool returns, then the empty-result caveat, then the sibling routing. No filler and nothing buried.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
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 the shape of returned events (type, form, lead id) and their ordering, plus the delivery model and cost. An agent has everything needed to call and correctly interpret the result.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100% and the single `limit` parameter already documents default 50 and max 200. The description only restates ordering ('newest first'), adding no syntax or format detail 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.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource (lead events pushed to Hermoso for this brand), enumerates the event types (new lead / deleted lead) and the payload fields (form and lead id), and fixes ordering (newest first). This is clearly distinguishable from list_linkedin_leads and get_linkedin_lead, which are named in the same breath.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly routes the agent: use list_linkedin_leads to read every lead regardless of push delivery, use subscribe_linkedin_leads to start delivery, and use get_linkedin_lead to read a lead's answers. It also gives the crucial when-not interpretation — an empty result means none have arrived yet, not that no leads exist.
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 formsARead-onlyIdempotentInspect
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 …). Make a new one with create_linkedin_lead_form; edit or archive one with update_linkedin_lead_form. If it answers that the connection must be reconnected, say exactly that: the lead-sync permission is granted at authorise time. Read-only, free.
| Name | Required | Description | Default |
|---|---|---|---|
| pageId | No | the company Page that owns the form — from list_linkedin_pages; omit when one Page is shared | |
| adAccountId | No | read forms owned by an AD ACCOUNT instead of a Page |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnly/idempotent/non-destructive, and the description reinforces this while adding cost context ('free') and, more valuably, a behavioral rule for the authorise-time permission failure — information not derivable from any structured field. It does not discuss pagination or result limits, so it is strong but not exhaustive.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loads the resource and its return shape, then moves to sibling routing and finally the error-handling note. Every sentence carries information; only the trailing 'Read-only, free' partly restates the readOnlyHint annotation.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
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, and it covers both parameters and the notable failure mode. For a simple two-optional-param list tool this is nearly sufficient; only pagination/volume expectations are absent.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100% with only two optional parameters, both already explained in the schema (pageId from list_linkedin_pages, adAccountId for ad-account ownership). The description's phrase 'a LinkedIn company Page or ad account owns' mirrors that but adds no new syntax, defaults, or precedence rules for when both are supplied.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Names the exact resource (LinkedIn lead gen forms owned by a Page or ad account) and enumerates the returned fields (id, name, state, version, field list), which cleanly separates it from lead-oriented siblings like list_linkedin_leads and get_linkedin_lead. It does not explicitly contrast itself with those siblings, but the resource specificity makes selection unambiguous.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives explicit routing for adjacent operations: use create_linkedin_lead_form to make one, update_linkedin_lead_form to edit or archive. It also gives a concrete fallback instruction for the reconnect-permission failure. Missing is any statement of when NOT to use this versus list_linkedin_leads or list_linkedin_lead_subscriptions, so it stops short of full alternative coverage.
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)ARead-onlyIdempotentInspect
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.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | per page, max 100 | |
| since | No | ISO date or epoch milliseconds | |
| start | No | offset for the next page | |
| until | No | end of the time window, ISO date or epoch milliseconds; omit for now | |
| formId | No | only this form (from list_linkedin_lead_forms) | |
| pageId | No | the company Page that owns the form — from list_linkedin_pages; omit when one Page is shared | |
| leadType | No | defaults 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 | |
| adAccountId | No | read forms owned by an AD ACCOUNT instead of a Page | |
| formVersion | No | default 1 | |
| testLeadsOnly | No | true returns ONLY test submissions |
TDQS
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 material context on top — newest-first ordering, the personal-data handling policy ('never repeat it into a post or an unrelated tool'), pagination via start, and that the call is free. These are behavioral facts the 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.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loads what a lead record contains before moving to filters, privacy and pagination — the right order for an agent deciding whether to call it. It is a long run-on sentence chain with caps emphasis and an ellipsis in the field list, which costs a little crispness but no sentence is truly wasted.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 10-parameter read tool with no output schema, the description carries the important load: it describes the returned record shape, ordering, pagination and data-handling obligations. Minor gaps remain — no mention of the default page size or what happens when no formId/owner is supplied — but those are covered by schema descriptions.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100% and every parameter carries its own description (including the leadType default-by-owner rule and pageId sourcing), so the schema does the heavy lifting. The description only re-surfaces formId, since/until and testLeadsOnly and adds the pagination hint for start; it says nothing about limit, pageId, adAccountId or formVersion beyond what the schema already states.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
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') and then enumerates the return payload — answers keyed by field, campaign/creative attribution, consents, test-lead flag. That level of specificity makes it clearly distinct from get_linkedin_lead (single lead) and list_linkedin_lead_forms (form definitions) without needing to name them.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives concrete guidance on how to use it: filter by formId, a since/until window, or testLeadsOnly, and 'pass start for the next page' for pagination. It also prescribes what to do with the results (show to the user, hand to the named CRM). It does not name alternatives for single-lead or event-level lookups, so it stops short of explicit when-not-to-use routing.
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 webhooksBRead-onlyIdempotentInspect
The lead notification webhooks registered on a LinkedIn Page or ad account, with the id delete_linkedin_lead_subscription takes. Read-only, free.
| Name | Required | Description | Default |
|---|---|---|---|
| pageId | No | the company Page that owns the form — from list_linkedin_pages; omit when one Page is shared | |
| leadType | No | SPONSORED for an ad account; COMPANY, EVENT or ORGANIZATION_PRODUCT for a Page; defaults by owner | |
| adAccountId | No | read forms owned by an AD ACCOUNT instead of a Page |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint, idempotentHint and destructiveHint=false, so 'Read-only' restates structured data. 'Free' is genuinely additive (cost transparency not in annotations) and the reference to the delete consumer clarifies the read-only nature of the resource. No mention of pagination, return shape, or what a subscription represents.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
A single dense sentence fragment with no filler; the deletion-id purpose is front-loaded at the end of the lead clause. Slightly awkward as a noun phrase that lacks a leading verb, but nothing is wasted.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a zero-required-param read tool with full schema coverage and safe annotations, the description covers what the resource is and why you'd read it. It stops short of explaining what a subscription/webhook contains or how results are ordered, but no output schema exists to relieve that gap.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the three parameters (pageId, leadType, adAccountId) are already fully documented in the schema. The description's 'on a LinkedIn Page or ad account' echoes the mutually exclusive owner scoping but adds no syntax, defaults or format detail beyond the schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The title and description state the resource precisely — lead notification webhooks registered on a LinkedIn Page or ad account — and even name the downstream consumer of the returned ids (delete_linkedin_lead_subscription). It doesn't distinguish itself from near siblings like list_linkedin_lead_forms, list_linkedin_leads or subscribe_linkedin_leads, so an agent must infer the boundary.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The clause 'with the id delete_linkedin_lead_subscription takes' implicitly tells the agent why to call this: to discover webhook ids before deleting them. Beyond that coupling there is no explicit when-to-use, no exclusion against the other LinkedIn lead tools, and no prerequisite info.
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 administersARead-onlyIdempotentInspect
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.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
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.
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.
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.
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.
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.
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 memoryARead-onlyIdempotentInspect
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.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | max items (default 50, max 200) | |
| category | No | filter to one bucket (Brand/Audience/Taste/Do/Don’t/Preference) |
TDQS
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.
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.
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.
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.
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.
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 accountsARead-onlyIdempotentInspect
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.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
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.
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.
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.
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.
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.
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 postsARead-onlyIdempotentInspect
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. 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.
| Name | Required | Description | Default |
|---|---|---|---|
| brand | No | 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. | |
| limit | No | how many posts (default 25, max 100) | |
| cursor | No | paging cursor returned by a previous call | |
| pageId | No | which connected Page — omit when the profile has only one | |
| tagged | No | INSTAGRAM — list posts OTHER accounts tagged the brand in (UGC, creator posts, partner mentions), with each author’s @handle, instead of the brand’s own posts. Free. | |
| target | No | default facebook; 'instagram' reads the Page's linked IG business account | |
| account | No | which 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. | |
| includeUnpublished | No | Facebook only — also return unpublished drafts (hidden by default) |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Goes well beyond the annotations by disclosing that IG Stories are excluded because Meta's media edge doesn't return them, that Facebook hides unpublished drafts unless asked, that only a brand-connected Page is ever read, and that the call costs 0 credits. These are behavioral constraints and caveats an agent could not infer 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.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with purpose and returned fields, then the postId-routing value proposition, then caveats — a sensible ordering. It is dense and clause-heavy in the back half (Stories caveat, drafts caveat, read-only, credits), 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.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With 8 optional parameters and no output schema, the description carries the important load: it names the returned fields and marks pagination via the cursor param documented in schema. It does not describe pagination behavior or result ordering in prose, a minor gap for a list tool of this size.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so baseline is 3, but the description adds real meaning: 'target:instagram' reads the linked IG business account and Stories are never returned, and the unpublished-draft default is surfaced as a behavior rather than just a flag. The remaining params (limit, cursor, pageId, account, tagged, brand) are left to the schema entirely.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('List the connected Facebook Page's or Instagram account's OWN existing posts') and enumerates the returned fields (id, caption, permalink, publish date, format). The emphasis on 'OWN' clearly separates it from sibling search tools like search_instagram or search_posts that scan other accounts.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly positions the tool as the postId source for meta_post_insights, list_meta_comments and manage_meta_post, and names concrete use cases ('how did our last few posts do', loose post descriptions, before backfill_posts). It also states when to pass target:'instagram', which is the main routing decision an agent faces here.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_notion_pagesList Notion pagesARead-onlyIdempotentInspect
Find the Notion pages and databases the user shared with Hermoso, by title, newest edited first. Gives the page id that save_to_notion, append_to_notion_page and read_notion_page take, or the database id to save rows into. Read-only, free. Needs Notion connected (Settings > Connectors > Notion).
| Name | Required | Description | Default |
|---|---|---|---|
| type | No | limit results to pages or to databases; omit for both | |
| limit | No | 1-100, default 25 | |
| query | No | words in the title | |
| cursor | No | from the previous page |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already establish read-only, non-destructive behavior. The description adds useful context beyond annotations, including result ordering (newest edited first), credit cost ('free'), and the prerequisite that Notion must be connected.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is three sentences with no wasted words. It front-loads the core purpose, then downstream ID usage, then the connection prerequisite.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Although there is no output schema, the description explains the key return value: page IDs for downstream tools or database IDs for saving rows. Combined with annotations and full schema coverage, 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.
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 documented in the schema. The description adds high-level meaning around title search and ordering, but does not elaborate on parameters like type, limit, or cursor beyond what the schema already provides.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific verb and resource: finding Notion pages and databases the user shared. It distinguishes itself from sibling tools by explaining that it returns IDs consumed by save_to_notion, append_to_notion_page, and read_notion_page.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It clearly conveys when to use this tool: to find pages/databases by title or to obtain IDs for downstream Notion tools. It does not explicitly exclude scenarios or name a direct alternative, but the intended use is unambiguous.
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 filesARead-onlyIdempotentInspect
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.
| Name | Required | Description | Default |
|---|---|---|---|
| query | No | search — only items whose name matches this | |
| folderId | No | list the contents of this folder id | |
| pageSize | No | rows per page (1–200, default 50) | |
| pageToken | No | cursor from a previous call | |
| onlyFolders | No | list folders only |
TDQS
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.
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.
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.
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.
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.
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 boardsARead-onlyIdempotentInspect
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).
| Name | Required | Description | Default |
|---|---|---|---|
| account | No | which connected account: its @handle or id from list_connector_accounts; needed only when several are connected | |
| privacy | No | filter by board privacy; default is everything the connection can see |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare read-only, idempotent, non-destructive, so the safety profile is covered. The description adds value beyond that: zero credit cost and the connector prerequisite that determines whether the call can succeed at all. It stops short of covering pagination or what is returned when no boards exist, so it is not a full 5.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Four compact sentences, each earning its place: purpose and fields, then the ordering rule, then the cost/safety note, then the prerequisite. The most decision-critical instruction (call before post_to_pinterest) is front-loaded after the purpose.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With zero required parameters, a fully documented schema, and no output schema, the description fills the remaining gap by listing exactly which board fields come back. An agent has everything needed to know when to call it, what it costs, and what it gets.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so both the `account` handle/id semantics and the `privacy` enum with its default are already documented. The description echoes the privacy dimension as a returned field but adds no filter syntax or formatting guidance beyond the schema, making this the baseline for a fully described schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ("List the boards on the user's connected Pinterest account") and enumerates the returned fields (id, name, privacy, pin count). This makes it trivially distinguishable from siblings like list_meta_pages or list_linkedin_pages, which target other platforms.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives an explicit mandatory ordering rule ("ALWAYS call this before post_to_pinterest") plus a hard behavioral constraint ("let the USER pick: Pinterest requires a board and Hermoso never chooses one for them"). It also states the prerequisite (Pinterest connected via Settings > Connectors).
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_playbooksList playbooksARead-onlyIdempotentInspect
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.
| Name | Required | Description | Default |
|---|---|---|---|
| full | No | true to return every hook/angle/play in the text, not just the headline counts | |
| limit | No | max playbooks to return (default 25, max 100) |
TDQS
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.
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.
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.
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.
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.
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 photosAIdempotentInspect
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 + the product it shows (the shop's own title when one is on file). These are already saved: never ask the user to upload them again. 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).
| Name | Required | Description | Default |
|---|---|---|---|
| brandId | No | a profile id/name from list_brands whose product library to list; omit to use the active profile |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Goes well beyond annotations by disclosing cost ("FREE"), the return shape (each photo's url plus the product title when on file), and critically the hidden write side effect of brandId ("that switches this key's active profile like use_brand") — which reconciles the readOnlyHint=false annotation with a tool named 'list'. It also explains that photos from other creations appear because sets land in a shared library.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Purpose is front-loaded in the first clause, but the prose is dense and shouty with caps, em-dashes, and parenthetical nesting that slightly obscures the sequence of ideas. Every sentence carries information, so it is efficient but not clean.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
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 contents itself, plus cost, source scope, the profile-switch caveat, and the intended order relative to set_product_image. An agent has everything needed 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.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so brandId is already documented; the description still adds meaningful semantics by warning that passing it switches the active profile like use_brand, and that omitting it uses the active profile. This is a genuine side-effect disclosure beyond the schema's field description.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ("List the product photos ALREADY saved in your workspace") and defines the exact scope: brand library plus app-store screens plus photos from other creations. It is clearly separable from siblings like set_product_image and fetch_app_screens.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives explicit sequencing advice ("Call it before set_product_image to see the existing photos you can reuse") and a behavioral rule ("never ask the user to upload them again"). It stops short of naming when NOT to use it or pointing at fetch_app_screens as the alternative for new screens, so not a full 5.
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 publishedARead-onlyIdempotentInspect
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.
| Name | Required | Description | Default |
|---|---|---|---|
| brand | No | 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. | |
| limit | No | max posts (default 50, max 200), newest first | |
| cursor | No | nextCursor from a previous reply: the next, older page | |
| channel | No | filter to one channel: facebook, instagram, threads, x, linkedin, youtube, tiktok, pinterest, bluesky, google_business |
TDQS
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 still adds genuine context by stating there is no cap and how pagination terminates via nextCursor. It also discloses the 'captured' vs 'backfilled' provenance semantics and warns that a dash for engagement means unreported, not zero — real behavioral detail an agent could otherwise misread. The 'read-only, 0 credits' claim mostly restates the annotations, 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.
Is the description appropriately sized, front-loaded, and free of redundancy?
Effectively two front-loaded sentences: scope and returned columns first, then pagination and provenance semantics. Nearly every clause earns its place, though the dense field enumeration and all-caps emphasis (WHOLE, HOOK, SUBJECT, NOT) push it toward the verbose end for a read-only list tool.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
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 carries the return-shape burden and does: it lists the fields per row, explains pagination to exhaustion, interprets the provenance flag, and disambiguates a dash value. For a four-parameter read-only listing tool with full schema coverage and annotations, nothing an agent needs in order to call or interpret it is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so brand, limit, cursor, and channel are already documented, and the description only reinforces the cursor loop with 'pass the reply's nextCursor as `cursor` for older posts.' It adds no channel-enum detail, default/max limits, or brand-resolution guidance beyond what the schema provides, so the baseline 3 holds.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
"List every post Hermoso has recorded publishing for this brand, newest first, across all channels" gives a precise verb, resource, ordering, and scope, and then enumerates the returned fields (channel, permalink, caption, format, hook, subject, engagement). That distinguishes it from retrieval-style siblings like search_posts, list_meta_posts, and post_performance without the agent needing to open a schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Usage context is implied and partially stated: it is the record of what was actually published, and the cursor sentence tells the agent how to walk older pages. However, it never names an alternative (search_posts for keyword discovery, post_performance for metric deep-dives, diagnose_posts for analysis) or states an exclusion, so an agent must infer routing from the name and description alone.
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 postsARead-onlyIdempotentInspect
Use this when the user asks what is scheduled, what already posted, or whether a post went out. 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.
| Name | Required | Description | Default |
|---|---|---|---|
| id | No | one post id from this list: returns that post in full, every caption and setting included | |
| brand | No | 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. | |
| fired | No | how many already-fired posts to list, most recent last (default 15, max 200) | |
| channel | No | only posts that include this channel, e.g. "pinterest" or "x" | |
| upcoming | No | how many queued posts to list, soonest first (default 25, max 200) |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already cover readOnly/idempotent/non-destructive, and the description adds genuinely non-obvious behavior: the list is truncated (next 25 queued, last 15 fired) with captions shortened, past items are rebuilt from published-post records rather than job runs, and each item reports per-channel verdicts so partial failures aren't masked. Cost ('0 credits') and the full-detail escape hatch via `id` round it out.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with the use case and the most decision-relevant facts (compactness, per-channel outcomes) before the parameter walkthrough. Dense but mostly earns its length; the second sentence lightly restates the first, and the ALL-CAPS emphasis is stylistically heavy, costing a point.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 5-param read tool with no output schema, the description covers return shape (compact list, per-channel rows, truncated captions), default limits, filtering options, and the full-detail mode, while annotations carry the safety profile. Nothing needed 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.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100% and the schema descriptions are already rich, so the baseline is 3. The description adds value beyond the schema by explaining the workflow meaning of `id` (returns the post in full, required before reschedule_post) and framing `channel`/`upcoming`/`fired` as filters/expanders rather than raw numbers.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb+resource ('list' scheduled and past posts) and immediately clarifies scope: queued items plus fired items, per-channel outcomes, one row per channel. It distinguishes itself from list_jobs explicitly ('they are not job runs'), so an agent can separate the two 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.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Opens with an explicit trigger ('Use this when the user asks what is scheduled, what already posted, or whether a post went out') and names the alternative for job runs (list_jobs). It also ties the `id` parameter to a downstream workflow ('which you need before reschedule_post replaces a caption map'), giving concrete when-to-use context.
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 SheetARead-onlyIdempotentInspect
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.
| Name | Required | Description | Default |
|---|---|---|---|
| sheetUrl | No | a Google Sheets URL — the id is extracted from it | |
| spreadsheetId | No | the spreadsheet id (from create_sheet, or list_drive_files for one the user picked) |
TDQS
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.
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.
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.
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.
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.
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 skillsARead-onlyIdempotentInspect
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.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
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.
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.
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.
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.
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.
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 swipefileARead-onlyIdempotentInspect
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.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | max ads to return (default 50, max 500) | |
| collection | No | only list ads in this collection (by name or id) — omit for every collection |
TDQS
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.
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.
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.
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.
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.
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 membersARead-onlyIdempotentInspect
List the members of the current profile — email, role (admin/member) and status. Read-only, free.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
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.
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.
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.
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.
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.
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 idsARead-onlyIdempotentInspect
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.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | how many recent updates to scan, 1–100 (default 100) | |
| account | No | which connected account: its @handle or id from list_connector_accounts; needed only when several are connected |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Adds substantial behavior beyond the annotations: the list covers only chats that sent an update in the last 24 hours, is empty when an outgoing webhook is configured, and does not consume pending updates by confirming an offset. It also notes the zero-credit cost. This is unusually rich disclosure for a read-only tool.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is front-loaded and each sentence addresses a real failure mode, especially the 24-hour window and webhook caveat. It is somewhat emphatic and repetitive in places, but not wasteful enough to drop below 4.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given no output schema, the description explains the critical edge cases that produce an empty or misleading result and tells the agent not to treat absence as definitive. It could say more about the exact shape of returned chat entries, but the operational completeness is strong.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, and both parameters (limit, account) are fully described in the schema. The description adds no parameter-specific syntax or meaning beyond that baseline, so a 3 is appropriate.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource: find the chat ids this Telegram bot can be addressed by. It explicitly corrects the likely misconception that this lists all chats the bot belongs to, but it does not directly contrast with sibling tools such as post_to_telegram.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives clear operational guidance: absence from this list does not mean the bot cannot post, and posting should be done by @username or numeric id anyway. It also explains the webhook case that makes the list empty, but it does not explicitly state when to call this versus post_to_telegram.
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 market radarARead-onlyIdempotentInspect
Read what a brand's MARKET RADAR (set_competitor_watch) has found: each watched brand's new ads, the winners (ads past 30 days running, with a ready brief to make your own version), the trending formats and top posts for its topics, and the ideas to make, plus its state (lastRun, nextRunAt, lastRunOk, paused) and the quote for its next check. Findings marked seed:true are what a brand was already running at its first check, not new launches. Read-only and free; it never runs a check.
| Name | Required | Description | Default |
|---|---|---|---|
| brand | No | profile id/name from list_brands, this call only | |
| limit | No | max ads (default 25, max 75) | |
| competitor | No | only this watched brand's ads (exact name as returned) |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true, idempotentHint=true, and destructiveHint=false, but the description adds valuable behavior: the call is free, it never runs a check, and `seed:true` findings represent pre-existing ads rather than new launches. It also names the state fields returned, going beyond annotation coverage.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loads the core action ('Read what...has found') and then uses the enumeration efficiently; every clause adds substantive return-value or behavioral information. No filler or redundancy.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
No output schema exists, but the description comprehensively explains the return shape (ads, winners, trends, posts, ideas, state, quote) and clarifies seed semantics. Combined with full schema coverage, an agent has everything needed to invoke and interpret the tool.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100% and all three parameters are documented in the schema, so the baseline is 3. The description does not add syntax or filtering nuance beyond what the schema already provides for brand, limit, and competitor.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Uses a specific verb ('Read') and names the resource ('MARKET RADAR' findings produced by set_competitor_watch), then enumerates exactly what is returned: new ads, winners, trending formats, top posts, ideas, state, and next-check quote. An agent can immediately distinguish this from the setup sibling set_competitor_watch.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives clear context for when to call it and an implicit when-not ('it never runs a check'), which steers agents away from using it to refresh data. It does not explicitly route to alternatives such as competitor_teardown or pull_competitor_ads, so it falls short of a full 5.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_whatsapp_accountsWhatsApp Business accounts and numbersARead-onlyIdempotentInspect
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.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
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.
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.
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.
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.
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.
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.
| Name | Required | Description | Default |
|---|---|---|---|
| image | Yes | the finished static ad: URL, Library item URL, upload_file URL or local path | |
| fixLabel | No | 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) | |
| languages | Yes | target languages, by name |
TDQS
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.
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.
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.
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.
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.
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_effectRun a one-click effect on a photoAInspect
ONE-CLICK EFFECTS, the same ones as the /tools pages: a photo goes in, a styled picture comes out, and most can then become a 5-second clip. Plush toy, 3D figure, mascot, comic book, pixel game, billboard ad, giant product, bullet time, packshot, outfit swap, headshot, relight and many more. Name no effect for the free list. effect alone: what it takes. effect + images: a free quote; add confirm:true to make the picture. Then effect + still (that picture's URL): a free quote for the clip; confirm:true makes it. describe adds anything of your own. For a look not on the list use generate_image. A real person's photo follows the same likeness rules as every render.
| Name | Required | Description | Default |
|---|---|---|---|
| text | No | a line printed exactly, on an effect that takes one | |
| still | No | CLIP STEP: the finished picture to animate | |
| effect | No | effect id from the list | |
| images | No | its pictures, in the order listed: URL, local path or Library id | |
| speech | No | CLIP STEP: a line said in the clip, on an effect that speaks | |
| confirm | No | true = run it (real spend); without it, a free quote | |
| options | No | its listed options, e.g. {material:"knit"}; "custom" means use `describe` | |
| describe | No | your own words: added to the look, or the custom choice | |
| aspectRatio | No | one of its listed shapes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only say readOnly=false, destructive=false, openWorld=true, idempotent=false; the description adds the critical behavior the agent cannot infer — that calls without confirm are a free quote and confirm:true triggers real spend, plus a second paid step for the 5-second clip. It also flags the likeness policy for real people's photos. It stops short of describing whether the render is synchronous or a polled job (relevant given get_job/resume_job siblings), so not 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.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with the purpose and effect catalog, then a compact sequence of call patterns; nearly every clause carries routing or gating information. The telegraphic style ('`effect` alone: what it takes') is efficient but slightly opaque on first read, keeping it off a 5.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 9-parameter, nested-object, multi-step tool with no output schema, the description covers the full workflow including both paid stages and the free-quote gate. The main gap is that it never says what a 'quote' returns or how to retrieve the finished asset after confirming, which matters for a generate-and-wait tool.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so a 3 is the floor; the description earns above it by explaining how parameters combine into distinct operations (effect+images vs effect+still) and what `describe` and `confirm` mean in the flow. It still leaves `options`/`text`/`speech`/`aspectRatio` largely 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.
Does the description clearly state what the tool does and how it differs from similar tools?
States a concrete verb and resource ('a photo goes in, a styled picture comes out') and enumerates actual effects (plush toy, 3D figure, bullet time, outfit swap, relight) so the agent knows the domain instantly. It also names the sibling it is not ('For a look not on the list use generate_image'), separating it from generate_image/edit_image 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.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives an explicit call matrix: no `effect` = free list, `effect` alone = requirements, `effect` + `images` = free quote, add confirm:true = make the picture, `effect` + `still` = clip quote, confirm:true = make the clip. It also routes out-of-list looks to generate_image, so both when-to-use and when-not-to-use are covered.
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 subtitles timed to the narration (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.
| Name | Required | Description | Default |
|---|---|---|---|
| lane | No | '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. | |
| music | No | 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. | |
| style | No | 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. | |
| topic | No | what the explainer should teach or explain — a topic or a short brief (host_episode: or pass `script`) | |
| voice | No | narration voice name — omit for the default warm read | |
| dryRun | No | true = return the estimated 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. | |
| format | No | 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, runs exactly the length asked: a last part-block is rendered whole and cut). Omit and a history / kids / fairytale channel is a faceless channel video. 'host_episode' = the AI host episode (see the top). | |
| script | No | host_episode: the exact words, said verbatim and split at natural breaks into 4-30s pieces | |
| cameras | No | host_episode: the rotation, ids frontal / three_quarter / close or framings in words; one entry = one fixed camera | |
| channel | No | the 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'. | |
| creator | No | host_episode: the host, a saved creator or a preset by name or id (list_creators) | |
| endCard | No | append the branded end card. DEFAULT FALSE — set true ONLY when the user asks for one | |
| setting | No | host_episode: the set in words (default: written to fit the topic) | |
| upscale | No | optional 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. | |
| captions | No | turn 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 text (CAPS in the stills lane), thin black outline, bottom safe band, no plate, no box. | |
| fromFilm | No | A PRESENTER ON A FINISHED FILM (only when the user asks for one): the job id of a finished video-blocks explainer or faceless-channel film. With `presenter`, that film comes back with a presenter speaking each block's line on screen; every block keeps its own picture and the film without the presenter is kept (cleanVideo). Never add a presenter unasked. | |
| brandName | No | brand name for the end card — omit to leave it unbranded | |
| fromDraft | No | host_episode: a finished draft's job id, to render it in HD (720p) with the same script, cameras, set and host | |
| hostImage | No | host_episode: a photo URL of the host instead (upload_file for a local file); a real person's face needs a paid plan | |
| presenter | No | with fromFilm: who presents and how. A face the video model flags stops it, nothing charged, with the choices to offer. | |
| subtitles | No | which on-screen text, once `captions` is on. LEAVE IT UNSET (or true) for SUBTITLES — every spoken word, in order, timed to the narration; no extra render, and there is NO cue limit, so the whole film is subtitled however long it runs (at most 4 words and one line a cue; 5 words / 32 characters in stills). 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: in the default blocks lane each cue is timed to its own spoken words (one listen to the narration, a tiny charge inside the quote; per-block timing, said, if it fails). In the stills lane cues are spread over each section’s measured narration by character count, exact at section edges, a few tenths off within one: it is not a word-level speech clock, so never promise frame-accurate sync there. | |
| thumbnail | No | host_episode: one thumbnail of the host (default true) | |
| aspectRatio | No | '9:16' default (a faceless channel video and a host episode default to 16:9) | |
| frameDensity | No | 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. | |
| durationSeconds | No | target length 20-120s (default 60); drives the section count — ~10s of narration each, 3-8 sections |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only say non-read-only, open-world, non-idempotent, so the description carries most of the load and does so well: it discloses that host_episode returns a 480p draft with HD as a separate fromDraft call, that a stills film is image rendering rather than video-model renders, that production takes several minutes with concurrent frame painting, and that it needs a writing model plus a voice engine connected. Cost behavior is unusually well documented (hold amounts, frameDensity savings, upscale priced by length and several times the render cost), though it never says what a failure or partial render leaves behind.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Every claim is substantive, but the ordering is poor: the definition opens with the narrow host_episode special case in all-caps before ever stating the primary topic-to-explainer job, and several constraints (defaults off, ask before spending) are restated in three or four places. A reader must reconstruct the main path from a wall of capitalized caveats.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 25-parameter tool with a nested presenter object and no output schema, the description supplies the missing pieces: what gets produced at what resolution, draft-versus-HD sequencing, cost and duration expectations, prerequisites, and which defaults must not be changed silently. Nothing an agent needs in order to call it correctly — or to explain the spend to a user — appears to be absent.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3, and much of the prose mirrors the schema field text (frameDensity, captions, subtitles). What it adds beyond the schema is cross-parameter meaning: subtitles:true implies captions:true, a named style overrides the channel's default look, endCard defaults false and should stay false, upsale/captions interact ('burned BEFORE it so they upscale with the frame'), and dryRun should precede a paid run. Those relationships are not derivable from the individual property descriptions.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a concrete verb and resource ('turn a TOPIC into a finished narrated explainer video') and then enumerates the two output shapes it can take ('blocks' vs 'stills') plus the host_episode special case. It explicitly distances itself from the nearest siblings (render_ad, generate_video, make_template_ad), so an agent can tell what this produces 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.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives explicit routing rules: '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.' It also states which lane is the default, when a channel is treated as faceless, when to quote with dryRun before running, and that upscale/endCard/fromFilm must never be enabled unasked.
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 clipBInspect
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.
| Name | Required | Description | Default |
|---|---|---|---|
| image | No | still picture URL; use this or video | |
| sound | Yes | sound URL (find_sound returns one), or a video whose own audio is used | |
| video | No | video URL to take the picture from, starting at videoStart | |
| seconds | No | fixed insert length, 0.2 to 6 seconds, instead of when the sound lands | |
| soundEnd | No | seconds into the sound where it ends | |
| soundStart | No | seconds into the sound where the insert's sound starts | |
| videoStart | No | seconds into the video where the insert starts |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already establish that this is a non-readonly, open-world, non-idempotent operation. The description adds useful behavioral context about the output being 1080x1920 and how timing is determined by the sound or a fixed seconds value, but it does not disclose whether the call returns a clip immediately, a job ID, credit cost, or other generation behavior.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The definition is a single dense sentence with the output artifact front-loaded and no obvious filler. The phrasing around sound timing is slightly cryptic, but it remains compact and usable.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The description covers the core output and timing logic, which is helpful for a generation tool with no output schema. It still leaves important operational details unspecified, such as return shape, async job behavior, and whether image or video is required in practice despite only sound being required in the schema.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so all seven parameters are already documented in the schema. The description reinforces the image/video source and sound-timing relationship, but it largely restates schema content and does not add much beyond what the individual parameter descriptions already say.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific output artifact: a 1080x1920 reaction clip made from an image or video plus sound, cut to the sound landing or a fixed duration. It distinguishes the tool from generic video generators through the reaction-picture and sound-timed cut framing, but it does not explicitly contrast it with close siblings such as insert_scene or clip_video.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The phrase 'for post_edit join' implies this tool is used as part of a post_edit workflow, which gives some usage context. However, there is no explicit guidance on when to choose this over alternatives like insert_scene, or on prerequisites such as calling find_sound first.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
make_motion_filmMake a motion graphics videoAInspect
MOTION GRAPHICS and ANIMATED VIDEO in one call: kinetic typography (animated text), a logo animation or intro, a product launch film, an animated social cut. MOVING PICTURES are the default for launch, product and social: in each scene with a picture, one thing physically happens (a seed splits open, an eye opens, the view travels through a forest), filmed by an image-to-video model, with the type, transitions and the music's beat put on top; a few minutes, priced per shot, and with no pictures given each scene paints its own. moving:false, and always for type and logo, makes it in code with no video model: type, pictures and drawn graphics filmed by a moving virtual camera, a 1080p MP4 in about a minute, billed for its compute only. Pick it for 'animated video', 'motion graphics', 'kinetic text', 'text animation', 'logo animation', 'logo intro', 'animated explainer' asks. recipe: 'type' (every line its own animated card, no picture needed), 'logo' (the logo assembles out of dots and settles, the name and tagline land under it), 'launch' (30s 16:9), 'product' (15s), 'social' (15s 9:16). Or spec, the whole film as JSON (help:true returns the language). STEPS: without confirm it is a FREE quote that gives both prices (length, the music's tempo; the spec in structuredContent): say both; preview:true is a 1-credit contact sheet of the still version; confirm:true renders. You may write moves yourself, or the film's own planner writes them for a few credits. Only the user's or the brand's words appear: never invent a claim, number or review. projectUrl = a past film's url returns its saved spec to edit and render again.
| Name | Required | Description | Default |
|---|---|---|---|
| help | No | true returns the spec language and recipes; renders nothing | |
| hook | No | opening line; '*word*' = italic accent | |
| logo | No | logo image: https, upload_file URL or Library id; default the brand's | |
| mood | No | music and pace | |
| name | No | default: the saved brand's | |
| size | No | frame shape: 16:9, 9:16, 1:1 or 4:5 | |
| spec | No | the whole film as JSON, from a quote or a saved film; help:true shows the spec language | |
| brief | No | what the film is about, for the planner | |
| lines | No | more short lines, in order | |
| model | No | image-to-video model id from hermoso_capabilities; default is the best value for short shots | |
| moves | No | per scene, what happens in its picture; omit and the planner writes them | |
| music | No | 'off' = silent | |
| theme | No | logo: dark for a light logo | |
| energy | No | how much the film keeps moving, 0 to 1; 0 films exactly what is written | |
| images | No | pictures, hero first (https, upload_file URL or Library id) | |
| moving | No | true: one thing physically happens in each picture, filmed by a video model. false: stills moved by a virtual camera, far cheaper. Default true for launch, product, social | |
| recipe | No | type, logo, launch, product or social; help:true lists the recipes | |
| confirm | No | true renders the film for real credits; without it you get a free quote | |
| preview | No | true renders a 1-credit contact sheet to approve before the full render | |
| seconds | No | logo 4-10, others 6-60 | |
| tagline | No | line shown under the headline or logo | |
| projectUrl | No | a past film's url; returns its saved spec to edit and render again | |
| resolution | No | the moving shots: 720p or 1080p (default) where the model has them |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Goes well beyond the supplied annotations by disclosing the cost model: no confirm yields a FREE quote with both prices, preview:true is a 1-credit contact sheet, confirm:true spends real credits, moving shots are priced per film-second while code-drawn films are billed for compute only, and the planner costs a few extra credits. It also states the runtime/format expectations (a few minutes vs. a 1080p MP4 in about a minute) and a content safety constraint (never invent a claim, number or review).
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The critical content is front-loaded, but the text is a dense run-on block with erratic capitalization and parenthetical nesting that makes the pricing and confirmation rules easy to miss. Several clauses restate schema-level facts (line/logo/mood meanings) instead of concentrating on workflow.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 23-parameter, nested-object tool with no output schema, the description covers the essential lifecycle: quote, preview, render, and reuse via projectUrl. It does not say whether the render is asynchronous or returns a job id that must be polled with get_job/resume_job, which is a real gap given the sibling set.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
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 nevertheless adds meaning the schema lacks, notably the recipe expansions ('launch' = 30s 16:9, 'product'/'social' = 15s, 'social' = 9:16) and the confirm/preview/projectUrl workflow semantics. The moves-vs-planner tradeoff and 'moves' optionality are also clarified.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Opens with a specific verb and resource ("MOTION GRAPHICS and ANIMATED VIDEO in one call") and then enumerates concrete deliverables: kinetic typography, logo animation/intro, product launch film, social cut. The two production paths (image-to-video 'moving' shots vs. code-drawn virtual camera) are distinguished explicitly, so an agent can tell this apart from generic video siblings.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives trigger phrases ('animated video', 'motion graphics', 'kinetic text', 'logo intro', 'animated explainer') and an explicit exclusion rule: moving:false, and always for type and logo, avoids the video model. It does not name sibling tools such as generate_video or make_explainer, so the agent must infer boundaries from the keyword list rather than a direct routing statement.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
make_seriesMake a series of faceless short videos (Content Factory)AInspect
CONTENT FACTORY: turn ONE topic into a SERIES of faceless short videos (narration over pictures, no presenter), optionally in the STYLE of a trending reference video. Give topic ("make a video about…"), episodes (1-10, default 1) or your own episodeTitles, durationSeconds (20-120 per episode, default 30) and optionally reference (a TikTok / Reel / YouTube Short / Facebook / X link or a video file URL whose look, pacing and opening hook every episode copies, with plain subtitles only if the reference burns its own; never its words or characters). Find a trending reference with search_tiktok / search_instagram / search_youtube when the user wants one. THREE STEPS, ALWAYS IN THIS ORDER: (1) call with no confirm for a FREE quote of the whole series (per episode and total); (2) optionally planOnly:true to see the episode titles and the style card first (a small planning charge); (3) confirm:true (pass the plan from step 2 to reuse it) queues every episode and returns their job ids; poll get_job. To make MORE of a series later, pass its returned plan as moreOf: same style, no episode repeated, the reference not watched again. Each finished episode can be scheduled with this tool again: pass jobIds plus schedule {channels, at, everyHours, timezone} and it books one post per finished episode, spaced everyHours apart (default 24). Captions follow the reference (on if it burns words on screen) unless captions says otherwise.
| Name | Required | Description | Default |
|---|---|---|---|
| plan | No | the `plan` a planOnly call returned, to run it without planning again | |
| style | No | override the look in words (e.g. "flat 2D cartoon") instead of the reference's | |
| topic | No | what the series is about, in the user's words | |
| voice | No | narrator: 'female' or 'male' (default follows the reference), or any narration voice name make_explainer takes | |
| format | No | renderer lane: 'explainer' (default; exact length) or 'faceless_channel' (2K, whole 10s blocks) | |
| jobIds | No | SCHEDULE MODE: the episode job ids to schedule (finished ones are booked) | |
| moreOf | No | MORE EPISODES: the `plan` an earlier call returned. Keeps its style and length, writes new episodes only | |
| confirm | No | REQUIRED true to start rendering (real spend). Without it you get a free quote | |
| captions | No | burn subtitles of every spoken word. Default: on only when the reference itself burns its spoken words as subtitles, otherwise off; nothing else is ever put on screen (no app screens, URLs, cards or stickers) unless the user asks | |
| episodes | No | how many episodes, 1-10 (default 1) | |
| planOnly | No | write the episode plan and style card, render nothing (a small planning charge) | |
| schedule | No | SCHEDULE MODE: where and when the first episode posts; the rest follow every `everyHours` (default 24) | |
| reference | No | a video link whose style every episode copies | |
| aspectRatio | No | default: the reference's shape, else 9:16 | |
| postCaptions | No | SCHEDULE MODE: one post caption per episode, in order (default: the episode title) | |
| episodeTitles | No | your own episode titles, in order (sets the episode count) | |
| durationSeconds | No | length of EACH episode, 20-120 seconds (default 30) |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Beyond the annotations (readOnlyHint=false, destructiveHint=false, idempotentHint=false), the description discloses the cost model: no `confirm` yields a free quote, `confirm:true` means real spend, `planOnly` incurs a small planning charge. It also explains what happens on confirm (every episode queued, job ids returned, poll get_job) and that a reference is not re-watched on `moreOf`, which is exactly the kind of behavior an agent cannot 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.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with a clear 'CONTENT FACTORY:' framing and then explicitly enumerated steps, so the reader hits the workflow early. It is dense and long for a single description, and the reference-styling point is stated twice (once in the summary clause, once in the `reference` explanation), but almost every sentence carries operational information.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 17-parameter tool with a nested `schedule` object, multiple modes (quote, plan, render, schedule, extend) and no output schema, the description covers the mode-selection logic, cost implications, return values (plan, job ids) and next-step tooling. Nothing an agent needs in order to invoke 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.
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: `reference` is limited to look/pacing/hook and never its words or characters, `captions` defaults to on only when the reference burns its own subtitles, `moreOf` never repeats an episode, and scheduling books one post per finished episode spaced by `everyHours`. It leaves a few parameters (aspectRatio, format, voice) to the schema alone.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Names a specific verb and resource — turning one topic into a series of faceless short videos — and immediately scopes it (narration over pictures, no presenter). It also distinguishes itself from adjacent tools by referencing make_explainer for voice names and search_tiktok/search_instagram/search_youtube for finding references, 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.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives an explicit, ordered three-step workflow (free quote → optional planOnly → confirm:true), plus separate guidance for extending a series with `moreOf` and for scheduling finished episodes via `jobIds` + `schedule`. It also states when to reach for sibling search tools and when the plan from a prior call should be reused.
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
Use this when the user asks for a quick, low-cost ad or post built from their words and pictures: a slideshow, carousel, iMessage or ChatGPT chat, Notes, kinetic type, myth vs fact, an app tour, or their own HTML design. Rendered from HTML, no AI model, ~30s, billed for its compute only, ready to post. 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 public https URL; notes[] lists what failed to load. YOU write preset copy: short, casual, believable, finished phrases within budget. Preset ids and their fields are listed on config. config.music on a VIDEO: omit for a free library bed matched to its mood when the library is stocked (hermoso_capabilities hasMusic; else only its own sound effects), 'off' for silence, or words (a mood or description) to compose one for a flat fee.
| Name | Required | Description | Default |
|---|---|---|---|
| config | Yes | MUST include config.template: 'custom' or a preset id, plus its fields; language ('Portuguese') for its own words. 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, which takes config.music like any video) }; 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? } is OPTIONAL on every video preset: include it ONLY when the user asked for an end card (a closing card with their logo); without it the video ends on its own last frame. palette and fontStack optional. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Beyond the annotations it discloses cost structure (2 credits base, +1 past 5 slides, +2 for MP4, voiceover surcharge), rendering mechanics (HTML frame-stepped, scripts stripped, no AI model, ~30s), asset loading rules (public https URLs for images and fonts, notes[] reports failures), and music behavior on video. That is exactly the kind of operational detail 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.
Is the description appropriately sized, front-loaded, and free of redundancy?
Intent is front-loaded in the first sentence, but the remainder is a single dense run-on block of parentheticals, abbreviations and credit math that is hard to scan. Much of the preset enumeration sits uneasily next to the same information in the config schema, so several clauses do not earn their place in the description itself.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a complex tool with one deeply nested config object and no output schema, the description covers presets, custom designs, pricing, timing and asset constraints thoroughly. What it omits is the return shape and job lifecycle (whether this is async and needs get_job/resume_job), which matters given no output schema exists.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
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 semantics the schema does not: brand tokens ({{logo}}, {{brandName}}, {{domain}}, {{accent}}), the notes[] failure channel, and the custom size/duration/slides typing rules. It also usefully defers preset field lists to `config` rather than duplicating all of them.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The opening sentence gives a concrete trigger ('the user asks for a quick, low-cost ad or post built from their words and pictures') and enumerates the output families (slideshow, carousel, iMessage chat, kinetic type, custom HTML), so an agent knows exactly what it produces. It never names a sibling (render_ad, generate_video, plan_ad, make_motion_film), so the boundary with adjacent rendering tools 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.
Does the description explain when to use this tool, when not to, or what alternatives exist?
There is clear when-to-use framing ('quick, low-cost', 'no AI model, ~30s, billed for its compute only') plus internal routing between presets ('Presets are SHORTCUTS; custom is YOUR OWN design'). There is no when-not guidance and no explicit statement of when to pick a sibling like render_ad or generate_video instead.
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:
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.TEXT — default is a CLEAN render with the headline TYPESET over it (free, legible, correctly spelled): pass
headline.bakeText:trueonly on an explicit ask for words painted INTO the image. Never infer text intent from the topic.HOW MANY — ask once: one, or a SET (offer 4: one concept at different emotions / camera takes). Default 1;
variantscaps 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.
| Name | Required | Description | Default |
|---|---|---|---|
| font | No | headline font: Anton (default) or any Google Fonts family | |
| logo | No | a brand logo URL or path to place into the composition | |
| split | No | split/panel LAYOUT — only when the user asks for one ("split", "before/after", "versus screen"). "X vs Y" as a SCENE stays one unified frame | |
| takes | No | camera takes per emotion, 1–4: designed framing / low-angle hero / extreme close-up / wide dutch tilt | |
| topic | No | the video's topic — used to pick the hero object when you don't name keyElements | |
| tweak | No | 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 | |
| logo3d | No | first turn the flat logo into a volumetric 3D render (one extra billed image), then composite that | |
| people | No | people described in prose instead of by photo (each still gets the chosen expression) | |
| emotion | No | the expression on the face (default 'shock') — shock · hype · fear · confusion · determination · smug · charisma · disgust · awe · rage · laugh, or your own phrase | |
| bakeText | No | default false. true paints the headline INTO the generation — only on an explicit user ask; it leaks garbled text elsewhere in the frame | |
| emotions | No | render one variant per emotion (variants = emotions × takes, max 16) | |
| headline | No | 2–4 word headline. Typeset OVER the finished render by default (free, always legible); newlines split it into stacked lines | |
| location | No | place, time of day, weather, atmosphere | |
| rimColor | No | colored back+hair light — ONLY when the user names one: 'ice-blue' / 'neon-magenta' / 'toxic-lime' / 'amber-gold' / 'pure-white' | |
| variants | No | how many thumbnails to render (default 1, max 16). Each is its own billed render — offer a set of 4 rather than assuming | |
| framework | No | 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 | |
| reference | No | fields 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 | |
| background | No | override the default bold saturated colour-field background | |
| faceImages | No | up to 3 face photos (URLs or local paths) — each becomes a locked CHARACTER identity, in order | |
| sceneBrief | No | what the thumbnail depicts — the concept in one dense sentence, rendered exactly | |
| aspectRatio | No | '16:9' (YouTube, default) / '9:16' (Shorts) / '4:5' (Instagram) / '4:3' / '1:1' | |
| bakedUiText | No | short label for a text-carrying framework (a chat bubble, a DAY N badge, a news lower-third, a map callout) — needs frameworkRequested:true | |
| composition | No | override the default large-foreground-subject composition | |
| keyElements | No | signature props / effects that make it pop — oversized, flying toward camera | |
| sourceImage | No | the finished thumbnail URL a `tweak` edits; tweaks chain, so feed each accepted output into the next | |
| overlayStyle | No | headline style: beast (default), fire, neon-lime, clean-glass, marker, or your own CSS declarations | |
| forceGenerate | No | render the 'screenshot' framework anyway (it is normally a real video frame, not a generation) | |
| headlineLines | No | explicit headline lines (up to 3) — overrides splitting `headline` on newlines | |
| headlinePlace | No | bottom (default), top, center, or a 0-1 fraction from the top; never over the face | |
| logoPlacement | No | overlay (exact, flat) | in_scene (the real file printed onto a blank panel in the shot; with logo3d, a 3D render of it, checked); auto: in_scene with logo3d | |
| restrainedGrade | No | true for a calm / premium / muted look instead of the default punchy poster grade | |
| castGenericPerson | No | pass true only after the user has explicitly chosen a generated stranger over their own face | |
| frameworkRequested | No | true 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
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare the billing/destructive profile, but the description adds substantially beyond them: ~9 credits per variant with a free headline overlay, silent refusal with no charge when a person framework lacks a face photo, the garbled-text leak risk of bakeText, chained tweaks via tweak + sourceImage, and the instruction to check postRenderCheck before presenting. The gaps are minor — no stated latency or failure/retry behavior for a render that returns nothing useful.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with purpose, the generate_image alternative, and cost before any procedural detail; the gate and prompt-language sections are clearly headed and scannable. It is long and occasionally shouty (frequent ALL-CAPS emphasis, repeated 'ask once' framing), but for a 33-parameter tool almost every sentence carries a decision rule.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a complex, costly, multi-parameter generation tool with no output schema, the description covers what an agent needs: cost per variant, defaults, refusal behavior, edit path for finished images, and an explicit post-render verification step. Nothing required 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.
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 earns above that by supplying cross-parameter rules absent from the schema: variants = emotions × takes (max 16), tweak requires sourceImage and chains, and the English-versus-verbatim rule for descriptive fields versus headline/bakedUiText. It still leaves several of the 33 parameters (font, logo, logoPlacement, overlayStyle, restrainedGrade, takes) to the schema alone, so it stops short of a 5.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('Render a click-driving YOUTUBE / Shorts / Instagram THUMBNAIL or video cover through the full production pipeline') and explicitly distinguishes itself from a named sibling: 'Use it for any thumbnail... ask INSTEAD of generate_image', with 'not a bare image prompt' clarifying the boundary. An agent can route between this and generate_image 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.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicit when-to-use (thumbnail/video cover/MrBeast-style packaging instead of generate_image), when-not (bakeText only on explicit ask; split only when the user asks for a layout; rimColor only when the user names one), and what to do when blocked (the three gates: ask once about who is in frame, text mode, and variant count). Alternatives and defaults are all stated rather than inferred.
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 tabADestructiveInspect
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.
| Name | Required | Description | Default |
|---|---|---|---|
| tab | No | which tab — its title or numeric sheetId (rename / delete) | |
| title | No | the name for the new tab (action:"add") | |
| action | Yes | add, rename or delete a tab | |
| confirm | No | true deletes a tab that holds data; needs confirmCells | |
| newTitle | No | what to rename the tab to (action:"rename") | |
| sheetUrl | No | Google Sheets link, instead of spreadsheetId | |
| confirmCells | No | the filled-cell count an unconfirmed delete reported | |
| spreadsheetId | No | Google spreadsheet id; use this or sheetUrl |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare destructiveHint=true, but the description goes well beyond: it says exactly what is destroyed ('DELETING a tab destroys everything on it'), mandates the unconfirmed-call-then-confirm:true+confirmCells handshake with the filled-cell count as the token, and discloses that Google refuses deleting the last tab and that refusal is surfaced by name with a way out. The read-back verification promise closes the loop on whether the operation actually took effect.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with the action verbs and their parameter maps, then escalates to the destructive path and the last-tab edge case. The compact mid-dot notation ('action:"add" + title · ...') is efficient, though the single dense block is slightly harder to scan than separated lines.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With 8 parameters, no output schema and a destructive annotation, the description carries the full burden and does so: it covers input mapping, the confirmation gate, the platform-level failure mode, refusal messaging, and post-action verification. Nothing material an agent needs to call this safely is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
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: which parameters pair with which action, that 'tab' accepts either a title or a numeric sheetId, and that confirmCells must be the count a prior unconfirmed delete reported. Only mild remaining gap is that it never mentions sheetUrl/spreadsheetId alternates in prose.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Opens with a specific verb set and resource: 'Add, rename or delete a tab in a Google Spreadsheet.' It then enumerates the three actions so the agent knows exactly which operations are in scope, and the sibling list_sheet_tabs is explicitly named later as the discovery counterpart.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives an explicit parameter recipe per action (action:"add" + title, action:"rename" + tab + newTitle, action:"delete" + tab), states the two-step protocol for destructive deletes, and names concrete alternatives/escape hatches (clear it, or delete_drive_file). No inference is required to pick the right path.
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 credits on the reads and the model's token cost.
| Name | Required | Description | Default |
|---|---|---|---|
| brandId | No | a profile id/name from list_brands to mine for; omit to use the active profile | |
| reviews | No | 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) | |
| reviewsUrl | No | 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. | |
| useOwnReviewsOnly | No | true = mine only your reviews, no public search (no search credits). Default false = merge with public customer language. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=false, openWorldHint=true, idempotentHint=false, destructiveHint=false, so the mutation/external-read profile is known. The description then adds genuinely useful context the annotations cannot: credit spend on reads and model tokens, hard input limits (300 reviews / 2,000 chars / 40,000 total) that are refused at no cost, and the side effect that brandId silently switches the key's active profile like use_brand. That side-effect disclosure is the standout — it is the kind of destructive-adjacent behavior an agent must know. It stops short of describing retry/failure behavior beyond the size refusal or how long a run takes, so 3 rather than 4.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded correctly — the verb, data sources, and return shape come first, with alternatives and limits after. Every section carries information, but the dense ALL-CAPS emphasis and repeated format enumerations ('one per line, numbered, blank-line separated...') make it longer than it needs to be; it reads as one packed paragraph rather than structured segments.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
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 names the angle tags, quote count, score breakdown, hook field, and the `next` object containing exact plan_variations/generate_image arguments. Combined with credit, limit, and profile-switching disclosure, an agent has everything needed to call it correctly and chain it forward.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3, and the description still adds meaning: accepted review-block formats beyond the enum-less schema text, that reviewsUrl can be a plain file path on a local CLI, and that useOwnReviewsOnly carries a real cost difference (no search credits). It duplicates some schema wording (upload_file provenance, CSV review column) rather than adding to it, which keeps it at 4.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb+resource ('Mine ad ANGLES from real customer language') and then details exactly what the output contains: ranked angle bank, taxonomy tags, 2-5 verbatim quotes, 0-100 score with breakdown, ready-to-run hook. It is unambiguously distinct from sibling competitor_teardown and from search_* tools that only gather raw data.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly routes the agent away from this tool ('To tear down a COMPETITOR use competitor_teardown instead') and gives condition-based guidance for its own variants: brandId to target another profile, useOwnReviewsOnly:true to skip public search, and the review-volume limits that force batching. The when-to-use and when-not-to-use cases are both covered.
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: quoteOnly:true returns the exact credits free: state that first. A paid planning read (planCredits) runs next, then each variant bills perVariantCredits. dryRun:true pays for that read; pass its plan back to render without paying again. Your own pictures: images takes up to 9 of the user's product, people, clothes or places, and change names them @Image1, @Image2 in order ('she holds the bottle in @Image1'); the saved brand's product photo is read as a reference to its real look (useBrand:false leaves it out). A TikTok, Reel, Facebook, X or YouTube post link works as the source: one paid lookup reads it into a file. quality: 1080p (default) or 720p; quoteOnly prices both. 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.
| Name | Required | Description | Default |
|---|---|---|---|
| axes | No | what to vary: character, outfit, location, objects (default all four), or your own, e.g. "season" | |
| plan | No | the `plan` a dryRun returned, rendered without planning again | |
| count | No | how many variants, 1-12 (default 6) | |
| notes | No | anything the variants must respect, e.g. "keep it women 25-40", "no gyms" | |
| video | Yes | the source video: a file URL, or a TikTok, Reel, Facebook, X or YouTube post link | |
| change | No | 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) | |
| dryRun | No | true = the paid plan and the price, no render | |
| images | No | up to 9 pictures to put in the variants, in the order @Image1, @Image2 name them: URLs or local paths | |
| quality | No | 1080p is the default; 720p costs less, and hands holding a product are less reliable at 720p | |
| regions | No | markets to restyle for, one or more variants each, e.g. ["Berlin","Tokyo","São Paulo"] — visuals only; audio is never translated here | |
| useBrand | No | false = send no brand product photo (for a video that is not this workspace brand) | |
| quoteOnly | No | true = the exact credits, free; nothing runs |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Far exceeds the annotations (which only say non-read-only, open-world, non-idempotent). It discloses the cost mechanics — quoteOnly is free, a paid planning read follows, each variant bills perVariantCredits, dryRun pays for the read and its `plan` can be replayed without paying again — plus the async contract (one job per variant, poll get_job until done), the 30-second refusal with a free error message, and that burned-in captions are stripped while audio is preserved.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The core is front-loaded with the key invariant stated first, which is good. But it is delivered as one unbroken block that duplicates itself — the 'same cut / same audio / same length' promise is made twice in near-identical wording — and crams cost, error handling and parameter notes into the same run-on paragraph, making it harder to scan than its content warrants.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no output schema and 12 parameters, the description carries the return contract itself: it says the response contains a plan and one job per variant, that URLs arrive only when a job reports done, and that no variant should be described before then. Combined with cost tiers, source-length limits and source-link types, an agent has everything needed to call it and interpret the result.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Although schema coverage is 100%, the prose adds meaning the schema does not: `change` cannot touch voice, words, language, music, length or captions and will name the tool that can; `images` are addressable as @Image1/@Image2 in `change` order; `useBrand:false` drops the brand product photo; 720p costs less but degrades hands-holding-product fidelity; `regions` restyles visuals only and never translates audio.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
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 immediately delimits what is invariant (cut, camera motion, pacing, original audio) versus what changes (character, outfit, location, objects). It also names the siblings it is not — clip_video for highlight clipping and dub_video for translation — 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.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives clear when-to-use and when-not conditions: sources up to 30 seconds work as-is, longer ones are refused and should go to post_edit trim first, and cross-language work is a separate dub_video step. It prescribes the ordering of the paid flow (quoteOnly first, then plan, then per-variant billing). It stops short of differentiating against closer siblings like restyle_video or plan_variations, so not a full 5.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
onedrive_file_permissionsWho can open a OneDrive fileADestructiveIdempotentInspect
action list (default): every sharing link and person with access to a OneDrive file or folder: link URL, view or edit, scope, expiry, password, whether it is inherited from a parent folder. action remove: revoke one (permissionId from the list); anyone relying on it loses access at once and the link cannot be restored, so WITHOUT confirm:true nothing is removed and you get what would go. An inherited permission is removed on the parent folder, and the owner's own access cannot be removed. Removal is verified by reading the list back. 0 credits. Needs OneDrive connected.
| Name | Required | Description | Default |
|---|---|---|---|
| action | No | list (default) or remove | |
| fileId | Yes | OneDrive file or folder id | |
| confirm | No | required true for remove | |
| permissionId | No | for remove |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already flag destructiveHint and idempotentHint, but the description goes well beyond them: anyone relying on the link loses access at once, the link cannot be restored, a dry-run result is returned without confirm:true, inherited permissions must be removed on the parent folder, owner access is immune, and removal is verified by re-reading the list. It also discloses cost (0 credits) and the prerequisite that OneDrive be connected.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The default action is front-loaded and each sentence carries new information (return fields, irreversibility, edge cases, cost, prerequisite). The single-paragraph, colon-delimited run-on makes it dense to parse, which keeps it from a 5, but there is no filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no output schema, the description steps in to enumerate the list response fields (link URL, view/edit, scope, expiry, password, inherited) and explains that removal is verified by reading the list back. Combined with the prerequisite and credit cost, an agent has everything needed to call this correctly and interpret the result.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the schema baseline carries most of the load, but the description adds genuine semantics: permissionId must come from the list action, and confirm:true is what actually triggers the deletion versus a preview. It does not explain the fileId format beyond the schema's 'OneDrive file or folder id'.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description names a specific verb and resource for each mode: 'action list (default)' enumerates every sharing link and person with access to a OneDrive file or folder, and 'action remove' revokes a single permission. This is clearly distinguishable from siblings like share_onedrive_file or delete_onedrive_file, which act on the file itself rather than its permissions.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It states that list is the default and remove requires permissionId taken from the list, plus a concrete when-not rule (no confirm:true means nothing is removed). It also flags the two cases where removal will not behave as expected (inherited permissions, owner access). It stops short of naming sibling alternatives such as share_onedrive_file for granting access, so it is a strong 4 rather than a 5.
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
Plan an ad: turns a brand and a product or brief into an ad concept, with copy variants (headline, primary text, call to action) and either an image prompt or a timed video storyboard, plus the recipe and the model ids to render with. It renders nothing; pass its output to render_ad, generate_image or generate_video. When the user names a length ("a 30 second ad", "make it 45s"), pass it as durationSeconds: the storyboard is written to that length and render_ad renders it as one clip or as joined clips. Without it the planner chooses a length that suits the format, so a length the user asked for belongs in durationSeconds rather than only in the brief. The user’s own screenshots or pictures to show inside the video go in inserts. Uses credits for the planning step.
| Name | Required | Description | Default |
|---|---|---|---|
| hook | No | force 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. | |
| brand | No | brand 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 | |
| draft | No | Only after a video refusal that offered a lighter draft: the {model, durationSeconds} that refusal named. The plan is then written to that length and priced on that model. Use only the values the refusal gave; when an account cannot cover a video, the refusal comes before planning with three options (an image, adding credits, or this draft when one fits) and the user chooses. | |
| format | No | 'image', 'video', or 'auto' when unspecified | |
| recipe | No | a recipe id from hermoso_capabilities to force an archetype | |
| talent | No | who is on camera; omit to follow the format. Say 'someone new' in product to skip reusing a saved creator. | |
| inserts | No | VIDEO ONLY: the user's own SCREENS cut in at these seconds (each item's look, kind, start and end say how), the voice running underneath. How many: at most 8, about one per 2.5 s of film (6 s: 2, 10 s: 4, 15 s: 6, 20 s: 8, 30 s: 8), 1.2-2 s each, on the line that talks about it. When there are more, the most crowded are left out first, never the last (the payoff) or one marked key, and the reply names them. The plan keeps those shots free of screens and returns the pictures as creative.inserts for render_ad to lay in (about 2 credits). One that does not fit is left out and the reply says so. | |
| product | Yes | what to advertise + any angle/offer the user specified | |
| setting | No | force 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. | |
| concepts | No | several variants: one line per concept (a different angle each); built exactly | |
| language | No | output language for the ad copy (e.g. Spanish) — default English | |
| reference | No | 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 | |
| durationSeconds | No | For video: the total length the user asked for, in seconds (30 for "a 30 second ad"). The storyboard’s scenes add up to it and the script is sized for it. Supported range 4 to 180 seconds; a value outside the range is clamped and the reply says so. A length that fits one clip of the render model renders as one continuous clip; a longer one is joined from clips filled to that model’s clip maximum, remainder last (on a 15s-clip model, 40 becomes 15+15+10 and 17 becomes 13+4), and is never sped up. The clip maximum depends on the model: 15s on most, 30s on the longest-clip model, so a 30s ad can be one take. Leave it out when the user named no length; the planner then picks one that suits the format. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Goes well beyond the annotations: discloses that nothing is rendered, that planning consumes credits, that inserts add roughly 2 credits, that out-of-range durations are clamped and reported, and that over-budget video requests trigger a refusal with three options. The 'hook that cannot be delivered is DROPPED with the reason' behavior is also surfaced in the schema and echoed in the description's planning semantics.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with the one-line purpose and the 'renders nothing' handoff, then targeted guidance on durationSeconds and inserts. Slightly long and partially redundant with the schema's durationSeconds text (clip-maximum arithmetic appears in both places), but no filler sentences.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With 13 params, nested objects and no output schema, the description still tells the agent what comes back (copy variants, image prompt or storyboard, recipe, model ids, creative.inserts) and how to consume it. Credit cost and failure modes are covered. Only the multi-concept and reference-cloning behavior rely on the schema alone.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
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 a genuine decision rule the schema cannot: a length the user spoke belongs in durationSeconds rather than buried in the brief, and why (the storyboard is written to that length). The inserts guidance is also reiterated with routing to render_ad. Some of the clip-splitting detail duplicates the schema verbatim.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('turns a brand and a product or brief into an ad concept') and enumerates the deliverables (copy variants, image prompt or storyboard, recipe and model ids). It also draws the boundary explicitly: 'It renders nothing; pass its output to render_ad, generate_image or generate_video', which separates it from the rendering siblings.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives concrete routing rules: use this to plan, then hand off to render_ad/generate_image/generate_video; put a user-named length in durationSeconds rather than only in the brief; omit the length when none was named. It also names the alternative path (clone_video via the reference param) so the agent can pick the right entry point.
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.
| Name | Required | Description | Default |
|---|---|---|---|
| brand | No | brand name or profile object; OMIT to use the workspace’s saved brand | |
| count | No | how many distinct variants (default 6) | |
| product | Yes | what to advertise | |
| language | No | output language for the variant copy (e.g. Spanish) — default English |
TDQS
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.
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.
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.
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.
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.
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.
plan_weekPlan a week of postsAInspect
Use this when the user wants a week of social posts planned, or answers yes or no to planning their week. It drafts one batch of 5 posts for the active brand: a new image, a caption and a suggested day and time for each. Without confirm it returns the price and whether the batch fits the account’s posting allowance, and spends nothing. confirm:true starts the batch and bills credits; read the posts with get_job. The posts stay drafts until the user approves them with run_post_refill, and this tool publishes nothing. decline:true records that the user said no.
| Name | Required | Description | Default |
|---|---|---|---|
| confirm | No | true only after the user agreed to the price | |
| decline | No | true when the user said no | |
| timezone | No | the user’s IANA timezone when known, e.g. America/New_York; the suggested times use it |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Goes well beyond the annotations: it discloses the cost two-phase flow (no confirm = price and allowance check, spends nothing; confirm:true = starts the batch and bills credits), the draft lifecycle (posts stay drafts until run_post_refill), and the fact this tool publishes nothing. None of that is inferable from readOnlyHint=false / destructiveHint=false.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with the trigger, then the behavior, then the hand-offs to get_job and run_post_refill – a sensible order. It is dense and nearly every clause earns its place, though 'decline:true records that the user said no' largely repeats the schema's own wording.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 3-parameter tool with no output schema, it covers everything an agent needs: trigger conditions, spend behavior, what is produced (5 posts with image, caption, timing), how to retrieve results, and what happens next. The absence of return-value docs is handled by redirecting to get_job.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
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 behavioral meaning beyond the schema: confirm actually starts the batch and bills credits, while decline records a refusal. timezone is left to the schema, which already explains it adequately, so it stops short of a 5.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a concrete verb and artifact: 'drafts one batch of 5 posts for the active brand: a new image, a caption and a suggested day and time for each.' This clearly separates it from plan_ad, plan_variations and run_post_refill, which an agent could otherwise confuse with week planning.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Opens with an explicit trigger ('when the user wants a week of social posts planned, or answers yes or no'), then names the follow-on tools: get_job to read the posts and run_post_refill to approve them. When-to-use, when-not (publishes nothing) and alternatives are all present.
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 or generate_sfx 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.
| Name | Required | Description | Default |
|---|---|---|---|
| ops | Yes | the ordered edit plan (max 6 ops) | |
| accent | No | override the brand accent hex | |
| domain | No | override the brand website | |
| dryRun | No | true = return the estimated credits this edit reserves and run nothing | |
| videoUrl | Yes | the video to edit: a render / Library URL, a direct file, or a public post link | |
| brandName | No | override the workspace brand name |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only declare the generic mutation profile (readOnly=false, destructive=false, idempotent=false, openWorld=true). The description adds the operationally important facts an agent needs: ~2 credits flat, ~1 credit for voiceover, up to 8 credits for generated payoff sound, seconds-long runtime, non-destructive output as a NEW video, and defaults for ducking/flash/shake and seam matching.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loading is strong: the core identity, cost, and no-AI distinction land in the first clause before any op detail. The remainder is a dense run-on of parentheticals that is harder to scan than it needs to be, though nearly every fragment carries distinct information rather than filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a complex, multi-op editing tool with 100% schema coverage and no output schema, the description covers everything decisive: what it produces, cost profile, op-by-op semantics, presets, the hook/bridge pattern, and which sibling tool to use instead. No material gap remains that would cause a misinvocation.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
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 earns credit by explaining relationships the schema cannot, such as join meaning 'this video FOLLOWED BY clips[]', bridge sound fallback order (own audio, then generated, then neutral impact), and voiceover pacing at ~2.5 words/s. It does not re-explain the top-level overrides (brandName, accent, domain, dryRun), which the schema already covers.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Opens with a precise verb-and-resource claim: mechanical post-production on an existing video URL, run by ffmpeg, no AI model, output as a new video. It enumerates the exact op set and explicitly rules out siblings ('ANY OTHER EDIT... is edit_timeline', 'NEVER generate_video/render_ad for these'), so an agent can route correctly 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.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives positive selection criteria (mechanical ops on an existing video), negative exclusions (edit_timeline for whip/zoom/freeze/wipe/split screen/generated shots; never generate_video/render_ad), and conditional rules for paid branches ('track generate... ONLY when the user asks', text bridge 'only when the user asks for words over the cut'). It also states the default bridge behavior for the viral-hook pattern, which is genuine routing guidance.
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 tractionARead-onlyIdempotentInspect
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.
| Name | Required | Description | Default |
|---|---|---|---|
| axis | No | what to group by — default hook; recipe = the format of the creative | |
| days | No | look back N days (1-730) over the whole history; omit for the recent posts only | |
| brand | No | 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. | |
| channel | No | restrict to one channel |
TDQS
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.
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.
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.
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.
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.
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 BlueskyADestructiveInspect
Use this when the user wants to post to Bluesky. Publishing costs no credits. Publish a post to Bluesky as the connected account. Text up to 300 characters — Bluesky ALSO caps a post at 3000 UTF-8 bytes, so an emoji-heavy post can be under 300 characters and still be refused; Hermoso checks both before spending the round trip and says which limit and by how much. @MENTIONS written as the full handle (@alice.bsky.social) are looked up and tagged as Bluesky mentions; one that does not resolve stays plain text and the reply names it. MEDIA: either up to 10 images (imageUrls + altText; 5–10 post as a Bluesky gallery, more than 10 is refused, never cut; a picture over Bluesky's 2 MB limit is re-encoded to fit and the reply says so) 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.
| Name | Required | Description | Default |
|---|---|---|---|
| hook | No | the post's angle — a list_hooks id or your own wording, reused exactly | |
| text | Yes | The post, up to 300 characters / 3000 UTF-8 bytes. | |
| brand | No | 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. | |
| langs | No | BCP-47 language tags, e.g. ['en']. | |
| ideaId | No | short id of the content-plan idea this post came from | |
| recipe | No | the post's FORMAT id, e.g. "slideshow" or "imessage_chat" — post_performance groups by it, so reuse one id per format | |
| account | No | 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. | |
| altText | No | Alt 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. | |
| subject | No | what the post is about | |
| captions | No | Up 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. | |
| linkCard | No | Rich 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. | |
| videoAlt | No | Alt text describing the video, for accessibility. | |
| videoUrl | No | One public MP4 URL. Cannot be combined with imageUrls. Bluesky transcodes it, which takes a minute or two. | |
| imageUrls | No | Up to 10 public image URLs to attach (1–4 a standard image post, 5–10 a Bluesky gallery; more than 10 is refused, never cut). Cannot be combined with videoUrl. | |
| platformCover | No | 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. | |
| allowDuplicate | No | post it even though an identical post was just made | |
| idempotencyKey | No | any stable string: a repeat within 24h returns the original post instead of posting again |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The description adds extensive behavior beyond the annotations: no credit cost, 300-character and 3000-byte limits, mention resolution, image/video constraints, video email confirmation, automatic link-card construction, embed conflict handling, and the returned bsky.app URL. The annotations already flag write/open-world behavior, and the description richly supplements them without contradicting them.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is long, but the complexity of 17 parameters and multiple media/embed constraints justifies much of the length. It is front-loaded with the primary use case and the most important limits, though the dense all-caps emphasis makes it less crisp than it could be.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the complex posting behavior and lack of an output schema, the description covers what the tool does, the major media/link-card constraints, the video prerequisite, and the return value. An agent has enough context to invoke it correctly without needing additional prose.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
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 useful semantics beyond the schema: it explains automatic link-card behavior, the precedence when a URL is in text versus media, and the refusal when linkCard is explicitly combined with media. It does not cover every one of the 17 parameters, but it clarifies the highest-risk interactions.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific verb and resource: posting to Bluesky as the connected account. Because sibling tools are platform-specific posting tools, the platform name alone lets an agent distinguish this from post_to_x, post_to_linkedin, etc.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It explicitly says to use this when the user wants to post to Bluesky and notes the connection requirement. It does not compare against schedule_post or other platform-posting siblings, so it lacks explicit exclusions, 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.
post_to_google_businessPost to Google Business ProfileADestructiveInspect
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).
| Name | Required | Description | Default |
|---|---|---|---|
| hook | No | 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. | |
| link | No | the URL the button opens — not for CALL, and ignored on an OFFER | |
| brand | No | 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. | |
| title | No | headline — REQUIRED for EVENT and OFFER | |
| ideaId | No | short id of the content-plan idea this post came from | |
| recipe | No | the post's FORMAT id, e.g. "slideshow" or "imessage_chat" — post_performance groups by it, so reuse one id per format | |
| endDate | No | YYYY-MM-DD, defaults to startDate | |
| subject | No | 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. | |
| summary | No | the body text of the Post | |
| imageUrl | No | a Hermoso render image URL (or an upload_file url) to show on the Post | |
| startDate | No | YYYY-MM-DD — REQUIRED for EVENT and OFFER | |
| topicType | No | default STANDARD | |
| actionType | No | the button on the Post | |
| couponCode | No | OFFER only | |
| locationId | No | which listing, e.g. 'locations/123' from list_business_locations — only needed when the account manages more than one | |
| languageCode | No | BCP-47 language of the Post, default 'en' | |
| redeemOnlineUrl | No | OFFER only — this is the link Google actually uses on an offer | |
| termsConditions | No | OFFER only |
TDQS
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.
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.
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.
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.
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.
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 LinkedInADestructiveInspect
Use this when the user wants to post to their personal LinkedIn profile. Publishing costs no credits. Publish a post to the user’s connected LinkedIn profile — text, and optionally an image (imageUrl), a VIDEO (videoUrl, with captionsSrt and a videoThumbnailUrl cover), a 2–20 image MULTI-IMAGE post (imageUrls), a LINK post with a real preview card (linkUrl + linkTitle / linkDescription / linkThumbnailUrl — LinkedIn never scrapes the page), a DOCUMENT post (documentUrl: a PDF / PPTX readers swipe through), and @mentions of companies (mentions). 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).
| Name | Required | Description | Default |
|---|---|---|---|
| hook | No | 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. | |
| text | Yes | the post text | |
| brand | No | 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. | |
| title | No | video title | |
| ideaId | No | short id of the content-plan idea this post came from | |
| recipe | No | the post's FORMAT id, e.g. "slideshow" or "imessage_chat" — post_performance groups by it, so reuse one id per format | |
| altText | No | image alt text — a string for every image, or an array per slide | |
| linkUrl | No | a LINK post with a real preview card. Not with media. | |
| subject | No | 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. | |
| imageUrl | No | a 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). | |
| mentions | No | TAG COMPANIES: write "@Name" in the text where each tag goes and list each company here — its vanity name (the part after linkedin.com/company/), its company page URL, or {organizationId, name}. Hermoso resolves it on LinkedIn and LinkedIn renders a real clickable mention; a company with no "@" spot in the text is refused rather than appended. | |
| videoUrl | No | a Hermoso-hosted video (a render, or the user’s footage via upload_file). LinkedIn processes it before publishing, which takes a minute. | |
| imageUrls | No | a MULTI-IMAGE post: 2–20 Hermoso-hosted images in order, one post the reader swipes through (LinkedIn’s MultiImage API takes a member author on w_member_social). Images only. | |
| linkTitle | No | the card headline — LinkedIn never scrapes the page, so without it the card is unlabelled | |
| visibility | No | default PUBLIC | |
| captionsSrt | No | SRT caption CONTENT for a videoUrl post (English only, attached during upload) | |
| documentUrl | No | a DOCUMENT post — a Hermoso-hosted PDF / PPTX / DOCX (upload_file first) that readers swipe through page by page in the feed; LinkedIn takes up to 100MB and 300 pages. One kind of media per post: not with an image, video, slides or linkUrl. | |
| documentTitle | No | the title shown on the document (defaults to the file name) | |
| allowDuplicate | No | post 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. | |
| idempotencyKey | No | 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. | |
| linkDescription | No | link card description; LinkedIn does not read the page, so set it here | |
| linkThumbnailUrl | No | Hermoso-hosted image for the card | |
| videoThumbnailUrl | No | cover image for a videoUrl post (Hermoso-hosted) |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The description adds substantial behavior beyond the annotations: publishing is immediate and public, explicit user approval is mandatory, publishing costs no credits, a connected LinkedIn account is required, media must be Hermoso-hosted, and idempotency/duplicate handling is explained. These details go well beyond the destructive/open-world annotation hints.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is long, but the tool has 23 parameters and many media modes, so most sentences earn their place. The critical safety instruction is front-loaded, though the media-mode enumeration is dense and could be more scannable.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a complex publishing tool with no output schema, the description covers the critical prerequisites, safety confirmation, media hosting constraints, and duplicate-retry behavior. It does not describe the normal success return shape beyond the idempotency case, which is a minor gap.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
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 still adds meaningful cross-parameter semantics: one media kind per post, multi-image constraints, link cards not being scraped, mention placement rules, and the role of upload_file for arbitrary user files.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific verb and resource: publishing a post to the user's personal LinkedIn profile. It distinguishes itself from the sibling post_to_linkedin_page by emphasizing 'personal LinkedIn profile' rather than a company page.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It gives clear usage context: use when the user wants to post to their personal LinkedIn profile, requires a connected LinkedIn account, and always requires explicit user confirmation before publishing. It does not explicitly name the page-posting sibling as an alternative for company pages, but the personal-profile framing provides strong implied routing.
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 PageADestructiveInspect
Use this when the user wants to post to a LinkedIn company Page. Publishing costs no credits. 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. A DOCUMENT post (documentUrl — a PDF/PPTX deck readers swipe through) is LinkedIn’s strongest organic B2B format. Tag companies with @Name in the text plus mentions. 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).
| Name | Required | Description | Default |
|---|---|---|---|
| hook | No | 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. | |
| text | Yes | the post text | |
| brand | No | 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. | |
| title | No | video title | |
| ideaId | No | short id of the content-plan idea this post came from | |
| recipe | No | the post's FORMAT id, e.g. "slideshow" or "imessage_chat" — post_performance groups by it, so reuse one id per format | |
| altText | No | accessibility 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. Also works on a personal-profile post (post_to_linkedin). | |
| linkUrl | No | publish 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. | |
| subject | No | 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. | |
| imageUrl | No | a 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. | |
| mentions | No | TAG COMPANIES: write "@Name" in the text where each tag goes and list each company here — its vanity name (the part after linkedin.com/company/), its company page URL, or {organizationId, name}. Hermoso resolves it on LinkedIn and LinkedIn renders a real clickable mention; a company with no "@" spot in the text is refused rather than appended. | |
| videoUrl | No | a Hermoso-hosted video URL — a render, or the user’s own footage via upload_file. LinkedIn processes it before publishing, which takes a minute. | |
| coverAtMs | No | 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. | |
| imageUrls | No | CAROUSEL — 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–20 (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. | |
| linkTitle | No | the 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. | |
| visibility | No | default PUBLIC | |
| captionsSrt | No | CLOSED 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. | |
| documentUrl | No | a DOCUMENT post — a Hermoso-hosted PDF / PPTX / DOCX (upload_file first) that readers swipe through page by page in the feed; LinkedIn takes up to 100MB and 300 pages. One kind of media per post: not with an image, video, slides or linkUrl. | |
| firstComment | No | FIRST COMMENT — posted on the new post AS THE PAGE right after it publishes. Needs the LinkedIn comments authorization on this brand (provider linkedin_community). Up to 2,000 characters, refused rather than cut. If the comment fails the post stays live and firstComment in the result says why. | |
| documentTitle | No | the title shown on the document (defaults to the file name) | |
| platformCover | No | 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. | |
| allowDuplicate | No | post 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. | |
| idempotencyKey | No | 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. | |
| organizationId | No | numeric Page id from list_linkedin_pages | |
| targetAudience | No | 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. | |
| linkDescription | No | the sub-line on the preview card. Same rule as linkTitle: absent means blank, because LinkedIn will not fetch it. | |
| linkThumbnailUrl | No | a Hermoso-hosted image used as the card’s picture (uploaded to LinkedIn for you). Without it the card has no image. | |
| videoThumbnailUrl | No | the 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
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already flag destructive and open-world, but the description adds crucial context: publishing is immediate and public, user must confirm exact text before calling, publishing costs no credits, LinkedIn disables URL scraping so linkTitle/linkDescription are needed for preview cards, media must be Hermoso-hosted, captions and thumbnails can only be set during upload, and media cannot be swapped after publishing (copy can be edited with manage_linkedin_post).
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is long but front-loaded with the core use case and sibling distinction. It is well-structured, covering formats, warnings, and media specifics. However, some parameter details (e.g., carousel per-channel limits) are repeated from the schema, slightly reducing conciseness.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given 28 parameters, no output schema, and only safety annotations, the description is remarkably complete: it covers prerequisites, behavioral constraints, failure modes, format-specific rules, and confirmation requirements. An agent has enough to call it correctly and avoid common pitfalls.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
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 some extra meaning beyond the schema: it specifies that organizationId comes from list_linkedin_pages and should be omitted only when the account administers exactly one Page, and clarifies when to use linkUrl versus other media (mutual exclusivity is also in schema). Much of the parameter detail overlaps with the rich schema, limiting the score to 4.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb (post/publish) and resource (LinkedIn company Page), and explicitly distinguishes it from the sibling post_to_linkedin: 'This is a DIFFERENT thing from post_to_linkedin, which publishes to the person’s own profile.' An agent can choose correctly 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.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly says when to use this tool ('when the user wants to post to a LinkedIn company Page'), names the alternative (post_to_linkedin), and instructs 'pick the one the user actually asked for and never substitute.' It also gives format-specific guidance (linkUrl use, carousel vs single post, document posts).
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 ThreadsADestructiveInspect
Use this when the user wants to post now to Facebook, Instagram or Threads. 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, an ad made here or the user’s own 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. 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.
| Name | Required | Description | Default |
|---|---|---|---|
| hook | No | 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. | |
| link | No | a URL to attach (FB text post only) | |
| async | No | publish 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. | |
| brand | No | 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. | |
| place | No | FACEBOOK — tag a location on the Page post. The pageId from search_meta_places: the NUMERIC ID OF THE FACEBOOK PAGE for that place, not a place name and not coordinates. | |
| story | No | INSTAGRAM OR FACEBOOK PAGE 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. Link stickers are not available on a Story posted here. An Instagram Story keeps its caption: people see the start under the Story in the mobile app and tap for the rest. It takes NO collaborators, product tags or trialReel — any of those is refused by name and nothing is posted. FACEBOOK PAGE STORIES WORK TOO (target facebook): one image, or one vertical 9:16 video of 3–90 seconds, no caption (a message is refused by name), not schedulable natively — use schedule_post to queue one; the reply reads the story back from the Page. | |
| ideaId | No | short id of the content-plan idea this post came from | |
| pageId | No | target Page id (from list_meta_pages). Omit when the brand has one Page. With several and none named the call refuses and lists them by name and id: ask the user which Page, never pick one yourself. | |
| recipe | No | the post's FORMAT id, e.g. "slideshow" or "imessage_chat" — post_performance groups by it, so reuse one id per format | |
| target | No | default facebook; instagram -> the Page’s linked IG; threads -> the brand’s connected Threads account | |
| account | No | WHICH 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. | |
| altText | No | 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 — use that 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.) | |
| audioId | No | 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. | |
| message | No | post text / caption | |
| subject | No | 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. | |
| audience | No | 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. | |
| coverUrl | No | 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. | |
| imageUrl | No | public https URL, a data: URI, or a Hermoso /generated path (upload_file gives you one for a local file) | |
| linkName | No | 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. | |
| topicTag | No | THREADS ONLY — one topic tag for discovery, 1–50 characters. A leading # is stripped for you; Threads refuses "." and "&". | |
| userTags | No | INSTAGRAM USER TAGS — tag people (public accounts) on the post; each is notified. Usernames, or { username, x, y } with x/y from 0 to 1 = where the tag sits on the IMAGE (a tag without a position sits at the centre). A Reel tag takes no position; a story position is optional. Not on a carousel (refused by name). | |
| videoUrl | No | public https URL, data: URI, or /generated path — FB video post / IG Reel | |
| audioName | No | INSTAGRAM REEL — the name of the Reel’s audio track, which is what viewers tap through to. REELS ONLY. | |
| coverAtMs | No | 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. | |
| imageUrls | No | CAROUSEL — 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–20 (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. | |
| placeName | No | the name the user saw for that place; a different business is refused | |
| trialReel | No | 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. | |
| locationId | No | TAG A PLACE. THREADS: a place id from search_threads_locations. INSTAGRAM: the pageId from search_meta_places, 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. | |
| scheduleAt | No | FACEBOOK 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. | |
| threadsGif | No | THREADS ONLY — a GIPHY GIF id (the id in a giphy.com/gifs/… link) attached to a TEXT Threads post. GIPHY is the only provider Threads takes. | |
| aiGenerated | No | INSTAGRAM / FACEBOOK REEL / THREADS — Meta’s is_ai_generated self-disclosure (Threads: is_made_with_ai, the AI info label; a text-only Threads post is declared only when you pass true). 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. | |
| audioVolume | No | INSTAGRAM REEL — how loud that track plays, 0–100 (Instagram’s default 100). Needs audioId. | |
| linkPicture | No | 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. | |
| productTags | No | INSTAGRAM 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. | |
| quotePostId | No | THREADS ONLY — the id of a post to QUOTE. A quote post is a distinct post type, not a formatting option. | |
| shareToFeed | No | 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. | |
| thumbOffset | No | 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. | |
| videoVolume | No | 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. | |
| callToAction | No | 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. | |
| countryCodes | No | 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. | |
| firstComment | No | FIRST COMMENT — posted on the new post as the same account right after it publishes (Facebook or Instagram; not Threads, not a story, not an unpublished post). Up to 2,000 characters, refused rather than cut. If the comment fails the post stays live and firstComment in the result says why. | |
| replyControl | No | THREADS ONLY — who may reply. Default is everyone. | |
| collaborators | No | INSTAGRAM 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. 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. | |
| instagramPoll | No | 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. | |
| platformCover | No | 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. | |
| allowDuplicate | No | post 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. | |
| idempotencyKey | No | 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. | |
| linkAttachment | No | THREADS 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. | |
| linkDescription | No | 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. | |
| paidPartnership | No | INSTAGRAM — the PAID PARTNERSHIP label. A COMPLIANCE DECLARATION: 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. | |
| threadsLongText | No | THREADS ONLY — LONG-FORM text, up to 10,000 characters, attached under a TEXT-only Threads post (Threads' text attachment). The way to publish more than Threads' 500-character post: keep a short lead in the message and put the full text here. Not with an image, video or carousel. | |
| callToActionLink | No | FACEBOOK — where the button goes, when that is not the post’s own `link`. | |
| crossreshareToIg | No | THREADS 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. | |
| threadsGhostPost | No | THREADS ONLY — a GHOST post: text only, Threads archives it automatically after 24 hours (a flash offer, a 24h-only note). No other Threads option can ride it. | |
| crossreshareDarkMode | No | THREADS 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. | |
| instagramPollExtended | No | 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. | |
| instagramCommentPrompt | No | INSTAGRAM — make the caption a COMMENT PROMPT that people answer in the comments. Same limits as instagramPoll, and never together with it. | |
| brandedContentSponsorIds | No | 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. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only flag writability and open-world reach; the description adds the operationally critical context: immediate publish with no undo, the required connected Meta/Threads account and permission, refusal-by-name semantics for unsupported combinations, and the fact that a collab is only an invite that may be declined with no notification. It even discloses where confirmation is unavailable (crossreshareToIg) and tells the agent not to report success.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded and free of filler, which is right for a 58-parameter tool. But the heavy ALL-CAPS emphasis and the long target/carousel paragraph make it dense to scan, and a few clauses (e.g. the alt-text cross-reference inside the body) duplicate structured content rather than adding routing value.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a zero-required-param, 58-parameter publishing tool with no output schema, this covers everything an agent needs: prerequisites, media-source handling, per-channel constraints, scheduling boundaries, idempotent retry behavior, and how to interpret the reply. 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.
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 prose adds routing meaning the schema alone doesn't: target defaults and how each target changes required media, imageUrls[] as ONE swipeable post rather than several, and the upload_file hand-off for local files. The rest of the 58 parameters are documented in-schema and the description wisely doesn't restate them.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Opens with a concrete verb+resource+scope: "Use this when the user wants to post now to Facebook, Instagram or Threads," then names the three targets and the supported media types. It is immediately distinguishable from siblings like post_to_x, post_to_linkedin_page, schedule_post and plan_ad.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly states when to use (post now, this publishes immediately, confirm copy+media first) and routes around alternatives: call upload_file for a LOCAL file, use schedule_post to queue a Facebook Story, and use instagram_collaborators later to check an invite. It also warns against the common mistake of publishing only slide 1 of a carousel.
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 PinADestructiveInspect
Use this when the user wants to publish a Pin to Pinterest. Publishing costs no credits. 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).
| Name | Required | Description | Default |
|---|---|---|---|
| hook | No | 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. | |
| link | No | destination URL the Pin clicks through to | |
| brand | No | 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. | |
| title | No | Pin title, max 100 characters | |
| ideaId | No | short id of the content-plan idea this post came from | |
| recipe | No | the post's FORMAT id, e.g. "slideshow" or "imessage_chat" — post_performance groups by it, so reuse one id per format | |
| account | No | 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. | |
| altText | No | accessibility 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. | |
| boardId | Yes | numeric board id from list_pinterest_boards — the user picks it, never guess | |
| subject | No | 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. | |
| imageUrl | No | a Hermoso render image URL (or an upload_file url) | |
| videoUrl | No | a Hermoso render video URL — takes 1–2 minutes to ingest | |
| coverAtMs | No | 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. | |
| imageUrls | No | CAROUSEL — 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–20 (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. | |
| slideText | No | PINTEREST 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. | |
| aiGenerated | No | Pinterest’s AI disclosure (ai_disclosures AI_MODIFIED). OMIT IT and Hermoso decides from provenance: a Hermoso render is declared, an image or video that came through upload_file or an external URL (the user’s own) is NOT. Pass true or false only to override. | |
| description | No | Pin description, max 800 characters — this is what Pinterest search reads | |
| coverImageUrl | No | video Pins only — a render to use as the cover frame | |
| platformCover | No | 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. | |
| allowDuplicate | No | post 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. | |
| boardSectionId | No | optional section within the board | |
| idempotencyKey | No | 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. | |
| syntheticPerformer | No | ALSO declare SYNTHETIC_PERFORMER — the Pin shows a realistic AI-generated person. Only with an AI disclosure; never guessed, so pass true only when the user says the person is AI-made. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Beyond the annotations, it discloses that publishing is free, goes to the user's public profile, requires Pinterest to be connected, and that video Pins take a minute or two to ingest. It also gives a confirmation safeguard before the public write. This adds substantial context beyond the destructiveHint/openWorldHint annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is front-loaded with the core use case and stays focused on operational details. However, it is a single dense paragraph, and a few phrases ('ride along') are less precise than they could be.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 23-parameter publish tool, the description covers the critical workflow: board selection, media types, link/description importance, public visibility, confirmation, and connection requirements. Optional tracking and retry parameters are fully documented in the schema, so no important calling context is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
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 reinforces boardId as required and highlights the link and description fields, but does not add syntax or meaning beyond what the already-rich schema provides for those parameters.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description names a specific verb and resource: publish/create a Pin on Pinterest. It clearly distinguishes the Pinterest domain but does not explicitly name or distinguish sibling posting tools such as schedule_post or post_to_meta.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It gives explicit when-to-use guidance ('Use this when the user wants to publish a Pin to Pinterest') and a prerequisite workflow ('boardId is REQUIRED: call list_pinterest_boards first'). It also instructs the agent to confirm the board, title, and link before calling. It does not discuss when to choose an alternative such as scheduling.
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 TelegramADestructiveInspect
Use this when the user wants to post to a Telegram channel or group. Publishing costs no credits. 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. @yourchannel) 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.
| Name | Required | Description | Default |
|---|---|---|---|
| hook | No | the post's angle — a list_hooks id or your own wording, reused exactly | |
| text | No | the message. ≤4096 characters on its own; ≤1024 once any image or video is attached. | |
| brand | No | 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. | |
| chatId | Yes | REQUIRED — the destination: a public channel's @username, or the numeric chat id. Never guessed; ask the user, or use list_telegram_chats. | |
| ideaId | No | short id of the content-plan idea this post came from | |
| recipe | No | the post's FORMAT id, e.g. "slideshow" or "imessage_chat" — post_performance groups by it, so reuse one id per format | |
| silent | No | deliver without a notification sound (Telegram’s disable_notification). This is NOT a visibility setting — the message is just as visible. | |
| account | No | 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. | |
| buttons | No | URL BUTTONS under the post, Telegram's call to action: [{text:'Shop now', url:'https://…'}] one per row, or rows [[a,b],[c]]. URL buttons only (https://, http:// or tg://), optional style danger|success|primary. A single photo, a video or a text post can carry them; an ALBUM cannot (Telegram's sendMediaGroup takes no buttons), so that combination is refused. | |
| subject | No | what the post is about | |
| imageUrl | No | one image (≤10MB after upload) | |
| videoUrl | No | one video (≤50MB). Passed alongside imageUrls it joins the album as one more item. | |
| coverAtMs | No | 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. | |
| imageUrls | No | an ALBUM of 2–10 media, in order. Photos and videos may be mixed — Telegram allows it. | |
| parseMode | No | 'HTML' to format the text with Telegram's HTML: <b>, <i>, <u>, <s>, <a href="…">, <code>, <pre>, <blockquote>, <tg-spoiler>. Omit for plain text (the default). In HTML mode a literal <, > or & in the copy must be written < > &, which is why it is never turned on by itself. Markdown (**bold**) is NOT parsed; convert it to HTML tags. | |
| platformCover | No | 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. | |
| allowDuplicate | No | post it even though an identical post was just made | |
| disablePreview | No | suppress the link-preview card on a text-only post. Default is Telegram’s own behaviour (previews on). | |
| idempotencyKey | No | any stable string: a repeat within 24h returns the original post instead of posting again |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Goes well beyond the annotations: zero credit cost, byte-upload vs link trade-off with the resulting 10MB/50MB ceilings, the 4096/1024 character budgets and pre-flight refusal, the album/buttons incompatibility, and the null `url` for private groups. The idempotencyKey's 24h dedupe is described as an opt-in behavior, which is consistent with idempotentHint=false 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.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with the trigger and the non-negotiable chatId requirement, and sectioned by TEXT/MEDIA. Slightly overlong: 'costs no credits' appears at the top and again as '0 credits' at the end, and the chatId no-default point is restated across two sentences.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 19-param, open-world, destructive-tagged publish tool with no output schema, the description covers the failure modes an agent would otherwise hit (missing chat, missing bot membership, over-long caption, album+buttons) and describes the return value (message id, t.me link or null). 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.
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; the description earns above that by adding meaning the schema lacks for the highest-risk params — chatId's @username vs numeric id forms and why no default exists, and the caption-vs-message character budget interaction with media. It says nothing about the remaining 15 optional params (brand, hook, buttons, parseMode, covers), leaving those entirely to the schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('post to a Telegram channel or group'), names the acting identity (the brand's own bot), and is clearly distinguishable from siblings such as list_telegram_chats or post_to_bluesky. An agent knows exactly what this call 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.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Opens with an explicit trigger ('Use this when the user wants to post to a Telegram channel or group') and routes to the sibling: list_telegram_chats is described as a shortcut for finding an id and explicitly NOT a roster. It also states the precondition (bot must be in the chat, admin with Post Messages for channels) and the refusal behavior when it is absent.
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 TikTokADestructiveInspect
Use this when the user wants to post a video or photo post to TikTok now. Publishing costs no credits. 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).
| Name | Required | Description | Default |
|---|---|---|---|
| hook | No | 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. | |
| brand | No | 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. | |
| title | No | the caption — hashtags go here (video ≤2200 chars, photo post ≤4000) | |
| ideaId | No | short id of the content-plan idea this post came from | |
| recipe | No | the post's FORMAT id, e.g. "slideshow" or "imessage_chat" — post_performance groups by it, so reuse one id per format | |
| account | No | 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. | |
| privacy | No | REQUIRED for destination:"post", for photos and video alike. Must be one the creator actually allows — read them from tiktok_creator_info, never guess. | |
| subject | No | 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. | |
| videoUrl | No | the video to post — a Hermoso render URL or an upload_file url. Omit for a photo post. | |
| imageUrls | No | a PHOTO POST: 1–35 image URLs in slide order. One url = a single-image photo post. Do not combine with videoUrl. | |
| yourBrand | No | discloses that this promotes the creator’s own brand | |
| coverIndex | No | photo posts: which slide is the cover, 0-based. Default 0 (the first slide). | |
| photoTitle | No | photo posts only: a short title above the caption (≤90 chars). Defaults to the caption’s first line. | |
| aiGenerated | No | 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. | |
| destination | No | "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. | |
| disableDuet | No | video only — TikTok has no duet on a photo post | |
| autoAddMusic | No | photo posts only: let TikTok add a recommended track (default true — a silent slideshow reads as broken) | |
| disableStitch | No | video only — TikTok has no stitch on a photo post | |
| platformCover | No | 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. | |
| brandedContent | No | discloses a paid partnership — cannot be combined with SELF_ONLY privacy | |
| disableComment | No | true = turn comments off on this post | |
| coverTimestampMs | No | video only: which frame to use as the cover, in ms |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Beyond the annotations (destructiveHint, openWorld, non-idempotent), it discloses that publishing costs no credits, requires a connected TikTok account, requires user-selected privacy plus an explicit yes, and that drafts land in the user's inbox unpublished. It also explains the autoAddMusic behavior and TikTok's sound limitation for videos. No contradiction with annotations — indeed it reinforces the destructive/open-world nature.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
It is front-loaded with the core action and the most important branching decision (destination), and each paragraph serves a purpose. It is somewhat long and heavily capitalized, but justified by a 22-parameter, two-mode tool.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given 22 parameters and no output schema, the description covers prerequisites, content-shape rules, and privacy/draft constraints well. The main gap is what happens after publishing (async job, status polling via tiktok_post_status), which an agent would need to know to complete the workflow.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the schema already explains imageUrls range/order, destination semantics, privacy enum source, and autoAddMusic defaults. The description largely restates those same facts rather than adding new parameter meaning, so the baseline 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
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 ('post a video or photo post to TikTok now') and immediately distinguishes the two content types and the two destinations. An agent can tell exactly what this does and how it differs from siblings like tiktok_post_status or schedule_post.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It gives explicit when-to-use rules for destination:"post" vs "draft", names the precondition of calling tiktok_creator_info first, and states forward exclusions ('Never pick draft on your own', 'Never pass both'). Alternatives and the branch conditions that select them are fully spelled out.
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)ADestructiveInspect
Use this when the user wants to post to X (Twitter) now. 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 at X’s per-post price, every post in a thread billed: a post that contains a link (any web address, and every quote post) is X’s link rate, far higher than one without (hermoso_credits gives both figures). When the user asks what a post will cost, call this with dryRun:true: it posts NOTHING and returns that exact post’s costCredits. 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 the X Ads campaign tools (campaign, then line item, then promoted post), and a promoted post needs a post id, so publish here first and promote that post. Needs X connected (Settings > Connectors > X).
| Name | Required | Description | Default |
|---|---|---|---|
| hook | No | 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. | |
| poll | No | run 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. | |
| text | No | the post text. 280 characters without X Premium, up to 25,000 with it — write the full thing, it is never truncated. With `thread` also set, this is the FIRST post and the thread entries follow it (a thread whose first entry is already this text is not posted twice). | |
| brand | No | 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. | |
| dryRun | No | true = post NOTHING and return what this exact post would cost (costCredits). Free. | |
| ideaId | No | short id of the content-plan idea this post came from | |
| recipe | No | the post's FORMAT id, e.g. "slideshow" or "imessage_chat" — post_performance groups by it, so reuse one id per format | |
| thread | No | a thread: each string is one post, published in order, each replying to the previous (if `text` is also set it is posted first and these follow). Max 25 including `text`. 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. | |
| account | No | 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. | |
| altText | No | accessibility 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). | |
| subject | No | 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. | |
| imageUrl | No | alias of mediaUrl for an IMAGE — same as passing it as mediaUrl | |
| mediaUrl | No | a Hermoso render (image or video) to attach to the first post — pass its served URL, or an upload_file url for external media | |
| videoCta | No | VIDEO posts only — a button on the video; needs videoCtaUrl (https) | |
| videoUrl | No | alias of mediaUrl for a VIDEO — same as passing it as mediaUrl | |
| mediaUrls | No | UP 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. | |
| replyToId | No | 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. | |
| dmDeepLink | No | a "Message us" button: true for the connected account, or https://x.com/messages/compose?recipient_id=<id>. TEXT posts only — X refuses it alongside media. | |
| videoTitle | No | VIDEO posts only — a headline under the video, max 70 (measured: X drops it on an image post, so it is refused there) | |
| communityId | No | 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. | |
| quotePostId | No | 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. | |
| videoCtaUrl | No | https link the video button opens; used with videoCta | |
| platformCover | No | 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. | |
| replySettings | No | restrict who can reply — omit for everyone, which is the right default for a brand post | |
| taggedUserIds | No | tag up to 10 people (numeric X user ids, from x_user) in the post’s media | |
| paidPartnership | No | label 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. | |
| videoDescription | No | VIDEO posts only — a line under the title, max 200 |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare destructive and open-world behavior, and the description adds substantial context: immediate public publication requiring confirmation, credit billing per post including links at a higher rate, a rolling 24-hour spend ceiling that refuses whole requests, and dryRun returning cost. It stops short of describing the success response (e.g. the returned post id), 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.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loads the core trigger well, but the prose is long and repetitive, restating the 280/25,000 length rule and the credit-cost rule more than once, and leaning on all-caps emphasis that crowds out scanning.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Complete for a 27-parameter publishing tool: it covers the safety gate, cost model, rate ceilings, length semantics, thread/media/poll exclusivity, and reply-policy constraints. The main gap is that the successful publish response (post id, thread ids) is never described and there is no output schema.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
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 by explaining when to use dryRun for cost queries, why altText should be written per-media, and how thread vs one long post interacts with billing and length.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a precise verb and resource (publish a post to X) and enumerates the supported shapes: single post, media post, reply, thread, poll. It also explicitly rules out ad campaigns, naming the X Ads tools instead, so an agent can separate this from siblings like post_to_meta or post_to_linkedin.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives explicit when-to-use ('user wants to post to X now'), prerequisites (X connected, per-account length, reply eligibility), and alternatives: prefer one long Premium post over a thread, publish here first then promote for ads, and reply via x.com when X's API restricts cold replies.
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 YouTubeADestructiveInspect
Use this when the user wants to upload a video or Short to YouTube. Publishing costs no credits. 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).
| Name | Required | Description | Default |
|---|---|---|---|
| hook | No | 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. | |
| tags | No | up to 30 tags | |
| brand | No | 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. | |
| title | No | REQUIRED in practice — the headline every search result and the Shorts feed shows (≤100 chars); an upload with no title is refused | |
| ideaId | No | short id of the content-plan idea this post came from | |
| recipe | No | the post's FORMAT id, e.g. "slideshow" or "imessage_chat" — post_performance groups by it, so reuse one id per format | |
| account | No | 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. | |
| license | No | YouTube licence: youtube (standard) or creativeCommon | |
| privacy | No | 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) | |
| subject | No | 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. | |
| videoUrl | Yes | the video to post — a Hermoso render URL or an upload_file url | |
| coverAtMs | No | 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. | |
| publishAt | No | SCHEDULE 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. | |
| categoryId | No | YouTube 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. | |
| embeddable | No | true = allow embedding on other sites | |
| aiGenerated | No | 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. | |
| description | No | 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 | |
| brandPartner | No | sponsoring brand's channel id (UC…) or @handle — gives that brand access to run the video as a partnership ad; "" unlinks (update only) | |
| thumbnailUrl | No | 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. | |
| localizations | No | translated title/description per language, e.g. {"es":{"title":"…"}}; needs defaultLanguage | |
| platformCover | No | 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. | |
| recordingDate | No | YYYY-MM-DD | |
| defaultLanguage | No | language of title/description, e.g. "en" | |
| notifySubscribers | No | THE 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. | |
| publicStatsViewable | No | true = show public view and like counts | |
| defaultAudioLanguage | No | spoken language, e.g. "es" | |
| paidProductPlacement | No | true = contains PAID product placement (sponsored) — YouTube shows the disclosure; legally required for sponsored content |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Goes well beyond the destructive/openWorld annotations: default-public publishing and the resulting subscriber notification, the irreversibility of a subscriber blast, publishAt only being valid on a private video, thumbnail rejection not failing the upload with a thumbnailNote explaining why, aiGenerated defaulting from provenance, and the YouTube API's inability to take a music track. These are exactly the consequences an agent must know 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.
Is the description appropriately sized, front-loaded, and free of redundancy?
It is front-loaded with the trigger, but the body is a dense wall of all-caps clauses that repeat the same points — the unlisted-for-ads rule and 'private cannot be ads' each appear multiple times across the description and the schema. Given 27 parameters some length is justified, yet the emphasis noise and duplication hurt scanability.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no output schema and only safety annotations, the description carries the behavioral burden and does: it covers the prerequisite (connected channel), defaults and their side effects, scheduling constraints, cover/thumbnail fallbacks, and even what the reply reports back ('thumbnailNote says why', 'the reply reports which way it went and why'). An agent has enough to call it correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100% and the per-parameter descriptions are already extremely detailed, so the description's discussion of privacy, publishAt, categoryId, thumbnailUrl and notifySubscribers largely restates what the schema says rather than adding new meaning. Baseline 3 is appropriate when the schema carries the semantics.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The first two sentences state a specific verb and resource — 'upload a video or Short to YouTube', 'Publish a finished video to the brand's connected YouTube channel' — and the platform specificity separates it cleanly from siblings like post_to_tiktok and post_to_meta. Scope (finished video, render or upload_file URL) is unambiguous.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives a clear trigger ('when the user wants to upload a video or Short to YouTube') plus several when/when-not rules: ads must be unlisted, private cannot run as ads, and music requests require a confirmation before ever choosing unlisted. It does not name alternative tools (e.g. schedule_post or other platform posters) to route against, so it stops short of the top score.
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 XADestructiveInspect
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).
| Name | Required | Description | Default |
|---|---|---|---|
| body | Yes | the 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. | |
| hook | No | 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. | |
| brand | No | 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. | |
| title | Yes | the Article title — X requires one and refuses a draft without it. This is what shows on the timeline card. | |
| ideaId | No | short id of the content-plan idea this post came from | |
| recipe | No | the post's FORMAT id, e.g. "slideshow" or "imessage_chat" — post_performance groups by it, so reuse one id per format | |
| account | No | which connected account: its @handle or id from list_connector_accounts; needed only when several are connected | |
| publish | No | default 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. | |
| subject | No | 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. | |
| headings | No | 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. | |
| allowLossy | No | publish 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. | |
| coverImageUrl | No | optional 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
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already flag destructive/openWorld/non-idempotent, but the description goes far beyond them: undocumented hard caps of ~10 drafts and 5 publishes/day, failed attempts counting against the cap, immutability after publish, credit cost (three API requests), and that unrepresentable formatting refuses rather than silently dropping. These are exactly the behavioral traits an agent cannot 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.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with the core action and the Article-vs-POST distinction, then layers preconditions, caps, and safety. It is dense and earns most of its length, but the all-caps emphasis and repeated warnings (public/never-editable/republish-costs-another) make it longer than strictly necessary.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 12-parameter, destructive, public, no-output-schema tool, the description covers the missing pieces an agent needs: nothing is published silently, the daily budget is disclosed, failure mode on unrepresentable formatting is stated, and the credit/auth prerequisites are given. No output schema exists, so the lack of return-value explanation is not a gap.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
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 beyond the schema for the highest-risk knobs: allowLossy is framed as an explicit escape hatch that should 'usually stay off,' and publish:false is tied to the draft-vs-live decision and the daily-publish budget. It does not add much for the analytics params (hook, subject, recipe, ideaId), which are left to their schema text.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Names the specific verb and resource (publish a long-form Article to X) and explicitly distinguishes it from the nearest sibling concept: 'X's own long-form format, which is a different thing from a long POST.' An agent can route between post_x_article and post_to_x 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.
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 guidance: use publish:false to save a draft when the user wants to review, and never iterate by republishing because failures count against the daily cap. It also names the alternative remedy path (edit_x_post will refuse, so delete and republish) and the preconditions (show full text, get explicit yes; X must be connected).
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.5 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 2.5 render: ≈870 credits at the DEFAULT 1080p, ≈360 at 720p, ≈170 at 480p; a 4k ask renders on Seedance 2.0, the only Seedance tier with 4k, ≈2,360 (call hermoso_capabilities for the live seedance-2.5 per-duration numbers; the dicing and the cards are free, and the music bed is included in these figures). 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.
| Name | Required | Description | Default |
|---|---|---|---|
| cta | No | closing CTA line, ≤30 chars | |
| specs | No | up to 4 spec lines for the typeset cards, ≤26 chars each | |
| style | No | the LOOK of the sizzle (same price for every look): hypermotion (Fast cuts, crash zooms and speed ramps), product_2d (The real product with playful hand-drawn 2D animation around it), typography (Product framed by huge bold letterforms in the brand colours), dark_minimal (Moody, premium, a single hard light on black), color_pop (Bright saturated colour, glossy, joyful), mixed_media (Editorial collage: paper, cut-outs and texture around the product), saas (For apps and software: floating screens, light trails, particles), product_showcase (Hero commercial: ingredients, splashes and a clean pack shot). Default hypermotion. | |
| prompt | Yes | what the sizzle should show — the product, the setting, the look | |
| seconds | No | finished 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 | |
| refImage | No | product packshot URL that anchors the real label — strongly recommended | |
| brandName | No | brand name on the cards — defaults to the workspace brand | |
| musicMood | No | music-bed mood, e.g. driving / cinematic / upbeat | |
| resolution | No | hero-clip resolution and therefore the whole cost — DEFAULT '1080p' (≈1,040 credits); '720p' ≈470, '480p' ≈220, '4k' ≈4,130 | |
| aspectRatio | No | '9:16' default; anything the Seedance row that renders it does not list falls back to 9:16 |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only say write/open-world/non-idempotent; the description goes far beyond by disclosing the cost model (hero render is the only paid leg, dicing and cards free, music included), the model-tier fallback for 4k, and the credit-spend confirmation requirement. It loses a point because its credit figures (≈870 at 1080p, ≈170 at 480p, ≈2,360 at 4k) conflict with the numbers in the resolution parameter's own schema text (≈1,040 / ≈220 / ≈4,130), leaving the agent with two different prices for the same operation.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with the artifact and its construction, then constraints, then cost, then alternatives — a sensible order with no filler sentences. It runs long, and the spend paragraph partly duplicates pricing already present in the resolution parameter, so it is dense rather than tight.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 10-parameter, expensive, open-world render it covers cost, confirmation, reference image necessity, output length semantics and sibling alternatives. The notable omission is execution behaviour: nothing says whether the call returns a job handle for get_job/resume_job or blocks for the full render, which matters for a 15s paid job. No output schema exists, so the missing return description carries weight.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3 and the description does not need to re-explain parameters. What it adds for `seconds` is genuinely useful (the finished length only repacks cuts and cards; the paid hero is always 15s) and its 'faceless by design / cards carry every word' note explains why specs and cta exist. But its resolution pricing contradicts the schema's own resolution descriptions, which is value subtraction rather than addition.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a precise verb and artifact ('Render an 18-30s music-led PRODUCT SIZZLE'), then details the construction (one 15s hero clip diced into fast cuts, intercut with typeset spec/CTA cards over a brand-coloured grain background, music bed). It also names exactly what this is not — faceless, no voiceover, no spoken lines — so an agent can distinguish it from talking-ad 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.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicit routing: 'For a talking/UGC ad use render_ad or generate_avatar; for a cheap deterministic format use make_template_ad.' It adds a hard prerequisite ('Pass a real packshot as refImage or the label will not be yours') and an operational gate ('Confirm the spend with the user before calling'), which is exactly the when-to-use guidance a paid tool needs.
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 or video onto a Shopify productADestructiveInspect
Attach a finished IMAGE or VIDEO 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 mediaUrl, which is what every Hermoso render returns. Whether it is a video is read from the file (.mp4 / .mov / .webm, or the link's content type); pass kind to say so outright. An image is fetched by Shopify directly; a video is uploaded to Shopify for you (MP4, MOV or WEBM, up to Shopify's 1 GB and 10-minute product-video limits; a file over a limit is refused by name, never cut). Shopify processes media asynchronously: the reply is the status read back from Shopify, and READY means it is live on the listing while PROCESSING means it will appear when Shopify finishes. 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.
| Name | Required | Description | Default |
|---|---|---|---|
| alt | No | alt text for accessibility and SEO; defaults to a generic credit | |
| kind | No | only when the link does not say (no file extension and no content type) | |
| imageUrl | No | older name for mediaUrl; still accepted | |
| mediaUrl | No | any public https image or video URL: a Hermoso render works, and so does ANY file of your own brought in with upload_file | |
| productId | Yes | gid://shopify/Product/… from list_shopify_products |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Goes well beyond annotations by explaining async processing (READY vs PROCESSING, and that PROCESSING is success, not failure), the 1 GB / 10-minute video limits with refusal-by-name rather than truncation, that videos are uploaded on the caller's behalf, and that the call is free. It does not address the annotated destructiveHint=true (e.g. whether prior media is replaced) or idempotentHint=false (whether re-publishing duplicates media), which are the remaining behavioral unknowns.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loads the action and the two key inputs in the first sentence, then layers limits and async semantics. It is dense rather than padded, though the parenthetical about file extensions/content-type and the 'kind' restatement slightly overlap with the schema.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
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 supplies what the reply looks like (Shopify status, READY/PROCESSING) plus failure semantics for oversized files. Combined with a fully documented 5-parameter schema, an agent has everything needed to invoke correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
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 genuine context by naming the gid://shopify/Product/… form, the public-https requirement, the Hermoso-render/upload_file provenance of mediaUrl, and the fallback role of kind. It adds nothing about alt or the legacy imageUrl alias, though the schema already documents both.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a concrete verb+resource: attach a finished IMAGE or VIDEO to a Shopify product listing as product media. It distinguishes itself from nearby siblings by naming list_shopify_products as the source of productId and by clarifying that any file brought in with upload_file also works as mediaUrl.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives explicit prerequisites (account must be created by installing Hermoso from the Shopify App Store) and explicit input routing (productId comes from list_shopify_products in gid:// form; mediaUrl must be a public https URL). It also states when to supply kind ('only when the link does not say'), which heads off ambiguous calls.
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.
| Name | Required | Description | Default |
|---|---|---|---|
| url | No | same as domain | |
| name | No | same as companyName | |
| sort | No | 'longest_running' (default) etc. | |
| brand | No | same as companyName | |
| limit | No | max ads per platform (default 30) | |
| domain | No | the advertiser domain, e.g. liquiddeath.com — a full website URL works (url / website are read as this too). Pass companyName OR domain | |
| company | No | same as companyName | |
| country | No | 2-letter, default 'US' | |
| website | No | same as domain | |
| companyName | No | the advertiser name, e.g. "Liquid Death" (company / brand / name are read as this too) |
TDQS
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.
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.
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.
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.
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.
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.
query_notion_databaseRead a Notion databaseARead-onlyIdempotentInspect
Read the ROWS of a Notion database the user shared with Hermoso — e.g. the team's content calendar: each row's title, id, link and every property as a plain value (status, date, channels, caption, links), plus the database's columns with their types and options. Filter with match {"Status": "Scheduled"} (typed from the database's own columns) or a raw Notion filter object; sort with sortBy (a column, or created_time / last_edited_time) and direction. Up to 100 rows per call, cursor for more; the answer says when Notion stopped at its 10,000-result limit. Use it to import a calendar and schedule its posts, or to find the row update_notion_row changes. Read-only, free. Needs Notion connected (Settings > Connectors > Notion).
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | 1-100, default 50 | |
| match | No | {"Column": value} — equality for status/select/checkbox/number/date, contains for text/multi-select | |
| cursor | No | nextCursor from the previous page of rows | |
| filter | No | a raw Notion filter object | |
| sortBy | No | column name to sort by, or created_time or last_edited_time | |
| archived | No | read archived rows instead | |
| direction | No | ascending (default) or descending | |
| databaseId | No | database id or link (list_notion_pages type:"database") | |
| dataSourceId | No | data source id from an earlier read, as an alternative to databaseId |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare read-only/non-destructive semantics, yet the description still adds real behavior: page size cap of 100 with cursor continuation, the upstream 10,000-result ceiling being surfaced in the answer, and the connectivity prerequisite (Settings > Connectors > Notion). It even notes the call is free, which matters for an agent deciding whether to loop reads.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Purpose and return shape are front-loaded in the first sentence, and the operative constraints (limits, cursor, auth) follow in a logical order. The 'team's content calendar' illustration and the property enumeration are slightly padded, but no sentence is pure filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 9-parameter, nested-object tool with no output schema, the description covers everything an agent needs: what comes back, how to filter and sort, how to paginate, where the upstream cap bites, and what must be connected first. Return values are described inline, so the absence of an output schema is not a gap.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
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 it: it explains that match values are typed from the database's own columns, that sortBy accepts created_time/last_edited_time as well as a column, and that cursor is how you exceed the 100-row page. It leaves the databaseId vs dataSourceId choice and the archived flag untouched.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('Read the ROWS of a Notion database') and immediately defines the return shape — row title, id, link, every property, plus the database's columns with types and options. That distinguishes it from siblings like read_notion_page (a page) and list_notion_pages (enumeration), so an agent can pick correctly 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.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives concrete use cases ('import a calendar and schedule its posts', 'find the row update_notion_row changes'), which ties it to a specific downstream sibling. It does not explicitly exclude the page-reading sibling or state when to prefer list_notion_pages over this, so it stops just 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.
read_docRead a Google DocARead-onlyIdempotentInspect
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.
| Name | Required | Description | Default |
|---|---|---|---|
| docUrl | No | a Google Docs URL to read — the document id is extracted from it | |
| documentId | No | the document id (from create_doc) |
TDQS
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.
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.
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.
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.
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.
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_notion_pageRead a Notion pageARead-onlyIdempotentInspect
Read a Notion page the user shared with Hermoso, as markdown (pageId or a Notion link). A database id answers with its data sources instead. Read-only, free.
| Name | Required | Description | Default |
|---|---|---|---|
| pageId | Yes | page id or Notion link |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint/idempotentHint, so the description focuses on what they don't cover: the return is markdown, the call is free (no credit cost), and a database id returns its data sources rather than page content — a genuinely non-obvious behavior.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two short sentences, zero filler, with the resource and output format front-loaded and the edge case and cost note trailing. Every clause earns its place.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no output schema and only one parameter, the description covers the essentials: what is returned (markdown), the cost (free), and the database-id edge case. Only a pointer to id discovery (list_notion_pages) is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100% and the single parameter already documents 'page id or Notion link', so the parenthetical in the description merely restates the schema. Baseline 3 applies; no extra format or example is added.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb+resource ('Read a Notion page') plus the output form ('as markdown'), which cleanly separates it from list_notion_pages, save_to_notion and append_to_notion_page. The database-id caveat further sharpens the scope.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Usage is implied by the precondition 'the user shared with Hermoso' and by the database-id behavior note, but no sibling is named and there is no explicit when-not-to-use guidance (e.g., use list_notion_pages to discover ids first).
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 rangeARead-onlyIdempotentInspect
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.
| Name | Required | Description | Default |
|---|---|---|---|
| range | No | A1 range, e.g. "A1:D50" (default A1:Z1000) | |
| sheetUrl | No | a Google Sheets URL to read — the spreadsheet id is extracted from it | |
| spreadsheetId | No | the spreadsheet id (from create_sheet) |
TDQS
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.
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.
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.
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.
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.
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
Copy the motion of any video (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 + ONE photo of a person (saved, real or AI), 4-30s -> seedance (Seedance 2.5 through our face library, 720p: the clip's scene, beats and sound kept, the WHOLE person swapped) when the clip is one of our renders or faceRoute is set; otherwise wan (Wan 3.0, scene kept, 3-15s, 720p), past 15s kling-edit (Kling 4.0 Edit, 3-30s, 1080p by asking); several photos (one per person) -> h3 (H3 Max Recast, ≤4 photos, 5-30s); no person in the clip -> seedance. By name: kling-edit; wan = Wan 3.0 (≤15s, keeps the set); wan-prime; h3; kling = Kling Motion Control (ONE picture, its background becomes the set). voice/language: the new performer gets a new voice (same timing) or speaks another language (lip-synced), in the same job and price. Billed per output second, ~5 min per 5s; dryRun quotes.
| Name | Required | Description | Default |
|---|---|---|---|
| tier | No | 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 | |
| image | No | 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. | |
| video | Yes | the reference video whose motion to re-perform | |
| voice | No | a NEW voice for the new performer: 'match' (a stock voice that fits them), 'female'/'male', or Aria, Sarah, Charlotte, George, Brian, Charlie. Omit = the clip's own sound. Never the original speaker's or a cloned voice | |
| dryRun | No | return the credits this exact job would hold and the engine auto picks, without rendering | |
| engine | No | default auto | |
| images | No | seedance/wan: more pictures, @Image2…: a character, product, outfit or place | |
| prompt | No | optional, e.g. 'same moves, new location: Tokyo at night' | |
| language | No | the new voice speaks this language (e.g. 'Spanish'), lip-synced to the new words; voice defaults to 'match' | |
| faceRoute | No | '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. | |
| resolution | No | a person swap defaults to 720p (seedance or kling-edit); otherwise seedance and wan default 1080p; h3 720p or 1080p, default the clip’s own | |
| orientation | No | kling only: which aspect to keep: the video's (default) or the image's |
TDQS
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 write, and the description adds substantially beyond that: per-output-second billing, ~5 min per 5s, dryRun quoting, consent/licensing obligations for real faces and voices, and the rule that a new voice is never the original speaker's or a clone. It leaves the returned job/asset shape and how to poll for completion unstated.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The opening sentence is front-loaded and useful, but the body collapses into a semicolon- and parenthetical-heavy run-on covering engines, durations, resolutions and defaults in one block. Almost every clause carries information, yet the density makes it hard to scan; breaking the engine-selection rules into a list would cost nothing and gain a lot.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 12-parameter generative tool with no output schema, the description covers engine selection, billing, consent and voice rules well. It never says what the call returns (job handle, asset URL) or that completion is checked via get_job/resume_job, which is the main gap for an agent wiring up a long-running render.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
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 genuinely enriches the enums: it maps engine values to concrete models and limits (wan = Wan 3.0 ≤15s keeps the set; kling = Kling Motion Control, ONE picture whose background becomes the set) and explains what image/images mean in the prompt (@Image1, @Image2…) and how resolution defaults shift between engines. That is meaning the schema text alone does not carry.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource up front: 'Copy the motion of any video (motion transfer): re-perform a clip's motion, camera and timing with YOUR character.' That clearly separates it from generate_video, restyle_video or clone_video, which do not re-perform existing motion. An agent can pick this tool 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.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives rich conditional guidance for engine choice (person clip + one photo -> seedance; several photos -> h3; no person -> seedance; past 15s -> kling-edit) and states the dryRun quoting path. It never compares against sibling tools such as restyle_video or make_motion_film, so the exclusion side is missing, but the internal usage context is unusually explicit.
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): the whole original frame is kept and the new canvas around it is generated, with the sound and timing unchanged. Clips up to 300 seconds. Paid render; returns the served URL of the reframed video. Every placement at once (a free blur-pad by default): repurpose_video. A presenter on any video: add_presenter.
| Name | Required | Description | Default |
|---|---|---|---|
| video | Yes | the source video URL | |
| aspectRatio | Yes | the target aspect ratio |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnly=false, destructive=false, openWorld=true, and idempotent=false, so the description need only add context — which it does: it discloses that the original frame is preserved and the canvas around it is generated, audio/timing are unchanged, renders are paid, and a served URL is returned. The only gap is that it never explicitly warns that re-running is not idempotent and costs credits again, even though idempotentHint=false flags it.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Very compact and front-loaded: capability, then preservation guarantees, then duration limit, then cost/return, then sibling routing. The telegraphic fragments ("Every placement at once (a free blur-pad by default): repurpose_video.") are dense but each clause carries distinct information.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a two-parameter paid render tool with no output schema, the description covers the key facts an agent needs: what changes, what stays the same, the duration ceiling, the cost, and the return value (served URL). Nothing essential is missing, though the credit cost per call is not quantified.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
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, including the full aspect-ratio enum, so the description only adds the illustrative 16:9 -> 9:16 mapping. That is marginal value beyond what the schema already provides, which matches the baseline 3.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ("Reframe a video to a different aspect ratio") plus the example 16:9 master -> 9:16 vertical. It also distinguishes itself from the two nearest siblings by naming them and their distinct jobs (repurpose_video for every placement, add_presenter for presenters).
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly routes the agent to alternatives: repurpose_video when every placement is needed at once (free blur-pad by default), add_presenter for a presenter on video. It also implies a hard constraint (clips up to 300 seconds) that gates use, though it never states an explicit when-not-to-use beyond the duration limit.
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.
| Name | Required | Description | Default |
|---|---|---|---|
| text | Yes | the fact/preference, concise | |
| brand | No | profile id/name from list_brands, this call only | |
| category | No | short bucket: Brand, Audience, Taste, Do, Don’t, or Preference (default General) |
TDQS
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.
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.
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.
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.
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.
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.
| Name | Required | Description | Default |
|---|---|---|---|
| brandId | No | a profile id/name from list_brands to clone for; omit to use the active profile | |
| imageUrl | Yes | 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. |
TDQS
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.
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.
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.
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.
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.
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 teammateADestructiveIdempotentInspect
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.
| Name | Required | Description | Default |
|---|---|---|---|
| Yes | the member’s email | ||
| confirm | No | REQUIRED true |
TDQS
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.
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.
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.
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.
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.
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
Use this when the user asks for a video ad, Reel, TikTok ad, UGC video or commercial for their business or product. It renders a finished ad on a real video model, on their brand (real product photos, brand voice), as an mp4 ready to post: timed shots, clean speech, text set in post, no music, captions or end card unless asked. Pass plan_ad’s full output as creative (plan_ad turns a one-line ask into the concept). UGC and cinematic plans render from a painted STORYBOARD (a cinematic spot paints a location anchor first, and a hero when someone is on camera); a product-only spot renders from the real product photo, and board:true or foundations:'choose' adds an identity sheet, a moodboard and a board. A plan that fits one clip of the render model renders as one continuous pass; a longer one as STITCHED ACTS (the fewest balanced clips), never time-compressed. Most models cap a clip at 15s and the longest at 30s: dryRun:true shows the act split free, before spending. CAST A SAVED CREATOR with creator (list_creators) so the same person stars in every ad; otherwise each render casts a new face. The user’s own pictures (creative.inserts, or inserts) are laid in full screen at their seconds. An app shown on a device screen with no real screens to lay there returns needsAppScreens, nothing charged: ask, then pass screenChoice or screens. 1–3 min; poll get_job while it renders. Spends credits.
| Name | Required | Description | Default |
|---|---|---|---|
| board | No | 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 | |
| model | No | video model id from hermoso_capabilities (default: the engine the router picks for the plan: seedance-2.5 for every ad, a person talking to camera included). Naming one is a DELIBERATE pick — the server asks before ever swapping it (no silent fallback) | |
| music | No | music bed: OFF unless asked. true = the plan's own music line; or the bed in words ('lo-fi jazz, brushed drums'); false = none | |
| dryRun | No | return the routing decision (single pass vs stitched acts, resolved model + act lengths) and the estimated credits the real render reserves, WITHOUT submitting a render — free, nothing charged | |
| lockup | No | brand wordmark + tagline composited over the closing seconds. DEFAULT FALSE — set true ONLY when the user asks for branding on the close | |
| creator | No | 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 (casting skips the portrait we would otherwise generate). 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. | |
| endCard | No | 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) | |
| inserts | No | the user's own screens laid over the finished video at these seconds (each item's look says how), sound running underneath (about 2 credits). The film always opens and ends on moving footage. Omit to use plan_ad's creative.inserts; [] renders without them. plan_ad with inserts writes the board around them. | |
| screens | No | real screenshots of the app or site for the shots that film it on a screen (URLs, upload_file URLs or Library ids), laid over them as cards | |
| captions | No | 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 | |
| creative | Yes | the FULL structured output of plan_ad (must contain video_storyboard) | |
| ttsVoice | No | voiceover voice name (e.g. Rachel / George) when the plan voices over | |
| faceRoute | No | 'face_lane' records the user's statement "I own the rights to this face" for the real person in their reference (paid plans; the likeness terms apply). Set it only when the user picked that option after a render said it could not use that face, and send the same request again; it then renders through Hermoso's face library at the normal price. Never set it on your own. The other options are a different `model` or one of the user's saved `creator`s. | |
| textStyle | No | 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. | |
| musicModel | No | compose the bed on minimax-music-3 or lyria-3.5, skipping the free library | |
| resolution | No | Omit = the default tier, hermoso_capabilities defaultResolution (720p), the tier its `credits` prices are for. 1080p is ~2.25x, 4k more, 480p less: quote creditsByRes and pass the tier you quoted. A tier a model's `resolutions` lacks renders at its best one. | |
| aspectRatio | No | output aspect ratio, e.g. 9:16 (default) / 1:1 / 16:9 | |
| foundations | No | 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 | |
| screenChoice | No | the user's answer to needsAppScreens: brand = their saved app screens, site = found on their website (saved to the brand), without = none, device kept turned away | |
| moodboardPick | No | which moodboard panel (1-4, left to right, top to bottom) the storyboard follows; default 1 | |
| durationSeconds | No | total ad length in seconds, 4–180 (clamped). Omit to keep the plan’s own length, almost always right: this only RE-TIMES the authored board; to change the ad’s length, re-run plan_ad with durationSeconds. A length that fits ONE clip renders in one pass; longer is stitched from acts of the model’s clip maximum (15s on most, 30s on the longest-clip one), remainder last. dryRun:true shows the split free. | |
| foundationImages | No | images a foundations:'choose' call returned, reused as-is (not repainted) | |
| allowGenericProduct | No | proceed 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
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only cover the safety profile (not read-only, not idempotent, non-destructive), and the description adds substantial unsurfaced behavior: credits are spent and reserved, the render takes 1-3 minutes and requires polling get_job, dryRun is free, a missing app screen returns needsAppScreens with nothing charged, and named-model swaps require server confirmation rather than silent fallback. It also discloses consent/takedown obligations for real faces through the creator and faceRoute rules.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Usage is front-loaded in the first sentence and the dense material (act stitching, model clip caps, casting, inserts, needsAppScreens) is ordered by the order an agent would encounter it. It is a long single block with heavy parenthetical nesting, but with 23 parameters and a multi-stage pipeline nearly every clause carries operational information rather than filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
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 describing the return paths an agent must handle: dryRun's routing/credits preview, needsAppScreens, the ~2 credit cost of inserts, and the need to poll get_job during a 1-3 minute render. What is not stated is what an accepted render call returns immediately (e.g. a job id to poll), which is the one gap for a long-running, credit-spending tool.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the schema already carries per-field meaning and the baseline would be 3. The description goes beyond it by explaining cross-parameter workflow: `creative` must be plan_ad's FULL output, board/foundations interact with the storyboard, creator casting persists a face across acts and ads, and dryRun previews the act split. It does not re-explain every one of the 23 fields, which is fine given the schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The opening sentence uses a specific verb plus resource ('renders a finished ad on a real video model'), enumerates exactly the request types it serves (video ad, Reel, TikTok ad, UGC, commercial), and positions itself downstream of plan_ad's concept output, which differentiates it from siblings like plan_ad, generate_video, product_sizzle and restyle_video.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It states the trigger ('Use this when the user asks for...'), the prerequisite (pass plan_ad's full output as `creative`), and negative guidance that would otherwise be botched: no music, captions or end card unless asked, dryRun first to see the act split before spending, and the needsAppScreens branch to ask the user rather than guess. Alternatives and the routing decision between one-clip and stitched-act renders are both spelled out.
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.
| Name | Required | Description | Default |
|---|---|---|---|
| details | Yes | what you were doing, the tool + arguments you called, what you expected, and what actually happened (paste the exact error) | |
| summary | Yes | one-line summary of the bug | |
| severity | No | high = blocks the task or loses paid work; medium = wrong output but workable; low = cosmetic |
TDQS
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.
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.
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.
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.
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.
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.
repurpose_videoRepurpose a video for every placementAInspect
ONE video in, EVERY placement out, in one job, as a named file list ([_].mp4) with a manifest. Placements: 'everywhere' (default: 9:16, 4:5, 1:1, 16:9), any W:H, a pixel size (1200x628) or a platform word (tiktok, reels, stories, feed, square, youtube, linkedin, pinterest). Each is reframed by a BLUR-PAD by default (the whole frame over a blurred copy of itself; free, no AI), reframe 'crop' (fill, edges cut; free) or 'ai' (the new canvas generated around the frame; paid, for a canvas the engine makes, else padded and said). A placement may be {placement, reframe} to mix. Optional captions (word-timed, ~4 words a cue, placed for each canvas) and languages (a lip-synced dub per language; every placement of each). A pack of pads and crops only is billed 0. dryRun:true = the exact price (the sum of every leg's own price), which is what it charges. For ONE canvas use post_edit op pad.
| Name | Required | Description | Default |
|---|---|---|---|
| pad | No | the pad's look: fill 'blur' (default) or a #rrggbb colour, blur 0-1, dim 0-0.6, position top/center/bottom or 0-1 | |
| name | No | the asset name the files are named after | |
| video | Yes | the video: an https / upload_file URL, a Library id (L0…), or a local path on the CLI | |
| dryRun | No | price only: nothing made, nothing charged | |
| reframe | No | default 'pad' (free); 'ai' is paid and only when the user asks | |
| captions | No | true = word-timed captions on every file (only when the user asks); a textStyle preset name or object sets the look | |
| languages | No | dub into these languages (paid), e.g. ['Spanish','de']; the original is kept too | |
| placements | No | default ['everywhere']; a canvas, a WxH size, a platform word, or {placement, reframe} | |
| wordsPerCue | No | captions: 1-8 words a cue (default 4) |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations cover safety (readOnlyHint=false, openWorldHint=true) but say nothing about cost or side effects; the description carries that load, stating that pad/crop legs are free, 'ai' reframes and dubs are paid, a pads-and-crops-only pack bills 0, and dryRun returns the exact price that would be charged. It also discloses the fallback behavior when the AI canvas is unavailable (padded and said). The one gap is what the job returns and whether it must be polled via get_job.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Information is front-loaded: the one-in/many-out framing and the output filename convention come before the option inventory, and every clause carries a fact (defaults, price, fallbacks, alternatives). It is dense and parenthetical to the point of being a wall of text on a single run-on sentence, which costs it a point but not more.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 9-parameter, nested-object, async-looking generation tool with no output schema, the definition explains inputs and pricing well but never says what comes back (job id? manifest shape? where files land) or whether the caller must poll get_job/resume_job. Those omissions matter for correct invocation sequencing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so baseline would be 3, but the description adds real semantics: the 'everywhere' default expands to 9:16/4:5/1:1/16:9, platform words are enumerated (tiktok, reels, stories, feed, square, youtube, linkedin, pinterest), and the {placement, reframe} mix form is explained. It does not mention the maxItems caps (8 placements, 4 languages) or the object's label/width/height fields, so it stops short of full coverage.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a precise verb and resource with its scope: 'ONE video in, EVERY placement out, in one job' with a named output convention (<name>_<placement>_<WxH>[_<lang>].mp4) plus a manifest. It enumerates accepted placement forms (canvas, W:H, pixel size, platform word) and names the sibling that handles the single-canvas case (post_edit op pad), so it is separable from reframe_video, resize_ad and post_edit.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives clear routing: use this for many placements at once, use post_edit op pad for ONE canvas. It also sets conditional-use rules for the paid paths ('ai' only when the user asks, captions only when the user asks) and points to dryRun:true for pricing. What it lacks is any explicit comparison against close siblings like reframe_video, resize_ad or localize_ad/dub_video.
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.
| Name | Required | Description | Default |
|---|---|---|---|
| details | Yes | what the user was actually trying to achieve, why the current tools couldn't do it, and what you'd expect the capability to do | |
| summary | Yes | one line: the capability you need |
TDQS
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.
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.
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.
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.
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.
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 postADestructiveIdempotentInspect
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.
| Name | Required | Description | Default |
|---|---|---|---|
| at | No | the new time: local wall clock (2026-10-30T10:00, read in `timezone` or the brand’s zone on that date), UTC (…Z) or epoch ms. Future, at most 365 days out. | |
| id | Yes | the scheduled post id from list_scheduled | |
| link | No | destination URL for a Pinterest Pin, Facebook text post or Google Business post | |
| poll | No | X — replaces the poll; an empty options list removes it. | |
| tags | No | YOUTUBE — replaces the WHOLE tag list; an empty array [] clears the tags. | |
| brand | No | 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. | |
| event | No | GOOGLE BUSINESS — replaces the whole event record {title, startDate, startTime, endDate, endTime}. | |
| offer | No | GOOGLE BUSINESS — replaces the whole offer record {couponCode, redeemOnlineUrl, termsConditions}. | |
| place | No | FACEBOOK — replaces the tagged place (the numeric id of its Facebook Page); an empty string removes it. | |
| story | No | INSTAGRAM — true makes it a 24-hour Story, false an ordinary feed post. One image or one video, no carousel. | |
| title | No | PINTEREST / YOUTUBE — replace the headline; "" clears it and goes back to deriving one from the caption | |
| chatId | No | TELEGRAM — 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. | |
| ideaId | No | short id of the content-plan idea this post came from | |
| pageId | No | FACEBOOK / INSTAGRAM / THREADS — publish from a different connected Page (list_meta_pages) | |
| recipe | No | the post's FORMAT id, e.g. "slideshow" or "imessage_chat" — post_performance groups by it, so reuse one id per format | |
| silent | No | TELEGRAM / DISCORD — no notification. | |
| thread | No | X — replaces the WHOLE thread; an explicit [] drops back to a single post using the caption. | |
| wpSlug | No | WORDPRESS — the post slug | |
| wpTags | No | WORDPRESS — tags (names or ids) | |
| altText | No | ACCESSIBILITY — 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. | |
| audioId | No | 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. | |
| boardId | No | PINTEREST — move the Pin to a different board (list_pinterest_boards) | |
| buttons | No | TELEGRAM ONLY — replace the URL buttons ([] removes them). | |
| excerpt | No | WORDPRESS — the excerpt under the title | |
| message | No | replace the caption used for every channel that has no override | |
| audience | No | FACEBOOK — replaces who can see the Page post {countries, regions, cities, minAge}; {} removes the limit. | |
| captions | No | replaces the WHOLE per-channel caption map — send every override you want to keep, not just the new one | |
| channels | No | replaces the channel list | |
| coverUrl | No | INSTAGRAM REEL — replaces the cover image url; an empty string removes it. | |
| imageUrl | No | swap the image; "" removes it | |
| linkName | No | FACEBOOK — replaces the link preview headline; an empty string removes the override. | |
| timezone | No | IANA zone for a wall-clock `at` | |
| topicTag | No | THREADS ONLY — one topic tag, without the leading #. | |
| userTags | No | INSTAGRAM USER TAGS — tag people (public accounts) on the post; each is notified. Usernames, or { username, x, y } with x/y from 0 to 1 = where the tag sits on the IMAGE (a tag without a position sits at the centre). A Reel tag takes no position; a story position is optional. Not on a carousel (refused by name). | |
| videoUrl | No | swap the video; "" removes it | |
| whopPoll | No | WHOP: poll options, 2 to 10 | |
| xArticle | No | X: replaces the X Article (title, headings); {} makes it an ordinary X post again. | |
| audioName | No | INSTAGRAM REEL — replaces the audio track name; an empty string removes it. | |
| coverAtMs | No | THE VIDEO COVER on every channel, as one frame in milliseconds (see schedule_post). | |
| imageUrls | No | replace 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. | |
| parseMode | No | TELEGRAM ONLY — 'HTML' for Telegram's HTML tags. | |
| placeName | No | the name the user saw for that place; a different business is refused | |
| slideText | No | PINTEREST CAROUSEL ONLY — per-slide title / description / link, aligned to slide order. | |
| topicType | No | GOOGLE BUSINESS — the kind of Post; EVENT and OFFER both require `event`. | |
| trialReel | No | INSTAGRAM — 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. | |
| yourBrand | No | TIKTOK — the own-brand disclosure; false turns it off. | |
| actionType | No | GOOGLE BUSINESS — the call-to-action button; "" clears it. | |
| locationId | No | GOOGLE BUSINESS — a different listing (list_business_locations) | |
| madeWithAi | No | X — the AI-media label; false turns it off. | |
| threadName | No | DISCORD: forum thread title | |
| threadsGif | No | THREADS ONLY — a GIPHY GIF id (the id in a giphy.com/gifs/… link) attached to a TEXT Threads post. GIPHY is the only provider Threads takes. | |
| visibility | No | NOTE: changing this without also naming visibilityByChannel clears any per-channel overrides, so "make it all draft" is not a no-op | |
| whopPinned | No | WHOP: pin the forum post | |
| aiGenerated | No | AI-CONTENT DISCLOSURE (Instagram / Facebook Reel is_ai_generated, TikTok is_aigc, YouTube containsSyntheticMedia, Pinterest ai_disclosures, Threads is_made_with_ai). 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. | |
| audioVolume | No | INSTAGRAM REEL — how loud that track plays, 0–100 (Instagram’s default 100). Needs audioId. | |
| communityId | No | X — the community to publish into; an empty string goes back to the main timeline. | |
| description | No | YOUTUBE — replace the video description; "" clears it and the caption is used. | |
| disableDuet | No | TIKTOK VIDEO ONLY — block Duets. | |
| linkPicture | No | FACEBOOK — replaces the link preview image url; an empty string removes the override. | |
| quotePostId | No | THREADS ONLY — the id of the Threads post this one quotes. | |
| shareToFeed | No | INSTAGRAM REEL — whether the Reel also shows in the Feed grid. | |
| thumbOffset | No | INSTAGRAM REEL — replaces the cover frame, in milliseconds; 0 removes it. Never together with coverUrl. | |
| videoVolume | No | 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. | |
| whopMention | No | WHOP: notify every member | |
| brandPartner | No | YOUTUBE: sponsoring brand channel (UC… or @handle); "" removes it | |
| callToAction | No | FACEBOOK — replaces the button on the Page post; "" removes it. | |
| countryCodes | No | THREADS ONLY — two-letter country codes limiting who can see the post. | |
| firstComment | No | FIRST COMMENT — posted on each new post as the same account right after it publishes, on Facebook, Instagram and LinkedIn company Pages (LinkedIn needs its comments authorization, provider linkedin_community). Other channels publish without it and their result row says so. Up to 2,000 characters, refused rather than cut; never on a story. A failed comment never undoes the post. | |
| optimizeCopy | No | 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. | |
| privacyLevel | No | 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. | |
| replyControl | No | THREADS ONLY — who may reply. | |
| slackChannel | No | SLACK app: channel id or #name (default: the brand's) | |
| thumbnailUrl | No | YOUTUBE: replace the custom thumbnail; "" goes back to a frame of the video, "auto" to YouTube’s pick. | |
| wpCategories | No | WORDPRESS — categories (names or ids) | |
| xQuotePostId | No | X — the post this one QUOTES; an empty string removes the quote. Named apart from the Threads `quotePostId` on this same schedule. | |
| collaborators | No | INSTAGRAM — 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. | |
| coverImageUrl | No | THE VIDEO COVER on every channel, as a Hermoso-hosted picture (see schedule_post). | |
| disableStitch | No | TIKTOK VIDEO ONLY — block Stitches. | |
| discordServer | No | DISCORD bot: which server | |
| instagramPoll | No | 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. | |
| platformCover | No | 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. | |
| replySettings | No | X — who may reply; "" goes back to everyone. | |
| slackThreadTs | No | SLACK app: reply in this thread (parent ts) | |
| brandedContent | No | TIKTOK — the paid-partnership disclosure; false turns it off. | |
| disableComment | No | TIKTOK — comments off on this post. | |
| disablePreview | No | TELEGRAM ONLY — no link preview. | |
| discordChannel | No | DISCORD bot: channel id or #name (default: the brand's) | |
| discordReplyTo | No | DISCORD bot: reply to this message id | |
| linkAttachment | No | THREADS 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. | |
| slackWorkspace | No | SLACK app: which workspace | |
| targetAudience | No | LINKEDIN COMPANY PAGE — replaces who sees the post; {} removes the limit. The matching audience must be over 300 followers. | |
| whopVisibility | No | WHOP: who can see the forum post | |
| discordMentions | No | DISCORD: let mentions ping | |
| linkDescription | No | FACEBOOK — replaces the link preview description; an empty string removes the override. | |
| paidPartnership | No | INSTAGRAM AND X — the paid-partnership label; false turns it off. | |
| threadsLongText | No | THREADS ONLY — LONG-FORM text, up to 10,000 characters, attached under a TEXT-only Threads post (Threads' text attachment). The way to publish more than Threads' 500-character post: keep a short lead in the message and put the full text here. Not with an image, video or carousel. | |
| callToActionLink | No | FACEBOOK — replaces where the button goes; an empty string falls back to the post link. | |
| coverTimestampMs | No | TIKTOK VIDEO ONLY — cover frame in milliseconds. | |
| crossreshareToIg | No | THREADS ONLY — also share to Instagram as a Story when it fires; false turns it off. Refused on a carousel. | |
| threadsGhostPost | No | THREADS ONLY — a GHOST post: text only, Threads archives it automatically after 24 hours (a flash offer, a 24h-only note). No other Threads option can ride it. | |
| whopExperienceId | No | WHOP — move it to a different forum or chat (exp_…, from list_whop_destinations). | |
| whopPaywallCents | No | WHOP: forum-post paywall in cents (needs a title) | |
| commercialContent | No | TIKTOK — 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. | |
| discordStartThread | No | DISCORD bot: open a thread under the post, this title | |
| instagramLocationId | No | 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. | |
| visibilityByChannel | No | per-channel visibility override, e.g. {tiktok: draft}; values public, unlisted, private or draft | |
| whopPaywallCurrency | No | WHOP: paywall currency | |
| crossreshareDarkMode | No | THREADS ONLY — dark-mode that Instagram Story. Needs crossreshareToIg. | |
| paidProductPlacement | No | YOUTUBE: paid product placement (shows the disclosure) — false removes it | |
| instagramPollExtended | No | 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. | |
| instagramCommentPrompt | No | INSTAGRAM — make the caption a COMMENT PROMPT that people answer in the comments. Same limits as instagramPoll, and never together with it. | |
| linkedinOrganizationId | No | LINKEDIN — target a different company Page, or "" to post as the connected person instead | |
| brandedContentSponsorIds | No | INSTAGRAM — replaces the sponsor user ids behind the paid-partnership label (at most 2); [] removes them. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare destructiveHint=true, idempotentHint=true and openWorldHint=true, so the mutation profile is covered structurally. The description still adds real behavioral value: omitted fields are preserved, an explicit empty string clears a field (with the linkedinOrganizationId example), and every edit is re-validated against the same per-channel rules a create passed. It does not cover failure/partial-success behavior or what a successful edit returns.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded: the mutation scope lands in the first clause, the patch contract immediately after, and the sibling routing rule last. Dense and largely waste-free, though the third sentence about re-checking visibility/length/media is somewhat long and could be tightened without losing meaning.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 113-parameter mutation tool with no output schema, the description supplies the essentials: scope, patch semantics, validation guarantee, and the handoff to sibling tools for non-queued posts. What is missing is modest — no note on the response shape or on what happens when an edit is partially rejected.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
With 113 parameters at 100% schema coverage, the schema already carries per-field meaning, so the baseline would be 3. The description contributes the cross-cutting PATCH semantics that no individual field description conveys — omit means unchanged, "" means clear, and edits cannot bypass create-time refusals — which is the single most important thing to know before populating this enormous payload.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a precise verb and resource ('Change a post that is still QUEUED') and then enumerates the concrete mutations available — retime, caption, media, channels, board/Page/listing. It distinguishes the tool from the published-post siblings by name (manage_meta_post / manage_linkedin_post / delete_x_post), so an agent can route correctly 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.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives an explicit precondition (post must still be QUEUED), an explicit anti-case (something already published is edited/removed with the named sibling tools instead), and the acquisition path for the required id ('Get the id from list_scheduled'). The 'PASS ONLY WHAT CHANGES' rule is spelled out as the operating contract, 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.
research_adsResearch adsAInspect
Use this when the user asks an open-ended ad question across several brands or platforms, such as comparisons, which angles are working in a market, or ad examples for a category. Do not use for one named brand’s current ads: pull_competitor_ads covers that. 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.
| Name | Required | Description | Default |
|---|---|---|---|
| brand | No | brand name or profile object to tailor the research to; omit to use the workspace’s saved brand | |
| query | Yes | what to research, e.g. "the longest-running protein-pancake ads on Meta" |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Goes well beyond the annotations: discloses that it runs an agentic loop of several library pulls plus a written synthesis, takes 30-60 seconds, and spends a handful of credits rather than a single-call cost. This aligns with readOnlyHint=false and openWorldHint=true and adds the operational detail an agent needs to decide.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded and useful, but repetitive: the 'one named brand → pull_competitor_ads' rule appears twice, and 'agentic loop' / credit cost is restated three times. Trimming the duplication would lose nothing.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
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 saying the output is a written synthesis from multiple pulls, along with latency and cost. For a two-parameter, expensive, long-running research tool, an agent has everything needed to select and invoke it correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so brand and query are already documented. The description adds value by characterizing the kind of query that fits (open-ended, non-self-answering) and by warning that a question naming its own answer is wrong for this tool, which sharpens interpretation of the required query parameter.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb+resource (agentic ad research across brands/platforms) and defines its scope by contrast: comparisons, market-level angle questions, category examples. It explicitly names pull_competitor_ads and search_meta_ads as the alternatives for narrower questions, so an agent can route 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.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives both when-to-use (open-ended, multi-source, judgment-requiring questions) and when-not-to-use (one named brand's current ads → pull_competitor_ads; one keyword/advertiser on Meta → search_meta_ads), plus the cost/latency discriminator that rules it out for pinpoint questions.
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. Several sizes AND versions (headlines, languages, offers) at once: repurpose_image. For VIDEO use reframe_video.
| Name | Required | Description | Default |
|---|---|---|---|
| image | Yes | the finished static ad: URL, Library item URL, upload_file URL or local path | |
| fixLabel | No | 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) | |
| aspectRatios | No | target canvases (default 1:1, 4:5, 9:16) |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations cover the mutation profile (readOnlyHint=false, destructiveHint=false, idempotentHint=false), and the description adds genuinely beyond them: a 3-credit text read first, one charged image edit per canvas, a label check that is charged even when it passes, an extra charge for product lookup, and the promise that it is 'Priced before it runs.' It does not say whether the call is synchronous, returns a job id, or how the resulting assets are returned, which is the main behavioral gap.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
It is front-loaded with purpose and ends with sibling routing, and nearly every sentence carries actionable information. However, it is a single dense block with stacked parentheticals (ratio lists, credit asides, label-check caveats) that could be broken into short lines for easier scanning without losing content.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a three-parameter, non-idempotent generation tool with no output schema, the pricing model, per-canvas behavior, and label-fix pipeline are all disclosed. The remaining gap is the return contract — whether the result is a job handle (get_job exists as a sibling) or inline images — which an agent would still need to discover.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
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 it: the ad's own aspect ratio is skipped, and fixLabel:false disables product lookup, label check, and re-print rather than merely toggling a flag. The allowed ratio list and defaults are repeated from the enum/schema, which is why this is not a 5.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The opening clause names a specific verb and resource ('Re-lay out ONE finished static ad for other placements') and immediately defines the semantics as native recomposition rather than cropping. It explicitly distinguishes itself from repurpose_image (multi-size plus versions) and reframe_video (video), so an agent can route without opening sibling schemas.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description states when to use this tool (one finished ad, other placements) and names the two alternatives with the exact conditions that select them: repurpose_image for 'Several sizes AND versions (headlines, languages, offers) at once' and reframe_video for video. It also gives an opt-out condition (fixLabel:false) tied to a specific scenario.
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 4.0 Edit, 3-15s); none -> wan (Wan 3.0, recommended, ≤15s) else seedance (Seedance 2.5, 4-30s); wan-prime = Wan 3.0 Prime by name (costs more). Paid; dryRun quotes it. One change only: edit_video.
| Name | Required | Description | Default |
|---|---|---|---|
| style | No | a look id from hermoso_capabilities (e.g. claymation, knitted-yarn, cel-shaded-anime-cg) | |
| video | Yes | the source video URL (a render, job result or list_library) | |
| engine | No | default auto | |
| describe | No | a look in the user’s own words, when no preset fits, or extra detail on top of a preset | |
| faceRoute | No | '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. | |
| characters | No | 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. | |
| resolution | No | Seedance and Wan; default 1080p | |
| styleImages | No | pictures that ARE the look (their style, never their subject) |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare non-readOnly, openWorld, non-idempotent, non-destructive, but the description adds substantial behavioral context: paid operation with dryRun quoting, per-engine duration caps and cost asymmetry (wan-prime costs more), faceRoute requiring the user's explicit say-so, and characters being paid-plan-only and 'recorded' with a consent/liability term. Minor gap: it references a dryRun action that has no corresponding schema parameter, and it doesn't say whether the call is async/returns a job.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with the core transformation before branching into looks, engine, and cost. Very dense but each clause carries a fact; the semicolon-chained engine rule is one long sentence that could be broken for scanability, and the 'dryRun' reference adds an unresolved action.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For an 8-parameter paid generation tool with no output schema, the description covers look sourcing, engine selection, cost, consent and per-engine duration limits. It stops short of describing what comes back (job handle vs finished asset) and whether polling via get_job/list_jobs is needed, which matters for an async paid render.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
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 genuinely differentiates the three look inputs (preset id vs describe vs styleImages, noting styleImages carry 'their style, never their subject') and gives the engine auto decision tree with duration ranges. It also constrains faceRoute to user-initiated use and notes resolution applies to Seedance and Wan, which the schema does not state.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a concrete verb+resource ('RESTYLE a clip into a new LOOK') and immediately defines the transformation boundary: every frame is redrawn while shots, motion, camera, framing, timing and sound are preserved. It also names a sibling ('One change only: edit_video') so the agent can distinguish it from edit_video 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.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicit routing: looks come from `Restyle looks` in hermoso_capabilities, from the user's own words (describe), or from styleImages; engine auto resolves by clip content with the rule stated ('real-looking person -> kling; none -> wan; else seedance'). It also states the exclusion case ('One change only: edit_video') and the paid/quote path.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
resume_jobResume a stopped filmAInspect
Finish a video-blocks explainer or faceless-channel film (make_explainer) that STOPPED partway: a block the video model would not render, or a server restart. What it already made is kept (the script, the style key, the narration and every block that rendered), so this renders ONLY the missing blocks and re-assembles the same film, with the same topic, look, voice, length and options. It reserves credits only for what is missing and never charges again for what was kept. get_job and list_jobs show which jobs can be resumed and which blocks are kept. Quote it with dryRun first and tell the user the number. A finished film is not resumed: to redo one block of a finished film, use fix_beat with its job id.
| Name | Required | Description | Default |
|---|---|---|---|
| jobId | Yes | the id of the film that stopped (job_...), as get_job or list_jobs shows it | |
| dryRun | No | true = return the credits the resume reserves and render nothing |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Beyond the annotations, which only declare this is a non-read-only, non-destructive, non-idempotent open-world operation, the description explains that completed work is preserved and not re-rendered, credits are reserved only for missing blocks, kept work is never charged again, and the resulting film keeps the same topic, look, voice, length, and options.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The definition is front-loaded with the core action and condition, then covers preservation, credits, verification, and the alternative. It is dense but not bloated; the repeated emphasis on not charging again for kept work is slightly redundant but reinforces an important cost constraint.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a two-parameter mutation with no output schema, the description covers the stopping condition, preserved state, what gets re-rendered, credit behavior, dryRun preflight, discovery via get_job/list_jobs, and the correct alternative for finished films. Nothing material is missing for correct invocation.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
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. The description adds meaningful procedural semantics by framing dryRun as a cost-estimate step that should be run first and communicated to the user, and by clarifying that jobId refers to a film that stopped, while the schema alone mostly documents the fields.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description uses a specific verb (resume) and resource (a stopped film) and defines the exact situation: a video-blocks explainer or faceless-channel film made by make_explainer that stopped partway. It explicitly distinguishes the tool from fix_beat for finished films, so an agent can select it without inspecting siblings.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It states precisely when to use it (a job stopped because a block would not render or after a server restart), how to identify candidates (get_job and list_jobs), a required preflight step (dryRun), and an explicit exclusion routing finished films to fix_beat instead.
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 postADestructiveInspect
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.
| Name | Required | Description | Default |
|---|---|---|---|
| at | No | hold 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. | |
| id | Yes | the scheduled post id from list_scheduled | |
| brand | No | 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. | |
| chatId | No | CORRECT 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. | |
| pageId | No | CORRECT THE PAGE on retry — which connected Facebook Page (and its linked Instagram/Threads) publishes, from list_meta_pages. | |
| boardId | No | CORRECT 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. | |
| message | No | CORRECT 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. | |
| captions | No | CORRECT ONE CHANNEL’S CAPTION on retry, e.g. { "x": "..." } when only that channel refused the text. | |
| channels | No | retry only these channels (default: every channel that did not publish) | |
| locationId | No | CORRECT THE LISTING on retry — which Google Business Profile location, e.g. "locations/123" from list_business_locations. | |
| allowDuplicate | No | ONLY 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. | |
| whopExperienceId | No | WHOP — correct the forum or chat on retry (exp_…, from list_whop_destinations). | |
| linkedinOrganizationId | No | CORRECT 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
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only flag destructive/openWorld/non-idempotent; the description adds the crucial mechanics behind those flags — partial fan-out failures, re-queuing as a NEW post that publishes within seconds, the original retaining its failure record, refusal to re-post an already-published channel, and REPLAY for channels that genuinely posted. That is exactly the behavioural context the 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.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with the core action and the partial-failure rationale before the safeguards and correction options. It is long and heavy on capitalisation for emphasis, but nearly every clause carries distinct information; a slightly tighter phrasing of the double-post paragraph would help.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 13-parameter, nested-object mutation with no output schema, the description covers outcomes (new post, immediate dispatch, original history preserved), failure modes (already-published refused, unresolved timeout refused), and the escape hatch (allowDuplicate). Nothing an agent needs to call it safely appears missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
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 cross-parameter semantics the schema cannot: what is not named is copied from the original, corrections are re-validated like a fresh schedule, and the retry targets only non-succeeding channels by default. It does not add much about the `at` or `brand` parameters beyond the schema text.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
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 what is retried ('ONLY the channels that did not succeed'). It explicitly contrasts with the sibling duplicate_scheduled, so an agent can pick between them 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.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives when-to-use (after fixing the cause of a failure), when-not-to-use ('to send the same thing again ON PURPOSE, use duplicate_scheduled'), and the alternative path for correcting fields in-call. It also warns that retrying without fixing the underlying field fails identically, which is actionable selection guidance.
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 draftsADestructiveInspect
Run AUTOPILOT POSTING (autoposting, paid plans only) NOW instead of waiting for its daily turn, or review the drafts it made: schedule them, delete one, change a caption, or save a note such as "too salesy". By default it is a preview: 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 its model cost (the reply says exactly how many credits). 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 and is paid; without confirm:true it returns only the price. 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.
| Name | Required | Description | Default |
|---|---|---|---|
| edit | No | REVIEW: change drafts before approving them. Edited copy is screened by the voice rules. | |
| redo | No | remake one draft from feedback; paid, so without confirm:true it returns only the price | |
| force | No | plan a preview even while autoposting is switched off — useful for showing someone what it would do before they turn it on. | |
| learn | No | notes for future batches, e.g. "too salesy". Free. | |
| dryRun | No | default TRUE (a preview: nothing rendered, nothing queued; the copy it writes bills its model cost). false renders fresh creative and schedules or drafts it per the mode. | |
| approve | No | REVIEW: draft ids to schedule, or ["all"]. A draft whose time has passed moves to the next free posting slot. | |
| discard | No | REVIEW: draft ids to delete, or ["all"]. Nothing is posted for them. |
TDQS
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 the costly, non-obvious behaviors: the preview still writes scenes and copy with a model and bills credits, dryRun:false spends credits to render creative, redo is paid and returns only a price without confirm:true, learn is free, the Library is never re-posted, and captions failing voice rules are silently dropped so a plan can come back shorter than cadence. That is precisely the 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.
Is the description appropriately sized, front-loaded, and free of redundancy?
The default-is-a-preview fact is front-loaded and every sentence carries operational weight, including cost and failure modes. It is a dense single block with heavy capitalization and mid-sentence branching, so an agent must parse carefully, but there is little true redundancy to cut.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a seven-parameter, nested-object, paid and destructive tool with no output schema, the description covers cost, default behavior, mode branching, each review action, and the failure mode of dropped captions. It even sketches the preview's return contents (scene, caption, channels, price) and states that credit counts appear in the reply, leaving little an agent needs to guess.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3, and the description earns the bump by explaining semantics the schema does not: dryRun defaults to true as a cost-incurring preview, approve accepts ["all"], edit's `at` reschedules the draft, and redo's confirm gates payment. The `force` parameter is the one notable gap, since its behavior is only explained in the schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific verb+resource+scope: run autopilot posting now, or review/approve the drafts it produced. It enumerates the review sub-actions (schedule, delete, edit caption, save note) and names the sibling that lists drafts (get_post_refill), so an agent can distinguish it from get_post_refill and set_post_refill 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.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It explicitly defines the default path as a preview, the condition that selects the real path (dryRun:false plus mode "auto" vs "review"), and routes review work to approve/discard/edit/learn/redo with the exact argument shapes. It also names alternatives for adjacent needs: get_post_refill for listing drafts and cancel_scheduled for pulling a scheduled post once this tool has already left it queued.
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.
| Name | Required | Description | Default |
|---|---|---|---|
| look | No | their canonical wardrobe/appearance in words — reused to hold the look steady across ads | |
| name | Yes | what to call this creator (e.g. “Sarah”) — list_creators and the app’s picker match on it | |
| brand | No | profile id/name from list_brands, this call only | |
| image | No | 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 | |
| poses | No | up to 4 extra full-body / angle plates of the SAME person (public urls) — they make a wider shot hold the identity | |
| voice | No | a default voice name for this persona (engines + voices are in hermoso_capabilities) | |
| source | No | "generated" (default) = an AI-made person; "upload" / "social" = a REAL person | |
| useAnyway | No | 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 |
TDQS
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.
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.
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.
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.
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.
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.
| Name | Required | Description | Default |
|---|---|---|---|
| name | Yes | the playbook headline — what it is, in a few words | |
| brand | No | profile id/name from list_brands, this call only | |
| hooks | No | the opening hooks worth reusing, verbatim | |
| plays | No | the concrete plays to run ({title, detail}) — the actionable half | |
| angles | No | the persuasion angles ({title, detail}) | |
| source | No | where it came from, e.g. “teardown · Ridge” | |
| formats | No | the formats/recipes this plays best in (e.g. ugc_selfie, cinematic, static) |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare the safety profile (not read-only, non-destructive, closed-world), so the description's job is to add destination and cost context — and it does: it lands in the same Playbooks library the web app lists and runs, and it is free. It does not address what happens on a duplicate name (idempotentHint is false), which is the one behavior an agent might reasonably need.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three sentences, front-loaded with the definition before the usage and differentiation clauses. Dense but every sentence carries a distinct payload; only minor trimming (e.g. the appending 'Free.') would tighten it further.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a seven-parameter, fully-described write tool with no output schema, the description covers purpose, content shape, destination library, alternatives, and cost. The remaining gap — dedupe/overwrite behavior and what the call returns — is minor and partly implied by the non-idempotent annotation.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so all seven parameters are already documented. The description's enumeration of hooks, angles, formats and concrete plays loosely maps to those fields but adds no syntax, ordering, or required-vs-optional nuance beyond the schema. Baseline 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
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 immediately defines what a playbook contains — hooks, angles, formats, concrete plays. It also explicitly names what it is not (save_skill, swipefile), so the agent can distinguish it from the nearest siblings 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.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives concrete trigger contexts: 'keep what a competitor_teardown or mine_angles just found' or 'bank a creative you want to repeat.' It also names two alternatives and the axis of difference (directive applied to every ad vs raw saved creative), 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).
| Name | Required | Description | Default |
|---|---|---|---|
| name | Yes | short skill name, e.g. “Founder-story hook” | |
| brand | No | profile id/name from list_brands, this call only | |
| directive | Yes | the full instruction the skill applies when used (1–6 sentences, imperative) |
TDQS
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.
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.
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.
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.
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.
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.
| Name | Required | Description | Default |
|---|---|---|---|
| url | No | a single Hermoso render URL to save | |
| name | No | file name (single save) | |
| urls | No | several render URLs (up to 20) to save in one call | |
| folder | No | Drive folder name to save into (created if new) |
TDQS
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.
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.
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.
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.
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.
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_notionSave to NotionAInspect
Save to the user's Notion as a NEW page: a research report, competitor teardown or campaign report as content (markdown: headings, lists, links, and an image or video URL on its own line becomes the media), a swipefile collection (swipefileCollection: name or id; each ad with its creative, copy, run dates and link), the content calendar (includeSchedule:true), or a mix. Put it under parentPageId, as a row in databaseId, or omit both for a private page in their workspace. Long text is split across blocks, never cut. The answer is read back from Notion. Free; never overwrites anything.
| Name | Required | Description | Default |
|---|---|---|---|
| icon | No | one emoji | |
| title | No | title of the new Notion page | |
| content | No | markdown body | |
| databaseId | No | database id or link: saves as a new row | |
| dataSourceId | No | data source id to save into as a new row; pass only one of parentPageId, databaseId, dataSourceId | |
| parentPageId | No | page id or link to save under | |
| includeSchedule | No | add the scheduled posts | |
| swipefileCollection | No | swipefile collection name or id |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare non-read-only, non-destructive, non-idempotent, open-world behavior; the description adds real value on top by stating 'never overwrites anything', 'Long text is split across blocks, never cut', that the result is read back from Notion, and that the call is free. It does not cover auth/permission requirements or rate limits, so it is strong but not exhaustive.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The core action and destination logic are front-loaded, and each clause carries information about a distinct payload type. The single dense paragraph is slightly run-on, but there is little filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With 8 optional parameters, no output schema, and mutation semantics, the description covers destination selection, content encoding, and overwrite behavior adequately, and it gestures at the return ('The answer is read back from Notion'). Return shape and failure behavior remain thin, keeping it below 5.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
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 earns above baseline by explaining markdown handling in `content` (headings, lists, links, and a standalone image/video URL becoming media) and by characterizing swipefileCollection and includeSchedule. It does not clarify the mutual exclusivity of parentPageId/databaseId/dataSourceId beyond what the schema says.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('Save to the user's Notion as a NEW page') and enumerates the concrete payload types it accepts (research report, competitor teardown, campaign report, swipefile collection, content calendar). The emphasis on NEW page distinguishes it from append_to_notion_page and export_schedule_to_notion among the siblings.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explains how to choose the destination (parentPageId, databaseId, or omit both for a private workspace page) and when to set includeSchedule. It gives clear usage context but never names a sibling and says 'use X instead' or states when-not-to-use, 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.
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).
| Name | Required | Description | Default |
|---|---|---|---|
| url | No | a single Hermoso render URL to save | |
| name | No | file name (single save) | |
| urls | No | several render URLs (up to 20) to save in one call | |
| folder | No | OneDrive folder name to save into (created if new) |
TDQS
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.
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.
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.
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.
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.
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 swipefileAIdempotentInspect
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.
| Name | Required | Description | Default |
|---|---|---|---|
| brand | No | profile id/name from list_brands, this call only | |
| items | Yes | the ads to save | |
| collection | Yes | the collection name — an existing one, or a new one to create |
TDQS
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.
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.
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.
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.
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.
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 laterADestructiveInspect
Use this when the user wants to schedule a social media post for later, on one channel or several at once. Scheduling and publishing cost no credits on any Hermoso plan (X alone bills per call: hermoso_credits gives the exact credits per X post). THE FREE PLAN INCLUDES 10 POSTS A MONTH ON UP TO 3 CHANNELS (a post counts once, however many channels it goes to; counted in the UTC month the post goes out); every paid plan posts without a limit. A post over that allowance is refused here and nothing is queued; the refusal says how many are used and when the count resets. Relay it as written, never retry it and never split the post to fit. billing_status and list_scheduled show how many are used. 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, wordpress, discord, slack — every one live. whop works where its connector is offered (list_connectors says) and is held back elsewhere: there a whop post is refused, not queued. 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): a Facebook post on a brand with several Pages and none named is refused when you schedule it, with the Pages listed by name and id, so ask the user which one. Several Google Business listings need locationId (list_business_locations), and without it that post is refused when it fires. Resolve those FIRST and let the user pick. 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.
| Name | Required | Description | Default |
|---|---|---|---|
| at | No | when to post: local wall clock, no offset (2026-10-30T10:00), read in `timezone` or the brand’s zone ON THAT DATE so a series survives a clock change; or UTC (…Z) / epoch ms. Future, at most 365 days out. Give this OR useQueue, never both. | |
| hook | No | 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. | |
| link | No | a link to attach (Facebook) | |
| poll | No | X — 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. | |
| tags | No | YOUTUBE — up to 30 search tags for the video (plain words, no #). | |
| brand | No | 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. | |
| event | No | GOOGLE 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. | |
| offer | No | GOOGLE BUSINESS — OFFER posts only: {couponCode, redeemOnlineUrl, termsConditions}. redeemOnlineUrl is where an offer actually sends people, since the button link is ignored on an Offer. | |
| place | No | FACEBOOK — tag a location on the Page post. The pageId from search_meta_places: the NUMERIC ID OF THE FACEBOOK PAGE for that place, not a place name and not coordinates. | |
| story | No | INSTAGRAM OR FACEBOOK PAGE 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. Link stickers are not available on a Story posted here. An Instagram Story keeps its caption: people see the start under the Story in the mobile app and tap for the rest. It takes NO collaborators, product tags or trialReel — any of those is refused by name and nothing is posted. FACEBOOK PAGE STORIES WORK TOO (target facebook): one image, or one vertical 9:16 video of 3–90 seconds, no caption (a message is refused by name), not schedulable natively — use schedule_post to queue one; the reply reads the story back from the Page. | |
| title | No | PINTEREST / 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. | |
| chatId | No | TELEGRAM — REQUIRED whenever telegram is a channel: WHICH chat, group or channel the bot posts to. A public channel’s @username (@yourchannel) 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. | |
| ideaId | No | short id of the content-plan idea this post came from | |
| pageId | No | 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: a Facebook post with several and no id is refused WHEN YOU SCHEDULE IT, with the Pages listed by name and id, so ask the user which Page and schedule again with its id. It is never sent from a guessed Page. | |
| recipe | No | the post's FORMAT id, e.g. "slideshow" or "imessage_chat" — post_performance groups by it, so reuse one id per format | |
| silent | No | TELEGRAM / DISCORD — no notification. | |
| thread | No | 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. | |
| wpSlug | No | WORDPRESS — the post slug | |
| wpTags | No | WORDPRESS — tags (names or ids) | |
| altText | No | ACCESSIBILITY — 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 — use that 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. | |
| audioId | No | 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. | |
| boardId | No | PINTEREST — 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. | |
| buttons | No | TELEGRAM ONLY — URL buttons under the post, as post_to_telegram takes them; never on an album. | |
| excerpt | No | WORDPRESS — the excerpt under the title | |
| message | No | the caption/text used for every channel unless overridden in captions | |
| subject | No | 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. | |
| accounts | No | WHICH 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). | |
| audience | No | 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. | |
| captions | No | per-channel caption overrides, e.g. { "instagram": "…", "threads": "…" } — platforms want different lengths and hashtag conventions | |
| channels | Yes | one or more channels to post to at that time | |
| coverUrl | No | 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. | |
| imageUrl | No | a 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. | |
| linkName | No | 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. | |
| timezone | No | IANA zone (e.g. "Africa/Cairo") a wall-clock `at` or the queue is read in; overrides the brand’s saved zone for this one post. Without either, UTC. | |
| topicTag | No | THREADS ONLY — one topic tag for the post, without the leading #. | |
| useQueue | No | 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. | |
| userTags | No | INSTAGRAM USER TAGS — tag people (public accounts) on the post; each is notified. Usernames, or { username, x, y } with x/y from 0 to 1 = where the tag sits on the IMAGE (a tag without a position sits at the centre). A Reel tag takes no position; a story position is optional. Not on a carousel (refused by name). | |
| videoUrl | No | a 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. | |
| whopPoll | No | WHOP FORUM POSTS ONLY — a poll: 2 to 10 options, in order. | |
| xArticle | No | 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. | |
| audioName | No | INSTAGRAM REEL — the name of the Reel’s audio track, which is what viewers tap through to. REELS ONLY. | |
| coverAtMs | No | 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. | |
| imageUrls | No | CAROUSEL — an ORDERED list of image URLs to publish as ONE swipeable post on every channel that supports it (Instagram 2–20, 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. | |
| parseMode | No | TELEGRAM ONLY — 'HTML' for Telegram's HTML tags; omit for plain text. | |
| placeName | No | the name the user saw for that place; a different business is refused | |
| slideText | No | PINTEREST CAROUSEL ONLY — per-slide title / description / link, aligned to slide order. Every other platform takes ONE caption for the whole carousel. | |
| topicType | No | GOOGLE BUSINESS — the KIND of Post. STANDARD is the default; EVENT and OFFER both REQUIRE `event` (title + start date), and OFFER also takes `offer`. | |
| trialReel | No | 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. | |
| yourBrand | No | TIKTOK — 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. | |
| actionType | No | GOOGLE 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. | |
| locationId | No | GOOGLE 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. | |
| madeWithAi | No | X — 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. | |
| threadName | No | DISCORD: forum thread title | |
| threadsGif | No | THREADS ONLY — a GIPHY GIF id (the id in a giphy.com/gifs/… link) attached to a TEXT Threads post. GIPHY is the only provider Threads takes. | |
| visibility | No | how 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. | |
| whopPinned | No | WHOP FORUM POSTS ONLY — pin it to the top of the forum. | |
| aiGenerated | No | AI-CONTENT DISCLOSURE (Instagram / Facebook Reel is_ai_generated, TikTok is_aigc, YouTube containsSyntheticMedia, Pinterest ai_disclosures, Threads is_made_with_ai). 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. | |
| audioVolume | No | INSTAGRAM REEL — how loud that track plays, 0–100 (Instagram’s default 100). Needs audioId. | |
| communityId | No | 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. | |
| description | No | YOUTUBE: the video DESCRIPTION, max 5000 characters, carrying the links, the CTA and what YouTube search reads. Omit it and the caption is used. | |
| disableDuet | No | TIKTOK 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. | |
| linkPicture | No | 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. | |
| quotePostId | No | THREADS ONLY — the id of the Threads post this one quotes. | |
| shareToFeed | No | 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. | |
| thumbOffset | No | 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. | |
| videoVolume | No | 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. | |
| whopMention | No | WHOP FORUM POSTS ONLY — notify every member (off by default). | |
| brandPartner | No | YOUTUBE: sponsoring brand channel (UC… or @handle) | |
| callToAction | No | 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. | |
| countryCodes | No | THREADS ONLY — two-letter country codes to limit who can see the post. Omit to show it everywhere. | |
| firstComment | No | FIRST COMMENT — posted on each new post as the same account right after it publishes, on Facebook, Instagram and LinkedIn company Pages (LinkedIn needs its comments authorization, provider linkedin_community). Other channels publish without it and their result row says so. Up to 2,000 characters, refused rather than cut; never on a story. A failed comment never undoes the post. | |
| optimizeCopy | No | 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. | |
| privacyLevel | No | 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. | |
| replyControl | No | THREADS ONLY — who may reply. Omit for Threads' own default (everyone). | |
| slackChannel | No | SLACK app: channel id or #name (default: the brand's) | |
| thumbnailUrl | No | 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. | |
| wpCategories | No | WORDPRESS — categories (names or ids) | |
| xQuotePostId | No | 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. | |
| collaborators | No | INSTAGRAM — 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. | |
| coverImageUrl | No | 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. | |
| disableStitch | No | TIKTOK VIDEO ONLY — block Stitches. Same photo-post rule as disableDuet. | |
| discordServer | No | DISCORD bot: which server | |
| instagramPoll | No | 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. | |
| platformCover | No | 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. | |
| replySettings | No | X — who may reply. Omit for everyone, which is the right default for a brand post. | |
| slackThreadTs | No | SLACK app: reply in this thread (parent ts) | |
| brandedContent | No | TIKTOK — 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. | |
| disableComment | No | TIKTOK — turn comments off on this post. | |
| disablePreview | No | TELEGRAM ONLY — no link preview. | |
| discordChannel | No | DISCORD bot: channel id or #name (default: the brand's) | |
| discordReplyTo | No | DISCORD bot: reply to this message id | |
| linkAttachment | No | THREADS 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). | |
| slackWorkspace | No | SLACK app: which workspace | |
| targetAudience | No | 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. | |
| whopVisibility | No | WHOP FORUM POSTS ONLY — who can see it; unset is the forum’s own default. | |
| discordMentions | No | DISCORD: let mentions ping | |
| linkDescription | No | 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. | |
| paidPartnership | No | 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. | |
| threadsLongText | No | THREADS ONLY — LONG-FORM text, up to 10,000 characters, attached under a TEXT-only Threads post (Threads' text attachment). The way to publish more than Threads' 500-character post: keep a short lead in the message and put the full text here. Not with an image, video or carousel. | |
| callToActionLink | No | FACEBOOK — where the button goes, when that is not the post’s own `link`. | |
| coverTimestampMs | No | 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). | |
| crossreshareToIg | No | THREADS 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. | |
| threadsGhostPost | No | THREADS ONLY — a GHOST post: text only, Threads archives it automatically after 24 hours (a flash offer, a 24h-only note). No other Threads option can ride it. | |
| whopExperienceId | No | WHOP — which forum or chat in the whop (exp_…, from list_whop_destinations). Omit it when the whop has only one; with several and none named the post is refused at fire time, never guessed. | |
| whopPaywallCents | No | WHOP FORUM POSTS ONLY — a paywall in cents (500 = $5.00): readers pay to see the body; needs a title. | |
| commercialContent | No | TIKTOK — 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. | |
| discordStartThread | No | DISCORD bot: open a thread under the post, this title | |
| instagramLocationId | No | INSTAGRAM — tag a place (called locationId on post_to_meta; locationId here is the Google Business listing). It is the pageId from search_meta_places, 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. | |
| visibilityByChannel | No | override visibility for one channel, e.g. { "tiktok": "draft" } to go live everywhere but stage TikTok for review | |
| whopPaywallCurrency | No | WHOP — the paywall currency, a 3-letter code (default usd). | |
| crossreshareDarkMode | No | THREADS ONLY — render that Instagram Story in dark mode. Needs crossreshareToIg. | |
| paidProductPlacement | No | YOUTUBE: paid product placement (shows the disclosure) | |
| instagramPollExtended | No | 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. | |
| instagramCommentPrompt | No | INSTAGRAM — make the caption a COMMENT PROMPT that people answer in the comments. Same limits as instagramPoll, and never together with it. | |
| linkedinOrganizationId | No | LINKEDIN — 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. | |
| brandedContentSponsorIds | No | 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. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations declare destructiveHint=true and openWorldHint=true but say nothing about cost, refusals, or partial failure; the description supplies all three. It states that scheduling is free on every plan, that the free plan caps at 10 posts over 3 channels per UTC month and refuses rather than queues, that channels are attempted independently, and that a post goes live publicly by default and is never quietly downgraded. Refusal semantics are documented per-feature (wrong board, carousel on X, unverified Facebook link domain).
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loading is good — usage condition, then channel scope, then plan limits, then per-channel rules — and for 116 params the length is defensible. But the ALL-CAPS shouting ("A MULTI-SLIDE creative goes in imageUrls[] as a CAROUSEL", "THE FREE PLAN INCLUDES 10 POSTS A MONTH") and repeated restatements of refusal behavior add bulk that a tighter voice would remove without losing information.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 116-parameter multi-channel publishing tool with no output schema, the description covers what an agent must know: which identifiers must be resolved before calling, what gets refused at schedule time versus fire time, that per-channel result rows exist, and how defaults behave (public visibility, platformCover, aiGenerated inferred from provenance). Nothing material is missing for correct invocation.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so each of the 116 parameters is already documented; the description adds the cross-parameter rules the schema cannot express well: at XOR useQueue, imageUrls[] order being "THE ORDER IS THE PRODUCT" and carousel refusal on X/YouTube/Google Business, altText arrays aligned to slide order with the tightest-length refusal rule, and visibilityByChannel overrides. It also explains that quotePostId is Threads-only while xQuotePostId is billed at X's link rate, resolving a genuine name collision.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
"Queue a post for a future time on one or more connected channels at once" states a specific verb (queue/schedule), the resource (a social post), and the scope (multi-channel, future-dated). It also enumerates the fifteen channels and marks whop as conditional, so an agent knows exactly what the tool covers 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.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Opens with "Use this when the user wants to schedule a social media post for later," then gives explicit prerequisites that must be resolved first (boardId via list_pinterest_boards, linkedinOrganizationId via list_linkedin_pages, pageId via list_meta_pages, locationId via list_business_locations, chatId for telegram), plus mutual exclusions (at vs useQueue, coverUrl vs thumbOffset, poll vs media). It distinguishes queue-based scheduling ("what 'just queue it' means") from naming a time.
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.
| Name | Required | Description | Default |
|---|---|---|---|
| url | Yes | the ad asset URL (a /generated/ path or public URL) | |
| kind | No | 'image' (default) or 'video' | |
| intent | No | what the ad is trying to achieve, for goal-fit scoring |
TDQS
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.
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.
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.
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.
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.
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.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | max ads returned (1–25, default 8) | |
| domain | No | the advertiser's domain, e.g. nike.com | |
| region | No | 2-letter region, default US | |
| advertiserId | No | Google advertiser id (AR…) when the domain is ambiguous |
TDQS
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.
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.
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.
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.
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.
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. It matches captions worldwide and cannot be limited to a country or language: for one market, search in the words people there use, or read named local accounts' Reels through fetch_social_data's Instagram profile endpoint, which is the better first step for a local market. Spends about a credit.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | max reels returned (1–25, default 8) | |
| query | Yes | keyword to search reels for |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The description adds behavior the annotations do not carry: a credit cost ('spends about a credit'), the result ordering (ranked by plays), the exact return shape, and the hard limitation that matching is captions-worldwide with no country or language filter. The annotations only broadly declare openWorldHint and non-readOnly, which the credit spend is consistent with.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The core scope and the sibling routing are front-loaded in the first clause, and the return shape follows. Sentences are long and dense with parentheticals, but each carries a distinct fact (endpoint, output fields, ranking, geographic limitation, cost).
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
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 enumerating the returned fields per reel. Combined with cost, ranking, and the geographic limitation, an agent has everything needed to call this correctly and interpret the response.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100% and both parameters are documented there, including the limit range and default. The description adds only indirect meaning — that the query matches captions worldwide — and nothing about how limit interacts with the cost-per-call or ranking.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description names a specific verb and resource (organic Instagram Reels keyword search) and immediately scopes it against the sibling it is not — profile and hashtag pulls go through fetch_social_data with a handle. An agent can distinguish this surface from fetch_social_data 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.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It gives explicit routing rules: use this for keyword search, use fetch_social_data with a handle for profiles/hashtags, and for a single local market either search in local vocabulary or start with named local accounts' Reels via the profile endpoint. Both the when and the when-not are stated.
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's library includes impression counts. Spends about a credit.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | max ads returned (1–25, default 8) | |
| company | No | advertiser company name | |
| keyword | No | keyword across all advertisers | |
| companyId | No | LinkedIn company id (numeric) when the name is ambiguous | |
| countries | No | CSV of 2-letter codes like 'US,CA'; omit or 'ALL' = worldwide |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations cover the safety profile (destructiveHint=false, openWorldHint=true) but do not convey cost. The description adds 'Spends about a credit', which is real behavioral context an agent needs before calling. It also discloses the return shape, though it doesn't explain pagination or what limits real results.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three tight clauses: purpose, routing alternative, return shape and cost. Purpose and the sibling disambiguation are front-loaded, and every sentence carries distinct information with no filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 5-param read tool with no output schema, the description supplies the otherwise-missing return fields, notes the LinkedIn-specific impression data, discloses credit cost, and routes to the sibling tool. Everything an agent needs to select and invoke it is present.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so all five parameters (limit, company, keyword, companyId, countries) are already documented. The description only echoes the three lookup keys already in the schema and adds no format or constraint detail beyond it, so the baseline of 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
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 supported lookup keys (company name, keyword, companyId). It explicitly distinguishes itself from the sibling research_ads, 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.
Does the description explain when to use this tool, when not to, or what alternatives exist?
'use for a targeted B2B pull; use research_ads for open-ended research' gives an explicit when-to-use and names the alternative with the condition that selects it. Nothing is left to inference about routing.
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 credits on each ad-library read it makes: one for a keyword; for a brand name, the page lookup and its ads (plus a keyword search when that page has none).
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | max ads returned (1–25, default 8) | |
| query | No | keyword search across ALL advertisers (use INSTEAD of companyName/pageId) | |
| domain | No | the advertiser’s website, e.g. buffer.com — with companyName, used to confirm which Meta page is really theirs | |
| pageId | No | one advertiser’s ads by Facebook page id (most precise) | |
| status | No | ACTIVE = currently running; default ALL (includes proven past winners) | |
| country | No | 2-letter code or 'ALL' (default ALL) | |
| mediaType | No | filter by creative type (default ALL) | |
| companyName | No | one advertiser’s ads by brand name — resolved to the brand’s own page only on evidence (its Instagram, its domain, or ads that land on its domain); pass domain too for the best match |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Adds context beyond annotations: it discloses credit-spend behavior per read, including the surprising cost model for brand lookups (page lookup + ads + fallback keyword search), and gives the return shape. Annotations flag readOnlyHint=false and openWorldHint=true but say nothing about cost or fallback behavior, so these disclosures are genuinely additive; pagination/rate limits are not covered, 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.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loads the identity of the tool, then the when-to-use condition, return shape, alternative, and cost model — every sentence carries distinct information with no filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For an 8-parameter tool with no output schema, the description supplies the return shape ({page_name, body, cta, link, dates, media}), the routing rule versus research_ads, and the credit cost, which is 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.
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 the key semantic point that query and companyName/pageId are mutually exclusive modes of fetch. It reinforces rather than merely repeats the schema text, and frames limit implicitly via the credit model.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('Structured Meta (Facebook/Instagram) Ad Library pull') and immediately scopes it to the case where you know WHAT to fetch. It names the sibling it is not (research_ads), so an agent can distinguish 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.
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 OR one advertiser'), explicit exclusion with a named alternative ('For open-ended research that needs judgment across platforms, use research_ads instead'), and it clarifies the keyword-vs-advertiser selection.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
search_meta_placesFind a place to tag on Instagram or FacebookAInspect
Find a PLACE to tag on an Instagram or Facebook post: a shop, cafe, restaurant, venue or landmark. Give its name with the city ("Blue Bottle Coffee Oakland"), or paste its Facebook Page link, an Instagram place link (instagram.com/explore/locations/...) or its numeric id. A pasted link or id is read and comes back as that Page's taggable pageId with its name and address, or a plain reason it cannot be tagged. Returns a handful of matching Facebook Pages that have an address: pageId, name, address, category, link. Pass the chosen pageId as locationId (Instagram) or place (Facebook) on post_to_meta, or instagramLocationId on schedule_post. These are matches to CONFIRM with the user (check the name and address; a place tag is public), and Instagram checks the place when the post is made. Needs no connected account. Spends about 1 credit per Page it checks (a name search checks up to 5 Pages, so about 5 to 7 credits); a pasted Facebook Page link or numeric id is about 1 credit; an Instagram place link costs nothing.
| Name | Required | Description | Default |
|---|---|---|---|
| near | No | optional city or area to narrow a name search, e.g. "Paris" | |
| limit | No | how many Pages to check on a name search, 1 to 8 (default 5); each is about 1 credit | |
| query | Yes | the place name with its city (e.g. "Blue Bottle Coffee Oakland"), or a Facebook Page link, an Instagram place link, or a numeric id |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations give readOnlyHint=false and destructiveHint=false, but the description carries far more behavioral weight than annotations could: explicit credit costs per path (name search ~5-7 credits, pasted FB link/id ~1, IG place link free), no connected account needed, what a pasted link resolves to, and the 'plain reason it cannot be tagged' failure mode. Consistent with readOnlyHint=false because the call spends credits.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with the core action and input formats, then the return shape, then routing to downstream tools, then cost. It is dense and long, but each sentence carries distinct information — only the credit-cost detail is repeated slightly between the prose and the limit param schema.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
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 naming the returned fields (pageId, name, address, category, link) and the failure outcome. For a 3-param search tool with openWorldHint, nothing an agent needs to select or invoke it is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
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: it enumerates the accepted query forms and ties limit to a concrete cost ('each Page is about 1 credit'), beyond the schema's 'how many Pages to check'. Slight overlap with schema text keeps it from a full 5.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb (find) and resource (a place to tag) with a tightly scoped domain: shop, cafe, restaurant, venue, landmark on Instagram/Facebook. This is clearly distinct from adjacent siblings like list_meta_pages or list_business_locations, which the agent can rule out from the description alone.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Tells the agent exactly what input forms are accepted (name+city, Facebook Page link, Instagram place link, numeric id) and where the result feeds: pageId → locationId on post_to_meta, or instagramLocationId on schedule_post. It also states the follow-up behavior — confirm matches with the user because the tag is public, and Instagram re-checks at post time.
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.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | posts per platform, 1–60 (default 24) | |
| topic | Yes | subject, brand, product or hashtag — "liquid death", "coffee", "#homecafe" | |
| queries | No | query variants per platform, 1–4 (default 1); each is a paid search call | |
| platforms | No | default all three |
TDQS
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.
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.
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.
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.
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.
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.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | max posts returned (1–25, default 8) | |
| query | Yes | what to search Reddit for |
TDQS
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.
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.
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.
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.
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.
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.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | max posts returned (1–25, default 8) | |
| query | Yes | keyword to search Threads for |
TDQS
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.
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.
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.
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.
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.
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; for what to POST, tiktok_ideas. Spends about a credit.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | max videos returned (1–25, default 8) | |
| query | Yes | keyword or hashtag (no # needed) |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations declare readOnlyHint=false, idempotentHint=false and openWorldHint=true on what is nominally a read operation, which would otherwise be confusing; the description resolves this by disclosing 'Spends about a credit,' explaining why a search is not marked read-only. It also spells out the exact return shape and ranking, which is genuine added value beyond the annotations. It stops short of noting result volatility or that credit cost varies with limit.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three sentences, each earning its place: purpose/scope first, output contract second, disambiguation and cost last. Heavy information density with no filler or restated title.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With only two simple params, no nested objects and no output schema, the description supplies everything an agent needs: what it searches, what comes back field-by-field, how results are ordered, the cost, and which sibling to use instead. Nothing material is missing for correct invocation.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so both params (query, limit) are already documented, making a baseline 3 appropriate. The description adds a little meaning by explaining that results are ranked by plays and that 'query' accepts a keyword or hashtag, but it does not reconcile the limit range (description of the param says 1–25 while the schema maximum is unbounded).
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('Organic TikTok keyword search') plus the key scoping fact that there is no TikTok ad library, and characterizes the output ('top-performing videos... ranked by plays'). An agent can distinguish this from search_meta_ads, search_google_ads, or search_youtube 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.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly routes the agent: 'Use research_ads for open-ended research; for what to POST, tiktok_ideas' — naming both the alternative and the condition that selects it. The only weakness is that 'tiktok_ideas' does not appear in the sibling tool list, so that pointer may be a dead reference and could send an agent looking for a nonexistent tool.
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.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | max videos returned (1–25, default 8) | |
| query | Yes | keyword to search videos for |
TDQS
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.
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.
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.
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.
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.
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-reloadAIdempotentInspect
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.
| Name | Required | Description | Default |
|---|---|---|---|
| enabled | Yes | true to turn auto-reload on, false to turn it off | |
| reloadCredits | No | how many credits to add each reload — must match a credit pack size (see buy_credits) | |
| thresholdCredits | No | reload when the balance drops below this many credits |
TDQS
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.
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.
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.
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.
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.
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 market radarADestructiveIdempotentInspect
Set, change or stop a brand's MARKET RADAR (the standing competitor watch; Ad Spy > Watching in the web app; each brand has its own), run weekly or daily: newAds (what watched brands launched), winners (their ad passing 30 days running), trends + topPosts (formats and most-viewed posts for niche topics), ideas (3 to make). Only what you pass changes. competitors / topics REPLACE their lists (max 8 brands, 5 topics); both empty stops it and clears findings. auto:true picks 3-5 competitors, 2-3 similar brands and 2-3 topics from the saved brand or seed (one model call, billed at its token cost). dryRun:true quotes one check, saves nothing. Saving is free; each check holds its quote first and is skipped when the balance is short. runNow is refused while a check runs or just after one finished. Read it with list_watch_findings.
| Name | Required | Description | Default |
|---|---|---|---|
| auto | No | true picks competitors, similar brands and topics from the saved brand or seed; bills one model call at its token cost | |
| seed | No | for auto: a website, brand or niche | |
| brand | No | profile id/name from list_brands, this call only | |
| No | the weekly email | ||
| force | No | with runNow: check again right after a check, allowed once what is watched changed | |
| dryRun | No | true quotes one check and saves nothing | |
| runNow | No | check now (spends now) | |
| topics | No | niche search phrases, e.g. "protein breakfast" | |
| cadence | No | weekly (default) or daily check | |
| modules | No | on/off; omitted keep theirs | |
| competitors | No | the COMPLETE brand list |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Adds substantial context beyond the annotations: only passed fields change, competitors/topics are replacement lists with caps, both-empty destroys findings, auto burns a billed model call, dryRun only quotes, saving is free but checks reserve quote and skip on low balance, and runNow is refused during or right after a check. This matches and enriches the destructiveHint and idempotentHint semantics.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with the core definition and then dense operational notes; almost every clause adds decision-relevant information. It is a bit semicolon-heavy and reads like a compressed paragraph, but there is little waste for an 11-parameter mutation tool.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a destructive, credit-spending, 11-parameter mutation with no output schema, the description covers mutation scope, clearing behavior, cost mechanics, dry runs, refusal windows, and where to read results. An agent has what it needs to call it correctly without opening other documentation.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
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 replacement semantics for competitors/topics, max list sizes (8 brands, 5 topics), auto's selection counts, and the billing/quote behavior of dryRun and runNow. These are meaningful constraints not fully captured in the schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb set (set/change/stop) and a precise resource (the brand's MARKET RADAR, mapped to Ad Spy > Watching, one per brand). It also distinguishes the tool from the sibling that reads it (list_watch_findings), so an agent can tell what it configures versus what it reads.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives clear operational routing ('Read it with list_watch_findings'), the stop condition (both empty clears findings), and when runNow is refused. It does not compare against adjacent discovery tools like find_competitors, research_ads, or competitor_teardown, 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.
set_connector_accountsChoose which accounts a brand may useADestructiveIdempotentInspect
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, 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.
| Name | Required | Description | Default |
|---|---|---|---|
| provider | Yes | which connector to scope | |
| accountIds | Yes | the ids (from list_connector_accounts) this profile may use — an empty array shares nothing |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Goes well beyond the destructive/idempotent annotations by explaining the REPLACE-not-merge semantics, that omitted ids are un-shared, that an empty list shares nothing and causes publishing to fail closed, and that the server re-verifies every id against the live connection and rejects unreachable ones. These are exactly the consequences an agent needs before 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.
Is the description appropriately sized, front-loaded, and free of redundancy?
Effect and replace semantics are front-loaded, and each clause carries weight. The trailing enumeration of 24 provider names duplicates the schema enum and could be dropped, and the trailing 'Free.' is terse but does convey no credit cost.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
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, the description covers scope, replacement semantics, empty-list behavior, validation behavior, and user confirmation. Nothing an agent needs in order 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.
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 meaning by naming the account types behind provider values (Facebook Pages, Meta ad accounts, Google Ads customers, LinkedIn company Pages) and by stressing that accountIds come from list_connector_accounts. The provider list is largely redundant with the enum.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb (Set) and resource (which connector accounts this profile may use), and spells out the concrete effect: allowed to post to and spend from. It is clearly distinguishable from siblings like list_connector_accounts (read) and disconnect_connector (remove).
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives an explicit prerequisite ('Pass ids from list_connector_accounts') and a user-interaction rule ('Ask the user which accounts they mean') with a stated reason. It lacks an explicit when-not-to-use or a contrast with disconnect_connector/leave_connector, which keeps it short of 5.
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 postingADestructiveIdempotentInspect
Turn AUTOPILOT POSTING (autoposting, the posting refill) on, change it or pause it. Available on paid plans: on the Free plan switching it on is refused, so relay that and do not retry. Examples: "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.
| Name | Required | Description | Default |
|---|---|---|---|
| goal | No | 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. | |
| mode | No | "review" (default): each batch of fresh posts waits as drafts for approval. "auto": fresh posts are scheduled to publish automatically. | |
| avoid | No | THE BRIEF: never mention or show these — topics, claims, competitors, faces. | |
| chatId | No | TELEGRAM — 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. | |
| pageId | No | FACEBOOK / INSTAGRAM / THREADS — which connected Page to publish from (list_meta_pages). Omit for the brand’s only Page. | |
| boardId | No | PINTEREST — 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. | |
| enabled | No | 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. | |
| formats | No | 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. | |
| pillars | No | THE BRIEF: content pillars / topics, up to 8 short lines. Every post belongs to one. get_post_refill suggests some from the brand profile. | |
| accounts | No | accounts to post to on a channel holding several: {"tiktok":["@a","@b"]} or "all"; null clears. A new setup must pick; an older one posts to the primary until it does. | |
| channels | No | REPLACES the list; a channel connected later stays out until added. "all" = every channel, later ones too, only if the user asks | |
| goalNote | No | THE BRIEF: a line about the goal (e.g. the launch date and what is launching). | |
| timezone | No | IANA timezone the posting times are in, e.g. "America/New_York" (default: the brand's, else UTC) | |
| daysAhead | No | how far ahead to keep the queue full, 1–30 (default 7) | |
| learnings | No | notes learned from reviews; REPLACES the list | |
| addChannels | No | add these | |
| postsPerDay | No | 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. | |
| postingTimes | No | 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. | |
| removeChannels | No | stop posting here | |
| excludeChannels | No | never these channels, even ones connected later; [] clears | |
| whopExperienceId | No | WHOP — which forum or chat posts go to (exp_…, from list_whop_destinations). Without one, the whop’s only forum or chat is used; with several, whop is skipped rather than guessed. | |
| channelPostsPerDay | No | 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. | |
| forgetCaptionEdits | No | true = forget the caption edits learned from past reviews | |
| linkedinOrganizationId | No | 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. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Beyond annotations, it discloses paid-plan restrictions, refusal conditions, the fact that it always creates new posts and never re-posts the Library, the `mode` behavior, and that `enabled:false` removes the recurring job while leaving queued posts untouched. These details materially clarify the destructive and open-world behavior.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is long but well structured, with front-loaded purpose and labeled sections such as WHERE, HOW OFTEN, and SWITCHING IT ON ASKS THE BRIEF ONCE. It is dense and somewhat repetitive in places, but the length is justified by 24 parameters and several conditional behaviors.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a complex 24-parameter mutation tool with no output schema, the description is highly complete: it covers eligibility, defaults, refusal cases, pause behavior, brief requirements, posting-frequency math, and related tools. An agent has enough context to invoke it correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
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 meaning: pass only what changes, channels/default behavior, `mode` default semantics, and the relationship between `postsPerDay`, `postingTimes`, and `channelPostsPerDay`. Some of this is repeated from schema descriptions, but the interaction rules add genuine value.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description specifies the verb and resource precisely: turn autopilot posting on, change it, or pause it. It also distinguishes related siblings by naming `get_post_refill` for cost/current settings and `run_post_refill approve` for approving drafts.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It gives explicit usage examples and conditions, including paid-plan refusal on Free, when setup is refused (no connected channel or nothing daily), and when to ask the user rather than guess. It also names alternatives like `get_post_refill` and `cancel_scheduled`.
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 photoADestructiveIdempotentInspect
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).
| Name | Required | Description | Default |
|---|---|---|---|
| brandId | No | a profile id/name from list_brands to lock the product for; omit to use the active profile | |
| imageUrl | Yes | the image URL to lock as the product (from a research result, a workspace / list_product_photos url, or any public product photo) | |
| source_note | No | a short note on where it came from, e.g. "from their IG post" |
TDQS
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.
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.
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.
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.
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.
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 roleADestructiveIdempotentInspect
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.
| Name | Required | Description | Default |
|---|---|---|---|
| role | Yes | the new role | |
| Yes | the member’s email | ||
| confirm | No | REQUIRED true |
TDQS
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.
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.
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.
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.
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.
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.
| Name | Required | Description | Default |
|---|---|---|---|
| model | No | video model id from hermoso_capabilities — omit to let the router pick | |
| voice | No | voiceover voice name, e.g. Rachel / George | |
| scenes | Yes | array of scene objects (visual + optional voiceover/seconds) | |
| voiceover | No | full voiceover script spoken across the scenes | |
| resolution | No | 720p (default), 1080p for full detail, or 480p for a cheaper draft | |
| aspectRatio | No | output aspect ratio, e.g. 9:16 (default) / 1:1 / 16:9 | |
| durationSeconds | No | total spot length in seconds (defaults to the sum of the scenes’ seconds) |
TDQS
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.
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.
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.
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.
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.
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 storeARead-onlyIdempotentInspect
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.)
| Name | Required | Description | Default |
|---|---|---|---|
| key | Yes | the store key to read (one of the allowlisted keys) | |
| limit | No | max array items to return (default 50) |
TDQS
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.
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.
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.
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.
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.
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 deliveryAIdempotentInspect
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.
| Name | Required | Description | Default |
|---|---|---|---|
| pageId | No | the company Page that owns the form — from list_linkedin_pages; omit when one Page is shared | |
| leadType | No | defaults by owner: SPONSORED for an ad account, COMPANY for a Page | |
| forwardTo | No | optional public HTTPS URL Hermoso relays each lead event to (a CRM, Zapier, Make) | |
| adAccountId | No | read forms owned by an AD ACCOUNT instead of a Page |
TDQS
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.
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.
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.
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.
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.
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 listADestructiveIdempotentInspect
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 model call, billed at its token cost.
| Name | Required | Description | Default |
|---|---|---|---|
| brand | No | profile id/name from list_brands, this call only | |
| confirm | No | true to APPLY the proposal; omit to only see it |
TDQS
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 genuinely new behavior: the default call is a non-mutating proposal, deletes carry tombstones so they persist across devices, writes go through the same typed writers as the app, and each invocation is one model call billed at token cost. That is exactly the context an agent needs before performing 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.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three dense sentences with the dry-run-then-apply protocol front-loaded and no filler. The nested parentheticals and em-dash clauses make it slightly heavy to scan, but every clause carries real information.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a destructive, default-safe bulk mutation with two optional params and no output schema, the description covers safety model, persistence guarantees, billing implications, and what the proposal contains (removals and merges with reasons). Nothing essential is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
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 (brand as a profile id from list_brands, confirm as the apply flag). The description reinforces confirm's two-mode semantics but says nothing about the brand parameter or its 'this call only' scoping, so it only marginally exceeds the schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('Clean up and consolidate the workspace Memory') and immediately enumerates the operations performed: dropping product-behaviour entries, dropping phone numbers/emails, and merging near-duplicate facts. This is clearly distinguishable from siblings like list_memory, remember, and forget, which read or write individual entries.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives an explicit two-step invocation protocol: call with no argument for a proposal, call again with confirm:true to apply. That is strong operational guidance. It stops short of 5 because it never explicitly contrasts itself with forget or manual cleanup, nor states when this tool should be avoided.
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 publishingARead-onlyIdempotentInspect
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 url once TikTok’s moderation has named it; urlStatus is ready, pending, private or draft), 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).
| Name | Required | Description | Default |
|---|---|---|---|
| account | No | which connected TikTok account the post went out on (@handle or id from list_connector_accounts) when the profile has more than one | |
| publishId | Yes | the publishId post_to_tiktok returned |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already cover the safety profile (readOnlyHint, idempotent, non-destructive), yet the description still adds substantial context: the three possible outcomes, the meaning of urlStatus values, that 'still processing' for a few minutes is normal rather than a failure, and the prerequisite that TikTok must be connected under Settings > Connectors. This is materially 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.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with the trigger condition and the outcome taxonomy, and nearly every sentence carries distinct information. Minor redundancy: 'Free, read-only' partly restates readOnlyHint, though 'Free' and the connector requirement do add value.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
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 carries the return-value burden itself — and it does, enumerating done/failed/processing, the `url` availability timing, urlStatus values, and the failure-reason field. Combined with the prerequisite and idempotency context, 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.
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 parameters. The description's 'pass the publishId it returned' restates what the schema already says verbatim, and the optional `account` parameter is never mentioned in the description. 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.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb (follow/check status of) and resource (a TikTok post sent by post_to_tiktok), and ties itself to the exact condition that produces the need: the post came back pending:true. It is unmistakably distinct from the sibling post_to_tiktok, which creates the post rather than tracking it.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicit trigger ('post_to_tiktok sent but TikTok was still processing'), explicit input provenance ('pass the publishId it returned'), and an explicit anti-pattern ('Never post the video again while it is processing'). The alternative course of action is named and rejected, which is exactly what routing guidance should do.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
update_brandUpdate profile fieldsADestructiveInspect
Patch SPECIFIC fields of the active profile (name, domain, sells, summary, category, audience, positioning, voice, style, goal, logos, brand/product name pronunciations, profileType: explore = kept but unused) 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.
| Name | Required | Description | Default |
|---|---|---|---|
| goal | No | current marketing goal | |
| logo | No | logo image: URL or local file path to a PNG, JPEG or WebP (no SVG) | |
| name | No | brand name | |
| brand | No | profile id/name from list_brands, this call only | |
| sells | No | what the brand sells | |
| style | No | visual style — palette, typography, aesthetic | |
| voice | No | brand voice/tone | |
| domain | No | website domain | |
| summary | No | one-line description | |
| audience | No | who the brand sells to, one line | |
| category | No | product or business category, one line | |
| logoDark | No | for light pictures | |
| logoLight | No | for dark pictures; "none" removes | |
| pronounce | No | 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. | |
| positioning | No | how the brand is positioned against alternatives, one line | |
| profileType | No | website, social, describe or explore; explore keeps the profile but stops using it | |
| pronunciations | No | how PRODUCT names are said aloud, e.g. {"Power Cakes": "POW-er cakes"}. Merged into the saved ones; an empty string removes one. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Beyond annotations (readOnlyHint=false, destructiveHint=true, idempotentHint=false), the description discloses the patch/merge semantics ('WITHOUT overwriting the rest') and the side-effect that profileType=explore keeps the profile but stops using it, plus the confirmation caution for inferred brands. It stops short of stating whether calls are reversible or what a failed call leaves behind, which matters 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.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with the core semantic (partial patch, no overwrite) in the first clause, then usage triggers, then the alternative. It runs long with two stacked parentheticals and a multi-clause confirmation rule, but nearly every sentence carries distinct decision-relevant information.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 17-parameter destructive mutation with no output schema, the description covers scope, merge behavior, the ambiguous enum value, and the confirmation prerequisite. What remains uncovered — returned value and error/rollback behavior — is modest but noticeable for a destructive tool.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
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 earns more by adding operational meaning the schema lacks — 'Only pass the fields you're changing', the explore enum's runtime effect, and clarification that the listed fields are the patch surface rather than a full replacement.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('Patch SPECIFIC fields of the active profile'), enumerates the patchable fields, and frames the operation as 'a read-modify-write on the saved brand' — distinguishing it from draft_brand/create_brand. An agent can identify the tool and its scope 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.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicit triggers are given with examples ('change our voice to playful', 'we sell to dentists now'), the alternative is named for a different job ('To onboard a brand from scratch, use draft_brand'), and a pre-condition is spelled out: confirm an inferred brand before saving if none exists yet.
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 placeADestructiveIdempotentInspect
EDIT a Google Doc — the correction append_to_doc cannot make. 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.
| Name | Required | Description | Default |
|---|---|---|---|
| docUrl | No | a Google Docs URL — the id is extracted from it | |
| confirm | No | true runs a whole-body rewrite after the character count is shown | |
| rewrite | No | replace the WHOLE body with this text ("" empties the doc) | |
| dropdowns | No | dropdown chips to set, or dropdowns to change | |
| documentId | No | the document id (from create_doc, or list_drive_files for one the user picked) | |
| confirmCells | No | echo back the character count the unconfirmed call reported (rewrite only) | |
| replacements | No | find/replace pairs, applied in order |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Goes well beyond the annotations: find/replace reports occurrence counts and treats zero matches as a visible FAILURE, dropdown errors are refused atomically with the real option list, and the confirm/confirmCells round-trip exists to surface character counts before a destructive rewrite. This is exactly the behavioral context annotations alone 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.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with the verb and the three shapes; every sentence carries operational information. It is a single dense block with semicolon chains, which slightly hurts scanability for a seven-parameter tool, but there is no filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a complex, no-output-schema tool, it covers the confirm flow, failure reporting, and dropdown edge cases. It does not state what is returned on a successful dropdown mutation or describe partial-success behavior across multiple replacements, which are the remaining gaps.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
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 options/newTitle entry changes the dropdown definition itself for every chip built from it, a dropped option needs replace:{Old:New}, and matchCase:false relaxes matching. It reinforces rather than duplicates the schema, though some of the same detail is already in the property descriptions.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Opens with a specific verb+resource ('EDIT a Google Doc') and immediately differentiates from the sibling append_to_doc ('the correction append_to_doc cannot make'). The three operation shapes are enumerated so an agent knows exactly what the tool does before 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.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicit routing: call read_doc first to match text exactly, use replacements for targeted fixes vs rewrite for the entire body, and pass the unconfirmed call before confirm:true for destructive rewrites. Alternative tools and preconditions are named rather than implied.
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 fileADestructiveIdempotentInspect
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.
| Name | Required | Description | Default |
|---|---|---|---|
| name | No | new name | |
| trash | No | true -> move to Trash; false -> restore from Trash | |
| fileId | Yes | the Drive file id | |
| moveToFolderId | No | folder id to move the file into (from create_drive_folder / list_drive_files) | |
| removeFromFolderId | No | the old parent folder id to remove (when moving) |
TDQS
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.
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.
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.
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.
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.
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_notion_rowUpdate a Notion database rowADestructiveIdempotentInspect
Change properties on ONE row of a Notion database (pageId from query_notion_database) — e.g. mark a calendar row Status "Posted" and put the live post link in its URL column. properties is {"Column": value} by column name: text, number, checkbox, select (an existing option or a new one), status (must be an existing option), multi-select (array; replaces the whole value), date (ISO, or {start, end}), url, email, phone; null clears. Validated against the database's own columns before anything is sent and refused by name otherwise, never coerced; columns you do not name keep their values. Read back from Notion. trash:true moves the row to Notion's trash (restorable; returns a preview first and needs confirm:true), trash:false restores it. Free.
| Name | Required | Description | Default |
|---|---|---|---|
| trash | No | true moves the row to Notion trash (needs confirm); false restores it | |
| pageId | Yes | the row id or link | |
| confirm | No | true confirms moving the row to trash; not needed for other changes | |
| properties | No | {"Column": value} |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Goes well beyond the annotations: values are validated against the database's own columns and refused by name rather than coerced, unnamed columns are preserved, results are read back from Notion, and trash is described as restorable and preview-then-confirm. That last point is the key context for a tool annotated destructiveHint=true — the agent learns the destructive action is reversible and gated.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loads the core action and example, then layers type rules and trash behavior, with no filler sentences. The type enumeration is a dense semicolon chain that reads more like a reference dump than prose, which costs it a point but not its efficiency.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
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, everything an agent needs is present: accepted value shapes, validation and refusal behavior, preservation of unnamed columns, and the two-step trash/confirm flow. Return behavior is summarized as 'read back from Notion,' which suffices without an output schema.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so baseline would be 3, but the description substantially enriches it: per-type value formats (text, number, checkbox, select new-or-existing, status existing-only, multi-select array that replaces the whole value, ISO date or {start, end}, url/email/phone), null-to-clear semantics, and confirm:true being required only for trashing. The schema's bare {"Column": value} and boolean hints would leave an agent guessing without this.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Names a specific verb and resource (change properties on ONE row of a Notion database) and scopes it to a single page, which cleanly separates it from append_to_notion_page and query_notion_database. The concrete example (marking a calendar row Status "Posted" with a live post link) makes the intended job unmistakable.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Tells the agent where pageId comes from (query_notion_database) and clearly bifurcates the two modes: property edits via properties, versus trash:true/false with confirm:true. It does not explicitly say what to use instead for adding page content, so the sibling routing is implied rather than stated.
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 fileADestructiveIdempotentInspect
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.
| Name | Required | Description | Default |
|---|---|---|---|
| name | No | new name | |
| fileId | Yes | the OneDrive item id | |
| moveToFolderId | No | folder id to move the item into (from create_onedrive_folder / list_onedrive_files) |
TDQS
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.
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.
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.
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.
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.
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)ADestructiveIdempotentInspect
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.
| Name | Required | Description | Default |
|---|---|---|---|
| key | Yes | the saved row's key from list_swipefile, e.g. tiktok:handle | |
| note | No | replaces the existing note; pass "" to clear it | |
| brand | No | profile id/name from list_brands, this call only | |
| status | No | outreach status: new, contacted, replied, booked or passed |
TDQS
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 profile is covered structurally. The description adds genuinely useful non-schema facts — 'Free' and 'Reads back the updated row' — but says nothing about what destruction actually means here (the destructive hint is really just a note replacement) or any permission/auth requirement.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with the action and resource, then flow, then enum, then the lookup cue — all in four tight sentences with no filler. Even the trailing 'Free.' earns its place as a cost signal.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
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 full schema coverage, existing annotations and no output schema, the description covers purpose, prerequisite, allowed values and return behavior. Only gap is the absence of any partial-update/omission semantics narrative beyond what the schema sentences carry.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
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 repeats the enum values already enumerated in the schema and gives flavor for note content ('deal terms, rate, what was sent') but adds no semantics beyond what the fields already state.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource — set outreach status and/or note on an already-saved creator — and scopes it to the swipefile. The phrase 'a creator already saved in the swipefile' cleanly separates it from save_creator / save_to_swipefile siblings.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives the discovery prerequisite explicitly ('Use list_swipefile to find the key') and traces the upstream flow (find_creators -> save_to_swipefile, or the heart on a creator card). It stops short of naming when NOT to use it (e.g. delete_creator for removal), so not a full 5.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
update_settingsChange app settingsADestructiveIdempotentInspect
Change this account's app settings. language = the language EVERY ad, script and plan is written in from now on (chat replies follow the language the user writes in) (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.
| Name | Required | Description | Default |
|---|---|---|---|
| theme | No | app appearance | |
| language | No | language for generated ads and copy (chat replies follow the language the user writes in) — e.g. "English", "German", "Japanese" | |
| watchEmail | No | weekly competitor-watch email on/off |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already cover the safety profile (readOnly=false, destructiveHint=true, idempotentHint=true), so this is supplementary. The description adds genuinely useful context the annotations cannot convey: the change is account-wide across every profile, and it applies to MCP renders as well as in-app renders. It doesn't address rollback or validation failure behavior, so it isn't complete.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The core action and account-wide scope are front-loaded, and each field explanation earns its place. The nested parenthetical inside the language sentence is dense and could be split for faster scanning.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
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 carry the full burden — and it does: mutation scope, partial-update behavior, activation timing, and per-field semantics are all covered. Nothing an agent needs in order to invoke this correctly is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
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 goes beyond the schema on 'language' — it clarifies the setting governs all future ads, scripts and plans, that chat replies are exempt, and that plain-English names are expected. The theme and watchEmail entries largely restate their schema descriptions, so value is uneven rather than universal.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('Change this account's app settings'), and the field-by-field enumeration makes the scope unambiguous. It does not explicitly name get_settings as the read counterpart, so differentiation is by implication rather than stated, keeping it below a 5.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
'Only pass what you are changing' establishes partial-update semantics, and 'it takes effect on the next call' tells the agent when the change becomes live. There is no explicit exclusion or pointer to get_settings for verification, 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.
update_sheetWrite to a range in a Google SheetADestructiveIdempotentInspect
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.
| Name | Required | Description | Default |
|---|---|---|---|
| range | No | A1 range or anchor cell, e.g. "B2:C5", "B2", or "Q3 Report!B2" (default A1) | |
| values | No | array of row arrays to write | |
| confirm | No | required only when the target range already holds values | |
| updates | No | write SEVERAL disjoint ranges in one call, instead of range+values | |
| sheetUrl | No | Google Sheets link, instead of spreadsheetId | |
| spreadsheetId | No | Google spreadsheet id; use this or sheetUrl | |
| valueInputOption | No | USER_ENTERED (default) parses formulas, dates and numbers the way typing them would; RAW stores every value as literal text |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already mark the tool destructive, idempotent, and open-world, but the description adds critical behavior beyond them: overwrites are refused before confirmation, the user must approve the exact count of filled cells, and the result is read back from the sheet rather than trusting Google's response.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is front-loaded with the core action and safety model, and its length is mostly justified by the confirm/overwrite behavior. Some motivational phrasing about wrong numbers staying wrong forever is explanatory rather than essential, so it is slightly over-written but not bloated.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a destructive write tool with no output schema, the description covers the essential safety flow, range syntax, overwrite refusal, confirmation requirement, and read-back result. The remaining optional parameters are fully documented in the schema, so nothing critical is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
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 useful semantics for range A1 notation, anchor-cell expansion down and right, row-array values, and the confirm flow. It does not explain updates, sheetUrl, spreadsheetId, or valueInputOption beyond what the schema already provides.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific verb and resource: correcting/writing values to an exact range in a Google Sheet. It explicitly distinguishes the tool from append_to_sheet by explaining that appending cannot overwrite existing rows.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It gives clear when-to-use guidance versus append_to_sheet and explains the decision path: writes to empty cells pass through, while overwrites are refused and require showing the user the affected cells before retrying with confirm:true. This is explicit operational guidance, not implied usage.
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 the SUBSCRIPTION plan (admin only). No argument lists the monthly plans. Basic ($9.99) is the posting plan: unlimited posting and Autopilot with NO monthly credits (it includes the same 250+ free credits as the Free plan, monthly credits start on Starter, and Autopilot spends credits). Starter, Pro and Studio include monthly credits and no markup on models. Every paid plan posts without a limit (Free: 10 posts a month on 3 channels). New subscriber: a Checkout URL for the human. Existing subscriber upgrading: a quote + quote_token; once the human agrees, call again with confirm:true and quote_token to charge the card on file. Downgrade: a billing-page link; it starts at the term's end.
| Name | Required | Description | Default |
|---|---|---|---|
| plan | No | the plan id to move to (e.g. pro) — omit to list the available plans first | |
| period | No | monthly only ('yr' buys monthly) | |
| confirm | No | true (with quote_token) to make a quoted upgrade | |
| quote_token | No | the quoteToken from the quote step |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Goes far beyond the annotations: it discloses that a real card on file is charged on the confirm step, that upgrades are two-phase with a quote_token, that downgrades take effect at the term's end, and that pricing/credits differ per plan. The annotations only say it is a non-destructive, non-idempotent, open-world mutation; the description supplies the actual consequence and sequencing.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with the action, then plan economics, then the three call flows in order — a sensible structure. Slightly verbose on plan pricing and the phrase 'No argument lists the monthly plans' is grammatically awkward enough to need a second read, but nothing is truly wasted.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no output schema, the description covers what comes back on each path (Checkout URL, quote plus quote_token, billing-page link), which is exactly what an agent needs to chain the next call. For a 4-parameter, zero-required billing mutation this is complete.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
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 procedural meaning the schema alone does not — how quote_token and confirm:true pair together to actually charge the card, and what omitting plan does. Plan-tier economics (Basic vs Starter/Pro/Studio credits and markup) also inform the plan value choice.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Opens with a specific verb and resource ('Change the SUBSCRIPTION plan') plus an explicit admin-only qualifier, and the no-argument branch tells the agent the tool also enumerates plans. This separates it cleanly from billing/credit siblings such as buy_credits and billing_status.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Lays out three distinct usage paths — new subscriber (Checkout URL for a human), existing subscriber upgrade (quote then confirm), and downgrade (billing-page link, effective at term end) — so the agent knows which branch applies before calling. It does not name adjacent tools like buy_credits or billing_status as alternatives, which keeps it just 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.
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. Kept in the Library. 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}.
| Name | Required | Description | Default |
|---|---|---|---|
| url | No | a 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. | |
| name | No | original file name — helps pick the right extension | |
| path | No | local filesystem path (stdio/CLI only — refused on the hosted connector) | |
| dataUri | No | base64 data: URI of the file bytes (data:<mime>;base64,<…>) — bytes travel over this connection, so keep it small | |
| getUploadUrl | No | 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. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations are thin (readOnlyHint=false, openWorldHint=true, idempotentHint=false), and the description carries the real burden: 150MB cap, bytes crossing vs. not crossing the connection, hosted-connector disk invisibility, expiring one-time upload URL, and the return shape {url, kind, bytes}. It also documents the non-obvious raw-body/`?url=` behavior of the underlying POST /api/upload route.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with purpose and the BYO-path framing before the source-selection rules, and each paragraph maps to a distinct decision. It is, however, dense to the point of noise — heavy ALL-CAPS emphasis, a 17-tool sibling list, and a partially redundant restatement of the trusted-URL point in the LinkedIn/Meta paragraph.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 5-param, zero-required upload tool with no output schema, the description covers everything an agent needs: source selection, per-surface constraints, size limits, the returned object, and the fallback to a one-time upload URL when running a shell. Nothing material is left to inference.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is already 100%, so baseline would be 3, but the description adds semantics the schema cannot express: mutual exclusivity of `url`/`path`/`dataUri` (passing two is an error, not a preference), relative reliability ranking per surface, and the size tradeoffs that drive choosing `dataUri` vs `getUploadUrl`. That is genuine decision guidance beyond field-level docs.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Opens with a specific verb+resource+result ("Persist an ARBITRARY user file ... get back a durable public URL") and explicitly separates this from the render pipeline by labeling it "THE BRING-YOUR-OWN-CREATIVE PATH" for files with "NOTHING to do with a Hermoso render." It also enumerates the sibling tools that consume the returned URL, so the agent can place it precisely in the workflow.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives explicit selection rules among the three sources (exactly one; `url` always works and is preferred; `path` only on stdio/CLI; `dataUri` under ~15MB) and states when to route through this tool vs. passing an external link directly (LinkedIn, Pinterest, YouTube thumbnails, Google Ads refuse external hosts). It closes with a default heuristic, "When in doubt, use this."
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). Three 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; engine:'detail' rebuilds fine detail such as hair, skin and fabric, the sharpest result for a hero shot and the most expensive. Pick 'flux' or 'detail' when the user asks for added detail rather than a faithful enlargement.
| Name | Required | Description | Default |
|---|---|---|---|
| mode | No | FLUX only — 'creative' turns on its detail-enhancement pass; default precise | |
| video | Yes | the source video URL | |
| engine | No | default 'standard', the precision upscaler. 'flux' = the FLUX 3 video upscaler. 'detail' = rebuilds fine detail, costs the most |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare this is a non-read-only, non-idempotent, open-world write. The description adds information annotations cannot: it is a paid render, it returns a served URL, and the engines differ sharply in cost ('the most expensive'). It does not say whether a re-run bills again or what resolution a plain 2x yields on smaller sources, but the cost/model disclosure is well above 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.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with the action, the 2x scope, the fact it is paid, and the return value before any engine detail. Four sentences are dense but each carries decision-relevant content; the parenthetical asides ('same take, native 1080p') are slightly compressed but not wasteful.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
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 states it returns the served URL, and with only three fully described parameters nothing structural is missing. Minor gaps remain around billing on repeat calls and how 2x interacts with the flux engine's 1080p/2K/4K targets.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
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 earns credit by framing the engine choice in user-intent terms ('faithful enlargement' vs 'added detail', 'hero shot', 'safe precision upscaler') and by noting mode:'creative' is tied only to 'flux'. Some of this overlaps the schema text, keeping it below 5.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('Upscale a video to higher resolution (2x) for final delivery') and immediately distinguishes itself from neighboring operations by naming finish_draft as the draft-finishing path. The three engine variants further sharpen what this tool is versus a generic 'restyle' or 'reframe' sibling.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicit routing advice is given: use finish_draft for a 480p Seedance draft, and 'Pick flux or detail when the user asks for added detail rather than a faithful enlargement.' It also states 'standard' is the safe default, so the agent knows both when to use this tool and when to escalate engines.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
use_brandSwitch profileAIdempotentInspect
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).
| Name | Required | Description | Default |
|---|---|---|---|
| brand | Yes | profile id (e.g. default / p_xxx), its exact name from list_brands, or the profile id of one shared with you |
TDQS
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.
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.
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.
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.
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.
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.
41 tool updates
v0.1.519- Removed
autofill_canva_template - Changed
connect_connector1 field changed- changed
Input schema / properties / provider / descriptionPrevious value: -"the connector id: applovin_ads, stripe, intercom, vercel, openai_ads, apple_ads, bluesky, telegram, bing_webmaster, posthog, mixpanel, amplitude, slack, discord, webhook"New value: +"the connector id: applovin_ads, stripe, intercom, mailchimp, klaviyo, vercel, openai_ads, apple_ads, bluesky, telegram, bing_webmaster, posthog, mixpanel, amplitude, wordpress, slack, discord, webhook"
- Removed
create_canva_folder - Changed
diagnose_posts1 field changed- changed
Input schema / properties / channel / descriptionPrevious value: -"restrict to one channel: facebook, instagram, threads, x, linkedin, youtube, tiktok, reddit, pinterest"New value: +"restrict to one channel: facebook, instagram, threads, x, linkedin, youtube, tiktok, pinterest, bluesky"
- Changed
disconnect_connector1 field changed- changed
Input schema / properties / account / descriptionPrevious value: -"on 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"New value: +"on a channel with several connected accounts (TikTok, X, YouTube, Threads, Bluesky, Telegram, Pinterest): remove ONLY this account (@handle or id from list_connector_accounts) and keep the others"
- Changed
duplicate_scheduled2 fields changed- changed
Input schema / properties / channels / items / enumPrevious value: -[ - "facebook", - "instagram", - "threads", - "tiktok", - "youtube", - "linkedin", - "x", - "pinterest", - "google_business", - "bluesky", - "telegram" -]New value: +[ + "facebook", + "instagram", + "threads", + "tiktok", + "youtube", + "linkedin", + "x", + "pinterest", + "google_business", + "bluesky", + "telegram", + "wordpress", + "discord", + "slack", + "whop" +] - added
Input schema / properties / whopExperienceIdAdded value: +{ + "description": "WHOP — which forum or chat the copy goes to (exp_…, from list_whop_destinations).", + "type": "string" +}
- Changed
edit_image1 field changed- added
Input schema / properties / logoAdded value: +{ + "type": "boolean" +}
- Changed
edit_video1 field changed- added
Input schema / properties / textChangesAdded value: +{ + "description": "EVERY change of on-screen words at once ([{from, to}], e.g. each line and its translation). A video laid out from a design is made again with all of them in ONE render, about 2 credits, never a video edit", + "items": { + "properties": { + "from": { + "type": "string" + }, + "to": { + "type": "string" + } + }, + "required": [ + "from", + "to" + ], + "type": "object" + }, + "type": "array" +}
- Removed
export_canva_design - Changed
fix_beat9 fields changed- added
Input schema / properties / atSecondsAdded value: +{ + "description": "with `job`: the moment in the film instead of `block` (\"the line at 0:12\" = 12)", + "type": "number" +} - added
Input schema / properties / blockAdded value: +{ + "description": "with `job`: which block, 1-based (block 3 = 20-30s of a film of 10s blocks)", + "type": "number" +} - changed
Input schema / properties / endSeconds / descriptionPrevious value: -"window end in seconds (window 1.5-8s)"New value: +"window end in seconds (window 1.5-8s; not needed with `job`)" - added
Input schema / properties / jobAdded value: +{ + "description": "a finished video-blocks film (explainer / faceless channel): its job id. Edits one block of it instead of splicing a window", + "type": "string" +} - added
Input schema / properties / lineAdded value: +{ + "description": "with `job`: the new narration for that block, the exact words to say (said as written, never rewritten; refused for free when it cannot fit the block)", + "type": "string" +} - changed
Input schema / properties / prompt / descriptionPrevious value: -"what the replacement footage should show — describe the shot, matching the master's style"New value: +"what the replacement footage should show — describe the shot, matching the master's style. With `job`: the change to that block's picture, in words" - changed
Input schema / properties / startSeconds / descriptionPrevious value: -"window start in seconds"New value: +"window start in seconds (not needed with `job`)" - changed
Input schema / properties / videoUrl / descriptionPrevious value: -"the served URL of the master video to fix"New value: +"the served URL of the master video to fix (not needed with `job`)" - removed
Input schema / requiredRemoved value: -[ - "videoUrl", - "startSeconds", - "endSeconds", - "prompt" -]
- Changed
generate_image2 fields changed- added
Input schema / properties / jobAdded value: +{ + "description": "what the picture is for. With no `model` named it renders on the engine judged best for that job (hermoso_capabilities `jobEngines.image` lists them): static_ad (the default when neither `model` nor `job` is given) = a finished image ad with its headline, copy and button on the picture; product_photo = a studio, lifestyle, flat-lay or spotlight photo of the real product; marketplace = a listing card; thumbnail = a video cover (make_thumbnail is the full thumbnail pipeline); character_sheet = a multi-view sheet of one person; vector = a logo, icon or flat illustration as an editable SVG (it takes no reference pictures, so the saved brand's photos stay off it). A named `model` always wins", + "enum": [ + "static_ad", + "product_photo", + "marketplace", + "thumbnail", + "character_sheet", + "vector" + ], + "type": "string" +} - changed
Input schema / properties / logoPlacement / descriptionPrevious value: -"overlay: real logo file laid flat. in_scene: on something in the scene, from the file, checked. auto (default): from the prompt"New value: +"overlay: real logo file laid flat. in_scene: the scene leaves a blank patch on the object and the real file is printed onto it (perspective, light). Never drawn by the model. auto (default): from the prompt"
- Changed
generate_video1 field changed- changed
Input schema / properties / resolution / descriptionPrevious value: -"'1080p' default; '480p'/'720p' cheaper drafts, '4k' premium (more credits). A model renders only the tiers in its own `resolutions` list in hermoso_capabilities; a tier it lacks renders at its best one without a word, so check before promising 1080p or 4k."New value: +"Omit = the default tier, hermoso_capabilities defaultResolution (720p), the tier its `credits` prices are for. 1080p is ~2.25x, 4k more, 480p less: quote creditsByRes and pass the tier you quoted. A tier a model's `resolutions` lacks renders at its best one."
- Changed
generate_voice6 fields changed- added
Input schema / properties / dryRunAdded value: +{ + "description": "true = the exact credits only, nothing made or charged", + "type": "boolean" +} - changed
Input schema / properties / engine / descriptionPrevious value: -"voice-engine id: 'seed-audio' (default), 'eleven-v4' (Eleven v4), 'minimax-speech', or 'kokoro' — listed in hermoso_capabilities"New value: +"voice-engine id: one voice defaults to 'seed-audio'; a dialogue defaults to the dialogue model ('eleven-dialogue'); or 'eleven-v4', 'minimax-speech', 'kokoro'… (hermoso_capabilities)" - added
Input schema / properties / linesAdded value: +{ + "description": "DIALOGUE: the turns in speaking order", + "items": { + "properties": { + "speaker": { + "description": "who speaks; the same name keeps the same voice", + "type": "string" + }, + "text": { + "description": "what they say", + "type": "string" + }, + "voice": { + "description": "that speaker's voice from the engine's list (or an ElevenLabs voice id); omit to be given one", + "type": "string" + } + }, + "required": [ + "text" + ], + "type": "object" + }, + "type": "array" +} - changed
Input schema / properties / text / descriptionPrevious value: -"the script to speak (≤900 characters)"New value: +"ONE VOICE: the script to speak (≤900 characters)" - changed
Input schema / properties / voice / descriptionPrevious value: -"a voice preset from the chosen engine (e.g. 'Aria'/'George' on eleven-v4, 'stokie_en' on seed-audio) — omit for the engine default"New value: +"ONE VOICE: a preset from the chosen engine (e.g. 'Aria'/'George' on eleven-v4, 'stokie_en' on seed-audio); omit for the engine default" - removed
Input schema / requiredRemoved value: -[ - "text" -]
- Changed
get_job1 field changed- changed
Input schema / properties / id / descriptionPrevious value: -"the job id, e.g. job_xxx"New value: +"the job id: starts with job_, from a render tool's jobId or from list_jobs (generate_voice and generate_sfx return their file directly and have no job)"
- Removed
list_canva_brand_templates - Removed
list_canva_designs - Removed
list_canva_folder - Changed
list_connector_accounts1 field changed- changed
Input schema / properties / provider / enumPrevious value: -[ - "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" -]New value: +[ + "tiktok", + "x", + "youtube", + "threads", + "bluesky", + "telegram", + "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" +]
- Changed
list_hooks1 field changed- changed
Input schema / properties / channel / descriptionPrevious value: -"restrict the performance half to one channel (facebook, instagram, threads, x, linkedin, youtube, tiktok, reddit, pinterest)"New value: +"restrict the performance half to one channel (facebook, instagram, threads, x, linkedin, youtube, tiktok, pinterest, bluesky)"
- Changed
list_library2 fields changed- changed
Input schema / properties / kind / enumPrevious value: -[ - "image", - "video", - "all" -]New value: +[ + "image", + "video", + "document", + "all" +] - added
Input schema / properties / uploadsAdded value: +{ + "type": "boolean" +}
- Changed
list_published_posts1 field changed- changed
Input schema / properties / channel / descriptionPrevious value: -"filter to one channel: facebook, instagram, threads, x, linkedin, youtube, tiktok, reddit, pinterest, google_business"New value: +"filter to one channel: facebook, instagram, threads, x, linkedin, youtube, tiktok, pinterest, bluesky, google_business"
- Added
make_effect - Changed
make_explainer5 fields changed- changed
Input schema / properties / captions / descriptionPrevious value: -"turn 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."New value: +"turn 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 text (CAPS in the stills lane), thin black outline, bottom safe band, no plate, no box." - changed
Input schema / properties / format / descriptionPrevious value: -"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)."New value: +"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, runs exactly the length asked: a last part-block is rendered whole and cut). Omit and a history / kids / fairytale channel is a faceless channel video. 'host_episode' = the AI host episode (see the top)." - added
Input schema / properties / fromFilmAdded value: +{ + "description": "A PRESENTER ON A FINISHED FILM (only when the user asks for one): the job id of a finished video-blocks explainer or faceless-channel film. With `presenter`, that film comes back with a presenter speaking each block's line on screen; every block keeps its own picture and the film without the presenter is kept (cleanVideo). Never add a presenter unasked.", + "type": "string" +} - added
Input schema / properties / presenterAdded value: +{ + "description": "with fromFilm: who presents and how. A face the video model flags stops it, nothing charged, with the choices to offer.", + "properties": { + "creator": { + "description": "a saved creator or a preset, by name or id (list_creators)", + "type": "string" + }, + "image": { + "description": "or a photo / avatar of the presenter (upload_file for a local file); a real person's face follows the real-face rules", + "type": "string" + }, + "look": { + "description": "'corner' (default): cut out of a green screen, standing in a bottom corner; 'badge': a round badge in a corner; 'split': the block on one half, the presenter on the other", + "enum": [ + "corner", + "badge", + "split" + ], + "type": "string" + }, + "position": { + "description": "'br' (default), 'bl', 'tr' or 'tl' (a corner cutout stays at the bottom); for split, 'bl' puts the presenter first", + "type": "string" + }, + "scale": { + "description": "the share of the frame the presenter takes, 0.2-0.8 (default: the look's own size)", + "type": "number" + }, + "voice": { + "description": "the presenter's voice (default: the film's own narrator)", + "type": "string" + } + }, + "type": "object" +} - changed
Input schema / properties / subtitles / descriptionPrevious value: -"which 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."New value: +"which on-screen text, once `captions` is on. LEAVE IT UNSET (or true) for SUBTITLES — every spoken word, in order, timed to the narration; no extra render, and there is NO cue limit, so the whole film is subtitled however long it runs (at most 4 words and one line a cue; 5 words / 32 characters in stills). 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: in the default blocks lane each cue is timed to its own spoken words (one listen to the narration, a tiny charge inside the quote; per-block timing, said, if it fails). In the stills lane cues are spread over each section’s measured narration by character count, exact at section edges, a few tenths off within one: it is not a word-level speech clock, so never promise frame-accurate sync there."
- Changed
make_motion_film6 fields changed- added
Input schema / properties / briefAdded value: +{ + "description": "what the film is about, for the planner", + "type": "string" +} - added
Input schema / properties / energyAdded value: +{ + "description": "how much the film keeps moving, 0 to 1; 0 films exactly what is written", + "type": "number" +} - added
Input schema / properties / modelAdded value: +{ + "description": "image-to-video model id from hermoso_capabilities; default is the best value for short shots", + "type": "string" +} - added
Input schema / properties / movesAdded value: +{ + "description": "per scene, what happens in its picture; omit and the planner writes them", + "items": { + "properties": { + "action": { + "description": "the one thing that physically happens, in plain words", + "type": "string" + }, + "layer": { + "description": "the picture layer's id, when the scene has several", + "type": "string" + }, + "matchNext": { + "description": "the shot ends on the next scene's picture", + "type": "boolean" + }, + "scene": { + "description": "scene id", + "type": "string" + }, + "still": { + "description": "a picture to animate, when not the one in the scene", + "type": "string" + }, + "stillPrompt": { + "description": "a picture to paint first, for a scene with none", + "type": "string" + }, + "video": { + "description": "a shot already filmed: kept, and not paid for again", + "type": "string" + } + }, + "required": [ + "scene", + "action" + ], + "type": "object" + }, + "type": "array" +} - added
Input schema / properties / movingAdded value: +{ + "description": "true: one thing physically happens in each picture, filmed by a video model. false: stills moved by a virtual camera, far cheaper. Default true for launch, product, social", + "type": "boolean" +} - added
Input schema / properties / resolutionAdded value: +{ + "description": "the moving shots: 720p or 1080p (default) where the model has them", + "type": "string" +}
- Changed
make_series1 field changed- added
Input schema / properties / moreOfAdded value: +{ + "description": "MORE EPISODES: the `plan` an earlier call returned. Keeps its style and length, writes new episodes only" +}
- Changed
make_template_ad1 field changed- changed
Input schema / properties / config / descriptionPrevious 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, which takes config.music like any video) }; 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? } is OPTIONAL on every video preset: include it ONLY when the user asked for an end card (a closing card with their logo); without it the video ends on its own last frame. palette and fontStack optional."New value: +"MUST include config.template: 'custom' or a preset id, plus its fields; language ('Portuguese') for its own words. 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, which takes config.music like any video) }; 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? } is OPTIONAL on every video preset: include it ONLY when the user asked for an end card (a closing card with their logo); without it the video ends on its own last frame. palette and fontStack optional."
- Changed
make_thumbnail1 field changed- changed
Input schema / properties / logoPlacement / descriptionPrevious value: -"overlay (exact, flat) | in_scene (from the file, checked); auto: in_scene with logo3d"New value: +"overlay (exact, flat) | in_scene (the real file printed onto a blank panel in the shot; with logo3d, a 3D render of it, checked); auto: in_scene with logo3d"
- Changed
multiply_ad4 fields changed- added
Input schema / properties / imagesAdded value: +{ + "description": "up to 9 pictures to put in the variants, in the order @Image1, @Image2 name them: URLs or local paths", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "properties": { + "role": { + "description": "what it is, in your words, e.g. \"our founder\"", + "type": "string" + }, + "url": { + "type": "string" + } + }, + "required": [ + "url" + ], + "type": "object" + } + ] + }, + "type": "array" +} - added
Input schema / properties / qualityAdded value: +{ + "description": "1080p is the default; 720p costs less, and hands holding a product are less reliable at 720p", + "enum": [ + "1080p", + "720p" + ], + "type": "string" +} - added
Input schema / properties / useBrandAdded value: +{ + "description": "false = send no brand product photo (for a video that is not this workspace brand)", + "type": "boolean" +} - changed
Input schema / properties / video / descriptionPrevious value: -"the source video URL"New value: +"the source video: a file URL, or a TikTok, Reel, Facebook, X or YouTube post link"
- Changed
plan_ad8 fields changed- added
Input schema / properties / conceptsAdded value: +{ + "description": "several variants: one line per concept (a different angle each); built exactly", + "items": { + "type": "string" + }, + "maxItems": 4, + "type": "array" +} - changed
Input schema / properties / inserts / descriptionPrevious value: -"VIDEO ONLY: the user's own SCREENS (an app, a page, a dashboard, a document, a review, a chart) shown at these seconds as a rounded card over the upper middle of the footage, which keeps playing around it (look 'full' covers the frame, only when the user asked), the voice running underneath. Fewer and shorter: about one per 4 s of film, 1.2-2 s each, on the line that talks about it; never in the first second or the last 1.5 s. The plan frames those shots for the card, keeps them free of screens, and returns the pictures as creative.inserts for render_ad to lay in (about 2 credits). A photo of a person, product or place (kind 'photo') is not laid in: pass a product photo as the brand's product photo and a person as a saved creator. One that does not fit is left out and the reply says so."New value: +"VIDEO ONLY: the user's own SCREENS cut in at these seconds (each item's look, kind, start and end say how), the voice running underneath. How many: at most 8, about one per 2.5 s of film (6 s: 2, 10 s: 4, 15 s: 6, 20 s: 8, 30 s: 8), 1.2-2 s each, on the line that talks about it. When there are more, the most crowded are left out first, never the last (the payoff) or one marked key, and the reply names them. The plan keeps those shots free of screens and returns the pictures as creative.inserts for render_ad to lay in (about 2 credits). One that does not fit is left out and the reply says so." - added
Input schema / properties / inserts / items / properties / focusAdded value: +{ + "description": "the readable part to fill the frame with, as fractions of the picture from its top-left (e.g. the flagged-items table); a crop as tall as wide, or taller, reads best on a phone. Omit for the whole picture, plain margins trimmed", + "properties": { + "h": { + "type": "number" + }, + "w": { + "type": "number" + }, + "x": { + "type": "number" + }, + "y": { + "type": "number" + } + }, + "required": [ + "x", + "y", + "w", + "h" + ], + "type": "object" +} - added
Input schema / properties / inserts / items / properties / keyAdded value: +{ + "description": "true = the story needs this one (the payoff): never left out when there are more screens than the length takes", + "type": "boolean" +} - changed
Input schema / properties / inserts / items / properties / kind / descriptionPrevious value: -"screen (default): an app, a page, a dashboard, a document, a review, a chart. photo: a person, a product or a place, which is NOT laid over the video (a product photo goes in as the brand's product photo, a person to star as a saved creator)"New value: +"screen (default): an app, page, dashboard, document, review or chart. photo: a person, product or place, NOT laid over the video (a product photo goes in as the brand's product photo, a person as a saved creator)" - changed
Input schema / properties / inserts / items / properties / look / descriptionPrevious value: -"card (default): a rounded card over the upper middle of the footage, which keeps playing. full: covers the whole frame, ONLY when the user asked for full-screen"New value: +"cutaway (default): a hard cut to the screen filling the frame, cropped to focus, held still, never enlarged past 1.25x its pixels (any gap is the screenshot's own background colour; a screen too small to read is left out and said). card: picture-in-picture over the playing footage, clear of the speaker's face. full: the whole picture uncropped on a blurred backdrop. card and full ONLY when the user asked" - changed
Input schema / properties / inserts / items / properties / look / enumPrevious value: -[ - "card", - "full" -]New value: +[ + "cutaway", + "card", + "full" +] - changed
Input schema / properties / inserts / items / properties / start / descriptionPrevious value: -"second it appears (never before 1 s: the film opens on moving footage)"New value: +"second it appears, never before 1 s (the film opens on moving footage)"
- Added
plan_week - Changed
post_to_meta2 fields changed- changed
Input schema / properties / pageId / descriptionPrevious value: -"target Page id (from list_meta_pages); omit = first Page"New value: +"target Page id (from list_meta_pages). Omit when the brand has one Page. With several and none named the call refuses and lists them by name and id: ask the user which Page, never pick one yourself." - changed
Input schema / properties / story / descriptionPrevious value: -"INSTAGRAM OR FACEBOOK PAGE 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 link sticker cannot be added through Meta’s publishing API on either channel: add it in the Instagram app. An Instagram Story keeps its caption: people see the start under the Story in the app and tap for the rest (instagram.com does not show it). It takes NO collaborators, product tags or trialReel — any of those is refused by name and nothing is posted. FACEBOOK PAGE STORIES WORK TOO (target facebook): one image, or one vertical 9:16 video of 3–90 seconds, no caption (a message is refused by name), not schedulable natively — use schedule_post to queue one; the reply reads the story back from the Page."New value: +"INSTAGRAM OR FACEBOOK PAGE 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. Link stickers are not available on a Story posted here. An Instagram Story keeps its caption: people see the start under the Story in the mobile app and tap for the rest. It takes NO collaborators, product tags or trialReel — any of those is refused by name and nothing is posted. FACEBOOK PAGE STORIES WORK TOO (target facebook): one image, or one vertical 9:16 video of 3–90 seconds, no caption (a message is refused by name), not schedulable natively — use schedule_post to queue one; the reply reads the story back from the Page."
- Changed
product_sizzle1 field changed- changed
Input schema / properties / aspectRatio / descriptionPrevious value: -"'9:16' default; anything the seedance-2 catalog entry does not list falls back to 9:16"New value: +"'9:16' default; anything the Seedance row that renders it does not list falls back to 9:16"
- Changed
render_ad10 fields changed- changed
Input schema / properties / inserts / descriptionPrevious value: -"the user's own screens laid over the finished video at these seconds, as a card over the playing footage by default (look 'full' covers the frame, only when asked), sound running underneath (about 2 credits). The film always opens and ends on moving footage. Omit to use plan_ad's creative.inserts; [] renders without them. plan_ad with inserts writes the board around them."New value: +"the user's own screens laid over the finished video at these seconds (each item's look says how), sound running underneath (about 2 credits). The film always opens and ends on moving footage. Omit to use plan_ad's creative.inserts; [] renders without them. plan_ad with inserts writes the board around them." - added
Input schema / properties / inserts / items / properties / focusAdded value: +{ + "description": "the readable part to fill the frame with, as fractions of the picture from its top-left (e.g. the flagged-items table); a crop as tall as wide, or taller, reads best on a phone. Omit for the whole picture, plain margins trimmed", + "properties": { + "h": { + "type": "number" + }, + "w": { + "type": "number" + }, + "x": { + "type": "number" + }, + "y": { + "type": "number" + } + }, + "required": [ + "x", + "y", + "w", + "h" + ], + "type": "object" +} - added
Input schema / properties / inserts / items / properties / keyAdded value: +{ + "description": "true = the story needs this one (the payoff): never left out when there are more screens than the length takes", + "type": "boolean" +} - changed
Input schema / properties / inserts / items / properties / kind / descriptionPrevious value: -"screen (default): an app, a page, a dashboard, a document, a review, a chart. photo: a person, a product or a place, which is NOT laid over the video (a product photo goes in as the brand's product photo, a person to star as a saved creator)"New value: +"screen (default): an app, page, dashboard, document, review or chart. photo: a person, product or place, NOT laid over the video (a product photo goes in as the brand's product photo, a person as a saved creator)" - changed
Input schema / properties / inserts / items / properties / look / descriptionPrevious value: -"card (default): a rounded card over the upper middle of the footage, which keeps playing. full: covers the whole frame, ONLY when the user asked for full-screen"New value: +"cutaway (default): a hard cut to the screen filling the frame, cropped to focus, held still, never enlarged past 1.25x its pixels (any gap is the screenshot's own background colour; a screen too small to read is left out and said). card: picture-in-picture over the playing footage, clear of the speaker's face. full: the whole picture uncropped on a blurred backdrop. card and full ONLY when the user asked" - changed
Input schema / properties / inserts / items / properties / look / enumPrevious value: -[ - "card", - "full" -]New value: +[ + "cutaway", + "card", + "full" +] - changed
Input schema / properties / inserts / items / properties / start / descriptionPrevious value: -"second it appears (never before 1 s: the film opens on moving footage)"New value: +"second it appears, never before 1 s (the film opens on moving footage)" - changed
Input schema / properties / resolution / descriptionPrevious value: -"'1080p' default; '480p'/'720p' cheaper drafts, '4k' premium (more credits). A model renders only the tiers in its own `resolutions` list in hermoso_capabilities (the 30s model tops out at 720p); a tier it lacks renders at its best one without a word, so check before promising 1080p or 4k."New value: +"Omit = the default tier, hermoso_capabilities defaultResolution (720p), the tier its `credits` prices are for. 1080p is ~2.25x, 4k more, 480p less: quote creditsByRes and pass the tier you quoted. A tier a model's `resolutions` lacks renders at its best one." - added
Input schema / properties / screenChoiceAdded value: +{ + "description": "the user's answer to needsAppScreens: brand = their saved app screens, site = found on their website (saved to the brand), without = none, device kept turned away", + "enum": [ + "brand", + "site", + "without" + ], + "type": "string" +} - added
Input schema / properties / screensAdded value: +{ + "description": "real screenshots of the app or site for the shots that film it on a screen (URLs, upload_file URLs or Library ids), laid over them as cards", + "items": { + "type": "string" + }, + "maxItems": 6, + "type": "array" +}
- Added
repurpose_video - Changed
reschedule_post22 fields changed- changed
Input schema / properties / channels / items / enumPrevious value: -[ - "facebook", - "instagram", - "threads", - "tiktok", - "youtube", - "linkedin", - "x", - "pinterest", - "google_business", - "bluesky", - "telegram" -]New value: +[ + "facebook", + "instagram", + "threads", + "tiktok", + "youtube", + "linkedin", + "x", + "pinterest", + "google_business", + "bluesky", + "telegram", + "wordpress", + "discord", + "slack", + "whop" +] - added
Input schema / properties / discordChannelAdded value: +{ + "description": "DISCORD bot: channel id or #name (default: the brand's)", + "type": "string" +} - added
Input schema / properties / discordMentionsAdded value: +{ + "description": "DISCORD: let mentions ping", + "type": "boolean" +} - added
Input schema / properties / discordReplyToAdded value: +{ + "description": "DISCORD bot: reply to this message id", + "type": "string" +} - added
Input schema / properties / discordServerAdded value: +{ + "description": "DISCORD bot: which server", + "type": "string" +} - added
Input schema / properties / discordStartThreadAdded value: +{ + "description": "DISCORD bot: open a thread under the post, this title", + "type": "string" +} - added
Input schema / properties / excerptAdded value: +{ + "description": "WORDPRESS — the excerpt under the title", + "type": "string" +} - changed
Input schema / properties / silent / descriptionPrevious value: -"TELEGRAM ONLY — no notification sound."New value: +"TELEGRAM / DISCORD — no notification." - added
Input schema / properties / slackChannelAdded value: +{ + "description": "SLACK app: channel id or #name (default: the brand's)", + "type": "string" +} - added
Input schema / properties / slackThreadTsAdded value: +{ + "description": "SLACK app: reply in this thread (parent ts)", + "type": "string" +} - added
Input schema / properties / slackWorkspaceAdded value: +{ + "description": "SLACK app: which workspace", + "type": "string" +} - added
Input schema / properties / threadNameAdded value: +{ + "description": "DISCORD: forum thread title", + "type": "string" +} - added
Input schema / properties / whopExperienceIdAdded value: +{ + "description": "WHOP — move it to a different forum or chat (exp_…, from list_whop_destinations).", + "type": "string" +} - added
Input schema / properties / whopMentionAdded value: +{ + "description": "WHOP: notify every member", + "type": "boolean" +} - added
Input schema / properties / whopPaywallCentsAdded value: +{ + "description": "WHOP: forum-post paywall in cents (needs a title)", + "maximum": 9007199254740991, + "minimum": 0, + "type": "integer" +} - added
Input schema / properties / whopPaywallCurrencyAdded value: +{ + "description": "WHOP: paywall currency", + "type": "string" +} - added
Input schema / properties / whopPinnedAdded value: +{ + "description": "WHOP: pin the forum post", + "type": "boolean" +} - added
Input schema / properties / whopPollAdded value: +{ + "description": "WHOP: poll options, 2 to 10", + "items": { + "type": "string" + }, + "type": "array" +} - added
Input schema / properties / whopVisibilityAdded value: +{ + "description": "WHOP: who can see the forum post", + "enum": [ + "members_only", + "globally_visible" + ], + "type": "string" +} - added
Input schema / properties / wpCategoriesAdded value: +{ + "description": "WORDPRESS — categories (names or ids)", + "items": { + "type": "string" + }, + "type": "array" +} - added
Input schema / properties / wpSlugAdded value: +{ + "description": "WORDPRESS — the post slug", + "type": "string" +} - added
Input schema / properties / wpTagsAdded value: +{ + "description": "WORDPRESS — tags (names or ids)", + "items": { + "type": "string" + }, + "type": "array" +}
- Added
resume_job - Changed
retry_scheduled2 fields changed- changed
Input schema / properties / channels / items / enumPrevious value: -[ - "facebook", - "instagram", - "threads", - "tiktok", - "youtube", - "linkedin", - "x", - "pinterest", - "google_business", - "bluesky", - "telegram" -]New value: +[ + "facebook", + "instagram", + "threads", + "tiktok", + "youtube", + "linkedin", + "x", + "pinterest", + "google_business", + "bluesky", + "telegram", + "wordpress", + "discord", + "slack", + "whop" +] - added
Input schema / properties / whopExperienceIdAdded value: +{ + "description": "WHOP — correct the forum or chat on retry (exp_…, from list_whop_destinations).", + "type": "string" +}
- Removed
save_to_canva - Changed
schedule_post24 fields changed- changed
Input schema / properties / channels / items / enumPrevious value: -[ - "facebook", - "instagram", - "threads", - "tiktok", - "youtube", - "linkedin", - "x", - "pinterest", - "google_business", - "bluesky", - "telegram" -]New value: +[ + "facebook", + "instagram", + "threads", + "tiktok", + "youtube", + "linkedin", + "x", + "pinterest", + "google_business", + "bluesky", + "telegram", + "wordpress", + "discord", + "slack", + "whop" +] - added
Input schema / properties / discordChannelAdded value: +{ + "description": "DISCORD bot: channel id or #name (default: the brand's)", + "type": "string" +} - added
Input schema / properties / discordMentionsAdded value: +{ + "description": "DISCORD: let mentions ping", + "type": "boolean" +} - added
Input schema / properties / discordReplyToAdded value: +{ + "description": "DISCORD bot: reply to this message id", + "type": "string" +} - added
Input schema / properties / discordServerAdded value: +{ + "description": "DISCORD bot: which server", + "type": "string" +} - added
Input schema / properties / discordStartThreadAdded value: +{ + "description": "DISCORD bot: open a thread under the post, this title", + "type": "string" +} - added
Input schema / properties / excerptAdded value: +{ + "description": "WORDPRESS — the excerpt under the title", + "type": "string" +} - changed
Input schema / properties / pageId / descriptionPrevious 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."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: a Facebook post with several and no id is refused WHEN YOU SCHEDULE IT, with the Pages listed by name and id, so ask the user which Page and schedule again with its id. It is never sent from a guessed Page." - changed
Input schema / properties / silent / descriptionPrevious value: -"TELEGRAM ONLY — no notification sound."New value: +"TELEGRAM / DISCORD — no notification." - added
Input schema / properties / slackChannelAdded value: +{ + "description": "SLACK app: channel id or #name (default: the brand's)", + "type": "string" +} - added
Input schema / properties / slackThreadTsAdded value: +{ + "description": "SLACK app: reply in this thread (parent ts)", + "type": "string" +} - added
Input schema / properties / slackWorkspaceAdded value: +{ + "description": "SLACK app: which workspace", + "type": "string" +} - changed
Input schema / properties / story / descriptionPrevious value: -"INSTAGRAM OR FACEBOOK PAGE 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 link sticker cannot be added through Meta’s publishing API on either channel: add it in the Instagram app. An Instagram Story keeps its caption: people see the start under the Story in the app and tap for the rest (instagram.com does not show it). It takes NO collaborators, product tags or trialReel — any of those is refused by name and nothing is posted. FACEBOOK PAGE STORIES WORK TOO (target facebook): one image, or one vertical 9:16 video of 3–90 seconds, no caption (a message is refused by name), not schedulable natively — use schedule_post to queue one; the reply reads the story back from the Page."New value: +"INSTAGRAM OR FACEBOOK PAGE 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. Link stickers are not available on a Story posted here. An Instagram Story keeps its caption: people see the start under the Story in the mobile app and tap for the rest. It takes NO collaborators, product tags or trialReel — any of those is refused by name and nothing is posted. FACEBOOK PAGE STORIES WORK TOO (target facebook): one image, or one vertical 9:16 video of 3–90 seconds, no caption (a message is refused by name), not schedulable natively — use schedule_post to queue one; the reply reads the story back from the Page." - added
Input schema / properties / threadNameAdded value: +{ + "description": "DISCORD: forum thread title", + "type": "string" +} - added
Input schema / properties / whopExperienceIdAdded value: +{ + "description": "WHOP — which forum or chat in the whop (exp_…, from list_whop_destinations). Omit it when the whop has only one; with several and none named the post is refused at fire time, never guessed.", + "type": "string" +} - added
Input schema / properties / whopMentionAdded value: +{ + "description": "WHOP FORUM POSTS ONLY — notify every member (off by default).", + "type": "boolean" +} - added
Input schema / properties / whopPaywallCentsAdded value: +{ + "description": "WHOP FORUM POSTS ONLY — a paywall in cents (500 = $5.00): readers pay to see the body; needs a title.", + "maximum": 9007199254740991, + "minimum": 0, + "type": "integer" +} - added
Input schema / properties / whopPaywallCurrencyAdded value: +{ + "description": "WHOP — the paywall currency, a 3-letter code (default usd).", + "type": "string" +} - added
Input schema / properties / whopPinnedAdded value: +{ + "description": "WHOP FORUM POSTS ONLY — pin it to the top of the forum.", + "type": "boolean" +} - added
Input schema / properties / whopPollAdded value: +{ + "description": "WHOP FORUM POSTS ONLY — a poll: 2 to 10 options, in order.", + "items": { + "type": "string" + }, + "type": "array" +} - added
Input schema / properties / whopVisibilityAdded value: +{ + "description": "WHOP FORUM POSTS ONLY — who can see it; unset is the forum’s own default.", + "enum": [ + "members_only", + "globally_visible" + ], + "type": "string" +} - added
Input schema / properties / wpCategoriesAdded value: +{ + "description": "WORDPRESS — categories (names or ids)", + "items": { + "type": "string" + }, + "type": "array" +} - added
Input schema / properties / wpSlugAdded value: +{ + "description": "WORDPRESS — the post slug", + "type": "string" +} - added
Input schema / properties / wpTagsAdded value: +{ + "description": "WORDPRESS — tags (names or ids)", + "items": { + "type": "string" + }, + "type": "array" +}
- Changed
set_connector_accounts1 field changed- changed
Input schema / properties / provider / enumPrevious value: -[ - "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" -]New value: +[ + "tiktok", + "x", + "youtube", + "threads", + "bluesky", + "telegram", + "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" +]
- Changed
set_post_refill9 fields changed- added
Input schema / properties / accountsAdded value: +{ + "additionalProperties": { + "anyOf": [ + { + "items": { + "type": "string" + }, + "type": "array" + }, + { + "const": "all", + "type": "string" + }, + { + "type": "null" + } + ] + }, + "description": "accounts to post to on a channel holding several: {\"tiktok\":[\"@a\",\"@b\"]} or \"all\"; null clears. A new setup must pick; an older one posts to the primary until it does.", + "propertyNames": { + "type": "string" + }, + "type": "object" +} - added
Input schema / properties / addChannelsAdded value: +{ + "description": "add these", + "items": { + "enum": [ + "facebook", + "instagram", + "threads", + "tiktok", + "youtube", + "linkedin", + "x", + "pinterest", + "google_business", + "bluesky", + "telegram", + "wordpress", + "discord", + "slack", + "whop" + ], + "type": "string" + }, + "type": "array" +} - added
Input schema / properties / channels / anyOfAdded value: +[ + { + "items": { + "enum": [ + "facebook", + "instagram", + "threads", + "tiktok", + "youtube", + "linkedin", + "x", + "pinterest", + "google_business", + "bluesky", + "telegram", + "wordpress", + "discord", + "slack", + "whop" + ], + "type": "string" + }, + "type": "array" + }, + { + "const": "all", + "type": "string" + } +] - changed
Input schema / properties / channels / descriptionPrevious value: -"restrict it to these channels. Omit (or send an empty list) to use every connected channel that can carry each post."New value: +"REPLACES the list; a channel connected later stays out until added. \"all\" = every channel, later ones too, only if the user asks" - removed
Input schema / properties / channels / itemsRemoved value: -{ - "enum": [ - "facebook", - "instagram", - "threads", - "tiktok", - "youtube", - "linkedin", - "x", - "pinterest", - "google_business", - "bluesky", - "telegram" - ], - "type": "string" -} - removed
Input schema / properties / channels / typeRemoved value: -"array" - added
Input schema / properties / excludeChannelsAdded value: +{ + "description": "never these channels, even ones connected later; [] clears", + "items": { + "enum": [ + "facebook", + "instagram", + "threads", + "tiktok", + "youtube", + "linkedin", + "x", + "pinterest", + "google_business", + "bluesky", + "telegram", + "wordpress", + "discord", + "slack", + "whop" + ], + "type": "string" + }, + "type": "array" +} - added
Input schema / properties / removeChannelsAdded value: +{ + "description": "stop posting here", + "items": { + "enum": [ + "facebook", + "instagram", + "threads", + "tiktok", + "youtube", + "linkedin", + "x", + "pinterest", + "google_business", + "bluesky", + "telegram", + "wordpress", + "discord", + "slack", + "whop" + ], + "type": "string" + }, + "type": "array" +} - added
Input schema / properties / whopExperienceIdAdded value: +{ + "description": "WHOP — which forum or chat posts go to (exp_…, from list_whop_destinations). Without one, the whop’s only forum or chat is used; with several, whop is skipped rather than guessed.", + "type": "string" +}
8 tool updates
v0.1.489- Changed
generate_music2 fields changed- added
Input schema / properties / modelAdded value: +{ + "description": "minimax-music-3 or lyria-3.5", + "type": "string" +} - added
Input schema / properties / secondsAdded value: +{ + "description": "track length, up to 300", + "type": "number" +}
- Changed
generate_video1 field changed- added
Input schema / properties / musicModelAdded value: +{ + "description": "compose the bed on minimax-music-3 or lyria-3.5, skipping the free library", + "type": "string" +}
- Changed
hook_variants2 fields changed- changed
Input schema / properties / dryRun / descriptionPrevious value: -"true = return the plan and the quote, render nothing"New value: +"true = the paid plan and the price, no render" - added
Input schema / properties / quoteOnlyAdded value: +{ + "description": "true = the exact credits, free; nothing runs", + "type": "boolean" +}
- Changed
multiply_ad3 fields changed- changed
Input schema / properties / dryRun / descriptionPrevious value: -"true = return the plan and the quote, render nothing"New value: +"true = the paid plan and the price, no render" - added
Input schema / properties / planAdded value: +{ + "description": "the `plan` a dryRun returned, rendered without planning again" +} - added
Input schema / properties / quoteOnlyAdded value: +{ + "description": "true = the exact credits, free; nothing runs", + "type": "boolean" +}
- Changed
post_edit1 field changed- added
Input schema / properties / ops / items / properties / modelAdded value: +{ + "description": "music, generate: the model", + "type": "string" +}
- Changed
post_to_meta1 field changed- changed
Input schema / properties / story / descriptionPrevious value: -"INSTAGRAM OR FACEBOOK PAGE 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 takes NO CAPTION, collaborators, product tags or trialReel — any of those is refused by name and nothing is posted (a story must never silently drop your words). FACEBOOK PAGE STORIES WORK TOO (target facebook): one image, or one vertical 9:16 video of 3–90 seconds, no caption (a message is refused by name), not schedulable natively — use schedule_post to queue one; the reply reads the story back from the Page."New value: +"INSTAGRAM OR FACEBOOK PAGE 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 link sticker cannot be added through Meta’s publishing API on either channel: add it in the Instagram app. An Instagram Story keeps its caption: people see the start under the Story in the app and tap for the rest (instagram.com does not show it). It takes NO collaborators, product tags or trialReel — any of those is refused by name and nothing is posted. FACEBOOK PAGE STORIES WORK TOO (target facebook): one image, or one vertical 9:16 video of 3–90 seconds, no caption (a message is refused by name), not schedulable natively — use schedule_post to queue one; the reply reads the story back from the Page."
- Changed
render_ad1 field changed- added
Input schema / properties / musicModelAdded value: +{ + "description": "compose the bed on minimax-music-3 or lyria-3.5, skipping the free library", + "type": "string" +}
- Changed
schedule_post1 field changed- changed
Input schema / properties / story / descriptionPrevious value: -"INSTAGRAM OR FACEBOOK PAGE 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 takes NO CAPTION, collaborators, product tags or trialReel — any of those is refused by name and nothing is posted (a story must never silently drop your words). FACEBOOK PAGE STORIES WORK TOO (target facebook): one image, or one vertical 9:16 video of 3–90 seconds, no caption (a message is refused by name), not schedulable natively — use schedule_post to queue one; the reply reads the story back from the Page."New value: +"INSTAGRAM OR FACEBOOK PAGE 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 link sticker cannot be added through Meta’s publishing API on either channel: add it in the Instagram app. An Instagram Story keeps its caption: people see the start under the Story in the app and tap for the rest (instagram.com does not show it). It takes NO collaborators, product tags or trialReel — any of those is refused by name and nothing is posted. FACEBOOK PAGE STORIES WORK TOO (target facebook): one image, or one vertical 9:16 video of 3–90 seconds, no caption (a message is refused by name), not schedulable natively — use schedule_post to queue one; the reply reads the story back from the Page."
11 tool updates
v0.1.484- Changed
edit_image1 field changed- changed
Input schema / properties / dryRun / descriptionPrevious value: -"true = return the estimated credits this edit reserves and render nothing"New value: +"true = return what this edit usually costs in credits and render nothing"
- Changed
generate_image1 field changed- changed
Input schema / properties / fixLabel / descriptionPrevious value: -"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."New value: +"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 re-print costs more, and dryRun:true returns the up-to price). 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."
- Changed
generate_video1 field changed- added
Input schema / properties / dryRunAdded value: +{ + "description": "true = render NOTHING and return the credits this exact clip would hold (an estimate; the final charge is its actual cost). Free. Call this to price a clip before running it: without it the call renders and bills.", + "type": "boolean" +}
- Changed
make_series2 fields changed- changed
Input schema / properties / voice / descriptionPrevious value: -"narrator; default follows the reference"New value: +"narrator: 'female' or 'male' (default follows the reference), or any narration voice name make_explainer takes" - removed
Input schema / properties / voice / enumRemoved value: -[ - "female", - "male" -]
- Changed
post_to_meta5 fields changed- changed
Input schema / properties / aiGenerated / descriptionPrevious 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: +"INSTAGRAM / FACEBOOK REEL / THREADS — Meta’s is_ai_generated self-disclosure (Threads: is_made_with_ai, the AI info label; a text-only Threads post is declared only when you pass true). 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." - changed
Input schema / properties / locationId / descriptionPrevious 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."New value: +"TAG A PLACE. THREADS: a place id from search_threads_locations. INSTAGRAM: the pageId from search_meta_places, 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." - changed
Input schema / properties / place / descriptionPrevious value: -"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."New value: +"FACEBOOK — tag a location on the Page post. The pageId from search_meta_places: the NUMERIC ID OF THE FACEBOOK PAGE for that place, not a place name and not coordinates." - added
Input schema / properties / placeNameAdded value: +{ + "description": "the name the user saw for that place; a different business is refused", + "type": "string" +} - changed
Input schema / properties / story / descriptionPrevious value: -"INSTAGRAM OR FACEBOOK PAGE 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. FACEBOOK PAGE STORIES WORK TOO (target facebook): one image, or one vertical 9:16 video of 3–90 seconds, no caption (a message is refused by name), not schedulable natively — use schedule_post to queue one; the reply reads the story back from the Page."New value: +"INSTAGRAM OR FACEBOOK PAGE 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 takes NO CAPTION, collaborators, product tags or trialReel — any of those is refused by name and nothing is posted (a story must never silently drop your words). FACEBOOK PAGE STORIES WORK TOO (target facebook): one image, or one vertical 9:16 video of 3–90 seconds, no caption (a message is refused by name), not schedulable natively — use schedule_post to queue one; the reply reads the story back from the Page."
- Changed
post_to_x3 fields changed- added
Input schema / properties / dryRunAdded value: +{ + "description": "true = post NOTHING and return what this exact post would cost (costCredits). Free.", + "type": "boolean" +} - changed
Input schema / properties / text / descriptionPrevious value: -"the 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."New value: +"the post text. 280 characters without X Premium, up to 25,000 with it — write the full thing, it is never truncated. With `thread` also set, this is the FIRST post and the thread entries follow it (a thread whose first entry is already this text is not posted twice)." - changed
Input schema / properties / thread / descriptionPrevious value: -"a 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."New value: +"a thread: each string is one post, published in order, each replying to the previous (if `text` is also set it is posted first and these follow). Max 25 including `text`. 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."
- Changed
reschedule_post4 fields changed- changed
Input schema / properties / aiGenerated / descriptionPrevious value: -"AI-CONTENT DISCLOSURE (Instagram / Facebook Reel is_ai_generated, TikTok is_aigc, YouTube containsSyntheticMedia, Pinterest ai_disclosures). 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."New value: +"AI-CONTENT DISCLOSURE (Instagram / Facebook Reel is_ai_generated, TikTok is_aigc, YouTube containsSyntheticMedia, Pinterest ai_disclosures, Threads is_made_with_ai). 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." - added
Input schema / properties / brandPartnerAdded value: +{ + "description": "YOUTUBE: sponsoring brand channel (UC… or @handle); \"\" removes it", + "type": "string" +} - added
Input schema / properties / paidProductPlacementAdded value: +{ + "description": "YOUTUBE: paid product placement (shows the disclosure) — false removes it", + "type": "boolean" +} - added
Input schema / properties / placeNameAdded value: +{ + "description": "the name the user saw for that place; a different business is refused", + "type": "string" +}
- Changed
run_post_refill1 field changed- changed
Input schema / properties / dryRun / descriptionPrevious 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."New value: +"default TRUE (a preview: nothing rendered, nothing queued; the copy it writes bills its model cost). false renders fresh creative and schedules or drafts it per the mode."
- Changed
schedule_post5 fields changed- changed
Input schema / properties / aiGenerated / descriptionPrevious value: -"AI-CONTENT DISCLOSURE (Instagram / Facebook Reel is_ai_generated, TikTok is_aigc, YouTube containsSyntheticMedia, Pinterest ai_disclosures). 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."New value: +"AI-CONTENT DISCLOSURE (Instagram / Facebook Reel is_ai_generated, TikTok is_aigc, YouTube containsSyntheticMedia, Pinterest ai_disclosures, Threads is_made_with_ai). 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." - changed
Input schema / properties / instagramLocationId / descriptionPrevious value: -"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."New value: +"INSTAGRAM — tag a place (called locationId on post_to_meta; locationId here is the Google Business listing). It is the pageId from search_meta_places, 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." - changed
Input schema / properties / place / descriptionPrevious value: -"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."New value: +"FACEBOOK — tag a location on the Page post. The pageId from search_meta_places: the NUMERIC ID OF THE FACEBOOK PAGE for that place, not a place name and not coordinates." - added
Input schema / properties / placeNameAdded value: +{ + "description": "the name the user saw for that place; a different business is refused", + "type": "string" +} - changed
Input schema / properties / story / descriptionPrevious value: -"INSTAGRAM OR FACEBOOK PAGE 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. FACEBOOK PAGE STORIES WORK TOO (target facebook): one image, or one vertical 9:16 video of 3–90 seconds, no caption (a message is refused by name), not schedulable natively — use schedule_post to queue one; the reply reads the story back from the Page."New value: +"INSTAGRAM OR FACEBOOK PAGE 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 takes NO CAPTION, collaborators, product tags or trialReel — any of those is refused by name and nothing is posted (a story must never silently drop your words). FACEBOOK PAGE STORIES WORK TOO (target facebook): one image, or one vertical 9:16 video of 3–90 seconds, no caption (a message is refused by name), not schedulable natively — use schedule_post to queue one; the reply reads the story back from the Page."
- Changed
set_competitor_watch1 field changed- changed
Input schema / properties / auto / descriptionPrevious value: -"true picks competitors, similar brands and topics from the saved brand or seed; costs a few credits"New value: +"true picks competitors, similar brands and topics from the saved brand or seed; bills one model call at its token cost"
- Changed
upscale_video2 fields changed- changed
Input schema / properties / engine / descriptionPrevious value: -"default 'standard', the precision upscaler. 'flux' = the FLUX 3 video upscaler"New value: +"default 'standard', the precision upscaler. 'flux' = the FLUX 3 video upscaler. 'detail' = rebuilds fine detail, costs the most" - changed
Input schema / properties / engine / enumPrevious value: -[ - "standard", - "flux" -]New value: +[ + "standard", + "flux", + "detail" +]
1 tool update
v0.1.467- Changed
edit_video1 field changed- changed
Input schema / properties / engine / descriptionPrevious value: -"default auto: Seedance 2.5 Edit without a real-looking person, else Kling O3 Edit"New value: +"default auto: Seedance 2.5 Edit without a real-looking person, else Kling 4.0 Edit"
61 tool updates
v0.1.465- Changed
add_subtitles5 fields changed- added
Input schema / properties / auto / descriptionAdded value: +"sentences (default) or words: timing unit for transcribed subtitles" - added
Input schema / properties / cues / items / properties / end / descriptionAdded value: +"second the line disappears" - added
Input schema / properties / cues / items / properties / start / descriptionAdded value: +"second the line appears" - added
Input schema / properties / cues / items / properties / text / descriptionAdded value: +"subtitle line text, max 90 characters, no emoji" - added
Input schema / properties / wordsPerCue / descriptionAdded value: +"words per subtitle line when auto is words"
- Changed
append_to_notion_page2 fields changed- added
Input schema / properties / includeSchedule / descriptionAdded value: +"true appends the scheduled posts too" - added
Input schema / properties / swipefileCollection / descriptionAdded value: +"swipefile collection name or id to append, with each ad's creative, copy, dates and link"
- Added
autofill_canva_template - Changed
clear_sheet_range3 fields changed- added
Input schema / properties / confirm / descriptionAdded value: +"true clears the range; call without it first to get the filled-cell count" - added
Input schema / properties / sheetUrl / descriptionAdded value: +"Google Sheets link, instead of spreadsheetId" - added
Input schema / properties / spreadsheetId / descriptionAdded value: +"Google spreadsheet id; use this or sheetUrl"
- Added
create_canva_folder - Changed
delete_linkedin_lead_subscription1 field changed- added
Input schema / properties / subscriptionId / descriptionAdded value: +"webhook id from list_linkedin_lead_subscriptions"
- Changed
duplicate_scheduled5 fields changed- added
Input schema / properties / imageUrl / descriptionAdded value: +"replacement image for the copy: a Hermoso render URL or an upload_file URL" - added
Input schema / properties / link / descriptionAdded value: +"destination URL for the copy (Pinterest, Facebook text post or Google Business post)" - added
Input schema / properties / title / descriptionAdded value: +"Pinterest or YouTube headline for the copy, max 100 characters" - added
Input schema / properties / videoUrl / descriptionAdded value: +"replacement video for the copy: a Hermoso render URL or an upload_file URL" - added
Input schema / properties / visibility / descriptionAdded value: +"public (default), unlisted or private (YouTube), or draft (TikTok or an unpublished Facebook Page post)"
- Added
export_canva_design - Changed
export_schedule_to_notion2 fields changed- added
Input schema / properties / dataSourceId / descriptionAdded value: +"data source id of an existing database to write rows into, matched to columns by name" - added
Input schema / properties / parentPageId / descriptionAdded value: +"Notion page to create the new database under; omit for a private page in the workspace"
- Changed
find_creators1 field changed- added
Input schema / properties / minAvgViews / descriptionAdded value: +"minimum average views per post; creators below this floor are dropped"
- Changed
find_sound3 fields changed- added
Input schema / properties / pick / descriptionAdded value: +"candidate number from the previous list to take instead of the first" - added
Input schema / properties / query / descriptionAdded value: +"sound by name, moment or meaning, e.g. 'bad news reaction'" - added
Input schema / properties / url / descriptionAdded value: +"link to a TikTok sound, a post, a meme-sound page or an audio file"
- Changed
format_sheet4 fields changed- added
Input schema / properties / autoResize / descriptionAdded value: +"auto-size columns to fit their content; default true, false skips it" - added
Input schema / properties / boldHeader / descriptionAdded value: +"bold the header row; default true, false skips it" - added
Input schema / properties / sheetUrl / descriptionAdded value: +"Google Sheets link, instead of spreadsheetId" - added
Input schema / properties / spreadsheetId / descriptionAdded value: +"Google spreadsheet id; use this or sheetUrl"
- Changed
get_linkedin_lead1 field changed- added
Input schema / properties / leadId / descriptionAdded value: +"lead id from list_linkedin_leads"
- Changed
hook_variants3 fields changed- added
Input schema / properties / hooks / items / properties / mechanic / descriptionAdded value: +"a named hook mechanic id from list_hooks" - added
Input schema / properties / hooks / items / properties / prompt / descriptionAdded value: +"an opening described in words" - added
Input schema / properties / hooks / items / properties / url / descriptionAdded value: +"viral hook to join in front: a post link or a video file"
- Added
list_canva_brand_templates - Added
list_canva_designs - Added
list_canva_folder - Changed
list_linkedin_lead_events1 field changed- added
Input schema / properties / limit / descriptionAdded value: +"events to return, newest first; default 50, max 200"
- Changed
list_linkedin_lead_subscriptions1 field changed- added
Input schema / properties / leadType / descriptionAdded value: +"SPONSORED for an ad account; COMPANY, EVENT or ORGANIZATION_PRODUCT for a Page; defaults by owner"
- Changed
list_linkedin_leads1 field changed- added
Input schema / properties / until / descriptionAdded value: +"end of the time window, ISO date or epoch milliseconds; omit for now"
- Changed
list_meta_posts1 field changed- added
Input schema / properties / taggedAdded value: +{ + "description": "INSTAGRAM — list posts OTHER accounts tagged the brand in (UGC, creator posts, partner mentions), with each author’s @handle, instead of the brand’s own posts. Free.", + "type": "boolean" +}
- Changed
list_notion_pages1 field changed- added
Input schema / properties / type / descriptionAdded value: +"limit results to pages or to databases; omit for both"
- Changed
list_pinterest_boards1 field changed- added
Input schema / properties / accountAdded value: +{ + "description": "which connected account: its @handle or id from list_connector_accounts; needed only when several are connected", + "type": "string" +}
- Changed
list_telegram_chats1 field changed- added
Input schema / properties / accountAdded value: +{ + "description": "which connected account: its @handle or id from list_connector_accounts; needed only when several are connected", + "type": "string" +}
- Changed
make_insert7 fields changed- added
Input schema / properties / image / descriptionAdded value: +"still picture URL; use this or video" - added
Input schema / properties / seconds / descriptionAdded value: +"fixed insert length, 0.2 to 6 seconds, instead of when the sound lands" - added
Input schema / properties / sound / descriptionAdded value: +"sound URL (find_sound returns one), or a video whose own audio is used" - added
Input schema / properties / soundEnd / descriptionAdded value: +"seconds into the sound where it ends" - added
Input schema / properties / soundStart / descriptionAdded value: +"seconds into the sound where the insert's sound starts" - added
Input schema / properties / video / descriptionAdded value: +"video URL to take the picture from, starting at videoStart" - added
Input schema / properties / videoStart / descriptionAdded value: +"seconds into the video where the insert starts"
- Changed
make_motion_film8 fields changed- added
Input schema / properties / confirm / descriptionAdded value: +"true renders the film for real credits; without it you get a free quote" - added
Input schema / properties / help / descriptionAdded value: +"true returns the spec language and recipes; renders nothing" - added
Input schema / properties / preview / descriptionAdded value: +"true renders a 1-credit contact sheet to approve before the full render" - added
Input schema / properties / projectUrl / descriptionAdded value: +"a past film's url; returns its saved spec to edit and render again" - added
Input schema / properties / recipe / descriptionAdded value: +"type, logo, launch, product or social; help:true lists the recipes" - added
Input schema / properties / size / descriptionAdded value: +"frame shape: 16:9, 9:16, 1:1 or 4:5" - added
Input schema / properties / spec / descriptionAdded value: +"the whole film as JSON, from a quote or a saved film; help:true shows the spec language" - added
Input schema / properties / tagline / descriptionAdded value: +"line shown under the headline or logo"
- Changed
make_series4 fields changed- added
Input schema / properties / schedule / properties / at / descriptionAdded value: +"time of the first post: YYYY-MM-DDTHH:MM wall-clock, or an ISO date-time" - added
Input schema / properties / schedule / properties / channels / descriptionAdded value: +"channels to post each episode to, e.g. tiktok, instagram, youtube (as schedule_post takes them)" - added
Input schema / properties / schedule / properties / everyHours / descriptionAdded value: +"hours between episodes; default 24" - added
Input schema / properties / schedule / properties / timezone / descriptionAdded value: +"timezone name for at, as schedule_post takes it"
- Changed
make_template_ad1 field changed- changed
Input schema / properties / config / descriptionPrevious 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, which takes config.music like any video) }; 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."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, which takes config.music like any video) }; 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? } is OPTIONAL on every video preset: include it ONLY when the user asked for an end card (a closing card with their logo); without it the video ends on its own last frame. palette and fontStack optional."
- Changed
make_thumbnail5 fields changed- added
Input schema / properties / people / items / properties / describe / descriptionAdded value: +"the person's appearance in English prose, used instead of a face photo" - added
Input schema / properties / split / properties / mode / descriptionAdded value: +"layout: plain, before_after, versus or custom" - added
Input schema / properties / split / properties / panels / descriptionAdded value: +"panel descriptions in order, left to right or top to bottom; sets the panel count (minimum 2)" - added
Input schema / properties / tweak / properties / kind / descriptionAdded value: +"emotion, background, background_color or rim_light, or another kind with the edit in words" - added
Input schema / properties / tweak / properties / value / descriptionAdded value: +"new value for the kind: an emotion, a colour, or the edit in words"
- Changed
manage_sheet_tabs5 fields changed- added
Input schema / properties / action / descriptionAdded value: +"add, rename or delete a tab" - added
Input schema / properties / confirm / descriptionAdded value: +"true deletes a tab that holds data; needs confirmCells" - added
Input schema / properties / confirmCells / descriptionAdded value: +"the filled-cell count an unconfirmed delete reported" - added
Input schema / properties / sheetUrl / descriptionAdded value: +"Google Sheets link, instead of spreadsheetId" - added
Input schema / properties / spreadsheetId / descriptionAdded value: +"Google spreadsheet id; use this or sheetUrl"
- Added
onedrive_file_permissions - Changed
plan_ad2 fields changed- added
Input schema / properties / draft / properties / durationSeconds / descriptionAdded value: +"clip length in seconds from that same refusal; use only the value it gave" - added
Input schema / properties / draft / properties / model / descriptionAdded value: +"video model id from a refusal that offered a lighter draft; use only the value it gave"
- Changed
post_edit16 fields changed- added
Input schema / properties / ops / items / properties / bridge / properties / flash / descriptionAdded value: +"true (default) adds a white flash on the cut; false skips it" - added
Input schema / properties / ops / items / properties / bridge / properties / kind / descriptionAdded value: +"impact (cut before the hook's payoff) or text (words over the cut)" - added
Input schema / properties / ops / items / properties / bridge / properties / matchCut / descriptionAdded value: +"second in our clip where it starts" - added
Input schema / properties / ops / items / properties / bridge / properties / shake / descriptionAdded value: +"true (default) adds a camera shake on the cut; false skips it" - added
Input schema / properties / ops / items / properties / bridge / properties / text / descriptionAdded value: +"text bridge only: words shown over the cut, only when the user asks for them" - added
Input schema / properties / ops / items / properties / bridge / properties / then / descriptionAdded value: +"text bridge only: a line shown after the text" - added
Input schema / properties / ops / items / properties / clips / items / properties / end / descriptionAdded value: +"seconds into this clip to stop at" - added
Input schema / properties / ops / items / properties / clips / items / properties / match / descriptionAdded value: +"seam match for this clip: auto (default), off, or a grade object" - added
Input schema / properties / ops / items / properties / clips / items / properties / reframe / descriptionAdded value: +"shot change at this seam: auto, off, or a step from 1.1 to 1.5" - added
Input schema / properties / ops / items / properties / clips / items / properties / start / descriptionAdded value: +"seconds into this clip to start from" - added
Input schema / properties / ops / items / properties / clips / items / properties / url / descriptionAdded value: +"clip to join after the video: Library URL, direct file or public post link" - added
Input schema / properties / ops / items / properties / keepSound / descriptionAdded value: +"voiceover only: true (default) keeps the clip's sound under the voice; false replaces it" - added
Input schema / properties / ops / items / properties / op / descriptionAdded value: +"the edit to apply, in order; each op takes only the fields listed for it in the tool description" - added
Input schema / properties / ops / items / properties / textStyle / descriptionAdded value: +"text op: a textStyle object or preset name, same as style" - added
Input schema / properties / ops / items / properties / voice / descriptionAdded value: +"voiceover voice: female or male" - added
Input schema / properties / ops / items / properties / y / descriptionAdded value: +"vertical centre for text or watermark: 0 to 1 of frame height, or pixels"
- Changed
post_to_bluesky1 field changed- changed
Input schema / properties / imageUrls / descriptionPrevious value: -"Up to 4 public image URLs to attach. Cannot be combined with videoUrl."New value: +"Up to 10 public image URLs to attach (1–4 a standard image post, 5–10 a Bluesky gallery; more than 10 is refused, never cut). Cannot be combined with videoUrl."
- Changed
post_to_linkedin13 fields changed- added
Input schema / properties / altTextAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "items": { + "type": "string" + }, + "type": "array" + } + ], + "description": "image alt text — a string for every image, or an array per slide" +} - added
Input schema / properties / captionsSrtAdded value: +{ + "description": "SRT caption CONTENT for a videoUrl post (English only, attached during upload)", + "type": "string" +} - added
Input schema / properties / documentTitleAdded value: +{ + "description": "the title shown on the document (defaults to the file name)", + "type": "string" +} - added
Input schema / properties / documentUrlAdded value: +{ + "description": "a DOCUMENT post — a Hermoso-hosted PDF / PPTX / DOCX (upload_file first) that readers swipe through page by page in the feed; LinkedIn takes up to 100MB and 300 pages. One kind of media per post: not with an image, video, slides or linkUrl.", + "type": "string" +} - changed
Input schema / properties / imageUrls / descriptionPrevious value: -"A 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."New value: +"a MULTI-IMAGE post: 2–20 Hermoso-hosted images in order, one post the reader swipes through (LinkedIn’s MultiImage API takes a member author on w_member_social). Images only." - added
Input schema / properties / linkDescriptionAdded value: +{ + "description": "link card description; LinkedIn does not read the page, so set it here", + "type": "string" +} - added
Input schema / properties / linkThumbnailUrlAdded value: +{ + "description": "Hermoso-hosted image for the card", + "type": "string" +} - added
Input schema / properties / linkTitleAdded value: +{ + "description": "the card headline — LinkedIn never scrapes the page, so without it the card is unlabelled", + "type": "string" +} - added
Input schema / properties / linkUrlAdded value: +{ + "description": "a LINK post with a real preview card. Not with media.", + "type": "string" +} - added
Input schema / properties / mentionsAdded value: +{ + "description": "TAG COMPANIES: write \"@Name\" in the text where each tag goes and list each company here — its vanity name (the part after linkedin.com/company/), its company page URL, or {organizationId, name}. Hermoso resolves it on LinkedIn and LinkedIn renders a real clickable mention; a company with no \"@\" spot in the text is refused rather than appended.", + "items": {}, + "type": "array" +} - added
Input schema / properties / titleAdded value: +{ + "description": "video title", + "type": "string" +} - added
Input schema / properties / videoThumbnailUrlAdded value: +{ + "description": "cover image for a videoUrl post (Hermoso-hosted)", + "type": "string" +} - added
Input schema / properties / videoUrlAdded value: +{ + "description": "a Hermoso-hosted video (a render, or the user’s footage via upload_file). LinkedIn processes it before publishing, which takes a minute.", + "type": "string" +}
- Changed
post_to_linkedin_page14 fields changed- changed
Input schema / properties / altText / descriptionPrevious value: -"accessibility 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."New value: +"accessibility 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. Also works on a personal-profile post (post_to_linkedin)." - added
Input schema / properties / documentTitleAdded value: +{ + "description": "the title shown on the document (defaults to the file name)", + "type": "string" +} - added
Input schema / properties / documentUrlAdded value: +{ + "description": "a DOCUMENT post — a Hermoso-hosted PDF / PPTX / DOCX (upload_file first) that readers swipe through page by page in the feed; LinkedIn takes up to 100MB and 300 pages. One kind of media per post: not with an image, video, slides or linkUrl.", + "type": "string" +} - added
Input schema / properties / firstCommentAdded value: +{ + "description": "FIRST COMMENT — posted on the new post AS THE PAGE right after it publishes. Needs the LinkedIn comments authorization on this brand (provider linkedin_community). Up to 2,000 characters, refused rather than cut. If the comment fails the post stays live and firstComment in the result says why.", + "type": "string" +} - changed
Input schema / properties / imageUrls / descriptionPrevious value: -"CAROUSEL — 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."New value: +"CAROUSEL — 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–20 (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." - added
Input schema / properties / mentionsAdded value: +{ + "description": "TAG COMPANIES: write \"@Name\" in the text where each tag goes and list each company here — its vanity name (the part after linkedin.com/company/), its company page URL, or {organizationId, name}. Hermoso resolves it on LinkedIn and LinkedIn renders a real clickable mention; a company with no \"@\" spot in the text is refused rather than appended.", + "items": {}, + "type": "array" +} - added
Input schema / properties / targetAudience / properties / degrees / descriptionAdded value: +"education degree values to narrow the followers shown the post; company Page posts only" - added
Input schema / properties / targetAudience / properties / fieldsOfStudy / descriptionAdded value: +"fields of study to narrow the followers shown the post; company Page posts only" - added
Input schema / properties / targetAudience / properties / geoLocations / descriptionAdded value: +"LinkedIn geo URNs or ids of Page followers to show the post to; company Page posts only" - added
Input schema / properties / targetAudience / properties / industries / descriptionAdded value: +"LinkedIn industry URNs or ids to narrow the followers shown the post; company Page posts only" - added
Input schema / properties / targetAudience / properties / jobFunctions / descriptionAdded value: +"LinkedIn job function URNs or ids to narrow the followers shown the post; company Page posts only" - added
Input schema / properties / targetAudience / properties / organizations / descriptionAdded value: +"organization URNs or ids to narrow the followers shown the post; company Page posts only" - added
Input schema / properties / targetAudience / properties / seniorities / descriptionAdded value: +"LinkedIn seniority URNs or ids to narrow the followers shown the post; company Page posts only" - added
Input schema / properties / targetAudience / properties / staffCountRanges / descriptionAdded value: +"company size bands to narrow the followers shown the post; company Page posts only"
- Changed
post_to_meta8 fields changed- added
Input schema / properties / audience / properties / minAge / descriptionAdded value: +"Facebook Page post audience: minimum age, one of 13, 15, 18, 21 or 25" - added
Input schema / properties / firstCommentAdded value: +{ + "description": "FIRST COMMENT — posted on the new post as the same account right after it publishes (Facebook or Instagram; not Threads, not a story, not an unpublished post). Up to 2,000 characters, refused rather than cut. If the comment fails the post stays live and firstComment in the result says why.", + "type": "string" +} - changed
Input schema / properties / imageUrls / descriptionPrevious value: -"CAROUSEL — 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."New value: +"CAROUSEL — 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–20 (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." - changed
Input schema / properties / story / descriptionPrevious value: -"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."New value: +"INSTAGRAM OR FACEBOOK PAGE 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. FACEBOOK PAGE STORIES WORK TOO (target facebook): one image, or one vertical 9:16 video of 3–90 seconds, no caption (a message is refused by name), not schedulable natively — use schedule_post to queue one; the reply reads the story back from the Page." - added
Input schema / properties / threadsGhostPostAdded value: +{ + "description": "THREADS ONLY — a GHOST post: text only, Threads archives it automatically after 24 hours (a flash offer, a 24h-only note). No other Threads option can ride it.", + "type": "boolean" +} - added
Input schema / properties / threadsGifAdded value: +{ + "description": "THREADS ONLY — a GIPHY GIF id (the id in a giphy.com/gifs/… link) attached to a TEXT Threads post. GIPHY is the only provider Threads takes.", + "type": "string" +} - added
Input schema / properties / threadsLongTextAdded value: +{ + "description": "THREADS ONLY — LONG-FORM text, up to 10,000 characters, attached under a TEXT-only Threads post (Threads' text attachment). The way to publish more than Threads' 500-character post: keep a short lead in the message and put the full text here. Not with an image, video or carousel.", + "type": "string" +} - added
Input schema / properties / userTagsAdded value: +{ + "description": "INSTAGRAM USER TAGS — tag people (public accounts) on the post; each is notified. Usernames, or { username, x, y } with x/y from 0 to 1 = where the tag sits on the IMAGE (a tag without a position sits at the centre). A Reel tag takes no position; a story position is optional. Not on a carousel (refused by name).", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "properties": { + "username": { + "type": "string" + }, + "x": { + "maximum": 1, + "minimum": 0, + "type": "number" + }, + "y": { + "maximum": 1, + "minimum": 0, + "type": "number" + } + }, + "required": [ + "username" + ], + "type": "object" + } + ] + }, + "type": "array" +}
- Changed
post_to_pinterest6 fields changed- added
Input schema / properties / aiGeneratedAdded value: +{ + "description": "Pinterest’s AI disclosure (ai_disclosures AI_MODIFIED). OMIT IT and Hermoso decides from provenance: a Hermoso render is declared, an image or video that came through upload_file or an external URL (the user’s own) is NOT. Pass true or false only to override.", + "type": "boolean" +} - changed
Input schema / properties / imageUrls / descriptionPrevious value: -"CAROUSEL — 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."New value: +"CAROUSEL — 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–20 (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." - added
Input schema / properties / slideText / items / properties / description / descriptionAdded value: +"carousel slide description for this slide" - added
Input schema / properties / slideText / items / properties / link / descriptionAdded value: +"carousel slide destination URL for this slide" - added
Input schema / properties / slideText / items / properties / title / descriptionAdded value: +"carousel slide title; one object per slide, in slide order" - added
Input schema / properties / syntheticPerformerAdded value: +{ + "description": "ALSO declare SYNTHETIC_PERFORMER — the Pin shows a realistic AI-generated person. Only with an AI disclosure; never guessed, so pass true only when the user says the person is AI-made.", + "type": "boolean" +}
- Changed
post_to_telegram2 fields changed- added
Input schema / properties / buttonsAdded value: +{ + "description": "URL BUTTONS under the post, Telegram's call to action: [{text:'Shop now', url:'https://…'}] one per row, or rows [[a,b],[c]]. URL buttons only (https://, http:// or tg://), optional style danger|success|primary. A single photo, a video or a text post can carry them; an ALBUM cannot (Telegram's sendMediaGroup takes no buttons), so that combination is refused.", + "items": { + "anyOf": [ + { + "properties": { + "style": { + "enum": [ + "danger", + "success", + "primary" + ], + "type": "string" + }, + "text": { + "type": "string" + }, + "url": { + "type": "string" + } + }, + "required": [ + "text", + "url" + ], + "type": "object" + }, + { + "items": { + "properties": { + "style": { + "enum": [ + "danger", + "success", + "primary" + ], + "type": "string" + }, + "text": { + "type": "string" + }, + "url": { + "type": "string" + } + }, + "required": [ + "text", + "url" + ], + "type": "object" + }, + "type": "array" + } + ] + }, + "type": "array" +} - added
Input schema / properties / parseModeAdded value: +{ + "description": "'HTML' to format the text with Telegram's HTML: <b>, <i>, <u>, <s>, <a href=\"…\">, <code>, <pre>, <blockquote>, <tg-spoiler>. Omit for plain text (the default). In HTML mode a literal <, > or & in the copy must be written < > &, which is why it is never turned on by itself. Markdown (**bold**) is NOT parsed; convert it to HTML tags.", + "enum": [ + "HTML" + ], + "type": "string" +}
- Changed
post_to_tiktok1 field changed- added
Input schema / properties / disableComment / descriptionAdded value: +"true = turn comments off on this post"
- Changed
post_to_x6 fields changed- added
Input schema / properties / dmDeepLinkAdded value: +{ + "description": "a \"Message us\" button: true for the connected account, or https://x.com/messages/compose?recipient_id=<id>. TEXT posts only — X refuses it alongside media.", + "type": [ + "string", + "boolean" + ] +} - added
Input schema / properties / taggedUserIdsAdded value: +{ + "description": "tag up to 10 people (numeric X user ids, from x_user) in the post’s media", + "items": { + "type": "string" + }, + "type": "array" +} - added
Input schema / properties / videoCtaAdded value: +{ + "description": "VIDEO posts only — a button on the video; needs videoCtaUrl (https)", + "enum": [ + "visit_site", + "watch_now" + ], + "type": "string" +} - added
Input schema / properties / videoCtaUrlAdded value: +{ + "description": "https link the video button opens; used with videoCta", + "type": "string" +} - added
Input schema / properties / videoDescriptionAdded value: +{ + "description": "VIDEO posts only — a line under the title, max 200", + "type": "string" +} - added
Input schema / properties / videoTitleAdded value: +{ + "description": "VIDEO posts only — a headline under the video, max 70 (measured: X drops it on an image post, so it is refused there)", + "type": "string" +}
- Changed
post_to_youtube9 fields changed- added
Input schema / properties / brandPartnerAdded value: +{ + "description": "sponsoring brand's channel id (UC…) or @handle — gives that brand access to run the video as a partnership ad; \"\" unlinks (update only)", + "type": "string" +} - added
Input schema / properties / defaultAudioLanguageAdded value: +{ + "description": "spoken language, e.g. \"es\"", + "type": "string" +} - added
Input schema / properties / defaultLanguageAdded value: +{ + "description": "language of title/description, e.g. \"en\"", + "type": "string" +} - added
Input schema / properties / embeddableAdded value: +{ + "description": "true = allow embedding on other sites", + "type": "boolean" +} - added
Input schema / properties / licenseAdded value: +{ + "description": "YouTube licence: youtube (standard) or creativeCommon", + "enum": [ + "youtube", + "creativeCommon" + ], + "type": "string" +} - added
Input schema / properties / localizationsAdded value: +{ + "additionalProperties": { + "properties": { + "description": { + "type": "string" + }, + "title": { + "type": "string" + } + }, + "type": "object" + }, + "description": "translated title/description per language, e.g. {\"es\":{\"title\":\"…\"}}; needs defaultLanguage", + "propertyNames": { + "type": "string" + }, + "type": "object" +} - added
Input schema / properties / paidProductPlacementAdded value: +{ + "description": "true = contains PAID product placement (sponsored) — YouTube shows the disclosure; legally required for sponsored content", + "type": "boolean" +} - added
Input schema / properties / publicStatsViewableAdded value: +{ + "description": "true = show public view and like counts", + "type": "boolean" +} - added
Input schema / properties / recordingDateAdded value: +{ + "description": "YYYY-MM-DD", + "type": "string" +}
- Changed
post_x_article1 field changed- added
Input schema / properties / accountAdded value: +{ + "description": "which connected account: its @handle or id from list_connector_accounts; needed only when several are connected", + "type": "string" +}
- Changed
publish_to_shopify_product4 fields changed- changed
Input schema / properties / imageUrl / descriptionPrevious value: -"any public https image URL — a Hermoso render works, and so does ANY file of your own brought in with upload_file"New value: +"older name for mediaUrl; still accepted" - added
Input schema / properties / kindAdded value: +{ + "description": "only when the link does not say (no file extension and no content type)", + "enum": [ + "image", + "video" + ], + "type": "string" +} - added
Input schema / properties / mediaUrlAdded value: +{ + "description": "any public https image or video URL: a Hermoso render works, and so does ANY file of your own brought in with upload_file", + "type": "string" +} - changed
Input schema / requiredPrevious value: -[ - "productId", - "imageUrl" -]New value: +[ + "productId" +]
- Added
query_notion_database - Changed
render_ad4 fields changed- added
Input schema / properties / foundationImages / properties / hero / descriptionAdded value: +"hero image URL from a foundations:'choose' call; reused as-is" - added
Input schema / properties / foundationImages / properties / identitySheet / descriptionAdded value: +"identity sheet image URL from a foundations:'choose' call; reused as-is" - added
Input schema / properties / foundationImages / properties / location / descriptionAdded value: +"location image URL from a foundations:'choose' call; reused as-is" - added
Input schema / properties / foundationImages / properties / moodboard / descriptionAdded value: +"moodboard image URL from a foundations:'choose' call; reused as-is"
- Changed
reschedule_post39 fields changed- changed
Input schema / properties / aiGenerated / descriptionPrevious 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."New value: +"AI-CONTENT DISCLOSURE (Instagram / Facebook Reel is_ai_generated, TikTok is_aigc, YouTube containsSyntheticMedia, Pinterest ai_disclosures). 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." - added
Input schema / properties / audience / properties / cities / descriptionAdded value: +"Facebook Page post audience: Meta numeric city keys (the ads location search returns them)" - added
Input schema / properties / audience / properties / countries / descriptionAdded value: +"Facebook Page post audience: two-letter country codes, e.g. CA, US" - added
Input schema / properties / audience / properties / minAge / descriptionAdded value: +"Facebook Page post audience: minimum age, one of 13, 15, 18, 21 or 25" - added
Input schema / properties / audience / properties / regions / descriptionAdded value: +"Facebook Page post audience: Meta numeric region keys (the ads location search returns them)" - added
Input schema / properties / buttonsAdded value: +{ + "description": "TELEGRAM ONLY — replace the URL buttons ([] removes them).", + "items": { + "anyOf": [ + { + "properties": { + "style": { + "enum": [ + "danger", + "success", + "primary" + ], + "type": "string" + }, + "text": { + "type": "string" + }, + "url": { + "type": "string" + } + }, + "required": [ + "text", + "url" + ], + "type": "object" + }, + { + "items": { + "properties": { + "style": { + "enum": [ + "danger", + "success", + "primary" + ], + "type": "string" + }, + "text": { + "type": "string" + }, + "url": { + "type": "string" + } + }, + "required": [ + "text", + "url" + ], + "type": "object" + }, + "type": "array" + } + ] + }, + "type": "array" +} - added
Input schema / properties / disablePreviewAdded value: +{ + "description": "TELEGRAM ONLY — no link preview.", + "type": "boolean" +} - added
Input schema / properties / event / properties / endDate / descriptionAdded value: +"event end date, e.g. 2026-11-01" - added
Input schema / properties / event / properties / endTime / descriptionAdded value: +"event end time, e.g. 20:00" - added
Input schema / properties / event / properties / startDate / descriptionAdded value: +"event start date, e.g. 2026-11-01" - added
Input schema / properties / event / properties / startTime / descriptionAdded value: +"event start time, e.g. 18:00" - added
Input schema / properties / event / properties / title / descriptionAdded value: +"Google Business event title; required for an event or offer post" - added
Input schema / properties / firstCommentAdded value: +{ + "description": "FIRST COMMENT — posted on each new post as the same account right after it publishes, on Facebook, Instagram and LinkedIn company Pages (LinkedIn needs its comments authorization, provider linkedin_community). Other channels publish without it and their result row says so. Up to 2,000 characters, refused rather than cut; never on a story. A failed comment never undoes the post.", + "type": "string" +} - added
Input schema / properties / link / descriptionAdded value: +"destination URL for a Pinterest Pin, Facebook text post or Google Business post" - added
Input schema / properties / offer / properties / couponCode / descriptionAdded value: +"Google Business offer coupon code" - added
Input schema / properties / offer / properties / redeemOnlineUrl / descriptionAdded value: +"link where the offer is redeemed online" - added
Input schema / properties / offer / properties / termsConditions / descriptionAdded value: +"terms text shown with the offer" - added
Input schema / properties / parseModeAdded value: +{ + "description": "TELEGRAM ONLY — 'HTML' for Telegram's HTML tags.", + "enum": [ + "HTML" + ], + "type": "string" +} - added
Input schema / properties / poll / properties / durationMinutes / descriptionAdded value: +"X poll length in minutes, 5 to 10080; default 1440 (one day)" - added
Input schema / properties / poll / properties / options / descriptionAdded value: +"X poll choices: 2 to 4 options, max 25 characters each" - added
Input schema / properties / silentAdded value: +{ + "description": "TELEGRAM ONLY — no notification sound.", + "type": "boolean" +} - added
Input schema / properties / slideText / items / properties / description / descriptionAdded value: +"Pinterest carousel slide: description for this slide" - added
Input schema / properties / slideText / items / properties / link / descriptionAdded value: +"Pinterest carousel slide: destination URL for this slide" - added
Input schema / properties / slideText / items / properties / title / descriptionAdded value: +"Pinterest carousel slide: title for this slide" - added
Input schema / properties / targetAudience / properties / degrees / descriptionAdded value: +"LinkedIn degree facet: numeric id or urn:li:degree value from search_linkedin_ads_targeting" - added
Input schema / properties / targetAudience / properties / fieldsOfStudy / descriptionAdded value: +"LinkedIn field of study facet: numeric id or urn:li:fieldOfStudy value from search_linkedin_ads_targeting" - added
Input schema / properties / targetAudience / properties / geoLocations / descriptionAdded value: +"LinkedIn geo facet: numeric id or urn:li:geo value from search_linkedin_ads_targeting" - added
Input schema / properties / targetAudience / properties / industries / descriptionAdded value: +"LinkedIn industry facet: numeric id or urn:li:industry value from search_linkedin_ads_targeting" - added
Input schema / properties / targetAudience / properties / jobFunctions / descriptionAdded value: +"LinkedIn job function facet: numeric id or urn:li:function value from search_linkedin_ads_targeting" - added
Input schema / properties / targetAudience / properties / organizations / descriptionAdded value: +"LinkedIn school or organization facet: numeric id or urn:li:organization value from search_linkedin_ads_targeting" - added
Input schema / properties / targetAudience / properties / seniorities / descriptionAdded value: +"LinkedIn seniority facet: numeric id or urn:li:seniority value from search_linkedin_ads_targeting" - added
Input schema / properties / targetAudience / properties / staffCountRanges / descriptionAdded value: +"LinkedIn company size bands, e.g. SIZE_11_TO_50" - added
Input schema / properties / threadsGhostPostAdded value: +{ + "description": "THREADS ONLY — a GHOST post: text only, Threads archives it automatically after 24 hours (a flash offer, a 24h-only note). No other Threads option can ride it.", + "type": "boolean" +} - added
Input schema / properties / threadsGifAdded value: +{ + "description": "THREADS ONLY — a GIPHY GIF id (the id in a giphy.com/gifs/… link) attached to a TEXT Threads post. GIPHY is the only provider Threads takes.", + "type": "string" +} - added
Input schema / properties / threadsLongTextAdded value: +{ + "description": "THREADS ONLY — LONG-FORM text, up to 10,000 characters, attached under a TEXT-only Threads post (Threads' text attachment). The way to publish more than Threads' 500-character post: keep a short lead in the message and put the full text here. Not with an image, video or carousel.", + "type": "string" +} - added
Input schema / properties / userTagsAdded value: +{ + "description": "INSTAGRAM USER TAGS — tag people (public accounts) on the post; each is notified. Usernames, or { username, x, y } with x/y from 0 to 1 = where the tag sits on the IMAGE (a tag without a position sits at the centre). A Reel tag takes no position; a story position is optional. Not on a carousel (refused by name).", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "properties": { + "username": { + "type": "string" + }, + "x": { + "maximum": 1, + "minimum": 0, + "type": "number" + }, + "y": { + "maximum": 1, + "minimum": 0, + "type": "number" + } + }, + "required": [ + "username" + ], + "type": "object" + } + ] + }, + "type": "array" +} - added
Input schema / properties / visibilityByChannel / descriptionAdded value: +"per-channel visibility override, e.g. {tiktok: draft}; values public, unlisted, private or draft" - added
Input schema / properties / xArticle / properties / headings / descriptionAdded value: +"blocks (default) keeps real headings; text turns headings into bold lines" - added
Input schema / properties / xArticle / properties / title / descriptionAdded value: +"X Article headline; the post text becomes the article body in markdown"
- Changed
run_post_refill7 fields changed- added
Input schema / properties / edit / items / properties / captions / descriptionAdded value: +"per-channel caption overrides for the draft, e.g. {instagram: ...}" - added
Input schema / properties / edit / items / properties / id / descriptionAdded value: +"draft id to change, from get_post_refill" - added
Input schema / properties / edit / items / properties / message / descriptionAdded value: +"replacement caption for the draft" - added
Input schema / properties / edit / items / properties / title / descriptionAdded value: +"Pinterest or YouTube headline for the draft" - added
Input schema / properties / redo / descriptionAdded value: +"remake one draft from feedback; paid, so without confirm:true it returns only the price" - added
Input schema / properties / redo / properties / feedback / descriptionAdded value: +"what to change, e.g. too salesy; saved for later batches when remember is on" - added
Input schema / properties / redo / properties / id / descriptionAdded value: +"draft id to remake, from get_post_refill"
- Changed
save_playbook4 fields changed- added
Input schema / properties / angles / items / properties / detail / descriptionAdded value: +"what the angle is and why it persuades; optional, up to 600 characters" - added
Input schema / properties / angles / items / properties / title / descriptionAdded value: +"the angle, in a few words" - added
Input schema / properties / plays / items / properties / detail / descriptionAdded value: +"how to run the play; optional, up to 600 characters" - added
Input schema / properties / plays / items / properties / title / descriptionAdded value: +"the play to run, in a few words"
- Added
save_to_canva - Changed
save_to_notion2 fields changed- added
Input schema / properties / dataSourceId / descriptionAdded value: +"data source id to save into as a new row; pass only one of parentPageId, databaseId, dataSourceId" - added
Input schema / properties / title / descriptionAdded value: +"title of the new Notion page"
- Changed
schedule_post39 fields changed- changed
Input schema / properties / aiGenerated / descriptionPrevious 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."New value: +"AI-CONTENT DISCLOSURE (Instagram / Facebook Reel is_ai_generated, TikTok is_aigc, YouTube containsSyntheticMedia, Pinterest ai_disclosures). 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." - added
Input schema / properties / audience / properties / minAge / descriptionAdded value: +"Facebook Page post audience: minimum age, one of 13, 15, 18, 21 or 25" - added
Input schema / properties / brandPartnerAdded value: +{ + "description": "YOUTUBE: sponsoring brand channel (UC… or @handle)", + "type": "string" +} - added
Input schema / properties / buttonsAdded value: +{ + "description": "TELEGRAM ONLY — URL buttons under the post, as post_to_telegram takes them; never on an album.", + "items": { + "anyOf": [ + { + "properties": { + "style": { + "enum": [ + "danger", + "success", + "primary" + ], + "type": "string" + }, + "text": { + "type": "string" + }, + "url": { + "type": "string" + } + }, + "required": [ + "text", + "url" + ], + "type": "object" + }, + { + "items": { + "properties": { + "style": { + "enum": [ + "danger", + "success", + "primary" + ], + "type": "string" + }, + "text": { + "type": "string" + }, + "url": { + "type": "string" + } + }, + "required": [ + "text", + "url" + ], + "type": "object" + }, + "type": "array" + } + ] + }, + "type": "array" +} - changed
Input schema / properties / chatId / descriptionPrevious value: -"TELEGRAM — 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."New value: +"TELEGRAM — REQUIRED whenever telegram is a channel: WHICH chat, group or channel the bot posts to. A public channel’s @username (@yourchannel) 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." - added
Input schema / properties / disablePreviewAdded value: +{ + "description": "TELEGRAM ONLY — no link preview.", + "type": "boolean" +} - added
Input schema / properties / event / properties / endDate / descriptionAdded value: +"event end date, e.g. 2026-11-01" - added
Input schema / properties / event / properties / endTime / descriptionAdded value: +"event end time, e.g. 20:00" - added
Input schema / properties / event / properties / startDate / descriptionAdded value: +"event start date, e.g. 2026-11-01" - added
Input schema / properties / event / properties / startTime / descriptionAdded value: +"event start time, e.g. 18:00" - added
Input schema / properties / event / properties / title / descriptionAdded value: +"Google Business event title; required for an event or offer post" - added
Input schema / properties / firstCommentAdded value: +{ + "description": "FIRST COMMENT — posted on each new post as the same account right after it publishes, on Facebook, Instagram and LinkedIn company Pages (LinkedIn needs its comments authorization, provider linkedin_community). Other channels publish without it and their result row says so. Up to 2,000 characters, refused rather than cut; never on a story. A failed comment never undoes the post.", + "type": "string" +} - changed
Input schema / properties / imageUrls / descriptionPrevious value: -"CAROUSEL — 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."New value: +"CAROUSEL — an ORDERED list of image URLs to publish as ONE swipeable post on every channel that supports it (Instagram 2–20, 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." - added
Input schema / properties / offer / properties / couponCode / descriptionAdded value: +"Google Business offer coupon code" - added
Input schema / properties / offer / properties / redeemOnlineUrl / descriptionAdded value: +"link where the offer is redeemed online" - added
Input schema / properties / offer / properties / termsConditions / descriptionAdded value: +"terms text shown with the offer" - added
Input schema / properties / paidProductPlacementAdded value: +{ + "description": "YOUTUBE: paid product placement (shows the disclosure)", + "type": "boolean" +} - added
Input schema / properties / parseModeAdded value: +{ + "description": "TELEGRAM ONLY — 'HTML' for Telegram's HTML tags; omit for plain text.", + "enum": [ + "HTML" + ], + "type": "string" +} - added
Input schema / properties / poll / properties / durationMinutes / descriptionAdded value: +"X poll length in minutes, 5 to 10080; default 1440 (one day)" - added
Input schema / properties / poll / properties / options / descriptionAdded value: +"X poll choices: 2 to 4 options, max 25 characters each" - added
Input schema / properties / silentAdded value: +{ + "description": "TELEGRAM ONLY — no notification sound.", + "type": "boolean" +} - added
Input schema / properties / slideText / items / properties / description / descriptionAdded value: +"Pinterest carousel slide: description for this slide" - added
Input schema / properties / slideText / items / properties / link / descriptionAdded value: +"Pinterest carousel slide: destination URL for this slide" - added
Input schema / properties / slideText / items / properties / title / descriptionAdded value: +"Pinterest carousel slide: title for this slide" - changed
Input schema / properties / story / descriptionPrevious value: -"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."New value: +"INSTAGRAM OR FACEBOOK PAGE 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. FACEBOOK PAGE STORIES WORK TOO (target facebook): one image, or one vertical 9:16 video of 3–90 seconds, no caption (a message is refused by name), not schedulable natively — use schedule_post to queue one; the reply reads the story back from the Page." - added
Input schema / properties / targetAudience / properties / degrees / descriptionAdded value: +"LinkedIn degree facet: numeric id or urn:li:degree value from search_linkedin_ads_targeting" - added
Input schema / properties / targetAudience / properties / fieldsOfStudy / descriptionAdded value: +"LinkedIn field of study facet: numeric id or urn:li:fieldOfStudy value from search_linkedin_ads_targeting" - added
Input schema / properties / targetAudience / properties / geoLocations / descriptionAdded value: +"LinkedIn geo facet: numeric id or urn:li:geo value from search_linkedin_ads_targeting" - added
Input schema / properties / targetAudience / properties / industries / descriptionAdded value: +"LinkedIn industry facet: numeric id or urn:li:industry value from search_linkedin_ads_targeting" - added
Input schema / properties / targetAudience / properties / jobFunctions / descriptionAdded value: +"LinkedIn job function facet: numeric id or urn:li:function value from search_linkedin_ads_targeting" - added
Input schema / properties / targetAudience / properties / organizations / descriptionAdded value: +"LinkedIn school or organization facet: numeric id or urn:li:organization value from search_linkedin_ads_targeting" - added
Input schema / properties / targetAudience / properties / seniorities / descriptionAdded value: +"LinkedIn seniority facet: numeric id or urn:li:seniority value from search_linkedin_ads_targeting" - added
Input schema / properties / targetAudience / properties / staffCountRanges / descriptionAdded value: +"LinkedIn company size bands, e.g. SIZE_11_TO_50" - added
Input schema / properties / threadsGhostPostAdded value: +{ + "description": "THREADS ONLY — a GHOST post: text only, Threads archives it automatically after 24 hours (a flash offer, a 24h-only note). No other Threads option can ride it.", + "type": "boolean" +} - added
Input schema / properties / threadsGifAdded value: +{ + "description": "THREADS ONLY — a GIPHY GIF id (the id in a giphy.com/gifs/… link) attached to a TEXT Threads post. GIPHY is the only provider Threads takes.", + "type": "string" +} - added
Input schema / properties / threadsLongTextAdded value: +{ + "description": "THREADS ONLY — LONG-FORM text, up to 10,000 characters, attached under a TEXT-only Threads post (Threads' text attachment). The way to publish more than Threads' 500-character post: keep a short lead in the message and put the full text here. Not with an image, video or carousel.", + "type": "string" +} - added
Input schema / properties / userTagsAdded value: +{ + "description": "INSTAGRAM USER TAGS — tag people (public accounts) on the post; each is notified. Usernames, or { username, x, y } with x/y from 0 to 1 = where the tag sits on the IMAGE (a tag without a position sits at the centre). A Reel tag takes no position; a story position is optional. Not on a carousel (refused by name).", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "properties": { + "username": { + "type": "string" + }, + "x": { + "maximum": 1, + "minimum": 0, + "type": "number" + }, + "y": { + "maximum": 1, + "minimum": 0, + "type": "number" + } + }, + "required": [ + "username" + ], + "type": "object" + } + ] + }, + "type": "array" +} - added
Input schema / properties / xArticle / properties / headings / descriptionAdded value: +"blocks (default) keeps real headings; text turns headings into bold lines" - added
Input schema / properties / xArticle / properties / title / descriptionAdded value: +"X Article headline; the post text becomes the article body in markdown"
- Added
search_meta_places - Changed
set_competitor_watch10 fields changed- added
Input schema / properties / auto / descriptionAdded value: +"true picks competitors, similar brands and topics from the saved brand or seed; costs a few credits" - added
Input schema / properties / cadence / descriptionAdded value: +"weekly (default) or daily check" - added
Input schema / properties / competitors / items / properties / kind / descriptionAdded value: +"competitor (head-to-head rival) or similar (adjacent brand)" - added
Input schema / properties / competitors / items / properties / name / descriptionAdded value: +"brand name to watch" - added
Input schema / properties / dryRun / descriptionAdded value: +"true quotes one check and saves nothing" - added
Input schema / properties / modules / properties / ideas / descriptionAdded value: +"true includes three ideas to make from the findings" - added
Input schema / properties / modules / properties / newAds / descriptionAdded value: +"true includes new ads the watched brands launched" - added
Input schema / properties / modules / properties / topPosts / descriptionAdded value: +"true includes the most-viewed posts for the topics" - added
Input schema / properties / modules / properties / trends / descriptionAdded value: +"true includes formats trending in the topics" - added
Input schema / properties / modules / properties / winners / descriptionAdded value: +"true includes watched ads that have run 30 days"
- Changed
set_post_refill1 field changed- added
Input schema / properties / forgetCaptionEdits / descriptionAdded value: +"true = forget the caption edits learned from past reviews"
- Added
share_onedrive_file - Changed
update_brand6 fields changed- added
Input schema / properties / audience / descriptionAdded value: +"who the brand sells to, one line" - added
Input schema / properties / category / descriptionAdded value: +"product or business category, one line" - added
Input schema / properties / logo / descriptionAdded value: +"logo image: URL or local file path to a PNG, JPEG or WebP (no SVG)" - added
Input schema / properties / name / descriptionAdded value: +"brand name" - added
Input schema / properties / positioning / descriptionAdded value: +"how the brand is positioned against alternatives, one line" - added
Input schema / properties / profileType / descriptionAdded value: +"website, social, describe or explore; explore keeps the profile but stops using it"
- Changed
update_doc5 fields changed- added
Input schema / properties / confirm / descriptionAdded value: +"true runs a whole-body rewrite after the character count is shown" - added
Input schema / properties / dropdowns / items / properties / tabId / descriptionAdded value: +"tab id holding the dropdown, when the document has several tabs" - added
Input schema / properties / replacements / items / properties / find / descriptionAdded value: +"exact text to find, as read_doc shows it; zero matches is reported as a failure" - added
Input schema / properties / replacements / items / properties / matchCase / descriptionAdded value: +"false ignores letter case when matching; default matches case" - added
Input schema / properties / replacements / items / properties / replace / descriptionAdded value: +"text to put in place of each match"
- Added
update_notion_row - Changed
update_saved_creator1 field changed- added
Input schema / properties / status / descriptionAdded value: +"outreach status: new, contacted, replied, booked or passed"
- Changed
update_sheet4 fields changed- added
Input schema / properties / sheetUrl / descriptionAdded value: +"Google Sheets link, instead of spreadsheetId" - added
Input schema / properties / spreadsheetId / descriptionAdded value: +"Google spreadsheet id; use this or sheetUrl" - added
Input schema / properties / updates / items / properties / range / descriptionAdded value: +"A1 range or anchor cell for this block, e.g. B2:C5 or Q3!B2" - added
Input schema / properties / updates / items / properties / values / descriptionAdded value: +"rows for this block, as an array of row arrays"
27 tool updates
v0.1.454- Changed
add_subtitles1 field changed- changed
Input schema / properties / textStyle / anyOfPrevious 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" - } -]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": { + "description": "top|center|lower|bottom or 0.05-0.95 from the top", + "type": [ + "string", + "number" + ] + }, + "preset": { + "type": "string" + }, + "shadow": { + "type": "boolean" + }, + "size": { + "description": "s|m|l|xl, '120px', or a 0.015-0.15 frame fraction", + "type": [ + "string", + "number" + ] + }, + "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" + } +]
- Added
append_to_notion_page - Changed
append_to_sheet2 fields changed- removed
Input schema / properties / rows / items / items / anyOfRemoved value: -[ - { - "type": "string" - }, - { - "type": "number" - }, - { - "type": "boolean" - } -] - added
Input schema / properties / rows / items / items / typeAdded value: +[ + "string", + "number", + "boolean" +]
- Added
click_to_ad - Changed
create_sheet2 fields changed- removed
Input schema / properties / rows / items / items / anyOfRemoved value: -[ - { - "type": "string" - }, - { - "type": "number" - }, - { - "type": "boolean" - } -] - added
Input schema / properties / rows / items / items / typeAdded value: +[ + "string", + "number", + "boolean" +]
- Added
delete_library_item - Changed
duplicate_scheduled2 fields changed- changed
Input schema / properties / at / descriptionPrevious value: -"when the copy goes out — ISO timestamp or epoch milliseconds (default: an hour from now)"New value: +"when the copy goes out: local wall clock (2026-10-30T10:00), UTC or epoch ms (default: an hour from now)" - changed
Input schema / properties / timezone / descriptionPrevious value: -"IANA zone for the queue, e.g. \"America/New_York\""New value: +"IANA zone for a wall-clock `at` or the queue"
- Added
export_schedule_to_notion - Changed
fetch_app_screens1 field changed- added
Input schema / properties / storeUrlAdded value: +{ + "description": "a Google Play / App Store link or package id", + "type": "string" +}
- Changed
find_creators1 field changed- changed
Input schema / properties / enrich / descriptionPrevious value: -"read follower counts for the top 6 (default true, ~1 credit each)"New value: +"followers + public contact for the top 3 on TikTok and on Instagram (default true, 1 credit each)"
- Added
insert_scene - Added
list_notion_pages - Changed
list_watch_findings3 fields changed- added
Input schema / properties / brandAdded value: +{ + "description": "profile id/name from list_brands, this call only", + "type": "string" +} - changed
Input schema / properties / competitor / descriptionPrevious value: -"only findings for this watched brand (exact name as returned in `watching`) — omit for all of them"New value: +"only this watched brand's ads (exact name as returned)" - changed
Input schema / properties / limit / descriptionPrevious value: -"max findings to return (default 25, max 75 — the server keeps at most 75, and at most 15 per brand)"New value: +"max ads (default 25, max 75)"
- Added
make_motion_film - Added
make_series - Changed
plan_ad2 fields changed- changed
Input schema / properties / draft / descriptionPrevious value: -"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."New value: +"Only after a video refusal that offered a lighter draft: the {model, durationSeconds} that refusal named. The plan is then written to that length and priced on that model. Use only the values the refusal gave; when an account cannot cover a video, the refusal comes before planning with three options (an image, adding credits, or this draft when one fits) and the user chooses." - changed
Input schema / properties / durationSeconds / descriptionPrevious 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: when it is omitted the planner picks a length suited to the format, and a length the user names always wins."New value: +"For video: the total length the user asked for, in seconds (30 for \"a 30 second ad\"). The storyboard’s scenes add up to it and the script is sized for it. Supported range 4 to 180 seconds; a value outside the range is clamped and the reply says so. A length that fits one clip of the render model renders as one continuous clip; a longer one is joined from clips filled to that model’s clip maximum, remainder last (on a 15s-clip model, 40 becomes 15+15+10 and 17 becomes 13+4), and is never sped up. The clip maximum depends on the model: 15s on most, 30s on the longest-clip model, so a 30s ad can be one take. Leave it out when the user named no length; the planner then picks one that suits the format."
- Changed
post_to_meta2 fields changed- changed
Input schema / properties / altText / descriptionPrevious 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.)"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 — use that 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.)" - changed
Input schema / properties / paidPartnership / descriptionPrevious value: -"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."New value: +"INSTAGRAM — the PAID PARTNERSHIP label. A COMPLIANCE DECLARATION: 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."
- Changed
product_sizzle1 field changed- added
Input schema / properties / styleAdded value: +{ + "description": "the LOOK of the sizzle (same price for every look): hypermotion (Fast cuts, crash zooms and speed ramps), product_2d (The real product with playful hand-drawn 2D animation around it), typography (Product framed by huge bold letterforms in the brand colours), dark_minimal (Moody, premium, a single hard light on black), color_pop (Bright saturated colour, glossy, joyful), mixed_media (Editorial collage: paper, cut-outs and texture around the product), saas (For apps and software: floating screens, light trails, particles), product_showcase (Hero commercial: ingredients, splashes and a clean pack shot). Default hypermotion.", + "enum": [ + "hypermotion", + "product_2d", + "typography", + "dark_minimal", + "color_pop", + "mixed_media", + "saas", + "product_showcase" + ], + "type": "string" +}
- Added
read_notion_page - Changed
recast_motion2 fields changed- changed
Input schema / properties / engine / enumPrevious value: -[ - "auto", - "h3", - "seedance", - "wan", - "wan-prime", - "kling" -]New value: +[ + "auto", + "kling-edit", + "h3", + "seedance", + "wan", + "wan-prime", + "kling" +] - changed
Input schema / properties / resolution / descriptionPrevious value: -"seedance and wan default 1080p; h3 takes 720p or 1080p, default the clip’s own"New value: +"a person swap defaults to 720p (seedance or kling-edit); otherwise seedance and wan default 1080p; h3 720p or 1080p, default the clip’s own"
- Changed
render_ad3 fields changed- removed
Input schema / properties / music / anyOfRemoved value: -[ - { - "type": "boolean" - }, - { - "type": "string" - } -] - added
Input schema / properties / music / typeAdded value: +[ + "boolean", + "string" +] - changed
Input schema / properties / textStyle / anyOfPrevious 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" - } -]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": { + "description": "top|center|lower|bottom or 0.05-0.95 from the top", + "type": [ + "string", + "number" + ] + }, + "preset": { + "type": "string" + }, + "shadow": { + "type": "boolean" + }, + "size": { + "description": "s|m|l|xl, '120px', or a 0.015-0.15 frame fraction", + "type": [ + "string", + "number" + ] + }, + "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" + } +]
- Changed
reschedule_post2 fields changed- changed
Input schema / properties / at / descriptionPrevious value: -"the new time — ISO timestamp (2026-08-05T09:00:00Z) or epoch milliseconds. Must be in the future, at most 365 days out."New value: +"the new time: local wall clock (2026-10-30T10:00, read in `timezone` or the brand’s zone on that date), UTC (…Z) or epoch ms. Future, at most 365 days out." - added
Input schema / properties / timezoneAdded value: +{ + "description": "IANA zone for a wall-clock `at`", + "type": "string" +}
- Added
save_to_notion - Changed
schedule_post3 fields changed- changed
Input schema / properties / altText / descriptionPrevious value: -"ACCESSIBILITY — 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."New value: +"ACCESSIBILITY — 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 — use that 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." - changed
Input schema / properties / at / descriptionPrevious value: -"when 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."New value: +"when to post: local wall clock, no offset (2026-10-30T10:00), read in `timezone` or the brand’s zone ON THAT DATE so a series survives a clock change; or UTC (…Z) / epoch ms. Future, at most 365 days out. Give this OR useQueue, never both." - changed
Input schema / properties / timezone / descriptionPrevious value: -"IANA 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."New value: +"IANA zone (e.g. \"Africa/Cairo\") a wall-clock `at` or the queue is read in; overrides the brand’s saved zone for this one post. Without either, UTC."
- Changed
set_competitor_watch15 fields changed- added
Input schema / properties / autoAdded value: +{ + "type": "boolean" +} - added
Input schema / properties / brandAdded value: +{ + "description": "profile id/name from list_brands, this call only", + "type": "string" +} - added
Input schema / properties / cadenceAdded value: +{ + "enum": [ + "weekly", + "daily" + ], + "type": "string" +} - changed
Input schema / properties / competitors / descriptionPrevious value: -"the brands to watch — the COMPLETE list, replacing whatever was set before. Empty array = stop watching."New value: +"the COMPLETE brand list" - changed
Input schema / properties / competitors / items / properties / domain / descriptionPrevious value: -"its domain, e.g. ridge.com — required for Google Ads Transparency, and what disambiguates a common brand name on Meta"New value: +"e.g. ridge.com: Google is read by domain, and it finds the right Meta page" - added
Input schema / properties / competitors / items / properties / kindAdded value: +{ + "enum": [ + "competitor", + "similar" + ], + "type": "string" +} - removed
Input schema / properties / competitors / items / properties / name / descriptionRemoved value: -"the brand name, as it advertises" - added
Input schema / properties / dryRunAdded value: +{ + "type": "boolean" +} - added
Input schema / properties / emailAdded value: +{ + "description": "the weekly email", + "type": "boolean" +} - added
Input schema / properties / forceAdded value: +{ + "description": "with runNow: check again right after a check, allowed once what is watched changed", + "type": "boolean" +} - added
Input schema / properties / modulesAdded value: +{ + "description": "on/off; omitted keep theirs", + "properties": { + "ideas": { + "type": "boolean" + }, + "newAds": { + "type": "boolean" + }, + "topPosts": { + "type": "boolean" + }, + "trends": { + "type": "boolean" + }, + "winners": { + "type": "boolean" + } + }, + "type": "object" +} - changed
Input schema / properties / runNow / descriptionPrevious value: -"true to run one check immediately (spends credits now) instead of waiting a week for the first one"New value: +"check now (spends now)" - added
Input schema / properties / seedAdded value: +{ + "description": "for auto: a website, brand or niche", + "type": "string" +} - added
Input schema / properties / topicsAdded value: +{ + "description": "niche search phrases, e.g. \"protein breakfast\"", + "items": { + "type": "string" + }, + "type": "array" +} - removed
Input schema / requiredRemoved value: -[ - "competitors" -]
- Changed
update_settings1 field changed- changed
Input schema / properties / language / descriptionPrevious value: -"language for generated ads, copy and answers — e.g. \"English\", \"German\", \"Japanese\""New value: +"language for generated ads and copy (chat replies follow the language the user writes in) — e.g. \"English\", \"German\", \"Japanese\""
- Changed
update_sheet4 fields changed- removed
Input schema / properties / updates / items / properties / values / items / items / anyOfRemoved value: -[ - { - "type": "string" - }, - { - "type": "number" - }, - { - "type": "boolean" - } -] - added
Input schema / properties / updates / items / properties / values / items / items / typeAdded value: +[ + "string", + "number", + "boolean" +] - removed
Input schema / properties / values / items / items / anyOfRemoved value: -[ - { - "type": "string" - }, - { - "type": "number" - }, - { - "type": "boolean" - } -] - added
Input schema / properties / values / items / items / typeAdded value: +[ + "string", + "number", + "boolean" +]
1 tool update
v0.1.436- Changed
draft_brand3 fields changed- changed
Input schema / properties / domain / descriptionPrevious value: -"a website to scrape"New value: +"a website to scrape, or a social link (profile, YouTube channel or one post) to draft from the account it names" - changed
Input schema / properties / platform / descriptionPrevious value: -"platform for socialHandle (instagram/tiktok/…)"New value: +"socialHandle platform: instagram/tiktok/youtube/twitter/threads" - changed
Input schema / properties / socialHandle / descriptionPrevious value: -"a social handle to draft from (influencers/creators) — pair with platform"New value: +"a social handle (creators); needs platform, never guessed"
16 tool updates
v0.1.435- Changed
connect_connector1 field changed- changed
Input schema / properties / provider / descriptionPrevious value: -"the connector id: applovin_ads, stripe, vercel, openai_ads, apple_ads, bluesky, telegram, bing_webmaster, posthog, mixpanel, amplitude, slack, discord, webhook"New value: +"the connector id: applovin_ads, stripe, intercom, vercel, openai_ads, apple_ads, bluesky, telegram, bing_webmaster, posthog, mixpanel, amplitude, slack, discord, webhook"
- Changed
edit_image1 field changed- changed
Input schema / properties / dryRun / descriptionPrevious value: -"true = return the exact credits this edit reserves and render nothing"New value: +"true = return the estimated credits this edit reserves and render nothing"
- Changed
fix_beat1 field changed- changed
Input schema / properties / dryRun / descriptionPrevious value: -"true = return the exact credits this fix reserves and render nothing"New value: +"true = return the estimated credits this fix reserves and render nothing"
- Changed
generate_image1 field changed- changed
Input schema / properties / mask / descriptionPrevious value: -"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."New value: +"MASKED EDIT — change ONE region and keep the rest: a mask (path or URL, any size) for refImages[0]. TRANSPARENT = change, or on a mask with no transparency WHITE = change, black = keep; the reply says which it read. The mask guides rather than stencils, so the change can blend a little past its edge. Runs on gpt-image-2.5 (`refs.mask` in hermoso_capabilities): leave `model` empty or name it; another named model is refused, free. Needs refImages; keeps the source image's frame, so aspectRatio is not applied."
- Changed
generate_video10 fields changed- changed
Input schema / properties / cameraMove / descriptionPrevious 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."New value: +"A named camera move around the still in refImage (orbit = a quarter turn, the default; orbit_half / orbit_full = a half turn / a turntable). Only minimax-h3-max-camera renders one: with model omitted it is picked; any other named model is refused, nothing charged. Needs refImage." - changed
Input schema / properties / durationSeconds / descriptionPrevious value: -"length 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)."New value: +"length of THIS ONE clip in seconds: one of the CHOSEN model’s listed durations in hermoso_capabilities (they differ per model, 4–8s on the short ones up to 30s). One continuous generation, so a longer ask than the model’s longest clip is REFUSED, nothing charged (never quietly truncated). Past ~15s NAME a long-clip model in `model`. For a longer spot use plan_ad with durationSeconds then render_ad, which stitches acts (on a 15s model, 40s = 15+15+10)." - changed
Input schema / properties / extend / descriptionPrevious value: -"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)."New value: +"true = EXTEND refVideo by durationSeconds (default 5) with what your prompt says happens next, as ONE clip. By default it CUTS to a NEW SHOT: new angle, same scene, room sound carried, no music at the cut. A named model (any i2v) renders it." - added
Input schema / properties / extendModeAdded value: +{ + "description": "with extend: 'continue' = the SAME take past its last frame, only when asked seamless (models with extend in hermoso_capabilities).", + "enum": [ + "cut", + "continue" + ], + "type": "string" +} - changed
Input schema / properties / model / descriptionPrevious value: -"video model id from hermoso_capabilities; a named model is never swapped without asking. Omit 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; its default per job (jobEngines in hermoso_capabilities, from watched side-by-side tests) is seedance-2.5 for product, lifestyle, a person talking to camera and 30s single takes (cheaper engines broke the product there). A model the user picks is kept; never switch it" - changed
Input schema / properties / musicMood / descriptionPrevious value: -"WHICH 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."New value: +"the mood of the music bed (upbeat / calm / warm / epic / tense / playful / elegant / hype / chill / dramatic; default warm). It does not decide WHETHER there is one: a clip with no audio track (a \"silent\" model in hermoso_capabilities, or one that came back mute) gets a bed composed and CHARGED automatically at explainerMusicCredits. audio:false = silent, no bed, no charge." - changed
Input schema / properties / refImages / descriptionPrevious value: -"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."New value: +"SEVERAL reference images (paths or URLs: a person, products, a place) that must all appear. A model uses up to its `refs.max` in hermoso_capabilities (one-image models take the first); with `refs.promptAddressed`, name them Image 1, Image 2… in order. minimax-h3-max-ref takes 9 and keeps each as a reference, not a first frame." - changed
Input schema / properties / refVideo / descriptionPrevious 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."New value: +"URL of an existing video to EDIT rather than generate from scratch — the clip is transformed per your prompt, keeping the SOURCE clip’s aspect ratio and, on models hermoso_capabilities marks `sourceLength`, its LENGTH (their `durations` are the per-second price ladder; a durationSeconds sent is reported as unused). 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." - changed
Input schema / properties / resolution / descriptionPrevious 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."New value: +"'1080p' default; '480p'/'720p' cheaper drafts, '4k' premium (more credits). A model renders only the tiers in its own `resolutions` list in hermoso_capabilities; a tier it lacks renders at its best one without a word, so check before promising 1080p or 4k." - changed
Input schema / properties / shots / descriptionPrevious 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."New value: +"MULTI-SHOT: one clip cut into these shots, in order; the seconds add up to a length the model renders. Replaces `prompt`. Only models with multiShot true in hermoso_capabilities; a shot prompt over that model’s shotPromptMaxChars (Kling 3.0: 512) is refused free, never cut."
- Changed
generate_voice2 fields changed- changed
Input schema / properties / engine / descriptionPrevious value: -"voice-engine id: 'seed-audio' (default), 'eleven-v3', 'minimax-speech', or 'kokoro' — listed in hermoso_capabilities"New value: +"voice-engine id: 'seed-audio' (default), 'eleven-v4' (Eleven v4), 'minimax-speech', or 'kokoro' — listed in hermoso_capabilities" - changed
Input schema / properties / voice / descriptionPrevious value: -"a voice preset from the chosen engine (e.g. 'Aria'/'George' on eleven-v3, 'stokie_en' on seed-audio) — omit for the engine default"New value: +"a voice preset from the chosen engine (e.g. 'Aria'/'George' on eleven-v4, 'stokie_en' on seed-audio) — omit for the engine default"
- Changed
list_drive_files1 field changed- added
Input schema / properties / recursiveAdded value: +{ + "description": "with folderId: the whole tree", + "type": "boolean" +}
- Changed
make_explainer1 field changed- changed
Input schema / properties / dryRun / descriptionPrevious value: -"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."New value: +"true = return the estimated 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."
- Changed
plan_ad1 field changed- added
Input schema / properties / insertsAdded value: +{ + "description": "VIDEO ONLY: the user's own SCREENS (an app, a page, a dashboard, a document, a review, a chart) shown at these seconds as a rounded card over the upper middle of the footage, which keeps playing around it (look 'full' covers the frame, only when the user asked), the voice running underneath. Fewer and shorter: about one per 4 s of film, 1.2-2 s each, on the line that talks about it; never in the first second or the last 1.5 s. The plan frames those shots for the card, keeps them free of screens, and returns the pictures as creative.inserts for render_ad to lay in (about 2 credits). A photo of a person, product or place (kind 'photo') is not laid in: pass a product photo as the brand's product photo and a person as a saved creator. One that does not fit is left out and the reply says so.", + "items": { + "properties": { + "end": { + "description": "second it ends: at most 2 s after start (2.5 s full-screen), never in the last 1.5 s of the film", + "type": "number" + }, + "image": { + "description": "an https or upload_file URL, a Library id (L0, L1…) or a local path on the CLI", + "type": "string" + }, + "kind": { + "description": "screen (default): an app, a page, a dashboard, a document, a review, a chart. photo: a person, a product or a place, which is NOT laid over the video (a product photo goes in as the brand's product photo, a person to star as a saved creator)", + "enum": [ + "screen", + "photo" + ], + "type": "string" + }, + "look": { + "description": "card (default): a rounded card over the upper middle of the footage, which keeps playing. full: covers the whole frame, ONLY when the user asked for full-screen", + "enum": [ + "card", + "full" + ], + "type": "string" + }, + "start": { + "description": "second it appears (never before 1 s: the film opens on moving footage)", + "type": "number" + } + }, + "required": [ + "image", + "start", + "end" + ], + "type": "object" + }, + "maxItems": 8, + "type": "array" +}
- Changed
post_edit1 field changed- changed
Input schema / properties / dryRun / descriptionPrevious value: -"true = return the exact credits this edit reserves and run nothing"New value: +"true = return the estimated credits this edit reserves and run nothing"
- Changed
post_to_meta1 field changed- changed
Input schema / properties / collaborators / descriptionPrevious value: -"INSTAGRAM 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."New value: +"INSTAGRAM 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. 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."
- Changed
recast_motion1 field changed- changed
Input schema / properties / engine / enumPrevious value: -[ - "auto", - "h3", - "seedance", - "wan", - "kling" -]New value: +[ + "auto", + "h3", + "seedance", + "wan", + "wan-prime", + "kling" +]
- Changed
render_ad6 fields changed- changed
Input schema / properties / creator / descriptionPrevious 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."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 (casting skips the portrait we would otherwise generate). 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." - changed
Input schema / properties / dryRun / descriptionPrevious 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"New value: +"return the routing decision (single pass vs stitched acts, resolved model + act lengths) and the estimated credits the real render reserves, WITHOUT submitting a render — free, nothing charged" - changed
Input schema / properties / durationSeconds / descriptionPrevious value: -"total 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."New value: +"total ad length in seconds, 4–180 (clamped). Omit to keep the plan’s own length, almost always right: this only RE-TIMES the authored board; to change the ad’s length, re-run plan_ad with durationSeconds. A length that fits ONE clip renders in one pass; longer is stitched from acts of the model’s clip maximum (15s on most, 30s on the longest-clip one), remainder last. dryRun:true shows the split free." - added
Input schema / properties / insertsAdded value: +{ + "description": "the user's own screens laid over the finished video at these seconds, as a card over the playing footage by default (look 'full' covers the frame, only when asked), sound running underneath (about 2 credits). The film always opens and ends on moving footage. Omit to use plan_ad's creative.inserts; [] renders without them. plan_ad with inserts writes the board around them.", + "items": { + "properties": { + "end": { + "description": "second it ends: at most 2 s after start (2.5 s full-screen), never in the last 1.5 s of the film", + "type": "number" + }, + "image": { + "description": "an https or upload_file URL, a Library id (L0, L1…) or a local path on the CLI", + "type": "string" + }, + "kind": { + "description": "screen (default): an app, a page, a dashboard, a document, a review, a chart. photo: a person, a product or a place, which is NOT laid over the video (a product photo goes in as the brand's product photo, a person to star as a saved creator)", + "enum": [ + "screen", + "photo" + ], + "type": "string" + }, + "look": { + "description": "card (default): a rounded card over the upper middle of the footage, which keeps playing. full: covers the whole frame, ONLY when the user asked for full-screen", + "enum": [ + "card", + "full" + ], + "type": "string" + }, + "start": { + "description": "second it appears (never before 1 s: the film opens on moving footage)", + "type": "number" + } + }, + "required": [ + "image", + "start", + "end" + ], + "type": "object" + }, + "maxItems": 8, + "type": "array" +} - changed
Input schema / properties / model / descriptionPrevious value: -"video 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)"New value: +"video model id from hermoso_capabilities (default: the engine the router picks for the plan: seedance-2.5 for every ad, a person talking to camera included). Naming one is a DELIBERATE pick — the server asks before ever swapping it (no silent fallback)" - changed
Input schema / properties / resolution / descriptionPrevious 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; '480p'/'720p' cheaper drafts, '4k' premium (more credits). A model renders only the tiers in its own `resolutions` list in hermoso_capabilities (the 30s model tops out at 720p); a tier it lacks renders at its best one without a word, so check before promising 1080p or 4k."
- Changed
restyle_video1 field changed- changed
Input schema / properties / engine / enumPrevious value: -[ - "auto", - "seedance", - "wan", - "kling" -]New value: +[ + "auto", + "seedance", + "wan", + "wan-prime", + "kling" +]
- Changed
search_meta_ads2 fields changed- changed
Input schema / properties / companyName / descriptionPrevious value: -"one advertiser’s ads by brand name"New value: +"one advertiser’s ads by brand name — resolved to the brand’s own page only on evidence (its Instagram, its domain, or ads that land on its domain); pass domain too for the best match" - added
Input schema / properties / domainAdded value: +{ + "description": "the advertiser’s website, e.g. buffer.com — with companyName, used to confirm which Meta page is really theirs", + "type": "string" +}
- Changed
upgrade_plan3 fields changed- added
Input schema / properties / confirmAdded value: +{ + "description": "true (with quote_token) to make a quoted upgrade", + "type": "boolean" +} - changed
Input schema / properties / period / descriptionPrevious value: -"billing cadence — monthly (default) or yearly (2 months free)"New value: +"monthly only ('yr' buys monthly)" - added
Input schema / properties / quote_tokenAdded value: +{ + "description": "the quoteToken from the quote step", + "type": "string" +}
4 tool updates
v0.1.410- Changed
connect_connector1 field changed- changed
Input schema / properties / provider / descriptionPrevious value: -"the connector id: applovin_ads, stripe, openai_ads, apple_ads, bluesky, telegram, bing_webmaster, posthog, mixpanel, amplitude, slack, discord, webhook"New value: +"the connector id: applovin_ads, stripe, vercel, openai_ads, apple_ads, bluesky, telegram, bing_webmaster, posthog, mixpanel, amplitude, slack, discord, webhook"
- Changed
generate_video2 fields changed- changed
Input schema / properties / faceRoute / descriptionPrevious value: -"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`)."New value: +"'face_lane' records the user's statement \"I own the rights to this face\" for the real person in their reference (paid plans; the likeness terms apply). Set it only when the user picked that option after a render said it could not use that face, and send the same request again; it then renders through Hermoso's face library at the normal price. Never set it on your own. The other options are a different `model` or one of the user's saved `creator`s." - changed
Input schema / properties / raw / descriptionPrevious 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."New value: +"Send the prompt to the model exactly as written, without the wording Hermoso usually adds (packaging and label guidance, reference lines, colour names for hex codes, quality hints). Use it when you want the model's own interpretation 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."
- Changed
plan_ad1 field changed- changed
Input schema / properties / durationSeconds / descriptionPrevious 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 15s spot in one take, or 30s for a two-person format such as a street interview (2026-10-14), and a length the user names always wins."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: when it is omitted the planner picks a length suited to the format, and a length the user names always wins."
- Changed
render_ad1 field changed- changed
Input schema / properties / faceRoute / descriptionPrevious value: -"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`)."New value: +"'face_lane' records the user's statement \"I own the rights to this face\" for the real person in their reference (paid plans; the likeness terms apply). Set it only when the user picked that option after a render said it could not use that face, and send the same request again; it then renders through Hermoso's face library at the normal price. Never set it on your own. The other options are a different `model` or one of the user's saved `creator`s."
5 tool updates
v0.1.409- Changed
dub_video2 fields changed- added
Input schema / properties / dryRunAdded value: +{ + "description": "quote only", + "type": "boolean" +} - changed
Input schema / properties / voice / descriptionPrevious value: -"optional target voice preset, e.g. 'Aria' (warm female) or 'George' (confident male). Defaults to a voice matching the source speaker's register."New value: +"optional, e.g. 'Aria' (woman) or 'George' (man): a native voice of that sex speaks. Default: the source speaker's sex."
- Changed
make_template_ad1 field changed- changed
Input schema / properties / config / descriptionPrevious 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."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, which takes config.music like any video) }; 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."
- Changed
plan_ad1 field changed- changed
Input schema / properties / durationSeconds / descriptionPrevious 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."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 15s spot in one take, or 30s for a two-person format such as a street interview (2026-10-14), and a length the user names always wins."
- Changed
recast_motion3 fields changed- added
Input schema / properties / dryRunAdded value: +{ + "description": "return the credits this exact job would hold and the engine auto picks, without rendering", + "type": "boolean" +} - added
Input schema / properties / languageAdded value: +{ + "description": "the new voice speaks this language (e.g. 'Spanish'), lip-synced to the new words; voice defaults to 'match'", + "type": "string" +} - added
Input schema / properties / voiceAdded value: +{ + "description": "a NEW voice for the new performer: 'match' (a stock voice that fits them), 'female'/'male', or Aria, Sarah, Charlotte, George, Brian, Charlie. Omit = the clip's own sound. Never the original speaker's or a cloned voice", + "type": "string" +}
- Changed
update_brand1 field changed- added
Input schema / properties / profileTypeAdded value: +{ + "enum": [ + "website", + "social", + "describe", + "explore" + ], + "type": "string" +}
8 tool updates
v0.1.398- Changed
generate_image1 field changed- added
Input schema / properties / logoPlacementAdded 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" +}
- Changed
generate_video4 fields changed- changed
Input schema / properties / prompt / descriptionPrevious 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`" - changed
Input schema / properties / raw / descriptionPrevious 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." - changed
Input schema / properties / shots / descriptionPrevious 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." - removed
Input schema / requiredRemoved value: -[ - "prompt" -]
- Changed
make_thumbnail1 field changed- added
Input schema / properties / logoPlacementAdded value: +{ + "description": "overlay (exact, flat) | in_scene (from the file, checked); auto: in_scene with logo3d", + "enum": [ + "auto", + "overlay", + "in_scene" + ], + "type": "string" +}
- Changed
recast_motion3 fields changed- changed
Input schema / properties / engine / enumPrevious value: -[ - "auto", - "seedance", - "wan", - "kling" -]New value: +[ + "auto", + "h3", + "seedance", + "wan", + "kling" +] - changed
Input schema / properties / image / descriptionPrevious 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." - changed
Input schema / properties / resolution / descriptionPrevious value: -"seedance and wan; default 1080p"New value: +"seedance and wan default 1080p; h3 takes 720p or 1080p, default the clip’s own"
- Changed
run_post_refill7 fields changed- added
Input schema / properties / approveAdded 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" +} - added
Input schema / properties / discardAdded value: +{ + "description": "REVIEW: draft ids to delete, or [\"all\"]. Nothing is posted for them.", + "items": { + "type": "string" + }, + "type": "array" +} - changed
Input schema / properties / dryRun / descriptionPrevious 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." - added
Input schema / properties / editAdded 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" +} - changed
Input schema / properties / force / descriptionPrevious 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." - added
Input schema / properties / learnAdded value: +{ + "description": "notes for future batches, e.g. \"too salesy\". Free.", + "items": { + "type": "string" + }, + "type": "array" +} - added
Input schema / properties / redoAdded 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" +}
- Changed
set_post_refill18 fields changed- removed
Input schema / properties / assetCooldownDaysRemoved 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" -} - added
Input schema / properties / avoidAdded value: +{ + "description": "THE BRIEF: never mention or show these — topics, claims, competitors, faces.", + "items": { + "type": "string" + }, + "type": "array" +} - added
Input schema / properties / channelPostsPerDayAdded 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" +} - removed
Input schema / properties / dryRunRemoved value: -{ - "description": "true (the default) = plan and preview only, queue nothing. Set false ONLY after a human has read a preview.", - "type": "boolean" -} - changed
Input schema / properties / enabled / descriptionPrevious 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." - added
Input schema / properties / forgetCaptionEditsAdded value: +{ + "type": "boolean" +} - added
Input schema / properties / formatsAdded 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" +} - added
Input schema / properties / goalAdded 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" +} - added
Input schema / properties / goalNoteAdded value: +{ + "description": "THE BRIEF: a line about the goal (e.g. the launch date and what is launching).", + "type": "string" +} - added
Input schema / properties / learningsAdded value: +{ + "description": "notes learned from reviews; REPLACES the list", + "items": { + "type": "string" + }, + "type": "array" +} - removed
Input schema / properties / maxCreditsPerDayRemoved value: -{ - "description": "a hard credit ceiling per day, checked BEFORE any render starts. It binds independently of the counts above.", - "type": "number" -} - removed
Input schema / properties / maxImagesPerDayRemoved value: -{ - "description": "how many NEW images a day it may render when the Library runs dry. 0 (default) = none, spend nothing.", - "type": "number" -} - removed
Input schema / properties / maxVideosPerDayRemoved 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" -} - added
Input schema / properties / modeAdded 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" +} - added
Input schema / properties / pillarsAdded 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" +} - added
Input schema / properties / postingTimesAdded 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" +} - changed
Input schema / properties / postsPerDay / descriptionPrevious 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." - added
Input schema / properties / timezoneAdded value: +{ + "description": "IANA timezone the posting times are in, e.g. \"America/New_York\" (default: the brand's, else UTC)", + "type": "string" +}
- Changed
update_brand3 fields changed- added
Input schema / properties / logoAdded value: +{ + "type": "string" +} - added
Input schema / properties / logoDarkAdded value: +{ + "description": "for light pictures", + "type": "string" +} - added
Input schema / properties / logoLightAdded value: +{ + "description": "for dark pictures; \"none\" removes", + "type": "string" +}
- Changed
update_doc5 fields changed- changed
Input schema / properties / dropdowns / descriptionPrevious value: -"dropdown chips to set"New value: +"dropdown chips to set, or dropdowns to change" - added
Input schema / properties / dropdowns / items / properties / newTitleAdded value: +{ + "description": "rename the dropdown", + "type": "string" +} - added
Input schema / properties / dropdowns / items / properties / optionsAdded 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" +} - added
Input schema / properties / dropdowns / items / properties / replaceAdded value: +{ + "additionalProperties": { + "type": "string" + }, + "description": "for an option you remove that a chip shows: {\"Old\": \"New\"}", + "propertyNames": { + "type": "string" + }, + "type": "object" +} - removed
Input schema / properties / dropdowns / items / requiredRemoved value: -[ - "value" -]
53 tool updates
v0.1.374- Changed
backfill_posts1 field changed- changed
Input schema / properties / accountRef / descriptionPrevious value: -"which Page / account, when the brand has more than one"New value: +"which Page / account, when the profile has more than one"
- Changed
cancel_scheduled1 field changed- changed
Input schema / properties / brand / descriptionPrevious 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."
- Changed
clone_static1 field changed- changed
Input schema / properties / brandId / descriptionPrevious 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"
- Changed
collect_post_metrics1 field changed- changed
Input schema / properties / brand / descriptionPrevious 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."
- Changed
connect_connector1 field changed- changed
Input schema / properties / provider / descriptionPrevious 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"
- Changed
create_brand2 fields changed- changed
Input schema / properties / activate / descriptionPrevious 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" - changed
Input schema / properties / name / descriptionPrevious value: -"the brand / client name for the new workspace"New value: +"the name for the new profile (a brand, client or creator name)"
- Changed
delete_brand2 fields changed- changed
Input schema / properties / brand / descriptionPrevious value: -"brand id or exact name from list_brands"New value: +"profile id or exact name from list_brands" - changed
Input schema / properties / confirm / descriptionPrevious 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"
- Changed
delete_creator1 field changed- changed
Input schema / properties / brand / descriptionPrevious value: -"brand id/name from list_brands, this call only"New value: +"profile id/name from list_brands, this call only"
- Changed
delete_playbook1 field changed- changed
Input schema / properties / brand / descriptionPrevious value: -"brand id/name from list_brands, this call only"New value: +"profile id/name from list_brands, this call only"
- Changed
delete_skill1 field changed- changed
Input schema / properties / brand / descriptionPrevious value: -"brand id/name from list_brands, this call only"New value: +"profile id/name from list_brands, this call only"
- Changed
draft_brand1 field changed- changed
Input schema / properties / save / descriptionPrevious 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"
- Changed
duplicate_scheduled1 field changed- changed
Input schema / properties / brand / descriptionPrevious 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."
- Changed
edit_video2 fields changed- added
Input schema / properties / engineAdded value: +{ + "description": "default auto: Seedance 2.5 Edit without a real-looking person, else Kling O3 Edit", + "enum": [ + "auto", + "seedance", + "kling" + ], + "type": "string" +} - added
Input schema / properties / faceRouteAdded 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" +}
- Changed
fetch_app_screens1 field changed- changed
Input schema / properties / brandId / descriptionPrevious 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"
- Changed
forget1 field changed- changed
Input schema / properties / brand / descriptionPrevious value: -"brand id/name from list_brands, this call only"New value: +"profile id/name from list_brands, this call only"
- Changed
get_brand1 field changed- changed
Input schema / properties / brand / descriptionPrevious value: -"brand id/name from list_brands, this call only"New value: +"profile id/name from list_brands, this call only"
- Changed
list_meta_posts2 fields changed- changed
Input schema / properties / brand / descriptionPrevious 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." - changed
Input schema / properties / pageId / descriptionPrevious value: -"which connected Page — omit when the brand has only one"New value: +"which connected Page — omit when the profile has only one"
- Changed
list_product_photos1 field changed- changed
Input schema / properties / brandId / descriptionPrevious 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"
- Changed
list_published_posts1 field changed- changed
Input schema / properties / brand / descriptionPrevious 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."
- Changed
list_scheduled1 field changed- changed
Input schema / properties / brand / descriptionPrevious 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."
- Changed
mine_angles1 field changed- changed
Input schema / properties / brandId / descriptionPrevious 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"
- Changed
post_edit4 fields changed- added
Input schema / properties / ops / items / properties / keepSoundAdded value: +{ + "type": "boolean" +} - changed
Input schema / properties / ops / items / properties / op / enumPrevious 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" +] - changed
Input schema / properties / ops / items / properties / text / descriptionPrevious value: -"text: the words, verbatim"New value: +"text/voiceover: the words, verbatim" - added
Input schema / properties / ops / items / properties / voiceAdded value: +{ + "type": "string" +}
- Changed
post_performance1 field changed- changed
Input schema / properties / brand / descriptionPrevious 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."
- Changed
post_to_bluesky2 fields changed- changed
Input schema / properties / account / descriptionPrevious 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." - changed
Input schema / properties / brand / descriptionPrevious 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."
- Changed
post_to_google_business1 field changed- changed
Input schema / properties / brand / descriptionPrevious 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."
- Changed
post_to_linkedin1 field changed- changed
Input schema / properties / brand / descriptionPrevious 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."
- Changed
post_to_linkedin_page1 field changed- changed
Input schema / properties / brand / descriptionPrevious 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."
- Changed
post_to_meta1 field changed- changed
Input schema / properties / brand / descriptionPrevious 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."
- Changed
post_to_pinterest2 fields changed- changed
Input schema / properties / account / descriptionPrevious 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." - changed
Input schema / properties / brand / descriptionPrevious 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."
- Changed
post_to_telegram2 fields changed- changed
Input schema / properties / account / descriptionPrevious 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." - changed
Input schema / properties / brand / descriptionPrevious 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."
- Changed
post_to_tiktok2 fields changed- changed
Input schema / properties / account / descriptionPrevious 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." - changed
Input schema / properties / brand / descriptionPrevious 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."
- Changed
post_to_x2 fields changed- changed
Input schema / properties / account / descriptionPrevious 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." - changed
Input schema / properties / brand / descriptionPrevious 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."
- Changed
post_to_youtube2 fields changed- changed
Input schema / properties / account / descriptionPrevious 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." - changed
Input schema / properties / brand / descriptionPrevious 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."
- Changed
post_x_article1 field changed- changed
Input schema / properties / brand / descriptionPrevious 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."
- Changed
recast_motion9 fields changed- added
Input schema / properties / engineAdded value: +{ + "description": "default auto", + "enum": [ + "auto", + "seedance", + "wan", + "kling" + ], + "type": "string" +} - added
Input schema / properties / faceRouteAdded 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" +} - changed
Input schema / properties / image / descriptionPrevious 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." - added
Input schema / properties / imagesAdded value: +{ + "description": "seedance/wan: more pictures, @Image2…: a character, product, outfit or place", + "items": { + "type": "string" + }, + "maxItems": 8, + "type": "array" +} - changed
Input schema / properties / orientation / descriptionPrevious 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" - changed
Input schema / properties / prompt / descriptionPrevious value: -"optional scene/style guidance"New value: +"optional, e.g. 'same moves, new location: Tokyo at night'" - added
Input schema / properties / resolutionAdded value: +{ + "description": "seedance and wan; default 1080p", + "enum": [ + "480p", + "720p", + "1080p" + ], + "type": "string" +} - changed
Input schema / properties / tier / descriptionPrevious 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" - changed
Input schema / requiredPrevious value: -[ - "image", - "video" -]New value: +[ + "video" +]
- Changed
remember1 field changed- changed
Input schema / properties / brand / descriptionPrevious value: -"brand id/name from list_brands, this call only"New value: +"profile id/name from list_brands, this call only"
- Changed
remix_static1 field changed- changed
Input schema / properties / brandId / descriptionPrevious 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"
- Changed
reschedule_post1 field changed- changed
Input schema / properties / brand / descriptionPrevious 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."
- Changed
restyle_video6 fields changed- added
Input schema / properties / charactersAdded 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" +} - added
Input schema / properties / engineAdded value: +{ + "description": "default auto", + "enum": [ + "auto", + "seedance", + "wan", + "kling" + ], + "type": "string" +} - added
Input schema / properties / faceRouteAdded 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" +} - added
Input schema / properties / resolutionAdded value: +{ + "description": "Seedance and Wan; default 1080p", + "enum": [ + "480p", + "720p", + "1080p" + ], + "type": "string" +} - changed
Input schema / properties / style / descriptionPrevious 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)" - added
Input schema / properties / styleImagesAdded value: +{ + "description": "pictures that ARE the look (their style, never their subject)", + "items": { + "type": "string" + }, + "maxItems": 9, + "type": "array" +}
- Changed
retry_scheduled1 field changed- changed
Input schema / properties / brand / descriptionPrevious 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."
- Changed
save_creator1 field changed- changed
Input schema / properties / brand / descriptionPrevious value: -"brand id/name from list_brands, this call only"New value: +"profile id/name from list_brands, this call only"
- Changed
save_playbook1 field changed- changed
Input schema / properties / brand / descriptionPrevious value: -"brand id/name from list_brands, this call only"New value: +"profile id/name from list_brands, this call only"
- Changed
save_skill1 field changed- changed
Input schema / properties / brand / descriptionPrevious value: -"brand id/name from list_brands, this call only"New value: +"profile id/name from list_brands, this call only"
- Changed
save_to_swipefile1 field changed- changed
Input schema / properties / brand / descriptionPrevious value: -"brand id/name from list_brands, this call only"New value: +"profile id/name from list_brands, this call only"
- Changed
schedule_post3 fields changed- changed
Input schema / properties / brand / descriptionPrevious 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." - changed
Input schema / properties / pageId / descriptionPrevious 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." - changed
Input schema / properties / useQueue / descriptionPrevious 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."
- Changed
set_connector_accounts1 field changed- changed
Input schema / properties / accountIds / descriptionPrevious 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"
- Changed
set_post_refill1 field changed- changed
Input schema / properties / linkedinOrganizationId / descriptionPrevious 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."
- Changed
set_product_image1 field changed- changed
Input schema / properties / brandId / descriptionPrevious 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"
- Changed
tidy_memory1 field changed- changed
Input schema / properties / brand / descriptionPrevious value: -"brand id/name from list_brands, this call only"New value: +"profile id/name from list_brands, this call only"
- Changed
tiktok_post_status1 field changed- changed
Input schema / properties / account / descriptionPrevious 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"
- Changed
update_brand1 field changed- changed
Input schema / properties / brand / descriptionPrevious value: -"brand id/name from list_brands, this call only"New value: +"profile id/name from list_brands, this call only"
- Changed
update_saved_creator1 field changed- changed
Input schema / properties / brand / descriptionPrevious value: -"brand id/name from list_brands, this call only"New value: +"profile id/name from list_brands, this call only"
- Changed
use_brand1 field changed- changed
Input schema / properties / brand / descriptionPrevious 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"
6 tool updates
v0.1.372- Changed
append_to_doc3 fields changed- added
Input schema / properties / dropdownAdded 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" +} - changed
Input schema / properties / text / descriptionPrevious 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)" - changed
Input schema / requiredPrevious value: -[ - "documentId", - "text" -]New value: +[ + "documentId" +]
- Changed
post_to_meta3 fields changed- added
Input schema / properties / instagramCommentPromptAdded 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" +} - added
Input schema / properties / instagramPollAdded 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" +} - added
Input schema / properties / instagramPollExtendedAdded 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" +}
- Changed
reschedule_post3 fields changed- added
Input schema / properties / instagramCommentPromptAdded 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" +} - added
Input schema / properties / instagramPollAdded 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" +} - added
Input schema / properties / instagramPollExtendedAdded 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" +}
- Added
restyle_video - Changed
schedule_post3 fields changed- added
Input schema / properties / instagramCommentPromptAdded 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" +} - added
Input schema / properties / instagramPollAdded 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" +} - added
Input schema / properties / instagramPollExtendedAdded 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" +}
- Changed
update_doc1 field changed- added
Input schema / properties / dropdownsAdded 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" +}
17 tool updates
v0.1.371- Changed
delete_creator1 field changed- added
Input schema / properties / brandAdded value: +{ + "description": "brand id/name from list_brands, this call only", + "type": "string" +}
- Changed
delete_playbook1 field changed- added
Input schema / properties / brandAdded value: +{ + "description": "brand id/name from list_brands, this call only", + "type": "string" +}
- Changed
delete_skill1 field changed- added
Input schema / properties / brandAdded value: +{ + "description": "brand id/name from list_brands, this call only", + "type": "string" +}
- Changed
forget1 field changed- added
Input schema / properties / brandAdded value: +{ + "description": "brand id/name from list_brands, this call only", + "type": "string" +}
- Changed
get_brand1 field changed- added
Input schema / properties / brandAdded value: +{ + "description": "brand id/name from list_brands, this call only", + "type": "string" +}
- Changed
post_to_meta1 field changed- added
Input schema / additionalPropertiesAdded value: +{}
- Changed
remember1 field changed- added
Input schema / properties / brandAdded value: +{ + "description": "brand id/name from list_brands, this call only", + "type": "string" +}
- Changed
reschedule_post1 field changed- changed
Input schema / properties / aiGenerated / descriptionPrevious 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."
- Changed
save_creator1 field changed- added
Input schema / properties / brandAdded value: +{ + "description": "brand id/name from list_brands, this call only", + "type": "string" +}
- Changed
save_playbook1 field changed- changed
Input schema / properties / brand / descriptionPrevious value: -"which brand this is for (defaults to the workspace brand)"New value: +"brand id/name from list_brands, this call only"
- Changed
save_skill1 field changed- added
Input schema / properties / brandAdded value: +{ + "description": "brand id/name from list_brands, this call only", + "type": "string" +}
- Changed
save_to_swipefile1 field changed- added
Input schema / properties / brandAdded value: +{ + "description": "brand id/name from list_brands, this call only", + "type": "string" +}
- Changed
schedule_post1 field changed- changed
Input schema / properties / aiGenerated / descriptionPrevious 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."
- Changed
tidy_memory1 field changed- added
Input schema / properties / brandAdded value: +{ + "description": "brand id/name from list_brands, this call only", + "type": "string" +}
- Added
tiktok_post_status - Changed
update_brand1 field changed- added
Input schema / properties / brandAdded value: +{ + "description": "brand id/name from list_brands, this call only", + "type": "string" +}
- Changed
update_saved_creator1 field changed- added
Input schema / properties / brandAdded value: +{ + "description": "brand id/name from list_brands, this call only", + "type": "string" +}
18 tool updates
v0.1.366- Changed
clone_static1 field changed- changed
Input schema / properties / imageUrl / descriptionPrevious 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."
- Changed
draft_brand1 field changed- changed
Input schema / properties / save / descriptionPrevious 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"
- Changed
edit_image1 field changed- added
Input schema / properties / fixLabelAdded 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" +}
- Changed
generate_avatar8 fields changed- added
Input schema / properties / acceptQueueAdded value: +{ + "description": "only for an engine hermoso_capabilities marks oneAtATime: wait in line", + "type": "boolean" +} - added
Input schema / properties / dryRunAdded value: +{ + "description": "return the credits this exact job would hold, without rendering", + "type": "boolean" +} - added
Input schema / properties / engineAdded value: +{ + "description": "leave out for the standard engine; only an engine listed in hermoso_capabilities avatarEngines is accepted", + "type": "string" +} - changed
Input schema / properties / image / descriptionPrevious 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." - added
Input schema / properties / promptAdded value: +{ + "description": "motion direction, only for an engine other than standard that hermoso_capabilities lists", + "type": "string" +} - changed
Input schema / properties / resolution / descriptionPrevious value: -"'1080p' (default) or '480p'/'720p' draft"New value: +"'720p' (default) or '480p'" - added
Input schema / properties / seedAdded value: +{ + "description": "fixed seed, only for an engine other than standard", + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" +} - changed
Input schema / properties / voice / descriptionPrevious 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"
- Changed
generate_image2 fields changed- added
Input schema / properties / fixLabelAdded 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" +} - changed
Input schema / properties / refImages / descriptionPrevious 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."
- Changed
generate_video5 fields changed- added
Input schema / properties / anglesAdded 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" +} - added
Input schema / properties / creatorAdded 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" +} - added
Input schema / properties / faceRouteAdded 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" +} - changed
Input schema / properties / refImage / descriptionPrevious 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." - added
Input schema / properties / speakAdded 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" +}
- Changed
headline_variants1 field changed- added
Input schema / properties / fixLabelAdded 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" +}
- Changed
list_creators1 field changed- added
Input schema / properties / genderAdded value: +{ + "description": "filter the PRESET creators by gender (the saved cast is never filtered)", + "enum": [ + "female", + "male" + ], + "type": "string" +}
- Changed
localize_ad1 field changed- added
Input schema / properties / fixLabelAdded 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" +}
- Changed
make_explainer14 fields changed- changed
Input schema / properties / aspectRatio / descriptionPrevious value: -"'9:16' default"New value: +"'9:16' default (a faceless channel video and a host episode default to 16:9)" - added
Input schema / properties / camerasAdded 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" +} - added
Input schema / properties / creatorAdded value: +{ + "description": "host_episode: the host, a saved creator or a preset by name or id (list_creators)", + "type": "string" +} - added
Input schema / properties / dryRunAdded 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" +} - added
Input schema / properties / formatAdded 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" +} - changed
Input schema / properties / frameDensity / descriptionPrevious 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." - added
Input schema / properties / fromDraftAdded 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" +} - added
Input schema / properties / hostImageAdded 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" +} - added
Input schema / properties / laneAdded 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" +} - added
Input schema / properties / scriptAdded value: +{ + "description": "host_episode: the exact words, said verbatim and split at natural breaks into 4-30s pieces", + "type": "string" +} - added
Input schema / properties / settingAdded value: +{ + "description": "host_episode: the set in words (default: written to fit the topic)", + "type": "string" +} - added
Input schema / properties / thumbnailAdded value: +{ + "description": "host_episode: one thumbnail of the host (default true)", + "type": "boolean" +} - changed
Input schema / properties / topic / descriptionPrevious 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`)" - removed
Input schema / requiredRemoved value: -[ - "topic" -]
- Changed
multiply_ad1 field changed- added
Input schema / properties / changeAdded 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" +}
- Changed
plan_ad2 fields changed- changed
Input schema / properties / durationSeconds / descriptionPrevious 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." - added
Input schema / properties / talentAdded 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" +}
- Changed
post_edit8 fields changed- changed
Input schema / properties / ops / items / properties / db / descriptionPrevious value: -"audio_gain -20..+6 dB"New value: +"audio_gain -20..+6 dB / music: trim on its automatic level, -20..+12" - added
Input schema / properties / ops / items / properties / duckAdded value: +{ + "anyOf": [ + { + "const": "auto", + "type": "string" + }, + { + "type": "boolean" + } + ], + "description": "music: 'auto' default = dips while the clip's own sound plays" +} - added
Input schema / properties / ops / items / properties / fadeOutAdded value: +{ + "description": "music: fade-out seconds 0-5", + "type": "number" +} - added
Input schema / properties / ops / items / properties / fromAdded value: +{ + "description": "music: the second of the track to start from", + "type": "number" +} - added
Input schema / properties / ops / items / properties / moodAdded value: +{ + "description": "music: mood words for the library pick, or the brief for generated music", + "type": "string" +} - changed
Input schema / properties / ops / items / properties / op / enumPrevious 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" +] - added
Input schema / properties / ops / items / properties / replaceAdded value: +{ + "description": "music: true = replaces the clip's own sound", + "type": "boolean" +} - added
Input schema / properties / ops / items / properties / trackAdded 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" +}
- Changed
recast_motion1 field changed- changed
Input schema / properties / image / descriptionPrevious 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."
- Changed
remix_static1 field changed- changed
Input schema / properties / imageUrl / descriptionPrevious 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."
- Changed
render_ad7 fields changed- added
Input schema / properties / boardAdded 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" +} - changed
Input schema / properties / creator / descriptionPrevious 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." - added
Input schema / properties / faceRouteAdded 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" +} - added
Input schema / properties / foundationImagesAdded 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" +} - added
Input schema / properties / foundationsAdded 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" +} - added
Input schema / properties / moodboardPickAdded value: +{ + "description": "which moodboard panel (1-4, left to right, top to bottom) the storyboard follows; default 1", + "maximum": 4, + "minimum": 1, + "type": "integer" +} - changed
Input schema / properties / music / descriptionPrevious 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"
- Changed
resize_ad1 field changed- added
Input schema / properties / fixLabelAdded 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" +}
- Changed
update_brand2 fields changed- added
Input schema / properties / pronounceAdded 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" +} - added
Input schema / properties / pronunciationsAdded 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" +}
29 tool updates
v0.1.320- Changed
cancel_scheduled1 field changed- changed
Input schema / properties / brand / descriptionPrevious 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."
- Changed
collect_post_metrics1 field changed- changed
Input schema / properties / brand / descriptionPrevious 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."
- Changed
duplicate_scheduled1 field changed- changed
Input schema / properties / brand / descriptionPrevious 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."
- Changed
edit_image1 field changed- added
Input schema / properties / dryRunAdded value: +{ + "description": "true = return the exact credits this edit reserves and render nothing", + "type": "boolean" +}
- Changed
edit_video11 fields changed- changed
Input schema / properties / elements / descriptionPrevious 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" - changed
Input schema / properties / instruction / descriptionPrevious value: -"the exact transformation to apply, in the user’s own words"New value: +"the change, in the user’s own words" - changed
Input schema / properties / interactionId / descriptionPrevious 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." - changed
Input schema / properties / keepAudio / descriptionPrevious 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" - added
Input schema / properties / lightingAdded 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" +} - added
Input schema / properties / literalAdded value: +{ + "description": "true = send the instruction exactly as written, with no preserve/lock wrapper", + "type": "boolean" +} - added
Input schema / properties / previewAtAdded value: +{ + "description": "second to preview (default 0); resend with previewStill", + "type": "number" +} - added
Input schema / properties / previewFirstFrameAdded value: +{ + "description": "true = edit ONE still of the first frame first and quote the clip; the paid clip does not run", + "type": "boolean" +} - added
Input schema / properties / previewStillAdded value: +{ + "description": "the still a previewFirstFrame call returned; the clip then matches the changed thing to it", + "type": "string" +} - added
Input schema / properties / referenceAdded 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" +} - changed
Input schema / properties / video / descriptionPrevious 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)"
- Changed
fix_beat1 field changed- added
Input schema / properties / dryRunAdded value: +{ + "description": "true = return the exact credits this fix reserves and render nothing", + "type": "boolean" +}
- Changed
generate_image2 fields changed- changed
Input schema / properties / prompt / descriptionPrevious 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." - removed
Input schema / requiredRemoved value: -[ - "prompt" -]
- Changed
list_meta_posts1 field changed- changed
Input schema / properties / brand / descriptionPrevious 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."
- Changed
list_published_posts1 field changed- changed
Input schema / properties / brand / descriptionPrevious 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."
- Changed
make_template_ad1 field changed- changed
Input schema / properties / config / descriptionPrevious 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."
- Changed
make_thumbnail2 fields changed- changed
Input schema / properties / emotion / descriptionPrevious 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" - changed
Input schema / properties / framework / descriptionPrevious 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"
- Changed
post_edit22 fields changed- added
Input schema / properties / dryRunAdded value: +{ + "description": "true = return the exact credits this edit reserves and run nothing", + "type": "boolean" +} - changed
Input schema / properties / ops / items / properties / background / descriptionPrevious 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" - added
Input schema / properties / ops / items / properties / bridge / properties / sound / descriptionAdded value: +"'auto' default, 'own', 'impact', 'whoosh', 'none', or an audio URL (find_sound)" - removed
Input schema / properties / ops / items / properties / bridge / properties / sound / enumRemoved value: -[ - "auto", - "own", - "impact", - "whoosh", - "none" -] - changed
Input schema / properties / ops / items / properties / card_html / descriptionPrevious 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)" - added
Input schema / properties / ops / items / properties / clips / items / properties / matchAdded value: +{} - added
Input schema / properties / ops / items / properties / clips / items / properties / reframeAdded value: +{} - changed
Input schema / properties / ops / items / properties / corner / descriptionPrevious value: -"watermark corner (default br)"New value: +"watermark corner (default br), or x/y" - changed
Input schema / properties / ops / items / properties / headline / descriptionPrevious value: -"append_card: big line (defaults to the brand name)"New value: +"append_card: big line (default: brand name)" - added
Input schema / properties / ops / items / properties / intensity / anyOfAdded value: +[ + { + "enum": [ + "default", + "strong" + ], + "type": "string" + }, + { + "type": "number" + } +] - changed
Input schema / properties / ops / items / properties / intensity / descriptionPrevious value: -"grain look"New value: +"grain: or 0-1 (default 0.25)" - removed
Input schema / properties / ops / items / properties / intensity / enumRemoved value: -[ - "default", - "strong" -] - removed
Input schema / properties / ops / items / properties / intensity / typeRemoved value: -"string" - added
Input schema / properties / ops / items / properties / matchAdded value: +{ + "description": "join: seam match, 'auto' default | 'off' | {grade,level,grain,blur,strength}" +} - changed
Input schema / properties / ops / items / properties / position / descriptionPrevious value: -"text: where"New value: +"text: where, or x/y" - added
Input schema / properties / ops / items / properties / reframeAdded value: +{ + "description": "join: shot change at a same-framing stitch, 'auto' | 'off' | step 1.1-1.5" +} - changed
Input schema / properties / ops / items / properties / seconds / descriptionPrevious 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" - changed
Input schema / properties / ops / items / properties / sub / descriptionPrevious 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" - changed
Input schema / properties / ops / items / properties / transition / descriptionPrevious value: -"join"New value: +"join: 'cut' default, 'crossfade', or any ffmpeg xfade name (wipeleft…)" - removed
Input schema / properties / ops / items / properties / transition / enumRemoved value: -[ - "cut", - "crossfade" -] - added
Input schema / properties / ops / items / properties / xAdded value: +{ + "description": "text/watermark centre: 0-1 of frame, or px", + "type": "number" +} - added
Input schema / properties / ops / items / properties / yAdded value: +{ + "type": "number" +}
- Changed
post_performance1 field changed- changed
Input schema / properties / brand / descriptionPrevious 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."
- Changed
post_to_bluesky1 field changed- changed
Input schema / properties / brand / descriptionPrevious 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."
- Changed
post_to_google_business3 fields changed- changed
Input schema / properties / brand / descriptionPrevious 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." - changed
Input schema / properties / hook / descriptionPrevious 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." - changed
Input schema / properties / subject / descriptionPrevious 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."
- Changed
post_to_linkedin4 fields changed- changed
Input schema / properties / brand / descriptionPrevious 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." - changed
Input schema / properties / hook / descriptionPrevious 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." - changed
Input schema / properties / idempotencyKey / descriptionPrevious 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." - changed
Input schema / properties / subject / descriptionPrevious 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."
- Changed
post_to_linkedin_page5 fields changed- changed
Input schema / properties / brand / descriptionPrevious 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." - added
Input schema / properties / coverAtMsAdded 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" +} - changed
Input schema / properties / hook / descriptionPrevious 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." - changed
Input schema / properties / idempotencyKey / descriptionPrevious 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." - changed
Input schema / properties / subject / descriptionPrevious 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."
- Changed
post_to_meta8 fields changed- added
Input schema / properties / audioIdAdded 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" +} - added
Input schema / properties / audioVolumeAdded value: +{ + "description": "INSTAGRAM REEL — how loud that track plays, 0–100 (Instagram’s default 100). Needs audioId.", + "maximum": 100, + "minimum": 0, + "type": "integer" +} - changed
Input schema / properties / brand / descriptionPrevious 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." - added
Input schema / properties / coverAtMsAdded 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" +} - changed
Input schema / properties / hook / descriptionPrevious 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." - changed
Input schema / properties / idempotencyKey / descriptionPrevious 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." - changed
Input schema / properties / subject / descriptionPrevious 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." - added
Input schema / properties / videoVolumeAdded 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" +}
- Changed
post_to_pinterest5 fields changed- changed
Input schema / properties / brand / descriptionPrevious 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." - added
Input schema / properties / coverAtMsAdded 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" +} - changed
Input schema / properties / hook / descriptionPrevious 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." - changed
Input schema / properties / idempotencyKey / descriptionPrevious 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." - changed
Input schema / properties / subject / descriptionPrevious 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."
- Changed
post_to_telegram2 fields changed- changed
Input schema / properties / brand / descriptionPrevious 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." - added
Input schema / properties / coverAtMsAdded 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" +}
- Changed
post_to_tiktok4 fields changed- changed
Input schema / properties / brand / descriptionPrevious 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." - changed
Input schema / properties / destination / descriptionPrevious 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." - changed
Input schema / properties / hook / descriptionPrevious 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." - changed
Input schema / properties / subject / descriptionPrevious 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."
- Changed
post_to_x3 fields changed- changed
Input schema / properties / brand / descriptionPrevious 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." - changed
Input schema / properties / hook / descriptionPrevious 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." - changed
Input schema / properties / subject / descriptionPrevious 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."
- Changed
post_to_youtube5 fields changed- changed
Input schema / properties / brand / descriptionPrevious 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." - added
Input schema / properties / coverAtMsAdded 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" +} - changed
Input schema / properties / hook / descriptionPrevious 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." - changed
Input schema / properties / privacy / descriptionPrevious 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)" - changed
Input schema / properties / subject / descriptionPrevious 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."
- Changed
post_x_article3 fields changed- changed
Input schema / properties / brand / descriptionPrevious 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." - changed
Input schema / properties / hook / descriptionPrevious 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." - changed
Input schema / properties / subject / descriptionPrevious 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."
- Changed
pull_competitor_ads7 fields changed- added
Input schema / properties / brandAdded value: +{ + "description": "same as companyName", + "type": "string" +} - added
Input schema / properties / companyAdded value: +{ + "description": "same as companyName", + "type": "string" +} - changed
Input schema / properties / companyName / descriptionPrevious value: -"the advertiser name"New value: +"the advertiser name, e.g. \"Liquid Death\" (company / brand / name are read as this too)" - changed
Input schema / properties / domain / descriptionPrevious 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" - added
Input schema / properties / nameAdded value: +{ + "description": "same as companyName", + "type": "string" +} - added
Input schema / properties / urlAdded value: +{ + "description": "same as domain", + "type": "string" +} - added
Input schema / properties / websiteAdded value: +{ + "description": "same as domain", + "type": "string" +}
- Changed
render_ad1 field changed- changed
Input schema / properties / dryRun / descriptionPrevious 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"
- Changed
reschedule_post6 fields changed- added
Input schema / properties / audioIdAdded 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" +} - added
Input schema / properties / audioVolumeAdded value: +{ + "description": "INSTAGRAM REEL — how loud that track plays, 0–100 (Instagram’s default 100). Needs audioId.", + "maximum": 100, + "minimum": 0, + "type": "integer" +} - changed
Input schema / properties / brand / descriptionPrevious 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." - added
Input schema / properties / coverAtMsAdded value: +{ + "description": "THE VIDEO COVER on every channel, as one frame in milliseconds (see schedule_post).", + "type": "number" +} - added
Input schema / properties / coverImageUrlAdded value: +{ + "description": "THE VIDEO COVER on every channel, as a Hermoso-hosted picture (see schedule_post).", + "type": "string" +} - added
Input schema / properties / videoVolumeAdded 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" +}
- Changed
retry_scheduled1 field changed- changed
Input schema / properties / brand / descriptionPrevious 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."
- Changed
schedule_post8 fields changed- added
Input schema / properties / audioIdAdded 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" +} - added
Input schema / properties / audioVolumeAdded value: +{ + "description": "INSTAGRAM REEL — how loud that track plays, 0–100 (Instagram’s default 100). Needs audioId.", + "maximum": 100, + "minimum": 0, + "type": "integer" +} - changed
Input schema / properties / brand / descriptionPrevious 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." - added
Input schema / properties / coverAtMsAdded 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" +} - added
Input schema / properties / coverImageUrlAdded 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" +} - changed
Input schema / properties / hook / descriptionPrevious 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." - changed
Input schema / properties / subject / descriptionPrevious 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." - added
Input schema / properties / videoVolumeAdded 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" +}
20 tool updates
v0.1.285- Changed
add_subtitles5 fields changed- added
Input schema / properties / autoAdded value: +{ + "enum": [ + "sentences", + "words" + ], + "type": "string" +} - added
Input schema / properties / cuesAdded 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" +} - changed
Input schema / properties / textStyle / anyOfPrevious 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" + } +] - changed
Input schema / properties / textStyle / descriptionPrevious value: -"the look: a preset or overrides; omit for the default"New value: +"the look: words, a preset or fields; omit for the default" - added
Input schema / properties / wordsPerCueAdded value: +{ + "type": "number" +}
- Changed
analyze_video3 fields changed- added
Input schema / properties / anchorsAdded value: +{ + "description": "{afterWord|beforeWord|atWord, occurrence?, offset?} -> s", + "items": {}, + "type": "array" +} - added
Input schema / properties / framesAdded value: +{ + "description": "also return the frames as images", + "type": "boolean" +} - added
Input schema / properties / wordsAdded value: +{ + "description": "true | 'only': each spoken word's start/end" +}
- Changed
clip_video2 fields changed- changed
Input schema / properties / aspectRatio / descriptionPrevious 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" - removed
Input schema / properties / aspectRatio / enumRemoved value: -[ - "9:16", - "1:1", - "16:9", - "keep" -]
- Changed
clone_static1 field changed- changed
Input schema / properties / imageUrl / descriptionPrevious 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"
- Added
find_sound - Changed
generate_avatar1 field changed- changed
Input schema / properties / image / descriptionPrevious 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"
- Changed
generate_image1 field changed- changed
Input schema / properties / refImages / descriptionPrevious 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."
- Added
generate_music - Changed
generate_video1 field changed- changed
Input schema / properties / refImage / descriptionPrevious 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"
- Changed
hook_variants1 field changed- added
Input schema / properties / hooksAdded 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" +}
- Changed
make_explainer3 fields changed- changed
Input schema / properties / music / descriptionPrevious 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." - changed
Input schema / properties / style / descriptionPrevious 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." - removed
Input schema / properties / style / enumRemoved 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" -]
- Added
make_insert - Changed
make_template_ad1 field changed- changed
Input schema / properties / config / descriptionPrevious 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"
- Changed
make_thumbnail7 fields changed- changed
Input schema / properties / font / descriptionPrevious 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" - changed
Input schema / properties / framework / descriptionPrevious 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" - changed
Input schema / properties / headlinePlace / descriptionPrevious 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" - removed
Input schema / properties / headlinePlace / enumRemoved value: -[ - "bottom", - "top", - "center" -] - changed
Input schema / properties / overlayStyle / descriptionPrevious 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" - changed
Input schema / properties / tweak / descriptionPrevious 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" - removed
Input schema / properties / tweak / properties / kind / enumRemoved value: -[ - "emotion", - "background", - "background_color", - "rim_light" -]
- Changed
multiply_ad2 fields changed- changed
Input schema / properties / axes / descriptionPrevious 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\"" - removed
Input schema / properties / axes / items / enumRemoved value: -[ - "character", - "outfit", - "location", - "objects" -]
- Changed
post_edit2 fields changed- added
Input schema / properties / ops / items / properties / bridgeAdded 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" +} - added
Input schema / properties / ops / items / properties / textStyleAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "additionalProperties": {}, + "properties": {}, + "type": "object" + } + ] +}
- Changed
recast_motion1 field changed- changed
Input schema / properties / image / descriptionPrevious 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"
- Changed
remix_static1 field changed- changed
Input schema / properties / imageUrl / descriptionPrevious 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"
- Changed
render_ad6 fields changed- changed
Input schema / properties / creator / descriptionPrevious 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." - added
Input schema / properties / music / anyOfAdded value: +[ + { + "type": "boolean" + }, + { + "type": "string" + } +] - changed
Input schema / properties / music / descriptionPrevious 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')" - removed
Input schema / properties / music / typeRemoved value: -"boolean" - changed
Input schema / properties / textStyle / anyOfPrevious 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" + } +] - changed
Input schema / properties / textStyle / descriptionPrevious 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."
- Changed
save_creator1 field changed- removed
Input schema / properties / consentedRemoved value: -{ - "description": "REAL people only: the user has confirmed that person consented to their likeness being used in ads", - "type": "boolean" -}
18 tool updates
v0.1.281- Changed
duplicate_scheduled2 fields changed- added
Input schema / properties / ideaIdAdded value: +{ + "description": "short id of the content-plan idea this post came from", + "type": "string" +} - added
Input schema / properties / recipeAdded 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" +}
- Changed
generate_image1 field changed- changed
Input schema / properties / aspectRatio / descriptionPrevious 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"
- Changed
list_published_posts1 field changed- added
Input schema / properties / cursorAdded value: +{ + "description": "nextCursor from a previous reply: the next, older page", + "type": "string" +}
- Changed
post_edit11 fields changed- added
Input schema / properties / ops / items / properties / clipsAdded 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" +} - changed
Input schema / properties / ops / items / properties / end / descriptionPrevious value: -"trim/mute window end (s)"New value: +"trim/mute/text window end (s)" - changed
Input schema / properties / ops / items / properties / op / enumPrevious 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" +] - added
Input schema / properties / ops / items / properties / positionAdded value: +{ + "description": "text: where", + "enum": [ + "top", + "center", + "lower", + "bottom" + ], + "type": "string" +} - changed
Input schema / properties / ops / items / properties / seconds / descriptionPrevious 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" - changed
Input schema / properties / ops / items / properties / start / descriptionPrevious value: -"trim/mute window start (s)"New value: +"trim/mute/text window start (s)" - added
Input schema / properties / ops / items / properties / styleAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "additionalProperties": {}, + "properties": {}, + "type": "object" + } + ], + "description": "text: a look name or a textStyle" +} - changed
Input schema / properties / ops / items / properties / sub / descriptionPrevious 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" - added
Input schema / properties / ops / items / properties / textAdded value: +{ + "description": "text: the words, verbatim", + "type": "string" +} - added
Input schema / properties / ops / items / properties / transitionAdded value: +{ + "description": "join", + "enum": [ + "cut", + "crossfade" + ], + "type": "string" +} - changed
Input schema / properties / videoUrl / descriptionPrevious 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"
- Changed
post_performance1 field changed- added
Input schema / properties / daysAdded value: +{ + "description": "look back N days (1-730) over the whole history; omit for the recent posts only", + "type": "number" +}
- Changed
post_to_bluesky6 fields changed- added
Input schema / properties / allowDuplicateAdded value: +{ + "description": "post it even though an identical post was just made", + "type": "boolean" +} - added
Input schema / properties / hookAdded value: +{ + "description": "the post's angle — a list_hooks id or your own wording, reused exactly", + "type": "string" +} - added
Input schema / properties / ideaIdAdded value: +{ + "description": "short id of the content-plan idea this post came from", + "type": "string" +} - added
Input schema / properties / idempotencyKeyAdded value: +{ + "description": "any stable string: a repeat within 24h returns the original post instead of posting again", + "type": "string" +} - added
Input schema / properties / recipeAdded 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" +} - added
Input schema / properties / subjectAdded value: +{ + "description": "what the post is about", + "type": "string" +}
- Changed
post_to_google_business2 fields changed- added
Input schema / properties / ideaIdAdded value: +{ + "description": "short id of the content-plan idea this post came from", + "type": "string" +} - added
Input schema / properties / recipeAdded 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" +}
- Changed
post_to_linkedin2 fields changed- added
Input schema / properties / ideaIdAdded value: +{ + "description": "short id of the content-plan idea this post came from", + "type": "string" +} - added
Input schema / properties / recipeAdded 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" +}
- Changed
post_to_linkedin_page2 fields changed- added
Input schema / properties / ideaIdAdded value: +{ + "description": "short id of the content-plan idea this post came from", + "type": "string" +} - added
Input schema / properties / recipeAdded 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" +}
- Changed
post_to_meta2 fields changed- added
Input schema / properties / ideaIdAdded value: +{ + "description": "short id of the content-plan idea this post came from", + "type": "string" +} - added
Input schema / properties / recipeAdded 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" +}
- Changed
post_to_pinterest2 fields changed- added
Input schema / properties / ideaIdAdded value: +{ + "description": "short id of the content-plan idea this post came from", + "type": "string" +} - added
Input schema / properties / recipeAdded 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" +}
- Changed
post_to_telegram6 fields changed- added
Input schema / properties / allowDuplicateAdded value: +{ + "description": "post it even though an identical post was just made", + "type": "boolean" +} - added
Input schema / properties / hookAdded value: +{ + "description": "the post's angle — a list_hooks id or your own wording, reused exactly", + "type": "string" +} - added
Input schema / properties / ideaIdAdded value: +{ + "description": "short id of the content-plan idea this post came from", + "type": "string" +} - added
Input schema / properties / idempotencyKeyAdded value: +{ + "description": "any stable string: a repeat within 24h returns the original post instead of posting again", + "type": "string" +} - added
Input schema / properties / recipeAdded 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" +} - added
Input schema / properties / subjectAdded value: +{ + "description": "what the post is about", + "type": "string" +}
- Changed
post_to_tiktok2 fields changed- added
Input schema / properties / ideaIdAdded value: +{ + "description": "short id of the content-plan idea this post came from", + "type": "string" +} - added
Input schema / properties / recipeAdded 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" +}
- Changed
post_to_x2 fields changed- added
Input schema / properties / ideaIdAdded value: +{ + "description": "short id of the content-plan idea this post came from", + "type": "string" +} - added
Input schema / properties / recipeAdded 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" +}
- Changed
post_to_youtube2 fields changed- added
Input schema / properties / ideaIdAdded value: +{ + "description": "short id of the content-plan idea this post came from", + "type": "string" +} - added
Input schema / properties / recipeAdded 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" +}
- Changed
post_x_article2 fields changed- added
Input schema / properties / ideaIdAdded value: +{ + "description": "short id of the content-plan idea this post came from", + "type": "string" +} - added
Input schema / properties / recipeAdded 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" +}
- Changed
reschedule_post2 fields changed- added
Input schema / properties / ideaIdAdded value: +{ + "description": "short id of the content-plan idea this post came from", + "type": "string" +} - added
Input schema / properties / recipeAdded 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" +}
- Changed
schedule_post2 fields changed- added
Input schema / properties / ideaIdAdded value: +{ + "description": "short id of the content-plan idea this post came from", + "type": "string" +} - added
Input schema / properties / recipeAdded 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" +}
21 tool updates
v0.1.272- Changed
collect_post_metrics1 field changed- added
Input schema / properties / brandAdded 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" +}
- Added
edit_image - Changed
find_creators1 field changed- added
Input schema / properties / marketplaceAdded value: +{ + "description": "also search Instagram’s creator marketplace (Meta’s own creator directory, free) for the same niche; needs the Meta connector", + "type": "boolean" +}
- Added
headline_variants - Added
hook_variants - Changed
list_meta_posts1 field changed- added
Input schema / properties / brandAdded 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" +}
- Changed
list_published_posts1 field changed- added
Input schema / properties / brandAdded 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" +}
- Added
localize_ad - Changed
mine_angles3 fields changed- added
Input schema / properties / reviewsAdded 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)" +} - added
Input schema / properties / reviewsUrlAdded 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" +} - added
Input schema / properties / useOwnReviewsOnlyAdded value: +{ + "description": "true = mine only your reviews, no public search (no search credits). Default false = merge with public customer language.", + "type": "boolean" +}
- Changed
plan_ad2 fields changed- added
Input schema / properties / draftAdded 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" +} - changed
Input schema / properties / durationSeconds / descriptionPrevious 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."
- Changed
post_performance1 field changed- added
Input schema / properties / brandAdded 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" +}
- Changed
post_to_bluesky1 field changed- added
Input schema / properties / brandAdded 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" +}
- Changed
post_to_meta5 fields changed- changed
Input schema / properties / countryCodes / descriptionPrevious 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." - changed
Input schema / properties / linkDescription / descriptionPrevious 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." - changed
Input schema / properties / linkName / descriptionPrevious 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." - changed
Input schema / properties / linkPicture / descriptionPrevious 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." - changed
Input schema / properties / target / descriptionPrevious 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"
- Changed
post_to_pinterest1 field changed- changed
Input schema / properties / platformCover / descriptionPrevious 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."
- Changed
post_to_telegram1 field changed- added
Input schema / properties / brandAdded 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" +}
- Changed
render_ad1 field changed- changed
Input schema / properties / creator / descriptionPrevious 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."
- Changed
reschedule_post1 field changed- changed
Input schema / properties / privacyLevel / descriptionPrevious 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."
- Added
resize_ad - Changed
save_creator3 fields changed- changed
Input schema / properties / image / descriptionPrevious 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" - added
Input schema / properties / useAnywayAdded 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" +} - changed
Input schema / requiredPrevious value: -[ - "name", - "image" -]New value: +[ + "name" +]
- Changed
schedule_post4 fields changed- changed
Input schema / properties / linkDescription / descriptionPrevious 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." - changed
Input schema / properties / linkName / descriptionPrevious 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." - changed
Input schema / properties / linkPicture / descriptionPrevious 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." - changed
Input schema / properties / privacyLevel / descriptionPrevious 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."
- Changed
update_drive_file1 field changed- changed
Input schema / properties / trash / descriptionPrevious value: -"true → move to Trash; false → restore from Trash"New value: +"true -> move to Trash; false -> restore from Trash"
TDQS
Scored across 204 tools
Most tools have genuinely distinct domains and the descriptions work hard to route callers (e.g. plan_ad vs render_ad vs generate_video, post_edit vs edit_timeline vs fix_beat). But at 204 tools there are real near-neighbours an agent must arbitrate: clone_static/remix_static (self-declared alias), reframe_video/repurpose_video/post_edit pad, edit_video/restyle_video/insert_scene/fix_beat, hook_variants/multiply_ad, generate_video/render_ad/stitch_video/make_motion_film/make_series/product_sizzle/make_template_ad. The overlap is documented rather than eliminated, so selection still relies on reading long prose.
Strong verb_noun snake_case throughout (create_/list_/get_/update_/delete_/post_to_/save_to_/search_/make_). Minor deviations exist: product-named tools like hermoso_credits and hermoso_capabilities, store_get, the legacy remix_static kept as an alias, and mixed families (make_* vs render_* vs generate_* for creation), but nothing that reads as chaos.
204 tools is far past any reasonable single roster, spanning ads, organic publishing, editors, research, storage, billing, team and connectors. The server mitigates this deliberately (core-first roster, enable_tools groups, find_tools/call_tool), which is the right architecture, but the tool set being evaluated is still an extreme over-scope for one surface.
Coverage is essentially exhaustive for a marketing production platform: plan, render, edit, restyle, repurpose, voice/music, publish to ~15 channels, schedule/reschedule/retry, autopilot, analytics and diagnosis, competitor research, Drive/Sheets/Docs/Notion/OneDrive, billing, team and error introspection. Lifecycle gaps are minor and often explicitly acknowledged with a named alternative tool.
Maintenance
Related MCP Connectors
Real-time Meta, Google, LinkedIn and TikTok ad library access for AI assistants. No API keys.
AI video studio for agents — script to MP4, b-roll sourcing, Gemini QA. 43 tools.
Make social content from your agent: hooks, AI UGC videos, competitor ads, posts to 30+ networks.
Competitive ad intelligence for AI agents: portfolios, ad/content search, analytics, discovery.
Related MCP Servers
- AlicenseAqualityDmaintenanceProvides 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.17MIT
- AlicenseNot gradedqualityDmaintenanceSearches and analyzes competitor ads and content across Meta, Google, Instagram, TikTok, and YouTube with AI-powered creative analysis and cross-platform brand discovery.1MIT
- AlicenseNot gradedqualityCmaintenanceManage ad campaigns across Meta, Google, and TikTok, create campaigns, analyze performance, spy on competitors, and generate AI creatives.19MIT
- AlicenseAqualityBmaintenance50 tools for Meta Ads campaign management, creative analysis, audience building, and conversion tracking, accessible to any MCP-compatible AI agent.6996 npm13MIT