@slbdn/mcp-tester 1.1.0 → 1.3.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/CHANGELOG.md CHANGED
@@ -5,6 +5,109 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [1.3.0] - 2026-07-01
9
+
10
+ ### Added
11
+ - **TypeScript type generator** (`src/generate-types.ts`) — generate typed `.d.ts` from MCP tool schemas:
12
+ - `generateTypes()` API: connect to server, inspect schemas, emit TypeScript declarations
13
+ - CLI: `npx mcp-tester generate-types node ./server.js -o server.d.ts`
14
+ - Handles: primitives, enums, arrays, nested objects, oneOf/anyOf/allOf, $ref, const
15
+ - Generated types: `{Tool}Args`, `ToolName`, `ToolArgsMap`, `ToolCall`, `ResourceUri`, `PromptCall`
16
+ - 59 tests (12 e2e + 4 options + 43 unit)
17
+ - **Server health checks** — detect zombie processes and monitor server health:
18
+ - `isHealthy()` — sends a lightweight `tools/list` ping, returns `HealthStatus` with latency, PID, message
19
+ - `getServerPid()` — get the server process PID
20
+ - `getLastHealthStatus()` — cached result without re-checking
21
+ - `startHealthMonitor(options)` — periodic health monitoring with `onUnhealthy`/`onRecovery`/`onCheck` callbacks
22
+ - `stopHealthMonitor()` — stop periodic monitoring (also auto-stopped by `client.stop()`)
23
+ - Zombie process detection via `process.kill(pid, 0)`
24
+ - 17 tests (including SIGKILL zombie detection test)
25
+ - **Enhanced mock server** (`src/__tests__/fixtures/mock-server.ts`) — 68 tests:
26
+ - Configurable delays (`defaultDelay`) and random failures (`failureRate`)
27
+ - Input schema validation (`validateSchemas` option)
28
+ - Stateful tools: `counter` (increment/get/reset), `items` (add/list/remove/clear)
29
+ - Transform tool: `upper`/`lower`/`reverse`/`length` operations
30
+ - Custom handlers: `registerToolHandler`, `registerResourceHandler`, `registerPromptHandler`
31
+ - Call history: `getCallHistory`, `getCallCount` for test assertions
32
+ - Streaming support: `setupStream`, `nextStreamChunk`
33
+ - Dynamic registration/removal of tools, resources, prompts
34
+
35
+ ### Changed
36
+ - **CLI version** is now read dynamically from `package.json` (was hardcoded `1.0.0`)
37
+ - Applied safe dependency patches: `@typescript-eslint/*` 8.60.0, `@types/node` 20.19.41, `prettier` 3.8.3, `ts-jest` 29.4.11
38
+ - `npm audit fix` — 0 vulnerabilities (was 11)
39
+
40
+ ### Test Suite
41
+ - **635 tests** (17 suites), up from 491 (14 suites)
42
+ - New: mock server (68), generate-types (59), health checks (17)
43
+
44
+ ## [1.2.0] - 2026-04-30
45
+
46
+ ### Added
47
+ - **Visual test reports** — HTML test report auto-generated on every `npm test` run:
48
+ - Uses `jest-html-reporters` with collapsible test trees, timing, failure details
49
+ - Report saved to `reports/test-report.html`
50
+ - CI uploads report as artifact (14-day retention)
51
+ - Open locally: `open reports/test-report.html`
52
+ - **Property-based tests** (`src/__tests__/property-based.test.ts`) — 73 tests using fast-check:
53
+ - 44 validation tests: reject/accept all input types, error code invariants
54
+ - 17 masking tests: idempotency, completeness, secret detection, length bounds
55
+ - 12 generate-tests tests: no-throw, serializability, schema priority, nested arrays
56
+ - Fixed flaky `fc.double()` producing NaN/Infinity → use `fc.integer()` for numeric fields
57
+ - **Enhanced mock server** (`src/__tests__/fixtures/mock-server.ts`) — 68 tests:
58
+ - Configurable delays (`defaultDelay`) and random failures (`failureRate`)
59
+ - Input schema validation (`validateSchemas` option)
60
+ - Stateful tools: `counter` (increment/get/reset), `items` (add/list/remove/clear)
61
+ - Transform tool: `upper`/`lower`/`reverse`/`length` operations
62
+ - Custom handlers: `registerToolHandler`, `registerResourceHandler`, `registerPromptHandler`
63
+ - Call history: `getCallHistory`, `getCallCount` for test assertions
64
+ - Streaming support: `setupStream`, `nextStreamChunk`
65
+ - Dynamic registration/removal of tools, resources, prompts
66
+ - **TypeScript type generator** (`src/generate-types.ts`) — generate typed `.d.ts` from MCP tool schemas:
67
+ - `generateTypes()` API: connect to server, inspect schemas, emit TypeScript declarations
68
+ - CLI: `npx mcp-tester generate-types node ./server.js -o server.d.ts`
69
+ - Handles: primitives, enums, arrays, nested objects, oneOf/anyOf/allOf, $ref, const
70
+ - Generated types: `{Tool}Args`, `ToolName`, `ToolArgsMap`, `ToolCall`, `ResourceUri`, `PromptCall`
71
+ - 59 tests (12 e2e + 4 options + 43 unit)
72
+ - **Server health checks** — detect zombie processes and monitor server health:
73
+ - `isHealthy()` — sends a lightweight `tools/list` ping, returns `HealthStatus` with latency, PID, message
74
+ - `getServerPid()` — get the server process PID
75
+ - `getLastHealthStatus()` — cached result without re-checking
76
+ - `startHealthMonitor(options)` — periodic health monitoring with `onUnhealthy`/`onRecovery`/`onCheck` callbacks
77
+ - `stopHealthMonitor()` — stop periodic monitoring (also auto-stopped by `client.stop()`)
78
+ - Zombie process detection via `process.kill(pid, 0)`
79
+ - 17 tests (including SIGKILL zombie detection test)
80
+ - **Test code generator** (`src/generate-tests.ts`) — generate a complete test file from MCP server inspection:
81
+ - CLI: `mcp-tester generate node ./server.js -o server.test.ts` (alias: `gen`)
82
+ - API: `generateTests({ command, args, framework, ... })`
83
+ - Generates lifecycle tests, tool call tests with sample args from JSON Schema, resource read tests, prompt tests
84
+ - Options: `--framework jest|vitest`, `--output <file>`, `--description <name>`, `--no-tools`, `--no-resources`, `--no-prompts`, `--no-matchers`
85
+ - Sample arguments derived from JSON Schema: types, enums, defaults, examples
86
+ - 21 tests in `generate-tests.test.ts`
87
+ - **Input validation** (`src/utils/validation.ts`) — all MCPClient methods validate inputs before execution:
88
+ - `start()`: validates command (required string), args (string array), env (string values), startupDelay (non-negative number)
89
+ - `callTool()`: validates name (required string), arguments (object), timeout (positive number), retries (non-negative number)
90
+ - `readResource()`: validates URI (required non-empty string)
91
+ - `getPrompt()`: validates name (required string), args (string values)
92
+ - `requestSampling()`: validates messages (non-empty array)
93
+ - Constructor: validates timeout (positive), retries/retryDelay/startupDelay (non-negative), name/version (string)
94
+ - Validation runs before connection checks — invalid args throw `MCPClientError`, not `MCPNotStartedError`
95
+ - 91 tests in `validation.test.ts`
96
+ - **Code coverage comments on PRs** — GitHub Actions workflow now posts a coverage table as a sticky comment on every pull request (statements/branches/functions/lines with thresholds)
97
+ - **Dedicated `coverage` CI job** — runs on single Node.js version (faster than matrix), generates `coverage-summary.json`, uploads to Codecov v4
98
+ - **`coverageReporters`** in `jest.config.js` — added `json-summary` reporter for CI coverage data extraction
99
+ - **VS Code snippets** (`.vscode/mcp-tester.code-snippets`) — 15 snippets for MCP test patterns (type `mcp` prefix)
100
+
101
+ ### Changed
102
+ - **Per-file coverage thresholds** updated for new files (`masking.ts`, `logger.ts`) and actual coverage numbers
103
+ - Global thresholds: 67% statements, 61% branches, 60% functions, 67% lines (was 76/57/64/76)
104
+ - **README** coverage badge and table updated with real numbers (68/61/60/68)
105
+ - **docs/cicd.md** — rewritten with full workflow including coverage job, PR comments, and Codecov
106
+ - **docs/testing.md** — updated coverage thresholds (was "80%", now per-file table)
107
+ - **AGENTS.md** — updated test count, coverage stats, CI job descriptions, file listing
108
+ - Fixed flaky `delay` test (100ms → 90ms tolerance for timing variance)
109
+ - **Updated test count**: 491 (was 306)
110
+
8
111
  ## [1.1.0] - 2026-04-23
