mcp-postgres-db-claude
README.md
# I Gave Claude Access to My PostgreSQL Database
A practical MCP project for **The Curious Architect**.
The project connects:
**Claude → MCP → Python MCP Server → PostgreSQL**
Claude can inspect the database schema and ask the MCP server to run read-only SQL queries.
## What this demonstrates
- MCP tools
- MCP resources
- Streamable HTTP transport
- Python MCP SDK v2
- PostgreSQL with `psycopg`
- Schema discovery
- AI-generated SQL
- Read-only database access
- A dedicated PostgreSQL read-only user
- Query result limiting and statement timeout
## Project structure
```text
mcp-postgres-claude/
├── server.py
├── pyproject.toml
├── .env.example
├── docker-compose.yml
├── db/
│ └── init.sql
└── README.md
```
## 1. Start PostgreSQL
Make sure Docker is installed, then:
```bash
docker compose up -d
```
The demo database contains:
- `customers`
- `orders`
Check it:
```bash
docker compose ps
```
## 2. Create the Python environment
Python 3.10+ is required.
Using `uv`:
```bash
python3 -m venv .venv
source .venv/bin/activate
uv pip install -e .
```
Or with pip:
```bash
python -m venv .venv
source .venv/bin/activate
pip install -e .
```
The current official MCP Python SDK is v2, and `pip install mcp` now installs the v2 line. The server therefore uses `from mcp.server import MCPServer` rather than the old v1 `FastMCP` import.
## 3. Configure the database
```bash
cp .env.example .env
```
The example points to the Docker PostgreSQL instance:
```text
DATABASE_URL=postgresql://mcp_readonly:mcp_readonly_password@localhost:5432/company
```
The database user is intentionally read-only.
## 4. Start the MCP server
```bash
python server.py
```
The server starts at:
```text
http://127.0.0.1:8000/mcp
```
You can also use the MCP CLI during development:
```bash
mcp dev server.py
```
## 5. Test with MCP Inspector
For the HTTP server, connect the Inspector to:
```text
http://127.0.0.1:8000/mcp
```
Try:
### list_tables
```text
What tables are available?
```
### describe_table
```text
Describe the orders table.
```
### query_database
```sql
SELECT
c.country,
COUNT(*) AS orders,
SUM(o.amount) AS revenue
FROM customers c
JOIN orders o ON o.customer_id = c.id
WHERE o.status = 'paid'
GROUP BY c.country
ORDER BY revenue DESC
```
The model can generate this query from a natural-language request such as:
> Show me revenue by country for paid orders.
## 6. Connect Claude
Expose the local MCP endpoint through a secure tunnel if your Claude setup requires a public HTTPS endpoint.
For example, with ngrok:
```bash
ngrok config add-authtoken <TOKEN>
ngrok http 8000
```
Use the HTTPS MCP endpoint:
```text
https://YOUR-NGROK-DOMAIN/mcp
```
Then add it as an MCP connector in the Claude environment you are using.
Do not expose a production database directly to the public internet.
## Available MCP primitives
### Tools
#### `list_tables`
Returns tables in the public schema.
#### `describe_table`
Returns column names, data types, nullability, and defaults.
#### `query_database`
Runs one read-only `SELECT` or `WITH` query and returns structured rows.
### Resource
#### `postgres://schema`
Provides a compact representation of the database schema so an MCP host can understand the available tables and columns.
## Safety design
This project is intentionally designed as a demo with multiple read-only boundaries.
### 1. Database user
The MCP server connects as:
```text
mcp_readonly
```
That user receives `SELECT` permission only.
### 2. Read-only transaction
Every query is executed inside a PostgreSQL read-only transaction.
### 3. SQL validation
The MCP tool only accepts queries beginning with `SELECT` or `WITH` and blocks common write/administrative operations.
### 4. Row limit
Results are capped by `MAX_ROWS`, defaulting to 50.
### 5. Statement timeout
Queries are limited by `STATEMENT_TIMEOUT_MS`, defaulting to 5000 ms.
These are demo safeguards, not a complete production security model. For production, add authentication/authorization, stronger SQL policy enforcement, auditing, least-privilege schemas, network controls, and carefully designed domain-specific tools.
## Good Claude demo prompts
Try these in the video:
```text
What tables do I have?
```
```text
How many customers are in each country?
```
```text
Which customer generated the most paid revenue?
```
```text
Show me monthly paid revenue for 2026.
```
```text
What is the average paid order value?
```
```text
Find customers who have never placed an order.
```
A stronger demo is:
```text
Analyze the database and tell me the top 3 countries by paid revenue.
Explain how you calculated it and show the SQL query you used.
```
## Architecture
```text
Natural language
│
▼
┌───────────┐
│ Claude │
└─────┬─────┘
│ MCP
▼
┌─────────────────────┐
│ Python MCP Server │
│ │
│ list_tables │
│ describe_table │
│ query_database │
│ postgres://schema │
└──────────┬──────────┘
│ psycopg
▼
┌─────────────────────┐
│ PostgreSQL │
│ │
│ customers │
│ orders │
└─────────────────────┘
```
## Video positioning
The point of this project is not to teach PostgreSQL.
The story is:
> **What happens when an AI assistant can actually understand and query a real database?**
MCP is the bridge that exposes controlled database capabilities to Claude.
## Next project ideas
This can naturally lead into:
1. MCP + PostgreSQL + authentication
2. MCP + PostgreSQL + charts
3. MCP + multiple databases
4. MCP + APIs + PostgreSQL
5. Build an AI Data Analyst with MCP
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessNo issues