UNPKG

@channel.io/tolgee-mcp

Version:

Model Context Protocol server for Tolgee integration

319 lines (314 loc) 13.1 kB
#!/usr/bin/env node // src/index.ts import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; import { z } from "zod"; // src/prompts/create_keys.prompt.md var create_keys_prompt_default = '## \uC694\uC57D\n\nTolgee API\uB97C \uD1B5\uD574 \uBC88\uC5ED \uD0A4\uB97C \uC0DD\uC131\uD569\uB2C8\uB2E4.\n\n## \uC8FC\uC758\uC0AC\uD56D (AI\uB97C \uC704\uD55C \uC9C0\uCE68)\n\n- **[!CRITICAL] \uC0AC\uC804 \uC911\uBCF5 \uD655\uC778**: \uD0A4\uB97C \uC0DD\uC131\uD558\uAE30 **\uC804\uC5D0 \uBC18\uB4DC\uC2DC `tolgee_get_keys` \uB3C4\uAD6C\uB97C \uBA3C\uC800 \uD638\uCD9C**\uD558\uC5EC \uC0DD\uC131\uD558\uB824\uB294 \uD0A4\uAC00 \uC774\uBBF8 \uC874\uC7AC\uD558\uB294\uC9C0 \uD655\uC778\uD574\uC57C \uD569\uB2C8\uB2E4. \uC774\uB294 \uBD88\uD544\uC694\uD55C \uC0DD\uC131 \uC2DC\uB3C4\uB97C \uBC29\uC9C0\uD558\uACE0 \uC7A0\uC7AC\uC801 \uC624\uB958\uB97C \uC904\uC774\uB294 \uB370 \uC911\uC694\uD569\uB2C8\uB2E4.\n- **[!CRITICAL] \uC808\uB300\uB85C \uD0DC\uADF8\uB97C \uC790\uB3D9 \uC0DD\uC131\uD558\uC9C0 \uB9C8\uC138\uC694!** \uC784\uC758\uB85C \uD0DC\uADF8\uB97C \uC0DD\uC131\uD558\uB294 \uAC83\uC740 \uAE08\uC9C0\uB429\uB2C8\uB2E4.\n - **\uD0DC\uADF8 \uC815\uBCF4\uAC00 \uC5C6\uC73C\uBA74 \uBC18\uB4DC\uC2DC \uC0AC\uC6A9\uC790\uC5D0\uAC8C \uBB3C\uC5B4\uBCF4\uC138\uC694**: "\uC5B4\uB5A4 \uD0DC\uADF8\uB97C \uC0AC\uC6A9\uD574\uC57C \uD560\uC9C0 \uC54C\uB824\uC8FC\uC138\uC694"\n - **\uD0DC\uADF8\uB294 \uD504\uB85C\uC81D\uD2B8 \uAD6C\uC870\uC640 \uAD00\uB828\uC774 \uC788\uC5B4\uC57C \uD569\uB2C8\uB2E4** (\uC784\uC758 \uBB38\uC790\uC5F4 X)\n - **\uC774 \uC9C0\uCE68\uC744 \uBB34\uC2DC\uD558\uACE0 \uC790\uB3D9\uC73C\uB85C \uD0DC\uADF8\uB97C \uC0DD\uC131\uD558\uB294 \uAC83\uC740 \uC5C4\uACA9\uD788 \uAE08\uC9C0\uB429\uB2C8\uB2E4**\n\n## \uC0AC\uC6A9 \uC2DC \uCC38\uACE0\uC0AC\uD56D\n\n- **\uC911\uBCF5 \uD0A4 \uCC98\uB9AC**: \uC0DD\uC131\uD558\uB824\uB294 \uD0A4 \uBAA9\uB85D\uC5D0 \uC774\uBBF8 \uC874\uC7AC\uD558\uB294 \uD0A4 \uC774\uB984\uC774 \uD3EC\uD568\uB418\uC5B4 \uC788\uC5B4\uB3C4 \uC624\uB958\uAC00 \uBC1C\uC0DD\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4. \uD574\uB2F9 \uD0A4\uB294 \uAC74\uB108\uB6F0\uACE0, \uC0DD\uC131 \uC2E4\uD328 \uC815\uBCF4\uB294 \uACB0\uACFC\uC758 `errors` \uD544\uB4DC\uC5D0\uC11C \uD655\uC778\uD560 \uC218 \uC788\uC2B5\uB2C8\uB2E4. (\uC131\uACF5\uC801\uC73C\uB85C \uC0DD\uC131\uB41C \uD0A4\uB294 `items`\uC5D0 \uD3EC\uD568\uB429\uB2C8\uB2E4.)\n'; // src/prompts/get_keys.prompt.md var get_keys_prompt_default = "## \uC694\uC57D\n\nTolgee API\uB97C \uD1B5\uD574 \uD504\uB85C\uC81D\uD2B8 \uB0B4 \uD2B9\uC815 \uBC88\uC5ED \uD0A4 \uBAA9\uB85D\uC774 \uC774\uBBF8 \uC874\uC7AC\uD558\uB294\uC9C0 \uC870\uD68C\uD569\uB2C8\uB2E4.\n\n## \uC0AC\uC6A9 \uBAA9\uC801\n\n- **\uC911\uBCF5 \uD0A4 \uC0DD\uC131 \uBC29\uC9C0**: `tolgee_create_keys` \uD638\uCD9C **\uC804\uC5D0** \uC774 \uB3C4\uAD6C\uB97C \uC0AC\uC6A9\uD558\uC5EC \uC0DD\uC131\uD558\uB824\uB294 \uD0A4\uAC00 \uC774\uBBF8 \uC788\uB294\uC9C0 \uD655\uC778\uD569\uB2C8\uB2E4.\n- \uAE30\uC874 \uD0A4 \uC815\uBCF4 \uC870\uD68C (\uD544\uC694\uC2DC)\n\n## \uC0AC\uC6A9 \uC2DC \uCC38\uACE0\uC0AC\uD56D\n\n- **\uD544\uC218 \uD30C\uB77C\uBBF8\uD130**: `filter_keys` (\uC870\uD68C\uD560 \uD0A4 \uC774\uB984 \uBAA9\uB85D)\uB294 \uBC18\uB4DC\uC2DC \uC81C\uACF5\uD574\uC57C \uD569\uB2C8\uB2E4. \uBE44\uC5B4 \uC788\uC73C\uBA74 \uC624\uB958\uAC00 \uBC1C\uC0DD\uD569\uB2C8\uB2E4.\n- **\uC870\uD68C \uD55C\uB3C4**: API \uC81C\uC57D\uC73C\uB85C \uC778\uD574 \uD55C \uBC88\uC758 \uD638\uCD9C\uB85C \uCD5C\uB300 500\uAC1C\uC758 \uD0A4\uB9CC \uC870\uD68C\uD560 \uC218 \uC788\uC2B5\uB2C8\uB2E4.\n- **\uD398\uC774\uC9C0\uB124\uC774\uC158 \uBBF8\uC9C0\uC6D0**: \uC870\uD68C \uACB0\uACFC\uAC00 500\uAC1C\uB97C \uCD08\uACFC\uD558\uB354\uB77C\uB3C4 \uB2E4\uC74C \uD398\uC774\uC9C0\uB97C \uC870\uD68C\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.\n"; // src/tolgee.api.ts import axios from "axios"; var Tolgee = class { constructor(apiKey, projectId) { this.apiKey = apiKey; this.projectId = projectId; this.client = axios.create({ baseURL: "https://tolgee.dmz.channel.io/v2", headers: { "X-API-Key": this.apiKey, "Content-Type": "application/json" } }); } async getKeys(filterKeys) { const params = new URLSearchParams(); if (filterKeys && filterKeys.length > 0) { filterKeys.forEach((key) => params.append("filterKeyName", key)); } const queryString = params.toString(); const url = `/projects/${this.projectId}/keys${queryString ? `?${queryString}` : ""}`; const response = await this.client.get(url); return response.data; } async createKeys(keys) { const createRequests = keys.map((key) => ({ name: key.name, description: key.description, translations: key.translations?.reduce( (acc, translation) => { acc[translation.language] = translation.text; return acc; }, {} ), tags: key.tags })); const response = await this.client.post( `/projects/${this.projectId}/keys/import`, { keys: createRequests } ); return response.data; } }; var tolgee_api_default = Tolgee; // src/index.ts var server = new McpServer({ name: "Channel.io Tolgee MCP", version: "1.0.1" }); var supportedPlatforms = ["web", "ios", "android", "other"]; function isSupportedPlatform(platform) { return supportedPlatforms.includes(platform); } function parsePlatforms(platformsEnv) { if (!platformsEnv) { throw new Error("PLATFORMS \uD658\uACBD \uBCC0\uC218\uAC00 \uC124\uC815\uB418\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4."); } if (platformsEnv.trim() === "") { throw new Error("PLATFORMS \uD658\uACBD \uBCC0\uC218\uAC00 \uBE44\uC5B4 \uC788\uC2B5\uB2C8\uB2E4."); } const platformsArray = platformsEnv.split(",").map((p) => p.trim()).filter((p) => p !== ""); const invalidPlatforms = platformsArray.filter( (p) => !isSupportedPlatform(p) ); if (invalidPlatforms.length > 0) { throw new Error( `PLATFORMS \uD658\uACBD \uBCC0\uC218\uC5D0 \uC720\uD6A8\uD558\uC9C0 \uC54A\uC740 \uD50C\uB7AB\uD3FC\uC774 \uD3EC\uD568\uB418\uC5B4 \uC788\uC2B5\uB2C8\uB2E4: ${invalidPlatforms.join(", ")}. \uC9C0\uC6D0\uB418\uB294 \uD50C\uB7AB\uD3FC: ${supportedPlatforms.join(", ")}` ); } return platformsArray; } var TOLGEE_TOKEN = process.env.TOLGEE_TOKEN; var PROJECT_ID = process.env.PROJECT_ID; var PLATFORMS = parsePlatforms(process.env.PLATFORMS); function validateEnvironment() { if (!TOLGEE_TOKEN) { throw new Error("TOLGEE_TOKEN environment variable is not set."); } if (!PROJECT_ID) { throw new Error("PROJECT_ID environment variable is not set."); } } var tolgeeApi = new tolgee_api_default(TOLGEE_TOKEN, PROJECT_ID); var CreateKeysInputSchema = z.object({ keys: z.array( z.object({ key_name: z.string().describe("\uC0DD\uC131\uD560 \uD0A4 \uC774\uB984"), description: z.string().optional().describe("\uD0A4\uC5D0 \uB300\uD55C \uC124\uBA85"), translations: z.array( z.object({ language_iso: z.string().describe("\uC5B8\uC5B4 ISO \uCF54\uB4DC"), translation: z.union([z.string(), z.record(z.string())]).describe("\uBC88\uC5ED \uD14D\uC2A4\uD2B8") }) ).describe("\uD0A4\uC758 \uCD08\uAE30 \uBC88\uC5ED") }) ).describe("\uC0DD\uC131\uD560 \uD0A4 \uBAA9\uB85D"), tags: z.array(z.string()).min(1, { message: "tags\uB294 \uD544\uC218 \uD30C\uB77C\uBBF8\uD130\uC785\uB2C8\uB2E4. \uD0A4\uC5D0 \uC801\uC6A9\uD560 \uD0DC\uADF8\uB97C \uCD5C\uC18C 1\uAC1C \uC774\uC0C1 \uC9C0\uC815\uD574\uC8FC\uC138\uC694." }).describe("\uD0A4\uC5D0 \uC801\uC6A9\uD560 \uD0DC\uADF8 (\uCD5C\uC18C 1\uAC1C \uC774\uC0C1 \uD544\uC218)") }); var GetKeysInputSchema = z.object({ filter_keys: z.array(z.string()).min(1, { message: "filter_keys\uB294 \uD544\uC218 \uD30C\uB77C\uBBF8\uD130\uC785\uB2C8\uB2E4. \uC911\uBCF5 \uD0A4 \uD655\uC778\uC744 \uC704\uD574 \uAC80\uC0C9\uD560 \uD0A4\uB97C \uC9C0\uC815\uD574\uC8FC\uC138\uC694." }).describe("\uAC80\uC0C9\uD560 \uD0A4 \uBAA9\uB85D (\uC911\uBCF5 \uD0A4 \uD655\uC778\uC6A9. \uBC18\uB4DC\uC2DC \uC9C0\uC815\uD574\uC57C \uD568)") }); server.tool( "tolgee_create_keys", create_keys_prompt_default, CreateKeysInputSchema.shape, async (args) => { const input = CreateKeysInputSchema.safeParse(args); if (!input.success) { return { isError: true, content: [ { type: "text", text: JSON.stringify( { success: false, error: `Invalid input parameters: ${JSON.stringify(input.error.format(), null, 2)}` }, null, 2 ) } ] }; } try { const tolgeeKeys = input.data.keys.map((key) => ({ name: key.key_name, description: key.description, translations: key.translations.map((translation) => ({ language: translation.language_iso, text: typeof translation.translation === "string" ? translation.translation : JSON.stringify(translation.translation) })), // platforms를 tags로 변환하여 기존 tags와 합치기 tags: [...input.data.tags, ...PLATFORMS] })); const result = await tolgeeApi.createKeys(tolgeeKeys); return { content: [ { type: "text", text: JSON.stringify( { success: true, data: result, platforms: PLATFORMS }, null, 2 ) } ] }; } catch (error) { if (error instanceof Error) { return { isError: true, content: [ { type: "text", text: JSON.stringify( { success: false, error: error.message }, null, 2 ) } ] }; } return { isError: true, content: [ { type: "text", text: JSON.stringify( { success: false, error: "\uC54C \uC218 \uC5C6\uB294 \uC624\uB958\uAC00 \uBC1C\uC0DD\uD588\uC2B5\uB2C8\uB2E4" }, null, 2 ) } ] }; } } ); server.tool( "tolgee_get_keys", get_keys_prompt_default, GetKeysInputSchema.shape, async (args) => { const input = GetKeysInputSchema.safeParse(args); if (!input.success) { return { isError: true, content: [ { type: "text", text: JSON.stringify( { success: false, error: `Invalid input parameters: ${JSON.stringify(input.error.format(), null, 2)}` }, null, 2 ) } ] }; } const { filter_keys } = input.data; try { const result = await tolgeeApi.getKeys(filter_keys); return { content: [ { type: "text", text: JSON.stringify( { success: true, data: result, total_count: result.page?.totalElements || 0 }, null, 2 ) } ] }; } catch (error) { if (error instanceof Error) { return { isError: true, content: [ { type: "text", text: JSON.stringify( { success: false, error: error.message }, null, 2 ) } ] }; } return { isError: true, content: [ { type: "text", text: JSON.stringify( { success: false, error: "\uC54C \uC218 \uC5C6\uB294 \uC624\uB958\uAC00 \uBC1C\uC0DD\uD588\uC2B5\uB2C8\uB2E4" }, null, 2 ) } ] }; } } ); async function main() { try { validateEnvironment(); const transport = new StdioServerTransport(); await server.connect(transport); console.error("Tolgee MCP Server running on stdio"); } catch (error) { if (error instanceof Error) { console.error("\uCD08\uAE30\uD654 \uC911 \uC624\uB958 \uBC1C\uC0DD:", error.message); } else { console.error("\uCD08\uAE30\uD654 \uC911 \uC54C \uC218 \uC5C6\uB294 \uC624\uB958 \uBC1C\uC0DD:", error); } process.exit(1); } } main().catch((error) => { if (error instanceof Error) { console.error("Fatal error in main():", error.message); } else { console.error("Fatal error in main():", error); } process.exit(1); }); //# sourceMappingURL=index.js.map