Last updated: September 2026
Jev MCP server: the judgment layer and the action layer
Jev is how an agent decides. An MCP server is how it acts. Wire both and the agent can score a draft cheaply and quickly enough to do it on everything, then call a publish tool only when the number clears a bar you set in code.
OpenTweet runs the action layer: 43 MCP tools over Streamable HTTP at mcp.opentweet.io/mcp, publishing to X, Bluesky and LinkedIn. Below are the real configs, the gate in code, and what each layer is not allowed to do.
Three layers, not one model
The reason to reach for Jev at all is that the middle row is a different shape of problem from the rows either side of it.
Jev cannot do the first row. It has no free-form string output, so it cannot write a post, a summary or a reason. It also cannot do the third row, because it has no side effects at all. It answers questions you declared, and nothing else.
How an agent reaches Jev
TypeSafe publishes an HTTP API, two SDKs and an agent skill for coding agents. Its docs do not list a first-party MCP server. The Jev MCP servers on GitHub are community work, and they are handing your API key to code you did not write, so read it first.
In practice the SDK call is three lines inside your own agent, which is simpler than adding a server just to make one HTTP request. Routing details are on getting Jev API access.
The action layer: OpenTweet over MCP
One key, ot_ prefixed, created at /developer. No X developer account, no OAuth dance in your agent.
Hosted, Streamable HTTP
{
"mcpServers": {
"opentweet": {
"type": "streamable-http",
"url": "https://mcp.opentweet.io/mcp",
"headers": { "Authorization": "Bearer ot_your_key" }
}
}
}The endpoint is stateless. Only POST is a protocol endpoint, a missing or malformed key returns 401, and GET /health returns a status object you can point a monitor at.
Local, stdio
{
"mcpServers": {
"opentweet": {
"command": "npx",
"args": ["-y", "@opentweet/mcp-server"],
"env": { "OPENTWEET_API_KEY": "ot_your_key_here" }
}
}
}Both forms expose the same 43 tools. Posting and threads, scheduling and batch scheduling, X Articles, the evergreen queue, analytics, inspiration search, media upload and generation, and DM campaigns. The full list is on the MCP server page.
No MCP client in the loop?
The MCP server is a thin layer over the same REST API, so an agent without MCP support can publish with one HTTP call to/api/v1/posts.curl -X POST https://opentweet.io/api/v1/posts \
-H "Authorization: Bearer ot_your_key" \
-H "Content-Type: application/json" \
-d '{"text": "Hello from my agent.", "platforms": ["x", "bluesky"], "publish_now": true}'The loop: score first, publish second
This is the part worth getting right. The gate belongs in your code, not in the prompt. An instruction that says only publish above a 2 is a request the agent can reason its way around. A branch that refuses to call the tool is a rule.
import { TypeSafeClient, noul, score } from "@typesafe-ai/sdk";
const jev = new TypeSafeClient({ timeout: 4000 });
// One file, reviewable, version controlled. Not spread across prompts.
const 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."
),
toxicity: noul(
"This text contains slurs, harassment, sexual content, or abuse targeted at a person or group."
),
};
const PUBLISH_AT = 2.0; // score, out of 4
const CONFIDENT_AT = 0.6; // confidence on that score
const SLOP_CEILING = 0.5; // noul, a probability
export async function decide(draft) {
const { answers } = await jev.systemOne({
state: { draft_post: draft, platform: "X (Twitter)" },
questions: QUESTIONS,
});
// A Noul returns only `noul`. There is no confidence field on it.
if (answers.toxicity.noul > 0.2) return { act: "block", why: "toxicity" };
if (answers.slop.noul > SLOP_CEILING) return { act: "rewrite", why: "reads as AI written" };
// A Score returns `score` and `confidence`. Both matter for an action this hard to undo.
if (answers.strength.confidence < CONFIDENT_AT) return { act: "ask_human", why: "low confidence" };
if (answers.strength.score < PUBLISH_AT) return { act: "rewrite", why: "weak draft" };
return { act: "publish", score: answers.strength.score };
}Look at the two different shapes in that code. The Noul checks read .noul and nothing else, because a Noul answer has no confidence field. The Score check reads both .score and .confidence. Third-party write-ups get this wrong constantly, and code copied from them silently reads undefined.
// Only reached when decide() returned "publish".
// The agent calls the MCP tool; the tool is not exposed to the model before this point.
await mcp.callTool("opentweet_create_tweet", {
text: draft,
platforms: ["x", "bluesky", "linkedin"],
publish_now: true,
});Zero hallucinations is a claim about the schema
Jev cannot return a value outside the answer space you declared, so schema errors are gone by construction. It can still return a valid answer that is wrong about the post. TypeSafe's own launch post says the zero figure "is not empirical" and that schema matching is what is guaranteed, and its CEO conceded the same point on Hacker News. Treat a high score as evidence, not as proof.What this costs
Jev bills $0.042 per million input tokens, and output tokens are not billed at all, per TypeSafe's pricing. There is no free tier.
The free post scorer is this exact pattern. It sends seven Jev questions per draft, one Score for reach strength across five rubric levels, three Scores for hook, clarity and specificity, and three Nouls for AI slop, engagement bait and toxicity. It needs no signup, so you can try it in about ten seconds.
A judgment layer priced the way Jev prices input tokens changes what you are willing to check. That, rather than any benchmark number, is the argument.
Four things to keep out of the judgment layer
- Arithmetic and counting. TypeSafe's own limitations page says to count in code. Do not ask Jev how many posts went out this week.
- Date and time comparison. Dates are treated as text, not as ordered values. Window membership and duration belong in your code.
- Anything needing an explanation. There is no string output, so a Jev answer cannot tell you why. If you need a reason for a human reviewer, an LLM writes it after the fact.
- Trust in untrusted state. Jev does not treat state as hostile. Text from strangers can carry instructions aimed at your criteria, so be explicit in the criteria and test before deploying.
The full list, sourced to TypeSafe's own docs, is on what Jev is bad at. If you are still deciding which question type to use, start with Choice, Score and Noul.
Frequently asked questions
Is there an official Jev MCP server?
TypeSafe publishes an HTTP API, a Python SDK, a JavaScript SDK and an agent skill for coding agents. Its docs do not list a first-party MCP server. The Jev MCP servers you find on GitHub, such as jev-mcp and decide-mcp, are community projects, so read the source before you hand one an API key.
Why would an agent need Jev if it is already a model?
Because asking a chat model to grade its own draft puts the judgment inside the same generation that produced the draft, and the answer comes back as prose you then have to parse. Jev returns a number from an answer space you declared, in one parallel pass, for a fraction of a cent. Your code branches on the number instead of on a sentence.
How many tools does the OpenTweet MCP server expose?
43. They cover posting and threads, scheduling and batch scheduling, X Articles, the evergreen queue, analytics, inspiration search, media upload and generation, and DM campaigns. The hosted endpoint and the local stdio server expose the identical set.
What transport does mcp.opentweet.io use?
Streamable HTTP, stateless. Only POST is a protocol endpoint. Authentication is an Authorization header carrying a Bearer token that starts with ot_, and a missing or malformed key returns 401. There is also a GET /health that returns a status object.
Should the threshold live in the prompt or in the code?
In the code. A prompt that says only publish if the score is above 2 is a request, and the agent can talk itself past it. A branch in your own code that refuses to call the publish tool below 2 is a rule. Keep the model out of the decision about whether to respect the decision.
Does Jev replace the LLM in this setup?
No. Jev cannot generate text. It has no free-form string output at all, so it cannot write a post, a summary or an explanation. A working pipeline has three parts: an LLM writes, Jev judges, and an MCP server publishes.
Keep exploring
Where the thresholds come from, and what the action layer can reach.
Using Jev in Claude Code
The same two layers, set up inside an agentic coding tool.
How to set a Jev confidence threshold
Where the 2.0 and the 0.6 in the code above should actually come from.
The OpenTweet MCP server
Install, client configs, and what every tool does.
X MCP server
Posting to X from Claude, Cursor and Windsurf without an X developer account.
Best MCP servers for social media
What is available, what each one can actually publish to, and what it costs.
The same split in n8n
HTTP Request to Jev, IF node on the number, OpenTweet node to publish.
Give your agent the action layer
One key at /developer turns on the REST API, the CLI, the n8n node and 43 MCP tools that publish to X, Bluesky and LinkedIn.
7-day free trial. No X developer account needed.