warehouse-mcp-server
Provides safe, read-only access to a PostgreSQL warehouse, enabling schema introspection and guarded SELECT queries with row limits and timeouts.
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@warehouse-mcp-serverShow me the columns and types of the dim_customer table."
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
warehouse-mcp-server
A Model Context Protocol server that gives an LLM safe, read-only access to a PostgreSQL warehouse — schema introspection, guarded SQL, and result caps that tell the caller when they truncated.
npm install && npm test # 33 tests, including a real MCP client↔server round-tripThe suite runs against PGlite (PostgreSQL compiled to WebAssembly), so the introspection queries, the read-only transaction and the row caps are exercised for real rather than mocked into agreement — no Docker, no service to start.
Why the safety model is layered
Handing a language model a database connection is the most useful and most dangerous thing in this repo. A model can be talked into things; a prompt injection buried in a row of data is a real attack, not a thought experiment. So there are three layers, ordered by how much each deserves to be trusted:
1. A read-only database role. The actual boundary. The connection has SELECT on the mart schema and nothing else, so even a total bypass of everything below can only read what a dashboard could read. If you deploy this with a superuser connection string, nothing else here saves you.
2. A read-only transaction and a statement timeout. Every query runs inside BEGIN; SET TRANSACTION READ ONLY; SET LOCAL statement_timeout = …. Postgres refuses writes and kills runaway scans, independently of anything this process believes. There is a test that bypasses the static checks entirely to prove this layer holds on its own.
3. Static SQL checks. Reject bad queries before they are sent, so the model gets only SELECT queries are allowed instead of an opaque permission error it will start trying to work around.
Layer 3 is defence in depth and a UX feature. It is deliberately not the thing standing between a prompt injection and your data — a string-matching filter never should be.
Related MCP server: psql-mcp
What the static checks actually catch
The parser strips comments and string literals first, replacing them with spaces, and every check runs on that. Without it the checks are trivially defeated and simultaneously produce false positives:
Input | Verdict | Why |
| allowed | The word is inside a string. A naive filter blocks this. |
| rejected | Second statement — the classic way a "read-only" tool executes a DELETE. |
| rejected | Comments cannot hide a statement separator. |
| rejected | A writable CTE. It starts with |
| rejected |
|
| rejected | Nothing an analytics question needs reaches outside the database. |
| rejected | No session tampering. |
| rejected | Postgres block comments nest; the stripper does too. |
Row caps are applied by wrapping, not appending: SELECT * FROM (⟨query⟩) AS _guarded LIMIT n. Appending LIMIT to a query that already has one is a syntax error, and appending to a UNION silently limits the last branch only.
Designing tools for a model rather than a human
The server fetches one row more than the cap so it can report truncated: true with a note. A model that does not know it saw a partial result will reason as if it saw everything — and then confidently tell someone the top location is whichever one happened to sort first.
Errors are written for the caller deciding what to do next:
schema "core" is not exposed. This connection serves: mart. Call list_tables to see what is available.
rather than permission denied for table dim_item, which sends a model looking for a way around instead of somewhere useful.
Failures come back as tool results with isError: true, not protocol errors. A protocol error aborts the call; a readable tool result lets the model correct itself. Unexpected exceptions are logged for us and generalised for the caller, because a raw Postgres error can carry a connection string or an internal path.
Tools
Tool | Purpose |
| Every table, view and materialized view in the exposed schemas, with descriptions. Cheap; call it first. |
| Columns, types, nullability and column comments for one relation. |
| One read-only |
Table and column comments are surfaced in the output, which is the cheapest schema-documentation win available: a COMMENT ON COLUMN written once stops a model guessing what net_amount means for the rest of the model's life.
Running it
DATABASE_URL='postgres://warehouse_reader:…@host/db' \
EXPOSED_SCHEMAS=mart \
DEFAULT_ROW_LIMIT=200 \
npx warehouse-mcp-serverIn an MCP client's config:
{
"mcpServers": {
"warehouse": {
"command": "npx",
"args": ["-y", "warehouse-mcp-server"],
"env": {
"DATABASE_URL": "postgres://warehouse_reader:…@host/db",
"EXPOSED_SCHEMAS": "mart"
}
}
}
}Use a role that can only read the schemas you name. That role is the security boundary; everything in this process is the second and third lines of defence.
Variable | Default | |
| — | required; point it at a read-only role |
|
| comma-separated |
|
| |
|
| hard ceiling the caller cannot raise |
|
|
Note that stdout is the protocol channel — all logging goes to stderr, because a stray console.log corrupts the stream and produces a failure that is genuinely baffling to debug.
Layout
src/
sql-guard.js literal/comment-aware stripper + read-only policy + row-limit wrapper
tools.js the three tools, transport-agnostic and independently testable
server.js MCP wiring — the thinnest file here, on purpose
test/
guard.test.js 16 tests, every bypass above
tools.test.js 12 tests against real Postgres via PGlite
protocol.test.js 5 tests over a real MCP client↔server transport pairRequires Node 20+. MIT licensed.
This server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Servers
- Alicense-qualityDmaintenanceA read-only MCP server for PostgreSQL that enables safe database introspection and querying via natural language.764MIT
- Alicense-qualityAmaintenanceA hardened, read-only Postgres MCP server that enables LLMs to safely query databases without write, DDL, shell, or credential exposure.MIT
- Alicense-qualityCmaintenanceRead-only MCP server for SQL databases (SQLite/PostgreSQL) that enables listing tables, describing schemas, and executing SELECT queries with safety guardrails.MIT
- FlicenseAqualityBmaintenanceA security-hardened Postgres MCP server that enables LLM agents to run safe, read-only SQL queries with enforcement via SQL-AST inspection and read-only transactions.1
Related MCP Connectors
MCP server for managing Prisma Postgres.
Query PostgreSQL databases in plain English — LLM-generated, safety-validated SQL.
Analytical memory for AI agents: a real Postgres queried in plain English over MCP. One command.
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
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/fabianguiliani/warehouse-mcp-server'
If you have feedback or need assistance with the MCP directory API, please join our Discord server