9
112
 
10
113
  ### Added
package/README.md CHANGED
@@ -6,9 +6,10 @@
6
6
 
7
7
  [![npm version](https://img.shields.io/npm/v/@slbdn/mcp-tester)](https://www.npmjs.com/package/@slbdn/mcp-tester)
8
8
  [![npm downloads](https://img.shields.io/npm/dm/@slbdn/mcp-tester)](https://www.npmjs.com/package/@slbdn/mcp-tester)
9
- [![Node.js Version](https://img.shields.io/node/v/@slbdn/mcp-tester)](https://nodejs.org)
9
+ [![Node.js Version](https://img.shields.io/badge/node-%3E%3D18-brightgreen)](https://nodejs.org)
10
10
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
11
- [![Test Status](https://img.shields.io/badge/tests-227_passing-brightgreen)](https://github.com/islobodan/mcp-tester/actions/workflows/test.yml)
11
+ [![Test Status](https://img.shields.io/badge/tests-635%20passing-brightgreen)](https://github.com/islobodan/mcp-tester/actions/workflows/test.yml)
12
+ [![Coverage](https://img.shields.io/badge/coverage-68%2F61%2F60%2F68-brightgreen)](https://github.com/islobodan/mcp-tester/actions/workflows/test.yml) <!-- statements/branches/functions/lines -->
12
13
 
13
14
  A production-ready MCP (Model Context Protocol) client for **automated testing** of MCP servers with Jest.
14
15
  No more manual clicking through inspectors — write real tests, run them in CI, break builds on regressions.
@@ -25,13 +26,49 @@ No more manual clicking through inspectors — write real tests, run them in CI,
25
26
  | **Notifications** | React to resource changes, logging messages, tool list updates |
26
27
  | **Error Handling** | Custom error classes for timeouts, connection failures, server errors |
27
28
  | **Retries** | Exponential backoff with configurable attempts and delay |
29
+ | **Benchmarks** | Measure latency, throughput, and payload performance with `npm run benchmark` |
28
30
  | **CLI** | Smoke-test any server from the terminal in seconds |
29
31
 
32
+ ## Architecture
33
+
34
+ ```
35
+ ┌─────────────────────────────────────────────────────────────────────┐
36
+ │ Your Test Suite │
37
+ │ ┌──────────┐ ┌───────────┐ ┌────────┐ ┌───────────────────┐ │
38
+ │ │ Jest │ │ Vitest │ │ assert │ │ Custom Runner │ │
39
+ │ │ matchers │ │ matchers │ │ module │ │ (any framework) │ │
40
+ │ └────┬─────┘ └─────┬─────┘ └───┬────┘ └────────┬──────────┘ │
41
+ │ │ │ │ │ │
42
+ │ ┌────▼──────────────▼─────────────▼─────────────────▼──────────┐ │
43
+ │ │ MCPClient │ │
44
+ │ │ ┌──────────────────────────────────────────────────────┐ │ │
45
+ │ │ │ start() │ stop() │ listTools() │ callTool() │ ... │ │ │
46
+ │ │ └──────────────────────┬───────────────────────────────────┘ │ │
47
+ │ │ │ │ │
48
+ │ │ ┌──────────────────────▼───────────────────────────────┐ │ │
49
+ │ │ │ Retry │ Timeout │ Error Handling │ │ │
50
+ │ │ │ MCPTimeoutError │ MCPConnectionError │ ... │ │ │
51
+ │ │ └──────────────────────┬───────────────────────────────┘ │ │
52
+ │ └─────────────────────────┼─────────────────────────────────────┘ │
53
+ │ │ stdio (JSON-RPC) │
54
+ │ ┌─────────────────────────▼─────────────────────────────────────┐ │
55
+ │ │ MCP Server (child process) │ │
56
+ │ │ ┌────────┐ ┌───────────┐ ┌──────────┐ ┌────────────┐ │ │
57
+ │ │ │ Tools │ │ Resources │ │ Prompts │ │ Sampling │ │ │
58
+ │ │ └────────┘ └───────────┘ └──────────┘ └────────────┘ │ │
59
+ │ └──────────────────────────────────────────────────────────────┘ │
60
+ └─────────────────────────────────────────────────────────────────────┘
61
+ ```
62
+
63
+ **Data flow**: Your test calls `MCPClient` → which sends JSON-RPC over stdio → to the MCP server process → server responds → `MCPClient` returns typed results → your assertion validates them.
64
+
65
+ **Key design**: `MCPClient` manages the server lifecycle (spawn/kill) and protocol handling. Your tests never deal with the transport layer directly.
66
+
30
67
  ## Why MCP Tester?
31
68
 
32
69
  - **Full protocol support** — tools, resources, prompts, sampling, elicitation, notifications
33
70
  - **Built on the official SDK** — `@modelcontextprotocol/sdk` v1.29.0
34
- - **Works out of the box** — mock server included, 227 tests passing
71
+ - **Works out of the box** — mock server included, 257 tests passing
35
72
  - **CLI included** — test any server from the command line
36
73
  - **TypeScript first** — strict types, ESM, full IntelliSense
37
74
  - **Parallel execution** — fire multiple tool calls concurrently with `Promise.all` for blazing-fast test suites
@@ -39,6 +76,30 @@ No more manual clicking through inspectors — write real tests, run them in CI,
39
76
  - **In-memory mock server** — unit test your test logic without spawning real processes
40
77
  - **Custom Jest matchers** — `toHaveTool()`, `toHaveResource()`, `toHavePrompt()` — and Vitest support
41
78
 
79
+ ## Test Coverage
80
+
81
+ | Metric | Coverage |
82
+ |--------|----------|
83
+ | Statements | 68% |
84
+ | Branches | 61% |
85
+ | Functions | 60% |
86
+ | Lines | 68% |
87
+ | Tests | 635 passing |
88
+
89
+ Per-file coverage thresholds are set in `jest.config.js` to catch regressions while avoiding CI noise. Run `npm run test:coverage` to see the full breakdown by file. PRs automatically receive a coverage comment via GitHub Actions.
90
+
91
+ ## Test Reports
92
+
93
+ Every `npm test` run auto-generates an HTML test report:
94
+
95
+ ```bash
96
+ npm test # generates reports/test-report.html
97
+ open reports/test-report.html # macOS
98
+ xdg-open reports/test-report.html # Linux
99
+ ```
100
+
101
+ The report includes collapsible test trees, timing per test, and failure details with stack traces. CI uploads reports as artifacts (14-day retention).
102
+
42
103
  ## Install
43
104
 
44
105
  ```bash
@@ -124,12 +185,90 @@ npx @slbdn/mcp-tester list-tools node ./server.js --json
124
185
  # Call a tool
125
186
  npx @slbdn/mcp-tester call-tool echo node ./server.js --params '{"message":"Hello"}'
126
187
 
127
- # Short aliases: lt, ct, rr, gp
188
+ # Generate test file from server inspection
189
+ npx @slbdn/mcp-tester generate node ./server.js -o server.test.ts
190
+ npx @slbdn/mcp-tester gen node ./server.js --framework vitest --no-resources
191
+
192
+ # Short aliases: lt, ct, rr, gp, gen
128
193
  npx @slbdn/mcp-tester lt node ./server.js
129
194
  ```
130
195
 
131
196
  See [CLI Reference](./docs/cli.md) for all commands and options.
132
197
 
198
+ ### Generate Test Files
199
+
200
+ Connect to any MCP server and generate a ready-to-run test file:
201
+
202
+ ```bash
203
+ # Generate Jest test file
204
+ npx @slbdn/mcp-tester generate node ./server.js -o server.test.ts
205
+
206
+ # Generate Vitest test file
207
+ npx @slbdn/mcp-tester gen node ./server.js --framework vitest
208
+
209
+ # Skip specific sections
210
+ npx @slbdn/mcp-tester generate node ./server.js --no-resources --no-prompts -o tools-only.test.ts
211
+ ```
212
+
213
+ Or use the API:
214
+
215
+ ```typescript
216
+ import { generateTests } from '@slbdn/mcp-tester';
217
+
218
+ const code = await generateTests({
219
+ command: 'node',
220
+ args: ['./server.js'],
221
+ framework: 'jest',
222
+ description: 'My Server Tests',
223
+ });
224
+
225
+ fs.writeFileSync('server.test.ts', code);
226
+ ```
227
+
228
+ The generator connects to the server, inspects all tools/resources/prompts, and generates:
229
+ - Connection and lifecycle tests
230
+ - Tool call tests with **sample arguments derived from JSON Schema**
231
+ - Resource read tests
232
+ - Prompt tests with sample arguments
233
+ - Proper `beforeEach`/`afterEach` cleanup
234
+
235
+ ### Generate TypeScript Types
236
+
237
+ Connect to any MCP server and generate typed `.d.ts` declarations from tool schemas:
238
+
239
+ ```bash
240
+ # Generate TypeScript types
241
+ npx @slbdn/mcp-tester generate-types node ./server.js -o server.d.ts
242
+ ```
243
+
244
+ Or use the API:
245
+
246
+ ```typescript
247
+ import { generateTypes } from '@slbdn/mcp-tester';
248
+ import { writeFileSync } from 'fs';
249
+
250
+ const types = await generateTypes({
251
+ command: 'node',
252
+ args: ['./server.js'],
253
+ });
254
+
255
+ writeFileSync('server.d.ts', types);
256
+ ```
257
+
258
+ This produces typed interfaces you can use for type-safe tool calls:
259
+
260
+ ```typescript
261
+ import type { ToolArgsMap } from './server.d.ts';
262
+
263
+ // TypeScript knows: { a: number; b: number }
264
+ const result = await client.callTool({
265
+ name: 'add',
266
+ arguments: { a: 1, b: 2 } as ToolArgsMap['add'],
267
+ });
268
+ ```
269
+
270
+ Supports: primitives, enums, nested objects, arrays, `oneOf`/`anyOf`/`allOf`, `$ref`, `const`.
271
+
133
272
  ## Speed Up Your Tests with Parallel Execution
134
273
 
135
274
  MCP Tester is built for concurrency. Fire off multiple tool calls at once — no waiting in line:
@@ -167,6 +306,10 @@ No extra config needed. Just `Promise.all` and go.
167
306
  | `start(config)` | `Promise<void>` | Connect to an MCP server |
168
307
  | `stop()` | `Promise<void>` | Disconnect and clean up |
169
308
  | `isConnected()` | `boolean` | Check connection state |
309
+ | `isHealthy()` | `Promise<HealthStatus>` | Check server health and responsiveness |
310
+ | `getServerPid()` | `number | null` | Get server process PID |
311
+ | `startHealthMonitor(opts)` | `void` | Start periodic health monitoring |
312
+ | `stopHealthMonitor()` | `void` | Stop health monitoring |
170
313
  | `listTools()` | `Promise<Tool[]>` | List available tools |
171
314
  | `callTool(options)` | `Promise<CallToolResult>` | Call a tool with arguments |
172
315
  | `listResources()` | `Promise<Resource[]>` | List available resources |
@@ -179,6 +322,25 @@ No extra config needed. Just `Promise.all` and go.
179
322
 
180
323
  See [API Reference](./docs/api-reference.md) for full details including types, options, and error classes.
181
324
 
325
+ ## Server Health Checks
326
+
327
+ Detect crashed servers and zombie processes during long test runs:
328
+
329
+ ```typescript
330
+ // One-time check
331
+ const health = await client.isHealthy();
332
+ console.log(`Healthy: ${health.healthy}, PID: ${health.pid}, latency: ${health.latencyMs}ms`);
333
+
334
+ // Periodic monitoring
335
+ client.startHealthMonitor({
336
+ interval: 3000,
337
+ onUnhealthy: (status) => console.error('Server down:', status.message),
338
+ onRecovery: (status) => console.log('Server recovered!'),
339
+ });
340
+ ```
341
+
342
+ Health checks detect zombie processes (killed/crashed but `isConnected()` still returns `true`) using `process.kill(pid, 0)`.
343
+
182
344
  ## Custom Jest Matchers
183
345
 
184
346
  Built-in matchers so your assertions read like English:
@@ -317,7 +479,7 @@ mcp-tester/
317
479
  │ │ ├── errors.ts # Error classes
318
480
  │ │ ├── logger.ts # Logging
319
481
  │ │ └── env.ts # Environment utilities
320
- │ └── __tests__/ # 227 tests
482
+ │ └── __tests__/ # 306 tests
321
483
  │ ├── everything-server.test.ts # Integration tests (server-everything)
322
484
  │ ├── real-server.test.ts # Integration tests (stdio transport)
323
485
  │ ├── cli.test.ts # CLI tool tests
@@ -328,7 +490,10 @@ mcp-tester/
328
490
  │ ├── jest-matchers-example.ts # Jest matchers usage
329
491
  │ ├── assert-example.ts # Assert utilities usage
330
492
  │ ├── everything-server-test.ts # Test against real MCP server
331
- │ └── mock-server.js # Standalone MCP server
493
+ │ ├── stateful-server-test.ts # Stateful server testing pattern
494
+ │ ├── mock-server.js # Basic standalone MCP server
495
+ │ ├── simple-server.ts # Minimal TypeScript MCP server
496
+ │ └── stateful-server.ts # Stateful MCP server (counter + todos)
332
497
  └── docs/ # Documentation
333
498
  ```
334
499
 
@@ -337,11 +502,117 @@ mcp-tester/
337
502
  ```bash
338
503
  npm install # Install dependencies
339
504
  npm run build # Compile TypeScript
340
- npm test # Run tests (227 tests)
505
+ npm run dev # Watch mode — auto-rebuild on source changes
506
+ npm test # Run tests (auto-generates reports/test-report.html)
341
507
  npm run lint # Lint code
342
508
  npm run format # Format with Prettier
343
509
  ```
344
510
 
511
+ ## Troubleshooting
512
+
513
+ ### "Client not started" Error
514
+
515
+ Calling methods before `start()`:
516
+
517
+ ```typescript
518
+ // ✗ Wrong
519
+ const client = new MCPClient();
520
+ const tools = await client.listTools(); // throws MCPNotStartedError
521
+
522
+ // ✓ Correct
523
+ const client = new MCPClient();
524
+ await client.start({ command: 'node', args: ['./server.js'] });
525
+ const tools = await client.listTools();
526
+ ```
527
+
528
+ ### Timeout Errors
529
+
530
+ ```typescript
531
+ // Global timeout
532
+ const client = new MCPClient({ timeout: 60000 });
533
+
534
+ // Per-call override
535
+ await client.callTool({ name: 'slow-tool', arguments: {}, timeout: 30000 });
536
+ ```
537
+
538
+ ### Server Won't Start
539
+
540
+ ```bash
541
+ # Verify the server runs standalone first
542
+ node ./my-server.js
543
+ ```
544
+
545
+ Check that the command and args are correct — `MCPConnectionError` includes the command and suggestions.
546
+
547
+ ### ESM Import Errors
548
+
549
+ ```typescript
550
+ // ✗ Wrong — missing .js extension
551
+ import { MCPClient } from './client/MCPClient';
552
+
553
+ // ✓ Correct
554
+ import { MCPClient } from './client/MCPClient.js';
555
+ ```
556
+
557
+ ### Tests Fail in CI
558
+
559
+ - Set `maxWorkers: 1` in Jest config (parallel workers can crash)
560
+ - Verify Node.js >= 18
561
+ - Check that server paths are correct
562
+
563
+ ### Debug Mode
564
+
565
+ ```typescript
566
+ const client = new MCPClient({ logLevel: 'debug', enableProtocolLogging: true });
567
+ ```
568
+
569
+ All logs go to stderr — they won't interfere with test output.
570
+
571
+ New logging features:
572
+ - **Timing**: operations log elapsed time (e.g., `Listed 4 tools in 12ms`)
573
+ - **Secret masking**: API keys, tokens, and passwords are auto-masked (e.g., `sk-ab...789`)
574
+ - **Pretty-printed JSON**: protocol messages are formatted and masked
575
+ - **Timestamps**: enable with `{ timestamps: true }`
576
+ - **Colors**: auto-enabled in terminals (cyan=debug, green=info, yellow=warn, red=error)
577
+
578
+ ```typescript
579
+ import { startTimer, prettyPrint } from '@slbdn/mcp-tester';
580
+
581
+ // Measure operation timing
582
+ const elapsed = startTimer();
583
+ await someOperation();
584
+ console.log(`Took ${elapsed()}ms`);
585
+
586
+ // Pretty-print objects with secret masking
587
+ prettyPrint({ apiKey: 'sk-proj-abc123...', count: 42 });
588
+ ```
589
+
590
+ See [Troubleshooting Guide](./docs/troubleshooting.md) for more details.
591
+
592
+ ## VS Code Snippets
593
+
594
+ Type `mcp` in any TypeScript file to get started — 15 built-in snippets:
595
+
596
+ | Prefix | Inserts |
597
+ |--------|--------|
598
+ | `mcp-test` | Full test suite with lifecycle setup |
599
+ | `mcp-tool` | Tool call test + assertion |
600
+ | `mcp-resource` | Resource read test |
601
+ | `mcp-prompt` | Prompt get test |
602
+ | `mcp-client` | `new MCPClient()` + `start()` |
603
+ | `mcp-client-opts` | Client with timeout, retries, log level |
604
+ | `mcp-error` | Error handling test (`rejects.toThrow`) |
605
+ | `mcp-parallel` | `Promise.all` parallel tool calls |
606
+ | `mcp-list-tools` | List tools + `toHaveTool` assertion |
607
+ | `mcp-list-resources` | List resources + `toHaveResource` assertion |
608
+ | `mcp-list-prompts` | List prompts + `toHavePrompt` assertion |
609
+ | `mcp-assert-text` | `assert.toolTextEquals()` |
610
+ | `mcp-assert-json` | `assert.toolJsonEquals()` |
611
+ | `mcp-notifications` | Notification handler setup |
612
+ | `mcp-import` / `mcp-import-full` | Import statements |
613
+
614
+ Snippets are in `.vscode/mcp-tester.code-snippets` — works out of the box when you open the project in VS Code.
615
+
345
616
  ## Contributing
346
617
 
347
618
  1. Fork the repository
package/dist/cli/index.js CHANGED
@@ -3,11 +3,27 @@
3
3
  import { Command } from 'commander';
4
4
  import { MCPClient } from '../client/MCPClient.js';
5
5
  import { MCPConnectionError, MCPTimeoutError, MCPServerError } from '../utils/errors.js';
6
+ import { generateTests } from '../generate-tests.js';
7
+ import { generateTypes } from '../generate-types.js';
8
+ import { readFileSync } from 'fs';
9
+ import { fileURLToPath } from 'url';
10
+ import { dirname, join } from 'path';
11
+ // Read version from package.json (works in both src and dist)
12
+ const __dirname = dirname(fileURLToPath(import.meta.url));
13
+ function getPackageVersion() {
14
+ try {
15
+ const pkg = JSON.parse(readFileSync(join(__dirname, '../../package.json'), 'utf-8'));
16
+ return pkg.version || '0.0.0';
17
+ }
18
+ catch {
19
+ return '0.0.0';
20
+ }
21
+ }
6
22
  const program = new Command();
7
23
  program
8
24
  .name('mcp-tester')
9
25
  .description('CLI tool for testing MCP servers')
10
- .version('1.0.0', '-V, --version');
26
+ .version(getPackageVersion(), '-V, --version');
11
27
  // Global options - use string type since commander passes strings
12
28
  program
13
29
  .option('-t, --timeout <ms>', 'Request timeout in milliseconds', '30000')
@@ -290,6 +306,101 @@ program
290
306
  await client.stop();
291
307
  }
292
308
  });
309
+ // Generate test file command
310
+ program
311
+ .command('generate')
312
+ .alias('gen')
313
+ .description('Generate test file from MCP server inspection')
314
+ .argument('<command>', 'Command to run the MCP server')
315
+ .argument('[args...]', 'Arguments for the server command')
316
+ .option('-o, --output <file>', 'Output file path (prints to stdout if omitted)')
317
+ .option('--framework <fw>', 'Test framework: jest or vitest', 'jest')
318
+ .option('--description <desc>', 'Description for the test suite')
319
+ .option('--no-resources', 'Skip resource tests')
320
+ .option('--no-prompts', 'Skip prompt tests')
321
+ .option('--no-tools', 'Skip tool tests')
322
+ .option('--no-matchers', 'Skip custom matchers import')
323
+ .action(async (command, serverArgs, opts) => {
324
+ const framework = String(opts['framework'] || 'jest');
325
+ if (framework !== 'jest' && framework !== 'vitest') {
326
+ console.error('❌ Framework must be "jest" or "vitest"');
327
+ process.exit(1);
328
+ }
329
+ try {
330
+ console.error('🔍 Inspecting MCP server...');
331
+ const code = await generateTests({
332
+ command,
333
+ args: serverArgs,
334
+ framework: framework,
335
+ description: opts['description'],
336
+ includeResources: opts['resources'] !== false,
337
+ includePrompts: opts['prompts'] !== false,
338
+ includeTools: opts['tools'] !== false,
339
+ includeMatchers: opts['matchers'] !== false,
340
+ timeout: parseInt(String(opts['timeout'] || '30000')),
341
+ });
342
+ if (opts['output']) {
343
+ const fs = await import('fs');
344
+ const path = await import('path');
345
+ const outputPath = String(opts['output']);
346
+ const dir = path.dirname(outputPath);
347
+ if (dir && dir !== '.') {
348
+ fs.mkdirSync(dir, { recursive: true });
349
+ }
350
+ fs.writeFileSync(outputPath, code, 'utf-8');
351
+ console.error(`✅ Generated test file: ${outputPath}`);
352
+ }
353
+ else {
354
+ process.stdout.write(code);
355
+ }
356
+ }
357
+ catch (error) {
358
+ console.error(`❌ Error: ${error instanceof Error ? error.message : String(error)}`);
359
+ process.exit(1);
360
+ }
361
+ });
362
+ // Generate TypeScript types command
363
+ program
364
+ .command('generate-types')
365
+ .alias('gen-types')
366
+ .description('Generate TypeScript type declarations from MCP server tool schemas')
367
+ .argument('<command>', 'Command to run the MCP server')
368
+ .argument('[args...]', 'Arguments for the server command')
369
+ .option('-o, --output <file>', 'Output file path (prints to stdout if omitted)')
370
+ .option('--module-name <name>', 'Module name for import hints', '@slbdn/mcp-tester')
371
+ .option('--no-resources', 'Skip resource URI types')
372
+ .option('--no-prompts', 'Skip prompt argument types')
373
+ .action(async (command, serverArgs, opts) => {
374
+ try {
375
+ console.error('🔍 Inspecting MCP server for type generation...');
376
+ const types = await generateTypes({
377
+ command,
378
+ args: serverArgs,
379
+ moduleName: opts['moduleName'],
380
+ includeResources: opts['resources'] !== false,
381
+ includePrompts: opts['prompts'] !== false,
382
+ timeout: parseInt(String(opts['timeout'] || '30000')),
383
+ });
384
+ if (opts['output']) {
385
+ const fs = await import('fs');
386
+ const path = await import('path');
387
+ const outputPath = String(opts['output']);
388
+ const dir = path.dirname(outputPath);
389
+ if (dir && dir !== '.') {
390
+ fs.mkdirSync(dir, { recursive: true });
391
+ }
392
+ fs.writeFileSync(outputPath, types, 'utf-8');
393
+ console.error(`✅ Generated type declarations: ${outputPath}`);
394
+ }
395
+ else {
396
+ process.stdout.write(types);
397
+ }
398
+ }
399
+ catch (error) {
400
+ console.error(`❌ Error: ${error instanceof Error ? error.message : String(error)}`);
401
+ process.exit(1);
402
+ }
403
+ });
293
404
  // Add custom help with examples using Commander's built-in help system
294
405
  program.addHelpText('after', `
295
406
  Examples:
@@ -300,6 +411,8 @@ Examples:
300
411
  mcp-tester get-prompt greet node ./server.js --args '{"name":"World"}'
301
412
  mcp-tester lt node ./server.js # short alias for list-tools
302
413
  mcp-tester ct echo node ./server.js --params '{}' # short alias for call-tool
414
+ mcp-tester generate node ./server.js -o server.test.ts # generate test file
415
+ mcp-tester generate-types node ./server.js -o server.d.ts # generate TypeScript types
303
416
  `);
304
417
  // Show help when invoked with no arguments
305
418
  const args = process.argv.slice(2);