Enforcing Row Limits on Postgres MCP Server Queries to Prevent Bulk Data Extraction
Enforce Postgres MCP server row limits in layers. Use a scoped, read-only database role with a statement timeout, apply a hard result cap in the MCP server outside the agent's SQL, and add tool-call policy in the execution path that bounds cumulative volume through rate limits and human approval. No single layer stops bulk extraction, because a per-response cap leaves repeated calls and pagination loops open.
Why a per-query limit is not enough
An AI agent connected to Postgres through an MCP server can issue SELECT statements and receive the results in its model context. The exposure is the total volume of sensitive records an agent pulls in a session, not the size of any single response. A row limit that caps one response says little about fifty responses.
NIST SP 800-228 on API protection offers a useful frame, applied here by analogy because it does not mention MCP. It recommends payload limits that enforce maximum request and response sizes (REC-API-15-3), and it pairs them with rate limiting, concurrency limits and abuse detection. It notes that rate limiting can make data exfiltration, such as a script scraping all chat logs, harder and more discoverable.
Its example values, such as a maximum of 1,000 records per request or 10 items for a list request, are illustrations and not required thresholds. Choose your own limits from the sensitivity of the data and the task the agent performs.
Access scoping is a separate control. Role-based access, row-level restrictions and column-level restrictions decide what a principal may see, not how much of it can be retrieved in total. Treat scoping and volume limits as complementary.
What each enforcement layer can and cannot stop
The layers differ in where the limit is applied and in how much of the agent's behavior they can see. The table summarizes what each stops and what it leaves open.
- Postgres role, row-level security, statement_timeout: Stops access to tables, columns and rows outside the role's scope, and queries that run too long. Leaves open: none of these caps rows returned by a permitted query. A timeout bounds time, not volume, and a fast query over a permitted table can still return a lot of data.
- Cursors and fetch limits: Stops a single call from reading more than a set number of rows. Leaves open: the caller can fetch again, so the cap holds only if the server also limits how many fetches one session may make.
- MCP server result cap: Stops oversized single responses when the cap is applied to the executed statement and the returned result. Leaves open: pagination loops, repeated calls, and any code path that skips the cap.
- Approved or predefined query shapes: Stops arbitrary SQL and lets you bound parameters such as a time range. Leaves open: a parameterized tool can still be called repeatedly with different parameters, and flexibility for the agent drops.
- Execution-path policy on tool calls: Decides per call and can bound cumulative use through rate limits and escalation. Leaves open: it does not replace least-privilege database roles, and it applies only to traffic that passes through the path.
Bypass patterns to design against
Query construction is the first gap. If the server enforces a limit by appending LIMIT to the agent's SQL text, the agent can defeat it with its own LIMIT clause, a trailing comment, a multi-statement string, a CTE or a subquery. A sturdier approach wraps the statement so the cap applies to the outer result, rejects multiple statements, and truncates the result set in server code regardless of what the SQL says.
Pagination is the second gap. A limit of 100 rows does not stop an agent from repeating the query with OFFSET 0, 100, 200 and so on, or from using keyset conditions on an indexed column. Each call is individually compliant. Only a control that tracks calls across a session or principal sees the pattern.
Repeated and parallel calls are the third gap. Concurrent tool calls, or many narrow queries with different filters, can reach the same total volume as one large query. This is why NIST pairs response caps with rate limits and concurrency limits.
The fourth gap is scope drift in parameters. If a user or agent can supply a time range, an unbounded range permits a very large query. NIST gives the example of limiting a range to the last 30 days rather than allowing a query from 1972 onward. Apply the same idea to date ranges, ID ranges and filters.
A layered row-limit design
Build from the database outward, and treat each step as reducing what the next layer has to catch.
- 1. Constrain the database identity: Connect the MCP server with a dedicated read-only role limited to the tables and columns the tool needs. Add row-level security where rows differ by principal, and set a statement timeout on the role.
- 2. Cap results in server code: Apply a maximum row count to the executed statement and to the returned result, independent of the agent's SQL. Reject multi-statement input and return a clear truncation indicator.
- 3. Prefer approved query shapes: Expose parameterized tools instead of free-form SQL where the use case allows. Bound parameters such as time ranges and cap sub-calls per query.
- 4. Limit pagination explicitly: Cap the offset depth or the number of pages per session, or issue short-lived cursors that expire. Do not rely on the per-response cap alone.
- 5. Bound cumulative volume: Apply rate limits and concurrency limits per principal, session and time window, and define a threshold above which a call is blocked or escalated for human approval.
- 6. Record every decision: Retain evidence of the limit applied and the outcome for each call, so a reviewer can reconstruct the session.
Evidence to retain for each tool call
Limits that cannot be demonstrated are hard to defend in review. For each tool call, a regulated enterprise should be able to show the principal and session, the tool invoked, the query or parameters submitted, the policy and limit values in force, the decision taken, and the number of rows actually returned.
The decision field matters as much as the query. Record whether the call was allowed, redacted, blocked, rate limited or escalated, and where the limit was enforced: database, server or execution path. That lets a reviewer trace a control back to a framework reference such as NIST SP 800-228 REC-API-15 or REC-API-16 if you use it as a baseline.
Retained records of limit triggers also support detection. Repeated near-threshold calls or a burst of rate-limit events can indicate an extraction attempt, which is consistent with NIST's point that rate limiting makes exfiltration more discoverable. NIST SP 800-228 is guidance, not law, so set retention periods from your own regulatory obligations.
Enforcing policy in the execution path
Database and server controls act on a single query. Policy enforced in the execution path between the agent and the tool acts on the individual tool call, with context about the principal, the session and what came before. That is where cumulative limits, rate limiting and approval workflows belong.
Trussed enforces policy in the execution path across models, agents, tools and MCP servers. Decisions are allow, redact, block, rate limit or escalate for human approval, applied to individual interactions and tool calls and enforced at runtime, not reviewed after the fact. The enforcement itself generates audit-ready evidence, mapped to frameworks including HIPAA, NAIC, NYDFS, the EU AI Act and NIST, and Trussed can be deployed self-managed in the customer cloud with configurable retention and data residency.
This complements the layers above and does not replace them. A scoped database role and a hard cap in server code remain the first defenses. Policy in the path adds a per-call decision, a cumulative view across calls and a record of each outcome.
Layers that bound rows returned to a model
Database
Scoped read-only role, row-level security, statement timeout
MCP server
Hard result cap, bounded parameters, fixed query shapes
Execution path
Per-call decision: allow, redact, block, rate limit, escalate
Evidence
Query, decision and volume recorded for each tool call
See how tool-call enforcement and evidence work together
Review how policy enforced in the execution path can allow, redact, block, rate limit or escalate MCP tool calls, and how each decision becomes audit-ready evidence.
See How Evidence Is ProducedHow does your AI governance program compare?
See where your program has gaps in less than 2 minutes.