benjivers/jev-mcp Public Public: anyone can read this; nobody can change it.

workbenchthis machine
Files cbaceb505b32e2f74693377ae6d3e69cde6a98b6
167 lines · 7.0 KB · ~1.8k tok Raw History
1
# jev-mcp
2
3
An MCP server that exposes [TypeSafe Jev](https://docs.typesafe.ai) as typed judgment tools.
4
5
Jev is a System One model. It returns a typed answer and a calibrated probability
6
distribution, never prose. These tools surface that faithfully rather than hiding
7
it behind a label.
8
9
## Install
10
11
Requires Node 20.12+ and a TypeSafe API key from
12
[console.typesafe.ai](https://console.typesafe.ai/settings/keys).
13
14
```bash
15
npm install
16
npm run build
17
```
18
19
Register with Claude Code:
20
21
```bash
22
claude mcp add --scope user jev -- node /absolute/path/to/jev-mcp/dist/index.js
23
```
24
25
The key is read from `TYPESAFE_API_KEY` in the server's environment. It is never a
26
tool argument, so it cannot land in a transcript or in a model's context. Put it in
27
`~/.zshenv` rather than in any repo, and make sure it is **exported**:
28
29
```sh
30
export TYPESAFE_API_KEY="ts_..."
31
```
32
33
Without `export` the variable exists only in the shell that read it. Child processes,
34
including every MCP server Claude Code spawns, never see it, and every tool call fails
35
with a missing-key error.
36
37
## Tools
38
39
| Tool | Primitive | Use when |
40
| --- | --- | --- |
41
| `jev_classify` | Choice | The answer is one of a fixed set you define |
42
| `jev_score` | Score | The answer is a degree on an ordered scale |
43
| `jev_check` | Noul | The answer is yes or no, and you want the probability |
44
| `jev_ask` | all three | You have several questions about the same state |
45
| `jev_models` | — | Confirm the key works and find a model id |
46
47
Every tool returns the full probability distribution alongside the answer, plus
48
`confidence` for Choice and Score. Results come back as MCP structured content, so a
49
client gets typed data rather than a JSON string to re-parse.
50
51
### Prefer `jev_ask`
52
53
Jev prefills the state once and scores every question in a single forward pass, so
54
extra questions add almost no latency or cost. TypeSafe's own
55
[measurement](https://docs.typesafe.ai/cookbooks/parallel_questions) on a
56
document-dominated workload puts one batched call at 12.2x cheaper and 10.0x faster
57
than one call per question, with no change in the answers.
58
59
Questions in one request cannot see each other's answers. State any speculative
60
premise explicitly and let your own code decide which answers apply.
61
62
## Design rules
63
64
1. **The caller owns the option set.** `jev_classify` requires options from you, so
65
   the model can pick the wrong one but can never invent one. A selector cannot
66
   choose a candidate the enumerator dropped. This is the single most common way
67
   these integrations fail.
68
2. **Probabilities are always returned.** Not just the winner.
69
3. **The key lives in the environment.** Never in an argument.
70
4. **Nothing is silently dropped, overwritten, or truncated.** A request that cannot
71
   be honoured exactly fails with a reason instead of quietly changing meaning.
72
5. **Only JSON-RPC reaches stdout.** Logs go to stderr, always.
73
74
### The no-match option
75
76
Each selecting tool adds a `none` option by default so the model can decline rather
77
than being forced to pick. Turn it off with `add_none: false` when one option must
78
always apply.
79
80
If you already use the name `none` for an option of your own, the added option takes
81
a different key instead of overwriting yours, and the response reports which key
82
carries the no-match meaning in `none_option`. Your option and its probability always
83
survive intact.
84
85
### Confidence gating
86
87
Choice and Score answers include an `action` of `act`, `review`, or `abstain`, derived
88
from `confidence` and the thresholds you pass in `act_above` and `review_above`
89
(defaults 0.8 and 0.5). `jev_check` returns a `verdict` of `yes`, `no`, or `uncertain`
90
from `yes_at_or_above` and `no_at_or_below` (defaults 0.7 and 0.3).
91
92
These defaults are starting points, not universal rules. Calibrate them on your own
93
data and on what it costs to be wrong; a destructive action deserves a higher bar than
94
a read-only one. Confidence describes how concentrated the distribution is. It is not
95
a claim that the answer is correct.
96
97
A Noul near 0.5 means yes and no are close to equally likely, not that the answer is
98
"medium", which is why the middle band reports `uncertain` rather than rounding.
99
100
### Errors
101
102
Failures come back with `isError` and a classified body: `kind`, `retryable`, and
103
where available `status`, `requestId`, and a `hint`. A rejected key (`authentication`,
104
never retryable) is distinguishable from a rate limit (`rate_limit`, retryable) and
105
from a malformed question (`invalid_request`). The SDK already retries 408, 429, and
106
5xx with backoff before an error surfaces here.
107
108
### Limits
109
110
A Choice question accepts at most 255 options, which is an API limit; past that,
111
search in two passes, one question picking a window and a second ranking within it.
112
Question count and state size are local caps that bound cost on a single call, and
113
both are configurable. Oversized state is rejected rather than truncated, because
114
truncating silently changes the material the judgment rests on.
115
116
## Configuration
117
118
| Variable | Effect |
119
| --- | --- |
120
| `TYPESAFE_API_KEY` | Required. `JEV_API_KEY` also works. |
121
| `JEV_MODEL` | Model id. Defaults to `jev-latest`. |
122
| `JEV_TIMEOUT_MS` | Per-attempt timeout. Defaults to 15000. |
123
| `JEV_MAX_QUESTIONS` | Questions per `jev_ask`. Defaults to 64. |
124
| `JEV_MAX_STATE_CHARS` | Largest state accepted. Defaults to 200000. |
125
| `TYPESAFE_LOG_LEVEL` | SDK verbosity. Safe at any level; all output goes to stderr. |
126
127
An unusable value for any numeric setting falls back to the default and warns on
128
stderr, rather than becoming `NaN` and disabling the limit it was meant to enforce.
129
130
## Troubleshooting
131
132
**Every call reports a missing key.** Some MCP clients filter the environment before
133
spawning servers, which drops `TYPESAFE_API_KEY`. Confirm the variable is exported,
134
then pass it explicitly in the client's server config if it still does not arrive.
135
Run `jev_models` to check the key in isolation.
136
137
**The server connects and then dies.** On a stdio transport, anything written to
138
stdout that is not JSON-RPC breaks the connection. This server routes all logging to
139
stderr, so `TYPESAFE_LOG_LEVEL=debug` is safe to turn on while debugging.
140
141
## When not to reach for this
142
143
Jev earns its place on a decision that repeats thousands of times inside software,
144
where code can enumerate the options first and you need a number to threshold on.
145
146
For a one-off judgment during a conversation, an ordinary model answer is usually
147
better, because it comes with reasoning you can argue with. Jev gives you a number
148
and a label. That is a feature at scale and a limitation in dialogue.
149
150
If deterministic code already decides the case correctly, keep the deterministic
151
code. Typed output guarantees the interface, not the truth. Measure before adopting.
152
153
## Development
154
155
```bash
156
npm run typecheck
157
npm test          # offline: unit tests plus regression tests against a local stand-in
158
npm run test:e2e  # live, requires TYPESAFE_API_KEY
159
```
160
161
The offline suite runs the real built server over stdio against a local stand-in for
162
the TypeSafe API and asserts on the request bodies it actually sends, so a regression
163
in what reaches the model fails the build.
164
165
## License
166
167
MIT
↑↓ navigate ↵ open esc close
↑↓ navigate ↵ open esc close

Keyboard shortcuts