@slbdn/mcp-tester 1.4.3 → 1.5.4

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.
Files changed (66) hide show
  1. package/CHANGELOG.md +59 -0
  2. package/README.md +78 -26
  3. package/dist/ai/provider.d.ts +145 -0
  4. package/dist/ai/provider.d.ts.map +1 -0
  5. package/dist/ai/provider.js +428 -0
  6. package/dist/ai/provider.js.map +1 -0
  7. package/dist/cli/index.js +214 -14
  8. package/dist/cli/index.js.map +1 -1
  9. package/dist/generate-cases.d.ts +113 -0
  10. package/dist/generate-cases.d.ts.map +1 -0
  11. package/dist/generate-cases.js +565 -0
  12. package/dist/generate-cases.js.map +1 -0
  13. package/dist/generate-tests.d.ts +52 -1
  14. package/dist/generate-tests.d.ts.map +1 -1
  15. package/dist/generate-tests.js +208 -81
  16. package/dist/generate-tests.js.map +1 -1
  17. package/dist/index.d.ts +3 -1
  18. package/dist/index.d.ts.map +1 -1
  19. package/dist/index.js +2 -0
  20. package/dist/index.js.map +1 -1
  21. package/dist/matchers.d.ts +31 -21
  22. package/dist/matchers.d.ts.map +1 -1
  23. package/dist/matchers.js.map +1 -1
  24. package/dist/templates/full-stack/.github/workflows/test.yml +34 -0
  25. package/dist/templates/full-stack/README.md +73 -0
  26. package/dist/templates/full-stack/examples/mock-server.js +115 -0
  27. package/dist/templates/full-stack/jest.config.js +39 -0
  28. package/dist/templates/full-stack/package.json +31 -0
  29. package/dist/templates/full-stack/scripts/generate-tests.ts +32 -0
  30. package/dist/templates/full-stack/scripts/generate-types.ts +28 -0
  31. package/dist/templates/full-stack/src/server.ts +78 -0
  32. package/dist/templates/full-stack/tests/helpers.ts +29 -0
  33. package/dist/templates/full-stack/tests/parallel.test.ts +44 -0
  34. package/dist/templates/full-stack/tests/server.test.ts +36 -0
  35. package/dist/templates/full-stack/tests/tools.test.ts +61 -0
  36. package/dist/templates/full-stack/tsconfig.json +19 -0
  37. package/dist/templates/minimal-jest/README.md +42 -0
  38. package/dist/templates/minimal-jest/examples/mock-server.js +62 -0
  39. package/dist/templates/minimal-jest/jest.config.js +24 -0
  40. package/dist/templates/minimal-jest/package.json +24 -0
  41. package/dist/templates/minimal-jest/tests/server.test.ts +64 -0
  42. package/dist/templates/minimal-jest/tsconfig.json +19 -0
  43. package/dist/templates/standard-jest/.github/workflows/test.yml +32 -0
  44. package/dist/templates/standard-jest/README.md +44 -0
  45. package/dist/templates/standard-jest/examples/mock-server.js +122 -0
  46. package/dist/templates/standard-jest/jest.config.js +39 -0
  47. package/dist/templates/standard-jest/package.json +27 -0
  48. package/dist/templates/standard-jest/tests/helpers.ts +43 -0
  49. package/dist/templates/standard-jest/tests/prompts.test.ts +37 -0
  50. package/dist/templates/standard-jest/tests/resources.test.ts +37 -0
  51. package/dist/templates/standard-jest/tests/server.test.ts +30 -0
  52. package/dist/templates/standard-jest/tests/tools.test.ts +87 -0
  53. package/dist/templates/standard-jest/tsconfig.json +19 -0
  54. package/docs/advanced.md +424 -0
  55. package/docs/ai-generation.md +211 -0
  56. package/docs/api-reference.md +819 -0
  57. package/docs/cicd.md +289 -0
  58. package/docs/cli.md +225 -0
  59. package/docs/examples.md +300 -0
  60. package/docs/nodejs-compatibility.md +81 -0
  61. package/docs/releases.md +413 -0
  62. package/docs/starter-templates.md +110 -0
  63. package/docs/testing.md +443 -0
  64. package/docs/troubleshooting.md +139 -0
  65. package/jest.d.ts +43 -0
  66. package/package.json +5 -2
