NEWPromptev MCP Server: one URL for every tool, every agent and your team’s knowledge. Connect Claude Code, Cursor, or any MCP client.→

Context Engine CLI reference

The context-engine command ships in both packages with the same four subcommands and the same flags. This page lists every flag, its default, what each command needs installed and how it exits.

Overview

Installing promptev-context-engine (Python 3.12+) or @promptev/context-engine (Node 22+) puts a context-engine executable on your path. The four subcommands behave the same in both; the few differences are called out where they occur.

CommandWhat it doesNeeds
migrateCreate or upgrade the engine’s tables in a Postgres database.A Postgres driver
mcpServe the MCP tool surface over streamable HTTP, for trying it out.The MCP extra or peer, a Postgres driver, CE_ config
check-acl-exposureMeasure ACL-filtered retrieval accuracy on your own corpus. Offline and read-only.A Postgres driver
bm25-enableOpt this database in to the experimental BM25 keyword ranking: install its triggers and count the stored chunks.A Postgres driver
bm25-disableRemove the BM25 triggers and statistics.A Postgres driver
bm25-compactFold the BM25 statistics rows each chunk write adds, one per source.A Postgres driver
install-skillInstall the agent skill so a coding assistant uses the library correctly.Nothing extra

Python dependencies: the Postgres driver is the [postgres] extra (psycopg2). mcp also needs the [mcp] extra (the official MCP Python SDK, mcp 2.2 or later), which brings uvicorn. install-skill needs nothing beyond the base install.

TypeScript dependencies: the Postgres driver is the optional peer pg. mcp also needs the optional peers @modelcontextprotocol/server (^2.2.0) and @modelcontextprotocol/node (^2.1.0), the official MCP SDK. install-skill needs nothing beyond the package.

Running the command

Install the package and call context-engine, or run it once without installing. Remember the driver either way.

Shell (pip / uvx)
# Installed in your environment
pip install "promptev-context-engine[postgres]"
context-engine migrate --database-url postgresql://user:pass@localhost:5432/mydb

# Or without installing, via uv
uvx --from "promptev-context-engine[postgres]" context-engine migrate \
    --database-url postgresql://user:pass@localhost:5432/mydb
Shell (npx)
# Installed in your project
npm install @promptev/context-engine pg
npx context-engine migrate --database-url postgresql://user:pass@localhost:5432/mydb

# Or without installing (pg is an optional peer, so name it)
npx -p @promptev/context-engine -p pg context-engine migrate \
    --database-url postgresql://user:pass@localhost:5432/mydb

context-engine --help and context-engine <command> --help print the flags.

migrate

Creates or upgrades the engine’s schema. Run it on every deploy: it is idempotent, and the migration history ships inside the package, so you need no alembic.ini or copied scripts.

FlagDefaultMeaning
--database-url <url>requiredPostgres connection URL.
--dim <n>1536Embedding vector width. It becomes the width of every embedding column and is recorded in context_engine_meta. It must match your embedding model.
--graphoffAlso create the graph tables (entities, relationships, chunk entities, communities, and the record of removals a Neo4j backend still owes). Run it before starting workers: the engine looks for the removals table once per process.
Shell
context-engine migrate --database-url postgresql://user:pass@localhost:5432/mydb --dim 1536

# Later, add the graph tables on top of the same database
context-engine migrate --database-url postgresql://user:pass@localhost:5432/mydb --dim 1536 --graph
Shell
npx context-engine migrate --database-url postgresql://user:pass@localhost:5432/mydb --dim 1536

# Later, add the graph tables on top of the same database
npx context-engine migrate --database-url postgresql://user:pass@localhost:5432/mydb --dim 1536 --graph

What it does

Extensions. Creates vector, pg_trgm and unaccent if missing, so the database role needs permission to CREATE EXTENSION (or an administrator creates them first).

Base schema, always. Documents, chunks, meta, structured keys, and the tool, tool call and tool approval tables.

Graph schema, with --graph. Running it later with --graph adds the graph tables on top. Running it again without the flag is a no-op, never a downgrade.

Its own version table. Progress is recorded in context_engine_alembic_version, so it does not collide with your application’s Alembic history in the same database.

Indexes without write locks. Every index after the initial schema is built with CREATE INDEX CONCURRENTLY, so ingest keeps writing while it builds, and an index left invalid by an interrupted build is dropped and rebuilt by the next run. The CLI prints a line per index to stderr, because a concurrent build waits for open write transactions and can be silent for minutes otherwise; SELECT * FROM pg_stat_progress_create_index shows how far along it is. A lock_timeout or statement_timeout on the connection applies to these builds, and migrate does not override it.

Self-healing. Every statement is idempotent, so a failed migrate is safe to re-run, and re-running is the repair: the database converges to the current schema.

Safe when servers start together. migrate holds one advisory lock for the whole run, so a second process migrating the same database waits, finds everything applied and exits cleanly. On a database that is already up to date it takes no table lock, so a migrate at every boot never queues your traffic, and a repair that does need a lock gives up after 5 seconds instead of waiting behind open transactions.

