CLI

SLayer provides a command-line interface for server management, querying, and model operations.

Storage

All commands accept a --storage flag to specify where models and datasources are stored. When omitted, SLayer uses a platform-appropriate default (~/.local/share/slayer on Linux, ~/Library/Application Support/slayer on macOS, %LOCALAPPDATA%\slayer on Windows). See Storage for full details on backends, resolution, and overrides.

Commands

slayer serve

Start the HTTP server (REST API + MCP SSE endpoint at /mcp/sse).

slayer serve
slayer serve --host 0.0.0.0 --port 8080
slayer serve --storage slayer.db
slayer serve --demo                  # auto-ingest the bundled Jaffle Shop demo first
slayer serve --ingest-on-startup     # run idempotent ingest over every configured datasource first
Flag Default Description
--host 0.0.0.0 Bind address
--port 5143 Port number
--storage platform default Storage path (directory for YAML, .db file for SQLite)
--demo off Generate and ingest the bundled Jaffle Shop demo before starting (idempotent).
--ingest-on-startup off Walk every configured datasource and run idempotent auto-ingestion before the port opens. Per-datasource errors are logged to stderr and never abort startup. Also enabled by SLAYER_INGEST_ON_STARTUP=1.

slayer mcp

Run SLayer as an MCP server using stdio transport. This command is not meant to be run manually — it is spawned by an AI agent (Claude Code, Cursor, etc.) as a subprocess. To set it up, register the command with your agent:

# Register with Claude Code (the agent will spawn the process)
claude mcp add slayer -- slayer mcp --ingest-on-startup --storage ./slayer_data

# If slayer is in a virtualenv, use the full executable path:
#   claude mcp add slayer -- $(poetry env info -p)/bin/slayer mcp --ingest-on-startup --storage /abs/path/to/slayer_data

For MCP over HTTP (SSE), use slayer serve instead — it exposes MCP at /mcp/sse alongside the REST API.

Flag Default Description
--storage platform default Storage path (directory for YAML, .db file for SQLite)
--demo off Generate and ingest the bundled Jaffle Shop demo before starting (idempotent).
--ingest-on-startup off Walk every configured datasource and run idempotent auto-ingestion before stdio JSON-RPC starts. Per-datasource errors are logged to stderr and never abort startup. Also enabled by SLAYER_INGEST_ON_STARTUP=1.

slayer query

Execute a query from the terminal.

# Inline JSON
slayer query '{"source_model": "orders", "measures": ["*:count"], "dimensions": ["status"]}'

# From a file
slayer query @query.json

# JSON output
slayer query '{"source_model": "orders", "measures": ["*:count"]}' --format json

# Preview SQL without executing
slayer query '{"source_model": "orders", "measures": ["*:count"]}' --dry-run

# Show execution plan
slayer query @query.json --explain
Flag Default Description
--storage platform default Storage path (directory for YAML, .db file for SQLite)
--format table Output format: table or json
--dry-run Generate SQL without executing
--explain Run EXPLAIN ANALYZE on the query

slayer ingest

Auto-generate models from a datasource.

slayer ingest --datasource my_postgres
slayer ingest --datasource my_postgres --schema public
slayer ingest --datasource my_postgres --include orders,customers
slayer ingest --datasource my_postgres --exclude migrations,django_session
Flag Required Description
--datasource Yes Datasource name
--schema No Database schema to inspect
--include No Comma-separated tables to include
--exclude No Comma-separated tables to exclude
--storage No Storage path

slayer import-dbt

Import dbt Semantic Layer definitions into SLayer.

slayer import-dbt ./my_dbt_project --datasource my_postgres
slayer import-dbt ./my_dbt_project --datasource my_postgres --include-hidden-models
Flag Required Description
dbt_project_path Yes Path to the dbt project root (or a models directory)
--datasource Yes SLayer datasource name for the imported models
--include-hidden-models No Also import regular dbt models (those not wrapped by a semantic_model) as hidden SLayer models via SQL introspection. Requires the dbt extra (pip install 'motley-slayer[dbt]'). See dbt Import.
--storage No Storage path

slayer models

Manage models.

