feat: MCP permission model + unified policy enforcement + settings UI (Phase 4) - #129
Merged
Conversation
- 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)
… 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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
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)data-studio-agent→ rev49f89a4(PolicyAction + confirm_destructive)McpConfig.policyfield (serde default, backward compatible with existingmcp-config.json)check_policy()gate: allowlist → read-only override → confirm_destructive → mode;/invoke403 with clear messageinvoke_with_policy()extracted for testability (no AppHandle needed in unit tests)/toolsreturns flat[{name, description, inputSchema, metadata:{riskLevel, requiredPermission}}]filtered by policy (was OpenAI-shaped + unfiltered)get_mcp_statusreturnspolicy;save_mcp_configacceptspolicyarg (preserves existing when omitted)2. Built-in agent loop: server-side Deny (
agent_adapters.rs)run_agent_loopbuilds ashould_denyclosure fromMcpPolicy(mcp-config.json) and passes it to the crate loopdenied+ error result, never reach execution or the confirmation dialog — no frontend round-trip3. Settings UI (
mcp-bridge.vue)save_mcp_configsetting.mcp.*i18n keys (enUS + zhCN)4. Review fixes (
97495da)save_mcp_policycommand persists + hot-swaps the policy into the running bridge via a sharedArc<RwLock<McpPolicy>>(McpServerHandle.policyaliased byBridgeState). Permission toggles no longer shut down the bridge, so in-flight LLM requests are never interrupted.save_mcp_configgained arestartparam 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)./invokedenies non-AppLocal capabilities invoked without aconnection_idwhen the allowlist is non-empty (mirrors the agent-loop guard), returning an actionable 403;/toolsnow filters the advertisedconnectionslist to allowlisted IDs.should_denyreloadsMcpPolicyper tool decision instead of snapshotting at loop start — mid-flight Settings changes apply to the next tool call./toolsconnection filtering.Architecture (three layers)
RiskLevel)McpPolicy.decide())Permission model
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 clippyclean on changed files;cargo buildgreennpm run build/lint:check/npm test(455) greenPart of geek-fun/data-studio-agent#9 / #10. Companion PR: data-studio-agent #20 (crate), sqlkit #129.