Skip to main content
Glama

๐Ÿ˜ Postgres MCP Server

An AI Database Assistant, built on the Model Context Protocol

Connects an AI client to a real PostgreSQL database โ€” schema exploration, safe querying, and LLM-powered reasoning, all through MCP tools.

Python PostgreSQL SQLAlchemy Groq Docker Tests

Docker Hub ยท Architecture ยท Installation ยท Example Tool Calls


What is this?

A production-grade Model Context Protocol (MCP) server that connects an AI client (like Claude) to a real PostgreSQL database, combining direct schema/data access with LLM-powered reasoning (via Groq) for SQL generation, query explanation, optimization, and business insight generation.

Built on a fictional company database ("Orbitals Inc.") โ€” a software consultancy with employees, departments, clients, projects, orders, invoices, support tickets, and meetings โ€” to exercise realistic relational queries and multi-hop relationships.

What makes this an "AI Database Assistant," not just a SQL executor

Capability

Tool

Schema exploration

list_tables, describe_table, list_columns, search_schema

Safe, validated querying

execute_select (SELECT-only, injection-hardened, row-limited)

Relationship discovery

find_related_tables (BFS over the FK graph, direct + indirect)

AI-assisted SQL

generate_sql, explain_query, optimize_query

Business reasoning

summarize_database, business_insights (generates SQL, executes it, interprets results in plain language)

The database is the source of truth for all facts; the LLM is used for reasoning, translation, and interpretation โ€” never as a substitute for real query execution.

Architecture

MCP Layer (tools/)        โ†’ thin, declares tools only
Service Layer (services/) โ†’ business logic, orchestration
Repository Layer (repositories/) โ†’ raw SQL / SQLAlchemy queries
Database Layer (db/)      โ†’ async engine, connection pooling
LLM Client Layer (llm/)   โ†’ Groq abstraction, provider-agnostic

Each layer only knows about the one below it โ€” MCP tools never touch the database directly, and the LLM provider is swappable behind an interface (llm/base.py) without touching services.

Folder Structure

postgres-mcp-server/
โ”œโ”€โ”€ src/postgres_mcp/
โ”‚   โ”œโ”€โ”€ config.py, server.py, logging_config.py, exceptions.py
โ”‚   โ”œโ”€โ”€ db/               # async engine, session management
โ”‚   โ”œโ”€โ”€ models/            # SQLAlchemy models (11 tables)
โ”‚   โ”œโ”€โ”€ repositories/       # raw SQL against Postgres
โ”‚   โ”œโ”€โ”€ services/           # business logic
โ”‚   โ”œโ”€โ”€ llm/                # Groq provider (swappable interface)
โ”‚   โ”œโ”€โ”€ tools/               # MCP tool registration
โ”‚   โ””โ”€โ”€ utils/                # SQL injection defenses (sql_guard.py)
โ”œโ”€โ”€ scripts/               # check_connection, check_llm, seed_database
โ”œโ”€โ”€ alembic/                # schema migrations
โ”œโ”€โ”€ tests/
โ”‚   โ”œโ”€โ”€ unit/                # sql_guard, schema_service, insight_service
โ”‚   โ””โ”€โ”€ integration/          # real Neon queries
โ”œโ”€โ”€ Dockerfile, docker-compose.yml
โ””โ”€โ”€ alembic.ini

Database Schema

11 tables modeling a software consultancy: departments, employees (self-referencing manager hierarchy), clients, projects, project_assignments (many-to-many), products, orders, invoices, support_tickets, meetings, meeting_attendees (many-to-many). Full relational integrity via foreign keys, check constraints (e.g., positive order quantities, valid status enums), and indexes on frequently-filtered columns.

Installation

Prerequisites

  • Python 3.12+

  • A Neon PostgreSQL database (free tier works)

  • A Groq API key (free tier works)

  • Docker (optional, for containerized runs)

Setup

git clone <your-repo-url>
cd postgres-mcp-server
python3 -m venv .venv
source .venv/bin/activate
pip install -e ".[dev]"
cp .env.example .env
# fill in .env with your real Neon connection string and Groq API key

Environment Variables

Variable

Description

DATABASE_URL

Neon connection string, async form: postgresql+asyncpg://user:pass@host/db?ssl=require

GROQ_API_KEY

Your Groq API key

GROQ_MODEL

Model name (default: llama-3.3-70b-versatile)

LOG_LEVEL

Logging verbosity (default: INFO)

ENVIRONMENT

development or production

Neon Setup

  1. Create a free account and project at neon.tech

  2. Copy the connection string from your project dashboard

  3. Convert it to async form: change postgresql:// to postgresql+asyncpg://, and sslmode=require to ssl=require, dropping any channel_binding param

Database Setup

alembic upgrade head              # creates all 11 tables
python scripts/seed_database.py   # populates realistic fictional data

Running Locally

python -m postgres_mcp.server

This starts the MCP server over stdio transport. To test it interactively:

npx @modelcontextprotocol/inspector@latest python -m postgres_mcp.server

Public MCP Endpoint

The hosted MCP server is available at:

https://aman-postgres-mcp-server.fastmcp.app/mcp

Running with Docker

docker build -t postgres-mcp-server:latest .
docker run -i --env-file .env postgres-mcp-server:latest

Or via Docker Compose:

docker compose up --build

Pre-built image available on Docker Hub:

docker pull amankhan1009/postgres-mcp-server:latest

Testing

pytest -m "not integration" -v   # unit + mocked tests (fast, no DB needed)
pytest -m integration -v          # integration tests (hits real Neon)
pytest -v                          # everything

22 tests passing: 20 unit/mocked (including full sql_guard injection-defense coverage) + 2 integration tests against real seeded data.

Example Tool Calls

list_tables()

["clients", "departments", "employees", "invoices", "meeting_attendees",
 "meetings", "orders", "products", "project_assignments", "projects", "support_tickets"]

business_insights("which department has the highest average employee salary?")

The department with the highest average employee salary is Engineering, with an average salary of $101,125.

find_related_tables("employees", max_depth=2)

{
  "table_name": "employees",
  "related_tables": {
    "departments": 1, "project_assignments": 1, "meeting_attendees": 1, "support_tickets": 1,
    "projects": 2, "clients": 2, "meetings": 2
  }
}

Security

  • All user/LLM-supplied SQL passes through sql_guard.py: single-statement enforcement, forbidden keyword/function blocking, and a hard row limit

  • Every query runs inside a READ ONLY Postgres transaction as a final safety net

  • Table names from tool inputs are validated against a live whitelist (list_tables()) before being used in any query, since identifiers can't be parameterized like values

  • Secrets are never baked into the Docker image โ€” passed only at runtime via --env-file

  • Container runs as a non-root user

Future Improvements

  • Optional, separately-hardened write capability (structured insert_row-style tools with strict table/column whitelisting, audit logging, and confirmation steps) โ€” deliberately out of scope for this version, which is read-only by design

  • Support for a second LLM provider (OpenAI/Anthropic) via the existing LLMProvider interface

  • Query result caching for repeated describe_table/list_tables calls

  • Rate limiting on execute_select and LLM-powered tools


Built as a portfolio project to demonstrate production-grade backend architecture, PostgreSQL fluency, and safe AI-database integration via MCP.

Latest Blog Posts

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/Amankhan1009/postgres-mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server