Provides read-only access to PostgreSQL databases with tools for executing SELECT queries, inspecting database schemas, viewing table structures and indexes, and tracking query history with multiple output formats (JSON, CSV, Markdown).
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., "@PostgreSQL MCP Servershow me the last 5 orders with customer names in markdown format"
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.
PostgreSQL MCP Server
A Model Context Protocol (MCP) server that provides read-only access to PostgreSQL databases. Execute SELECT queries, inspect database schema, and track query history with built-in safety features.
Features
Read-Only Query Execution: Execute SELECT queries with automatic read-only transaction enforcement
Multiple Output Formats: Results in JSON, CSV, or Markdown table format
Schema Inspection: List tables, describe table structures, view indexes, and explore schemas
Query History: Track recently executed queries with execution time and metadata
Connection Pooling: Efficient connection management with configurable pool sizes
Security: Query validation, SQL injection prevention, and read-only transaction guarantees
Database Statistics: View database size, table counts, and connection information
Installation
Clone this repository:
git clone <repository-url>
cd postgres-mcpInstall dependencies using
uv:
uv syncConfiguration
Environment Variables
Create a .env file in the project root (use .env.example as a template):
# Required: PostgreSQL Connection Parameters
POSTGRES_HOST=localhost
POSTGRES_PORT=5432
POSTGRES_DATABASE=myapp
POSTGRES_USER=readonly_user
POSTGRES_PASSWORD=secure_password
# Optional: Connection Pool Configuration
POSTGRES_POOL_MIN_SIZE=2 # Default: 2
POSTGRES_POOL_MAX_SIZE=10 # Default: 10
POSTGRES_COMMAND_TIMEOUT=60 # Default: 60 seconds
POSTGRES_CONNECTION_TIMEOUT=10 # Default: 10 seconds
# Optional: Query History Configuration
QUERY_HISTORY_SIZE=100 # Default: 100
# Optional: Logging Configuration
LOG_LEVEL=INFO # Default: INFO (DEBUG, INFO, WARNING, ERROR)Database User Setup
For security, create a dedicated read-only PostgreSQL user:
-- Create read-only user
CREATE USER readonly_user WITH PASSWORD 'secure_password';
-- Grant connect permission
GRANT CONNECT ON DATABASE myapp TO readonly_user;
-- Grant schema usage
GRANT USAGE ON SCHEMA public TO readonly_user;
-- Grant select on all tables
GRANT SELECT ON ALL TABLES IN SCHEMA public TO readonly_user;
-- Grant select on future tables
ALTER DEFAULT PRIVILEGES IN SCHEMA public
GRANT SELECT ON TABLES TO readonly_user;Usage
Running the Server
uv run python main.pyThe server communicates via stdio and can be integrated with MCP clients like Claude Desktop.
Integrating with Claude Desktop
Add this configuration to your Claude Desktop config file:
MacOS: ~/Library/Application Support/Claude/claude_desktop_config.json
Windows: %APPDATA%\Claude\claude_desktop_config.json
{
"mcpServers": {
"postgres": {
"command": "python",
"args": ["/path/to/postgres-mcp/main.py"],
"env": {
"POSTGRES_HOST": "localhost",
"POSTGRES_PORT": "5432",
"POSTGRES_DATABASE": "myapp",
"POSTGRES_USER": "readonly_user",
"POSTGRES_PASSWORD": "secure_password"
}
}
}
}Alternatively, if using uv:
{
"mcpServers": {
"postgres": {
"command": "uv",
"args": [
"run",
"--directory",
"/path-to-postgres-mcp/postgres-mcp",
"python",
"main.py"
]
}
}Available Tools
1. query_database
Execute SELECT queries on the database with formatted output.
Input:
{
"query": "SELECT * FROM users LIMIT 10",
"format": "json",
"timeout": 30
}Parameters:
query(required): SQL SELECT query to executeformat(optional): Output format -json(default),csv, ormarkdowntimeout(optional): Query timeout in seconds (max 300)
Output:
{
"rows": [...],
"row_count": 10,
"columns": ["id", "name", "email"],
"execution_time_ms": 45.32,
"format": "json",
"formatted_output": "..."
}2. list_tables
List all tables in the database with metadata.
Input:
{
"schema": "public"
}Parameters:
schema(optional): Filter tables by schema name
Output:
{
"tables": [
{
"schema": "public",
"name": "users",
"row_count_estimate": 1500,
"size": "128 KB"
}
]
}3. describe_table
Get detailed table structure including columns, types, indexes, and constraints.
Input:
{
"table_name": "users",
"schema": "public"
}Parameters:
table_name(required): Name of the tableschema(optional): Schema name (default:public)
Output:
{
"schema": "public",
"table": "users",
"columns": [
{
"name": "id",
"type": "integer",
"nullable": false,
"default": "nextval('users_id_seq')",
"primary_key": true
}
],
"indexes": [...],
"foreign_keys": [...]
}4. list_schemas
List all schemas in the database.
Input:
{}Output:
{
"schemas": ["public", "auth", "analytics"]
}5. get_table_indexes
Get all indexes for a specific table.
Input:
{
"table_name": "users",
"schema": "public"
}Parameters:
table_name(required): Name of the tableschema(optional): Schema name (default:public)
Output:
{
"indexes": [
{
"name": "users_pkey",
"type": "btree",
"columns": ["id"],
"unique": true,
"primary": true
}
]
}6. get_query_history
Retrieve recent query history with execution metadata.
Input:
{
"limit": 20
}Parameters:
limit(optional): Maximum queries to return (default: 20, max: 100)
Output:
{
"queries": [
{
"query": "SELECT * FROM users",
"timestamp": "2025-12-04T10:30:00Z",
"execution_time_ms": 45.32,
"row_count": 10,
"format": "json",
"success": true,
"error": null
}
]
}7. get_database_stats
Get overall database statistics and metadata.
Input:
{}Output:
{
"database_name": "myapp",
"size": "45 MB",
"table_count": 12,
"connection_count": 5,
"version": "PostgreSQL 15.3"
}Security Features
Read-Only Enforcement
All queries are executed within read-only transactions:
async with conn.transaction(readonly=True):
result = await conn.fetch(query)Query Validation
Queries are validated before execution to prevent:
INSERT, UPDATE, DELETE operations
DROP, CREATE, ALTER operations
TRUNCATE, GRANT, REVOKE operations
Other write/admin operations
SQL Injection Prevention
Input sanitization for table and schema identifiers
Parameterized queries where applicable
Regex-based validation of identifiers
Architecture
Components
config.py: Environment configuration and validationdatabase.py: Connection pool management and read-only query executionvalidators.py: Query validation and sanitizationformatters.py: Result formatting (JSON, CSV, Markdown)history.py: Thread-safe query history trackingtools.py: MCP tool implementationsserver.py: MCP server setup and lifecycle managementtypes.py: Pydantic models for type safety
Connection Pooling
Min Size: 2 warm connections
Max Size: 10 concurrent connections
Timeout: 60 seconds command timeout, 10 seconds connection timeout
Idle Lifetime: Automatic cleanup of inactive connections
Troubleshooting
Connection Errors
Error: "Database authentication failed"
Verify
POSTGRES_USERandPOSTGRES_PASSWORDare correctCheck if the user exists in PostgreSQL
Ensure the user has CONNECT permission
Error: "Database 'myapp' not found"
Verify
POSTGRES_DATABASEmatches an existing databaseCheck database name spelling
Error: "Connection refused"
Verify PostgreSQL is running on the specified host and port
Check firewall settings
Verify
POSTGRES_HOSTandPOSTGRES_PORTare correct
Query Errors
Error: "Query contains forbidden keyword: INSERT"
This server only allows SELECT queries
Use a different tool for write operations
Error: "Table does not exist"
Verify table name and schema are correct
Use
list_tablesto see available tablesCheck if user has SELECT permission on the table
Error: "Query execution timeout"
Query took longer than the specified timeout
Optimize the query or increase timeout parameter
Check for missing indexes on large tables
Permission Errors
Error: "permission denied for table X"
The database user lacks SELECT permission
Grant appropriate permissions (see Database User Setup)
Development
Running Tests
# Add your test commands here
pytestProject Structure
postgres-mcp/
--- .env # Configuration (not in git)
--- .env.example # Configuration template
--- .gitignore
--- README.md
--- pyproject.toml
--- main.py # Entry point
--- src/
--- postgres_mcp/
--- __init__.py
--- config.py # Configuration
--- database.py # Connection pool
--- formatters.py # Output formatting
--- history.py # Query history
--- server.py # MCP server
--- tools.py # MCP tools
--- types.py # Pydantic models
--- validators.py # Query validationLicense
[Add your license here]
Contributing
[Add contribution guidelines here]
This server cannot be installed
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.