Lint the questions you send to TypeSafe's Jev model: a VS Code extension, a command line for CI and an MCP server for AI agents
- ✓Open-source license (MIT)
- ✓Actively maintained (<30d)
- ✓Clear description
- ✓Topics declared
- ✓Documented (README)
claude mcp add decisionlint -- npx -y decisionlint{
"mcpServers": {
"decisionlint": {
"command": "npx",
"args": ["-y", "decisionlint"]
}
}
}MCP Servers overview
# DecisionLint
Lint the questions you send to a decision model: TypeSafe's Jev, OpenAI's
Decisions API, and every platform that serves either shape, from OpenRouter
and Vercel AI Gateway to a llama.cpp on your own machine. DecisionLint reads
them out of your JSON and your code, and reports the ones written in a way
that is measured to produce bad answers. Linting makes no API calls and needs
no key. One optional command asks the model itself to check a file, using
your own key, at whichever platform you send it to.
By the author of the LE tools.

## Install
- **VS Code:** search for DecisionLint in the Extensions view, or run
`ext install nolindnaidoo.decisionlint`.
- **Cursor, VSCodium and other editors that use Open VSX:** the same name,
`nolindnaidoo.decisionlint`.
- **Command line:** `npx decisionlint`. Nothing to install first.
- **MCP server, for an agent host:** `npx -y decisionlint-mcp`.
## At a glance
- **Lints as you type** in JSON, JavaScript, TypeScript, Python, Rust and Go.
- **Fixes on the lightbulb** for the mechanical mistakes, and a way to silence
a finding you disagree with.
- **The same rules in CI**, from a command line, and for AI agents, from an
MCP server. One settings file covers all three.
- **Every platform, one set of rules.** TypeSafe's `/v1/systemone` shape and
OpenAI's `/v1/decisions` shape, `predicate`, `choices` and `levels`, are
read into the same rules, and the AI SDK's `decide()` call for either.
The limits a request is held to are the platform's own, read from the
model id's spelling, a URL in the file or a `target` setting: Vercel's 255
choices, LiteLLM's 128 questions, none where a platform publishes none.
- **Two optional commands that use your own key**: one asks the model
whether your questions have problems a text pattern cannot see, and one
re-sends a question with its layout changed to see whether the answer
holds. Both go to the platform the model id names, or to an endpoint you
set, a local llama.cpp included.
A request like this one looks fine and has three problems:
```json
{
"model": "jev-latest",
"state": "My parcel came three days late and the lid is cracked.",
"questions": {
"team": {
"type": "choice",
"instructions": "Which team should handle this?",
"criteria": { "billing": "Charges and refunds", "delivery": "Late or damaged parcels" }
},
"late": { "type": "noul", "instructions": "Did more than two parcels arrive late?" }
}
}
```
| Finding | Why it matters |
|---|---|
| `DEC001` on `jev-latest` | The alias moves with each release, so answers can change with no change on your side |
| `DEC004` on `team` | A message about neither team is still forced into one of them |
| `DEC102` on `late` | Jev does not count reliably. Ask about each parcel and count in code |
## What it catches
Jev and Luna guarantee the type of the answer, not the answer. A badly formed
question still comes back with a confident-looking number. These rules catch
the mistakes that can be read from the text.
| Code | Name | Default | What it means |
|---|---|---|---|
| DEC000 | unreadable | hint | Part of the question is built at runtime, so the rules that need it did not run |
| DEC001 | unpinned-model | warning | An alias such as `jev-latest` moves with each release, so answers can change with no change on your side |
| DEC002 | choice-option-limit | error | A Choice has more than 255 options. The API rejects it |
| DEC003 | score-level-limit | error | A Score has more than 10 levels. The API rejects it |
| DEC004 | no-fallback-option | info | A Choice has no `other` or `none of the above`, so an input that fits no option is forced into one. The largest cost measured anywhere, and the hardest to tell from text |
| DEC005 | duplicate | error | A question id, option or level appears twice. In an object the later one silently replaces the earlier |
| DEC006 | criteria-shape | error | A Choice needs a map of options, a Score needs an array of levels, and a Noul takes `true` and `false` |
| DEC007 | invalid-question | error | The type is missing or unknown, or the instructions are an empty string |
| DEC008 | numeric-levels | warning | Score levels are bare numbers. Jev matches the state against each description and never sees its position |
| DEC009 | too-few-options | info | A Choice with one option or a Score with one level gives every input the same answer |
| DEC010 | unused-disable | warning | A `decisionlint-disable` comment silences nothing, so it can only hide a finding added later |
| DEC011 | description-repeats-name | info | An option's description only repeats its name, or a Noul's criteria say yes and no. Jev matches the state against the description, and this one says nothing |
| DEC012 | terse-instructions | info | The instructions are one or two words, so the question leans on its id. Jev is sent the instructions and never the id |
| DEC013 | newer-model | info | `model` names a Jev version newer than the one these rules were measured on, so a finding may cost nothing there and a weakness new to it is not looked for |
| DEC014 | question-limit | error | A request has more questions than the platform it goes to takes in one call. The platform rejects it |
| DEC101 | double-negative | info | A question negates twice in one clause, or a negation sits directly on another |
| DEC102 | arithmetic | warning | The question asks Jev to count or compare numbers |
| DEC103 | date-comparison | info | The question asks Jev to order two times or measure the gap between them |
| DEC104 | compound | off | A Noul joins two judgments with 'and' |
| DEC105 | generation | warning | The question asks for a value or for text to be written |
| DEC107 | negated-noul | off | A Noul with no criteria is phrased so that yes means something is absent |
| DEC108 | inverted-criteria | off | A Noul's 'true' criterion describes the negative case |
| DEC110 | degree-levels | warning | Score levels are degree words, bare labels, or one word turned up and down. Nothing for Jev to match the state against |
| DEC111 | numeric-encoding | off | The question refers to a value by hex or RGB encoding |
| DEC112 | undefined-boundary | info | A Noul with no criteria turns on a word such as large, often or enough |
| DEC301 | model-counting | warning | Jev reads the question as needing counting or arithmetic |
| DEC302 | model-undefined-boundary | info | Jev reads a Noul as turning on a matter of degree with no stated line |
| DEC303 | model-overlapping-options | info | Jev reads two options of a Choice as covering the same cases |
| DEC304 | model-overlapping-levels | warning | Jev reads two levels of a Score as the same situation |
| DEC305 | model-label-mismatch | warning | Jev reads an option as named for one thing and described as another |
| DEC306 | model-criteria-off-topic | info | Jev reads the criteria as deciding something the instructions do not ask |
| DEC307 | model-ordered-options | info | Jev reads a Choice's options as steps on one scale, which a Score would place between |
| DEC308 | model-unordered-levels | info | Jev reads a Score's levels as unordered categories, which a Choice would pick from |
| DEC309 | model-depends-on-sibling | info | Jev reads a question as needing another question's answer from the same request |
| DEC310 | model-overlapping-questions | info | Jev reads two questions in one request as asking for the same judgment |
| DEC311 | model-answer-not-in-state | info | Jev reads the state written in the file as not holding what the question asks about |
| DEC312 | model-orders-in-state | info | Jev reads part of the state written in the file as giving orders to its reader |
Each finding links to [its own page](https://github.com/nolindnaidoo/decisionlint/blob/main/docs/rules/README.md), with an example that is flagged, one that is not, how to fix it and how to silence it. Each page links on to the TypeSafe documentation the rule comes from.
## Where it looks
- JSON and JSONC request bodies, in TypeSafe's shape or OpenAI's.
- Object literals in JavaScript and TypeScript, including requests passed
inline to `client.systemOne(...)` and `client.decisions.create(...)`, and
the Vercel AI SDK's `decide({ questions })` for either vendor, with the
model id inside `typeSafeAi.decisionModel('...')` read as the model.
- `noul()`, `choice()` and `score()` calls in files that use
`@typesafe-ai/sdk`.
- Python: dicts, `Noul(...)`, `Choice(...)` and `Score(...)` from
`typesafe_sdk`, from LangChain's `langchain_typesafe` with its
`NoulCriteria(...)`, or from a library that wraps either, and requests
passed to `system_one(...)` or `client.decisions.create(...)`.
- Rust: the JSON inside `json!`, structs and enum variants named for a
question type, and `::noul(...)` style constructors.
- Go: maps with string keys, structs with a `Type` field, structs named
for a question type, and OpenAI's Go SDK, `openai.DecisionNewParams{...}`
with its `OfPredicate`, `OfChoice` and `OfScore` questions.
- A request pasted as JSON into a string, in any of these languages.
- Any other language: have your program write the request it sends to a
`.jev.json` file and open that. Every check runs on it, and nothing in it
is built at runtime, so nothing is skipped.
- `jev/ask` rules in an `oxlint-plugin-jev` config.
## OpenAI's Decisions API
OpenAI's Decisions API, model `gpt-6-luna`, asks the same kind of question
as Jev in a different shape. DecisionLint reads it from 0.5.0. What that
covers, exactly:
- **Read.** A `/v1/decisions` request body, `client.decisions.create(...)`
in TypeScript and Python, and the Vercel AI SDK's `decide()`. A
`predicate` is read as a Noul, `choices: [{ value, description }]` as a
Choice's options and `levels: [{ label, description }]` as a Score's
levels, into the same rules.
- **What people ask about decisionlint
What is nolindnaidoo/decisionlint?
+
nolindnaidoo/decisionlint is mcp servers for the Claude AI ecosystem. Lint the questions you send to TypeSafe's Jev model: a VS Code extension, a command line for CI and an MCP server for AI agents It has 1 GitHub stars and its last recorded update is dated 2026-10-15.
How do I install decisionlint?
+
You can install decisionlint by cloning the repository (https://github.com/nolindnaidoo/decisionlint) or following the README instructions on GitHub. ClaudeWave also provides quick install blocks on this page.
Is nolindnaidoo/decisionlint safe to use?
+
Our security agent has analyzed nolindnaidoo/decisionlint and assigned a Trust Score of 95/100 (tier: Verified). See the full breakdown of passed checks and flags on this page.
Who maintains nolindnaidoo/decisionlint?
+
nolindnaidoo/decisionlint is maintained by nolindnaidoo. The last recorded GitHub activity is dated 2026-10-15, with 0 open issues.
Are there alternatives to decisionlint?
+
Yes. On ClaudeWave you can browse similar mcp servers at /categories/mcp, sorted by popularity or recent activity.
Deploy decisionlint to your cloud
Ship this repo to production in minutes. Each platform spins up its own environment with editable env vars.
Maintain this repo? Add a badge to your README
Drop the badge into your GitHub README to show it's tracked on ClaudeWave. Each badge links back to this page and reflects the live Trust Score.
[](https://claudewave.com/repo/nolindnaidoo-decisionlint)<a href="https://claudewave.com/repo/nolindnaidoo-decisionlint"><img src="https://claudewave.com/api/badge/nolindnaidoo-decisionlint" alt="Featured on ClaudeWave: nolindnaidoo/decisionlint" width="320" height="64" /></a>More MCP Servers
Reverse engineer anything with agents, from app behavior down to native binaries.
Fair-code workflow automation platform with native AI capabilities. Combine visual building with custom code, self-host or cloud, 400+ integrations.
User-friendly AI Interface (Supports Ollama, OpenAI API, ...)
An open-source AI agent that brings the power of Gemini directly into your terminal.
Real-time global intelligence dashboard. AI-powered news aggregation, geopolitical monitoring, and infrastructure tracking in a unified situational awareness interface
🕷️ An adaptive Web Scraping framework that handles everything from a single request to a full-scale crawl! Don't be shy, join here: https://discord.gg/EMgGbDceNQ and follow here for daily tips and tricks: https://x.com/Scrapling_dev