Comparison
PgBeam vs an MCP gateway
MCP gateways like Pomerium, MintMCP, and MCPGuard put identity and per-tool policy in front of every MCP server an agent uses. They decide whether the call happens. PgBeam decides what the SQL inside that call may do, masks the columns coming back, and is still in the path when the agent skips MCP and opens a Postgres connection.
an MCP gateway: An MCP gateway is an identity-aware proxy between an agent and its MCP servers, adding SSO, RBAC, per-tool allow and deny rules, and an audit of tool invocations.
MCP gateways and PgBeam both stand between an agent and something it should not be trusted with, at different layers. A gateway governs the JSON-RPC call: which identity is calling, which server it reaches, which tools on that server it may invoke. PgBeam governs what happens once that call is allowed, inside the SQL, and it is also in the path when the agent never speaks MCP at all. Read this page for where the line falls rather than for a verdict. For most teams the answer is both, with the gateway in front.
PgBeam vs an MCP gateway, side by side
| Capability | PgBeam | an MCP gateway |
|---|---|---|
| Enforcement layer | The SQL statement | The JSON-RPC tool call |
| Allow or deny a whole tool | Yes | Yes |
| Read the SQL inside a tool call | Yes, parsed | No, opaque argument |
| Table and column allowlists | Yes | No |
| PII masking in results | Yes | No |
| Row-level filters | Yes | No |
| Query, row, and egress budgets | Yes | No |
| Covers a raw connection string | Yes, same policy | No, MCP door only |
| Governs non-database tools | No, Postgres only | Yes, every server |
| SSO and SCIM | Yes, for your org | Yes, for tool access |
Allowing a tool is not allowing a statement
The finest grain a gateway has on a Postgres MCP server is the tool. It can allow query and deny a write-shaped tool, or deny the whole server for one group. Once query is allowed, every statement that fits inside it is allowed, because the SQL is a string argument in the JSON-RPC payload and the gateway does not parse it. SELECT id FROM orders LIMIT 10 and SELECT * FROM customers are the same call.
PgBeam parses the statement with the Postgres parser and decides on the parse tree: the statement kind, every relation it names, every column it projects. Allowing public.orders while refusing public.customers is a decision it can make. So is returning [redacted] for customers.email while the rest of the row comes through, and appending a tenant predicate to the WHERE clause before the query reaches the database.
The gateway is only in the MCP path
A gateway is in the path when the agent speaks MCP. A lot of agent code does not. A SQL chain in a framework, a psql shell a coding agent opens, an ORM in a background job, a serverless HTTP driver: those hold a connection string and talk to Postgres directly, and no MCP gateway is in that conversation.
PgBeam has two front doors, a Postgres connection string and a hosted MCP endpoint, and one policy engine behind both. The credential an MCP client presents and the credential a driver dials with are the same kind of object, with the same policy, the same audit trail, and the same kill-switch. A tool-call gateway cannot cover the driver case, because there is no tool call to inspect.
When an MCP gateway is the better fit
A gateway is the right shape for the problem it solves, and PgBeam does not solve that problem. Postgres is usually one of a dozen tools an agent holds: Slack, GitHub, an issue tracker, an internal API, a filesystem server. A gateway gives you one place to put identity, one RBAC model, one stream of tool-call audit, and one switch that covers all of them. PgBeam covers exactly one of those tools and has nothing to say about the other eleven. If your requirement is a single control plane for every MCP server in the company, the gateway is the product that meets it, and PgBeam is one line item inside that requirement rather than an answer to it. Put us behind the gateway for the database, or do not, but do not read this page as a case for replacing it.
Run PgBeam behind your MCP gateway
The two layers compose without either one knowing about the other. The gateway keeps doing identity and per-tool policy; PgBeam becomes the upstream it points at for the database, and enforces inside the calls the gateway lets through.
Split the rules by layer before you write any
Most of the argument about which product to buy disappears once the rules are sorted by which layer can actually see the thing they refer to. Write the split down first, because a rule placed at the wrong layer looks enforced and is not.
Who owns whatgateway which identity may reach the database server at all gateway which tools on it are callable (query, explain, list_tables) gateway SSO, SCIM, group membership, session revocation pgbeam which tables and columns a statement may touch pgbeam which columns come back masked, and how pgbeam row filters, row caps, query and egress budgets pgbeam the same rules on the raw connection stringCreate the policy
This is the half a gateway cannot express, because every line refers to something inside the SQL or inside the result. It is enforced in the Postgres wire protocol, so it holds for the MCP endpoint and the connection string alike.
Terminalpgbeam policies create \ --name agent-mcp \ --mode read_only \ --allow public.orders --allow public.customers \ --mask customers.email=redact \ --mask customers.ssn=hash \ --max-rows 500 \ --budget-queries-per-hour 300 \ --statement-timeout-ms 8000Issue a credential and register the endpoint upstream
The credential carries the policy. Register the hosted endpoint with your gateway the way it registers any remote MCP server, with a URL and a bearer header, and the agent keeps talking to the gateway address it already has.
Terminalpgbeam agents create --name research-agent --policy pol_xxx --expires 30d # what the gateway needs for the upstream url https://abc.proxy.pgbeam.app/mcp header Authorization: Bearer pba_xxxCheck that the split holds
dry-eval runs a statement through the data plane's own policy engine and prints the verdict without touching your database, so you can confirm the rules you thought you wrote are the rules that will fire. Then watch the refusals arrive.
Terminalpgbeam policies dry-eval --policy pol_xxx --sql "SELECT email FROM public.customers" pgbeam policies dry-eval --policy pol_xxx --sql "UPDATE public.orders SET status = 'paid'" pgbeam audit list --event blocked --limit 10
The two audit streams stay separate and both are worth keeping. The gateway records that an identity invoked a tool; PgBeam records the statement, the decision, the rule, rows, bytes, and latency. Ship both to the same destination if you want one timeline: PgBeam exports to Splunk HEC, Datadog, and Elastic, and to any endpoint over signed webhooks.
One allowed tool, six statements
Every row below is the same tool, called by the same identity, on the same server. Only the sql argument changes. A gateway that allowed query allowed all six of them. Here is what each one meets one layer down.
1 SELECT id, total FROM public.orders LIMIT 10
2 SELECT email, ssn FROM public.customers WHERE id = 42
3 UPDATE public.orders SET status = 'paid' WHERE id = 42
4 SELECT most_common_vals FROM pg_stats WHERE tablename = 'customers'
5 SELECT o.id FROM public.orders o JOIN internal.payouts p ON p.order_id = o.id
6 SELECT id FROM public.orders FOR UPDATE1 allow allow ok 10 rows
2 allow mask email, ssn email=[redacted], ssn hashed
3 allow block read_only this credential is read-only
4 allow block table_not_allowed pg_stats is not in the allowlist
5 allow block table_not_allowed internal.payouts is not allowed
6 allow block locking_select FOR UPDATE takes row locksThe gateway column is the same on every line for a structural reason, not an oversight. The six statements arrive as the same tool name with the same caller, and the only thing that differs is a string it does not parse. Tool-level policy is genuinely useful, and this is the granularity it has.
Line 2 is not a block, which is the case a deny-list mindset misses. The statement runs, the agent gets the row it asked for, and the two sensitive columns come back as a redaction token and a hash. There is no result for a tool-call gateway to rewrite, because the result is a Postgres wire message it forwards untouched.
Line 4 is the one people do not predict. Nothing in that statement selects customers.ssn, and it still reads sampled values of it out of the planner statistics. Catalogs that carry query text, credential material, raw object bytes, or sampled row values are excluded from the automatic catalog grant for that reason, and an operator has to name them in the allowlist to permit them.
Line 5 names an allowed table first. A check on the tool name passes it, and so does a check on the first relation in the statement. Deciding on the parse tree, over every relation the statement touches, is what catches the second one.
Where each layer stops
These are the questions that decide whether you need one product, the other, or both. The answers include the places PgBeam is not the right tool.
We already run a gateway in front of every MCP server
Keep it. Register PgBeam's hosted endpoint as the upstream for the database and the gateway keeps owning identity, group membership, and which tools are callable. Nothing about your existing setup has to move.
Where this stops: PgBeam has no view of your chat, source-control, or filesystem servers and no plans for one. If a single control plane for every tool is the requirement, the gateway is the product that meets it and PgBeam is one upstream behind it.
The agent has the connection string as well as the MCP endpoint
That path never reaches the gateway. It does reach PgBeam, because what the agent holds is a PgBeam credential rather than your database's own. The same policy, audit trail, and kill-switch apply to a psql session, an ORM, and a serverless driver.
DATABASE_URL=postgresql://agent_x:secret@abc.proxy.pgbeam.app:5432/mydbWhere this stops: PgBeam governs connections that go through PgBeam. If the agent still holds a direct connection string to the database itself, neither product is in that path, and the fix is to stop issuing that string rather than to add a layer.
A planted instruction tells the agent to read the customer table
The statement is refused with SQLSTATE 42501 and a reason written for a model to read, so the agent's next turn is a corrected query instead of a retry loop. The refusal is recorded with the rule that fired.
blocked by PgBeam agent policy: table "public.customers" is not in
this credential's allowlistWhere this stops: If public.customers is in the allowlist because the agent legitimately needs it, the read is allowed, and masking, row filters, and the row cap are what bound the damage instead. A policy is only as narrow as what you wrote, which is why dry-eval exists.
We want one audit trail of everything the agent did
You get two halves that fit together. The gateway records the invocation; PgBeam records the statement, the decision, the rule, rows returned, bytes out, latency, region, and credential, hash-chained so an edited or deleted row breaks the chain and a verify endpoint reports where.
pgbeam audit list --event blocked --limit 20
pgbeam audit verifyWhere this stops: PgBeam's trail covers what crossed PgBeam. Tool calls to servers that are not the database do not appear in it, so the joined picture comes from shipping both streams to the same destination.
We need a ceiling on how much data the agent can pull
Set a row cap, a queries-per-hour or per-day budget, and a daily egress budget on the policy. Row-returning MCP results carry a budget block with the limit, the amount used, the remainder, and the reset time for each capped window, so a well-behaved agent can pace itself instead of discovering the wall.
Where this stops: Budget counts are per-region approximations rather than a globally coordinated ledger, and the egress figure can trail the last statement by a moment. Leave headroom rather than setting a limit you intend to run right up to.
Questions
Safe Postgres access for your agents
Start with a 14-day free trial. No credit card required.
Get Started