⚡ Sangsakala Proxy 🏠 Home
Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
fix: do not offer Agent writes the caller cannot enforce
The Agent volunteered "save this as a shared DeepSQL brain note" after
answering a metric, then 403'd for users without manage-content. Stamp
callerCapabilities on get_brain_context / list_connections, fail-close
save_brain_note before the POST, and stop SOUL.md from asking whether
to persist a definition the current account cannot write.

Co-authored-by: Venkat SF <venkatesh.sakamuri@stayflexi.com>
  • Loading branch information
cursoragent and venkateshsakamuri-lab committed Aug 22, 2026
commit f005607af768874c22e60a9ac0c3839d8b1fbffd
7 changes: 7 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,13 @@ The Agent tab must not inherit the admin MCP token. `/api/agent/session` mints a
resilience but only ever handled a *moved ref*, re-issuing the identical refused
request against a 429. A fallback that fails the same way as the thing it backs
up is not a fallback.
7. **Never offer a write the caller cannot enforce.** `SOUL.md` once asked
"Should everyone on this database see this?" after every good answer, so
Agent chat offered "save this as a shared DeepSQL brain note" to users
without `canManageContent` and then 403'd. `get_brain_context` now stamps
`callerCapabilities`; if `doNotOffer` includes `save_brain_note`, the
agent must not mention it. MCP `save_brain_note` also fail-closes before
the POST.

### Verification Anti-Patterns (do not repeat)

Expand Down
26 changes: 17 additions & 9 deletions agent/SOUL.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ You are **DeepSQL DBA**, an AI database performance assistant. You answer questi

**Lead with the answer.** You ground thoroughly with the tools, but you do **not** narrate that work in your reply. No "I checked / I joined…", no "Grounding used", no "Filters applied", no "Used:" footnotes, no column/filter walkthroughs. Answer with just the result — a number, a short ranked table, or a one-line sentence — and apply business rules silently. Tool steps already show what ran; don't repeat that in the bubble.

After the answer you may offer **one short follow-up question** (a single line) when it helps the user go deeper. Do not stack multiple offers. If the user wants the SQL, the tables, or how you got there, they'll ask, and then you show it. Admit uncertainty instead of guessing; prefer one correct answer over a verbose survey.
After the answer you may offer **one short follow-up question** (a single line) when it helps the user go deeper — a question they can answer, not an action they cannot take. Do not stack multiple offers. If the user wants the SQL, the tables, or how you got there, they'll ask, and then you show it. Admit uncertainty instead of guessing; prefer one correct answer over a verbose survey.

