@jmoak/vibrissa 0.1.0 → 2.0.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.
@@ -1,31 +1,547 @@
1
1
  {
2
2
  "$schema": "https://json-schema.org/draft/2020-12/schema",
3
- "$id": "https://unpkg.com/vibrissa/schema/vibrissa.schema.json",
3
+ "$id": "https://unpkg.com/@jmoak/vibrissa/schema/vibrissa.schema.json",
4
4
  "title": "Vibrissa Config",
5
+ "description": "Vibrissa config (vibrissa.json, vibrissa.jsonc, or package.json#vibrissa). Strings support ${ENV_VAR} and ${ENV_VAR:-fallback} interpolation. Precedence: defaults, then this file, then the selected profile, then CLI flags.",
5
6
  "type": "object",
6
7
  "additionalProperties": false,
7
8
  "properties": {
8
- "$schema": { "type": "string" },
9
+ "$schema": {
10
+ "type": "string",
11
+ "description": "Path or URL of this schema (for editor completion)."
12
+ },
9
13
  "server": {
14
+ "$ref": "#/$defs/server",
15
+ "description": "v1 shorthand for servers.default."
16
+ },
17
+ "servers": {
18
+ "type": "object",
19
+ "additionalProperties": {
20
+ "$ref": "#/$defs/server"
21
+ },
22
+ "description": "Named servers under test. Cases pick one with \"server\"; otherwise defaultServer is used."
23
+ },
24
+ "defaultServer": {
25
+ "type": "string",
26
+ "description": "Key into servers used when a case names no server. Default: \"default\", or the only server."
27
+ },
28
+ "globs": {
29
+ "description": "Case file globs relative to the config file. Prefix with ! to exclude. Default [\"tests/integration/**/*.json\"].",
30
+ "oneOf": [
31
+ {
32
+ "type": "string"
33
+ },
34
+ {
35
+ "type": "array",
36
+ "items": {
37
+ "type": "string"
38
+ }
39
+ }
40
+ ]
41
+ },
42
+ "manifest": {
43
+ "type": "string",
44
+ "description": "Manifest lock file path. Default \"tests/integration/__manifest__.json\"."
45
+ },
46
+ "manifestIgnore": {
47
+ "type": "array",
48
+ "items": {
49
+ "type": "string"
50
+ },
51
+ "description": "Dotted manifest paths ignored when diffing against the lock (e.g. \"tools.*.description\"). Default [\"server.version\", \"protocolVersion\", \"listMeta\"]: release and protocol-era noise, so one lock serves both eras. `vib manifest diff --strict` compares everything."
52
+ },
53
+ "requireManifest": {
54
+ "type": "boolean",
55
+ "description": "Fail the run when a server's manifest lock file is missing (instead of warning). Turn it on in CI so an unrecorded surface cannot pass silently. Default false."
56
+ },
57
+ "outDir": {
58
+ "type": "string",
59
+ "description": "Directory for reporter artifacts (junit.xml, results.json, traces/), relative to the config file. Default \"reports\"."
60
+ },
61
+ "checks": {
62
+ "$ref": "#/$defs/checks"
63
+ },
64
+ "normalize": {
65
+ "$ref": "#/$defs/normalize"
66
+ },
67
+ "timeoutMs": {
68
+ "type": "integer",
69
+ "minimum": 0,
70
+ "description": "Per-request timeout in ms. Default 15000."
71
+ },
72
+ "budgetMs": {
73
+ "type": "integer",
74
+ "minimum": 0,
75
+ "description": "Default latency budget per case/step in ms; slower fails."
76
+ },
77
+ "failFast": {
78
+ "type": "boolean",
79
+ "description": "Stop at the first failing case. Default false."
80
+ },
81
+ "allowEmpty": {
82
+ "type": "boolean",
83
+ "description": "If true, matching zero case files exits 0. Default false (fail loud)."
84
+ },
85
+ "reporters": {
86
+ "description": "Reporters to run: console, json, junit, github, tap. Default [\"console\"].",
87
+ "oneOf": [
88
+ {
89
+ "type": "string"
90
+ },
91
+ {
92
+ "type": "array",
93
+ "items": {
94
+ "type": "string"
95
+ }
96
+ }
97
+ ]
98
+ },
99
+ "tags": {
100
+ "type": "array",
101
+ "items": {
102
+ "type": "string"
103
+ },
104
+ "description": "Run only cases carrying at least one of these tags."
105
+ },
106
+ "excludeTags": {
107
+ "type": "array",
108
+ "items": {
109
+ "type": "string"
110
+ },
111
+ "description": "Skip cases carrying any of these tags."
112
+ },
113
+ "grep": {
114
+ "type": "string",
115
+ "description": "Run only cases whose name matches this regular expression."
116
+ },
117
+ "repeat": {
118
+ "type": "integer",
119
+ "minimum": 1,
120
+ "description": "Run every case N times and report pass rates (flake detection)."
121
+ },
122
+ "shard": {
123
+ "$ref": "#/$defs/shard"
124
+ },
125
+ "concurrency": {
126
+ "type": "integer",
127
+ "minimum": 1,
128
+ "description": "Parallel workers (one server session each). Default 1."
129
+ },
130
+ "update": {
131
+ "type": "boolean",
132
+ "description": "Rewrite failing goldens from live output (like jest -u)."
133
+ },
134
+ "trace": {
135
+ "type": "boolean",
136
+ "description": "Write JSON-RPC transcripts for every case, not only failures."
137
+ },
138
+ "check": {
139
+ "type": "boolean",
140
+ "description": "Also run spec-sanity checks and description lint."
141
+ },
142
+ "baseline": {
143
+ "type": "string",
144
+ "description": "Previous results file; fail only on regressions against it."
145
+ },
146
+ "hooks": {
147
+ "$ref": "#/$defs/hooks"
148
+ },
149
+ "profiles": {
150
+ "type": "object",
151
+ "description": "Named overlays merged over the base config with --profile <name>.",
152
+ "additionalProperties": {
153
+ "type": "object",
154
+ "description": "A profile: any config key except profiles; servers entries patch the base by name.",
155
+ "additionalProperties": false,
156
+ "properties": {
157
+ "server": {
158
+ "$ref": "#/$defs/serverPatch",
159
+ "description": "Override for servers.default (v1 sugar)."
160
+ },
161
+ "servers": {
162
+ "type": "object",
163
+ "additionalProperties": {
164
+ "$ref": "#/$defs/serverPatch"
165
+ },
166
+ "description": "Per-server overrides, merged into the base servers by name."
167
+ },
168
+ "defaultServer": {
169
+ "type": "string",
170
+ "description": "Key into servers used when a case names no server. Default: \"default\", or the only server."
171
+ },
172
+ "globs": {
173
+ "description": "Case file globs relative to the config file. Prefix with ! to exclude. Default [\"tests/integration/**/*.json\"].",
174
+ "oneOf": [
175
+ {
176
+ "type": "string"
177
+ },
178
+ {
179
+ "type": "array",
180
+ "items": {
181
+ "type": "string"
182
+ }
183
+ }
184
+ ]
185
+ },
186
+ "manifest": {
187
+ "type": "string",
188
+ "description": "Manifest lock file path. Default \"tests/integration/__manifest__.json\"."
189
+ },
190
+ "manifestIgnore": {
191
+ "type": "array",
192
+ "items": {
193
+ "type": "string"
194
+ },
195
+ "description": "Dotted manifest paths ignored when diffing against the lock (e.g. \"tools.*.description\"). Default [\"server.version\", \"protocolVersion\", \"listMeta\"]: release and protocol-era noise, so one lock serves both eras. `vib manifest diff --strict` compares everything."
196
+ },
197
+ "requireManifest": {
198
+ "type": "boolean",
199
+ "description": "Fail the run when a server's manifest lock file is missing (instead of warning). Turn it on in CI so an unrecorded surface cannot pass silently. Default false."
200
+ },
201
+ "outDir": {
202
+ "type": "string",
203
+ "description": "Directory for reporter artifacts (junit.xml, results.json, traces/), relative to the config file. Default \"reports\"."
204
+ },
205
+ "checks": {
206
+ "$ref": "#/$defs/checks"
207
+ },
208
+ "normalize": {
209
+ "$ref": "#/$defs/normalize"
210
+ },
211
+ "timeoutMs": {
212
+ "type": "integer",
213
+ "minimum": 0,
214
+ "description": "Per-request timeout in ms. Default 15000."
215
+ },
216
+ "budgetMs": {
217
+ "type": "integer",
218
+ "minimum": 0,
219
+ "description": "Default latency budget per case/step in ms; slower fails."
220
+ },
221
+ "failFast": {
222
+ "type": "boolean",
223
+ "description": "Stop at the first failing case. Default false."
224
+ },
225
+ "allowEmpty": {
226
+ "type": "boolean",
227
+ "description": "If true, matching zero case files exits 0. Default false (fail loud)."
228
+ },
229
+ "reporters": {
230
+ "description": "Reporters to run: console, json, junit, github, tap. Default [\"console\"].",
231
+ "oneOf": [
232
+ {
233
+ "type": "string"
234
+ },
235
+ {
236
+ "type": "array",
237
+ "items": {
238
+ "type": "string"
239
+ }
240
+ }
241
+ ]
242
+ },
243
+ "tags": {
244
+ "type": "array",
245
+ "items": {
246
+ "type": "string"
247
+ },
248
+ "description": "Run only cases carrying at least one of these tags."
249
+ },
250
+ "excludeTags": {
251
+ "type": "array",
252
+ "items": {
253
+ "type": "string"
254
+ },
255
+ "description": "Skip cases carrying any of these tags."
256
+ },
257
+ "grep": {
258
+ "type": "string",
259
+ "description": "Run only cases whose name matches this regular expression."
260
+ },
261
+ "repeat": {
262
+ "type": "integer",
263
+ "minimum": 1,
264
+ "description": "Run every case N times and report pass rates (flake detection)."
265
+ },
266
+ "shard": {
267
+ "$ref": "#/$defs/shard"
268
+ },
269
+ "concurrency": {
270
+ "type": "integer",
271
+ "minimum": 1,
272
+ "description": "Parallel workers (one server session each). Default 1."
273
+ },
274
+ "update": {
275
+ "type": "boolean",
276
+ "description": "Rewrite failing goldens from live output (like jest -u)."
277
+ },
278
+ "trace": {
279
+ "type": "boolean",
280
+ "description": "Write JSON-RPC transcripts for every case, not only failures."
281
+ },
282
+ "check": {
283
+ "type": "boolean",
284
+ "description": "Also run spec-sanity checks and description lint."
285
+ },
286
+ "baseline": {
287
+ "type": "string",
288
+ "description": "Previous results file; fail only on regressions against it."
289
+ },
290
+ "hooks": {
291
+ "$ref": "#/$defs/hooks"
292
+ }
293
+ }
294
+ }
295
+ }
296
+ },
297
+ "$defs": {
298
+ "protocol": {
299
+ "description": "Protocol era negotiation. \"auto\" (default) probes server/discover and falls back to the 2025 initialize handshake; \"legacy\" forces the 2025 handshake; \"modern\" requires the 2026-07-28 era; a revision string or { \"pin\": \"<revision>\" } requires exactly that revision.",
300
+ "oneOf": [
301
+ {
302
+ "type": "string",
303
+ "enum": ["auto", "legacy", "modern"]
304
+ },
305
+ {
306
+ "type": "string",
307
+ "pattern": "^\\d{4}-\\d{2}-\\d{2}$",
308
+ "description": "Pin to this revision, e.g. \"2026-07-28\"."
309
+ },
310
+ {
311
+ "type": "object",
312
+ "additionalProperties": false,
313
+ "required": ["pin"],
314
+ "properties": {
315
+ "pin": {
316
+ "type": "string",
317
+ "description": "Exact protocol revision to require, e.g. \"2026-07-28\"."
318
+ }
319
+ }
320
+ }
321
+ ]
322
+ },
323
+ "stdioServer": {
10
324
  "type": "object",
325
+ "description": "A server spawned as a child process and spoken to over stdio.",
11
326
  "additionalProperties": false,
12
327
  "required": ["cmd"],
13
328
  "properties": {
14
- "cmd": { "type": "string" },
15
- "args": { "type": "array", "items": { "type": "string" } },
16
- "cwd": { "type": "string" },
329
+ "cmd": {
330
+ "type": "string",
331
+ "minLength": 1,
332
+ "description": "Executable to spawn (stdio transport), e.g. \"node\"."
333
+ },
334
+ "args": {
335
+ "type": "array",
336
+ "items": {
337
+ "type": "string"
338
+ },
339
+ "description": "Arguments passed to cmd."
340
+ },
341
+ "cwd": {
342
+ "type": "string",
343
+ "description": "Working directory for the server process, relative to the config file."
344
+ },
17
345
  "env": {
18
346
  "type": "object",
19
- "additionalProperties": { "type": "string" }
347
+ "additionalProperties": {
348
+ "type": "string"
349
+ },
350
+ "description": "Extra environment variables for the server process (merged over the current environment)."
351
+ },
352
+ "protocol": {
353
+ "$ref": "#/$defs/protocol"
354
+ },
355
+ "spawnTimeoutMs": {
356
+ "type": "integer",
357
+ "minimum": 0,
358
+ "description": "Max time (ms) to spawn/connect and finish the handshake. Default: timeoutMs clamped to 2000..10000."
20
359
  }
21
360
  }
22
361
  },
23
- "globs": { "type": "array", "items": { "type": "string" } },
24
- "timeoutMs": { "type": "integer", "minimum": 0 },
25
- "failFast": { "type": "boolean" },
26
- "allowEmpty": {
27
- "type": "boolean",
28
- "description": "If true, matching zero case files exits 0. Default false (fail loud)."
362
+ "httpServer": {
363
+ "type": "object",
364
+ "description": "A server reached over the Streamable HTTP transport.",
365
+ "additionalProperties": false,
366
+ "required": ["url"],
367
+ "properties": {
368
+ "url": {
369
+ "type": "string",
370
+ "pattern": "^(https?://|\\$\\{)",
371
+ "description": "Streamable HTTP endpoint, e.g. \"https://mcp.example.com/mcp\" (may use ${ENV} interpolation)."
372
+ },
373
+ "headers": {
374
+ "type": "object",
375
+ "additionalProperties": {
376
+ "type": "string"
377
+ },
378
+ "description": "Static HTTP headers sent on every request, e.g. { \"Authorization\": \"Bearer ${MCP_TOKEN}\" }."
379
+ },
380
+ "protocol": {
381
+ "$ref": "#/$defs/protocol"
382
+ },
383
+ "spawnTimeoutMs": {
384
+ "type": "integer",
385
+ "minimum": 0,
386
+ "description": "Max time (ms) to spawn/connect and finish the handshake. Default: timeoutMs clamped to 2000..10000."
387
+ }
388
+ }
389
+ },
390
+ "server": {
391
+ "description": "A server under test: { \"cmd\": ... } for stdio or { \"url\": ... } for Streamable HTTP.",
392
+ "oneOf": [
393
+ {
394
+ "$ref": "#/$defs/stdioServer"
395
+ },
396
+ {
397
+ "$ref": "#/$defs/httpServer"
398
+ }
399
+ ]
400
+ },
401
+ "serverPatch": {
402
+ "type": "object",
403
+ "description": "Profile override for a named server. Include cmd or url to replace the entry; otherwise the listed fields patch the base entry.",
404
+ "additionalProperties": false,
405
+ "properties": {
406
+ "cmd": {
407
+ "type": "string",
408
+ "minLength": 1,
409
+ "description": "Executable to spawn (stdio transport), e.g. \"node\"."
410
+ },
411
+ "args": {
412
+ "type": "array",
413
+ "items": {
414
+ "type": "string"
415
+ },
416
+ "description": "Arguments passed to cmd."
417
+ },
418
+ "cwd": {
419
+ "type": "string",
420
+ "description": "Working directory for the server process, relative to the config file."
421
+ },
422
+ "env": {
423
+ "type": "object",
424
+ "additionalProperties": {
425
+ "type": "string"
426
+ },
427
+ "description": "Extra environment variables for the server process (merged over the current environment)."
428
+ },
429
+ "url": {
430
+ "type": "string",
431
+ "pattern": "^(https?://|\\$\\{)",
432
+ "description": "Streamable HTTP endpoint, e.g. \"https://mcp.example.com/mcp\" (may use ${ENV} interpolation)."
433
+ },
434
+ "headers": {
435
+ "type": "object",
436
+ "additionalProperties": {
437
+ "type": "string"
438
+ },
439
+ "description": "Static HTTP headers sent on every request, e.g. { \"Authorization\": \"Bearer ${MCP_TOKEN}\" }."
440
+ },
441
+ "protocol": {
442
+ "$ref": "#/$defs/protocol"
443
+ },
444
+ "spawnTimeoutMs": {
445
+ "type": "integer",
446
+ "minimum": 0,
447
+ "description": "Max time (ms) to spawn/connect and finish the handshake. Default: timeoutMs clamped to 2000..10000."
448
+ }
449
+ }
450
+ },
451
+ "normalize": {
452
+ "type": "object",
453
+ "description": "Result envelope normalization applied before matching and recording.",
454
+ "additionalProperties": false,
455
+ "properties": {
456
+ "stripMeta": {
457
+ "type": "array",
458
+ "items": {
459
+ "type": "string"
460
+ },
461
+ "description": "Glob-ish keys to strip from every _meta object. Default [\"io.modelcontextprotocol/*\"]."
462
+ },
463
+ "dropKeys": {
464
+ "type": "array",
465
+ "items": {
466
+ "type": "string"
467
+ },
468
+ "description": "Top-level result keys to drop. Default [\"resultType\"]."
469
+ },
470
+ "ttlMs": {
471
+ "type": "string",
472
+ "enum": ["keep", "ignore"],
473
+ "description": "Whether list results keep their ttlMs/cacheScope cache hints. Default \"ignore\"."
474
+ }
475
+ }
476
+ },
477
+ "shard": {
478
+ "description": "Run only one shard of the suite: \"<index>/<total>\" (1-based) or { \"index\", \"total\" }.",
479
+ "oneOf": [
480
+ {
481
+ "type": "string",
482
+ "pattern": "^\\d+/\\d+$"
483
+ },
484
+ {
485
+ "type": "object",
486
+ "additionalProperties": false,
487
+ "required": ["index", "total"],
488
+ "properties": {
489
+ "index": {
490
+ "type": "integer",
491
+ "minimum": 1,
492
+ "description": "1-based shard index."
493
+ },
494
+ "total": {
495
+ "type": "integer",
496
+ "minimum": 1,
497
+ "description": "Total number of shards."
498
+ }
499
+ }
500
+ }
501
+ ]
502
+ },
503
+ "checks": {
504
+ "type": "object",
505
+ "description": "Tuning for spec-sanity checks and description lint (vib check, vib run --check). Profiles extend allow and merge severity.",
506
+ "additionalProperties": false,
507
+ "properties": {
508
+ "allow": {
509
+ "type": "array",
510
+ "items": {
511
+ "type": "string"
512
+ },
513
+ "description": "Check ids to skip, e.g. [\"desc/embedded-url\"]."
514
+ },
515
+ "severity": {
516
+ "type": "object",
517
+ "description": "Per-check severity override: check id -> \"failed\" (fails the run) or \"warned\" (reported only).",
518
+ "additionalProperties": {
519
+ "type": "string",
520
+ "enum": ["failed", "warned"]
521
+ }
522
+ }
523
+ }
524
+ },
525
+ "hooks": {
526
+ "type": "object",
527
+ "description": "Suite-level steps run once per server session.",
528
+ "additionalProperties": false,
529
+ "properties": {
530
+ "setup": {
531
+ "type": "array",
532
+ "items": {
533
+ "type": "object"
534
+ },
535
+ "description": "Steps run before the first case (step shape: case.schema.json#/$defs/step)."
536
+ },
537
+ "teardown": {
538
+ "type": "array",
539
+ "items": {
540
+ "type": "object"
541
+ },
542
+ "description": "Steps run after the last case, even on failure (step shape: case.schema.json#/$defs/step)."
543
+ }
544
+ }
29
545
  }
30
546
  }
31
547
  }