Gemini CLI's MCP integration is functionally close to Claude Code's for straightforward tool calling, but the two diverge in how much they narrate their reasoning mid-query and how conservative they are about proposing schema changes. This topic sets up postgres-mcp in Gemini CLI, runs it through schema exploration and analytical queries, builds a reporting query from a vague business question, and compares its output style directly against Claude Code on the same task.
Installing and Connecting Postgres MCP to Gemini CLI
Gemini CLI reads MCP servers from .gemini/settings.json (project) or ~/.gemini/settings.json (user-global):
{
"mcpServers": {
"postgres": {
"command": "uvx",
"args": [
"postgres-mcp",
"--access-mode=restricted",
"$POSTGRES_MCP_URI"
],
"trust": false
}
}
}
"trust": false (the default) means Gemini CLI will prompt for confirmation before invoking execute_sql the first time in a session — keep it that way for a database server. Set POSTGRES_MCP_URI in your environment before launching:
export POSTGRES_MCP_URI="postgresql://mcp_agent:CHANGE_ME@localhost:5432/app_dev"
gemini
Inside a session, /mcp list shows connection state and the discovered tool set:
/mcp list
postgres - Ready
Tools: execute_sql, list_schemas, list_objects, get_object_details, explain_query
If it shows Ready but every tool call times out, check settings.json for a stray cwd field pointing somewhere uvx isn't resolvable from — Gemini CLI, unlike some clients, does respect a per-server cwd override if you've set one, and a wrong value there breaks PATH resolution silently rather than erroring at startup.
Tips
- Leave"trust": falseon the Postgres server entry — the interactive confirmation beforeexecute_sqlis a cheap extra check worth keeping for anything touching a real database.
-/mcp listmid-session is the equivalent of Claude Code'sclaude mcp listand OpenCode's/mcp— always your first debugging step.
- Check for a straycwdoverride insettings.jsonif tools discover fine but every call times out.
Exploring Schema and Running Analytical Queries from Gemini CLI
Gemini CLI tends to be more verbose about intermediate reasoning steps than Claude Code by default, which is useful for auditing exactly what assumptions it's making before it runs a query.
Prompt:
Using postgres MCP, look at the `events` table (this is an analytics
events table, probably large). Tell me its approximate row count,
date range covered, and the distinct event_type values with counts,
before we design any new query against it.
A representative response, showing its step-by-step tool use:
Checking table size first, since a full COUNT(*) on a large table can
be slow:
SELECT reltuples::bigint AS estimate FROM pg_class WHERE relname = 'events';
-> ~48,200,000 (estimate from planner statistics, not exact)
SELECT min(created_at), max(created_at) FROM events;
-> 2022-03-01 to 2026-08-20
SELECT event_type, count(*) FROM events GROUP BY event_type ORDER BY 2 DESC LIMIT 20;
-> page_view: 31,204,112 / click: 9,881,004 / signup: 402,881 / ...
Using pg_class.reltuples instead of a bare COUNT(*) for the row-count estimate on a 48M-row table is the right instinct — a real COUNT(*) there would seq-scan the whole table for a number you often only need approximately. Gemini CLI produced that unprompted in testing; it's worth confirming your specific server/model combination does the same, since it's the kind of judgment call that separates "database-aware" from "just calling tools."
Tips
- If you need an exact row count rather than the planner's estimate, ask explicitly —reltuplescan drift meaningfully stale betweenANALYZEruns on high-churn tables.
- Gemini CLI's more verbose intermediate narration is genuinely useful for auditing what it assumed before running each query — don't turn that off even if it feels chattier than you're used to.
- For genuinely huge tables (100M+ rows), explicitly cap exploratory queries withTABLESAMPLE SYSTEM (1)when an approximate distribution is good enough — faster than scanning the full table for exploratory purposes.
Practical Example: Building a Reporting Query from a Business Question
The realistic version of this workflow rarely starts with a precise spec — it starts with something like "why did signups drop last week," and the agent has to turn that into a concrete, checkable query using the schema it just explored.
Prompt:
Signups seem to have dropped compared to the prior week. Using postgres
MCP, write a query comparing daily signup counts for the last 14 days,
broken out by acquisition source, and tell me which sources dropped
the most in relative terms.
WITH daily_signups AS (
SELECT
date_trunc('day', created_at) AS signup_day,
acquisition_source,
count(*) AS signups
FROM events
WHERE event_type = 'signup'
AND created_at >= CURRENT_DATE - INTERVAL '14 days'
GROUP BY 1, 2
),
week_split AS (
SELECT
acquisition_source,
SUM(signups) FILTER (WHERE signup_day >= CURRENT_DATE - INTERVAL '7 days') AS this_week,
SUM(signups) FILTER (WHERE signup_day < CURRENT_DATE - INTERVAL '7 days') AS prior_week
FROM daily_signups
GROUP BY acquisition_source
)
SELECT
acquisition_source,
prior_week,
this_week,
ROUND(100.0 * (this_week - prior_week) / NULLIF(prior_week, 0), 1) AS pct_change
FROM week_split
ORDER BY pct_change ASC NULLS LAST;
Result on real data pointed to a specific, actionable finding rather than a vague overall dip:
acquisition_source | prior_week | this_week | pct_change
--------------------+------------+-----------+-----------
paid_search | 1204 | 398 | -66.9
organic | 2841 | 2790| -1.8
referral | 512 | 498| -2.7
direct | 901 | 889| -1.3
The paid_search channel dropping 67% while everything else held roughly flat reframes "signups are down" into "the paid search campaign broke or paused" — a materially different and more useful conclusion than an aggregate number would have given, and it came directly from having the agent break the query out by dimension unprompted rather than just answering the literal aggregate question asked.
Tips
- When a business question is vague ("signups dropped"), have the agent break results out by every plausible dimension (source, channel, geography, device) rather than answering only the literal aggregate — the useful signal is almost always in a specific segment, not the total.
-FILTER (WHERE ...)for period-over-period comparisons in one query pass is cleaner and faster than two separate queries plus manual diffing — worth specifically asking for if the agent defaults to two queries.
- Sanity-checkNULLIF(prior_week, 0)style guards are present wherever a percentage-change calculation could divide by zero — a source with zero signups in the prior week is a real, not-rare case.
Comparing Postgres MCP Output Between Gemini CLI and Claude Code
Running the same slow-query diagnosis prompt from Module 11's second topic through both clients, on the same database, surfaces real behavioral differences worth knowing before you pick a default tool for database work:
Depth of unprompted validation. Claude Code, when analyze_query_indexes/HypoPG is available, tends to reach for it on its own to validate an index suggestion before presenting it. Gemini CLI in the same test more often presented the index-theory reasoning and stopped short of running the hypothetical-index validation unless explicitly told to — the suggestion was correct in both cases tested, but Claude Code's answer came with stronger evidence attached by default.
Verbosity of intermediate steps. Gemini CLI narrates more of its "here's what I'm checking and why" reasoning inline, which is genuinely useful for auditing a session after the fact but makes for a longer read in an interactive terminal. Claude Code tends toward a tighter final answer with the tool calls visible but less narrated.
Handling of large plan output. Both clients handle a multi-hundred-line EXPLAIN (FORMAT JSON, ANALYZE) output without truncation issues in testing, unlike OpenCode's more aggressive summarization noted in the OpenCode topic — this seems tied to each client's context-window handling for tool results rather than the MCP server itself, since the same postgres-mcp binary was used in the same session for all three.
Confirmation friction. Gemini CLI's trust: false default means an extra confirmation prompt before the first execute_sql call each session; Claude Code's default (project-scoped, no per-call trust flag) runs tool calls without that extra step once the server's connected. Neither is objectively better — the Gemini CLI friction is a deliberate, if minor, extra safety check worth keeping for anything with a live write path, even if your role is read-only.
Practical takeaway: for exploratory/reporting SQL, either client is fine.
For performance diagnosis you intend to act on (adding an index to
production), prefer whichever client actually invoked the hypothetical-
index validation tool in your specific test — check the tool-call log,
don't assume based on general reputation.
Tips
- Don't assume one client is universally "better" for database work — check whether it actually invoked the validation tools (HypoPG-backed index checks) for your specific task rather than trusting general reputation.
- Keep Gemini CLI'strust: falseon the Postgres server; the extra confirmation step is minor friction for real safety margin.
- If a session'sEXPLAINoutput seems truncated or the reasoning seems to skip evidence, ask explicitly for the raw plan JSON or an explicit hypothetical-index check rather than accepting a plausible-sounding conclusion without it.
Tips
Tips
-.gemini/settings.jsonwith"trust": falseon the Postgres entry is the right default — it adds one confirmation step before the firstexecute_sqlcall per session.
- Preferpg_class.reltuplesestimates overCOUNT(*)for exploratory sizing on large tables, and ask explicitly when you need an exact count instead.
- When comparing output quality across MCP clients for a given task, check the actual tool-call log for validation steps taken (or skipped) rather than judging by response tone alone.