mcp server
jev-mcp
An eval-first MCP server for TypeSafe's Jev, a System One model that returns typed judgments (noul, choice, score) with probabilities instead of generated text.
Set up
Runs on your computer. Pick your agent and operating system.
Before you start
- Node.js 20 or newer
{
"mcpServers": {
"byk-jev-mcp": {
"command": "npx",
"args": [
"-y",
"jev-eval-mcp"
]
}
}
}same format Cursor, GitHub Copilot CLI, Junie, JetBrains AI Assistant, Augment (Auggie CLI), Factory Droid, Kiro, Devin CLI +12
Values in angle brackets are placeholders. Restart Claude Desktop after editing its config.Claude Desktop docs
README
synced Oct 25, 2026jev-eval-mcp
An eval-first MCP server for TypeSafe's Jev, a System One model that
returns typed judgments (noul, choice, score) with probabilities instead of generated text.
Most Jev integrations expose "ask the model a question". The hard part in practice is not asking — it is knowing whether a question works and where to put the threshold. This server is built around that:
| tool | use it for |
|---|---|
jev_ask | prototype a question against one state and see the full probability distribution |
jev_map | run a question set over many items, get one compact row each, filter/sort/save |
jev_eval | measure question variants against labeled examples: accuracy, calibration, threshold sweep, worst misses |
jev_map exists because the expensive part of triaging 500 files, findings, or tickets is reading
them into the agent's context. jev_eval exists because a threshold picked by vibes is the usual
reason a classifier gate misbehaves in production.
Install
Requires Node 20+ and a TypeSafe API key from https://console.typesafe.ai/settings/keys.
Register the server with your MCP client. Claude Code:
claude mcp add jev -e TYPESAFE_API_KEY=sk-... -- npx -y jev-eval-mcp
Or in a mcp.json-style config:
{
"mcpServers": {
"jev": {
"command": "npx",
"args": ["-y", "jev-eval-mcp"],
"env": { "TYPESAFE_API_KEY": "sk-..." }
}
}
}
For opencode, in ~/.config/opencode/opencode.json (or a
project-level opencode.json):
{
"$schema": "https://opencode.ai/config.json",
"mcp": {
"jev": {
"type": "local",
"command": ["npx", "-y", "jev-eval-mcp"],
"enabled": true,
"environment": { "TYPESAFE_API_KEY": "{env:TYPESAFE_API_KEY}" }
}
}
}
From a clone, build first (npm install && npm run build) and point the client at
node /path/to/jev-mcp/dist/index.js instead.
Environment: TYPESAFE_API_KEY (required), JEV_MODEL (default jev-latest),
TYPESAFE_BASE_URL (default https://api.typesafe.ai).
Questions
A question is one of three types, matching the Jev API:
{ "type": "noul", "instructions": "Is this ticket urgent?" }
{ "type": "score", "instructions": "How severe is this?", "criteria": ["trivial", "minor", "major", "critical"] }
{ "type": "choice", "instructions": "Which team owns this?", "criteria": { "billing": "payments", "platform": "availability" } }
Questions are keyed by an id you choose; ids are not sent to the model, so the meaning belongs in
instructions. All questions in one call are answered independently against the same state in a
single request, so asking several at once costs one round trip.
Typical loop
jev_ask— try two or three wordings on a state you understand.jev_eval— run the promising ones over labeled examples (inline, or a.jsonl/.jsonfile of{ id?, state, label? }). Read the threshold sweep and the worst misses; pick a threshold from the table, not from intuition.jev_map— apply the question you measured to the real workload, sorting or filtering by the answer and usingsave_pathto keep the bulk out of context.
> jev_eval dataset_path=examples/support-tickets.jsonl variants={plain, detailed}
16 labeled items · 2 variant(s) · jev-1.13.0 · 268 ms · 4987 input tokens
| variant | headline metrics |
| plain | best F1 1.000 @ threshold 0.50 · AUC 1.000 · Brier 0.012 · ECE 0.097 |
| detailed | best F1 1.000 @ threshold 0.50 · AUC 1.000 · Brier 0.021 · ECE 0.095 |
Labels are coerced to the question type: booleans/yes/no/1/0 for noul, the option key for
choice, and either a level name or its index for score.
Thresholds are only as good as the dataset behind them, and they are tied to a model version —
jev_eval reports the resolved version so a rerun after an upgrade is comparable.
Development
npm run typecheck && npm run lint && npm test # metrics unit tests, no network
npm run build && npm run smoke # live end-to-end over stdio, needs TYPESAFE_API_KEY
node scripts/call.ts jev_ask '{"state": "...", "questions": {}}' # drive one tool by hand
The tool descriptions in this server are themselves measured with jev_eval: examples/tool-routing.jsonl
holds labeled requests and scripts/routing-eval.json compares the shipped descriptions against a terse
variant, so a reworded description can be checked for routing regressions instead of argued about.
node scripts/call.ts jev_eval - < scripts/routing-eval.json
MIT licensed.