First 5 Queries
You’ve run schemabrain init and restarted your MCP host. This is what to actually do with it for the next 10 minutes. Five queries that exercise each of the four load-bearing mechanisms — read-only tools, PII-aware refusal, audit chain, structured recovery — plus a closing CLI step that proves what happened.
Run these in order against the bundled SaaS fixture (the default if you pressed Enter at the wizard’s URL prompt). They work identically against your own Postgres — only the entity names change.
Prerequisites
schemabrain initcompleted successfully (Stage 7 reported a wired host).- MCP host restarted cold (Cmd+Q on macOS, full quit on Windows, new terminal session on
claude-code). - A new conversation window open.
schemabrain doctor --verify first — it smoke-tests the wiring without needing an Anthropic key. Details in /setup/claude-desktop and siblings.
Query 1 — verify the wire
Ask Claude: list the entities SchemaBrain knows aboutWhat happens. Claude calls
list_entities. With the bundled fixture, the response is the pre-curated entity pack the wizard auto-applies on the demo path — no ANTHROPIC_API_KEY required:
| name | bound table | one-line description |
|---|---|---|
workspace | public.workspaces | a tenant account — owns users, subscriptions, and billing |
user | public.users | a workspace member account (login credential + contact details) |
plan | public.plans | a subscription plan in the catalog (tier, price, seats, SLA) |
subscription | public.subscriptions | a workspace’s binding to a plan, with lifecycle timestamps |
subscription_item | public.subscription_items | a per-seat line item on a subscription |
invoice | public.invoices | a billing document issued to a workspace |
payment_method | public.payment_methods | a card on file for a workspace (tokenized last-4, brand, expiry) |
billing_profile | public.billing_profiles | a workspace’s legal / tax identity used for invoicing |
api_key | public.api_keys | a programmatic API credential issued to a workspace |
session | public.sessions | an authenticated login session for a user |
usage_event | public.usage_events | a metered telemetry event (api_call / login / export) |
support_ticket | public.support_tickets | a customer-filed support request (free-text body) |
init runs the LLM-driven entity suggester instead of the bundled pack — names + descriptions will vary, but the shape stays the same: one EntitySummary per inferred entity, each bound to a physical table.
What it proves. The MCP stdio transport is up, the local SQLite store has entities applied, and Claude is calling SchemaBrain tools instead of guessing at your schema. If Claude responds without calling a tool — that’s an unwired host, not a SchemaBrain problem. Run schemabrain doctor --verify.
Query 2 — see the semantic layer with PII tags
Ask Claude: describe the user entityWhat happens. Claude calls
describe_entity(name="user"). The response lists every column on the bound physical table with its PII classification:
EntityDetail shape — including LLM-enriched descriptions, sample values, and description_source — is in the MCP tool reference.)
What it proves. Columns are tagged at index time against the 12-category PII taxonomy. On a zero-config install, --pii-block defaults to credential,government_id,payment_card — the three catastrophic-leak categories. password_hash is tagged credential and redacted: true — the firewall will refuse any query that returns this column. contact is tagged but not blocked by default, so redacted: false on email is correct: the agent sees the tag as advisory metadata and can self-regulate even when policy doesn’t refuse.
Query 3 — run a metric (happy path)
Ask Claude: what’s our total revenue per month?What happens. Claude calls
list_metrics to discover the vocabulary, finds total_revenue (entity invoice, sum of invoice_total_cents, time-dimension invoice.issued_at), then calls get_metric:
default_transaction_read_only=on, and returns one row per month (totals in integer cents — a total_revenue of 613634 is $6,136.34). WHERE-clause values (filter_predicates) bind through SQLAlchemy parameters — the only operator-controlled string baked into the SQL is the validated metric / column / entity identifier set.
What it proves. The semantic-layer substrate works end-to-end. The agent didn’t author SQL — it composed a structured tool call against an operator-defined metric. The compiled SQL is read-only at the session level, runs through a NullPool connection (no session-state leakage between calls), and any operator filter values flow through bound parameters. See /mechanism/read-only.
Query 4 — see the firewall refuse (zero config)
Ask Claude: break our revenue down by the card number on file for each workspaceWhat happens. Claude finds
total_revenue (anchored on invoice) and tries to group it by payment_method.card_number_last4, reached via the bundled canonical join chain invoice → workspace → payment_method (invoice_workspace + workspace_payment_methods). That group-by column is tagged payment_card at index time. The metric compiler propagates PII tags through the joins, sees payment_card in the default blocked set, and refuses before the database is queried. The response is a structured envelope, not prose:
suggested_args.name points Claude back at the metric’s anchor to re-inspect the join path. Claude calls describe_entity(name="payment_method"), sees that card_number_last4 is redacted: true, and either (a) drops the card grouping and retries against card_brand + card_exp_* only, or (b) reports back that the agent shouldn’t expose card data and asks the operator to confirm intent. Either pivot is programmatic — no human round-trip to debug.
What it proves. This is the canonical-PII-propagation demo: PII tags cross JOIN boundaries. Even though the metric anchors on a non-PII table (invoices.invoice_total_cents), the compiler refused the joined result because the grouping reached payment_methods.card_number_last4 two joins away. The database never saw the query. The refusal is a typed contract (one of 26 ErrorKind values) with a recovery.suggested_tool the agent can branch on programmatically. See /mechanism/pii-taxonomy and /mechanism/structured-recovery.
Try the credential variant too.Ask Claude: count users grouped by their password hash. The bundledusers.password_hashcolumn is taggedcredential, soget_metricrefuses for the same reason — different category, same shape.
Query 5 — verify the audit chain
This one is CLI-side, not agent-side. Open a terminal:mcp_audit row written by Queries 1–4. Each row carries chain_hash[N] = sha256(chain_hash[N-1] || canonical(row[N])). The verifier exits:
0— chain intact, no row rewritten since it was committed.1— mismatch found; the row index that broke the chain is reported.2— operational error (DB missing, schema mismatch, IO failure).
schemabrain audit list --limit 10 shows the last ten tool calls with their tool_name, status, pii_categories, and chain_hash columns.
What it proves. Every agent tool call is captured in an append-only table guarded by SQL triggers (mcp_audit_no_update, mcp_audit_no_delete) at the SQLite layer. Tampering with any past row breaks the chain at that row and every later one. The chain head can be persisted externally (post-verify cron) to detect even a full-table rewrite. See /mechanism/audit-chain.
What you just exercised
| Query | Mechanism |
|---|---|
| Q1 — list entities | Read-only by architecture — agent composed a structured call, not SQL |
| Q2 — describe entity | PII taxonomy — 12-category tagging at index time, surfaced as advisory metadata |
| Q3 — get metric | Read-only by architecture + trust signal — operator-validated metric, parameterized SQL, default_transaction_read_only=on |
| Q4 — refused metric | PII propagation + structured recovery — compile-time refusal, typed envelope, agent self-pivots |
| Q5 — audit verify | Tamper-evident audit chain — SHA256 chain, append-only triggers, exit-code contract |
Where to next
- Add your own metric. Drop a YAML under
./schemabrain/metrics/and runschemabrain metrics apply ./schemabrain/metrics/ --url-env DATABASE_URL. Reference:docs/semantic-layer.md. - Stream the audit feed.
schemabrain tailshows every tool call live. Useful while you’re tuning policy. Reference:docs/operations.md. - Wire a second host. Same
init, different--hostflag. Reference:docs/setup/cursor,docs/setup/windsurf,docs/setup/claude-code. - Read the threat model. If you’re packaging SchemaBrain into a procurement review,
docs/security.mdis the summary;docs/threat-model.mdis the full walk-through.