Inside the Architecture of an MCP Server for PostgreSQL

As AI agents evolve from basic chatbots and code completion tools to sophisticated entities capable of direct interaction with production databases, the Model Context Protocol (MCP) has emerged as a pivotal standard interface between large language models and external systems, notably PostgreSQL. While it may be tempting to view an MCP server merely as a connector—transferring SQL queries from an LLM to a database and returning results—this perspective overlooks the critical nature of control inherent in its design. The architecture of an MCP server for PostgreSQL is not just about connectivity; it is fundamentally about managing what queries are executed, determining the data returned, and regulating the context consumed during interactions. Without a well-thought-out architecture, teams may face risks such as unsafe SQL execution, overexposed schemas, escalating token costs, and the kind of production instability that can lead to late-night calls for database administrators.

Having dedicated significant effort to building and open-sourcing an MCP server for PostgreSQL, I would like to share some practical insights and trade-offs that influenced its design.

Architecting the MCP Layer: From Connector to Control Plane

The first key insight is that an MCP server cannot function merely as a proxy for SQL between an LLM and PostgreSQL. A simplistic pass-through approach would be akin to granting an intern unrestricted superuser access to a production cluster on their first day. Instead, the MCP layer must serve as a mediation and policy layer, enforcing explicit security boundaries before any query reaches the database.

This necessitates a design centered around two fundamental principles: role-based access controls and read-only defaults. Connections should default to read-only at the session level, ensuring that even if query-level validation fails, PostgreSQL will reject any write attempts. Write access should be opt-in per database rather than a global setting, and the MCP server’s tool descriptions should dynamically indicate whether a connection is read-only or write-enabled, allowing the AI agent to understand its operational boundaries. Additionally, configuring the MCP server to connect using a PostgreSQL role with minimal privileges adds another layer of defense; even if the MCP layer’s controls are bypassed, the database will enforce the role’s access restrictions on schemas, tables, and operations.

Perhaps most critically, every piece of AI-generated SQL must be treated as untrusted input, similar to how a well-designed web application handles form submissions. This requires validation, constraints, and sanitization prior to execution. The MCP server should encapsulate queries within transactions using appropriate isolation levels, employ safe identifier quoting to prevent injection in any server-constructed SQL, and reject attempts to manipulate session variables that could undermine read-only protections. Notably, LLM-generated SQL cannot easily utilize traditional parameterized queries, as the agent constructs the entire statement—including table names, column references, and filter values—as a single string. Therefore, the MCP layer must rely on structural validation and execution-time controls rather than parameter binding alone. The MCP is not merely a convenience layer; it is infrastructure that warrants the same defensive design rigor as any other component in the data path.

Building Query Safety into the MCP Server

Even semantically valid SQL can impose significant operational strain on PostgreSQL. For instance, a perfectly legal SELECT * against a table containing fifty million rows can consume memory, saturate I/O, and block other workloads while the database dutifully returns every record. An AI agent generating queries lacks an inherent understanding of the operational costs associated with its outputs, necessitating that the MCP layer enforce guardrails on its behalf.

Statement classification serves as a useful starting point. By categorizing incoming queries as SELECT, DDL, DML, or DML-with-RETURNING, the MCP server can apply distinct policies to each type. Row limits should be enforced by default—setting a sensible ceiling of 100 rows, with a configurable maximum of perhaps 1,000—and the server should transparently append LIMIT clauses to SELECT statements when omitted by the agent. A particularly effective strategy is to fetch one row beyond the requested limit, allowing the server to detect truncation and inform the agent of additional available pages, thus enabling pagination without returning an unbounded result set.

The critical architectural principle here is the separation of query generation from execution approval. The AI agent generates SQL based on its understanding of the task, while the MCP server independently decides whether and how to execute that SQL. This separation creates a natural checkpoint where restrictions, execution guards, and cost controls can be applied without burdening the agent with operational concerns.

