hermoso 0.1.269 → 0.1.270

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -5,7 +5,7 @@ scripts. Research the ads already winning in a market, generate finished image &
5
5
  composited in, copy + CTA included), publish them to your own social channels, and build & manage the ad
6
6
  campaigns behind them, all over [MCP](https://modelcontextprotocol.io) tools, a CLI, or installable Claude skills.
7
7
 
8
- **855 tools.** `tools/list` is always the authoritative set; `hermoso_capabilities` (free) returns the live model
8
+ **856 tools.** `tools/list` is always the authoritative set; `hermoso_capabilities` (free) returns the live model
9
9
  catalog with exact per-render credit costs plus the full capability map.
10
10
 
11
11
  **Most of it costs nothing.** Publishing and scheduling posts, building and managing paid campaigns, analytics
@@ -216,7 +216,7 @@ block entirely if you signed in above; it is there for CI, where the process can
216
216
 
217
217
  Then ask your agent: *“Generate an image ad with Hermoso.”*
218
218
 
219
- ### What the 855 tools cover
219
+ ### What the 856 tools cover
220
220
 
221
221
  **Ad spy / research** — `find_competitors`, `competitor_teardown`, `pull_competitor_ads`, `research_ads`; the
222
222
  Meta / Google / LinkedIn ad libraries (`search_meta_ads`, `search_google_ads`, `search_linkedin_ads`); organic
package/mcp/tools.mjs CHANGED
@@ -7073,7 +7073,7 @@ function buildTools(rawServer, opts = {}, sink = null) {
7073
7073
  title: 'Subscribe a Facebook Page to real-time events',
7074
7074
  description: 'Have Meta PUSH events to Hermoso instead of Hermoso polling for them. Subscribe a Page to `feed` (comments, posts, likes and shares — this is what makes a new comment ARRIVE rather than be polled for), `mention` (someone mentions the Page), `leadgen` (a lead-ad form submission delivered the moment it happens, which turns "read the leads when asked" into "the leads arrive"), and `messages` (an inbound Messenger or Instagram DM — THE ONLY WAY to be told about one, and Meta’s 24-hour reply window starts the moment it arrives, so being told rather than polling is time on the clock). Read what has arrived with list_meta_webhook_events. THE ANSWER IS READ BACK FROM META, never the 200: Meta can accept a subscription and record FEWER fields than you asked for, and a caller told "subscribed to leadgen" whose leads never arrive has no way to discover why — so any field Meta did not record is named. Fields Hermoso does not consume (message_reactions, messaging_postbacks, message_echoes) are refused by name with the reason, so an agent gets a fact rather than "unsupported". 0 credits.',
7075
7075
  inputSchema: {
7076
- fields: z.array(z.enum(['feed', 'mention', 'leadgen', 'messages'])).describe('what to be told about. At least one — Meta marks it required, and defaulting it would decide on the brand’s behalf what they hear about.'),
7076
+ fields: z.array(z.enum(['feed', 'mention', 'leadgen', 'messages', 'marketing_messages', 'marketing_message_echoes', 'marketing_message_deliveries', 'marketing_message_reads', 'marketing_message_clicks', 'marketing_message_delivery_failed'])).describe('what to be told about. `marketing_messages` is shorthand for the five marketing_message_* outcome fields (sent / delivered / read / clicked / failed of a paid Messenger marketing message, read with get_messenger_marketing_message_status). Pass every field the Page should keep: Meta does not document whether a subscribe merges or replaces the list, and the read-back shows what it recorded. At least one — Meta marks it required, and defaulting it would decide on the brand’s behalf what they hear about.'),
7077
7077
  pageId: z.string().optional().describe('Facebook Page id — omit when only one Page is connected'),
7078
7078
  },
7079
7079
  outputSchema: { pageId: z.string().optional(), page: z.string().optional(), asked: z.array(z.string()).optional(), subscribed: z.boolean().nullable().optional(), fields: z.array(z.string()).optional(), summary: z.string().optional(), retry: z.string().optional(), callback: z.string().optional() },
@@ -7651,8 +7651,8 @@ function buildTools(rawServer, opts = {}, sink = null) {
7651
7651
 
7652
7652
  // ── MESSENGER MARKETING MESSAGES (2026-09-05). Meta's paid re-engagement channel: messages OUTSIDE the 24-hour
7653
7653
  // window to people who opted in, billed by Meta to the brand's ad account per delivered message. Flow 2 of Meta's
7654
- // onboarding (user token + marketing_messages_messenger) rides the existing Meta connection. App-Review gated:
7655
- // until Meta grants the scope it works for app-role holders only, and list_messenger_subscribers says which.
7654
+ // onboarding (user token + marketing_messages_messenger) rides the existing Meta connection. The scope was
7655
+ // APPROVED in App Review round 5 (2026-09-22); list_messenger_subscribers reports whether THIS connection carries it.
7656
7656
  // Every endpoint, limit and error code: lib/messenger-marketing.mjs (read live 2026-09-05).
7657
7657
  // MESSENGER MARKETING MESSAGES ARE A CHANNEL CAPABILITY, NOT PAID ADS (2026-09-07). This block was registered inside
7658
7658
  // the ads section, so it rode the heaviest opt-in group while dispatching to no paid-advertising route — unreachable
@@ -7697,6 +7697,25 @@ function buildTools(rawServer, opts = {}, sink = null) {
7697
7697
  const d = await apiGet('/api/meta/marketing-messages/campaigns', a);
7698
7698
  return ok(`${d.note}${(d.campaigns || []).length ? `\n${d.campaigns.map(c => `• ${c.name || '(unnamed)'} — ${c.campaignId}${c.dailyBudgetUsd != null ? `, $${c.dailyBudgetUsd}/day` : ''}${c.lifetimeBudgetUsd != null ? `, $${c.lifetimeBudgetUsd} lifetime` : ''}`).join('\n')}` : ''}`, d);
7699
7699
  }));
7700
+ // DID IT LAND? (2026-09-22). The send returns a tracking id meaning ACCEPTED. The outcome arrives only on the
7701
+ // Page webhook; every payload shape and what each one lacks is in lib/messenger-marketing.mjs.
7702
+ server.registerTool('get_messenger_marketing_message_status', {
7703
+ title: 'Did a Messenger marketing message land?',
7704
+ description: 'Whether a paid Messenger marketing message was delivered. send_messenger_marketing_message returns a tracking id, which only means Meta ACCEPTED it; this reads what Meta pushed back on the Page webhook for each tracking id: sent, delivered (the event Meta bills on), read, clicked (with count), or failed (with Meta’s reason). "unknown" means NO event has arrived, never that it failed: events arrive only when the Page is subscribed to the marketing_message_* fields (the reply says whether it is, read back from Meta; fix with subscribe_meta_webhooks fields ["marketing_messages"]) and only for messages sent after Hermoso began recording them on 2026-09-22. A failure carries no tracking id, so it is matched through Hermoso’s own send log. Read-only, 0 credits.',
7705
+ inputSchema: {
7706
+ trackingId: z.string().optional().describe('the marketing_message_tracking_id send_messenger_marketing_message returned'),
7707
+ trackingIds: z.array(z.string()).optional().describe('several at once, ≤200'),
7708
+ campaignId: z.string().optional().describe('instead of ids: every send Hermoso logged from this campaign'),
7709
+ pageId: z.string().optional().describe('the sending Page, for the subscription check; defaults to the brand’s one shared Page'),
7710
+ },
7711
+ outputSchema: { count: z.number().optional(), counts: z.record(z.number()).optional(), results: z.array(z.any()).optional(), pageId: z.string().nullable().optional(), subscribed: z.boolean().nullable().optional(), missingFields: z.array(z.string()).optional(), note: z.string().optional() },
7712
+ annotations: { readOnlyHint: true, destructiveHint: false, openWorldHint: true },
7713
+ }, wrap(async (a) => {
7714
+ const q = { ...a, ...(Array.isArray(a.trackingIds) ? { trackingIds: a.trackingIds.join(',') } : {}) };
7715
+ const d = await apiGet('/api/meta/marketing-messages/status', q);
7716
+ const lines = (d.results || []).slice(0, 50).map(r => `• ${r.trackingId}: ${r.state} — ${r.note}`);
7717
+ return ok(`${d.note}${lines.length ? `\n${lines.join('\n')}` : ''}`, d);
7718
+ }));
7700
7719
  server.registerTool('estimate_messenger_marketing_delivery', {
7701
7720
  title: 'Estimate Messenger marketing-message delivery and cost',
7702
7721
  description: 'Meta’s own estimate of how many marketing messages a budget would deliver from a Page and what it would cost, before anything is created. Free. Meta marks the metric “in development”, so treat it as a range.',
@@ -7705,7 +7724,7 @@ function buildTools(rawServer, opts = {}, sink = null) {
7705
7724
  }, wrap(async (a) => { const d = await apiGet('/api/meta/marketing-messages/estimate', a); return ok(d.note, d); }));
7706
7725
  server.registerTool('send_messenger_marketing_message', {
7707
7726
  title: 'Send a paid Messenger marketing message',
7708
- description: 'Send a PAID marketing message on Messenger to opted-in subscribers (Meta act_<AD>/messages). A campaign created in the last ~hour answers 2300012 then 2300041 (Meta still preparing it) with the seconds left; prefer an existing campaign from list_messenger_marketing_campaigns. One message per subscriber per 12 hours — Meta’s rule, enforced before dispatch and by Meta. message.type: text | button (text + up to 3 web_url buttons) | generic (a card: title, subtitle, image, tap-through url, up to 3 buttons) | media (imageUrl or videoId + buttons). Give subscriptionTokens (≤200 per call, from list_messenger_subscribers) OR customAudienceId of a MESSENGER_SUBSCRIBER_LIST audience of 100+ people for a bulk send. Meta bills the ad account per delivered message; delivery, read and click events arrive on the Page webhook. dryRun:true previews the wire body and sends nothing. Meta’s frequency caps are silent: a refusal saying the person is capped is Meta protecting them, not a broken send.',
7727
+ description: 'Send a PAID marketing message on Messenger to opted-in subscribers (Meta act_<AD>/messages). A campaign created in the last ~hour answers 2300012 then 2300041 (Meta still preparing it) with the seconds left; prefer an existing campaign from list_messenger_marketing_campaigns. One message per subscriber per 12 hours — Meta’s rule, enforced before dispatch and by Meta. message.type: text | button (text + up to 3 web_url buttons) | generic (a card: title, subtitle, image, tap-through url, up to 3 buttons) | media (imageUrl or videoId + buttons). Give subscriptionTokens (≤200 per call, from list_messenger_subscribers) OR customAudienceId of a MESSENGER_SUBSCRIBER_LIST audience of 100+ people for a bulk send. Meta bills the ad account per delivered message. A successful send means ACCEPTED, not delivered: read sent / delivered / read / clicked / failed per tracking id with get_messenger_marketing_message_status (Meta pushes them on the Page webhook; the Page must be subscribed with subscribe_meta_webhooks fields ["marketing_messages"]). dryRun:true previews the wire body and sends nothing. Meta’s frequency caps are silent: a refusal saying the person is capped is Meta protecting them, not a broken send.',
7709
7728
  inputSchema: {
7710
7729
  adAccountId: z.string(),
7711
7730
  campaignId: z.string().describe('the message campaign id (from create_messenger_marketing_campaign or list_messenger_marketing_campaigns) — OR the campaign NAME as the user said it: a non-numeric value is resolved against the account\'s own campaigns, so you never need to ask for an id'),
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "hermoso",
3
- "version": "0.1.269",
3
+ "version": "0.1.270",
4
4
  "mcpName": "io.github.hermoso-ai/hermoso",
5
- "description": "Marketing on autopilot, run from your own AI agent. 855 tools. Publishing, scheduling, ad campaign management, comments, DMs and analytics cost no credits on every plan; credits are only for generating creative and for Ad Spy research. AD PLATFORMS: Meta, Google Ads, TikTok Ads, LinkedIn Ads, Reddit Ads, X Ads, Pinterest Ads, Snapchat Ads, Microsoft Advertising, Apple Search Ads and ChatGPT Ads, plus product feeds in Google Merchant Center. PUBLISHING AND SCHEDULING: Facebook, Instagram, Threads, TikTok, YouTube, X, LinkedIn, Pinterest, Bluesky and Telegram. AD RESEARCH: the Meta, Google and LinkedIn ad libraries plus organic TikTok, Instagram, YouTube, Threads and Reddit. ANALYTICS: Google Analytics 4, Google Search Console and every connected platform's own post and campaign insights. Also brand onboarding, 50+ image and video generation models, ad scoring, competitor teardowns, Google Drive and OneDrive, a CLI and installable Claude skills.",
5
+ "description": "Marketing on autopilot, run from your own AI agent. 856 tools. Publishing, scheduling, ad campaign management, comments, DMs and analytics cost no credits on every plan; credits are only for generating creative and for Ad Spy research. AD PLATFORMS: Meta, Google Ads, TikTok Ads, LinkedIn Ads, Reddit Ads, X Ads, Pinterest Ads, Snapchat Ads, Microsoft Advertising, Apple Search Ads and ChatGPT Ads, plus product feeds in Google Merchant Center. PUBLISHING AND SCHEDULING: Facebook, Instagram, Threads, TikTok, YouTube, X, LinkedIn, Pinterest, Bluesky and Telegram. AD RESEARCH: the Meta, Google and LinkedIn ad libraries plus organic TikTok, Instagram, YouTube, Threads and Reddit. ANALYTICS: Google Analytics 4, Google Search Console and every connected platform's own post and campaign insights. Also brand onboarding, 50+ image and video generation models, ad scoring, competitor teardowns, Google Drive and OneDrive, a CLI and installable Claude skills.",
6
6
  "type": "module",
7
7
  "bin": {
8
8
  "hermoso": "bin/hermoso.mjs"