Skip to content

Commit 9490054

Browse files
committed
feat(manifest): replace and expand manifest with detailed tooling and prompts
Update the manifest with an extensively detailed description, expanded tool and prompt definitions, and refined configuration fields to enhance the AI- powered localization assistant capabilities. - Replace the previous manifest with a fully enriched one listing 59 tools across 11 domains, detailed usage descriptions, and 38 well-defined prompts. - Enhance user configuration options and environment variable mappings for improved flexibility and clarity. - Provide comprehensive long descriptions and examples to improve user understanding of the capabilities. - Refine tool descriptions to include required, optional arguments, and return data, aiding better usability. - Adjust compatibility and runtime versions to align with updates. - Modify related code to improve tool extraction, allowing multiline and escaped strings parsing for accurate manifest generation. - Enrich tool registration descriptions with detailed usage, inputs, and outputs. These changes significantly improve the manifest's clarity, completeness, and usability, laying groundwork for advanced conversational workflows in the Lokalise MCP project.
1 parent 834de6e commit 9490054

10 files changed

Lines changed: 359 additions & 241 deletions

File tree

.dxtignore

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
# Development files
2+
.cursor/
3+
.claude/
4+
.vscode/
5+
.idea/
6+
*.log
7+
*.tmp
8+
.DS_Store
9+
.env
10+
.env.*
11+
12+
# Test files
13+
coverage/
14+
*.test.ts
15+
*.test.js
16+
__tests__/
17+
__mocks__/
18+
__snapshots__/
19+
__fixtures__/
20+
vitest.config.ts
21+
22+
# Documentation and development guides
23+
*.md
24+
!README.md
25+
!LICENSE
26+
27+
# CI/CD
28+
.github/
29+
.releaserc.json
30+
31+
# Build artifacts
32+
*.tgz
33+
release/
34+
certs/
35+
36+
# Development config
37+
.nvmrc
38+
.node-version
39+
biome.json
40+
tsconfig.json
41+
.gitignore
42+
.dxtignore
43+
44+
# Misc
45+
.git/
46+
node_modules/

.npmignore

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,10 @@ coverage/
1414
*.test.ts
1515
*.test.js
1616
__tests__/
17-
jest.config.js
17+
__mocks__/
18+
__snapshots__/
19+
__fixtures__/
20+
vitest.config.ts
1821

1922
# Documentation and development guides
2023
*.md

manifest.json

Lines changed: 163 additions & 207 deletions
Large diffs are not rendered by default.

scripts/generate-manifest.js

