lotux-mcp 0.23.0 → 0.25.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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/tools.mjs +28 -3
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lotux-mcp",
3
- "version": "0.23.0",
3
+ "version": "0.25.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": {
package/tools.mjs CHANGED
@@ -6,7 +6,7 @@
6
6
 
7
7
  import { z } from "zod";
8
8
 
9
- export const SERVER_INFO = { name: "lotux", version: "0.22.0" };
9
+ export const SERVER_INFO = { name: "lotux", version: "0.25.0" };
10
10
 
11
11
  export function registerTools(server, rpc) {
12
12
  const ok = (result) => ({
@@ -38,6 +38,13 @@ export function registerTools(server, rpc) {
38
38
  async (args) => ok(await rpc("planning_overview", args))
39
39
  );
40
40
 
41
+ server.tool(
42
+ "planning_supply",
43
+ "Can we plan more, and who runs out of concepts? Per person, inside the planning horizon (Planner settings): free working minutes, ready concepts per client + kind (statics: Ready for production; videos: Ready for post; ids included, plan them with plan_items kind 'post'), how many days of work they cover, this week's need, and the client's strategist. low = less than 3 days of ready work while the client still needs pieces. person = id, name or 'me'; omit for everyone.",
44
+ { person: z.string().optional() },
45
+ async (args) => ok(await rpc("planning_supply", args))
46
+ );
47
+
41
48
  server.tool(
42
49
  "list_unplanned",
43
50
  "The Unplanned Log: concept blocks (post / go_live) and tasks without a date, in the team's order. Finished concepts (archive, testing, scaling, turned off) are left out. Filter by person, client name, bucket (static / video / …), kind or search (title or concept code).",
@@ -161,14 +168,14 @@ export function registerTools(server, rpc) {
161
168
 
162
169
  server.tool(
163
170
  "get_brand",
164
- "One client's workspace settings: slug, client_status, second brain (Kennisbank id + name, shared with the client or not), workspace_folder, dropbox_client_path, raw_footage_paths, Claude project name, the Claude project kit (instructions text) and the active work claims.",
171
+ "One client's workspace settings: ad_languages (main + translations), slug, client_status, second brain (Kennisbank id + name, shared with the client or not), workspace_folder, dropbox_client_path, raw_footage_paths, Claude project name, the Claude project kit (instructions text) and the active work claims.",
165
172
  { account_id: z.string().describe("Brand id from list_brands") },
166
173
  async (args) => ok(await rpc("get_brand", args))
167
174
  );
168
175
 
169
176
  server.tool(
170
177
  "update_brand_settings",
171
- "Change a client's workspace settings. Omitted fields stay as they are. slug is refused once client setup has run (it names the workspace folder). dropbox_client_path follows the Dropbox connection and workspace_folder follows the slug, so neither is set here.",
178
+ "Change a client's workspace settings, incl. the brand's ad languages (main_language + up to 2 translation_languages). Omitted fields stay as they are. slug is refused once client setup has run (it names the workspace folder). dropbox_client_path follows the Dropbox connection and workspace_folder follows the slug, so neither is set here.",
172
179
  {
173
180
  account_id: z.string().describe("Brand id from list_brands"),
174
181
  client_status: z.enum(["active", "archived", "internal"]).optional(),
@@ -176,6 +183,11 @@ export function registerTools(server, rpc) {
176
183
  kb_workspace_id: z.string().nullable().optional().describe("Kennisbank workspace id (list_kb_workspaces) to link as the second brain"),
177
184
  raw_footage_paths: z.array(z.string()).optional().describe("Dropbox paths, e.g. /CLIENTS/Active/Dr. Kegel/01_video/01_raw footage"),
178
185
  claude_project_name: z.string().optional(),
186
+ main_language: z.string().optional().describe("Ad languages: the main (script base) language for new concepts, full-written, e.g. Dutch"),
187
+ translation_languages: z
188
+ .array(z.string())
189
+ .optional()
190
+ .describe("Ad languages: up to 2 translation languages, full-written (e.g. [\"French\", \"English\"]). New concepts get a script tab for each. [] clears them."),
179
191
  },
180
192
  async (args) => ok(await rpc("update_brand_settings", args))
181
193
  );
@@ -530,6 +542,19 @@ export function registerTools(server, rpc) {
530
542
  async ({ concept_id, confirm_code }) => ok(await rpc("delete_concept", { concept_id, confirm_code }))
531
543
  );
532
544
 
545
+ server.tool(
546
+ "apply_brand_languages",
547
+ "Bring existing concepts in line with the brand's ad languages (get_brand → ad_languages; change them with update_brand_settings). Pick concepts by concept_ids and/or stage (stage key like copy_check or the column name like \"Copy check\"); max 100 per call. Per concept: the base language becomes the brand's main language (the base text is re-labelled, not translated) and every script gets a tab for each missing brand language, holding a copy of the base text. Existing tabs are never changed or removed. dry_run: true shows the plan only. More than 10 concepts to change returns a preview first — show it to the user, then call again with confirm: true. Returns per concept what changed, the new tabs (script_id + language) and the linked ad copy with its language. LOTUX does not translate: afterwards write each translation with save_script and fix ad copy languages with update_copy / add_copy_translation.",
548
+ {
549
+ account_id: z.string().describe("Brand id from list_brands"),
550
+ concept_ids: z.array(z.string()).optional(),
551
+ stage: z.string().optional().describe("Stage key (copy_check) or column name (Copy check)"),
552
+ dry_run: z.boolean().optional(),
553
+ confirm: z.boolean().optional().describe("Required when more than 10 concepts would change"),
554
+ },
555
+ async (args) => ok(await rpc("apply_brand_languages", args))
556
+ );
557
+
533
558
  server.tool(
534
559
  "set_base_language",
535
560
  "Set the concept's base script language, e.g. English. Existing base-language script texts are re-labelled to the new language.",