hermoso 0.1.196 → 0.1.197

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
@@ -116,7 +116,7 @@ the routes.
116
116
 
117
117
  ## Instant: the hosted Claude.ai connector
118
118
 
119
- Paste **`https://app.hermoso.ai/mcp`** into Claude → Settings → Connectors → *Add custom connector*, pick
119
+ Paste **`https://app.hermoso.ai/mcp?src=readme`** into Claude → Settings → Connectors → *Add custom connector*, pick
120
120
  **Always required** when Claude asks about authentication (its detector suggests "None" because our discovery
121
121
  handshake is open; "None" would leave every tool call unauthenticated), approve with your Hermoso account, done — the full toolset with your saved brand context, billed to your plan.
122
122
 
@@ -143,7 +143,7 @@ claude mcp add hermoso -e HERMOSO_TOKEN=hmk_… -- npx -y hermoso mcp
143
143
  ```
144
144
 
145
145
  The hosted URL works in Claude Code too, but it is the worse path there and it is worth knowing why:
146
- `claude mcp add --transport http hermoso https://app.hermoso.ai/mcp` is accepted, and then `claude mcp list`
146
+ `claude mcp add --transport http hermoso "https://app.hermoso.ai/mcp?src=readme"` is accepted, and then `claude mcp list`
147
147
  reports `! Needs authentication` because the client will not start the OAuth flow by itself — you have to open a
148
148
  session, run `/mcp`, find the server and press Authenticate. Measured against Claude Code 2.1.241 on 2026-08-23.
149
149
 
package/mcp/tools.mjs CHANGED
@@ -2135,8 +2135,8 @@ const makeEnableToolsHandler = (ctx) => async ({ groups }) => {
2135
2135
  const held = heldBack
2136
2136
  ? ` ${heldBack} more tool${heldBack === 1 ? ' is' : 's are'} built and ready but not listed because their account is not connected in this workspace yet — Hermoso supports them all; connect the account under Workspace ▸ Connectors (https://app.hermoso.ai/?connect=<provider>, or list_connectors to see what is linked) and they appear.`
2137
2137
  : '';
2138
- const route = 'reconnect with `?tools=all` on the server URL, or run the `hermoso` CLI, which reaches every'
2139
- + ' tool with no roster at all.';
2138
+ const route = 'call find_tools to locate the tool and call_tool to run it by name — that needs no reload and works on every host; or reconnect'
2139
+ + ' with `?tools=all` on the server URL, or run the `hermoso` CLI, which reaches every tool with no roster at all.';
2140
2140
  // A CACHED CLIENT AND AN UNCONNECTED ACCOUNT ARE DIFFERENT DIAGNOSES, AND ONLY ONE OF THEM IS EVER TRUE HERE
2141
2141
  // (2026-08-26). The two branches below tell an agent that if the tools do not appear, its client cached the
2142
2142
  // roster — correct when we really did enable something. When nothing was enabled BECAUSE nothing is connected,
@@ -2148,7 +2148,7 @@ const makeEnableToolsHandler = (ctx) => async ({ groups }) => {
2148
2148
  : (n === 0 && heldBack
2149
2149
  ? `Switched on ${added.join(', ')} server-side, but nothing new is listed:${held} Active groups: ${enabled.join(', ')}.`
2150
2150
  : fixedRoster
2151
- ? `Switched on ${added.join(', ')} server-side — but THIS host fixed its tool list when the connection was made and will not pick up the ${n} new tool${n === 1 ? '' : 's'} until it reconnects, so do not expect to see them in this conversation. To use them, ${route}${held} Active groups: ${enabled.join(', ')}.`
2151
+ ? `Switched on ${added.join(', ')} server-side — but THIS host fixed its tool list when the connection was made and will not pick up the ${n} new tool${n === 1 ? '' : 's'} until it reconnects. USE THEM NOW ANYWAY: find_tools({query}) finds the tool and call_tool({name, args}) runs it through this same connection — no reconnect needed. Do not expect to see them in this conversation's list. To use them, ${route}${held} Active groups: ${enabled.join(', ')}.`
2152
2152
  : `Switched on ${added.join(', ')} — ${n} more tool${n === 1 ? '' : 's'} are callable now. If they do not appear your client has cached its tool list, in which case ${route}${held} Active groups: ${enabled.join(', ')}.`);
2153
2153
  // The agent took the route our own instructions name, on a host where it provably cannot show anything. That is
2154
2154
  // our guidance failing, not the agent, so it is recorded on our side of the board.
@@ -10932,7 +10932,7 @@ function buildTools(rawServer, opts = {}, sink = null) {
10932
10932
  inputSchema: {}, annotations: { readOnlyHint: true, destructiveHint: false, openWorldHint: true },
10933
10933
  }, wrap(async () => oaiNote(await apiGet('/api/openai-ads/spend-windows'))));
10934
10934
  server.registerTool('set_openai_ads_spend_window', {
10935
- title: 'Create or edit a ChatGPT Ads spend-limit window', description: 'Create (no windowId) or edit (windowId) an account-level spend-limit window: startDate YYYY-MM-DD inclusive, endDate YYYY-MM-DD EXCLUSIVE, amount in the account currency, optional name and insertion-order id. A window is a CEILING; raising it lets campaigns spend up to their budgets, so show the user old and new amounts.',
10935
+ 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). MEASURED 2026-09-03: OpenAI\'s live API answered "Invalid URL" for this path although it is in their published spec; the tool reports that honestly until they ship it.',
10936
10936
  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() },
10937
10937
  annotations: { readOnlyHint: false, destructiveHint: false, openWorldHint: true },
10938
10938
  }, wrap(async (a) => oaiNote(await apiPost('/api/openai-ads/spend-window', a))));
