{"slug": "let-claude-code-and-cursor-query-your-database-safely-the-mcp-setup", "title": "Let Claude Code and Cursor query your database (safely) — the MCP setup", "summary": "Termal.in has introduced a setup that lets AI coding agents like Claude Code and Cursor safely query databases through a local MCP server, keeping credentials in the app's vault and enabling read-only access by default. The configuration requires enabling the 'Let agents query your databases' setting and registering the termalin-mcp binary with either tool, after which agents can list connections, inspect tables and schemas, and run queries with a default 200-row cap.", "body_md": "An agent that can read your database answers questions you'd otherwise open a SQL editor for. \"Why did signups drop on Tuesday?\" \"Which orders have been stuck in `pending` for more than an hour?\" \"What's actually in the `events` table?\" With a query capability the model answers in seconds; without one it guesses. The useful part isn't the SQL — Claude Code and Cursor both write SQL fine — it's giving the agent a way to *run* a read without handing it your credentials. That handoff is the whole friction, and it's what this post walks through for both tools.\n\nWe've written separately about [why pasting a database connection string into an agent is the move you can't undo](https://termal.in/blog/give-an-ai-agent-database-access/) — the credential is bearer access, it can write and drop as well as read, and once it's passed through a model's context you can't un-share it. Read that for the safety model; here we take it as settled and get the wiring done. The short version: the client holds the credential, the agent gets a scoped, read-only-by-default *ask*.\n\nOne expectation to set: for Claude Code and Cursor, database access runs through the **local** stdio MCP server, so the agent needs to be on the same machine as your unlocked desktop app. (There's a hosted path too — the connectors for claude.ai and ChatGPT can query an *enrolled* server's databases passwordless through `termal.in/api/v1/mcp`, using the server's own local database trust — but for a local IDE agent, the local server is the one you want.)\n\nThis is off by default, and deliberately so. Open **Settings → Agent** and enable **\"Let agents query your databases.\"** The subtitle spells out the posture: *off by default, and stays read-only unless a connection grants full access.* Until you flip it, every database tool returns `database access for agents is off — enable it in Settings → Agent`, full stop.\n\nTwo defaults are worth internalizing before you go further:\n\nThe credential itself never moves: the connection's host, port and password stay in the app's vault, and the agent only gets to *ask the running app* to run a query and hand back rows. There's no connection string in a config file for the agent — or a prompt injection — to read.\n\n**Claude Code** — one command:\n\n```\nclaude mcp add termalin -- <path>/termalin-mcp\n```\n\nReplace `<path>` with wherever the `termalin-mcp` binary lives (the app can point you at it). Restart Claude Code and it picks up the tools.\n\n**Cursor** — Cursor reads MCP servers from a JSON file: `.cursor/mcp.json` in a project root for that project only, or `~/.cursor/mcp.json` to make the server available everywhere. For a local stdio server the entry is a command and its arguments:\n\n```\n{\n  \"mcpServers\": {\n    \"termalin\": {\n      \"command\": \"<path>/termalin-mcp\",\n      \"args\": []\n    }\n  }\n}\n```\n\nThe global file is the sensible choice — database access isn't a per-repo concern. Restart Cursor (or reload the MCP list in its settings) and confirm the server shows as running. This is the same registration the [Cursor SSH walkthrough](https://termal.in/blog/cursor-ssh-access-for-agents/) and the [Claude Code one](https://termal.in/blog/connect-claude-code-to-your-server-over-ssh/) use — one server, and it now carries the database tools alongside the SSH ones.\n\nWith the server registered and the switch on, the agent sees four database tools:\n\n```\ndata_list     # your saved connections: id, name, engine, and whether the agent may write\ndata_tables   # tables / collections / measurements for a connection, with row counts\ndata_schema   # one table's columns: name, type, nullable, primary key\ndata_query    # run a query in the connection's own language; default cap 200 rows\n```\n\nThey mirror how you'd explore a database by hand: list what's connected, see the tables, describe a table, run the query. `data_query` speaks whatever the connection speaks — SQL for the relational engines and ClickHouse, Cypher for Neo4j, a Redis command, a `collection {filter}` for MongoDB — and returns rows up to a cap you can raise per call (`maxRows`), defaulting to **200** so an agent can't accidentally pull a million rows into its context.\n\nNow give it a question you'd actually ask, not a demo query:\n\nSignups dropped on Tuesday according to the dashboard. Using the analytics database, figure out what happened.\n\nWatch the agent work the tools in order:\n\n`data_list`` analytics` connection, sees the engine is Postgres, and notes it's read-only for the agent.`data_tables`` signups` and `signup_events`, and reads the row counts to know what's worth querying.` data_schema``created_at`, `source`, `status`, …) and now knows how to slice by day and by source.` data_query``SELECT date_trunc('day', created_at) AS day, source, count(*) FROM signups WHERE created_at >= now() - interval '10 days' GROUP BY 1, 2 ORDER BY 1` and reads the rows back.\nThe rows tell the story — maybe one `source` went to zero on Tuesday (a broken referral link), maybe the total held but `status = 'failed'` spiked (a provider outage). Either way the agent formed its query from the real schema, not a guess, and never held anything more than the ability to ask. If a result truncates at the 200-row cap, it says so and narrows the query rather than pushing for more.\n\nBrief, because the [safety post](https://termal.in/blog/give-an-ai-agent-database-access/) covers it in full — but the defaults you're relying on:\n\n`SELECT`, `SHOW`, `DESCRIBE`, `EXPLAIN`, `WITH`, `PRAGMA`. On Redis, only read commands; on MongoDB, an aggregation with `$out`/`$merge` is blocked; on Neo4j, `CREATE`/` MERGE`/` DELETE`/` SET`/` DROP` are blocked. A confused or hijacked model gets a read, not a For anything sensitive, do both: point the Data connection at a read replica or a least-privilege database role *and* leave the agent read-only. The client-side gate catches mistakes; the database grant sets the ceiling.\n\nMost \"connect your agent to a database\" guides end at pasting a connection string into a config file, which quietly grants an agent the same write-and-drop power your app has. Termalin's Data client inverts that: the app is the custodian, the agent gets four scoped tools driven through the running window, and the whole capability is read-only until you decide otherwise and off with a single switch. It's the same custody idea we apply to [SSH access for agents](https://termal.in/blog/connect-claude-code-to-your-server-over-ssh/) and to [what an MCP server actually is](https://termal.in/blog/what-is-an-mcp-server/) — a database is just the version where a bad write is measured in rows. Start with one connection, keep it read-only, and ask something real before you consider widening anything.\n\n*Termalin is a free, cross-platform SSH and [database client](https://termal.in/database-client/) with a built-in MCP server and a per-connection agent policy — [download it](https://termal.in/download/), or read how it [handles credentials safely](https://termal.in/security/).*", "url": "https://wpnews.pro/news/let-claude-code-and-cursor-query-your-database-safely-the-mcp-setup", "canonical_source": "https://dev.to/wolfhound1995/let-claude-code-and-cursor-query-your-database-safely-the-mcp-setup-1376", "published_at": "2026-09-08 17:20:07+00:00", "updated_at": "2026-09-08 17:25:40.594499+00:00", "lang": "en", "topics": ["developer-tools", "ai-agents", "ai-tools"], "entities": ["Termal.in", "Claude Code", "Cursor", "MCP"], "alternates": {"html": "https://wpnews.pro/news/let-claude-code-and-cursor-query-your-database-safely-the-mcp-setup", "markdown": "https://wpnews.pro/news/let-claude-code-and-cursor-query-your-database-safely-the-mcp-setup.md", "text": "https://wpnews.pro/news/let-claude-code-and-cursor-query-your-database-safely-the-mcp-setup.txt", "jsonld": "https://wpnews.pro/news/let-claude-code-and-cursor-query-your-database-safely-the-mcp-setup.jsonld"}}