hermoso 0.1.430 → 0.1.431

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.
@@ -1810,7 +1810,7 @@ export function libraryToolDefs() {
1810
1810
  campaignId: z.string().max(80).describe('the campaign id'),
1811
1811
  status: z.enum(['active', 'paused']).describe("'paused' stops it; 'active' turns it on and starts spending"),
1812
1812
  },
1813
- annotations: { title: 'Pause or turn on a campaign', readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: true },
1813
+ annotations: { title: 'Pause or turn on a campaign', readOnlyHint: false, destructiveHint: true, idempotentHint: true, openWorldHint: true },
1814
1814
  _meta: { ...APP_ONLY },
1815
1815
  },
1816
1816
  };
package/mcp/tools.mjs CHANGED
@@ -5191,7 +5191,7 @@ function buildTools(rawServer, opts = {}, sink = null) {
5191
5191
  hide: z.boolean().optional().describe('false to UNHIDE (default true)'),
5192
5192
  },
5193
5193
  outputSchema: { ok: z.boolean().optional(), hidden: z.boolean().optional() },
5194
- annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: true },
5194
+ annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: true, openWorldHint: true },
5195
5195
  }, wrap(async (a) => {
5196
5196
  const d = await apiPost('/api/threads/reply/hide', { replyId: a.replyId, hide: a.hide });
5197
5197
  return ok(`Reply ${a.replyId} ${d.hidden ? 'hidden' : 'unhidden'}.`, d);
@@ -9236,7 +9236,7 @@ function buildTools(rawServer, opts = {}, sink = null) {
9236
9236
  instoreProductLocation: z.string().nullable().optional().describe('local only, max 20 bytes'),
9237
9237
  },
9238
9238
  outputSchema: { merchantCenterId: z.string().optional(), kind: z.string().optional(), key: z.string().optional(), productKey: z.string().optional(), replaced: z.boolean().optional(), preserved: z.array(z.string()).optional(), cleared: z.array(z.string()).optional(), attributes: z.any().optional(), warning: z.string().optional(), note: z.string().optional() },
9239
- annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: true },
9239
+ annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: true, openWorldHint: true },
9240
9240
  }, wrap(async (a) => {
9241
9241
  const d = await apiPost('/api/merchant/inventory', a);
9242
9242
  return ok(d.note, d);
@@ -9723,7 +9723,7 @@ function buildTools(rawServer, opts = {}, sink = null) {
9723
9723
  loginCustomerId: z.string().optional().describe('manager id if operating through an MCC'),
9724
9724
  },
9725
9725
  outputSchema: { ok: z.boolean().optional(), campaignId: z.string().optional(), changed: z.boolean().optional(), networks: z.object({}).passthrough().optional(), note: z.string().optional() },
9726
- annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: true },
9726
+ annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: true, openWorldHint: true },
9727
9727
  }, wrap(async (a) => {
9728
9728
  const d = await apiPost('/api/google-ads/networks', a);
9729
9729
  return ok(d.note || `campaign ${a.campaignId} networks updated.`, d);
@@ -11498,7 +11498,7 @@ function buildTools(rawServer, opts = {}, sink = null) {
11498
11498
  description: "Subscribe a Stripe customer (cus_…) to a price (price_…), optional quantity and trialDays. THIS CHARGES THE CUSTOMER'S SAVED PAYMENT METHOD when there is no trial, so it ALWAYS needs confirm:true (test keys included) after you show the user exactly what will be created. Read back; an INCOMPLETE status means no chargeable card is on file. Needs Stripe connected (Settings > Connectors > Stripe, or connect_connector).",
11499
11499
  inputSchema: { customerId: z.string(), priceId: z.string(), quantity: z.number().optional(), trialDays: z.number().optional(), metadata: z.record(z.any()).optional(), confirm: z.boolean().optional() },
11500
11500
  outputSchema: { ok: z.boolean().optional(), note: z.string().optional(), count: z.number().optional(), hasMore: z.boolean().optional() },
11501
- annotations: { readOnlyHint: false, destructiveHint: false, openWorldHint: true },
11501
+ annotations: { readOnlyHint: false, destructiveHint: true, openWorldHint: true },
11502
11502
  }, wrap(async (a) => { const d = await apiPost('/api/stripe/subscription', a); return ok(d.note, d); }));
11503
11503
  server.registerTool('cancel_stripe_subscription', {
11504
11504
  title: 'cancel stripe subscription',
@@ -11512,7 +11512,7 @@ function buildTools(rawServer, opts = {}, sink = null) {
11512
11512
  description: "Refund a Stripe charge (ch_… or a pi_… payment intent): the full refundable amount, or a partial `amount` in major units; optional reason duplicate / fraudulent / requested_by_customer. ALWAYS needs confirm:true, test keys included. Read back. Needs Stripe connected (Settings > Connectors > Stripe, or connect_connector).",
11513
11513
  inputSchema: { chargeId: z.string(), amount: z.number().optional(), reason: z.string().optional(), confirm: z.boolean().optional() },
11514
11514
  outputSchema: { ok: z.boolean().optional(), note: z.string().optional(), count: z.number().optional(), hasMore: z.boolean().optional() },
11515
- annotations: { readOnlyHint: false, destructiveHint: false, openWorldHint: true },
11515
+ annotations: { readOnlyHint: false, destructiveHint: true, openWorldHint: true },
11516
11516
  }, wrap(async (a) => { const d = await apiPost('/api/stripe/refund', a); return ok(d.note, d); }));
11517
11517
  // ---------- HubSpot (2026-09-09): the brand's OWN CRM — contacts, an upsert by email, lead forms → contacts, deals ----------
11518
11518
  server.registerTool('list_hubspot_contacts', {
@@ -11923,7 +11923,7 @@ function buildTools(rawServer, opts = {}, sink = null) {
11923
11923
  update: z.array(z.object({ criterionId: z.string(), bidAdjustment: z.number().describe('whole percent, -90 to 900') })).optional(),
11924
11924
  confirm: z.boolean().optional().describe('REQUIRED true when the campaign is ACTIVE'),
11925
11925
  },
11926
- annotations: { readOnlyHint: false, destructiveHint: false, openWorldHint: true },
11926
+ annotations: { readOnlyHint: false, destructiveHint: true, openWorldHint: true },
11927
11927
  }, wrap(async (a) => { const d = await apiPost('/api/microsoft-ads/profile-targeting', a); return ok(d.note, d); }));
11928
11928
  server.registerTool('set_microsoft_ads_budget', {
11929
11929
  title: 'Set a Microsoft Advertising daily budget',
@@ -12196,7 +12196,7 @@ function buildTools(rawServer, opts = {}, sink = null) {
12196
12196
  clickUrl: z.string().optional(), impressionUrl: z.string().optional(), confirm: z.boolean().optional().describe('required for a spend change on a LIVE campaign'),
12197
12197
  },
12198
12198
  outputSchema: { ...alOut, changed: z.array(z.string()).optional(), campaign: z.any().optional() },
12199
- annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: true },
12199
+ annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: true, openWorldHint: true },
12200
12200
  }, wrap(async (a) => { const d = await apiPost('/api/applovin-ads/campaign/update', a); return ok(d.note, d); }));
