# pg-readonly-mcp [Health: Active]

**Category:** 🗄️ Databases  
**Repository:** https://github.com/les-k/pg-readonly-mcp  
**GitHub Stars:** 0  
**Views:** 0  
**Installs:** 0  
**Upvotes:** 0  
**Directory Page:** https://allmcps.com/mcp/pg-readonly-mcp

## Description
A read-only Postgres MCP server that parses SQL rather than pattern-matching it.

## Claude Desktop Quick Installation
Install path detected from listing signals. Uses `uvx` (confidence: high):

```json
"mcpServers": {
  "pg-readonly-mcp": {
    "command": "uvx",
    "args": ["pg-readonly-mcp"],
    "env": {
      "PG_READONLY_MCP_DSN": ""
    }
  }
}
```

**Requires environment variables:** `PG_READONLY_MCP_DSN` — the values above are empty placeholders; fill in real credentials before running (see the repository for what each one is for).

## Documentation

## What pg-readonly-mcp does

The pg-readonly-mcp MCP server gives an AI client one database operation: `query`. It accepts a SQL statement for PostgreSQL, validates the statement before execution, and returns up to a configured number of rows. The intended use is inspection and analysis of database contents without allowing the agent to modify the database.

SQL safety is based on parsing rather than searching for suspicious text. The validator requires one statement and permits only read-oriented outer forms: `SELECT`, set operations such as `UNION`, `INTERSECT`, and `EXCEPT`, or a CTE based on those forms. It also walks the complete syntax tree to find data-modifying operations hidden inside nested expressions or CTEs.

## How it works

The pg-readonly-mcp MCP server applies two separate controls. First, `sqlglot` parses the incoming SQL and rejects multiple statements, unsupported shapes, and write operations anywhere in the parsed tree. A statement that is unfamiliar to the validator is rejected instead of being treated as safe.

Second, startup checks inspect the PostgreSQL connection role. The server refuses to start when the role is a superuser, can create databases or roles, can bypass row-level security, or has grants beyond `SELECT` on tables it can see. These checks address common privilege mistakes but are not a complete PostgreSQL privilege audit.

The `query` tool also applies a maximum row count and a statement timeout. Their defaults are 1,000 rows and 5,000 milliseconds respectively.

## Setup and configuration

Install the package with:

```bash
pip install pg-readonly-mcp
```

A source checkout can be installed in editable mode with `pip install -e .`. Supply the PostgreSQL connection string through the `--dsn` option or the `PG_READONLY_MCP_DSN` environment variable. Using the environment variable keeps the password out of command-line arguments and client configuration files.

A typical stdio client configuration is:

```json
{
  "mcpServers": {
    "pg-readonly-mcp": {
      "command": "pg-readonly-mcp",
      "env": {
        "PG_READONLY_MCP_DSN": "postgresql://readonly_role:...@host:5432/db"
      }
    }
  }
}
```

The configured role should have no privileges beyond `SELECT`; the server checks this condition during startup.

## Tools and capabilities

The pg-readonly-mcp MCP server provides one tool:

- `query`: Executes one validated, SELECT-shaped PostgreSQL statement, subject to the row cap and statement timeout.

The project includes unit tests for SQL validation that do not require a database, plus integration tests using a live PostgreSQL instance to check role attributes and grants.

## Limitations and notes

Read-only enforcement does not make expensive queries free. A wide result within the row limit or a costly query plan can still consume database resources, although the row cap and timeout limit exposure.

The server controls database queries, not what an AI agent does with returned data. Any data readable by the configured role can be exposed to the agent. The startup privilege check also does not enumerate every possible PostgreSQL privilege path, including ownership, `PUBLIC` grants, or all row-level security behavior.

Finally, SQL parsing uses `sqlglot`, while PostgreSQL uses its own parser. A disagreement on an edge case is possible. The database role check provides an independent barrier if the parser and PostgreSQL interpret a statement differently.

_Full upstream README: https://allmcps.com/mcp/pg-readonly-mcp/readme_

