lotux-mcp 0.5.2 → 0.8.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 +5 -1
- package/index.mjs +285 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -59,8 +59,12 @@ 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 |
|
|
66
|
+
| `content_options` / `create_flywheel` / `update_flywheel` / `list_flywheels` / `get_flywheel` | Content flywheels (Marketing → Content, agency-only): one long-form video. Start with `content_options`; give flywheels a `key` so re-runs reuse them |
|
|
67
|
+
| `push_assets` / `add_asset` / `get_asset` / `list_assets` / `update_asset` / `set_asset_status` / `add_comment` / `delete_asset` | Content assets, upserted on their `key`. Statuses: scripting → production → post_production → to_review → revisions → approved → live. Content you write lands in `to_review`; Claude can't set revisions, approve or mark live; deleting signed-off work needs `force` |
|
|
64
68
|
|
|
65
69
|
Beats (`add_beat`, `update_beat`, `delete_beat`, `set_beat_translation`, `add_language`) were
|
|
66
70
|
retired in Sep 2026 — scripts are documents now. Calling one returns a message pointing at the
|
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.8.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(
|
|
@@ -384,6 +454,218 @@ server.tool(
|
|
|
384
454
|
async (args) => ok(await rpc("update_kb_doc", args))
|
|
385
455
|
);
|
|
386
456
|
|
|
457
|
+
// ── Content flywheels (Marketing → Content, agency-only) ─────────────────────
|
|
458
|
+
// A flywheel = one long-form YouTube video + every asset made from it. LOTUX
|
|
459
|
+
// only TRACKS content: creating a flywheel creates no assets — your skills push
|
|
460
|
+
// them (push_assets / add_asset). Anything you write lands in "to_review"; a
|
|
461
|
+
// human approves in LOTUX. Built for re-runs: give flywheels and assets a stable
|
|
462
|
+
// `key` (flywheel = the video's content folder, asset = e.g. "clip-3") and every
|
|
463
|
+
// push updates instead of duplicating. account_id is optional everywhere
|
|
464
|
+
// (defaults to your agency). Call content_options first for the allowed values.
|
|
465
|
+
|
|
466
|
+
const PLATFORMS = ["youtube", "youtube_shorts", "instagram", "tiktok", "linkedin", "x", "email", "meta_ads", "opt_in_page", "google_doc", "internal"];
|
|
467
|
+
// Statuses follow production. You may set the first four; revisions,
|
|
468
|
+
// approved and live are a human's call.
|
|
469
|
+
const STATUSES = ["scripting", "production", "post_production", "to_review", "revisions", "approved", "live"];
|
|
470
|
+
const CLAUDE_STATUSES = ["scripting", "production", "post_production", "to_review"];
|
|
471
|
+
// video = "To long form".
|
|
472
|
+
const CTAS = ["none", "video", "lead_magnet", "dm_keyword", "booking", "follow"];
|
|
473
|
+
// long_form carries the YouTube pieces: title = YouTube title, body =
|
|
474
|
+
// description + chapters, cover_url = thumbnail. clip / creator_video: cover_url
|
|
475
|
+
// = reel cover.
|
|
476
|
+
const ASSET_TYPES = ["long_form", "clip", "creator_video", "story_sequence", "carousel", "post", "email", "vsl_ad", "follower_ad", "lead_magnet_ad", "lead_magnet_page", "other"];
|
|
477
|
+
const attachment = z.object({ url: z.string(), label: z.string().optional() });
|
|
478
|
+
const flywheelRef = {
|
|
479
|
+
flywheel_id: z.string().optional(),
|
|
480
|
+
flywheel_key: z.string().optional().describe("The key you gave the flywheel (instead of flywheel_id)"),
|
|
481
|
+
};
|
|
482
|
+
const assetRef = {
|
|
483
|
+
asset_id: z.string().optional(),
|
|
484
|
+
key: z.string().optional().describe("The asset's key — with flywheel_id or flywheel_key — instead of asset_id"),
|
|
485
|
+
...flywheelRef,
|
|
486
|
+
};
|
|
487
|
+
// Every asset field. Omitted = left as is (on update) or the type's default (on create).
|
|
488
|
+
const assetFields = {
|
|
489
|
+
asset_type: z.enum(ASSET_TYPES).optional(),
|
|
490
|
+
title: z.string().optional().describe("Card title. long_form: the YouTube title"),
|
|
491
|
+
platform: z.enum(PLATFORMS).optional().describe("Defaults from the type"),
|
|
492
|
+
cross_post: z.array(z.enum(PLATFORMS)).optional().describe("Also posted on (defaults from the type)"),
|
|
493
|
+
account: z.enum(["personal", "company"]).optional().describe("personal = Niels, company = the agency"),
|
|
494
|
+
cta: z.enum(CTAS).optional(),
|
|
495
|
+
dm_keyword: z.string().optional(),
|
|
496
|
+
dm_reply: z.enum(["booking", "lead_magnet"]).optional().describe("For a dm_keyword CTA: what the DM reply sends"),
|
|
497
|
+
destination_url: z.string().optional().describe("Override the flywheel URL this CTA would use"),
|
|
498
|
+
posting_date: z.string().optional().describe("YYYY-MM-DD"),
|
|
499
|
+
deadline: z.string().optional().describe("YYYY-MM-DD; defaults to posting date − the agency's lead days"),
|
|
500
|
+
owner: z.string().optional().describe('"claude", "none" or a profile id (see content_options.owners)'),
|
|
501
|
+
status: z.enum(CLAUDE_STATUSES).optional(),
|
|
502
|
+
must_post: z.boolean().optional(),
|
|
503
|
+
body: z.string().optional().describe("Markdown content. long_form: YouTube description + chapters"),
|
|
504
|
+
attachments: z.array(attachment).optional().describe("Replaces all links (Figma, Drive, Dropbox…)"),
|
|
505
|
+
cover_url: z.string().optional().describe("long_form: thumbnail · clip / creator_video: reel cover"),
|
|
506
|
+
published_url: z.string().optional(),
|
|
507
|
+
notes: z.string().optional(),
|
|
508
|
+
};
|
|
509
|
+
|
|
510
|
+
server.tool(
|
|
511
|
+
"content_options",
|
|
512
|
+
"Allowed values for content: asset types (with their default platform / cross-posts / CTA and what body + cover_url mean), platforms, CTAs, DM reply targets, accounts, statuses (and which you may set), owners (team members + claude), the default deadline lead and the rules. Call this first.",
|
|
513
|
+
{ account_id: z.string().optional() },
|
|
514
|
+
async (args) => ok(await rpc("content_options", args))
|
|
515
|
+
);
|
|
516
|
+
|
|
517
|
+
server.tool(
|
|
518
|
+
"create_flywheel",
|
|
519
|
+
"Create a content flywheel for a long-form YouTube video. With a key that already exists you get that flywheel back (created: false) — safe to re-run. Creates no assets. Returns { id, created }.",
|
|
520
|
+
{
|
|
521
|
+
key: z.string().optional().describe("Your stable id for this video, e.g. the content folder '2026-10 Claude Code'"),
|
|
522
|
+
title: z.string().describe("Video title"),
|
|
523
|
+
d0: z.string().describe("Long-form publish date, YYYY-MM-DD"),
|
|
524
|
+
topic: z.string().optional(),
|
|
525
|
+
youtube_url: z.string().optional(),
|
|
526
|
+
lead_magnet_url: z.string().optional().describe("Lead magnet page: email form that redirects to the video"),
|
|
527
|
+
booking_url: z.string().optional(),
|
|
528
|
+
transcript: z.string().optional().describe("Full transcript text"),
|
|
529
|
+
notes: z.string().optional(),
|
|
530
|
+
account_id: z.string().optional(),
|
|
531
|
+
},
|
|
532
|
+
async (args) => ok(await rpc("create_flywheel", args))
|
|
533
|
+
);
|
|
534
|
+
|
|
535
|
+
server.tool(
|
|
536
|
+
"update_flywheel",
|
|
537
|
+
"Update a flywheel: title, topic, status, URLs, transcript, notes. Changing d0 moves every asset that isn't live by the same number of days. Omitted fields are left unchanged.",
|
|
538
|
+
{
|
|
539
|
+
...flywheelRef,
|
|
540
|
+
title: z.string().optional(),
|
|
541
|
+
topic: z.string().optional(),
|
|
542
|
+
d0: z.string().optional().describe("YYYY-MM-DD — shifts the assets with it"),
|
|
543
|
+
status: z.enum(["planning", "production", "live", "done"]).optional(),
|
|
544
|
+
youtube_url: z.string().optional(),
|
|
545
|
+
lead_magnet_url: z.string().optional(),
|
|
546
|
+
booking_url: z.string().optional(),
|
|
547
|
+
transcript: z.string().optional(),
|
|
548
|
+
notes: z.string().optional(),
|
|
549
|
+
new_key: z.string().optional().describe("Give the flywheel a (new) key"),
|
|
550
|
+
account_id: z.string().optional(),
|
|
551
|
+
},
|
|
552
|
+
async (args) => ok(await rpc("update_flywheel", args))
|
|
553
|
+
);
|
|
554
|
+
|
|
555
|
+
server.tool(
|
|
556
|
+
"list_flywheels",
|
|
557
|
+
"List content flywheels with progress (approved / total), next deadline and overdue count. Filter by status, a title/key search, or an exact key.",
|
|
558
|
+
{
|
|
559
|
+
status: z.enum(["planning", "production", "live", "done"]).optional(),
|
|
560
|
+
query: z.string().optional().describe("Matches the title or key"),
|
|
561
|
+
key: z.string().optional(),
|
|
562
|
+
account_id: z.string().optional(),
|
|
563
|
+
},
|
|
564
|
+
async (args) => ok(await rpc("list_flywheels", args))
|
|
565
|
+
);
|
|
566
|
+
|
|
567
|
+
server.tool(
|
|
568
|
+
"get_flywheel",
|
|
569
|
+
"One flywheel and all its assets (each with `link` = where its CTA / DM reply points, and `warnings`). Bodies and the transcript only when asked.",
|
|
570
|
+
{
|
|
571
|
+
...flywheelRef,
|
|
572
|
+
include_bodies: z.boolean().optional(),
|
|
573
|
+
include_transcript: z.boolean().optional(),
|
|
574
|
+
account_id: z.string().optional(),
|
|
575
|
+
},
|
|
576
|
+
async (args) => ok(await rpc("get_flywheel", args))
|
|
577
|
+
);
|
|
578
|
+
|
|
579
|
+
server.tool(
|
|
580
|
+
"push_assets",
|
|
581
|
+
"Push many assets into a flywheel in one call (max 100). Each item is upserted on its `key`: new keys are created (asset_type + title required), existing keys are updated with only the fields you send. Content lands in to_review. Returns counts + per-item { key, id, created, status, link, warnings } or { key, error }.",
|
|
582
|
+
{
|
|
583
|
+
...flywheelRef,
|
|
584
|
+
assets: z.array(z.object({ key: z.string().optional(), ...assetFields })).max(100),
|
|
585
|
+
account_id: z.string().optional(),
|
|
586
|
+
},
|
|
587
|
+
async (args) => ok(await rpc("push_assets", args))
|
|
588
|
+
);
|
|
589
|
+
|
|
590
|
+
server.tool(
|
|
591
|
+
"add_asset",
|
|
592
|
+
"Push one asset. With a key that exists on the flywheel it updates that asset instead. The type fills platform, cross-posts and CTA when you leave them out. With content it lands in to_review, owned by Claude; without, as an unassigned slot in scripting.",
|
|
593
|
+
{
|
|
594
|
+
...flywheelRef,
|
|
595
|
+
key: z.string().optional(),
|
|
596
|
+
...assetFields,
|
|
597
|
+
account_id: z.string().optional(),
|
|
598
|
+
},
|
|
599
|
+
async (args) => ok(await rpc("add_asset", args))
|
|
600
|
+
);
|
|
601
|
+
|
|
602
|
+
server.tool(
|
|
603
|
+
"get_asset",
|
|
604
|
+
"One asset in full — body, attachments, cover/thumbnail, resolved link, warnings — plus its activity (status changes and comments, newest first). Use it to read what a human asked to change.",
|
|
605
|
+
{ ...assetRef, account_id: z.string().optional() },
|
|
606
|
+
async (args) => ok(await rpc("get_asset", args))
|
|
607
|
+
);
|
|
608
|
+
|
|
609
|
+
server.tool(
|
|
610
|
+
"list_assets",
|
|
611
|
+
"Assets across flywheels with filters. Find work: status revisions (a human asked for changes), scripting + owner claude, posting_to next week, overdue.",
|
|
612
|
+
{
|
|
613
|
+
...flywheelRef,
|
|
614
|
+
key: z.string().optional(),
|
|
615
|
+
asset_type: z.array(z.enum(ASSET_TYPES)).optional(),
|
|
616
|
+
status: z.array(z.enum(STATUSES)).optional(),
|
|
617
|
+
platform: z.array(z.enum(PLATFORMS)).optional().describe("Matches the main platform or a cross-post"),
|
|
618
|
+
account: z.enum(["personal", "company"]).optional(),
|
|
619
|
+
owner: z.string().optional().describe('"claude", "none" (unassigned) or a profile id'),
|
|
620
|
+
overdue: z.boolean().optional(),
|
|
621
|
+
posting_from: z.string().optional().describe("YYYY-MM-DD"),
|
|
622
|
+
posting_to: z.string().optional().describe("YYYY-MM-DD"),
|
|
623
|
+
include_bodies: z.boolean().optional(),
|
|
624
|
+
limit: z.number().int().min(1).max(500).optional(),
|
|
625
|
+
account_id: z.string().optional(),
|
|
626
|
+
},
|
|
627
|
+
async (args) => ok(await rpc("list_assets", args))
|
|
628
|
+
);
|
|
629
|
+
|
|
630
|
+
server.tool(
|
|
631
|
+
"update_asset",
|
|
632
|
+
"Update an asset by asset_id (or flywheel + key). Changing body, attachments or cover_url moves it to to_review (even if it was approved) so a human signs it off. Omitted fields are left unchanged.",
|
|
633
|
+
{
|
|
634
|
+
...assetRef,
|
|
635
|
+
...assetFields,
|
|
636
|
+
add_attachments: z.array(attachment).optional().describe("Appends links"),
|
|
637
|
+
new_key: z.string().optional().describe("Give the asset a (new) key"),
|
|
638
|
+
account_id: z.string().optional(),
|
|
639
|
+
},
|
|
640
|
+
async (args) => ok(await rpc("update_asset", args))
|
|
641
|
+
);
|
|
642
|
+
|
|
643
|
+
server.tool(
|
|
644
|
+
"set_asset_status",
|
|
645
|
+
"Move an asset through scripting, production, post_production or to_review (to_review hands it to a human). Revisions, approved and live are a human's call in LOTUX.",
|
|
646
|
+
{
|
|
647
|
+
...assetRef,
|
|
648
|
+
status: z.enum(CLAUDE_STATUSES),
|
|
649
|
+
comment: z.string().optional().describe("Shown in the asset's activity"),
|
|
650
|
+
account_id: z.string().optional(),
|
|
651
|
+
},
|
|
652
|
+
async (args) => ok(await rpc("set_asset_status", args))
|
|
653
|
+
);
|
|
654
|
+
|
|
655
|
+
server.tool(
|
|
656
|
+
"add_comment",
|
|
657
|
+
"Leave a comment on an asset (shown in its activity in LOTUX).",
|
|
658
|
+
{ ...assetRef, comment: z.string(), account_id: z.string().optional() },
|
|
659
|
+
async (args) => ok(await rpc("add_comment", args))
|
|
660
|
+
);
|
|
661
|
+
|
|
662
|
+
server.tool(
|
|
663
|
+
"delete_asset",
|
|
664
|
+
"Delete an asset. An approved or live one needs force: true.",
|
|
665
|
+
{ ...assetRef, force: z.boolean().optional(), account_id: z.string().optional() },
|
|
666
|
+
async (args) => ok(await rpc("delete_asset", args))
|
|
667
|
+
);
|
|
668
|
+
|
|
387
669
|
async function main() {
|
|
388
670
|
const transport = new StdioServerTransport();
|
|
389
671
|
await server.connect(transport);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lotux-mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.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": {
|