@channel.io/tolgee-mcp
Version:
Model Context Protocol server for Tolgee integration
319 lines (314 loc) • 13.1 kB
JavaScript
#!/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