sieve
sieve is a local MCP server that gives coding agents probability-based judgments from TypeSafe's Jev model—no generated text—by scoring files, code, candidates, web results, claims, and threads.
jev_grep: rank a repository's files or functions against a plain-language question, returning relevant file/function locations with probabilities.
jev_rank: rerank a list of candidates (e.g. code snippets, options) by relevance to a question.
jev_search: search the web via Brave, Claude, or Codex, dedupe hits, and rerank them by relevance to the original query.
jev_ask: ask a yes/no, multiple-choice, or ordered-scale question about each item in a batch; returns per-item probabilities.
jev_verify: check claims against cited files or web pages, with support/contradiction probabilities and source-hash tracking.
jev_route: choose the best route (or none) for an incoming request from caller-supplied route descriptions.
jev_triage_threads: score conversation threads for unanswered requests that need human input.
jev_triage_paseo: apply the same triage scoring to local Paseo agent indexes and native Claude/Codex transcripts.
sieve-ask: shell access to the same ask/judge/choose/score functionality for non-MCP callers.
Provides web search through the Brave Search API, returning URLs, titles, and snippets that are deduplicated and reranked for relevance.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@sieverank files in the repo by relevance to user login"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
sieve
sieve is a local MCP server that puts Jev, TypeSafe's System One model, in a
coding agent's hands. Jev returns probabilities over a closed set of candidates
and never generates text. The general tool is jev_ask: the agent writes the
question, enumerates the answer space, passes the items, and gets one answer per
item. The rest — jev_grep, jev_rank, jev_search, jev_verify, jev_route,
jev_triage_* — are shortcuts for recurring shapes over the same core, with the
candidate enumeration or the criteria already written. The agent still opens and
reads the survivors itself.
The server runs locally but sends file previews and candidate text to the TypeSafe API, so a TypeSafe API key is required. Get one at https://docs.typesafe.ai/introduction/quickstart.
See Cost notes for pricing and Status for the recall gate results.
Install
Requires Python 3.12 or later and uv.
git clone https://github.com/derwells/sieve.git
cd sieve
uv syncsieve reads the TypeSafe API key from TYPESAFE_API_KEY in its environment. The
bin/sieve-mcp launcher also sources an env file if one exists, so the key never
has to sit in a harness config file. The default path is ~/.config/sieve/env;
SIEVE_ENV_FILE overrides it. The launcher sources this file as shell code and
exports its assignments; values in the file override existing environment values.
mkdir -p ~/.config/sieve
cat > ~/.config/sieve/env <<'EOF'
TYPESAFE_API_KEY=...
BRAVE_API_KEY=...
EOF
chmod 600 ~/.config/sieve/envReplace ... with your API key. BRAVE_API_KEY is optional and only affects
jev_search; remove that line if you do not use Brave, since even the placeholder
selects the Brave backend. If no env file exists,
the launcher uses whatever is already in the environment.
Register the server with a harness. The launcher resolves the repository from its
own location, so any clone path works. Replace /path/to/sieve/bin/sieve-mcp
with the absolute launcher path in your clone. The launcher requires Bash,
readlink -f, and uv on the MCP client's executable search path.
Claude Code, user scope:
claude mcp add --scope user sieve -- /path/to/sieve/bin/sieve-mcpCodex, in ~/.codex/config.toml:
[mcp_servers.sieve]
command = "/path/to/sieve/bin/sieve-mcp"OpenCode, in ~/.config/opencode/opencode.json:
{
"mcp": {
"sieve": {
"type": "local",
"command": ["/path/to/sieve/bin/sieve-mcp"],
"enabled": true
}
}
}To run the server directly from the clone: uv run sieve. It speaks MCP over
stdio. This command does not source the env file; export TYPESAFE_API_KEY first
or use the launcher.
Related MCP server: reposniffer-mcp
Tools
jev_ask(question, items, kind="judge", yes=None, no=None, options=None, levels=None, top_k=None, threshold=0.0, max_chars=4000, budget_usd=0.50)
One question you wrote, asked of every item you hold. Write the question,
enumerate the answer space, call it. kind picks the primitive:
| answer space | per item |
|
|
|
|
|
|
|
|
|
Items are plain strings or {id, text}; a missing id falls back to the list
position. Each item's text is cut to max_chars, 40 items go in one request,
requests run concurrently under the same budget check the other tools use, and
every answer is validated client-side before it is returned. Answers are cached
in sqlite under ~/.cache/sieve/ask/, keyed on (model, question, item text,
criteria), so repeating a question costs nothing.
{
"results": [{"id": "crash", "probability": 0.98}, {"id": "dark-mode", "probability": 0.03}],
"kind": "judge",
"items_scored": 2,
"tokens": 1032,
"input_tokens": 1012,
"output_tokens": 20,
"requests": 1,
"cache_hits": 0,
"cost_usd": 0.000043,
"budget_exhausted": false
}Write the criteria concretely and make them mutually exclusive; Jev reads them
literally, and vague criteria give probabilities near 0.5 across the board. Put
shared context — the traceback, the spec, the goal — in the question rather than
repeating it in every item. Add your own none option to choose when no listed
label may fit.
jev_grep(question, path, mode="files", top_k=20, threshold=0.5, budget_usd=0.50)
Ranks a repository against a plain-language question. The walk is
gitignore-aware and never follows symlinks. mode="files" scores every file using
its path and preview. Files longer than 80 lines use their first 40 lines plus
a line-numbered outline; shorter files use their full text. Previews have a
6_000-character cap.
mode="functions" runs the files pass first, then splits the strongest surviving
files into functions with tree-sitter (fixed 60-line chunks where no grammar
applies) and scores those.
Returns:
{
"results": [
{"path": "sieve/validate.py", "line_start": 1, "line_end": 91, "kind": "file", "probability": 0.91}
],
"units_scored": 137,
"tokens": 204233,
"input_tokens": 201411,
"output_tokens": 2822,
"requests": 18,
"cache_hits": 0,
"cost_usd": 0.008459,
"budget_exhausted": false
}Results are sorted by probability. Units below threshold are dropped. Answers
are cached in sqlite, keyed on (model, question, unit text, prompt), so a repeated
question incurs no Jev charge when all units are cached and those inputs are
unchanged. The cache lives in .sieve-cache/ inside the searched
repository when sieve can add that pattern to an existing .gitignore (which
it modifies if necessary), and under ~/.cache/sieve/<repo-hash>/ otherwise, so it never leaves an untracked directory
in someone else's working tree.
jev_rank(question, candidates, top_k=None, threshold=0.0)
Reranks candidates the agent already holds. candidates is [{id, text}]; a
missing id falls back to the list position. Returned IDs are strings.
top_k=None returns all candidates that meet threshold. Each candidate is cut to 2000
characters, 40 go in one request, and requests run concurrently.
Returns:
{
"results": [{"id": "postgres", "probability": 0.94}],
"candidates_scored": 5,
"tokens": 1204,
"input_tokens": 1180,
"output_tokens": 24,
"requests": 1,
"cache_hits": 0,
"cost_usd": 0.00005,
"budget_exhausted": false
}jev_route(ask, routes, budget_usd=0.10)
Chooses one route for ask from caller supplied routes, each with id,
description, and aliases, or chooses none when no route fits. It asks one
Choice over the routes and none. With more than 10 routes, it scores groups of
at most 10, keeps the two strongest routes from each group, then asks a final
Choice over the survivors and none. Usage reports the number of stages and
groups. For more than five groups, the final Choice uses the 10 strongest
survivors from the first stage.
Returns:
{
"choice": "billing",
"confidence": 0.92,
"confidence_source": "sdk",
"probabilities": {"billing": 0.94, "docs": 0.04, "none": 0.02},
"usage": {"tokens": 410, "input_tokens": 380, "output_tokens": 30, "requests": 1, "cache_hits": 0, "cost_usd": 0.000016, "budget_exhausted": false, "stages": 1, "batches": 1}
}jev_search(query, top_k=10, variants=3)
Searches the web and reranks the results. Code requests 2 to 4 query variants: the original, one with
filler words stripped, a reordered rephrase, and docs and github suffixes when
the query names software. Duplicate variants are removed, so fewer may run.
The variants run concurrently through one backend. Hits are deduped by canonical URL (lowercase host, no www., no default port, no
fragment, no tracking parameters), and the survivors are reranked against the
original query through the jev_rank path. Only the top k reach the agent. The JSON below is abbreviated: usage also
contains input_tokens, output_tokens, and budget_exhausted. Partial backend
failures add backend_errors; if every variant fails, the tool raises an error.
Returns:
{
"results": [{"url": "https://docs.typesafe.ai/...", "title": "Re-ranking", "snippet": "...", "probability": 0.93}],
"variants": ["typesafe jev rerank cookbook", "..."],
"backend": "brave",
"usage": {"tokens": 3120, "requests": 1, "cost_usd": 0.00013, "cache_hits": 0},
"hits_found": 30,
"hits_deduped": 21,
"backend_calls": [{"query": "...", "hits": 10, "wall_seconds": 0.7, "usage": {}}],
"wall_seconds": 2.1
}String rules produce query variants. The backend supplies each result's url, title and snippet. Jev assigns a relevance probability to each deduped hit.
jev_verify(records=None, report=None, base_path=None, budget_usd=0.50, support_threshold=0.6, contradict_threshold=0.5)
Checks claims against cited files or web pages. Provide either records or a
Markdown report. A record has a claim, optional claim_context, optional
kind (fact or recommendation), optional premises as strings, and
citations as [{"locator": "...", "quote": "..."}]. A locator is an HTTP URL
or a file path. Relative paths resolve against base_path. Recommendations are
exempt from a verdict; their premises are checked as facts using the same citations.
For a report, sieve extracts sentences, bullets, and table rows in code. Headings,
parent list items, and table headers supply context. These claims carry
extraction_uncertain: true, so review their wording before relying on a verdict.
sieve fetches each cited source once, up to 2 MB, and records the SHA256 hash of
its bytes as source_version. It finds supplied quotes by normalised text match,
then selects passages near the quote or by lexical overlap. Jev compares each
passage with the entire claim and returns probabilities for supports_fully,
partially_supports, contradicts, and does_not_address. The result keeps all
passage distributions, the passage with the strongest full support, and the
largest contradiction probability. A failed fetch is reported as a flag, not
as a low support score.
counts groups factual claims by their top verdict. flagged lists claims with
fetch, quote, evidence, threshold, or budget flags. The
unqualified_factual_relay_blocked field is true if a factual claim has no
fetchable citation or any passage reaches contradict_threshold. The default
thresholds are provisional until fitted on the verification eval. usage
reports tokens, requests, cache hits, cost, and budget exhaustion.
jev_triage_threads(threads, budget_usd=0.50, request_threshold=0.5)
Scores requests for human input across several normalized threads. Each thread
has thread_id, chronological events, contract, and optional status and
journal_priority. Events have id, ts, role, kind, and text. The
contract has title, first_prompt, and human_amendments. Results contain
each request, its raw probabilities, source event IDs, coverage, a snapshot
pointer, a thread bucket, and usage. The threshold is provisional.
Candidate requests come from assistant prose. For each candidate, separate Noul questions check whether it requests input, whether later human dialogue answers it, whether the assistant withdrew it, and whether the latest assistant statement says it blocks progress. Long dialogue is scored in overlapping windows. With full coverage, no later human turn makes the request unanswered; incomplete coverage makes it unknown. A running agent with newer assistant progress is not marked blocked. If the contract states acceptance criteria, another Noul checks whether exactly one action remains.
jev_triage_paseo(agent_ids, tail=400, budget_usd=0.50, request_threshold=0.5)
Reads local Paseo agent indexes and native Claude or Codex transcripts, then
calls the same scorer. When a native transcript is unavailable, it reads
paseo logs text and marks coverage as truncated. The tool accepts agent IDs
only. It does not accept log text.
sieve-ask: the same path from a shell
bin/sieve-ask is jev_ask for callers that are not MCP clients. Same core,
same batching, caching, budget and validation. Items are a JSON list of strings
or {id, text} objects on stdin or in --items FILE.
# one yes/no probability per item
printf '%s' '["fix crash on launch","add dark mode"]' | bin/sieve-ask judge \
"Does the item describe a bug?" \
--yes "It reports broken or incorrect behaviour." \
--no "It asks for new behaviour or is not about behaviour."
# one option per item, with the full distribution
bin/sieve-ask choose "Which team owns this ticket?" --items tickets.json \
--option "web=browser UI, CSS, React" \
--option "api=HTTP endpoints, auth, database" \
--option "none=no listed team fits"
# one level on an ordered scale per item, lowest first
bin/sieve-ask score "How badly does this hurt someone using the product today?" --items bugs.json \
--level "a blemish nobody is blocked by" \
--level "an annoyance with a workaround" \
--level "work cannot be completed or the result is wrong"judge takes --top-k, --threshold; all three take --budget-usd,
--max-chars (default 4000 per item), --model, and --no-cache. From Python,
sieve.ask.ask(question, items, kind, ...) dispatches on kind, and
judge(...), choose(...) and score(...) are async with the same arguments as
keywords. bin/sieve-ask sources the same env file as bin/sieve-mcp.
Backends and auto-selection
backend | how | snippets | titles |
| Brave Search HTTP API, needs | real | verbatim |
| headless | none | verbatim |
| headless | none | model-transcribed |
SIEVE_SEARCH_BACKEND picks one. Otherwise brave is used whenever
BRAVE_API_KEY is set, and claude if it is not. SIEVE_SEARCH_CMD replaces the
CLI command line, and SIEVE_SEARCH_TIMEOUT the 90 s per-call limit. The CLI
backends require an installed, authenticated claude or codex executable.
codex exec does not put search results on its event stream in the measured setup: the
web_search event carries only the query, so that backend parses the markdown
bullet list the model writes afterwards, which makes its titles transcribed rather
than verbatim. Claude's usage.server_tool_use.web_search_requests reports 0
even when results come back, so sieve counts tool results instead.
Cost notes
Measured 2026-09-22, same query, three variants each:
backend | wall time | cost |
| 2.1 s | a fraction of a cent |
| 16.6 s | ~$0.15 for the three calls |
| 29.0 s | not measured |
The brave and codex rows exclude the backend's own charges: Brave bills
separately under its API pricing, and Codex runs under a Codex subscription
rather than metered API cost. The claude figure is the API-equivalent cost
that Claude Code itself reports for the call, not a separate metered charge.
In this measurement, one headless Claude call used ~65k tokens and cost ~$0.05 with ~13 s wall time, even for a small query. Claude Code's own system prompt and tool schemas are the floor, even with MCP, settings and extra tools stripped off.
sieve calculates Jev costs from input tokens at $0.042 per million, the rate
configured in sieve/jev.py. The eval includes a 1,090-file repository
at HEAD. A jev_grep files pass over its 1080-file parent snapshot cost $0.054
and 5.6 s cold; over a 190-file repository, $0.0084 and 2.0 s.
jev_grep and jev_verify expose a budget_usd cap in the MCP interface. They return partial
results with budget_exhausted: true when the budget stops scoring. The check
uses estimated token costs, so actual spend can exceed the cap.
jev_rank and jev_search expose no budget parameter. Search usage.cost_usd
covers Jev reranking; backend charges are separate. These are measured costs,
not a current provider price list.
Design rules
Jev selects or scores over a closed set. The fixed tools define that set in code;
jev_asklets the caller define it, and enforces the same closure.Batch every question that shares a state into one request.
Validate every answer client-side: probabilities cover the offered set and sum to ~1; a Choice (a selection from fixed options) must pick the max-probability option. Reject answers that fail validation.
Relevance floats are filters, not truth. Thresholds are evaluated on real asks, not copied from cookbooks.
The API key is read from the environment. It never appears in a harness config file or in this repository.
Out of scope
Jev does not generate summaries or write queries. The Codex search backend does use generated text to extract results.
Indexing or embeddings. Every call enumerates fresh; the cache covers repeats.
Multi-hop code tracing.
Status
Implemented and evaluated:
jev_askserved over stdio byuv run sieve, withjudge,chooseandscoreexercised live through a real stdio client intests/test_ask_live.py. Not separately evaluated: the question is the caller's, so its accuracy is the caller's to check.jev_grep,jev_rank,jev_searchandjev_verifyserved over stdio byuv run sieve.Recall eval on five past asks in four private repositories, written up anonymised in
evals/. At the current default of 8 units per request, the files mode gate requires recall@10 at least 0.8 on 4 of 5 asks. Strict recall over every previously existing file edited by the fix reached 3 of 5, so it failed. Relaxed recall over each ask's single primary fix file reached 5 of 5, so it passed. Recall@10 is the fraction of ground truth files retrieved in the top 10. The eval usedthreshold=0.0; the default filter can omit additional files. The earlier eval led to outline previews and 8 units per request instead of 4. A criteria sweep did not justify another prompt change.Citation eval for
jev_verifyon 40 hand-built cases from public sources, half true and half altered, inevals/. Thresholds fitted on 20 and tested on the other 20: no altered claim accepted, no true claim flagged, 18 of 20 four-way verdicts correct on each half. Scope alterations come back as contradicts rather than partial support.All three search backends exercised live. On the acceptance query,
braveandclaudeput the right page first;codexmissed it and transcribed its links.Registration verified headless in Claude Code, Codex and OpenCode, and through a Paseo (an agent management app) plugin that injects the server into every agent. That plugin is separate from the installation instructions above.
Roadmap:
jev_route(ask): Choice over a code-defined set of routes, for an orchestrator that has to pick a project for an incoming request.A Noul (a probability-valued judgment) gate for auto-approving read-only shell commands that no static rule matches.
Sharper
jev_grepcriteria for large repositories, where 35 files can legitimately answer "would a developer have to open this".jev_triage_threads(events, contract): ranks agent threads for a human briefing. Code enumerates candidate requests for human input; one Noul per request decides whether later human dialogue answered it and whether progress is waiting on it. Shipped with a Paseo adapter; eval inevals/. On 30 private thread snapshots the candidate method did not beat whole-window scoring on the test half (6 of 15 buckets right against 10 of 15), so the chief should treat its output as a filter to inspect, not a ranking to trust.
Stack
Python 3.12, uv, typesafe-sdk (async client), mcp (stdio; FastMCP is
MCPServer in mcp 2.x), tree-sitter-language-pack, pytest. The brave
backend uses httpx2, which the TypeSafe SDK already pins.
Tests: uv run pytest -q -m "not live" for the offline suite. uv run pytest -m live also hits the real API and needs TYPESAFE_API_KEY.
References
Docs index: https://docs.typesafe.ai/llms.txt
Pipeline constants borrowed from
superagents-lab/jev-search: 40 per rerank batch, 0.6 source threshold, 8 results per lane.Answer validation pattern from
browser-use/jev-ultrafast.
License
MIT. See LICENSE.
Repository enumeration in sieve/enumerate.py is adapted from
keltokhy/jgrep, MIT, Copyright (c) 2026
Khaled Eltokhy. Its license notice is reproduced in NOTICE and in the
module itself.
Available Tools
3 toolsjev_grepRank a repository against a questionB
Rank the files, or the functions inside the strongest files, of a repository by how relevant they are to a plain-language question. Returns {path, line_start, line_end, kind, probability} sorted by probability, plus token and cost usage. Enumeration is gitignore-aware; answers are cached per (model, question, unit).
| Name | Required | Description | Default |
|---|---|---|---|
| mode | No | 'files' scores whole files; 'functions' then splits the best files. | files |
| path | Yes | Absolute path to the repository or directory to search. | |
| top_k | No | How many results to return. | |
| question | Yes | What you are trying to find out, in plain language. | |
| threshold | No | Drop units scoring below this probability. | |
| budget_usd | No | Stop and return partial results before spending more than this. |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full burden of behavioral disclosure. It reveals that enumeration is gitignore-aware, results are cached per (model, question, unit), and returns token/cost usage, which is helpful. However, it omits details like side effects (if any), required permissions, or behavior on empty repos, which are not covered elsewhere.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is two sentences, front-loaded with the primary action and output. The first sentence states the core function and return structure; the second adds two behavioral traits. There is no fluff or repetition, making it efficient and easy to scan.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The tool has a rich output schema and 6 parameters. The description covers the main function, output fields, and key behaviors (gitignore, caching). It does not explicitly mention the budget_usd stop behavior or edge cases, but the schema documents those parameters and the output schema covers return details, so the description is sufficiently complete for correct invocation.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so each parameter is already documented in the schema. The description adds contextual behavior (gitignore-aware path enumeration, caching keyed by question) that relates to parameters but does not add parameter-specific syntax or format details beyond the schema. This meets the baseline.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states a specific verb (rank) and resource (files/functions of a repository) by relevance to a question, and specifies the output format. It does not explicitly differentiate from sibling tools like jev_search or jev_rank, but its function is unambiguous and distinct from typical search tools.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description provides no guidance on when to use this tool versus the siblings jev_search and jev_rank. There is no mention of alternatives, exclusions, or preferred contexts, leaving the agent to infer usage from the name and description alone.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
jev_rankRerank candidates against a questionB
Score each candidate for relevance to a question and return [{id, probability}] sorted by probability, plus token and cost usage. Candidates are truncated to 2000 characters and sent 40 per request.
| Name | Required | Description | Default |
|---|---|---|---|
| top_k | No | Return at most this many results; null returns all. | |
| question | Yes | What the candidates are being ranked against. | |
| threshold | No | Drop candidates scoring below this probability. | |
| candidates | Yes | Candidates as [{id, text}]. Missing ids fall back to list position. |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description carries the full burden of behavioral disclosure. It adds useful non-obvious constraints: candidates are truncated to 2000 characters, processed 40 per request, and the response includes token/cost usage. These are valuable operational details. It does not mention rate limits, failure modes, or side effects, but for a stateless rerank operation the disclosed behavior is reasonably sufficient.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is two sentences with no filler. The first sentence front-loads the core purpose and output format; the second adds the key processing constraints. Every sentence earns its place, and the structure makes the tool easy to scan.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a tool of this complexity, the description covers the essential invocation context: what it returns, how candidates are truncated, and the request batching. The output schema exists to document return values, so that burden is shared. It omits edge cases like maximum candidate count or error handling, but those are not necessary for correct invocation in most agent workflows.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The input schema has 100% description coverage, so the baseline is 3. The tool description adds no new meaning beyond the schema: it does not explain top_k or threshold beyond what the schema already says, and the candidate shape is also already documented. The description's output-format reference reinforces the parameter purpose but does not supplement it.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the verb ('Score'), the resource ('each candidate'), and the outcome ('return [{id, probability}] sorted by probability, plus token and cost usage'). This goes beyond the title's 'Rerank' by specifying the exact output shape. However, it does not distinguish the tool from its siblings (jev_search, jev_grep), so an agent cannot tell when ranking should replace searching or grepping.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
There is no explicit guidance on when to use jev_rank versus the sibling tools. The description does not mention that this should be used after candidates have already been retrieved, nor does it contrast with jev_search or jev_grep. The intended use is only implied by the phrase 'Score each candidate for relevance to a question', which is not enough for an agent to make a routing decision.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
jev_searchSearch the web and keep only the relevant hitsA
Search the web for a plain-language query and return [{url, title, snippet, probability}] sorted by probability. sieve proposes 2-4 query variants in code, runs them concurrently through the configured backend (brave, headless claude, or headless codex), dedupes by canonical url, and reranks everything against your original query with Jev. Snippets are empty on the CLI backends; codex titles are model-transcribed.
| Name | Required | Description | Default |
|---|---|---|---|
| query | Yes | What you are trying to find on the web, in plain language. | |
| top_k | No | How many results to return. | |
| variants | No | How many query variants to run concurrently. |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations present, the description carries the full burden of behavioral disclosure, and it does so thoroughly. It explains concurrency ('runs them concurrently'), deduplication ('dedupes by canonical url'), reranking ('reranks everything against your original query with Jev'), and backend-specific caveats ('Snippets are empty on the CLI backends; codex titles are model-transcribed'). This is rich, honest behavior context.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is three dense sentences with no fluff. It front-loads the return shape and ordering, then covers mechanics and backend caveats. Every sentence adds information an agent needs.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given an output schema is present and the description already covers return structure, concurrency behavior, deduplication, reranking, and backend-specific output quirks, an agent has enough context to invoke the tool correctly. No annotations are needed because the description itself is sufficiently comprehensive.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the baseline is 3. The description adds some process-level context around variants ('sieve proposes 2-4 query variants in code') and clarifies the query type ('plain-language query'), but the individual parameter meanings are already well covered by the schema descriptions, so this does not rise above baseline.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states a specific verb and resource: 'Search the web for a plain-language query and return [{url, title, snippet, probability}]'. It is easy to understand the tool's core function, but it does not explicitly differentiate from sibling tools jev_grep and jev_rank, so it falls just short of a 5.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The intended use is implied by 'Search the web for a plain-language query', which suggests this is for web searches, but the description provides no explicit guidance about when to prefer jev_search over jev_grep or jev_rank, nor any exclusions. The usage context is inferable but not stated.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
3 tool updates
v0.1.0- First observed
jev_grep - First observed
jev_rank - First observed
jev_search
TDQS
Scored across 3 tools
Each tool targets a clearly distinct data domain: web search results (jev_search), repository code units (jev_grep), and arbitrary candidate text (jev_rank). Despite all returning probability-ranked output, the input types and descriptions make selection unambiguous.
All tools share the jev_ prefix followed by a simple verb: search, grep, rank. This predictable verb-oriented pattern makes the tool names easy to learn and distinguish.
Three tools is minimal but well-scoped for a focused relevance-ranking server. Each tool handles a distinct retrieval or ranking task, and none feels redundant or missing.
The set covers the core relevance-ranking workflow: search the web, find relevant code within a repository, and rerank arbitrary candidates. There are no obvious dead ends or critical missing operations for the stated purpose.
Maintenance
Related MCP Connectors
Search GitHub, npm, PyPI, StackOverflow, ArXiv from one MCP — built for coding agents.
Shared memory for coding agents. Stop re-explaining your codebase every session.
Finds real, maintained open-source repos that fit your project. MCP grounding for coding agents.
Codebase intelligence for agents: 152 structured artifacts across 21 programs, one call.
Related MCP Servers
- AlicenseNot gradedqualityAmaintenanceEnables coding agents to scout, rank, and preflight software work before implementation, returning evidence-backed ACT, VERIFY, or SKIP decisions for issues and pull requests.59 npm2MIT
- AlicenseAqualityAmaintenanceEnables AI agents to discover and evaluate GitHub repositories from natural-language feature descriptions, returning ranked adoption-grade candidates with evidence and quality signals.31MIT
- AlicenseAqualityBmaintenanceEnables coding agents to make cheap, fast probabilistic decisions on every turn, with tools for coding-loop checks, review, verification, screening untrusted input, and ranking candidates.6981 npm38MIT
- AlicenseAqualityBmaintenanceEnables frontier coding agents to delegate routine probabilistic judgments to TypeSafe Jev, providing calibrated triage signals for failures, attempts, completion, context ranking, findings, risk, and generic evidence-grounded questions.7MIT