12201
12201
  server.registerTool('set_applovin_ads_status', {
12202
12202
  title: 'Turn an AppLovin campaign or creative set live or paused',
@@ -12220,7 +12220,7 @@ function buildTools(rawServer, opts = {}, sink = null) {
12220
12220
  description: 'Create an AppLovin creative set from uploaded asset ids, born PAUSED, optionally inside a campaign. It must hold EITHER an HTML playable OR a portrait image (IMG_INTER_P) plus a video; Hermoso checks that against AppLovin’s own asset types before creating it.' + AL_CM_NOTE,
12221
12221
  inputSchema: { name: z.string(), assetIds: z.array(z.string()).describe('AppLovin asset ids'), campaignId: z.string().optional(), countries: alIds, languages: alIds.describe('e.g. ENGLISH'), url: z.string().optional().describe('landing page for this set only (WEB)') },
12222
12222
  outputSchema: { ...alOut, creativeSet: z.any().optional(), pausedAfterCreate: z.boolean().optional() },
12223
- annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: true },
12223
+ annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: true },
12224
12224
  }, wrap(async (a) => { const d = await apiPost('/api/applovin-ads/creative-set', a); return ok(d.note, d); }));
12225
12225
  server.registerTool('remove_applovin_ads_creative_set', {
12226
12226
  title: 'Remove an AppLovin creative set from its campaigns',
@@ -12234,14 +12234,14 @@ function buildTools(rawServer, opts = {}, sink = null) {
12234
12234
  description: 'Edit an AppLovin creative set, read back: rename it, change its countries or languages (an empty list means all), its landing page (url; "" falls back to the campaign’s), replace its assets (assetIds, checked against AppLovin’s own asset types first), or add it to more campaigns (addToCampaignIds, up to 20). Adding a LIVE set to a LIVE campaign needs confirm:true. Status is set_applovin_ads_status.' + AL_CM_NOTE,
12235
12235
  inputSchema: { creativeSetId: z.string(), name: z.string().optional(), countries: alIds.describe('ISO codes; [] = all countries'), languages: alIds.describe('e.g. ENGLISH; [] = all'), url: z.string().optional().describe('landing page for this set; "" = the campaign’s'), assetIds: alIds.describe('REPLACES the set’s assets'), addToCampaignIds: alIds.describe('also run it in these campaigns (up to 20)'), confirm: z.boolean().optional().describe('required to add a LIVE set to a LIVE campaign') },
12236
12236
  outputSchema: { ...alOut, changed: z.array(z.string()).optional(), creativeSet: z.any().optional() },
12237
- annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: true },
12237
+ annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: true, openWorldHint: true },
12238
12238
  }, wrap(async (a) => { const d = await apiPost('/api/applovin-ads/creative-set/update', a); return ok(d.note, d); }));
