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.

274 lines (273 loc) • 11.4 kB
import { ErrorType, McpError } from "../../shared/utils/error.util.js"; import { handleControllerError } from "../../shared/utils/error-handler.util.js"; import { Logger } from "../../shared/utils/logger.util.js"; import { formatAddContributorsResult, formatContributorDetails, formatContributorsList, formatRemoveContributorResult, formatUpdateContributorResult, } from "./contributors.formatter.js"; import { contributorsService } from "./contributors.service.js"; /** * @namespace ContributorsController * @description Controller responsible for handling Lokalise Contributors API operations. * It orchestrates calls to the contributors service, applies defaults, * maps options, and formats the response using the formatter. */ /** * @function listContributors * @description Fetches a list of contributors from a Lokalise project. * @memberof ContributorsController * @param {ListContributorsToolArgsType} args - Arguments containing project ID and pagination options * @returns {Promise<ControllerResponse>} A promise that resolves to the standard controller response containing the formatted contributors list in Markdown. * @throws {McpError} Throws an McpError (handled by `handleControllerError`) if the service call fails or returns an error. */ async function listContributors(args) { const methodLogger = Logger.forContext("contributors.controller.ts", "listContributors"); methodLogger.debug("Getting Lokalise contributors list...", args); try { // Validate project ID if (!args.projectId || typeof args.projectId !== "string") { throw new McpError("Project ID is required and must be a string.", ErrorType.API_ERROR); } // Validate pagination parameters if (args.limit !== undefined && (args.limit < 1 || args.limit > 100)) { throw new McpError("Invalid limit parameter. Must be between 1 and 100.", ErrorType.API_ERROR); } if (args.page !== undefined && args.page < 1) { throw new McpError("Invalid page parameter. Must be 1 or greater.", ErrorType.API_ERROR); } // Call service layer const result = await contributorsService.list(args); // Format response using the formatter const formattedContent = formatContributorsList(result); methodLogger.debug("Contributors list fetched successfully", { projectId: args.projectId, contributorsCount: result.items?.length || 0, }); return { content: formattedContent, }; } catch (error) { throw handleControllerError(error, { source: "ContributorsController.listContributors", entityType: "Contributors", entityId: args.projectId, operation: "listing", }); } } /** * @function getContributor * @description Fetches details of a specific contributor. * @memberof ContributorsController * @param {GetContributorToolArgsType} args - Arguments containing project ID and contributor ID * @returns {Promise<ControllerResponse>} A promise that resolves to the formatted contributor details. * @throws {McpError} Throws an McpError if the service call fails. */ async function getContributor(args) { const methodLogger = Logger.forContext("contributors.controller.ts", "getContributor"); methodLogger.debug("Getting contributor details...", args); try { // Validate inputs if (!args.projectId || typeof args.projectId !== "string") { throw new McpError("Project ID is required and must be a string.", ErrorType.API_ERROR); } if (!args.contributorId) { throw new McpError("Contributor ID is required.", ErrorType.API_ERROR); } // Call service layer const result = await contributorsService.get(args); // Format response const formattedContent = formatContributorDetails(result); methodLogger.debug("Contributor details fetched successfully", { projectId: args.projectId, contributorId: args.contributorId, }); return { content: formattedContent, }; } catch (error) { throw handleControllerError(error, { source: "ContributorsController.getContributor", entityType: "Contributor", entityId: { project: args.projectId, contributor: String(args.contributorId), }, operation: "retrieving", }); } } /** * @function addContributors * @description Adds one or more contributors to a project. * @memberof ContributorsController * @param {AddContributorsToolArgsType} args - Arguments containing project ID and contributor data * @returns {Promise<ControllerResponse>} A promise that resolves to the formatted result. * @throws {McpError} Throws an McpError if the service call fails. */ async function addContributors(args) { const methodLogger = Logger.forContext("contributors.controller.ts", "addContributors"); methodLogger.debug("Adding contributors...", args); try { // Validate inputs if (!args.projectId || typeof args.projectId !== "string") { throw new McpError("Project ID is required and must be a string.", ErrorType.API_ERROR); } if (!args.contributors || args.contributors.length === 0) { throw new McpError("At least one contributor is required.", ErrorType.API_ERROR); } // Call service layer const result = await contributorsService.create(args); // Format response const formattedContent = formatAddContributorsResult(result); methodLogger.debug("Contributors added successfully", { projectId: args.projectId, addedCount: result.length, }); return { content: formattedContent, }; } catch (error) { throw handleControllerError(error, { source: "ContributorsController.addContributors", entityType: "Contributors", entityId: args.projectId, operation: "adding", }); } } /** * @function getCurrentUser * @description Gets the current user's contributor profile for a project. * @memberof ContributorsController * @param {GetCurrentUserToolArgsType} args - Arguments containing project ID * @returns {Promise<ControllerResponse>} A promise that resolves to the formatted current user details. * @throws {McpError} Throws an McpError if the service call fails. */ async function getCurrentUser(args) { const methodLogger = Logger.forContext("contributors.controller.ts", "getCurrentUser"); methodLogger.debug("Getting current user contributor profile...", args); try { // Validate inputs if (!args.projectId || typeof args.projectId !== "string") { throw new McpError("Project ID is required and must be a string.", ErrorType.API_ERROR); } // Call service layer const result = await contributorsService.me(args); // Format response const formattedContent = formatContributorDetails(result); methodLogger.debug("Current user profile fetched successfully", { projectId: args.projectId, userId: result.user_id, }); return { content: formattedContent, }; } catch (error) { throw handleControllerError(error, { source: "ContributorsController.getCurrentUser", entityType: "Current User", entityId: args.projectId, operation: "retrieving", }); } } /** * @function updateContributor * @description Updates a contributor's permissions in a project. * @memberof ContributorsController * @param {UpdateContributorToolArgsType} args - Arguments containing project ID, contributor ID and update data * @returns {Promise<ControllerResponse>} A promise that resolves to the formatted update result. * @throws {McpError} Throws an McpError if the service call fails. */ async function updateContributor(args) { const methodLogger = Logger.forContext("contributors.controller.ts", "updateContributor"); methodLogger.debug("Updating contributor...", args); try { // Validate inputs if (!args.projectId || typeof args.projectId !== "string") { throw new McpError("Project ID is required and must be a string.", ErrorType.API_ERROR); } if (!args.contributorId) { throw new McpError("Contributor ID is required.", ErrorType.API_ERROR); } // Call service layer const result = await contributorsService.update(args); // Format response const formattedContent = formatUpdateContributorResult(result); methodLogger.debug("Contributor updated successfully", { projectId: args.projectId, contributorId: args.contributorId, }); return { content: formattedContent, }; } catch (error) { throw handleControllerError(error, { source: "ContributorsController.updateContributor", entityType: "Contributor", entityId: { project: args.projectId, contributor: String(args.contributorId), }, operation: "updating", }); } } /** * @function removeContributor * @description Removes a contributor from a project. * @memberof ContributorsController * @param {RemoveContributorToolArgsType} args - Arguments containing project ID and contributor ID * @returns {Promise<ControllerResponse>} A promise that resolves to the formatted deletion result. * @throws {McpError} Throws an McpError if the service call fails. */ async function removeContributor(args) { const methodLogger = Logger.forContext("contributors.controller.ts", "removeContributor"); methodLogger.debug("Removing contributor...", args); try { // Validate inputs if (!args.projectId || typeof args.projectId !== "string") { throw new McpError("Project ID is required and must be a string.", ErrorType.API_ERROR); } if (!args.contributorId) { throw new McpError("Contributor ID is required.", ErrorType.API_ERROR); } // Call service layer const result = await contributorsService.delete(args); // Format response const formattedContent = formatRemoveContributorResult(result); methodLogger.debug("Contributor removed successfully", { projectId: args.projectId, contributorId: args.contributorId, }); return { content: formattedContent, }; } catch (error) { throw handleControllerError(error, { source: "ContributorsController.removeContributor", entityType: "Contributor", entityId: { project: args.projectId, contributor: String(args.contributorId), }, operation: "removing", }); } } /** * Export the controller functions */ const contributorsController = { listContributors, getContributor, addContributors, getCurrentUser, updateContributor, removeContributor, }; export default contributorsController;