hermoso 0.1.190 → 0.1.192
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/tools.mjs +42 -0
- package/package.json +1 -1
package/mcp/tools.mjs
CHANGED
|
@@ -1812,6 +1812,8 @@ export function parseToolScope(raw) {
|
|
|
1812
1812
|
// everything on 2026-08-16, so a caller who genuinely wants all 436 tools needs a way to say so that is not
|
|
1813
1813
|
// "list every group by name and hope none was added since".
|
|
1814
1814
|
if (asked.includes('all')) return { groups: [...TOOL_GROUP_NAMES] };
|
|
1815
|
+
if (asked.includes('directory-full')) return { groups: [...DEFAULT_TOOL_GROUPS], directory: 'full' }; // the submitted listing: everything but the billing tools — see DIRECTORY_BILLING_TOOLS
|
|
1816
|
+
if (asked.includes('directory')) return { groups: [...DIRECTORY_GROUPS], directory: 'scoped' }; // the fully scoped fallback — see DIRECTORY_GROUPS
|
|
1815
1817
|
const unknown = asked.filter((v) => !TOOL_GROUP_NAMES.includes(v));
|
|
1816
1818
|
if (unknown.length) {
|
|
1817
1819
|
// No tool COUNT in this message: a committed count goes stale (four different wrong numbers shipped at once
|
|
@@ -1825,6 +1827,39 @@ export function parseToolScope(raw) {
|
|
|
1825
1827
|
// a HOST rule, not a capability we removed: every other surface still offers both.
|
|
1826
1828
|
export const WITHHELD_FROM_WIDGET_HOSTS = new Set(['buy_credits', 'upgrade_plan', 'set_auto_reload']);
|
|
1827
1829
|
|
|
1830
|
+
// ── THE DIRECTORY ROSTER (2026-09-02) — `?tools=directory` ─────────────────────────────────────────────────────
|
|
1831
|
+
// Anthropic's Software Directory Policy prohibits "software that uses AI models to generate images, video, or
|
|
1832
|
+
// audio content" (design aids excepted) and software that "executes financial transactions on behalf of users".
|
|
1833
|
+
// Hermoso as a whole does both, so the Claude Connectors Directory listing is a SCOPED server: the `create` group
|
|
1834
|
+
// is out, every tool that calls a generative image/video/audio model is out wherever it lives, and the three tools
|
|
1835
|
+
// that move money are out (the same three ChatGPT is denied). Dave, 2026-09-02: "We're way more than ad generation,
|
|
1836
|
+
// we can focus on all the other huge benefits like our organic posting, ads management, analytics, dms, etc."
|
|
1837
|
+
// It is a CAGE, deliberately, unlike every other scope: enable_tools may not widen it into `create` or `all`,
|
|
1838
|
+
// because the listing's compliance acknowledgments are only honest if no path on the connection reaches a
|
|
1839
|
+
// generative model. Pinned by tools/directory-roster-check.mjs.
|
|
1840
|
+
export const DIRECTORY_GROUPS = ['research', 'channels', 'channel_admin', 'ads', 'analytics', 'files', 'workspace'];
|
|
1841
|
+
export const DIRECTORY_BANNED_GROUPS = ['create'];
|
|
1842
|
+
// Generative-media tools that live OUTSIDE `create` (post-production and variation lanes) plus the money movers.
|
|
1843
|
+
// Kept as an explicit set rather than a regex so a new generative tool has to be named here — a regex that
|
|
1844
|
+
// happened to miss it would list it in the directory.
|
|
1845
|
+
export const WITHHELD_FROM_DIRECTORY = new Set([
|
|
1846
|
+
'buy_credits', 'upgrade_plan', 'set_auto_reload',
|
|
1847
|
+
'generate_image', 'generate_video', 'generate_avatar', 'generate_voice', 'render_ad', 'plan_ad', 'plan_variations',
|
|
1848
|
+
'make_thumbnail', 'make_explainer', 'product_sizzle', 'dub_video', 'change_voice', 'edit_video', 'recast_motion',
|
|
1849
|
+
'upscale_video', 'reframe_video', 'multiply_ad', 'remix_static', 'stitch_video', 'fix_beat',
|
|
1850
|
+
]);
|
|
1851
|
+
// TWO DIRECTORY MODES (Dave, 2026-09-02, after the directory turned out to list Tofu Ads — an AI ad-image generator
|
|
1852
|
+
// — under the policy's design-asset carve-out): `directory` is the fully scoped cage above; `directory-full` keeps
|
|
1853
|
+
// generation (an ad-creation workflow with the brand's own product and copy, the carve-out's shape) and withholds
|
|
1854
|
+
// ONLY the three tools that move money, which is the listing that was actually submitted. Both are cages for what
|
|
1855
|
+
// they withhold: enable_tools never re-lists a billing tool on either.
|
|
1856
|
+
export const DIRECTORY_BILLING_TOOLS = new Set(['buy_credits', 'upgrade_plan', 'set_auto_reload']);
|
|
1857
|
+
export const toolHeldBackByDirectory = (name, group, ctx) => {
|
|
1858
|
+
if (!ctx?.directory) return false;
|
|
1859
|
+
if (ctx.directory === 'full') return DIRECTORY_BILLING_TOOLS.has(name);
|
|
1860
|
+
return WITHHELD_FROM_DIRECTORY.has(name) || DIRECTORY_BANNED_GROUPS.includes(group);
|
|
1861
|
+
};
|
|
1862
|
+
|
|
1828
1863
|
// ── A REFERENCE LOOKUP MUST NOT BE AN APP SURFACE (2026-08-24) ─────────────────────────────────────────────────
|
|
1829
1864
|
// MEASURED IN CHATGPT, not theorised: "make a 4 second vertical video ad for a coffee roaster" produced a
|
|
1830
1865
|
// full-height scrolling MODEL CATALOG card ahead of the ad, and the catalog is ~70 rows, so the thing the user
|
|
@@ -1969,6 +2004,10 @@ const makeEnableToolsHandler = (ctx) => async ({ groups }) => {
|
|
|
1969
2004
|
const want = (Array.isArray(groups) ? groups : []).map((g) => String(g || '').trim().toLowerCase()).filter(Boolean);
|
|
1970
2005
|
if (!want.length) return { content: [{ type: 'text', text: "Name at least one group to turn on, e.g. groups:['ads']." }], isError: true };
|
|
1971
2006
|
const expand = want.includes('all') ? [...TOOL_GROUP_NAMES] : want;
|
|
2007
|
+
// THE DIRECTORY CAGE (2026-09-02): a connection scoped for the Claude directory may not be widened into the
|
|
2008
|
+
// generative group, by name or through 'all' — see DIRECTORY_GROUPS. The way to those tools is the app or the
|
|
2009
|
+
// unscoped server URL, and the refusal says so instead of silently trimming the ask.
|
|
2010
|
+
if (ctx.directory === 'scoped' && (want.includes('all') || expand.some((g) => DIRECTORY_BANNED_GROUPS.includes(g)))) return { content: [{ type: 'text', text: `This connection is the Claude directory listing, which deliberately carries no image, video or audio generation tools and no billing tools. Those live in the Hermoso app (https://app.hermoso.ai) and on the unscoped server URL https://app.hermoso.ai/mcp — not on this connection. Groups you can turn on here: ${DIRECTORY_GROUPS.join(', ')}.` }], isError: true };
|
|
1972
2011
|
const unknown = expand.filter((g) => !TOOL_GROUP_NAMES.includes(g));
|
|
1973
2012
|
// REFUSED BY NAME, never dropped: silently ignoring a typo would report success and leave the agent looking
|
|
1974
2013
|
// for a tool that is still disabled — the failure this whole mechanism exists to avoid.
|
|
@@ -1989,6 +2028,7 @@ const makeEnableToolsHandler = (ctx) => async ({ groups }) => {
|
|
|
1989
2028
|
// says how many and why, because a group that turns on "8 tools" when the agent expected 240 with no
|
|
1990
2029
|
// explanation is the [[prompt-rosters-go-stale]] failure — the agent concludes the capability is missing.
|
|
1991
2030
|
if (toolHeldBackByConnectors(name, ctx.conn)) { heldBack++; continue; }
|
|
2031
|
+
if (toolHeldBackByDirectory(name, grp, ctx)) continue; // the cage holds across a group flip too
|
|
1992
2032
|
try { h.enable(); n++; } catch {}
|
|
1993
2033
|
}
|
|
1994
2034
|
}
|
|
@@ -2048,6 +2088,7 @@ function newToolScope(opts) {
|
|
|
2048
2088
|
enabledGroups: asked, groupOf: Object.create(null), handleOf: Object.create(null),
|
|
2049
2089
|
conn: opts.connectors || null,
|
|
2050
2090
|
widgetHost: !!opts.widgetHost,
|
|
2091
|
+
directory: opts.directory === 'full' ? 'full' : (opts.directory ? 'scoped' : false),
|
|
2051
2092
|
};
|
|
2052
2093
|
}
|
|
2053
2094
|
// THE THREE REASONS A REGISTERED TOOL IS HELD BACK. Applied identically on the build path, the replay path and
|
|
@@ -2067,6 +2108,7 @@ function applyToolGates(h, name, group, ctx, opts) {
|
|
|
2067
2108
|
if (!ctx.enabledGroups.has(group)) { try { h.disable(); } catch {} }
|
|
2068
2109
|
if (WITHHELD_FROM_WIDGET_HOSTS.has(name) && opts.widgetHost) { try { h.disable(); } catch {} }
|
|
2069
2110
|
if (toolHeldBackByConnectors(name, ctx.conn)) { try { h.disable(); } catch {} }
|
|
2111
|
+
if (toolHeldBackByDirectory(name, group, ctx)) { try { h.disable(); } catch {} } // (4) the directory cage — see DIRECTORY_GROUPS
|
|
2070
2112
|
}
|
|
2071
2113
|
|
|
2072
2114
|
// Replay the cached canon onto a fresh server. This is the whole hot path for every session after the first.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "hermoso",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.192",
|
|
4
4
|
"mcpName": "io.github.hermoso-ai/hermoso",
|
|
5
5
|
"description": "AI ad studio and marketing MCP server with 746 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",
|