12239
12239
  server.registerTool('clone_applovin_ads_creative_set', {
12240
12240
  title: 'Copy an AppLovin creative set into another campaign (paused)',
12241
12241
  description: 'Copy an AppLovin creative set into another campaign, born PAUSED and read back. AppLovin only clones a set that already belongs to a campaign.' + AL_CM_NOTE,
12242
12242
  inputSchema: { creativeSetId: z.string(), campaignId: z.string().describe('the campaign to copy it into') },
12243
12243
  outputSchema: { ...alOut, sourceId: z.string().optional(), campaignId: z.string().optional(), version: z.string().optional(), creativeSet: z.any().optional(), pausedAfterCreate: z.boolean().optional() },
12244
- annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: true },
12244
+ annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: true },
12245
12245
  }, wrap(async (a) => { const d = await apiPost('/api/applovin-ads/creative-set/clone', a); return ok(d.note, d); }));
12246
12246
  server.registerTool('list_applovin_ads_catalogs', {
12247
12247
  title: 'List AppLovin catalogs',
@@ -12770,7 +12770,7 @@ function buildTools(rawServer, opts = {}, sink = null) {
12770
12770
  budgetOrderIds: z.array(z.string()).optional().describe('budget order id(s) from list_apple_ads_budget_orders to assign this campaign to — a budget order is a spend CEILING SHARED across every campaign assigned to it, and it works ALONGSIDE dailyBudget rather than instead of it. Without this a budget order can never be drawn from.'),
12771
12771
  },
12772
12772
  outputSchema: { ok: z.boolean().optional(), level: z.string().optional(), id: z.string().optional(), verified: z.boolean().optional(), read: z.any().optional(), summary: z.string().optional(), note: z.string().optional() },
12773
- annotations: { readOnlyHint: false, destructiveHint: false, openWorldHint: true },
12773
+ annotations: { readOnlyHint: false, destructiveHint: true, openWorldHint: true },
12774
12774
  }, wrap(async (a) => { const d = await apiPost('/api/apple-ads/update', a); return ok(`Apple Ads ${d.level} ${d.id} updated (verified: ${d.verified}):\n${d.summary}\n${d.note || ''}`, d); }));
12775
12775
 
12776
12776
  server.registerTool('set_apple_ads_status', {
@@ -13060,7 +13060,7 @@ function buildTools(rawServer, opts = {}, sink = null) {
13060
13060
  confirm: z.boolean().optional().describe('REQUIRED true. Without it nothing changes and the refusal states the campaign and the exact budget or CPA change.'),
13061
13061
  },
13062
13062
  outputSchema: { ok: z.boolean().optional(), type: z.string().optional(), id: z.string().optional(), state: z.string().optional(), campaignId: z.string().nullable().optional(), armsSpend: z.boolean().optional(), verified: z.boolean().optional(), applied: z.any().optional(), read: z.any().optional(), summary: z.string().optional(), note: z.string().optional() },
13063
- annotations: { readOnlyHint: false, destructiveHint: false, openWorldHint: true },
13063
+ annotations: { readOnlyHint: false, destructiveHint: true, openWorldHint: true },
13064
13064
  }, wrap(async (a) => { const d = await apiPost('/api/apple-ads/recommendation/apply', a); return ok(`Apple Ads ${d.type} recommendation ${d.id} — ${d.verified ? 'APPLIED' : 'NOT CONFIRMED'}:\n${d.summary}\n${d.note || ''}`, d); }));
13065
13065
 
13066
13066
  server.registerTool('dismiss_apple_ads_recommendation', {
@@ -13147,7 +13147,7 @@ function buildTools(rawServer, opts = {}, sink = null) {
13147
13147
  invoiceName: z.string().optional(), orderNumber: z.string().optional(), clientName: z.string().optional(), primaryBuyerName: z.string().optional(),
13148
13148
  },
13149
13149
  outputSchema: { ok: z.boolean().optional(), level: z.string().optional(), id: z.string().optional(), verified: z.boolean().optional(), read: z.any().optional(), summary: z.string().optional(), note: z.string().optional() },
13150
- annotations: { readOnlyHint: false, destructiveHint: false, openWorldHint: true },
13150
+ annotations: { readOnlyHint: false, destructiveHint: true, openWorldHint: true },
13151
13151
  }, wrap(async (a) => { const d = await apiPost('/api/apple-ads/budget-order', a); return ok(`Apple Ads budget order created:\n${d.summary}\n${d.note || ''}`, d); }));
13152
13152
 
13153
13153
  server.registerTool('update_apple_ads_budget_order', {
@@ -13166,7 +13166,7 @@ function buildTools(rawServer, opts = {}, sink = null) {
13166
13166
  primaryBuyerName: z.string().optional(), primaryBuyerEmail: z.string().optional(), billingEmail: z.string().optional(),
13167
13167
  },
13168
13168
  outputSchema: { ok: z.boolean().optional(), level: z.string().optional(), id: z.string().optional(), verified: z.boolean().optional(), requested: z.any().optional(), read: z.any().optional(), summary: z.string().optional(), note: z.string().optional() },
13169
- annotations: { readOnlyHint: false, destructiveHint: false, openWorldHint: true },
13169
+ annotations: { readOnlyHint: false, destructiveHint: true, openWorldHint: true },
13170
13170
  }, wrap(async (a) => { const d = await apiPost('/api/apple-ads/budget-order/update', a); return ok(`Apple Ads budget order ${d.id} updated (verified: ${d.verified}):\n${d.summary}\n${d.note || ''}`, d); }));
