@l10nmonster/mcp 3.1.0 → 3.1.1

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/CHANGELOG.md CHANGED
@@ -1,3 +1,18 @@
1
+ ## @l10nmonster/mcp [3.1.1](https://public-github/l10nmonster/l10nmonster/compare/@l10nmonster/mcp@3.1.0...@l10nmonster/mcp@3.1.1) (2025-12-23)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * Improve type definitions and checks ([826b412](https://public-github/l10nmonster/l10nmonster/commit/826b412f0f7e761d404165a243b0c2b26c416ac1))
7
+
8
+
9
+
10
+
11
+
12
+ ### Dependencies
13
+
14
+ * **@l10nmonster/core:** upgraded to 3.1.1
15
+
1
16
  # @l10nmonster/mcp [3.1.0](https://public-github/l10nmonster/l10nmonster/compare/@l10nmonster/mcp@3.0.0...@l10nmonster/mcp@3.1.0) (2025-12-20)
2
17
 
3
18
 
@@ -0,0 +1,77 @@
1
+ /**
2
+ * Central type definitions for @l10nmonster/mcp
3
+ * This is the single source of truth for MCP tool interfaces.
4
+ */
5
+
6
+ /**
7
+ * MCP tool metadata definition.
8
+ */
9
+ export interface McpToolMetadata {
10
+ /** Tool name identifier. */
11
+ name: string;
12
+ /** Tool description for discovery. */
13
+ description: string;
14
+ /** Zod input schema for validation. */
15
+ inputSchema: unknown;
16
+ }
17
+
18
+ /**
19
+ * MCP content item in a response.
20
+ */
21
+ export interface McpContentItem {
22
+ /** Content type (text, image, resource). */
23
+ type: 'text' | 'image' | 'resource';
24
+ /** Text content (for type='text'). */
25
+ text?: string;
26
+ /** Base64 data (for type='image'). */
27
+ data?: string;
28
+ /** MIME type (for type='image' or 'resource'). */
29
+ mimeType?: string;
30
+ }
31
+
32
+ /**
33
+ * MCP tool response format.
34
+ */
35
+ export interface McpToolResponse {
36
+ /** Array of content items. */
37
+ content: McpContentItem[];
38
+ /** True if this response represents an error. */
39
+ isError?: boolean;
40
+ }
41
+
42
+ /**
43
+ * MCP tool interface - defines contract for MCP tools.
44
+ */
45
+ export interface McpToolInterface {
46
+ /** Static metadata for the tool. */
47
+ metadata: McpToolMetadata;
48
+
49
+ /**
50
+ * Execute the tool with given arguments.
51
+ * @param mm - MonsterManager instance.
52
+ * @param args - Tool arguments.
53
+ * @returns Tool result.
54
+ */
55
+ execute(mm: unknown, args: Record<string, unknown>): Promise<unknown>;
56
+
57
+ /**
58
+ * Get handler function for MCP registration.
59
+ * @param mm - MonsterManager instance.
60
+ * @returns Handler function.
61
+ */
62
+ handler(mm: unknown): (args: Record<string, unknown>) => Promise<McpToolResponse>;
63
+
64
+ /**
65
+ * Format a result for MCP response.
66
+ * @param result - Tool execution result.
67
+ * @returns Formatted MCP response.
68
+ */
69
+ formatResult(result: unknown): McpToolResponse;
70
+
71
+ /**
72
+ * Format an error for MCP response.
73
+ * @param error - Error to format.
74
+ * @returns Formatted error response.
75
+ */
76
+ formatError(error: Error): McpToolResponse;
77
+ }
package/package.json CHANGED
@@ -1,31 +1,32 @@
1
1
  {
2
- "name": "@l10nmonster/mcp",
3
- "version": "3.1.0",
4
- "description": "L10n Monster Model Context Protocol (MCP) Server",
5
- "type": "module",
6
- "main": "index.js",
7
- "scripts": {
8
- "test": "node --test tests/*.test.js"
9
- },
10
- "dependencies": {
11
- "@modelcontextprotocol/sdk": "^1.18.1",
12
- "zod": "^3"
13
- },
14
- "peerDependencies": {
15
- "@l10nmonster/core": "3.1.0"
16
- },
17
- "engines": {
18
- "node": ">=22.11.0"
19
- },
20
- "devDependencies": {
21
- "@types/node": "^22.0.0"
22
- },
23
- "keywords": [
24
- "mcp",
25
- "localization",
26
- "translation",
27
- "l10n"
28
- ],
29
- "author": "L10n Monster",
30
- "license": "MIT"
2
+ "name": "@l10nmonster/mcp",
3
+ "version": "3.1.1",
4
+ "description": "L10n Monster Model Context Protocol (MCP) Server",
5
+ "type": "module",
6
+ "main": "index.js",
7
+ "scripts": {
8
+ "test": "node --test tests/*.test.js",
9
+ "typecheck": "tsc --noEmit"
10
+ },
11
+ "dependencies": {
12
+ "@modelcontextprotocol/sdk": "^1.18.1",
13
+ "zod": "^3"
14
+ },
15
+ "peerDependencies": {
16
+ "@l10nmonster/core": "3.1.1"
17
+ },
18
+ "engines": {
19
+ "node": ">=22.11.0"
20
+ },
21
+ "devDependencies": {
22
+ "@types/node": "^22.0.0"
23
+ },
24
+ "keywords": [
25
+ "mcp",
26
+ "localization",
27
+ "translation",
28
+ "l10n"
29
+ ],
30
+ "author": "L10n Monster",
31
+ "license": "MIT"
31
32
  }
