@i18n-agent/mcp-client 1.8.21 ā 1.8.23
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/install.js +3 -0
- package/namespace-detector.js +362 -0
- package/package.json +2 -1
package/install.js
CHANGED
|
@@ -586,6 +586,9 @@ Step 2: Add API key to the config file
|
|
|
586
586
|
Step 3: Restart your IDE
|
|
587
587
|
Close and reopen your IDE to load the new configuration
|
|
588
588
|
`);
|
|
589
|
+
} else if (idesWithApiKey.length > 0 && idesNeedingApiKey.length === 0) {
|
|
590
|
+
// All IDEs have API keys - no setup needed, just restart
|
|
591
|
+
console.log(`š” All IDEs have API keys configured. Just restart your IDE(s) to use the updated MCP client.`);
|
|
589
592
|
}
|
|
590
593
|
|
|
591
594
|
// Show test instructions (for all IDEs)
|
|
@@ -0,0 +1,362 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Namespace Auto-Detection Utility for MCP Client
|
|
3
|
+
*
|
|
4
|
+
* Analyzes file paths and suggests namespace names based on project structure.
|
|
5
|
+
* Follows the same validation rules as the service-mcp namespace validator.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Auto-detect namespace from file path
|
|
10
|
+
*/
|
|
11
|
+
export function detectNamespaceFromPath(filePath, options = {}) {
|
|
12
|
+
const { includeAlternatives = true, maxAlternatives = 3 } = options;
|
|
13
|
+
|
|
14
|
+
if (!filePath || typeof filePath !== 'string') {
|
|
15
|
+
return {
|
|
16
|
+
suggestion: null,
|
|
17
|
+
confidence: 0,
|
|
18
|
+
source: 'none',
|
|
19
|
+
reasoning: 'No file path provided',
|
|
20
|
+
alternatives: []
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// Normalize path separators
|
|
25
|
+
const normalizedPath = filePath.replace(/\\/g, '/');
|
|
26
|
+
const pathSegments = normalizedPath.split('/').filter(segment => segment.length > 0);
|
|
27
|
+
|
|
28
|
+
// Pattern detection strategies (ordered by confidence)
|
|
29
|
+
const detectionStrategies = [
|
|
30
|
+
detectServicePattern,
|
|
31
|
+
detectGitRepoPattern,
|
|
32
|
+
detectProjectFolderPattern,
|
|
33
|
+
detectDirectoryPattern
|
|
34
|
+
];
|
|
35
|
+
|
|
36
|
+
let bestResult = null;
|
|
37
|
+
const allSuggestions = [];
|
|
38
|
+
|
|
39
|
+
for (const strategy of detectionStrategies) {
|
|
40
|
+
const result = strategy(pathSegments, normalizedPath);
|
|
41
|
+
if (result.suggestion && isValidNamespace(result.suggestion)) {
|
|
42
|
+
if (!bestResult || result.confidence > bestResult.confidence) {
|
|
43
|
+
bestResult = result;
|
|
44
|
+
}
|
|
45
|
+
if (includeAlternatives && !allSuggestions.includes(result.suggestion)) {
|
|
46
|
+
allSuggestions.push(result.suggestion);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (!bestResult) {
|
|
52
|
+
return {
|
|
53
|
+
suggestion: null,
|
|
54
|
+
confidence: 0,
|
|
55
|
+
source: 'none',
|
|
56
|
+
reasoning: 'Could not detect a valid namespace from the file path',
|
|
57
|
+
alternatives: []
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Generate alternatives
|
|
62
|
+
const alternatives = includeAlternatives
|
|
63
|
+
? allSuggestions
|
|
64
|
+
.filter(s => s !== bestResult.suggestion)
|
|
65
|
+
.slice(0, maxAlternatives)
|
|
66
|
+
: [];
|
|
67
|
+
|
|
68
|
+
return {
|
|
69
|
+
...bestResult,
|
|
70
|
+
alternatives
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Detect service-* pattern (highest confidence)
|
|
76
|
+
* Examples:
|
|
77
|
+
* - /path/to/service-platform/file.json ā "service-platform"
|
|
78
|
+
* - /path/to/service-auth/config.yaml ā "service-auth"
|
|
79
|
+
*/
|
|
80
|
+
function detectServicePattern(pathSegments) {
|
|
81
|
+
for (let i = 0; i < pathSegments.length; i++) {
|
|
82
|
+
const segment = pathSegments[i];
|
|
83
|
+
if (segment.startsWith('service-') && segment.length > 8) {
|
|
84
|
+
const serviceName = segment.toLowerCase();
|
|
85
|
+
return {
|
|
86
|
+
suggestion: serviceName,
|
|
87
|
+
confidence: 0.9,
|
|
88
|
+
source: 'service-name',
|
|
89
|
+
reasoning: `Detected service name "${serviceName}" in path`,
|
|
90
|
+
alternatives: []
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
return { suggestion: null, confidence: 0, source: 'none', reasoning: '', alternatives: [] };
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Detect git repository pattern (high confidence)
|
|
100
|
+
* Examples:
|
|
101
|
+
* - /path/to/i18n-agent/service-platform/file.json ā "service-platform"
|
|
102
|
+
* - /path/to/my-project/src/file.ts ā "my-project"
|
|
103
|
+
*/
|
|
104
|
+
function detectGitRepoPattern(pathSegments) {
|
|
105
|
+
// Look for common project root indicators
|
|
106
|
+
const projectRootIndicators = [
|
|
107
|
+
'package.json', '.git', 'node_modules', 'src', 'lib', 'app',
|
|
108
|
+
'components', 'pages', 'public', 'assets', 'docs', 'tests'
|
|
109
|
+
];
|
|
110
|
+
|
|
111
|
+
// Find potential project root by looking for these indicators
|
|
112
|
+
for (let i = pathSegments.length - 1; i >= 0; i--) {
|
|
113
|
+
const segment = pathSegments[i];
|
|
114
|
+
|
|
115
|
+
// If this looks like a project folder, use it
|
|
116
|
+
if (segment.includes('-') || segment.includes('_')) {
|
|
117
|
+
// Check if next segments contain project indicators
|
|
118
|
+
const remainingPath = pathSegments.slice(i + 1);
|
|
119
|
+
const hasProjectIndicators = remainingPath.some(seg =>
|
|
120
|
+
projectRootIndicators.includes(seg) ||
|
|
121
|
+
seg === 'src' ||
|
|
122
|
+
seg === 'lib' ||
|
|
123
|
+
seg.includes('component') ||
|
|
124
|
+
seg.includes('page')
|
|
125
|
+
);
|
|
126
|
+
|
|
127
|
+
if (hasProjectIndicators) {
|
|
128
|
+
const suggestion = normalizeNamespace(segment);
|
|
129
|
+
if (suggestion) {
|
|
130
|
+
return {
|
|
131
|
+
suggestion,
|
|
132
|
+
confidence: 0.7,
|
|
133
|
+
source: 'git-repo',
|
|
134
|
+
reasoning: `Detected project root "${segment}" based on directory structure`,
|
|
135
|
+
alternatives: []
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
return { suggestion: null, confidence: 0, source: 'none', reasoning: '', alternatives: [] };
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Detect project folder pattern (medium confidence)
|
|
147
|
+
* Examples:
|
|
148
|
+
* - /Users/user/Documents/my-awesome-project/file.js ā "my-awesome-project"
|
|
149
|
+
* - /workspace/client-docs/locales/en.json ā "client-docs"
|
|
150
|
+
*/
|
|
151
|
+
function detectProjectFolderPattern(pathSegments) {
|
|
152
|
+
// Look for segments that contain hyphens or underscores (common in project names)
|
|
153
|
+
const projectLikeSegments = pathSegments.filter(segment =>
|
|
154
|
+
(segment.includes('-') || segment.includes('_')) &&
|
|
155
|
+
segment.length >= 3 &&
|
|
156
|
+
!segment.startsWith('.') &&
|
|
157
|
+
segment !== 'node_modules'
|
|
158
|
+
);
|
|
159
|
+
|
|
160
|
+
if (projectLikeSegments.length > 0) {
|
|
161
|
+
// Use the last project-like segment (closest to the file)
|
|
162
|
+
const lastProjectSegment = projectLikeSegments[projectLikeSegments.length - 1];
|
|
163
|
+
const suggestion = normalizeNamespace(lastProjectSegment);
|
|
164
|
+
|
|
165
|
+
if (suggestion) {
|
|
166
|
+
return {
|
|
167
|
+
suggestion,
|
|
168
|
+
confidence: 0.5,
|
|
169
|
+
source: 'project-folder',
|
|
170
|
+
reasoning: `Detected project-like folder "${lastProjectSegment}" in path`,
|
|
171
|
+
alternatives: []
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
return { suggestion: null, confidence: 0, source: 'none', reasoning: '', alternatives: [] };
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Detect directory pattern (low confidence fallback)
|
|
181
|
+
* Uses the immediate parent directory if it looks reasonable
|
|
182
|
+
*/
|
|
183
|
+
function detectDirectoryPattern(pathSegments) {
|
|
184
|
+
if (pathSegments.length < 2) {
|
|
185
|
+
return { suggestion: null, confidence: 0, source: 'none', reasoning: '', alternatives: [] };
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// Use the parent directory of the file
|
|
189
|
+
const parentDir = pathSegments[pathSegments.length - 2];
|
|
190
|
+
const suggestion = normalizeNamespace(parentDir);
|
|
191
|
+
|
|
192
|
+
if (suggestion && suggestion.length >= 3) {
|
|
193
|
+
return {
|
|
194
|
+
suggestion,
|
|
195
|
+
confidence: 0.3,
|
|
196
|
+
source: 'directory-name',
|
|
197
|
+
reasoning: `Using parent directory "${parentDir}" as fallback`,
|
|
198
|
+
alternatives: []
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
return { suggestion: null, confidence: 0, source: 'none', reasoning: '', alternatives: [] };
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Normalize a potential namespace to follow validation rules
|
|
207
|
+
*/
|
|
208
|
+
function normalizeNamespace(input) {
|
|
209
|
+
if (!input || typeof input !== 'string') return null;
|
|
210
|
+
|
|
211
|
+
// Convert to lowercase and trim
|
|
212
|
+
let normalized = input.toLowerCase().trim();
|
|
213
|
+
|
|
214
|
+
// Replace invalid characters with hyphens
|
|
215
|
+
normalized = normalized.replace(/[^a-z0-9_-]/g, '-');
|
|
216
|
+
|
|
217
|
+
// Remove multiple consecutive hyphens/underscores
|
|
218
|
+
normalized = normalized.replace(/[-_]{2,}/g, '-');
|
|
219
|
+
|
|
220
|
+
// Remove leading/trailing hyphens or underscores
|
|
221
|
+
normalized = normalized.replace(/^[-_]+|[-_]+$/g, '');
|
|
222
|
+
|
|
223
|
+
// Check length constraints
|
|
224
|
+
if (normalized.length < 3 || normalized.length > 50) {
|
|
225
|
+
return null;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// Check if it's a reserved keyword
|
|
229
|
+
const reservedKeywords = [
|
|
230
|
+
'admin', 'api', 'www', 'system', 'root', 'default',
|
|
231
|
+
'translations', 'upload', 'download', 'temp', 'cache',
|
|
232
|
+
'public', 'private', 'test', 'staging', 'production'
|
|
233
|
+
];
|
|
234
|
+
|
|
235
|
+
if (reservedKeywords.includes(normalized)) {
|
|
236
|
+
return null;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
return normalized;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* Quick validation using the same rules as service-mcp
|
|
244
|
+
*/
|
|
245
|
+
function isValidNamespace(namespace) {
|
|
246
|
+
if (!namespace || typeof namespace !== 'string') return false;
|
|
247
|
+
|
|
248
|
+
// Basic validation
|
|
249
|
+
const pattern = /^[a-z0-9_-]+$/;
|
|
250
|
+
return (
|
|
251
|
+
namespace.length >= 3 &&
|
|
252
|
+
namespace.length <= 50 &&
|
|
253
|
+
pattern.test(namespace) &&
|
|
254
|
+
!['admin', 'api', 'www', 'system', 'root', 'default'].includes(namespace)
|
|
255
|
+
);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* Generate additional namespace suggestions based on file context
|
|
260
|
+
*/
|
|
261
|
+
export function generateNamespaceSuggestions(fileName, fileContent) {
|
|
262
|
+
const suggestions = [];
|
|
263
|
+
|
|
264
|
+
if (fileName) {
|
|
265
|
+
// Extract potential namespace from filename
|
|
266
|
+
const baseName = fileName.replace(/\.(json|yaml|yml|ts|js|md)$/i, '');
|
|
267
|
+
const normalized = normalizeNamespace(baseName);
|
|
268
|
+
if (normalized) {
|
|
269
|
+
suggestions.push(normalized);
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
// Look for common patterns in filename
|
|
273
|
+
if (fileName.includes('i18n') || fileName.includes('locale') || fileName.includes('lang')) {
|
|
274
|
+
suggestions.push('localization', 'translations', 'i18n-project');
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
if (fileName.includes('api') || fileName.includes('endpoint')) {
|
|
278
|
+
suggestions.push('api-docs', 'api-project');
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
if (fileName.includes('component') || fileName.includes('ui')) {
|
|
282
|
+
suggestions.push('components', 'ui-library');
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
if (fileContent) {
|
|
287
|
+
// Analyze content for clues (basic implementation)
|
|
288
|
+
const content = fileContent.toLowerCase();
|
|
289
|
+
|
|
290
|
+
if (content.includes('api') || content.includes('endpoint')) {
|
|
291
|
+
suggestions.push('api-docs');
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
if (content.includes('component') || content.includes('react') || content.includes('vue')) {
|
|
295
|
+
suggestions.push('components');
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
if (content.includes('translation') || content.includes('locale')) {
|
|
299
|
+
suggestions.push('translations');
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
// Remove duplicates and filter valid ones
|
|
304
|
+
return [...new Set(suggestions)]
|
|
305
|
+
.filter(s => isValidNamespace(s))
|
|
306
|
+
.slice(0, 5);
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
/**
|
|
310
|
+
* Get helpful namespace suggestions text for user guidance
|
|
311
|
+
*/
|
|
312
|
+
export function getNamespaceSuggestionText(filePath, fileName) {
|
|
313
|
+
if (!filePath && !fileName) {
|
|
314
|
+
return `š” Namespace Suggestions:
|
|
315
|
+
⢠Use descriptive project names: "my-website", "mobile-app"
|
|
316
|
+
⢠Include service names: "service-auth", "api-gateway"
|
|
317
|
+
⢠Use organization prefixes: "company-docs", "team-frontend"
|
|
318
|
+
⢠Avoid generic names: "app", "project", "files"`;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
const detection = detectNamespaceFromPath(filePath || fileName);
|
|
322
|
+
|
|
323
|
+
if (detection.suggestion) {
|
|
324
|
+
let text = `šÆ Auto-detected suggestion: "${detection.suggestion}"`;
|
|
325
|
+
text += `\nš Source: ${getSourceDescription(detection.source)}`;
|
|
326
|
+
text += `\nšŖ Confidence: ${Math.round(detection.confidence * 100)}%`;
|
|
327
|
+
|
|
328
|
+
if (detection.alternatives.length > 0) {
|
|
329
|
+
text += `\nš Alternatives: ${detection.alternatives.join(', ')}`;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
return text;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
return `š” Namespace Suggestions for "${fileName || filePath}":
|
|
336
|
+
⢠Extract from path: "${getPathBasedSuggestion(filePath || fileName)}"
|
|
337
|
+
⢠Use project context: "docs", "website", "api"
|
|
338
|
+
⢠Include team/org: "frontend-team", "backend-api"
|
|
339
|
+
⢠Keep it descriptive: "user-dashboard", "admin-panel"`;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
function getSourceDescription(source) {
|
|
343
|
+
const descriptions = {
|
|
344
|
+
'service-name': 'Service name pattern detected',
|
|
345
|
+
'git-repo': 'Git repository structure analyzed',
|
|
346
|
+
'project-folder': 'Project folder pattern found',
|
|
347
|
+
'directory-name': 'Parent directory used as fallback'
|
|
348
|
+
};
|
|
349
|
+
return descriptions[source] || 'Unknown source';
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
function getPathBasedSuggestion(path) {
|
|
353
|
+
if (!path) return 'my-project';
|
|
354
|
+
|
|
355
|
+
const segments = path.replace(/\\/g, '/').split('/').filter(s => s.length > 0);
|
|
356
|
+
for (let i = segments.length - 1; i >= 0; i--) {
|
|
357
|
+
const normalized = normalizeNamespace(segments[i]);
|
|
358
|
+
if (normalized) return normalized;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
return 'my-project';
|
|
362
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@i18n-agent/mcp-client",
|
|
3
|
-
"version": "1.8.
|
|
3
|
+
"version": "1.8.23",
|
|
4
4
|
"description": "š i18n-agent MCP Client - 48 languages, AI-powered translation for Claude, Claude Code, Cursor, VS Code, Codex. Get API key at https://app.i18nagent.ai",
|
|
5
5
|
"main": "mcp-client.js",
|
|
6
6
|
"bin": {
|
|
@@ -42,6 +42,7 @@
|
|
|
42
42
|
},
|
|
43
43
|
"files": [
|
|
44
44
|
"mcp-client.js",
|
|
45
|
+
"namespace-detector.js",
|
|
45
46
|
"install.js",
|
|
46
47
|
"README.md",
|
|
47
48
|
"LICENSE",
|