13171
13171
 
13172
13172
  server.registerTool('bulk_apple_ads_keywords', {
@@ -13181,7 +13181,7 @@ function buildTools(rawServer, opts = {}, sink = null) {
13181
13181
  confirm: z.boolean().optional().describe('Required only when the target ad group or campaign is already ENABLED, where the new keywords could serve immediately.'),
13182
13182
  },
13183
13183
  outputSchema: { ok: z.boolean().optional(), kind: z.string().optional(), op: z.string().optional(), requested: z.number().optional(), succeeded: z.number().optional(), failed: z.number().optional(), verified: z.boolean().optional(), results: z.array(z.any()).optional(), summary: z.string().optional(), note: z.string().optional() },
13184
- annotations: { readOnlyHint: false, destructiveHint: false, openWorldHint: true },
13184
+ annotations: { readOnlyHint: false, destructiveHint: true, openWorldHint: true },
13185
13185
  }, wrap(async (a) => { const d = await apiPost('/api/apple-ads/keywords/bulk', a); return ok(`${d.summary}\n${d.note || ''}`, d); }));
13186
13186
 
13187
13187
  server.registerTool('search_apple_ads_geo', {
@@ -13267,7 +13267,7 @@ function buildTools(rawServer, opts = {}, sink = null) {
13267
13267
  purchaseOrderNumber: z.string().optional(),
13268
13268
  },
13269
13269
  outputSchema: { accountId: z.string().optional(), campaignId: z.string().optional(), updated: z.array(z.string()).optional(), changed: z.number().optional(), status: z.string().nullable().optional(), note: z.string().optional() },
13270
- annotations: { readOnlyHint: false, destructiveHint: false, openWorldHint: true },
13270
+ annotations: { readOnlyHint: false, destructiveHint: true, openWorldHint: true },
13271
13271
  }, wrap(async (a) => { const d = await apiPost('/api/x-ads/campaign-update', a); return ok(d.note, d); }));
13272
13272
  server.registerTool('update_x_ads_line_item', {
13273
13273
  title: 'Change an X line item (ad group)',
@@ -13283,7 +13283,7 @@ function buildTools(rawServer, opts = {}, sink = null) {
13283
13283
  totalBudget: z.number().optional().describe('only if the campaign is not budget-optimised'),
13284
13284
  },
13285
13285
  outputSchema: { accountId: z.string().optional(), lineItemId: z.string().optional(), updated: z.array(z.string()).optional(), changed: z.number().optional(), status: z.string().nullable().optional(), note: z.string().optional() },
13286
- annotations: { readOnlyHint: false, destructiveHint: false, openWorldHint: true },
13286
+ annotations: { readOnlyHint: false, destructiveHint: true, openWorldHint: true },
13287
13287
  }, wrap(async (a) => { const d = await apiPost('/api/x-ads/line-item-update', a); return ok(d.note, d); }));
13288
13288
  server.registerTool('list_x_ads_promoted_tweets', {
13289
13289
  title: 'List X promoted posts',
@@ -13504,7 +13504,7 @@ function buildTools(rawServer, opts = {}, sink = null) {
13504
13504
  expectedRevision: z.union([z.number(), z.string()]).optional().describe('REQUIRED for replace, optional for add/remove — membershipRevision from get_openai_ads_audience'),
13505
13505
  },
13506
13506
  outputSchema: { operationId: z.string().optional(), audienceId: z.string().optional(), operation: z.string().optional(), status: z.string().optional(), membersSent: z.number().optional(), rejected: z.number().optional(), note: z.string().optional() },
13507
- annotations: { readOnlyHint: false, destructiveHint: false, openWorldHint: true },
13507
+ annotations: { readOnlyHint: false, destructiveHint: true, openWorldHint: true },
13508
13508
  }, wrap(async (a) => { const d = await apiPost('/api/openai-ads/audience/members', a); return ok(d.note, d); }));
13509
13509
  server.registerTool('list_openai_ads_audience_operations', {
13510
13510
  title: 'List ChatGPT Ads audience membership operations',
@@ -13653,7 +13653,7 @@ function buildTools(rawServer, opts = {}, sink = null) {
13653
13653
  products: z.array(z.record(z.any())).describe('[{id, variants:[{id, title?, price?:{amount:int minor units, currency}, availability?:{available:bool}|{status:"in_stock"|"out_of_stock"}}]}]'),
13654
13654
  },
13655
13655
  outputSchema: { feedId: z.string().optional(), accepted: z.boolean().optional(), products: z.number().optional(), variants: z.number().optional(), note: z.string().optional() },
13656
- annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: true },
13656
+ annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: true, openWorldHint: true },
13657
13657
  }, wrap(async (a) => { const d = await apiPost('/api/openai-ads/feed-products', a); return ok(d.note, d); }));
