UNPKG

lokalise-mcp

Version:

The Lokalise MCP Server brings Lokalise's localization power to Claude and AI assistants—manage projects, keys, and translations by chat.

333 lines (332 loc) • 14.1 kB
import { createApiError, createUnexpectedError, } from "../../shared/utils/error.util.js"; import { Logger } from "../../shared/utils/logger.util.js"; import { getLokaliseApi } from "../../shared/utils/lokalise-api.util.js"; // Create a contextualized logger for this file const serviceLogger = Logger.forContext("services/vendor.lokalise.com.tasks.service.ts"); // Log service initialization serviceLogger.debug("Lokalise Tasks API service initialized"); /** * @function getTasks * @description Fetches a list of tasks from a Lokalise project with optional filtering and pagination. * @memberof VendorLokaliseTasksService * @param {TaskListParams} options - Parameters including project ID, filters, and pagination options * @returns {Promise<PaginatedResult<Task>>} A promise that resolves to the API response containing the tasks list * @throws {McpError} Throws an McpError with details if the API call fails */ export async function getTasks(options) { const methodLogger = serviceLogger.forMethod("getTasks"); try { methodLogger.debug("Calling Lokalise Tasks API - list", { projectId: options.project_id, limit: options.limit, page: options.page, filterTitle: options.filter_title, filterStatuses: options.filter_statuses, }); const api = getLokaliseApi(); // Prepare API parameters with proper typing const apiParams = { project_id: options.project_id, limit: options.limit || 100, page: options.page || 1, }; if (options.filter_title) { apiParams.filter_title = options.filter_title; } if (options.filter_statuses) { apiParams.filter_statuses = options.filter_statuses; } const result = await api.tasks().list(apiParams); methodLogger.debug("Lokalise Tasks API call successful", { projectId: options.project_id, tasksCount: result.items?.length || 0, hasNextPage: result.hasNextPage(), }); return result; } catch (error) { methodLogger.error("Lokalise Tasks API call failed - list", { error: error.message, projectId: options.project_id, }); if (error.code === 404) { throw createApiError(`Project not found: ${options.project_id}`, 404); } if (error.code === 403) { throw createApiError("Access denied to this project", 403); } if (error.code === 401) { throw createApiError("Invalid API key", 401); } throw createUnexpectedError(`Failed to fetch tasks from project ${options.project_id}: ${error.message}`); } } /** * @function createTask * @description Creates a task in a Lokalise project * @memberof VendorLokaliseTasksService * @param {CreateTaskServiceParams} options - Parameters including project ID and task data * @returns {Promise<Task>} A promise that resolves to the API response containing created task * @throws {McpError} Throws an McpError with details if the API call fails */ export async function createTask(options) { const methodLogger = serviceLogger.forMethod("createTask"); try { methodLogger.debug("Calling Lokalise Tasks API - create", { projectId: options.project_id, title: options.title, languagesCount: options.languages?.length || 0, }); const api = getLokaliseApi(); // Prepare API parameters matching SDK interface const apiParams = { title: options.title, }; // Only include optional parameters if they have values if (options.description !== undefined) { apiParams.description = options.description; } if (options.keys !== undefined && options.keys.length > 0) { apiParams.keys = options.keys; } if (options.languages !== undefined) { apiParams.languages = options.languages; } if (options.due_date !== undefined) { apiParams.due_date = options.due_date; } if (options.source_language_iso !== undefined) { apiParams.source_language_iso = options.source_language_iso; } if (options.auto_close_languages !== undefined) { apiParams.auto_close_languages = options.auto_close_languages; } if (options.auto_close_task !== undefined) { apiParams.auto_close_task = options.auto_close_task; } if (options.auto_close_items !== undefined) { apiParams.auto_close_items = options.auto_close_items; } if (options.task_type !== undefined) { apiParams.task_type = options.task_type; } if (options.parent_task_id !== undefined) { apiParams.parent_task_id = options.parent_task_id; } if (options.closing_tags !== undefined && options.closing_tags.length > 0) { apiParams.closing_tags = options.closing_tags; } if (options.do_lock_translations !== undefined) { apiParams.do_lock_translations = options.do_lock_translations; } if (options.custom_translation_status_ids !== undefined && options.custom_translation_status_ids.length > 0) { apiParams.custom_translation_status_ids = options.custom_translation_status_ids; } // Ensure languages have users or groups if they're provided empty if (apiParams.languages && apiParams.languages.length > 0) { apiParams.languages = apiParams.languages.map((lang) => { // If neither users nor groups are specified, we need to provide at least one if (!lang.users?.length && !lang.groups?.length) { // If assignees were provided at the top level, use them for each language if (options.assignees && options.assignees.length > 0) { return { ...lang, users: options.assignees }; } // Otherwise, throw a helpful error before calling the API throw createApiError("Tasks with languages require either 'users' or 'groups' to be specified for each language, or use the 'assignees' parameter to assign users to all languages", 400); } return lang; }); } const result = await api .tasks() .create(apiParams, { project_id: options.project_id }); methodLogger.debug("Lokalise Tasks API call successful - create", { projectId: options.project_id, taskId: result.task_id, title: result.title, }); return result; } catch (error) { methodLogger.error("Lokalise Tasks API call failed - create", { error: error.message, projectId: options.project_id, }); if (error.code === 404) { throw createApiError(`Project not found: ${options.project_id}`, 404); } if (error.code === 403) { throw createApiError("Access denied to this project", 403); } if (error.code === 401) { throw createApiError("Invalid API key", 401); } throw createUnexpectedError(`Failed to create task in project ${options.project_id}: ${error.message}`); } } /** * @function getTask * @description Fetches a single task from a Lokalise project * @memberof VendorLokaliseTasksService * @param {GetTaskParams} options - Parameters including project ID and task ID * @returns {Promise<Task>} A promise that resolves to the task data * @throws {McpError} Throws an McpError with details if the API call fails */ export async function getTask(options) { const methodLogger = serviceLogger.forMethod("getTask"); try { methodLogger.debug("Calling Lokalise Tasks API - get", { projectId: options.project_id, taskId: options.task_id, }); const api = getLokaliseApi(); const result = await api .tasks() .get(options.task_id, { project_id: options.project_id }); methodLogger.debug("Lokalise Tasks API call successful - get", { projectId: options.project_id, taskId: options.task_id, title: result.title, }); return result; } catch (error) { methodLogger.error("Lokalise Tasks API call failed - get", { error: error.message, projectId: options.project_id, taskId: options.task_id, }); if (error.code === 404) { throw createApiError(`Task not found: ${options.task_id} in project ${options.project_id}`, 404); } if (error.code === 403) { throw createApiError("Access denied to this project", 403); } if (error.code === 401) { throw createApiError("Invalid API key", 401); } throw createUnexpectedError(`Failed to fetch task ${options.task_id} from project ${options.project_id}: ${error.message}`); } } /** * @function updateTask * @description Updates a single task in a Lokalise project * @memberof VendorLokaliseTasksService * @param {UpdateTaskServiceParams} options - Parameters including project ID, task ID and update data * @returns {Promise<Task>} A promise that resolves to the updated task data * @throws {McpError} Throws an McpError with details if the API call fails */ export async function updateTask(options) { const methodLogger = serviceLogger.forMethod("updateTask"); try { methodLogger.debug("Calling Lokalise Tasks API - update", { projectId: options.project_id, taskId: options.task_id, }); const api = getLokaliseApi(); // Prepare update data matching SDK interface const updateData = {}; if (options.title !== undefined) { updateData.title = options.title; } if (options.description !== undefined) { updateData.description = options.description; } if (options.due_date !== undefined) { updateData.due_date = options.due_date; } if (options.languages !== undefined) { updateData.languages = options.languages; } if (options.auto_close_languages !== undefined) { updateData.auto_close_languages = options.auto_close_languages; } if (options.auto_close_task !== undefined) { updateData.auto_close_task = options.auto_close_task; } if (options.auto_close_items !== undefined) { updateData.auto_close_items = options.auto_close_items; } if (options.closing_tags !== undefined && options.closing_tags.length > 0) { updateData.closing_tags = options.closing_tags; } if (options.do_lock_translations !== undefined) { updateData.do_lock_translations = options.do_lock_translations; } if (options.close_task !== undefined) { updateData.close_task = options.close_task; } const result = await api .tasks() .update(options.task_id, updateData, { project_id: options.project_id }); methodLogger.debug("Lokalise Tasks API call successful - update", { projectId: options.project_id, taskId: options.task_id, title: result.title, }); return result; } catch (error) { methodLogger.error("Lokalise Tasks API call failed - update", { error: error.message, projectId: options.project_id, taskId: options.task_id, }); if (error.code === 404) { throw createApiError(`Task not found: ${options.task_id} in project ${options.project_id}`, 404); } if (error.code === 403) { throw createApiError("Access denied to this project", 403); } if (error.code === 401) { throw createApiError("Invalid API key", 401); } throw createUnexpectedError(`Failed to update task ${options.task_id} in project ${options.project_id}: ${error.message}`); } } /** * @function deleteTask * @description Deletes a single task from a Lokalise project * @memberof VendorLokaliseTasksService * @param {DeleteTaskParams} options - Parameters including project ID and task ID * @returns {Promise<TaskDeleted>} A promise that resolves to the deletion confirmation * @throws {McpError} Throws an McpError with details if the API call fails */ export async function deleteTask(options) { const methodLogger = serviceLogger.forMethod("deleteTask"); try { methodLogger.debug("Calling Lokalise Tasks API - delete", { projectId: options.project_id, taskId: options.task_id, }); const api = getLokaliseApi(); const result = await api .tasks() .delete(options.task_id, { project_id: options.project_id }); methodLogger.debug("Lokalise Tasks API call successful - delete", { projectId: options.project_id, taskId: options.task_id, }); return result; } catch (error) { methodLogger.error("Lokalise Tasks API call failed - delete", { error: error.message, projectId: options.project_id, taskId: options.task_id, }); if (error.code === 404) { throw createApiError(`Task not found: ${options.task_id} in project ${options.project_id}`, 404); } if (error.code === 403) { throw createApiError("Access denied to this project", 403); } if (error.code === 401) { throw createApiError("Invalid API key", 401); } throw createUnexpectedError(`Failed to delete task ${options.task_id} from project ${options.project_id}: ${error.message}`); } }