schema-navigator-mcp
v0.2.0
Published
MCP server for PostgreSQL schema introspection — tables, columns, FKs, RLS policies, constraints, functions. Works with Supabase, Neon, Railway, CockroachDB, local Postgres, Aurora.
Maintainers
Readme
schema-navigator-mcp
MCP server for PostgreSQL schema introspection. Gives Claude live access to your database structure — columns, types, constraints, foreign keys, RLS policies, functions, and more.
Works with any PostgreSQL-compatible database: Supabase, Neon, Railway, CockroachDB, AWS Aurora, local Postgres.
Why
The most common bugs when working with databases:
- Wrong column names —
total_pricedoesn't exist, it'stotal_amount - Invalid constraint values —
'complete'is not an allowed status - RLS client mismatch — browser client blocked by row-level security
- Blind migrations — dropping a column referenced by 5 foreign keys
This server eliminates all four by giving your AI assistant live schema access.
Quick Start
Option 1: Supabase (no password needed)
The easiest setup for Supabase projects. Uses the Management API — no database password or connection string required.
- Get a Personal Access Token from supabase.com/dashboard/account/tokens
- Find your project ref in your dashboard URL:
supabase.com/dashboard/project/<project-ref>
Add to your project's .mcp.json:
{
"mcpServers": {
"db": {
"command": "npx",
"args": ["-y", "schema-navigator-mcp@latest"],
"env": {
"SUPABASE_ACCESS_TOKEN": "sbp_...",
"SUPABASE_PROJECT_REF": "your-project-ref"
}
}
}
}Or if you already have SUPABASE_URL or NEXT_PUBLIC_SUPABASE_URL set, the project ref is extracted automatically:
{
"mcpServers": {
"db": {
"command": "npx",
"args": ["-y", "schema-navigator-mcp@latest"],
"env": {
"SUPABASE_ACCESS_TOKEN": "sbp_...",
"SUPABASE_URL": "${SUPABASE_URL}"
}
}
}
}Option 2: Direct connection (any PostgreSQL)
Works with any PostgreSQL-compatible database — Supabase, Neon, Railway, CockroachDB, AWS Aurora, local Postgres.
{
"mcpServers": {
"db": {
"command": "npx",
"args": ["-y", "schema-navigator-mcp@latest"],
"env": { "DATABASE_URL": "${DATABASE_URL}" }
}
}
}Set DATABASE_URL in your environment (e.g. .env.local) and restart Claude Code.
Priority
If both are set, DATABASE_URL takes priority (direct connection is faster and has no rate limits).
With the schema-nav Plugin
For slash commands (/db:table, /db:find, etc.) and auto-activated validation skills, install the companion schema-nav plugin.
Tools
7 read-only tools, each under 200 chars description (~4.9K total context cost):
| Tool | What it does |
|------|-------------|
| schema_overview | Database dashboard — all tables with row counts and sizes, views, enums, extensions, health warnings |
| schema_table | Complete profile of a table/view/matview — columns, types, defaults, constraints, FKs, indexes, RLS, grants |
| schema_find | Cross-schema search across tables, columns, functions, views, policies, enums, comments |
| schema_relations | FK relationship graph — outbound and inbound references with cascade actions |
| schema_functions | Function inspector — arguments, return type, language, volatility, security definer, source code |
| schema_constraints | Value extractor — allowed values from CHECK constraints and ENUM types, merged |
| schema_policies | RLS analyzer — enabled status, policy expressions, role-by-operation access matrix |
Example: schema_table
schema_table("orders") →
{
"object_type": "table",
"columns": [
{ "name": "id", "type": "uuid", "nullable": false, "default": "gen_random_uuid()" },
{ "name": "status", "type": "text", "nullable": false, "comment": "Order lifecycle state" },
{ "name": "total_amount", "type": "numeric", "nullable": false },
...
],
"constraints": [
{ "type": "CHECK", "column": "status", "allowed_values": ["pending", "processing", "shipped", ...] }
],
"fks_in": [
{ "from_table": "order_items", "from_column": "order_id", "on_delete": "CASCADE" }
],
"rls_enabled": true,
"row_count": 51200,
"total_size": "32.1 MB"
}Architecture
┌─────────────────────────────────────────────────────────┐
│ schema-navigator-mcp │
│ │
│ ┌─ DATABASE_URL ──► PgAdapter (pg.Pool) │
│ │ ├── BEGIN TRANSACTION READ ONLY │
│ │ ├── query (15s timeout) │
│ │ └── COMMIT │
│ │ │
│ └─ SUPABASE_ACCESS_TOKEN ──► SupabaseAdapter │
│ ├── Management API │
│ ├── batch N queries → 1 │
│ └── 30s timeout │
│ │
│ 7 tools registered via MCP SDK │
│ stdio transport (JSON-RPC) │
└─────────────────────────────────────────────────────────┘Direct connection (PgAdapter)
- Read-only transactions — every query wrapped in
BEGIN TRANSACTION READ ONLY. Cannot modify data. - Connection pooling —
pg.Poolwith max 5 connections, 5s connect timeout, 30s idle timeout. - Query timeout — 15s
statement_timeoutprevents hanging queries. - Graceful shutdown — SIGINT/SIGTERM handlers close pool connections cleanly.
Supabase Management API (SupabaseAdapter)
- Zero-password — authenticates via Personal Access Token, no database credentials needed.
- Batch queries — multiple queries batched into a single API call using
json_build_array, staying within the 10 req/min rate limit. - Parameter inlining —
$1,$2placeholders safely inlined with SQL escaping (all params are Zod-validated schema/table names). - 30s timeout — per-request timeout with
AbortController.
Universality
The server uses two query layers with automatic detection:
Universal (information_schema — SQL standard):
- Tables, columns, data types, defaults, nullability
- Foreign keys, CHECK constraints, UNIQUE constraints
- Views, routines, triggers
PostgreSQL-specific (pg_catalog — graceful degradation):
- RLS policies → returns
[]+ note on non-Postgres - ENUM types → returns
[] - Function source code → signature-only fallback
- Table sizes, row estimates →
null - Column comments →
null
Detection runs once at startup via SELECT version().
Programmatic API
import { createSchemaNavigator } from "schema-navigator-mcp";
// Direct PostgreSQL connection
const server = await createSchemaNavigator("postgresql://...");
// Supabase Management API
const server = await createSchemaNavigator({
accessToken: "sbp_...",
projectRef: "your-project-ref",
});
// server is an McpServer instance — connect your own transportSecurity
- Read-only — direct connections use read-only transactions; Management API queries are all SELECT statements
- No credentials stored —
DATABASE_URL/SUPABASE_ACCESS_TOKENfrom environment only - No data access — introspects schema metadata, never reads table data
- Parameterized queries — direct connections use native
$1parameters; Management API inlines with SQL escaping (all inputs are Zod-validated)
Requirements
- Node.js >= 18
- One of:
DATABASE_URL— any PostgreSQL-compatible connection stringSUPABASE_ACCESS_TOKEN+SUPABASE_PROJECT_REF(orSUPABASE_URL/NEXT_PUBLIC_SUPABASE_URL)
License
MIT