13658
13658
  server.registerTool('update_openai_ads_object', {
13659
13659
  title: 'Edit a ChatGPT Ads campaign / ad group / ad',
@@ -13677,7 +13677,7 @@ function buildTools(rawServer, opts = {}, sink = null) {
13677
13677
  confirm: z.boolean().optional().describe('REQUIRED true to change budget / bid / creative on a LIVE object'),
13678
13678
  },
13679
13679
  outputSchema: { ok: z.boolean().optional(), level: z.string().optional(), id: z.string().optional(), object: z.any().optional(), note: z.string().optional() },
13680
- annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: true },
13680
+ annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: true, openWorldHint: true },
13681
13681
  }, wrap(async (a) => {
13682
13682
  const d = await apiPost('/api/openai-ads/update', a);
13683
13683
  return ok(d.note, d);
@@ -13853,7 +13853,7 @@ function buildTools(rawServer, opts = {}, sink = null) {
13853
13853
  }, wrap(async (a) => oaiNote(await apiPost('/api/openai-ads/lead-form/update', a))));
13854
13854
  server.registerTool('publish_openai_ads_lead_form', {
13855
13855
  title: 'Publish a ChatGPT Ads lead form', description: 'Publish the current draft of a ChatGPT Ads lead form so Business Agents can use it. expectedDraftRevisionId is the draftRevisionId you last read. Show the user the fields before publishing: the form is what real people will see.',
13856
- inputSchema: { leadFormId: z.string(), expectedDraftRevisionId: z.string() }, annotations: { readOnlyHint: false, destructiveHint: false, openWorldHint: true },
13856
+ inputSchema: { leadFormId: z.string(), expectedDraftRevisionId: z.string() }, annotations: { readOnlyHint: false, destructiveHint: true, openWorldHint: true },
13857
13857
  }, wrap(async (a) => oaiNote(await apiPost('/api/openai-ads/lead-form/publish', a))));
13858
13858
  server.registerTool('archive_openai_ads_lead_form', {
13859
13859
  title: 'Archive a ChatGPT Ads lead form', description: 'Archive a ChatGPT Ads lead form. Not reversible through the API, and refused by ChatGPT Ads while a PUBLISHED Business Agent references it. confirm:true is required; without it nothing changes and the reply says what would.',
@@ -13890,7 +13890,7 @@ function buildTools(rawServer, opts = {}, sink = null) {
13890
13890
  businessAgentId: z.string().optional().describe('replace THIS agent’s configuration instead of creating a new one'),
13891
13891
  name: z.string(), instructions: z.string(), description: z.string().optional(), privacyPolicyUrl: z.string().optional(),
13892
13892
  conversationStarters: z.array(z.string()).optional(), productFeedIds: z.array(z.string()).optional(), connectorIds: z.array(z.string()).optional(), toolIds: z.array(z.string()).optional(), leadFormId: z.string().optional(),
13893
- }, annotations: { readOnlyHint: false, destructiveHint: false, openWorldHint: true },
13893
+ }, annotations: { readOnlyHint: false, destructiveHint: true, openWorldHint: true },
13894
13894
  }, wrap(async (a) => oaiNote(await apiPost('/api/openai-ads/business-agent', a))));
