I created an MCP server called jev-mcp .

It is intended to be invoked during Codex work to call the TypeSafe AI decision model Jev. Implemented in Rust, it communicates with Codex via standard input/output.

Jev, TypeSafe AI’s decision model

Instead of handing the entire coding task over to another agent, I want to extract the small decisions that arise during the investigation.

For example, questions like “Is this file related to the current bug?” or “Which subsystem should be examined next?” While Codex continues the investigation, editing, and testing, we use Jev for the intermediate classification and evaluation.

Since this is still in the experimental stage, it’s not about claiming development speedups or token reductions. For now, we’ve simply set up the connection and usage.

Below are notes on the implementation of b1688a68 as of September 20.

Pass only a single decision, not the entire workflow

The coding agent’s tasks involve reading files, editing them, and running tests, with decisions about the next action interspersed between these operations.

It’s possible to handle all of that with a single model. However, for tasks such as classifying candidates or judging the relevance of supplied evidence, the starting point here is that we can break the question down into smaller parts and hand them off to a different model.

Codex examines code, diffs, and logs
    -> Gather the necessary information and questions for decision-making
    -> Send from jev-mcp to the TypeSafe API
    -> Receive a typed response and a probability distribution
    -> Codex proceeds with additional investigation, editing, and testing

We are not using the approach of handing over an entire request like “Analyze and fix this bug.”

The relevance of a change, the choice of where to investigate, and the magnitude of the impact are each treated as separate questions. Based on the results, the caller decides what action to take next.

There’s no need to ask the model for things that could be resolved by a simple string search or by running tests. If invoking Jev itself becomes the goal, it only adds more communication overhead.

Four tools have been prepared

Jev is a model that receives questions specifying a state and type, and returns structured judgments. In this server, we have exposed that entry point as four MCP tools.

ToolPurposeMain response
jev.noulDetermine whether a single condition holdsProbability of “Yes” noul
jev.choiceChoose one option from named choiceschoice, probabilities, confidence
jev.scoreEvaluate a single aspect on a scalescore, legend, probabilities, confidence
jev.batchCombine independent questions about the same materialAnswers per question ID

Here, “Choice” is used to select a single option. If you want to know which of the five files are potentially relevant, it’s more appropriate to aggregate the “Noul” results for each file rather than pose a single question.

Score isn’t an arbitrary rating out of 100; instead, we provide concrete evaluation criteria starting from the lowest level and moving upward. The current implementation handles scales from 2 to 10 levels.

For a three‑level scale, the score range is 0–2 and may include fractional values. This represents a weighted average of the level numbers, not the probability that a failure will occur.

The caller gathers the judgment material

jev-mcp itself has no functionality for searching the repository or automatically collecting the agent’s execution history.

What you pass is state and the question. Sending only a file path does not cause it to read the file’s contents.

For example, you would provide arguments to jev.batch as follows. This is an illustrative example, not an actual failure log.

{
  "state": {
    "diff": "Changed the worker retry limit from 3 to 0.",
    "failure": "worker_retries_transient_failure: expected 3 attempts, got 1"
  },
  "questions": {
    "related": {
      "type": "noul",
      "instructions": "Is the failure related to the diff?"
    },
    "investigate": {
      "type": "choice",
      "instructions": "Given the diff and failure, choose where to investigate first.",
      "criteria": {
        "worker": "Worker retry control",
        "api": "HTTP request handling",
        "unknown": "Insufficient evidence"
      }
    }
  }
}

Question IDs such as related and investigate are names used to match answers. The actual question text is written to instructions.

Including unknown among the choices prevents the model from having to pick something even when the evidence is insufficient. Of course, this does not eliminate the possibility of incorrect judgments.

Even if Jev reports that something is relevant, that does not mean the root cause has been confirmed. The subsequent steps—reading the retrial control, running the tests, and verifying the results—complete the workflow.

Consolidate questions about the same information

Repeatedly querying the same diff and failure log separately is not ideal. Therefore, independent questions can be bundled using jev.batch.

A single API request can contain a common state along with multiple questions, and the answers are returned keyed by question ID.

However, questions within a batch do not use the answers from previous questions. It is not a sequential process where “if something is relevant, choose the next step based on that reason.”

If you need to read additional logs or change the candidate set based on a prior judgment, you create a new state at that point and make the next call.

Internally, the server converts even single‑tool requests into a batch of question IDs under result. The HTTP request and response‑validation paths are standardized, with individual results placed in answers.result and batch results in answers.<question_id>.

Do not treat confidence as an accuracy rate

Both Choice and Score are accompanied by confidence, which is a metric derived from the answer’s probability distribution. Even in the TypeSafe documentation, it is treated as an indicator of whether the probabilities are concentrated on a particular option or spread out across them.

TypeSafe overview

Just because it is confidence: 0.9 does not mean that the decision has been verified to be correct with 90 % probability.

