MCP Server

SLayer runs as an MCP server, allowing AI agents (Claude, Cursor, etc.) to discover and query data conversationally.

Transports

SLayer supports two MCP transports. Both expose the exact same tools — the only difference is how the agent connects.

Stdio (local)

The agent spawns SLayer as a subprocess and communicates via stdin/stdout. You do not run slayer mcp manually — the agent launches it. You only need to register the command with your agent.

Claude Code setup:

claude mcp add slayer -- slayer mcp --ingest-on-startup --storage ./slayer_data

--ingest-on-startup runs idempotent auto-ingestion across every configured datasource before the stdio channel opens. Drop the flag (or set SLAYER_INGEST_ON_STARTUP=0) to defer ingestion to a manual ingest_datasource_models call.

If slayer is installed in a virtualenv (e.g. via Poetry), use the full path to the executable so the agent can find it regardless of working directory:

# Find the virtualenv path
poetry env info -p
# e.g. /home/user/.venvs/slayer-abc123

# Register with the full path
claude mcp add slayer -- /home/user/.venvs/slayer-abc123/bin/slayer mcp --storage /path/to/slayer_data

SSE (remote)

MCP over HTTP via Server-Sent Events. You run slayer serve yourself — it exposes both the REST API and the MCP SSE endpoint on the same port:

# 1. Start the server
slayer serve --ingest-on-startup --storage ./slayer_data
# REST API at http://localhost:5143/
# MCP SSE at http://localhost:5143/mcp/sse

For container / systemd contexts where the CLI command isn't easy to modify, set SLAYER_INGEST_ON_STARTUP=1 in the process environment — same effect as the flag.

Then, in a separate terminal, register the remote endpoint with your agent:

# 2. Connect the agent
claude mcp add slayer-remote --transport sse --url http://localhost:5143/mcp/sse

This is useful when SLayer runs on a different machine, in Docker, or when multiple agents need to share the same server.

Verify

claude mcp list

Tools Reference

Datasource Management

Tool Description
create_datasource Create a DB connection, test it, and auto-ingest models (set auto_ingest=false to skip).
list_datasources List configured datasources (no credentials shown).
describe_datasource Show details, test connection, list available schemas, and (by default) list tables in the given or default schema. Params: name, list_tables (default true), schema_name (empty = dialect default).
edit_datasource Edit an existing datasource config.
delete_datasource Remove a datasource config.

Model Management

