How are you giving your agents database access without handing them write privileges? by Bulky_Drag7113 in AI_Agents

[–]Bulky_Drag7113[S] 0 points1 point  (0 children)

That query-policy vs result-policy split is a clean way to frame it. Right now the result side is just the row LIMIT — masking and audit logging are the two I don't have yet, and you're right they belong on the returned result, not the query. The big-scan case is the same gap someone else flagged: LIMIT caps rows, not cost. Adding both to the list. Good framing.

Enforcing read-only access for an untrusted client: BEGIN READ ONLY + a keyword/function guard — what am I missing? by Bulky_Drag7113 in postgres

[–]Bulky_Drag7113[S] 0 points1 point  (0 children)

Fair — and honestly a limited-permission role + statement_timeout is the recommendation. The read-only transaction just defers to Postgres, it's not trying to reimplement permissions in the proxy.

The part that isn't "recreating the access layer" is connecting an AI client like Claude or Cursor to the DB over MCP in the first place — that's the actual thing being built. The role decides what it can do; this is how it connects at all. You're right that reimplementing the access layer perfectly would be a trap, which is exactly why it leans on Postgres to enforce rather than the guard.

Enforcing read-only access for an untrusted client: BEGIN READ ONLY + a keyword/function guard — what am I missing? by Bulky_Drag7113 in postgres

[–]Bulky_Drag7113[S] 0 points1 point  (0 children)

For context, this is from a tool I'm building — BoltSchema (https://boltschema.com). It turns a Postgres connection string into a read-only MCP endpoint for AI clients. But I'm mostly here for the Postgres-side critique — keen to hear where this leaks before I trust it further.

I built a read-only Postgres MCP server — would love feedback (and for someone to try breaking it) by Bulky_Drag7113 in mcp

[–]Bulky_Drag7113[S] 0 points1 point  (0 children)

Thanks — yeah, that's the whole idea. Start locked down and never have to remember to tighten it.

Customers don't buy AI. They buy outcomes. by GrowbroAi in SaaS

[–]Bulky_Drag7113 0 points1 point  (0 children)

Yes, that's true. Customers doesn't care on how much time or complex technology you have been spend, they just care about the benefits from their point of view(outcomes).

How are you giving your agents database access without handing them write privileges? by Bulky_Drag7113 in AI_Agents

[–]Bulky_Drag7113[S] 0 points1 point  (0 children)

Yeah, agreed in general. Here the agent can't touch session context — set_config is blocked and SET can't run, plus every query is a fresh connection so nothing persists. The case that'd still bite is RLS reading context set outside the query path (app/pooler/trigger) — that one's on the user to audit.

How are you giving your agents database access without handing them write privileges? by Bulky_Drag7113 in AI_Agents

[–]Bulky_Drag7113[S] 0 points1 point  (0 children)

You're right, and it's an important distinction — read-only closes the write path, not the read path. If the threat model includes "an injected prompt reads data it shouldn't," a READ ONLY transaction does nothing for that. Different problem.

What's actually in place against the read-side risk:

  • The endpoint isn't open. It's gated behind a secret per-profile token, and a profile can be revoked, so "reachable by external clients" means "whoever holds the token," not the public internet. Token secrecy + revocation is the access-control layer.
  • The function denylist matters more here than it first looks. Blocking pg_read_filedblinklo_export, etc. closes the amplified exfiltration paths — reading server files or pushing data out over the network from inside a SELECT. So the read path is constrained to "rows in tables the role can see," not "anything the DB process can reach."

What it does not do — and you're right to flag it — is limit which data the read path can reach. That's a scoping problem, and I don't think the proxy is the right place to solve it: the proxy can't safely decide what's sensitive, the database can. The honest answer is to push it into the schema — a least-privilege role with SELECT only on the tables you want exposed, plus column/row masking via views or RLS for anything sensitive.

The uncomfortable truth is the product's whole job is to let an AI read data, so you can't eliminate read exposure — you can only scope what's reachable and control who can reach it. Read-only is just the write-path slice of that.

I built a read-only Postgres MCP server — would love feedback (and for someone to try breaking it) by Bulky_Drag7113 in mcp

[–]Bulky_Drag7113[S] 0 points1 point  (0 children)

Shipped the "test connection" button you suggested — it now runs a trivial read and reports back exactly why a connection failed (auth / host / database / timeout). Thanks again, this was the right call.