It is important to note that the MCP specification currently lacks hard mechanisms for enforcing user approval prior to tool execution. The existing spec defines tool annotations such as readOnlyHint and destructiveHint, but these are advisory in nature. The specification recommends maintaining human oversight for sensitive operations without mandating it. Ongoing community discussions are exploring stronger enforcement models, including proposals for cryptographically signed approvals and mandatory action routing for enterprise security. For the time being, the MCP server must assume responsibility for execution safety.

Designing for Token Efficiency

When AI systems engage with databases, every response consumes limited context space and computational resources. A query returning several hundred rows of wide tabular data can easily consume thousands of tokens; when multiplied by a multi-step reasoning chain, this can exhaust an agent’s context window before it completes its task. While token efficiency is often framed as a cost issue, it has a more immediate impact on reliability, responsiveness, and system stability.

MCP server design should prioritize returning only what is necessary, in the most compact representation possible. One effective approach is to utilize tab-separated values instead of JSON for query results; TSV is significantly more compact and, counterintuitively, easier for LLMs to parse than deeply nested JSON structures. Type-aware formatting is also crucial: converting PostgreSQL’s native types (numerics, timestamps, UUIDs, intervals) into clean, human-readable strings minimizes the verbosity of raw type metadata. NULL values present an interesting design consideration; representing them as empty strings in the output is significantly more compact than repeating the word ‘null’ multiple times across a result set. While NULL and an empty string are semantically distinct in PostgreSQL, this distinction rarely matters to an LLM focused on reasoning about query results rather than performing precise data comparisons.

Schema introspection warrants particular attention. A get_schema_info tool that dumps every table, column, index, and constraint in a database can easily generate tens of thousands of tokens—far exceeding what any agent requires for most tasks. Filtering schema responses by specific schema names and structuring the output so that AI agents can request progressively more detail keeps the context window focused on relevant metadata. The objective is to provide the agent with just enough information to reason correctly, rather than reproducing the entirety of pg_catalog.

Operationalising the MCP in Production

Deploying the MCP in production introduces challenges that may not be apparent during development. When multiple AI agents share a PostgreSQL instance, connection management becomes paramount; each agent session requires its own isolated connection pool, keyed to its authentication token, to prevent cross-session data leakage and ensure that one agent’s workload does not starve another of connections. Connection pools should be configurable per database, with sensible defaults for maximum connections, idle timeouts, and health check intervals, while multi-host configurations should support failover with automatic health checking.

Observability is arguably the most underinvested area in early MCP server designs. At a minimum, the MCP layer should log every executed query along with metadata: row counts, whether results were truncated, estimated token consumption, and execution duration. A structured trace log in JSONL format, encompassing entries for tool calls, tool results, resource reads, and session lifecycle events, provides an invaluable audit trail for debugging, compliance, and performance analysis. Sensitive parameters (passwords, API keys, session tokens) must be automatically redacted from trace output, as it is all too easy to inadvertently log credentials when capturing the full input and output of every tool invocation.

Long-running agent sessions introduce additional complexities. As an agent accumulates context over numerous database interactions, earlier results become less relevant but continue to consume the context window. Designing the MCP server to support concise, paginated responses from the outset equips agents (and the orchestration layers above them) with the necessary tools to manage context effectively. Coupled with per-request tracking (session IDs, request IDs, token hashes), this creates a framework where operators can trace any agent action from prompt to SQL execution and back, even across sessions that extend for hours.

Looking Forward

Creating an MCP server for PostgreSQL is fundamentally an exercise in architectural discipline; it demands the same meticulous consideration of security boundaries, failure modes, and operational concerns that we apply to any other critical infrastructure. Security, query safety, token efficiency, and observability cannot be retrofitted; they must be integrated into the design of the MCP layer from the outset. As AI-native workflows transition from prototypes to production environments, purpose-built MCP servers will become essential components in how organizations integrate AI with their PostgreSQL deployments. Teams that approach this integration as a first-class architectural challenge will be the ones to thrive.

Tech Optimizer
Inside the Architecture of an MCP Server for PostgreSQL