@syntholoc/mcp 0.3.1 → 0.4.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/README.md CHANGED
@@ -113,6 +113,27 @@ Translation runs asynchronously and returns a job id; a `wait_for_job` tool
113
113
  blocks until completion, so you can chain "translate → wait → export" in a
114
114
  single request.
115
115
 
116
+ ## Contributing: adding a tool
117
+
118
+ Tools are authored by hand — the curated descriptions, input aliases, and
119
+ validation are what make an LLM use them well, so they are deliberately not
120
+ generated one-per-operation. Two mechanisms keep the manual layer honest:
121
+
122
+ - **Generated input shapes.** `bun run codegen` emits zod schemas for every
123
+ GraphQL input type into `src/generated/input-schemas.ts` from the API's
124
+ committed SDL. Compose a schema's `.shape` instead of re-typing a mutation's
125
+ fields, then curate on top — see `configure_llm_provider` in
126
+ `src/tools/providers.ts`, which spreads `ConfigureProviderInputSchema().shape`
127
+ and tightens the API's free-form `type: String` down to a concrete enum.
128
+ - **Drift guard.** `src/operation-coverage.spec.ts` fails the build when an
129
+ operation exported by `@syntholoc/sdk` is not surfaced by any tool or resource
130
+ (or explicitly exempted). Add an operation to the SDK and you must either wire
131
+ up a tool/resource or record why it is MCP-exempt.
132
+
133
+ Every tool set has contract tests (`src/tools/*.spec.ts`) that pin both the
134
+ GraphQL operation + variables a tool emits and the JSON it returns, via an
135
+ in-memory MCP client against a mock GraphQL client (`src/spec-helpers.ts`).
136
+
116
137
  ## License
117
138
 
118
139
  MIT