@@ -0,0 +1,61 @@
1
+ /**
2
+ * Tool tests for the full-stack server.
3
+ */
4
+
5
+ import { afterAll, beforeAll, describe, expect, it } from '@jest/globals';
6
+ import type { MCPClient } from '@slbdn/mcp-tester';
7
+ import { contentText, createConnectedClient } from './helpers.js';
8
+
9
+ describe('Tools', () => {
10
+ let client: MCPClient;
11
+
12
+ beforeAll(async () => {
13
+ client = await createConnectedClient('tools-test');
14
+ });
15
+
16
+ afterAll(async () => {
17
+ if (client?.isConnected()) {
18
+ await client.stop();
19
+ }
20
+ });
21
+
22
+ describe('greet', () => {
23
+ it('greets by name', async () => {
24
+ const result = await client.callTool({ name: 'greet', arguments: { name: 'Alice' } });
25
+ expect(contentText(result)).toBe('Hello, Alice!');
26
+ });
27
+ });
28
+
29
+ describe('sum', () => {
30
+ it('sums an array of numbers', async () => {
31
+ const result = await client.callTool({
32
+ name: 'sum',
33
+ arguments: { numbers: [1, 2, 3, 4] },
34
+ });
35
+ expect(contentText(result)).toBe('10');
36
+ });
37
+
38
+ it('returns 0 for an empty array', async () => {
39
+ const result = await client.callTool({ name: 'sum', arguments: { numbers: [] } });
40
+ expect(contentText(result)).toBe('0');
41
+ });
42
+ });
43
+
44
+ describe('uppercase', () => {
45
+ it('uppercases the input', async () => {
46
+ const result = await client.callTool({
47
+ name: 'uppercase',
48
+ arguments: { text: 'hello' },
49
+ });
50
+ expect(contentText(result)).toBe('HELLO');
51
+ });
52
+
53
+ it('preserves non-letter characters', async () => {
54
+ const result = await client.callTool({
55
+ name: 'uppercase',
56
+ arguments: { text: 'abc 123!' },
57
+ });
58
+ expect(contentText(result)).toBe('ABC 123!');
59
+ });
60
+ });
61
+ });
@@ -0,0 +1,19 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "module": "ES2022",
5
+ "moduleResolution": "node",
6
+ "esModuleInterop": true,
7
+ "strict": true,
8
+ "skipLibCheck": true,
9
+ "forceConsistentCasingInFileNames": true,
10
+ "resolveJsonModule": true,
11
+ "outDir": "./dist",
12
+ "rootDir": "./",
13
+ "declaration": true,
14
+ "sourceMap": true,
15
+ "allowSyntheticDefaultImports": true
16
+ },
17
+ "include": ["src/**/*.ts", "tests/**/*.ts", "scripts/**/*.ts", "examples/**/*.js"],
18
+ "exclude": ["node_modules", "dist"]
19
+ }
@@ -0,0 +1,42 @@
1
+ # MCP Test Minimal — Starter Template
2
+
3
+ The smallest working project that uses [`@slbdn/mcp-tester`](https://www.npmjs.com/package/@slbdn/mcp-tester) to test an MCP server.
4
+
5
+ ## What's included
6
+ - A mock MCP server (`examples/mock-server.js`) with two tools: `echo` and `add`.
7
+ - One Jest test file (`tests/server.test.ts`) that connects, lists, and calls.
8
+ - TypeScript + `ts-jest` ESM configuration.
9
+ - A `package.json` with everything you need.
10
+
11
+ ## Quick start
12
+
13
+ ```bash
14
+ npm install
15
+ npm test
16
+ ```
17
+
18
+ You should see something like:
19
+
20
+ ```
21
+ PASS tests/server.test.ts
22
+ Mock MCP server
23
+ ✓ should connect to the server
24
+ ✓ should list at least one tool
25
+ ✓ should echo a message
26
+ ✓ should add two numbers
27
+ ```
28
+
29
+ ## Point this at your own server
30
+
31
+ Replace `examples/mock-server.js` with your server (or update `tests/server.test.ts` to point at a real one):
32
+
33
+ ```ts
34
+ await client.start({
35
+ command: 'node',
36
+ args: ['./path/to/your-server.js'],
37
+ });
38
+ ```
39
+
40
+ ## Next steps
41
+ - **Standard tests**: see `templates/standard-jest/` for tools/resources/prompts suites, fixtures, and CI.
42
+ - **Generation**: see `templates/full-stack/` for `generateTests` / `generateTypes` integration.
@@ -0,0 +1,62 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Minimal mock MCP server for testing.
5
+ *
6
+ * Tools:
7
+ * - echo(message): echoes back the input message
8
+ * - add(a, b): returns the sum of two numbers
9
+ */
10
+
11
+ import { Server } from '@modelcontextprotocol/sdk/server/index.js';
12
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
13
+ import { CallToolRequestSchema, ListToolsRequestSchema } from '@modelcontextprotocol/sdk/types.js';
14
+
15
+ const server = new Server(
16
+ { name: 'mock-mcp-server', version: '1.0.0' },
17
+ { capabilities: { tools: {} } }
18
+ );
19
+
20
+ server.setRequestHandler(ListToolsRequestSchema, async () => ({
21
+ tools: [
22
+ {
23
+ name: 'echo',
24
+ description: 'Echo back the input message',
25
+ inputSchema: {
26
+ type: 'object',
27
+ properties: { message: { type: 'string', description: 'Message to echo' } },
28
+ required: ['message'],
29
+ },
30
+ },
31
+ {
32
+ name: 'add',
33
+ description: 'Add two numbers',
34
+ inputSchema: {
35
+ type: 'object',
36
+ properties: {
37
+ a: { type: 'number', description: 'First number' },
38
+ b: { type: 'number', description: 'Second number' },
39
+ },
40
+ required: ['a', 'b'],
41
+ },
42
+ },
43
+ ],
44
+ }));
45
+
46
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
47
+ const { name, arguments: args } = request.params;
48
+
49
+ switch (name) {
50
+ case 'echo':
51
+ return { content: [{ type: 'text', text: String(args.message) }] };
52
+ case 'add': {
53
+ const sum = Number(args.a) + Number(args.b);
54
+ return { content: [{ type: 'text', text: String(sum) }] };
55
+ }
56
+ default:
57
+ throw new Error(`Unknown tool: ${name}`);
58
+ }
59
+ });
60
+
61
+ const transport = new StdioServerTransport();
62
+ await server.connect(transport);
@@ -0,0 +1,24 @@
1
+ /** @type {import('jest').Config} */
2
+ export default {
3
+ preset: 'ts-jest/presets/default-esm',
4
+ testEnvironment: 'node',
5
+ extensionsToTreatAsEsm: ['.ts'],
6
+ moduleNameMapper: {
7
+ '^(\\.{1,2}/.*)\\.js$': '$1',
8
+ },
9
+ transform: {
10
+ '^.+\\.tsx?$': [
11
+ 'ts-jest',
12
+ {
13
+ useESM: true,
14
+ tsconfig: {
15
+ esModuleInterop: true,
16
+ allowSyntheticDefaultImports: true,
17
+ },
18
+ },
19
+ ],
20
+ },
21
+ testMatch: ['**/tests/**/*.test.ts'],
22
+ testTimeout: 30000,
23
+ maxWorkers: 1,
24
+ };
@@ -0,0 +1,24 @@
1
+ {
2
+ "name": "__NAME__",
3
+ "version": "0.1.0",
4
+ "private": true,
5
+ "type": "module",
6
+ "description": "Minimal MCP server test scaffold using @slbdn/mcp-tester + Jest",
7
+ "scripts": {
8
+ "test": "node --experimental-vm-modules node_modules/jest/bin/jest.js"
9
+ },
10
+ "engines": {
11
+ "node": ">=20"
12
+ },
13
+ "dependencies": {
14
+ "@slbdn/mcp-tester": "^1.4.3",
15
+ "@modelcontextprotocol/sdk": "^1.29.0"
16
+ },
17
+ "devDependencies": {
18
+ "@types/jest": "^30.0.0",
19
+ "@types/node": "^20.0.0",
20
+ "jest": "^30.5.2",
21
+ "ts-jest": "^29.4.11",
22
+ "typescript": "^5.3.0"
23
+ }
24
+ }
@@ -0,0 +1,64 @@
1
+ /**
2
+ * Minimal MCP server test scaffold.
3
+ *
4
+ * Demonstrates:
5
+ * - Starting a stdio MCP server
6
+ * - Listing tools
7
+ * - Calling a tool
8
+ * - Cleanup
9
+ */
10
+
11
+ import { MCPClient, setupJestMatchers } from '@slbdn/mcp-tester';
12
+ import { fileURLToPath } from 'node:url';
13
+ import { dirname, join } from 'node:path';
14
+
15
+ const here = dirname(fileURLToPath(import.meta.url));
16
+ const serverPath = join(here, '..', 'examples', 'mock-server.js');
17
+
18
+ describe('Mock MCP server', () => {
19
+ let client: MCPClient;
20
+
21
+ beforeAll(async () => {
22
+ setupJestMatchers();
23
+ client = new MCPClient({
24
+ name: 'minimal-test',
25
+ version: '0.1.0',
26
+ timeout: 10000,
27
+ });
28
+ await client.start({
29
+ command: 'node',
30
+ args: [serverPath],
31
+ });
32
+ });
33
+
34
+ afterAll(async () => {
35
+ if (client?.isConnected()) {
36
+ await client.stop();
37
+ }
38
+ });
39
+
40
+ it('should connect to the server', () => {
41
+ expect(client.isConnected()).toBe(true);
42
+ });
43
+
44
+ it('should list at least one tool', async () => {
45
+ const tools = await client.listTools();
46
+ expect(tools.length).toBeGreaterThan(0);
47
+ });
48
+
49
+ it('should echo a message', async () => {
50
+ const result = await client.callTool({
51
+ name: 'echo',
52
+ arguments: { message: 'hello' },
53
+ });
54
+ expect(result).toReturnText('hello');
55
+ });
56
+
57
+ it('should add two numbers', async () => {
58
+ const result = await client.callTool({
59
+ name: 'add',
60
+ arguments: { a: 2, b: 3 },
61
+ });
62
+ expect(result).toReturnText('5');
63
+ });
64
+ });
@@ -0,0 +1,19 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "module": "ES2022",
5
+ "moduleResolution": "node",
6
+ "esModuleInterop": true,
7
+ "strict": true,
8
+ "skipLibCheck": true,
9
+ "forceConsistentCasingInFileNames": true,
10
+ "resolveJsonModule": true,
11
+ "outDir": "./dist",
12
+ "rootDir": "./",
13
+ "declaration": true,
14
+ "sourceMap": true,
15
+ "allowSyntheticDefaultImports": true
16
+ },
17
+ "include": ["tests/**/*.ts", "examples/**/*.js"],
18
+ "exclude": ["node_modules", "dist"]
19
+ }
@@ -0,0 +1,32 @@
1
+ name: Test
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+ branches: [main]
8
+
9
+ jobs:
10
+ test:
11
+ runs-on: ubuntu-latest
12
+ strategy:
13
+ matrix:
14
+ node-version: [20, 22]
15
+ steps:
16
+ - uses: actions/checkout@v5
17
+ - name: Setup Node.js ${{ matrix.node-version }}
18
+ uses: actions/setup-node@v5
19
+ with:
20
+ node-version: ${{ matrix.node-version }}
21
+ cache: 'npm'
22
+ - name: Install
23
+ run: npm ci
24
+ - name: Test
25
+ run: npm test
26
+ - name: Upload HTML report
27
+ if: always()
28
+ uses: actions/upload-artifact@v4
29
+ with:
30
+ name: test-report-${{ matrix.node-version }}
31
+ path: reports/
32
+ retention-days: 14
@@ -0,0 +1,44 @@
1
+ # MCP Test Standard — Starter Template
2
+
3
+ A "blessed" layout for testing MCP servers with [`@slbdn/mcp-tester`](https://www.npmjs.com/package/@slbdn/mcp-tester). Splits tests by capability, ships an HTML reporter, includes a CI workflow, and demonstrates richer mocking patterns.
4
+
5
+ ## What's included
6
+
7
+ ```
8
+ standard-jest/
9
+ ├── examples/
10
+ │ └── mock-server.js # Stdio mock server: echo, add, slow, version resource, greet prompt
11
+ ├── tests/
12
+ │ ├── helpers.ts # Shared client setup
13
+ │ ├── server.test.ts # Lifecycle and health
14
+ │ ├── tools.test.ts # Tool listing, schemas, calls (incl. timeout)
15
+ │ ├── resources.test.ts # Resource listing and reads
16
+ │ └── prompts.test.ts # Prompt listing and gets
17
+ ├── .github/workflows/
18
+ │ └── test.yml # CI matrix (Node 20, 22) + HTML report upload
19
+ ├── jest.config.js
20
+ ├── tsconfig.json
21
+ └── package.json
22
+ ```
23
+
24
+ ## Quick start
25
+
26
+ ```bash
27
+ npm install
28
+ npm test
29
+ ```
30
+
31
+ Open `reports/test-report.html` for the visual report.
32
+
33
+ ## What's different from `minimal-jest`
34
+ - **Test split**: separate files per capability for clearer failure attribution.
35
+ - **HTML reporter**: jest-html-reporters preconfigured.
36
+ - **Coverage**: `npm run test:coverage` runs with text/lcov reporters.
37
+ - **CI workflow**: GitHub Actions matrix on Node 20 and 22.
38
+ - **Timeout testing**: demonstrates `timeout` per-call option.
39
+
40
+ ## Point this at your own server
41
+ Replace `examples/mock-server.js` with your server, or update `tests/helpers.ts` to point at it.
42
+
43
+ ## Next steps
44
+ - **Full stack**: see `templates/full-stack/` for code generation (`generateTests` / `generateTypes`) and typed tool calls.
@@ -0,0 +1,122 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Standard mock MCP server — used by all test files in this template.
5
+ *
6
+ * Tools:
7
+ * - echo(message): returns the input message
8
+ * - add(a, b): returns the sum
9
+ * - slow(ms): waits N ms then returns (good for timeout tests)
10
+ *
11
+ * Resources:
12
+ * - config://version: returns "1.0.0"
13
+ *
14
+ * Prompts:
15
+ * - greet(user): returns a greeting
16
+ */
17
+
18
+ import { Server } from '@modelcontextprotocol/sdk/server/index.js';
19
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
20
+ import {
21
+ CallToolRequestSchema,
22
+ ListToolsRequestSchema,
23
+ ListResourcesRequestSchema,
24
+ ReadResourceRequestSchema,
25
+ ListPromptsRequestSchema,
26
+ GetPromptRequestSchema,
27
+ } from '@modelcontextprotocol/sdk/types.js';
28
+
29
+ const server = new Server(
30
+ { name: 'standard-mock-server', version: '1.0.0' },
31
+ { capabilities: { tools: {}, resources: {}, prompts: {} } }
32
+ );
33
+
34
+ server.setRequestHandler(ListToolsRequestSchema, async () => ({
35
+ tools: [
36
+ {
37
+ name: 'echo',
38
+ description: 'Echo back the input message',
39
+ inputSchema: {
40
+ type: 'object',
41
+ properties: { message: { type: 'string' } },
42
+ required: ['message'],
43
+ },
44
+ },
45
+ {
46
+ name: 'add',
47
+ description: 'Add two numbers',
48
+ inputSchema: {
49
+ type: 'object',
50
+ properties: {
51
+ a: { type: 'number' },
52
+ b: { type: 'number' },
53
+ },
54
+ required: ['a', 'b'],
55
+ },
56
+ },
57
+ {
58
+ name: 'slow',
59
+ description: 'A slow tool for testing timeouts',
60
+ inputSchema: {
61
+ type: 'object',
62
+ properties: { ms: { type: 'number', default: 100 } },
63
+ },
64
+ },
65
+ ],
66
+ }));
67
+
68
+ server.setRequestHandler(ListResourcesRequestSchema, async () => ({
69
+ resources: [
70
+ {
71
+ uri: 'config://version',
72
+ name: 'version',
73
+ description: 'Server version',
74
+ mimeType: 'text/plain',
75
+ },
76
+ ],
77
+ }));
78
+
79
+ server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
80
+ if (request.params.uri === 'config://version') {
81
+ return { contents: [{ uri: 'config://version', mimeType: 'text/plain', text: '1.0.0' }] };
82
+ }
83
+ throw new Error(`Unknown resource: ${request.params.uri}`);
84
+ });
85
+
86
+ server.setRequestHandler(ListPromptsRequestSchema, async () => ({
87
+ prompts: [
88
+ {
89
+ name: 'greet',
90
+ description: 'Greet a user by name',
91
+ arguments: [{ name: 'user', description: 'Name', required: true }],
92
+ },
93
+ ],
94
+ }));
95
+
96
+ server.setRequestHandler(GetPromptRequestSchema, async (request) => {
97
+ if (request.params.name === 'greet') {
98
+ const user = String(request.params.arguments?.user ?? 'world');
99
+ return {
100
+ messages: [{ role: 'assistant', content: { type: 'text', text: `Hello, ${user}!` } }],
101
+ };
102
+ }
103
+ throw new Error(`Unknown prompt: ${request.params.name}`);
104
+ });
105
+
106
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
107
+ const { name, arguments: args } = request.params;
108
+ switch (name) {
109
+ case 'echo':
110
+ return { content: [{ type: 'text', text: String(args.message) }] };
111
+ case 'add':
112
+ return { content: [{ type: 'text', text: String(Number(args.a) + Number(args.b)) }] };
113
+ case 'slow':
114
+ await new Promise((r) => setTimeout(r, Number(args.ms ?? 100)));
115
+ return { content: [{ type: 'text', text: `waited ${args.ms ?? 100}ms` }] };
116
+ default:
117
+ throw new Error(`Unknown tool: ${name}`);
118
+ }
119
+ });
120
+
121
+ const transport = new StdioServerTransport();
122
+ await server.connect(transport);
@@ -0,0 +1,39 @@
1
+ /** @type {import('jest').Config} */
2
+ export default {
3
+ preset: 'ts-jest/presets/default-esm',
4
+ testEnvironment: 'node',
5
+ cacheDirectory: '/tmp/mcp-tester-cache',
6
+ extensionsToTreatAsEsm: ['.ts'],
7
+ moduleNameMapper: {
8
+ '^(\\.{1,2}/.*)\\.js$': '$1',
9
+ },
10
+ transform: {
11
+ '^.+\\.tsx?$': [
12
+ 'ts-jest',
13
+ {
14
+ useESM: true,
15
+ tsconfig: {
16
+ esModuleInterop: true,
17
+ allowSyntheticDefaultImports: true,
18
+ },
19
+ },
20
+ ],
21
+ },
22
+ testMatch: ['**/tests/**/*.test.ts'],
23
+ testTimeout: 30000,
24
+ maxWorkers: 1,
25
+ reporters: [
26
+ 'default',
27
+ [
28
+ 'jest-html-reporters',
29
+ {
30
+ publicPath: './reports',
31
+ filename: 'test-report.html',
32
+ expand: true,
33
+ pageTitle: 'MCP Test Report',
34
+ hideIcon: true,
35
+ },
36
+ ],
37
+ ],
38
+ coverageReporters: ['text', 'text-summary', 'lcov'],
39
+ };
@@ -0,0 +1,27 @@
1
+ {
2
+ "name": "__NAME__",
3
+ "version": "0.1.0",
4
+ "private": true,
5
+ "type": "module",
6
+ "description": "Standard MCP server test scaffold using @slbdn/mcp-tester + Jest (tools, resources, prompts, fixtures, CI)",
7
+ "scripts": {
8
+ "test": "node --experimental-vm-modules node_modules/jest/bin/jest.js",
9
+ "test:coverage": "node --experimental-vm-modules node_modules/jest/bin/jest.js --coverage",
10
+ "test:report": "node --experimental-vm-modules node_modules/jest/bin/jest.js --reporters=default --reporters=jest-html-reporters"
11
+ },
12
+ "engines": {
13
+ "node": ">=20"
14
+ },
15
+ "dependencies": {
16
+ "@slbdn/mcp-tester": "^1.4.3",
17
+ "@modelcontextprotocol/sdk": "^1.29.0"
18
+ },
19
+ "devDependencies": {
20
+ "@types/jest": "^30.0.0",
21
+ "@types/node": "^20.0.0",
22
+ "jest": "^30.5.2",
23
+ "jest-html-reporters": "^3.0.11",
24
+ "ts-jest": "^29.4.11",
25
+ "typescript": "^5.3.0"
26
+ }
27
+ }
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Shared test helpers: path resolution and per-suite client lifecycle.
3
+ */
4
+
5
+ import { MCPClient } from '@slbdn/mcp-tester';
6
+ import { fileURLToPath } from 'node:url';
7
+ import { dirname, join } from 'node:path';
8
+
9
+ const here = dirname(fileURLToPath(import.meta.url));
10
+ export const serverPath = join(here, '..', 'examples', 'mock-server.js');
11
+
12
+ /** The result type of {@link MCPClient.callTool}. */
13
+ type ToolCallResult = Awaited<ReturnType<MCPClient['callTool']>>;
14
+
15
+ /** Extract the text of the first content item (empty string if not text). */
16
+ export function contentText(result: ToolCallResult): string {
17
+ const first = result.content[0];
18
+ return first && first.type === 'text' ? first.text : '';
19
+ }
20
+
21
+ /** Extract the text of the first prompt message (empty string if not text). */
22
+ export function promptText(result: Awaited<ReturnType<MCPClient['getPrompt']>>): string {
23
+ const first = result.messages[0];
24
+ const content = first?.content;
25
+ return content && content.type === 'text' ? content.text : '';
26
+ }
27
+
28
+ /** Extract the text of the first resource contents entry (empty string if not text). */
29
+ export function resourceText(result: Awaited<ReturnType<MCPClient['readResource']>>): string {
30
+ const first = result.contents[0];
31
+ return first && 'text' in first ? (first.text as string) : '';
32
+ }
33
+
34
+ /** Create a connected MCPClient pointed at the standard mock server. */
35
+ export async function createConnectedClient(name = 'standard-test'): Promise<MCPClient> {
36
+ const client = new MCPClient({
37
+ name,
38
+ version: '0.1.0',
39
+ timeout: 10000,
40
+ });
41
+ await client.start({ command: 'node', args: [serverPath] });
42
+ return client;
43
+ }
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Prompt tests.
3
+ */
4
+
5
+ import { afterAll, beforeAll, describe, expect, it } from '@jest/globals';
6
+ import type { MCPClient } from '@slbdn/mcp-tester';
7
+ import { contentText, createConnectedClient, promptText, resourceText } from './helpers.js';
8
+
9
+ describe('Prompts', () => {
10
+ let client: MCPClient;
11
+
12
+ beforeAll(async () => {
13
+ client = await createConnectedClient('prompts-test');
14
+ });
15
+
16
+ afterAll(async () => {
17
+ if (client?.isConnected()) {
18
+ await client.stop();
19
+ }
20
+ });
21
+
22
+ it('lists available prompts', async () => {
23
+ const prompts = await client.listPrompts();
24
+ expect(prompts.map((p) => p.name)).toContain('greet');
25
+ });
26
+
27
+ it('gets the greet prompt with arguments', async () => {
28
+ const result = await client.getPrompt('greet', { user: 'Alice' });
29
+ expect(result.messages.length).toBeGreaterThan(0);
30
+ const text = promptText(result);
31
+ expect(text).toContain('Alice');
32
+ });
33
+
34
+ it('rejects unknown prompts', async () => {
35
+ await expect(client.getPrompt('unknown-prompt')).rejects.toThrow();
36
+ });
37
+ });