Skip to main content
Glama
Arun-kc

schemabrain

by Arun-kc

SchemaBrain compiles every query from definitions you control — no path from a prompt to raw SQL at your database.

Three guarantees that close the trust gap between AI agents and your database:

  • Read-only by architecture — 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.

  • PII-aware refusal at retrieval — 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.

  • Cryptographic audit chain — 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.

See it in action — ask for something the schema can't answer, and it refuses instead of fabricating a join:

You: compute usage volume by plan tier

SchemaBrain → agent: { "kind": "unreachable_entity", "recovery": { "suggested_tool": "resolve_join" } } — there's no plan_id on usage events, so it won't invent one.

Claude: I can't fake that join — here's contracted revenue by plan tier instead, which actually resolves. ✓

Full session, with the SQL and results

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:


uvx schemabrain init
# then: Cmd+Q Claude Desktop, relaunch, and ask: "list the entities SchemaBrain knows about"
# prefer a persistent install? pipx install schemabrain (or) pip install schemabrain

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.

Status: 0.6.0 (beta). Postgres supported today (the local store itself is SQLite). SQLite / Snowflake / BigQuery / MySQL source connectors on the roadmap.


Contents

Read next based on what you need:

Goal

Where to go

Try it on the bundled fixture

Quickstart

Understand the safety guarantees

Safety guarantees

Wire up your MCP client

Claude Desktop · Claude Code · Cursor · Windsurf · Cline · ChatGPT (roadmap)

Plug into your own agent loop

docs/setup/manual.md

Build a semantic layer

docs/semantic-layer.md

Run in production (audit, drift, Docker)

docs/operations.md

Observe the agent (tail, audit log, OTel)

docs/observability.md

Compare with Querybear / Anthropic reference Postgres MCP

vs Querybear · vs Anthropic reference

Compare with Vanna / Atlan / dbt-mcp / WrenAI

docs/landscape.md


Related MCP server: safedb-mcp

Quickstart

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.

Three 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.

1. Install

uvx schemabrain init        # zero-install: runs the wizard in one shot
# or install persistently first:
pipx install schemabrain    # (or) pip install schemabrain
schemabrain --version

Source install (git clone + uv sync --extra dev) is documented in docs/setup.md.

2. Run the activation wizard

schemabrain init

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:

  • 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).

  • 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.

SchemaBrain init — activation wizard

  [1/7] Source check       ✓ source reachable + read-only
  [2/7] Index schema       ✓ 12 tables, 84 columns indexed
  [3/7] Curate entities    ✓ 12 entities applied (bundled demo pack)
  [4/7] Curate metrics     ✓ 5 metrics applied (bundled demo pack)
  [5/7] Curate joins       ✓ 11 canonical joins applied (bundled demo pack)
  [6/7] Wire host          ✓ wrote schemabrain entry to claude_desktop_config.json
                           (default; switch with --host claude-code|cursor|windsurf|manual)
  [7/7] Next               ✓ restart your MCP host, then ask: "list the entities SchemaBrain knows about"

Full 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.

3. Restart Claude Desktop and ask

  1. Quit Claude Desktop fully — Cmd+Q, not just close the window. The MCP config is only read on cold start.

  2. Relaunch.

  3. New conversation:

    list the entities SchemaBrain knows about

If Claude calls list_entities and reports user, order, etc., you're done. If not, see Troubleshooting.

After the wizard, schemabrain inspect shows what the agent has and schemabrain tail streams every tool call live — see docs/operations.md.

Your project files

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:

schemabrain init --url-env DATABASE_URL --emit-yaml-dir ./schemabrain
# → ./schemabrain/pii_policy.yaml + entities/ + metrics/ + joins/

Edit 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.


Safety guarantees

Six properties SchemaBrain enforces at the SQL boundary today:

1. Read-only by architecture, not configuration

The 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 →

2. PII-aware refusal at the get_metric tool boundary

Any 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 →

3. Tamper-evident audit log

Every 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.

schemabrain audit verify   # exit 0 = chain clean

Tamper-evident audit chain →

4. Failure is a contract, not a string

Every 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.

{ "status": "error", "kind": "ambiguous_time_dimension",
  "recovery": { "suggested_tool": "get_metric",
                "suggested_args": {"time_dimension": "order.placed_at"} } }

Structured recovery →

5. Compile path: definitions → parameterized SQL

Entities, 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 →

6. Pluggable into any agent loop

The 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 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 →


Observability dashboard

SchemaBrain 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.

pip install "schemabrain[ui]"
schemabrain dashboard
# → http://127.0.0.1:7878

It'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:

  • 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.

  • Overview (/overview) — the home surface: entity / metric / join / catastrophic-PII counts at a glance.

  • Entities (/entities) — a sortable index; drill into any entity's columns, PII, metrics, and canonical joins.

  • Data Dictionary (/dict) — every table, column, type, PII class, join, and metric, with one-click Markdown export (the same artifact schemabrain docs writes).

  • 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.

  • 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.

  • 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.

  • 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.

  • Drift (/drift) — config and enrichment drift the store can detect, each with a copy-the-CLI fix.