The dimension is fixed once recorded. If the database already records a different embedding_dim, migrate refuses before running any DDL, because changing the width would invalidate every stored vector. Use a fresh database for a different embedding width.

The same step is available from code:

Python
from context_engine.cli import run_migrate

run_migrate("postgresql://user:pass@localhost:5432/mydb", dim=1536, graph=False)
TypeScript
import { runMigrate } from "@promptev/context-engine";

await runMigrate("postgresql://user:pass@localhost:5432/mydb", { dim: 1536, graph: false });

mcp

Serves the engine’s MCP tools (search_knowledge_base, search_tools and execute_tool) over streamable HTTP. It is a quick way to try the MCP surface, not a deployment target.

Security: unauthenticated, anonymous, loopback by default

This server has no authentication. Every request runs as an anonymous caller (empty principals) over the whole corpus (UNSCOPED), so anyone who can reach it can read every unrestricted document and execute every unrestricted tool. It binds 127.0.0.1 by default for that reason. Binding any other host prints a warning that says so. To serve MCP for real, mount the app in your own server with a principals resolver behind your own auth (below).

FlagDefaultMeaning
--database-url <url>requiredPostgres connection URL. Every other setting comes from CE_ environment variables.
--host <host>127.0.0.1Interface to bind. Widen it only if you accept that anyone who can reach it can use it.
--port <port>8080Port to serve on.
--dim <n>from env, else detectedOverrides embedding.dim after the config is loaded.
--graphoffSets graph.enabled after the config is loaded. The graph still needs its CE_GRAPH__* variables (at least the extraction LLM); the flag does not supply them.
Shell
pip install "promptev-context-engine[postgres,mcp]"

export CE_EMBEDDING__PROVIDER=openai
export CE_EMBEDDING__MODEL=text-embedding-3-small
export CE_EMBEDDING__API_KEY=sk-...

context-engine mcp --database-url postgresql://user:pass@localhost:5432/mydb --port 8080
# streamable HTTP endpoint: http://127.0.0.1:8080/mcp
Shell
npm install @promptev/context-engine pg @modelcontextprotocol/server @modelcontextprotocol/node

export CE_EMBEDDING__PROVIDER=openai
export CE_EMBEDDING__MODEL=text-embedding-3-small
export CE_EMBEDDING__API_KEY=sk-...

npx context-engine mcp --database-url postgresql://user:pass@localhost:5432/mydb --port 8080
# prints: context-engine mcp listening on http://127.0.0.1:8080

Configuration it reads

Everything but the URL comes from the environment. The command builds ContextEngineConfig from CE_ variables (Python constructs it directly; TypeScript calls ContextEngineConfig.fromEnv), so CE_EMBEDDING__PROVIDER and CE_EMBEDDING__MODEL are required. CE_LLM__* turns on the LLM-backed actions, and CE_ENABLE_CODE_EXECUTION governs compute. See the configuration reference.

Paths. The Python server is uvicorn serving the MCP app, with the endpoint at /mcp. The TypeScript server is a Node HTTP server that hands every request to the MCP handler. Both answer the 2026-07-28 protocol version and the older initialize handshake, and neither issues a session id.

Loopback answers only loopback. Bound to 127.0.0.1, localhost or ::1, the server answers only those names at its own port in the Host header. Without that check any web page could reach this unauthenticated, tool-running surface by DNS rebinding. Bound elsewhere with --host, it sets no Host list and warns instead.

Serving MCP for real

Python
from context_engine import create_mcp_app, UNSCOPED

# Your own ASGI app, behind your own auth: principals come from the caller.
app.mount("/mcp", create_mcp_app(engine, principals=my_principals, scope=UNSCOPED))
TypeScript
import { createMcpApp, UNSCOPED } from "@promptev/context-engine";

// Behind your own auth: principals come from the caller.
const handler = await createMcpApp(engine, { principals: myPrincipals, scope: UNSCOPED });

principals and scope are both required: there is no default caller identity. Pass a narrower scope than UNSCOPED when the tool should reach only some sources. The knowledge tool reference covers the tool’s actions.

check-acl-exposure

Answers one question against your own database: for callers who can see only part of the corpus, does the vector leg return what an exact search would? It compares a bare scoped query and the engine’s own retrieval path, both scored against an exact scan computed inside Postgres.

Offline

No embedding provider is called. Query vectors are sampled from embeddings already stored, and no data leaves the process.

Read-only

No temp tables, no index changes, no ANALYZE. The only session settings are SET LOCAL.

FlagDefaultMeaning
--database-url <url>requiredPostgres connection URL of a migrated database with embedded documents.
--samples <n>100Query vectors to sample from embeddings already stored.
--top-k <n>10Result depth scored against the exact answer.
--max-scopes <n>8How many of the most common ACL values to test.
Shell
context-engine check-acl-exposure --database-url postgresql://user:pass@localhost:5432/mydb