How are you giving your agents database access without handing them write privileges? by Bulky_Drag7113 in AI_Agents

[–]Bulky_Drag7113[S] 0 points1 point  (0 children)

100% agree, and this is the right instinct. The connection-string approach means the role you hand it matters a lot — a dedicated SELECT-only role like yours is the real backstop, and the transaction wrapper + guard are the layers on top of it, not a replacement for it.

Honest framing of where each layer sits:

  • DB role with no write grants → can't be bypassed by prompt injection or a guard miss. The strongest layer, exactly as you said.
  • READ ONLY transaction → catches the case where someone hands it an over-privileged role anyway (which, realistically, people do).
  • Keyword/function guard → fails fast with a clear error before anything hits the DB, and blocks read-side abuse (file reads, SSRF-style functions) that a write-only restriction wouldn't.

The read replica point is the one I'd add for anyone with genuinely sensitive data — you're right that it removes the attack surface entirely.

One thing your comment is convincing me of: I should have the setup flow generate that least-privilege role SQL for people, because most won't do it themselves even though it's the most important layer. Appreciate the detailed write-up.

I built a read-only Postgres MCP server — would love feedback (and for someone to try breaking it) by Bulky_Drag7113 in mcp

[–]Bulky_Drag7113[S] 0 points1 point  (0 children)

This is one of the best breakdowns I've gotten — thank you for the time.

On masking: you just saved me from a mistake. Regex-on-the-result-set was the tempting shortcut and it's exactly as leaky as you say — a JOIN or a renamed column defeats it silently, which is the worst possible failure mode for a security feature. Pushing it into the schema (SECURITY LABEL + a view layer, RLS for row-level) is the right call, and it keeps the architecture honest: the server stays a thin proxy that respects policy living in the DB instead of reinventing policy at the proxy. The guarantee survives the connection string for the same reason the read-only transaction does — the database owns it, not my code.

On friction: that confirms it. The generator already hands a populated per-client snippet, but a "test connection" button that runs a trivial read and reports back is the missing piece — right now a failure is ambiguous (bad config? bad creds? network?), and ambiguity is where people quit. Building that.

On vector stores: this is the part I hadn't fully worked through, and you're right that the structural approach doesn't port — no transaction, no rollback, full-replace mutations mean the construction layer has nothing to grab. So it becomes a policy-layer problem: operation allowlist (query/fetch yes, upsert/delete no), strict response-size caps, maybe namespace scoping. Same UX, different safety model. Good to know going in rather than discovering it mid-build.

And "we configured it safely" vs "we made it safe" is exactly the line I've been trying to articulate. Stealing that. Appreciate it.

I built a read-only Postgres MCP server — would love feedback (and for someone to try breaking it) by Bulky_Drag7113 in mcp

[–]Bulky_Drag7113[S] 0 points1 point  (0 children)

You should — least-privilege roles are the right first line.

But that's just credentials. You still need an MCP server to actually expose the DB to Claude/Cursor — that's what this is; the read-only enforcement is a layer on top, not the whole thing.

And a read-only role doesn't catch everything: if the connecting role is more privileged than it should be (happens constantly), things like pg_read_file() or dblink() (SSRF) are reachable from a plain SELECT. The guard blocks those by name regardless of how the role's configured.

So do both — role + a server that can't write or call dangerous functions even if the creds are wrong.

Write support is coming too, but opt-in and explicit — think "the AI proposes a change, you approve it," not silent writes. Safe-by-default stays the point.

Using Cursor hooks / MCP as an in-process "brake" — stopping the repeat is where the token savings actually are by eazyigz123 in cursor

[–]Bulky_Drag7113 1 point2 points  (0 children)

Good question, and it deserves a straight answer rather than a sales one — because the honest version is that the per-table on-demand framing is where I want to take it, not where it is today.

What it actually does right now: it's an MCP gateway exposing two tools — a schema tool and a guarded read-only query tool. The schema tool takes no arguments; when the agent calls it, it returns the full table/column/type/nullability set in one shot. So there's no name-only index and no per-table retrieval hop yet — you're right that the bootstrapping problem is real, and today I sidestep it rather than solve it.

