@mcpkit-dev/testing 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.
package/README.md ADDED
@@ -0,0 +1,343 @@
1
+ # @mcpkit-dev/testing
2
+
3
+ Testing utilities for MCPKit MCP servers. Provides mock clients, in-memory transports, and helpers for writing comprehensive tests.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install -D @mcpkit-dev/testing
9
+ ```
10
+
11
+ ## Features
12
+
13
+ - **MockMcpClient** - Full-featured mock MCP client for testing servers
14
+ - **InMemoryTransport** - Zero-latency transport for unit tests
15
+ - **Test helpers** - Utilities for common testing patterns
16
+
17
+ ## Quick Start
18
+
19
+ ```typescript
20
+ import { describe, it, expect, beforeEach, afterEach } from 'vitest';
21
+ import { MockMcpClient } from '@mcpkit-dev/testing';
22
+ import { listen } from '@mcpkit-dev/core';
23
+ import { MyServer } from './my-server';
24
+
25
+ describe('MyServer', () => {
26
+ let client: MockMcpClient;
27
+ let cleanup: () => Promise<void>;
28
+
29
+ beforeEach(async () => {
30
+ const { client: mockClient, serverTransport } = MockMcpClient.create();
31
+ client = mockClient;
32
+
33
+ const server = await listen(MyServer);
34
+ await server.server.connect(serverTransport);
35
+ await client.connect();
36
+
37
+ cleanup = async () => {
38
+ await client.close();
39
+ await server.close();
40
+ };
41
+ });
42
+
43
+ afterEach(async () => {
44
+ await cleanup();
45
+ });
46
+
47
+ it('should list available tools', async () => {
48
+ const result = await client.listTools();
49
+ expect(result.tools).toHaveLength(1);
50
+ expect(result.tools[0].name).toBe('greet');
51
+ });
52
+
53
+ it('should call a tool', async () => {
54
+ const result = await client.callTool('greet', { name: 'World' });
55
+ expect(result.content[0].text).toBe('Hello, World!');
56
+ });
57
+ });
58
+ ```
59
+
60
+ ## API Reference
61
+
62
+ ### MockMcpClient
63
+
64
+ A mock MCP client that communicates with servers over in-memory transports.
65
+
66
+ #### Creating a Client
67
+
68
+ ```typescript
69
+ import { MockMcpClient } from '@mcpkit-dev/testing';
70
+
71
+ // Create with default options
72
+ const { client, serverTransport } = MockMcpClient.create();
73
+
74
+ // Create with custom options
75
+ const { client, serverTransport } = MockMcpClient.create({
76
+ name: 'test-client',
77
+ version: '1.0.0',
78
+ });
79
+ ```
80
+
81
+ #### Methods
82
+
83
+ ##### `connect(): Promise<void>`
84
+
85
+ Connect the client to the server.
86
+
87
+ ```typescript
88
+ await client.connect();
89
+ ```
90
+
91
+ ##### `close(): Promise<void>`
92
+
93
+ Close the client connection.
94
+
95
+ ```typescript
96
+ await client.close();
97
+ ```
98
+
99
+ ##### `listTools(): Promise<ListToolsResult>`
100
+
101
+ List all available tools from the server.
102
+
103
+ ```typescript
104
+ const { tools } = await client.listTools();
105
+ console.log(tools); // [{ name: 'greet', description: '...' }]
106
+ ```
107
+
108
+ ##### `callTool(name: string, args?: Record<string, unknown>)`
109
+
110
+ Call a tool by name with optional arguments.
111
+
112
+ ```typescript
113
+ const result = await client.callTool('greet', { name: 'Alice' });
114
+ console.log(result.content[0].text); // 'Hello, Alice!'
115
+ ```
116
+
117
+ ##### `listResources(): Promise<ListResourcesResult>`
118
+
119
+ List all available resources.
120
+
121
+ ```typescript
122
+ const { resources } = await client.listResources();
123
+ ```
124
+
125
+ ##### `readResource(uri: string): Promise<ReadResourceResult>`
126
+
127
+ Read a resource by URI.
128
+
129
+ ```typescript
130
+ const result = await client.readResource('config://settings');
131
+ console.log(result.contents[0].text);
132
+ ```
133
+
134
+ ##### `listPrompts(): Promise<ListPromptsResult>`
135
+
136
+ List all available prompts.
137
+
138
+ ```typescript
139
+ const { prompts } = await client.listPrompts();
140
+ ```
141
+
142
+ ##### `getPrompt(name: string, args?: Record<string, string>): Promise<GetPromptResult>`
143
+
144
+ Get a prompt by name with optional arguments.
145
+
146
+ ```typescript
147
+ const result = await client.getPrompt('greeting', { name: 'Bob' });
148
+ console.log(result.messages);
149
+ ```
150
+
151
+ ##### `rawClient: Client`
152
+
153
+ Access the underlying MCP SDK client for advanced operations.
154
+
155
+ ```typescript
156
+ const sdkClient = client.rawClient;
157
+ ```
158
+
159
+ ### InMemoryTransport
160
+
161
+ A transport implementation that allows direct communication between client and server without network overhead.
162
+
163
+ #### Creating a Transport Pair
164
+
165
+ ```typescript
166
+ import { InMemoryTransport } from '@mcpkit-dev/testing';
167
+
168
+ const { clientTransport, serverTransport } = InMemoryTransport.createPair();
169
+
170
+ // Connect to server
171
+ await server.connect(serverTransport);
172
+
173
+ // Connect client
174
+ await client.connect(clientTransport);
175
+ ```
176
+
177
+ #### Methods
178
+
179
+ ##### `start(): Promise<void>`
180
+
181
+ Start the transport and process any queued messages.
182
+
183
+ ##### `send(message: JSONRPCMessage): Promise<void>`
184
+
185
+ Send a message to the peer transport.
186
+
187
+ ##### `close(): Promise<void>`
188
+
189
+ Close the transport and its peer.
190
+
191
+ ##### `deliverMessage(message: JSONRPCMessage): void`
192
+
193
+ Directly deliver a message (for testing edge cases).
194
+
195
+ ### Helper Functions
196
+
197
+ #### `createTestClient(options?)`
198
+
199
+ Convenience function to create a mock client.
200
+
201
+ ```typescript
202
+ import { createTestClient } from '@mcpkit-dev/testing';
203
+
204
+ const { client, serverTransport } = createTestClient({
205
+ name: 'my-test-client',
206
+ });
207
+ ```
208
+
209
+ #### `waitForCondition(condition, options?)`
210
+
211
+ Wait for a condition to be true with timeout.
212
+
213
+ ```typescript
214
+ import { waitForCondition } from '@mcpkit-dev/testing';
215
+
216
+ // Wait up to 5 seconds for server to be ready
217
+ await waitForCondition(() => server.isReady, {
218
+ timeout: 5000,
219
+ interval: 100,
220
+ });
221
+ ```
222
+
223
+ **Options:**
224
+
225
+ | Option | Type | Default | Description |
226
+ |--------|------|---------|-------------|
227
+ | `timeout` | `number` | `5000` | Maximum time to wait (ms) |
228
+ | `interval` | `number` | `50` | Check interval (ms) |
229
+
230
+ #### `createTestServer(ServerClass, listenFn)`
231
+
232
+ Create a complete test environment with server and connected client.
233
+
234
+ ```typescript
235
+ import { createTestServer } from '@mcpkit-dev/testing';
236
+ import { listen } from '@mcpkit-dev/core';
237
+
238
+ const { client, server, instance, cleanup } = await createTestServer(
239
+ MyServer,
240
+ listen
241
+ );
242
+
243
+ // Run tests...
244
+ const tools = await client.listTools();
245
+
246
+ // Clean up
247
+ await cleanup();
248
+ ```
249
+
250
+ ## Testing Patterns
251
+
252
+ ### Testing Tools
253
+
254
+ ```typescript
255
+ it('should handle tool errors gracefully', async () => {
256
+ const result = await client.callTool('divide', { a: 10, b: 0 });
257
+ expect(result.isError).toBe(true);
258
+ expect(result.content[0].text).toContain('Cannot divide by zero');
259
+ });
260
+
261
+ it('should validate tool parameters', async () => {
262
+ await expect(
263
+ client.callTool('greet', { invalidParam: 'value' })
264
+ ).rejects.toThrow();
265
+ });
266
+ ```
267
+
268
+ ### Testing Resources
269
+
270
+ ```typescript
271
+ it('should read dynamic resources', async () => {
272
+ const result = await client.readResource('users://123');
273
+ const user = JSON.parse(result.contents[0].text);
274
+ expect(user.id).toBe('123');
275
+ });
276
+
277
+ it('should list resource templates', async () => {
278
+ const { resources } = await client.listResources();
279
+ const template = resources.find(r => r.uri.includes('{'));
280
+ expect(template).toBeDefined();
281
+ });
282
+ ```
283
+
284
+ ### Testing Prompts
285
+
286
+ ```typescript
287
+ it('should generate prompt with arguments', async () => {
288
+ const result = await client.getPrompt('code-review', {
289
+ language: 'typescript',
290
+ focus: 'security',
291
+ });
292
+
293
+ expect(result.messages[0].content.text).toContain('typescript');
294
+ expect(result.messages[0].content.text).toContain('security');
295
+ });
296
+ ```
297
+
298
+ ### Testing with Mocked Dependencies
299
+
300
+ ```typescript
301
+ import { vi } from 'vitest';
302
+
303
+ // Mock external service
304
+ vi.mock('./weather-api', () => ({
305
+ getWeather: vi.fn().mockResolvedValue({ temp: 22, condition: 'sunny' }),
306
+ }));
307
+
308
+ it('should use mocked weather data', async () => {
309
+ const result = await client.callTool('getWeather', { city: 'London' });
310
+ expect(result.content[0].text).toContain('22');
311
+ });
312
+ ```
313
+
314
+ ## Integration with Test Frameworks
315
+
316
+ ### Vitest
317
+
318
+ ```typescript
319
+ import { describe, it, expect, beforeAll, afterAll } from 'vitest';
320
+
321
+ describe('MCP Server Integration', () => {
322
+ // ... setup and tests
323
+ });
324
+ ```
325
+
326
+ ### Jest
327
+
328
+ ```typescript
329
+ import { describe, it, expect, beforeAll, afterAll } from '@jest/globals';
330
+
331
+ describe('MCP Server Integration', () => {
332
+ // ... setup and tests
333
+ });
334
+ ```
335
+
336
+ ## Related Packages
337
+
338
+ - [@mcpkit-dev/core](https://www.npmjs.com/package/@mcpkit-dev/core) - Core decorators and server framework
339
+ - [@mcpkit-dev/cli](https://www.npmjs.com/package/@mcpkit-dev/cli) - CLI tool for project scaffolding
340
+
341
+ ## License
342
+
343
+ MIT
@@ -0,0 +1,504 @@
1
+ import { Client } from '@modelcontextprotocol/sdk/client/index.js';
2
+ import { Transport } from '@modelcontextprotocol/sdk/shared/transport.js';
3
+ import { JSONRPCMessage } from '@modelcontextprotocol/sdk/types.js';
4
+
5
+ /**
6
+ * Options for creating a mock MCP client
7
+ */
8
+ interface MockClientOptions {
9
+ /** Client name */
10
+ name?: string;
11
+ /** Client version */
12
+ version?: string;
13
+ }
14
+ /**
15
+ * Result from creating a mock client
16
+ */
17
+ interface MockClientResult {
18
+ /** The MCP client instance */
19
+ client: MockMcpClient;
20
+ /** Transport for connecting to a server */
21
+ serverTransport: Transport;
22
+ }
23
+ /**
24
+ * Mock MCP client for testing servers
25
+ *
26
+ * Provides a simple interface for testing MCP server implementations
27
+ * without needing actual network connections.
28
+ *
29
+ * @example
30
+ * ```typescript
31
+ * const { client, serverTransport } = MockMcpClient.create();
32
+ *
33
+ * // Connect your server to serverTransport
34
+ * const server = await listen(MyServer);
35
+ * await server.server.connect(serverTransport);
36
+ *
37
+ * // Use the client to test your server
38
+ * const tools = await client.listTools();
39
+ * const result = await client.callTool('myTool', { param: 'value' });
40
+ * ```
41
+ */
42
+ declare class MockMcpClient {
43
+ private client;
44
+ private clientTransport;
45
+ private connected;
46
+ private constructor();
47
+ /**
48
+ * Create a mock client with linked transports
49
+ */
50
+ static create(options?: MockClientOptions): MockClientResult;
51
+ /**
52
+ * Connect to the server
53
+ */
54
+ connect(): Promise<void>;
55
+ /**
56
+ * Close the connection
57
+ */
58
+ close(): Promise<void>;
59
+ /**
60
+ * List all available tools
61
+ */
62
+ listTools(): Promise<{
63
+ [x: string]: unknown;
64
+ tools: {
65
+ inputSchema: {
66
+ [x: string]: unknown;
67
+ type: "object";
68
+ properties?: Record<string, object> | undefined;
69
+ required?: string[] | undefined;
70
+ };
71
+ name: string;
72
+ description?: string | undefined;
73
+ outputSchema?: {
74
+ [x: string]: unknown;
75
+ type: "object";
76
+ properties?: Record<string, object> | undefined;
77
+ required?: string[] | undefined;
78
+ } | undefined;
79
+ annotations?: {
80
+ title?: string | undefined;
81
+ readOnlyHint?: boolean | undefined;
82
+ destructiveHint?: boolean | undefined;
83
+ idempotentHint?: boolean | undefined;
84
+ openWorldHint?: boolean | undefined;
85
+ } | undefined;
86
+ execution?: {
87
+ taskSupport?: "optional" | "required" | "forbidden" | undefined;
88
+ } | undefined;
89
+ _meta?: Record<string, unknown> | undefined;
90
+ icons?: {
91
+ src: string;
92
+ mimeType?: string | undefined;
93
+ sizes?: string[] | undefined;
94
+ }[] | undefined;
95
+ title?: string | undefined;
96
+ }[];
97
+ _meta?: {
98
+ [x: string]: unknown;
99
+ "io.modelcontextprotocol/related-task"?: {
100
+ [x: string]: unknown;
101
+ taskId: string;
102
+ } | undefined;
103
+ } | undefined;
104
+ nextCursor?: string | undefined;
105
+ }>;
106
+ /**
107
+ * Call a tool by name
108
+ */
109
+ callTool(name: string, args?: Record<string, unknown>): Promise<{
110
+ [x: string]: unknown;
111
+ content: ({
112
+ type: "text";
113
+ text: string;
114
+ annotations?: {
115
+ audience?: ("user" | "assistant")[] | undefined;
116
+ priority?: number | undefined;
117
+ lastModified?: string | undefined;
118
+ } | undefined;
119
+ _meta?: Record<string, unknown> | undefined;
120
+ } | {
121
+ type: "image";
122
+ data: string;
123
+ mimeType: string;
124
+ annotations?: {
125
+ audience?: ("user" | "assistant")[] | undefined;
126
+ priority?: number | undefined;
127
+ lastModified?: string | undefined;
128
+ } | undefined;
129
+ _meta?: Record<string, unknown> | undefined;
130
+ } | {
131
+ type: "audio";
132
+ data: string;
133
+ mimeType: string;
134
+ annotations?: {
135
+ audience?: ("user" | "assistant")[] | undefined;
136
+ priority?: number | undefined;
137
+ lastModified?: string | undefined;
138
+ } | undefined;
139
+ _meta?: Record<string, unknown> | undefined;
140
+ } | {
141
+ type: "resource";
142
+ resource: {
143
+ uri: string;
144
+ text: string;
145
+ mimeType?: string | undefined;
146
+ _meta?: Record<string, unknown> | undefined;
147
+ } | {
148
+ uri: string;
149
+ blob: string;
150
+ mimeType?: string | undefined;
151
+ _meta?: Record<string, unknown> | undefined;
152
+ };
153
+ annotations?: {
154
+ audience?: ("user" | "assistant")[] | undefined;
155
+ priority?: number | undefined;
156
+ lastModified?: string | undefined;
157
+ } | undefined;
158
+ _meta?: Record<string, unknown> | undefined;
159
+ } | {
160
+ uri: string;
161
+ name: string;
162
+ type: "resource_link";
163
+ description?: string | undefined;
164
+ mimeType?: string | undefined;
165
+ annotations?: {
166
+ audience?: ("user" | "assistant")[] | undefined;
167
+ priority?: number | undefined;
168
+ lastModified?: string | undefined;
169
+ } | undefined;
170
+ _meta?: {
171
+ [x: string]: unknown;
172
+ } | undefined;
173
+ icons?: {
174
+ src: string;
175
+ mimeType?: string | undefined;
176
+ sizes?: string[] | undefined;
177
+ }[] | undefined;
178
+ title?: string | undefined;
179
+ })[];
180
+ _meta?: {
181
+ [x: string]: unknown;
182
+ "io.modelcontextprotocol/related-task"?: {
183
+ [x: string]: unknown;
184
+ taskId: string;
185
+ } | undefined;
186
+ } | undefined;
187
+ structuredContent?: Record<string, unknown> | undefined;
188
+ isError?: boolean | undefined;
189
+ } | {
190
+ [x: string]: unknown;
191
+ toolResult: unknown;
192
+ _meta?: {
193
+ [x: string]: unknown;
194
+ "io.modelcontextprotocol/related-task"?: {
195
+ [x: string]: unknown;
196
+ taskId: string;
197
+ } | undefined;
198
+ } | undefined;
199
+ }>;
200
+ /**
201
+ * List all available resources
202
+ */
203
+ listResources(): Promise<{
204
+ [x: string]: unknown;
205
+ resources: {
206
+ uri: string;
207
+ name: string;
208
+ description?: string | undefined;
209
+ mimeType?: string | undefined;
210
+ annotations?: {
211
+ audience?: ("user" | "assistant")[] | undefined;
212
+ priority?: number | undefined;
213
+ lastModified?: string | undefined;
214
+ } | undefined;
215
+ _meta?: {
216
+ [x: string]: unknown;
217
+ } | undefined;
218
+ icons?: {
219
+ src: string;
220
+ mimeType?: string | undefined;
221
+ sizes?: string[] | undefined;
222
+ }[] | undefined;
223
+ title?: string | undefined;
224
+ }[];
225
+ _meta?: {
226
+ [x: string]: unknown;
227
+ "io.modelcontextprotocol/related-task"?: {
228
+ [x: string]: unknown;
229
+ taskId: string;
230
+ } | undefined;
231
+ } | undefined;
232
+ nextCursor?: string | undefined;
233
+ }>;
234
+ /**
235
+ * Read a resource by URI
236
+ */
237
+ readResource(uri: string): Promise<{
238
+ [x: string]: unknown;
239
+ contents: ({
240
+ uri: string;
241
+ text: string;
242
+ mimeType?: string | undefined;
243
+ _meta?: Record<string, unknown> | undefined;
244
+ } | {
245
+ uri: string;
246
+ blob: string;
247
+ mimeType?: string | undefined;
248
+ _meta?: Record<string, unknown> | undefined;
249
+ })[];
250
+ _meta?: {
251
+ [x: string]: unknown;
252
+ "io.modelcontextprotocol/related-task"?: {
253
+ [x: string]: unknown;
254
+ taskId: string;
255
+ } | undefined;
256
+ } | undefined;
257
+ }>;
258
+ /**
259
+ * List all available prompts
260
+ */
261
+ listPrompts(): Promise<{
262
+ [x: string]: unknown;
263
+ prompts: {
264
+ name: string;
265
+ description?: string | undefined;
266
+ arguments?: {
267
+ name: string;
268
+ description?: string | undefined;
269
+ required?: boolean | undefined;
270
+ }[] | undefined;
271
+ _meta?: {
272
+ [x: string]: unknown;
273
+ } | undefined;
274
+ icons?: {
275
+ src: string;
276
+ mimeType?: string | undefined;
277
+ sizes?: string[] | undefined;
278
+ }[] | undefined;
279
+ title?: string | undefined;
280
+ }[];
281
+ _meta?: {
282
+ [x: string]: unknown;
283
+ "io.modelcontextprotocol/related-task"?: {
284
+ [x: string]: unknown;
285
+ taskId: string;
286
+ } | undefined;
287
+ } | undefined;
288
+ nextCursor?: string | undefined;
289
+ }>;
290
+ /**
291
+ * Get a prompt by name
292
+ */
293
+ getPrompt(name: string, args?: Record<string, string>): Promise<{
294
+ [x: string]: unknown;
295
+ messages: {
296
+ role: "user" | "assistant";
297
+ content: {
298
+ type: "text";
299
+ text: string;
300
+ annotations?: {
301
+ audience?: ("user" | "assistant")[] | undefined;
302
+ priority?: number | undefined;
303
+ lastModified?: string | undefined;
304
+ } | undefined;
305
+ _meta?: Record<string, unknown> | undefined;
306
+ } | {
307
+ type: "image";
308
+ data: string;
309
+ mimeType: string;
310
+ annotations?: {
311
+ audience?: ("user" | "assistant")[] | undefined;
312
+ priority?: number | undefined;
313
+ lastModified?: string | undefined;
314
+ } | undefined;
315
+ _meta?: Record<string, unknown> | undefined;
316
+ } | {
317
+ type: "audio";
318
+ data: string;
319
+ mimeType: string;
320
+ annotations?: {
321
+ audience?: ("user" | "assistant")[] | undefined;
322
+ priority?: number | undefined;
323
+ lastModified?: string | undefined;
324
+ } | undefined;
325
+ _meta?: Record<string, unknown> | undefined;
326
+ } | {
327
+ type: "resource";
328
+ resource: {
329
+ uri: string;
330
+ text: string;
331
+ mimeType?: string | undefined;
332
+ _meta?: Record<string, unknown> | undefined;
333
+ } | {
334
+ uri: string;
335
+ blob: string;
336
+ mimeType?: string | undefined;
337
+ _meta?: Record<string, unknown> | undefined;
338
+ };
339
+ annotations?: {
340
+ audience?: ("user" | "assistant")[] | undefined;
341
+ priority?: number | undefined;
342
+ lastModified?: string | undefined;
343
+ } | undefined;
344
+ _meta?: Record<string, unknown> | undefined;
345
+ } | {
346
+ uri: string;
347
+ name: string;
348
+ type: "resource_link";
349
+ description?: string | undefined;
350
+ mimeType?: string | undefined;
351
+ annotations?: {
352
+ audience?: ("user" | "assistant")[] | undefined;
353
+ priority?: number | undefined;
354
+ lastModified?: string | undefined;
355
+ } | undefined;
356
+ _meta?: {
357
+ [x: string]: unknown;
358
+ } | undefined;
359
+ icons?: {
360
+ src: string;
361
+ mimeType?: string | undefined;
362
+ sizes?: string[] | undefined;
363
+ }[] | undefined;
364
+ title?: string | undefined;
365
+ };
366
+ }[];
367
+ _meta?: {
368
+ [x: string]: unknown;
369
+ "io.modelcontextprotocol/related-task"?: {
370
+ [x: string]: unknown;
371
+ taskId: string;
372
+ } | undefined;
373
+ } | undefined;
374
+ description?: string | undefined;
375
+ }>;
376
+ /**
377
+ * Ensure the client is connected
378
+ */
379
+ private ensureConnected;
380
+ /**
381
+ * Get the underlying MCP client
382
+ */
383
+ get rawClient(): Client;
384
+ }
385
+
386
+ /**
387
+ * Result from creating a test server
388
+ */
389
+ interface TestServerResult<T> {
390
+ /** The server instance */
391
+ instance: T;
392
+ /** The bootstrapped server */
393
+ server: {
394
+ server: unknown;
395
+ transport: unknown;
396
+ connect: () => Promise<void>;
397
+ close: () => Promise<void>;
398
+ };
399
+ /** The test client */
400
+ client: MockMcpClient;
401
+ /** Cleanup function */
402
+ cleanup: () => Promise<void>;
403
+ }
404
+ /**
405
+ * Create a test client for MCP server testing
406
+ *
407
+ * @example
408
+ * ```typescript
409
+ * const { client, serverTransport } = createTestClient();
410
+ * ```
411
+ */
412
+ declare function createTestClient(options?: MockClientOptions): MockClientResult;
413
+ /**
414
+ * Wait for a condition to be true, with timeout
415
+ *
416
+ * @example
417
+ * ```typescript
418
+ * await waitForCondition(() => server.isReady, { timeout: 5000 });
419
+ * ```
420
+ */
421
+ declare function waitForCondition(condition: () => boolean | Promise<boolean>, options?: {
422
+ timeout?: number;
423
+ interval?: number;
424
+ }): Promise<void>;
425
+ /**
426
+ * Create a test server with a connected client
427
+ *
428
+ * This is a convenience function that sets up a complete test environment
429
+ * with a server instance, bootstrapped server, and connected client.
430
+ *
431
+ * @example
432
+ * ```typescript
433
+ * import { createTestServer } from '@mcpkit-dev/testing';
434
+ * import { listen } from '@mcpkit-dev/core';
435
+ *
436
+ * // In your test
437
+ * const { client, cleanup } = await createTestServer(MyServer, listen);
438
+ *
439
+ * // Test your server
440
+ * const tools = await client.listTools();
441
+ * expect(tools.tools).toHaveLength(1);
442
+ *
443
+ * // Clean up
444
+ * await cleanup();
445
+ * ```
446
+ */
447
+ declare function createTestServer<T extends new () => object>(ServerClass: T, listenFn: (ServerClass: T) => Promise<{
448
+ server: unknown;
449
+ transport: unknown;
450
+ connect: () => Promise<void>;
451
+ close: () => Promise<void>;
452
+ }>): Promise<TestServerResult<InstanceType<T>>>;
453
+
454
+ /**
455
+ * In-memory transport for testing MCP servers
456
+ *
457
+ * This transport allows direct communication between a client and server
458
+ * without any network overhead, perfect for unit and integration tests.
459
+ *
460
+ * @example
461
+ * ```typescript
462
+ * const { clientTransport, serverTransport } = InMemoryTransport.createPair();
463
+ *
464
+ * // Connect server to serverTransport
465
+ * await server.connect(serverTransport);
466
+ *
467
+ * // Use clientTransport to send messages
468
+ * await clientTransport.send({ jsonrpc: '2.0', method: 'ping', id: 1 });
469
+ * ```
470
+ */
471
+ declare class InMemoryTransport implements Transport {
472
+ private peer;
473
+ private messageQueue;
474
+ private started;
475
+ private closed;
476
+ onclose?: () => void;
477
+ onerror?: (error: Error) => void;
478
+ onmessage?: (message: JSONRPCMessage) => void;
479
+ /**
480
+ * Create a linked pair of transports for testing
481
+ */
482
+ static createPair(): {
483
+ clientTransport: InMemoryTransport;
484
+ serverTransport: InMemoryTransport;
485
+ };
486
+ /**
487
+ * Start the transport
488
+ */
489
+ start(): Promise<void>;
490
+ /**
491
+ * Send a message to the peer transport
492
+ */
493
+ send(message: JSONRPCMessage): Promise<void>;
494
+ /**
495
+ * Close the transport
496
+ */
497
+ close(): Promise<void>;
498
+ /**
499
+ * Deliver a message directly (for testing purposes)
500
+ */
501
+ deliverMessage(message: JSONRPCMessage): void;
502
+ }
503
+
504
+ export { InMemoryTransport, type MockClientOptions, type MockClientResult, MockMcpClient, type TestServerResult, createTestClient, createTestServer, waitForCondition };
package/dist/index.js ADDED
@@ -0,0 +1,229 @@
1
+ // src/mock-client.ts
2
+ import { Client } from "@modelcontextprotocol/sdk/client/index.js";
3
+
4
+ // src/transport.ts
5
+ var InMemoryTransport = class _InMemoryTransport {
6
+ peer = null;
7
+ messageQueue = [];
8
+ started = false;
9
+ closed = false;
10
+ onclose;
11
+ onerror;
12
+ onmessage;
13
+ /**
14
+ * Create a linked pair of transports for testing
15
+ */
16
+ static createPair() {
17
+ const clientTransport = new _InMemoryTransport();
18
+ const serverTransport = new _InMemoryTransport();
19
+ clientTransport.peer = serverTransport;
20
+ serverTransport.peer = clientTransport;
21
+ return { clientTransport, serverTransport };
22
+ }
23
+ /**
24
+ * Start the transport
25
+ */
26
+ async start() {
27
+ if (this.started) {
28
+ throw new Error("Transport already started");
29
+ }
30
+ if (this.closed) {
31
+ throw new Error("Transport is closed");
32
+ }
33
+ this.started = true;
34
+ while (this.messageQueue.length > 0) {
35
+ const message = this.messageQueue.shift();
36
+ if (message) {
37
+ this.onmessage?.(message);
38
+ }
39
+ }
40
+ }
41
+ /**
42
+ * Send a message to the peer transport
43
+ */
44
+ async send(message) {
45
+ if (this.closed) {
46
+ throw new Error("Transport is closed");
47
+ }
48
+ if (!this.peer) {
49
+ throw new Error("No peer transport connected");
50
+ }
51
+ await Promise.resolve();
52
+ if (this.peer.started) {
53
+ this.peer.onmessage?.(message);
54
+ } else {
55
+ this.peer.messageQueue.push(message);
56
+ }
57
+ }
58
+ /**
59
+ * Close the transport
60
+ */
61
+ async close() {
62
+ if (this.closed) {
63
+ return;
64
+ }
65
+ this.closed = true;
66
+ this.onclose?.();
67
+ if (this.peer && !this.peer.closed) {
68
+ await this.peer.close();
69
+ }
70
+ }
71
+ /**
72
+ * Deliver a message directly (for testing purposes)
73
+ */
74
+ deliverMessage(message) {
75
+ if (this.started) {
76
+ this.onmessage?.(message);
77
+ } else {
78
+ this.messageQueue.push(message);
79
+ }
80
+ }
81
+ };
82
+
83
+ // src/mock-client.ts
84
+ var MockMcpClient = class _MockMcpClient {
85
+ client;
86
+ clientTransport;
87
+ connected = false;
88
+ constructor(options = {}) {
89
+ this.client = new Client(
90
+ {
91
+ name: options.name ?? "test-client",
92
+ version: options.version ?? "1.0.0"
93
+ },
94
+ {
95
+ capabilities: {}
96
+ }
97
+ );
98
+ this.clientTransport = new InMemoryTransport();
99
+ }
100
+ /**
101
+ * Create a mock client with linked transports
102
+ */
103
+ static create(options) {
104
+ const mockClient = new _MockMcpClient(options);
105
+ const { clientTransport, serverTransport } = InMemoryTransport.createPair();
106
+ mockClient.clientTransport = clientTransport;
107
+ return {
108
+ client: mockClient,
109
+ serverTransport
110
+ };
111
+ }
112
+ /**
113
+ * Connect to the server
114
+ */
115
+ async connect() {
116
+ if (this.connected) {
117
+ return;
118
+ }
119
+ await this.client.connect(this.clientTransport);
120
+ this.connected = true;
121
+ }
122
+ /**
123
+ * Close the connection
124
+ */
125
+ async close() {
126
+ if (!this.connected) {
127
+ return;
128
+ }
129
+ await this.client.close();
130
+ this.connected = false;
131
+ }
132
+ /**
133
+ * List all available tools
134
+ */
135
+ async listTools() {
136
+ await this.ensureConnected();
137
+ return this.client.listTools();
138
+ }
139
+ /**
140
+ * Call a tool by name
141
+ */
142
+ async callTool(name, args) {
143
+ await this.ensureConnected();
144
+ return this.client.callTool({ name, arguments: args });
145
+ }
146
+ /**
147
+ * List all available resources
148
+ */
149
+ async listResources() {
150
+ await this.ensureConnected();
151
+ return this.client.listResources();
152
+ }
153
+ /**
154
+ * Read a resource by URI
155
+ */
156
+ async readResource(uri) {
157
+ await this.ensureConnected();
158
+ return this.client.readResource({ uri });
159
+ }
160
+ /**
161
+ * List all available prompts
162
+ */
163
+ async listPrompts() {
164
+ await this.ensureConnected();
165
+ return this.client.listPrompts();
166
+ }
167
+ /**
168
+ * Get a prompt by name
169
+ */
170
+ async getPrompt(name, args) {
171
+ await this.ensureConnected();
172
+ return this.client.getPrompt({ name, arguments: args });
173
+ }
174
+ /**
175
+ * Ensure the client is connected
176
+ */
177
+ async ensureConnected() {
178
+ if (!this.connected) {
179
+ await this.connect();
180
+ }
181
+ }
182
+ /**
183
+ * Get the underlying MCP client
184
+ */
185
+ get rawClient() {
186
+ return this.client;
187
+ }
188
+ };
189
+
190
+ // src/helpers.ts
191
+ function createTestClient(options) {
192
+ return MockMcpClient.create(options);
193
+ }
194
+ async function waitForCondition(condition, options = {}) {
195
+ const { timeout = 5e3, interval = 50 } = options;
196
+ const startTime = Date.now();
197
+ while (Date.now() - startTime < timeout) {
198
+ if (await condition()) {
199
+ return;
200
+ }
201
+ await new Promise((resolve) => setTimeout(resolve, interval));
202
+ }
203
+ throw new Error(`Condition not met within ${timeout}ms`);
204
+ }
205
+ async function createTestServer(ServerClass, listenFn) {
206
+ const { client, serverTransport } = createTestClient();
207
+ const instance = new ServerClass();
208
+ const bootstrapped = await listenFn(ServerClass);
209
+ const server = bootstrapped.server;
210
+ await server.connect(serverTransport);
211
+ await client.connect();
212
+ return {
213
+ instance,
214
+ server: bootstrapped,
215
+ client,
216
+ cleanup: async () => {
217
+ await client.close();
218
+ await bootstrapped.close();
219
+ }
220
+ };
221
+ }
222
+ export {
223
+ InMemoryTransport,
224
+ MockMcpClient,
225
+ createTestClient,
226
+ createTestServer,
227
+ waitForCondition
228
+ };
229
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/mock-client.ts","../src/transport.ts","../src/helpers.ts"],"sourcesContent":["import { Client } from '@modelcontextprotocol/sdk/client/index.js';\nimport type { Transport } from '@modelcontextprotocol/sdk/shared/transport.js';\nimport { InMemoryTransport } from './transport.js';\n\n/**\n * Options for creating a mock MCP client\n */\nexport interface MockClientOptions {\n /** Client name */\n name?: string;\n /** Client version */\n version?: string;\n}\n\n/**\n * Result from creating a mock client\n */\nexport interface MockClientResult {\n /** The MCP client instance */\n client: MockMcpClient;\n /** Transport for connecting to a server */\n serverTransport: Transport;\n}\n\n/**\n * Mock MCP client for testing servers\n *\n * Provides a simple interface for testing MCP server implementations\n * without needing actual network connections.\n *\n * @example\n * ```typescript\n * const { client, serverTransport } = MockMcpClient.create();\n *\n * // Connect your server to serverTransport\n * const server = await listen(MyServer);\n * await server.server.connect(serverTransport);\n *\n * // Use the client to test your server\n * const tools = await client.listTools();\n * const result = await client.callTool('myTool', { param: 'value' });\n * ```\n */\nexport class MockMcpClient {\n private client: Client;\n private clientTransport: InMemoryTransport;\n private connected = false;\n\n private constructor(options: MockClientOptions = {}) {\n this.client = new Client(\n {\n name: options.name ?? 'test-client',\n version: options.version ?? '1.0.0',\n },\n {\n capabilities: {},\n },\n );\n\n this.clientTransport = new InMemoryTransport();\n }\n\n /**\n * Create a mock client with linked transports\n */\n static create(options?: MockClientOptions): MockClientResult {\n const mockClient = new MockMcpClient(options);\n const { clientTransport, serverTransport } = InMemoryTransport.createPair();\n\n mockClient.clientTransport = clientTransport;\n\n return {\n client: mockClient,\n serverTransport,\n };\n }\n\n /**\n * Connect to the server\n */\n async connect(): Promise<void> {\n if (this.connected) {\n return;\n }\n\n await this.client.connect(this.clientTransport);\n this.connected = true;\n }\n\n /**\n * Close the connection\n */\n async close(): Promise<void> {\n if (!this.connected) {\n return;\n }\n\n await this.client.close();\n this.connected = false;\n }\n\n /**\n * List all available tools\n */\n async listTools() {\n await this.ensureConnected();\n return this.client.listTools();\n }\n\n /**\n * Call a tool by name\n */\n async callTool(name: string, args?: Record<string, unknown>) {\n await this.ensureConnected();\n return this.client.callTool({ name, arguments: args });\n }\n\n /**\n * List all available resources\n */\n async listResources() {\n await this.ensureConnected();\n return this.client.listResources();\n }\n\n /**\n * Read a resource by URI\n */\n async readResource(uri: string) {\n await this.ensureConnected();\n return this.client.readResource({ uri });\n }\n\n /**\n * List all available prompts\n */\n async listPrompts() {\n await this.ensureConnected();\n return this.client.listPrompts();\n }\n\n /**\n * Get a prompt by name\n */\n async getPrompt(name: string, args?: Record<string, string>) {\n await this.ensureConnected();\n return this.client.getPrompt({ name, arguments: args });\n }\n\n /**\n * Ensure the client is connected\n */\n private async ensureConnected(): Promise<void> {\n if (!this.connected) {\n await this.connect();\n }\n }\n\n /**\n * Get the underlying MCP client\n */\n get rawClient(): Client {\n return this.client;\n }\n}\n","import type { Transport } from '@modelcontextprotocol/sdk/shared/transport.js';\nimport type { JSONRPCMessage } from '@modelcontextprotocol/sdk/types.js';\n\n/**\n * In-memory transport for testing MCP servers\n *\n * This transport allows direct communication between a client and server\n * without any network overhead, perfect for unit and integration tests.\n *\n * @example\n * ```typescript\n * const { clientTransport, serverTransport } = InMemoryTransport.createPair();\n *\n * // Connect server to serverTransport\n * await server.connect(serverTransport);\n *\n * // Use clientTransport to send messages\n * await clientTransport.send({ jsonrpc: '2.0', method: 'ping', id: 1 });\n * ```\n */\nexport class InMemoryTransport implements Transport {\n private peer: InMemoryTransport | null = null;\n private messageQueue: JSONRPCMessage[] = [];\n private started = false;\n private closed = false;\n\n onclose?: () => void;\n onerror?: (error: Error) => void;\n onmessage?: (message: JSONRPCMessage) => void;\n\n /**\n * Create a linked pair of transports for testing\n */\n static createPair(): { clientTransport: InMemoryTransport; serverTransport: InMemoryTransport } {\n const clientTransport = new InMemoryTransport();\n const serverTransport = new InMemoryTransport();\n\n clientTransport.peer = serverTransport;\n serverTransport.peer = clientTransport;\n\n return { clientTransport, serverTransport };\n }\n\n /**\n * Start the transport\n */\n async start(): Promise<void> {\n if (this.started) {\n throw new Error('Transport already started');\n }\n if (this.closed) {\n throw new Error('Transport is closed');\n }\n\n this.started = true;\n\n // Process any queued messages\n while (this.messageQueue.length > 0) {\n const message = this.messageQueue.shift();\n if (message) {\n this.onmessage?.(message);\n }\n }\n }\n\n /**\n * Send a message to the peer transport\n */\n async send(message: JSONRPCMessage): Promise<void> {\n if (this.closed) {\n throw new Error('Transport is closed');\n }\n\n if (!this.peer) {\n throw new Error('No peer transport connected');\n }\n\n // Simulate async behavior\n await Promise.resolve();\n\n if (this.peer.started) {\n this.peer.onmessage?.(message);\n } else {\n this.peer.messageQueue.push(message);\n }\n }\n\n /**\n * Close the transport\n */\n async close(): Promise<void> {\n if (this.closed) {\n return;\n }\n\n this.closed = true;\n this.onclose?.();\n\n // Also close peer\n if (this.peer && !this.peer.closed) {\n await this.peer.close();\n }\n }\n\n /**\n * Deliver a message directly (for testing purposes)\n */\n deliverMessage(message: JSONRPCMessage): void {\n if (this.started) {\n this.onmessage?.(message);\n } else {\n this.messageQueue.push(message);\n }\n }\n}\n","import { type MockClientOptions, type MockClientResult, MockMcpClient } from './mock-client.js';\n\n/**\n * Result from creating a test server\n */\nexport interface TestServerResult<T> {\n /** The server instance */\n instance: T;\n /** The bootstrapped server */\n server: {\n server: unknown;\n transport: unknown;\n connect: () => Promise<void>;\n close: () => Promise<void>;\n };\n /** The test client */\n client: MockMcpClient;\n /** Cleanup function */\n cleanup: () => Promise<void>;\n}\n\n/**\n * Create a test client for MCP server testing\n *\n * @example\n * ```typescript\n * const { client, serverTransport } = createTestClient();\n * ```\n */\nexport function createTestClient(options?: MockClientOptions): MockClientResult {\n return MockMcpClient.create(options);\n}\n\n/**\n * Wait for a condition to be true, with timeout\n *\n * @example\n * ```typescript\n * await waitForCondition(() => server.isReady, { timeout: 5000 });\n * ```\n */\nexport async function waitForCondition(\n condition: () => boolean | Promise<boolean>,\n options: { timeout?: number; interval?: number } = {},\n): Promise<void> {\n const { timeout = 5000, interval = 50 } = options;\n const startTime = Date.now();\n\n while (Date.now() - startTime < timeout) {\n if (await condition()) {\n return;\n }\n await new Promise((resolve) => setTimeout(resolve, interval));\n }\n\n throw new Error(`Condition not met within ${timeout}ms`);\n}\n\n/**\n * Create a test server with a connected client\n *\n * This is a convenience function that sets up a complete test environment\n * with a server instance, bootstrapped server, and connected client.\n *\n * @example\n * ```typescript\n * import { createTestServer } from '@mcpkit-dev/testing';\n * import { listen } from '@mcpkit-dev/core';\n *\n * // In your test\n * const { client, cleanup } = await createTestServer(MyServer, listen);\n *\n * // Test your server\n * const tools = await client.listTools();\n * expect(tools.tools).toHaveLength(1);\n *\n * // Clean up\n * await cleanup();\n * ```\n */\nexport async function createTestServer<T extends new () => object>(\n ServerClass: T,\n listenFn: (ServerClass: T) => Promise<{\n server: unknown;\n transport: unknown;\n connect: () => Promise<void>;\n close: () => Promise<void>;\n }>,\n): Promise<TestServerResult<InstanceType<T>>> {\n const { client, serverTransport } = createTestClient();\n const instance = new ServerClass() as InstanceType<T>;\n const bootstrapped = await listenFn(ServerClass);\n\n // Connect server to transport\n // We need to access the internal server and connect it\n const server = bootstrapped.server as {\n connect: (transport: unknown) => Promise<void>;\n };\n await server.connect(serverTransport);\n\n // Connect client\n await client.connect();\n\n return {\n instance,\n server: bootstrapped,\n client,\n cleanup: async () => {\n await client.close();\n await bootstrapped.close();\n },\n };\n}\n"],"mappings":"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","names":[]}
package/package.json ADDED
@@ -0,0 +1,54 @@
1
+ {
2
+ "name": "@mcpkit-dev/testing",
3
+ "version": "1.0.0",
4
+ "description": "Testing utilities for mcpkit MCP servers",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "import": {
11
+ "types": "./dist/index.d.ts",
12
+ "default": "./dist/index.js"
13
+ }
14
+ }
15
+ },
16
+ "files": [
17
+ "dist"
18
+ ],
19
+ "scripts": {
20
+ "build": "tsup",
21
+ "dev": "tsup --watch",
22
+ "test": "vitest run",
23
+ "test:watch": "vitest",
24
+ "typecheck": "tsc --noEmit",
25
+ "clean": "rm -rf dist"
26
+ },
27
+ "dependencies": {
28
+ "@modelcontextprotocol/sdk": "^1.12.1"
29
+ },
30
+ "peerDependencies": {
31
+ "@mcpkit-dev/core": ">=0.1.0"
32
+ },
33
+ "devDependencies": {
34
+ "@types/node": "^22.10.2",
35
+ "tsup": "^8.3.5",
36
+ "typescript": "^5.7.2",
37
+ "vitest": "^2.1.9"
38
+ },
39
+ "engines": {
40
+ "node": ">=18.0.0"
41
+ },
42
+ "keywords": [
43
+ "mcp",
44
+ "testing",
45
+ "mcpkit",
46
+ "model-context-protocol"
47
+ ],
48
+ "license": "MIT",
49
+ "repository": {
50
+ "type": "git",
51
+ "url": "https://github.com/v-checha/mcpkit.git",
52
+ "directory": "packages/testing"
53
+ }
54
+ }