@babelize/mcp 1.0.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.
Files changed (43) hide show
  1. package/LICENSE +6 -0
  2. package/README.md +119 -0
  3. package/dist/client.d.ts +3 -0
  4. package/dist/client.d.ts.map +1 -0
  5. package/dist/client.js +22 -0
  6. package/dist/client.js.map +1 -0
  7. package/dist/config.d.ts +6 -0
  8. package/dist/config.d.ts.map +1 -0
  9. package/dist/config.js +46 -0
  10. package/dist/config.js.map +1 -0
  11. package/dist/index.d.ts +3 -0
  12. package/dist/index.d.ts.map +1 -0
  13. package/dist/index.js +63 -0
  14. package/dist/index.js.map +1 -0
  15. package/dist/server.d.ts +3 -0
  16. package/dist/server.d.ts.map +1 -0
  17. package/dist/server.js +109 -0
  18. package/dist/server.js.map +1 -0
  19. package/dist/tools/glossary-get.d.ts +20 -0
  20. package/dist/tools/glossary-get.d.ts.map +1 -0
  21. package/dist/tools/glossary-get.js +53 -0
  22. package/dist/tools/glossary-get.js.map +1 -0
  23. package/dist/tools/glossary-set.d.ts +23 -0
  24. package/dist/tools/glossary-set.d.ts.map +1 -0
  25. package/dist/tools/glossary-set.js +64 -0
  26. package/dist/tools/glossary-set.js.map +1 -0
  27. package/dist/tools/run.d.ts +21 -0
  28. package/dist/tools/run.d.ts.map +1 -0
  29. package/dist/tools/run.js +41 -0
  30. package/dist/tools/run.js.map +1 -0
  31. package/dist/tools/scan.d.ts +19 -0
  32. package/dist/tools/scan.d.ts.map +1 -0
  33. package/dist/tools/scan.js +60 -0
  34. package/dist/tools/scan.js.map +1 -0
  35. package/dist/tools/status.d.ts +19 -0
  36. package/dist/tools/status.d.ts.map +1 -0
  37. package/dist/tools/status.js +36 -0
  38. package/dist/tools/status.js.map +1 -0
  39. package/dist/utils/scan-strings.d.ts +13 -0
  40. package/dist/utils/scan-strings.d.ts.map +1 -0
  41. package/dist/utils/scan-strings.js +144 -0
  42. package/dist/utils/scan-strings.js.map +1 -0
  43. package/package.json +53 -0