package/server.js CHANGED
@@ -4,8 +4,7 @@ import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/
4
4
  import { isInitializeRequest } from '@modelcontextprotocol/sdk/types.js';
5
5
  import * as mcpTools from './tools/index.js';
6
6
  import { registry } from './tools/registry.js';
7
- import { readFile } from 'node:fs/promises';
8
- import path from 'node:path';
7
+ import { l10nMonsterVersion } from '@l10nmonster/core';
9
8
 
10
9
  // Session management for HTTP transport
11
10
  const sessions = new Map(); // sessionId -> { transport, lastActivity }
@@ -15,20 +14,8 @@ const SESSION_TIMEOUT_MS = 30 * 60 * 1000; // 30 minutes
15
14
  // Use a WeakMap to avoid memory leaks. Once all sessions to a server are closed the server will be garbage collected.
16
15
  const serverInstances = new WeakMap(); // monsterManager -> McpServer
17
16
 
18
-
19
- async function getMcpPackageVersion() {
20
- try {
21
- const packageJsonContent = await readFile(path.join(import.meta.dirname, 'package.json'), 'utf-8');
22
- const packageJson = JSON.parse(packageJsonContent.toString());
23
- return packageJson.version;
24
- } catch (error) {
25
- console.error('Error parsing MCP package version:', error);
26
- return '0.0.1-unknown';
27
- }
28
- }
29
-
30
- // Set server version to be the package version
31
- const serverVersion = await getMcpPackageVersion();
17
+ // Server version from core package
18
+ const serverVersion = l10nMonsterVersion;
32
19
 
33
20
  /**
34
21
  * Setup tools on an MCP server instance
@@ -116,9 +103,9 @@ async function cleanupExpiredSessions() {
116
103
  /**
117
104
  * Creates MCP route handlers for use with the serve action extension mechanism.
118
105
  * Returns route definitions that can be registered via ServeAction.registerExtension.
119
- *
120
- * @param {import('@l10nmonster/core').MonsterManager} mm - MonsterManager instance
121
- * @returns {Array<[string, string, Function]>} Array of [method, path, handler] route definitions
106
+ *
107
+ * @param {unknown} mm - MonsterManager instance.
108
+ * @returns {Array<[string, string, Function]>} Route definitions.
122
109
  */
