Skip to the content.

MCPg Security Model

MCPg’s threat model and the controls that mitigate each threat. Living document — kept in step with the implementation as new mitigations land.

Related documents. For how to report a vulnerability, see SECURITY.md at the repo root. For the living roadmap of shipped (✅) vs queued (⬜) hardening features, see security-hardening.md.


What MCPg is

MCPg is an MCP server that exposes a PostgreSQL database to an AI agent through a fixed set of tools. The agent does not get a raw database connection — it can only call the tools MCPg registers, and every call is validated and audited.


Trust boundaries

  Agent / MCP client   ──tools──▶   MCPg server   ──SQL──▶   PostgreSQL
   (untrusted input)               (trusted code)          (the asset)

Assets

  1. Data in the database — confidentiality and integrity.
  2. Database credentials — both the DSN MCPg uses and any credentials nested in tool arguments / result payloads.
  3. Database availability.
  4. The MCPg process itself — its environment, its in-flight connections, its audit trail.

Threats and mitigations

T1 — SQL injection / unsafe statements

Mitigation.

T2 — Unintended writes / privilege escalation through tool surface

Mitigation.

T3 — Resource exhaustion / denial of service

Mitigation.

T4 — Credential disclosure

Mitigation.

T5 — Plaintext database connection

Mitigation.

T6 — Unauthenticated remote access (HTTP transports)

Mitigation.

T7 — Tenant-isolation bypass

Mitigation.

T8 — Lack of attribution / audit gaps

Mitigation.

T9a — Credentials in plaintext env / config files

Mitigation.

T9b — Observability leakage

Mitigation.

T9 — Supply-chain compromise

Mitigation.


Operator responsibilities (defence in depth)

MCPg is not a replacement for database-side security. Operators should:


Multi-tenancy and Row-Level Security

PostgreSQL Row-Level Security (RLS) policies are evaluated against the connecting role (and session settings). Without per-request role switching, MCPg connects with a single database role and pools those connections, so every agent request would look like the same principal from the database’s perspective — and RLS keyed on current_user would not isolate tenants.

MCPg’s mitigation: the SET LOCAL ROLE workflow described in T7. Set MCPG_DEFAULT_ROLE for a static deployment, or wire HTTP clients / OIDC to send a per-request role and pair with MCPG_ALLOWED_ROLES. The tenant driver wraps every query in a transaction with SET LOCAL ROLE, so RLS sees the correct principal even on a shared pool.

When the tenant boundary is at the database level (separate databases per tenant, not separate roles), run one MCPg instance per tenant with a tenant-specific MCPG_DATABASE_URL.


Reporting a vulnerability

See SECURITY.md at the repo root for the full reporting policy. Summary:

Do not file public issues for security reports.


See also