hermoso 0.1.221 → 0.1.223

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.
Files changed (2) hide show
  1. package/mcp/tools.mjs +3 -3
  2. package/package.json +1 -1
package/mcp/tools.mjs CHANGED
@@ -3285,7 +3285,7 @@ function buildTools(rawServer, opts = {}, sink = null) {
3285
3285
  // will; if it does not name the brand we just pinned, the switch did not take and saying otherwise is the bug.
3286
3286
  const w = await apiGet('/api/workspace').catch(() => null);
3287
3287
  const took = w && (hit.id === 'default' ? (!w.profileId || w.profileId === 'default' || w.storeSuffix === '') : String(w.profileId) === String(hit.id)) && !w.shared;
3288
- if (!took) return { content: [{ type: 'text', text: `The switch to ${hit.name} (${hit.id}) did NOT take: after pinning, the server still resolves this connection to ${w ? `profile ${w.profileId || 'default'}${w.shared ? ' (a shared workspace)' : ''}` : 'an unreadable workspace'}. Nothing was changed on that brand. If this is a CLI, an exported HERMOSO_PROFILE / HERMOSO_OWNER in your shell is overriding the key's pin — unset it and try again.` }], isError: true };
3288
+ if (!took) return { content: [{ type: 'text', text: `The switch to ${hit.name} (${hit.id}) did NOT take: after pinning, the server still resolves this connection to ${w ? `profile ${w.profileId || 'default'}${w.shared ? ' (a shared workspace)' : ''}` : 'an unreadable workspace'}. Nothing was changed on that brand. If this is a CLI, an exported ${ENV_PREFIX}_PROFILE / ${ENV_PREFIX}_OWNER in your shell is overriding the key's pin — unset it and try again.` }], isError: true };
3289
3289
  // The roster was gated to the workspace this SESSION started in; the workspace just moved, so re-gate it.
3290
3290
  const _rg = await regateForWorkspace(ctx);
3291
3291
  const _rgNote = _rg && _rg.providers
@@ -4252,7 +4252,7 @@ function buildTools(rawServer, opts = {}, sink = null) {
4252
4252
  locationId: z.string().optional().describe("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."),
4253
4253
  visibility: z.enum(['public', 'unlisted', 'private', 'draft']).optional().describe("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."),
4254
4254
  visibilityByChannel: z.record(z.string()).optional().describe('override visibility for one channel, e.g. { "tiktok": "draft" } to go live everywhere but stage TikTok for review'),
4255
- optimizeCopy: z.boolean().optional().describe('RECOMMENDED when one caption goes to several channels: fit the shared caption to each channel’s own rules at publish time wherever no per-channel caption was written — YouTube gets a keyword title, a structured multi-paragraph description and search tags; Instagram/TikTok hashtags; LinkedIn longer; X/Bluesky short; Pinterest keyword-rich. The angle and every claim stay the author’s; a channel with its own caption is left exactly as written. Off by default so nobody’s words are rewritten unasked.'),
4255
+ optimizeCopy: z.boolean().optional().describe('RECOMMENDED when one caption goes to several channels: fit the shared caption to each channel’s own rules when you schedule it (the fitted caption is stored on the scheduled post, so what you scheduled is what publishes) wherever no per-channel caption was written — YouTube gets a keyword title, a structured multi-paragraph description and search tags; Instagram/TikTok hashtags; LinkedIn longer; X/Bluesky short; Pinterest keyword-rich. The angle and every claim stay the author’s; a channel with its own caption is left exactly as written. Off by default so nobody’s words are rewritten unasked.'),
4256
4256
  },
4257
4257
  outputSchema: { id: z.string().optional(), at: z.string().optional(), channels: z.array(z.string()).optional(), label: z.string().optional() },
4258
4258
  annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: true },
@@ -4292,7 +4292,7 @@ function buildTools(rawServer, opts = {}, sink = null) {
4292
4292
  at: z.string().optional().describe('the new time — ISO timestamp (2026-08-05T09:00:00Z) or epoch milliseconds. Must be in the future, at most 365 days out.'),
4293
4293
  message: z.string().optional().describe('replace the caption used for every channel that has no override'),
4294
4294
  captions: z.record(z.string()).optional().describe('replaces the WHOLE per-channel caption map — send every override you want to keep, not just the new one'),
4295
- optimizeCopy: z.boolean().optional().describe('fit the shared caption to each channel’s own rules at publish time wherever no per-channel caption was written (YouTube keyword title + structured description + tags, Instagram/TikTok hashtags, LinkedIn longer, X/Bluesky short, Pinterest keyword-rich); a channel with its own caption is left exactly as written. Send false to switch it off on this item.'),
4295
+ optimizeCopy: z.boolean().optional().describe('fit the shared caption to each channel’s own rules when you schedule it (the fitted caption is stored on the scheduled post, so what you scheduled is what publishes) wherever no per-channel caption was written (YouTube keyword title + structured description + tags, Instagram/TikTok hashtags, LinkedIn longer, X/Bluesky short, Pinterest keyword-rich); a channel with its own caption is left exactly as written. Send false to switch it off on this item.'),
4296
4296
  channels: z.array(z.enum(['facebook', 'instagram', 'threads', 'tiktok', 'youtube', 'linkedin', 'x', 'pinterest', 'google_business', 'bluesky', 'telegram'])).optional().describe('replaces the channel list'),
4297
4297
  imageUrl: z.string().optional().describe('swap the image; "" removes it'),
4298
4298
  videoUrl: z.string().optional().describe('swap the video; "" removes it'),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hermoso",
3
- "version": "0.1.221",
3
+ "version": "0.1.223",
4
4
  "mcpName": "io.github.hermoso-ai/hermoso",
5
5
  "description": "AI ad studio and marketing MCP server with 822 tools. Research the ads already running in any market, generate finished image, video and UGC avatar ads, publish and schedule them to your own channels, build and manage the ad campaigns behind them, and read what they achieved. 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",