13895
13895
  server.registerTool('get_openai_ads_business_agent', {
13896
13896
  title: 'Read a ChatGPT Ads Business Agent', description: 'One Business Agent with its full configuration and publish state. Read-only, 0 credits.',
@@ -13902,7 +13902,7 @@ function buildTools(rawServer, opts = {}, sink = null) {
13902
13902
  }, wrap(async (a) => oaiNote(await apiPost('/api/openai-ads/business-agent/preview', a))));
13903
13903
  server.registerTool('publish_openai_ads_business_agent', {
13904
13904
  title: 'Publish a ChatGPT Ads Business Agent', description: 'Publish a Business Agent’s draft so every campaign referencing it serves this version to real users. confirm:true required; show the user the instructions first.',
13905
- inputSchema: { businessAgentId: z.string(), confirm: z.boolean().optional() }, annotations: { readOnlyHint: false, destructiveHint: false, openWorldHint: true },
13905
+ inputSchema: { businessAgentId: z.string(), confirm: z.boolean().optional() }, annotations: { readOnlyHint: false, destructiveHint: true, openWorldHint: true },
13906
13906
  }, wrap(async (a) => oaiNote(await apiPost('/api/openai-ads/business-agent/publish', a))));
13907
13907
  server.registerTool('list_openai_ads_spend_windows', {
13908
13908
  title: 'List ChatGPT Ads account spending limits', description: 'Account-level SPENDING LIMITS on ChatGPT Ads, both kinds: the date-range SPEND-LIMIT WINDOWS (a ceiling on what the whole account may spend between an inclusive start date and an exclusive end date, with amount and spent so far) and the DAILY LIMIT (a per-day ceiling that renews at midnight in the account timezone, with what is spent and left today). Also returns the configuration `revision` that set_openai_ads_daily_spend_limit and remove_openai_ads_daily_spend_limit need as expectedRevision, and the earliest date a new daily limit may start. A limit caps spend; it never makes anything spend. OpenAI serves account spending limits only to ad accounts on postpaid INVOICE billing, and every spending-limit request, reads included, needs billing-management permission: a card-billed account is refused (a 403 that says only admins can do it) from any key, including one its own admin created. Read-only, 0 credits.',
@@ -13911,7 +13911,7 @@ function buildTools(rawServer, opts = {}, sink = null) {
13911
13911
  server.registerTool('set_openai_ads_spend_window', {
13912
13912
  title: 'Create or edit a ChatGPT Ads spend-limit window', description: 'Create (no windowId) or edit (windowId) an account-level spend-limit window on ChatGPT Ads: startDate YYYY-MM-DD inclusive, endDate YYYY-MM-DD EXCLUSIVE (the window ends the day before), amount in the account currency (sent as micros), optional name and insertion-order id (ioId). A window is a CEILING on what the whole account may spend in that range; it never makes anything spend, and campaign budgets still apply underneath it. On edit, pass only the fields that change; a window whose canEdit is false (already started, per OpenAI) cannot be edited. Raising the amount lets campaigns spend up to their budgets, so show the user the old and new amounts. Reply carries the stored window (amount, spent so far, status). The live API serves this path since at least 2026-09-12 (it answered "Invalid URL" on 2026-09-03). It needs postpaid INVOICE billing and billing-management permission; a card-billed account is refused whatever key is used.',
13913
13913
  inputSchema: { windowId: z.string().optional(), startDate: z.string().optional(), endDate: z.string().optional(), amount: z.number().optional(), name: z.string().optional(), ioId: z.string().optional() },
13914
- annotations: { readOnlyHint: false, destructiveHint: false, openWorldHint: true },
13914
+ annotations: { readOnlyHint: false, destructiveHint: true, openWorldHint: true },
13915
13915
  }, wrap(async (a) => oaiNote(await apiPost('/api/openai-ads/spend-window', a))));
13916
13916
  server.registerTool('delete_openai_ads_spend_window', {
13917
13917
  title: 'Delete a ChatGPT Ads spend-limit window', description: 'Delete an active or scheduled spend-limit window before it ends — this REMOVES a spending ceiling. confirm:true required.',
@@ -14128,7 +14128,7 @@ function buildTools(rawServer, opts = {}, sink = null) {
14128
14128
  confirm: z.boolean().optional().describe('REQUIRED true to change bid / budget / campaign on a LIVE ad group'),
14129
14129
  },
14130
14130
  outputSchema: { ok: z.boolean().optional(), adAccountId: z.string().optional(), campaignId: z.string().optional(), adGroupId: z.string().optional(), changed: z.array(z.string()).optional(), adGroup: z.record(z.any()).optional(), note: z.string().optional() },
14131
- annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: true },
14131
+ annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: true, openWorldHint: true },
14132
14132
  }, wrap(async (a) => { const d = await apiPost('/api/pinterest/ads-ad-group/update', a); return ok(d.note, d); }));
14133
14133
  server.registerTool('update_pinterest_ads_ad', {
14134
14134
  title: 'Edit a Pinterest ad',
@@ -14156,7 +14156,7 @@ function buildTools(rawServer, opts = {}, sink = null) {
14156
14156
  pinId: z.string().optional().describe('DRAFT ads only — Pinterest refuses it on any other status'),
14157
14157
  },
14158
14158
  outputSchema: { ok: z.boolean().optional(), adAccountId: z.string().optional(), adGroupId: z.string().optional(), adId: z.string().optional(), changed: z.array(z.string()).optional(), reviewStatus: z.string().optional(), ad: z.record(z.any()).optional(), note: z.string().optional() },
14159
- annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: true },
14159
+ annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: true, openWorldHint: true },
14160
14160
  }, wrap(async (a) => { const d = await apiPost('/api/pinterest/ads-ad/update', a); return ok(d.note, d); }));
14161
14161
  server.registerTool('set_pinterest_ads_budget', {
14162
14162
  title: 'Set a Pinterest campaign budget',
@@ -14379,7 +14379,7 @@ function buildTools(rawServer, opts = {}, sink = null) {
14379
14379
  allowComments: z.boolean().describe('REQUIRED — Reddit demands allow_comments on every post update, and it is the only field it permits'),
14380
14380
  },
14381
14381
  outputSchema: { id: z.string().optional(), headline: z.string().optional(), allowComments: z.boolean().optional(), note: z.string().optional() },
14382
- annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: true },
14382
+ annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: true, openWorldHint: true },
14383
14383
  }, wrap(async (a) => {
14384
14384
  const d = await apiPost('/api/reddit-ads/posts/update', a);
14385
14385
  return ok(d.note, d);
@@ -14475,7 +14475,7 @@ function buildTools(rawServer, opts = {}, sink = null) {
14475
14475
  schedule: z.array(z.any()).optional(),
14476
14476
  },
14477
14477
  outputSchema: { id: z.string().optional(), name: z.string().optional(), status: z.string().optional(), budget: z.number().nullable().optional(), productSetId: z.string().nullable().optional(), shoppingType: z.string().nullable().optional(), note: z.string().optional() },
14478
- annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: true },
14478
+ annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: true, openWorldHint: true },
14479
14479
  }, wrap(async (a) => {
14480
14480
  const d = await apiPost('/api/reddit-ads/ad-groups/update', a);
14481
14481
  return ok(d.note, d);
@@ -14568,7 +14568,7 @@ function buildTools(rawServer, opts = {}, sink = null) {
14568
14568
  supplementaryText: z.string().optional().describe('up to 100 characters'),
14569
14569
  userGeneratedContent: z.enum(['OPT_IN', 'OPT_OUT']).optional(),
14570
14570
  },
14571
- annotations: { readOnlyHint: false, destructiveHint: false, openWorldHint: true },
14571
+ annotations: { readOnlyHint: false, destructiveHint: true, openWorldHint: true },
14572
14572
  }, wrap(async (a) => { const d = await apiPost('/api/reddit-ads/max-template/update', a); return ok(d.note, d); }));
