lotux-mcp 0.5.2 → 0.6.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 +3 -1
- package/index.mjs +73 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -59,7 +59,9 @@ Restart Claude, then try: *"list my LOTUX brands"*.
|
|
|
59
59
|
| `save_script` | Write a script: markdown body for one language (`- **Hook:** …`, `- **1:** …`, `- **CTA:** …`), the shotlist, a rename. Pass `language` for a translation |
|
|
60
60
|
| `add_script` / `delete_script` / `remove_script_language` / `set_base_language` | More scripts per concept; language housekeeping |
|
|
61
61
|
| `set_beats` / `set_beat_translations` | **Compatibility** for older prompts: a beat list is stored as the script document. Prefer `save_script` |
|
|
62
|
-
| `list_copy` / `create_copy` / `link_copy` / `unlink_copy` / `add_copy_translation` | Ad copy library + concept links |
|
|
62
|
+
| `list_copy` / `create_copy` / `update_copy` / `delete_copy` / `link_copy` / `unlink_copy` / `add_copy_translation` | Ad copy library + concept links. `update_copy` edits in place; `delete_copy` removes it everywhere (status stays app-only) |
|
|
63
|
+
| `list_landing_pages` / `create_landing_page` | A brand's landing pages + the agency pool; link them with `set_concept_fields` |
|
|
64
|
+
| `iterate_concept` | Spawn a variation ({root}_Var{n}), the same as the app's Iterate button |
|
|
63
65
|
| `list_kb_*` / `get_kb_doc` / `create_kb_doc` / `update_kb_doc` | Kennisbank pages |
|
|
64
66
|
|
|
65
67
|
Beats (`add_beat`, `update_beat`, `delete_beat`, `set_beat_translation`, `add_language`) were
|
package/index.mjs
CHANGED
|
@@ -52,7 +52,7 @@ const ok = (result) => ({
|
|
|
52
52
|
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
|
53
53
|
});
|
|
54
54
|
|
|
55
|
-
const server = new McpServer({ name: "lotux", version: "0.
|
|
55
|
+
const server = new McpServer({ name: "lotux", version: "0.6.0" });
|
|
56
56
|
|
|
57
57
|
// ── Reads ─────────────────────────────────────────────────────────────────────
|
|
58
58
|
|
|
@@ -157,6 +157,14 @@ server.tool(
|
|
|
157
157
|
angle_ids: z.array(z.string()).optional(),
|
|
158
158
|
use_case_ids: z.array(z.string()).optional(),
|
|
159
159
|
offer_ids: z.array(z.string()).optional(),
|
|
160
|
+
landing_page_ids: z
|
|
161
|
+
.array(z.string())
|
|
162
|
+
.optional()
|
|
163
|
+
.describe("Landing page ids from list_landing_pages; replaces the concept's set"),
|
|
164
|
+
landing_page_id: z
|
|
165
|
+
.string()
|
|
166
|
+
.optional()
|
|
167
|
+
.describe("Convenience for a single landing page — ignored if landing_page_ids is given"),
|
|
160
168
|
// Team roles — pass a member id from list_members.
|
|
161
169
|
strategist_id: z.string().optional().describe("Member id from list_members"),
|
|
162
170
|
copy_checker_id: z.string().optional().describe("Member id from list_members"),
|
|
@@ -164,8 +172,14 @@ server.tool(
|
|
|
164
172
|
media_buyer_id: z.string().optional().describe("Member id from list_members"),
|
|
165
173
|
final_check_id: z.string().optional().describe("Member id from list_members"),
|
|
166
174
|
},
|
|
167
|
-
|
|
168
|
-
|
|
175
|
+
// A concept can run several landing pages (one per market), so the column is
|
|
176
|
+
// a list. Fold the single-id convenience into it; an explicit list wins.
|
|
177
|
+
async ({ concept_id, landing_page_id, ...patch }) => {
|
|
178
|
+
if (landing_page_id && patch.landing_page_ids === undefined) {
|
|
179
|
+
patch.landing_page_ids = [landing_page_id];
|
|
180
|
+
}
|
|
181
|
+
return ok(await rpc("set_concept_fields", { concept_id, patch }));
|
|
182
|
+
}
|
|
169
183
|
);
|
|
170
184
|
|
|
171
185
|
// ── Scripts (document model) ────────────────────────────────────────────────
|
|
@@ -288,6 +302,29 @@ server.tool(
|
|
|
288
302
|
async (args) => ok(await rpc("unlink_copy", args))
|
|
289
303
|
);
|
|
290
304
|
|
|
305
|
+
server.tool(
|
|
306
|
+
"update_copy",
|
|
307
|
+
"Edit an existing copy item in place (fix a typo, correct the language or the product). Only the fields you pass change. Status is deliberately not settable here — approving or rejecting stays in the app.",
|
|
308
|
+
{
|
|
309
|
+
copy_item_id: z.string().describe("From list_copy or get_concept"),
|
|
310
|
+
text: z.string().optional(),
|
|
311
|
+
type: z.enum(["adcopy", "headline", "description"]).optional(),
|
|
312
|
+
product_ids: z.array(z.string()).optional(),
|
|
313
|
+
promotion: z.string().optional(),
|
|
314
|
+
country: z.string().optional(),
|
|
315
|
+
language: z.string().optional().describe("Full-written name, e.g. English"),
|
|
316
|
+
funnel_stage: z.string().optional(),
|
|
317
|
+
},
|
|
318
|
+
async (args) => ok(await rpc("update_copy", args))
|
|
319
|
+
);
|
|
320
|
+
|
|
321
|
+
server.tool(
|
|
322
|
+
"delete_copy",
|
|
323
|
+
"Delete a copy item from the brand library. It disappears from every concept it was linked to. To keep the copy and remove it from one concept, use unlink_copy instead.",
|
|
324
|
+
{ copy_item_id: z.string() },
|
|
325
|
+
async (args) => ok(await rpc("delete_copy", args))
|
|
326
|
+
);
|
|
327
|
+
|
|
291
328
|
server.tool(
|
|
292
329
|
"add_copy_translation",
|
|
293
330
|
"Add a language variant to an existing copy ('primary text') so it reads as one copy with a language switcher in the app. Clones the base copy's products/promotion/country/funnel with a new language + text and groups them (translation_group_id). base_copy_id comes from list_copy or get_concept; language is a full-written name (e.g. Dutch). Optionally pass concept_id to also link the new variant to that concept. Returns the new copy id.",
|
|
@@ -300,6 +337,39 @@ server.tool(
|
|
|
300
337
|
async (args) => ok(await rpc("add_copy_translation", args))
|
|
301
338
|
);
|
|
302
339
|
|
|
340
|
+
// ── Landing pages ────────────────────────────────────────────────────────
|
|
341
|
+
|
|
342
|
+
server.tool(
|
|
343
|
+
"list_landing_pages",
|
|
344
|
+
"A brand's landing pages, plus the agency-wide pool. Use it to get an id for set_concept_fields (landing_page_id / landing_page_ids) or for an offer's landing_page_id.",
|
|
345
|
+
{ account_id: z.string().describe("Brand id from list_brands") },
|
|
346
|
+
async (args) => ok(await rpc("list_landing_pages", args))
|
|
347
|
+
);
|
|
348
|
+
|
|
349
|
+
server.tool(
|
|
350
|
+
"create_landing_page",
|
|
351
|
+
"Create a landing page for a brand. `type` is required and feeds the ad-name token (pdp = product page, hp = homepage, cp = category page, li = listicle, adv = advertorial, lp = landing page). `id_code` is the short suffix ad names use (pdpa1). Tag `country` when the brand runs several markets — that is how a concept picks the right page.",
|
|
352
|
+
{
|
|
353
|
+
account_id: z.string().describe("Brand id from list_brands"),
|
|
354
|
+
title: z.string().describe("The page's name in LOTUX"),
|
|
355
|
+
url: z.string(),
|
|
356
|
+
type: z.enum(["pdp", "hp", "cp", "li", "adv", "lp"]),
|
|
357
|
+
id_code: z.string().optional(),
|
|
358
|
+
country: z.string().optional().describe("2-letter code, e.g. NL"),
|
|
359
|
+
language: z.string().optional().describe("Full-written name, e.g. Dutch"),
|
|
360
|
+
},
|
|
361
|
+
async (args) => ok(await rpc("create_landing_page", args))
|
|
362
|
+
);
|
|
363
|
+
|
|
364
|
+
// ── Iterate ──────────────────────────────────────────────────────────────
|
|
365
|
+
|
|
366
|
+
server.tool(
|
|
367
|
+
"iterate_concept",
|
|
368
|
+
"Spawn a variation of a concept, the same as the app's Iterate button. The new concept is coded {root}_Var{n} (89 becomes 89_Var1), lands in Idea, and inherits the products / personas / angles / offers, team, brief, scripts, copy and references. Variations are flat: iterating 89_Var2 makes 89_Var3 off 89. Timeline, media buying and landing pages are reset, and base_language goes back to English even though the copied script texts keep their own languages. Returns { id, concept_code, script_id }; script_id is null when the source had no script.",
|
|
369
|
+
{ concept_id: z.string() },
|
|
370
|
+
async (args) => ok(await rpc("iterate_concept", args))
|
|
371
|
+
);
|
|
372
|
+
|
|
303
373
|
// ── Brand-DB entities (products / personas / angles / use-cases / offers) ──────
|
|
304
374
|
|
|
305
375
|
server.tool(
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lotux-mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Local MCP server that lets Claude create LOTUX concepts (fetch brand-DB entities, create concepts, fill scripts + copy). Talks to the LOTUX app with a personal token; no business logic or secrets live here.",
|
|
6
6
|
"bin": {
|