Skip to content

feat: MCP permission model + unified policy enforcement + settings UI (Phase 4) - #129

Merged
Blankll merged 11 commits into
masterfrom
feat/mcp-permissions-ui
Aug 3, 2026
Merged

feat: MCP permission model + unified policy enforcement + settings UI (Phase 4)#129
Blankll merged 11 commits into
masterfrom
feat/mcp-permissions-ui

Conversation

@Blankll

@Blankll Blankll commented Aug 2, 2026

Copy link
Copy Markdown
Member

Summary

Mirror of dockit #473 with sqlkit deltas (port 9121, db_type key, pages.settings.mcp.* i18n namespace).

Phase 4 of the Data Studio MCP permission model for dockit: server-side policy enforcement in the MCP bridge AND the built-in agent loop, plus the permission settings UI.

Changes

1. Bridge: McpPolicy enforcement + flat /tools (mcp_bridge.rs)

  • Bump data-studio-agent → rev 49f89a4 (PolicyAction + confirm_destructive)
  • McpConfig.policy field (serde default, backward compatible with existing mcp-config.json)
  • Pure check_policy() gate: allowlist → read-only override → confirm_destructive → mode; /invoke 403 with clear message
  • invoke_with_policy() extracted for testability (no AppHandle needed in unit tests)
  • /tools returns flat [{name, description, inputSchema, metadata:{riskLevel, requiredPermission}}] filtered by policy (was OpenAI-shaped + unfiltered)
  • get_mcp_status returns policy; save_mcp_config accepts policy arg (preserves existing when omitted)

2. Built-in agent loop: server-side Deny (agent_adapters.rs)

  • run_agent_loop builds a should_deny closure from McpPolicy (mcp-config.json) and passes it to the crate loop
  • Policy-denied tools get status denied + error result, never reach execution or the confirmation dialog — no frontend round-trip
  • The MCP bridge and built-in loop now enforce the same policy (single policy, two enforcers); previously the built-in loop had zero server-side enforcement

3. Settings UI (mcp-bridge.vue)

  • Permission Mode RadioGroup — Read Only / Data Read-Write / Full Access
  • Confirm Destructive Switch — gated on Full Access
  • Connection Allowlist checkbox list (from connection store)
  • Per-connection Read-only switch list
  • snake_case policy round-trip via save_mcp_config
  • 15 new setting.mcp.* i18n keys (enUS + zhCN)

4. Review fixes (97495da)

  • Policy hot-swap, no restart: new save_mcp_policy command persists + hot-swaps the policy into the running bridge via a shared Arc<RwLock<McpPolicy>> (McpServerHandle.policy aliased by BridgeState). Permission toggles no longer shut down the bridge, so in-flight LLM requests are never interrupted.
  • Restart dead-end fixed: save_mcp_config gained a restart param that forces a (re)start even when auto-start is off; config-only saves no longer shut down a running bridge. Restart button re-enabled after failures (was permanently disabled).
  • Allowlist bypass closed: /invoke denies non-AppLocal capabilities invoked without a connection_id when the allowlist is non-empty (mirrors the agent-loop guard), returning an actionable 403; /tools now filters the advertised connections list to allowlisted IDs.
  • Agent loop stale policy: should_deny reloads McpPolicy per tool decision instead of snapshotting at loop start — mid-flight Settings changes apply to the next tool call.
  • UI double-fire guard: mode radio clicks (item + wrapper) saved twice → one save.
  • 3 new unit tests: allowlist-missing-connection reason, invoke denial, /tools connection filtering.

Architecture (three layers)

Layer What Where
L1 Tool risk (RiskLevel) capability registry (static)
L2 Server policy (McpPolicy.decide()) MCP bridge + built-in loop (both enforce Deny)
L3 Client session policy (Ask/Auto) frontend — unchanged

Permission model

Mode Safe Elevated Destructive
ReadOnly
DataReadWrite
FullAccess ✅ (requires confirm_destructive=true)

Allowlist (empty = all) + per-connection read-only override layer on top.

Verification

  • cargo test — 324 passed (304 lib + integration)
  • cargo fmt / cargo clippy clean on changed files; cargo build green
  • npm run build / lint:check / npm test (455) green

Part of geek-fun/data-studio-agent#9 / #10. Companion PR: data-studio-agent #20 (crate), sqlkit #129.