@@ -2,7 +2,7 @@ import {
2
2
  TOOL_SET_NAMES,
3
3
  buildMcpServer,
4
4
  parseList
5
- } from "./chunk-MUY33R6O.js";
5
+ } from "./chunk-P7LZNIW2.js";
6
6
  export {
7
7
  TOOL_SET_NAMES,
8
8
  buildMcpServer,
@@ -115,6 +115,12 @@ function registerPrompts(server) {
115
115
  ` - If a locale's ON-DISK code differs from its BCP-47 code (e.g. "zh-CN" stored as "cn", "pt-BR" as "pt"),`,
116
116
  ' add `languagesMapping`, e.g. { "two_letters_code": { "zh-CN": "cn", "pt-BR": "pt" } }.',
117
117
  ' - Do NOT invent fields like `i18nDir`, `format: "nested-dirs"`, or `localeMapping` \u2014 they are not read.',
118
+ " - CRITICAL: `.syntholoc/config.json` MUST be committed and tracked \u2014 it is the mapping source of",
119
+ " truth the server reads from the branch. NEVER add `.syntholoc` (or the config file) to `.gitignore`.",
120
+ " If `.gitignore` already ignores it (a common reflex, next to `.claude`), remove that line. After",
121
+ " committing, VERIFY it is tracked (e.g. `git ls-files .syntholoc/config.json` returns the path, or",
122
+ " `git check-ignore` reports nothing) \u2014 an ignored config means every sync/pull/write-back resolves",
123
+ " zero files and silently opens no PR.",
118
124
  " c. Connect it with `connect_repository`. Ask the user which method they prefer:",
119
125
  " - GitHub App (no token, recommended): `get_github_app_install_url` \u2192 give the URL to the user",
120
126
  " to open and authorize (you cannot click it for them; you may offer to open it via the shell).",
@@ -440,8 +446,12 @@ function registerGitTools(server, gql) {
440
446
  provider: z3.enum(["GITHUB", "GITLAB", "BITBUCKET"]).default("GITHUB"),
441
447
  fullName: z3.string().describe('owner/repo, e.g. "acme/webapp".'),
442
448
  branch: z3.string().optional().describe('Source branch (default "main").'),
443
- files: z3.array(fileMapping).optional().describe("Crowdin-style source\u2192translation mappings. Fallback only \u2014 an in-repo `.syntholoc/config.json` wins."),
444
- paths: z3.array(z3.string()).optional().describe("Simple mode: source file paths/globs to track (use instead of `files`). Fallback for in-repo config."),
449
+ files: z3.array(fileMapping).optional().describe(
450
+ "Crowdin-style source\u2192translation mappings. Fallback only \u2014 an in-repo `.syntholoc/config.json` wins."
451
+ ),
452
+ paths: z3.array(z3.string()).optional().describe(
453
+ "Simple mode: source file paths/globs to track (use instead of `files`). Fallback for in-repo config."
454
+ ),
445
455
  installationId: z3.number().int().optional().describe("GitHub App installation id. Mutually exclusive with `token`."),
446
456
  token: z3.string().optional().describe("Access token (PAT mode), stored encrypted. Mutually exclusive with `installationId`."),
447
457
  translationBranch: z3.string().optional().describe("Branch translations are committed to (default l10n_<branch>)."),
@@ -507,7 +517,7 @@ function registerGitTools(server, gql) {
507
517
  "pull_translations",
508
518
  {
509
519
  title: "Pull translations",
510
- description: "Pulls translation files from the connected repo and imports them per locale. `autoApprove` lands them as APPROVED; `conflict` is OVERWRITE | SKIP_EXISTING | KEEP_APPROVED.",
520
+ description: "Queues a pull of translation files from the connected repo, importing them per locale. Returns the tracking job ids \u2014 the import itself runs in the background. `autoApprove` lands them as APPROVED; `conflict` is OVERWRITE | SKIP_EXISTING | KEEP_APPROVED.",
511
521
  inputSchema: {
512
522
  projectId: z3.string(),
513
523
  repositoryId: z3.string(),
@@ -900,6 +910,21 @@ function resolveLocaleCodes(raw) {
900
910
  return out;
901
911
  }
902
912
 
913
+ // ../../packages/shared/src/constants.ts
914
+ var ORG_PLANS = ["FREE", "PRO", "ENTERPRISE"];
915
+ function isPaidPlan(plan) {
916
+ switch (plan) {
917
+ case "PRO":
918
+ case "ENTERPRISE":
919
+ return true;
920
+ case "FREE":
921
+ return false;
922
+ default:
923
+ return false;
924
+ }
925
+ }
926
+ var PAID_PLANS = ORG_PLANS.filter(isPaidPlan);
927
+
903
928
  // src/tools/locales.ts
904
929
  function registerLocaleTools(server, gql) {
905
930
  server.registerTool(
@@ -1081,8 +1106,27 @@ function registerProjectTools(server, gql) {
1081
1106
  }
1082
1107
 
1083
1108
  // src/tools/providers.ts
1084
- import { z as z9 } from "zod";
1109
+ import { z as z10 } from "zod";
1085
1110
  import { CONFIGURE_LLM_PROVIDER, LIST_LLM_PROVIDERS } from "@syntholoc/sdk";
1111
+
1112
+ // src/generated/input-schemas.ts
1113
+ import { z as z9 } from "zod";
1114
+ var isDefinedNonNullAny = (v) => v !== void 0 && v !== null;
1115
+ var definedNonNullAnySchema = z9.any().refine((v) => isDefinedNonNullAny(v));
1116
+ var CoverageCategorySchema = z9.enum(["DYNAMIC", "NOT_CAPTURED", "NOT_IN_CODE", "SCREENSHOT", "SERVER", "TEST", "UNKNOWN"]);
1117
+ var QaCriticScopeSchema = z9.enum(["ALL", "UNREVIEWED"]);
1118
+ var TranslateScopeSchema = z9.enum(["ALL", "DRAFT", "UNAPPROVED", "UNTRANSLATED"]);
1119
+ function ConfigureProviderInputSchema() {
1120
+ return z9.object({
1121
+ apiKey: z9.string(),
1122
+ baseUrl: z9.string().nullish(),
1123
+ defaultModel: z9.string().nullish(),
1124
+ label: z9.string(),
1125
+ type: z9.string()
1126
+ });
1127
+ }
1128
+
1129
+ // src/tools/providers.ts
1086
1130
  function registerProviderTools(server, gql) {
1087
1131
  server.registerTool(
1088
1132
  "list_llm_providers",
@@ -1099,12 +1143,12 @@ function registerProviderTools(server, gql) {
1099
1143
  {
1100
1144
  title: "Configure LLM provider",
1101
1145
  description: "Adds a BYOK LLM provider (OPENROUTER | OPENAI | ANTHROPIC). The key is stored encrypted at rest.",
1146
+ // Base field shapes (apiKey/label/baseUrl/defaultModel) are generated from
1147
+ // the GraphQL ConfigureProviderInput; we curate `type` from the API's
1148
+ // free-form String down to the concrete provider set.
1102
1149
  inputSchema: {
1103
- type: z9.enum(["OPENROUTER", "OPENAI", "ANTHROPIC"]),
1104
- label: z9.string(),
1105
- apiKey: z9.string(),
1106
- baseUrl: z9.string().optional(),
1107
- defaultModel: z9.string().optional()
1150
+ ...ConfigureProviderInputSchema().shape,
1151
+ type: z10.enum(["OPENROUTER", "OPENAI", "ANTHROPIC"])
1108
1152
  },
1109
1153
  annotations: { openWorldHint: true }
1110
1154
  },
@@ -1113,15 +1157,19 @@ function registerProviderTools(server, gql) {
1113
1157
  }
1114
1158
 
1115
1159
  // src/tools/qa.ts
1116
- import { z as z10 } from "zod";
1160
+ import { z as z11 } from "zod";
1117
1161
  import { LIST_QA_ISSUES as LIST_QA_ISSUES2, RUN_LOCALE_QA, RUN_QA } from "@syntholoc/sdk";
1118
1162
  function registerQaTools(server, gql) {
1119
1163
  server.registerTool(
1120
1164
  "run_locale_qa",
1121
1165
  {
1122
1166
  title: "Run locale QA",
1123
- description: "Queues an LLM QA-critic pass over a locale's non-approved translations. Returns tracking `jobIds`.",
1124
- inputSchema: { projectId: z10.string(), localeId: z10.string() },
1167
+ description: "Queues an LLM QA-critic pass over a locale's non-approved translations. `scope` UNREVIEWED (default) critiques only translations not yet reviewed at their current text; ALL re-critiques every one. Returns tracking `jobIds`.",
1168
+ inputSchema: {
1169
+ projectId: z11.string(),
1170
+ localeId: z11.string(),
1171
+ scope: z11.enum(["UNREVIEWED", "ALL"]).optional()
1172
+ },
1125
1173
  annotations: { openWorldHint: true }
1126
1174
  },
1127
1175
  async (vars) => jsonResult((await gql.request(RUN_LOCALE_QA, vars)).runLocaleQa)
@@ -1131,7 +1179,7 @@ function registerQaTools(server, gql) {
1131
1179
  {
1132
1180
  title: "Run QA on a translation",
1133
1181
  description: "Runs QA on one translation and returns its score and issues (synchronous).",
1134
- inputSchema: { projectId: z10.string(), translationId: z10.string() },
1182
+ inputSchema: { projectId: z11.string(), translationId: z11.string() },
1135
1183
  annotations: { openWorldHint: true }
1136
1184
  },
1137
1185
  async (vars) => jsonResult((await gql.request(RUN_QA, vars)).runQa)
@@ -1142,11 +1190,11 @@ function registerQaTools(server, gql) {
1142
1190
  title: "List QA issues",
1143
1191
  description: "Open QA issues for a locale, plus the issue-type facets (filter tabs).",
1144
1192
  inputSchema: {
1145
- projectId: z10.string(),
1146
- localeId: z10.string(),
1147
- type: z10.string().optional(),
1148
- skip: z10.number().int().min(0).default(0),
1149
- take: z10.number().int().min(1).max(100).default(25)
1193
+ projectId: z11.string(),
1194
+ localeId: z11.string(),
1195
+ type: z11.string().optional(),
1196
+ skip: z11.number().int().min(0).default(0),
1197
+ take: z11.number().int().min(1).max(100).default(25)
1150
1198
  },
1151
1199
  annotations: { readOnlyHint: true, openWorldHint: true }
1152
1200
  },
@@ -1155,7 +1203,7 @@ function registerQaTools(server, gql) {
1155
1203
  }
1156
1204
 
1157
1205
  // src/tools/styleguides.ts
1158
- import { z as z11 } from "zod";
1206
+ import { z as z12 } from "zod";
1159
1207
  import { CREATE_STYLE_GUIDE, GENERATE_STYLE_GUIDE, LIST_STYLE_GUIDES as LIST_STYLE_GUIDES2 } from "@syntholoc/sdk";
1160
1208
  function registerStyleGuideTools(server, gql) {
1161
1209
  server.registerTool(
@@ -1163,7 +1211,7 @@ function registerStyleGuideTools(server, gql) {
1163
1211
  {
1164
1212
  title: "List style guides",
1165
1213
  description: "Lists style guides (project-scoped if projectId given, else org-wide).",
1166
- inputSchema: { projectId: z11.string().optional() },
1214
+ inputSchema: { projectId: z12.string().optional() },
1167
1215
  annotations: { readOnlyHint: true, openWorldHint: true }
1168
1216
  },
1169
1217
  async ({ projectId }) => jsonResult((await gql.request(LIST_STYLE_GUIDES2, { projectId })).styleGuides)
@@ -1173,7 +1221,7 @@ function registerStyleGuideTools(server, gql) {
1173
1221
  {
1174
1222
  title: "Generate style guide (draft)",
1175
1223
  description: "Drafts a tone/rules/prompt-fragment style guide from the project (optionally per locale). Returns a draft \u2014 persist it with create_style_guide.",
1176
- inputSchema: { projectId: z11.string(), localeCode: z11.string().optional() },
1224
+ inputSchema: { projectId: z12.string(), localeCode: z12.string().optional() },
1177
1225
  annotations: { openWorldHint: true }
1178
1226
  },
1179
1227
  async ({ projectId, localeCode }) => jsonResult((await gql.request(GENERATE_STYLE_GUIDE, { input: { projectId, localeCode } })).generateStyleGuide)
@@ -1184,12 +1232,12 @@ function registerStyleGuideTools(server, gql) {
1184
1232
  title: "Create style guide",
1185
1233
  description: "Persists a style guide (tone, rules JSON, prompt fragment), project- or locale-scoped.",
1186
1234
  inputSchema: {
1187
- name: z11.string(),
1188
- projectId: z11.string().optional(),
1189
- localeCode: z11.string().optional(),
1190
- tone: z11.string().optional(),
1191
- promptFragment: z11.string().optional(),
1192
- rules: z11.array(z11.string()).optional()
1235
+ name: z12.string(),
1236
+ projectId: z12.string().optional(),
1237
+ localeCode: z12.string().optional(),
1238
+ tone: z12.string().optional(),
1239
+ promptFragment: z12.string().optional(),
1240
+ rules: z12.array(z12.string()).optional()
1193
1241
  },
1194
1242
  annotations: { openWorldHint: true }
1195
1243
  },
@@ -1198,7 +1246,7 @@ function registerStyleGuideTools(server, gql) {
1198
1246
  }
1199
1247
 
1200
1248
  // src/tools/tm.ts
1201
- import { z as z12 } from "zod";
1249
+ import { z as z13 } from "zod";
1202
1250
  import { TM_ENTRIES, TM_SEARCH, TM_STATS as TM_STATS2 } from "@syntholoc/sdk";
1203
1251
  function registerTmTools(server, gql) {
1204
1252
  server.registerTool(
@@ -1207,10 +1255,10 @@ function registerTmTools(server, gql) {
1207
1255
  title: "Search translation memory",
1208
1256
  description: "Concordance search \u2014 the nearest prior translations for a source string in a target locale, with cosine distance (0 = identical). Scope to a project with `projectId` (includes its group siblings); omit for the whole organization. Requires an embedding-capable provider.",
1209
1257
  inputSchema: {
1210
- sourceText: z12.string(),
1211
- targetLocale: z12.string(),
1212
- projectId: z12.string().optional(),
1213
- limit: z12.number().int().min(1).max(20).default(5)
1258
+ sourceText: z13.string(),
1259
+ targetLocale: z13.string(),
1260
+ projectId: z13.string().optional(),
1261
+ limit: z13.number().int().min(1).max(20).default(5)
1214
1262
  },
1215
1263
  annotations: { readOnlyHint: true, openWorldHint: true }
1216
1264
  },
@@ -1221,7 +1269,7 @@ function registerTmTools(server, gql) {
1221
1269
  {
1222
1270
  title: "Translation memory stats",
1223
1271
  description: "Entry counts per target locale (org-wide, or for a project with `projectId`).",
1224
- inputSchema: { projectId: z12.string().optional() },
1272
+ inputSchema: { projectId: z13.string().optional() },
1225
1273
  annotations: { readOnlyHint: true, openWorldHint: true }
1226
1274
  },
1227
1275
  async ({ projectId }) => jsonResult((await gql.request(TM_STATS2, { projectId })).tmStats)
@@ -1232,10 +1280,10 @@ function registerTmTools(server, gql) {
1232
1280
  title: "Browse translation memory",
1233
1281
  description: "Lists stored TM entries (paginated, newest first), optionally filtered by project and locale.",
1234
1282
  inputSchema: {
1235
- projectId: z12.string().optional(),
1236
- targetLocale: z12.string().optional(),
1237
- skip: z12.number().int().min(0).default(0),
1238
- take: z12.number().int().min(1).max(100).default(25)
1283
+ projectId: z13.string().optional(),
1284
+ targetLocale: z13.string().optional(),
1285
+ skip: z13.number().int().min(0).default(0),
1286
+ take: z13.number().int().min(1).max(100).default(25)
1239
1287
  },
1240
1288
  annotations: { readOnlyHint: true, openWorldHint: true }
1241
1289
  },
@@ -1244,7 +1292,7 @@ function registerTmTools(server, gql) {
1244
1292
  }
1245
1293
 
1246
1294
  // src/tools/translations.ts
1247
- import { z as z13 } from "zod";
1295
+ import { z as z14 } from "zod";
1248
1296
  import {
1249
1297
  APPROVE_TRANSLATION,
1250
1298
  EXPORT_LOCALE,
@@ -1263,9 +1311,9 @@ function registerTranslationTools(server, gql) {
1263
1311
  title: "Translate locale",
1264
1312
  description: "Queues AI translation of a locale (scope ALL | UNAPPROVED | UNTRANSLATED) and returns the tracking `jobIds`. Executes immediately and unconditionally \u2014 the batch threshold is a client-side policy for PROACTIVE syncs, not for this tool.",
1265
1313
  inputSchema: {
1266
- projectId: z13.string(),
1267
- localeId: z13.string(),
1268
- scope: z13.enum(["ALL", "UNAPPROVED", "UNTRANSLATED"]).default("UNTRANSLATED")
1314
+ projectId: z14.string(),
1315
+ localeId: z14.string(),
1316
+ scope: z14.enum(["ALL", "UNAPPROVED", "UNTRANSLATED"]).default("UNTRANSLATED")
1269
1317
  },
1270
1318
  annotations: { openWorldHint: true }
1271
1319
  },
@@ -1276,7 +1324,7 @@ function registerTranslationTools(server, gql) {
1276
1324
  {
1277
1325
  title: "Translate key",
1278
1326
  description: "Queues AI translation of a single key into a locale. Returns tracking `jobIds`.",
1279
- inputSchema: { projectId: z13.string(), keyId: z13.string(), localeId: z13.string() },
1327
+ inputSchema: { projectId: z14.string(), keyId: z14.string(), localeId: z14.string() },
1280
1328
  annotations: { openWorldHint: true }
1281
1329
  },
1282
1330
  async (vars) => jsonResult((await gql.request(TRANSLATE_KEY, vars)).translateKey)
@@ -1287,11 +1335,11 @@ function registerTranslationTools(server, gql) {
1287
1335
  title: "Set translation",
1288
1336
  description: "Manually sets the translation text for a key in a locale.",
1289
1337
  inputSchema: {
1290
- projectId: z13.string(),
1291
- keyId: z13.string(),
1292
- localeId: z13.string(),
1293
- text: z13.string(),
1294
- origin: z13.string().optional()
1338
+ projectId: z14.string(),
1339
+ keyId: z14.string(),
1340
+ localeId: z14.string(),
1341
+ text: z14.string(),
1342
+ origin: z14.string().optional()
1295
1343
  },
1296
1344
  annotations: { idempotentHint: true, openWorldHint: true }
1297
1345
  },
@@ -1305,10 +1353,10 @@ function registerTranslationTools(server, gql) {
1305
1353
  title: "List translations",
1306
1354
  description: "Lists translations for a locale with status, origin, model and QA score.",
1307
1355
  inputSchema: {
1308
- projectId: z13.string(),
1309
- localeId: z13.string(),
1310
- skip: z13.number().int().min(0).default(0),
1311
- take: z13.number().int().min(1).max(200).default(25)
1356
+ projectId: z14.string(),
1357
+ localeId: z14.string(),
1358
+ skip: z14.number().int().min(0).default(0),
1359
+ take: z14.number().int().min(1).max(200).default(25)
1312
1360
  },
1313
1361
  annotations: { readOnlyHint: true, openWorldHint: true }
1314
1362
  },
@@ -1319,7 +1367,7 @@ function registerTranslationTools(server, gql) {
1319
1367
  {
1320
1368
  title: "Approve translation",
1321
1369
  description: "Marks a translation APPROVED.",
1322
- inputSchema: { projectId: z13.string(), translationId: z13.string() },
1370
+ inputSchema: { projectId: z14.string(), translationId: z14.string() },
1323
1371
  annotations: { idempotentHint: true, openWorldHint: true }
1324
1372
  },
1325
1373
  async ({ projectId, translationId }) => jsonResult((await gql.request(APPROVE_TRANSLATION, { projectId, id: translationId })).approveTranslation)
@@ -1329,7 +1377,7 @@ function registerTranslationTools(server, gql) {
1329
1377
  {
1330
1378
  title: "Reject translation",
1331
1379
  description: "Marks a translation REJECTED (queued for re-translation).",
1332
- inputSchema: { projectId: z13.string(), translationId: z13.string() },
1380
+ inputSchema: { projectId: z14.string(), translationId: z14.string() },
1333
1381
  annotations: { idempotentHint: true, openWorldHint: true }
1334
1382
  },
1335
1383
  async ({ projectId, translationId }) => jsonResult((await gql.request(REJECT_TRANSLATION, { projectId, id: translationId })).rejectTranslation)
@@ -1340,12 +1388,12 @@ function registerTranslationTools(server, gql) {
1340
1388
  title: "Export locale",
1341
1389
  description: 'Exports a locale to a file string in the chosen format \u2014 the basis for writing translations back into local files. Set `onlyApproved` to exclude drafts. For a multi-file project prefer `sourceFile` (the real path, e.g. "messages/en/home.json") to export exactly that file with file-local key names \u2014 reliable. `keyPrefix` is the legacy name-prefix heuristic.',
1342
1390
  inputSchema: {
1343
- projectId: z13.string(),
1344
- localeId: z13.string(),
1345
- format: z13.enum(FORMATS2).default("JSON"),
1346
- onlyApproved: z13.boolean().optional(),
1347
- sourceFile: z13.string().optional().describe('Real source file path to export, e.g. "messages/en/home.json" (reliable, preferred).'),
1348
- keyPrefix: z13.string().optional().describe('Legacy namespace prefix, e.g. "home".')
1391
+ projectId: z14.string(),
1392
+ localeId: z14.string(),
1393
+ format: z14.enum(FORMATS2).default("JSON"),
1394
+ onlyApproved: z14.boolean().optional(),
1395
+ sourceFile: z14.string().optional().describe('Real source file path to export, e.g. "messages/en/home.json" (reliable, preferred).'),
1396
+ keyPrefix: z14.string().optional().describe('Legacy namespace prefix, e.g. "home".')
1349
1397
  },
1350
1398
  annotations: { readOnlyHint: true, openWorldHint: true }
1351
1399
  },
@@ -1357,14 +1405,14 @@ function registerTranslationTools(server, gql) {
1357
1405
  title: "Import translations",
1358
1406
  description: "Imports ready-made translations for a locale (paste file content). `autoApprove` lands them as APPROVED; `conflict` is OVERWRITE | SKIP_EXISTING | KEEP_APPROVED; `dryRun` previews. For a multi-file / namespaced project pass `keyPrefix` (the namespace) to match prefixed source keys.",
1359
1407
  inputSchema: {
1360
- projectId: z13.string(),
1361
- localeId: z13.string(),
1362
- content: z13.string(),
1363
- format: z13.enum(FORMATS2),
1364
- keyPrefix: z13.string().optional().describe('Namespace prefix, e.g. "home" (per-file, multi-file projects).'),
1365
- autoApprove: z13.boolean().default(false),
1366
- conflict: z13.enum(["OVERWRITE", "SKIP_EXISTING", "KEEP_APPROVED"]).default("OVERWRITE"),
1367
- dryRun: z13.boolean().default(false)
1408
+ projectId: z14.string(),
1409
+ localeId: z14.string(),
1410
+ content: z14.string(),
1411
+ format: z14.enum(FORMATS2),
1412
+ keyPrefix: z14.string().optional().describe('Namespace prefix, e.g. "home" (per-file, multi-file projects).'),
1413
+ autoApprove: z14.boolean().default(false),
1414
+ conflict: z14.enum(["OVERWRITE", "SKIP_EXISTING", "KEEP_APPROVED"]).default("OVERWRITE"),
1415
+ dryRun: z14.boolean().default(false)
1368
1416
  },
1369
1417
  annotations: { idempotentHint: true, openWorldHint: true }
1370
1418
  },
@@ -1407,7 +1455,7 @@ function registerAllTools(server, gql, opts = {}) {
1407
1455
  }
1408
1456
 
1409
1457
  // package.json
1410
- var version = "0.3.1";
1458
+ var version = "0.4.0";
1411
1459
 
1412
1460
  // src/build-server.ts
1413
1461
  function buildMcpServer(gql, opts = {}) {
package/dist/index.js CHANGED
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  buildMcpServer,
4
4
  parseList
5
- } from "./chunk-MUY33R6O.js";
5
+ } from "./chunk-P7LZNIW2.js";
6
6
 
7
7
  // src/index.ts
8
8
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@syntholoc/mcp",
3
- "version": "0.3.1",
3
+ "version": "0.4.0",
4
4
  "description": "Syntholoc MCP server — manage AI localization and translation from Codex, Claude Code, and other MCP clients.",
5
5
  "homepage": "https://syntholoc.com",
6
6
  "keywords": [
@@ -47,14 +47,17 @@
47
47
  },
48
48
  "dependencies": {
49
49
  "@modelcontextprotocol/sdk": "1.22.0",
50
- "@syntholoc/sdk": "0.3.0",
50
+ "@syntholoc/sdk": "0.3.1",
51
51
  "zod": "3.25.76"
52
52
  },
53
53
  "devDependencies": {
54
+ "@graphql-codegen/cli": "7.1.2",
55
+ "@graphql-codegen/typescript": "5.0.2",
54
56
  "@syntholoc/shared": "0.1.0",
55
57
  "@types/bun": "1.3.14",
56
58
  "@types/node": "22.19.21",
57
59
  "graphql": "16.14.2",
60
+ "graphql-codegen-typescript-validation-schema": "0.19.0",
58
61
  "tsup": "8.5.1",
59
62
  "typescript": "6.0.3"
60
63
  }