Decide — free typed judgments from Claude Code

SkillAI & models

Use when the user wants to try BlockRun's free typed-judgment endpoint (POST api.blockrun.ai/v1/decide, served by OpenJev) on their own data from Claude Code, yes/no, labelled choice, or scored-rung questions over a text or JSON state, up to 64 per call. Not an MCP tool: call it with curl from the shell. Covers the request shape, what the confidence number does and does not mean, and why OpenJev is not Jev.

Instructions available. Your AI can read the instructions. Execution depends on the setup they require.

Add ahel to your AI once: Claude, ChatGPT, Cursor, Claude Code or Codex. Then ask it to use this.

Then ask your AI: use the Decide skill

What this skill tells your AI

The instructions your AI receives, as published by blockrunai/blockrun-mcp in skills/decide/SKILL.md and read by ahel’s review.

POST https://api.blockrun.ai/v1/decide takes a state (a message, a ticket, a diff, a tool result, a row of data) and up to 64 questions, and returns a typed answer with a number beside each one. No prose to parse. The backend is OpenJev, an open-source natural-language-inference model BlockRun hosts.

It is not a BlockRun MCP tool, on purpose: you are already a frontier model, and for a one-off "is this urgent?" you are the better judge. The endpoint earns its place when the user wants the same fixed ruler over many items, or is prototyping a judgment they will later run from a pipeline without a model. From Claude Code, call it with curl from the shell.

What it is not

  • Not paid, not x402. It is free behind a registered API key. There is no payment header, no wallet path, and it is served by api.blockrun.ai, not the x402 gateway at blockrun.ai (a POST to blockrun.ai/v1/decide is a 404).
  • OpenJev is not Jev. Jev is TypeSafe's model. OpenJev is an unaffiliated open-source NLI cross-encoder (MIT, published by AlexWortega on Hugging Face, Qwen3.5 4B base). It is not made by the people who make Jev and it is not a smaller or free tier of it. BlockRun does not sell or resell Jev. There is one backend, and the x-blockrun-backend response header names it on every call.
  • No quality comparison exists. BlockRun expects OpenJev to be materially weaker than Jev, has not benchmarked the two, and will not put a number on the gap. Do not invent one. The model authors' own zero-shot NLI figures, with attribution and a read date, are at https://blockrun.ai/openjev.

Getting a key without changing how the MCP pays

Keys are minted at https://user.blockrun.ai/dashboard/keys (brk_live_…, shown once; registration, not a card).

For experiments, export the key in the shell and leave the MCP server alone:

export BLOCKRUN_API_KEY=brk_live_…

Do not write it to ~/.blockrun/.api-key just to try decide. The MCP server reads that file at startup and a present key moves every paid tool from wallet mode to account billing — the same switch BLOCKRUN_API_KEY in the MCP server's own config makes. That is fine if the user wants account billing (see the blockrun-setup skill); it is a surprise if they only wanted a free judgment. If the MCP is already on account billing, the same key works for both.

Request

curl -sS -X POST https://api.blockrun.ai/v1/decide \
  -H "authorization: Bearer $BLOCKRUN_API_KEY" \
  -H "content-type: application/json" \
  -D /dev/stderr \
  -d '{
    "state": "Help! My payouts have been failing for 3 days.",
    "questions": {
      "is_urgent":   { "type": "noul",   "instructions": "Does this convey urgency?" },
      "department":  { "type": "choice", "instructions": "Which team should handle this?",
                       "criteria": { "billing": "Payments, refunds",
                                     "technical": "Bugs, outages" } },
      "frustration": { "type": "score",  "instructions": "How frustrated is the customer?",
                       "criteria": ["Calm", "Frustrated", "Very angry"] }
    }
  }'

-D /dev/stderr shows the response headers (backend, rate-limit) without mixing them into the JSON on stdout.

FieldTypeRequiredNotes
statestring | object | arrayyesWhat to judge. Text and JSON both work. If a fact matters, put it in the state — the model looks nothing up.
questionsobjectyesYour own id → question. 1 to 64 per call.
modelstringnoDefaults to openjev, the only backend. Leave it out.

Three question types, one operation underneath (state = premise, question = hypothesis, answer = how strongly the premise entails it):

