@syntholoc/mcp 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +44 -0
- package/dist/build-server.d.ts +11 -0
- package/dist/build-server.js +7 -0
- package/dist/build-server.js.map +1 -0
- package/dist/chunk-N4PXYXAP.js +850 -0
- package/dist/chunk-N4PXYXAP.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +30 -0
- package/dist/index.js.map +1 -0
- package/package.json +59 -0
- package/src/build-server.ts +18 -0
- package/src/config.ts +8 -0
- package/src/index.ts +20 -0
- package/src/prompts.ts +73 -0
- package/src/resources.ts +43 -0
- package/src/tools/analytics.ts +55 -0
- package/src/tools/config.ts +19 -0
- package/src/tools/git.ts +68 -0
- package/src/tools/glossary.ts +84 -0
- package/src/tools/index.ts +30 -0
- package/src/tools/jobs.ts +53 -0
- package/src/tools/keys.ts +114 -0
- package/src/tools/locales.ts +60 -0
- package/src/tools/projects.ts +74 -0
- package/src/tools/providers.ts +35 -0
- package/src/tools/qa.ts +46 -0
- package/src/tools/styleguides.ts +49 -0
- package/src/tools/translations.ts +156 -0
- package/src/util.ts +9 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/build-server.ts","../src/prompts.ts","../src/resources.ts","../src/tools/analytics.ts","../src/util.ts","../src/tools/config.ts","../src/tools/git.ts","../src/tools/glossary.ts","../src/tools/jobs.ts","../src/tools/keys.ts","../src/tools/locales.ts","../src/tools/projects.ts","../src/tools/providers.ts","../src/tools/qa.ts","../src/tools/styleguides.ts","../src/tools/translations.ts","../src/tools/index.ts"],"sourcesContent":["import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport type { GraphQLClient } from '@syntholoc/sdk';\nimport { registerPrompts } from './prompts';\nimport { registerResources } from './resources';\nimport { registerAllTools } from './tools';\n\n/**\n * Builds a fully-configured Syntholoc MCP server (tools + resources + prompts)\n * bound to the given GraphQL client. Shared by the stdio entrypoint and the\n * NestJS remote `/mcp` (Streamable HTTP) endpoint.\n */\nexport function buildMcpServer(gql: GraphQLClient): McpServer {\n const server = new McpServer({ name: 'syntholoc', version: '0.1.0' });\n registerAllTools(server, gql);\n registerResources(server, gql);\n registerPrompts(server);\n return server;\n}\n","import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { z } from 'zod';\n\nfunction userMessage(text: string) {\n return { messages: [{ role: 'user' as const, content: { type: 'text' as const, text } }] };\n}\n\n/** Registers workflow prompts (surfaced in Claude Code as /mcp__syntholoc__*). */\nexport function registerPrompts(server: McpServer): void {\n server.registerPrompt(\n 'localize_changed',\n {\n title: 'Localize changed keys',\n description: 'Sync changed source strings from the local diff, translate them, and write back.',\n argsSchema: { projectId: z.string().optional() },\n },\n ({ projectId }) =>\n userMessage(\n [\n 'Localize my recent source-string changes via Syntholoc:',\n '1. Read `.syntholoc/config.json` for the source file(s) and project.',\n projectId ? ` (Use project ${projectId} if config is missing.)` : '',\n '2. From `git diff` on the source file(s), collect added/changed keys and `upsert_keys` (dryRun first to preview).',\n '3. Call `get_config` for `maxBulkSize`. For EACH target locale check `locale_progress` → `untranslated`.',\n ' - If untranslated >= maxBulkSize: `translate_locale` (scope UNTRANSLATED) and `wait_for_job`.',\n ' - If below the threshold: tell me the counts and ASK whether to translate now or accumulate.',\n '4. `export_locale` for each translated locale and write the result back into the mapped local files.',\n '5. Summarize keys added, locales translated, and the cost from the job usage.',\n ]\n .filter(Boolean)\n .join('\\n'),\n ),\n );\n\n server.registerPrompt(\n 'review_locale',\n {\n title: 'Review a locale',\n description: 'Surface low-confidence and QA-flagged translations for a locale and help fix them.',\n argsSchema: { projectId: z.string(), locale: z.string() },\n },\n ({ projectId, locale }) =>\n userMessage(\n [\n `Review the \"${locale}\" locale in project ${projectId}:`,\n '1. Resolve the locale id via `list_locales`.',\n '2. `list_qa_issues` and `list_translations` (focus on low qaScore / non-approved).',\n '3. For each problem, propose a fix; apply with `set_translation`, then `approve_translation`.',\n '4. Summarize what changed and what still needs human attention.',\n ].join('\\n'),\n ),\n );\n\n server.registerPrompt(\n 'bootstrap_project',\n {\n title: 'Bootstrap a project from this repo',\n description: 'Detect i18n files, create a Syntholoc project, seed locales, and write .syntholoc/config.json.',\n argsSchema: {},\n },\n () =>\n userMessage(\n [\n 'Set up Syntholoc for this repository:',\n '1. Find the i18n source files (e.g. messages/en.json, locales/en/*.json) and infer the source locale.',\n '2. `create_project`, then `seed_starter_locales` (or `add_locale`) for the target languages.',\n '3. `import_source_strings` from the source file(s).',\n '4. Write `.syntholoc/config.json` mapping source → translation file patterns and the project id.',\n '5. Offer to run the localize_changed flow next.',\n ].join('\\n'),\n ),\n );\n}\n","import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { ResourceTemplate } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { CONFIG_RESOURCE, PROJECTS_RESOURCE, PROJECT_RESOURCE, type GraphQLClient } from '@syntholoc/sdk';\n\nfunction json(uri: URL, data: unknown) {\n return { contents: [{ uri: uri.href, mimeType: 'application/json', text: JSON.stringify(data, null, 2) }] };\n}\n\n/** Registers read-only MCP resources so the model can pull context cheaply. */\nexport function registerResources(server: McpServer, gql: GraphQLClient): void {\n server.registerResource(\n 'config',\n 'syntholoc://config',\n {\n title: 'Syntholoc config',\n description: 'Server metadata incl. maxBulkSize (auto-translate batch threshold).',\n mimeType: 'application/json',\n },\n async (uri) => json(uri, (await gql.request(CONFIG_RESOURCE)).appInfo),\n );\n\n server.registerResource(\n 'projects',\n 'syntholoc://projects',\n {\n title: 'Projects',\n description: 'All localization projects in the active organization.',\n mimeType: 'application/json',\n },\n async (uri) => json(uri, (await gql.request(PROJECTS_RESOURCE)).projects),\n );\n\n server.registerResource(\n 'project',\n new ResourceTemplate('syntholoc://project/{projectId}', { list: undefined }),\n {\n title: 'Project overview',\n description: 'A project with its locales and per-locale progress.',\n mimeType: 'application/json',\n },\n async (uri, { projectId }) => json(uri, await gql.request(PROJECT_RESOURCE, { id: String(projectId) })),\n );\n}\n","import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { z } from 'zod';\nimport { COST_ANALYTICS, LOCALE_PROGRESS, PROJECT_ANALYTICS, USAGE, type GraphQLClient } from '@syntholoc/sdk';\nimport { jsonResult } from '../util';\n\nexport function registerAnalyticsTools(server: McpServer, gql: GraphQLClient): void {\n server.registerTool(\n 'project_analytics',\n {\n title: 'Project analytics',\n description: 'Real-time quality + progress metrics for a project (per-locale progress, open issues).',\n inputSchema: { projectId: z.string() },\n annotations: { readOnlyHint: true, openWorldHint: true },\n },\n async ({ projectId }) => jsonResult((await gql.request(PROJECT_ANALYTICS, { projectId })).projectAnalytics),\n );\n\n server.registerTool(\n 'cost_analytics',\n {\n title: 'Cost analytics',\n description: 'LLM token/cost time-series and per-model breakdown over a window (default 30 days).',\n inputSchema: {\n projectId: z.string().optional(),\n days: z.number().int().min(1).max(365).optional(),\n },\n annotations: { readOnlyHint: true, openWorldHint: true },\n },\n async (vars) => jsonResult((await gql.request(COST_ANALYTICS, vars)).costAnalytics),\n );\n\n server.registerTool(\n 'usage',\n {\n title: 'Org usage & plan',\n description: 'Organization plan, hosted words and seat usage.',\n inputSchema: {},\n annotations: { readOnlyHint: true, openWorldHint: true },\n },\n async () => jsonResult((await gql.request(USAGE)).usage),\n );\n\n server.registerTool(\n 'locale_progress',\n {\n title: 'Locale progress',\n description:\n 'Progress counts for one locale: total / untranslated / autoTranslated / humanApproved. ' +\n 'The `untranslated` count drives the batch threshold (see get_config).',\n inputSchema: { projectId: z.string(), localeId: z.string() },\n annotations: { readOnlyHint: true, openWorldHint: true },\n },\n async (vars) => jsonResult((await gql.request(LOCALE_PROGRESS, vars)).localeProgress),\n );\n}\n","/** Shared helpers for MCP tool handlers. */\n\nexport const sleep = (ms: number): Promise<void> =>\n new Promise<void>((resolve) => setTimeout(resolve, ms));\n\n/** Wraps a value as an MCP text result (pretty-printed JSON). */\nexport function jsonResult(data: unknown): { content: { type: 'text'; text: string }[] } {\n return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };\n}\n","import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { APP_INFO, type GraphQLClient } from '@syntholoc/sdk';\nimport { jsonResult } from '../util';\n\nexport function registerConfigTools(server: McpServer, gql: GraphQLClient): void {\n server.registerTool(\n 'get_config',\n {\n title: 'Get Syntholoc config',\n description:\n 'Server metadata incl. `maxBulkSize` — the max keys per LLM batch. Use it as the ' +\n 'auto-translate threshold: translate proactively only when pending untranslated keys ' +\n 'reach this number; otherwise ask the user (an explicit translate request always runs).',\n inputSchema: {},\n annotations: { readOnlyHint: true, openWorldHint: true },\n },\n async () => jsonResult((await gql.request(APP_INFO)).appInfo),\n );\n}\n","import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { z } from 'zod';\nimport {\n LIST_REPOSITORIES,\n PULL_TRANSLATIONS,\n PUSH_TRANSLATIONS,\n SYNC_REPOSITORY,\n type GraphQLClient,\n} from '@syntholoc/sdk';\nimport { jsonResult } from '../util';\n\nexport function registerGitTools(server: McpServer, gql: GraphQLClient): void {\n server.registerTool(\n 'list_repositories',\n {\n title: 'List repositories',\n description: 'Lists git repositories connected to a project.',\n inputSchema: { projectId: z.string() },\n annotations: { readOnlyHint: true, openWorldHint: true },\n },\n async ({ projectId }) => jsonResult((await gql.request(LIST_REPOSITORIES, { projectId })).repositories),\n );\n\n server.registerTool(\n 'sync_repository',\n {\n title: 'Sync repository',\n description: 'Queues an immediate sync of a connected repository (pull → translate → PR).',\n inputSchema: { projectId: z.string(), repositoryId: z.string() },\n annotations: { openWorldHint: true },\n },\n async ({ projectId, repositoryId }) =>\n jsonResult((await gql.request(SYNC_REPOSITORY, { projectId, id: repositoryId })).syncRepository),\n );\n\n server.registerTool(\n 'push_translations',\n {\n title: 'Push translations (PR)',\n description: 'Queues write-back of a locale: export approved translations and open a PR.',\n inputSchema: { projectId: z.string(), repositoryId: z.string(), localeId: z.string() },\n annotations: { openWorldHint: true },\n },\n async (vars) => jsonResult((await gql.request(PUSH_TRANSLATIONS, vars)).pushTranslations),\n );\n\n server.registerTool(\n 'pull_translations',\n {\n title: 'Pull translations',\n description:\n 'Pulls translation files from the connected repo and imports them per locale. ' +\n '`autoApprove` lands them as APPROVED; `conflict` is OVERWRITE | SKIP_EXISTING | KEEP_APPROVED.',\n inputSchema: {\n projectId: z.string(),\n repositoryId: z.string(),\n autoApprove: z.boolean().default(false),\n conflict: z.enum(['OVERWRITE', 'SKIP_EXISTING', 'KEEP_APPROVED']).default('OVERWRITE'),\n },\n annotations: { openWorldHint: true },\n },\n async ({ projectId, repositoryId, autoApprove, conflict }) =>\n jsonResult(\n (await gql.request(PULL_TRANSLATIONS, { projectId, repositoryId, input: { autoApprove, conflict } }))\n .pullTranslations,\n ),\n );\n}\n","import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { z } from 'zod';\nimport {\n CREATE_GLOSSARY_TERM,\n DELETE_GLOSSARY_TERM,\n LIST_GLOSSARY,\n SUGGEST_GLOSSARY_TERMS,\n UPDATE_GLOSSARY_TERM,\n type GraphQLClient,\n} from '@syntholoc/sdk';\nimport { jsonResult } from '../util';\n\nexport function registerGlossaryTools(server: McpServer, gql: GraphQLClient): void {\n server.registerTool(\n 'list_glossary',\n {\n title: 'List glossary terms',\n description: 'Lists glossary terms (project-scoped if projectId given, else org-wide).',\n inputSchema: {\n projectId: z.string().optional(),\n skip: z.number().int().min(0).default(0),\n take: z.number().int().min(1).max(100).default(25),\n },\n annotations: { readOnlyHint: true, openWorldHint: true },\n },\n async (vars) => jsonResult((await gql.request(LIST_GLOSSARY, vars)).glossaryTerms),\n );\n\n server.registerTool(\n 'upsert_glossary_term',\n {\n title: 'Create or update glossary term',\n description:\n 'Creates a glossary term, or updates it when `termId` is given. `translations` is a ' +\n 'per-locale map, e.g. {\"de-DE\":\"…\"}. `forbidden` lists disallowed renderings.',\n inputSchema: {\n termId: z.string().optional().describe('Provide to update an existing term'),\n projectId: z.string().optional().describe('Project scope; omit for org-wide (create only)'),\n term: z.string().optional(),\n description: z.string().optional(),\n partOfSpeech: z.string().optional(),\n doNotTranslate: z.boolean().optional(),\n forbidden: z.array(z.string()).optional(),\n translations: z.record(z.string(), z.string()).optional(),\n },\n annotations: { idempotentHint: true, openWorldHint: true },\n },\n async ({ termId, projectId, ...rest }) => {\n if (termId) {\n const data = await gql.request(UPDATE_GLOSSARY_TERM, { id: termId, input: rest });\n return jsonResult(data.updateGlossaryTerm);\n }\n if (!rest.term) throw new Error('`term` is required when creating a glossary term');\n const data = await gql.request(CREATE_GLOSSARY_TERM, {\n input: { term: rest.term, ...rest, projectId },\n });\n return jsonResult(data.createGlossaryTerm);\n },\n );\n\n server.registerTool(\n 'delete_glossary_term',\n {\n title: 'Delete glossary term',\n description: 'Deletes a glossary term. Destructive — confirm first.',\n inputSchema: { termId: z.string() },\n annotations: { destructiveHint: true, openWorldHint: true },\n },\n async ({ termId }) =>\n jsonResult((await gql.request(DELETE_GLOSSARY_TERM, { id: termId })).deleteGlossaryTerm),\n );\n\n server.registerTool(\n 'suggest_glossary_terms',\n {\n title: 'Suggest glossary terms',\n description: 'AI-suggested glossary terms extracted from the project source strings.',\n inputSchema: { projectId: z.string() },\n annotations: { readOnlyHint: true, openWorldHint: true },\n },\n async ({ projectId }) =>\n jsonResult((await gql.request(SUGGEST_GLOSSARY_TERMS, { projectId })).suggestGlossaryTerms),\n );\n}\n","import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { z } from 'zod';\nimport { GET_JOB, LIST_JOBS, waitForJob, type GraphQLClient } from '@syntholoc/sdk';\nimport { jsonResult } from '../util';\n\nexport function registerJobTools(server: McpServer, gql: GraphQLClient): void {\n server.registerTool(\n 'get_job',\n {\n title: 'Get job',\n description: 'Status, progress, token usage and cost of a background job by id.',\n inputSchema: { jobId: z.string(), projectId: z.string() },\n annotations: { readOnlyHint: true, openWorldHint: true },\n },\n async ({ jobId, projectId }) => jsonResult((await gql.request(GET_JOB, { id: jobId, projectId })).job),\n );\n\n server.registerTool(\n 'list_jobs',\n {\n title: 'List jobs',\n description: 'Recent background jobs for a project (translate / QA / git sync).',\n inputSchema: {\n projectId: z.string(),\n skip: z.number().int().min(0).default(0),\n take: z.number().int().min(1).max(100).default(25),\n },\n annotations: { readOnlyHint: true, openWorldHint: true },\n },\n async (vars) => jsonResult((await gql.request(LIST_JOBS, vars)).jobs),\n );\n\n server.registerTool(\n 'wait_for_job',\n {\n title: 'Wait for job',\n description:\n 'Polls a job until it reaches a terminal state (COMPLETED/FAILED/CANCELLED) or the timeout ' +\n 'elapses, then returns its final detail. Use after translate/QA to follow a run.',\n inputSchema: {\n jobId: z.string(),\n projectId: z.string(),\n timeoutSec: z.number().int().min(1).max(900).default(180),\n pollIntervalSec: z.number().int().min(1).max(30).default(2),\n },\n annotations: { readOnlyHint: true, openWorldHint: true },\n },\n async ({ jobId, projectId, timeoutSec, pollIntervalSec }) => {\n const { job, timedOut } = await waitForJob(gql, projectId, jobId, { timeoutSec, pollIntervalSec });\n return jsonResult({ ...job, timedOut });\n },\n );\n}\n","import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { z } from 'zod';\nimport {\n DELETE_KEY,\n IMPORT_SOURCE_STRINGS,\n LIST_KEYS,\n UPDATE_KEY,\n UPSERT_KEYS,\n type GraphQLClient,\n} from '@syntholoc/sdk';\nimport { jsonResult } from '../util';\n\nconst FORMATS = ['JSON', 'YAML', 'CSV', 'PO', 'XLIFF', 'ANDROID_XML', 'IOS_STRINGS'] as const;\n\nexport function registerKeyTools(server: McpServer, gql: GraphQLClient): void {\n server.registerTool(\n 'list_keys',\n {\n title: 'List keys',\n description:\n 'Lists source keys. With `localeId` + `status` (untranslated | flagged | approved) the ' +\n 'result `totalCount` is the pending count for that status — use it for the batch threshold.',\n inputSchema: {\n projectId: z.string(),\n localeId: z.string().optional(),\n status: z.enum(['untranslated', 'flagged', 'approved']).optional(),\n search: z.string().optional(),\n skip: z.number().int().min(0).default(0),\n take: z.number().int().min(1).max(200).default(25),\n },\n annotations: { readOnlyHint: true, openWorldHint: true },\n },\n async (vars) => jsonResult((await gql.request(LIST_KEYS, vars)).keys),\n );\n\n server.registerTool(\n 'upsert_keys',\n {\n title: 'Upsert keys (bulk)',\n description:\n 'Bulk-upserts source keys by name (delta sync from local i18n files). Keys not listed are ' +\n 'left untouched. Returns added/updated/unchanged counts; set `dryRun` to preview.',\n inputSchema: {\n projectId: z.string(),\n keys: z\n .array(\n z.object({\n key: z.string(),\n sourceText: z.string(),\n context: z.string().optional(),\n maxLength: z.number().int().positive().optional(),\n }),\n )\n .min(1),\n dryRun: z.boolean().default(false),\n },\n annotations: { idempotentHint: true, openWorldHint: true },\n },\n async ({ projectId, keys, dryRun }) =>\n jsonResult((await gql.request(UPSERT_KEYS, { projectId, input: keys, dryRun })).upsertKeys),\n );\n\n server.registerTool(\n 'update_key',\n {\n title: 'Update key',\n description: 'Updates a single key (source text, context, max length).',\n inputSchema: {\n projectId: z.string(),\n keyId: z.string(),\n sourceText: z.string().optional(),\n context: z.string().optional(),\n maxLength: z.number().int().positive().optional(),\n },\n annotations: { idempotentHint: true, openWorldHint: true },\n },\n async ({ projectId, keyId, ...input }) =>\n jsonResult((await gql.request(UPDATE_KEY, { projectId, id: keyId, input })).updateKey),\n );\n\n server.registerTool(\n 'delete_key',\n {\n title: 'Delete key',\n description: 'Deletes a key and its translations. Destructive — confirm first.',\n inputSchema: { projectId: z.string(), keyId: z.string() },\n annotations: { destructiveHint: true, openWorldHint: true },\n },\n async ({ projectId, keyId }) =>\n jsonResult((await gql.request(DELETE_KEY, { projectId, id: keyId })).deleteKey),\n );\n\n server.registerTool(\n 'import_source_strings',\n {\n title: 'Import source strings',\n description:\n 'Imports source strings from a file (paste the raw content) and returns the diff summary. ' +\n 'Use `dryRun` to preview. For a structured delta prefer upsert_keys.',\n inputSchema: {\n projectId: z.string(),\n content: z.string(),\n format: z.enum(FORMATS),\n dryRun: z.boolean().default(false),\n },\n annotations: { idempotentHint: true, openWorldHint: true },\n },\n async ({ projectId, content, format, dryRun }) =>\n jsonResult(\n (await gql.request(IMPORT_SOURCE_STRINGS, { projectId, input: { content, format, dryRun } }))\n .importSourceStrings,\n ),\n );\n}\n","import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { z } from 'zod';\nimport { ADD_LOCALE, LIST_LOCALES, REMOVE_LOCALE, SEED_STARTER_LOCALES, type GraphQLClient } from '@syntholoc/sdk';\nimport { jsonResult } from '../util';\n\nexport function registerLocaleTools(server: McpServer, gql: GraphQLClient): void {\n server.registerTool(\n 'list_locales',\n {\n title: 'List locales',\n description: 'Lists the target locales configured for a project.',\n inputSchema: { projectId: z.string() },\n annotations: { readOnlyHint: true, openWorldHint: true },\n },\n async ({ projectId }) => jsonResult((await gql.request(LIST_LOCALES, { projectId })).locales),\n );\n\n server.registerTool(\n 'add_locale',\n {\n title: 'Add locale',\n description: 'Adds a target locale (BCP-47 code) to a project.',\n inputSchema: {\n projectId: z.string(),\n code: z.string().describe('BCP-47 code, e.g. \"de-DE\"'),\n name: z.string().optional(),\n formality: z.string().optional(),\n gender: z.string().optional(),\n variant: z.string().optional(),\n },\n annotations: { openWorldHint: true },\n },\n async ({ projectId, ...input }) =>\n jsonResult((await gql.request(ADD_LOCALE, { projectId, input })).addLocale),\n );\n\n server.registerTool(\n 'seed_starter_locales',\n {\n title: 'Seed starter locales',\n description: 'Adds a set of common starter locales to a project (omit codes for the defaults).',\n inputSchema: { projectId: z.string(), codes: z.array(z.string()).optional() },\n annotations: { openWorldHint: true },\n },\n async ({ projectId, codes }) =>\n jsonResult((await gql.request(SEED_STARTER_LOCALES, { projectId, codes })).seedStarterLocales),\n );\n\n server.registerTool(\n 'remove_locale',\n {\n title: 'Remove locale',\n description: 'Removes a target locale and its translations. Destructive — confirm first.',\n inputSchema: { projectId: z.string(), localeId: z.string() },\n annotations: { destructiveHint: true, openWorldHint: true },\n },\n async ({ projectId, localeId }) =>\n jsonResult((await gql.request(REMOVE_LOCALE, { projectId, id: localeId })).removeLocale),\n );\n}\n","import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { z } from 'zod';\nimport { CREATE_PROJECT, GET_PROJECT, LIST_PROJECTS, UPDATE_PROJECT, type GraphQLClient } from '@syntholoc/sdk';\nimport { jsonResult } from '../util';\n\nexport function registerProjectTools(server: McpServer, gql: GraphQLClient): void {\n server.registerTool(\n 'list_projects',\n {\n title: 'List projects',\n description: 'Lists localization projects in the active organization (paginated).',\n inputSchema: {\n skip: z.number().int().min(0).default(0),\n take: z.number().int().min(1).max(100).default(25),\n },\n annotations: { readOnlyHint: true, openWorldHint: true },\n },\n async ({ skip, take }) => jsonResult((await gql.request(LIST_PROJECTS, { skip, take })).projects),\n );\n\n server.registerTool(\n 'get_project',\n {\n title: 'Get project',\n description:\n 'Project settings plus analytics, including per-locale progress with the `untranslated` ' +\n 'count used to decide the batch threshold (see get_config).',\n inputSchema: { projectId: z.string() },\n annotations: { readOnlyHint: true, openWorldHint: true },\n },\n async ({ projectId }) => jsonResult(await gql.request(GET_PROJECT, { id: projectId })),\n );\n\n server.registerTool(\n 'create_project',\n {\n title: 'Create project',\n description: 'Creates a localization project. `sourceLocale` defaults to \"en\".',\n inputSchema: {\n name: z.string(),\n sourceLocale: z.string().default('en'),\n description: z.string().optional(),\n context: z.string().optional(),\n requireReview: z.boolean().optional(),\n qaCriticEnabled: z.boolean().optional(),\n },\n annotations: { openWorldHint: true },\n },\n async (input) => jsonResult((await gql.request(CREATE_PROJECT, { input })).createProject),\n );\n\n server.registerTool(\n 'update_project',\n {\n title: 'Update project',\n description: 'Updates project settings (name, source locale, review/QA flags, context).',\n inputSchema: {\n projectId: z.string(),\n name: z.string().optional(),\n sourceLocale: z.string().optional(),\n description: z.string().optional(),\n context: z.string().optional(),\n requireReview: z.boolean().optional(),\n qaCriticEnabled: z.boolean().optional(),\n exportApprovedOnly: z.boolean().optional(),\n sortKeysAlphabetically: z.boolean().optional(),\n status: z.string().optional(),\n },\n annotations: { idempotentHint: true, openWorldHint: true },\n },\n async ({ projectId, ...input }) =>\n jsonResult((await gql.request(UPDATE_PROJECT, { id: projectId, input })).updateProject),\n );\n}\n","import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { z } from 'zod';\nimport { CONFIGURE_LLM_PROVIDER, LIST_LLM_PROVIDERS, type GraphQLClient } from '@syntholoc/sdk';\nimport { jsonResult } from '../util';\n\nexport function registerProviderTools(server: McpServer, gql: GraphQLClient): void {\n server.registerTool(\n 'list_llm_providers',\n {\n title: 'List LLM providers',\n description: 'Configured LLM providers for the org (never exposes the API key).',\n inputSchema: {},\n annotations: { readOnlyHint: true, openWorldHint: true },\n },\n async () => jsonResult((await gql.request(LIST_LLM_PROVIDERS)).llmProviders),\n );\n\n server.registerTool(\n 'configure_llm_provider',\n {\n title: 'Configure LLM provider',\n description:\n 'Adds a BYOK LLM provider (OPENROUTER | OPENAI | ANTHROPIC). The key is stored encrypted at rest.',\n inputSchema: {\n type: z.enum(['OPENROUTER', 'OPENAI', 'ANTHROPIC']),\n label: z.string(),\n apiKey: z.string(),\n baseUrl: z.string().optional(),\n defaultModel: z.string().optional(),\n },\n annotations: { openWorldHint: true },\n },\n async (input) => jsonResult((await gql.request(CONFIGURE_LLM_PROVIDER, { input })).configureLlmProvider),\n );\n}\n","import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { z } from 'zod';\nimport { LIST_QA_ISSUES, RUN_LOCALE_QA, RUN_QA, type GraphQLClient } from '@syntholoc/sdk';\nimport { jsonResult } from '../util';\n\nexport function registerQaTools(server: McpServer, gql: GraphQLClient): void {\n server.registerTool(\n 'run_locale_qa',\n {\n title: 'Run locale QA',\n description:\n \"Queues an LLM QA-critic pass over a locale's non-approved translations. Returns tracking `jobIds`.\",\n inputSchema: { projectId: z.string(), localeId: z.string() },\n annotations: { openWorldHint: true },\n },\n async (vars) => jsonResult((await gql.request(RUN_LOCALE_QA, vars)).runLocaleQa),\n );\n\n server.registerTool(\n 'run_qa',\n {\n title: 'Run QA on a translation',\n description: 'Runs QA on one translation and returns its score and issues (synchronous).',\n inputSchema: { projectId: z.string(), translationId: z.string() },\n annotations: { openWorldHint: true },\n },\n async (vars) => jsonResult((await gql.request(RUN_QA, vars)).runQa),\n );\n\n server.registerTool(\n 'list_qa_issues',\n {\n title: 'List QA issues',\n description: 'Open QA issues for a locale, plus the issue-type facets (filter tabs).',\n inputSchema: {\n projectId: z.string(),\n localeId: z.string(),\n type: z.string().optional(),\n skip: z.number().int().min(0).default(0),\n take: z.number().int().min(1).max(100).default(25),\n },\n annotations: { readOnlyHint: true, openWorldHint: true },\n },\n async (vars) => jsonResult(await gql.request(LIST_QA_ISSUES, vars)),\n );\n}\n","import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { z } from 'zod';\nimport { CREATE_STYLE_GUIDE, GENERATE_STYLE_GUIDE, LIST_STYLE_GUIDES, type GraphQLClient } from '@syntholoc/sdk';\nimport { jsonResult } from '../util';\n\nexport function registerStyleGuideTools(server: McpServer, gql: GraphQLClient): void {\n server.registerTool(\n 'list_style_guides',\n {\n title: 'List style guides',\n description: 'Lists style guides (project-scoped if projectId given, else org-wide).',\n inputSchema: { projectId: z.string().optional() },\n annotations: { readOnlyHint: true, openWorldHint: true },\n },\n async ({ projectId }) => jsonResult((await gql.request(LIST_STYLE_GUIDES, { projectId })).styleGuides),\n );\n\n server.registerTool(\n 'generate_style_guide',\n {\n title: 'Generate style guide (draft)',\n description:\n 'Drafts a tone/rules/prompt-fragment style guide from the project (optionally per locale). ' +\n 'Returns a draft — persist it with create_style_guide.',\n inputSchema: { projectId: z.string(), localeCode: z.string().optional() },\n annotations: { openWorldHint: true },\n },\n async ({ projectId, localeCode }) =>\n jsonResult((await gql.request(GENERATE_STYLE_GUIDE, { input: { projectId, localeCode } })).generateStyleGuide),\n );\n\n server.registerTool(\n 'create_style_guide',\n {\n title: 'Create style guide',\n description: 'Persists a style guide (tone, rules JSON, prompt fragment), project- or locale-scoped.',\n inputSchema: {\n name: z.string(),\n projectId: z.string().optional(),\n localeCode: z.string().optional(),\n tone: z.string().optional(),\n promptFragment: z.string().optional(),\n rules: z.array(z.string()).optional(),\n },\n annotations: { openWorldHint: true },\n },\n async (input) => jsonResult((await gql.request(CREATE_STYLE_GUIDE, { input })).createStyleGuide),\n );\n}\n","import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { z } from 'zod';\nimport {\n APPROVE_TRANSLATION,\n EXPORT_LOCALE,\n IMPORT_TRANSLATIONS,\n LIST_TRANSLATIONS,\n REJECT_TRANSLATION,\n SET_TRANSLATION,\n TRANSLATE_KEY,\n TRANSLATE_LOCALE,\n type GraphQLClient,\n} from '@syntholoc/sdk';\nimport { jsonResult } from '../util';\n\nconst FORMATS = ['JSON', 'YAML', 'CSV', 'PO', 'XLIFF', 'ANDROID_XML', 'IOS_STRINGS'] as const;\n\nexport function registerTranslationTools(server: McpServer, gql: GraphQLClient): void {\n server.registerTool(\n 'translate_locale',\n {\n title: 'Translate locale',\n description:\n 'Queues AI translation of a locale (scope ALL | UNAPPROVED | UNTRANSLATED) and returns the ' +\n 'tracking `jobIds`. Executes immediately and unconditionally — the batch threshold is a ' +\n 'client-side policy for PROACTIVE syncs, not for this tool.',\n inputSchema: {\n projectId: z.string(),\n localeId: z.string(),\n scope: z.enum(['ALL', 'UNAPPROVED', 'UNTRANSLATED']).default('UNTRANSLATED'),\n },\n annotations: { openWorldHint: true },\n },\n async (vars) => jsonResult((await gql.request(TRANSLATE_LOCALE, vars)).translateLocale),\n );\n\n server.registerTool(\n 'translate_key',\n {\n title: 'Translate key',\n description: 'Queues AI translation of a single key into a locale. Returns tracking `jobIds`.',\n inputSchema: { projectId: z.string(), keyId: z.string(), localeId: z.string() },\n annotations: { openWorldHint: true },\n },\n async (vars) => jsonResult((await gql.request(TRANSLATE_KEY, vars)).translateKey),\n );\n\n server.registerTool(\n 'set_translation',\n {\n title: 'Set translation',\n description: 'Manually sets the translation text for a key in a locale.',\n inputSchema: {\n projectId: z.string(),\n keyId: z.string(),\n localeId: z.string(),\n text: z.string(),\n origin: z.string().optional(),\n },\n annotations: { idempotentHint: true, openWorldHint: true },\n },\n async ({ projectId, keyId, localeId, text, origin }) =>\n jsonResult(\n (await gql.request(SET_TRANSLATION, { projectId, input: { keyId, localeId, text, origin } }))\n .setTranslation,\n ),\n );\n\n server.registerTool(\n 'list_translations',\n {\n title: 'List translations',\n description: 'Lists translations for a locale with status, origin, model and QA score.',\n inputSchema: {\n projectId: z.string(),\n localeId: z.string(),\n skip: z.number().int().min(0).default(0),\n take: z.number().int().min(1).max(200).default(25),\n },\n annotations: { readOnlyHint: true, openWorldHint: true },\n },\n async (vars) => jsonResult((await gql.request(LIST_TRANSLATIONS, vars)).translations),\n );\n\n server.registerTool(\n 'approve_translation',\n {\n title: 'Approve translation',\n description: 'Marks a translation APPROVED.',\n inputSchema: { projectId: z.string(), translationId: z.string() },\n annotations: { idempotentHint: true, openWorldHint: true },\n },\n async ({ projectId, translationId }) =>\n jsonResult((await gql.request(APPROVE_TRANSLATION, { projectId, id: translationId })).approveTranslation),\n );\n\n server.registerTool(\n 'reject_translation',\n {\n title: 'Reject translation',\n description: 'Marks a translation REJECTED (queued for re-translation).',\n inputSchema: { projectId: z.string(), translationId: z.string() },\n annotations: { idempotentHint: true, openWorldHint: true },\n },\n async ({ projectId, translationId }) =>\n jsonResult((await gql.request(REJECT_TRANSLATION, { projectId, id: translationId })).rejectTranslation),\n );\n\n server.registerTool(\n 'export_locale',\n {\n title: 'Export locale',\n description:\n 'Exports a locale to a file string in the chosen format — the basis for writing translations ' +\n 'back into local files. Set `onlyApproved` to exclude drafts.',\n inputSchema: {\n projectId: z.string(),\n localeId: z.string(),\n format: z.enum(FORMATS).default('JSON'),\n onlyApproved: z.boolean().optional(),\n },\n annotations: { readOnlyHint: true, openWorldHint: true },\n },\n async (vars) => jsonResult({ content: (await gql.request(EXPORT_LOCALE, vars)).exportLocale }),\n );\n\n server.registerTool(\n 'import_translations',\n {\n title: 'Import translations',\n description:\n 'Imports ready-made translations for a locale (paste file content). `autoApprove` lands them ' +\n 'as APPROVED; `conflict` is OVERWRITE | SKIP_EXISTING | KEEP_APPROVED; `dryRun` previews.',\n inputSchema: {\n projectId: z.string(),\n localeId: z.string(),\n content: z.string(),\n format: z.enum(FORMATS),\n autoApprove: z.boolean().default(false),\n conflict: z.enum(['OVERWRITE', 'SKIP_EXISTING', 'KEEP_APPROVED']).default('OVERWRITE'),\n dryRun: z.boolean().default(false),\n },\n annotations: { idempotentHint: true, openWorldHint: true },\n },\n async ({ projectId, localeId, content, format, autoApprove, conflict, dryRun }) =>\n jsonResult(\n (\n await gql.request(IMPORT_TRANSLATIONS, {\n projectId,\n localeId,\n input: { content, format, autoApprove, conflict, dryRun },\n })\n ).importTranslations,\n ),\n );\n}\n","import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport type { GraphQLClient } from '@syntholoc/sdk';\nimport { registerAnalyticsTools } from './analytics';\nimport { registerConfigTools } from './config';\nimport { registerGitTools } from './git';\nimport { registerGlossaryTools } from './glossary';\nimport { registerJobTools } from './jobs';\nimport { registerKeyTools } from './keys';\nimport { registerLocaleTools } from './locales';\nimport { registerProjectTools } from './projects';\nimport { registerProviderTools } from './providers';\nimport { registerQaTools } from './qa';\nimport { registerStyleGuideTools } from './styleguides';\nimport { registerTranslationTools } from './translations';\n\n/** Registers the full Syntholoc tool surface on the given MCP server. */\nexport function registerAllTools(server: McpServer, gql: GraphQLClient): void {\n registerConfigTools(server, gql);\n registerProjectTools(server, gql);\n registerLocaleTools(server, gql);\n registerKeyTools(server, gql);\n registerTranslationTools(server, gql);\n registerQaTools(server, gql);\n registerGlossaryTools(server, gql);\n registerStyleGuideTools(server, gql);\n registerGitTools(server, gql);\n registerProviderTools(server, gql);\n registerJobTools(server, gql);\n registerAnalyticsTools(server, gql);\n}\n"],"mappings":"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","names":["z","z","z","z","z","z","data","z","z","z","z","z","z","z","z","z","z","z","z","z","z","z","FORMATS","z"]}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
buildMcpServer
|
|
4
|
+
} from "./chunk-N4PXYXAP.js";
|
|
5
|
+
|
|
6
|
+
// src/index.ts
|
|
7
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
8
|
+
import { GraphQLClient } from "@syntholoc/sdk";
|
|
9
|
+
|
|
10
|
+
// src/config.ts
|
|
11
|
+
function loadConfig(env = process.env) {
|
|
12
|
+
return {
|
|
13
|
+
apiUrl: env.SYNTHOLOC_API_URL ?? "http://localhost:4000/graphql",
|
|
14
|
+
apiKey: env.SYNTHOLOC_API_KEY
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// src/index.ts
|
|
19
|
+
async function main() {
|
|
20
|
+
const config = loadConfig();
|
|
21
|
+
const gql = new GraphQLClient(config);
|
|
22
|
+
const server = buildMcpServer(gql);
|
|
23
|
+
await server.connect(new StdioServerTransport());
|
|
24
|
+
console.error(`[syntholoc-mcp] ready (api: ${config.apiUrl}${config.apiKey ? "" : ", no API key set"})`);
|
|
25
|
+
}
|
|
26
|
+
main().catch((err) => {
|
|
27
|
+
console.error("[syntholoc-mcp] fatal:", err);
|
|
28
|
+
process.exit(1);
|
|
29
|
+
});
|
|
30
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/config.ts"],"sourcesContent":["#!/usr/bin/env node\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\nimport { GraphQLClient } from '@syntholoc/sdk';\nimport { buildMcpServer } from './build-server';\nimport { loadConfig } from './config';\n\nasync function main(): Promise<void> {\n const config = loadConfig();\n const gql = new GraphQLClient(config);\n const server = buildMcpServer(gql);\n\n await server.connect(new StdioServerTransport());\n // stdout is the MCP transport — log diagnostics to stderr only.\n console.error(`[syntholoc-mcp] ready (api: ${config.apiUrl}${config.apiKey ? '' : ', no API key set'})`);\n}\n\nmain().catch((err: unknown) => {\n console.error('[syntholoc-mcp] fatal:', err);\n process.exit(1);\n});\n","import type { ClientConfig } from '@syntholoc/sdk';\n\nexport function loadConfig(env: NodeJS.ProcessEnv = process.env): ClientConfig {\n return {\n apiUrl: env.SYNTHOLOC_API_URL ?? 'http://localhost:4000/graphql',\n apiKey: env.SYNTHOLOC_API_KEY,\n };\n}\n"],"mappings":";;;;;;AACA,SAAS,4BAA4B;AACrC,SAAS,qBAAqB;;;ACAvB,SAAS,WAAW,MAAyB,QAAQ,KAAmB;AAC7E,SAAO;AAAA,IACL,QAAQ,IAAI,qBAAqB;AAAA,IACjC,QAAQ,IAAI;AAAA,EACd;AACF;;;ADDA,eAAe,OAAsB;AACnC,QAAM,SAAS,WAAW;AAC1B,QAAM,MAAM,IAAI,cAAc,MAAM;AACpC,QAAM,SAAS,eAAe,GAAG;AAEjC,QAAM,OAAO,QAAQ,IAAI,qBAAqB,CAAC;AAE/C,UAAQ,MAAM,+BAA+B,OAAO,MAAM,GAAG,OAAO,SAAS,KAAK,kBAAkB,GAAG;AACzG;AAEA,KAAK,EAAE,MAAM,CAAC,QAAiB;AAC7B,UAAQ,MAAM,0BAA0B,GAAG;AAC3C,UAAQ,KAAK,CAAC;AAChB,CAAC;","names":[]}
|
package/package.json
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@syntholoc/mcp",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Syntholoc MCP server — use the localization platform from Claude Code.",
|
|
5
|
+
"homepage": "https://github.com/SergiyIva/syntholoc/tree/main/apps/mcp#readme",
|
|
6
|
+
"keywords": [
|
|
7
|
+
"syntholoc",
|
|
8
|
+
"localization",
|
|
9
|
+
"i18n",
|
|
10
|
+
"translation",
|
|
11
|
+
"mcp",
|
|
12
|
+
"model-context-protocol",
|
|
13
|
+
"claude-code"
|
|
14
|
+
],
|
|
15
|
+
"type": "module",
|
|
16
|
+
"license": "MIT",
|
|
17
|
+
"repository": {
|
|
18
|
+
"type": "git",
|
|
19
|
+
"url": "git+https://github.com/SergiyIva/syntholoc.git",
|
|
20
|
+
"directory": "apps/mcp"
|
|
21
|
+
},
|
|
22
|
+
"bin": {
|
|
23
|
+
"syntholoc-mcp": "./dist/index.js"
|
|
24
|
+
},
|
|
25
|
+
"exports": {
|
|
26
|
+
"./server": {
|
|
27
|
+
"types": "./dist/build-server.d.ts",
|
|
28
|
+
"default": "./dist/build-server.js"
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
"files": [
|
|
32
|
+
"dist",
|
|
33
|
+
"src"
|
|
34
|
+
],
|
|
35
|
+
"publishConfig": {
|
|
36
|
+
"registry": "https://registry.npmjs.org",
|
|
37
|
+
"access": "public",
|
|
38
|
+
"bin": {
|
|
39
|
+
"syntholoc-mcp": "./dist/index.js"
|
|
40
|
+
},
|
|
41
|
+
"exports": {
|
|
42
|
+
"./server": {
|
|
43
|
+
"types": "./dist/build-server.d.ts",
|
|
44
|
+
"default": "./dist/build-server.js"
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
"dependencies": {
|
|
49
|
+
"@modelcontextprotocol/sdk": "1.22.0",
|
|
50
|
+
"@syntholoc/sdk": "0.1.0",
|
|
51
|
+
"zod": "3.25.76"
|
|
52
|
+
},
|
|
53
|
+
"devDependencies": {
|
|
54
|
+
"@types/bun": "1.3.14",
|
|
55
|
+
"@types/node": "22.19.21",
|
|
56
|
+
"tsup": "8.5.1",
|
|
57
|
+
"typescript": "6.0.3"
|
|
58
|
+
}
|
|
59
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
|
+
import type { GraphQLClient } from '@syntholoc/sdk';
|
|
3
|
+
import { registerPrompts } from './prompts';
|
|
4
|
+
import { registerResources } from './resources';
|
|
5
|
+
import { registerAllTools } from './tools';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Builds a fully-configured Syntholoc MCP server (tools + resources + prompts)
|
|
9
|
+
* bound to the given GraphQL client. Shared by the stdio entrypoint and the
|
|
10
|
+
* NestJS remote `/mcp` (Streamable HTTP) endpoint.
|
|
11
|
+
*/
|
|
12
|
+
export function buildMcpServer(gql: GraphQLClient): McpServer {
|
|
13
|
+
const server = new McpServer({ name: 'syntholoc', version: '0.1.0' });
|
|
14
|
+
registerAllTools(server, gql);
|
|
15
|
+
registerResources(server, gql);
|
|
16
|
+
registerPrompts(server);
|
|
17
|
+
return server;
|
|
18
|
+
}
|
package/src/config.ts
ADDED
package/src/index.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
3
|
+
import { GraphQLClient } from '@syntholoc/sdk';
|
|
4
|
+
import { buildMcpServer } from './build-server';
|
|
5
|
+
import { loadConfig } from './config';
|
|
6
|
+
|
|
7
|
+
async function main(): Promise<void> {
|
|
8
|
+
const config = loadConfig();
|
|
9
|
+
const gql = new GraphQLClient(config);
|
|
10
|
+
const server = buildMcpServer(gql);
|
|
11
|
+
|
|
12
|
+
await server.connect(new StdioServerTransport());
|
|
13
|
+
// stdout is the MCP transport — log diagnostics to stderr only.
|
|
14
|
+
console.error(`[syntholoc-mcp] ready (api: ${config.apiUrl}${config.apiKey ? '' : ', no API key set'})`);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
main().catch((err: unknown) => {
|
|
18
|
+
console.error('[syntholoc-mcp] fatal:', err);
|
|
19
|
+
process.exit(1);
|
|
20
|
+
});
|
package/src/prompts.ts
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
|
|
4
|
+
function userMessage(text: string) {
|
|
5
|
+
return { messages: [{ role: 'user' as const, content: { type: 'text' as const, text } }] };
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
/** Registers workflow prompts (surfaced in Claude Code as /mcp__syntholoc__*). */
|
|
9
|
+
export function registerPrompts(server: McpServer): void {
|
|
10
|
+
server.registerPrompt(
|
|
11
|
+
'localize_changed',
|
|
12
|
+
{
|
|
13
|
+
title: 'Localize changed keys',
|
|
14
|
+
description: 'Sync changed source strings from the local diff, translate them, and write back.',
|
|
15
|
+
argsSchema: { projectId: z.string().optional() },
|
|
16
|
+
},
|
|
17
|
+
({ projectId }) =>
|
|
18
|
+
userMessage(
|
|
19
|
+
[
|
|
20
|
+
'Localize my recent source-string changes via Syntholoc:',
|
|
21
|
+
'1. Read `.syntholoc/config.json` for the source file(s) and project.',
|
|
22
|
+
projectId ? ` (Use project ${projectId} if config is missing.)` : '',
|
|
23
|
+
'2. From `git diff` on the source file(s), collect added/changed keys and `upsert_keys` (dryRun first to preview).',
|
|
24
|
+
'3. Call `get_config` for `maxBulkSize`. For EACH target locale check `locale_progress` → `untranslated`.',
|
|
25
|
+
' - If untranslated >= maxBulkSize: `translate_locale` (scope UNTRANSLATED) and `wait_for_job`.',
|
|
26
|
+
' - If below the threshold: tell me the counts and ASK whether to translate now or accumulate.',
|
|
27
|
+
'4. `export_locale` for each translated locale and write the result back into the mapped local files.',
|
|
28
|
+
'5. Summarize keys added, locales translated, and the cost from the job usage.',
|
|
29
|
+
]
|
|
30
|
+
.filter(Boolean)
|
|
31
|
+
.join('\n'),
|
|
32
|
+
),
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
server.registerPrompt(
|
|
36
|
+
'review_locale',
|
|
37
|
+
{
|
|
38
|
+
title: 'Review a locale',
|
|
39
|
+
description: 'Surface low-confidence and QA-flagged translations for a locale and help fix them.',
|
|
40
|
+
argsSchema: { projectId: z.string(), locale: z.string() },
|
|
41
|
+
},
|
|
42
|
+
({ projectId, locale }) =>
|
|
43
|
+
userMessage(
|
|
44
|
+
[
|
|
45
|
+
`Review the "${locale}" locale in project ${projectId}:`,
|
|
46
|
+
'1. Resolve the locale id via `list_locales`.',
|
|
47
|
+
'2. `list_qa_issues` and `list_translations` (focus on low qaScore / non-approved).',
|
|
48
|
+
'3. For each problem, propose a fix; apply with `set_translation`, then `approve_translation`.',
|
|
49
|
+
'4. Summarize what changed and what still needs human attention.',
|
|
50
|
+
].join('\n'),
|
|
51
|
+
),
|
|
52
|
+
);
|
|
53
|
+
|
|
54
|
+
server.registerPrompt(
|
|
55
|
+
'bootstrap_project',
|
|
56
|
+
{
|
|
57
|
+
title: 'Bootstrap a project from this repo',
|
|
58
|
+
description: 'Detect i18n files, create a Syntholoc project, seed locales, and write .syntholoc/config.json.',
|
|
59
|
+
argsSchema: {},
|
|
60
|
+
},
|
|
61
|
+
() =>
|
|
62
|
+
userMessage(
|
|
63
|
+
[
|
|
64
|
+
'Set up Syntholoc for this repository:',
|
|
65
|
+
'1. Find the i18n source files (e.g. messages/en.json, locales/en/*.json) and infer the source locale.',
|
|
66
|
+
'2. `create_project`, then `seed_starter_locales` (or `add_locale`) for the target languages.',
|
|
67
|
+
'3. `import_source_strings` from the source file(s).',
|
|
68
|
+
'4. Write `.syntholoc/config.json` mapping source → translation file patterns and the project id.',
|
|
69
|
+
'5. Offer to run the localize_changed flow next.',
|
|
70
|
+
].join('\n'),
|
|
71
|
+
),
|
|
72
|
+
);
|
|
73
|
+
}
|
package/src/resources.ts
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
|
+
import { ResourceTemplate } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
3
|
+
import { CONFIG_RESOURCE, PROJECTS_RESOURCE, PROJECT_RESOURCE, type GraphQLClient } from '@syntholoc/sdk';
|
|
4
|
+
|
|
5
|
+
function json(uri: URL, data: unknown) {
|
|
6
|
+
return { contents: [{ uri: uri.href, mimeType: 'application/json', text: JSON.stringify(data, null, 2) }] };
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
/** Registers read-only MCP resources so the model can pull context cheaply. */
|
|
10
|
+
export function registerResources(server: McpServer, gql: GraphQLClient): void {
|
|
11
|
+
server.registerResource(
|
|
12
|
+
'config',
|
|
13
|
+
'syntholoc://config',
|
|
14
|
+
{
|
|
15
|
+
title: 'Syntholoc config',
|
|
16
|
+
description: 'Server metadata incl. maxBulkSize (auto-translate batch threshold).',
|
|
17
|
+
mimeType: 'application/json',
|
|
18
|
+
},
|
|
19
|
+
async (uri) => json(uri, (await gql.request(CONFIG_RESOURCE)).appInfo),
|
|
20
|
+
);
|
|
21
|
+
|
|
22
|
+
server.registerResource(
|
|
23
|
+
'projects',
|
|
24
|
+
'syntholoc://projects',
|
|
25
|
+
{
|
|
26
|
+
title: 'Projects',
|
|
27
|
+
description: 'All localization projects in the active organization.',
|
|
28
|
+
mimeType: 'application/json',
|
|
29
|
+
},
|
|
30
|
+
async (uri) => json(uri, (await gql.request(PROJECTS_RESOURCE)).projects),
|
|
31
|
+
);
|
|
32
|
+
|
|
33
|
+
server.registerResource(
|
|
34
|
+
'project',
|
|
35
|
+
new ResourceTemplate('syntholoc://project/{projectId}', { list: undefined }),
|
|
36
|
+
{
|
|
37
|
+
title: 'Project overview',
|
|
38
|
+
description: 'A project with its locales and per-locale progress.',
|
|
39
|
+
mimeType: 'application/json',
|
|
40
|
+
},
|
|
41
|
+
async (uri, { projectId }) => json(uri, await gql.request(PROJECT_RESOURCE, { id: String(projectId) })),
|
|
42
|
+
);
|
|
43
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import { COST_ANALYTICS, LOCALE_PROGRESS, PROJECT_ANALYTICS, USAGE, type GraphQLClient } from '@syntholoc/sdk';
|
|
4
|
+
import { jsonResult } from '../util';
|
|
5
|
+
|
|
6
|
+
export function registerAnalyticsTools(server: McpServer, gql: GraphQLClient): void {
|
|
7
|
+
server.registerTool(
|
|
8
|
+
'project_analytics',
|
|
9
|
+
{
|
|
10
|
+
title: 'Project analytics',
|
|
11
|
+
description: 'Real-time quality + progress metrics for a project (per-locale progress, open issues).',
|
|
12
|
+
inputSchema: { projectId: z.string() },
|
|
13
|
+
annotations: { readOnlyHint: true, openWorldHint: true },
|
|
14
|
+
},
|
|
15
|
+
async ({ projectId }) => jsonResult((await gql.request(PROJECT_ANALYTICS, { projectId })).projectAnalytics),
|
|
16
|
+
);
|
|
17
|
+
|
|
18
|
+
server.registerTool(
|
|
19
|
+
'cost_analytics',
|
|
20
|
+
{
|
|
21
|
+
title: 'Cost analytics',
|
|
22
|
+
description: 'LLM token/cost time-series and per-model breakdown over a window (default 30 days).',
|
|
23
|
+
inputSchema: {
|
|
24
|
+
projectId: z.string().optional(),
|
|
25
|
+
days: z.number().int().min(1).max(365).optional(),
|
|
26
|
+
},
|
|
27
|
+
annotations: { readOnlyHint: true, openWorldHint: true },
|
|
28
|
+
},
|
|
29
|
+
async (vars) => jsonResult((await gql.request(COST_ANALYTICS, vars)).costAnalytics),
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
server.registerTool(
|
|
33
|
+
'usage',
|
|
34
|
+
{
|
|
35
|
+
title: 'Org usage & plan',
|
|
36
|
+
description: 'Organization plan, hosted words and seat usage.',
|
|
37
|
+
inputSchema: {},
|
|
38
|
+
annotations: { readOnlyHint: true, openWorldHint: true },
|
|
39
|
+
},
|
|
40
|
+
async () => jsonResult((await gql.request(USAGE)).usage),
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
server.registerTool(
|
|
44
|
+
'locale_progress',
|
|
45
|
+
{
|
|
46
|
+
title: 'Locale progress',
|
|
47
|
+
description:
|
|
48
|
+
'Progress counts for one locale: total / untranslated / autoTranslated / humanApproved. ' +
|
|
49
|
+
'The `untranslated` count drives the batch threshold (see get_config).',
|
|
50
|
+
inputSchema: { projectId: z.string(), localeId: z.string() },
|
|
51
|
+
annotations: { readOnlyHint: true, openWorldHint: true },
|
|
52
|
+
},
|
|
53
|
+
async (vars) => jsonResult((await gql.request(LOCALE_PROGRESS, vars)).localeProgress),
|
|
54
|
+
);
|
|
55
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
|
+
import { APP_INFO, type GraphQLClient } from '@syntholoc/sdk';
|
|
3
|
+
import { jsonResult } from '../util';
|
|
4
|
+
|
|
5
|
+
export function registerConfigTools(server: McpServer, gql: GraphQLClient): void {
|
|
6
|
+
server.registerTool(
|
|
7
|
+
'get_config',
|
|
8
|
+
{
|
|
9
|
+
title: 'Get Syntholoc config',
|
|
10
|
+
description:
|
|
11
|
+
'Server metadata incl. `maxBulkSize` — the max keys per LLM batch. Use it as the ' +
|
|
12
|
+
'auto-translate threshold: translate proactively only when pending untranslated keys ' +
|
|
13
|
+
'reach this number; otherwise ask the user (an explicit translate request always runs).',
|
|
14
|
+
inputSchema: {},
|
|
15
|
+
annotations: { readOnlyHint: true, openWorldHint: true },
|
|
16
|
+
},
|
|
17
|
+
async () => jsonResult((await gql.request(APP_INFO)).appInfo),
|
|
18
|
+
);
|
|
19
|
+
}
|
package/src/tools/git.ts
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import {
|
|
4
|
+
LIST_REPOSITORIES,
|
|
5
|
+
PULL_TRANSLATIONS,
|
|
6
|
+
PUSH_TRANSLATIONS,
|
|
7
|
+
SYNC_REPOSITORY,
|
|
8
|
+
type GraphQLClient,
|
|
9
|
+
} from '@syntholoc/sdk';
|
|
10
|
+
import { jsonResult } from '../util';
|
|
11
|
+
|
|
12
|
+
export function registerGitTools(server: McpServer, gql: GraphQLClient): void {
|
|
13
|
+
server.registerTool(
|
|
14
|
+
'list_repositories',
|
|
15
|
+
{
|
|
16
|
+
title: 'List repositories',
|
|
17
|
+
description: 'Lists git repositories connected to a project.',
|
|
18
|
+
inputSchema: { projectId: z.string() },
|
|
19
|
+
annotations: { readOnlyHint: true, openWorldHint: true },
|
|
20
|
+
},
|
|
21
|
+
async ({ projectId }) => jsonResult((await gql.request(LIST_REPOSITORIES, { projectId })).repositories),
|
|
22
|
+
);
|
|
23
|
+
|
|
24
|
+
server.registerTool(
|
|
25
|
+
'sync_repository',
|
|
26
|
+
{
|
|
27
|
+
title: 'Sync repository',
|
|
28
|
+
description: 'Queues an immediate sync of a connected repository (pull → translate → PR).',
|
|
29
|
+
inputSchema: { projectId: z.string(), repositoryId: z.string() },
|
|
30
|
+
annotations: { openWorldHint: true },
|
|
31
|
+
},
|
|
32
|
+
async ({ projectId, repositoryId }) =>
|
|
33
|
+
jsonResult((await gql.request(SYNC_REPOSITORY, { projectId, id: repositoryId })).syncRepository),
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
server.registerTool(
|
|
37
|
+
'push_translations',
|
|
38
|
+
{
|
|
39
|
+
title: 'Push translations (PR)',
|
|
40
|
+
description: 'Queues write-back of a locale: export approved translations and open a PR.',
|
|
41
|
+
inputSchema: { projectId: z.string(), repositoryId: z.string(), localeId: z.string() },
|
|
42
|
+
annotations: { openWorldHint: true },
|
|
43
|
+
},
|
|
44
|
+
async (vars) => jsonResult((await gql.request(PUSH_TRANSLATIONS, vars)).pushTranslations),
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
server.registerTool(
|
|
48
|
+
'pull_translations',
|
|
49
|
+
{
|
|
50
|
+
title: 'Pull translations',
|
|
51
|
+
description:
|
|
52
|
+
'Pulls translation files from the connected repo and imports them per locale. ' +
|
|
53
|
+
'`autoApprove` lands them as APPROVED; `conflict` is OVERWRITE | SKIP_EXISTING | KEEP_APPROVED.',
|
|
54
|
+
inputSchema: {
|
|
55
|
+
projectId: z.string(),
|
|
56
|
+
repositoryId: z.string(),
|
|
57
|
+
autoApprove: z.boolean().default(false),
|
|
58
|
+
conflict: z.enum(['OVERWRITE', 'SKIP_EXISTING', 'KEEP_APPROVED']).default('OVERWRITE'),
|
|
59
|
+
},
|
|
60
|
+
annotations: { openWorldHint: true },
|
|
61
|
+
},
|
|
62
|
+
async ({ projectId, repositoryId, autoApprove, conflict }) =>
|
|
63
|
+
jsonResult(
|
|
64
|
+
(await gql.request(PULL_TRANSLATIONS, { projectId, repositoryId, input: { autoApprove, conflict } }))
|
|
65
|
+
.pullTranslations,
|
|
66
|
+
),
|
|
67
|
+
);
|
|
68
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import {
|
|
4
|
+
CREATE_GLOSSARY_TERM,
|
|
5
|
+
DELETE_GLOSSARY_TERM,
|
|
6
|
+
LIST_GLOSSARY,
|
|
7
|
+
SUGGEST_GLOSSARY_TERMS,
|
|
8
|
+
UPDATE_GLOSSARY_TERM,
|
|
9
|
+
type GraphQLClient,
|
|
10
|
+
} from '@syntholoc/sdk';
|
|
11
|
+
import { jsonResult } from '../util';
|
|
12
|
+
|
|
13
|
+
export function registerGlossaryTools(server: McpServer, gql: GraphQLClient): void {
|
|
14
|
+
server.registerTool(
|
|
15
|
+
'list_glossary',
|
|
16
|
+
{
|
|
17
|
+
title: 'List glossary terms',
|
|
18
|
+
description: 'Lists glossary terms (project-scoped if projectId given, else org-wide).',
|
|
19
|
+
inputSchema: {
|
|
20
|
+
projectId: z.string().optional(),
|
|
21
|
+
skip: z.number().int().min(0).default(0),
|
|
22
|
+
take: z.number().int().min(1).max(100).default(25),
|
|
23
|
+
},
|
|
24
|
+
annotations: { readOnlyHint: true, openWorldHint: true },
|
|
25
|
+
},
|
|
26
|
+
async (vars) => jsonResult((await gql.request(LIST_GLOSSARY, vars)).glossaryTerms),
|
|
27
|
+
);
|
|
28
|
+
|
|
29
|
+
server.registerTool(
|
|
30
|
+
'upsert_glossary_term',
|
|
31
|
+
{
|
|
32
|
+
title: 'Create or update glossary term',
|
|
33
|
+
description:
|
|
34
|
+
'Creates a glossary term, or updates it when `termId` is given. `translations` is a ' +
|
|
35
|
+
'per-locale map, e.g. {"de-DE":"…"}. `forbidden` lists disallowed renderings.',
|
|
36
|
+
inputSchema: {
|
|
37
|
+
termId: z.string().optional().describe('Provide to update an existing term'),
|
|
38
|
+
projectId: z.string().optional().describe('Project scope; omit for org-wide (create only)'),
|
|
39
|
+
term: z.string().optional(),
|
|
40
|
+
description: z.string().optional(),
|
|
41
|
+
partOfSpeech: z.string().optional(),
|
|
42
|
+
doNotTranslate: z.boolean().optional(),
|
|
43
|
+
forbidden: z.array(z.string()).optional(),
|
|
44
|
+
translations: z.record(z.string(), z.string()).optional(),
|
|
45
|
+
},
|
|
46
|
+
annotations: { idempotentHint: true, openWorldHint: true },
|
|
47
|
+
},
|
|
48
|
+
async ({ termId, projectId, ...rest }) => {
|
|
49
|
+
if (termId) {
|
|
50
|
+
const data = await gql.request(UPDATE_GLOSSARY_TERM, { id: termId, input: rest });
|
|
51
|
+
return jsonResult(data.updateGlossaryTerm);
|
|
52
|
+
}
|
|
53
|
+
if (!rest.term) throw new Error('`term` is required when creating a glossary term');
|
|
54
|
+
const data = await gql.request(CREATE_GLOSSARY_TERM, {
|
|
55
|
+
input: { term: rest.term, ...rest, projectId },
|
|
56
|
+
});
|
|
57
|
+
return jsonResult(data.createGlossaryTerm);
|
|
58
|
+
},
|
|
59
|
+
);
|
|
60
|
+
|
|
61
|
+
server.registerTool(
|
|
62
|
+
'delete_glossary_term',
|
|
63
|
+
{
|
|
64
|
+
title: 'Delete glossary term',
|
|
65
|
+
description: 'Deletes a glossary term. Destructive — confirm first.',
|
|
66
|
+
inputSchema: { termId: z.string() },
|
|
67
|
+
annotations: { destructiveHint: true, openWorldHint: true },
|
|
68
|
+
},
|
|
69
|
+
async ({ termId }) =>
|
|
70
|
+
jsonResult((await gql.request(DELETE_GLOSSARY_TERM, { id: termId })).deleteGlossaryTerm),
|
|
71
|
+
);
|
|
72
|
+
|
|
73
|
+
server.registerTool(
|
|
74
|
+
'suggest_glossary_terms',
|
|
75
|
+
{
|
|
76
|
+
title: 'Suggest glossary terms',
|
|
77
|
+
description: 'AI-suggested glossary terms extracted from the project source strings.',
|
|
78
|
+
inputSchema: { projectId: z.string() },
|
|
79
|
+
annotations: { readOnlyHint: true, openWorldHint: true },
|
|
80
|
+
},
|
|
81
|
+
async ({ projectId }) =>
|
|
82
|
+
jsonResult((await gql.request(SUGGEST_GLOSSARY_TERMS, { projectId })).suggestGlossaryTerms),
|
|
83
|
+
);
|
|
84
|
+
}
|