{
  "markdown": "# warehouse-mcp\n\n[![CI](https://github.com/kalehdoo/warehouse-mcp/actions/workflows/ci.yml/badge.svg)](https://github.com/kalehdoo/warehouse-mcp/actions/workflows/ci.yml)\n[![License: Apache 2.0](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://github.com/kalehdoo/warehouse-mcp/blob/main/LICENSE)\n\nProduction MCP (Model Context Protocol) server for your data warehouse. Read-only enforcement, five-tier role-based access, optional warehouse-role impersonation (for native RLS / CLS), JSONL audit log, optional output PII masking, optional semantic-metadata layer (glossary + table docs) with per-session toggle. Self-host the Docker image, install via npx, or wait for the upcoming managed cloud variant.\n\n> **Status:** v0.4.x — production-ready for the v1 warehouse list. See [CHANGELOG](https://github.com/kalehdoo/warehouse-mcp/blob/main/CHANGELOG.md) for what shipped in each release.\n\n## Demo Videos\n[Warehouse MCP in action using Cursor](https://youtu.be/DGgI7cczEtk)\n\n## Supported warehouses\n\n| Warehouse | Adapter docs |\n|---|---|\n| Postgres 12+ | [docs/adapters/postgres.md](https://github.com/kalehdoo/warehouse-mcp/blob/main/docs/adapters/postgres.md) |\n| Oracle 12c+ (Thin mode, no Instant Client) | [docs/adapters/oracle.md](https://github.com/kalehdoo/warehouse-mcp/blob/main/docs/adapters/oracle.md) |\n| Amazon Redshift (cluster + Serverless) | [docs/adapters/redshift.md](https://github.com/kalehdoo/warehouse-mcp/blob/main/docs/adapters/redshift.md) |\n| Snowflake (key-pair auth) | [docs/adapters/snowflake.md](https://github.com/kalehdoo/warehouse-mcp/blob/main/docs/adapters/snowflake.md) |\n| Google BigQuery | [docs/adapters/bigquery.md](https://github.com/kalehdoo/warehouse-mcp/blob/main/docs/adapters/bigquery.md) |\n| DuckDB (local file or `:memory:`) | [docs/adapters/duckdb.md](https://github.com/kalehdoo/warehouse-mcp/blob/main/docs/adapters/duckdb.md) |\n| MotherDuck (cloud-hosted DuckDB, `md:` paths) | [docs/adapters/duckdb.md](https://github.com/kalehdoo/warehouse-mcp/blob/main/docs/adapters/duckdb.md#motherduck-cloud-hosted-duckdb) |\n\nDatabricks SQL is a fast-follow.\n\n## Tools exposed (16, all read-only)\n\n| Tool | Purpose |\n|---|---|\n| `query` | Execute a SELECT (validator-enforced read-only, dialect-aware) |\n| `list_schemas`, `list_tables`, `describe_table` | Browse the catalog |\n| `find_columns` | Search column names across the warehouse with a LIKE pattern |\n| `get_foreign_keys` | Discover declared FK relationships for safe joins |\n| `get_view_definition` | Read the SQL body of a view (where business logic lives) |\n| `sample_table`, `count_rows` | Peek at data, check size before scanning |\n| `column_stats`, `top_values` | Profile a single column |\n| `time_series` | Bucket by hour/day/week/month/quarter/year — dialect-correct everywhere |\n| `search_value` | Find a literal across a table's text columns |\n| `glossary_lookup` | Read business-glossary terms from the semantic layer (in-memory, no warehouse I/O) |\n| `schema_lookup` | Read schema-level docs — purpose, owner, refresh, table list |\n| `table_lookup` | Read full semantic doc for one table — description + column metadata |\n\n## Roles (five tiers)\n\n| Role | Tools allowed |\n|---|---|\n| `semantic_only` | Only the three semantic-lookup tools — zero warehouse access; pair with `semantic=on` for a docs-viewer persona |\n| `metadata_only` | Catalog discovery + semantic lookups — never reads row data |\n| `reader_restricted` | Aggregates / samples / time series — no arbitrary SELECT |\n| `reader` | Adds `query` and `search_value` (the general analyst tier) |\n| `admin` | Everything; future write tools when `ENABLE_WRITE_TOOLS` ships |\n\nPer-key role assigned via `MCP_API_KEYS=key:role[:set_role=warehouse_role][:semantic=on|off]`. The optional `set_role=` directive issues `SET ROLE` on Postgres/Redshift so the warehouse's own RLS / CLS / masking policies enforce per-key access — no policy duplication in MCP. The optional `semantic=on|off` directive overrides `SEMANTIC_DEFAULT` for that one key, controlling whether the session sees the `warehouse://semantic/*` resources (see [docs/semantic-metadata.md](https://github.com/kalehdoo/warehouse-mcp/blob/main/docs/semantic-metadata.md#toggling-semantic-per-session)).\n\nFor deployments with multiple existing DB roles (finance, hr, payroll, etc.) and many human users, see [docs/multi-role-deployment.md](https://github.com/kalehdoo/warehouse-mcp/blob/main/docs/multi-role-deployment.md) — walks through mapping ~10 DB roles to MCP keys, the recommended `<area>` / `<area>_restricted` pattern, and when to graduate from static keys to OIDC.\n\n## Sample mcp connection json(change based on your role):\n{\n  \"mcpServers\": {\n    \"warehouse-admin-semanticon\": {\n      \"command\": \"npx\",\n      \"args\": [\n        \"-y\",\n        \"mcp-remote@latest\",\n        \"http://localhost:3001/mcp\",\n        \"--header\",\n        \"Authorization: Bearer admin-key-change-me\"\n      ]\n    },\n    \"warehouse-admin-semanticoff\": {\n      \"command\": \"npx\",\n      \"args\": [\n        \"-y\",\n        \"mcp-remote@latest\",\n        \"http://localhost:3001/mcp\",\n        \"--header\",\n        \"Authorization: Bearer admin-key-nosemantic\"\n      ]\n    },\n    \"warehouse-reader-semanticon\": {\n      \"command\": \"npx\",\n      \"args\": [\n        \"-y\",\n        \"mcp-remote@latest\",\n        \"http://localhost:3001/mcp\",\n        \"--header\",\n        \"Authorization: Bearer reader-key-change-me\"\n      ]\n    },\n    \"warehouse-reader-semanticoff\": {\n      \"command\": \"npx\",\n      \"args\": [\n        \"-y\",\n        \"mcp-remote@latest\",\n        \"http://localhost:3001/mcp\",\n        \"--header\",\n        \"Authorization: Bearer reader-key-nosemantic\"\n      ]\n    },\n    \"warehouse-reader-restricted-semanticon\": {\n      \"command\": \"npx\",\n      \"args\": [\n        \"-y\",\n        \"mcp-remote@latest\",\n        \"http://localhost:3001/mcp\",\n        \"--header\",\n        \"Authorization: Bearer restricted-key-change-me\"\n      ]\n    },\n    \"warehouse-reader-restricted-semanticoff\": {\n      \"command\": \"npx\",\n      \"args\": [\n        \"-y\",\n        \"mcp-remote@latest\",\n        \"http://localhost:3001/mcp\",\n        \"--header\",\n        \"Authorization: Bearer restricted-key-nosemantic\"\n      ]\n    },\n    \"warehouse-metadata-only-semanticon\": {\n      \"command\": \"npx\",\n      \"args\": [\n        \"-y\",\n        \"mcp-remote@latest\",\n        \"http://localhost:3001/mcp\",\n        \"--header\",\n        \"Authorization: Bearer metadata-only-key-change-me\"\n      ]\n    },\n    \"warehouse-metadata-only-semanticoff\": {\n      \"command\": \"npx\",\n      \"args\": [\n        \"-y\",\n        \"mcp-remote@latest\",\n        \"http://localhost:3001/mcp\",\n        \"--header\",\n        \"Authorization: Bearer metadata-only-key-nosemantic\"\n      ]\n    },\n    \"warehouse-docs-viewer-only\": {\n      \"command\": \"npx\",\n      \"args\": [\n        \"-y\",\n        \"mcp-remote@latest\",\n        \"http://localhost:3001/mcp\",\n        \"--header\",\n        \"Authorization: Bearer docs_viewer_key_change_me\"\n      ]\n    }\n  }\n}\n\n## Quick start\n\n### Option A — Docker compose (5-minute demo with seeded Postgres)\n\n```bash\ngit clone https://github.com/kalehdoo/warehouse-mcp.git\ncd warehouse-mcp\ndocker compose up\n# server on http://localhost:3001, seeded ecommerce data in Postgres\n```\n\n### Option B — Docker against your own warehouse\n\nThe same image bundles every adapter; pick one with `WAREHOUSE_TYPE` plus the matching credentials. For credentials, prefer `--env-file` (or your secrets manager) over inline `-e` flags so passwords don't end up in shell history.\n\n```bash\n# Postgres (REDSHIFT_* env vars for Redshift; same driver under the hood)\ndocker run -d -p 3001:3001 \\\n  -e WAREHOUSE_TYPE=postgres \\\n  -e PG_HOST=db -e PG_DATABASE=analytics -e PG_USER=mcp_reader -e PG_PASSWORD=... \\\n  -e MCP_API_KEYS=\"$(openssl rand -hex 24):reader\" \\\n  ghcr.io/kalehdoo/warehouse-mcp:latest\n\n# Oracle (Thin mode, no Instant Client)\ndocker run -d -p 3001:3001 \\\n  -e WAREHOUSE_TYPE=oracle \\\n  -e ORACLE_USER=MCP_READER -e ORACLE_PASSWORD=... \\\n  -e ORACLE_CONNECT_STRING=\"db.host:1521/SERVICE\" \\\n  ghcr.io/kalehdoo/warehouse-mcp:latest\n\n# Snowflake (key-pair, mount the .p8)\ndocker run -d -p 3001:3001 \\\n  -e WAREHOUSE_TYPE=snowflake \\\n  -e SNOWFLAKE_ACCOUNT=xy12345.us-east-1 -e SNOWFLAKE_USER=MCP_READER \\\n  -e SNOWFLAKE_PRIVATE_KEY_PATH=/keys/snowflake.p8 \\\n  -e SNOWFLAKE_WAREHOUSE=COMPUTE_WH -e SNOWFLAKE_DATABASE=ANALYTICS \\\n  -v /opt/keys:/keys:ro \\\n  ghcr.io/kalehdoo/warehouse-mcp:latest\n\n# BigQuery (mount the service-account JSON)\ndocker run -d -p 3001:3001 \\\n  -e WAREHOUSE_TYPE=bigquery \\\n  -e GOOGLE_APPLICATION_CREDENTIALS=/keys/bq-sa.json \\\n  -e BIGQUERY_PROJECT=my-gcp-project \\\n  -v /opt/keys:/keys:ro \\\n  ghcr.io/kalehdoo/warehouse-mcp:latest\n\n# DuckDB (file or in-memory)\ndocker run -d -p 3001:3001 \\\n  -e WAREHOUSE_TYPE=duckdb -e DUCKDB_PATH=:memory: \\\n  ghcr.io/kalehdoo/warehouse-mcp:latest\n\n# MotherDuck (hosted DuckDB)\ndocker run -d -p 3001:3001 \\\n  -e WAREHOUSE_TYPE=duckdb -e DUCKDB_PATH=md:my_database \\\n  -e MOTHERDUCK_TOKEN=... \\\n  ghcr.io/kalehdoo/warehouse-mcp:latest\n```\n\n### Option C — npx (no container)\n\n```bash\nnpx warehouse-mcp@latest init     # interactive setup; writes .env, prints Claude Desktop snippet\nnpx warehouse-mcp doctor          # verify the connection without booting the server\nnpx warehouse-mcp start           # bind the MCP server to MCP_SERVER_PORT (default 3001)\n```\n\nThen point an AI client at it. Drop-in configs:\n- [Claude Desktop](https://github.com/kalehdoo/warehouse-mcp/blob/main/docs/install-claude-desktop.md)\n- [Cursor](https://github.com/kalehdoo/warehouse-mcp/blob/main/docs/install-cursor.md)\n- [Docker (production)](https://github.com/kalehdoo/warehouse-mcp/blob/main/docs/deploy-docker.md)\n- [Kubernetes](https://github.com/kalehdoo/warehouse-mcp/blob/main/docs/deploy-kubernetes.md)\n\nFor a step-by-step walkthrough from \"I have a warehouse\" to \"Claude is querying it\", see the [onboarding guide](https://github.com/kalehdoo/warehouse-mcp/blob/main/docs/onboarding.md). For common errors, see [troubleshooting](https://github.com/kalehdoo/warehouse-mcp/blob/main/docs/troubleshooting.md).\n\n## Local development\n\n```bash\nnvm use            # Node 20\nnpm install\nnpm test           # unit tests, sub-second\nnpm run lint\ncp .env.example .env\n```\n\nFor testcontainers integration tests against real Postgres: `npm run test:integration` (Docker required).\n\n## env setup\n1. MCP_API_KEYS\n2. WAREHOUSE_TYPE\n3. SEMANTIC_DIR\n\n## Optional: Semantic metadata (v0.4+)\n\nTeach the AI agent **what your warehouse means**, not just what's in it. Drop YAML files describing your business glossary and table semantics into a directory, point `SEMANTIC_DIR` at it, and the agent gets two access channels into the same in-memory index:\n\n- **MCP resources** at `warehouse://semantic/*` — what tool-aware clients (Cursor, MCP Inspector) read proactively before issuing queries.\n- **MCP tools** `glossary_lookup`, `schema_lookup`, `table_lookup` — same data via the tool channel, for clients (Claude Desktop) whose UI is tool-centric and may suppress resource-only servers. Pure in-memory `Map.get`s — no warehouse I/O, no audit cost beyond a normal tool log line.\n\nFormat follows dbt's `schema.yml` v2 with one extension (`meta.schema:` per model). Customers using dbt can point `SEMANTIC_DIR` at their existing `models/` directory and reuse most of what they have. See [docs/semantic-metadata.md](https://github.com/kalehdoo/warehouse-mcp/blob/main/docs/semantic-metadata.md) and the starter [docs/semantic-templates/](https://github.com/kalehdoo/warehouse-mcp/tree/main/docs/semantic-templates).\n\n### Per-session toggle\n\nWhether a session sees the semantic layer is independent of whether the YAMLs are loaded — `SEMANTIC_DIR` controls loading; `SEMANTIC_DEFAULT=on|off` and the per-key `semantic=on|off` option (or `include_semantic` JWT claim) control per-session visibility. The YAMLs are always loaded and validated at boot so you can flip a kill-switch without restarting. Three precedence layers, highest first:\n\n1. **Per-JWT claim** — `include_semantic: true|false` (OIDC).\n2. **Per-API-key option** — `semantic=on|off` in `MCP_API_KEYS`.\n3. **Server default** — `SEMANTIC_DEFAULT=on|off` (default `on`).\n\nEach tool-call audit row records the resolved `include_semantic` value so you can correlate query quality with semantic exposure after the fact.\n\n## Optional: OpenTelemetry tracing\n\nOff by default. Set `OTEL_EXPORTER_OTLP_ENDPOINT=http://otel-collector:4318/v1/traces` in your env and the server will emit per-tool-call spans. Resource attributes: `service.name=warehouse-mcp`, `service.version=<package.json version>`. Span attributes: `warehouse.tenant`, `warehouse.role`. Works with any OTLP/HTTP backend (Grafana Tempo, Honeycomb, Datadog APM, New Relic, SigNoz).\n\n## Optional: Output PII masking\n\nOff by default. Set `GUARDRAIL_PII_MASK=on` and the server masks emails, SSNs, formatted phones, IPv4 addresses, and Luhn-validated credit cards in result rows. Mask level depends on the caller's role: `admin` sees raw, `reader` sees partial (`a***@example.com`), `reader_restricted` sees full redaction tags.\n\n## How it works\n\nRead [the architecture doc](https://github.com/kalehdoo/warehouse-mcp/blob/main/docs/architecture.md) for the request flow — what files execute when an MCP client makes a call, how the guardrail pipeline composes around the tool handler, and the recipes for adding a new tool / guardrail / adapter. Single-page orientation for operators, security reviewers, and contributors.\n\n## Security\n\nRead [the threat model](https://github.com/kalehdoo/warehouse-mcp/blob/main/docs/threat-model.md) before deploying. It covers the OWASP Top 10 mapping, what the codebase mitigates, and what is left to your deployment (TLS, secrets management, network isolation, cost guardrails). Report vulnerabilities per [SECURITY.md](https://github.com/kalehdoo/warehouse-mcp/blob/main/SECURITY.md).\n\n## Contributing\n\nIssues and PRs welcome. Start with [CONTRIBUTING.md](https://github.com/kalehdoo/warehouse-mcp/blob/main/CONTRIBUTING.md) — it covers the dev workflow, the adapter contract, and how to add a new warehouse. By participating you agree to the [Code of Conduct](https://github.com/kalehdoo/warehouse-mcp/blob/main/CODE_OF_CONDUCT.md).\n\n## License\n\nApache-2.0\n",
  "bytes": 14286,
  "sha": "01e7f3cfac7307f444c2badb8803dec76bc1b6aa9ca353f605feebd7b78c6658",
  "repo_slug": "kalehdoo/warehouse-mcp",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/mcp_io_github_kalehdoo_warehouse_mcp_ba14d636/readme"
}