{
  "markdown": "<!-- mcp-name: io.github.Arun-kc/schemabrain -->\n\n<p align=\"center\">\n  <picture>\n    <source media=\"(prefers-color-scheme: dark)\" srcset=\"docs/assets/readme-hero-dark.svg\">\n    <img src=\"docs/assets/readme-hero-light.svg\" alt=\"SchemaBrain — the trust and intelligence layer between AI agents and your database\" width=\"100%\">\n  </picture>\n</p>\n\n<h1 align=\"center\">\n  <strong>Stop giving AI agents raw database connection strings.</strong>\n</h1>\n\n<h2 align=\"center\">\n  Give them SchemaBrain instead — a read-only trust and intelligence layer where the agent never writes SQL, PII is refused before the query runs, and every call lands in a tamper-evident audit log.\n</h2>\n\n<p align=\"center\">\n  <a href=\"https://github.com/Arun-kc/schemabrain/actions/workflows/ci.yml\"><img src=\"https://img.shields.io/github/actions/workflow/status/Arun-kc/schemabrain/ci.yml?style=flat-square&label=CI&labelColor=0A0A0A&color=3ECF8E\" alt=\"CI\"></a>\n  <a href=\"https://pypi.org/project/schemabrain/\"><img src=\"https://img.shields.io/pypi/v/schemabrain?style=flat-square&label=pypi&labelColor=0A0A0A&color=3ECF8E\" alt=\"PyPI version\"></a>\n  <a href=\"https://pypi.org/project/schemabrain/\"><img src=\"https://img.shields.io/pypi/dm/schemabrain?style=flat-square&label=downloads&labelColor=0A0A0A&color=3ECF8E\" alt=\"PyPI downloads\"></a>\n  <a href=\"https://www.python.org/\"><img src=\"https://img.shields.io/badge/python-3.11%20%7C%203.12-0A0A0A?style=flat-square&labelColor=0A0A0A\" alt=\"Python 3.11 | 3.12\"></a>\n  <a href=\"LICENSE\"><img src=\"https://img.shields.io/badge/license-Apache--2.0-0A0A0A?style=flat-square&labelColor=0A0A0A\" alt=\"License: Apache 2.0\"></a>\n  <a href=\"https://modelcontextprotocol.io\"><img src=\"https://img.shields.io/badge/MCP-compatible-3ECF8E?style=flat-square&labelColor=0A0A0A\" alt=\"MCP compatible\"></a>\n</p>\n\n<p align=\"center\">\n  <em>Works with Claude Desktop · Claude Code · Cursor · Windsurf · any MCP host</em>\n</p>\n\nSchemaBrain compiles every query from definitions you control — no path from a prompt to raw SQL at your database.\n\nThree guarantees that close the trust gap between AI agents and your database:\n\n- **[Read-only by architecture](#1-read-only-by-architecture-not-configuration)** — twelve MCP tools, none of which can write. No `execute()` tool, no `query()` tool, no path from agent prompt to a write at your database.\n- **[PII-aware refusal at retrieval](#2-pii-aware-refusal-at-the-get_metric-tool-boundary)** — PII tags propagate from the physical schema through joins and metrics. If a query touches a blocked category, SchemaBrain refuses before the database is queried.\n- **[Cryptographic audit chain](#3-tamper-evident-audit-log)** — every call, refusal, and recovery is recorded in a SHA256-hashed append-only log (best-effort: a disk-full or no-writer configuration logs a warning and continues rather than failing the query). `audit verify` exits non-zero if any past row was rewritten.\n\n**See it in action** — ask for something the schema can't answer, and it refuses instead of fabricating a join:\n\n> **You:** *compute usage volume by plan tier*\n>\n> **SchemaBrain → agent:** `{ \"kind\": \"unreachable_entity\", \"recovery\": { \"suggested_tool\": \"resolve_join\" } }` — there's no `plan_id` on usage events, so it won't invent one.\n>\n> **Claude:** I can't fake that join — here's **contracted revenue by plan tier** instead, which actually resolves. ✓\n\n→ [Full session, with the SQL and results](#sample-session)\n\n**Watch it run** — a live Postgres schema becomes a governed knowledge graph, the firewall computes the safe metric and refuses the leaks, and every call lands in a tamper-evident audit log. No agent, no API key:\n\n<p align=\"center\">\n  <img src=\"docs/assets/demo-cli-curated.gif\" alt=\"SchemaBrain command-line walkthrough: indexing a live Postgres schema, applying the curated semantic layer of entities, joins, and metrics, then the firewall computing a safe revenue metric, refusing PII and credential leaks, recovering an unreachable join, and the operator inspecting definitions and verifying a tamper-evident audit log.\" width=\"100%\">\n</p>\n\n---\n\n```bash\nuvx schemabrain init\n# then: Cmd+Q Claude Desktop, relaunch, and ask: \"list the entities SchemaBrain knows about\"\n# prefer a persistent install? pipx install schemabrain (or) pip install schemabrain\n```\n\n**Cost:** **$0** to run the bundled demo (pre-curated pack, no API key) · ~$0.03 to LLM-index a fresh 84-column schema · **$0** to re-index unchanged schemas. Detail in [Sample session](#sample-session).\n\n**Status: 0.6.0 (beta).** Postgres supported today (the local store itself is SQLite). SQLite / Snowflake / BigQuery / MySQL source connectors on the roadmap.\n\n---\n\n## Contents\n\n**Read next based on what you need:**\n\n| Goal | Where to go |\n|---|---|\n| Try it on the bundled fixture | [Quickstart](#quickstart) |\n| Understand the safety guarantees | [Safety guarantees](#safety-guarantees) |\n| Wire up your MCP client | [Claude Desktop](docs/setup/claude-desktop.md) · [Claude Code](docs/setup/claude-code.md) · [Cursor](docs/setup/cursor.md) · [Windsurf](docs/setup/windsurf.md) · [Cline](docs/setup/cline.md) · [ChatGPT (roadmap)](docs/setup/chatgpt.md) |\n| Plug into your own agent loop | [`docs/setup/manual.md`](docs/setup/manual.md#3-wire-your-own-agent-anthropic-sdk) |\n| Build a semantic layer | [`docs/semantic-layer.md`](docs/semantic-layer.md) |\n| Run in production (audit, drift, Docker) | [`docs/operations.md`](docs/operations.md) |\n| Observe the agent (tail, audit log, OTel) | [`docs/observability.md`](docs/observability.md) |\n| Compare with Querybear / Anthropic reference Postgres MCP | [vs Querybear](docs/compare/querybear.mdx) · [vs Anthropic reference](docs/compare/anthropic-postgres-mcp.mdx) |\n| Compare with Vanna / Atlan / dbt-mcp / WrenAI | [`docs/landscape.md`](docs/landscape.md) |\n\n---\n\n## Quickstart\n\n> **Just want to see what it does?** `uvx schemabrain demo` — one command, zero prompts. Builds the sample SaaS layer, then lets you open the dashboard or run a terminal firewall showcase. **No API key, and no Docker** for the dashboard / showcase paths. The steps below are for wiring SchemaBrain into your own agent against your own database.\n\nThree steps from `uvx schemabrain init` to a working Claude Desktop integration. If you paste your own Postgres URL — no Docker needed, ~30s. Press Enter for the bundled demo and `init` invokes Docker + downloads a ~67 MB embedding model first time; ~45s once cached.\n\n### 1. Install\n\n```bash\nuvx schemabrain init        # zero-install: runs the wizard in one shot\n# or install persistently first:\npipx install schemabrain    # (or) pip install schemabrain\nschemabrain --version\n```\n\nSource install (`git clone` + [`uv`](https://docs.astral.sh/uv/) `sync --extra dev`) is documented in [`docs/setup.md`](docs/setup.md#1-install--run-the-wizard).\n\n### 2. Run the activation wizard\n\n```bash\nschemabrain init\n```\n\n`init` is a seven-stage wizard that takes you from \"I have a Postgres database\" to \"Claude Desktop can answer questions about it\" in one command. On first run it prompts for what it needs:\n\n- **A Postgres URL** — paste your own connection string, or press **Enter** to spin up a local demo Postgres container with the bundled SaaS fixture (Docker is invoked automatically; idempotent on re-runs).\n- **An `ANTHROPIC_API_KEY`** — optional. Skip and the wizard still wires Claude Desktop. On the **demo path**, entities + metrics + joins are pre-curated from a bundled YAML pack — the semantic layer works zero-config. On **your own database**, entity curation can run later via `schemabrain entities suggest --apply` once you have a key.\n\n```\nSchemaBrain init — activation wizard\n\n  [1/7] Source check       ✓ source reachable + read-only\n  [2/7] Index schema       ✓ 12 tables, 84 columns indexed\n  [3/7] Curate entities    ✓ 12 entities applied (bundled demo pack)\n  [4/7] Curate metrics     ✓ 5 metrics applied (bundled demo pack)\n  [5/7] Curate joins       ✓ 11 canonical joins applied (bundled demo pack)\n  [6/7] Wire host          ✓ wrote schemabrain entry to claude_desktop_config.json\n                           (default; switch with --host claude-code|cursor|windsurf|manual)\n  [7/7] Next               ✓ restart your MCP host, then ask: \"list the entities SchemaBrain knows about\"\n```\n\nFull wizard reference (stages explained, flags, dbt auto-detection, `--print-only` for non-Claude-Desktop hosts, `--no-entities` / `--no-metrics` / `--no-joins` opt-outs, cost-cap pauses): [`docs/setup.md`](docs/setup.md#2-what-the-wizard-does).\n\n### 3. Restart Claude Desktop and ask\n\n1. Quit Claude Desktop fully — **Cmd+Q**, not just close the window. The MCP config is only read on cold start.\n2. Relaunch.\n3. New conversation:\n\n   > list the entities SchemaBrain knows about\n\nIf Claude calls `list_entities` and reports `user`, `order`, etc., you're done. If not, see [Troubleshooting](#troubleshooting).\n\nAfter the wizard, `schemabrain inspect` shows what the agent has and `schemabrain tail` streams every tool call live — see [`docs/operations.md`](docs/operations.md).\n\n### Your project files\n\n`init` writes just `./schemabrain.db` (the local store — gitignore it) plus your host config. To tune the **PII policy** and **semantic layer** as editable YAML, re-run with `--emit-yaml-dir`:\n\n```bash\nschemabrain init --url-env DATABASE_URL --emit-yaml-dir ./schemabrain\n# → ./schemabrain/pii_policy.yaml + entities/ + metrics/ + joins/\n```\n\nEdit a file, `schemabrain apply ./schemabrain`, `schemabrain check` to validate, restart `serve`. There is no `schemabrain.yaml` — config is CLI flags + `SCHEMABRAIN_*` env vars (auto-loaded from `.env`) + that YAML tree. Full map: [**Your project**](docs/your-project.md).\n\n---\n\n## Safety guarantees\n\n<p align=\"center\">\n  <picture>\n    <source media=\"(prefers-color-scheme: dark)\" srcset=\"docs/assets/readme-architecture-compact-dark.svg\">\n    <img src=\"docs/assets/readme-architecture-compact-light.svg\" alt=\"SchemaBrain architecture: agent talks to SchemaBrain over MCP stdio (12 read-only tools); SchemaBrain emits parameterized SQL to Postgres; the SchemaBrain boundary is the trust boundary; audit log is tamper-evident.\" width=\"100%\">\n  </picture>\n</p>\n\nSix properties SchemaBrain enforces at the SQL boundary today:\n\n### 1. Read-only by architecture, not configuration\n\nThe MCP surface exposes twelve tools — **none of which can write**. No `execute()`, no `query()`, no path from agent prompt to a write at your database, regardless of session state — the guarantee is structural, not a flag the agent can flip. `schemabrain serve` also pins `default_transaction_read_only=on` as belt-and-suspenders. [Read-only by architecture →](docs/mechanism/read-only.mdx)\n\n### 2. PII-aware refusal at the `get_metric` tool boundary\n\nAny `get_metric` touching a blocked PII category returns a `refused` envelope — the compiled SQL never runs and the refusal lands in `mcp_audit`. `describe_entity` enforces the same at the column level (blocked columns ship `redacted=True`). `init` blocks the catastrophic-leak set by default (`credential,payment_card,government_id`); `--pii-block` **replaces** the set, so widen by listing the full target. Detection is column-name pattern matching across twelve GDPR / CCPA / HIPAA / PCI categories; content-aware classification is on the roadmap. [PII taxonomy & propagation →](docs/mechanism/pii-taxonomy.mdx)\n\n### 3. Tamper-evident audit log\n\nEvery tool call writes one row to an append-only `mcp_audit` table — PII categories, content-addressable fingerprints, sha256 hash chain. `audit verify` re-walks the chain and exits non-zero if any past row was rewritten.\n\n```bash\nschemabrain audit verify   # exit 0 = chain clean\n```\n\n[Tamper-evident audit chain →](docs/mechanism/audit-chain.mdx)\n\n### 4. Failure is a contract, not a string\n\nEvery non-success call — refused, error, or degraded — returns a structured `recovery.suggested_args` block, not a message to parse. PII blocks (`status: \"refused\"`) ship the entity to retry; ambiguous dimensions and unreachable entities (`status: \"error\"`) ship the candidate to pick or the next tool to call. Only policy refusals are `refused`; \"I won't guess\" is `error` with a recovery payload.\n\n```json\n{ \"status\": \"error\", \"kind\": \"ambiguous_time_dimension\",\n  \"recovery\": { \"suggested_tool\": \"get_metric\",\n                \"suggested_args\": {\"time_dimension\": \"order.placed_at\"} } }\n```\n\n[Structured recovery →](docs/mechanism/structured-recovery.mdx)\n\n### 5. Compile path: definitions → parameterized SQL\n\nEntities, metrics, and canonical joins compile to parameterized SQL SchemaBrain runs on its side. The agent sees rows + the SQL that ran — never arbitrary statements at your database. LLM-suggested definitions during `init` are reviewed and applied explicitly. [Build your semantic layer →](docs/semantic-layer.md)\n\n### 6. Pluggable into any agent loop\n\nThe same MCP stdio surface Claude Desktop sees is exposed to any MCP host — your own Anthropic, OpenAI, or LangGraph loop included. [`examples/anthropic_demo.py`](examples/anthropic_demo.py) is a ~260-LOC drop-in that wires Claude Haiku 4.5 to `schemabrain serve` and prints exactly which tools the agent chose. [Anthropic SDK walkthrough →](docs/setup/manual.md#3-wire-your-own-agent-anthropic-sdk)\n\n---\n\n## Observability dashboard\n\nSchemaBrain ships an opt-in, read-only dashboard over the same audit + PII + refusal data the MCP server is already writing. `schemabrain dashboard` boots a local FastAPI sidecar serving a pre-built static UI — no Node runtime, no network exposure, no write paths.\n\n```bash\npip install \"schemabrain[ui]\"\nschemabrain dashboard\n# → http://127.0.0.1:7878\n```\n\nIt's a viewer, not a console — no settings, no SQL pad, no write path. **Nine read-only surfaces**, each answering an operator question the MCP envelope alone never surfaces visually. The signature surface is the **Knowledge Graph** — your schema rendered as the same entity-relationship projection the semantic layer compiles joins against:\n\n- **Knowledge Graph** (`/graph`) — *how does my schema actually connect?* Entities as nodes, canonical joins as edges (solid for declared FKs, dashed for log-mined), PII-bearing entities flagged, and refusal hotspots highlighted, with declared-FK cardinality shown on the highlighted join path — the schema as a graph, not a table list.\n- **Overview** (`/overview`) — the home surface: entity / metric / join / catastrophic-PII counts at a glance.\n- **Entities** (`/entities`) — a sortable index; drill into any entity's columns, PII, metrics, and canonical joins.\n- **Data Dictionary** (`/dict`) — every table, column, type, PII class, join, and metric, with one-click Markdown export (the same artifact `schemabrain docs` writes).\n- **PII matrix** (`/pii`) — *which columns carry sensitive data?* A heatmap with one row per classified column and one cell per PII category, each column tagged block / redact / allow by its advisory band. Columns in a catastrophic-leak category (`credential`, `payment_card`, `government_id`) are hard-blocked regardless of policy and pinned to the top — so you catch a `payment_card` column hiding inside `users` before you point an agent at a new schema, and see at a glance what trips the default `--pii-block` policy. Select any row to drill into its entity's columns, metrics, and joins.\n- **Refusals** (`/refusals`) — *what did SchemaBrain block, and what did the agent see?* A chronological feed of held calls; expand any row to reveal the full envelope inline — the reason that fired (`pii_blocked`, `allowlist_violation`, `fragment_unsafe`, `cost_cap_exceeded`, `ambiguous_resolution`, `schema_drift`), the exact category set that intersected the policy, and the structured `error.recovery` (suggested tool + args) the agent got back to recover. Use it to triage \"the agent says it can't access that\" and to review whether those hints actually helped.\n- **Audit Viewer** (`/audit`) — *is the audit chain still intact?* The visual face of the tamper-evident log: every tool call writes exactly one row — whatever the outcome — anchored by `chain_hash = sha256(prev_hash || canonical(row))`. An integrity strip reads `not verified this session` until you run a pass, then `verified · n/N intact` (or flags `N rows edited after write`); the **Verify** button re-walks the chain server-side *and* recomputes each visible row's RFC-6962 Merkle inclusion proof in your browser. Selecting a row opens the full body (tool, status, cost class, PII categories, fingerprint, `chain_hash`, and the proof ladder up to the root). Reload to pick up new calls.\n- **Policy** (`/policy`) — the block / redact / allow grid the firewall enforces, with the always-on catastrophic-leak floor disclosed (it can't be removed). Changes are made via copy-the-CLI actions — the dashboard never writes.\n- **Drift** (`/drift`) — config and enrichment drift the store can detect, each with a copy-the-CLI fix.\n\n<p align=\"center\">\n  <img src=\"docs/assets/dashboard-graph.png\" alt=\"Knowledge Graph — entities as nodes, canonical joins as edges, catastrophic-PII entities flagged red, with the compiled join path highlighted\" width=\"100%\"><br>\n  <em>Knowledge Graph — your schema as the entity-relationship projection the semantic layer compiles joins against; catastrophic-PII entities flagged, the canonical join path traced.</em>\n</p>\n\n<details>\n<summary><strong>More dashboard views</strong> — Overview, PII matrix, Refusals, Audit, Entities, Data Dictionary, Policy &amp; Drift</summary>\n\n<p align=\"center\">\n  <img src=\"docs/assets/dashboard-overview.png\" alt=\"Overview — bento summary of bound entities, catastrophic-PII floors, refusals, and audit-chain health\" width=\"100%\"><br>\n  <em>Overview — the whole boundary on one screen: what's bound, what's protected, what's drifted.</em>\n</p>\n\n<p align=\"center\">\n  <img src=\"docs/assets/dashboard-pii-ledger.png\" alt=\"PII matrix — one row per classified column across the PII categories, catastrophic columns hard-blocked\" width=\"100%\"><br>\n  <em>PII matrix — which columns carry sensitive data, and what the default policy blocks.</em>\n</p>\n\n<p align=\"center\">\n  <img src=\"docs/assets/dashboard-refusals.png\" alt=\"Refusals — a held call expanded to show the reason, blocked category, recovery hint, and reconstructed envelope\" width=\"100%\"><br>\n  <em>Refusals — every blocked call, the reason that fired, and the recovery hint the agent received.</em>\n</p>\n\n<p align=\"center\">\n  <img src=\"docs/assets/dashboard-audit-viewer.png\" alt=\"Audit Viewer — hash-chained ledger re-walked and verified intact, every row proven against the Merkle root\" width=\"100%\"><br>\n  <em>Audit Viewer — the tamper-evident chain, verified server-side down to each row's hash linkage.</em>\n</p>\n\n<p align=\"center\">\n  <img src=\"docs/assets/dashboard-entities.png\" alt=\"Entities — sortable index of every bound entity with PII exposure, binding confidence, metrics, and joins\" width=\"100%\"><br>\n  <em>Entities — every business entity bound out of the raw schema, with PII exposure and join counts.</em>\n</p>\n\n<p align=\"center\">\n  <img src=\"docs/assets/dashboard-dict.png\" alt=\"Data Dictionary — per-entity columns, types, PII classes, and joins with one-click Markdown export\" width=\"100%\"><br>\n  <em>Data Dictionary — every table, column, type, and join, exportable to Markdown for your repo or wiki.</em>\n</p>\n\n<p align=\"center\">\n  <img src=\"docs/assets/dashboard-policy.png\" alt=\"Policy — per-column block/redact/allow grid with the catastrophic-leak floor disclosed and the schemabrain.yaml it compiles\" width=\"100%\"><br>\n  <em>Policy — the block / redact / allow grid the firewall enforces, with the always-on floor disclosed.</em>\n</p>\n\n<p align=\"center\">\n  <img src=\"docs/assets/dashboard-drift.png\" alt=\"Drift — config and enrichment drift detection, here showing a fresh, in-sync context\" width=\"100%\"><br>\n  <em>Drift — config and enrichment drift the store can detect, each with a copy-the-CLI fix.</em>\n</p>\n\n</details>\n\nThe dashboard binds **`127.0.0.1` only** — there is no `--host` flag, by design. It's read-only and reads the same SQLite store `serve` writes to. No agent talks to it.\n\n[Dashboard guide →](docs/dashboard/overview.mdx) · [PII matrix →](docs/dashboard/pii-matrix.mdx) · [Refusals →](docs/dashboard/refusals.mdx) · [Audit Viewer →](docs/dashboard/audit-viewer.mdx)\n\n---\n\n## Works with\n\nSchemaBrain speaks the [Model Context Protocol](https://modelcontextprotocol.io) over **stdio**. `schemabrain init --host <X>` writes first-party config for four MCP clients; everything else that speaks MCP stdio works via `--host manual` (prints the snippet, you paste).\n\n### First-party wiring\n\n`schemabrain init --host <X>` writes the MCP entry directly into the host's config file.\n\n| Client | Setup guide | Config path |\n|---|---|---|\n| **Claude Desktop** | [`/setup/claude-desktop`](docs/setup/claude-desktop.md) | macOS: `~/Library/Application Support/Claude/claude_desktop_config.json`<br>Windows: `%APPDATA%\\Claude\\claude_desktop_config.json` |\n| **Claude Code** | [`/setup/claude-code`](docs/setup/claude-code.md) | Shells out to `claude mcp add` |\n| **Cursor** | [`/setup/cursor`](docs/setup/cursor.md) | `~/.cursor/mcp.json` |\n| **Windsurf** | [`/setup/windsurf`](docs/setup/windsurf.md) | `~/.codeium/windsurf/mcp_config.json` |\n\n### Any other MCP stdio host\n\n`schemabrain init --host manual` prints the JSON entry to stdout — paste it into whatever host config you're using. Any client that launches a subprocess and speaks MCP stdio should work in principle; we have not exhaustively tested each. Common targets:\n\n- **Zed** — full walkthrough at [`docs/setup/zed.md`](docs/setup/zed.md)\n- **Codex CLI** (working path for ChatGPT users) — full walkthrough at [`docs/setup/codex.md`](docs/setup/codex.md)\n- **Cline** (VS Code extension) — `schemabrain init --host manual` prints the `mcpServers` block; paste it into Cline's settings via **MCP Servers → Configure MCP Servers**. Full walkthrough at [`docs/setup/cline.md`](docs/setup/cline.md)\n- **Continue** — paste into `~/.continue/config.json`\n- **Your own agent loop** — see [`examples/anthropic_demo.py`](examples/anthropic_demo.py) for a ~250-LOC Anthropic-SDK reference\n\nThe 12-tool surface, PII-aware refusal, audit chain, and recovery contracts are transport-agnostic — any compliant stdio MCP client gets the same guarantees.\n\n### Agent frameworks\n\nThe same stdio MCP surface is reachable from any framework that can spawn an MCP server. The Anthropic SDK path is first-party-tested; the others work in principle if the framework's MCP integration speaks stdio.\n\n- **Anthropic SDK** — first-party walkthrough at [`docs/setup/manual.md`](docs/setup/manual.md#3-wire-your-own-agent-anthropic-sdk); reference loop at [`examples/anthropic_demo.py`](examples/anthropic_demo.py)\n- **LangChain / LangGraph** — via [`langchain-mcp-adapters`](https://github.com/langchain-ai/langchain-mcp-adapters)\n- **Pydantic AI** — via its [built-in MCP support](https://ai.pydantic.dev/mcp/)\n- **CrewAI / AutoGen / Agno / custom loops** — any framework with a stdio MCP client works in principle; we have not tested each\n\nWe don't ship per-framework adapters; the framework's standard MCP client is sufficient.\n\n### Not yet supported (cloud / HTTPS hosts)\n\nSchemaBrain v0.6 ships stdio only — no HTTPS / SSE transport. Clients that require a cloud HTTPS endpoint do **not** work today:\n\n- **ChatGPT Connectors** — see the [honest gap page](docs/setup/chatgpt.md) for workarounds and the v0.5+ roadmap\n- **Hosted MCP gateways** — by design (local-first wedge; see [vs Querybear](docs/compare/querybear.mdx))\n\nIf you need ChatGPT support today, a community stdio→HTTPS bridge (`mcp-remote`, `mcp-proxy`, etc.) may work; we have not validated any specific bridge against the PII / audit / recovery semantics.\n\n[Security posture for procurement / review →](docs/security.md)\n\n---\n\n## Sample session\n\nReal Claude Desktop session against the bundled SaaS fixture (12 tables, 84 columns, 12 entities). Condensed for length — a real agent explores `list_metrics` / `suggest_joins` first, then concludes the path is unreachable:\n\n> **You:** Using SchemaBrain, compute our usage volume broken down by plan tier.\n>\n> **Claude:** *(calls `get_metric(name=\"usage_volume\", group_by=[\"plan.title\"])`. The metric anchors on `usage_event`; reaching `plan` would need a join that isn't in the schema, so it refuses:)*\n>\n> ```json\n> { \"status\": \"error\",\n>   \"error\": {\n>     \"kind\": \"unreachable_entity\",\n>     \"message\": \"entity 'plan' is not reachable from metric anchor 'usage_event'; no canonical join exists. Run `schemabrain joins suggest` to surface candidate joins.\",\n>     \"recovery\": { \"suggested_tool\": \"resolve_join\",\n>                   \"suggested_args\": { \"entity_a\": \"usage_event\", \"entity_b\": \"plan\" } } } }\n> ```\n>\n> *(Claude confirms at the join layer — `resolve_join(\"usage_event\", \"plan\")` returns `kind: no_canonical_join`. `usage_events` carries only `workspace_id`, never a `plan_id`, and no table bridges the two.)*\n>\n> Verdict up front: I can't compute usage-by-plan, and you shouldn't want me to fake it — there is no modeled relationship between usage events and plans in this schema. `usage_events` records a `workspace_id`, not a plan, and nothing joins the two. \"Usage volume by plan tier\" is unanswerable until that link exists — and I'm not going to invent it.\n>\n> What I **can** give you — contracted revenue by plan tier (this path fully resolves: `subscription_item → subscription → plan`, both `fk_constraint` joins, `confidence: HIGH`):\n>\n> ```sql\n> SELECT p.title                                     AS plan_tier,\n>        SUM(si.unit_price_cents * si.seats) / 100.0  AS revenue   -- prices stored in integer cents\n> FROM   public.subscription_items si\n> JOIN   public.subscriptions      s ON si.subscription_id = s.id\n> JOIN   public.plans              p ON s.plan_id          = p.id\n> GROUP BY p.title\n> ORDER BY revenue DESC;\n> ```\n>\n> | plan_tier  | revenue     |\n> |------------|-------------|\n> | Enterprise | $379,620.00 |\n> | Pro        | $539.00     |\n> | Free       | $54.00      |\n>\n> Two honest caveats:\n> - **Contracted, not collected.** This is subscription line-item revenue (`seats × unit_price_cents`), not paid invoices. For billed totals, the `total_revenue` metric anchors on `invoices` instead.\n> - **Integer cents.** `unit_price_cents` is stored as an integer; the `/ 100.0` converts to currency.\n\nThe differentiator is what *didn't* happen: most LLM-over-database tools, asked for usage-by-plan, would confidently emit `JOIN plans p ON usage_events.plan_id = p.id` against a `plan_id` column that doesn't exist. SchemaBrain refused — `get_metric` returned `kind: unreachable_entity` with `recovery.suggested_tool: resolve_join`, not prose. The agent **acted on the structured recovery contract programmatically** instead of fabricating a join. Refusal-not-fabrication is the safety mechanism, demonstrated live.\n\n**Cost.** ~$0.0004/column with Claude Haiku 4.5 (cryptic-name columns can opt into Sonnet 4.6 via `--enable-sonnet`). The bundled 12-table fixture (84 columns, 12 entities + 5 metrics + 11 joins) ships pre-curated, so the demo path applies it for **$0** — no API key. Indexing those 84 columns with LLM column descriptions comes to about **$0.03**. The Pagila DVD-rental sample (87 columns after partition deduplication) is the directly measured reference — **$0.0299 in 105s**. Re-indexing an unchanged schema is **$0** — content-addressable fingerprinting skips the LLM call entirely.\n\nTo verify Claude's SQL is mechanically correct (and that flagged caveats are the actual data behavior), see [Validating SQL Claude generates](docs/setup/manual.md#6-validating-sql-claude-generates).\n\n**Run this exact session yourself:** `schemabrain init` walks you to a wired Claude Desktop in one command; then ask Claude *\"Using SchemaBrain, compute our usage volume broken down by plan tier.\"* and watch the refuse-then-pivot live.\n\n---\n\n## Where it's going\n\nSchemaBrain is evolving into a **trust and intelligence layer between AI agents and your database** — it gives the agent a semantic map of your schema, compiles answers from definitions you control, and keeps every call PII-aware and audited. SQL-boundary safety is one proof-point of that layer, not the whole identity.\n\nThat posture rests on a semantic substrate. You can't refuse \"this query touches PII\" without knowing which columns are PII. You can't answer \"join through this junction\" without canonical-join definitions. You can't serve a metric without knowing its grain.\n\nSo the engineering order is **schema intelligence → semantic substrate → trust primitives.** Today the agent never writes raw SQL: it calls `get_metric` and the semantic-layer tools, SchemaBrain compiles parameterized SQL the agent never sees, and you get PII-aware refusal, structured recovery on every refused or degraded call, read-only execution with statement timeouts and row caps, and a tamper-evident audit chain. That def-driven, compiled-SQL posture is the default and the recommended one. Inspecting arbitrary agent-emitted SQL (`validate_query` / `execute`) is a later, optional opt-in lane — not the direction we're pivoting to. See the [Roadmap](#roadmap).\n\n---\n\n## Roadmap\n\n> The `v0.5` / `v1` / `v2` / `v3` labels are **roadmap milestone names**, not package versions. The package follows strict semver — `1.0.0` is reserved for an API that's been battle-tested by external users without a forced break. See [ADR-0003](docs/adr/0003-versioning-policy.md).\n\nThe full, living roadmap — including explicit non-goals and how to influence priorities — lives in [`ROADMAP.md`](ROADMAP.md).\n\n### Now — shipping in v0.6.x\n\nWhat you get from `pip install schemabrain`:\n\n- **MCP server, 12 read-only tools** — `find_relevant_tables`, `find_relevant_entities`, `describe_table`, `describe_column`, `describe_entity`, `list_entities`, `list_metrics`, `list_joins`, `suggest_joins`, `resolve_join`, `get_example_queries`, `get_metric`.\n- **Def-driven compilation** — the agent never writes raw SQL; answers compile from definitions you control, with read-only execution enforced at the database layer plus statement timeouts and row caps.\n- **Schema-intelligence engine** — index Postgres into a local SQLite store; cost-capped LLM semantic enrichment (with opt-in Sonnet routing for cryptic columns, `--enable-sonnet`); on-device embeddings (BAAI/bge-small ONNX); semantic table retrieval (cosine similarity over those embeddings, ranked per-table by best-matching column); entity identification with rationale + confidence; declared-FK, query-log, and dbt-`relationships` join mining; a persisted canonical join graph with multi-hop BFS; and a metrics layer.\n- **Trust & safety** — PII classification (60 rules across 12 categories) with per-column confidence, tag propagation, a catastrophic-leak floor (grouping *by* a PII column refuses as row-level disclosure), an editable policy (block / redact / allow plus per-column overrides), and a tamper-evident sha256 hash-chained audit log with browser-verifiable RFC-6962 Merkle proofs and `audit verify`.\n- **Graph-led dashboard, 9 surfaces** — a signature interactive **Knowledge Graph**, plus **Overview**, **Entities** (sortable index + drilldown with a semantic pane), **Data Dictionary** (Export-to-Markdown), **PII matrix**, **Refusals**, **Audit Viewer**, an editable **Policy** editor, and **Drift** intelligence. Dual-theme, opt-in, read-only, `127.0.0.1`-only.\n- **CLI** — `init`, `demo`, `index`, `import dbt`, `inspect`, `diff`, `check`, `entities`, `joins`, `metrics`, `policy {show, apply, tag}`, `docs`, `dashboard`, `doctor`, `serve`, `audit`. Distributed on PyPI (Apache-2.0 licensed) and as a headless Docker image.\n\n### Later — roadmap (deferred; future direction only)\n\n**Phase 2 — differentiators**\n\n- Query cost estimation (`EXPLAIN` of the compiled SQL)\n- Tenant-isolation detection — missing-filter and cross-tenant-join checks\n- Impact analysis across definitions\n- Usage intelligence — hotspots and dead-table detection\n- A general policy-rule grammar\n- Implicit-FK discovery without query logs\n- Context budgeting for tool responses\n\n**Phase 3 — exploratory**\n\n- Persistent agent memory\n- Multi-agent coordination\n- Remote MCP transport plus a thin client SDK\n- An **optional, opt-in agent-authored-SQL lane** (`validate_query` / `execute`) behind an explicit flag. Def-driven compiled SQL stays the **default and recommended** posture; this lane is for teams that want parse-before-execute over arbitrary agent-emitted SQL, should it land. It is not shipped, and it is not a planned pivot away from the def-driven default.\n\nEverything on this roadmap is open source.\n\n---\n\n## Troubleshooting\n\nThe five most common first-run failures. Full troubleshooter in [`docs/setup/manual.md`](docs/setup/manual.md#5-troubleshooting).\n\n- **`pip install schemabrain` gave me an older version.** Check `schemabrain --version`. If it doesn't match the [latest release](https://pypi.org/project/schemabrain/) your pip cache is stale — run `pip install --upgrade schemabrain`. `schemabrain init` writes the same version into the Claude Desktop snippet so it stays reproducible across restarts. When you installed from PyPI **and** `uv` is on your PATH, the snippet runs `uvx schemabrain==<pin>` (bump the pin manually after a pip upgrade); otherwise — a non-PyPI install (local wheel, editable, or git checkout) or no `uvx` — it pins the absolute path of the installed `schemabrain` entry point, which tracks the environment you ran `init` from.\n- **`init` reports `source unreachable`.** Postgres may not be ready on first run — wait a few seconds and re-run. For your own database, verify host, port, and credentials. Connection URLs in any form are accepted (`postgresql://`, `postgres://`, `postgresql+psycopg://`).\n- **The first `init` or `schemabrain index` hangs for ~60 seconds.** Normal. The first index downloads the ONNX embedding model (~67 MB) and makes one LLM call per column. Subsequent runs are fast.\n- **`init` fails at stage 6 \"wire host\".** Claude Desktop must be installed first — SchemaBrain writes into its config file, which doesn't exist until Claude Desktop has launched at least once.\n- **Claude Desktop doesn't show SchemaBrain after restart.** Cmd+Q is required (close-window doesn't trigger a re-read of MCP config). Run `schemabrain doctor` to verify the config landed. If `doctor` says everything's good but Claude Desktop still doesn't see the tool, check `~/Library/Logs/Claude/mcp*.log`.\n- **Apple Silicon + Python 3.12.** `fastembed`'s `onnxruntime` dependency ships no arm64 wheel for Python 3.12+, so embeddings can't build. `init` catches this at preflight and tells you to either use Python 3.11 (e.g. `pyenv local 3.11.10`) or re-run with `--no-embed` (keyword search instead of semantic — everything else works).\n\n---\n\n## Documentation\n\n| Doc | What's inside |\n|---|---|\n| [`docs/setup.md`](docs/setup.md) | Activation wizard (recommended) — pick a host, run the wizard, ask the agent (~60s) |\n| [`docs/setup/docker.md`](docs/setup/docker.md) | Docker install (image with embedding model baked in, no first-run download) |\n| [`docs/setup/manual.md`](docs/setup/manual.md) | Manual `index`, mine-queries, logs config, troubleshooting, MCP Inspector, SQL-validation ladder |\n| [`docs/first-5-queries.md`](docs/first-5-queries.md) | What to actually *do* after `init` — five queries that exercise read-only, PII-aware refusal, audit chain, and structured recovery |\n| [`docs/semantic-layer.md`](docs/semantic-layer.md) | Building entities, metrics (incl. composite expressions), canonical joins (incl. multi-hop), dbt import |\n| [`docs/operations.md`](docs/operations.md) | `inspect`, `check` (drift), `index --dry-run`, Docker compose |\n| [`docs/observability.md`](docs/observability.md) | `tail`, audit log, OTel export, PII classification |\n| [`docs/reference/mcp-tools/overview.mdx`](docs/reference/mcp-tools/overview.mdx) | Full reference for all 12 MCP tools (overview + 12 per-tool pages) |\n| [`docs/architecture.mdx`](docs/architecture.mdx) | Pipeline, retrieval contract, cache logic, cost model, eval |\n| [`docs/dashboard/overview.mdx`](docs/dashboard/overview.mdx) | Read-only observability dashboard — PII matrix, refusals, audit viewer |\n| [`docs/landscape.md`](docs/landscape.md) | Comparison vs Vanna / Atlan / dbt-mcp / WrenAI; \"is this a semantic layer?\" |\n| [`docs/threat-model.md`](docs/threat-model.md) | Security model + boundaries |\n| [`docs/adr/`](docs/adr/) | Architecture decision records (audit/PII taxonomy, store protocol, versioning policy, observability bus) |\n| [`examples/`](examples/) | Copy-paste-ready MCP configs, headless agent loop, end-to-end ecommerce walkthrough |\n\n---\n\n## FAQ\n\n**Does my data leave my machine?**\nOnly LLM-enriched column descriptions and the redacted sample values that feed them. Three regex passes (email, US SSN, credit-card-shaped digit runs) run on every sample before it leaves the profiler module — see [`schemabrain/profiler/stats.py`](schemabrain/profiler/stats.py). The Anthropic API call sends column metadata + redacted samples + sibling-column context — no raw rows. Embeddings are generated locally via `fastembed` (BAAI/bge-small-en-v1.5, ONNX, ~67 MB).\n\n**What databases work today?**\nPostgres 16+ is the only **source** connector today (the local store itself is a SQLite file). A SQLite *source* connector, plus Snowflake / BigQuery / MySQL, is mostly a new `DataSource` implementation plus a profiler tweak — on the v1.x roadmap.\n\n**Why MCP and not a REST API?**\nThe consumer is an agent, not a service. MCP standardizes tool registration, schema description, and request/response transport. Agents discover SchemaBrain natively and get its tool surface — no API wrapper, no SDK to maintain per language.\n\n**Is this a semantic layer like Cube or dbt Semantic Layer?**\nNot exactly — SchemaBrain is the trust and intelligence layer between AI agents and your database, built on a semantic-layer substrate. Entities, metrics, and canonical joins are first-class persisted definitions (`list_entities`, `describe_entity`, `resolve_join`, `get_metric`), and they make the safety primitives possible — read-only-by-architecture, PII-aware refusal, audit chain. The semantic substrate is the foundation; SQL-boundary safety, including the firewall, is one proof-point of the layer, not its whole identity. Full comparison vs Cube / dbt-mcp / Vanna / WrenAI in [`docs/landscape.md`](docs/landscape.md).\n\nMore questions answered in [`docs/setup/manual.md`](docs/setup/manual.md#5-troubleshooting) (why local embeddings, more troubleshooting).\n\n---\n\n## Running it on your own Postgres?\n\nIf you're pointing AI agents at a real (non-demo) Postgres, I'd genuinely like to hear how it goes — what worked, what broke, what felt sharp or rough. Open a [GitHub Discussion](https://github.com/Arun-kc/schemabrain/discussions) or a [GitHub issue](https://github.com/Arun-kc/schemabrain/issues), or reach me on GitHub ([@Arun-kc](https://github.com/Arun-kc)). Happy to help you wire it up.\n\n---\n\n## Contributors\n\n<a href=\"https://github.com/Arun-kc/schemabrain/graphs/contributors\">\n  <img src=\"https://contrib.rocks/image?repo=Arun-kc/schemabrain\" alt=\"Contributors to schemabrain\" />\n</a>\n\n---\n\n## Contributing & License\n\nPRs welcome. The bar is high — see [`CONTRIBUTING.md`](CONTRIBUTING.md) for the test-first / 99%-coverage / conventional-commits / architecture-invariants checklist. CI enforces all of it.\n\nBugs and feature requests use the structured templates in `.github/ISSUE_TEMPLATE/`. Issues without a reproduction (bugs) or a clear underlying problem (features) get closed with a request to re-open with the right info.\n\n[Apache 2.0](LICENSE).\n",
  "bytes": 39831,
  "sha": "e74faf0117e93e2a59d709355d917fbbecc86bf2cbf767d3e31fef06765b35ac",
  "repo_slug": "arun-kc/schemabrain",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/mcp_io_github_arun_kc_schemabrain_10004e1d/readme"
}