slayer models list
slayer models show orders
slayer models create model.yaml
slayer models delete orders

slayer datasources

Manage datasources.

slayer datasources list
slayer datasources show my_postgres   # credentials masked
slayer datasources test my_postgres
slayer datasources delete my_postgres

slayer datasources create

Create a datasource from a connection URL. The name is derived from the database portion of the URL (or the filename stem for SQLite/DuckDB) unless --name is passed. Pass --ingest to create and ingest in a single step.

slayer datasources create postgresql://user:${DB_PW}@localhost/analytics
slayer datasources create postgresql://localhost/analytics --ingest
slayer datasources create sqlite:///path/to/app.db --name analytics --ingest
slayer datasources create demo --ingest        # bundled Jaffle Shop demo
Flag Required Description
connection_string Yes Database URL (e.g. postgresql://…, mysql+pymysql://…, sqlite:///path/to/file.db, duckdb:///…, clickhouse+http://…). ${ENV_VAR} references are resolved at use time. Pass the literal demo to spin up the bundled Jaffle Shop demo DuckDB.
--name No Override the auto-derived name (default for the demo: jaffle_shop)
--description No Human-readable description
--ingest No Run auto-ingestion immediately after creating the datasource
--schema No (with --ingest) Schema to ingest from
--include No (with --ingest) Comma-separated tables to include
--exclude No (with --ingest) Comma-separated tables to exclude
--years No (demo only) Years of synthetic data to generate (default: 2)
-y, --yes No Overwrite existing datasource / colliding models without prompting
--storage No Storage path

The demo path generates a DuckDB at <storage>/demo/jaffle_shop.duckdb and is idempotent — re-running reuses the existing file. duckdb and jafgen are core dependencies of motley-slayer, so the demo works after a single pip install motley-slayer with no extras needed.

If a datasource with the same name already exists, or (with --ingest) any generated model name collides with a stored model, SLayer prompts for confirmation. Use --yes for non-interactive use.

slayer inspect

Point-lookup of an entity by reference and kind — no ranking, no bundled memories (use slayer search for an entity in context). Pass two or more references to inspect several entities of the same kind in one call (DEV-1612): the output is one ## <canonical> block per reference, in input order (a JSON array under --format json), with per-reference error isolation.

slayer inspect jaffle_shop.orders --type model
slayer inspect jaffle_shop.orders.order_total --type column --no-compact
slayer inspect jaffle_shop.orders.customers.region --type column --no-compact  # join path → owning model
slayer inspect memory:42 --type memory --no-compact
slayer inspect jaffle_shop.orders --type model --format json
slayer inspect jaffle_shop.orders.order_total jaffle_shop.orders.order_id --type column --no-compact  # batch
Flag Required Description
reference Yes One or more entity references: canonical id, bare name, join path (resolved to the owning model), or memory:<id>. Two or more → a same-kind batch.
--type Yes Entity kind: datasource, model, column, measure, aggregation, or memory. Disambiguates same-named entities and asserts the kind.
--no-compact No Return the full render. The compact default is description-only for column/measure/aggregation/datasource/memory, and a cheap schema skeleton (column/measure/aggregation names + join targets, zero DB calls) for --type model; --no-compact on a datasource renders a per-model skeleton for each visible model.
--format No markdown (default) or json.
--num-rows No (model only) Sample-data rows. Ignored with a warning for other kinds.
--show-sql No (model only) Include generated SQL. No-op for column/measure/aggregation; warned for datasource/memory.
--section No (model only, repeatable) Restrict to a section subset. Ignored with a warning for other kinds.
--descriptions-max-chars No Truncate description fields. Applies to every kind.
--storage No Storage path.

Run semantic search over memories and canonical entities (datasources, models, columns, named measures, custom aggregations). Three retrieval channels run in parallel — BM25 over memory entity tags, Tantivy full-text over memories ∪ entities, and (with the advanced_search extra plus a provider API key) dense embeddings — and are RRF-fused into a single ranked list. See Search.

# Entity-driven
slayer search --entity jaffle_shop.orders.order_total

# Question-driven
slayer search --question "What stores are in jaffle_shop?"

# Query-driven (auto-extracts the entities the query references)
slayer search --query @draft_query.json

# Inline query JSON
slayer search --query '{"source_model": "orders", "measures": ["order_total:sum"]}'

# Narrow to one datasource
slayer search --question "lifetime spend" --datasource jaffle_shop

# Graph-narrow with cypher_filter (naive form, always available)
slayer search --question "Brooklyn POS" --cypher-filter 'MATCH (n:Memory) RETURN n.id AS id'

# Graph-narrow with cypher_filter (full openCypher; requires the advanced_search extra)
slayer search --question "store rev" --cypher-filter \
  "MATCH (d:Datasource {name: 'jaffle_shop'})-[:CONTAINS]->(m:Model)-[:CONTAINS]->(c:ModelColumn) RETURN c.id AS id"

# JSON output for piping
slayer search --question "lifetime spend" --format json
Flag Default Description
--entity ENT (repeatable) Canonical entity string (<ds>, <ds>.<model>, <ds>.<model>.<leaf>, memory:<id>). Pass multiple times to combine. Drives the BM25 channel.
--query JSON_OR_@FILE Inline SLayer query (or @path.json). Entities are auto-extracted from source_model, dimensions, measures, time dims, and filters.
--question TEXT Free-text question. Drives Tantivy + embeddings.
--datasource DS Pre-narrow every channel to ids rooted at the named datasource. Unknown name raises.
--cypher-filter CYPHER Pre-narrow all three channels via a graph query. Full openCypher with advanced_search (LadybugDB property graph with Memory / Datasource / Model / ModelColumn / Measure / Aggregation nodes and MENTIONS / CONTAINS / JOINS edges). Without the extra, only the naive MATCH (n:Label1:Label2…) RETURN n.id AS id form is accepted; anything richer raises with an install hint.
--max-results N 10 Cap applied after RRF fusion and the cypher_filter allowlist.
--format text text (newline-grouped human output) or json (full SearchResponse).

Each result row prints kind, id, score, and a one-line preview of text. Memory hits with query is not None are saved example queries; column hits include the structured sampled_values snapshot (top 50 by frequency) and a Distinct count: N line when cardinality overflows. Unresolved input entities surface as warnings rather than errors.

slayer search refresh-samples

Re-profile and persist Column.sampled / sampled_values / distinct_count for table-backed models. Per-column failures are reported but do not abort.

slayer search refresh-samples
slayer search refresh-samples --data-source jaffle_shop
slayer search refresh-samples --data-source jaffle_shop --model orders --model customers
Flag Default Description
--data-source X all Limit the refresh to one datasource.
--model M all Repeatable; limit to specific models.

slayer memory

Manage the agent-memory layer. See Memories.

# Save a learning (--entities is a single comma-separated string)
slayer memory save \
  --learning "orders.is_returned in {0,1,NULL}; treat NULL as not returned" \
  --entities jaffle_shop.orders.is_returned

# Save with a pinned id and multiple entities
slayer memory save \
  --learning "Brooklyn POS changed late 2024" \
  --entities jaffle_shop.orders.order_total,jaffle_shop.stores.name \
  --id kb.brooklyn-pos

# Save with an inline query (mutually exclusive with --entities)
slayer memory save \
  --learning "Top customers by lifetime spend" \
  --query @top_customers.json \
  --id kb.top-customers

# Forget by id
slayer memory forget kb.brooklyn-pos
Subcommand Flag Description
save --learning TEXT (required) The free-form note.
save --entities ENT,ENT,… Comma-separated canonical entity strings. memory:<id> is valid for cross-memory refs. Mutually exclusive with --query; one of the two is required.
save --query JSON_OR_@FILE Inline SLayer query (or @path.json). Entities are auto-extracted and the query is persisted on the memory. Mutually exclusive with --entities.
save --id ID User-pinned canonical memory id. Forbidden charset: :, /, ?, #, whitespace, ASCII control. Omit to auto-allocate (max(int-shaped id) + 1). Duplicate id → unconditional upsert; created_at preserved.
forget <id> (positional) Memory id. Cascade-strips every memory:<id> reference to it from every other memory's entities list.