jevql
Semantic SQL for vanilla PostgresplaygrounddocsGitHub

Ask your database a real question.

PutWHERE jev(people, 'could work from home')in a query: Postgres runs the SQL, Jev judges the rows, you get the table. From the CLI, from Go, TypeScript or Python, or from an agent over MCP. Works on any Postgres you can already connect to, no extension required.

works on any postgresno extensionno proxyanswers cached$0.042 / M tokens

download

Get jevql

latest release:

one line, detects your platform
curl -fsSL …/install.sh | sh

or pick a tarball

macOSApple Silicon
macOSIntel
Linuxx86_64
Linuxarm64

Tarballs contain the jevql binary; unpack it onto your PATH. Checksums are on the release page.

examples · people

you type

Plain SQL, one new function

query

you get

Rows, plus what it cost

result

install

Install it, then type jevql

orrelease tarballs ↗

First launch asks for a database URL and a TypeSafe key and saves them to ~/.config/jevql/env. Or set DATABASE_URL and TYPESAFE_API_KEY like any psql user.

1 / collectPostgres runs your SQL with the jev terms stripped. Indexed filters stay on the server.
2 / judgeSurviving rows go to TypeSafe in batches. Identical rows are judged once and cached.
3 / projectFilter, sort, group and limit happen in the CLI. You get a psql table.

functions

One family of functions

full surface ↗
booleanjev()jev(p, 'condition' [, 0.7])
A calibrated probability against a threshold. Works in WHERE, also as NOT jev().
numbersjev_prob() · jev_score()jev_prob(p, 'condition') → 0..1
jev_score(p, 'q', ARRAY['lo','mid','hi']) → weighted level
labelsjev_choice()jev_choice(t, 'which team?', ARRAY['billing','technical'])
Works with GROUP BY and count(*).

Send fewer columns with jev((name, bio), '…'). Anything without jev_* passes straight through, so it doubles as a plain psql.

sdks

The same engine, from your code

sdk docs
client, _ := jevql.New(ctx, jevql.Options{DatabaseURL: os.Getenv("DATABASE_URL")})
defer client.Close(ctx)

rows, err := client.QueryMaps(ctx, `
  SELECT name, jev_prob(people, 'could work from home') AS p
  FROM people WHERE country = 'PT'
  ORDER BY p DESC LIMIT 5`)

In process: go get github.com/kylemclaren/jevql/sdk/go. Same parser, cache and client as the CLI.

import { Jevql } from "jevql"

const db = new Jevql() // bundled engine, reads DATABASE_URL and TYPESAFE_API_KEY

const tickets = await db.queryObjects(`
  SELECT id, subject FROM tickets
  WHERE jev(tickets, 'is about billing') AND status = 'open'`)

npm i jevql. The engine ships inside the package; nothing else to install. Point it at a shared jevql serve with { url }.

from jevql import Jevql

with Jevql() as db:  # bundled engine; Jevql(url=...) for a shared one
    for row in db.query_dicts("""
        SELECT title FROM movies
        WHERE jev(movies, 'a safe pick for a first date')"""):
        print(row["title"])

pip install jevql. Standard library only, engine included. Same embedded-or-remote choice.

mcp

Agents get the same superpower

mcp docs

jevql mcp is a Model Context Protocol server. Claude, Cursor or any MCP client can ask your database real questions: the query tool runs SQL with jev() in it, explain prices a query before it runs,judge scores rows the agent already holds, and list_tables / describe_table let it find its way around. Read-only by default.

# local, on stdio
claude mcp add jevql -- jevql mcp

# a shared node over HTTP
claude mcp add --transport http jevql https://your-node.fly.dev/mcp \
  --header "Authorization: Bearer $JEVQL_TOKEN"

Reads DATABASE_URL and TYPESAFE_API_KEY from your shell, like the CLI.

# Codex CLI
codex mcp add jevql -- jevql mcp

# ChatGPT: Settings → Connectors → add an MCP server
#   URL    https://your-node.fly.dev/mcp
#   Auth   Bearer <your JEVQL_TOKEN>

ChatGPT only speaks to remote servers, so point it at a node. Codex runs the local one.

// .cursor/mcp.json (project) or ~/.cursor/mcp.json (global)
{
  "mcpServers": {
    "jevql": { "command": "jevql", "args": ["mcp"] }
  }
}

Enable it under Settings → MCP. Ask Cursor to "list the tables and find open tickets that sound angry".

// opencode.json
{
  "mcp": {
    "jevql": { "type": "local", "command": ["jevql", "mcp"], "enabled": true }
  }
}

Restart OpenCode; the five tools show up under jevql_*.

querySemantic SQL"Which open tickets sound like churn risks?" becomes WHERE jev(t, '…') and comes back as rows.
explainPrice firstRows after filters, batches, tokens and dollars, with no TypeSafe call. Agents check before they spend.
judgeRows in handScore, classify or filter any JSON rows the agent already has, same cache, no database round trip.
Read before pointing it at real data

Row contents go to TypeSafe. Every column of the judged alias is sent over HTTPS. Narrow it with jev((name, bio), …) or --columns.

It scans everything your SQL filters let through. Put cheap predicates first. --explain shows the collect SQL, row count and cost with no API call; --max-rows aborts before HTTP.

Not an extension. jev() exists only inside this CLI and its SDKs. Apps sending it straight through JDBC or psycopg will get "function does not exist".

The cache is a local sqlite file. Keyed by model, question and canonical row JSON. Mode 0600, no expiry. \cache clear or --no-cache.