hermoso 0.1.461 → 0.1.463
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/client.mjs +4 -1
- package/mcp/openai-extensions.mjs +1 -1
- package/mcp/roster-scope.mjs +16 -1
- package/mcp/tools.mjs +685 -94
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -5,7 +5,7 @@ scripts. Research the ads already winning in a market, generate image & video ad
|
|
|
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
|
+
**1090 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
|
**No markup on models.** Stop overpaying markups on AI models. Plans: [hermoso.ai/pricing](https://hermoso.ai/pricing).
|
|
@@ -242,7 +242,7 @@ block entirely if you signed in above; it is there for CI, where the process can
|
|
|
242
242
|
|
|
243
243
|
Then ask your agent: *“Generate an image ad with Hermoso.”*
|
|
244
244
|
|
|
245
|
-
### What the
|
|
245
|
+
### What the 1090 tools cover
|
|
246
246
|
|
|
247
247
|
**Ad spy / research** — `find_competitors`, `competitor_teardown`, `pull_competitor_ads`, `research_ads`; the
|
|
248
248
|
Meta / Google / LinkedIn ad libraries (`search_meta_ads`, `search_google_ads`, `search_linkedin_ads`); organic
|
package/mcp/client.mjs
CHANGED
|
@@ -423,7 +423,10 @@ export async function connectedProviders() {
|
|
|
423
423
|
const connectLink = typeof r?.connectLink === 'string' && r.connectLink.includes('{provider}') ? r.connectLink : '';
|
|
424
424
|
// withheld: tools the server does not offer right now (roster-scope.mjs toolWithheldByServer). Absent = none.
|
|
425
425
|
const withheld = new Set((Array.isArray(r?.withheld) ? r.withheld : []).filter((n) => typeof n === 'string' && n));
|
|
426
|
-
|
|
426
|
+
// shopifyStore (2026-10-27): true/false when the server could tell whether this account has a Shopify store;
|
|
427
|
+
// anything else is null = unknown, which holds nothing back (roster-scope.mjs toolHeldForNoShopifyStore).
|
|
428
|
+
const shopifyStore = r?.shopifyStore === true ? true : r?.shopifyStore === false ? false : null;
|
|
429
|
+
return { connected: new Set(list.filter((p) => typeof p === 'string' && p)), readOk: true, offered, gated, connectLink, withheld, shopifyStore };
|
|
427
430
|
} catch { return { connected: new Set(), readOk: false }; }
|
|
428
431
|
}
|
|
429
432
|
// Upload raw file BYTES to /api/upload (150MB, persists → returns {url,kind,bytes}). Overrides the JSON content-type so
|
|
@@ -1274,7 +1274,7 @@ const PROVIDER_INFO = {
|
|
|
1274
1274
|
openai_ads: ['ChatGPT Ads', 'ads'], apple_ads: ['Apple Ads', 'ads'], applovin_ads: ['AppLovin Ads', 'ads'],
|
|
1275
1275
|
google_drive: ['Google Drive, Sheets and Docs', 'data'], onedrive: ['OneDrive', 'data'], google_analytics: ['Google Analytics', 'data'],
|
|
1276
1276
|
search_console: ['Google Search Console', 'data'], merchant_center: ['Google Merchant Center', 'data'], slack: ['Slack', 'data'],
|
|
1277
|
-
shopify: ['Shopify', 'data'], stripe: ['Stripe', 'data'], hubspot: ['HubSpot', 'data'], notion: ['Notion', 'data'],
|
|
1277
|
+
shopify: ['Shopify', 'data'], stripe: ['Stripe', 'data'], hubspot: ['HubSpot', 'data'], notion: ['Notion', 'data'], canva: ['Canva', 'data'],
|
|
1278
1278
|
};
|
|
1279
1279
|
export const providerInfo = (p) => {
|
|
1280
1280
|
const hit = PROVIDER_INFO[p];
|
package/mcp/roster-scope.mjs
CHANGED
|
@@ -101,6 +101,8 @@ export const TOOL_PROVIDER_RULES = [
|
|
|
101
101
|
[/vercel/, 'vercel'],
|
|
102
102
|
// Notion (2026-10-25): the user's OWN Notion workspace, offered only to a workspace that connected it.
|
|
103
103
|
[/notion/, 'notion'],
|
|
104
|
+
// Canva (2026-10-27): the user's OWN Canva, offered only to a workspace that connected it.
|
|
105
|
+
[/canva/, 'canva'],
|
|
104
106
|
// ── posting-only channels ──
|
|
105
107
|
[/youtube/, 'youtube'],
|
|
106
108
|
[/google_business|business_location|^list_business_(categories|attributes)$|^business_google_updated$/, 'google_business'],
|
|
@@ -170,7 +172,20 @@ export const metaAlternativeNote = (name) => INSTAGRAM_LOGIN_TOOLS.has(String(na
|
|
|
170
172
|
// creator marketplace trio while META_CREATOR_MARKETPLACE_SCOPE is off). ONE server flag decides it, so flipping the
|
|
171
173
|
// env var lights every surface back up with no code change. Absent or unreadable = nothing withheld (fail open, like
|
|
172
174
|
// every other read of this set); a withheld tool is held like a connector-gated one and answered as 'unavailable'.
|
|
173
|
-
|
|
175
|
+
// ── SHOPIFY IS AN ACCOUNT TYPE, NOT A CONNECTOR (2026-10-27) ────────────────────────────────────────────────────────
|
|
176
|
+
// The Shopify tools (list_shopify_products, publish_to_shopify_product, the marketing-activity tools) work only for an
|
|
177
|
+
// account created by installing Hermoso from the Shopify App Store: the store is derived server-side from the verified
|
|
178
|
+
// account and there is no "connect Shopify" tile. So they cannot ride TOOL_PROVIDER_RULES — every provider there must
|
|
179
|
+
// be a CONNECTOR_INFO key (property 3), and "connect it under Settings ▸ Connectors" would be a dead end. Instead the
|
|
180
|
+
// same /api/connectors/providers read answers `shopifyStore`: true (this account has a live store), false (it does not,
|
|
181
|
+
// or Shopify is off on this deployment), or absent when the server could not tell. ONLY a definite false holds them
|
|
182
|
+
// back — an unreadable answer keeps them, like every other read of this set (property 1). Held, they are answered by
|
|
183
|
+
// their own reason ('shopify_store'), never as "not available in Hermoso yet".
|
|
184
|
+
export const SHOPIFY_STORE_TOOL_RE = /shopify/;
|
|
185
|
+
export const toolNeedsShopifyStore = (name) => SHOPIFY_STORE_TOOL_RE.test(String(name || ''));
|
|
186
|
+
export function toolHeldForNoShopifyStore(name, conn) { return !!(conn && conn.shopifyStore === false && toolNeedsShopifyStore(name)); }
|
|
187
|
+
export const SHOPIFY_STORE_NOTE = 'works only for a Shopify store that installed Hermoso from the Shopify App Store, and this account has no linked store. An existing Hermoso account cannot be linked to a store from here; a merchant who did install it opens Hermoso once from their Shopify admin (Apps \u2192 Hermoso).';
|
|
188
|
+
export function toolWithheldByServer(name, conn) { return !!(conn && ((conn.withheld instanceof Set && conn.withheld.has(String(name || ''))) || toolHeldForNoShopifyStore(name, conn))); }
|
|
174
189
|
export function toolHeldBackByConnectors(name, conn) {
|
|
175
190
|
if (toolWithheldByServer(name, conn)) return true; // the server says this capability is not offered now
|
|
176
191
|
if (!conn || !conn.readOk) return false; // property 1 — fail OPEN on an unreadable store
|