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

workbenchthis machine

feat: ship as Claude Code plugin and npm package with one-command install

- Plugin manifest declares the server inline and launches the published
  package via npx, so installs need no build step.
- Add marketplace manifest and thin jev skill bridging agents to the tools.
- package.json: repository metadata, prepublishOnly gate, version 0.3.0.
- Server reads its version from package.json instead of a hardcoded string.
- README: one-command install for Claude Code and any MCP client, key file setup.
- Drop stray pnpm lockfile; npm's package-lock.json is authoritative.
rashed parvez committed 1690489 Browse files
7 files changed +290 −15
.claude-plugin/marketplace.json added +29
@@ -0,0 +1,29 @@
1 {
2 "name": "jev-mcp",
3 "description": "TypeSafe Jev as typed judgment tools for coding agents.",
4 "owner": {
5 "name": "rashed parvez",
6 "url": "https://github.com/rashedInt32"
7 },
8 "plugins": [
9 {
10 "name": "jev",
11 "source": "./",
12 "displayName": "TypeSafe Jev",
13 "description": "Typed, calibrated judgments from TypeSafe Jev: classify, score, check, a batched multi-question ask, and model discovery.",
14 "version": "0.3.0",
15 "homepage": "https://github.com/rashedInt32/jev-mcp",
16 "repository": "https://github.com/rashedInt32/jev-mcp",
17 "license": "MIT",
18 "keywords": [
19 "mcp",
20 "typesafe",
21 "jev",
22 "system-one",
23 "classification",
24 "confidence"
25 ],
26 "category": "productivity"
27 }
28 ]
29 }
.claude-plugin/plugin.json added +31
@@ -0,0 +1,31 @@
1 {
2 "name": "jev",
3 "displayName": "TypeSafe Jev",
4 "description": "Typed, calibrated judgments from TypeSafe Jev: classify, score, check, a batched multi-question ask, and model discovery.",
5 "version": "0.3.0",
6 "author": {
7 "name": "rashed parvez"
8 },
9 "homepage": "https://github.com/rashedInt32/jev-mcp",
10 "repository": "https://github.com/rashedInt32/jev-mcp",
11 "license": "MIT",
12 "keywords": [
13 "mcp",
14 "typesafe",
15 "jev",
16 "system-one",
17 "classification",
18 "confidence"
19 ],
20 "skills": "./skills/",
21 "mcpServers": {
22 "jev": {
23 "type": "stdio",
24 "command": "npx",
25 "args": [
26 "-y",
27 "[email protected]"
28 ]
29 }
30 }
31 }
.gitignore +2
@@ -1,3 +1,5 @@
11 node_modules/
22 dist/
33 .env
4 .env.*
5 *.tgz
README.md +89 −13
@@ -11,28 +11,78 @@ it behind a label.
1111 Requires Node 20.12+ and a TypeSafe API key from
1212 [console.typesafe.ai](https://console.typesafe.ai/settings/keys).
1313
14 ```bash
15 npm install
16 npm run build
17 ```
14 ### Claude Code plugin
1815
19 Register with Claude Code:
16 One marketplace add, one install. This registers the server and the agent skill
17 together, so there is no separate skill step.
2018
2119 ```bash
22 claude mcp add --scope user jev -- node /absolute/path/to/jev-mcp/dist/index.js
20 /plugin marketplace add rashedInt32/jev-mcp
21 /plugin install jev@jev-mcp
2322 ```
2423
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**:
24 The plugin runs the published package through `npx -y jev-mcp@<version>`, so nothing
25 needs building. The first launch downloads the package, so allow a few seconds before
26 the server shows as connected in `claude mcp list`.
27
28 ### Any MCP client
29
30 ```bash
31 claude mcp add --scope user jev -- npx -y jev-mcp
32 ```
33
34 Or in a client's JSON config:
35
36 ```json
37 {
38 "mcpServers": {
39 "jev": { "command": "npx", "args": ["-y", "jev-mcp"] }
40 }
41 }
42 ```
43
44 Do not register the server directly **and** install the plugin. Two servers named
45 `jev` will otherwise both register.
46
47 ### The key
48
49 The server reads the key from `TYPESAFE_API_KEY` in its environment, then from
50 `JEV_API_KEY`, then from `~/.config/typesafe/key`. It is never a tool argument, so it
51 cannot land in a transcript or in a model's context.
52
53 The key file is the most reliable source, because some MCP clients strip the
54 environment before spawning servers:
2855
2956 ```sh
30 export TYPESAFE_API_KEY="ts_..."
57 mkdir -p ~/.config/typesafe
58 printf '%s' "ts_..." > ~/.config/typesafe/key
59 chmod 600 ~/.config/typesafe/key
3160 ```
3261
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.
62 If you prefer the variable, put it in `~/.zshenv` rather than in any repo, and make
63 sure it is **exported**. Without `export` the variable exists only in the shell that
64 read it, and every server Claude Code spawns fails with a missing-key error.
65
66 ### Plugin internals
67
68 The server is declared **inline** under `mcpServers` in `.claude-plugin/plugin.json`.
69 There is no `.mcp.json` anywhere in the repo, and that is deliberate.
70
71 A `.mcp.json` at the plugin root is auto-discovered by the plugin loader, so it works.
72 But any session opened in this directory *also* reads that same file as a project
73 config, where `${CLAUDE_PLUGIN_ROOT}` is undefined. The result is a missing-variable
74 warning and a scope conflict on the same server name. The inline form has exactly one
75 loader and produces neither. Verify with `claude mcp list`: the server appears as
76 `plugin:jev:jev` and the diagnostics section stays empty.
77
78 One trap. `claude plugin details jev` reports `MCP servers (0)` for an inline
79 declaration even while the server is connected and working. That is a gap in the
80 inventory count, not a failure. Trust `claude mcp list` over `plugin details` here.
81
82 The plugin deliberately ships **no `env` block**. Naming the key there would expand to
83 an empty string when the variable is unset, and an empty string is not nullish, so it
84 would shadow the `~/.config/typesafe/key` fallback and turn a working setup into a
85 missing-key error. Leaving `env` out keeps all three key sources live.
3686
3787 ## Tools
3888
@@ -113,6 +163,19 @@ Question count and state size are local caps that bound cost on a single call, a
113163 both are configurable. Oversized state is rejected rather than truncated, because
114164 truncating silently changes the material the judgment rests on.
115165
166 ## Agent skill
167
168 `skills/jev/SKILL.md` teaches an agent when to reach for these tools and how to shape
169 the call. It is a bridge, not a tutorial. Primitive semantics, state design, and
170 composition patterns live in TypeSafe's own `typesafe-ai` skill and in the docs, so
171 this one deliberately does not repeat them.
172
173 Its first section is a three-way test: answer it yourself, call a tool, or write SDK
174 code. That test follows the same line as [When not to reach for this](#when-not-to-reach-for-this)
175 below, so an agent loading the skill does not end up arguing with this README.
176
177 Point your client at the directory, or copy the file to `~/.claude/skills/jev/`.
178
116179 ## Configuration
117180
118181 | Variable | Effect |
@@ -153,11 +216,24 @@ code. Typed output guarantees the interface, not the truth. Measure before adopt
153216 ## Development
154217
155218 ```bash
219 npm ci
220 npm run build
156221 npm run typecheck
157222 npm test # offline: unit tests plus regression tests against a local stand-in
158223 npm run test:e2e # live, requires TYPESAFE_API_KEY
159224 ```
160225
226 To run your local build instead of the npm release, register it directly and skip
227 the plugin, since the plugin always launches the published version:
228
229 ```bash
230 claude mcp add --scope user jev -- node /absolute/path/to/jev-mcp/dist/index.js
231 ```
232
233 Releasing: bump `version` in `package.json`, `.claude-plugin/plugin.json`, and
234 `.claude-plugin/marketplace.json` together, then `npm publish`. The plugin cache is
235 keyed by version, so an unbumped plugin keeps serving the old snapshot.
236
161237 The offline suite runs the real built server over stdio against a local stand-in for
162238 the TypeSafe API and asserts on the request bodies it actually sends, so a regression
163239 in what reaches the model fails the build.
package.json +14 −1
@@ -1,14 +1,23 @@
11 {
22 "name": "jev-mcp",
3 "version": "0.2.0",
3 "version": "0.3.0",
44 "description": "MCP server exposing TypeSafe Jev typed judgments: classify, score, check, a batched multi-question ask, and model discovery.",
55 "type": "module",
66 "license": "MIT",
7 "repository": {
8 "type": "git",
9 "url": "git+https://github.com/rashedInt32/jev-mcp.git"
10 },
11 "homepage": "https://github.com/rashedInt32/jev-mcp#readme",
12 "bugs": {
13 "url": "https://github.com/rashedInt32/jev-mcp/issues"
14 },
715 "bin": {
816 "jev-mcp": "./dist/index.js"
917 },
1018 "files": [
1119 "dist",
20 "skills",
1221 "README.md",
1322 "LICENSE"
1423 ],
@@ -25,6 +34,7 @@
2534 "scripts": {
2635 "build": "tsc",
2736 "prepare": "npm run build",
37 "prepublishOnly": "npm run typecheck && npm test",
2838 "start": "node dist/index.js",
2939 "typecheck": "tsc --noEmit",
3040 "test": "node --test test/*.test.mjs",
@@ -41,5 +51,8 @@
4151 },
4252 "engines": {
4353 "node": ">=20.12"
54 },
55 "publishConfig": {
56 "access": "public"
4457 }
4558 }
skills/jev/SKILL.md added +115
@@ -0,0 +1,115 @@
1 ---
2 name: jev
3 description: >
4 Get typed, calibrated judgments from the connected `jev` MCP tools when the same
5 semantic decision repeats across many items — triaging files, ranking candidates,
6 checking claims against their sources, scoring a diff against each requirement — or
7 when you need a calibrated number to threshold on. Prefer an ordinary answer for a
8 single judgment in conversation. Not for judgments that ship inside the user's
9 application; those belong in SDK code, not in a tool call.
10 ---
11
12 # Jev judgments over MCP
13
14 Jev is a System One model. It returns a typed answer and a calibrated probability
15 distribution, never prose. The `jev` MCP server exposes it as five tools.
16
17 This skill covers **when to call those tools and how to shape the call**. For the
18 underlying theory — primitive semantics, state design, composition patterns — use the
19 `typesafe-ai` skill or the live docs at <https://docs.typesafe.ai>. Do not duplicate
20 that material here.
21
22 If the `jev_*` tools are not in your tool list, this skill does not apply. Call
23 `jev_models` to confirm the key works before assuming a failure is your own.
24
25 ## Three-way test
26
27 Decide which of these you are in before calling anything.
28
29 **Answer it yourself** when the judgment happens once, in conversation, and you can
30 explain your reasoning. That is most judgments. An ordinary answer carries an argument
31 the user can push back on. Jev returns a number and a label, which is weaker in
32 dialogue. Do not reach for a tool to look rigorous.
33
34 **Call the tool** when the same judgment repeats across many items, or when you need a
35 calibrated number to threshold on. Triaging forty files. Ranking twenty candidates.
36 Checking fifteen claims against their sources. Scoring a diff against each requirement
37 in a spec. Enumerate the candidates in code or by hand first; Jev only selects among
38 what you supply. Nothing is installed and no key is configured; the server holds it.
39
40 **Write SDK code** when the judgment runs in the user's application, on their traffic,
41 after you are gone. Then it belongs in their repo under test, not in a transcript.
42 Follow the `typesafe-ai` skill for that.
43
44 Do not call a tool to prototype what will become shipped code. Write the code.
45
46 ## Pick the tool
47
48 | The answer is | Tool | Notes |
49 | --- | --- | --- |
50 | One of a set you define | `jev_classify` | You supply `options`. Jev cannot invent one. |
51 | A degree on an ordered scale | `jev_score` | You supply `levels`, lowest first, at least two. |
52 | Yes or no | `jev_check` | Returns the probability of yes. No separate confidence. |
53 | Several questions, one subject | `jev_ask` | Mixes all three types. Up to 64 questions. |
54
55 When several labels can be true at once, that is not one `jev_classify`. It is one
56 `jev_check` per label, batched through `jev_ask`.
57
58 ## Prefer `jev_ask`
59
60 Jev prefills the state once and scores every question in one forward pass. Extra
61 questions add almost no latency or cost. One batched call beats a loop by a wide
62 margin on document-heavy state.
63
64 So ask everything you might need, including speculative branches. Questions cannot see
65 each other's answers, so state any premise explicitly in the question itself. Let your
66 own reasoning decide which answers apply afterwards.
67
68 Give each question a stable `id`. The id is never sent to the model, so the question
69 must carry its full meaning on its own.
70
71 ## Shape the call
72
73 Put the evidence in `state`. A string for text, an object or array when it has parts.
74 Include what the question needs to be answerable: the diff, the requirement, the
75 relevant file, the prior decision.
76
77 Put the judgment in `question`. State it in full. This is the only instruction Jev sees.
78
79 Describe every option and level concretely. A level must stand on its own without
80 reading its neighbours. Weak option descriptions are the most common cause of a bad
81 answer.
82
83 Leave `add_none` alone unless one option must always apply. The default no-match option
84 lets Jev decline rather than being forced into a wrong pick.
85
86 ## Read the answer
87
88 Never act on the label alone. Every response carries the full distribution.
89
90 `jev_classify` and `jev_score` return `action`: `act`, `review`, or `abstain`. It is
91 derived from `confidence` against `act_above` and `review_above`. `jev_check` returns
92 `verdict`: `yes`, `no`, or `uncertain`.
93
94 Treat `review` and `uncertain` as instructions to look at the evidence yourself, not as
95 a soft yes. Raise the thresholds when being wrong is expensive, and say that you did.
96
97 Confidence measures how concentrated the distribution is. It is not a claim that the
98 answer is correct. Two good options split probability and look uncertain.
99
100 For `jev_score`, the number is a position on the `levels` array you supplied. Read
101 `legend` to map it back.
102
103 For `jev_classify`, check `none_option` before interpreting a no-match result. If you
104 already used the name `none`, the added option took a different key.
105
106 ## Do not
107
108 Do not present a Jev answer as fact. Report the answer, the confidence, and what you
109 did about it.
110
111 Do not batch questions about unrelated subjects. One state, one subject.
112
113 Do not use a Score for a category, or a Choice for a degree.
114
115 Do not paste secrets, keys, or credentials into `state`.
src/index.ts +10 −1
@@ -39,7 +39,16 @@ import {
3939 readPositiveInt,
4040 } from "./lib.js";
4141
42 const VERSION = "0.2.0";
42 // Read from package.json so the advertised version cannot drift from the release.
43 const VERSION: string = (() => {
44 try {
45 const pkg = JSON.parse(readFileSync(new URL("../package.json", import.meta.url), "utf8")) as { version?: unknown };
46 if (typeof pkg.version === "string" && pkg.version.length > 0) return pkg.version;
47 } catch {
48 // fall through
49 }
50 return "0.0.0";
51 })();
4352
4453 // ── Configuration ───────────────────────────────────────────────────────────
4554
↑↓ navigate ↵ open esc close

Keyboard shortcuts