The 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.

Dashboard guide → · PII matrix → · Refusals → · Audit Viewer →


Works with

SchemaBrain speaks the Model Context Protocol 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).

First-party wiring

schemabrain init --host <X> writes the MCP entry directly into the host's config file.

Client

Setup guide

Config path

Claude Desktop

/setup/claude-desktop

macOS: ~/Library/Application Support/Claude/claude_desktop_config.jsonWindows: %APPDATA%\Claude\claude_desktop_config.json

Claude Code

/setup/claude-code

Shells out to claude mcp add

Cursor

/setup/cursor

~/.cursor/mcp.json

Windsurf

/setup/windsurf

~/.codeium/windsurf/mcp_config.json

Any other MCP stdio host

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:

  • Zed — full walkthrough at docs/setup/zed.md

  • Codex CLI (working path for ChatGPT users) — full walkthrough at docs/setup/codex.md

  • 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

  • Continue — paste into ~/.continue/config.json

  • Your own agent loop — see examples/anthropic_demo.py for a ~250-LOC Anthropic-SDK reference

The 12-tool surface, PII-aware refusal, audit chain, and recovery contracts are transport-agnostic — any compliant stdio MCP client gets the same guarantees.

Agent frameworks

The 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.

We don't ship per-framework adapters; the framework's standard MCP client is sufficient.

Not yet supported (cloud / HTTPS hosts)

SchemaBrain v0.6 ships stdio only — no HTTPS / SSE transport. Clients that require a cloud HTTPS endpoint do not work today:

  • ChatGPT Connectors — see the honest gap page for workarounds and the v0.5+ roadmap

  • Hosted MCP gateways — by design (local-first wedge; see vs Querybear)

If 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.

Security posture for procurement / review →


Sample session

Real 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:

You: Using SchemaBrain, compute our usage volume broken down by plan tier.

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:)

{ "status": "error",
  "error": {
    "kind": "unreachable_entity",
    "message": "entity 'plan' is not reachable from metric anchor 'usage_event'; no canonical join exists. Run `schemabrain joins suggest` to surface candidate joins.",
    "recovery": { "suggested_tool": "resolve_join",
                  "suggested_args": { "entity_a": "usage_event", "entity_b": "plan" } } } }

(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.)

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.

What I can give you — contracted revenue by plan tier (this path fully resolves: subscription_item → subscription → plan, both fk_constraint joins, confidence: HIGH):

SELECT p.title                                     AS plan_tier,
       SUM(si.unit_price_cents * si.seats) / 100.0  AS revenue   -- prices stored in integer cents
FROM   public.subscription_items si
JOIN   public.subscriptions      s ON si.subscription_id = s.id
JOIN   public.plans              p ON s.plan_id          = p.id
GROUP BY p.title
ORDER BY revenue DESC;

plan_tier

revenue

Enterprise

$379,620.00

Pro

$539.00

Free

$54.00

Two honest caveats:

  • 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.

  • Integer cents. unit_price_cents is stored as an integer; the / 100.0 converts to currency.

The 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.

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.

To verify Claude's SQL is mechanically correct (and that flagged caveats are the actual data behavior), see Validating SQL Claude generates.

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.


Where it's going

SchemaBrain 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.

That 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.

So 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

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.

The full, living roadmap — including explicit non-goals and how to influence priorities — lives in ROADMAP.md.

Now — shipping in v0.6.x

What you get from pip install schemabrain:

  • MCP server, 12 read-only toolsfind_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.

  • 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.

  • 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.

  • 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.

  • 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.

  • CLIinit, 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.

Later — roadmap (deferred; future direction only)

Phase 2 — differentiators

  • Query cost estimation (EXPLAIN of the compiled SQL)

  • Tenant-isolation detection — missing-filter and cross-tenant-join checks

  • Impact analysis across definitions

  • Usage intelligence — hotspots and dead-table detection

  • A general policy-rule grammar

  • Implicit-FK discovery without query logs

  • Context budgeting for tool responses

Phase 3 — exploratory

  • Persistent agent memory

  • Multi-agent coordination

  • Remote MCP transport plus a thin client SDK

  • 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.

Everything on this roadmap is open source.


Troubleshooting

The five most common first-run failures. Full troubleshooter in docs/setup/manual.md.

  • pip install schemabrain gave me an older version. Check schemabrain --version. If it doesn't match the latest release 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.

  • 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://).

  • 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.

  • 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.

  • 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.

  • 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).


Documentation

Doc

What's inside

docs/setup.md

Activation wizard (recommended) — pick a host, run the wizard, ask the agent (~60s)

docs/setup/docker.md

Docker install (image with embedding model baked in, no first-run download)

docs/setup/manual.md

