Skip to main content
Glama

dsh-jev-mcp

TypeSafe Jev decision tools for DeepSeek Harness (DSH).

Jev is a "System One" decision model: it evaluates typed questions against a state and returns structured answers with calibrated probabilities, instead of generating text. It is useful inside a coding agent for the small judgments that show up constantly: is this failure transient, which subsystem owns this bug, how severe is this defect, should this diff be blocked.

This repository contains:

  • lib/jev.mjs: a dependency-free client with jevCheck, jevRoute, jevScore, jevEvaluate, credential redaction, and probability thresholds.

  • server/jev-mcp.mjs: a dependency-free stdio MCP server that exposes the same four operations as MCP tools, so native DSH modes and code mode can both call Jev.

No vendor SDK and no runtime dependencies: plain Node 20+.

Tools

MCP tool

Question type

Use it for

jev_check

noul (yes/no)

atomic binary judgments: "is this failure transient?", "does this diff touch auth?"

jev_route

choice

picking one option: which subsystem owns a bug, which fix strategy to try

jev_score

score

ordered rubric ratings: severity, risk, complexity

jev_evaluate

any mix

several judgments against one state in a single call

All four return JSON, including the model id and token usage. Probabilities come from the service and are not post-processed, except for the coarse verdict field that jev_check adds using the thresholds below.

Related MCP server: jev-mcp

Install

Requirements: Node.js 20 or newer, a DSH install, and a TypeSafe API key from https://console.typesafe.ai/keys.

git clone <this-repo> dsh-jev-mcp
cd dsh-jev-mcp
bash scripts/install.sh

The installer copies the server to ${DSH_HOME:-$HOME/.dsh}/mcp/jev and creates ${DSH_HOME:-$HOME/.dsh}/secrets/jev.env (mode 600) if it does not exist. Put the key in that file:

printf 'TYPESAFE_API_KEY=%s\n' 'apikey_your_key_here' > ~/.dsh/secrets/jev.env
chmod 600 ~/.dsh/secrets/jev.env

Then register the MCP server in your profile patch, ${DSH_HOME:-$HOME/.dsh}/profiles/<profile>/cordis.patch.yml, using config/cordis.patch.example.yml as the template. Restart DSH afterwards: profile layers are composed at process start, so the tools appear in the next session.

On the DSH desktop app, run the server with DSH's bundled Node by pointing command at the app binary and setting ELECTRON_RUN_AS_NODE: "1" (see the example). On CLI installs plain node is enough.

Use from code mode

Code mode programs can import the library directly and call Jev inline, without a tool round trip:

import { evaluate, check, verdict } from "file:///path/to/dsh-jev-mcp/lib/jev.mjs";

// One judgment
const { probability, verdict: answer } = await check({
  state: buildLogTail,
  question: "Is this failure transient, meaning a retry could succeed?",
});
if (answer === "yes") {
  // retry
}

// Several judgments against the same state, in one API call
const result = await evaluate({
  state: diff,
  questions: {
    blocks_merge: { type: "noul", instructions: "Would a senior engineer block this diff as-is?" },
    security_risk: { type: "noul", instructions: "Does this diff introduce a security risk?" },
    severity: { type: "score", instructions: "How severe is this change?", levels: ["Cosmetic", "Minor", "Major", "Blocks release"] },
  },
});

The API key is resolved in this order: the apiKey option, TYPESAFE_API_KEY in the environment, then ${DSH_HOME:-$HOME/.dsh}/secrets/jev.env (override the path with JEV_KEY_FILE). A code mode sandbox often starts with an empty environment, so either pass apiKey explicitly or read the key file from the program.

examples/demo.mjs runs four end-to-end judgments (failure triage, routing, severity scoring, and a code review gate) and prints a summary table:

node examples/demo.mjs

examples/triage.mjs runs one command, captures its output, and asks three questions about the failure:

node examples/triage.mjs "npm test"

Library API

