@facelessad/mcp 2.1.1 → 2.3.0
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 +16 -1
- package/SKILL.md +44 -6
- package/index.js +18 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -34,7 +34,7 @@ parameters at all.
|
|
|
34
34
|
|
|
35
35
|
There is also a hosted version that needs no install: add
|
|
36
36
|
`https://facelessad.com/mcp` as a custom connector in Claude and authorise it
|
|
37
|
-
with your account. Same
|
|
37
|
+
with your account. Same thirteen tools, same fields.
|
|
38
38
|
|
|
39
39
|
## Tools
|
|
40
40
|
|
|
@@ -61,6 +61,21 @@ for the text layer drawn over the product video.
|
|
|
61
61
|
Videos build in the background (3–10 min); the assistant polls
|
|
62
62
|
`facelessad_get_video`. You are only charged for steps that succeed.
|
|
63
63
|
|
|
64
|
+
## 2.3.0 — visual_direction_refine
|
|
65
|
+
|
|
66
|
+
`facelessad_create_video` takes `visual_direction_refine` on animated-ad and
|
|
67
|
+
music-video: a preset id (`metaphoric`, `realistic`, `dramatic`, `calm`,
|
|
68
|
+
`playful`, `dark`, `bright`, `magical` — `facelessad_list_tools` returns them
|
|
69
|
+
under `visual_direction_refine.presets`) or your own instruction up to 300
|
|
70
|
+
characters. It reshapes how the visual story is told, not how it looks, and is
|
|
71
|
+
applied on top of `visual_direction` if given, otherwise on the generated one.
|
|
72
|
+
Free of charge; an error on other tools, never ignored.
|
|
73
|
+
|
|
74
|
+
`layout` (same two tools) picks the card layout — `A`, `HA`, `AC`, `HAC`,
|
|
75
|
+
`AIA`, `HAIA`, `AIAC`, `HAIAC`. Hook and CTA cards you chose always stay; the
|
|
76
|
+
data card is dropped when the video is too short, and the response says so
|
|
77
|
+
(`layout.applied`, `layout.notice`). `facelessad_list_tools` lists them.
|
|
78
|
+
|
|
64
79
|
## 1.2.0 — text files as materials + agent skill
|
|
65
80
|
|
|
66
81
|
The package now ships `SKILL.md` — the same agent skill as @facelessad/cli —
|
package/SKILL.md
CHANGED
|
@@ -29,10 +29,12 @@ Keys are created at https://facelessad.com/developers. There is also a hosted
|
|
|
29
29
|
server that needs no install and no key: add `https://facelessad.com/mcp` as
|
|
30
30
|
a custom connector and authorise it. Same thirteen tools, same fields.
|
|
31
31
|
|
|
32
|
-
**The API needs a paid plan
|
|
33
|
-
|
|
34
|
-
`api_access_required`, the
|
|
35
|
-
|
|
32
|
+
**The API needs a paid plan or an active trial.** The $15 trial (300
|
|
33
|
+
credits, 7 days) opens API keys and the hosted connector for its duration.
|
|
34
|
+
If a call answers `api_access_required`, the subscription has ended or the
|
|
35
|
+
trial has expired or run out of credits — that is a billing state, not a
|
|
36
|
+
broken key, so tell the user to subscribe (or start the trial) rather than
|
|
37
|
+
retrying.
|
|
36
38
|
|
|
37
39
|
## Test mode — build the integration before spending anything
|
|
38
40
|
|
|
@@ -65,7 +67,7 @@ and aspect ratios. Every id you send is validated against that same list, and
|
|
|
65
67
|
an unknown one is rejected with a message naming the valid set — never
|
|
66
68
|
silently swapped.
|
|
67
69
|
|
|
68
|
-
## The
|
|
70
|
+
## The thirteen tools
|
|
69
71
|
|
|
70
72
|
| tool | what it does |
|
|
71
73
|
|---|---|
|
|
@@ -148,6 +150,8 @@ out is chosen for you** from the materials.
|
|
|
148
150
|
| `voice_over` | force narration on or off where the tool allows a choice |
|
|
149
151
|
| `voiceover_text` | your own narration, word for word (see below) |
|
|
150
152
|
| `visual_direction` | how it should look and what happens, max 600. Animated Ad, Character, Music Video, Product Showcase |
|
|
153
|
+
| `visual_direction_refine` | how the story is TOLD, not how it looks: a preset id or your own instruction, max 300. Animated Ad, Music Video. Free |
|
|
154
|
+
| `layout` | card layout: A, HA, AC, HAC, AIA, HAIA, AIAC, HAIAC — hook/CTA stay, data card drops if too short. Animated Ad, Music Video |
|
|
151
155
|
| `hook_text` | your own first-frame card text, max 120 |
|
|
152
156
|
| `cta` | the call to action it ends on |
|
|
153
157
|
| `brand_name` / `brand_color` | brand as said and shown; colour as six-digit hex |
|
|
@@ -172,6 +176,39 @@ or video-banner.
|
|
|
172
176
|
`visual_direction` steers the storyboard and `hook_text` the first frame.
|
|
173
177
|
Neither replaces the style: the look still comes from `style`/`custom_style`.
|
|
174
178
|
|
|
179
|
+
## Refining the visual story (optional)
|
|
180
|
+
|
|
181
|
+
`visual_direction_refine` reshapes **how the visual story is told** — its
|
|
182
|
+
narrative device, scale, tempo, mood, what the product's effect leads to — not
|
|
183
|
+
how it looks. It is applied on top of `visual_direction` if you gave one, and
|
|
184
|
+
otherwise on the visual direction the script writer generates, so you do not
|
|
185
|
+
need to author one yourself: an intent is enough.
|
|
186
|
+
|
|
187
|
+
Two forms, no guessing between them:
|
|
188
|
+
- a preset id — `metaphoric`, `realistic`, `dramatic`, `calm`, `playful`,
|
|
189
|
+
`dark`, `bright`, `magical` (`facelessad_list_tools` returns them under
|
|
190
|
+
`visual_direction_refine.presets` with one-line descriptions)
|
|
191
|
+
- any other text (max 300 characters) — your own instruction, used as written
|
|
192
|
+
|
|
193
|
+
Animated Ad and Music Video only; on other tools it is an error, not ignored.
|
|
194
|
+
Free of charge. Art-style words (palette, 3D, watercolor…) belong in
|
|
195
|
+
`custom_style`, not here. If the refine call fails, the video is still built
|
|
196
|
+
with the unrefined visual direction.
|
|
197
|
+
|
|
198
|
+
## Card layout (optional)
|
|
199
|
+
|
|
200
|
+
`layout` picks the card layout on Animated Ad and Music Video: `A` (scenes
|
|
201
|
+
only), `HA` (hook card + scenes), `AC` (scenes + CTA card), `HAC`, `AIA`
|
|
202
|
+
(scenes + data card + scenes), `HAIA`, `AIAC`, `HAIAC`. `facelessad_list_tools`
|
|
203
|
+
lists them under `layouts` with a name and, per duration, what each becomes.
|
|
204
|
+
|
|
205
|
+
Length is never an error. The hook and CTA cards you chose always stay; the
|
|
206
|
+
data card is dropped when the video is too short for it — `HAIAC` at 30 s
|
|
207
|
+
becomes `HAC`, `AIA` at 15 s becomes `A`. The create response reports
|
|
208
|
+
`layout.applied` and a `layout.notice` when it was reduced. Omit `layout` to
|
|
209
|
+
let the ad structure decide (its own layout, fitted to the duration the same
|
|
210
|
+
way).
|
|
211
|
+
|
|
175
212
|
## Captions
|
|
176
213
|
|
|
177
214
|
On by default, outline style, no dark box over the visuals.
|
|
@@ -275,9 +312,10 @@ Every tool returns the API's own JSON; on failure `{ok:false, error, code}`.
|
|
|
275
312
|
|
|
276
313
|
**Access and capacity**
|
|
277
314
|
|
|
278
|
-
- `api_access_required` — no active paid plan.
|
|
315
|
+
- `api_access_required` — no active paid plan or live trial. An active $15 trial opens the API for its 7 days.
|
|
279
316
|
- `insufficient_credits` — the body carries the estimate and the balance; nothing was spent.
|
|
280
317
|
- `too_many_active` — the plan's queue is full (Starter 10, Growth 25, Scale 50; app and API share it). Wait for a build to finish, then retry.
|
|
318
|
+
- `test_quota_exceeded` — 100 test runs per day per account (`"test": true` only). Real runs are unaffected; the quota resets at 00:00 UTC and the body carries `retryAfter`.
|
|
281
319
|
- `rate_limited` — over 300 calls this hour (`facelessad_estimate` has its own 600). `retryAfter` gives the seconds until the counter resets on the hour.
|
|
282
320
|
- `unauthorized` — the key is unknown or revoked.
|
|
283
321
|
|
package/index.js
CHANGED
|
@@ -78,12 +78,13 @@ function qs(q) {
|
|
|
78
78
|
return [...q.keys()].length ? '?' + q.toString() : '';
|
|
79
79
|
}
|
|
80
80
|
|
|
81
|
-
const server = new McpServer({ name: 'facelessad', version: '2.
|
|
81
|
+
const server = new McpServer({ name: 'facelessad', version: '2.3.0' });
|
|
82
82
|
|
|
83
83
|
server.tool(
|
|
84
84
|
'facelessad_list_tools',
|
|
85
85
|
'The FacelessAd registry: available video tools with their styles, ad structures, hook formulas, duration bounds and aspect ratios. Call this FIRST to learn valid ids — style/structure/hook ids passed to other tools are validated against this list. Styles flagged genBg:true generate an AI photo background behind every part (+6 credits per part in the estimate); their finished videos expose bg-N parts for background regeneration.',
|
|
86
86
|
{},
|
|
87
|
+
{ title: 'List video tools', readOnlyHint: true },
|
|
87
88
|
async () => result(await api('GET', '/api/v1/tools'))
|
|
88
89
|
);
|
|
89
90
|
|
|
@@ -91,6 +92,7 @@ server.tool(
|
|
|
91
92
|
'facelessad_balance',
|
|
92
93
|
'The account plan and current credit balance.',
|
|
93
94
|
{},
|
|
95
|
+
{ title: 'Credit balance', readOnlyHint: true },
|
|
94
96
|
async () => result(await api('GET', '/api/v1/balance'))
|
|
95
97
|
);
|
|
96
98
|
|
|
@@ -98,6 +100,7 @@ server.tool(
|
|
|
98
100
|
'facelessad_list_brand_kits',
|
|
99
101
|
'The brands on this account: id, name, whether it is the default, how many of the brand questions are answered (filled / questions), and whether it carries a colour and a logo. Call this before naming brand_kit_id — ids are per account and cannot be guessed.',
|
|
100
102
|
{},
|
|
103
|
+
{ title: 'List brand kits', readOnlyHint: true },
|
|
101
104
|
async () => result(await api('GET', '/api/v1/brand-kits'))
|
|
102
105
|
);
|
|
103
106
|
|
|
@@ -108,6 +111,7 @@ server.tool(
|
|
|
108
111
|
language: z.string().optional().describe('Filter, e.g. "English (US)"'),
|
|
109
112
|
gender: z.string().optional().describe('Filter: "female" | "male"'),
|
|
110
113
|
},
|
|
114
|
+
{ title: 'List voices', readOnlyHint: true },
|
|
111
115
|
async ({ language, gender }) => {
|
|
112
116
|
const q = new URLSearchParams();
|
|
113
117
|
if (language) q.set('language', language);
|
|
@@ -202,6 +206,10 @@ const createShape = {
|
|
|
202
206
|
// 1.4.0 (§799): oma käsikirjoitus + visuaalinen ohjaus.
|
|
203
207
|
voiceover_text: z.string().optional().describe('Your own voice-over, spoken WORD-FOR-WORD — the AI script writer is skipped entirely (and not charged). Word limit follows duration: 15s fits 45 words, 30s fits 80, 50s fits 130; over the limit is an error, never a silent trim. Single narrator only; not on music-video (its script is sung lyrics) or video-banner (no narration).'),
|
|
204
208
|
visual_direction: z.string().optional().describe('How the video should LOOK and what happens visually (max 600 chars): setting, emotional arc, recurring motif, when the product appears. Steers the storyboard — the art style still comes from style/custom_style.'),
|
|
209
|
+
// 2.3.0 (§903): kerronnan esitystavan jalostus — preset tai vapaa ohje.
|
|
210
|
+
visual_direction_refine: z.string().optional().describe('animated-ad and music-video ONLY. Reshapes HOW the visual story is told — not how it looks. Either a preset id (metaphoric, realistic, dramatic, calm, playful, dark, bright, magical — facelessad_list_tools returns them under visual_direction_refine.presets) or your own free-text instruction (max 300 chars), e.g. an intent like "the product should have consequences far beyond its size". Applied on top of visual_direction if you gave one, otherwise on the generated one — so you do not need to write a full visual direction yourself. Free of charge. Art-style words (palette, 3D, watercolor…) belong in custom_style, not here.'),
|
|
211
|
+
// 2.3.0 (§912): H/I/C-layout — pituussovitus säännöllä, ei virhettä.
|
|
212
|
+
layout: z.string().optional().describe('animated-ad and music-video ONLY. The card layout: A (scenes only), HA (hook card + scenes), AC (scenes + CTA card), HAC, AIA (scenes + data card + scenes), HAIA, AIAC, HAIAC. facelessad_list_tools returns them under layouts with names and, per duration, what they become: hook and CTA cards you chose always stay, the data card is dropped when the video is too short (e.g. HAIAC at 30s becomes HAC). Never an error for length — the response reports layout.applied and a notice if it was reduced. Omit to let the ad structure decide.'),
|
|
205
213
|
hook_text: z.string().optional().describe('Your own first-frame hook card text, used as-is (max 120 chars). Omitted, it is derived from the script.'),
|
|
206
214
|
// 1.5.0 (§806): tekstitysten ulkoasu.
|
|
207
215
|
caption_style: z.string().optional().describe('Caption look: "outline" (default — no dark box), "bottom-bar", "word-pop", "karaoke" or "multi-font"'),
|
|
@@ -214,6 +222,7 @@ server.tool(
|
|
|
214
222
|
'facelessad_estimate',
|
|
215
223
|
'The upper-bound credit cost of a video, without creating it. Takes the same input as facelessad_create_video. The user is only charged for steps that actually succeed.',
|
|
216
224
|
createShape,
|
|
225
|
+
{ title: 'Estimate cost', readOnlyHint: true },
|
|
217
226
|
async (input) => result(await api('POST', '/api/v1/estimate', input))
|
|
218
227
|
);
|
|
219
228
|
|
|
@@ -221,6 +230,7 @@ server.tool(
|
|
|
221
230
|
'facelessad_create_video',
|
|
222
231
|
'Create a faceless video ad. THIS SPENDS THE USER\'S CREDITS — run facelessad_estimate first and tell the user the number before calling this, unless they have already approved the cost. Returns immediately with an id and status "queued"; the video builds in the background (typically 3–10 minutes) and lands in the user\'s My Files. Poll facelessad_get_video for progress — do not wait synchronously.',
|
|
223
232
|
createShape,
|
|
233
|
+
{ title: 'Create video', readOnlyHint: false, destructiveHint: false },
|
|
224
234
|
async (input) => result(await api('POST', '/api/v1/videos', input))
|
|
225
235
|
);
|
|
226
236
|
|
|
@@ -228,6 +238,7 @@ server.tool(
|
|
|
228
238
|
'facelessad_get_video',
|
|
229
239
|
'Status of one video by id. When finished, includes a download URL valid ~24 hours — call again for a fresh one rather than storing it.',
|
|
230
240
|
{ id: z.union([z.string(), z.number()]).describe('The id returned by facelessad_create_video') },
|
|
241
|
+
{ title: 'Video status', readOnlyHint: true },
|
|
231
242
|
async ({ id }) => result(await api('GET', '/api/v1/videos/' + encodeURIComponent(String(id))))
|
|
232
243
|
);
|
|
233
244
|
|
|
@@ -238,6 +249,7 @@ server.tool(
|
|
|
238
249
|
limit: z.number().int().optional().describe('1–100, default 25'),
|
|
239
250
|
offset: z.number().int().optional(),
|
|
240
251
|
},
|
|
252
|
+
{ title: 'List videos', readOnlyHint: true },
|
|
241
253
|
async ({ limit, offset }) => {
|
|
242
254
|
const q = new URLSearchParams();
|
|
243
255
|
if (limit) q.set('limit', String(limit));
|
|
@@ -255,6 +267,7 @@ server.tool(
|
|
|
255
267
|
'facelessad_list_parts',
|
|
256
268
|
'List the parts of a finished video so one of them can be regenerated. Model A / image-card videos return scene clips and image cards (uid, type, duration, current motion prompt, image_prompt, preview links). Graphics tools return graphics_block parts (gfx-0, gfx-1, ...) — and on background-image styles (styles flagged genBg in facelessad_list_tools) ALSO a background_image layer per part (bg-0, bg-1, ...), regenerated with prompt. Product Showcase returns BOTH layers per scene: the product clip and the graphics block over it. Each part says whether it is regenerable and which field it needs (regenerate_requires).',
|
|
257
269
|
{ video_id: z.string().describe('Video id from facelessad_create_video') },
|
|
270
|
+
{ title: 'List video parts', readOnlyHint: true },
|
|
258
271
|
async ({ video_id }) => result(await api('GET', '/api/v1/videos/' + encodeURIComponent(video_id) + '/parts'))
|
|
259
272
|
);
|
|
260
273
|
|
|
@@ -268,6 +281,7 @@ server.tool(
|
|
|
268
281
|
image_prompt: z.string().optional().describe('New image description (max 900 chars) — required for image cards'),
|
|
269
282
|
instruction: z.string().optional().describe('Plain-language change for a graphics_block part (max 900 chars)'),
|
|
270
283
|
},
|
|
284
|
+
{ title: 'Regenerate video part', readOnlyHint: false, destructiveHint: true },
|
|
271
285
|
async ({ video_id, part_uid, prompt, image_prompt, instruction }) => {
|
|
272
286
|
const body = {};
|
|
273
287
|
if (prompt) body.prompt = prompt;
|
|
@@ -281,6 +295,7 @@ server.tool(
|
|
|
281
295
|
'facelessad_regenerate_video',
|
|
282
296
|
'Rebuild a WHOLE video from the same request that created it — the answer for continuous videos, whose clips chain into each other and cannot be fixed part by part. The result is a NEW video with its own id (the original is untouched) and it bills like a normal new generation: script, images and clips are all rolled again. Only for videos created through the API, within 7 days. For cuts videos prefer facelessad_regenerate_part.',
|
|
283
297
|
{ video_id: z.string().describe('Id of the video to rebuild') },
|
|
298
|
+
{ title: 'Rebuild video', readOnlyHint: false, destructiveHint: false },
|
|
284
299
|
async ({ video_id }) => result(await api('POST', '/api/v1/videos/' + encodeURIComponent(video_id) + '/regenerate', {}))
|
|
285
300
|
);
|
|
286
301
|
|
|
@@ -291,6 +306,7 @@ server.tool(
|
|
|
291
306
|
'facelessad_cancel_video',
|
|
292
307
|
'Cancel a video that is still QUEUED. The queue slot is freed immediately and nothing is charged, because a queued build has not started any work. A build that has already started cannot be cancelled (409 not_cancellable) — check facelessad_get_video first if unsure.',
|
|
293
308
|
{ video_id: z.string().describe('Id of the queued video to cancel') },
|
|
309
|
+
{ title: 'Cancel queued video', readOnlyHint: false, destructiveHint: true },
|
|
294
310
|
async ({ video_id }) => result(await api('POST', '/api/v1/videos/' + encodeURIComponent(video_id) + '/cancel', {}))
|
|
295
311
|
);
|
|
296
312
|
|
|
@@ -307,6 +323,7 @@ server.tool(
|
|
|
307
323
|
caption_text_color: z.string().optional().describe('Hex colour of the rest of the caption text'),
|
|
308
324
|
caption_font_size: z.number().int().optional().describe('Caption size 2-40 (default 10)'),
|
|
309
325
|
},
|
|
326
|
+
{ title: 'Change render settings', readOnlyHint: false, destructiveHint: true },
|
|
310
327
|
async ({ video_id, ...rest }) => {
|
|
311
328
|
const body = {};
|
|
312
329
|
for (const [k, v] of Object.entries(rest)) if (v !== undefined) body[k] = v;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@facelessad/mcp",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.3.0",
|
|
4
4
|
"description": "FacelessAd as MCP tools \u2014 let your AI assistant create faceless video ads (Claude Desktop, Claude Code, Cursor, Windsurf, OpenClaw).",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|