@@ -10970,7 +10970,7 @@ function buildTools(rawServer, opts = {}, sink = null) {
10970
10970
  inputSchema: { feedId: z.string() }, annotations: { readOnlyHint: true, destructiveHint: false, openWorldHint: true },
10971
10971
  }, wrap(async (a) => oaiNote(await apiGet('/api/openai-ads/feed/sftp', a))));
10972
10972
  server.registerTool('set_openai_ads_feed_sftp', {
10973
- title: 'Set up, activate or pause feed SFTP delivery', description: 'action "create" (default): create or REPLACE the SFTP credentials for a product feed — authenticationMethod "password" (the password is returned ONCE) or "ssh_key" (+ sshPublicKey); replacing invalidates the previous credentials. action "activate" / "pause": switch delivery on or off.',
10973
+ title: 'Set up, activate or pause feed SFTP delivery', description: 'Manage SFTP bulk delivery for a ChatGPT Ads product feed (the alternative to pushing products through update_openai_ads_feed_products). action "create" (default): create or REPLACE the feed\'s SFTP credentials — authenticationMethod "password" (OpenAI returns the password ONCE in this reply; store it) or "ssh_key" (pass the public key as sshPublicKey). Replacing credentials invalidates the previous ones immediately, so re-run only when the user means to rotate. action "activate" / "pause": switch delivery on or off without touching credentials. The reply carries the connection URI and whether delivery is enabled; get_openai_ads_feed_sftp reads the same state without changing it. Files delivered over SFTP show up in list_openai_ads_feed_uploads with accepted / rejected / ads-eligible row counts.',
10974
10974
  inputSchema: { feedId: z.string(), action: z.enum(['create', 'activate', 'pause']).optional(), authenticationMethod: z.enum(['password', 'ssh_key']).optional(), sshPublicKey: z.string().optional() },
10975
10975
  annotations: { readOnlyHint: false, destructiveHint: false, openWorldHint: true },
10976
10976
  }, wrap(async (a) => oaiNote(await apiPost('/api/openai-ads/feed/sftp', a))));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hermoso",
3
- "version": "0.1.196",
3
+ "version": "0.1.197",
4
4
  "mcpName": "io.github.hermoso-ai/hermoso",
5
5
  "description": "AI ad studio and marketing MCP server with 768 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",