(Exception: the schema-consult flow in rule 8 — when proposing a table/migration you DO briefly state what already exists, because that's the point of the consult.)

Expand All @@ -24,25 +24,33 @@ After the answer you may offer **one short follow-up question** (a single line)

8. **Consult before you commit schema.** When the user says "add a table / track X / write a migration," STOP and run the brain consult (`get_brain_context` → `get_schema` → `list_business_rules` → `get_relationships` → `get_anti_patterns`). There is almost always an existing table or column to extend instead of duplicate. Narrate what you found before proposing DDL.

9. **Never offer an action the caller cannot enforce.** `get_brain_context` and
`list_connections` carry `callerCapabilities`. If `doNotOffer` lists an
action — especially `save_brain_note` — do not mention it, do not ask
"should I save this", and do not render a Yes button. Answering a metric
is not a request to persist it. Offering a write that 403s is a broken UX.

## Remembering things — two different places

There are TWO planes of memory. Route every "remember this" to the right one:
There are TWO planes of memory. Route a remember request only when the user
explicitly asked to remember / pin / save a definition:

1. **Company brain context (shared).** Durable facts about the *data* — what a
column means, a join path, a business definition, an accepted recommendation.
These ground EVERYONE's answers on this connection. Save them with
**`save_brain_note(connectionId, tableName, noteText, columnName?)`**.
- "Accept this recommendation" / "remember this for the team" → review with
**`list_brain_recommendations`**, then `save_brain_note` for each good one.
- This is **admin-only** (manage-content) and audited. If the user lacks
permission, the backend rejects it — say so, don't work around it.
**`save_brain_note(connectionId, tableName, noteText, columnName?)`**
**only if** `callerCapabilities.canWriteSharedBrainNotes` is true
(`list_connections.canManageContent`).
- If they asked to remember and they cannot write: tell them an admin with
manage-content on this connection has to save it. Do not call the tool.
2. **Individual preference (yours alone).** How *this* user likes answers
formatted, a private shortcut, a personal default. That is a **DeepSQL
skill** on the user's own profile — it does NOT belong in the shared brain.
Never push a personal preference into `save_brain_note`.

When unsure which plane a request belongs to, ask: "Should everyone on this
database see this, or just you?" Shared → brain note. Just you → DeepSQL skill.
Do not volunteer a shared-brain save after answering a data question. Do not
ask "should everyone on this database see this?" unless the user already asked
to remember something **and** they can write shared notes.

## Skills

Expand Down
2 changes: 1 addition & 1 deletion agent/skills/bi-query/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ Use when the user asks a question whose answer is **in the data** ("how many boo

6. **Run it** with `execute_sql(connectionId, sql, limit=…)`. Remember: default 100 rows, max 1000. For a total, `SELECT COUNT(*)` rather than counting a truncated result set.

7. **Answer only.** Reply with just the result — the number or a short ranked table — then optionally **one** short follow-up question. Apply business rules silently; do NOT append "Grounding used" / "Filters applied" / "Used:" / tool-narration / column-mapping sections. Only if the user asks how you got it do you show the tables, joins, and filters.
7. **Answer only.** Reply with just the result — the number or a short ranked table — then optionally **one** short follow-up question the user can actually act on. Do **not** offer to save a shared brain note, apply an index, or run DDL/DML unless `get_brain_context.callerCapabilities` says they can. Apply business rules silently; do NOT append "Grounding used" / "Filters applied" / "Used:" / tool-narration / column-mapping sections. Only if the user asks how you got it do you show the tables, joins, and filters.

## Guardrails

Expand Down
4 changes: 2 additions & 2 deletions docs/public/cli-and-mcp.md
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ The MCP surface mirrors the `deepsql` CLI for almost every read/diagnostic opera
| Tool | What it does |
|---|---|
| `list_connections` | List databases this user can see. **Always call first** — every other tool needs the UUID. |
| `get_current_user` | Authenticated user, role, and bound DeepSQL host. Use to know whether the caller is admin-capable before suggesting DDL. |
| `get_current_user` | Authenticated user, role, and `callerCapabilities`. Read `doNotOffer` before suggesting a write. |
| `show_connection` | One connection's saved config with all secret fields masked as `(set)`. |
| `test_connection` | Run the privilege report (+ SSH check) using the saved encrypted credentials. No plaintext crosses the agent's wire. |
| `reinit_connection_brain` | Trigger a fresh schema scan + brain re-embedding (use after the user reports stale schema knowledge). |
Expand All @@ -190,7 +190,7 @@ The MCP surface mirrors the `deepsql` CLI for almost every read/diagnostic opera
|---|---|
| `get_schema` | Fetch cached schema (tables, columns, FKs, types). Fast and cheap — call freely. |
| `get_database_objects` | Tables, views, functions, procedures. Use when you need DDL-level objects, not just columns. |
| `get_brain_context` | **Primary retrieval tool.** Returns the tables, columns, FKs, business rules, and anti-patterns most relevant to your question. **Call before generating any non-trivial SQL or DDL.** |
| `get_brain_context` | **Primary retrieval tool.** Returns the tables, columns, FKs, business rules, and anti-patterns most relevant to your question, plus `callerCapabilities`. **Call before generating any non-trivial SQL or DDL. Never offer an action in `doNotOffer`.** |
| `list_business_rules` | Active business rules and SQL guardrails. Honor these — they encode domain semantics (e.g. `always_filter_cancelled`). |
| `get_relationships` | Inferred + validated foreign keys with confidence scores. Many real-world DBs lack declared FKs; this fills the gap. |

Expand Down
4 changes: 2 additions & 2 deletions mcp/CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,15 +60,15 @@ in this version; ask before mid-session admin work.

| Tool | Purpose |
|---|---|
| `list_connections` | List databases the user has access to. Always call this first — you need IDs for everything else. |
| `list_connections` | List databases the user has access to. Always call this first — you need IDs for everything else. Each row includes `canManageContent`; if false, do not offer writes on that connection. |
| `get_schema` | Cached schema metadata (tables, columns, FKs, types). Cheap and fast — call freely. |
| `get_database_objects` | Tables, views, functions, procedures. Use when you need DDL-level objects, not just columns. |
| `get_brain_context` | **Your primary retrieval tool.** Given a question, returns the tables/columns/FKs/training docs/business rules/anti-patterns most relevant to it. |
| `list_business_rules` | Active business rules and SQL guardrails. Honor these — they encode domain semantics. |
| `get_relationships` | Inferred + validated foreign keys with confidence scores. Many real DBs lack declared FKs; this fills the gap. |
| `get_anti_patterns` | Schema-level (`kind=table`) or query-level (`kind=query`) anti-patterns. |
| `list_brain_recommendations` | The brain's AI-proposed notes to review for a connection (priority, reason, indicators, suggested prompt). The company-context review queue. |
| `save_brain_note` | **Accept/save a fact into the shared brain** — grounds every future answer for the connection. TABLE- or COLUMN-scoped. Admin (manage-content), audited. Personal preferences belong in a DeepSQL skill, not here. |
| `save_brain_note` | **Accept/save a fact into the shared brain** — only when the user asked to remember it AND `callerCapabilities.canWriteSharedBrainNotes` is true. Never volunteer after answering a question. |
| `list_brain_notes` | Knowledge already saved to the brain (filter by table/column; can be thousands). |
| `analyze_slow_queries` | Recent slow queries with fingerprints, durations, examples. Read-only; doesn't trigger new work. |
| `get_slow_query_timeline` | Day-by-day timeline for one query from the 30-day analytics store — call count, mean/max time, regression factor per day. Identify the query by its fingerprint (the `queryId` from `analyze_slow_queries`). Answers "is this query getting slower". |
Expand Down
6 changes: 3 additions & 3 deletions mcp/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -126,11 +126,11 @@ per-user admin ops (`users`/`access`/`permissions`).
|---|---|
| `get_schema` | Cached schema metadata (tables, columns, FKs, types) |
| `get_database_objects` | Tables, views, functions, procedures |
| `get_brain_context` | Retrieval brain: tables/columns/FKs/training docs/rules for a question |
| `get_brain_context` | Retrieval brain plus `callerCapabilities` (what this user can enforce) |
| `list_business_rules` | Active business rules and SQL guardrails for a connection |
| `get_relationships` | Inferred + validated foreign keys with confidence scores |
| `list_brain_recommendations` | The brain's AI-proposed notes to review (company-context queue) |
| `save_brain_note` | Accept/save a fact into the shared brain (admin; grounds future answers) |
| `list_brain_recommendations` | The brain's AI-proposed notes to review (do not offer accept unless caller can write) |
| `save_brain_note` | Save a shared brain fact only when the user asked and `canManageContent` is true |
| `list_brain_notes` | Knowledge already saved to the brain (filterable) |

### Anti-patterns + daily digest (4)
Expand Down
Loading
Loading