TypecriteriaYou get back
noulnone — it is true or falseone number for how strongly the state supports the claim
choicemap of label → its meaning, 2 to 255 entriesthe top label, plus a share per option
scorearray of rung descriptions in words, 2 to 255a weighted position across the rungs, plus the distribution

instructions — the question itself, in plain language — is required on all three.

Response

{
  "model": "openjev",
  "answers": {
    "is_urgent":   { "type": "noul",   "noul": 0.986 },
    "department":  { "type": "choice", "choice": "billing",
                     "probabilities": { "billing": 0.71, "technical": 0.29 },
                     "confidence": 0.71 },
    "frustration": { "type": "score",  "score": 1.42,
                     "legend": { "0": "Calm", "1": "Frustrated", "2": "Very angry" },
                     "probabilities": { "0": 0.11, "1": 0.36, "2": 0.53 },
                     "confidence": 0.53 }
  }
}

score is the probability-weighted rung index (1.42 sits between "Frustrated" and "Very angry"); legend maps index → rung text.

Read this before you build a threshold

The confidence on a choice is not the probability the answer is correct.

Every option is scored against the state, and the scores are then divided by their total so they sum to one. That step throws away how strong the scores were:

  • three options each scoring 0.1 — the model supporting none of them — come back as 0.33 / 0.33 / 0.33;
  • two options scoring 0.9 and 0.85 — both strongly supported — come back as 0.51 / 0.49.

Opposite situations, identical response. The number is a share of the agreement found — an ordering with a margin. Wide means the model clearly preferred one option; narrow means it did not. It says nothing about whether any option fits, and nothing has been fitted against real outcomes the way a calibrated model's probabilities are. The same applies to score's probabilities and confidence. A noul is the raw entailment score and is not normalised — but it is not calibrated either.

Set thresholds by running the user's own labelled examples through the endpoint and looking at where the errors land, never by reading the number as a percentage. An agent that gates on confidence > 0.8 as if it were accuracy will be wrong with no error to tell it so.

Trying it on the user's data — the shape that works

  1. Build the state with everything the judgment needs: the message, the records it refers to, the policy that governs it. Fetch with blockrun_exa or blockrun_search first if a fact is missing; decide cannot look it up.
  2. Ask the independent questions together in one call — one round trip, one rate-limit hit, up to 64 answers.
  3. Combine with deterministic checks the code already has (amount, account age, "was a refund already issued").
  4. Route by margin. Act automatically where the margin is wide and the checks agree; hand narrow cases to a reasoning model — blockrun_chat with a claude-*, o-series or DeepSeek model — or to a person.

A quick evaluation loop from Claude Code: put 20–50 labelled rows in a JSON file, loop curl over them with jq, and tabulate agreement per question. That is the only number that should decide whether the endpoint is good enough for the user's task.

jq -c '.[]' examples.json | while read -r row; do
  state=$(jq -c '.state' <<<"$row"); want=$(jq -r '.label' <<<"$row")
  got=$(curl -sS -X POST https://api.blockrun.ai/v1/decide \
    -H "authorization: Bearer $BLOCKRUN_API_KEY" -H "content-type: application/json" \
    -d "{\"state\": $state, \"questions\": {\"q\": {\"type\": \"choice\",
         \"instructions\": \"Which team should handle this?\",
         \"criteria\": {\"billing\": \"Payments, refunds\", \"technical\": \"Bugs, outages\"}}}}" \
    | jq -r '.answers.q.choice')
  echo "$want,$got"
done | sort | uniq -c

What it does not do

It does not write, summarise, or explain — there is no free-text field in the response. It has no memory between calls. Image input exists in the model's v2 checkpoint but the endpoint does not expose it.

Limits and errors

StatusMeaningWhat to do
400Body did not match the schema; the response names the fieldFix the field. Common: a choice with criteria as an array, a score with a map, fewer than 2 criteria, more than 64 questions.
401Missing or invalid keyBLOCKRUN_API_KEY is unset in this shell, or wrong. Check https://user.blockrun.ai/dashboard/keys.
429Per-key hourly limit reachedWait for the interval the response carries (retry-after). The limit is enforced server-side and reported in the response — read it there; do not assume a number.

None of these cost anything; nothing here ever does.

Related

Signals

GitHub stars
395
Forks
41
Last commit
Sep 2026

Others that do the same job

Advanced
Item type
skill
Key
decide-blockrunai
Source
github.com/blockrunai/blockrun-mcp