Tool Description
models_summary Brief summary of all non-hidden models in a datasource: each model's name, description, a table of its columns and measures (named formulas), and the list of models it joins to. The Markdown form (default) shows just name + description per column; the JSON form (format="json") additionally includes the column type. Neither form includes distinct values, sample data, or joined-model field expansion — call inspect_model for those. Params: datasource_name, format (default "markdown"; also "json").
inspect_model Complete view of a single model: metadata with row count (and a **meta:** bullet when the model has meta set), any model-level or column-level filters, columns table (with a sampled column — distinct values for string/boolean columns, min .. max for number/date/time columns — and a meta cell when set), measures table of named formulas (with formula, label, description, meta), custom aggregations (with meta), joins, all fields reachable via joins (default depth 5), and a sample-data table. Every Markdown table auto-prunes all-empty columns (so the meta column is hidden when no entity has meta) and collapses to a comma-separated backticked list when only one column remains. Params: model_name, num_rows (default 3), show_sql (default false — include SQL for the sample-data query, the custom-SQL block, model-level filters, the cached backing-query SQL, and aggregation formulas/param SQL), format (default "markdown"; also "json"), sections (subset of ["columns", "measures", "aggregations", "joins", "reachable_fields", "samples"] — default None/[] renders all six; the first four collapse to a one-line backticked CSV of names when omitted, reachable_fields/samples are dropped entirely, unknown names emit a footer warning. A non-empty list of only unknown names resolves to no sections — "all six" is reserved for None/[] so a typo can't silently trigger the full payload), descriptions_max_chars (when set, truncate each description longer than this with the suffix "... [truncated]" (prefixed by a space); applies to model, columns, measures, and aggregations; must be >= 0), reachable_fields_depth (max BFS depth in path segments — default 5, allowed range [0, 20]; ignored when reachable_fields is not in sections). When any section is trimmed, a quoted-Markdown footer lists what was shown / names-only / omitted with a hint on how to re-call. JSON output mirrors this with <section>_names siblings and top-level omitted_sections, names_only_sections, unknown_sections arrays.
create_model Create a model from a table/SQL definition or from a query. Pass sql_table/sql with columns (and optional named-formula measures) for table-based, or pass query (a SLayer query dict) to save it as a query-backed model whose columns + backing_query_sql are populated by a save-time dry-run.
edit_model Edit an existing model in one call. Upserts columns, measures (named formulas), aggregations, joins (pass the new entries; existing names are updated, new ones are added). Also accepts description, data_source, default_time_dimension, sql_table/sql/source_queries, query_variables, hidden, meta, add_filters/remove_filters, and remove: {"columns": [...], "measures": [...], "aggregations": [...], "joins": [...]} for entity removal.
delete_model Delete a model entirely.

Querying

Tool Description
query Execute a semantic query. See Queries for format.

query parameters:

Param Type Description
source_model string | ModelExtension | SlayerModel Model name (string), inline ModelExtension dict ({"source_name": "orders", "columns": [...], "joins": [...], "measures": [...]} — extend a saved model with extras for this query), or inline SlayerModel dict ({"name": "ad_hoc", "sql_table": "...", "data_source": "...", "columns": [...]} — define a model ad-hoc). Required.
measures list Aggregated values: column-aggregations, arithmetic, transforms. E.g. ["*:count", {"formula": "revenue:sum / *:count", "name": "aov", "label": "Average Order Value"}, "cumsum(revenue:sum)"]. Each entry has an optional label for human-readable display. Supports nesting: "change(cumsum(revenue:sum))". Bare names resolve to saved ModelMeasure formulas on the model.
dimensions list Dimension names, e.g. ["status"]. When using the engine directly, dimensions accept an optional label via {"name": "status", "label": "Order Status"}.
filters list[str] Filter formula strings, e.g. ["status = 'active'", "amount > 100"]. Supports operators (=, <>, >, >=, <, <=, IN, IS NULL, IS NOT NULL, LIKE, NOT LIKE), boolean logic (AND, OR, NOT), and inline transform expressions ("change(revenue:sum) > 0"). Filters on measures are automatically routed to HAVING.
time_dimensions list[dict] Time grouping. Each entry supports an optional label for display.
order list[dict] Sorting, e.g. [{"column": "*:count", "direction": "desc"}]
limit int Max rows
offset int Skip rows
whole_periods_only bool Snap date filters to time bucket boundaries, exclude the current incomplete time bucket
show_sql bool Include the generated SQL in the response for debugging
dry_run bool Generate and return the SQL without executing it
explain bool Run EXPLAIN ANALYZE and return the query plan
format string Output format: "markdown" (default, compact), "json" (structured), or "csv" (most compact). Case-insensitive

Ingestion

Tool Description
ingest_datasource_models Auto-generate models from DB schema with rollup joins. Params: datasource_name, include_tables, schema_name.

Conceptual Help

Tool Description
help Return SLayer concept explanations that complement the schema-focused tool docstrings. Call without arguments for the intro; pass topic="..." for a deep dive. The tool description lists every available topic — no exploratory call needed.

Available topics and what they cover (content lives in slayer/help/topics/*.md, discovered dynamically):

Topic Covers
queries Anatomy of a query; evaluation order; dimensions vs time dimensions on the same column; main_time_dimension disambiguation
formulas The formula mini-language shared by measures and filters; colon syntax; arithmetic; nesting
aggregations Built-in and custom aggregations; first/last time-column resolution; allowed_aggregations
transforms cumsum, time_shift, change, lag, the rank family (rank/percent_rank/dense_rank/ntile, optional partition_by=), last() — trade-offs and nesting (time post)
time Granularities, date_range, whole_periods_only, the three meanings of "last"
filters Operators; auto-routing to HAVING / post-filter; filtered measures; model-level filters
joins Dot syntax and the __ alias convention; cross-model measures and diamond joins (joins post, joined measures)
models Source modes (sql_table, sql, source_queries); query-backed models, query_variables, cached backing_query_sql; result column naming; default_time_dimension; hidden models (models ref)
extending ModelExtension, query lists, create_model_from_query (with variables=), run-by-name via query tool (multistage post)
workflow Tool-chaining playbook, query-iteration tips, common-error decoder

Typical Agent Workflows

Connect and explore a new database

1. create_datasource(name="mydb", type="postgres", host="localhost", database="app", username="user", password="pass")
   # auto_ingest=true by default — models are generated automatically
2. models_summary(datasource_name="mydb")      # see what was generated
3. inspect_model(model_name="orders")          # see schema + sample data

To explore first without auto-ingesting:

1. create_datasource(name="mydb", type="postgres", host="localhost", database="app", username="user", password="pass", auto_ingest=false)
2. describe_datasource(name="mydb", schema_name="public")  # verify connection + list tables
3. ingest_datasource_models(datasource_name="mydb", schema_name="public")
4. models_summary(datasource_name="mydb")      # see what was generated

Query data

1. list_datasources()                              # pick a datasource
2. models_summary(datasource_name="mydb")      # discover its models
3. inspect_model(model_name="orders")          # see schema + sample data
4. query(source_model="orders", measures=["*:count"], dimensions=["status"], limit=10)

Customize a model

1. edit_model(
     model_name="orders",
     add_measures=[{"name": "amount", "sql": "amount"}],
     add_dimensions=[{"name": "priority", "sql": "priority", "type": "string"}],
     remove=["old_measure"]
   )