Manual index, mine-queries, logs config, troubleshooting, MCP Inspector, SQL-validation ladder

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

docs/semantic-layer.md

Building entities, metrics (incl. composite expressions), canonical joins (incl. multi-hop), dbt import

docs/operations.md

inspect, check (drift), index --dry-run, Docker compose

docs/observability.md

tail, audit log, OTel export, PII classification

docs/reference/mcp-tools/overview.mdx

Full reference for all 12 MCP tools (overview + 12 per-tool pages)

docs/architecture.mdx

Pipeline, retrieval contract, cache logic, cost model, eval

docs/dashboard/overview.mdx

Read-only observability dashboard — PII matrix, refusals, audit viewer

docs/landscape.md

Comparison vs Vanna / Atlan / dbt-mcp / WrenAI; "is this a semantic layer?"

docs/threat-model.md

Security model + boundaries

docs/adr/

Architecture decision records (audit/PII taxonomy, store protocol, versioning policy, observability bus)

examples/

Copy-paste-ready MCP configs, headless agent loop, end-to-end ecommerce walkthrough


FAQ

Does my data leave my machine? Only 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. 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).

What databases work today? Postgres 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.

Why MCP and not a REST API? The 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.

Is this a semantic layer like Cube or dbt Semantic Layer? Not 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.

More questions answered in docs/setup/manual.md (why local embeddings, more troubleshooting).


Running it on your own Postgres?

If 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 or a GitHub issue, or reach me on GitHub (@Arun-kc). Happy to help you wire it up.


Contributors


Contributing & License

PRs welcome. The bar is high — see CONTRIBUTING.md for the test-first / 99%-coverage / conventional-commits / architecture-invariants checklist. CI enforces all of it.

Bugs 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.

Apache 2.0.

Available Tools

12 tools
describe_columnA
Read-onlyIdempotent

Use this when you need to drill into one column by its three-part qualified name (e.g. public.orders.user_id). Returns data type, nullability, default, LLM description, and BOTH join directions — outgoing FKs (this column joins out) and incoming FKs (which tables reference this column). Use describe_table instead when you want the whole table at once. Common composition: chain describe_table to describe_column to map a column's full role across schema.

ParametersJSON Schema
NameRequiredDescriptionDefault
qualified_nameYesPostgres `schema.table.column` qualified name (e.g. `public.orders.user_id`). Three dot-separated parts. Call `describe_table` first if you only know the table and need to discover its columns.

Output Schema

ParametersJSON Schema
NameRequiredDescription
statusYes
dataNo
errorNo
confidenceNo
provenanceNo
follow_up_hintsNo
degradation_reasonNo
charter_versionNo

TDQS

A4.9/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already provide safety profile (readOnly, idempotent, non-destructive). The description adds valuable behavioral details: it returns data type, nullability, default, LLM description, and both outgoing and incoming foreign key directions. No contradictions.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Three sentences with no fluff. First sentence states purpose and key output, second gives alternative, third provides composition pattern. Every sentence adds value.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given one parameter, full annotations, and an output schema (present, though not detailed here), the description covers the tool's purpose, output, and usage flow comprehensively. No missing information.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The single parameter 'qualified_name' is well-described in the schema (100% coverage). The description adds extra guidance on the format (e.g. 'public.orders.user_id') and a prerequisite hint to call describe_table first if needed, exceeding baseline 3.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool's verb ('drill into') and resource ('one column'), and distinguishes it from the sibling 'describe_table' by specifying it works on a single column with a three-part qualified name.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly states when to use this tool ('when you need to drill into one column'), when to use the alternative ('Use describe_table instead when you want the whole table'), and provides a common composition pattern ('chain describe_table to describe_column').

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

describe_entityA
Read-onlyIdempotent

