Give Cursor Safe Access to Your Database (MCP Setup)
To give Cursor safe access to your database, connect it through an MCP server with a token that is read-only by default, scoped to one project, and never the database owner password. When you want the agent to write, give it its own restricted key so destructive statements wait for your approval and its changes can be undone. This guide shows the mcp.json for each step, what Cursor does with approvals, and how to recover if an agent has already deleted data.
Cursor details below come from Cursor's own MCP documentation as of November 2026.
#Three ways Cursor can reach a database
| Approach | How it works | Risk |
|---|---|---|
Connection string in .env | The agent runs psql or a script in the terminal | It acts as whoever owns that string, usually the owner. Nothing stops DROP TABLE. |
| Generic local Postgres MCP server | A stdio server started by Cursor with your connection string | Same role as the string. Read-only only if the server enforces it. |
| Hosted MCP server with scoped tokens | Cursor calls a remote server with a token you can limit and revoke | The server decides which tools exist and what role SQL runs as. |
The first two are common in tutorials because they are quick. They are also how an agent ends up deleting the data you care about: the model meant to reset a test table, the connection pointed at production, and the role had every right. Safety has to come from the credential, not from the prompt.
#How MCP works in Cursor
Cursor reads MCP servers from two files:
.cursor/mcp.jsonin a project, for tools only that project uses;~/.cursor/mcp.jsonin your home folder, for tools available everywhere.
It supports stdio, SSE and Streamable HTTP transports. Remote servers take a url and optional headers. Cursor expands ${env:NAME} in url and headers, so the token can live in an environment variable instead of the file. By default Cursor asks for approval before it runs an MCP tool, and you can expand a call to see its arguments. In Auto-review mode, allowlisted MCP tools run at once and other calls go through Cursor's classifier.
That approval prompt is your last line of defense. The steps below make sure it is not your only one.
#Step 1: a read-only connection
StitchDB runs a remote MCP server at https://stitchdb.com/platform/mcp. Create a personal access token under Access tokens in the dashboard with access Read-only, an expiry, and only the project you are working on. The token starts with sdb_pat_ and is shown once (access tokens).
Put the token in your shell environment, not in the repository:
export STITCHDB_TOKEN=sdb_pat_...Then add the server to .cursor/mcp.json:
{
"mcpServers": {
"stitchdb": {
"url": "https://stitchdb.com/platform/mcp?project_ref=<project-ref>&read_only=true",
"headers": { "Authorization": "Bearer ${env:STITCHDB_TOKEN}" }
}
}
}?project_ref= limits the connection to one project and ?read_only=true makes it read-only whatever the token allows (URL options). Remove headers entirely to sign in with OAuth in the browser instead.
On a read-only connection, the tools that change anything are not even listed. execute_sql runs one statement inside a READ ONLY transaction, with a 15-second timeout and at most 1,000 rows. The service_role key, the JWT secret and the database password are never returned by any tool.
This covers most of what you want Cursor to do with a database: read the schema while writing code, write a query, explain an empty result, find a slow endpoint in the logs, list tables without Row Level Security. Try:
- "Describe the
orderstable and write the TypeScript type for a row." - "Which tables have RLS disabled?"
- "Why does
GET /rest/v1/projectsreturn an empty array for signed-in users?"
#Step 2: writes through an agent key
When the agent needs to change data or the schema, do not switch to a read-write personal token on production. Create an agent key instead, in Studio › AI agents › Agent keys (agent keys). Each key has:
- A preset: Read-only, App developer (read and write rows of allowed tables) or Migrations (schema changes; drops, column removals and policy changes run only after approval).
- Allowed and denied tables, for example allow
public.*, denypublic.payments. - Max rows per statement: a statement that changes more rows is rolled back and refused or sent for approval.
- Its own Postgres role, so the grants are enforced by Postgres itself, whatever SQL the agent sends.
Use it as the MCP token:
{
"mcpServers": {
"stitchdb-agent": {
"url": "https://stitchdb.com/platform/mcp",
"headers": { "Authorization": "Bearer ${env:STITCHDB_AGENT_KEY}" }
}
}
}With an agent key, the server offers a smaller set of tools: execute_sql through the query firewall, list_tables, describe_table, get_approval, get_session, undo_session and check_exposure. Each MCP session is an undo session.
#What happens when Cursor tries something destructive
The firewall parses every statement with the real Postgres parser before it runs. Transaction control, SET ROLE, DO blocks, COPY, dynamic SQL and platform schemas are always refused, with a reason the agent can read:
{ "error": "Table public.payments is denied for this agent key",
"code": "agent_firewall", "rule": "table_denied", "statement_index": 0 }Risky statements are not refused but held. DELETE or UPDATE without a real WHERE (including WHERE true and WHERE 1 = 1), changes to more rows than the key allows, DROP, TRUNCATE, RLS and privilege changes return:
{ "status": "pending_approval", "approval_id": "6d0c…", "reasons": ["unbounded_write"],
"approve_url": "https://stitchdb.com/dashboard/project/<ref>/agents/approvals/6d0c…" }You get an e-mail, look at the exact statement in Studio › AI agents › Approvals, and approve or reject it. The agent polls with get_approval. Requests expire after 24 hours (approvals).
So the realistic failure, the agent "cleaning up" a table on the wrong database, turns into a pending request instead of a lost table.
#If Cursor already deleted your data
Work from the most targeted fix to the broadest.
- Undo the agent session. If the agent used an agent key, open Studio › AI agents › Sessions & undo, pick the session and choose Undo session…. It shows a dry run first. Rows the agent changed or deleted come back with their old values; dropped tables went to a trash schema and dropped columns kept their values, so those come back too. Rows someone else changed afterwards are skipped as conflicts (undo).
- Restore to a point in time. Point-in-time recovery is included on every paid StitchDB plan (7 days on Pro, 14 on Scale, 30 on Enterprise). Free has no backups, so keep your own exports there. On paid plans you can restore into a new branch first, to look before you touch production (backups).
- Check what else happened. Undo covers the database only. E-mails sent, HTTP calls made from SQL and queue messages already consumed are not reversed.
Then fix the cause: revoke the credential the agent used, and replace any connection string in .env that the agent could reach with a scoped key. The longer walkthrough is in undo what an AI agent changed.
#A checklist for Cursor and databases
- Keep production credentials out of the repository and out of
.envfiles the agent reads. - Default to a read-only MCP connection scoped to one project.
- Use a development project or a preview branch for agent writes when you can.
- Give write access through an agent key with denied tables and a row limit.
- Leave Cursor's tool approval on for anything that writes.
- Turn on RLS Guard so every new table the agent creates gets Row Level Security (exposure check).
- Ask the agent to run
check_exposureafter it creates tables or policies.
#FAQ
#Can Cursor connect to Postgres directly?
Yes, through an MCP server or by running psql in the terminal with a connection string. An MCP server with scoped, revocable tokens is the safer choice, because the credential decides what the agent can do, not the prompt.
#Where does Cursor store MCP settings?
In .cursor/mcp.json inside a project and ~/.cursor/mcp.json for all projects. Use ${env:NAME} in headers to keep tokens out of the file.
#Does read-only mode stop all writes?
On StitchDB, a ?read_only=true connection hides every tool that changes anything, and execute_sql runs inside a READ ONLY transaction, so Postgres itself refuses writes.
#Can an agent key bypass Row Level Security?
Only if you turn on Bypass Row Level Security for that key. By default it sees only rows your policies allow for its role.
Connect Cursor to Postgres with guardrails: follow the Cursor setup and create a free project.