14573
14573
  server.registerTool('update_reddit_ads_ad', {
14574
14574
  title: 'Edit a Reddit ad',
@@ -14581,7 +14581,7 @@ function buildTools(rawServer, opts = {}, sink = null) {
14581
14581
  clickUrl: z.string().optional().describe('pass an empty string to clear it'),
14582
14582
  },
14583
14583
  outputSchema: { id: z.string().optional(), name: z.string().optional(), status: z.string().optional(), note: z.string().optional() },
14584
- annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: true },
14584
+ annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: true, openWorldHint: true },
14585
14585
  }, wrap(async (a) => {
14586
14586
  const d = await apiPost('/api/reddit-ads/ads/update', a);
14587
14587
  return ok(d.note, d);
@@ -14600,7 +14600,7 @@ function buildTools(rawServer, opts = {}, sink = null) {
14600
14600
  endTime: z.string().optional(),
14601
14601
  },
14602
14602
  outputSchema: { id: z.string().optional(), name: z.string().optional(), status: z.string().optional(), note: z.string().optional() },
14603
- annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: true },
14603
+ annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: true, openWorldHint: true },
14604
14604
  }, wrap(async (a) => {
14605
14605
  const d = await apiPost('/api/reddit-ads/campaign/update', a);
14606
14606
  return ok(d.note, d);
@@ -14814,7 +14814,7 @@ function buildTools(rawServer, opts = {}, sink = null) {
14814
14814
  })).describe('up to 2,500 rows; every row must carry the same fields'),
14815
14815
  },
14816
14816
  outputSchema: { ok: z.boolean().optional(), customAudienceId: z.string().optional(), action: z.string().optional(), rows: z.number().optional(), sizeUpper: z.number().nullable().optional(), note: z.string().optional() },
