feat: add server-level MCP auto-approval for wildcard tool servers

MCP servers configured with empty tools arrays (wildcard) now appear in the
auto-approval pill with a server-level toggle. When toggled, a server-level
approval key (mcp_server:<name>) is added to autoApprovedToolNames. The
sidecar matches this key against PermissionRequestMcp.ServerName to auto-
approve all tools from that server without needing individual tool names.

- Add buildMcpServerApprovalKey/isMcpServerApprovalKey helpers
- Create approval groups for all MCP servers including empty-tools ones
- Add serverApprovalKey to ApprovalToolGroup for server-level toggles
- Update sidecar RequiresToolCallApproval to check server-level keys
- Include server-level keys in listApprovalToolNames for pruning safety
- Add tests for both shared domain and sidecar approval matching

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
David Kaya
2026-03-28 18:34:19 +01:00
co-authored by Copilot
parent 53a08e0ed4
commit 3937904548
6 changed files with 210 additions and 25 deletions
+15 -4
View File
@@ -26,6 +26,8 @@ import { type PatternDefinition, type ReasoningEffort } from '@shared/domain/pat
import { isScratchpadProject, type ProjectRecord } from '@shared/domain/project';
import { resolveSessionToolingSelection, type SessionRecord } from '@shared/domain/session';
import {
groupApprovalToolsByProvider,
isMcpServerApprovalKey,
listApprovalToolDefinitions,
type RuntimeToolDefinition,
type SessionToolingSelection,
@@ -128,10 +130,19 @@ export function ChatPane({
),
[isApprovalOverridden, session.approvalSettings, pattern.approvalPolicy],
);
const effectiveAutoApprovedCount = useMemo(
() => approvalTools.filter((t) => effectiveAutoApproved.has(t.id)).length,
[approvalTools, effectiveAutoApproved],
);
const effectiveAutoApprovedCount = useMemo(() => {
const groups = groupApprovalToolsByProvider(approvalTools, toolingSettings);
let count = 0;
for (const group of groups) {
if (group.serverApprovalKey && effectiveAutoApproved.has(group.serverApprovalKey)) {
// Server-level approval: count as 1 approved group even with 0 declared tools
count += Math.max(group.tools.length, 1);
} else {
count += group.tools.filter((t) => effectiveAutoApproved.has(t.id)).length;
}
}
return count;
}, [approvalTools, effectiveAutoApproved, toolingSettings]);
useEffect(() => {
transcriptRef.current?.scrollTo({