@l10nmonster/mcp 3.0.0-alpha.16

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.
@@ -0,0 +1,215 @@
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
+