Skip to content

feat(mcp)!: confirm destructive SQL via elicitation and rename costConfirmation option - #408

Draft
barryroodt wants to merge 5 commits into
mainfrom
feat/destructive-sql-confirmation
Draft

feat(mcp)!: confirm destructive SQL via elicitation and rename costConfirmation option#408
barryroodt wants to merge 5 commits into
mainfrom
feat/destructive-sql-confirmation

Conversation

@barryroodt

@barryroodt barryroodt commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

What kind of change does this PR introduce?

Feature + breaking change: confirm destructive SQL through elicitation and rename costConfirmation to confirmation.

What is the current behavior?

execute_sql and apply_migration run without confirmation. Cost confirmation for create_project and create_branch uses duplicated state handling under the costConfirmation server option. Since #411, form-capable clients no longer see get_cost/confirm_cost and create_* drops confirm_cost_id for them.

What is the new behavior?

When enabled, form-capable clients receive an action-only confirm_destructive prompt before destructive SQL (DROP, DELETE, TRUNCATE, ALTER TABLE ... DROP COLUMN, UPDATE without WHERE) runs:

This SQL includes destructive operations (DROP, DELETE, TRUNCATE or UPDATE without WHERE).
It may permanently remove data, tables, schemas or other objects.
Run it on project <ref>?

apply_migration ends with Apply the migration to project <ref>? instead. The signed requestState binds the project and query hash, plus the migration name for apply_migration.

Accept runs once from the signed state. Decline and cancel run nothing, changed SQL fails, and non-elicit responses re-prompt. Non-destructive SQL, read-only servers, stdio, and clients without per-request form elicitation keep their current behavior.

BREAKING: createSupabaseMcpServer renames costConfirmation to confirmation. enabledTools now accepts create_project, create_branch, execute_sql, and apply_migration. The #411 behavior for form-capable clients (legacy cost tools hidden, confirm_cost_id omitted) is unchanged under the new name.

Platform (#37717, #37722, #37898, AI-1164) adopts the published 0.12.0 with costConfirmation first. Once this lands, it takes a second package bump with the one-word rename. This keeps the cost elicitation ship date independent from this PR.

The existing cost flows now share checkConfirmationState, including zod validation for decoded state. Their behavior stays unchanged.

How to Review

  1. Server option and shared helper

    • packages/mcp-server-supabase/src/server.ts
    • packages/mcp-server-supabase/src/tools/confirmation.ts
    • packages/mcp-server-supabase/src/tools/account-tools.ts
    • packages/mcp-server-supabase/src/tools/branching-tools.ts
  2. SQL tool flow and detector

    • packages/mcp-server-supabase/src/tools/database-operation-tools.ts
    • packages/mcp-server-supabase/src/tools/destructive-sql.ts
  3. Tests

    • packages/mcp-server-supabase/src/server.test.ts
    • packages/mcp-server-supabase/src/tools/confirmation.test.ts
    • packages/mcp-server-supabase/src/tools/destructive-sql.test.ts

Review questions

  • Does the costConfirmation to confirmation rename cover the full public option surface?
  • Does the signed state bind every argument that can change the SQL operation?
  • Are the detector's false-positive and false-negative tradeoffs acceptable?
  • Do stdio, read-only, and capability-free paths remain unchanged?
  • Does apply_migration execute only from the signed state after acceptance?

Verification

  • pnpm --filter @supabase/mcp-server-supabase test src/server.test.ts -t "confirmation via elicitation" (32 passed)
  • Full src/server.test.ts (154 passed)
  • pnpm --filter @supabase/mcp-server-supabase test src/tools/confirmation.test.ts src/tools/destructive-sql.test.ts (20 passed)
  • pnpm --filter @supabase/mcp-server-supabase build (passed)
  • pnpm test:packed-platform-consumer (3 assertions passed)
  • pnpm exec biome ci . (passed)

Additional context

A follow-up PR will cover unconditional confirmation for delete_branch, reset_branch, merge_branch, and rebase_branch. Hosted enablement stays in platform, where the SQL tools must be added to enabledTools.

Confirmations remain reusable within the requestState TTL, carrying over the known limit from #391.

The detector and its cases come from Studio's SQL editor warning. It keeps the same known blind spots for nested block comments and fully dynamic SQL, with zero new dependencies.

Merged with main at 145be89 (#411 and the 0.12.0 release) in f0e5131.

AI-assisted; human review required.

…tion option

Extract the duplicated requestState decode/tool/args/decline/cancel/drift
block from create_project and create_branch into checkConfirmationState in
tools/confirmation.ts (renamed from cost-confirmation.ts), validate the
decoded state with zod safeParse, and rename the server option
costConfirmation -> confirmation ahead of non-cost confirmations.

BREAKING CHANGE: `createSupabaseMcpServer` option `costConfirmation` is now `confirmation`.
Port Studio's regex-based destructive SQL check (DROP, DELETE, TRUNCATE,
ALTER TABLE ... DROP COLUMN incl. EXECUTE-string variants, and UPDATE
without WHERE) with comment stripping into tools/destructive-sql.ts, along
with the Studio test cases that pin its false-positive and false-negative
contract. No new dependencies.
When the `confirmation` option enables them and the client declares form
elicitation, execute_sql and apply_migration detect destructive SQL (DROP,
DELETE, TRUNCATE, ALTER TABLE ... DROP COLUMN, UPDATE without WHERE, ported
from Studio) and require an accepted action-only elicitation, bound by a
signed requestState to the project and a hash of the query, before running.
Read-only servers, capability-free clients and non-destructive SQL are
unchanged.
@coveralls

coveralls commented Sep 3, 2026

Copy link
Copy Markdown

Coverage Report for CI Build 33888451258

Coverage increased (+0.5%) to 97.074%

Details

  • Coverage increased (+0.5%) from the base build.
  • Patch coverage: 2 uncovered changes across 1 file (382 of 384 lines covered, 99.48%).
  • No coverage regressions found.

Uncovered Changes

File Changed Covered %
packages/mcp-server-supabase/src/tools/confirmation.ts 141 139 98.58%
Total (6 files) 384 382 99.48%

Coverage Regressions

No coverage regressions found.


Coverage Stats

Coverage Status
Relevant Lines: 3613
Covered Lines: 3526
Line Coverage: 97.59%
Relevant Branches: 523
Covered Branches: 489
Branch Coverage: 93.5%
Branches in Coverage %: Yes
Coverage Strength: 61.6 hits per line

💛 - Coveralls

Brings in #411 (ctx-aware tools(), hidden get_cost/confirm_cost and
confirm_cost_id omission for form-capable clients) and the 0.12.0 release.
Conflicts resolved so #411's per-request tool shaping runs under the renamed
`confirmation` option; #408's tests migrated from setupFormCapable to
setupModern with explicit FORM_CAPABLE capabilities.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants