hermoso 0.1.23 → 0.1.25

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,14 +1,17 @@
1
1
  # Hermoso — MCP, CLI & Skills
2
2
 
3
- Drive [Hermoso](https://hermoso.ai) the AI ad studio — from **any AI agent**: Claude Code, Claude.ai, Cursor,
4
- Codex, or your own scripts. Research the ads already winning in a market, plan a creative, and generate finished
5
- image & video ads (your real product composited in, copy + CTA included) all over
6
- [MCP](https://modelcontextprotocol.io) tools, a CLI, or installable Claude skills.
3
+ Run your whole marketing operation from **any AI agent**: Claude Code, Claude.ai, Cursor, Codex, or your own
4
+ scripts. Research the ads already winning in a market, generate finished image & video ads (your real product
5
+ composited in, copy + CTA included), publish them to your own social channels, and build & manage the ad
6
+ campaigns behind them — all over [MCP](https://modelcontextprotocol.io) tools, a CLI, or installable Claude skills.
7
+
8
+ **247 tools.** `tools/list` is always the authoritative set; `hermoso_capabilities` (free) returns the live model
9
+ catalog with exact per-render credit costs plus the full capability map.
7
10
 
8
11
  ## Instant: the hosted Claude.ai connector
9
12
 
10
13
  Paste **`https://app.hermoso.ai/mcp`** into Claude → Settings → Connectors → *Add custom connector*, approve with
11
- your Hermoso account, done — the full studio toolset with your saved brand context, billed to your plan.
14
+ your Hermoso account, done — the full toolset with your saved brand context, billed to your plan.
12
15
 
13
16
  ## Quickstart for Claude Code / Cursor / scripts (2 minutes)
14
17
 
@@ -27,8 +30,8 @@ learned memory you set up in the web app apply automatically (`get_brand` shows
27
30
 
28
31
  ## 1. MCP server (stdio) — Claude Code / Cursor / Codex
29
32
 
30
- `hermoso mcp` runs a stdio MCP server exposing the full studio toolset (40+ tools). The published `hermoso`
31
- package means no clone — `npx -y hermoso mcp` fetches and runs it:
33
+ `hermoso mcp` runs a stdio MCP server exposing the full toolset. The published `hermoso` package means no clone —
34
+ `npx -y hermoso mcp` fetches and runs it:
32
35
 
33
36
  ```bash
34
37
  claude mcp add hermoso -e HERMOSO_TOKEN=hmk_… -- npx -y hermoso mcp
@@ -43,25 +46,67 @@ Cursor / Codex — add to `mcp.json` (Codex uses the TOML equivalent):
43
46
 
44
47
  Then ask your agent: *“Generate an image ad with Hermoso.”*
45
48
 
46
- **Tools (40+):** research/ad-spy (`find_competitors`, `pull_competitor_ads`, `research_ads`, `search_meta_ads`,
47
- `search_google_ads`, `search_linkedin_ads`, `search_tiktok`, `search_instagram`, `search_youtube`, `search_reddit`,
48
- `search_threads`, `scrapecreators_fetch`), plan generate finish (`plan_ad`, `plan_variations`, `generate_image`,
49
- `generate_video`, `generate_avatar`, `render_ad`, `make_template_ad`, `stitch_video`, `reframe_video`,
50
- `upscale_video`, `dub_video`, `change_voice`, `recast_motion`, `remix_static`, `finish_video`, `fix_beat`),
51
- brand + account (`get_brand`, `list_brands`, `use_brand`, `draft_brand`, `list_product_photos`, `set_product_image`,
52
- `hermoso_capabilities`, `hermoso_credits`, `buy_credits`), and analysis/jobs (`analyze_video`, `score_ad`,
53
- `check_ad_policy`, `competitor_teardown`, `mine_angles`, `get_job`, `list_jobs`, `get_skill`, `list_skills`,
54
- `fetch_asset`). Call `hermoso_capabilities` first it returns valid model ids and per-render credit costs;
55
- `tools/list` is the authoritative current set. Render jobs queue server-side and poll to completion, returning a
56
- served URL.
49
+ ### What the 247 tools cover
50
+
51
+ **Ad spy / research** `find_competitors`, `competitor_teardown`, `pull_competitor_ads`, `research_ads`; the
52
+ Meta / Google / LinkedIn ad libraries (`search_meta_ads`, `search_google_ads`, `search_linkedin_ads`); organic
53
+ social (`search_tiktok`, `search_instagram`, `search_youtube`, `search_reddit`, `search_threads`);
54
+ `scrapecreators_fetch`, `mine_angles`, `analyze_video`, `check_ad_policy`, `list_skills` / `get_skill`.
55
+
56
+ **Create** `draft_brand` `plan_ad` `render_ad` (the Studio quality pipeline: composited text, clean speech,
57
+ music, brand end card), or `generate_image` / `generate_video` / `generate_avatar` (UGC creators + lip-sync).
58
+ Also `make_template_ad` (native HTML ad formats), `make_explainer`, `product_sizzle`, `make_thumbnail`,
59
+ `remix_static`, `recast_motion`, `reframe_video`, `upscale_video`, `dub_video`, `change_voice`, `finish_video`,
60
+ `fix_beat`, `stitch_video`, `clip_video`, `post_edit`, plus `plan_variations` + `score_ad` to fan out and rank.
61
+ **Length is yours to set:** pass `durationSeconds` to `plan_ad` and the storyboard is *authored* to it — ≤15s
62
+ renders as one continuous clip, longer is stitched from acts (40s = 15+15+10), never time-compressed.
63
+
64
+ **Raw model playground** — the full catalog (30+ image / video / voice / writing models, each with its exact
65
+ per-render credit cost) with no ad framing: `generate_image` / `generate_video` with `useBrand:false`,
66
+ `generate_voice`, `generate_text`.
67
+
68
+ **Publish to your own channels** — Facebook, Instagram and Threads (`post_to_meta`), TikTok (`post_to_tiktok`),
69
+ YouTube (`post_to_youtube` + `update_youtube_video`, `youtube_video_insights`, comments read/reply), X
70
+ (`post_to_x`, `x_post_metrics`, `x_post_insights`, `x_mentions`), LinkedIn profile **and** company Pages
71
+ (`post_to_linkedin`, `post_to_linkedin_page`), Pinterest (`post_to_pinterest` + boards), and Google Business
72
+ Profile (`post_to_google_business` — Google grants this API per project, so access must be approved before Posts
73
+ publish). `schedule_post` / `list_scheduled` / `cancel_scheduled` give you one content calendar across channels.
74
+ `upload_file` brings in any external or local media, not just Hermoso renders.
75
+ *X posting bills credits per API call (X charges per request); a post containing a link costs 13× one without.*
76
+
77
+ **Run the ads** — full campaign trees, built paused and read back before anything is reported, with every spend
78
+ change confirm-gated, on **Meta**, **Google Ads**, **LinkedIn Ads**, **Pinterest Ads**, **Microsoft Advertising**
79
+ and **ChatGPT Ads** (OpenAI's Advertiser API). Each has list + report + create + budget/status tools
80
+ (e.g. `list_google_ads_campaigns`, `google_ads_report`, `create_google_ads_campaign`, `set_google_ads_budget`,
81
+ `set_google_ads_status`). *X Ads are not supported — X posting only.*
82
+
83
+ **Files** — Google Drive CRUD (`save_to_drive`, `list_drive_files`, `update_drive_file`, `delete_drive_file`,
84
+ `create_drive_folder`), Google Sheets (`create_sheet`, `append_to_sheet`, `read_sheet`), Google Docs
85
+ (`create_doc`, `append_to_doc`), and OneDrive (`save_to_onedrive` + full CRUD).
86
+
87
+ **Workspace & account** — brand workspaces (`list_brands`, `create_brand`, `use_brand`, `update_brand`,
88
+ `delete_brand` — one account holds many brands, so an agency runs every client through here), memory
89
+ (`remember`, `forget`, `list_memory`), custom skills (`save_skill`, `delete_skill`), AI employees
90
+ (`save_employee`, `list_employees`, `set_active_employee`), team (`list_team`, `invite_member`, `remove_member`,
91
+ `set_role`), settings (`get_settings`, `update_settings` — including the **language** every ad, script and plan
92
+ is written in), connectors (`list_connectors`, `list_connector_accounts`, `set_connector_accounts`,
93
+ `disconnect_connector`), and billing (`hermoso_credits`, `billing_status`, `buy_credits`, `upgrade_plan`,
94
+ `set_auto_reload`), plus `list_jobs` / `get_job` for async renders.
95
+
96
+ **Connector accounts are picked, not guessed.** One person often administers several Facebook Pages, Google Ads
97
+ customers or LinkedIn company Pages. Only the accounts ticked for a brand are usable — enforced server-side, and
98
+ an empty selection shares nothing. Linking a *new* account is the one step that is not headless (it is an OAuth
99
+ consent screen, so the user does it in the app).
100
+
101
+ Render jobs queue server-side and poll to completion, returning a served URL.
57
102
 
58
103
  ## 2. CLI — the token-cheap path for terminal agents
59
104
 
60
- `bin/hermoso.mjs` mirrors the tools as subprocess commands, so an agent can shell out instead of carrying a fat
61
- tool manifest.
105
+ `bin/hermoso.mjs` mirrors the core tools as subprocess commands, so an agent can shell out instead of carrying a
106
+ fat tool manifest.
62
107
 
63
108
  ```bash
64
- npm install -g . # installs `hermoso`
109
+ npm install -g hermoso # installs `hermoso`
65
110
  hermoso capabilities # valid model ids + costs (run first)
66
111
  hermoso create --brand "YourBrand" --product "your best-selling product" --format image
67
112
  hermoso generate image --prompt "…" --ref ./product.png --wait
@@ -88,8 +133,9 @@ Then invoke `/hermoso-ad-from-brand an ad for yourbrand.com — our hero product
88
133
  | Env | Meaning |
89
134
  | --- | --- |
90
135
  | `HERMOSO_API_BASE` | The Hermoso API origin (default `https://app.hermoso.ai` — set `http://localhost:3000` if you run the app yourself) |
91
- | `HERMOSO_TOKEN` | Bearer token — required against the hosted app (rolling out) |
136
+ | `HERMOSO_TOKEN` | Bearer agent key (`hmk_…`) — required against the hosted app |
92
137
  | `HERMOSO_PROFILE` | Brand-workspace id, for accounts with multiple brand profiles |
138
+ | `HERMOSO_OWNER` | Only for a brand **another account shared with you** (a team workspace): the owning account id. Set it together with `HERMOSO_PROFILE`, and set `HERMOSO_PROFILE` to that workspace's **profileUuid** — a brand's short slug is refused. Run `list_brands` (or `hermoso brands`) to print both values for every workspace you can enter. The server re-authorizes the pair on every request, so a wrong value is refused, never trusted. |
93
139
 
94
140
  `mcp/http.mjs` is the hosted remote-connector transport (paste-a-URL into Claude.ai → Connectors). It ships in
95
141
  this repo for transparency and refuses to mount without authenticated identity — no anonymous spend, ever.
package/bin/hermoso.mjs CHANGED
@@ -78,6 +78,7 @@ async function main() {
78
78
  process.env.HERMOSO_API_BASE = process.env.HERMOSO_API_BASE || cfg.apiBase || 'https://app.hermoso.ai';
79
79
  if (cfg.token && !process.env.HERMOSO_TOKEN) process.env.HERMOSO_TOKEN = cfg.token;
80
80
  if (cfg.profile && !process.env.HERMOSO_PROFILE) process.env.HERMOSO_PROFILE = cfg.profile;
81
+ if (cfg.owner && !process.env.HERMOSO_OWNER) process.env.HERMOSO_OWNER = cfg.owner; // shared team workspace: the owning account (server re-authorizes it)
81
82
 
82
83
  // `hermoso mcp` → run the stdio MCP server (Claude Code / Cursor / Codex spawn this, e.g. `npx -y hermoso mcp`).
83
84
  // It OWNS stdout as the JSON-RPC channel, so hand off immediately and print nothing to stdout here. The
package/mcp/client.mjs CHANGED
@@ -17,11 +17,26 @@ const TOKEN = process.env.HERMOSO_TOKEN || '';
17
17
  // header > key.keyProfileId > 'default' (adapters/auth/middleware.js), so always sending the header permanently
18
18
  // masks the brand `use_brand` saved against the key — connectors on any non-default brand then look disconnected.
19
19
  export const PROFILE = process.env.HERMOSO_PROFILE || '';
20
+ // SHARED TEAM WORKSPACE: the OWNING account. The web client sends this as x-hermoso-owner from PROFILE_OWNER
21
+ // (public/app.js ctxHeaders) whenever the active brand belongs to someone else's account; the MCP twins never did,
22
+ // so a member driving Hermoso headlessly resolved every brand-scoped read against their OWN empty account —
23
+ // resolveWs's `if (owner && owner !== own)` branch simply never ran and it fell through to the own-account path.
24
+ // Symptom (live 2026-07-31): 0 connectors over MCP on a workspace showing 10 in the browser, with NO error.
25
+ // SAFE TO SEND: the server RE-AUTHORIZES it against profile_members on every request (adapters/auth/middleware.js
26
+ // resolveWs), so a forged or stale value is 403'd, never trusted. Like the profile header it must stay UNSET by
27
+ // default — sending an owner for your own account would make resolveWs take the shared branch against yourself.
28
+ // PAIR IT WITH THE PROFILE UUID, not the slug: profile_members keys on profiles.id, so a client_slug is the one
29
+ // thing isMember() cannot match and it 403s. list_brands names both values for every workspace you can enter.
30
+ export const OWNER = process.env.HERMOSO_OWNER || '';
31
+ // The env-var prefix THIS build reads. tools.mjs is byte-identical across the two twins, so it cannot
32
+ // hardcode either name when it tells a user which variables to set — it asks its own client.
33
+ export const ENV_PREFIX = 'HERMOSO';
20
34
 
21
35
  function headers(extra = {}) {
22
36
  const ctx = mcpCtx.getStore();
23
37
  const prof = ctx?.profile || PROFILE; // omitted when unpinned so the key's saved brand wins server-side
24
- const h = { 'Content-Type': 'application/json', ...(prof ? { 'x-hermoso-user': prof } : {}), ...extra };
38
+ const own = ctx?.owner || OWNER; // the wire name is x-hermoso-owner on BOTH twins — it is the server's header, not a brand
39
+ const h = { 'Content-Type': 'application/json', ...(prof ? { 'x-hermoso-user': prof } : {}), ...(own ? { 'x-hermoso-owner': own } : {}), ...extra };
25
40
  const tok = ctx?.token || TOKEN;
26
41
  if (tok) h.Authorization = `Bearer ${tok}`;
27
42
  return h;
@@ -131,4 +146,4 @@ export async function toRef(srcOrPath) {
131
146
  return `data:${mime};base64,${buf.toString('base64')}`;
132
147
  }
133
148
 
134
- export const authState = () => ({ apiBase: API_BASE, hasToken: !!TOKEN, profile: PROFILE });
149
+ export const authState = () => ({ apiBase: API_BASE, hasToken: !!TOKEN, profile: PROFILE, owner: OWNER });