@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,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.