package/LICENSE ADDED
@@ -0,0 +1,6 @@
1
+ Copyright (c) 2026 Babelize
2
+
3
+ All Rights Reserved.
4
+
5
+ This software and its source code are proprietary and confidential.
6
+ Unauthorized copying, modification, distribution, or use is prohibited.
package/README.md ADDED
@@ -0,0 +1,119 @@
1
+ # @babelize/mcp
2
+
3
+ **MCP (Model Context Protocol) server for Babelize** — let AI coding agents translate your apps directly from your editor.
4
+
5
+ Works with any MCP-compatible client: Cursor, Claude Desktop, opencode, VS Code (via extension), and more.
6
+
7
+ ## Install
8
+
9
+ ```bash
10
+ npm install -g @babelize/mcp
11
+ ```
12
+
13
+ Or run on-demand without installing:
14
+
15
+ ```bash
16
+ npx -y @babelize/mcp
17
+ ```
18
+
19
+ ## Quick Start
20
+
21
+ 1. **Get an API key** from [babelize.co](https://babelize.co)
22
+ 2. **Set your API key:**
23
+
24
+ ```bash
25
+ export BABELIZE_API_KEY=bblz_sk_...
26
+ ```
27
+
28
+ Or create `.babelize/config.json` in your project:
29
+
30
+ ```json
31
+ { "apiKey": "bblz_sk_..." }
32
+ ```
33
+
34
+ 3. **Configure your editor** (see below)
35
+
36
+ ## IDE Configuration
37
+
38
+ ### Cursor
39
+
40
+ Add to `.cursor/mcp.json`:
41
+
42
+ ```json
43
+ {
44
+ "mcpServers": {
45
+ "babelize": {
46
+ "command": "npx",
47
+ "args": ["-y", "@babelize/mcp"],
48
+ "env": {
49
+ "BABELIZE_API_KEY": "bblz_sk_..."
50
+ }
51
+ }
52
+ }
53
+ }
54
+ ```
55
+
56
+ ### Claude Desktop
57
+
58
+ Add to `claude_desktop_config.json`:
59
+
60
+ ```json
61
+ {
62
+ "mcpServers": {
63
+ "babelize": {
64
+ "command": "npx",
65
+ "args": ["-y", "@babelize/mcp"]
66
+ }
67
+ }
68
+ }
69
+ ```
70
+
71
+ ### opencode
72
+
73
+ Add to `.opencode.json`:
74
+
75
+ ```json
76
+ {
77
+ "mcpServers": {
78
+ "babelize": {
79
+ "command": "npx",
80
+ "args": ["-y", "@babelize/mcp"],
81
+ "env": []
82
+ }
83
+ }
84
+ }
85
+ ```
86
+
87
+ ## Available Tools
88
+
89
+ | Tool | Description |
90
+ |------|-------------|
91
+ | `babelize_run_start` | Start a translation pipeline run for the project |
92
+ | `babelize_run_status` | Check the status of a running translation |
93
+ | `babelize_glossary_get` | View the translation glossary |
94
+ | `babelize_glossary_set` | Add or update terms in the translation glossary |
95
+ | `babelize_scan` | Scan project files for translatable strings |
96
+ | `babelize_status` | Check Babelize API server health |
97
+
98
+ ## Configuration
99
+
100
+ The server resolves configuration in this priority order:
101
+
102
+ 1. **CLI args** — `--api-key`, `--api-url`, `--api-base`
103
+ 2. **Environment variables** — `BABELIZE_API_KEY`, `BABELIZE_API_URL`
104
+ 3. **Local config file** — `.babelize/config.json` in the current working directory
105
+
106
+ ### Options
107
+
108
+ | Option / Env Var | Description | Default |
109
+ |-----------------|-------------|---------|
110
+ | `--api-key` / `BABELIZE_API_KEY` | Your Babelize API key | — |
111
+ | `--api-url` / `BABELIZE_API_URL` | API base URL | `https://api.babelize.co/api` |
112
+ | `--api-base` | API base path (without `/api` suffix) | — |
113
+
114
+ ## Links
115
+
116
+ - [Website](https://babelize.co)
117
+ - [Docs](https://docs.babelize.co)
118
+ - [GitHub](https://github.com/babelize/babelize-dashboard)
119
+ - [npm](https://www.npmjs.com/package/@babelize/mcp)
@@ -0,0 +1,3 @@
1
+ import { AxiosInstance } from 'axios';
2
+ export declare function getClient(apiUrl: string, apiKey?: string): AxiosInstance;
3
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,OAAc,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AAK7C,wBAAgB,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,aAAa,CAoBxE"}
package/dist/client.js ADDED
@@ -0,0 +1,22 @@
1
+ import axios from 'axios';
2
+ import pkg from '../package.json' with { type: 'json' };
3
+ const CLI_VERSION = pkg.version;
4
+ export function getClient(apiUrl, apiKey) {
5
+ const baseURL = apiUrl.replace(/\/+$/, '') + '/api';
6
+ const client = axios.create({
7
+ baseURL,
8
+ timeout: 60_000,
9
+ headers: {
10
+ 'Content-Type': 'application/json',
11
+ 'User-Agent': `babelize-mcp/${CLI_VERSION}`,
12
+ },
13
+ });
14
+ if (apiKey) {
15
+ client.interceptors.request.use((req) => {
16
+ req.headers['Authorization'] = `Bearer ${apiKey}`;
17
+ return req;
18
+ });
19
+ }
20
+ return client;
21
+ }
22
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAwB,MAAM,OAAO,CAAC;AAC7C,OAAO,GAAG,MAAM,iBAAiB,CAAC,OAAO,IAAI,EAAE,MAAM,EAAE,CAAC;AAExD,MAAM,WAAW,GAAG,GAAG,CAAC,OAAiB,CAAC;AAE1C,MAAM,UAAU,SAAS,CAAC,MAAc,EAAE,MAAe;IACvD,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,GAAG,MAAM,CAAC;IAEpD,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC;QAC1B,OAAO;QACP,OAAO,EAAE,MAAM;QACf,OAAO,EAAE;YACP,cAAc,EAAE,kBAAkB;YAClC,YAAY,EAAE,gBAAgB,WAAW,EAAE;SAC5C;KACF,CAAC,CAAC;IAEH,IAAI,MAAM,EAAE,CAAC;QACX,MAAM,CAAC,YAAY,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE;YACtC,GAAG,CAAC,OAAO,CAAC,eAAe,CAAC,GAAG,UAAU,MAAM,EAAE,CAAC;YAClD,OAAO,GAAG,CAAC;QACb,CAAC,CAAC,CAAC;IACL,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC"}
@@ -0,0 +1,6 @@
1
+ export interface McpConfig {
2
+ apiKey?: string;
3
+ apiUrl: string;
4
+ }
5
+ export declare function getConfig(cwd?: string): McpConfig;
6
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,SAAS;IACxB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;CAChB;AA4BD,wBAAgB,SAAS,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,SAAS,CAyBjD"}
package/dist/config.js ADDED
@@ -0,0 +1,46 @@
1
+ import { readFileSync, existsSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+ function extractArg(args, name) {
4
+ const index = args.indexOf(name);
5
+ if (index !== -1 && index + 1 < args.length) {
6
+ return args[index + 1];
7
+ }
8
+ // Also support --key=value format
9
+ for (const arg of args) {
10
+ if (arg.startsWith(`${name}=`)) {
11
+ return arg.slice(name.length + 1);
12
+ }
13
+ }
14
+ return undefined;
15
+ }
16
+ function readLocalConfig(cwd) {
17
+ try {
18
+ const configPath = join(cwd ?? process.cwd(), '.babelize', 'config.json');
19
+ if (existsSync(configPath)) {
20
+ return JSON.parse(readFileSync(configPath, 'utf8'));
21
+ }
22
+ }
23
+ catch {
24
+ // Silently ignore malformed config
25
+ }
26
+ return null;
27
+ }
28
+ export function getConfig(cwd) {
29
+ const args = process.argv.slice(2);
30
+ const localConfig = readLocalConfig(cwd);
31
+ const apiKey = extractArg(args, '--api-key') ??
32
+ extractArg(args, '--apiKey') ??
33
+ process.env.BABELIZE_API_KEY ??
34
+ localConfig?.apiKey;
35
+ const apiUrl = extractArg(args, '--api-url') ??
36
+ extractArg(args, '--apiUrl') ??
37
+ process.env.BABELIZE_API_URL ??
38
+ localConfig?.apiUrl ??
39
+ 'https://api.babelize.co';
40
+ if (apiKey && !apiKey.startsWith('bblz_sk_')) {
41
+ throw new Error('Invalid API key format. Expected bblz_sk_... prefix.\n' +
42
+ 'Generate a valid key at https://app.babelize.co/settings/api-keys');
43
+ }
44
+ return { apiKey, apiUrl };
45
+ }
46
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAOjC,SAAS,UAAU,CAAC,IAAc,EAAE,IAAY;IAC9C,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IACjC,IAAI,KAAK,KAAK,CAAC,CAAC,IAAI,KAAK,GAAG,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC;QAC5C,OAAO,IAAI,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC;IACzB,CAAC;IACD,kCAAkC;IAClC,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;QACvB,IAAI,GAAG,CAAC,UAAU,CAAC,GAAG,IAAI,GAAG,CAAC,EAAE,CAAC;YAC/B,OAAO,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;QACpC,CAAC;IACH,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,SAAS,eAAe,CAAC,GAAY;IACnC,IAAI,CAAC;QACH,MAAM,UAAU,GAAG,IAAI,CAAC,GAAG,IAAI,OAAO,CAAC,GAAG,EAAE,EAAE,WAAW,EAAE,aAAa,CAAC,CAAC;QAC1E,IAAI,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;YAC3B,OAAO,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC,CAAC;QACtD,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,mCAAmC;IACrC,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,GAAY;IACpC,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IACnC,MAAM,WAAW,GAAG,eAAe,CAAC,GAAG,CAAC,CAAC;IAEzC,MAAM,MAAM,GACV,UAAU,CAAC,IAAI,EAAE,WAAW,CAAC;QAC7B,UAAU,CAAC,IAAI,EAAE,UAAU,CAAC;QAC5B,OAAO,CAAC,GAAG,CAAC,gBAAgB;QAC3B,WAAW,EAAE,MAA6B,CAAC;IAE9C,MAAM,MAAM,GACV,UAAU,CAAC,IAAI,EAAE,WAAW,CAAC;QAC7B,UAAU,CAAC,IAAI,EAAE,UAAU,CAAC;QAC5B,OAAO,CAAC,GAAG,CAAC,gBAAgB;QAC3B,WAAW,EAAE,MAA6B;QAC3C,yBAAyB,CAAC;IAE5B,IAAI,MAAM,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;QAC7C,MAAM,IAAI,KAAK,CACb,wDAAwD;YACxD,mEAAmE,CACpE,CAAC;IACJ,CAAC;IAED,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC;AAC5B,CAAC"}
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
package/dist/index.js ADDED
@@ -0,0 +1,63 @@
1
+ #!/usr/bin/env node
2
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
3
+ import { createServer } from './server.js';
4
+ import { getConfig } from './config.js';
5
+ import pkg from '../package.json' with { type: 'json' };
6
+ const CLI_VERSION = pkg.version;
7
+ function showHelp() {
8
+ const help = `
9
+ @babelize/mcp v${CLI_VERSION} — MCP server for Babelize AI-powered i18n
10
+
11
+ Usage:
12
+ babelize-mcp [options]
13
+
14
+ Options:
15
+ --api-key <key> Babelize API key (or BABELIZE_API_KEY env)
16
+ --apiKey <key> Alternative form of --api-key
17
+ --api-url <url> API base URL (default: https://api.babelize.co)
18
+ --apiUrl <url> Alternative form of --api-url
19
+ --help, -h Show this help message
20
+ --version, -v Show version
21
+
22
+ Environment variables:
23
+ BABELIZE_API_KEY API key (fallback if --api-key not provided)
24
+ BABELIZE_API_URL API URL (fallback if --api-url not provided)
25
+
26
+ Configuration file:
27
+ .babelize/config.json in the current working directory
28
+
29
+ Available tools (via MCP):
30
+ babelize_scan Scan a local directory for hardcoded strings
31
+ babelize_scan_strings Extract translatable strings from a code snippet
32
+ babelize_run Start a translation pipeline run
33
+ babelize_status Check status of a running translation
34
+ babelize_glossary_get List glossary entries for a project
35
+ babelize_glossary_set Add or update a glossary entry
36
+
37
+ Documentation: https://docs.babelize.co
38
+ `.trim();
39
+ console.error(help);
40
+ }
41
+ async function main() {
42
+ const args = process.argv.slice(2);
43
+ if (args.includes('--help') || args.includes('-h')) {
44
+ showHelp();
45
+ process.exit(0);
46
+ }
47
+ if (args.includes('--version') || args.includes('-v')) {
48
+ console.error(`@babelize/mcp v${CLI_VERSION}`);
49
+ process.exit(0);
50
+ }
51
+ const { apiKey, apiUrl } = getConfig();
52
+ const server = createServer(apiUrl, apiKey);
53
+ const transport = new StdioServerTransport();
54
+ console.error(`@babelize/mcp v${CLI_VERSION} server starting...`);
55
+ console.error(` API URL: ${apiUrl}`);
56
+ console.error(` Transport: stdio`);
57
+ await server.connect(transport);
58
+ }
59
+ main().catch((err) => {
60
+ console.error('Fatal error:', err);
61
+ process.exit(1);
62
+ });
63
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,GAAG,MAAM,iBAAiB,CAAC,OAAO,IAAI,EAAE,MAAM,EAAE,CAAC;AAExD,MAAM,WAAW,GAAG,GAAG,CAAC,OAAiB,CAAC;AAE1C,SAAS,QAAQ;IACf,MAAM,IAAI,GAAG;iBACE,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA6B3B,CAAC,IAAI,EAAE,CAAC;IACP,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;AACtB,CAAC;AAED,KAAK,UAAU,IAAI;IACjB,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IAEnC,IAAI,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;QACnD,QAAQ,EAAE,CAAC;QACX,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,IAAI,IAAI,CAAC,QAAQ,CAAC,WAAW,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;QACtD,OAAO,CAAC,KAAK,CAAC,kBAAkB,WAAW,EAAE,CAAC,CAAC;QAC/C,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,SAAS,EAAE,CAAC;IAEvC,MAAM,MAAM,GAAG,YAAY,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC5C,MAAM,SAAS,GAAG,IAAI,oBAAoB,EAAE,CAAC;IAE7C,OAAO,CAAC,KAAK,CAAC,kBAAkB,WAAW,qBAAqB,CAAC,CAAC;IAClE,OAAO,CAAC,KAAK,CAAC,cAAc,MAAM,EAAE,CAAC,CAAC;IACtC,OAAO,CAAC,KAAK,CAAC,oBAAoB,CAAC,CAAC;IAEpC,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;AAClC,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;IACnB,OAAO,CAAC,KAAK,CAAC,cAAc,EAAE,GAAG,CAAC,CAAC;IACnC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
@@ -0,0 +1,3 @@
1
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ export declare function createServer(apiUrl: string, apiKey?: string): McpServer;
3
+ //# sourceMappingURL=server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAgBpE,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CA+HvE"}
package/dist/server.js ADDED
@@ -0,0 +1,109 @@
1
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import { z } from 'zod';
3
+ import { getClient } from './client.js';
4
+ import { handleScan } from './tools/scan.js';
5
+ import { handleRun } from './tools/run.js';
6
+ import { handleStatus } from './tools/status.js';
7
+ import { handleGlossaryGet } from './tools/glossary-get.js';
8
+ import { handleGlossarySet } from './tools/glossary-set.js';
9
+ import { extractStrings } from './utils/scan-strings.js';
10
+ import pkg from '../package.json' with { type: 'json' };
11
+ const CLI_VERSION = pkg.version;
12
+ /* eslint-disable @typescript-eslint/no-explicit-any */
13
+ export function createServer(apiUrl, apiKey) {
14
+ const client = apiKey ? getClient(apiUrl, apiKey) : null;
15
+ const server = new McpServer({
16
+ name: '@babelize/mcp',
17
+ version: CLI_VERSION,
18
+ }, {
19
+ capabilities: {
20
+ tools: {},
21
+ },
22
+ });
23
+ const register = server.registerTool.bind(server);
24
+ register('babelize_scan', {
25
+ description: 'Scan a local directory for hardcoded strings. No API key required.',
26
+ inputSchema: z.object({ directory: z.string(), extensions: z.array(z.string()).optional() }),
27
+ }, async (params) => {
28
+ return handleScan(params);
29
+ });
30
+ register('babelize_run', {
31
+ description: 'Trigger a Babelize translation run for target languages. Requires API key.',
32
+ inputSchema: z.object({
33
+ project_id: z.string(),
34
+ target_locales: z.array(z.string()),
35
+ source_locale: z.string().optional(),
36
+ }),
37
+ }, async (params) => {
38
+ if (!client)
39
+ return {
40
+ content: [{ type: 'text', text: '❌ API key required. Set BABELIZE_API_KEY or pass --api-key.' }],
41
+ isError: true,
42
+ };
43
+ return handleRun(client, params);
44
+ });
45
+ register('babelize_status', {
46
+ description: 'Check status of a translation run. Requires API key.',
47
+ inputSchema: z.object({ babel_id: z.string() }),
48
+ }, async (params) => {
49
+ if (!client)
50
+ return {
51
+ content: [{ type: 'text', text: '❌ API key required. Set BABELIZE_API_KEY or pass --api-key.' }],
52
+ isError: true,
53
+ };
54
+ return handleStatus(client, params);
55
+ });
56
+ register('babelize_glossary_get', {
57
+ description: 'List glossary entries for a project. Requires API key.',
58
+ inputSchema: z.object({
59
+ project_id: z.string(),
60
+ search: z.string().optional(),
61
+ }),
62
+ }, async (params) => {
63
+ if (!client)
64
+ return {
65
+ content: [{ type: 'text', text: '❌ API key required. Set BABELIZE_API_KEY or pass --api-key.' }],
66
+ isError: true,
67
+ };
68
+ return handleGlossaryGet(client, params);
69
+ });
70
+ register('babelize_glossary_set', {
71
+ description: 'Add a glossary entry to ensure consistent translations. Requires API key.',
72
+ inputSchema: z.object({
73
+ project_id: z.string(),
74
+ source: z.string(),
75
+ target: z.string(),
76
+ type: z.string().optional(),
77
+ locale: z.string().optional(),
78
+ }),
79
+ }, async (params) => {
80
+ if (!client)
81
+ return {
82
+ content: [{ type: 'text', text: '❌ API key required. Set BABELIZE_API_KEY or pass --api-key.' }],
83
+ isError: true,
84
+ };
85
+ return handleGlossarySet(client, params);
86
+ });
87
+ register('babelize_scan_strings', {
88
+ description: 'Extract hardcoded translatable strings from a code snippet. No API key required.',
89
+ inputSchema: z.object({ code: z.string() }),
90
+ }, ({ code }) => {
91
+ const results = extractStrings(code);
92
+ if (results.length === 0) {
93
+ return {
94
+ content: [{ type: 'text', text: 'No translatable strings found.' }],
95
+ };
96
+ }
97
+ const lines = results.map((s) => ` Line ${s.line}: "${s.text}"`);
98
+ return {
99
+ content: [
100
+ {
101
+ type: 'text',
102
+ text: [`Found ${results.length} translatable strings:`, ...lines].join('\n'),
103
+ },
104
+ ],
105
+ };
106
+ });
107
+ return server;
108
+ }
109
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,20 @@
1
+ import { AxiosInstance } from 'axios';
2
+ interface GlossaryGetParams {
3
+ project_id: string;
4
+ search?: string;
5
+ }
6
+ export declare function handleGlossaryGet(client: AxiosInstance, params: GlossaryGetParams): Promise<{
7
+ content: {
8
+ type: "text";
9
+ text: string;
10
+ }[];
11
+ isError?: undefined;
12
+ } | {
13
+ content: {
14
+ type: "text";
15
+ text: string;
16
+ }[];
17
+ isError: boolean;
18
+ }>;
19
+ export {};
20
+ //# sourceMappingURL=glossary-get.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"glossary-get.d.ts","sourceRoot":"","sources":["../../src/tools/glossary-get.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AAEtC,UAAU,iBAAiB;IACzB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,wBAAsB,iBAAiB,CAAC,MAAM,EAAE,aAAa,EAAE,MAAM,EAAE,iBAAiB;;;;;;;;;;;;GAwDvF"}
@@ -0,0 +1,53 @@
1
+ export async function handleGlossaryGet(client, params) {
2
+ const { project_id, search } = params;
3
+ try {
4
+ const searchParams = new URLSearchParams();
5
+ if (search)
6
+ searchParams.set('search', search);
7
+ const query = searchParams.toString();
8
+ const url = `/projects/${project_id}/glossary${query ? `?${query}` : ''}`;
9
+ const response = await client.get(url);
10
+ const data = response.data;
11
+ const rawEntries = Array.isArray(data) ? data : data.data ?? [];
12
+ const entries = [];
13
+ for (const e of rawEntries) {
14
+ if (e && typeof e === 'object') {
15
+ entries.push({
16
+ term: String(e.term ?? ''),
17
+ type: String(e.type ?? 'never_translate'),
18
+ translations: e.translations,
19
+ });
20
+ }
21
+ }
22
+ if (entries.length === 0) {
23
+ return {
24
+ content: [{ type: 'text', text: '📖 No glossary entries found.' }],
25
+ };
26
+ }
27
+ const lines = [
28
+ `📖 Glossary (${entries.length} entries)${search ? ` matching "${search}"` : ''}:`,
29
+ '',
30
+ ];
31
+ for (const entry of entries) {
32
+ const translationStr = entry.translations && Object.keys(entry.translations).length > 0
33
+ ? Object.entries(entry.translations).map(([l, t]) => `${l}: "${t}"`).join(', ')
34
+ : '(never translated)';
35
+ lines.push(` • "${entry.term}" → ${translationStr} [${entry.type}]`);
36
+ }
37
+ return {
38
+ content: [{ type: 'text', text: lines.join('\n') }],
39
+ };
40
+ }
41
+ catch (err) {
42
+ const message = err instanceof Object && err !== null && 'response' in err
43
+ ? (err.response?.data?.error || 'Unknown error')
44
+ : err instanceof Error
45
+ ? err.message
46
+ : 'Unknown error';
47
+ return {
48
+ content: [{ type: 'text', text: `❌ Failed to get glossary: ${message}` }],
49
+ isError: true,
50
+ };
51
+ }
52
+ }
53
+ //# sourceMappingURL=glossary-get.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"glossary-get.js","sourceRoot":"","sources":["../../src/tools/glossary-get.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,23 @@
1
+ import { AxiosInstance } from 'axios';
2
+ interface GlossarySetParams {
3
+ project_id: string;
4
+ source: string;
5
+ target: string;
6
+ type?: string;
7
+ locale?: string;
8
+ }
9
+ export declare function handleGlossarySet(client: AxiosInstance, params: GlossarySetParams): Promise<{
10
+ content: {
11
+ type: "text";
12
+ text: string;
13
+ }[];
14
+ isError: boolean;
15
+ } | {
16
+ content: {
17
+ type: "text";
18
+ text: string;
19
+ }[];
20
+ isError?: undefined;
21
+ }>;
22
+ export {};
23
+ //# sourceMappingURL=glossary-set.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"glossary-set.d.ts","sourceRoot":"","sources":["../../src/tools/glossary-set.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AAEtC,UAAU,iBAAiB;IACzB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,wBAAsB,iBAAiB,CAAC,MAAM,EAAE,aAAa,EAAE,MAAM,EAAE,iBAAiB;;;;;;;;;;;;GAqEvF"}
@@ -0,0 +1,64 @@
1
+ export async function handleGlossarySet(client, params) {
2
+ const { project_id, source, target, type, locale } = params;
3
+ if (!source.trim() || !target.trim()) {
4
+ return {
5
+ content: [{ type: 'text', text: '❌ Both source and target are required.' }],
6
+ isError: true,
7
+ };
8
+ }
9
+ const backendType = type === 'always_translate_as' || type === 'always_translate'
10
+ ? 'always_translate_as'
11
+ : 'never_translate';
12
+ const trimmedLocale = locale?.trim();
13
+ if (backendType === 'always_translate_as' && !trimmedLocale) {
14
+ return {
15
+ content: [{ type: 'text', text: '❌ locale is required when type is always_translate_as.' }],
16
+ isError: true,
17
+ };
18
+ }
19
+ const translations = backendType === 'always_translate_as'
20
+ ? { [trimmedLocale]: target.trim() }
21
+ : {};
22
+ try {
23
+ const response = await client.post(`/projects/${project_id}/glossary`, {
24
+ term: source.trim(),
25
+ type: backendType,
26
+ translations,
27
+ });
28
+ const entry = response.data;
29
+ const term = String(entry?.term ?? source.trim());
30
+ const eType = String(entry?.type ?? backendType);
31
+ const rawTranslations = entry?.translations;
32
+ const responseTranslations = rawTranslations && typeof rawTranslations === 'object'
33
+ ? rawTranslations
34
+ : {};
35
+ const translationDisplay = Object.keys(responseTranslations).length > 0
36
+ ? Object.entries(responseTranslations).map(([l, t]) => `${l}: "${t}"`).join(', ')
37
+ : '(none)';
38
+ return {
39
+ content: [
40
+ {
41
+ type: 'text',
42
+ text: [
43
+ `✅ Glossary entry added:`,
44
+ ` Term: "${term}"`,
45
+ ` Type: ${eType}`,
46
+ ` Translations: ${translationDisplay}`,
47
+ ].join('\n'),
48
+ },
49
+ ],
50
+ };
51
+ }
52
+ catch (err) {
53
+ const message = err instanceof Object && err !== null && 'response' in err
54
+ ? (err.response?.data?.error || 'Unknown error')
55
+ : err instanceof Error
56
+ ? err.message
57
+ : 'Unknown error';
58
+ return {
59
+ content: [{ type: 'text', text: `❌ Failed to set glossary entry: ${message}` }],
60
+ isError: true,
61
+ };
62
+ }
63
+ }
64
+ //# sourceMappingURL=glossary-set.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"glossary-set.js","sourceRoot":"","sources":["../../src/tools/glossary-set.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,21 @@
1
+ import { AxiosInstance } from 'axios';
2
+ interface RunParams {
3
+ project_id: string;
4
+ target_locales: string[];
5
+ source_locale?: string;
6
+ }
7
+ export declare function handleRun(client: AxiosInstance, params: RunParams): Promise<{
8
+ content: {
9
+ type: "text";
10
+ text: string;
11
+ }[];
12
+ isError?: undefined;
13
+ } | {
14
+ content: {
15
+ type: "text";
16
+ text: string;
17
+ }[];
18
+ isError: boolean;
19
+ }>;
20
+ export {};
21
+ //# sourceMappingURL=run.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../src/tools/run.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AAEtC,UAAU,SAAS;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,wBAAsB,SAAS,CAAC,MAAM,EAAE,aAAa,EAAE,MAAM,EAAE,SAAS;;;;;;;;;;;;GAyCvE"}
@@ -0,0 +1,41 @@
1
+ export async function handleRun(client, params) {
2
+ const { project_id, target_locales, source_locale } = params;
3
+ try {
4
+ const response = await client.post('/engine/babel', {
5
+ project_id,
6
+ target_locales,
7
+ source_locale: source_locale || 'en',
8
+ dry_run: false,
9
+ });
10
+ const data = response.data;
11
+ const babelId = (data.babel_id ?? data.id);
12
+ return {
13
+ content: [
14
+ {
15
+ type: 'text',
16
+ text: [
17
+ `🚀 Babel translation run started!`,
18
+ ``,
19
+ ` Babel ID: ${babelId}`,
20
+ ` Target languages: ${target_locales.join(', ')}`,
21
+ ` Source language: ${source_locale || 'en'}`,
22
+ ``,
23
+ `Use babelize_status to check progress.`,
24
+ ].join('\n'),
25
+ },
26
+ ],
27
+ };
28
+ }
29
+ catch (err) {
30
+ const message = err instanceof Object && err !== null && 'response' in err
31
+ ? (err.response?.data?.error || 'Unknown error')
32
+ : err instanceof Error
33
+ ? err.message
34
+ : 'Unknown error';
35
+ return {
36
+ content: [{ type: 'text', text: `❌ Failed to start babel run: ${message}` }],
37
+ isError: true,
38
+ };
39
+ }
40
+ }
41
+ //# sourceMappingURL=run.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run.js","sourceRoot":"","sources":["../../src/tools/run.ts"],"names":[],"mappings":"AAQA,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,MAAqB,EAAE,MAAiB;IACtE,MAAM,EAAE,UAAU,EAAE,cAAc,EAAE,aAAa,EAAE,GAAG,MAAM,CAAC;IAE7D,IAAI,CAAC;QACH,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,eAAe,EAAE;YAClD,UAAU;YACV,cAAc;YACd,aAAa,EAAE,aAAa,IAAI,IAAI;YACpC,OAAO,EAAE,KAAK;SACf,CAAC,CAAC;QAEH,MAAM,IAAI,GAAG,QAAQ,CAAC,IAA+B,CAAC;QACtD,MAAM,OAAO,GAAG,CAAC,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC,EAAE,CAAW,CAAC;QAErD,OAAO;YACL,OAAO,EAAE;gBACP;oBACE,IAAI,EAAE,MAAe;oBACrB,IAAI,EAAE;wBACJ,mCAAmC;wBACnC,EAAE;wBACF,eAAe,OAAO,EAAE;wBACxB,uBAAuB,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE;wBAClD,sBAAsB,aAAa,IAAI,IAAI,EAAE;wBAC7C,EAAE;wBACF,wCAAwC;qBACzC,CAAC,IAAI,CAAC,IAAI,CAAC;iBACb;aACF;SACF,CAAC;IACJ,CAAC;IAAC,OAAO,GAAY,EAAE,CAAC;QACtB,MAAM,OAAO,GAAG,GAAG,YAAY,MAAM,IAAI,GAAG,KAAK,IAAI,IAAI,UAAU,IAAI,GAAG;YACxE,CAAC,CAAC,CAAE,GAAiD,CAAC,QAAQ,EAAE,IAAI,EAAE,KAAK,IAAI,eAAe,CAAC;YAC/F,CAAC,CAAC,GAAG,YAAY,KAAK;gBACpB,CAAC,CAAC,GAAG,CAAC,OAAO;gBACb,CAAC,CAAC,eAAe,CAAC;QACtB,OAAO;YACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,gCAAgC,OAAO,EAAE,EAAE,CAAC;YACrF,OAAO,EAAE,IAAI;SACd,CAAC;IACJ,CAAC;AACH,CAAC"}
@@ -0,0 +1,19 @@
1
+ interface ScanParams {
2
+ directory: string;
3
+ extensions?: string[];
4
+ }
5
+ export declare function handleScan(params: ScanParams): Promise<{
6
+ content: {
7
+ type: "text";
8
+ text: string;
9
+ }[];
10
+ isError: boolean;
11
+ } | {
12
+ content: {
13
+ type: "text";
14
+ text: string;
15
+ }[];
16
+ isError?: undefined;
17
+ }>;
18
+ export {};
19
+ //# sourceMappingURL=scan.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scan.d.ts","sourceRoot":"","sources":["../../src/tools/scan.ts"],"names":[],"mappings":"AAGA,UAAU,UAAU;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;CACvB;AAED,wBAAsB,UAAU,CAAC,MAAM,EAAE,UAAU;;;;;;;;;;;;GAiElD"}
@@ -0,0 +1,60 @@
1
+ import { stat } from 'node:fs/promises';
2
+ import { scanDirectory } from '../utils/scan-strings.js';
3
+ export async function handleScan(params) {
4
+ const { directory, extensions } = params;
5
+ // Verify the directory exists
6
+ try {
7
+ const dirStat = await stat(directory);
8
+ if (!dirStat.isDirectory()) {
9
+ return {
10
+ content: [{ type: 'text', text: 'Error: Path is not a directory' }],
11
+ isError: true,
12
+ };
13
+ }
14
+ }
15
+ catch {
16
+ return {
17
+ content: [{ type: 'text', text: `Error: Directory not found: ${directory}` }],
18
+ isError: true,
19
+ };
20
+ }
21
+ // Progress indicator for large directories
22
+ const startTime = Date.now();
23
+ process.stderr.write(`Scanning ${directory} for hardcoded strings...\n`);
24
+ const results = await scanDirectory(directory, extensions);
25
+ const elapsed = ((Date.now() - startTime) / 1000).toFixed(1);
26
+ process.stderr.write(`Scan complete in ${elapsed}s. Found ${results.length} files with strings.\n`);
27
+ if (results.length === 0) {
28
+ return {
29
+ content: [
30
+ {
31
+ type: 'text',
32
+ text: '✅ No hardcoded strings found. Your codebase looks clean!',
33
+ },
34
+ ],
35
+ };
36
+ }
37
+ const totalCount = results.reduce((sum, f) => sum + f.hardcoded_strings.length, 0);
38
+ const totalFiles = results.length;
39
+ const lines = [
40
+ `Found ${totalCount} hardcoded strings across ${totalFiles} files.\n`,
41
+ ];
42
+ for (const file of results.slice(0, 10)) {
43
+ lines.push(`📄 ${file.path}`);
44
+ const previews = file.hardcoded_strings.slice(0, 5);
45
+ for (const s of previews) {
46
+ lines.push(` Line ${s.line}: "${s.text}"`);
47
+ }
48
+ if (file.hardcoded_strings.length > 5) {
49
+ lines.push(` ... and ${file.hardcoded_strings.length - 5} more`);
50
+ }
51
+ lines.push('');
52
+ }
53
+ if (results.length > 10) {
54
+ lines.push(`... and ${results.length - 10} more files with hardcoded strings.`);
55
+ }
56
+ return {
57
+ content: [{ type: 'text', text: lines.join('\n') }],
58
+ };
59
+ }
60
+ //# sourceMappingURL=scan.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scan.js","sourceRoot":"","sources":["../../src/tools/scan.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ import { AxiosInstance } from 'axios';
2
+ interface StatusParams {
3
+ babel_id: string;
4
+ }
5
+ export declare function handleStatus(client: AxiosInstance, params: StatusParams): Promise<{
6
+ content: {
7
+ type: "text";
8
+ text: string;
9
+ }[];
10
+ isError?: undefined;
11
+ } | {
12
+ content: {
13
+ type: "text";
14
+ text: string;
15
+ }[];
16
+ isError: boolean;
17
+ }>;
18
+ export {};
19
+ //# sourceMappingURL=status.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../../src/tools/status.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AAEtC,UAAU,YAAY;IACpB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,wBAAsB,YAAY,CAAC,MAAM,EAAE,aAAa,EAAE,MAAM,EAAE,YAAY;;;;;;;;;;;;GAuC7E"}
@@ -0,0 +1,36 @@
1
+ export async function handleStatus(client, params) {
2
+ const { babel_id } = params;
3
+ try {
4
+ const response = await client.get(`/engine/status/${babel_id}`);
5
+ const data = response.data;
6
+ const status = data.status ?? 'unknown';
7
+ const completedAt = data.completed_at ?? null;
8
+ const error = data.error ?? null;
9
+ const lines = [
10
+ `📊 Babel Run Status: ${babel_id}`,
11
+ ``,
12
+ ` Status: ${status}`,
13
+ ];
14
+ if (completedAt) {
15
+ lines.push(` Completed: ${completedAt}`);
16
+ }
17
+ if (error) {
18
+ lines.push(` Error: ${error}`);
19
+ }
20
+ return {
21
+ content: [{ type: 'text', text: lines.join('\n') }],
22
+ };
23
+ }
24
+ catch (err) {
25
+ const message = err instanceof Object && err !== null && 'response' in err
26
+ ? (err.response?.data?.error || 'Unknown error')
27
+ : err instanceof Error
28
+ ? err.message
29
+ : 'Unknown error';
30
+ return {
31
+ content: [{ type: 'text', text: `❌ Failed to get status: ${message}` }],
32
+ isError: true,
33
+ };
34
+ }
35
+ }
36
+ //# sourceMappingURL=status.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"status.js","sourceRoot":"","sources":["../../src/tools/status.ts"],"names":[],"mappings":"AAMA,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,MAAqB,EAAE,MAAoB;IAC5E,MAAM,EAAE,QAAQ,EAAE,GAAG,MAAM,CAAC;IAE5B,IAAI,CAAC;QACH,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,GAAG,CAAC,kBAAkB,QAAQ,EAAE,CAAC,CAAC;QAChE,MAAM,IAAI,GAAG,QAAQ,CAAC,IAA+B,CAAC;QAEtD,MAAM,MAAM,GAAG,IAAI,CAAC,MAAgB,IAAI,SAAS,CAAC;QAClD,MAAM,WAAW,GAAG,IAAI,CAAC,YAAsB,IAAI,IAAI,CAAC;QACxD,MAAM,KAAK,GAAG,IAAI,CAAC,KAAe,IAAI,IAAI,CAAC;QAE3C,MAAM,KAAK,GAAa;YACtB,wBAAwB,QAAQ,EAAE;YAClC,EAAE;YACF,aAAa,MAAM,EAAE;SACtB,CAAC;QAEF,IAAI,WAAW,EAAE,CAAC;YAChB,KAAK,CAAC,IAAI,CAAC,gBAAgB,WAAW,EAAE,CAAC,CAAC;QAC5C,CAAC;QAED,IAAI,KAAK,EAAE,CAAC;YACV,KAAK,CAAC,IAAI,CAAC,YAAY,KAAK,EAAE,CAAC,CAAC;QAClC,CAAC;QAED,OAAO;YACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;SAC7D,CAAC;IACJ,CAAC;IAAC,OAAO,GAAY,EAAE,CAAC;QACtB,MAAM,OAAO,GAAG,GAAG,YAAY,MAAM,IAAI,GAAG,KAAK,IAAI,IAAI,UAAU,IAAI,GAAG;YACxE,CAAC,CAAC,CAAE,GAAiD,CAAC,QAAQ,EAAE,IAAI,EAAE,KAAK,IAAI,eAAe,CAAC;YAC/F,CAAC,CAAC,GAAG,YAAY,KAAK;gBACpB,CAAC,CAAC,GAAG,CAAC,OAAO;gBACb,CAAC,CAAC,eAAe,CAAC;QACtB,OAAO;YACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,2BAA2B,OAAO,EAAE,EAAE,CAAC;YAChF,OAAO,EAAE,IAAI;SACd,CAAC;IACJ,CAAC;AACH,CAAC"}
@@ -0,0 +1,13 @@
1
+ interface HardcodedString {
2
+ text: string;
3
+ line: number;
4
+ }
5
+ interface ScanResult {
6
+ path: string;
7
+ hardcoded_strings: HardcodedString[];
8
+ }
9
+ export declare function isTranslatable(text: string): boolean;
10
+ export declare function extractStrings(content: string): HardcodedString[];
11
+ export declare function scanDirectory(dirPath: string, extensions?: string[]): Promise<ScanResult[]>;
12
+ export {};
13
+ //# sourceMappingURL=scan-strings.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scan-strings.d.ts","sourceRoot":"","sources":["../../src/utils/scan-strings.ts"],"names":[],"mappings":"AAeA,UAAU,eAAe;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd;AAED,UAAU,UAAU;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,iBAAiB,EAAE,eAAe,EAAE,CAAC;CACtC;AA4BD,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAkBpD;AAMD,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,eAAe,EAAE,CA+BjE;AAED,wBAAsB,aAAa,CACjC,OAAO,EAAE,MAAM,EACf,UAAU,GAAE,MAAM,EAAuB,GACxC,OAAO,CAAC,UAAU,EAAE,CAAC,CAoDvB"}
@@ -0,0 +1,144 @@
1
+ import { readdir, readFile, stat } from 'node:fs/promises';
2
+ import * as path from 'node:path';
3
+ const DEFAULT_EXTENSIONS = ['.tsx', '.jsx', '.ts', '.js', '.vue'];
4
+ const ALWAYS_IGNORE = new Set([
5
+ 'node_modules',
6
+ 'dist',
7
+ '.git',
8
+ '.next',
9
+ 'build',
10
+ 'coverage',
11
+ '.cache',
12
+ ]);
13
+ // Matches string literals: '...', "...", or `...` (template literals)
14
+ const STRING_RE = /(['"`])((?:(?!\1|\\).|\\.)*?)\1/g;
15
+ // JSX text content: bare text between `>` or `}` and the next `{`, `<`, or end of line
16
+ const JSX_TEXT_RE = /(?:>|})([^{}<>]+?)(?=\{|<|$)/g;
17
+ // Context lines to skip: imports, requires, exports, CSS classNames, etc.
18
+ const SKIP_PATTERNS = [
19
+ /^\s*import\s/,
20
+ /^\s*export\s/,
21
+ /^\s*const\s+\w+\s*=\s*require\(/,
22
+ /^\s*\/\//,
23
+ /^\s*\*/, // JSDoc
24
+ /^\s*\/\*\*/,
25
+ /className:/,
26
+ /className=/,
27
+ /style=/,
28
+ /\.css$/,
29
+ /^\s*'use /,
30
+ /^\s*"use /,
31
+ /^\s*\/\/\s*eslint/,
32
+ /^\s*\/\/\s*@ts-/,
33
+ /^\s*\/\/\s*istanbul/,
34
+ ];
35
+ // Only match strings that look like user-facing text
36
+ export function isTranslatable(text) {
37
+ const trimmed = text.trim();
38
+ if (trimmed.length < 2)
39
+ return false;
40
+ // Skip single characters, numbers, short fragments
41
+ if (trimmed.length <= 2 && /^[\d\s\W]+$/.test(trimmed))
42
+ return false;
43
+ // Skip template literals with interpolation
44
+ if (/\$\{/.test(trimmed))
45
+ return false;
46
+ // Skip URLs
47
+ if (/^https?:\/\//.test(trimmed))
48
+ return false;
49
+ // Skip file paths
50
+ if (/^\.?\//.test(trimmed))
51
+ return false;
52
+ // Skip common non-translatable patterns
53
+ if (/^[a-z_]+$/.test(trimmed) && trimmed.length < 20)
54
+ return false;
55
+ // Must contain at least one letter
56
+ if (!/[a-zA-Z]/.test(trimmed))
57
+ return false;
58
+ // Skip empty JSX expressions like {true}, {false}, {0}
59
+ if (/^(true|false|null|undefined|\d+)$/.test(trimmed))
60
+ return false;
61
+ return true;
62
+ }
63
+ function shouldSkipLine(line) {
64
+ return SKIP_PATTERNS.some((p) => p.test(line));
65
+ }
66
+ export function extractStrings(content) {
67
+ const lines = content.split('\n');
68
+ const results = [];
69
+ for (let i = 0; i < lines.length; i++) {
70
+ const line = lines[i];
71
+ if (shouldSkipLine(line))
72
+ continue;
73
+ let match;
74
+ STRING_RE.lastIndex = 0;
75
+ while ((match = STRING_RE.exec(line)) !== null) {
76
+ const text = match[2];
77
+ if (isTranslatable(text)) {
78
+ results.push({ text, line: i + 1 });
79
+ }
80
+ }
81
+ JSX_TEXT_RE.lastIndex = 0;
82
+ while ((match = JSX_TEXT_RE.exec(line)) !== null) {
83
+ const text = match[1].trim();
84
+ if (text && isTranslatable(text)) {
85
+ if (!results.some(r => r.text === text && r.line === i + 1)) {
86
+ results.push({ text, line: i + 1 });
87
+ }
88
+ }
89
+ }
90
+ }
91
+ return results;
92
+ }
93
+ export async function scanDirectory(dirPath, extensions = DEFAULT_EXTENSIONS) {
94
+ const extSet = new Set(extensions);
95
+ const results = [];
96
+ async function walk(currentPath) {
97
+ let entries;
98
+ try {
99
+ entries = await readdir(currentPath, { withFileTypes: false });
100
+ }
101
+ catch {
102
+ return;
103
+ }
104
+ const dirEntries = await Promise.all(entries.map(async (name) => {
105
+ if (ALWAYS_IGNORE.has(name))
106
+ return null;
107
+ try {
108
+ const fullPath = path.join(currentPath, name);
109
+ const entryStat = await stat(fullPath);
110
+ return { name, fullPath, isDirectory: entryStat.isDirectory(), isFile: entryStat.isFile() };
111
+ }
112
+ catch {
113
+ return null;
114
+ }
115
+ }));
116
+ for (const entry of dirEntries) {
117
+ if (!entry)
118
+ continue;
119
+ if (entry.isDirectory) {
120
+ await walk(entry.fullPath);
121
+ }
122
+ else if (entry.isFile && extSet.has(path.extname(entry.name))) {
123
+ try {
124
+ const content = await readFile(entry.fullPath, 'utf-8');
125
+ const strings = extractStrings(content);
126
+ if (strings.length > 0) {
127
+ results.push({
128
+ path: entry.fullPath,
129
+ hardcoded_strings: strings,
130
+ });
131
+ }
132
+ }
133
+ catch {
134
+ // Skip unreadable files
135
+ }
136
+ }
137
+ }
138
+ }
139
+ await walk(dirPath);
140
+ // Sort by most strings found (descending), limit to top 50 files
141
+ results.sort((a, b) => b.hardcoded_strings.length - a.hardcoded_strings.length);
142
+ return results.slice(0, 50);
143
+ }
144
+ //# sourceMappingURL=scan-strings.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scan-strings.js","sourceRoot":"","sources":["../../src/utils/scan-strings.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,53 @@
1
+ {
2
+ "name": "@babelize/mcp",
3
+ "version": "1.0.0",
4
+ "description": "MCP (Model Context Protocol) server for Babelize AI-powered i18n — let AI coding agents translate your apps",
5
+ "keywords": [
6
+ "babelize",
7
+ "mcp",
8
+ "i18n",
9
+ "translation",
10
+ "ai",
11
+ "llm",
12
+ "cursor",
13
+ "claude",
14
+ "codex"
15
+ ],
16
+ "homepage": "https://babelize.co",
17
+ "repository": {
18
+ "type": "git",
19
+ "url": "git+https://github.com/babelize/babelize-dashboard.git"
20
+ },
21
+ "type": "module",
22
+ "bin": {
23
+ "babelize-mcp": "dist/index.js"
24
+ },
25
+ "files": [
26
+ "dist",
27
+ "README.md",
28
+ "LICENSE"
29
+ ],
30
+ "scripts": {
31
+ "build": "tsc",
32
+ "dev": "tsc --watch",
33
+ "start": "node dist/index.js",
34
+ "typecheck": "tsc --noEmit --project tsconfig.typecheck.json",
35
+ "lint": "eslint src",
36
+ "test": "node --experimental-vm-modules node_modules/jest/bin/jest.js --config jest.config.cjs",
37
+ "prepack": "rm -rf dist/__tests__",
38
+ "prepublishOnly": "npm test && npm run typecheck"
39
+ },
40
+ "dependencies": {
41
+ "@modelcontextprotocol/sdk": "^1.0.0",
42
+ "axios": "^1.15.2",
43
+ "zod": "^3.24.0"
44
+ },
45
+ "devDependencies": {
46
+ "@jest/globals": "^30.2.0",
47
+ "@types/jest": "^30.0.0",
48
+ "@types/node": "^22.0.0",
49
+ "jest": "^30.2.0",
50
+ "ts-jest": "^29.4.6",
51
+ "typescript": "^5.7.3"
52
+ }
53
+ }