Use this when the user names a specific entity (e.g. 'show me the customer entity', 'what's in the order entity'). Returns the entity's bound table, identity column, description, and full column list with PII sensitivity. Use list_entities instead when you don't yet know what entities exist. Common compositions: chain to describe_table to see the physical structure under the entity; chain to describe_column for one column's join graph.

ParametersJSON Schema
NameRequiredDescriptionDefault
nameYesEntity name (a single identifier — no dots, no schema qualifier; e.g. `customer`, not `public.customer`). Call `list_entities` first if you don't know the entity names.

Output Schema

ParametersJSON Schema
NameRequiredDescription
statusYes
dataNo
errorNo
confidenceNo
provenanceNo
follow_up_hintsNo
degradation_reasonNo
charter_versionNo

TDQS

A4.7/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint=true, destructiveHint=false, idempotentHint=true, openWorldHint=true. The description adds specific behavioral details: returns entity's bound table, identity column, description, and full column list with PII sensitivity. No contradictions with annotations.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is concise and front-loaded with the primary use case. Every sentence adds value, covering when to use, what it returns, and how to compose with other tools. No wasted words.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool has only one parameter, strong annotations, and an output schema, the description is complete. It explains the return structure, prerequisites, and integration with sibling tools, leaving no gaps for an AI agent to misinterpret.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100% and the input schema already provides a thorough parameter description including format constraints (no dots, no schema qualifier) and a prerequisite (call list_entities first). The tool description repeats these points without adding new parameter-specific semantics beyond the schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool's purpose: to describe a specific entity when the user names it. It specifies what the tool returns (bound table, identity column, description, full column list with PII sensitivity) and distinguishes it from sibling tools like list_entities.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicit guidance on when to use this tool (user names a specific entity) versus alternatives (list_entities when entities are unknown). Also provides common compositions with describe_table and describe_column, showing how to chain tools effectively.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

describe_tableA
Read-onlyIdempotent

Use this when the user names a specific table by qualified name (e.g. 'show me public.orders'). Returns columns with types, nullability, primary-key flags, LLM descriptions, and outgoing foreign keys. Use find_relevant_tables instead when the user describes the table semantically. Common compositions: chain to describe_column to drill into one column's join graph; chain to suggest_joins to find paths from this table to others.

ParametersJSON Schema
NameRequiredDescriptionDefault
qualified_nameYesPostgres `schema.table` qualified name (e.g. `public.orders`). Call `find_relevant_tables` first if you don't know the schema.

Output Schema

ParametersJSON Schema
NameRequiredDescription
statusYes
dataNo
errorNo
confidenceNo
provenanceNo
follow_up_hintsNo
degradation_reasonNo
charter_versionNo

TDQS

A4.7/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint=true, destructiveHint=false, idempotentHint=true, openWorldHint=true, covering safety and idempotency. The description adds valuable behavioral context by listing exactly what is returned (columns with types, nullability, PK flags, LLM descriptions, outgoing FKs), leveraging the annotations effectively.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is a single, well-structured paragraph that front-loads the purpose and then provides usage guidance. Every sentence adds value, with no redundancy.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the presence of an output schema (not needed to explain return values), the description covers all necessary aspects: when to use, what it returns, and how to chain with other tools. It is complete for a single-parameter tool.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100% with a clear parameter description. The tool description adds extra guidance by advising to call `find_relevant_tables` first if the schema is unknown, which goes beyond the schema alone.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description specifies the tool's exact purpose: describing a table by qualified name, returning detailed schema information. It clearly distinguishes from sibling tools like `find_relevant_tables` (for semantic description) and `describe_column` (drilling into columns).

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly states when to use this tool (user provides qualified name) and when to use `find_relevant_tables` instead (user describes table semantically). Provides common compositions like chaining to `describe_column` or `suggest_joins`, offering clear guidance.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

find_relevant_entitiesA
Read-onlyIdempotent

Use this when the user describes a business object (e.g. 'our customers', 'revenue data', 'product catalog'). Returns ranked entities — domain-named bindings to physical tables — so the agent stays in business terms. Use find_relevant_tables instead when no entities are curated. Common compositions: chain to describe_entity for one entity's full shape; chain to resolve_join to wire two entities together; chain to get_metric to compute a validated aggregation.

ParametersJSON Schema
NameRequiredDescriptionDefault
queryYesNatural-language description of the business object the user is asking about (e.g. 'customers', 'revenue', 'product catalog'). Embedded with the same model used to index column descriptions, then ranked by cosine similarity against the columns of each entity's bound table. Per-entity score is the MAX across columns.
limitNoMaximum number of ranked entity hits to return. Default 10. Use a small value (3-5) for narrow exploratory queries; use 10-20 when surveying an unfamiliar semantic layer.

Output Schema

ParametersJSON Schema
NameRequiredDescription
statusYes
dataNo
errorNo
confidenceNo
provenanceNo
follow_up_hintsNo
degradation_reasonNo
charter_versionNo

TDQS

A4.8/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint=true and destructiveHint=false. Description adds algorithmic details: ranking by cosine similarity, scoring as MAX across columns. Discloses what the tool does without contradicting annotations. Could mention any rate limits, but not essential.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Three sentences, front-loaded with purpose and usage, then alternatives and compositions. No wasted words; each sentence earns its place. Highly concise yet informative.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given output schema exists, annotations are rich, and both parameters are fully described in schema and description, the description covers purpose, usage, algorithm, and compositions. No gaps.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, but description adds significant meaning beyond parameter names/types: explains 'query' as natural-language and how searching works (embedding, cosine similarity), and 'limit' with usage guidance (3-5 for narrow, 10-20 for survey). This is exemplary.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states it is used when the user describes a business object, returns ranked entities, and distinguishes from sibling 'find_relevant_tables'. It specifies verb ('find'), resource ('relevant entities'), and scope ('business terms').

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly says 'Use this when...' and 'Use find_relevant_tables instead when...' and lists common compositions (chain to describe_entity, resolve_join, get_metric). Provides clear when-to-use and alternatives.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

find_relevant_tablesA
Read-onlyIdempotent

Use this when the user describes tables semantically (e.g. 'the table with customer orders', 'where we store payments'). Returns ranked hits with cosine scores plus the matched column and its LLM description so you see WHY each table surfaced. Use describe_table instead when the user names a specific table by qualified name. Common compositions: chain to describe_table for semantic-to-structural queries; chain to suggest_joins to discover then wire multi-table queries.

ParametersJSON Schema
NameRequiredDescriptionDefault
queryYesNatural-language description of the table or data the user is asking about (e.g. 'customer orders', 'where we store payments'). Embedded with the same model used to index column descriptions, then ranked by cosine similarity against per-column descriptions.
limitNoMaximum number of ranked hits to return. Default 10. Use a small value (3-5) for narrow exploratory queries; use 10-20 when surveying an unfamiliar schema.

Output Schema

ParametersJSON Schema
NameRequiredDescription
statusYes
dataNo
errorNo
confidenceNo
provenanceNo
follow_up_hintsNo
degradation_reasonNo
charter_versionNo

TDQS

A4.9/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already convey safety (readOnly, idempotent). Description adds critical behavioral details: returns cosine-ranked hits, shows matched column and its LLM description, and reveals the embedding model used. These details help the agent understand the return format and reasoning, beyond what annotations provide.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Three sentences, front-loaded with primary use case, no redundant information. Every sentence adds distinct value: purpose, differentiation, and composition advice. Highly efficient.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Despite the presence of an output schema (not shown), the description explains key return elements (cosine scores, matched column, LLM description) and provides composition chains with sibling tools. Given the tool's moderate complexity and 11 siblings, the description fully equips the agent to decide and invoke correctly.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Input schema covers both parameters with good descriptions, achieving 100% coverage. The description adds valuable usage guidance for the limit parameter ('Use a small value (3-5)...'), enhancing the agent's ability to select appropriate values. This extra context raises the score above baseline 3.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

Clearly states the tool's purpose: 'Use this when the user describes tables semantically' with concrete examples ('the table with customer orders'). Directly distinguishes from sibling tool `describe_table`, making purpose unmistakable.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly specifies when to use ('semantic descriptions'), when to use an alternative ('Use describe_table instead when the user names a specific table'), and suggests multi-step compositions with `describe_table` and `suggest_joins`. No ambiguity.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_example_queriesA
Read-onlyIdempotent

Use this when you need real example SQL for an indexed table to learn how it's actually used. Each item carries the SQL text, observation count, source, and PII categories touched. Returns status: empty when the table has no recorded examples yet (query log mining ships next). Use describe_table instead when you want the table's structural shape rather than usage patterns. Common composition: chain find_relevant_tables to get_example_queries.

ParametersJSON Schema
NameRequiredDescriptionDefault
qualified_nameYesPostgres `schema.table` qualified name (e.g. `public.orders`). Returns SQL agents (or humans) have actually run against this table, sourced from `pg_stat_statements`. Run `schemabrain mine-queries` first to populate the cache; until then this tool returns `status: empty`.

Output Schema

ParametersJSON Schema
NameRequiredDescription
statusYes
dataNo
errorNo
confidenceNo
provenanceNo
follow_up_hintsNo
degradation_reasonNo
charter_versionNo

TDQS

A5/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already indicate read-only, non-destructive, idempotent behavior; the description adds context about returning status:empty when not populated, the data source (pg_stat_statements), and the prerequisite (run schemabrain mine-queries), going well beyond annotations.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Every sentence adds value: purpose, return contents, empty status, alternative, and composition suggestion. No filler, well-front-loaded.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Covers what is returned, when empty, prerequisite, alternative, and composition. With an output schema present, no further detail needed on return structure.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The single parameter, qualified_name, is thoroughly described in the schema with format, example, source, and prerequisite; no ambiguity remains.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool provides real example SQL for an indexed table, distinguishing it from siblings like describe_table by explicitly contrasting usage patterns with structural shape.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It tells when to use (need real example SQL) and when not (table has no recorded examples), provides a direct alternative (describe_table), and suggests a common composition pattern (chain with find_relevant_tables).

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_metricA
Read-onlyIdempotent

Use this when you have a metric name + want ranked/sliced rows (top-N, most/highest/lowest). Returns rows + parameterised SQL. Compiler chains multi-hop joins automatically (anchor order_item + group_by user.email + order_by total_items_sold descorder_item → order → user). Pass order_by= for deterministic ranking; without it, limit is non-deterministic (envelope flags missing_order_by_with_limit). Use list_metrics instead when you don't know the metric name.

ParametersJSON Schema
NameRequiredDescriptionDefault
nameYesThe metric name to compute (e.g. `total_revenue`). Call `list_metrics` to enumerate every declared metric with its anchor entity, aggregation, and time-bucketing capabilities.
group_byNoTuple of `entity.column` references to slice by (e.g. `('product_category.name',)`). Each entity must be reachable from the metric's anchor via a chain of one or more canonical joins. Multi-hop chains (e.g. `order_item → order → user`) resolve automatically; if 2+ paths exist, the call refuses with `ambiguous_path` and the agent disambiguates via the `via` arg. Empty tuple = no slicing.
filtersNoTuple of `(column, op, value)` predicates. Column is `entity.column` form. Ops: eq, ne, lt, lte, gt, gte, in, not_in, is_null, not_null. Values bind as parameters — never inlined into SQL.
time_grainNoOne of the metric's declared time_grains (day, week, month, quarter, year). Defaults null = no time bucketing.
time_dimensionNoDisambiguates time-dimension inheritance when a metric carries no local `time_dimension` and 2+ timestamp columns are reachable via canonical joins. Pass `<entity>.<column>` form chosen from the `ambiguous_time_dimension` error's candidate list (also visible in the error message). Silently ignored when the metric has its own declared `time_dimension` — that always wins. Defaults null (no disambiguation).
limitNoMax rows returned. Defaults 1000, valid range 1-10000. Out-of-range values refuse with a typed `malformed_name` envelope (not a transport error). The compiler always emits LIMIT regardless of group_by complexity.
viaNoCanonical-join names the chain MUST traverse, used to disambiguate `ambiguous_path` (multiple paths between anchor and a group_by entity) or `ambiguous_join` (parallel canonical joins on a single hop). Pass one or more join names returned by `list_joins` or by the prior error's `candidate_paths` / `candidate_join_names`. Each name must appear on a valid chain; otherwise the call refuses with `unknown_via_join`. Defaults to empty (no constraint) — only set when an ambiguity error tells you to.
order_byNoORDER BY clauses applied to the result. Each entry's `column` must be EITHER the metric's `name` (the measure aggregate's SELECT alias) OR one of the `group_by` columns in `entity.column` form. Anything else refuses with `unknown_order_by_column`. `direction` is `asc` (default) or `desc`. Pass this when you want deterministic ranking (e.g. "top 5 users by total_items_sold" → `order_by=[{column:'total_items_sold', direction:'desc'}]`). The compiler auto-appends a tie-breaking secondary key (first group_by column ASC) so equal measure values produce identical row order across runs. Empty tuple + non-empty `group_by` defaults to ASC on every group column so the LIMIT N slice stays deterministic without the caller having to construct one; override by passing any explicit clause.

Output Schema

ParametersJSON Schema
NameRequiredDescription
statusYes
dataNo
errorNo
confidenceNo
provenanceNo
follow_up_hintsNo
degradation_reasonNo
charter_versionNo

TDQS

A4.9/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare the tool as readOnly, destructive=False, idempotent, and openWorld. The description adds significant behavioral context: automatic multi-hop join chaining, the impact of `order_by` on determinism and the `missing_order_by_with_limit` flag, limit enforcement, error handling (e.g., `ambiguous_path`, `unknown_order_by_column`), and the `via` disambiguation mechanism. No contradiction with annotations.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is concise but informative. It starts with the core purpose in the first sentence, then adds essential details about auto-joins, determinism, `order_by`, and error handling. Every sentence adds value, and there is no redundancy or fluff. The structure is logical and front-loaded.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool's complexity (8 parameters, output schema present, rich annotations), the description covers all necessary aspects: when to use, automated behavior, error conditions, and parameter guidance. The existence of an output schema reduces the need to describe return values. The description is complete for an AI agent to correctly select and invoke the tool.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so baseline is 3. The description adds value beyond the schema by explaining high-level usage patterns: e.g., how `order_by` enables deterministic ranking and that the compiler auto-appends a tie-breaking key, how `via` resolves ambiguity, and the effect of missing `order_by`. These contexts are not explicitly in the per-parameter descriptions.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description explicitly states the tool is for when you have a metric name and want ranked/sliced rows (top-N, most/highest/lowest). It distinguishes from the sibling `list_metrics` by noting that `list_metrics` is for when you don't know the metric name. The purpose is clear, specific, and actionable.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides clear when-to-use guidance (when you have a metric name and want ranked/sliced rows) and when-not-to-use (use `list_metrics` if you don't know the metric name). It also details behavior with and without `order_by`, error cases like `ambiguous_path`, and the `via` parameter for disambiguation, offering comprehensive contextual guidance.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

list_entitiesA
Read-onlyIdempotent

Use this when the user asks what semantic entities are defined (e.g. 'what entities do we have?', 'show me the entity list'). Returns every confirmed entity with its bound table, identity column, and provenance. Use describe_entity instead when you already know the entity name and want its full column shape. Common compositions: chain to describe_entity to drill in; chain to find_relevant_tables to discover physical tables that should become entities.

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

Output Schema

ParametersJSON Schema
NameRequiredDescription
statusYes
dataNo
errorNo
confidenceNo
provenanceNo
follow_up_hintsNo
degradation_reasonNo
charter_versionNo

TDQS

A4.7/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare read-only, non-destructive, idempotent, open-world. Description adds behavioral context: returns 'every confirmed entity' with specific fields. No contradiction.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Three sentences, each adds value: usage trigger, return description, differentiation, composition examples. No fluff.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Complete given zero parameters, full annotations, and presence of output schema. Covers purpose, usage, return structure, and composition.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

No parameters in schema; baseline for 0 params is 4. Description correctly omits parameter details as none exist.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

Clearly states the tool returns every confirmed entity with bound table, identity column, and provenance. Differentiates from sibling `describe_entity` by specifying when to use each.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly states when to use (user asks what entities are defined) and when not to (use `describe_entity` if entity name known). Provides common compositions like chaining to `describe_entity` or `find_relevant_tables`.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

list_joinsA
Read-onlyIdempotent

Use this when the user asks what canonical joins are defined (e.g. 'how are these entities connected?'). Returns each confirmed join with the entity pair it connects and provenance. Use resolve_join instead when you have a known pair and want the SQL skeleton (pass name= for 2+ joins on the pair). Use suggest_joins instead when only physical-table names are available.

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

Output Schema

ParametersJSON Schema
NameRequiredDescription
statusYes
dataNo
errorNo
confidenceNo
provenanceNo
follow_up_hintsNo
degradation_reasonNo
charter_versionNo

TDQS

A4.7/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint, destructiveHint, idempotentHint, and openWorldHint. The description adds that the tool returns 'each confirmed join with the entity pair it connects and provenance.' No contradictions. Could mention scope (all joins across workspace) but not necessary.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Three sentences with no waste. First sentence states purpose and trigger, second and third provide clear alternatives. Front-loaded structure.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

The tool has no parameters, comprehensive annotations, and an output schema (not shown but exists). The description covers what it does, when to use, what it returns, and distinguishes from siblings. Complete for a list tool.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

There are no parameters, and the schema coverage is 100%. Baseline for 0 parameters is 4. The description adds no parameter info, which is acceptable.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a specific verb ('list'), resource ('canonical joins'), and scope. It clearly distinguishes from sibling tools by explicitly naming `resolve_join` and `suggest_joins` with their appropriate use cases.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides explicit guidance on when to use this tool ('when the user asks what canonical joins are defined') and when to use alternatives (e.g., `resolve_join` for known pairs, `suggest_joins` for physical-table names only).

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

list_metricsA
Read-onlyIdempotent

Use this when the user asks any ranking, top-N, most/highest/lowest, or aggregation question (e.g. 'who bought the most', 'top 5 by revenue', 'rank customers', 'find users with the highest X', 'what's the total / average / count') — returns every declared metric with its anchor entity, aggregation, and time-bucketing so you can pick the right metric before calling get_metric. Use get_metric instead when you already have the metric name. Chain to describe_entity for full anchor shape.

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

Output Schema

ParametersJSON Schema
NameRequiredDescription
statusYes
dataNo
errorNo
confidenceNo
provenanceNo
follow_up_hintsNo
degradation_reasonNo
charter_versionNo

TDQS

A4.9/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Beyond annotations (readOnly, idempotent), description discloses that the tool returns every declared metric with structure details, adding value for agent understanding of behavior.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Three concise, front-loaded sentences that cover purpose, usage, and chaining without unnecessary words.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With 0 parameters and an output schema (assumed), description fully covers the tool's role, usage context, and output nature.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

No parameters exist, so baseline is 4. Description does not need to add parameter info.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description explicitly states the tool returns metrics for ranking/aggregation questions and specifies the output includes anchor entity, aggregation, and time-bucketing. It distinguishes from sibling 'get_metric' by clarifying when to use each.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Clearly says when to use (ranking/top-N/aggregation questions) and when not to (use 'get_metric' if metric name is known). Also provides chaining advice to 'describe_entity'.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

resolve_joinA
Read-onlyIdempotent

Use this when you have two entity names and need the canonical SQL join between them. Returns a ready-to-paste JOIN clause with column mapping. Use suggest_joins instead when you only have physical table names. Don't use when you need to discover what entities exist — call list_entities first.

ParametersJSON Schema
NameRequiredDescriptionDefault
entity_aYesThe first entity to join, by name (e.g. `customer`). Order doesn't matter — `resolve_join` is direction-insensitive; the response preserves the direction the join was originally confirmed in.
entity_bYesThe second entity to join, by name (e.g. `order`). Both entities must exist; call `list_entities` if unsure.
nameNoWhen 2+ canonical joins exist between the entity pair (billing vs shipping address, primary vs secondary user, etc.), pass the canonical-join name to disambiguate. Leave null to receive an ambiguity-refusal response listing the available names.

Output Schema

ParametersJSON Schema
NameRequiredDescription
statusYes
dataNo
errorNo
confidenceNo
provenanceNo
follow_up_hintsNo
degradation_reasonNo
charter_versionNo

TDQS

A5/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare read-only, idempotent, non-destructive. Description adds direction-insensitivity and disambiguation behavior (null returns ambiguity refusal with names), providing valuable behavioral context beyond annotations.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Four concise sentences covering purpose, usage context, and disambiguation behavior with no unnecessary words.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given annotations (read only, idempotent) and output schema existence, description covers key behavioral aspects (direction, disambiguation, return type) completely.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, providing baseline. Description adds meaning: order independence for entity_a/b, requirement for entities to exist, and detailed explanation of the name parameter's behavior when null.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

Description clearly states verb (resolve/gets join), resource (two entity names), and output (ready-to-paste JOIN clause with column mapping). Differentiates from siblings by naming alternatives (suggest_joins, list_entities).

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly says when to use (have two entity names, need canonical join), when not to use (discover entities first, use suggest_joins for physical tables), and provides context like direction-insensitivity.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

suggest_joinsA
Read-onlyIdempotent

Use this when you already know two or more tables and need the join paths between them. Pass qualified names (schema.table) and get one shortest FK path per pair, with columns on each side ready for a SQL JOIN. Multi-hop paths via intermediates are returned; pairs with no path within max_hops (default 6) land in unreachable_pairs. Use find_relevant_tables instead when you don't yet know the table names. Common composition: chain find_relevant_tables to suggest_joins.

ParametersJSON Schema
NameRequiredDescriptionDefault
tablesYesList of `schema.table` qualified names (minimum 2) to find join paths between. The tool returns one shortest FK path per unordered pair, plus an `unreachable_pairs` list for pairs with no path within `max_hops`.
max_hopsNoMaximum number of FK-graph hops to traverse when searching for join paths. Default 6 — covers M:N junction-table chains common in normalised OLTP schemas. Increase only for unusually deep schemas; higher values make the search non-trivially slower.

Output Schema

ParametersJSON Schema
NameRequiredDescription
statusYes
dataNo
errorNo
confidenceNo
provenanceNo
follow_up_hintsNo
degradation_reasonNo
charter_versionNo

TDQS

A5/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Discloses behavior beyond annotations: returns shortest FK path, multi-hop paths, unreachable pairs, and performance notes on `max_hops`. No contradiction with annotations (all safe, idempotent).

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Concise, front-loaded with usage context, then details. Every sentence adds value with no redundancy.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool's complexity, annotations, and schema, the description is complete. It covers input, output, behavior, and edge cases (unreachable pairs, depth limits).

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, but the description adds context: qualified name requirement, one shortest path per unordered pair, unreachable pairs list, and semantics of `max_hops` including default and performance considerations.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States the specific action: finding join paths between known tables using qualified names. Explicitly distinguishes from the sibling tool `find_relevant_tables`.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Clearly states when to use (when tables are known) and when not to (use `find_relevant_tables` instead). Also describes common composition with `find_relevant_tables`.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections. Dates show when Glama detected each change.

  1. 12 tool updatesv0.6.1
    • First observeddescribe_column
    • First observeddescribe_entity
    • First observeddescribe_table
    • First observedfind_relevant_entities
    • First observedfind_relevant_tables
    • First observedget_example_queries
    • First observedget_metric
    • First observedlist_entities
    • First observedlist_joins
    • First observedlist_metrics
    • First observedresolve_join
    • First observedsuggest_joins

TDQS

A4.8/5.0
Disambiguation5/5

Each tool has a clearly distinct purpose: describing at column/table/entity level, finding semantically, listing, getting metrics, resolving joins. No overlapping functionality; differences are explicitly noted in descriptions.

Naming Consistency5/5

All tools follow a consistent verb_noun pattern (e.g., describe_column, list_metrics, suggest_joins). No mixing of conventions like camelCase or different verb styles.

Tool Count5/5

12 tools is well-scoped for a schema analysis server, covering discovery, description, joining, and metric computation. Not excessively many nor too few.

Completeness4/5

The tool surface covers the core domain of schema exploration and metric queries comprehensively. Minor missing features like listing all tables at once, but overall no critical gaps for its purpose.

Maintenance

ActivityStale
ResponsivenessWithin a week

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    A
    maintenance
    Secure SQL proxy for AI agents. Translates natural language to safe SQL via Claude, validates at the AST level (SELECT-only, no DDL/DML), enforces per-agent row-level security, and audit-logs every query.
    1
    MIT
  • A
    license
    A
    quality
    C
    maintenance
    Secure MCP server for safe, read-only DB access by AI agents, with SQL guardrails, table allowlists, PII masking, and audit logs
    6
    50
    7
    MIT
  • F
    license
    Not graded
    quality
    D
    maintenance
    An MCP server that gives an AI agent scoped, safe access to your Postgres databases with per-connection access control, row caps, timeouts, and defense-in-depth read-only enforcement.
    -

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/Arun-kc/schemabrain'

If you have feedback or need assistance with the MCP directory API, please join our Discord server