@jmoak/vibrissa 0.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/LICENSE +21 -0
- package/README.md +315 -0
- package/bin/vib-test.js +2 -0
- package/dist/chunk-FTVETYUK.js +656 -0
- package/dist/chunk-FTVETYUK.js.map +1 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +726 -0
- package/dist/cli.js.map +1 -0
- package/dist/index.d.ts +73 -0
- package/dist/index.js +22 -0
- package/dist/index.js.map +1 -0
- package/package.json +74 -0
- package/schema/case.schema.json +43 -0
- package/schema/vibrissa.schema.json +31 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Jordan Moak
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
## vibrissa (whisker) — JSON‑Driven MCP Integration Test Runner
|
|
2
|
+
|
|
3
|
+
End‑to‑end test your MCP server over stdio using declarative JSON cases. Ship stable, contract‑focused integration suites that humans can review and LLMs can author.
|
|
4
|
+
|
|
5
|
+
### Why Vibrissa?
|
|
6
|
+
- Integration‑first: validates real I/O over MCP stdio
|
|
7
|
+
- JSON cases: simple, diff‑friendly, easy to generate
|
|
8
|
+
- Powerful matching: exact, partial, regex, wildcard, unordered arrays
|
|
9
|
+
- Flexible: CLI (`vib-test`) or library API
|
|
10
|
+
|
|
11
|
+
### Requirements
|
|
12
|
+
- Node.js >= 20
|
|
13
|
+
- ESM-only package (use `import`, not `require`)
|
|
14
|
+
|
|
15
|
+
### Install
|
|
16
|
+
```bash
|
|
17
|
+
npm i -D @jmoak/vibrissa
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
### Quickstart
|
|
21
|
+
```bash
|
|
22
|
+
npm i -D @jmoak/vibrissa
|
|
23
|
+
vib init
|
|
24
|
+
# edit vibrissa.json server block if needed
|
|
25
|
+
vib tools
|
|
26
|
+
vib call echo --arg text=hello --arg uppercase=true
|
|
27
|
+
vib record echo --arg text=hello --arg uppercase=true --name "echo uppercase"
|
|
28
|
+
vib run
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Or scaffold by hand:
|
|
32
|
+
|
|
33
|
+
1) Create `vibrissa.json` in your MCP server repo (root):
|
|
34
|
+
```json
|
|
35
|
+
{
|
|
36
|
+
"$schema": "./node_modules/@jmoak/vibrissa/schema/vibrissa.schema.json",
|
|
37
|
+
"server": { "cmd": "node", "args": ["dist/index.js"] },
|
|
38
|
+
"globs": ["tests/integration/**/*.json"]
|
|
39
|
+
}
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
2) Create a case: `tests/integration/echo.basic.json`
|
|
43
|
+
```json
|
|
44
|
+
{
|
|
45
|
+
"name": "echo basic",
|
|
46
|
+
"tool": "echo",
|
|
47
|
+
"args": {
|
|
48
|
+
"text": "hello",
|
|
49
|
+
"uppercase": true
|
|
50
|
+
},
|
|
51
|
+
"expect": {
|
|
52
|
+
"content": [
|
|
53
|
+
{
|
|
54
|
+
"type": "text",
|
|
55
|
+
"text": "HELLO"
|
|
56
|
+
}
|
|
57
|
+
]
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
3) Run
|
|
63
|
+
```bash
|
|
64
|
+
vib-test run
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Optional: add an NPM script
|
|
68
|
+
```json
|
|
69
|
+
{
|
|
70
|
+
"scripts": {
|
|
71
|
+
"test:integration": "vib-test run"
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### Test Case Semantics
|
|
77
|
+
- Default is exact deep equality.
|
|
78
|
+
- Partial
|
|
79
|
+
```json
|
|
80
|
+
{
|
|
81
|
+
"expect": {
|
|
82
|
+
"$partial": true,
|
|
83
|
+
"content": [
|
|
84
|
+
{
|
|
85
|
+
"type": "text"
|
|
86
|
+
}
|
|
87
|
+
]
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
```
|
|
91
|
+
- Regex
|
|
92
|
+
```json
|
|
93
|
+
{
|
|
94
|
+
"expect": {
|
|
95
|
+
"content": [
|
|
96
|
+
{
|
|
97
|
+
"type": "text",
|
|
98
|
+
"text": {
|
|
99
|
+
"$regex": "^hel+o$",
|
|
100
|
+
"flags": "i"
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
]
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
```
|
|
107
|
+
- Wildcard (glob‑like)
|
|
108
|
+
```json
|
|
109
|
+
{
|
|
110
|
+
"expect": {
|
|
111
|
+
"content": [
|
|
112
|
+
{
|
|
113
|
+
"type": "text",
|
|
114
|
+
"text": {
|
|
115
|
+
"$wildcard": "hel*o"
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
]
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
```
|
|
122
|
+
- Unordered arrays
|
|
123
|
+
```json
|
|
124
|
+
{
|
|
125
|
+
"expect": {
|
|
126
|
+
"content": {
|
|
127
|
+
"$unordered": true,
|
|
128
|
+
"value": [
|
|
129
|
+
{
|
|
130
|
+
"type": "text"
|
|
131
|
+
}
|
|
132
|
+
]
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
```
|
|
137
|
+
- Error expectations
|
|
138
|
+
```json
|
|
139
|
+
{
|
|
140
|
+
"expectError": {
|
|
141
|
+
"code": "InvalidParams",
|
|
142
|
+
"message": {
|
|
143
|
+
"$regex": "Invalid.*echo"
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
Note: `expect` is matched against the **entire** `tools/call` result object. If your server
|
|
150
|
+
returns fields beyond `content` (e.g. `structuredContent`, `_meta`), an exact match will fail
|
|
151
|
+
with an "unexpected key" mismatch — either include those fields in `expect` or set
|
|
152
|
+
`"$partial": true` to assert only the fields you care about. `vib record` captures the full
|
|
153
|
+
result, so recorded cases are always exact-match ready.
|
|
154
|
+
|
|
155
|
+
### CLI — vib / vib-test
|
|
156
|
+
|
|
157
|
+
Subcommands (default is `run` when omitted):
|
|
158
|
+
|
|
159
|
+
```bash
|
|
160
|
+
vib init
|
|
161
|
+
vib tools
|
|
162
|
+
vib call echo --arg text=hello --arg uppercase=true
|
|
163
|
+
vib record echo --arg text=hello --arg uppercase=true --name "echo uppercase"
|
|
164
|
+
vib run
|
|
165
|
+
vib inspect # opens MCP Inspector with the same server config
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
| Command | Purpose |
|
|
169
|
+
|--------|---------|
|
|
170
|
+
| `run` | Execute JSON cases (CI gate) |
|
|
171
|
+
| `tools` | List tools + argument shapes |
|
|
172
|
+
| `call` | One-shot `tools/call`, print result |
|
|
173
|
+
| `record` | One-shot call → write a golden case file |
|
|
174
|
+
| `init` | Scaffold `vibrissa.json` + sample case + npm script |
|
|
175
|
+
| `inspect` | Launch [MCP Inspector](https://modelcontextprotocol.io/docs/tools/inspector) against this server |
|
|
176
|
+
|
|
177
|
+
**Division of labor:** use Inspector for visual exploration and debugging; use Vibrissa `tools` → `call` → `record` → `run` for the authoring/regression loop. `inspect` reuses your `vibrissa.json` server block so you do not re-type command/args/env.
|
|
178
|
+
|
|
179
|
+
Shared options:
|
|
180
|
+
- **--config**: path to your config file (otherwise auto-discovered from the current directory)
|
|
181
|
+
- **--server**: override server command (quoted argv ok), e.g. `--server "node dist/index.js"`
|
|
182
|
+
- **--server-cwd**: working directory for starting the server (overrides `server.cwd`; relative paths resolve from the shell cwd)
|
|
183
|
+
- **--env KEY=VALUE**: extra environment (repeatable)
|
|
184
|
+
- **--timeout**: per-call / spawn timeout in ms
|
|
185
|
+
- **--json**: machine-readable output for `tools` / `call` / `record`
|
|
186
|
+
|
|
187
|
+
`run` options:
|
|
188
|
+
- **--cases**: glob of JSON cases (relative to the shell cwd when passed on the CLI)
|
|
189
|
+
- **--allow-empty**: exit 0 when no cases match (by default, zero matches is a failure)
|
|
190
|
+
- **--fail-fast**, **--pretty** / **--no-pretty**
|
|
191
|
+
|
|
192
|
+
`record` options: `--out`, `--name`, `--partial`, `--force`
|
|
193
|
+
|
|
194
|
+
Notes:
|
|
195
|
+
- Globs in `vibrissa.json` resolve relative to the config file's directory.
|
|
196
|
+
- Relative `server.cwd` values in config also resolve from that directory.
|
|
197
|
+
- Matching zero case files exits `1` unless `--allow-empty` / `allowEmpty: true` is set.
|
|
198
|
+
- Recorded cases include `$schema` pointing at `schema/case.schema.json` for editor validation.
|
|
199
|
+
|
|
200
|
+
### Case Layout (recommended)
|
|
201
|
+
```
|
|
202
|
+
tests/
|
|
203
|
+
integration/
|
|
204
|
+
echo.basic.json
|
|
205
|
+
tools/
|
|
206
|
+
invalid-args.json
|
|
207
|
+
flow.multistep.json
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
### Configuration (vibrissa.json)
|
|
211
|
+
Create a `vibrissa.json` in your project root to avoid repeating flags and enable IDE IntelliSense via `$schema`.
|
|
212
|
+
|
|
213
|
+
```json
|
|
214
|
+
{
|
|
215
|
+
"$schema": "./node_modules/@jmoak/vibrissa/schema/vibrissa.schema.json",
|
|
216
|
+
"server": {
|
|
217
|
+
"cmd": "node",
|
|
218
|
+
"args": ["dist/index.js"],
|
|
219
|
+
"cwd": ".",
|
|
220
|
+
"env": {
|
|
221
|
+
"LOG_LEVEL": "info"
|
|
222
|
+
}
|
|
223
|
+
},
|
|
224
|
+
"globs": [
|
|
225
|
+
"tests/integration/**/*.json"
|
|
226
|
+
],
|
|
227
|
+
"timeoutMs": 15000,
|
|
228
|
+
"failFast": false
|
|
229
|
+
}
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
Then run:
|
|
233
|
+
```bash
|
|
234
|
+
vib-test
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
Notes:
|
|
238
|
+
- `$schema` gives editor validation/autocomplete. Using the local path works immediately after install and offline.
|
|
239
|
+
- CLI flags always override config values.
|
|
240
|
+
|
|
241
|
+
### Library API
|
|
242
|
+
```ts
|
|
243
|
+
import { runCases } from "vibrissa";
|
|
244
|
+
|
|
245
|
+
await runCases({
|
|
246
|
+
server: { cmd: process.execPath, args: ["dist/index.js"], env: {} },
|
|
247
|
+
globs: ["tests/integration/**/*.json"],
|
|
248
|
+
failFast: true,
|
|
249
|
+
timeoutMs: 15000
|
|
250
|
+
});
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
### CI (GitHub Actions)
|
|
254
|
+
```yaml
|
|
255
|
+
name: integration
|
|
256
|
+
on: [push, pull_request]
|
|
257
|
+
jobs:
|
|
258
|
+
test:
|
|
259
|
+
runs-on: ubuntu-latest
|
|
260
|
+
steps:
|
|
261
|
+
- uses: actions/checkout@v4
|
|
262
|
+
- uses: actions/setup-node@v4
|
|
263
|
+
with:
|
|
264
|
+
node-version: 20
|
|
265
|
+
cache: npm
|
|
266
|
+
- run: npm ci
|
|
267
|
+
- run: npm run build
|
|
268
|
+
- run: npx vib-test --config vibrissa.json
|
|
269
|
+
- uses: actions/upload-artifact@v4
|
|
270
|
+
if: always()
|
|
271
|
+
with:
|
|
272
|
+
name: junit
|
|
273
|
+
path: reports/junit/junit.xml
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
### What to Test
|
|
277
|
+
- Tools contract: `listTools`, `tools/call`
|
|
278
|
+
- Arg validation: codes/messages for bad inputs
|
|
279
|
+
- Multi‑step flows: sequences across tools
|
|
280
|
+
- Streaming/long ops: use partial/regex for stability
|
|
281
|
+
- Optional performance thresholds per case
|
|
282
|
+
|
|
283
|
+
### Troubleshooting
|
|
284
|
+
- Hanging cases: increase `--timeout` and inspect server logs
|
|
285
|
+
- Flaky text: prefer `$partial` or `$regex`
|
|
286
|
+
- Globs not matching: run with quotes and verify working directory
|
|
287
|
+
|
|
288
|
+
### License
|
|
289
|
+
MIT
|
|
290
|
+
|
|
291
|
+
### Authoring from tdd-dsl
|
|
292
|
+
|
|
293
|
+
You can author Vibrissa cases from a shared `.tdd` contract and emit JSON with [tdd-dsl](https://github.com/JMoak/tdd-dsl):
|
|
294
|
+
|
|
295
|
+
```text
|
|
296
|
+
suite "Echo MCP contract"
|
|
297
|
+
target vibrissa "echo-server"
|
|
298
|
+
|
|
299
|
+
case "echo basic":
|
|
300
|
+
given input:
|
|
301
|
+
{"text": "hello"}
|
|
302
|
+
when call "echo"
|
|
303
|
+
then equals:
|
|
304
|
+
{"content": [{"type": "text", "text": "hello"}]}
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
```bash
|
|
308
|
+
tdd-dsl emit --target vibrissa --out-dir tests/integration contract.tdd
|
|
309
|
+
vib run
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
Mapping: `when call` → `tool`, `given input` → `args`, `then equals` → `expect`. Prefer recording once with `vib record` so `expect` matches the real MCP `tools/call` envelope.
|
|
313
|
+
|
|
314
|
+
**Layering:** keep Vitest/Jest for unit tests of Vibrissa itself and of MCP handler logic. Use Vibrissa JSON for protocol e2e. Do **not** pull Python / tdd-dsl into Vibrissa CI — emit cases in the contract repo (or regenerate locally) and commit the JSON that `vib run` executes.
|
|
315
|
+
|
package/bin/vib-test.js
ADDED