Last updated: September 2026
How to use Jev in Claude Code
Claude Code cannot call Jev on its own. Jev is a separate hosted API with its own key, so you give the agent a path to it: a small script it runs, TypeSafe's agent skill, or a community MCP server. Set TYPESAFE_API_KEY in the environment and the SDK finds it.
The half that makes this worth doing is what happens after the decision. Add a publishing MCP server and the agent can score a draft, then ship it, without you approving each one by hand.
Three ways to give the agent Jev
This page uses the script, because a script is a file you can read, diff and test, and because the same file works unchanged in Codex, Cursor or a CI job.
Step 1: two keys, both in the environment
Jev is waitlist gated in early access and there is no free tier. Join at typesafe.ai, or skip the waitlist by reaching Jev through the Vercel AI Gateway, OpenRouter or Cloudflare. The OpenTweet key is separate, starts with ot_, and is created at /developer.
# The TypeSafe SDKs read the key from the environment.
export TYPESAFE_API_KEY="sk_your_typesafe_key"
# The OpenTweet key is separate. It starts with ot_ and comes from /developer.
export OPENTWEET_API_KEY="ot_your_key"Keys do not go in prompts
Anything you type into a prompt lands in the transcript. Export the keys in the shell that launches the agent, or load them in the script from a gitignored env file. The agent needs the script to work, not the key itself.Step 2: the script
One file. It takes a draft, asks Jev two questions in a single request, prints the answers as JSON so the agent can read them, and exits non-zero when the draft does not clear the bar.
#!/usr/bin/env node
// scripts/score-draft.mjs
// Usage: node scripts/score-draft.mjs "draft text"
// Exits 0 when the draft clears the bar, 1 when it does not.
import { TypeSafeClient, noul, score } from "@typesafe-ai/sdk";
const PUBLISH_AT = 2.0; // strength score, 0 to 4
const CONFIDENT_AT = 0.6; // confidence on that score
const SLOP_CEILING = 0.5; // probability, not a confidence
const draft = process.argv[2];
if (!draft) {
console.error("usage: score-draft.mjs <draft text>");
process.exit(2);
}
const client = new TypeSafeClient({ timeout: 4000 });
const { answers } = await client.systemOne({
model: "jev-1.13.0",
state: { draft_post: draft, platform: "X (Twitter)" },
questions: {
strength: score(
"How far will this post travel compared to a typical post from the same author?",
[
"Flops. Gets less attention than a typical post from the same account.",
"Typical. Performs about the same as the account usually does.",
"Above typical. Noticeably more reach and replies than usual.",
"Strong. Several times the usual reach. Spreads past the existing audience.",
"Breakout. Orders of magnitude beyond usual.",
]
),
slop: noul(
"This reads like it was generated by an AI rather than written by a person."
),
},
});
// A Score answer has .score and .confidence. A Noul answer has only .noul.
const verdict = {
strength: answers.strength.score,
confidence: answers.strength.confidence,
slop: answers.slop.noul,
model: "jev-1.13.0",
};
console.log(JSON.stringify(verdict, null, 2));
const passes =
verdict.strength >= PUBLISH_AT &&
verdict.confidence >= CONFIDENT_AT &&
verdict.slop < SLOP_CEILING;
process.exit(passes ? 0 : 1);The exit code is the point. It is the difference between the agent being told the answer and the agent being stopped by it. An agent that sees a failing exit code has to do something about it.
Note the two different answer shapes, which is the detail most third-party Jev examples get wrong. A Score answer carries score, legend, probabilities and confidence. A Noul answer carries only noul, a float between 0 and 1, with no confidence field. Reading answers.slop.confidence gives you undefined, and a comparison against undefined fails quietly. Details are on Choice, Score and Noul.
Pin the model id. jev-latest is an alias, and when it moves, every threshold you calibrated moves with it.
Step 3: the publishing server
One command, and the agent gains 43 tools it can call:
claude mcp add --transport http opentweet \
https://mcp.opentweet.io/mcp \
--header "Authorization: Bearer ot_your_key"Or the equivalent config entry, if you prefer to keep it in a file the project checks in:
{
"mcpServers": {
"opentweet": {
"type": "streamable-http",
"url": "https://mcp.opentweet.io/mcp",
"headers": { "Authorization": "Bearer ot_your_key" }
}
}
}The transport is Streamable HTTP and the endpoint is stateless, so there is nothing to keep running locally. The tools cover posting and threads, scheduling and batch scheduling, X Articles, the evergreen queue, analytics, inspiration search, media upload and generation, and DM campaigns. There is also a local stdio option over npx if you would rather not send a key in a header. Both are on the MCP server page, and the Claude Code specific setup is on the Claude Code skill page.
Step 4: write the loop down
The agent needs to know the script exists and what its exit code means. That belongs in the project instructions. The thresholds themselves stay in the script.
## Publishing to X
Never publish a draft without scoring it first.
1. Run `node scripts/score-draft.mjs "<draft>"`.
2. If it exits 0, publish with the opentweet MCP tools.
3. If it exits 1, print the JSON it returned, rewrite the draft and score again.
4. After three failed attempts, stop and show me all three drafts with their scores.
The thresholds live in scripts/score-draft.mjs. Do not change them
and do not publish around a failing score.Now the loop is real. Claude writes a draft, runs the script, reads the JSON, rewrites the weak parts and scores again. Each round trip is cheap enough that three attempts cost less than one round of asking a chat model to grade itself.
The stopping rule matters
Without "after three failed attempts, stop", an agent will keep rewriting. A retry cap and a handoff to a person is the difference between a gate and a loop.What this does not fix
- Jev cannot write anything. It has no free-form text output. The rewriting is still Claude's job. Jev only says how far off the draft is.
- A passing score is not proof. Jev cannot return a value outside the answer space you declared, which removes schema errors. It does not remove being wrong. TypeSafe's launch post says the zero hallucination number "is not empirical", and its CEO conceded the same on Hacker News.
- The rubric is doing most of the work. Vague criteria produce vague scores. TypeSafe's own limitations page says Jev reads instructions literally, so state the exact condition and put the boundary cases in the criteria.
- Independent accuracy testing is thin. The one serious external test, by Every's head of evals, ran 777 judgments and found Jev caught 6 of 7 planted defects where Claude Fable 5.1 caught 7 of 7. He called it useful as an early warning system, not as a final check.
The honest limits, all sourced to TypeSafe's own documentation, are on what Jev is bad at.
Frequently asked questions
Can Claude Code call Jev directly?
Not out of the box. Jev is a separate hosted API from TypeSafe with its own key, and no coding agent ships with it built in. You give the agent a path to it: a small script it runs in the shell, TypeSafe’s agent skill, or a community MCP server. The script is the smallest of the three.
Where does TYPESAFE_API_KEY go?
The TypeSafe SDKs read it from the environment, so exporting it in the shell that launched the agent is enough for anything the agent runs as a command. Put the export in your shell profile or a local env file that is gitignored, and never paste the key into a prompt or a committed config.
Why not just ask Claude to grade the draft?
You can, and for a one-off it is fine. It stops being fine when the judgment has to be repeatable. A chat answer is prose you have to parse, it varies run to run, and the grade lives inside the same generation that wrote the draft. Jev returns a number from a rubric you declared, in one pass, and the same rubric produces comparable numbers next week.
What does the OpenTweet MCP server add?
43 tools that publish and schedule to X, Bluesky and LinkedIn, plus threads, X Articles, the evergreen queue, analytics, media upload and DM campaigns. One command adds it: claude mcp add --transport http opentweet https://mcp.opentweet.io/mcp --header "Authorization: Bearer ot_your_key".
Should the threshold be in CLAUDE.md or in code?
In code. An instruction in CLAUDE.md is a strong suggestion and the agent can reason its way past it. A script that exits non-zero below the threshold, or a branch that never reaches the publish call, is a rule. Write the rule in code and describe it in CLAUDE.md so the agent knows the rule exists.
Do I need a Jev key to try this?
For the Jev half, yes. Jev is waitlist gated in early access and there is no free tier, though the Vercel AI Gateway, OpenRouter and Cloudflare all route to it without the waitlist. For the publishing half you need an OpenTweet key, which starts with ot_ and comes from /developer.
Keep exploring
The decision layer, the thresholds, and the tools that publish.
Jev and MCP: judgment plus action
The same two layers, explained for any MCP client rather than Claude Code specifically.
How to set a Jev confidence threshold
Where the 2.0 and the 0.6 in the script should come from, and why it is not a gut number.
OpenTweet skill for Claude Code
The publishing half on its own, as a skill and an MCP server.
The OpenTweet MCP server
All 43 tools, the client configs, and what each one does.
The same pattern in n8n
For the runs that should happen on a schedule rather than in a terminal.
Will it go viral
The scoring rubric from the script, running live. Free, no signup.
Let the agent finish the job
Scoring a draft is only useful if something can publish it. One key at /developer adds 43 MCP tools for X, Bluesky and LinkedIn to Claude Code.
7-day free trial. No X developer account needed.