@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.
- package/CHANGELOG.md +59 -0
- package/README.md +78 -26
- package/dist/ai/provider.d.ts +145 -0
- package/dist/ai/provider.d.ts.map +1 -0
- package/dist/ai/provider.js +428 -0
- package/dist/ai/provider.js.map +1 -0
- package/dist/cli/index.js +214 -14
- package/dist/cli/index.js.map +1 -1
- package/dist/generate-cases.d.ts +113 -0
- package/dist/generate-cases.d.ts.map +1 -0
- package/dist/generate-cases.js +565 -0
- package/dist/generate-cases.js.map +1 -0
- package/dist/generate-tests.d.ts +52 -1
- package/dist/generate-tests.d.ts.map +1 -1
- package/dist/generate-tests.js +208 -81
- package/dist/generate-tests.js.map +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/matchers.d.ts +31 -21
- package/dist/matchers.d.ts.map +1 -1
- package/dist/matchers.js.map +1 -1
- package/dist/templates/full-stack/.github/workflows/test.yml +34 -0
- package/dist/templates/full-stack/README.md +73 -0
- package/dist/templates/full-stack/examples/mock-server.js +115 -0
- package/dist/templates/full-stack/jest.config.js +39 -0
- package/dist/templates/full-stack/package.json +31 -0
- package/dist/templates/full-stack/scripts/generate-tests.ts +32 -0
- package/dist/templates/full-stack/scripts/generate-types.ts +28 -0
- package/dist/templates/full-stack/src/server.ts +78 -0
- package/dist/templates/full-stack/tests/helpers.ts +29 -0
- package/dist/templates/full-stack/tests/parallel.test.ts +44 -0
- package/dist/templates/full-stack/tests/server.test.ts +36 -0
- package/dist/templates/full-stack/tests/tools.test.ts +61 -0
- package/dist/templates/full-stack/tsconfig.json +19 -0
- package/dist/templates/minimal-jest/README.md +42 -0
- package/dist/templates/minimal-jest/examples/mock-server.js +62 -0
- package/dist/templates/minimal-jest/jest.config.js +24 -0
- package/dist/templates/minimal-jest/package.json +24 -0
- package/dist/templates/minimal-jest/tests/server.test.ts +64 -0
- package/dist/templates/minimal-jest/tsconfig.json +19 -0
- package/dist/templates/standard-jest/.github/workflows/test.yml +32 -0
- package/dist/templates/standard-jest/README.md +44 -0
- package/dist/templates/standard-jest/examples/mock-server.js +122 -0
- package/dist/templates/standard-jest/jest.config.js +39 -0
- package/dist/templates/standard-jest/package.json +27 -0
- package/dist/templates/standard-jest/tests/helpers.ts +43 -0
- package/dist/templates/standard-jest/tests/prompts.test.ts +37 -0
- package/dist/templates/standard-jest/tests/resources.test.ts +37 -0
- package/dist/templates/standard-jest/tests/server.test.ts +30 -0
- package/dist/templates/standard-jest/tests/tools.test.ts +87 -0
- package/dist/templates/standard-jest/tsconfig.json +19 -0
- package/docs/advanced.md +424 -0
- package/docs/ai-generation.md +211 -0
- package/docs/api-reference.md +819 -0
- package/docs/cicd.md +289 -0
- package/docs/cli.md +225 -0
- package/docs/examples.md +300 -0
- package/docs/nodejs-compatibility.md +81 -0
- package/docs/releases.md +413 -0
- package/docs/starter-templates.md +110 -0
- package/docs/testing.md +443 -0
- package/docs/troubleshooting.md +139 -0
- package/jest.d.ts +43 -0
- package/package.json +5 -2
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
# AI-Assisted Test Generation
|
|
2
|
+
|
|
3
|
+
`mcp-tester` can derive test cases for your MCP server's tools in three ways:
|
|
4
|
+
|
|
5
|
+
| Mode | Flag | What it does | Needs network? |
|
|
6
|
+
|------|------|--------------|:---:|
|
|
7
|
+
| `static` | *(default)* | One happy-path call per tool | No |
|
|
8
|
+
| `edge` | `--edge-cases` | Deterministic boundary/invalid cases from JSON Schema rules | No |
|
|
9
|
+
| `ai` | `--ai-generate` | Edge cases **plus** AI-suggested cases, validated against the real schemas | Yes (or a local model) |
|
|
10
|
+
|
|
11
|
+
**AI never writes test code.** The model only proposes *structured test-case
|
|
12
|
+
data* (tool, arguments, expectation, rationale). A deterministic renderer emits
|
|
13
|
+
the final TypeScript, and every AI-proposed case is validated against the
|
|
14
|
+
tool's real input schema before it reaches your test file — hallucinated
|
|
15
|
+
arguments are dropped, not compiled.
|
|
16
|
+
|
|
17
|
+
## Quick start
|
|
18
|
+
|
|
19
|
+
### Deterministic edge cases (offline)
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
npx mcp-tester generate node ./server.js --edge-cases -o tests/generated.test.ts
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
The rules engine inspects each tool's `inputSchema` and emits cases per rule:
|
|
26
|
+
|
|
27
|
+
| Rule | Case | Expectation |
|
|
28
|
+
|------|------|-------------|
|
|
29
|
+
| `rule:required` | omit each required property | error |
|
|
30
|
+
| `rule:enum` | value outside the enum | error |
|
|
31
|
+
| `rule:min-length` | string one shorter than `minLength` | error |
|
|
32
|
+
| `rule:max-length` | string one longer than `maxLength` | observe |
|
|
33
|
+
| `rule:bounds` | number one below `minimum` / above `maximum` | error |
|
|
34
|
+
| `rule:type` | wrong JSON type per property | error |
|
|
35
|
+
| `rule:min-items` | array shorter than `minItems` | error |
|
|
36
|
+
| `rule:additional-properties` | unknown key when `additionalProperties: false` | error |
|
|
37
|
+
|
|
38
|
+
Expectations:
|
|
39
|
+
|
|
40
|
+
- **`error`** — the case asserts the call fails (`expect(isToolError(result)).toBe(true)`)
|
|
41
|
+
- **`observe`** — the case calls the tool and logs what happened (no hard
|
|
42
|
+
assertion; for behavior that is legitimately server-defined)
|
|
43
|
+
- **`success`** — the case asserts the call succeeds
|
|
44
|
+
|
|
45
|
+
### AI-suggested cases
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
export MCP_TESTER_AI_API_KEY=sk-...
|
|
49
|
+
npx mcp-tester generate node ./server.js --ai-generate -o tests/generated.test.ts
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
`--ai-generate` includes everything `--edge-cases` produces, then asks the
|
|
53
|
+
model for additional interesting cases (unusual-but-valid inputs, realistic
|
|
54
|
+
payloads, combinations the rules can't reason about). Each suggestion is
|
|
55
|
+
checked against the tool's schema:
|
|
56
|
+
|
|
57
|
+
```text
|
|
58
|
+
mcp-tester: AI suggested cases: 5, kept after schema validation: 4 (dropped 1)
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Works with any OpenAI-compatible endpoint:
|
|
62
|
+
|
|
63
|
+
| Provider | Base URL | Key needed |
|
|
64
|
+
|----------|----------|:---:|
|
|
65
|
+
| OpenAI | `https://api.openai.com/v1` (default) | yes |
|
|
66
|
+
| Ollama | `http://localhost:11434/v1` | no |
|
|
67
|
+
| LM Studio | `http://localhost:1234/v1` | no |
|
|
68
|
+
| vLLM | `http://host:8000/v1` | optional |
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
# Ollama — no API key required for localhost
|
|
72
|
+
npx mcp-tester generate node ./server.js \
|
|
73
|
+
--ai-generate --ai-base-url http://localhost:11434/v1 --ai-model llama3
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### Flags
|
|
77
|
+
|
|
78
|
+
| Flag | Meaning |
|
|
79
|
+
|------|---------|
|
|
80
|
+
| `--edge-cases` | Add deterministic schema-derived cases (offline) |
|
|
81
|
+
| `--ai-generate` | Edge cases + AI suggestions (implies `--edge-cases`) |
|
|
82
|
+
| `--ai-model <model>` | Model name (default `gpt-4o-mini` or `MCP_TESTER_AI_MODEL`) |
|
|
83
|
+
| `--ai-base-url <url>` | OpenAI-compatible base URL (default `MCP_TESTER_AI_BASE_URL`) |
|
|
84
|
+
| `--require-ai` | Fail instead of falling back when AI is unavailable |
|
|
85
|
+
| `--verify` | Dry-run every derived case against the live server first |
|
|
86
|
+
|
|
87
|
+
Passing both `--edge-cases` and `--ai-generate` is an error — `--ai-generate`
|
|
88
|
+
already includes the edge cases.
|
|
89
|
+
|
|
90
|
+
## Verifying derived cases against the real server
|
|
91
|
+
|
|
92
|
+
Derived expectations are *predictions*. `--verify` dry-runs every case against
|
|
93
|
+
the live server before writing the file:
|
|
94
|
+
|
|
95
|
+
```bash
|
|
96
|
+
npx mcp-tester generate node ./server.js --edge-cases --verify -o tests/generated.test.ts
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
- Cases whose prediction **holds** stay as normal assertions.
|
|
100
|
+
- Cases where the server **disagrees** (e.g. the rules predicted an error but
|
|
101
|
+
the server returned success) are emitted as `it.skip` blocks with a comment:
|
|
102
|
+
|
|
103
|
+
```typescript
|
|
104
|
+
// rule:required — "message" is required; the server should reject its absence
|
|
105
|
+
// verify: expected error but server returned success — adjust manually
|
|
106
|
+
it.skip('rejects call without required "message"', async () => { ... });
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
That skip is a signal, not a failure: either the server is too permissive (fix
|
|
110
|
+
the server — many MCP servers forget to validate `missing required property`)
|
|
111
|
+
or the expectation was wrong (edit the test). Either way you've learned
|
|
112
|
+
something concrete about your server.
|
|
113
|
+
|
|
114
|
+
## Environment variables
|
|
115
|
+
|
|
116
|
+
| Variable | Purpose | Default |
|
|
117
|
+
|----------|---------|---------|
|
|
118
|
+
| `MCP_TESTER_AI_API_KEY` | API key for hosted endpoints | *(none)* |
|
|
119
|
+
| `MCP_TESTER_AI_BASE_URL` | OpenAI-compatible endpoint | `https://api.openai.com/v1` |
|
|
120
|
+
| `MCP_TESTER_AI_MODEL` | Model name | `gpt-4o-mini` |
|
|
121
|
+
|
|
122
|
+
A base URL pointing at `localhost`/`127.0.0.1`/`[::1]` is considered
|
|
123
|
+
configured **without** an API key (local model servers don't need auth).
|
|
124
|
+
|
|
125
|
+
Without configuration, `--ai-generate` prints a warning and falls back to the
|
|
126
|
+
deterministic edge cases. With `--require-ai`, it fails instead.
|
|
127
|
+
|
|
128
|
+
## Library API
|
|
129
|
+
|
|
130
|
+
```typescript
|
|
131
|
+
import {
|
|
132
|
+
generateTests,
|
|
133
|
+
generateTestsFromClient,
|
|
134
|
+
OpenAICompatProvider,
|
|
135
|
+
suggestEdgeCasesForTools,
|
|
136
|
+
} from '@slbdn/mcp-tester';
|
|
137
|
+
|
|
138
|
+
// Deterministic cases only
|
|
139
|
+
await generateTests({ command: 'node', args: ['./server.js'], mode: 'edge' });
|
|
140
|
+
|
|
141
|
+
// AI-assisted with an explicit provider (no env vars needed)
|
|
142
|
+
await generateTests({
|
|
143
|
+
command: 'node',
|
|
144
|
+
args: ['./server.js'],
|
|
145
|
+
mode: 'ai',
|
|
146
|
+
provider: new OpenAICompatProvider({
|
|
147
|
+
baseUrl: 'http://localhost:11434/v1',
|
|
148
|
+
model: 'llama3',
|
|
149
|
+
}),
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
// Rules only, no file emission — inspect the cases yourself
|
|
153
|
+
const cases = suggestEdgeCasesForTools(tools);
|
|
154
|
+
// → [{ tool, title, args, expectation, rationale, source }, ...]
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
A `GeneratedCase` is plain data — safe to log, diff, and version-control:
|
|
158
|
+
|
|
159
|
+
```typescript
|
|
160
|
+
interface GeneratedCase {
|
|
161
|
+
tool: string; // tool name
|
|
162
|
+
title: string; // human-readable test title
|
|
163
|
+
args: Record<string, unknown>; // arguments to send
|
|
164
|
+
expectation: 'success' | 'error' | 'observe';
|
|
165
|
+
rationale: string; // why this case exists
|
|
166
|
+
source: `rule:${string}` | `ai:${string}`; // provenance, e.g. rule:required
|
|
167
|
+
}
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
Custom providers implement one method:
|
|
171
|
+
|
|
172
|
+
```typescript
|
|
173
|
+
interface AIProvider {
|
|
174
|
+
name: string;
|
|
175
|
+
suggestCases(analysis: ServerAnalysis): Promise<GeneratedCase[]>;
|
|
176
|
+
}
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
`OpenAICompatProvider` accepts `fetchImpl` for testing and custom HTTP
|
|
180
|
+
behavior, caches responses in `.mcp-tester-cache/` (set `cacheDir: null` to
|
|
181
|
+
disable), truncates long tool descriptions, and batches at most 40 tools per
|
|
182
|
+
request.
|
|
183
|
+
|
|
184
|
+
### Provider options reference
|
|
185
|
+
|
|
186
|
+
| Option | Default | Effect |
|
|
187
|
+
|---|---|---|
|
|
188
|
+
| `apiKey` | *(unset)* | Bearer token. Optional for local gateways (Ollama, LM Studio). |
|
|
189
|
+
| `baseUrl` | `https://api.openai.com/v1` | Chat-completions endpoint. |
|
|
190
|
+
| `model` | `gpt-4o-mini` | Model name. |
|
|
191
|
+
| `timeout` | `60000` | Per-request timeout in ms. |
|
|
192
|
+
| `maxTokens` | `4096` | Cap on completion tokens. |
|
|
193
|
+
| `cacheDir` | `.mcp-tester-cache` | Where to write cache files. `null` disables caching. |
|
|
194
|
+
| `cacheMaxAgeMs` | *(unset)* | When set, cache files older than this are ignored and re-fetched. Useful when iterating on a model. |
|
|
195
|
+
| `strictJson` | `true` | Sends `response_format: { type: 'json_object' }`. Set to `false` for gateways that reject it (Ollama, older vLLM). The provider auto-falls-back on HTTP 400 mentioning `response_format` regardless. |
|
|
196
|
+
| `fetchImpl` | `globalThis.fetch` | Injectable fetch for tests and hermetic CI. |
|
|
197
|
+
|
|
198
|
+
Cache key includes the model name, the `serverLabel`, and the tool set
|
|
199
|
+
(sorted by name) — so changing any of them invalidates the entry.
|
|
200
|
+
The cache file is JSON: `{ "model", "serverLabel", "cases": [...] }`.
|
|
201
|
+
|
|
202
|
+
## Safety and cost notes
|
|
203
|
+
|
|
204
|
+
- **No code from the model.** AI output is parsed as JSON case data and
|
|
205
|
+
validated; it can never become executable test logic.
|
|
206
|
+
- **Prompt-injection resistant.** Tool descriptions are untrusted input; at
|
|
207
|
+
worst they influence *which cases are proposed* — still schema-validated.
|
|
208
|
+
- **Caching.** Identical analyses hit the on-disk cache, so re-running
|
|
209
|
+
generation is free and deterministic.
|
|
210
|
+
- **Deterministic fallback.** No key, no quota, no network — you still get the
|
|
211
|
+
full rules engine.
|