Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
794ef97
docs: add master plan, progress tracker, and ADR template
claude May 20, 2026
3b5c927
docs: record Phase 0 spike outcome in ADR-0001 and ADR-0002
claude May 20, 2026
b0f3f3f
feat: vendor SQL-safety kernel and scaffold the uv project
claude May 20, 2026
a20a757
ci: add GitHub Actions workflow for lint, type-check, and tests
claude May 20, 2026
07ca022
chore: add contributing guide, pre-commit hooks, and templates
claude May 20, 2026
3e376f0
feat: add env-driven configuration loader
claude May 20, 2026
b048283
feat: add database connection lifecycle wrapper
claude May 20, 2026
328334f
ci: apply ruff formatting to database.py
claude May 20, 2026
9997989
feat: add MCP server bootstrap
claude May 20, 2026
ea9fb0d
feat: add the get_server_info tool
claude May 20, 2026
fdd8c3f
feat: add mcpg CLI entry point and enable CI coverage gate
claude May 20, 2026
5480db3
test: add integration-test harness and PG version matrix
claude May 20, 2026
52c2152
feat: add schema-introspection tools
claude May 20, 2026
cf4568e
feat: add safe read-only query execution
claude May 20, 2026
00fa043
feat: add explain_query for execution plans
claude May 20, 2026
8463cb1
feat: cap run_select results with a truncated flag
claude May 20, 2026
bd87962
feat: add access-mode policy engine
claude May 20, 2026
4203937
test: add adversarial SQL-safety regression suite
claude May 20, 2026
85911d8
feat: add audit logging of tool invocations
claude May 20, 2026
c69d54e
docs: add threat model and security documentation
claude May 20, 2026
64a61fc
feat: add run_write for gated DML execution
claude May 20, 2026
e8a9469
feat: add run_ddl for gated DDL execution
claude May 20, 2026
438beb0
test: verify write tool calls are audited end-to-end
claude May 20, 2026
9ca11b3
feat: add database health checks
claude May 20, 2026
7e71066
feat: add workload analysis via pg_stat_statements
claude May 20, 2026
2a5957d
docs: plan PostgreSQL extension support (Phases 8-11)
claude May 20, 2026
171d401
docs: record extension-sequencing decisions in the decisions log
claude May 20, 2026
0c76696
feat: add index recommendations
claude May 20, 2026
4483a25
feat: add query plan analysis
claude May 20, 2026
737379b
feat: support configurable connection-pool sizing
claude May 21, 2026
b560e64
docs: document multi-tenancy and Row-Level Security guidance
claude May 21, 2026
79fb249
docs: add scaling characteristics and benchmark harness
claude May 21, 2026
4735b3a
docs: add usage guide and tool reference
claude May 21, 2026
0a27fff
build: add Dockerfile and packaging instructions
claude May 21, 2026
4c4db7e
chore: prepare the v0.1.0 release
claude May 21, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
.git
.github
.venv
tests
benchmarks
docs
*.md
!README.md
.ruff_cache
.mypy_cache
.pytest_cache
__pycache__
*.pyc
.coverage
htmlcov
27 changes: 27 additions & 0 deletions .github/ISSUE_TEMPLATE/bug_report.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
---
name: Bug report
about: Report incorrect behaviour in MCPg
labels: bug
---

## Description

A clear description of the bug.

## Steps to reproduce

1.
2.

## Expected vs actual behaviour

## Environment

- MCPg version / commit:
- PostgreSQL version:
- Access mode:
- MCP client:

## Logs / details

<!-- Redact connection strings and credentials before pasting. -->
18 changes: 18 additions & 0 deletions .github/ISSUE_TEMPLATE/feature_request.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
---
name: Feature request
about: Suggest a capability or improvement for MCPg
labels: enhancement
---

## Problem

What are you trying to do that MCPg does not currently support?

## Proposed solution

What should MCPg do? If it relates to a tool, describe the tool's inputs,
outputs, and which access mode should expose it.

