hermoso 0.1.211 → 0.1.212
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 +2 -2
- package/mcp/tools.mjs +74 -0
- package/package.json +2 -2
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
|
-
**
|
|
8
|
+
**801 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
|
**What it connects to.** Ad platforms: Meta, Google Ads, TikTok Ads, LinkedIn Ads, Reddit Ads, X Ads,
|
|
@@ -171,7 +171,7 @@ block entirely if you signed in above; it is there for CI, where the process can
|
|
|
171
171
|
|
|
172
172
|
Then ask your agent: *“Generate an image ad with Hermoso.”*
|
|
173
173
|
|
|
174
|
-
### What the
|
|
174
|
+
### What the 801 tools cover
|
|
175
175
|
|
|
176
176
|
**Ad spy / research** — `find_competitors`, `competitor_teardown`, `pull_competitor_ads`, `research_ads`; the
|
|
177
177
|
Meta / Google / LinkedIn ad libraries (`search_meta_ads`, `search_google_ads`, `search_linkedin_ads`); organic
|
package/mcp/tools.mjs
CHANGED
|
@@ -11721,6 +11721,80 @@ function buildTools(rawServer, opts = {}, sink = null) {
|
|
|
11721
11721
|
const d = await apiGet('/api/reddit-ads/pixels', a);
|
|
11722
11722
|
return ok(d.note, d);
|
|
11723
11723
|
}));
|
|
11724
|
+
// REDDIT CREATIVE ASSET LIBRARY (2026-09-07) — read off Reddit's own OpenAPI (spec v3). Reddit DOWNLOADS the
|
|
11725
|
+
// media from a url you hand it; there is no byte upload. The library is STANDALONE today: no ad/post field in the
|
|
11726
|
+
// spec consumes an asset id yet, and every description says so rather than implying an ad can be built from it.
|
|
11727
|
+
server.registerTool('upload_reddit_ads_assets', {
|
|
11728
|
+
title: 'Upload creatives to the Reddit asset library',
|
|
11729
|
+
description: 'Store creatives in the Reddit Ads ASSET LIBRARY for a profile: images and videos (from a public https url — Reddit downloads it; a video may carry a posterUrl), HEADLINE text, and CTA labels from Reddit’s fixed list (Shop Now, Learn More, Sign Up, …). 1–20 per call. Processing is asynchronous: the reply is read back from the library after polling (up to ~20s) and says which assets are ACTIVE, which Reddit REJECTED and why, and which are still processing (finish those with get_reddit_ads_asset_uploads). NOTE: Reddit’s spec exposes no ad or post field that consumes an asset id yet, so this stores and organises creative; it does not attach it to an ad. Free.',
|
|
11730
|
+
inputSchema: {
|
|
11731
|
+
adAccountId: z.string().optional().describe('Reddit ad account id (a2_…) — omit when only one is shared'),
|
|
11732
|
+
redditProfileId: z.string().describe('the Reddit profile id (t2_…) from list_reddit_ads_profiles — assets hang off a profile, and it must be on the shared ad account'),
|
|
11733
|
+
assets: z.array(z.object({
|
|
11734
|
+
type: z.enum(['IMAGE', 'VIDEO', 'HEADLINE', 'CTA']),
|
|
11735
|
+
url: z.string().optional().describe('IMAGE/VIDEO: a public https url Reddit can download (upload_file gives you one)'),
|
|
11736
|
+
name: z.string().optional().describe('IMAGE/VIDEO: library name; defaults to the file name'),
|
|
11737
|
+
posterUrl: z.string().optional().describe('VIDEO only: an https url for the poster frame'),
|
|
11738
|
+
text: z.string().optional().describe('HEADLINE: the headline text'),
|
|
11739
|
+
callToAction: z.string().optional().describe('CTA: one of Reddit’s labels — Apply Now | Contact Us | Download | Get a Quote | Get Showtimes | Install | Learn More | Order Now | Play Now | Pre-order Now | See Menu | Shop Now | Sign Up | View More | Watch Now | Book Now | Buy Tickets | Get Directions | Listen Now | Read More | Subscribe | Visit Store | Donate Now | Remind Me'),
|
|
11740
|
+
referenceId: z.string().optional().describe('your own id, echoed back per upload'),
|
|
11741
|
+
})).min(1).max(20),
|
|
11742
|
+
},
|
|
11743
|
+
outputSchema: { adAccountId: z.string().optional(), profileId: z.string().optional(), count: z.number().optional(), ready: z.number().optional(), failed: z.number().optional(), pending: z.number().optional(), uploads: z.array(z.any()).optional(), note: z.string().optional() },
|
|
11744
|
+
annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: true },
|
|
11745
|
+
}, wrap(async (a) => { const d = await apiPost('/api/reddit-ads/assets/upload', a); return ok(d.note, d); }));
|
|
11746
|
+
|
|
11747
|
+
server.registerTool('get_reddit_ads_asset_uploads', {
|
|
11748
|
+
title: 'Poll Reddit asset uploads',
|
|
11749
|
+
description: 'The processing state of one or more Reddit creative-asset uploads (ids from upload_reddit_ads_assets): PROCESSING, ACTIVE with the stored asset, or INVALID_MEDIA with Reddit’s reasons. Ids Reddit no longer knows are listed as missing, never silently dropped. Free.',
|
|
11750
|
+
inputSchema: {
|
|
11751
|
+
adAccountId: z.string().optional().describe('Reddit ad account id (a2_…) — omit when only one is shared'),
|
|
11752
|
+
redditProfileId: z.string().describe('the Reddit profile id (t2_…) from list_reddit_ads_profiles — assets hang off a profile, and it must be on the shared ad account'),
|
|
11753
|
+
uploadIds: z.array(z.string()).min(1).max(50),
|
|
11754
|
+
},
|
|
11755
|
+
outputSchema: { profileId: z.string().optional(), count: z.number().optional(), uploads: z.array(z.any()).optional(), missing: z.array(z.string()).optional(), note: z.string().optional() },
|
|
11756
|
+
annotations: { readOnlyHint: true, destructiveHint: false, openWorldHint: true },
|
|
11757
|
+
}, wrap(async (a) => { const d = await apiGet('/api/reddit-ads/assets/uploads', a); return ok(d.note, d); }));
|
|
11758
|
+
|
|
11759
|
+
server.registerTool('list_reddit_ads_assets', {
|
|
11760
|
+
title: 'List the Reddit asset library',
|
|
11761
|
+
description: 'The IMAGE and VIDEO assets stored on a Reddit profile, with status, dimensions and media urls; filter by type and name, paginate with pageToken. Reddit’s list is media-only: HEADLINE and CTA assets are stored but never listed here — read those by id with get_reddit_ads_asset (ids come from upload_reddit_ads_assets). Free.',
|
|
11762
|
+
inputSchema: {
|
|
11763
|
+
adAccountId: z.string().optional().describe('Reddit ad account id (a2_…) — omit when only one is shared'),
|
|
11764
|
+
redditProfileId: z.string().describe('the Reddit profile id (t2_…) from list_reddit_ads_profiles — assets hang off a profile, and it must be on the shared ad account'),
|
|
11765
|
+
types: z.array(z.enum(['IMAGE', 'VIDEO'])).optional().describe('Reddit lists media only'),
|
|
11766
|
+
name: z.string().optional().describe('filter by asset name'),
|
|
11767
|
+
limit: z.number().optional().describe('1–100, default 25'),
|
|
11768
|
+
pageToken: z.string().optional(),
|
|
11769
|
+
},
|
|
11770
|
+
outputSchema: { profileId: z.string().optional(), count: z.number().optional(), assets: z.array(z.any()).optional(), nextPageToken: z.string().nullable().optional(), note: z.string().optional() },
|
|
11771
|
+
annotations: { readOnlyHint: true, destructiveHint: false, openWorldHint: true },
|
|
11772
|
+
}, wrap(async (a) => { const d = await apiGet('/api/reddit-ads/assets', a); return ok(d.note, d); }));
|
|
11773
|
+
|
|
11774
|
+
server.registerTool('get_reddit_ads_asset', {
|
|
11775
|
+
title: 'Read one Reddit creative asset',
|
|
11776
|
+
description: 'One stored Reddit creative asset by id — type, name, status, media dimensions and urls, or the headline/CTA text, plus any processing errors Reddit holds for it. Free.',
|
|
11777
|
+
inputSchema: { adAccountId: z.string().optional(), assetId: z.string() },
|
|
11778
|
+
outputSchema: { asset: z.any().optional(), note: z.string().optional() },
|
|
11779
|
+
annotations: { readOnlyHint: true, destructiveHint: false, openWorldHint: true },
|
|
11780
|
+
}, wrap(async (a) => { const d = await apiGet('/api/reddit-ads/asset', a); return ok(d.note, d); }));
|
|
11781
|
+
|
|
11782
|
+
server.registerTool('rename_reddit_ads_asset', {
|
|
11783
|
+
title: 'Rename a Reddit creative asset',
|
|
11784
|
+
description: 'Change a stored Reddit creative asset’s name — the ONLY field Reddit lets you edit on an asset (its PATCH takes {name} and nothing else). The reply is read back from the library. Free.',
|
|
11785
|
+
inputSchema: { adAccountId: z.string().optional(), assetId: z.string(), name: z.string() },
|
|
11786
|
+
outputSchema: { asset: z.any().optional(), confirmed: z.boolean().optional(), note: z.string().optional() },
|
|
11787
|
+
annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: true },
|
|
11788
|
+
}, wrap(async (a) => { const d = await apiPost('/api/reddit-ads/asset/rename', a); return ok(d.note, d); }));
|
|
11789
|
+
|
|
11790
|
+
server.registerTool('delete_reddit_ads_asset', {
|
|
11791
|
+
title: 'Delete a Reddit creative asset',
|
|
11792
|
+
description: 'Permanently delete a creative asset from the Reddit asset library. Without confirm:true it deletes nothing and reports what the asset is; with it, the reply is the read-back (the asset reads as DELETED or gone), never Reddit’s 200. Free.',
|
|
11793
|
+
inputSchema: { adAccountId: z.string().optional(), assetId: z.string(), confirm: z.boolean().optional() },
|
|
11794
|
+
outputSchema: { assetId: z.string().optional(), confirmed: z.boolean().optional(), note: z.string().optional() },
|
|
11795
|
+
annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: true, openWorldHint: true },
|
|
11796
|
+
}, wrap(async (a) => { const d = await apiPost('/api/reddit-ads/asset/delete', a); return ok(d.note, d); }));
|
|
11797
|
+
|
|
11724
11798
|
server.registerTool('send_reddit_ads_conversions', {
|
|
11725
11799
|
title: 'Send conversions to Reddit (Conversions API)',
|
|
11726
11800
|
description: 'Report conversions to Reddit server-side — purchases, leads, sign-ups, or your own custom events — so Reddit can attribute them to the ads that caused them and optimise delivery toward them. This is what makes a CONVERSIONS campaign work; without it Reddit optimises blind. Send events as close to real time as you can: Reddit REFUSES anything older than seven days, and deduplication against the browser pixel only works inside two days. Pass ordinary email addresses and phone numbers — they are canonicalised and SHA-256 hashed on our server before they reach Reddit, and a value you already hashed is passed through untouched. The more match keys per event (email, phone, clickId, uuid, externalId, IP + user agent) the better the attribution. Set conversionId on every event if you ALSO run the browser pixel, or the same purchase is counted twice. Costs no credits and spends no ad money — this is measurement. Needs the "adsconversions" permission: if Reddit answers 403, the connection predates it and the user must reconnect Reddit Ads.',
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "hermoso",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.212",
|
|
4
4
|
"mcpName": "io.github.hermoso-ai/hermoso",
|
|
5
|
-
"description": "AI ad studio and marketing MCP server with
|
|
5
|
+
"description": "AI ad studio and marketing MCP server with 801 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",
|
|
7
7
|
"bin": {
|
|
8
8
|
"hermoso": "bin/hermoso.mjs"
|