Durable facts and conventions for working in this repo. Kept short so it stays true; verify anything here against the code before relying on it.
A production-grade PostgreSQL MCP server. Single-process, async
(asyncio), Python 3.12–3.14. 254 MCP tools (read-only mode exposes
a subset). Ships to PyPI (mcpg), GHCR, the MCP registry, Smithery, and
a read-only HF Spaces demo.
- Verify before you write — especially docs. Every claim in a doc must be checked against the code, the CLI, or a live endpoint, never from memory. This is a hard project rule.
- Generated beats hand-maintained. If a catalogue can drift, it's generated and guarded by a test. Don't hand-edit generated blocks.
- Commit per logical slice, push often. Big multi-batch changes have been lost to context resets — small commits survive.
- Never hand-edit
src/mcpg/_vendor/(see below).
- Version:
src/mcpg/__init__.py__version__.pyproject.tomlreads it dynamically via hatchling (dynamic = ["version"]). The MCPB bundle pins are synced bypackaging/mcpb/sync_version.py;tests/unit/test_mcpb_bundle.pyenforces the match. A release bumps exactly one line. - Tool surface:
tests/contract/tool_surface.snapshot.json(252 tools) is the contract.about.py'stool_count = len(tool_names). Two contract snapshots (surface + return shapes) — regenerate withMCPG_REGENERATE_TOOL_SNAPSHOT=1/MCPG_REGENERATE_TOOL_RETURN_SHAPES=1and commit the diffs. - Doc reference tables: the tool index (
docs/tools.md) and module map (docs/architecture.md) are generated bytools/generate_doc_tables.pyand guarded bytests/contract/test_doc_tables.py. Regenerate and paste between the<!-- BEGIN/END generated -->markers; don't hand-edit. - Access modes:
src/mcpg/policy.py.read-only(reads) /restricted(reads + DML writes — the "safe read-write" tier) /unrestricted(everything).DDL/SHELL/LISTEN(andMIGRATE, which piggybacks onDDL) each additionally needMCPG_ALLOW_*inunrestrictedmode. - NL→SQL providers: 19 built-in via the
_PROVIDERSregistry insrc/mcpg/nl2sql.py(single source; deviating key env vars:HF_TOKEN,GITHUB_TOKEN,DEEPINFRA_TOKEN). The provider table indocs/user-guide.mdmirrors it.
src/mcpg/sql/ is MCPg's own SQL-safety kernel (roadmap 18.1 de-vendored
the former crystaldba/postgres-mcp copy; see ADR-0007, which supersedes
ADR-0001). Three modules, policy separated from mechanism:
sql/allowlist.py— the SQL-safety policy as data: permitted statement /pglastAST node / function / extension sets. The single auditable decision surface.sql/safety.py—SafeSqlDriver: thepglastparse + AST-walker + read-only execute path. Reads policy fromallowlist.py; can't widen it.sql/driver.py—SqlDriver/DbConnPool/obfuscate_password(pool + execution + credential redaction; no policy).
Exports SqlDriver, SafeSqlDriver, DbConnPool, obfuscate_password
(the seam ~74 mcpg.* modules depend on). Fully inside the coverage gate
mypy --strict+ruff+banditlike the rest ofmcpg. Adversarial- fuzz tests:
tests/unit/test_sql_kernel_*.py. Changing the allowlist is a security-sensitive edit — run the adversarial suite and treat thedocs/reviews/devendor-sql-kernel-security-review.mdthreat model as the bar.
- Release SOP:
docs/release-process.md. Includes §5.1 per-release documentation-validation checklist and §9 quarterly NL→SQL default-model sweep (models go stale on the vendors' cadence). - Adding a tool: follow
docs/contributing/adding-tools.md— module → registration → bucket → tests → regen both snapshots → changelog. - PRs: squash-merge; the template wants
Advances roadmap row: N.M(tools/roadmap_linkage.pyvalidates againstdocs/feature-shortlist.md); updateCHANGELOG.md[Unreleased]. - CI: lint +
mypy src/mcpg, unit (90% gate), integration matrix (PG 14–18 required; PG 19 + WarehousePG experimental lanes), contract.
- Roadmap 18.1 — de-vendor the SQL-safety kernel. Replace the
vendored
crystaldba/postgres-mcpcore with a first-party implementation, removing the last third-party runtime dependency. Planned as a plan-first, then large-size PRs effort (to minimise repeated doc-update / regression-testing churn). Seedocs/feature-shortlist.md§18.