openjev-router
This MCP server exposes local OpenJev-backed probabilistic decision tools for categorical choices, binary judgments, tiered scoring, and backend status checks.
Check backend, model, and endpoint reachability with
decision_status.Make calibrated categorical decisions with
decide_choiceover candidate options, optionally using criteria and abstention.Judge whether a binary assertion is TRUE or FALSE for a given state with
decide_noul.Rate a state across ordered tiers (e.g. severity) with
decide_score, returning expected score and per-tier probabilities.Accept context as a JSON object (
state), plus optional criteria and abstention flags.Support agent routing, guardrails, intent detection, and severity rating via MCP.
Allows using Ollama as a backend for typed probabilistic decisions, including Choice, Noul, and Score, with calibrated probabilities and abstention via OpenJev-compatible clients.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@openjev-router Route this ticket to the correct department"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
mcp-agent-openjev
Local typed probabilistic decision service — Choice, Noul, Score with
calibrated probabilities and abstention — powered by
OpenJev (the tuned 27B Qwen3.5 decision
model) over Bionic's Open Responses endpoint in
LM Studio.
OpenJev is tuned so a single output position carries the decision: one score per
option letter, then a calibration step. LM Studio's /v1/completions and
/v1/chat/completions never return logprobs (and cap top_logprobs at 20), so
this project drives the one surface that does — /v1/responses — with the two
settings that make the readout exact: reasoning.effort: "none" (the model emits
exactly one token, the answer letter) and undistorted logits
(temperature: 1.0, top_p: 1.0, frequency_penalty: 0, presence_penalty: 0).
For whom: anyone with a local OpenJev GGUF in LM Studio who wants System-1 style typed decisions (routing, guardrails, intent detection, severity ratings) — and agents that want them as an MCP tool.
How it works
For a Choice over N options, the client:
labels the options
A,B,C, … and builds the OpenJev prompt (State:/Question:/Options:);sends it to
POST /v1/responseswithmax_tokens=1,reasoning.effort=none, and an uncappedtop_logprobs=64;reads each letter's log-probability at the answer position;
temperature-scales the logits (softmax,
T=0.85) for calibrated posterior probabilities;abstains (
value="UNKNOWN") when the winner's confidence falls belowJEV_ABSTAIN_THRESHOLD(autoscales it to1.25 / N), preserving the raw argmax intentative_value.
Related MCP server: ember
Install
Requires uv and a running LM Studio with the OpenJev model loaded:
uv syncCopy .env.example to .env and set JEV_MODEL to the OpenJev model key LM
Studio serves (default openjev). Point JEV_BASE_URL at the LM Studio server
(default http://127.0.0.1:1234).
Usage
One-shot CLI
uv run mcp-agent-openjev doctor
uv run mcp-agent-openjev choice "unauthorized login from an unknown IP" `
-c billing -c tech_support -c security `
--criteria "pick the handling department"
uv run mcp-agent-openjev noul "the request is urgent" "this is a support request"
uv run mcp-agent-openjev score "PII exposed in a public bucket for 3 days" `
-t low -t medium -t high -t criticalScore tiers
A tier is either a label string or a dict with label (or value) and an
optional score:
// plain labels - weight is the position in the list
["low", "medium", "high", "critical"]
// explicit form - 'score' must equal the tier's position
[{"label": "low", "score": 0}, {"label": "critical", "score": 3}]score must match the tier's position because a tier's weight is its ordinal
position - that is what makes expected_score an expected tier index, which is
what callers compare against. A tier dict without label/value, a duplicate
label, or a score that disagrees with the position is rejected
(InvalidTierError, HTTP 400 on the service) rather than silently degraded to a
positional label - a degraded tier is rendered into the prompt as 0. 0, so the
model ends up ranking meaningless numbers. level_probabilities is always keyed
by tier label.
The response also echoes the scale it used, so the score never has to be read against an assumed convention:
{
"expected_score": 2.2056,
"level_probabilities": {"low": 0.00005, "high": 0.783, "critical": 0.211, "UNKNOWN": 0.0002},
"tier_weights": {"low": 0.0, "high": 2.0, "critical": 3.0, "UNKNOWN": 0.0},
"confidence": 0.783,
"abstained": false
}tier_weights is ordered by tier, and includes UNKNOWN (weight 0.0) when
abstention is enabled, so expected_score can be recomputed from
level_probabilities alone.
HTTP service
uv run mcp-agent-openjev http --host 127.0.0.1 --port 8377
curl http://localhost:8377/health
curl -X POST http://localhost:8377/v1/decide/choice -H "Content-Type: application/json" -d '{
"state": {"ticket": "unauthorized login from an unknown IP"},
"candidates": ["billing", "tech_support", "security"],
"criteria": {"billing": "payments", "security": "unauthorized access"}
}'Endpoints: POST /v1/decide/choice, POST /v1/decide/noul,
POST /v1/decide/score, GET /health. Choice and score accept optional
model / temperature / abstain_threshold overrides per request.
MCP server
uv run mcp-agent-openjev serve # stdio (default)
uv run mcp-agent-openjev serve --http # streamable-http on :8030Tools: decide_choice, decide_noul, decide_score, decision_status.
Wire it into opencode's MCP block:
"mcp-agent-openjev": {
"type": "local",
"command": ["uv", "--project", "<path-to-mcp_agent_openjev>", "run", "python", "-m", "mcp_agent_openjev", "serve"],
"environment": {}
}Examples
uv run python examples/ticket_router.py
uv run python examples/severity_score.pyDecisions
Type | Purpose | Returns |
| categorical decision over candidates |
|
| binary truth judgment |
|
| ordered tier evaluation |
|
Configuration
Variable | Default | Meaning |
|
| LM Studio server URL |
|
| Model key LM Studio serves |
| (empty) | Sent as |
|
| Softmax temperature scaling (OpenJev |
|
| Noul sigmoid scale (OpenJev |
|
| Noul sigmoid bias (OpenJev |
|
| Option orders to average over (accuracy mode; costs latency) |
|
| Abstention threshold; or |
|
| Backend request timeout (seconds) |
The released helper's READOUT_T, READOUT_NOUL_T, READOUT_NOUL_BIAS and
READOUT_PERMS variables are also honoured.
License
The adapter, service, CLI and MCP surface in this repository are MIT (see
LICENSE). Two upstream licenses are carried alongside because the calibration
mirrors their code and model:
LICENSE-APACHE-2.0.txt— Apache 2.0. Covers theopenjev-servercode this calibration mirrors (helper/,serve/) and OpenJev's Apache-2.0 open base model, as requested by the OpenJev model repo.CC BY-NC 4.0 — the OpenJev weights. Free for research and other non-commercial use with attribution; commercial use requires a separate licence (open a discussion on the model repo). The terms travel with the GGUF, so they apply however the model is served.
Available Tools
4 toolsdecide_choiceA
Select the single best candidate for the state, with calibrated probabilities.
state: dict of context to evaluate. candidates: the allowed options (strings). criteria: how to judge them (plain string or mapping). allow_abstain: when the best confidence is too low, value becomes UNKNOWN and tentative_value keeps the raw argmax. Returns ChoiceDecision JSON.
Inputs: state (required), candidates (required), criteria, allow_abstain. Returns: result
decide_noulB
Judge whether a binary assertion is TRUE or FALSE for the given state.
Inputs: state (required), assertion (required). Returns: result
decide_scoreC
Rate the state across ordered tiers (e.g. severity levels).
tiers: ordered list of strings, or dicts with 'label'/'value' and an optional 'score' weight. Returns ScoreDecision JSON with expected_score and per-tier probability mass.
Inputs: state (required), tiers (required), criteria, allow_abstain. Returns: result
decision_statusA
Report the configured backend, model, and endpoint reachability.
Inputs: no named parameters. Returns: result
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
TDQS
Scored across 4 tools
Each tool has a distinct purpose: decision_status reports system state, while the three decide_* tools handle different decision types (choice among options, binary assertion, and tiered scoring). No overlap or ambiguity between them.
Three tools follow the consistent 'decide_' prefix with action verbs, but decision_status deviates with a noun-based name. This is a minor inconsistency, but the pattern is still recognizable.
With 4 tools, the server is well-scoped for a decision-routing purpose. Each tool covers a necessary aspect without redundancy or bloat.
The surface covers status, choice selection, boolean judgment, and scoring, which aligns with the server's purpose. A possible gap is a tool for explaining or justifying decisions, but the provided functionality is solid.
Maintenance
Related MCP Connectors
MCP-first toolbox for agents: KV storage, auth, queue, and utility tools. Free in early access.
Free OpenAI-compatible inference with signed provenance receipts and 3 focused MCP tools.
MCP server for your apps' tools and custom tools, plus hosted AI agents and approval-gated workflows
LLM Orchestration Agent (Mcp)
Related MCP Servers
- AlicenseNot gradedqualityBmaintenanceEnables coding agents to query a locally running Kev decision model through MCP tools, returning calibrated probabilities for typed questions such as yes/no, choice, and score.Apache 2.0
- AlicenseAqualityBmaintenanceEnables coding agents to request calibrated probability judgments over typed questions—yes/no, named choices, or ordered scores—about a described situation, returning one probability per option rather than free text. Runs Cloudflare's Clef model locally on Apple Silicon Macs, with images or video frames optionally attached, and answers delivered as a single MCP tool call.183 PyPIMIT
- AlicenseNot gradedqualityBmaintenanceExposes local MCP tools for typed, calibrated LLM decisions — gating risky actions, yes/no judging, classification, ordinal rating, and multi-question decisions in one pass. Runs on the model Devin's CLI already provides via ACP (or a fully offline local backend), so agents get calibrated confidence with no extra API key, no Ollama, and no waitlist.34 PyPI6MIT
- AlicenseAqualityCmaintenanceAn MCP server that answers typed noul/choice/score questions about a given state with probability distributions and confidence, for routing, escalation, and gating decisions. It runs on pluggable backends (local Needle 3 or Laya models, or any OpenAI-compatible endpoint) and logs every decision so recorded ground truth can be used to track calibration.3MIT