const result = await evaluate({ state, questions, model, apiKey, keyFile, apiUrl, timeoutMs, maxRetries, redactState });
const checkResult = await check({ state, question, criterion, model, ...rest });
const routeResult = await route({ state, question, options, model, ...rest });   // options: { name: description }
const scoreResult = await score({ state, question, levels, model, ...rest });     // levels: lowest first

Shapes:

  • check: { type: "noul", probability, verdict, model, usage }

  • route: { type: "choice", choice, probabilities, confidence, model, usage }

  • score: { type: "score", score, legend, probabilities, confidence, model, usage }

  • evaluate: { answers, model, usage }

Errors throw JevError with a code: missing_key, invalid_arguments, auth (401), billing (402), invalid_request (422), api_error, unreachable, bad_response.

Retries happen only for 429 and 5xx responses, twice by default.

Thresholds

jev_check maps the probability to a coarse verdict with THRESHOLDS = { act: 0.7, ask: 0.4 }:

  • probability >= 0.7 becomes "yes"

  • probability < 0.4 becomes "no"

  • otherwise "unsure"

Treat these as sane defaults, not calibrated policy. Tune them against your own outcomes. For jev_route, compare the per-option probabilities; the separate confidence field is not the same quantity.

Security and privacy

  • Every call sends the state to TypeSafe at api.typesafe.ai, together with your API key. Send only what the judgment needs: the failing log tail, the changed hunk, a compact situation summary. Do not send whole repositories or credential files.

  • redactState: true (library) runs a conservative redactor over state first: private key blocks, common token shapes, bearer tokens, email addresses, long hex strings. It is a safety net, not a guarantee. Review what you send.

  • The API key is read from a mode-600 file or the environment. It is never written to logs. The MCP server writes nothing to stdout except JSON-RPC; diagnostics, if any, would go to stderr.

  • No analytics, no telemetry, no third-party dependencies.

Design notes

  • The MCP server speaks newline-delimited JSON-RPC over stdio (the MCP stdio framing) and implements initialize, ping, tools/list, and tools/call. It has no dependencies because DSH's stdio transport only needs that much.

  • The protocol version is negotiated: the server echoes the client's requested version when it is one of the supported versions, otherwise it offers the newest it knows.

  • Questions are validated locally (types, instructions, choice options, score levels), so malformed requests fail before any network call.

  • The client uses AbortSignal.timeout, so a hung request cannot pin a session.

Tests

npm test

The suite mocks the HTTP API and spawns the MCP server as a child process; it never contacts the network and needs no key.

Layout

lib/jev.mjs                 client, redaction, thresholds
server/jev-mcp.mjs          stdio MCP server
examples/triage.mjs         run a command, ask Jev about the failure
config/cordis.patch.example.yml   DSH profile snippet
scripts/install.sh          copy server into a DSH home
test/                       node:test suite

License

MIT. See LICENSE.

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    B
    maintenance
    An MCP server that exposes eleven typed decision tools—check, choose, score, judge, route, triage, guard, grep, rank, compact, and ask—so agents can make fast, branchable yes/no, option-pick, score, and filtering decisions on text via TypeSafe's Jev model.
    12 npm
    2
    MIT
  • A
    license
    Not graded
    quality
    A
    maintenance
    Serves three judgment tools over MCP stdio that return typed answers — yes/no, choice, and score — with calibrated probabilities instead of prose. It runs fully offline by default, requiring an explicit live provider and credential before any state leaves the host.
    66 npm
    Apache 2.0
  • A
    license
    Not graded
    quality
    A
    maintenance
    Enables any MCP host to obtain typed judgments — yes/no, choice, and score — with calibrated probabilities instead of prose, exposed as three tools over stdio. It runs fully offline with synthetic answers by default, and only transmits state to TypeSafe or OpenRouter when live routing is explicitly configured, reporting exactly what leaves at startup.
    66 npm
    Apache 2.0