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.
- package/mcp/openai-extensions.mjs +1 -1
- package/mcp/tools.mjs +39 -39
- package/package.json +1 -1
|
@@ -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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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.
|
|
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",
|