Even when planning the structure of this article, we passed the key implementation points to Jev for a single query. The model name returned was jev-1.13.0, and the Choice that selected the main topic produced the following results.

Candidateprobabilities
Focus on communication implementation0.48
Focus on division of judgment roles0.45
Insufficient judgment material0.06
Focus on savings and performance‑improvement results0.01

The selected option was the communication implementation, but its score was 0.32. The top two scores are fairly close.

Therefore, instead of taking only the selection result, we introduced a division of responsibilities, keeping the focus on communication implementation while continuing verification. Rather than outsourcing the judgment entirely, the usage is aimed at identifying where the uncertainty lies.

This is a single usage example, not an evaluation of the model’s accuracy. Moreover, this query consumed 807 input tokens and 65 output tokens, but that does not represent the total usage including Codex’s investigation and question‑generation steps. This figure alone cannot be used to claim any savings.

Both inputs and responses are validated on the Rust side

The implementation is divided into three parts: server.rs, which handles MCP; client.rs, which manages HTTP communication; and types.rs, which defines the types for inputs and responses. For MCP we use rmcp, for HTTP we use reqwest, and for JSON and schema handling we employ serde and schemars.

In the implementation of the types and validation (see the linked source), we check for unknown input fields, empty question sets, the number of choices in a Choice, the number of levels in a Score, and so on. Invalid inputs are rejected with an error before being sent to the API.

Type and validation implementation

We also do not consider it sufficient for the response to be merely parsable as JSON.

We verify that the requested question IDs are present, that the types of questions and answers match, and that the Choice answer is one of the supplied options. We also check that each probability lies between 0 and 1 and that the total probability distribution sums to 1 within an acceptable tolerance.

For Score, we confirm that the keys correspond to the appropriate levels and that the values fall within the expected ranges. However, this validation guarantees only the format and numeric bounds of the response, not the correctness of the judgment itself.

On success, we return model, answers, and usage as the structured MCP result. On error, we return isError: true and do not replace a failed evaluation with a “No” answer or a low score.

Be mindful of latency and transmitted data because it’s an external API

The MCP server runs as a local process, but the judgment does not stay local. The supplied information and questions are sent to https://api.typesafe.ai/v1/systemone.

If you use source code or logs, assume those contents will be transmitted to an external service. Include only the necessary parts and exclude authentication credentials or unrelated data. The server does not automatically strip out sensitive information.

We have imposed the following restrictions on the communication handling.

ItemCurrent Implementation
Connection timeout10 seconds
Timeout per request30 seconds
Overall time including retries and waits60 seconds
429/529Up to 2 retries
Size of successful responseUp to 8 MiB
RedirectsDo not follow

The standard wait time between retries is 0.5 seconds or 1 second. If a duration in the format Retry-After is provided, it will be respected, but even when a longer wait is requested, the total will not exceed 60 seconds. Other HTTP errors or communication errors are not automatically retried.

The error bodies returned by the API are not passed through to the tool’s output as‑is. Since they may contain sensitive information such as submitted data or credentials, they are replaced with the status code and a custom explanation we provide.

MCP Server and Skill are Separate

The repository also includes jev-decisions skill .

The MCP server provides the interface for making calls. The Skill organizes which decisions to pass, how to split questions, and how to use answers in subsequent investigations.

For example, multiple relevance assessments for the same piece of evidence are batched together. The Score focuses on a single perspective. When probabilities are close, the cause of the outcome is not determined solely by the selected result. Failed API calls are not repeatedly retried with the same arguments.

These points are described in the usage guidelines. Just because you can connect to the server does not mean that good questions are generated automatically.

Including only the Skill does not handle server connection or authentication. Using the system also requires a separate TypeSafe API key and an MCP connection. The setup is summarized in README .

Things Verified Through Testing and Things Still Unknown

We verify the communication layer using a local mock HTTP server. We test request contents, responses with mismatched types, malformed JSON, retries, timeouts, oversized responses, and so on.

In addition, there is a stdio test that actually launches jev-mcp as a child process. It checks initialization, enumeration of the four tools, tool errors when a key is not set, ping, and proper termination after standard input is closed. We also verify through this path that no strings other than the MCP output are mixed into standard output.

These can be run without invoking the actual paid API. However, the fact that the mock responded as expected does not prove that the real model will make good decisions.

What we want to reduce is the time spent on unnecessary investigations and duplicated judgments. At the same time, the effort required to aggregate evidence, API wait times, and the cost of using additional models also increase.

Going forward, we need to compare similar workflows with and without Jev, checking not only total usage and elapsed time but also whether any required files have been overlooked and whether the quality of modifications and tests is being maintained.

For now, we have managed to pass small decisions out to the external system and bring the results into be used in the workflow. The plan is to start using it and then separate the decisions that are worth extracting into a separate component from those that are faster to handle directly ourselves.