Lines changed: 111 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -32,19 +32,31 @@ async function extractToolsFromFile(filePath) {
3232
const content = await readFile(filePath, "utf-8");
3333
const tools = [];
3434

35-
// Match server.tool() calls with better multiline support
36-
// This regex captures the tool name and the full description string
37-
const toolRegex =
38-
/server\.tool\(\s*["'`]([^"'`]+)["'`]\s*,\s*["'`]((?:[^"'`\\]|\\.)*)["'`]/gs;
35+
// Find all server.tool() calls and parse their arguments properly
36+
const toolCallRegex = /server\.tool\(\s*/g;
3937
let match;
4038

41-
// biome-ignore lint/suspicious/noAssignInExpressions: YOLO
42-
while ((match = toolRegex.exec(content)) !== null) {
43-
const name = match[1] ? match[1].trim() : "";
44-
const description = match[2] ? match[2].trim() : "";
39+
// biome-ignore lint/suspicious/noAssignInExpressions: Need to iterate through matches
40+
while ((match = toolCallRegex.exec(content)) !== null) {
41+
const startIndex = match.index + match[0].length;
42+
43+
// Parse the first argument (tool name)
44+
const nameResult = parseStringLiteral(content, startIndex);
45+
if (!nameResult) continue;
46+
47+
// Skip comma and whitespace
48+
let nextIndex = nameResult.endIndex;
49+
while (nextIndex < content.length && /[\s,]/.test(content[nextIndex])) {
50+
nextIndex++;
51+
}
52+
53+
// Parse the second argument (description)
54+
const descResult = parseStringLiteral(content, nextIndex);
55+
if (!descResult) continue;
56+
4557
tools.push({
46-
name,
47-
description,
58+
name: nameResult.value,
59+
description: descResult.value,
4860
});
4961
}
5062

@@ -55,6 +67,69 @@ async function extractToolsFromFile(filePath) {
5567
}
5668
}
5769

70+
/**
71+
* Parse a string literal from the given position, handling escape sequences properly
72+
*/
73+
function parseStringLiteral(content, startIndex) {
74+
if (startIndex >= content.length) return null;
75+
76+
const quote = content[startIndex];
77+
if (quote !== '"' && quote !== "'" && quote !== "`") return null;
78+
79+
let i = startIndex + 1;
80+
let value = "";
81+
82+
while (i < content.length) {
83+
const char = content[i];
84+
85+
if (char === quote) {
86+
// Found closing quote
87+
return { value, endIndex: i + 1 };
88+
}
89+
90+
if (char === "\\") {
91+
// Handle escape sequence
92+
i++;
93+
if (i < content.length) {
94+
const escaped = content[i];
95+
switch (escaped) {
96+
case "n":
97+
value += "\n";
98+
break;
99+
case "t":
100+
value += "\t";
101+
break;
102+
case "r":
103+
value += "\r";
104+
break;
105+
case "\\":
106+
value += "\\";
107+
break;
108+
case '"':
109+
value += '"';
110+
break;
111+
case "'":
112+
value += "'";
113+
break;
114+
case "`":
115+
value += "`";
116+
break;
117+
default:
118+
value += escaped;
119+
break;
120+
}
121+
}
122+
} else {
123+
value += char;
124+
}
125+
126+
i++;
127+
}
128+
129+
// Unclosed string
130+
return null;
131+
}
132+
58133
/**
59134
* Discover all domain tool files
60135
*/
@@ -333,6 +408,19 @@ async function generateManifest() {
333408
manifest.repository = packageJson.repository;
334409
}
335410

411+
// Preserve manually crafted display_name and description if they exist and are custom
412+
if (!manifest.display_name || manifest.display_name === "Lokalise MCP") {
413+
manifest.display_name = "Lokalise MCP";
414+
}
415+
416+
if (
417+
!manifest.description ||
418+
manifest.description.includes("Manage 59 tools across 11 domains")
419+
) {
420+
manifest.description =
421+
"Transform Lokalise into your conversational AI assistant. Manage tools across domains with natural language - from project creation to translation workflows, team management to bulk operations. Stop clicking, start commanding.";
422+
}
423+
336424
// Discover and extract tools
337425
console.log(chalk.yellow("📂 Discovering domain tools..."));
338426
const toolFiles = await discoverToolFiles();
@@ -362,15 +450,24 @@ async function generateManifest() {
362450
manifest.tools = allTools;
363451
manifest.tools_generated = true;
364452

365-
// Generate long description
366-
console.log(chalk.yellow("📝 Generating long description..."));
367-
manifest.long_description = generateLongDescription(allTools, domainCounts);
453+
// Only generate long description if it doesn't exist or is the old auto-generated format
454+
if (
455+
!manifest.long_description ||
456+
manifest.long_description.includes(
457+
"Transform Your Localization Workflow with Conversational AI",
458+
)
459+
) {
460+
console.log(chalk.yellow("📝 Generating long description..."));
461+
manifest.long_description = generateLongDescription(allTools, domainCounts);
462+
} else {
463+
console.log(chalk.gray("📝 Preserving existing long description..."));
464+
}
368465

369466
// Extract prompts (if implemented)
370467
const prompts = await extractPrompts();
371468
if (prompts.length > 0) {
372469
manifest.prompts = prompts;
373-
manifest.prompts_generated = false; // Set to true when auto-generation is implemented
470+
manifest.prompts_generated = true; // Set to true when auto-generation is implemented
374471
}
375472

376473
// Write the manifest

src/domains/queuedprocesses/queuedprocesses.tool.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -107,14 +107,14 @@ function registerTools(server: McpServer) {
107107

108108
server.tool(
109109
"lokalise_list_queued_processes",
110-
"Lists all background/async processes in a Lokalise project with status tracking",
110+
"Lists all background/async processes in a Lokalise project with status tracking. Required: projectId. Optional: limit (100), page. Use to monitor file uploads, downloads, bulk operations, or troubleshoot process issues. Returns: Processes with status, progress, and completion estimates.",
111111
ListQueuedprocessesToolArgs.shape,
112112
handleListQueuedprocesses,
113113
);
114114

115115
server.tool(
116116
"lokalise_get_queued_process",
117-
"Gets detailed status and information about a specific async process (upload, download, etc.)",
117+
"Gets detailed status and information about a specific async process (upload, download, etc.). Required: projectId, processId. Use to check process completion, diagnose failures, or get detailed progress information. Returns: Complete process details with logs and status history.",
118118
GetQueuedprocessesToolArgs.shape,
119119
handleGetQueuedprocesses,
120120
);

src/domains/registry.ts

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -168,15 +168,24 @@ export class DomainRegistry {
168168
});
169169
return module;
170170
} catch (error) {
171-
methodLogger.error(`Failed to load domain ${name}`, { error });
171+
const errorMessage =
172+
error instanceof Error ? error.message : String(error);
173+
const errorStack = error instanceof Error ? error.stack : undefined;
174+
175+
methodLogger.error(`Failed to load domain ${name}`, {
176+
error: errorMessage,
177+
stack: errorStack,
178+
indexPath: join(this.domainsPath, name, "index.js"),
179+
name,
180+
});
172181

173182
// Update registry with error
174183
this.registryMap.set(name, {
175184
name,
176185
path: join(this.domainsPath, name),
177186
module: {},
178187
loaded: false,
179-
error: error instanceof Error ? error.message : String(error),
188+
error: errorMessage,
180189
});
181190

182191
return null;

src/domains/teamusers/teamusers.tool.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -180,28 +180,28 @@ function registerTools(server: McpServer) {
180180

181181
server.tool(
182182
"lokalise_list_team_users",
183-
"Lists all users in a Lokalise team with pagination support",
183+
"Lists all users in a Lokalise team with pagination support. Required: teamId. Optional: limit (100), page. Use to audit team composition, check access levels, or prepare team changes. Returns: Users with roles, permissions, and activity status.",
184184
ListTeamusersToolArgs.shape,
185185
handleListTeamusers,
186186
);
187187

188188
server.tool(
189189
"lokalise_get_team_user",
190-
"Gets detailed information about a specific user in a team",
190+
"Gets detailed information about a specific user in a team. Required: teamId, userId. Use to verify user permissions, check role assignments, or investigate access issues. Returns: Complete user profile with all team permissions and administrative rights.",
191191
GetTeamusersToolArgs.shape,
192192
handleGetTeamusers,
193193
);
194194

195195
server.tool(
196196
"lokalise_update_team_user",
197-
"Updates a team user's role (owner, admin, member, or biller)",
197+
"Updates a team user's role (owner, admin, member, or biller). Required: teamId, userId, role. Use to manage permissions, promote/demote, or adjust access levels. Returns: Updated user profile with new role and permissions.",
198198
UpdateTeamusersToolArgs.shape,
199199
handleUpdateTeamusers,
200200
);
201201

202202
server.tool(
203203
"lokalise_delete_team_user",
204-
"Removes a user from a Lokalise team",
204+
"Removes a user from a Lokalise team. Required: teamId, userId. Use to remove members, clean up permissions, or manage team structure. Returns: Success message with user details.",
205205
DeleteTeamusersToolArgs.shape,
206206
handleDeleteTeamusers,
207207
);

src/domains/usergroups/usergroups.tool.ts

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -317,63 +317,63 @@ function registerTools(server: McpServer) {
317317

318318
server.tool(
319319
"lokalise_list_usergroups",
320-
"Lists all user groups in a Lokalise team with pagination support",
320+
"Lists all user groups in a Lokalise team with pagination support. Required: teamId. Optional: limit (100), page. Use to audit team organization, check group structure, or prepare group management operations. Returns: User groups with names, member counts, permissions, and language assignments. Essential for understanding team hierarchy.",
321321
ListUsergroupsToolArgs.shape,
322322
handleListUsergroups,
323323
);
324324

325325
server.tool(
326326
"lokalise_get_usergroup",
327-
"Gets detailed information about a specific user group",
327+
"Gets detailed information about a specific user group including members, permissions, and project assignments. Required: teamId, groupId. Use to audit group configuration, verify member access, or understand permission structure. Returns: Complete group profile with admin rights, language permissions, and assigned projects/members.",
328328
GetUsergroupsToolArgs.shape,
329329
handleGetUsergroups,
330330
);
331331

332332
server.tool(
333333
"lokalise_create_usergroup",
334-
"Creates a new user group in a Lokalise team",
334+
"Creates a new user group in a Lokalise team for organized permission management. Required: teamId, name, isReviewer, isAdmin. Optional: adminRights, languages, projects, members. Use to establish role-based access control, organize team permissions, or set up project-specific groups. Returns: Created group with assigned ID and configuration.",
335335
CreateUsergroupsToolArgs.shape,
336336
handleCreateUsergroups,
337337
);
338338

339339
server.tool(
340340
"lokalise_update_usergroup",
341-
"Updates a user group's properties",
341+
"Updates a user group's properties including permissions and assignments. Required: teamId, groupId, name, isReviewer, isAdmin. Optional: adminRights, languages. Use to adjust group permissions, modify access levels, or reorganize team structure. Returns: Updated group configuration. Note: Cannot modify projects/members here - use dedicated tools.",
342342
UpdateUsergroupsToolArgs.shape,
343343
handleUpdateUsergroups,
344344
);
345345

346346
server.tool(
347347
"lokalise_delete_usergroup",
348-
"Deletes a user group from a Lokalise team",
348+
"Deletes a user group from a Lokalise team, removing all associated permissions. Required: teamId, groupId. Use for cleanup, removing obsolete groups, or restructuring team organization. Returns: Deletion confirmation. Warning: Removes all group assignments - members lose group-based permissions immediately.",
349349
DeleteUsergroupsToolArgs.shape,
350350
handleDeleteUsergroups,
351351
);
352352

353353
server.tool(
354354
"lokalise_add_members_to_group",
355-
"Adds users to a user group",
355+
"Adds users to a user group, granting them group-based permissions and project access. Required: teamId, groupId, userIds array. Use to onboard team members, assign role-based access, or batch permission updates. Returns: Operation confirmation. Members immediately gain group permissions and project access.",
356356
AddMembersToolArgs.shape,
357357
handleAddMembers,
358358
);
359359

360360
server.tool(
361361
"lokalise_remove_members_from_group",
362-
"Removes users from a user group",
362+
"Removes users from a user group, revoking group-based permissions and project access. Required: teamId, groupId, userIds array. Use for role changes, offboarding, or permission cleanup. Returns: Operation confirmation. Warning: Immediate effect - users lose group permissions and project access.",
363363
RemoveMembersToolArgs.shape,
364364
handleRemoveMembers,
365365
);
366366

367367
server.tool(
368368
"lokalise_add_projects_to_group",
369-
"Adds projects to a user group",
369+
"Adds projects to a user group, granting all group members access to specified projects. Required: teamId, groupId, projectIds array. Use to expand group project scope, onboard projects to existing teams, or batch project assignments. Returns: Operation confirmation. All group members gain immediate project access.",
370370
AddProjectsToolArgs.shape,
371371
handleAddProjects,
372372
);
373373

374374
server.tool(
375375
"lokalise_remove_projects_from_group",
376-
"Removes projects from a user group",
376+
"Removes projects from a user group, revoking group member access to specified projects. Required: teamId, groupId, projectIds array. Use to limit project scope, offboard projects, or restructure access. Returns: Operation confirmation. Warning: All group members lose immediate project access.",
377377
RemoveProjectsToolArgs.shape,
378378
handleRemoveProjects,
379379
);

src/shared/utils/index.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
// Shared utilities exports
2-
export * from "./cli.test.util.js";
32
export * from "./config.util.js";
43
export * from "./constants.util.js";
54
export * from "./error.util.js";

tsconfig.json

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,5 +113,13 @@
113113
"skipLibCheck": true /* Skip type checking all .d.ts files. */
114114
},
115115
"include": ["src/**/*"],
116-
"exclude": ["**/__fixtures__/**/*", "**/*.test.ts"]
116+
"exclude": [
117+
"**/__fixtures__/**/*",
118+
"**/*.test.ts",
119+
"**/*.test.js",
120+
"**/test-utils/**/*",
121+
"**/scripts/**/*",
122+
"**/__mocks__/**/*",
123+
"**/__snapshots__/**/*"
124+
]
117125
}

0 commit comments

Comments
 (0)