123
110
  export function createMcpRoutes(mm) {
124
111
  // Handle POST requests for client-to-server communication
package/tools/mcpTool.js CHANGED
@@ -57,14 +57,12 @@ export class McpProviderError extends McpToolError {
57
57
 
58
58
  /**
59
59
  * Base class for MCP tools that call underlying MonsterManager functions directly.
60
- *
60
+ *
61
61
  * MCP tools are separate from CLI actions and:
62
62
  * - Return structured data (not console output)
63
63
  * - Have MCP-optimized schemas (not CLI-optimized)
64
64
  * - Call underlying MonsterManager methods directly
65
- *
66
65
  */
67
-
68
66
  export class McpTool {
69
67
 
70
68
  /**
package/tsconfig.json ADDED
@@ -0,0 +1,18 @@
1
+ {
2
+ "extends": "../tsconfig.base.json",
3
+ "include": [
4
+ "*.js",
5
+ "**/*.js"
6
+ ],
7
+ "exclude": [
8
+ "node_modules",
9
+ "**/node_modules",
10
+ "test/**",
11
+ "tests/**",
12
+ "**/*.test.js",
13
+ "**/*.spec.js",
14
+ "dist/**",
15
+ "ui/**",
16
+ "types/**"
17
+ ]
18
+ }
package/.releaserc.json DELETED
@@ -1,31 +0,0 @@
1
- {
2
- "branches": [
3
- "main",
4
- {
5
- "name": "next",
6
- "prerelease": "alpha"
7
- },
8
- {
9
- "name": "beta",
10
- "prerelease": "beta"
11
- }
12
- ],
13
- "tagFormat": "@l10nmonster/mcp@${version}",
14
- "plugins": [
15
- "@semantic-release/commit-analyzer",
16
- "@semantic-release/release-notes-generator",
17
- {
18
- "path": "@semantic-release/changelog",
19
- "changelogFile": "CHANGELOG.md"
20
- },
21
- {
22
- "path": "@semantic-release/npm",
23
- "npmPublish": false
24
- },
25
- {
26
- "path": "@semantic-release/git",
27
- "assets": ["CHANGELOG.md", "package.json"],
28
- "message": "chore(release): @l10nmonster/mcp@${nextRelease.version} [skip ci]\n\n${nextRelease.notes}"
29
- }
30
- ]
31
- }
@@ -1,215 +0,0 @@
1
- import { describe, it, beforeEach } from 'node:test';
2
- import assert from 'node:assert';
3
- import { registerTool, McpTool, McpInputError } from '../index.js';
4
- import { registry } from '../tools/registry.js';
5
- import { z } from 'zod';
6
-
7
- // Create a custom tool for testing
8
- class CustomTestTool extends McpTool {
9
- static metadata = {
10
- name: 'custom_test_tool',
11
- description: 'A custom tool for integration testing',
12
- inputSchema: z.object({
13
- message: z.string().describe('A test message')
14
- })
15
- };
16
-
17
- static async execute(mm, args) {
18
- return {
19
- tool: 'custom_test_tool',
20
- message: args.message,
21
- timestamp: new Date().toISOString()
22
- };
23
- }
24
- }
25
-
26
- // Create a tool that overrides a built-in tool name
27
- class OverrideStatusTool extends McpTool {
28
- static metadata = {
29
- name: 'status',
30
- description: 'Custom status tool override',
31
- inputSchema: z.object({})
32
- };
33
-
34
- static async execute() {
35
- return { custom: true, overridden: true };
36
- }
37
- }
38
-
39
- describe('MCP Integration Tests', () => {
40
- beforeEach(() => {
41
- registry.clear();
42
- });
43
-
44
- it('should allow registering and using custom tools', async () => {
45
- // Register the custom tool
46
- registerTool(CustomTestTool);
47
-
48
- // Verify it's registered
49
- assert.strictEqual(registry.hasTool('custom_test_tool'), true);
50
-
51
- // Get the tool and create a handler
52
- const ToolClass = registry.getTool('custom_test_tool');
53
- const handler = ToolClass.handler({});
54
-
55
- // Execute the handler
56
- const result = await handler({ message: 'Hello from custom tool' });
57
-
58
- // Verify the result
59
- assert.ok(result.content);
60
- assert.strictEqual(result.content[0].type, 'text');
61
- const data = JSON.parse(result.content[0].text);
62
- assert.strictEqual(data.tool, 'custom_test_tool');
63
- assert.strictEqual(data.message, 'Hello from custom tool');
64
- });
65
-
66
- it('should allow overriding built-in tools', () => {
67
- // Register an override tool
68
- registerTool(OverrideStatusTool);
69
-
70
- // Verify it's registered with the same name
71
- assert.strictEqual(registry.hasTool('status'), true);
72
-
73
- // Get the tool and verify it's the override
74
- const ToolClass = registry.getTool('status');
75
- assert.strictEqual(ToolClass.metadata.description, 'Custom status tool override');
76
- });
77
-
78
- it('should handle tool execution errors gracefully', async () => {
79
- class ErrorTool extends McpTool {
80
- static metadata = {
81
- name: 'error_tool',
82
- description: 'Tool that throws errors',
83
- inputSchema: z.object({
84
- shouldError: z.boolean()
85
- })
86
- };
87
-
88
- static async execute(mm, args) {
89
- if (args.shouldError) {
90
- throw new McpInputError('Test error', {
91
- hints: ['Try setting shouldError to false']
92
- });
93
- }
94
- return { success: true };
95
- }
96
- }
97
-
98
- registerTool(ErrorTool);
99
-
100
- const ToolClass = registry.getTool('error_tool');
101
- const handler = ToolClass.handler({});
102
-
103
- // Execute with error
104
- const errorResult = await handler({ shouldError: true });
105
-
106
- // Verify error response
107
- assert.strictEqual(errorResult.isError, true);
108
- assert.ok(errorResult.content);
109
- assert.ok(errorResult.content[0].text.includes('Test error'));
110
-
111
- // Execute without error
112
- const successResult = await handler({ shouldError: false });
113
-
114
- // Verify success response
115
- assert.strictEqual(successResult.isError, undefined);
116
- const data = JSON.parse(successResult.content[0].text);
117
- assert.strictEqual(data.success, true);
118
- });
119
-
120
- it('should validate input schemas', async () => {
121
- class StrictTool extends McpTool {
122
- static metadata = {
123
- name: 'strict_tool',
124
- description: 'Tool with strict validation',
125
- inputSchema: z.object({
126
- required: z.string().min(1),
127
- optional: z.number().optional()
128
- })
129
- };
130
-
131
- static async execute(mm, args) {
132
- return args;
133
- }
134
- }
135
-
136
- registerTool(StrictTool);
137
-
138
- const ToolClass = registry.getTool('strict_tool');
139
- const handler = ToolClass.handler({});
140
-
141
- // Test with missing required field
142
- const invalidResult = await handler({});
143
- assert.strictEqual(invalidResult.isError, true);
144
-
145
- // Test with valid input
146
- const validResult = await handler({ required: 'test' });
147
- assert.strictEqual(validResult.isError, undefined);
148
- const data = JSON.parse(validResult.content[0].text);
149
- assert.strictEqual(data.required, 'test');
150
- });
151
-
152
- it('should support multiple tool registrations via function', () => {
153
- class Tool1 extends McpTool {
154
- static metadata = {
155
- name: 'tool_1',
156
- description: 'First tool',
157
- inputSchema: z.object({})
158
- };
159
- static async execute() { return { id: 1 }; }
160
- }
161
-
162
- class Tool2 extends McpTool {
163
- static metadata = {
164
- name: 'tool_2',
165
- description: 'Second tool',
166
- inputSchema: z.object({})
167
- };
168
- static async execute() { return { id: 2 }; }
169
- }
170
-
171
- class Tool3 extends McpTool {
172
- static metadata = {
173
- name: 'tool_3',
174
- description: 'Third tool',
175
- inputSchema: z.object({})
176
- };
177
- static async execute() { return { id: 3 }; }
178
- }
179
-
180
- // Register tools individually
181
- registerTool(Tool1);
182
- registerTool(Tool2);
183
- registerTool(Tool3);
184
-
185
- // Verify all are registered
186
- assert.strictEqual(registry.getAllTools().length, 3);
187
- assert.strictEqual(registry.hasTool('tool_1'), true);
188
- assert.strictEqual(registry.hasTool('tool_2'), true);
189
- assert.strictEqual(registry.hasTool('tool_3'), true);
190
- });
191
-
192
- it('should maintain tool isolation between registrations', () => {
193
- class IsolatedTool extends McpTool {
194
- static metadata = {
195
- name: 'isolated',
196
- description: 'Isolated tool',
197
- inputSchema: z.object({})
198
- };
199
- static async execute() { return { isolated: true }; }
200
- }
201
-
202
- // Register tool
203
- registerTool(IsolatedTool);
204
- assert.strictEqual(registry.getAllTools().length, 1);
205
-
206
- // Clear registry
207
- registry.clear();
208
- assert.strictEqual(registry.getAllTools().length, 0);
209
-
210
- // Register again
211
- registerTool(IsolatedTool);
212
- assert.strictEqual(registry.getAllTools().length, 1);
213
- });
214
- });
215
-