14817
- annotations: { readOnlyHint: false, destructiveHint: false, openWorldHint: true },
14817
+ annotations: { readOnlyHint: false, destructiveHint: true, openWorldHint: true },
14818
14818
  }, wrap(async (a) => {
14819
14819
  const d = await apiPost('/api/reddit-ads/audiences/users', a);
14820
14820
  return ok(d.note, d);
@@ -14867,7 +14867,7 @@ function buildTools(rawServer, opts = {}, sink = null) {
14867
14867
  targeting: z.record(z.any()).optional().describe('REPLACES the existing targeting'),
14868
14868
  },
14869
14869
  outputSchema: { id: z.string().optional(), name: z.string().optional(), status: z.string().optional(), activeAdGroups: z.number().nullable().optional(), note: z.string().optional() },
14870
- annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: true },
14870
+ annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: true, openWorldHint: true },
14871
14871
  }, wrap(async (a) => {
14872
14872
  const d = await apiPost('/api/reddit-ads/saved-audiences/update', a);
14873
14873
  return ok(d.note, d);
@@ -15848,7 +15848,7 @@ function buildTools(rawServer, opts = {}, sink = null) {
15848
15848
  leadSource: z.string().optional().describe('INSTANT_FORM (default) or DIRECT_MESSAGE'),
15849
15849
  },
15850
15850
  outputSchema: { action: z.string().optional(), advertiserId: z.string().optional(), leadSource: z.string().optional(), leadId: z.string().nullable().optional(), pageId: z.string().nullable().optional(), campaignId: z.string().nullable().optional(), adgroupId: z.string().nullable().optional(), adId: z.string().nullable().optional(), leadData: z.any().optional(), deleted: z.boolean().optional(), note: z.string().optional() },
15851
- annotations: { readOnlyHint: false, destructiveHint: false, openWorldHint: true },
15851
+ annotations: { readOnlyHint: false, destructiveHint: true, openWorldHint: true },
15852
15852
  }, wrap(async (a) => {
15853
15853
  const d = await apiPost('/api/tiktok-ads/test-lead', a);
15854
15854
  return ok(`${d.action === 'delete' ? `Test lead ${d.leadId} deleted.` : d.leadId ? `Test lead ${d.leadId} on form ${d.pageId}: ${JSON.stringify(d.leadData)}` : 'No test lead exists on that form.'} ${d.note}`, d);
@@ -16244,7 +16244,7 @@ function buildTools(rawServer, opts = {}, sink = null) {
16244
16244
  operation: z.enum(['HIDDEN', 'PUBLIC']).describe('HIDDEN takes them out of public view; PUBLIC puts them back'),
16245
16245
  },
16246
16246
  outputSchema: { advertiserId: z.string().optional(), commentIds: z.array(z.string()).optional(), operation: z.string().optional(), applied: z.boolean().optional(), note: z.string().optional() },
16247
- annotations: { readOnlyHint: false, destructiveHint: false, openWorldHint: true },
16247
+ annotations: { readOnlyHint: false, destructiveHint: true, openWorldHint: true },
16248
16248
  }, wrap(async (a) => {
16249
16249
  const d = await apiPost('/api/tiktok-ads/comment-moderate', a);
16250
16250
  return ok(d.note, d);
@@ -16364,7 +16364,7 @@ function buildTools(rawServer, opts = {}, sink = null) {
16364
16364
  confirm: z.boolean().optional().describe('REQUIRED true only to turn coverAllObjectives ON for the first time, because that is irreversible'),
16365
16365
  },
16366
16366
  outputSchema: { advertiserId: z.string().optional(), applied: z.boolean().optional(), before: z.any().optional(), after: z.any().optional(), changed: z.array(z.string()).optional(), note: z.string().optional() },
16367
- annotations: { readOnlyHint: false, destructiveHint: false, openWorldHint: true },
16367
+ annotations: { readOnlyHint: false, destructiveHint: true, openWorldHint: true },
16368
16368
  }, wrap(async (a) => {
16369
16369
  const d = await apiPost('/api/tiktok-ads/brand-safety', a);
16370
16370
  return ok(d.note, d);
@@ -16477,7 +16477,7 @@ function buildTools(rawServer, opts = {}, sink = null) {
16477
16477
  confirmScope: z.string().optional().describe('required in the same case — the scope token the refusal prints, computed from the version being sent'),
16478
16478
  },
16479
16479
  outputSchema: { advertiserId: z.string().optional(), ruleId: z.string().optional(), klass: z.string().optional(), canSpend: z.boolean().optional(), armingActions: z.array(z.string()).optional(), rule: z.any().optional(), verified: z.boolean().optional(), note: z.string().optional() },
16480
- annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: true },
16480
+ annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: true, openWorldHint: true },
16481
16481
  }, wrap(async (a) => {
16482
16482
  const d = await apiPost('/api/tiktok-ads/rule-update', a);
16483
16483
  return ok(d.note, d);
@@ -16888,7 +16888,7 @@ function buildTools(rawServer, opts = {}, sink = null) {
16888
16888
  sendNotification: z.boolean().optional().describe('default false'),
16889
16889
  },
16890
16890
  outputSchema: { ttoAccountId: z.string().optional(), campaignId: z.string().optional(), handleNames: z.array(z.string()).optional(), advertiserIds: z.array(z.string()).optional(), added: z.any().optional(), note: z.string().optional() },
16891
- annotations: { readOnlyHint: false, destructiveHint: false, openWorldHint: true },
16891
+ annotations: { readOnlyHint: false, destructiveHint: true, openWorldHint: true },
16892
16892
  }, wrap(async (a) => {
16893
16893
  const d = await apiPost('/api/tiktok-ads/tto-campaign-update', a);
16894
16894
  return ok(d.note, d);
@@ -17310,7 +17310,7 @@ function buildTools(rawServer, opts = {}, sink = null) {
17310
17310
  endTime: z.string().optional().describe('ISO 8601'),
17311
17311
  },
17312
17312
  outputSchema: { id: z.string().optional(), adAccountId: z.string().optional(), campaignId: z.string().optional(), name: z.string().optional(), status: z.string().optional(), optimizationGoal: z.string().optional(), dailyBudget: z.number().nullable().optional(), dailyBudgetMicro: z.number().nullable().optional(), currency: z.string().nullable().optional(), verified: z.boolean().optional(), note: z.string().optional() },
17313
- annotations: { readOnlyHint: false, destructiveHint: false, openWorldHint: true },
17313
+ annotations: { readOnlyHint: false, destructiveHint: true, openWorldHint: true },
17314
17314
  }, wrap(async (a) => {
17315
17315
  const d = await apiPost('/api/snapchat-ads/adsquad', a);
17316
17316
  const money = d.dailyBudget != null ? ` Daily budget ${d.dailyBudget} ${d.currency || ''} (${d.dailyBudgetMicro} micro).` : '';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hermoso",
3
- "version": "0.1.430",
3
+ "version": "0.1.431",
4
4
  "mcpName": "io.github.hermoso-ai/hermoso",
5
5
  "description": "AI marketing agent and AI ad generator, run from your own AI agent. 922 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, ChatGPT Ads and AppLovin Ads, plus product feeds in Google Merchant Center. PUBLISHING AND SCHEDULING: Facebook, Instagram, Threads, TikTok, YouTube, X, LinkedIn, Pinterest, Bluesky, Telegram and Google Business Profile. INSTAGRAM DM AUTOMATIONS: a keyword comment, a DM, a story reply or an ig.me link gets an automatic private reply from the brand. 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",