Blankll added 3 commits August 2, 2026 22:40
- bump data-studio-agent to feat/mcp-permissions (confirm_destructive)
- McpConfig.policy field (serde default, backward compatible)
- pure check_policy() gates /invoke: mode → allowlist → read-only override → confirm_destructive
- invoke_with_policy() extracted for testability
- /tools returns flat [{name, description, inputSchema, metadata:{riskLevel, requiredPermission}}] filtered by policy
- get_mcp_status returns policy; save_mcp_config accepts policy arg (preserves existing on None)
…connection overrides

- Permission Mode RadioGroup (ReadOnly/DataReadWrite/FullAccess)
- Confirm Destructive Switch (gated on FullAccess)
- Connection allowlist Checkbox list (from connectionStore)
- Per-connection read-only Switch list
- snake_case policy round-trip via save_mcp_config
- i18n: 15 new pages.settings.mcp.* keys (en/zh)
- bump data-studio-agent to 49f89a4 (PolicyAction + should_deny param)
- run_agent_loop passes should_deny closure: loads McpPolicy from mcp-config,
  denies tools where policy.allows(risk, conn_id) is false
- Deny short-circuits before confirmation await (no frontend round-trip)
@Blankll Blankll changed the title feat: MCP permission model enforcement + settings UI (Phase 4) feat: MCP permission model + unified policy enforcement + settings UI (Phase 4) Aug 2, 2026
Blankll added 8 commits August 3, 2026 00:44
… UX, confirm_destructive defaults

- agent_adapters: deny DB capabilities when allowlist set but connection_id
  omitted (fallback connection would bypass the allowlist); AppLocal tools exempt
- mcp-bridge.vue: default confirmDestructive true (match Rust default)
- onModeChange no longer resets confirm_destructive when leaving FullAccess
- allowlist: explicit enable switch — empty state shows disabled checkboxes
…dback

- bump data-studio-agent to 6fae4e5 (default DataReadWrite, McpAction)
- tests updated for DataReadWrite default (rejects Destructive)
- UI: Font Weight card-style permission mode (single row + desc),
  Port + Auto-start in one card, Confirm Destructive inside mode card
  (shown only for FullAccess, defaults on)
- Connection Access merged scrollable table with per-connection
  Read/Write/Delete actions (McpAction)
- Restart Bridge: phase feedback (shutting-down/starting), spinner,
  disabled during restart, 60s timeout, success/error toasts
- i18n: new keys (en/zh)
- tools_payload appends Neon-style 'MCP policy notice' to surviving tool
  descriptions when any capability class is gated (mode / confirm toggle),
  so the agent learns gated capabilities exist and how to lift the gate
- check_policy errors now carry deny_reason() guidance the agent can
  relay to the user (requires X mode / enable Confirm Destructive /
  connection not in allowlist)
- bump data-studio-agent to d492b96 for deny_reason()/policy_notice()
- tests: notice presence/absence, guidance in deny error
…order

- cn() now uses tailwind-merge (was clsx-only) — resolves grid/flex
  conflicts in shadcn components. Permission Mode cards render in one
  row instead of stacking vertically
- Port input shows default port 9121 as placeholder when unset
- Settings tab order: ... → AI → MCP → JRE → About (MCP right after LLM)
…mcp-v0.1.4

Switch from dev branch feat/mcp-permissions to the published v0.1.4
release (038f6f6) — same tree as master with full Phase 4 code.
data-studio-mcp-v0.1.4 is the npm package for code agents; the desktop
clients use the Rust crate, now released as v0.1.5 (f4871a4).
…tart dead-end

- save_mcp_policy: persist + hot-swap policy into the running bridge via a
  shared Arc<RwLock<McpPolicy>>, so permission toggles no longer restart it
- save_mcp_config: new restart param forces a start even when auto_start is
  off; no longer shuts down a running bridge on config-only saves
- allowlist bypass: /invoke denies non-AppLocal capabilities without a
  connection_id when the allowlist is set; /tools filters connections to
  allowlisted IDs
- agent loop: reload McpPolicy per tool decision instead of snapshotting at
  loop start
- settings UI: onModeChange double-fire guard, restart button re-enabled on
  failure, allowlist filter drops null ids
@Blankll
Blankll merged commit 3edcbea into master Aug 3, 2026
3 checks passed
@Blankll
Blankll deleted the feat/mcp-permissions-ui branch August 3, 2026 03:57
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.

1 participant