@slbdn/mcp-tester 1.0.2 → 1.1.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 +70 -82
- package/README.md +230 -1437
- package/dist/assert.d.ts +75 -0
- package/dist/assert.d.ts.map +1 -0
- package/dist/assert.js +250 -0
- package/dist/assert.js.map +1 -0
- package/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +311 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/client/MCPClient.d.ts +316 -0
- package/dist/client/MCPClient.d.ts.map +1 -1
- package/dist/client/MCPClient.js +240 -9
- package/dist/client/MCPClient.js.map +1 -1
- package/dist/index.d.ts +32 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +32 -1
- package/dist/index.js.map +1 -1
- package/dist/matchers.d.ts +262 -0
- package/dist/matchers.d.ts.map +1 -0
- package/dist/matchers.js +527 -0
- package/dist/matchers.js.map +1 -0
- package/dist/utils/errors.d.ts +38 -0
- package/dist/utils/errors.d.ts.map +1 -1
- package/dist/utils/errors.js +38 -0
- package/dist/utils/errors.js.map +1 -1
- package/dist/utils/logger.d.ts +57 -0
- package/dist/utils/logger.d.ts.map +1 -1
- package/dist/utils/logger.js +32 -3
- package/dist/utils/logger.js.map +1 -1
- package/package.json +32 -5
- package/vitest.d.ts +51 -0
package/README.md
CHANGED
|
@@ -1,329 +1,84 @@
|
|
|
1
1
|
# MCP Tester
|
|
2
2
|
|
|
3
|
+
> **This is not your grandpa's MCP Inspector.**
|
|
4
|
+
> This is a real testing framework. No GUI, no clicking, no manual verification.
|
|
5
|
+
> Write tests, run them in CI, ship with confidence. 🚀
|
|
6
|
+
|
|
3
7
|
[](https://www.npmjs.com/package/@slbdn/mcp-tester)
|
|
4
8
|
[](https://www.npmjs.com/package/@slbdn/mcp-tester)
|
|
5
9
|
[](https://nodejs.org)
|
|
6
10
|
[](https://opensource.org/licenses/MIT)
|
|
7
|
-
[
|
|
40
|
-
- [Troubleshooting](#troubleshooting)
|
|
41
|
-
- [Project Structure](#project-structure)
|
|
42
|
-
- [Development](#development)
|
|
43
|
-
- [Test Coverage](#test-coverage)
|
|
44
|
-
- [Security](#security)
|
|
45
|
-
- [License](#license)
|
|
46
|
-
- [Contributing](#contributing)
|
|
47
|
-
- [Additional Resources](#additional-resources)
|
|
48
|
-
- [Support](#support)
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
## 🤖 For AI Agents
|
|
52
|
-
|
|
53
|
-
If you're an AI agent or automated system working with this codebase, see [AGENTS.md](./AGENTS.md) for comprehensive documentation including:
|
|
54
|
-
|
|
55
|
-
- **Essential commands** for development, testing, and releases
|
|
56
|
-
- **Code organization** and file structure
|
|
57
|
-
- **Naming conventions** and style guidelines
|
|
58
|
-
- **Testing patterns** and common gotchas
|
|
59
|
-
- **CI/CD integration** details
|
|
60
|
-
- **Error handling** patterns
|
|
61
|
-
- **Development workflow** and best practices
|
|
62
|
-
- **Key files reference** for quick lookup
|
|
63
|
-
|
|
64
|
-
The AGENTS.md file is specifically designed to help AI agents understand the codebase, patterns, and conventions used in this project.
|
|
65
|
-
|
|
66
|
-
## 🚀 Installation
|
|
67
|
-
|
|
68
|
-
### Prerequisites
|
|
69
|
-
|
|
70
|
-
- **Node.js** >= 18
|
|
71
|
-
- **npm** >= 9
|
|
72
|
-
|
|
73
|
-
### Install from npm
|
|
11
|
+
[](https://github.com/islobodan/mcp-tester/actions/workflows/test.yml)
|
|
12
|
+
|
|
13
|
+
A production-ready MCP (Model Context Protocol) client for **automated testing** of MCP servers with Jest.
|
|
14
|
+
No more manual clicking through inspectors — write real tests, run them in CI, break builds on regressions.
|
|
15
|
+
|
|
16
|
+
## Capabilities
|
|
17
|
+
|
|
18
|
+
| Category | What You Can Test |
|
|
19
|
+
|----------|-------------------|
|
|
20
|
+
| **Tools** | List, call with arguments, validate results, test error handling |
|
|
21
|
+
| **Resources** | List available resources, read by URI, validate content |
|
|
22
|
+
| **Prompts** | List templates, get with arguments, validate generated messages |
|
|
23
|
+
| **Sampling** | Request LLM sampling, validate model responses |
|
|
24
|
+
| **Elicitation** | Handle server-initiated user input requests |
|
|
25
|
+
| **Notifications** | React to resource changes, logging messages, tool list updates |
|
|
26
|
+
| **Error Handling** | Custom error classes for timeouts, connection failures, server errors |
|
|
27
|
+
| **Retries** | Exponential backoff with configurable attempts and delay |
|
|
28
|
+
| **CLI** | Smoke-test any server from the terminal in seconds |
|
|
29
|
+
|
|
30
|
+
## Why MCP Tester?
|
|
31
|
+
|
|
32
|
+
- **Full protocol support** — tools, resources, prompts, sampling, elicitation, notifications
|
|
33
|
+
- **Built on the official SDK** — `@modelcontextprotocol/sdk` v1.29.0
|
|
34
|
+
- **Works out of the box** — mock server included, 227 tests passing
|
|
35
|
+
- **CLI included** — test any server from the command line
|
|
36
|
+
- **TypeScript first** — strict types, ESM, full IntelliSense
|
|
37
|
+
- **Parallel execution** — fire multiple tool calls concurrently with `Promise.all` for blazing-fast test suites
|
|
38
|
+
- **Retry with backoff** — flaky servers? Configure retries so your CI stays green
|
|
39
|
+
- **In-memory mock server** — unit test your test logic without spawning real processes
|
|
40
|
+
- **Custom Jest matchers** — `toHaveTool()`, `toHaveResource()`, `toHavePrompt()` — and Vitest support
|
|
41
|
+
|
|
42
|
+
## Install
|
|
74
43
|
|
|
75
44
|
```bash
|
|
76
45
|
npm install @slbdn/mcp-tester --save-dev
|
|
77
46
|
```
|
|
78
47
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
```bash
|
|
82
|
-
git clone https://github.com/islobodan/mcp-tester.git
|
|
83
|
-
cd mcp-tester
|
|
84
|
-
npm install
|
|
85
|
-
```
|
|
86
|
-
|
|
87
|
-
### Development Installation
|
|
88
|
-
|
|
89
|
-
```bash
|
|
90
|
-
# Clone the repository
|
|
91
|
-
git clone https://github.com/islobodan/mcp-tester.git
|
|
92
|
-
cd mcp-tester
|
|
93
|
-
|
|
94
|
-
# Install dependencies
|
|
95
|
-
npm install
|
|
96
|
-
|
|
97
|
-
# Build the project
|
|
98
|
-
npm run build
|
|
99
|
-
```
|
|
100
|
-
|
|
101
|
-
## 🌐 Node.js Compatibility
|
|
102
|
-
|
|
103
|
-
MCP Tester is designed to work with a wide range of Node.js versions. Here's the compatibility matrix:
|
|
104
|
-
|
|
105
|
-
### Supported Versions
|
|
106
|
-
|
|
107
|
-
| Node.js Version | Status | Notes |
|
|
108
|
-
|---------------|--------|-------|
|
|
109
|
-
| 18.x | ✅ Fully Supported | Minimum required version |
|
|
110
|
-
| 20.x (LTS) | ✅ Fully Supported | Recommended for production |
|
|
111
|
-
| 21.x | ✅ Fully Supported | Latest stable release |
|
|
112
|
-
| 22.x | ✅ Supported | Tested in CI/CD |
|
|
113
|
-
|
|
114
|
-
### Testing Matrix
|
|
115
|
-
|
|
116
|
-
MCP Tester is automatically tested against multiple Node.js versions in CI/CD:
|
|
117
|
-
|
|
118
|
-
```yaml
|
|
119
|
-
strategy:
|
|
120
|
-
matrix:
|
|
121
|
-
node-version: [18, 20, 21]
|
|
122
|
-
```
|
|
123
|
-
|
|
124
|
-
All tests pass successfully on:
|
|
125
|
-
- Node.js 18.19.0+
|
|
126
|
-
- Node.js 20.11.0+
|
|
127
|
-
- Node.js 21.7.0+
|
|
128
|
-
|
|
129
|
-
### Runtime Requirements
|
|
130
|
-
|
|
131
|
-
The following Node.js features are required:
|
|
132
|
-
- ES2022 Modules (ESM)
|
|
133
|
-
- Async/await support
|
|
134
|
-
- Promise-based APIs
|
|
135
|
-
- Child process `spawn` with stdio
|
|
136
|
-
- Fetch API (for potential HTTP transport support)
|
|
137
|
-
|
|
138
|
-
### Version-Specific Notes
|
|
139
|
-
|
|
140
|
-
#### Node.js 18.x
|
|
141
|
-
- Minimum required version
|
|
142
|
-
- All features fully supported
|
|
143
|
-
- No known issues
|
|
144
|
-
|
|
145
|
-
#### Node.js 20.x (LTS)
|
|
146
|
-
- **Recommended for production use**
|
|
147
|
-
- Includes performance improvements
|
|
148
|
-
- All features fully supported
|
|
149
|
-
|
|
150
|
-
#### Node.js 21.x
|
|
151
|
-
- Latest stable features
|
|
152
|
-
- Improved garbage collection
|
|
153
|
-
- Full feature compatibility
|
|
154
|
-
|
|
155
|
-
#### Node.js 22.x
|
|
156
|
-
- Testing indicates compatibility
|
|
157
|
-
- May include newer V8 improvements
|
|
158
|
-
|
|
159
|
-
### Package Engine Configuration
|
|
160
|
-
|
|
161
|
-
The `package.json` specifies the Node.js engine requirement:
|
|
162
|
-
|
|
163
|
-
```json
|
|
164
|
-
{
|
|
165
|
-
"engines": {
|
|
166
|
-
"node": ">=18"
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
```
|
|
170
|
-
|
|
171
|
-
This ensures that npm will prevent installation on unsupported Node.js versions.
|
|
172
|
-
|
|
173
|
-
### TypeScript Compatibility
|
|
174
|
-
|
|
175
|
-
MCP Tester targets TypeScript 5.3.0 with ES2022 output:
|
|
176
|
-
|
|
177
|
-
```json
|
|
178
|
-
{
|
|
179
|
-
"compilerOptions": {
|
|
180
|
-
"target": "ES2022",
|
|
181
|
-
"module": "ES2022",
|
|
182
|
-
"lib": ["ES2022"]
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
```
|
|
186
|
-
|
|
187
|
-
### Testing Your Node.js Version
|
|
188
|
-
|
|
189
|
-
To check your Node.js version:
|
|
190
|
-
|
|
191
|
-
```bash
|
|
192
|
-
# Check Node.js version
|
|
193
|
-
node --version
|
|
194
|
-
|
|
195
|
-
# Check npm version
|
|
196
|
-
npm --version
|
|
197
|
-
|
|
198
|
-
# Verify compatibility
|
|
199
|
-
node --version | awk '{print $1}' | xargs -I {} echo "Node.js {} is supported"
|
|
200
|
-
```
|
|
201
|
-
|
|
202
|
-
### Upgrading Node.js
|
|
203
|
-
|
|
204
|
-
If you're using an older version of Node.js (< 18):
|
|
205
|
-
|
|
206
|
-
**Using nvm (Node Version Manager):**
|
|
207
|
-
```bash
|
|
208
|
-
# Install latest Node.js
|
|
209
|
-
nvm install 20
|
|
210
|
-
|
|
211
|
-
# Use specific version
|
|
212
|
-
nvm use 20
|
|
213
|
-
|
|
214
|
-
# Set as default
|
|
215
|
-
nvm alias default 20
|
|
216
|
-
```
|
|
217
|
-
|
|
218
|
-
**Using n:**
|
|
219
|
-
```bash
|
|
220
|
-
# Install latest LTS
|
|
221
|
-
n 20
|
|
222
|
-
|
|
223
|
-
# Switch versions
|
|
224
|
-
n 18
|
|
225
|
-
```
|
|
226
|
-
|
|
227
|
-
**Using Node.js installer:**
|
|
228
|
-
```bash
|
|
229
|
-
# Download from https://nodejs.org/
|
|
230
|
-
# Install the latest LTS version (20.x recommended)
|
|
231
|
-
```
|
|
232
|
-
|
|
233
|
-
### CI/CD Version Specification
|
|
234
|
-
|
|
235
|
-
When using MCP Tester in CI/CD, specify the Node.js version:
|
|
236
|
-
|
|
237
|
-
**GitHub Actions:**
|
|
238
|
-
```yaml
|
|
239
|
-
- uses: actions/setup-node@v3
|
|
240
|
-
with:
|
|
241
|
-
node-version: '20' # Specify desired version
|
|
242
|
-
```
|
|
243
|
-
|
|
244
|
-
**CircleCI:**
|
|
245
|
-
```yaml
|
|
246
|
-
docker:
|
|
247
|
-
- image: cimg/node:20.11.0
|
|
248
|
-
```
|
|
249
|
-
|
|
250
|
-
**Jenkins:**
|
|
251
|
-
```groovy
|
|
252
|
-
tools {
|
|
253
|
-
nodejs 'Node.js 20.11.0'
|
|
254
|
-
}
|
|
255
|
-
```
|
|
256
|
-
|
|
257
|
-
### Known Issues by Version
|
|
258
|
-
|
|
259
|
-
| Node.js Version | Known Issue | Workaround |
|
|
260
|
-
|---------------|--------------|-------------|
|
|
261
|
-
| < 18 | Not supported | Upgrade to 18+ |
|
|
262
|
-
| 18.0-18.16 | Potential issues with ESM | Use 18.17+ |
|
|
263
|
-
| Any | Memory limits for large operations | Increase `--max-old-space-size` |
|
|
264
|
-
|
|
265
|
-
### Performance by Version
|
|
266
|
-
|
|
267
|
-
Relative performance benchmarks (lower is better):
|
|
268
|
-
|
|
269
|
-
| Operation | Node 18 | Node 20 | Node 21 |
|
|
270
|
-
|-----------|-----------|-----------|-----------|
|
|
271
|
-
| Tool Call | 1.0x | 0.95x | 0.92x |
|
|
272
|
-
| Resource Read | 1.0x | 0.96x | 0.93x |
|
|
273
|
-
| Concurrent Ops | 1.0x | 1.02x | 1.05x |
|
|
274
|
-
|
|
275
|
-
*Based on 1000 operations per test
|
|
276
|
-
|
|
277
|
-
### Platform Compatibility
|
|
278
|
-
|
|
279
|
-
MCP Tester works on:
|
|
280
|
-
- ✅ macOS (Intel & Apple Silicon)
|
|
281
|
-
- ✅ Linux (Ubuntu, Debian, CentOS, Alpine)
|
|
282
|
-
- ✅ Windows 10/11 (via WSL or native Node.js)
|
|
48
|
+
Requires **Node.js >= 18**. See [Node.js Compatibility](./docs/nodejs-compatibility.md) for details.
|
|
283
49
|
|
|
284
|
-
|
|
285
|
-
- Native support with Node.js
|
|
286
|
-
- WSL2 recommended for Unix-like development
|
|
287
|
-
- Ensure proper line endings (CRLF vs LF)
|
|
50
|
+
## Quick Start
|
|
288
51
|
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
### Basic Usage
|
|
52
|
+
### Connect and Use Tools
|
|
292
53
|
|
|
293
54
|
```typescript
|
|
294
|
-
import { MCPClient } from 'mcp-tester';
|
|
55
|
+
import { MCPClient } from '@slbdn/mcp-tester';
|
|
295
56
|
|
|
296
57
|
const client = new MCPClient({
|
|
297
58
|
name: 'my-test-client',
|
|
298
59
|
version: '1.0.0',
|
|
299
|
-
timeout: 30000,
|
|
300
60
|
});
|
|
301
61
|
|
|
302
|
-
//
|
|
62
|
+
// Connect to your MCP server
|
|
303
63
|
await client.start({
|
|
304
64
|
command: 'node',
|
|
305
65
|
args: ['./your-mcp-server.js'],
|
|
306
66
|
});
|
|
307
67
|
|
|
308
|
-
// List
|
|
68
|
+
// List and call tools
|
|
309
69
|
const tools = await client.listTools();
|
|
310
|
-
console.log('Available tools:', tools.map(t => t.name));
|
|
311
|
-
|
|
312
|
-
// Call a tool
|
|
313
70
|
const result = await client.callTool({
|
|
314
|
-
name: 'your-tool
|
|
315
|
-
arguments: {
|
|
71
|
+
name: 'your-tool',
|
|
72
|
+
arguments: { input: 'test' },
|
|
316
73
|
});
|
|
317
|
-
console.log('Tool result:', result);
|
|
318
74
|
|
|
319
|
-
// Clean up
|
|
320
75
|
await client.stop();
|
|
321
76
|
```
|
|
322
77
|
|
|
323
|
-
###
|
|
78
|
+
### Test with Jest
|
|
324
79
|
|
|
325
80
|
```typescript
|
|
326
|
-
import { MCPClient } from 'mcp-tester';
|
|
81
|
+
import { MCPClient } from '@slbdn/mcp-tester';
|
|
327
82
|
|
|
328
83
|
describe('My MCP Server', () => {
|
|
329
84
|
let client: MCPClient;
|
|
@@ -357,1206 +112,244 @@ describe('My MCP Server', () => {
|
|
|
357
112
|
});
|
|
358
113
|
```
|
|
359
114
|
|
|
360
|
-
###
|
|
115
|
+
### Test from the Command Line
|
|
361
116
|
|
|
362
117
|
```bash
|
|
363
|
-
#
|
|
364
|
-
npx
|
|
365
|
-
|
|
366
|
-
# Full capabilities test
|
|
367
|
-
npx tsx examples/full-test.ts
|
|
368
|
-
|
|
369
|
-
# Run mock server standalone (for manual testing)
|
|
370
|
-
node examples/mock-server.js
|
|
371
|
-
```
|
|
372
|
-
|
|
373
|
-
## 📚 API Reference
|
|
374
|
-
|
|
375
|
-
### MCPClient
|
|
376
|
-
|
|
377
|
-
The main class for interacting with MCP servers.
|
|
118
|
+
# Quick smoke test
|
|
119
|
+
npx @slbdn/mcp-tester test node ./server.js
|
|
378
120
|
|
|
379
|
-
|
|
121
|
+
# List tools (JSON output)
|
|
122
|
+
npx @slbdn/mcp-tester list-tools node ./server.js --json
|
|
380
123
|
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
```
|
|
384
|
-
|
|
385
|
-
**Parameters:**
|
|
386
|
-
|
|
387
|
-
- `options` (optional) - Client configuration options
|
|
388
|
-
- `name` (string): Client identifier (default: `'mcp-test-client'`)
|
|
389
|
-
- `version` (string): Client version (default: `'1.0.0'`)
|
|
390
|
-
- `timeout` (number): Default request timeout in milliseconds (default: `30000`)
|
|
124
|
+
# Call a tool
|
|
125
|
+
npx @slbdn/mcp-tester call-tool echo node ./server.js --params '{"message":"Hello"}'
|
|
391
126
|
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
```typescript
|
|
395
|
-
const client = new MCPClient({
|
|
396
|
-
name: 'production-test-client',
|
|
397
|
-
version: '2.1.0',
|
|
398
|
-
timeout: 60000,
|
|
399
|
-
});
|
|
127
|
+
# Short aliases: lt, ct, rr, gp
|
|
128
|
+
npx @slbdn/mcp-tester lt node ./server.js
|
|
400
129
|
```
|
|
401
130
|
|
|
402
|
-
|
|
131
|
+
See [CLI Reference](./docs/cli.md) for all commands and options.
|
|
403
132
|
|
|
404
|
-
|
|
133
|
+
## Speed Up Your Tests with Parallel Execution
|
|
405
134
|
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
**Parameters:**
|
|
409
|
-
|
|
410
|
-
- `config.command` (string): Command to execute the MCP server (e.g., `'node'`, `'python3'`)
|
|
411
|
-
- `config.args` (string[], optional): Arguments to pass to the server command
|
|
412
|
-
- `config.env` (Record<string, string>, optional): Environment variables for the server process
|
|
413
|
-
|
|
414
|
-
**Returns:** `Promise<void>`
|
|
415
|
-
|
|
416
|
-
**Throws:** `Error` if client is already started or server fails to start
|
|
417
|
-
|
|
418
|
-
**Example:**
|
|
135
|
+
MCP Tester is built for concurrency. Fire off multiple tool calls at once — no waiting in line:
|
|
419
136
|
|
|
420
137
|
```typescript
|
|
421
|
-
|
|
422
|
-
command: 'node',
|
|
423
|
-
args: ['./server.js', '--production'],
|
|
424
|
-
env: {
|
|
425
|
-
NODE_ENV: 'production',
|
|
426
|
-
API_KEY: process.env.API_KEY,
|
|
427
|
-
},
|
|
428
|
-
});
|
|
429
|
-
```
|
|
430
|
-
|
|
431
|
-
##### `stop(): Promise<void>`
|
|
138
|
+
import { MCPClient } from '@slbdn/mcp-tester';
|
|
432
139
|
|
|
433
|
-
|
|
140
|
+
const client = new MCPClient();
|
|
141
|
+
await client.start({ command: 'node', args: ['./server.js'] });
|
|
434
142
|
|
|
435
|
-
|
|
143
|
+
// 🔥 Run all calls in parallel — 5x faster than sequential
|
|
144
|
+
const [tools, resources, prompts] = await Promise.all([
|
|
145
|
+
client.listTools(),
|
|
146
|
+
client.listResources(),
|
|
147
|
+
client.listPrompts(),
|
|
148
|
+
]);
|
|
436
149
|
|
|
437
|
-
|
|
150
|
+
// Parallel tool calls? No problem.
|
|
151
|
+
const [echo, sum, env] = await Promise.all([
|
|
152
|
+
client.callTool({ name: 'echo', arguments: { message: 'hello' } }),
|
|
153
|
+
client.callTool({ name: 'get-sum', arguments: { a: 1, b: 2 } }),
|
|
154
|
+
client.callTool({ name: 'get-env', arguments: {} }),
|
|
155
|
+
]);
|
|
438
156
|
|
|
439
|
-
```typescript
|
|
440
157
|
await client.stop();
|
|
441
158
|
```
|
|
442
159
|
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
Check if the client is currently connected to an MCP server.
|
|
446
|
-
|
|
447
|
-
**Returns:** `boolean` - `true` if connected, `false` otherwise
|
|
448
|
-
|
|
449
|
-
**Example:**
|
|
450
|
-
|
|
451
|
-
```typescript
|
|
452
|
-
if (client.isConnected()) {
|
|
453
|
-
console.log('Client is active');
|
|
454
|
-
}
|
|
455
|
-
```
|
|
456
|
-
|
|
457
|
-
##### `listTools(): Promise<Tool[]>`
|
|
458
|
-
|
|
459
|
-
List all available tools from the connected MCP server.
|
|
460
|
-
|
|
461
|
-
**Returns:** `Promise<Tool[]>` - Array of tool objects
|
|
462
|
-
|
|
463
|
-
**Throws:** `Error` if client is not started
|
|
464
|
-
|
|
465
|
-
**Tool Object Properties:**
|
|
466
|
-
- `name` (string): Unique tool identifier
|
|
467
|
-
- `description` (string): Human-readable tool description
|
|
468
|
-
- `inputSchema` (object): JSON Schema for tool parameters
|
|
469
|
-
|
|
470
|
-
**Example:**
|
|
471
|
-
|
|
472
|
-
```typescript
|
|
473
|
-
const tools = await client.listTools();
|
|
474
|
-
tools.forEach(tool => {
|
|
475
|
-
console.log(`- ${tool.name}: ${tool.description}`);
|
|
476
|
-
});
|
|
477
|
-
```
|
|
478
|
-
|
|
479
|
-
##### `callTool(options: ToolCallOptions): Promise<ToolResult>`
|
|
480
|
-
|
|
481
|
-
Call a specific tool with optional arguments.
|
|
482
|
-
|
|
483
|
-
**Parameters:**
|
|
484
|
-
|
|
485
|
-
- `options.name` (string): Name of the tool to call
|
|
486
|
-
- `options.arguments` (Record<string, unknown>, optional): Arguments to pass to the tool
|
|
487
|
-
- `options.timeout` (number, optional): Override default timeout for this call
|
|
488
|
-
|
|
489
|
-
**Returns:** `Promise<ToolResult>` - Result containing content array
|
|
490
|
-
|
|
491
|
-
**Throws:** `Error` if client is not started, tool not found, or call fails
|
|
492
|
-
|
|
493
|
-
**Example:**
|
|
494
|
-
|
|
495
|
-
```typescript
|
|
496
|
-
const result = await client.callTool({
|
|
497
|
-
name: 'calculator-add',
|
|
498
|
-
arguments: { a: 5, b: 3 },
|
|
499
|
-
timeout: 10000,
|
|
500
|
-
});
|
|
501
|
-
|
|
502
|
-
console.log('Result:', result.content[0].text);
|
|
503
|
-
```
|
|
504
|
-
|
|
505
|
-
##### `listResources(): Promise<Resource[]>`
|
|
506
|
-
|
|
507
|
-
List all available resources from the connected MCP server.
|
|
508
|
-
|
|
509
|
-
**Returns:** `Promise<Resource[]>` - Array of resource objects
|
|
510
|
-
|
|
511
|
-
**Throws:** `Error` if client is not started
|
|
512
|
-
|
|
513
|
-
**Resource Object Properties:**
|
|
514
|
-
- `uri` (string): Unique resource identifier
|
|
515
|
-
- `name` (string): Human-readable resource name
|
|
516
|
-
- `description` (string): Resource description
|
|
517
|
-
- `mimeType` (string): Resource MIME type
|
|
518
|
-
|
|
519
|
-
**Example:**
|
|
520
|
-
|
|
521
|
-
```typescript
|
|
522
|
-
const resources = await client.listResources();
|
|
523
|
-
resources.forEach(resource => {
|
|
524
|
-
console.log(`- ${resource.uri}: ${resource.mimeType}`);
|
|
525
|
-
});
|
|
526
|
-
```
|
|
527
|
-
|
|
528
|
-
##### `readResource(uri: string): Promise<ResourceContents>`
|
|
529
|
-
|
|
530
|
-
Read a specific resource by its URI.
|
|
531
|
-
|
|
532
|
-
**Parameters:**
|
|
533
|
-
|
|
534
|
-
- `uri` (string): URI of the resource to read
|
|
535
|
-
|
|
536
|
-
**Returns:** `Promise<ResourceContents>` - Object containing resource contents
|
|
537
|
-
|
|
538
|
-
**Throws:** `Error` if client is not started or resource not found
|
|
539
|
-
|
|
540
|
-
**Example:**
|
|
541
|
-
|
|
542
|
-
```typescript
|
|
543
|
-
const result = await client.readResource('config://settings');
|
|
544
|
-
console.log('Settings:', result.contents[0].text);
|
|
545
|
-
```
|
|
546
|
-
|
|
547
|
-
##### `listPrompts(): Promise<Prompt[]>`
|
|
548
|
-
|
|
549
|
-
List all available prompts from the connected MCP server.
|
|
550
|
-
|
|
551
|
-
**Returns:** `Promise<Prompt[]>` - Array of prompt objects
|
|
552
|
-
|
|
553
|
-
**Throws:** `Error` if client is not started
|
|
554
|
-
|
|
555
|
-
**Prompt Object Properties:**
|
|
556
|
-
- `name` (string): Unique prompt identifier
|
|
557
|
-
- `description` (string): Human-readable prompt description
|
|
558
|
-
- `arguments` (array): Required/optional prompt arguments
|
|
559
|
-
|
|
560
|
-
**Example:**
|
|
561
|
-
|
|
562
|
-
```typescript
|
|
563
|
-
const prompts = await client.listPrompts();
|
|
564
|
-
prompts.forEach(prompt => {
|
|
565
|
-
console.log(`- ${prompt.name}: ${prompt.description}`);
|
|
566
|
-
});
|
|
567
|
-
```
|
|
568
|
-
|
|
569
|
-
##### `getPrompt(name: string, args?: Record<string, string>): Promise<PromptMessage[]>`
|
|
570
|
-
|
|
571
|
-
Get a specific prompt template, optionally with argument values.
|
|
572
|
-
|
|
573
|
-
**Parameters:**
|
|
574
|
-
|
|
575
|
-
- `name` (string): Name of the prompt to retrieve
|
|
576
|
-
- `args` (Record<string, string>, optional): Values for prompt arguments
|
|
577
|
-
|
|
578
|
-
**Returns:** `Promise<PromptMessage[]>` - Array of message objects
|
|
579
|
-
|
|
580
|
-
**Throws:** `Error` if client is not started or prompt not found
|
|
581
|
-
|
|
582
|
-
**Example:**
|
|
583
|
-
|
|
584
|
-
```typescript
|
|
585
|
-
const messages = await client.getPrompt('greet', { name: 'Alice' });
|
|
586
|
-
console.log('Prompt:', messages[0].content.text);
|
|
587
|
-
```
|
|
588
|
-
|
|
589
|
-
##### `requestSampling(request: CreateMessageRequestParams): Promise<SamplingResult>`
|
|
590
|
-
|
|
591
|
-
Request LLM sampling from the MCP server.
|
|
160
|
+
Works because each MCP call is independent — the client multiplexes requests over a single stdio connection.
|
|
161
|
+
No extra config needed. Just `Promise.all` and go.
|
|
592
162
|
|
|
593
|
-
|
|
163
|
+
## Core API
|
|
594
164
|
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
165
|
+
| Method | Returns | Description |
|
|
166
|
+
|--------|---------|-------------|
|
|
167
|
+
| `start(config)` | `Promise<void>` | Connect to an MCP server |
|
|
168
|
+
| `stop()` | `Promise<void>` | Disconnect and clean up |
|
|
169
|
+
| `isConnected()` | `boolean` | Check connection state |
|
|
170
|
+
| `listTools()` | `Promise<Tool[]>` | List available tools |
|
|
171
|
+
| `callTool(options)` | `Promise<CallToolResult>` | Call a tool with arguments |
|
|
172
|
+
| `listResources()` | `Promise<Resource[]>` | List available resources |
|
|
173
|
+
| `readResource(uri)` | `Promise<ReadResourceResult>` | Read a resource by URI |
|
|
174
|
+
| `listPrompts()` | `Promise<Prompt[]>` | List available prompts |
|
|
175
|
+
| `getPrompt(name, args?)` | `Promise<GetPromptResult>` | Get a prompt template |
|
|
176
|
+
| `requestSampling(request)` | `Promise<CreateMessageResult>` | Request LLM sampling |
|
|
177
|
+
| `setElicitationHandler(fn)` | `Promise<void>` | Handle server user-input requests |
|
|
178
|
+
| `setNotificationHandlers(handlers)` | `void` | Handle server notifications |
|
|
599
179
|
|
|
600
|
-
|
|
180
|
+
See [API Reference](./docs/api-reference.md) for full details including types, options, and error classes.
|
|
601
181
|
|
|
602
|
-
|
|
182
|
+
## Custom Jest Matchers
|
|
603
183
|
|
|
604
|
-
|
|
184
|
+
Built-in matchers so your assertions read like English:
|
|
605
185
|
|
|
606
186
|
```typescript
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
type: 'text',
|
|
613
|
-
text: 'Explain quantum computing',
|
|
614
|
-
},
|
|
615
|
-
},
|
|
616
|
-
],
|
|
617
|
-
maxTokens: 500,
|
|
618
|
-
});
|
|
619
|
-
```
|
|
187
|
+
import {
|
|
188
|
+
toHaveTool, toHaveResource, toHavePrompt,
|
|
189
|
+
toReturnText, toReturnError, toReturnJson,
|
|
190
|
+
setupJestMatchers,
|
|
191
|
+
} from '@slbdn/mcp-tester';
|
|
620
192
|
|
|
621
|
-
|
|
193
|
+
beforeAll(() => setupJestMatchers());
|
|
622
194
|
|
|
623
|
-
|
|
195
|
+
// Collection matchers
|
|
196
|
+
expect(tools).toHaveTool('echo');
|
|
197
|
+
expect(resources).toHaveResource('config://settings');
|
|
198
|
+
expect(prompts).toHavePrompt('greet');
|
|
199
|
+
expect(tools).toHaveToolCount(5);
|
|
624
200
|
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
**Returns:** `Promise<void>`
|
|
632
|
-
|
|
633
|
-
**Example:**
|
|
634
|
-
|
|
635
|
-
```typescript
|
|
636
|
-
await client.setElicitationHandler(async (request) => {
|
|
637
|
-
if (request.params.mode === 'form') {
|
|
638
|
-
return {
|
|
639
|
-
action: 'accept',
|
|
640
|
-
content: { userInput: 'User provided data' },
|
|
641
|
-
};
|
|
642
|
-
}
|
|
643
|
-
return { action: 'decline' };
|
|
644
|
-
});
|
|
201
|
+
// Tool result matchers
|
|
202
|
+
expect(result).toReturnText('Hello World');
|
|
203
|
+
expect(result).toReturnTextContaining('hello');
|
|
204
|
+
expect(result).toReturnError();
|
|
205
|
+
expect(result).toReturnJson({ status: 'ok' });
|
|
206
|
+
expect(result).toReturnContentCount(2);
|
|
645
207
|
```
|
|
646
208
|
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
Configure handlers for server-initiated notifications.
|
|
650
|
-
|
|
651
|
-
**Parameters:**
|
|
652
|
-
|
|
653
|
-
- `handlers.onLoggingMessage` (Function): Handle logging notifications
|
|
654
|
-
- `level` (string): Log level (`'debug' | 'info' | 'warning' | 'error'`)
|
|
655
|
-
- `data` (string): Log message content
|
|
656
|
-
|
|
657
|
-
- `handlers.onResourceListChanged` (Function): Handle resource list change notifications
|
|
658
|
-
|
|
659
|
-
**Example:**
|
|
209
|
+
### Vitest Setup
|
|
660
210
|
|
|
661
211
|
```typescript
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
onResourceListChanged: () => {
|
|
667
|
-
console.log('Resources updated, refreshing...');
|
|
668
|
-
},
|
|
669
|
-
});
|
|
670
|
-
```
|
|
671
|
-
|
|
672
|
-
## 🧪 Testing
|
|
673
|
-
|
|
674
|
-
### Running Tests
|
|
675
|
-
|
|
676
|
-
```bash
|
|
677
|
-
# Run all tests
|
|
678
|
-
npm test
|
|
679
|
-
|
|
680
|
-
# Run in watch mode (auto-rerun on changes)
|
|
681
|
-
npm run test:watch
|
|
682
|
-
|
|
683
|
-
# Run with coverage report
|
|
684
|
-
npm run test:coverage
|
|
685
|
-
|
|
686
|
-
# Lint code
|
|
687
|
-
npm run lint
|
|
688
|
-
```
|
|
689
|
-
|
|
690
|
-
### Test Structure
|
|
212
|
+
import { setupVitestMatchers } from '@slbdn/mcp-tester';
|
|
213
|
+
import { beforeAll } from 'vitest';
|
|
214
|
+
// Add vitest.d.ts to your tsconfig for type safety
|
|
215
|
+
// /// <reference types="@slbdn/mcp-tester/vitest" />
|
|
691
216
|
|
|
217
|
+
beforeAll(() => setupVitestMatchers());
|
|
692
218
|
```
|
|
693
|
-
src/__tests__/
|
|
694
|
-
├── client.test.ts # Basic client operations
|
|
695
|
-
├── resources-prompts.test.ts # Resources and prompts functionality
|
|
696
|
-
├── advanced.test.ts # Advanced features (sampling, elicitation)
|
|
697
|
-
├── real-server.test.ts # Integration tests (real server process via stdio)
|
|
698
|
-
├── helpers-example.test.ts # Test helpers usage examples
|
|
699
|
-
├── helpers.ts # Test utility functions
|
|
700
|
-
├── matchers.ts # Custom Jest matchers
|
|
701
|
-
└── fixtures/
|
|
702
|
-
└── mock-server.ts # In-memory mock MCP server for unit testing
|
|
703
|
-
```
|
|
704
|
-
|
|
705
|
-
### Writing Custom Tests
|
|
706
219
|
|
|
707
|
-
|
|
220
|
+
Works identically — same matchers, same `{ pass, message }` format, just `setupVitestMatchers()` instead.
|
|
708
221
|
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
222
|
+
| Matcher | Description |
|
|
223
|
+
|---------|-------------|
|
|
224
|
+
| `toHaveTool(name)` | Assert a tool exists by name |
|
|
225
|
+
| `toHaveToolWithSchema(name)` | Assert a tool exists and has an input schema |
|
|
226
|
+
| `toHaveToolCount(n)` | Assert exact number of tools |
|
|
227
|
+
| `toHaveResource(uri)` | Assert a resource exists by URI |
|
|
228
|
+
| `toHaveResourceByName(name)` | Assert a resource exists by display name |
|
|
229
|
+
| `toHaveResourceCount(n)` | Assert exact number of resources |
|
|
230
|
+
| `toHavePrompt(name)` | Assert a prompt exists by name |
|
|
231
|
+
| `toHavePromptWithArgs(name)` | Assert a prompt exists and has arguments |
|
|
232
|
+
| `toHavePromptCount(n)` | Assert exact number of prompts |
|
|
233
|
+
| `toReturnText(expected?)` | Tool text exact match (or just has text) |
|
|
234
|
+
| `toReturnTextContaining(sub)` | Tool text contains substring |
|
|
235
|
+
| `toReturnError()` | Tool result is an error |
|
|
236
|
+
| `toReturnOk()` | Tool result is successful |
|
|
237
|
+
| `toReturnJson(obj)` | Parse tool text as JSON, deep compare |
|
|
238
|
+
| `toReturnContentCount(n)` | Tool has N content items |
|
|
239
|
+
| `toReturnImage()` | Tool result contains an image |
|
|
240
|
+
| `toReturnResourceText(expected?)` | Resource text exact match (or just has text) |
|
|
241
|
+
| `toReturnResourceTextContaining(sub)` | Resource text contains substring |
|
|
242
|
+
| `toReturnPromptTextContaining(sub)` | Prompt message text contains substring |
|
|
243
|
+
| `toReturnPromptMessageCount(n)` | Prompt has N messages |
|
|
244
|
+
|
|
245
|
+
See [Testing Guide](./docs/testing.md) for full testing patterns.
|
|
246
|
+
|
|
247
|
+
## Assertion Utilities
|
|
248
|
+
|
|
249
|
+
Not using Jest? Running tests with a custom `runTest()` harness? The built-in `assert` module throws descriptive errors on failure — works with any runner:
|
|
250
|
+
|
|
251
|
+
```typescript
|
|
252
|
+
import { MCPClient, assert } from '@slbdn/mcp-tester';
|
|
715
253
|
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
name: 'test-client',
|
|
719
|
-
version: '1.0.0',
|
|
720
|
-
});
|
|
721
|
-
});
|
|
254
|
+
const client = new MCPClient();
|
|
255
|
+
await client.start({ command: 'node', args: ['./server.js'] });
|
|
722
256
|
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
}
|
|
257
|
+
await runTest("Get constant: c (speed of light)", async () => {
|
|
258
|
+
const result = await client.callTool({
|
|
259
|
+
name: "get_constant",
|
|
260
|
+
arguments: { name: "c" },
|
|
727
261
|
});
|
|
262
|
+
assert.toolNumEquals(result, 299792458);
|
|
263
|
+
});
|
|
728
264
|
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
});
|
|
734
|
-
expect(result).toBeDefined();
|
|
265
|
+
await runTest("Echo returns input", async () => {
|
|
266
|
+
const result = await client.callTool({
|
|
267
|
+
name: "echo",
|
|
268
|
+
arguments: { message: "hello" },
|
|
735
269
|
});
|
|
270
|
+
assert.toolTextContains(result, "hello");
|
|
736
271
|
});
|
|
737
272
|
```
|
|
738
273
|
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
274
|
+
| Assertion | Description |
|
|
275
|
+
|-----------|-------------|
|
|
276
|
+
| `equal(a, b)` / `notEqual(a, b)` | Strict equality checks |
|
|
277
|
+
| `deepEqual(a, b)` | JSON deep equality |
|
|
278
|
+
| `ok(val)` / `notOk(val)` | Truthy / falsy |
|
|
279
|
+
| `throws(fn)` / `doesNotThrow(fn)` | Async error checks |
|
|
280
|
+
| `greaterThan(a, b)` / `lessThan(a, b)` / `closeTo(a, b, eps?)` | Numeric comparisons |
|
|
281
|
+
| `contains(str, sub)` / `matches(str, regex)` | String checks |
|
|
282
|
+
| `toolTextEquals(r, str)` | Tool text exact match |
|
|
283
|
+
| `toolTextContains(r, str)` | Tool text contains |
|
|
284
|
+
| `toolNumEquals(r, num)` | Parse tool text as number, compare |
|
|
285
|
+
| `toolNumCloseTo(r, num, eps?)` | Numeric approximate match |
|
|
286
|
+
| `toolJsonEquals(r, obj)` | Parse tool text as JSON, deep compare |
|
|
287
|
+
| `toolIsError(r)` / `toolIsOk(r)` | Error/success check |
|
|
288
|
+
| `toolHasImage(r)` | Check for image content |
|
|
289
|
+
| `resourceTextContains(r, str)` | Resource text contains |
|
|
290
|
+
| `promptTextContains(r, str)` | Prompt text contains |
|
|
291
|
+
|
|
292
|
+
## Documentation
|
|
293
|
+
|
|
294
|
+
| Doc | Description |
|
|
295
|
+
|-----|-------------|
|
|
296
|
+
| [API Reference](./docs/api-reference.md) | Full method docs, types, and error classes |
|
|
297
|
+
| [Testing Guide](./docs/testing.md) | Writing tests, matchers, mock server |
|
|
298
|
+
| [Examples](./docs/examples.md) | Practical code examples |
|
|
299
|
+
| [Advanced Usage](./docs/advanced.md) | Timeouts, retries, concurrency, notifications, logging |
|
|
300
|
+
| [CLI Reference](./docs/cli.md) | CLI commands, options, and output formats |
|
|
301
|
+
| [CI/CD Integration](./docs/cicd.md) | GitHub Actions, CircleCI, Jenkins |
|
|
302
|
+
| [Troubleshooting](./docs/troubleshooting.md) | Common issues and solutions |
|
|
303
|
+
| [Releases](./docs/releases.md) | Release process and commands |
|
|
304
|
+
| [Node.js Compatibility](./docs/nodejs-compatibility.md) | Version matrix and upgrade guide |
|
|
305
|
+
| [AGENTS.md](./AGENTS.md) | Guide for AI agents working with this codebase |
|
|
306
|
+
|
|
307
|
+
## Project Structure
|
|
744
308
|
|
|
745
|
-
```typescript
|
|
746
|
-
it('should handle async operation', async () => {
|
|
747
|
-
const result = await client.listTools(); // ✓ Correct
|
|
748
|
-
// const result = client.listTools(); // ✗ Returns Promise, not result
|
|
749
|
-
});
|
|
750
309
|
```
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
310
|
+
mcp-tester/
|
|
311
|
+
├── src/
|
|
312
|
+
│ ├── client/MCPClient.ts # Main client class
|
|
313
|
+
│ ├── cli/index.ts # CLI tool
|
|
314
|
+
│ ├── assert.ts # Assertion utilities
|
|
315
|
+
│ ├── matchers.ts # Custom Jest matchers
|
|
316
|
+
│ ├── utils/
|
|
317
|
+
│ │ ├── errors.ts # Error classes
|
|
318
|
+
│ │ ├── logger.ts # Logging
|
|
319
|
+
│ │ └── env.ts # Environment utilities
|
|
320
|
+
│ └── __tests__/ # 227 tests
|
|
321
|
+
│ ├── everything-server.test.ts # Integration tests (server-everything)
|
|
322
|
+
│ ├── real-server.test.ts # Integration tests (stdio transport)
|
|
323
|
+
│ ├── cli.test.ts # CLI tool tests
|
|
324
|
+
│ └── fixtures/mock-server.ts # In-memory mock server
|
|
325
|
+
├── examples/
|
|
326
|
+
│ ├── basic-test.ts # Basic usage example
|
|
327
|
+
│ ├── full-test.ts # Full capabilities example
|
|
328
|
+
│ ├── jest-matchers-example.ts # Jest matchers usage
|
|
329
|
+
│ ├── assert-example.ts # Assert utilities usage
|
|
330
|
+
│ ├── everything-server-test.ts # Test against real MCP server
|
|
331
|
+
│ └── mock-server.js # Standalone MCP server
|
|
332
|
+
└── docs/ # Documentation
|
|
762
333
|
```
|
|
763
334
|
|
|
764
|
-
|
|
335
|
+
## Development
|
|
765
336
|
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
).rejects.toThrow();
|
|
773
|
-
});
|
|
337
|
+
```bash
|
|
338
|
+
npm install # Install dependencies
|
|
339
|
+
npm run build # Compile TypeScript
|
|
340
|
+
npm test # Run tests (227 tests)
|
|
341
|
+
npm run lint # Lint code
|
|
342
|
+
npm run format # Format with Prettier
|
|
774
343
|
```
|
|
775
344
|
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
MCP Tester includes custom test helpers and Jest matchers to reduce boilerplate and improve test readability.
|
|
779
|
-
|
|
780
|
-
#### Using Test Helpers
|
|
781
|
-
|
|
782
|
-
Import helper functions to simplify test setup:
|
|
783
|
-
|
|
784
|
-
```typescript
|
|
785
|
-
import { createTestClient, createTestSuite, createMockServerConfig } from './helpers.js';
|
|
786
|
-
import { describe, it, expect, beforeEach, afterEach } from '@jest/globals';
|
|
787
|
-
|
|
788
|
-
describe('My Feature', () => {
|
|
789
|
-
// Use createTestSuite for automatic setup/teardown
|
|
790
|
-
const testSuite = createTestSuite(createMockServerConfig());
|
|
791
|
-
|
|
792
|
-
beforeEach(async () => {
|
|
793
|
-
await testSuite.setup(); // Starts server automatically
|
|
794
|
-
});
|
|
795
|
-
|
|
796
|
-
afterEach(async () => {
|
|
797
|
-
await testSuite.teardown(); // Stops server automatically
|
|
798
|
-
});
|
|
799
|
-
|
|
800
|
-
it('should test feature', async () => {
|
|
801
|
-
const tools = await testSuite.client.listTools();
|
|
802
|
-
expect(tools).toBeDefined();
|
|
803
|
-
});
|
|
804
|
-
});
|
|
805
|
-
```
|
|
806
|
-
|
|
807
|
-
**Available Helper Functions:**
|
|
808
|
-
|
|
809
|
-
| Function | Description |
|
|
810
|
-
|----------|-------------|
|
|
811
|
-
| `createTestClient(options?)` | Creates client with default test options |
|
|
812
|
-
| `createTestSuite(serverConfig, options?)` | Creates test suite with auto setup/teardown |
|
|
813
|
-
| `setupTestServer(config, options?)` | Manual server setup |
|
|
814
|
-
| `teardownTestServer(client)` | Manual server cleanup |
|
|
815
|
-
| `createMockServerConfig(args?)` | Creates mock server configuration |
|
|
816
|
-
| `waitForClientState(client, isConnected, timeout?)` | Waits for client state |
|
|
817
|
-
| `runWithTimeout(fn, timeout?)` | Runs function with timeout |
|
|
818
|
-
| `retryUntil(fn, attempts?, delay?)` | Retries function until success |
|
|
819
|
-
| `callTool(client, toolName, args?)` | Helper for tool calls |
|
|
820
|
-
| `validateToolResult(result)` | Validates tool call result |
|
|
821
|
-
|
|
822
|
-
#### Using Custom Matchers
|
|
823
|
-
|
|
824
|
-
Import custom matchers for improved assertions:
|
|
825
|
-
|
|
826
|
-
```typescript
|
|
827
|
-
import { toHaveTool, toHaveResource, toHavePrompt } from './matchers.js';
|
|
828
|
-
|
|
829
|
-
it('should verify tool exists', async () => {
|
|
830
|
-
const tools = await client.listTools();
|
|
831
|
-
expect(tools).toHaveTool('echo'); // Custom matcher
|
|
832
|
-
});
|
|
833
|
-
|
|
834
|
-
it('should verify resource exists', async () => {
|
|
835
|
-
const resources = await client.listResources();
|
|
836
|
-
expect(resources).toHaveResource('text://example'); // Custom matcher
|
|
837
|
-
});
|
|
838
|
-
|
|
839
|
-
it('should verify prompt exists', async () => {
|
|
840
|
-
const prompts = await client.listPrompts();
|
|
841
|
-
expect(prompts).toHavePrompt('greeting'); // Custom matcher
|
|
842
|
-
});
|
|
843
|
-
```
|
|
844
|
-
|
|
845
|
-
**Available Custom Matchers:**
|
|
846
|
-
|
|
847
|
-
| Matcher | Description |
|
|
848
|
-
|----------|-------------|
|
|
849
|
-
| `toHaveTool(toolName)` | Checks if tool exists in tools array |
|
|
850
|
-
| `toHaveResource(uri)` | Checks if resource exists in resources array |
|
|
851
|
-
| `toHavePrompt(promptName)` | Checks if prompt exists in prompts array |
|
|
852
|
-
| `toHaveToolWithSchema(toolName)` | Checks if tool has input schema |
|
|
853
|
-
|
|
854
|
-
#### Example with Helpers and Matchers
|
|
855
|
-
|
|
856
|
-
```typescript
|
|
857
|
-
import { createTestSuite, createMockServerConfig, callTool } from './helpers.js';
|
|
858
|
-
import { toHaveTool, toHaveResource } from './matchers.js';
|
|
859
|
-
|
|
860
|
-
describe('Complete Example', () => {
|
|
861
|
-
const testSuite = createTestSuite(createMockServerConfig());
|
|
862
|
-
|
|
863
|
-
beforeEach(async () => await testSuite.setup());
|
|
864
|
-
afterEach(async () => await testSuite.teardown());
|
|
865
|
-
|
|
866
|
-
it('should verify tools', async () => {
|
|
867
|
-
const tools = await testSuite.client.listTools();
|
|
868
|
-
expect(tools).toHaveTool('echo');
|
|
869
|
-
});
|
|
870
|
-
|
|
871
|
-
it('should verify resources', async () => {
|
|
872
|
-
const resources = await testSuite.client.listResources();
|
|
873
|
-
expect(resources).toHaveResource('text://example');
|
|
874
|
-
});
|
|
875
|
-
|
|
876
|
-
it('should call tool', async () => {
|
|
877
|
-
const result = await callTool(testSuite.client, 'echo', {
|
|
878
|
-
message: 'hello',
|
|
879
|
-
});
|
|
880
|
-
expect(result).toBeDefined();
|
|
881
|
-
});
|
|
882
|
-
});
|
|
883
|
-
```
|
|
884
|
-
|
|
885
|
-
For more examples, see `src/__tests__/helpers-example.test.ts`.
|
|
886
|
-
|
|
887
|
-
## 💡 Examples
|
|
888
|
-
|
|
889
|
-
### Example 1: Basic Tool Testing
|
|
890
|
-
|
|
891
|
-
```typescript
|
|
892
|
-
import { MCPClient } from 'mcp-tester';
|
|
893
|
-
|
|
894
|
-
async function testTool() {
|
|
895
|
-
const client = new MCPClient();
|
|
896
|
-
|
|
897
|
-
try {
|
|
898
|
-
await client.start({
|
|
899
|
-
command: 'node',
|
|
900
|
-
args: ['./my-server.js'],
|
|
901
|
-
});
|
|
902
|
-
|
|
903
|
-
const tools = await client.listTools();
|
|
904
|
-
console.log(`Found ${tools.length} tools`);
|
|
905
|
-
|
|
906
|
-
for (const tool of tools) {
|
|
907
|
-
console.log(`\nTesting: ${tool.name}`);
|
|
908
|
-
const result = await client.callTool({
|
|
909
|
-
name: tool.name,
|
|
910
|
-
arguments: {},
|
|
911
|
-
});
|
|
912
|
-
console.log(`Result: ${result.content[0]?.text}`);
|
|
913
|
-
}
|
|
914
|
-
|
|
915
|
-
} finally {
|
|
916
|
-
await client.stop();
|
|
917
|
-
}
|
|
918
|
-
}
|
|
919
|
-
|
|
920
|
-
testTool();
|
|
921
|
-
```
|
|
922
|
-
|
|
923
|
-
### Example 2: Resource Reading
|
|
924
|
-
|
|
925
|
-
```typescript
|
|
926
|
-
import { MCPClient } from 'mcp-tester';
|
|
927
|
-
|
|
928
|
-
async function readConfiguration() {
|
|
929
|
-
const client = new MCPClient();
|
|
930
|
-
|
|
931
|
-
try {
|
|
932
|
-
await client.start({
|
|
933
|
-
command: 'node',
|
|
934
|
-
args: ['./config-server.js'],
|
|
935
|
-
});
|
|
936
|
-
|
|
937
|
-
const resources = await client.listResources();
|
|
938
|
-
console.log('Available resources:', resources.map(r => r.uri));
|
|
939
|
-
|
|
940
|
-
const settings = await client.readResource('config://settings');
|
|
941
|
-
console.log('Settings:', JSON.parse(settings.contents[0].text as string));
|
|
942
|
-
|
|
943
|
-
} finally {
|
|
944
|
-
await client.stop();
|
|
945
|
-
}
|
|
946
|
-
}
|
|
947
|
-
|
|
948
|
-
readConfiguration();
|
|
949
|
-
```
|
|
950
|
-
|
|
951
|
-
### Example 3: Using Prompts
|
|
952
|
-
|
|
953
|
-
```typescript
|
|
954
|
-
import { MCPClient } from 'mcp-tester';
|
|
955
|
-
|
|
956
|
-
async function usePrompt() {
|
|
957
|
-
const client = new MCPClient();
|
|
958
|
-
|
|
959
|
-
try {
|
|
960
|
-
await client.start({
|
|
961
|
-
command: 'node',
|
|
962
|
-
args: ['./prompt-server.js'],
|
|
963
|
-
});
|
|
964
|
-
|
|
965
|
-
const prompt = await client.getPrompt('greet', { name: 'World' });
|
|
966
|
-
console.log('Generated message:', prompt.messages[0].content.text);
|
|
967
|
-
|
|
968
|
-
} finally {
|
|
969
|
-
await client.stop();
|
|
970
|
-
}
|
|
971
|
-
}
|
|
972
|
-
|
|
973
|
-
usePrompt();
|
|
974
|
-
```
|
|
975
|
-
|
|
976
|
-
### Example 4: Complete Test Suite
|
|
977
|
-
|
|
978
|
-
```typescript
|
|
979
|
-
import { MCPClient } from 'mcp-tester';
|
|
980
|
-
|
|
981
|
-
describe('My MCP Server', () => {
|
|
982
|
-
let client: MCPClient;
|
|
983
|
-
|
|
984
|
-
beforeEach(async () => {
|
|
985
|
-
client = new MCPClient();
|
|
986
|
-
await client.start({
|
|
987
|
-
command: 'node',
|
|
988
|
-
args: ['./server.js'],
|
|
989
|
-
});
|
|
990
|
-
});
|
|
991
|
-
|
|
992
|
-
afterEach(async () => {
|
|
993
|
-
if (client.isConnected()) {
|
|
994
|
-
await client.stop();
|
|
995
|
-
}
|
|
996
|
-
});
|
|
997
|
-
|
|
998
|
-
it('should list tools correctly', async () => {
|
|
999
|
-
const tools = await client.listTools();
|
|
1000
|
-
expect(tools).toHaveLength(5);
|
|
1001
|
-
});
|
|
1002
|
-
|
|
1003
|
-
it('should execute tool with correct arguments', async () => {
|
|
1004
|
-
const result = await client.callTool({
|
|
1005
|
-
name: 'calculate',
|
|
1006
|
-
arguments: { operation: 'add', values: [10, 20] },
|
|
1007
|
-
});
|
|
1008
|
-
expect(result.content[0].text).toBe('30');
|
|
1009
|
-
});
|
|
1010
|
-
|
|
1011
|
-
it('should handle tool errors gracefully', async () => {
|
|
1012
|
-
await expect(
|
|
1013
|
-
client.callTool({ name: 'invalid-tool', arguments: {} })
|
|
1014
|
-
).rejects.toThrow();
|
|
1015
|
-
});
|
|
1016
|
-
|
|
1017
|
-
it('should read resources correctly', async () => {
|
|
1018
|
-
const resources = await client.listResources();
|
|
1019
|
-
expect(resources.length).toBeGreaterThan(0);
|
|
1020
|
-
});
|
|
1021
|
-
});
|
|
1022
|
-
```
|
|
1023
|
-
|
|
1024
|
-
## 🔄 CI/CD Integration
|
|
1025
|
-
|
|
1026
|
-
### GitHub Actions
|
|
1027
|
-
|
|
1028
|
-
Create `.github/workflows/test.yml`:
|
|
1029
|
-
|
|
1030
|
-
```yaml
|
|
1031
|
-
name: Test MCP Server
|
|
1032
|
-
|
|
1033
|
-
on:
|
|
1034
|
-
push:
|
|
1035
|
-
branches: [main, develop]
|
|
1036
|
-
pull_request:
|
|
1037
|
-
branches: [main, develop]
|
|
1038
|
-
|
|
1039
|
-
jobs:
|
|
1040
|
-
test:
|
|
1041
|
-
runs-on: ubuntu-latest
|
|
1042
|
-
|
|
1043
|
-
strategy:
|
|
1044
|
-
matrix:
|
|
1045
|
-
node-version: [18, 20, 21]
|
|
1046
|
-
|
|
1047
|
-
steps:
|
|
1048
|
-
- name: Checkout code
|
|
1049
|
-
uses: actions/checkout@v3
|
|
1050
|
-
|
|
1051
|
-
- name: Setup Node.js
|
|
1052
|
-
uses: actions/setup-node@v3
|
|
1053
|
-
with:
|
|
1054
|
-
node-version: ${{ matrix.node-version }}
|
|
1055
|
-
|
|
1056
|
-
- name: Install dependencies
|
|
1057
|
-
run: npm ci
|
|
1058
|
-
|
|
1059
|
-
- name: Build TypeScript
|
|
1060
|
-
run: npm run build
|
|
1061
|
-
|
|
1062
|
-
- name: Run tests
|
|
1063
|
-
run: npm test
|
|
1064
|
-
|
|
1065
|
-
- name: Run tests with coverage
|
|
1066
|
-
run: npm run test:coverage
|
|
1067
|
-
|
|
1068
|
-
- name: Upload coverage to Codecov
|
|
1069
|
-
uses: codecov/codecov-action@v3
|
|
1070
|
-
with:
|
|
1071
|
-
files: ./coverage/lcov.info
|
|
1072
|
-
flags: unittests
|
|
1073
|
-
name: codecov-umbrella
|
|
1074
|
-
```
|
|
1075
|
-
|
|
1076
|
-
### CircleCI
|
|
1077
|
-
|
|
1078
|
-
Create `.circleci/config.yml`:
|
|
1079
|
-
|
|
1080
|
-
```yaml
|
|
1081
|
-
version: 2.1
|
|
1082
|
-
jobs:
|
|
1083
|
-
test:
|
|
1084
|
-
docker:
|
|
1085
|
-
- image: cimg/node:18
|
|
1086
|
-
steps:
|
|
1087
|
-
- checkout
|
|
1088
|
-
- run: npm ci
|
|
1089
|
-
- run: npm run build
|
|
1090
|
-
- run: npm test
|
|
1091
|
-
```
|
|
1092
|
-
|
|
1093
|
-
### Jenkins
|
|
1094
|
-
|
|
1095
|
-
Create `Jenkinsfile`:
|
|
1096
|
-
|
|
1097
|
-
```groovy
|
|
1098
|
-
pipeline {
|
|
1099
|
-
agent any
|
|
1100
|
-
stages {
|
|
1101
|
-
stage('Test') {
|
|
1102
|
-
steps {
|
|
1103
|
-
sh 'npm ci'
|
|
1104
|
-
sh 'npm run build'
|
|
1105
|
-
sh 'npm test'
|
|
1106
|
-
}
|
|
1107
|
-
}
|
|
1108
|
-
}
|
|
1109
|
-
}
|
|
1110
|
-
```
|
|
1111
|
-
|
|
1112
|
-
## 🚀 Releases
|
|
1113
|
-
|
|
1114
|
-
### Automated Release Process
|
|
1115
|
-
|
|
1116
|
-
MCP Tester uses automated release workflow triggered by version tags:
|
|
1117
|
-
|
|
1118
|
-
```bash
|
|
1119
|
-
# 1. Update version
|
|
1120
|
-
npm run release:patch # or release:minor, release:major
|
|
1121
|
-
|
|
1122
|
-
# 2. Update CHANGELOG.md
|
|
1123
|
-
# Move items from [Unreleased] to new version section
|
|
1124
|
-
|
|
1125
|
-
# 3. Commit and tag
|
|
1126
|
-
git add package.json CHANGELOG.md
|
|
1127
|
-
git commit -m "chore: release v1.0.1"
|
|
1128
|
-
git tag v1.0.1
|
|
1129
|
-
git push origin main --tags
|
|
1130
|
-
```
|
|
1131
|
-
|
|
1132
|
-
The release workflow automatically:
|
|
1133
|
-
- ✅ Runs all tests across Node.js 18, 20, 21
|
|
1134
|
-
- ✅ Builds the project
|
|
1135
|
-
- ✅ Publishes to npm
|
|
1136
|
-
- ✅ Creates GitHub release with CHANGELOG notes
|
|
1137
|
-
|
|
1138
|
-
### Quick Release Commands
|
|
1139
|
-
|
|
1140
|
-
```bash
|
|
1141
|
-
# Patch release (1.0.0 → 1.0.1)
|
|
1142
|
-
npm run release:patch
|
|
1143
|
-
|
|
1144
|
-
# Minor release (1.0.0 → 1.1.0)
|
|
1145
|
-
npm run release:minor
|
|
1146
|
-
|
|
1147
|
-
# Major release (1.0.0 → 2.0.0)
|
|
1148
|
-
npm run release:major
|
|
1149
|
-
|
|
1150
|
-
# Prerelease (1.0.0 → 1.1.0-alpha.1)
|
|
1151
|
-
npm run release:prerelease
|
|
1152
|
-
```
|
|
1153
|
-
|
|
1154
|
-
### Prerequisites
|
|
1155
|
-
|
|
1156
|
-
Before releasing, ensure:
|
|
1157
|
-
|
|
1158
|
-
1. **NPM Token** is set in GitHub repository secrets as `NPM_TOKEN`
|
|
1159
|
-
2. **CHANGELOG.md** is updated with all changes
|
|
1160
|
-
3. **Tests pass** locally (`npm test`)
|
|
1161
|
-
4. **Build succeeds** (`npm run build`)
|
|
1162
|
-
|
|
1163
|
-
For detailed instructions, see [RELEASE.md](https://github.com/your-org/mcp-tester/blob/main/RELEASE.md).
|
|
1164
|
-
|
|
1165
|
-
## 🚀 Advanced Usage
|
|
1166
|
-
|
|
1167
|
-
### Concurrent Operations
|
|
1168
|
-
|
|
1169
|
-
Run multiple operations in parallel:
|
|
1170
|
-
|
|
1171
|
-
```typescript
|
|
1172
|
-
const results = await Promise.all([
|
|
1173
|
-
client.callTool({ name: 'tool1', arguments: {} }),
|
|
1174
|
-
client.callTool({ name: 'tool2', arguments: {} }),
|
|
1175
|
-
client.callTool({ name: 'tool3', arguments: {} }),
|
|
1176
|
-
]);
|
|
1177
|
-
|
|
1178
|
-
results.forEach((result, index) => {
|
|
1179
|
-
console.log(`Tool ${index + 1}:`, result.content[0]?.text);
|
|
1180
|
-
});
|
|
1181
|
-
```
|
|
1182
|
-
|
|
1183
|
-
### Timeout Handling
|
|
1184
|
-
|
|
1185
|
-
Customize timeouts for specific operations:
|
|
1186
|
-
|
|
1187
|
-
```typescript
|
|
1188
|
-
const result = await client.callTool({
|
|
1189
|
-
name: 'slow-operation',
|
|
1190
|
-
arguments: { duration: 5000 },
|
|
1191
|
-
timeout: 10000, // 10 second timeout for this call only
|
|
1192
|
-
});
|
|
1193
|
-
```
|
|
1194
|
-
|
|
1195
|
-
### Environment Variables
|
|
1196
|
-
|
|
1197
|
-
Pass environment variables to the MCP server:
|
|
1198
|
-
|
|
1199
|
-
```typescript
|
|
1200
|
-
await client.start({
|
|
1201
|
-
command: 'node',
|
|
1202
|
-
args: ['./server.js'],
|
|
1203
|
-
env: {
|
|
1204
|
-
NODE_ENV: 'test',
|
|
1205
|
-
API_ENDPOINT: process.env.API_ENDPOINT || 'http://localhost:3000',
|
|
1206
|
-
LOG_LEVEL: 'debug',
|
|
1207
|
-
},
|
|
1208
|
-
});
|
|
1209
|
-
```
|
|
1210
|
-
|
|
1211
|
-
### Notification Handling
|
|
1212
|
-
|
|
1213
|
-
Monitor server-initiated notifications:
|
|
1214
|
-
|
|
1215
|
-
```typescript
|
|
1216
|
-
client.setNotificationHandlers({
|
|
1217
|
-
onLoggingMessage: (level, data) => {
|
|
1218
|
-
console.log(`[${level}] ${data}`);
|
|
1219
|
-
|
|
1220
|
-
// Store for testing
|
|
1221
|
-
if (level === 'error') {
|
|
1222
|
-
// Handle errors
|
|
1223
|
-
}
|
|
1224
|
-
},
|
|
1225
|
-
onResourceListChanged: async () => {
|
|
1226
|
-
console.log('Resources changed, refreshing...');
|
|
1227
|
-
const resources = await client.listResources();
|
|
1228
|
-
console.log(`Now have ${resources.length} resources`);
|
|
1229
|
-
},
|
|
1230
|
-
});
|
|
1231
|
-
```
|
|
1232
|
-
|
|
1233
|
-
### Elicitation Handling
|
|
1234
|
-
|
|
1235
|
-
Respond to server requests for user input:
|
|
1236
|
-
|
|
1237
|
-
```typescript
|
|
1238
|
-
await client.setElicitationHandler(async (request) => {
|
|
1239
|
-
console.log('Elicitation request:', request.params.mode);
|
|
1240
|
-
|
|
1241
|
-
if (request.params.mode === 'form') {
|
|
1242
|
-
// Validate and collect user input
|
|
1243
|
-
const userInput = {
|
|
1244
|
-
username: 'test-user',
|
|
1245
|
-
email: 'test@example.com',
|
|
1246
|
-
};
|
|
1247
|
-
|
|
1248
|
-
return {
|
|
1249
|
-
action: 'accept',
|
|
1250
|
-
content: userInput,
|
|
1251
|
-
};
|
|
1252
|
-
}
|
|
1253
|
-
|
|
1254
|
-
return { action: 'decline' };
|
|
1255
|
-
});
|
|
1256
|
-
```
|
|
1257
|
-
|
|
1258
|
-
### Mock Server for Unit Testing
|
|
1259
|
-
|
|
1260
|
-
Use the included mock server for tests that don't require a real MCP server:
|
|
1261
|
-
|
|
1262
|
-
```typescript
|
|
1263
|
-
import { MockMCPServer } from './fixtures/mock-server.js';
|
|
1264
|
-
|
|
1265
|
-
describe('My Feature', () => {
|
|
1266
|
-
let mockServer: MockMCPServer;
|
|
1267
|
-
|
|
1268
|
-
beforeEach(() => {
|
|
1269
|
-
mockServer = new MockMCPServer();
|
|
1270
|
-
});
|
|
1271
|
-
|
|
1272
|
-
it('should handle mock tool call', async () => {
|
|
1273
|
-
const result = await mockServer.handleToolCall('echo', {
|
|
1274
|
-
message: 'test',
|
|
1275
|
-
});
|
|
1276
|
-
expect(result.content[0].text).toBe('Echo: test');
|
|
1277
|
-
});
|
|
1278
|
-
|
|
1279
|
-
it('should handle mock resource read', async () => {
|
|
1280
|
-
const result = await mockServer.handleResourceRead('config://settings');
|
|
1281
|
-
expect(result.contents[0].text).toContain('setting1');
|
|
1282
|
-
});
|
|
1283
|
-
|
|
1284
|
-
it('should handle mock prompt', async () => {
|
|
1285
|
-
const result = await mockServer.handlePromptGet('greet', {
|
|
1286
|
-
name: 'Alice',
|
|
1287
|
-
});
|
|
1288
|
-
expect(result.messages[0].content.text).toContain('Alice');
|
|
1289
|
-
});
|
|
1290
|
-
});
|
|
1291
|
-
```
|
|
1292
|
-
|
|
1293
|
-
## 🛠️ Troubleshooting
|
|
1294
|
-
|
|
1295
|
-
### Common Issues
|
|
1296
|
-
|
|
1297
|
-
#### "Client not started" Error
|
|
1298
|
-
|
|
1299
|
-
**Problem:** Calling client methods before `start()`
|
|
1300
|
-
|
|
1301
|
-
**Solution:** Always call `await client.start()` before other methods
|
|
1302
|
-
|
|
1303
|
-
```typescript
|
|
1304
|
-
// ✗ Wrong
|
|
1305
|
-
const client = new MCPClient();
|
|
1306
|
-
const tools = await client.listTools(); // Error: Client not started
|
|
1307
|
-
|
|
1308
|
-
// ✓ Correct
|
|
1309
|
-
const client = new MCPClient();
|
|
1310
|
-
await client.start();
|
|
1311
|
-
const tools = await client.listTools(); // Works
|
|
1312
|
-
```
|
|
1313
|
-
|
|
1314
|
-
#### Timeout Errors
|
|
1315
|
-
|
|
1316
|
-
**Problem:** Requests timing out
|
|
1317
|
-
|
|
1318
|
-
**Solution:** Increase timeout or check server performance
|
|
1319
|
-
|
|
1320
|
-
```typescript
|
|
1321
|
-
// Increase global timeout
|
|
1322
|
-
const client = new MCPClient({ timeout: 60000 });
|
|
1323
|
-
|
|
1324
|
-
// Or per-call timeout
|
|
1325
|
-
await client.callTool({ name: 'slow-tool', arguments: {}, timeout: 30000 });
|
|
1326
|
-
```
|
|
1327
|
-
|
|
1328
|
-
#### Server Process Fails to Start
|
|
1329
|
-
|
|
1330
|
-
**Problem:** Server command not found or fails immediately
|
|
1331
|
-
|
|
1332
|
-
**Solution:** Check command path and ensure server executable exists
|
|
1333
|
-
|
|
1334
|
-
```bash
|
|
1335
|
-
# Verify server exists
|
|
1336
|
-
ls -la ./my-server.js
|
|
1337
|
-
|
|
1338
|
-
# Test server standalone
|
|
1339
|
-
node ./my-server.js
|
|
1340
|
-
```
|
|
1341
|
-
|
|
1342
|
-
#### TypeScript Build Errors
|
|
1343
|
-
|
|
1344
|
-
**Problem:** Type errors after building
|
|
1345
|
-
|
|
1346
|
-
**Solution:** Ensure all dependencies installed and TypeScript version compatible
|
|
1347
|
-
|
|
1348
|
-
```bash
|
|
1349
|
-
# Clean and rebuild
|
|
1350
|
-
rm -rf dist/
|
|
1351
|
-
npm run build
|
|
1352
|
-
```
|
|
1353
|
-
|
|
1354
|
-
#### Test Failures
|
|
1355
|
-
|
|
1356
|
-
**Problem:** Tests failing in CI but passing locally
|
|
1357
|
-
|
|
1358
|
-
**Solution:** Check environment variables and server path
|
|
1359
|
-
|
|
1360
|
-
```yaml
|
|
1361
|
-
# GitHub Actions - add environment variables
|
|
1362
|
-
- name: Run tests
|
|
1363
|
-
env:
|
|
1364
|
-
MOCK_SERVER_PATH: ${{ github.workspace }}/my-server.js
|
|
1365
|
-
run: npm test
|
|
1366
|
-
```
|
|
1367
|
-
|
|
1368
|
-
### Debug Mode
|
|
1369
|
-
|
|
1370
|
-
Enable verbose logging for debugging:
|
|
1371
|
-
|
|
1372
|
-
```typescript
|
|
1373
|
-
import { MCPClient } from 'mcp-tester';
|
|
1374
|
-
|
|
1375
|
-
const client = new MCPClient({
|
|
1376
|
-
name: 'debug-client',
|
|
1377
|
-
});
|
|
1378
|
-
|
|
1379
|
-
await client.start({
|
|
1380
|
-
command: 'node',
|
|
1381
|
-
args: ['./server.js', '--verbose'],
|
|
1382
|
-
});
|
|
1383
|
-
|
|
1384
|
-
// Server logs to stderr, check for detailed output
|
|
1385
|
-
```
|
|
1386
|
-
|
|
1387
|
-
### Test Coverage
|
|
1388
|
-
|
|
1389
|
-
Generate coverage reports:
|
|
1390
|
-
|
|
1391
|
-
```bash
|
|
1392
|
-
# Run tests with coverage
|
|
1393
|
-
npm run test:coverage
|
|
1394
|
-
|
|
1395
|
-
# View coverage report
|
|
1396
|
-
open coverage/lcov-report/index.html
|
|
1397
|
-
```
|
|
1398
|
-
|
|
1399
|
-
## 📊 Project Structure
|
|
1400
|
-
|
|
1401
|
-
```
|
|
1402
|
-
mcp-tester/
|
|
1403
|
-
├── src/
|
|
1404
|
-
│ ├── client/
|
|
1405
|
-
│ │ ├── MCPClient.ts # Main client wrapper class
|
|
1406
|
-
│ │ └── index.ts # Client module exports
|
|
1407
|
-
│ ├── __tests__/
|
|
1408
|
-
│ │ ├── client.test.ts # Basic client operations tests
|
|
1409
|
-
│ │ ├── resources-prompts.test.ts # Resources & prompts tests
|
|
1410
|
-
│ │ ├── advanced.test.ts # Advanced features tests
|
|
1411
|
-
│ │ ├── real-server.test.ts # Integration tests (stdio transport)
|
|
1412
|
-
│ │ ├── helpers-example.test.ts # Test helpers usage examples
|
|
1413
|
-
│ │ ├── helpers.ts # Test utility functions
|
|
1414
|
-
│ │ ├── matchers.ts # Custom Jest matchers
|
|
1415
|
-
│ │ └── fixtures/
|
|
1416
|
-
│ │ └── mock-server.ts # In-memory mock MCP server
|
|
1417
|
-
│ └── index.ts # Library exports
|
|
1418
|
-
├── examples/
|
|
1419
|
-
│ ├── basic-test.ts # Basic usage example
|
|
1420
|
-
│ ├── full-test.ts # Full capabilities example
|
|
1421
|
-
│ └── mock-server.js # Standalone MCP server for testing
|
|
1422
|
-
├── dist/ # Compiled JavaScript output
|
|
1423
|
-
├── package.json
|
|
1424
|
-
├── tsconfig.json
|
|
1425
|
-
├── jest.config.js
|
|
1426
|
-
├── .eslintrc.json
|
|
1427
|
-
├── .github/workflows/
|
|
1428
|
-
│ └── test.yml # CI/CD workflow
|
|
1429
|
-
├── .gitignore
|
|
1430
|
-
├── LICENSE
|
|
1431
|
-
└── README.md
|
|
1432
|
-
```
|
|
1433
|
-
|
|
1434
|
-
## 🔧 Development
|
|
1435
|
-
|
|
1436
|
-
### Build
|
|
1437
|
-
|
|
1438
|
-
```bash
|
|
1439
|
-
# Build TypeScript to JavaScript
|
|
1440
|
-
npm run build
|
|
1441
|
-
```
|
|
1442
|
-
|
|
1443
|
-
### Lint
|
|
1444
|
-
|
|
1445
|
-
```bash
|
|
1446
|
-
# Run ESLint
|
|
1447
|
-
npm run lint
|
|
1448
|
-
|
|
1449
|
-
# Auto-fix issues
|
|
1450
|
-
npm run lint --fix
|
|
1451
|
-
```
|
|
1452
|
-
|
|
1453
|
-
### Watch Mode
|
|
1454
|
-
|
|
1455
|
-
For development with hot reloading:
|
|
1456
|
-
|
|
1457
|
-
```bash
|
|
1458
|
-
# Install tsx for TypeScript execution
|
|
1459
|
-
npm install -D tsx
|
|
1460
|
-
|
|
1461
|
-
# Watch and run tests
|
|
1462
|
-
npx tsx --watch --clear-screen=false
|
|
1463
|
-
```
|
|
1464
|
-
|
|
1465
|
-
## 📈 Test Coverage
|
|
1466
|
-
|
|
1467
|
-
Current test suite: **43 tests**
|
|
1468
|
-
|
|
1469
|
-
```
|
|
1470
|
-
Test Categories:
|
|
1471
|
-
- Basic Operations 3 tests
|
|
1472
|
-
- Tools (in-memory) 11 tests
|
|
1473
|
-
- Resources (in-memory) 5 tests
|
|
1474
|
-
- Prompts (in-memory) 4 tests
|
|
1475
|
-
- Advanced Features 3 tests
|
|
1476
|
-
- Helpers Examples 4 tests
|
|
1477
|
-
- Real Server (stdio) 13 tests
|
|
1478
|
-
```
|
|
1479
|
-
|
|
1480
|
-
Coverage targets:
|
|
1481
|
-
- **Branches:** 70%
|
|
1482
|
-
- **Functions:** 70%
|
|
1483
|
-
- **Lines:** 70%
|
|
1484
|
-
- **Statements:** 70%
|
|
1485
|
-
|
|
1486
|
-
## 🔐 Security
|
|
1487
|
-
|
|
1488
|
-
### Best Practices
|
|
1489
|
-
|
|
1490
|
-
1. **Never Commit Secrets**
|
|
1491
|
-
- Don't commit API keys, passwords, or tokens
|
|
1492
|
-
- Use environment variables in CI/CD
|
|
1493
|
-
- Add sensitive patterns to `.gitignore`
|
|
1494
|
-
|
|
1495
|
-
2. **Validate Input**
|
|
1496
|
-
- Always validate server responses
|
|
1497
|
-
- Use TypeScript for type safety
|
|
1498
|
-
- Sanitize user inputs
|
|
1499
|
-
|
|
1500
|
-
3. **Secure Connections**
|
|
1501
|
-
- Use stdio for local servers
|
|
1502
|
-
- Verify server identity in production
|
|
1503
|
-
- Use HTTPS for remote connections
|
|
1504
|
-
|
|
1505
|
-
4. **Error Handling**
|
|
1506
|
-
- Never expose internal errors to users
|
|
1507
|
-
- Log errors securely
|
|
1508
|
-
- Implement proper error recovery
|
|
1509
|
-
|
|
1510
|
-
### Known Limitations
|
|
1511
|
-
|
|
1512
|
-
- Only supports stdio transport (HTTP transport not implemented)
|
|
1513
|
-
- Designed for Node.js servers (other runtimes may need adjustments)
|
|
1514
|
-
- Mock server is minimal (real servers may have different behavior)
|
|
1515
|
-
|
|
1516
|
-
## 🤝 Contributing
|
|
1517
|
-
|
|
1518
|
-
Contributions are welcome! Please:
|
|
345
|
+
## Contributing
|
|
1519
346
|
|
|
1520
347
|
1. Fork the repository
|
|
1521
348
|
2. Create a feature branch (`git checkout -b feature/amazing-feature`)
|
|
1522
|
-
3.
|
|
1523
|
-
4.
|
|
349
|
+
3. Make changes and test (`npm run build && npm test`)
|
|
350
|
+
4. Format and lint (`npm run format && npm run lint`)
|
|
1524
351
|
5. Open a Pull Request
|
|
1525
352
|
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
```bash
|
|
1529
|
-
# Clone and setup
|
|
1530
|
-
git clone <your-fork>
|
|
1531
|
-
cd mcp-tester
|
|
1532
|
-
npm install
|
|
1533
|
-
|
|
1534
|
-
# Create feature branch
|
|
1535
|
-
git checkout -b feature/my-feature
|
|
1536
|
-
|
|
1537
|
-
# Make changes and test
|
|
1538
|
-
npm run build
|
|
1539
|
-
npm test
|
|
1540
|
-
|
|
1541
|
-
# Format and lint
|
|
1542
|
-
npm run lint --fix
|
|
1543
|
-
```
|
|
1544
|
-
|
|
1545
|
-
## 📚 Additional Resources
|
|
1546
|
-
- [AGENTS.md](./AGENTS.md) - Documentation for AI agents working with this codebase
|
|
1547
|
-
|
|
1548
|
-
- [MCP Specification](https://spec.modelcontextprotocol.io) - Official MCP protocol specification
|
|
1549
|
-
- [MCP TypeScript SDK](https://github.com/modelcontextprotocol/typescript-sdk) - Official SDK documentation
|
|
1550
|
-
- [MCP Examples](https://github.com/modelcontextprotocol/servers) - Example MCP servers
|
|
1551
|
-
- [Jest Documentation](https://jestjs.io/) - Testing framework documentation
|
|
1552
|
-
- [TypeScript Handbook](https://www.typescriptlang.org/docs/) - TypeScript language reference
|
|
1553
|
-
|
|
1554
|
-
## 📞 Support
|
|
1555
|
-
|
|
1556
|
-
- **Issues:** [GitHub Issues](https://github.com/your-org/mcp-tester/issues)
|
|
1557
|
-
- **Discussions:** [GitHub Discussions](https://github.com/your-org/mcp-tester/discussions)
|
|
1558
|
-
- **Documentation:** [Project Wiki](https://github.com/your-org/mcp-tester/wiki)
|
|
1559
|
-
|
|
1560
|
-
## 📝 License
|
|
353
|
+
## License
|
|
1561
354
|
|
|
1562
|
-
MIT
|
|
355
|
+
MIT — see [LICENSE](LICENSE) for details.
|