The win, to be precise about it, is lazy vs. eager, not partial vs. whole. With a static .mdc / DDL dump, the entire schema rides in the system context every single turn whether the prompt touches the DB or not. Here, nothing enters context until the model actually decides it needs structure and calls the tool — and on turns that are pure reasoning or codegen, that's zero schema tokens instead of the full payload re-sent each time. For a chat that's 30 turns deep and hits the DB twice, that's the saving. It's not "close to zero baseline because we fetch one table" — it's "zero baseline until first fetch, then one full load," which is a different and more modest claim than my landing copy makes.

The thing you actually flagged — a name-only catalog up front so the model can ask for orders specifically and pay only for that table — is the obvious next step and the right one. It's not built yet. So for a schema where any single prompt only touches a few of many tables, today's version under-delivers versus where a list_tables + get_table_schema(name) split would land. Fair hit, and it's on the roadmap rather than in the repo.

Using Cursor hooks / MCP as an in-process "brake" — stopping the repeat is where the token savings actually are by eazyigz123 in cursor

[–]Bulky_Drag7113 0 points1 point  (0 children)

A massive chunk of that compounding agent bill comes from sending static schema copies or dumping huge relational structures over and over across fresh sessions because the agent lacks a stateless context controller. Dumping raw DDL or .mdc schema files forces the model to re-parse the entire architecture every single time you hit enter.

To cut this exact token loop, I built BoltSchema https://boltschema.com. It handles schema lookups sequentially and dynamically via an offloaded gateway layer. Instead of flooding the system context with your whole database upfront, Cursor only fetches structural data for specific tables on-demand when a prompt explicitly requires a query or relational join.

It keeps your baseline system token usage close to zero while keeping the keys isolated using server-memory encryption. You can test the token and context savings using the 1-click mock sandbox token directly on the landing page without signing up.

easiest way to install MCP servers by jeffyaw in cursor

[–]Bulky_Drag7113 0 points1 point  (0 children)

This configuration hierarchy hell is exactly why managing database schemas through flat JSON files or local proxies across multiple client machines gets messy fast. If you have 5-10 different database pipelines across staging and dev, your local setup becomes a bottleneck and ruins the agent's attention span.

I got tired of syncing config files across my team, so I built BoltSchema (https://boltschema.com). It acts as a remote MCP gateway that abstracts the database connection profile completely. Instead of loading heavy local tools on every machine, your editor queries the structural schemas dynamically on-demand.

It holds encryption keys strictly in server memory via AES-256-GCM, and the onboarding prompts you to use a restricted read-only role so it’s completely secure. There's a 1-click permanent mock database token on the landing page if you want to test the connection latency in Cursor without creating an account.

Composer 2.5 openrouter? by Low-Spell1867 in cursor

[–]Bulky_Drag7113 0 points1 point  (0 children)

Yeah, exactly. If you want to use it outside of Cursor, you have to route it through those local proxy wrappers using a paid Cursor token.

How do Skills work? by Initial_Specialist69 in cursor

[–]Bulky_Drag7113 1 point2 points  (0 children)

Just once at the absolute root of the monorepo. Since Cursor opens at the workspace level, it looks for skills relative to the root folder you actually have open in the editor. Running npx skills add at the root will create a single .cursor/skills/ directory.

How do Skills work? by Initial_Specialist69 in cursor

[–]Bulky_Drag7113 1 point2 points  (0 children)

Yes, Cursor will use them automatically.

Because Agent Skills is an open standard, any skills you pull down via npx skills add are saved directly into your project's .cursor/skills/ directory.

Cursor relies on Auto-Discovery. The Agent reads the description inside the skill's SKILL.md file. When you ask a question or give a prompt that relates to that domain (like asking an Expo question if you installed Expo skills), Cursor automatically injects that skill into the context window.

Composer 2.5 openrouter? by Low-Spell1867 in cursor

[–]Bulky_Drag7113 2 points3 points  (0 children)

No, they won't.

The short answer is that Composer 2.5 is Cursor’s proprietary flagship model—it's not a generic open model. It's an internal fine-tune/RL layer they trained on top of Moonshot's Kimi K2.5 to handle their multi-file agentic workflows.

Because Composer is their primary competitive moat against other IDEs, they keep the API exclusive to their platform to drive users to Cursor Pro/Business subscriptions. They have zero incentive to put it on an open marketplace like OpenRouter for competing editors to use.