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.

167 lines (166 loc) • 6.55 kB
import { ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js"; import { formatErrorForMcpResource } from "../../shared/utils/error.util.js"; import { Logger } from "../../shared/utils/logger.util.js"; import translationsController from "./translations.controller.js"; /** * Translations MCP resources implementation. * Generated on 2025-07-08 for Translation content management. */ const logger = Logger.forContext("domains/translations/translations.resource.ts"); /** * Handle translations collection resource requests */ async function handleTranslationsCollectionResource(uri) { const methodLogger = logger.forMethod("handleTranslationsCollectionResource"); methodLogger.debug("Handling translations collection resource request", { uri: uri.toString(), }); try { // Extract project ID from path (after 'translations/') const pathParts = uri.pathname.split("/").filter(Boolean); if (pathParts.length < 2) { throw new Error("Invalid resource URI format. Expected: lokalise://translations/{projectId}"); } const projectId = pathParts[1]; // Second part is the project ID after 'translations' // Extract query parameters const limit = uri.searchParams.get("limit"); const cursor = uri.searchParams.get("cursor"); const filterLangId = uri.searchParams.get("filterLangId"); const filterIsReviewed = uri.searchParams.get("filterIsReviewed"); const filterIsUnverified = uri.searchParams.get("filterIsUnverified"); const filterQaIssues = uri.searchParams.get("filterQaIssues"); // Build arguments const args = { projectId, limit: limit ? Number.parseInt(limit, 10) : undefined, cursor: cursor || undefined, filterLangId: filterLangId ? Number.parseInt(filterLangId, 10) : undefined, filterIsReviewed: filterIsReviewed, filterIsUnverified: filterIsUnverified, filterQaIssues: filterQaIssues || undefined, }; // Call controller const result = await translationsController.listTranslations(args); return { contents: [ { uri: uri.toString(), mimeType: "text/markdown", text: result.content, }, ], }; } catch (error) { methodLogger.error("Resource handler failed", { error: error.message, }); return formatErrorForMcpResource(error, uri.toString()); } } /** * Handle translations detail resource requests */ async function handleTranslationsDetailResource(uri) { const methodLogger = logger.forMethod("handleTranslationsDetailResource"); methodLogger.debug("Handling translations detail resource request", { uri: uri.toString(), }); try { // Extract project ID and translations ID from path (after 'translations/') const pathParts = uri.pathname.split("/").filter(Boolean); if (pathParts.length < 3) { throw new Error("Invalid resource URI format. Expected: lokalise://translations/{projectId}/{translationId}"); } const projectId = pathParts[1]; // Second part is the project ID after 'translations' const translationsId = pathParts[2]; // Third part is the translation ID // Build arguments const args = { projectId, translationId: Number.parseInt(translationsId, 10), }; // Call controller const result = await translationsController.getTranslation(args); return { contents: [ { uri: uri.toString(), mimeType: "text/markdown", text: result.content, }, ], }; } catch (error) { methodLogger.error("Resource handler failed", { error: error.message, }); return formatErrorForMcpResource(error, uri.toString()); } } /** * Main resource request handler */ async function handleResourceRequest(uri) { const methodLogger = logger.forMethod("handleResourceRequest"); methodLogger.debug("Processing translations resource request", { uri: uri.toString(), }); try { // Route to appropriate handler based on URI structure const pathParts = uri.pathname.split("/").filter(Boolean); if (pathParts.length === 0) { throw new Error("Invalid resource URI. Expected lokalise://translations/{projectId} or lokalise://translations/{projectId}/{translationId}"); } // Check if this is a detail request (has translations ID) if (pathParts.length >= 3) { return await handleTranslationsDetailResource(uri); } // Otherwise, it's a collection request return await handleTranslationsCollectionResource(uri); } catch (error) { methodLogger.error("Failed to handle resource request", { error: error.message, uri: uri.toString(), }); return formatErrorForMcpResource(error, uri.toString()); } } /** * Register all MCP resources for the translations domain * @param server The MCP server instance to register resources with */ function registerResources(server) { const registerLogger = Logger.forContext("translations.resource.ts", "registerResources"); registerLogger.debug("Registering translations domain resources..."); // Register collection resource server.resource("lokalise-translations", new ResourceTemplate("lokalise://translations/{projectId}", { list: undefined, }), async (uri) => { return await handleResourceRequest(uri); }); // Register detail resource server.resource("lokalise-translation-details", new ResourceTemplate("lokalise://translations/{projectId}/{translationId}", { list: undefined }), async (uri) => { return await handleResourceRequest(uri); }); registerLogger.debug("Translations domain resources registered successfully"); } /** * Get metadata about the translations domain resources */ function getMeta() { return { name: "translations", description: "Translation content management MCP resources", version: "1.0.0", resourcesCount: 2, }; } const translationsResource = { registerResources, getMeta, }; export default translationsResource;