scherlok
Scherlok is an MCP server for zero-config data-quality monitoring: it learns what your warehouse tables normally look like, then detects and reports anomalies and gates CI on them — all read-only on the warehouse, with no arbitrary-SQL tool.
list_tables— discover which tables the configured connection can see, before profiling anything.investigate— profile tables (row counts, types, NULL rates, distributions, freshness, cardinality) and store the results as the baseline; no anomalies reported.watch— profile and compare against the baseline to surface anomalies (volume drop/spike, freshness, schema drift, NULL surge, distribution shift, cardinality explosion, plus new-table checks), each with severity, metric, baseline/current values and a hint.status— quick health glance: connection target (password redacted), visible table count, anomalies in the last 30 days.history— replay recorded anomalies from the local store over the last N days without re-profiling.check— run a watch and return a CI-stylepassed/failed gate withfail_onset tocriticalorwarning.verify— read-only gate that compares to the baseline without saving profiles or anomalies, so retries keep failing until data is fixed; supportsagainst(compare a dev schema to production's baseline) andignore=["volume"]for sampled builds.reconcile— read-only check that an output table adds up against its input: row counts, duplicate/missing/extra keys, and column sums within a tolerance.Safety by design — credentials resolved server-side, all warehouse access read-only, and no
accepttool, so an agent can never rewrite a baseline.
Works with dbt to automatically profile and monitor data models after runs.
Sends anomaly alerts via Discord webhooks.
Connects to DuckDB databases for profiling and monitoring.
Provides CI/CD integration with GitHub Actions to gate deployments on data quality.
Supports remote storage of profile data in Google Cloud Storage.
Connects to MySQL databases for profiling and monitoring.
Connects to PostgreSQL databases for profiling and monitoring.
Sends anomaly alerts via Slack webhooks.
Connects to Snowflake databases for profiling and monitoring.
pip install scherlok
scherlok ci postgres://user:pass@host/db # first run: baseline + new-table checks; every run after: driftNo database handy? The demo seeds one, learns it, breaks it, and catches it, in about a second:
uvx --from "scherlok[duckdb]" scherlok demoWorks with PostgreSQL, BigQuery, Snowflake, MySQL, DuckDB and dbt. Alerts go to Slack, Discord, Teams, email, or your CI exit code.
The Problem
Every data team has the same nightmare:
A source API silently changes from dollars to cents. Revenue dashboards show wrong numbers for 3 weeks before anyone notices.
A column starts returning NULLs. A table stops updating. Row counts drop 40% on a Tuesday. Nobody knows until the CEO asks why the report looks weird.
Current tools (Great Expectations, Soda, dbt tests) require you to define what "correct" looks like before you can detect what's wrong. Hundreds of rules. Dozens of YAML files. And you still miss things — because you can't write rules for problems you haven't imagined yet.
Related MCP server: aegis-dq
What It Catches
Anomaly | What Happened | Severity |
Volume drop | Row count dropped 40% overnight | CRITICAL |
Volume spike | 3x more rows than normal | WARNING |
Freshness alert | Table hasn't updated in 12h (normally every 2h) | CRITICAL |
Schema drift | Column removed or type changed | CRITICAL |
NULL surge | NULL rate jumped from 2% to 45% | CRITICAL |
Distribution shift | Column mean of a numeric column shifted 3+ standard deviations (Shewhart-style control limit) and at least 1% | INFO, WARNING above 5σ |
Cardinality explosion | Status column went from 5 values to 500 | CRITICAL |
Broken new table | First sighting, no baseline yet: the table is empty, a column is NULL in every row, or its | WARNING (CRITICAL under |
Constant column | First sighting: one value in 100+ non-NULL rows | INFO |
Every anomaly is auto-scored: INFO, WARNING, or CRITICAL. No thresholds to configure.
A table seen for the first time has nothing to compare against, so it gets the new-table checks instead, from the profile already collected. They report WARNING (INFO for a constant column), so a legitimately empty table doesn't fail ci with the default --fail-on critical; once the table has a baseline they never run again. scherlok verify --table treats the tables it names as just built, so an empty table, an all-NULL column or a duplicate key in one of them is CRITICAL.
How It Works
Scherlok takes the opposite approach of rule-based tools: learn first, then detect.
scherlok connect postgres://user:pass@host/db # connect once
scherlok investigate # learn your data
scherlok watch # detect anomaliesThree commands. Five minutes. Done. (scherlok ci <url> runs all three in one step for pipelines.)
After five valid profiles, Scherlok learns per-metric variability from the latest 30 profiles using robust historical baselines for volume, numeric mean shifts, NULL rates, and distinct counts. During cold start or when history is not usable, it keeps the conservative fixed defaults.
Every run is learned, except one that finds a CRITICAL anomaly on a table:
that table keeps its baseline, so the alert repeats on every run until the
data recovers, and the broken state never becomes the new normal. WARNING and
INFO findings are learned. When the change is intended, such as a planned
backfill, a person makes it the new normal with scherlok accept (below).
1. investigate — Learn the patterns
$ scherlok investigate
Profiling 12 tables...
✓ users — 45,231 rows, 8 columns
✓ orders — 1,203,847 rows, 15 columns
✓ products — 892 rows, 12 columns
...
Done. Profiles saved.Scherlok profiles every table: row counts, column types, NULL rates, value distributions, freshness cadence, cardinality. Stores everything locally in SQLite, as aggregates only: no value from your rows is saved (what Scherlok stores).
2. watch — Detect anomalies
$ scherlok watch
Checking 12 tables against learned profiles...
🔴 CRITICAL orders volume_drop Row count dropped 52% (1,203,847 → 578,412)
🟡 WARNING users null_increase Column "email": NULL rate 2.1% → 18.7%
🔵 INFO products distribution Column "price": mean shifted 3.2σ
3 anomalies detected. Exit code: 13. Alert — Slack, CI/CD, or both
# Slack
scherlok watch --webhook https://hooks.slack.com/services/...
# Discord
scherlok watch --webhook https://discord.com/api/webhooks/...
# Microsoft Teams
scherlok watch --webhook https://outlook.office.com/webhook/...
# Any endpoint (generic JSON payload)
scherlok watch --webhook https://my-api.com/alerts
# Repeating watch on the container's local time (install optional extra first)
pip install 'scherlok[schedule]'
scherlok watch --schedule "0 6 * * *"Auto-detects Slack, Discord, and Teams from the URL and formats the payload accordingly. Any other URL receives a generic JSON payload.
CI/CD Integration
Use Scherlok as a data quality gate on pull requests. The GitHub Action checks the change against the stored baseline, fails the check on anomalies and keeps one comment on the pull request up to date with the table, column, metric, baseline, current value and severity of each:
# .github/workflows/data-checks.yml
on: pull_request
permissions:
contents: read
pull-requests: write
jobs:
scherlok:
runs-on: ubuntu-latest
steps:
# build the change first, e.g. dbt run against a CI schema
- uses: rbmuller/scherlok@v1
with:
connection: ${{ secrets.DATABASE_URL }}
store: s3://my-bucket/scherlok/profiles.db
tables: orders,customers
fail-on: criticalThe connection is passed through the environment and masked, the comment never holds a value read from a row, and fork pull requests, which get no secrets, are skipped with a notice. 📖 GitHub Action guide →
Anywhere else, the ci command does it in one line. If Scherlok detects a critical anomaly, the pipeline fails:
scherlok config --store s3://my-bucket/scherlok/profiles.db
scherlok ci "$DATABASE_URL" --webhook "$SLACK_WEBHOOK" --fail-on criticalci learns every run without a CRITICAL anomaly, so after a failure on --fail-on warning the new state becomes the baseline and a retry passes. When a retry must keep failing until the data is fixed, as with an AI agent or a CI job that retries, gate with verify. It compares against the stored baseline and saves no profiles, no anomalies and no config, and it never uploads a remote store; it only records the run.
scherlok verify --table orders --fail-on critical # exit 1 while orders is still broken
scherlok verify --output json # one JSON object on stdout
scherlok verify --record scherlok-runs.jsonl # append the run record, e.g. a CI artifact
scherlok verify --against postgresql://db.example.com/shop --ignore volume # a dev build vs production--against compares the connection's tables, such as a dev build, with another database's baseline in the same store, such as production's, and saves nothing under either: a dropped column fails before the change ships. --ignore volume is for builds on sampled data: row counts are not compared, and the column checks only flag what a sample can't explain.
Tables without a baseline are listed under tables_without_baseline, never counted as verified. Those you name with --table count as just built: an empty table, a column NULL in every row or a duplicate key there is CRITICAL, so an agent can gate on a table it just created. Every watch, ci, check and verify run is recorded, passing or not: scherlok history --runs lists them. Every anomaly in the JSON also carries column, metric, baseline, current, change_pct and a fixed hint, so it can be acted on without parsing the message. 📖 CI/CD gate guide →
Reconcile output against input
A table's own history can't catch a join that fans out on its first build, or a total that changes while the row count stays the same. reconcile compares a pipeline's output with its input, in the same database, and exits 1 when they don't add up:
scherlok reconcile --source raw.orders --target mart.orders \
--key order_id --sum amount --tolerance 0.001It reports row_count_mismatch, duplicate_keys (a key repeated in the target: join fan-out), missing_keys, extra_keys and sum_mismatch (totals apart by more than the absolute --tolerance), all CRITICAL, with --output json for CI. It needs no baseline and saves nothing, and it reports counts and totals only, never a key value. On BigQuery and Snowflake every run scans both tables in full. 📖 Reconcile guide →
Accepting a change
A CRITICAL reading is never learned, so an intended change (a new column, a planned backfill) keeps alerting until a person accepts it:
scherlok accept --table orders --reason "Backfilled 2025 orders" # shows before/after, asks to confirm
scherlok accept --table orders --reason "..." --yes # no prompt, for a workflow a person triggers
scherlok history --approvals # who accepted what, and whyaccept re-profiles the table, shows its rows, columns and anomalies against the current baseline, and on confirmation saves the new state as the baseline with one row in an append-only approvals table: who approved (git config user.email, else $USER), the reason, and the before/after summary. --reason is required.
To keep agents and scheduled runs from moving baselines at all, lock them: scherlok config --baseline-locked or SCHERLOK_BASELINE_LOCKED=1. Then only accept writes a baseline, including a new table's first one; watch, ci, check, dbt and the MCP tools compare without saving profiles, and investigate refuses. accept is deliberately not an MCP tool.
This is a guardrail, not a security boundary. An agent with shell access can run scherlok accept or edit the SQLite file itself. Real enforcement means running verify in CI against a store the agent has no credentials for, with accept run from a workflow a person triggers. 📖 Accepting a change →
Works with dbt
Already running dbt? Scherlok complements dbt test with automatic anomaly detection — no rules to write.
pip install scherlok[dbt]
# After `dbt run`, point Scherlok at your project
scherlok dbt --project-dir ./my_dbt_projectScherlok reads target/manifest.json, discovers every materialized model (table, incremental, view), auto-resolves the connection from your profiles.yml, and profiles each model:
Investigating 4 dbt models in ./my_dbt_project (postgres)
✓ stg_customers (12,345 rows)
✓ stg_orders (98,765 rows)
✗ fct_orders CRITICAL: Row count dropped 42% (98,765 → 57,283)
✓ dim_customers_inc (12,300 rows)
Summary: 4 profiled, 1 anomalies (1 critical, 0 warning)Use it as a CI gate after dbt run:
- run: dbt run --target prod
- run: scherlok dbt --project-dir . --target prod --fail-on criticalOr collapse both steps into one with the wrapper:
- run: scherlok dbt-run-and-watch --project-dir . --target prod --fail-on criticalThe wrapper runs dbt run by default and uses the successful model nodes recorded in
target/run_results.json, so partial runs profile only what dbt actually built. Use
--build to run dbt build; successful models are still profiled when a test failure
causes downstream models to be skipped on dbt's handled failure path (exit 1), while
the wrapper preserves dbt build's exit code. Unhandled failures fail fast without
reading the artifact.
Both dbt and dbt-run-and-watch accept --output json for CI parsers — a single JSON document on stdout, nothing else.
Supported adapters: postgres, bigquery, snowflake, mysql, duckdb. For others, pass --connection-string explicitly.
📖 Full docs: dbt integration guide →
dbt Package — native tests
Prefer staying inside dbt? Install Scherlok as a dbt package for native data quality tests — no Python CLI needed.
# packages.yml
packages:
- git: https://github.com/rbmuller/scherlok.git
revision: v1.5.0Once the dbt Package Hub listing lands (dbt-labs/hubcap#456), this becomes package: rbmuller/scherlok with version: [">=1.0.0", "<2.0.0"].
# schema.yml
models:
- name: fct_orders
tests:
- scherlok.volume_anomaly:
sensitivity: 3.0
- scherlok.row_count_between:
min_value: 100
columns:
- name: email
tests:
- scherlok.not_null_proportion:
max_rate: 0.01
- name: updated_at
tests:
- scherlok.recency:
days: 2Tier 1 — Instant (no setup): not_null_proportion, row_count_between, recency, unique_proportion
Tier 2 — Auto-learning (Shewhart control limits): volume_anomaly, null_anomaly — require the scherlok_metrics model to build baseline history.
📖 Full docs: dbt package README →
HTML dashboard

scherlok dashboard --out report.htmlOne self-contained HTML file (~28 KB): KPIs, per-table incidents grouped with first-seen timestamps, +/−/~ schema-drift diff, sparklines, and full anomaly history. Auto dark/light theme via prefers-color-scheme.
📖 Full docs: dashboard guide →
Use it from an AI agent (MCP)
Let Claude Code / Claude Desktop run data-quality checks directly.
Claude Desktop: download scherlok-<version>.mcpb from the latest release and open it. One click, one setting (your connection string, stored as a secret).
Any other client:
pip install scherlok # scherlok-mcp ships built-in since v0.7.0{
"mcpServers": {
"scherlok": {
"command": "scherlok-mcp",
"env": { "SCHERLOK_CONNECTION": "postgresql://user:pass@host/db" }
}
}
}The agent gets list_tables, investigate, watch, status, history, check, verify and reconcile as tools; verify is the read-only gate that never changes the baseline, so an agent that retries can't pass a broken table, and reconcile checks a table it built against the table it built it from. There is no accept tool: a person approves baseline changes, and with baselines locked no tool writes one. Credentials are resolved server-side (never passed by the model), every operation is read-only on the warehouse, and there's no arbitrary-SQL tool.
📖 Full docs: MCP server guide →
Check an agent's dbt changes (Claude Code)
An agent that finishes dbt run reports success: the SQL ran. The Claude Code plugin makes it check the data. After every dbt run or dbt build, a hook runs scherlok verify on the models that run built and hands Claude the findings; the verify-data-pipeline skill has it fix the cause, verify again, and leave accepting a new baseline to a person.
Demo: a dbt project on DuckDB where the agent adds a join that fans out, the hook fires, the agent fixes the join and the check passes.
/plugin marketplace add rbmuller/scherlok
/plugin install scherlok@scherlokThe hook stays silent until Scherlok is installed and has a connection, and it never fails the session. Lock the baselines for agent sessions with SCHERLOK_BASELINE_LOCKED=1. Other coding agents get the same instructions from an AGENTS.md snippet, and scherlok verify --dbt-project . scopes a check to the last dbt run anywhere, CI included. 📖 Coding agents guide →
AI-explained alerts (--explain)
Your alert says what broke. --explain adds why — and what to check next.
pip install 'scherlok[explain]'
export ANTHROPIC_API_KEY=sk-ant-...
scherlok watch --webhook https://hooks.slack.com/... --explainWhen anomalies fire, Scherlok makes one Claude call for the whole batch and injects a short root-cause hypothesis into the same Slack/Discord/Teams/email/JSON alert:
Works on watch, ci, check, dbt, and dbt-run-and-watch. On dbt projects the hypothesis is lineage-aware: upstream parents from manifest.json go into the prompt, so cascading failures get traced to the source model instead of alerting on every downstream symptom.
What it costs — one call per fired run (not per anomaly), Claude Haiku 4.5 by default: well under a cent per run (~$0.003). Override the model with
SCHERLOK_EXPLAIN_MODEL. Runs with zero anomalies make no API call.What it sends — aggregates only: the anomaly type/severity/message strings already in your alert, dbt model names, detection timestamps. Never warehouse rows, cell values, or credentials — the test suite pins this as a contract.
How to turn it off — it's opt-in; don't pass
--explain. If the API call fails (no key, timeout, rate limit), the original alert is delivered unchanged with a one-line note. Alerting never blocks on the LLM.
📖 Full docs: explainer guide →
Email alerts
export SCHERLOK_SMTP_HOST=smtp.gmail.com
export SCHERLOK_SMTP_USER=alerts@company.com
export SCHERLOK_SMTP_PASSWORD=app-specific-password
scherlok watch --email team@company.com --email cto@company.comConnectors
# PostgreSQL
scherlok connect postgres://user:pass@host:5432/db
# BigQuery — see src/scherlok/connectors/bigquery.md for auth, billing, CI patterns
pip install scherlok[bigquery]
scherlok connect bigquery://project-id/dataset-name
# Snowflake
pip install scherlok[snowflake]
export SNOWFLAKE_USER=...
export SNOWFLAKE_PASSWORD=...
export SNOWFLAKE_WAREHOUSE=...
scherlok connect snowflake://account/database/schema
# MySQL
pip install scherlok[mysql]
scherlok connect mysql://user:pass@host:3306/dbname
# DuckDB
pip install scherlok[duckdb]
scherlok connect duckdb:///path/to/file.dbThe connection string names the dataset or schema, and every table in it is monitored. To monitor only some, name them when you connect: scherlok connect bigquery://project-id/dataset-name --table orders --table customers. 📖 Choosing the tables →
Database | Status |
PostgreSQL | Available |
BigQuery | Available |
Snowflake | Available |
MySQL | Available |
DuckDB | Available |
Remote Storage
Share profiles across CI runs and team members:
# AWS S3
scherlok config --store s3://my-bucket/scherlok/profiles.db
# Google Cloud Storage
scherlok config --store gs://my-bucket/scherlok/profiles.db
# Azure Blob Storage
scherlok config --store az://my-container/scherlok/profiles.dbOne store can hold several databases: baselines are kept per database (adapter, host, port and path of the connection, never credentials), so orders in staging never shares a baseline with orders in production. 📖 Where things live →
How it compares
Scherlok | Elementary | Soda | Great Expectations | Monte Carlo | |
Open-source core | MIT | Apache-2.0 dbt package + CLI | Apache-2.0 Soda Core | Apache-2.0 GX Core | No (SaaS) |
Config before detection starts | None | YAML per anomaly test | SodaCL YAML checks | Expectations you declare | Monitors configured in the product |
Learns baselines in the free tier | Yes, automatically | Yes, with per-test config | No (needs Soda Library + Cloud) | No (validates declared expectations) | n/a |
Works without dbt | Yes | No | Yes | Yes | Yes |
Self-hosted | Yes | OSS yes; Cloud is managed | Core yes; Cloud is managed | Core yes; Cloud is managed | No |
Pricing | Free | OSS free; Cloud by seats and environments | Core free; Cloud has a free plan | Core free; Cloud has a free Developer option | Quote-based |
Every claim links to the other tool's own documentation in the full comparison.
CLI Reference
scherlok connect <url> [--table T] Connect to a database; --table monitors only those tables
scherlok investigate [--table T] Profile the monitored tables (learn patterns)
scherlok watch [--table T] [-w <url>] [-e <email>] Detect anomalies and alert
scherlok ci <url> [--table T] [opts] All-in-one CI/CD command (connect + watch + exit code)
scherlok verify [--table T] [--dbt-project DIR] [--fail-on L] [--output json] [--record PATH] [--against SCOPE] [--ignore volume] Read-only gate: compare, keep the baseline
scherlok reconcile --source S --target T [--key K] [--sum C] [--tolerance X] [--output json] Check an output table against its input
scherlok accept --table T --reason "..." [--yes] Make the current state the baseline, with a recorded approval
scherlok dbt [--project-dir .] [--output json] Profile dbt models from manifest
scherlok dbt-run-and-watch [--build] [--output json] Run dbt + profile in one step
scherlok status [--output json] Quick health dashboard
scherlok history [--days N] [--runs | --approvals] [--output json] Past anomalies, every run, or every approval
scherlok report [--output json] Detailed profile summary
scherlok dashboard [--out .html] Generate self-contained HTML report
scherlok config --store <url> Set remote storage
scherlok config --baseline-locked Only `accept` writes baselines (--no-baseline-locked to undo)
scherlok config --output json Is a connection configured (never printed), the store, the lock
scherlok demo [--keep] [--output json] Self-contained demo on a sample DuckDB (no database needed)
scherlok version Show versionInstall
pip install scherlok
# With BigQuery support
pip install scherlok[bigquery]Requires Python 3.10+.
Run via Docker
A pre-built image with every warehouse extra (dbt, bigquery, snowflake) is published to GitHub Container Registry on every release tag:
docker run --rm ghcr.io/rbmuller/scherlok:latest versionMount your project directory and inject connection details the same way your CI does it; the entrypoint is the scherlok CLI:
docker run --rm \
-v "$PWD:/work" -w /work \
-e SCHERLOK_CONNECTION=postgres://... \
ghcr.io/rbmuller/scherlok:latest watchThe image is built from python:3.12-slim and runs unprivileged (USER scherlok).
Contributing
Contributions welcome! See CONTRIBUTING.md.
We're especially looking for:
New database connectors (e.g. Databricks — see #37)
Anomaly detection improvements
Documentation and examples
Star History
License
MIT — Developed by Robson Bayer Müller
Available Tools
8 toolscheckA
Run a watch and return a CI-style pass/fail gate.
It covers the tables saved with scherlok connect --table, else every
table.
fail_on is "critical" (default — fail only on CRITICAL) or "warning"
(stricter — fail on WARNING or worse). Mirrors scherlok ci. Returns
passed plus the severity counts the decision was based on. Like watch,
it saves what it sees except a table with a CRITICAL anomaly, so a check
failing on CRITICAL keeps failing until the data recovers, while one
failing on a WARNING passes next time; verify saves nothing. With
baselines locked, check saves no profiles either.
Inputs: fail_on. Returns: object
historyA
Return anomalies recorded in the last days days (capped).
Reads from the local profile store — does not re-profile the warehouse.
Only the configured connection's anomalies are returned. They carry the
same fields as watch; those recorded before the fields existed have
them all null.
Inputs: days. Returns: object
investigateA
Profile tables and store them as the baseline for future anomaly checks.
Pass tables to limit to specific tables, or omit to profile the tables
saved with scherlok connect --table, else everything visible. The
first profile of a table establishes its baseline; no
anomalies are reported here. Run watch later to detect drift. A table
whose current state has a CRITICAL anomaly against its baseline keeps that
baseline, with baseline_updated false: a broken state is not learned.
Only a person makes it the new baseline, with scherlok accept. Fails
while baselines are locked.
Inputs: tables. Returns: object
list_tablesA
List the tables visible to the configured warehouse connection.
Returns the table names (capped) and a total count. Use this to discover
what can be profiled before calling investigate or watch.
Inputs: no named parameters. Returns: object
reconcileA
Check that an output table adds up against the input it was built from.
Read-only: compares target with source on the configured connection
and saves nothing. Always compares row counts. With key_columns (unique
in the target; several for a composite key) it also counts keys repeated
in the target (a join that fans out), source keys missing from the target
and target keys the source lacks. With sum_columns it compares each
column's total, which may differ by at most tolerance (absolute).
Returns counts and totals only, never a key value; passed is false on
any mismatch, each a CRITICAL anomaly with a fix hint. Both tables must
be listed by list_tables.
Inputs: source (required), target (required), tolerance, key_columns, +1 more. Returns: object
statusA
Report the current monitoring state.
Returns the connection target (password redacted), how many tables the connection can see, and how many anomalies were recorded in the last 30 days. A quick health glance without profiling anything.
Inputs: no named parameters. Returns: object
verifyA
Compare tables with the stored baseline and return a pass/fail gate, leaving it unchanged.
Read-only, unlike watch and check: no profiles or anomalies are
written, so retrying after a failure keeps failing until the data is
fixed; only the run itself is recorded. Pass tables to limit scope, or
omit to verify the tables saved with scherlok connect --table, else
everything. fail_on is "critical" (default) or
"warning". Tables with no stored baseline are listed in
tables_without_baseline and never counted as verified. Naming one in
tables says it was just built, so an empty table, an all-NULL column or
a duplicate key there is CRITICAL; unnamed, they stay WARNING. Once the
data is right, investigate sets a baseline.
against compares with another database's baseline instead, e.g.
production's when the connection is a dev schema you just built: its
scope, such as postgresql://db.example.com/shop (adapter, host, port
and path, never a connection string with credentials). Only records
saved under that scope count, freshness is not compared, and a scope
with no baseline in the store is an error. ignore=["volume"] is for a
build on sampled data: row counts are not compared and the column checks
allow for a sample.
Inputs: ignore, tables, against, fail_on. Returns: object
watchA
Profile tables and detect anomalies against the stored baseline.
Returns the anomalies found (type, severity, message per table), capped.
Each also carries column, metric, baseline, current, change_pct
and a fixed hint on where to look, null when one does not apply.
Pass tables to limit scope, or omit to watch the tables saved with
scherlok connect --table, else everything. A table with no
prior baseline gets the new-table checks instead (empty_table,
all_null_column, duplicate_key as WARNING, constant_column as INFO),
and its baseline is set for next time. A table with a CRITICAL anomaly
keeps its baseline, so the anomaly is reported again on every call until
the data recovers or a person runs scherlok accept; WARNING and INFO
findings are learned. With baselines locked, nothing is learned.
Inputs: tables. Returns: object
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
TDQS
Scored across 8 tools
Most tools have clearly distinct roles: list_tables for discovery, investigate for baselines, watch for drift detection, and reconcile for source/target checks. The main overlap is among check, watch, and verify, which all perform anomaly detection and can return pass/fail gates, but the descriptions explicitly distinguish their persistence and read-only semantics.
Names are readable and consistently snake_case, but conventions are mixed: list_tables uses verb_noun, several tools are bare verbs (check, investigate, watch, verify, reconcile), and two are nouns (history, status). The set is understandable but not predictably patterned.
Eight tools is well-scoped for a data monitoring server, and each tool appears to earn its place across discovery, baseline creation, detection, gating, history, reconciliation, and status.
The core lifecycle is covered: discover tables, establish baselines, detect anomalies, run gates, inspect history, reconcile outputs, and check status. The notable gap is the lack of an accept-baseline tool even though multiple descriptions reference `scherlok accept` for resolving persistent CRITICAL anomalies, forcing a human/CLI workaround.
Maintenance
Related MCP Connectors
Auto-discover validation rules from data — scan, profile, health-score. No rules to write.
Data observability tools for engineering teams: alerts, freshness, schema drift, lineage, quality.
The grounded data layer for any LLM: governed SQL, metrics, lineage and catalog over your data.
Query your warehouse or a CSV with Claude/ChatGPT over MCP, governed by table-level ACL + audit.
Related MCP Servers
- AlicenseNot gradedqualityCmaintenanceData observability for AI agents. Query alerts, monitor freshness, investigate schema drift, and trace lineage across your data warehouse via 53 MCP tools.1MIT
- AlicenseBqualityDmaintenanceAgentic data quality MCP server — runs structured validation rules against warehouses (DuckDB, BigQuery, Athena, Databricks, Postgres), diagnoses failures with LLM root cause analysis, and proposes SQL remediations. Full audit trail of every AI decision.615 PyPI5Apache 2.0
- AlicenseNot gradedqualityCmaintenanceAI-driven MCP server that audits, profiles, detects schema drift, and auto-generates documentation for dbt projects, enabling natural language interaction with your dbt project's health.103MIT
- AlicenseBqualityBmaintenanceLocal-first MCP server for data quality that finds suspicious data, explains findings with evidence, tracks drift, and supports human-approved, reversible repair workflows. Deterministic by default, with AI optional.25253 PyPI2Apache 2.0