@jkudish/jev-mcp 0.10.0 → 0.11.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/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.11.0
4
+
5
+ - New `jev_audit` tool: a max-gated failure-mode battery (hallucinated, off-target, incomplete, wrong format) plus an omission check, auditing extracted values against their source before they're trusted. Via [#48](https://github.com/jkudish/jev-mcp/pull/48) by Rex-Gao, prompted by [#45](https://github.com/jkudish/jev-mcp/issues/45).
6
+ - `jev_decide`: names `contradicted_requirements` on the recommendation, and opt-in `escalate_on_contradiction` withdraws a contradicted pick in addition to the warning; no silent re-selection. Via [#46](https://github.com/jkudish/jev-mcp/pull/46) by Rex-Gao, prompted by [#43](https://github.com/jkudish/jev-mcp/issues/43); the structural-field idea from [#44](https://github.com/jkudish/jev-mcp/pull/44) by xujiantop-crypto.
7
+ - `jev_review` / `jev_gate`: optional per-file review mode — one request, composed auto/mean/min/limiting, combined request+tests+diffs budget, per-file truncation. Via [#47](https://github.com/jkudish/jev-mcp/pull/47) by Rex-Gao, prompted by [#42](https://github.com/jkudish/jev-mcp/issues/42).
8
+ - Docs: runnable Exa search → classification example. Via [#41](https://github.com/jkudish/jev-mcp/pull/41) by RogueTex.
9
+
10
+ ## 0.10.1
11
+
12
+ - `--http` advertises a static tool list and refuses `subscriptions/listen` in-band: previously a client that opened a listener parked an idle stream on a concurrency slot for its lifetime, and enough listeners could starve the server of slots. Slot accounting no longer leaks on a rejected handler either.
13
+ - Docs: the HTTP section names the transport env var and the static tool list.
14
+
3
15
  ## 0.10.0
4
16
 
5
17
  - Opt-in stateless Streamable HTTP: `jev-mcp --http` (or `JEV_MCP_TRANSPORT=http`) serves MCP 2026-07-28 per request and 2025-era clients through the SDK's stateless fallback, with no sessions, on `PORT` (default 8080) at `/mcp`, with `/health`. `HOST` defaults to loopback; binding beyond it is explicit. `JEV_MCP_AUTH_TOKEN` gates it with a bearer token and is required unless `HOST` is loopback; Host/Origin rebinding guards answer fixed-string 403s. Concurrent requests are capped at `JEV_MCP_MAX_CONCURRENCY` (default 16) with `429` backpressure, and a cancelled request aborts its in-flight regex worker and Jev calls. Stdio stays the default. Via [#37](https://github.com/jkudish/jev-mcp/pull/37) by shivasymbl.
package/README.md CHANGED
@@ -9,7 +9,7 @@
9
9
 
10
10
  Fast, cheap, typed judgments from TypeSafe's Jev model, as MCP tools.
11
11
 
12
- Give your agent eleven judgment tools:
12
+ Give your agent twelve judgment tools:
13
13
 
14
14
  - `jev_verify` checks claims against evidence.
15
15
  - `jev_screen` judges content before it enters context.
@@ -20,6 +20,7 @@ Give your agent eleven judgment tools:
20
20
  - `jev_decide` settles bounded alternatives.
21
21
  - `jev_compare` judges how two passages relate.
22
22
  - `jev_extract` pulls field values with regex plus judgment.
23
+ - `jev_audit` audits extracted values against their source before they are trusted.
23
24
  - `jev_review` scores a proposed diff before the task is called done.
24
25
  - `jev_gate` reviews a patch and verifies completion claims in one call.
25
26
 
@@ -136,17 +137,17 @@ Stdio is the default. To host one shared server for a team or a remote agent, ru
136
137
  JEV_MCP_AUTH_TOKEN="$(openssl rand -hex 32)" TYPESAFE_API_KEY=ts_... npx -y @jkudish/jev-mcp --http
137
138
  ```
138
139
 
139
- It listens on `PORT` (default `8080`) and `HOST` (default `127.0.0.1` — set `HOST=0.0.0.0` explicitly to serve beyond your machine), serves MCP at `/mcp` and a health check at `/health`. It speaks MCP 2026-07-28 and falls back to stateless serving for 2025-era clients, so it keeps no sessions and scales behind any load balancer. Every call spends your Jev key, so `JEV_MCP_AUTH_TOKEN` is required unless `HOST` is loopback. Clients send it as a bearer token:
140
+ It listens on `PORT` (default `8080`) and serves MCP at `/mcp`, with a health check at `/health`. `HOST` defaults to `127.0.0.1`; set `HOST=0.0.0.0` explicitly to serve beyond your machine. `--http` and `JEV_MCP_TRANSPORT=http` are equivalent, so containers and service units can select the transport without argv. It speaks MCP 2026-07-28 and falls back to stateless serving for 2025-era clients, so it keeps no sessions and scales behind any load balancer. Every call spends your Jev key, so `JEV_MCP_AUTH_TOKEN` is required unless `HOST` is loopback. Clients send it as a bearer token:
140
141
 
141
142
  ```bash
142
143
  claude mcp add --transport http jev https://jev.example.com/mcp --header "Authorization: Bearer $JEV_MCP_AUTH_TOKEN"
143
144
  ```
144
145
 
145
- The server itself speaks plain HTTP: terminate TLS at a reverse proxy or load balancer before exposing it beyond loopback, and put connection limits and request rate limits at that ingress — the process bounds admitted `/mcp` requests (`JEV_MCP_MAX_CONCURRENCY`, default 16; excess shed with `429`) and caps request bodies at 4 MiB, but it does not limit sockets waiting to finish headers or repeatedly rejected requests.
146
+ The server itself speaks plain HTTP: terminate TLS at a reverse proxy or load balancer before exposing it beyond loopback, and put connection limits and request rate limits at that ingress. The process bounds admitted `/mcp` requests (`JEV_MCP_MAX_CONCURRENCY`, default 16; excess shed with `429`) and caps request bodies at 4 MiB, but it does not limit sockets waiting to finish headers or repeatedly rejected requests. The tool list is static: the server advertises no `listChanged` capability and refuses `subscriptions/listen` requests, so an idle listener cannot hold one of the concurrency slots. Clients that never open a listener, the common case, see no difference.
146
147
 
147
148
  ### Agent skill
148
149
 
149
- The package ships an agent skill (`skills/jev/`) that teaches coding agents when to reach for each tool instead of answering from their own reading — the difference between tools that sit registered-but-unused and tools that get called. Copy it into your client's skills directory:
150
+ The package ships an agent skill (`skills/jev/`) that teaches coding agents when to reach for each tool instead of answering from their own reading: the difference between tools that sit registered-but-unused and tools that get called. Copy it into your client's skills directory:
150
151
 
151
152
  ```bash
152
153
  npm pack @jkudish/jev-mcp@latest
@@ -186,7 +187,7 @@ Check each claim in a report, PR description, or agent brief against the sources
186
187
  }
187
188
  ```
188
189
 
189
- Malformed or missing relation answers fail closed per claim (other valid claims are preserved). A missing, null, or non-object `answers` envelope invalidates every claim:
190
+ A malformed or missing relation answer fails closed per claim:
190
191
 
191
192
  ```jsonc
192
193
  // invalid result entry; tool/model/provider/auto_accept/summary/results/usage remain
@@ -197,6 +198,7 @@ Malformed or missing relation answers fail closed per claim (other valid claims
197
198
  }
198
199
  ```
199
200
 
201
+ - A malformed or missing relation answer fails closed for that claim; other valid claims are preserved. A missing, null, or non-object `answers` envelope invalidates every claim.
200
202
  - Relation choices must belong to the requested set and be a maximum-probability option. Distributions must contain exactly all relation keys, with finite probabilities in `[0,1]` summing to 1 within `0.01`.
201
203
  - Missing or null confidence stays `null` and requires `review`, even with `auto_accept: 0`. Non-number, non-finite, or out-of-range confidence invalidates the claim and is returned as `null`; numeric zero is valid.
202
204
  - With multiple evidence items, each claim also gets the id of the evidence it rests on. These source answers are optional auxiliary information; missing sources yield `supporting_evidence: null` without invalidating a valid relation. A present source must be a well-formed choice over the evidence ids plus `none`; anything else yields `null`.
@@ -234,8 +236,9 @@ Missing or malformed required answers return an error branch:
234
236
  }
235
237
  ```
236
238
 
237
- All requested probabilities must be finite numbers in `[0,1]`; zero is valid. Invalid or missing probabilities become `null`, while valid values are retained. Relevance is required only when a non-empty purpose is supplied; otherwise it is `null`. A missing, null, or non-object `answers` envelope also takes this error branch.
238
-
239
+ - All requested probabilities must be finite numbers in `[0,1]`; zero is valid. Invalid or missing probabilities become `null`, while valid values are retained.
240
+ - Relevance is required only when a non-empty `purpose` is supplied; otherwise it is `null`.
241
+ - A missing, null, or non-object `answers` envelope also takes this error branch.
239
242
  - The recommendation is advisory: `pass`, `review`, `block`, or `skip`. The server never blocks on its own; enforcement stays with the calling agent.
240
243
  - Low substance or relevance yields `skip`: the page is not worth reading.
241
244
  - `block_at` (default `0.75`) and `review_at` (default `0.25`) are thresholds on the injection probability. Both are parameters.
@@ -289,8 +292,9 @@ Missing or malformed `exists` or `best` answers return an error branch:
289
292
  }
290
293
  ```
291
294
 
292
- `exists` must be a finite number in `[0,1]`; zero validly means `absent`. On failure, a valid `exists` value is retained; an invalid or missing value becomes `null`. The best distribution must contain exactly all candidate ids, finite probabilities in `[0,1]` summing to 1 within `0.01`, and a string choice tied for the maximum probability. Missing, null, or non-object `answers` also returns this error branch. Protocol failure is reported only in `status`; `exists_verdict` is `null` on failure.
293
-
295
+ - `exists` must be a finite number in `[0,1]`; zero validly means `absent`. On failure, a valid `exists` value is retained; an invalid or missing value becomes `null`. Protocol failure is reported only in `status`; `exists_verdict` is `null` on failure.
296
+ - The best distribution must contain exactly all candidate ids, finite probabilities in `[0,1]` summing to 1 within `0.01`, and a string choice tied for the maximum probability.
297
+ - A missing, null, or non-object `answers` envelope also returns this error branch.
294
298
  - On successful responses, ranking always returns a winner, because Choice probabilities sum to 1. A top hit can masquerade as an answer when none is present; the exists check catches that. `exists_verdict` is `answered`, `partial`, or `absent`.
295
299
  - Up to 250 candidates per call. Candidate texts are truncated at 2,000 characters.
296
300
  - Pattern from the [semantic-find cookbook](https://docs.typesafe.ai/cookbooks/semantic_find).
@@ -329,6 +333,7 @@ Assign each item to one class from a shared catalog, in one batched request: the
329
333
  - Class descriptions carry the decision. Strong ones state a precise definition, what belongs, what does not, precedence over overlapping classes, and a short example.
330
334
  - Up to 250 classes and 64 items per call, with an 8,000 item-class budget per batch (split larger waves into multiple calls); item text is truncated at 2,000 characters.
331
335
  - A malformed or incomplete model response is reported as `status: invalid_response` on that item, never as model uncertainty. The chosen class must have the maximum probability (ties and differences within `1e-9` are accepted); otherwise that item returns `invalid_response`.
336
+ - For a runnable Exa search → classification pipeline with preserved source URLs and review routing, see the [Exa classification example](examples/exa-classify.md).
332
337
 
333
338
  ### jev_decide
334
339
 
@@ -352,14 +357,16 @@ One bounded decision, 2-6 candidates, evidence, and explicit priorities. Jev ret
352
357
  // live result, abridged
353
358
  {
354
359
  "recommendation": { "selected": "poll", "escaped": false, "confidence": 1,
355
- "probabilities": { "poll": 1, "push": 0, "ask_user": 0 } },
360
+ "probabilities": { "poll": 1, "push": 0, "ask_user": 0 },
361
+ "contradicted_requirements": [] },
356
362
  "checks": [ { "candidate": "poll", "requirement": 0, "answer": "supported" },
357
363
  { "candidate": "push", "requirement": 0, "answer": "contradicted" } ]
358
364
  }
359
365
  ```
360
366
 
361
367
  - Escape hatches (`ask_user`, `investigate`, `none`) let the model decline to rank when a preference or fact is missing; `escaped: true` in the result marks it. Disable with `escape_hatches: false` for closed-world choices.
362
- - Requirement checks run as independent questions in the same request and may disagree with the recommendation; a contradiction on the recommended candidate surfaces as a warning.
368
+ - Requirement checks run as independent questions in the same request and may disagree with the recommendation; `recommendation.contradicted_requirements` names the zero-based requirement indexes whose checks came back `contradicted` for the selected candidate, and a contradiction also surfaces as a warning.
369
+ - Pass `escalate_on_contradiction: true` to withdraw a contradicted recommendation in addition to the warning: the result comes back `selected: null` with `status: "escalate"` (the `jev_verify` vocabulary), probabilities and `contradicted_requirements` intact. The indexes describe the recommended candidate before withdrawal — `selected` is null afterward. The default keeps the recommendation and warns. No re-selection: a withdrawn recommendation is never silently replaced by the runner-up.
363
370
  - One call per unchanged decision. Repeat only with materially new evidence or criteria.
364
371
 
365
372
  <sub>Pattern credit: [thesammykins/jev_ampcode](https://github.com/thesammykins/jev_ampcode).</sub>
@@ -400,8 +407,8 @@ Score every candidate's relevance to a query and get them back sorted. You bring
400
407
  }
401
408
  ```
402
409
 
403
- Each candidate is a file: `id` is any handle you choose, echoed back verbatim, and `text` is the file's contents (truncated at 2,000 characters). No candidate contains the words bandwidth or triple. A shorter CDN TTL means more origin fetches, so `src/cache.ts` ranks first on meaning alone; the always-on VMs are cloud spend too, just not bandwidth.
404
-
410
+ - Why `src/cache.ts` ranks first: no candidate contains the words bandwidth or triple. A shorter CDN TTL means more origin fetches, so it wins on meaning alone; the always-on VMs are cloud spend too, just not bandwidth.
411
+ - Each candidate is a file: `id` is any handle you choose, echoed back verbatim, and `text` is the file's contents (truncated at 2,000 characters).
405
412
  - One relevance probability per candidate, all in a single request; cost scales with the number of candidates, not with candidate-pairs.
406
413
  - Candidate ids are preserved verbatim. If any answer comes back malformed, the whole ranking is reported `invalid_response` rather than sorting a missing score as a confident zero.
407
414
  - Up to 250 candidates and a 100,000-character aggregate budget; split larger batches.
@@ -471,6 +478,55 @@ Pull structured fields out of a document with your regex and Jev's judgment. You
471
478
  - Invalid patterns and regexes that time out (they run in a sandboxed worker with a 1-second deadline, so a pathological pattern cannot hang the server) return `invalid_pattern` with the error instead of failing the whole call.
472
479
  - Up to 32 fields per call and 20 candidate matches per field, judged in one request. The document is capped at 50,000 characters, and the candidate match text at 50,000 characters in aggregate.
473
480
 
481
+ ### jev_audit
482
+
483
+ Audit extracted values against the text they claim to come from, before the values are trusted. For each value, one request carries a failure-mode battery — hallucinated, off-target, incomplete, wrong format — each a yes/no question framed so that yes means something is wrong, plus a dedicated omission check for values that came back empty. A value's `p_wrong` is the maximum over its checks; any value at or above `wrong_at` (default 0.7) escalates the whole audit. Max-gated, never averaged: one fired flag cannot be diluted by clean siblings.
484
+
485
+ ```jsonc
486
+ // arguments
487
+ {
488
+ "source": "Invoice INV-7734. Total $1,240.00. Due 2026-10-15. Late fee 1.5% per month.",
489
+ "records": [
490
+ { "id": "total", "request": "The invoice total amount", "value": "$1,240.00" },
491
+ { "id": "due_date", "request": "The due date in YYYY-MM-DD", "value": "2026-10-15" },
492
+ { "id": "currency", "request": "The billing currency", "value": "EUR" }
493
+ ]
494
+ }
495
+ ```
496
+
497
+ ```jsonc
498
+ // live result, abridged
499
+ {
500
+ "action": "escalate",
501
+ "wrong_at": 0.7,
502
+ "summary": { "records": 3, "flagged": 1, "invalid": 0 },
503
+ "records": [
504
+ { "id": "total", "value": "$1,240.00", "action": "ok", "p_wrong": 0.02,
505
+ "checks": { "hallucinated": 0.02, "off_target": 0.01, "incomplete": 0.01, "format": 0.01 } },
506
+ { "id": "due_date", "value": "2026-10-15", "action": "ok", "p_wrong": 0.03, "checks": { "…": "…" } },
507
+ { "id": "currency", "value": "EUR", "action": "wrong", "p_wrong": 0.91,
508
+ "checks": { "hallucinated": 0.91, "off_target": 0.4, "incomplete": 0.05, "format": 0.2 } }
509
+ ]
510
+ }
511
+ ```
512
+
513
+ - Here the currency was fabricated — the invoice never states one — and the `hallucinated` check catches what a schema-valid extraction would happily pass through.
514
+ - The framing discipline is the point: every check asks "is something wrong", so one threshold routes the record. The battery, the omission special case, and the max gate come from TypeSafe's SDE cascade cookbook, where this verifier is what catches a schema-valid fabrication.
515
+ - An empty value gets only the omission check: wrong when the source supports a value the extractor missed, correct when returning nothing was right.
516
+ - Malformed answers mark the record `invalid_response` and escalate: a protocol failure is never a clean pass. A truncated `source` (over 50,000 characters) demotes `pass` to `review`, never keeps it.
517
+ - Up to 32 records per call, judged in one request; each request line is capped at 500 characters, each value at 2,000.
518
+
519
+ #### Multimodal intake
520
+
521
+ Jev reads text only — its state is a string, JSON object, or array, and images, audio, and video are not supported (pre-process to text first, per the TypeSafe docs). Multimodal judgment therefore lands as a cascade, with the text artifacts cross-checked by the text-only judge:
522
+
523
+ 1. **Extract** with your host model: a vision or ASR model produces a dense transcript of the image, scan, or recording, plus the structured values you want.
524
+ 2. **Screen** the transcript with `jev_screen`: transcripts of fetched content are untrusted text and get the injection screen before anything else. The screen is advice you enforce — honor `block` and `review` before passing the transcript onward. It protects what enters your context, not the vision or ASR model, which has already consumed the untrusted material.
525
+ 3. **Audit** the values against the transcript with `jev_audit`. This is a cross-check between two text artifacts, not verification of the original: when the original text exists (a document, a page), audit against it directly. For vision or ASR output, one host model usually produces both the transcript and the values, so the same misreading can appear in both and pass the audit — producing the two separately, or with independent models, makes the cross-check stronger. `pass` means no check crossed the threshold, never that the values were verified against the pixels or audio.
526
+ 4. **Judge** with the existing tools — verify claims, classify, review — over the audited text, keeping the provenance in state so downstream judgments know they read an extraction, not the original.
527
+
528
+ <sub>Question design adapted from the TypeSafe [SDE cascade cookbook](https://docs.typesafe.ai/cookbooks/sde_cascade) (see [#45](https://github.com/jkudish/jev-mcp/issues/45)).</sub>
529
+
474
530
  ### jev_review
475
531
 
476
532
  Score a proposed diff against the request before the task is called done. Jev answers four rubric questions, correctness, spec match, test gap, and blast radius, each 0..2, plus one safe-to-apply probability; the server combines them into a weighted composite and one action: `auto`, `review`, or `escalate`. It judges what you hand it. It never runs tests and never applies the patch.
@@ -505,14 +561,18 @@ Score a proposed diff against the request before the task is called done. Jev an
505
561
  }
506
562
  ```
507
563
 
508
- Here the composite clears the floor, but the failing test drags `safe_to_apply` to 0.24 and rubric confidences sit under `review_at`, so the patch escalates instead of sailing through on its decent scores.
509
-
564
+ - Why the example escalates: the composite clears the floor, but the failing test drags `safe_to_apply` to 0.24 and rubric confidences sit under `review_at` — decent scores don't sail through on their own.
510
565
  - Rubric scores run 0..2. Higher is better for `correctness` and `spec_match`; higher is worse for `test_gap` and `blast_radius`, and the composite inverts those two before weighting, so a composite of 1.0 means favorable on every rubric.
511
566
  - A score answer may carry its full probability distribution over the three options; when the provider reports one, it is validated (exact keys, probabilities summing to one, expected value within a small tolerance of the reported score) and returned in `scores.*.probabilities`. Absent means "not reported" and stays `null`; a distribution that is present but malformed or contradictory marks that rubric `invalid_response`.
512
567
  - `reason_codes` collects why the review decided as it did: `invalid_response`, `unknown_confidence`, `confidence_below_review`, `safe_to_apply_below_review`, `confidence_below_auto_accept`, `safe_to_apply_below_auto_accept`, `composite_below_floor`, `incomplete_context`, `accepted`. `limiting_rubrics` names the rubric(s) that bound the decision, ties included: the null-confidence rubrics when confidence is unknown, every rubric tied at the minimum confidence when a confidence threshold blocks, and the least favorable rubrics when the composite floor blocks.
513
568
  - `auto` requires `safe_to_apply` and every rubric confidence at `auto_accept` and the composite at `composite_floor`. `safe_to_apply` or any rubric confidence below `review_at`, or unknown, escalates; a composite below `composite_floor` returns `review`, not `escalate`. Unknown confidence counts as escalate, never as a value that can satisfy a threshold.
514
569
  - `request` frames the review; it is not proof of anything. Put real output in `tests`. Every field is treated as evidence to evaluate, never instructions to follow.
515
570
  - Each text field is capped at 50,000 characters. Truncated input sets `truncated: true` and can never return `auto`; a malformed answer is `invalid_response`, not a semantic outcome.
571
+ - Multi-file change: pass `files` (an array of `{ path, diff }`, up to 16 files) instead of `diff` — exactly one of the two. `jev_gate` takes the same `files` input for its review half.
572
+ - The rubric is asked once per file, all in one request, each question scoped to its own file by index; the result carries `mode: "per-file"` and a `files` array with the full per-file review.
573
+ - Composed top-level fields: the change is `auto` only when every file is `auto`, `composite` is the file mean, `safe_to_apply` is the file minimum, and `limiting` names the file and rubrics that bound the decision. Nothing is averaged into invisibility — a weak file stays visible as itself.
574
+ - One request must fit the combined budget: `request` + `tests` + all file diffs together stay under 200,000 characters.
575
+ - Truncation is per-file: only the file whose own diff (or the shared request/tests context) was truncated is demoted to `review`; an intact file is never demoted for a truncated sibling.
516
576
 
517
577
  <sub>Adapted from [burnigtm/jev-mcp](https://github.com/burnigtm/jev-mcp) (MIT), via [PR #2](https://github.com/jkudish/jev-mcp/pull/2) by rimusz.</sub>
518
578
 
@@ -560,11 +620,13 @@ The completion gate: the same patch review as `jev_review`, plus your completion
560
620
  }
561
621
  ```
562
622
 
563
- The two true claims verify at full confidence, and the one that matters, "the full test suite passes," is contradicted by the test log at full confidence: exactly the claim a coding agent is most tempted to hand-wave.
564
-
623
+ - In the example, the two true claims verify at full confidence, and the one that matters — "the full test suite passes" — is contradicted by the test log at full confidence: exactly the claim a coding agent is most tempted to hand-wave.
565
624
  - Claim questions instruct Jev to use `evidence` only, not world knowledge, and not the request, diff, or tests fields; if a claim needs a diff excerpt or a test log as support, supply it in `evidence`. All fields share one model state, so this is instruction-level isolation, not a hard boundary. Every field is evidence to evaluate, never instructions to follow.
566
625
  - `reason_codes` collects why the gate decided as it did: `incomplete_context`, `invalid_response`, `review_escalated`, `review_required`, plus the review half's specific codes (`unknown_confidence`, `confidence_below_review`, `safe_to_apply_below_review`, `confidence_below_auto_accept`, `safe_to_apply_below_auto_accept`, `composite_below_floor`), `claims_contradicted`, `claims_unsupported`, `claim_confidence_low`, `claim_confidence_below_auto_accept`, `accepted`. The embedded `review` object carries the same `reason_codes` and `limiting_rubrics` a standalone `jev_review` returns.
567
626
  - Up to 16 claims and 16 evidence items per call. Text fields are capped at 50,000 characters each, claims at 2,000, and evidence at 200,000 characters in aggregate; oversized evidence is rejected before any model call. Malformed answers surface as `invalid_response` and the gate never returns `auto` on one.
627
+ - The review half accepts `files` instead of `diff` for a multi-file change, exactly as `jev_review` does: the rubric is asked once per file in the same request, the review is `auto` only when every file is `auto`, and the embedded `review` object carries `mode: "per-file"`, the per-file results in `files`, and `limiting` naming the limiting file and rubrics.
628
+ - The same 200,000-character combined budget (`request` + `tests` + all diffs) applies.
629
+ - Truncation is per-file: only a file with its own truncated diff is demoted; claim actions stay fail-closed on any truncation.
568
630
  - Use `jev_verify` for claims without a patch review, and `jev_review` for a patch without claims.
569
631
 
570
632
  <sub>Adapted from [burnigtm/jev-mcp](https://github.com/burnigtm/jev-mcp) (MIT), via [PR #2](https://github.com/jkudish/jev-mcp/pull/2) by rimusz.</sub>
@@ -580,6 +642,7 @@ The two true claims verify at full confidence, and the one that matters, "the fu
580
642
  - `jev_decide`: choose between a handful of options with priorities in view.
581
643
  - `jev_compare`: how two passages relate, overall or per aspect.
582
644
  - `jev_extract`: pull field values a regex can find, verbatim.
645
+ - `jev_audit`: extracted values (from a document, or a vision/ASR transcript) before they are trusted.
583
646
  - `jev_review`: score a proposed diff before calling the task done.
584
647
  - `jev_gate`: that same review plus completion claims checked against evidence.
585
648
 
@@ -718,6 +781,8 @@ export JEV_MCP_MODEL=openjev
718
781
 
719
782
  The server sends `POST` requests with `{ model, state, questions }` and requires the standard response shape: an `answers` object plus a `usage` object reporting `input_tokens` and `output_tokens`, with an optional `model` string echoing the model that answered. Envelope problems (a non-object body or `answers`, malformed `usage` counts, a non-string `model`) are rejected at the transport boundary. Individual answers are not judged here: each tool validates them under its own `invalid_response` contract, so a missing or malformed answer fails closed in the tool instead of aborting the call. `JEV_API_BASE_URL` must be the full endpoint URL including the `/v1/systemone` path; it is used verbatim, with no trailing-slash or path normalization. The endpoint and credentials are kept in the local process environment. This adapter is provider-neutral; OpenJEV is one example, not a hard-coded dependency.
720
783
 
784
+ One compatibility note: the default model is `jev-latest`, and not every endpoint implements that alias. If calls fail against your endpoint with a client-error status, set `JEV_MCP_MODEL` to the model id your endpoint supports (bare, without a provider prefix like `opencode/`).
785
+
721
786
  ## Also in the family
722
787
 
723
788
  Need those judgments to drive a real browser? [Jev Browser](https://github.com/jkudish/jev-browser) gives an agent a task and a URL and lets Jev pick the actions: click, type, select, stop. It uses the same judgment style this server exposes. The npm package is [@jkudish/jev-browser](https://www.npmjs.com/package/@jkudish/jev-browser).
package/dist/http.js CHANGED
@@ -57,7 +57,12 @@ export async function serveHttp(factory, env = process.env) {
57
57
  // On loopback, reject foreign Host/Origin headers so a web page cannot reach
58
58
  // the server through DNS rebinding (the spec's Origin-validation MUST).
59
59
  const onLoopback = LOOPBACK.has(host);
60
- const mcp = toNodeHandler(createMcpHandler(factory), {
60
+ const mcp = toNodeHandler(createMcpHandler(factory, {
61
+ // The tool list never changes: refuse subscriptions/listen in-band
62
+ // instead of letting a client park an idle SSE stream on a slot.
63
+ maxSubscriptions: 0,
64
+ onerror: (error) => console.error(`[jev-mcp] http: ${error.message}`),
65
+ }), {
61
66
  onerror: (error) => console.error(`[jev-mcp] http: ${error.message}`),
62
67
  });
63
68
  let inFlight = 0;
@@ -82,7 +87,14 @@ export async function serveHttp(factory, env = process.env) {
82
87
  }
83
88
  else {
84
89
  inFlight++;
85
- void mcp(req, res).finally(() => inFlight--);
90
+ void mcp(req, res)
91
+ .catch((error) => {
92
+ // toNodeHandler reports its own failures through onerror; a promise
93
+ // rejection here must not become an unhandled one that kills Node.
94
+ console.error(`[jev-mcp] http: ${error?.message ?? error}`);
95
+ res.destroy();
96
+ })
97
+ .finally(() => inFlight--);
86
98
  }
87
99
  });
88
100
  await new Promise((resolve) => server.listen(port, host, resolve));
package/dist/http.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"http.js","sourceRoot":"","sources":["../src/http.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"http.js","sourceRoot":"","sources":["../src/http.ts"],"names":[],"mappings":"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"}