@syntholoc/mcp 0.4.0 → 0.5.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.
@@ -2,7 +2,7 @@ import {
2
2
  TOOL_SET_NAMES,
3
3
  buildMcpServer,
4
4
  parseList
5
- } from "./chunk-P7LZNIW2.js";
5
+ } from "./chunk-V5AAF2RJ.js";
6
6
  export {
7
7
  TOOL_SET_NAMES,
8
8
  buildMcpServer,
@@ -306,6 +306,9 @@ import { COST_ANALYTICS, LOCALE_PROGRESS, PROJECT_ANALYTICS as PROJECT_ANALYTICS
306
306
  function jsonResult(data) {
307
307
  return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
308
308
  }
309
+ function errorResult(message) {
310
+ return { content: [{ type: "text", text: message }], isError: true };
311
+ }
309
312
 
310
313
  // src/tools/analytics.ts
311
314
  function registerAnalyticsTools(server, gql) {
@@ -664,7 +667,8 @@ import {
664
667
  UPDATE_KEY,
665
668
  UPSERT_KEYS
666
669
  } from "@syntholoc/sdk";
667
- var FORMATS = ["JSON", "YAML", "CSV", "PO", "XLIFF", "ANDROID_XML", "IOS_STRINGS", "NJK"];
670
+ var FORMATS = ["JSON", "YAML", "MARKDOWN", "MDX", "CSV", "PO", "XLIFF", "ANDROID_XML", "IOS_STRINGS", "NJK"];
671
+ var DOCUMENT_FORMATS = /* @__PURE__ */ new Set(["MARKDOWN", "MDX"]);
668
672
  function registerKeyTools(server, gql) {
669
673
  server.registerTool(
670
674
  "list_keys",
@@ -734,7 +738,7 @@ function registerKeyTools(server, gql) {
734
738
  "import_source_strings",
735
739
  {
736
740
  title: "Import source strings",
737
- description: 'Imports source strings from ONE file (paste the raw content) and returns the diff summary. For initial setup of a repo-hosted project, PREFER connecting the repo and running `sync_repository` \u2014 it imports every source file server-side in one pass; use this tool only for file-based projects with no Git integration, or for one-off single-file imports. For a multi-file / namespaced project (a file per namespace, e.g. messages/en/home.json), pass `keyPrefix` (the namespace, e.g. "home") so keys become `home.title` and files with overlapping top-level keys do not collide \u2014 call once per file. Use `dryRun` to preview.',
741
+ description: 'Imports source strings from ONE file (paste the raw content) and returns the diff summary. For initial setup of a repo-hosted project, PREFER connecting the repo and running `sync_repository` \u2014 it imports every source file server-side in one pass; use this tool only for file-based projects with no Git integration, or for one-off single-file imports. For a multi-file / namespaced project (a file per namespace, e.g. messages/en/home.json), pass `keyPrefix` (the namespace, e.g. "home") so keys become `home.title` and files with overlapping top-level keys do not collide \u2014 call once per file. Use `dryRun` to preview. For MARKDOWN and MDX `sourceFile` is REQUIRED: the document is split into per-block segments, and without the file path a re-import cannot tell a moved paragraph from an edited one.',
738
742
  inputSchema: {
739
743
  projectId: z6.string(),
740
744
  content: z6.string(),
@@ -747,12 +751,17 @@ function registerKeyTools(server, gql) {
747
751
  },
748
752
  annotations: { idempotentHint: true, openWorldHint: true }
749
753
  },
750
- async ({ projectId, content, format, keyPrefix, sourceFile, dryRun }) => jsonResult(
751
- (await gql.request(IMPORT_SOURCE_STRINGS, {
752
- projectId,
753
- input: { content, format, keyPrefix, sourceFile, dryRun }
754
- })).importSourceStrings
755
- )
754
+ async ({ projectId, content, format, keyPrefix, sourceFile, dryRun }) => {
755
+ if (DOCUMENT_FORMATS.has(format) && !sourceFile?.trim()) {
756
+ return errorResult(`${format} needs a sourceFile \u2014 a document's segments only have an identity within a file.`);
757
+ }
758
+ return jsonResult(
759
+ (await gql.request(IMPORT_SOURCE_STRINGS, {
760
+ projectId,
761
+ input: { content, format, keyPrefix, sourceFile, dryRun }
762
+ })).importSourceStrings
763
+ );
764
+ }
756
765
  );
757
766
  }
758
767
 
@@ -1295,6 +1304,7 @@ function registerTmTools(server, gql) {
1295
1304
  import { z as z14 } from "zod";
1296
1305
  import {
1297
1306
  APPROVE_TRANSLATION,
1307
+ EXPORT_DOCUMENT_LOCALE,
1298
1308
  EXPORT_LOCALE,
1299
1309
  IMPORT_TRANSLATIONS,
1300
1310
  LIST_TRANSLATIONS,
@@ -1303,7 +1313,8 @@ import {
1303
1313
  TRANSLATE_KEY,
1304
1314
  TRANSLATE_LOCALE
1305
1315
  } from "@syntholoc/sdk";
1306
- var FORMATS2 = ["JSON", "YAML", "CSV", "PO", "XLIFF", "ANDROID_XML", "IOS_STRINGS", "NJK"];
1316
+ var FORMATS2 = ["JSON", "YAML", "MARKDOWN", "MDX", "CSV", "PO", "XLIFF", "ANDROID_XML", "IOS_STRINGS", "NJK"];
1317
+ var DOCUMENT_FORMATS2 = /* @__PURE__ */ new Set(["MARKDOWN", "MDX"]);
1307
1318
  function registerTranslationTools(server, gql) {
1308
1319
  server.registerTool(
1309
1320
  "translate_locale",
@@ -1386,7 +1397,7 @@ function registerTranslationTools(server, gql) {
1386
1397
  "export_locale",
1387
1398
  {
1388
1399
  title: "Export locale",
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.',
1400
+ 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. For MARKDOWN and MDX use `export_document` instead: a document is spliced into its original file, which the server reads from the connected repository.',
1390
1401
  inputSchema: {
1391
1402
  projectId: z14.string(),
1392
1403
  localeId: z14.string(),
@@ -1399,30 +1410,59 @@ function registerTranslationTools(server, gql) {
1399
1410
  },
1400
1411
  async (vars) => jsonResult({ content: (await gql.request(EXPORT_LOCALE, vars)).exportLocale })
1401
1412
  );
1413
+ server.registerTool(
1414
+ "export_document",
1415
+ {
1416
+ title: "Export document locale",
1417
+ description: "Exports one locale of a Markdown or MDX source document. Unlike `export_locale` this returns the whole translated document: the server reads the original file from the connected repository and splices the translations into it, so headings, list markers, code fences and indentation are preserved byte for byte. Segments with no translation keep their source text.",
1418
+ inputSchema: {
1419
+ projectId: z14.string(),
1420
+ localeId: z14.string(),
1421
+ sourceFile: z14.string().describe('Path of the source document in the repository, e.g. "docs/guide.md".'),
1422
+ repositoryId: z14.string().optional().describe("Only needed when the project has several repositories and the path is ambiguous."),
1423
+ onlyApproved: z14.boolean().optional()
1424
+ },
1425
+ annotations: { readOnlyHint: true, openWorldHint: true }
1426
+ },
1427
+ async (vars) => {
1428
+ if (!vars.sourceFile.trim()) {
1429
+ return errorResult("export_document needs a sourceFile \u2014 the path of the document in the repository.");
1430
+ }
1431
+ return jsonResult({ content: (await gql.request(EXPORT_DOCUMENT_LOCALE, vars)).exportDocumentLocale });
1432
+ }
1433
+ );
1402
1434
  server.registerTool(
1403
1435
  "import_translations",
1404
1436
  {
1405
1437
  title: "Import translations",
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.",
1438
+ 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. For MARKDOWN and MDX `sourceFile` is REQUIRED: a translated document has translated headings, so it is matched to the source by segment position and the two must have the same structure.",
1407
1439
  inputSchema: {
1408
1440
  projectId: z14.string(),
1409
1441
  localeId: z14.string(),
1410
1442
  content: z14.string(),
1411
1443
  format: z14.enum(FORMATS2),
1412
1444
  keyPrefix: z14.string().optional().describe('Namespace prefix, e.g. "home" (per-file, multi-file projects).'),
1445
+ sourceFile: z14.string().optional().describe('Source file these translations belong to; REQUIRED for MARKDOWN/MDX, e.g. "docs/guide.md".'),
1413
1446
  autoApprove: z14.boolean().default(false),
1414
1447
  conflict: z14.enum(["OVERWRITE", "SKIP_EXISTING", "KEEP_APPROVED"]).default("OVERWRITE"),
1415
1448
  dryRun: z14.boolean().default(false)
1416
1449
  },
1417
1450
  annotations: { idempotentHint: true, openWorldHint: true }
1418
1451
  },
1419
- async ({ projectId, localeId, content, format, keyPrefix, autoApprove, conflict, dryRun }) => jsonResult(
1420
- (await gql.request(IMPORT_TRANSLATIONS, {
1421
- projectId,
1422
- localeId,
1423
- input: { content, format, keyPrefix, autoApprove, conflict, dryRun }
1424
- })).importTranslations
1425
- )
1452
+ async ({ projectId, localeId, content, format, keyPrefix, sourceFile, autoApprove, conflict, dryRun }) => {
1453
+ if (DOCUMENT_FORMATS2.has(format) && !sourceFile?.trim()) {
1454
+ return errorResult(
1455
+ `${format} needs a sourceFile \u2014 a translated document is aligned to its source by position.`
1456
+ );
1457
+ }
1458
+ return jsonResult(
1459
+ (await gql.request(IMPORT_TRANSLATIONS, {
1460
+ projectId,
1461
+ localeId,
1462
+ input: { content, format, keyPrefix, sourceFile, autoApprove, conflict, dryRun }
1463
+ })).importTranslations
1464
+ );
1465
+ }
1426
1466
  );
1427
1467
  }
1428
1468
 
@@ -1455,7 +1495,7 @@ function registerAllTools(server, gql, opts = {}) {
1455
1495
  }
1456
1496
 
1457
1497
  // package.json
1458
- var version = "0.4.0";
1498
+ var version = "0.5.0";
1459
1499
 
1460
1500
  // src/build-server.ts
1461
1501
  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-P7LZNIW2.js";
5
+ } from "./chunk-V5AAF2RJ.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.4.0",
3
+ "version": "0.5.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": [