## Alternatives considered

## Additional context
15 changes: 15 additions & 0 deletions .github/PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
## Summary

<!-- What does this PR change, and why? -->

## Roadmap

<!-- Which PLAN.md phase / task does this advance? -->

## Checklist

- [ ] Tests added/updated first (TDD); suite passes locally
- [ ] `ruff`, `ruff format`, and `mypy src/mcpg` pass
- [ ] `CHANGELOG.md` updated under `[Unreleased]`
- [ ] `docs/PROGRESS.md` updated if a roadmap task was completed
- [ ] No hand-edits to `src/mcpg/_vendor/`
66 changes: 66 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
name: CI

on:
push:
branches: [main, "claude/**"]
pull_request:

# Cancel superseded runs on the same ref.
concurrency:
group: ci-${{ github.ref }}
cancel-in-progress: true

jobs:
lint:
name: Lint & type-check
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up uv
uses: astral-sh/setup-uv@v5
with:
python-version: "3.12"
enable-cache: true
- run: uv sync --frozen
- name: ruff (lint)
run: uv run ruff check .
- name: ruff (format)
run: uv run ruff format --check .
- name: mypy
run: uv run mypy src/mcpg

test:
name: Tests (PG ${{ matrix.postgres }})
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
postgres: ["14", "15", "16", "17"]
services:
postgres:
image: postgres:${{ matrix.postgres }}
env:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
POSTGRES_DB: mcpg_test
ports:
- 5432:5432
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 10
steps:
- uses: actions/checkout@v4
- name: Set up uv
uses: astral-sh/setup-uv@v5
with:
python-version: "3.12"
enable-cache: true
- run: uv sync --frozen
# Runs unit, vendored-kernel, and integration suites with the coverage
# gate (fail_under = 90, authored code only) against each PG version.
- name: pytest
env:
MCPG_TEST_DATABASE_URL: postgresql://postgres:postgres@localhost:5432/mcpg_test
run: uv run pytest -q --cov
21 changes: 21 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Python
__pycache__/
*.py[cod]
*.egg-info/
build/
dist/
.eggs/

# Environments / tooling
.venv/
.env
.python-version.local

# Test / coverage
.pytest_cache/
.coverage
.coverage.*
htmlcov/
coverage.xml
.mypy_cache/
.ruff_cache/
24 changes: 24 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Local hooks delegate to the tools already pinned in pyproject.toml, so hook
# versions never drift from CI. Install with: uv run pre-commit install
repos:
- repo: local
hooks:
- id: ruff-check
name: ruff (lint)
entry: uv run ruff check --fix
language: system
types: [python]
require_serial: true
- id: ruff-format
name: ruff (format)
entry: uv run ruff format
language: system
types: [python]
require_serial: true
- id: mypy
name: mypy
entry: uv run mypy src/mcpg
language: system
types: [python]
pass_filenames: false
require_serial: true
1 change: 1 addition & 0 deletions .python-version
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
3.12
84 changes: 84 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
# Changelog