# In a deployment pipeline: fails the step only when the verdict is "exposed"
context-engine check-acl-exposure --database-url "$DATABASE_URL" --samples 200 --top-k 20
Shell
npx context-engine check-acl-exposure --database-url postgresql://user:pass@localhost:5432/mydb

# In a deployment pipeline: fails the step only when the verdict is "exposed"
npx context-engine check-acl-exposure --database-url "$DATABASE_URL" --samples 200 --top-k 20

Verdicts

The report prints recall and the share of empty results per scope, then a verdict from the worst scope tested:

VerdictWhenExit code
okThe worst scope’s recall@k is at least 0.99.0
degradedThe worst scope’s recall@k is at least 0.90 and below 0.99.0
exposedThe worst scope’s recall@k is below 0.90.1
no-dataNo tested scope had eligible rows.0

On exposed the report suggests what to check: pgvector 0.8 or newer, a GIN index on the ACL column, and that searches go through the library rather than raw SQL.

bm25-enable, bm25-disable, bm25-compact

The bm25 keyword ranking is experimental and opt-in per database. Measured on five public datasets it is not a default and does not improve default search. Nothing changes for a database that does not use it: the migration adds two small tables and some SQL functions and never touches the chunk table. bm25-enable installs four triggers on the chunk table (taking its lock briefly, with a 2-second timeout and retries) and counts the chunks already stored, one source at a time, without blocking ingest. Run it again at any time; it recounts. Until it has finished, a bm25 search ranks with ts_rank_cd and reports why. Enabled, each chunk write adds one small statistics row and two writers never wait on each other. A source is compacted on its own after a write once it holds more than search.bm25.compact_after_deltas rows (default 1000); bm25-compact does the same for every source on demand, which matters only when that setting is turned off. Recounts and compactions take a per-source lock that only maintenance uses, so bm25-enable, bm25-compact and the automatic compaction are safe to overlap with each other and with ingest, for example several replicas enabling at startup. bm25-disable removes the triggers and statistics. Each takes --database-url only. The library calls are enable_bm25(), disable_bm25() and compact_bm25_stats()enableBm25(), disableBm25() and compactBm25Stats() on the engine.

Shell
context-engine bm25-enable --database-url "$DATABASE_URL"
context-engine bm25-compact --database-url "$DATABASE_URL"   # only needed with automatic compaction off
context-engine bm25-disable --database-url "$DATABASE_URL"
Shell
npx context-engine bm25-enable --database-url "$DATABASE_URL"
npx context-engine bm25-compact --database-url "$DATABASE_URL"   # only needed with automatic compaction off
npx context-engine bm25-disable --database-url "$DATABASE_URL"

install-skill

Copies the agent skill shipped inside the package (a SKILL.md stating the library’s invariants, such as TRUSTED instead of the deprecated principals=None) into a .claude/skills/context-engine/ directory. It is an explicit command rather than an install hook, because the file becomes instructions inside your agent.

FlagDefaultMeaning
--globaloffInstall under your home directory: ~/.claude/skills/context-engine/SKILL.md.
--dir <dir>current directoryInstall under this directory instead: <dir>/.claude/skills/context-engine/SKILL.md. Wins over --global.
--printoffWrite the skill to stdout instead of a file, for agents that do not read .claude/skills/.
--forceoffOverwrite an existing skill file you have edited.
Shell
context-engine install-skill            # -> ./.claude/skills/context-engine/SKILL.md
context-engine install-skill --global   # -> ~/.claude/skills/context-engine/SKILL.md
context-engine install-skill --dir ../app
context-engine install-skill --print > context-engine-skill.md
Shell
npx context-engine install-skill            # -> ./.claude/skills/context-engine/SKILL.md
npx context-engine install-skill --global   # -> ~/.claude/skills/context-engine/SKILL.md
npx context-engine install-skill --dir ../app
npx context-engine install-skill --print > context-engine-skill.md

Safe to re-run. If the file already matches the shipped skill it prints “Already up to date”. If you have edited it, the command refuses and exits 1 rather than overwrite your changes; pass --force to replace it.

Exit codes

CommandExit status
migrate0 on success. Non-zero on any error, including a --dim that differs from the recorded one.
mcpRuns until interrupted. Non-zero if it cannot start (for example a missing extra or missing CE_EMBEDDING__*).
check-acl-exposure1 when the verdict is exposed. 0 for ok, degraded and no-data. Non-zero on a connection or query error.
bm25-enable / bm25-disable / bm25-compact0 on success, after printing what it did. Non-zero on a connection or query error, or when the chunk table’s lock could not be taken after every retry.
install-skill0 when installed, already up to date, or printed. 1 when the target exists, differs from the shipped skill and --force was not given.
anyA missing required flag or an unknown option is a usage error: exit 2 from the Python CLI (argparse), 1 from the TypeScript CLI (commander). No command or an unknown command prints the usage and exits the same way: 2 in Python, 1 in TypeScript. --help exits 0.
2,500 free credits · No card required · No subscription