All notable changes to MCPg are documented here. The format is based on
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project
adheres to [Semantic Versioning](https://semver.org/).

## [Unreleased]

## [0.1.0] - 2026-05-21

First release: a production-grade PostgreSQL MCP server with 14 tools across
introspection, querying, writes, and tuning — read-only by default, every
statement validated, every tool call audited.

### Added

- Project plan, phased roadmap, and session-resume protocol (`PLAN.md`,
`docs/PROGRESS.md`).
- ADR-0001 (build approach: hard-fork) and ADR-0002 (technology stack).
- Vendored the self-contained `sql/` SQL-safety kernel from
`crystaldba/postgres-mcp` @ `07eb329` (MIT) into `src/mcpg/_vendor/sql/`,
with the upstream unit tests that port cleanly.
- Project scaffold: `pyproject.toml`, packaging, `ruff`/`mypy`/`pytest`/
coverage configuration, `NOTICE`.
- GitHub Actions CI (`.github/workflows/ci.yml`): lint, format, type-check,
and test jobs.
- `CONTRIBUTING.md`, local `pre-commit` hooks, and GitHub issue/PR templates.
- Env-driven configuration (`mcpg.config`): `Settings`, `AccessMode`,
`Transport`, and `load_settings`. Read-only is the default access mode and
the settings repr redacts database credentials.
- Database connection lifecycle (`mcpg.database`): `Database` wraps the pool
with connect/close, async-context-manager support, and a typed
`DatabaseError`.
- MCP server bootstrap (`mcpg.server`): `create_server` builds a configured
`FastMCP` whose lifespan owns the settings and database (no global state);
`run` serves over the stdio, streamable-HTTP, or SSE transport.
- First MCP tool, `get_server_info` (`mcpg.tools`): reports the server
version, access mode, transport, and database connection status.
- Console entry point: `mcpg` (and `python -m mcpg`) loads configuration
and runs the server.
- CI now enforces the test-coverage gate (90% of authored code).
- Integration-test harness (`tests/integration/`) running against a live
PostgreSQL; CI exercises the suite against PostgreSQL 14, 15, 16, and 17.
- Schema-introspection tools (`mcpg.introspection`): `list_schemas`,
`list_tables`, `describe_table`, `list_indexes`, and `list_extensions`,
using parameterised read-only catalog queries.
- Safe query execution (`mcpg.query`): the `run_select` tool validates
agent-supplied SQL against an allowlist and runs it read-only, returning a
typed result; unsafe statements are rejected.
- The `explain_query` tool returns a query's `EXPLAIN (FORMAT JSON)`
execution plan without running the query.
- `run_select` caps results at a configurable `max_rows` (default 1000) and
reports whether the result was `truncated`.
- Access-mode policy engine (`mcpg.policy`): tool registration is gated by
capability, so the available tools depend on the configured access mode.
- Adversarial SQL-safety regression suite covering statement stacking,
comment and transaction-control escapes, DDL/DML, `COPY`, and `DO` blocks.
- Audit logging (`mcpg.audit`): every tool invocation is logged to the
`mcpg.audit` logger with its outcome and arguments, with secrets masked.
- Security documentation (`docs/security.md`): threat model, trust
boundaries, mitigations, and operator responsibilities.
- Write execution (`mcpg.write`): the `run_write` tool executes a single
validated INSERT/UPDATE/DELETE statement, available only in unrestricted
access mode; statement stacking is rejected.
- The `run_ddl` tool executes a single validated DDL statement; it requires
unrestricted access mode and the `MCPG_ALLOW_DDL` opt-in.
- Database health checks (`mcpg.health`): the `check_database_health` tool
reports connection utilisation, buffer cache hit ratio, tables needing
vacuum, and invalid indexes.
- Workload analysis (`mcpg.workload`): the `analyze_workload` tool reports
the slowest queries via `pg_stat_statements`, degrading gracefully when
the extension is not installed.
- Index recommendations (`mcpg.indexing`): the `recommend_indexes` tool
flags large tables read mostly by sequential scan.
- Query plan analysis (`mcpg.query`): the `analyze_query_plan` tool
summarises a query's execution plan — total cost, estimated rows, node
types, and sequentially-scanned tables.
- Configurable connection-pool sizing via `MCPG_POOL_MIN_SIZE` and
`MCPG_POOL_MAX_SIZE` (defaults 1 and 5).
- Multi-tenancy / Row-Level Security guidance in `docs/security.md`.
- Scaling documentation (`docs/scaling.md`) and a benchmark harness
(`benchmarks/bench.py`).
- Usage guide (`docs/usage.md`), tool reference (`docs/tools.md`), and a
`uv`-based `Dockerfile`.
54 changes: 54 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# Contributing to MCPg

Thanks for your interest in MCPg. This document covers how we work.

## Project model

- The plan and roadmap live in [`PLAN.md`](PLAN.md); current progress and the
resume point live in [`docs/PROGRESS.md`](docs/PROGRESS.md).
- Significant decisions are recorded as ADRs in [`docs/adr/`](docs/adr/).

## Development setup

MCPg uses [`uv`](https://docs.astral.sh/uv/) and Python 3.12+.

```bash
uv sync # install dependencies into .venv
uv run pytest # run the test suite
uv run ruff check . # lint
uv run ruff format . # format
uv run mypy src/mcpg # type-check
```

Install the git hooks once: `uv run pre-commit install`.

## Test-Driven Development

MCPg is a TDD project. For all **authored** code:

1. **Red** — write a failing test that specifies the desired behaviour.
2. **Green** — write the minimum code to make it pass.
3. **Refactor** — clean up with the test as a safety net.

Do not open a PR with production code that has no accompanying test. The CI
coverage gate applies to authored code (`src/mcpg`, excluding `_vendor`).

## Vendored code

`src/mcpg/_vendor/` contains pinned third-party code (see its `README.md`). Do
not hand-edit it. To update it, follow the documented re-sync procedure. Before
modifying code that *calls into* the vendored kernel in a way that depends on
its behaviour, add characterization tests first.

## Commits and pull requests

- Use [Conventional Commits](https://www.conventionalcommits.org/):
`feat:`, `fix:`, `test:`, `docs:`, `refactor:`, `chore:`, `ci:`.
- Keep PRs focused; update `CHANGELOG.md` under `[Unreleased]`.
- Update `docs/PROGRESS.md` when you complete a roadmap task.
- All CI checks (lint, format, type-check, tests) must pass.

## Code style

`ruff` and `mypy --strict` are authoritative; their configuration lives in
`pyproject.toml`. New code targets Python 3.12 idioms.
24 changes: 24 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# MCPg — PostgreSQL MCP server.
# Build: docker build -t mcpg .
# Run: docker run -e MCPG_DATABASE_URL=postgresql://... -p 8000:8000 mcpg
FROM ghcr.io/astral-sh/uv:python3.12-bookworm-slim

WORKDIR /app

# Install dependencies and the package itself, without dev tooling.
# uv.lock keeps the build reproducible (--frozen).
COPY pyproject.toml uv.lock README.md LICENSE ./
COPY src ./src
RUN uv sync --frozen --no-dev

# Run as an unprivileged user.
RUN useradd --create-home --uid 1000 mcpg && chown -R mcpg /app
USER mcpg

# Containers serve over HTTP; MCPG_DATABASE_URL must be supplied at runtime.
ENV MCPG_TRANSPORT=streamable-http \
MCPG_HTTP_HOST=0.0.0.0 \
MCPG_HTTP_PORT=8000
EXPOSE 8000

ENTRYPOINT ["uv", "run", "--no-dev", "mcpg"]
23 changes: 23 additions & 0 deletions NOTICE
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
MCPg
Copyright (c) 2026 the MCPg authors

This product is licensed under the GNU Affero General Public License v3.0
or later (AGPL-3.0-or-later); see the LICENSE file.

------------------------------------------------------------------------
Third-party vendored code
------------------------------------------------------------------------

This product includes software developed by Crystal Corp.

Component : PostgreSQL SQL-safety kernel (the `sql/` subpackage)
Location : src/mcpg/_vendor/sql/
Source : https://github.com/crystaldba/postgres-mcp
Commit : 07eb329c8c48e49640e0d1b5b35465d4d024c3ee
License : MIT License, Copyright (c) 2025, Crystal Corp.
See src/mcpg/_vendor/LICENSE for the full license text.

The MIT-licensed code above is incorporated under the terms of the MIT
License, which is compatible with the AGPL-3.0 license of this project.
See src/mcpg/_vendor/README.md for provenance details and the re-sync
procedure.
Loading
Loading