mirror of
https://github.com/davidkaya/aryx.git
synced 2026-08-06 19:58:43 +02:00
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:
@@ -1,8 +1,10 @@
|
||||
import { describe, expect, test } from 'bun:test';
|
||||
|
||||
import {
|
||||
buildMcpServerApprovalKey,
|
||||
groupApprovalToolsByProvider,
|
||||
listApprovalToolDefinitions,
|
||||
listApprovalToolNames,
|
||||
normalizeWorkspaceSettings,
|
||||
resolveProjectToolingSettings,
|
||||
resolveToolLabel,
|
||||
@@ -441,7 +443,7 @@ describe('groupApprovalToolsByProvider', () => {
|
||||
expect(builtinGroups[0].tools.length).toBe(5);
|
||||
});
|
||||
|
||||
test('multi-provider tools go into first provider group', () => {
|
||||
test('multi-provider tools go into first provider group, second provider gets empty group', () => {
|
||||
const tooling = makeTooling([
|
||||
makeMcpServer('git-a', 'Git A', ['git.status']),
|
||||
makeMcpServer('git-b', 'Git B', ['git.status']),
|
||||
@@ -450,9 +452,13 @@ describe('groupApprovalToolsByProvider', () => {
|
||||
const groups = groupApprovalToolsByProvider(tools, tooling);
|
||||
|
||||
const mcpGroups = groups.filter((g) => g.kind === 'mcp');
|
||||
expect(mcpGroups.length).toBe(1);
|
||||
expect(mcpGroups[0].label).toBe('Git A');
|
||||
expect(mcpGroups[0].tools[0].providerNames).toEqual(['Git A', 'Git B']);
|
||||
expect(mcpGroups.length).toBe(2);
|
||||
const gitA = mcpGroups.find((g) => g.label === 'Git A');
|
||||
expect(gitA).toBeDefined();
|
||||
expect(gitA!.tools[0].providerNames).toEqual(['Git A', 'Git B']);
|
||||
const gitB = mcpGroups.find((g) => g.label === 'Git B');
|
||||
expect(gitB).toBeDefined();
|
||||
expect(gitB!.tools.length).toBe(0);
|
||||
});
|
||||
|
||||
test('sorts builtin first, then MCP by name, then LSP by name', () => {
|
||||
@@ -472,4 +478,61 @@ describe('groupApprovalToolsByProvider', () => {
|
||||
const mcpLabels = groups.filter((g) => g.kind === 'mcp').map((g) => g.label);
|
||||
expect(mcpLabels).toEqual(['Alpha', 'Zebra']);
|
||||
});
|
||||
|
||||
test('creates groups with serverApprovalKey for MCP servers', () => {
|
||||
const tooling = makeTooling([
|
||||
makeMcpServer('git', 'Git MCP', ['git.status']),
|
||||
]);
|
||||
const tools = listApprovalToolDefinitions(tooling);
|
||||
const groups = groupApprovalToolsByProvider(tools, tooling);
|
||||
|
||||
const mcpGroup = groups.find((g) => g.kind === 'mcp');
|
||||
expect(mcpGroup).toBeDefined();
|
||||
expect(mcpGroup!.serverApprovalKey).toBe('mcp_server:Git MCP');
|
||||
});
|
||||
|
||||
test('creates groups for MCP servers with empty tools array', () => {
|
||||
const tooling = makeTooling([
|
||||
makeMcpServer('empty', 'Empty Server', []),
|
||||
]);
|
||||
const tools = listApprovalToolDefinitions(tooling);
|
||||
const groups = groupApprovalToolsByProvider(tools, tooling);
|
||||
|
||||
const mcpGroups = groups.filter((g) => g.kind === 'mcp');
|
||||
expect(mcpGroups.length).toBe(1);
|
||||
expect(mcpGroups[0].label).toBe('Empty Server');
|
||||
expect(mcpGroups[0].tools.length).toBe(0);
|
||||
expect(mcpGroups[0].serverApprovalKey).toBe('mcp_server:Empty Server');
|
||||
});
|
||||
|
||||
test('builtin groups do not have serverApprovalKey', () => {
|
||||
const tooling = makeTooling();
|
||||
const tools = listApprovalToolDefinitions(tooling);
|
||||
const groups = groupApprovalToolsByProvider(tools, tooling);
|
||||
|
||||
const builtinGroup = groups.find((g) => g.kind === 'builtin');
|
||||
expect(builtinGroup!.serverApprovalKey).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe('server-level approval keys', () => {
|
||||
test('buildMcpServerApprovalKey produces mcp_server: prefixed key', () => {
|
||||
expect(buildMcpServerApprovalKey('My Server')).toBe('mcp_server:My Server');
|
||||
expect(buildMcpServerApprovalKey('git')).toBe('mcp_server:git');
|
||||
});
|
||||
|
||||
test('listApprovalToolNames includes server-level keys', () => {
|
||||
const tooling: WorkspaceToolingSettings = {
|
||||
mcpServers: [{
|
||||
id: 'git', name: 'Git MCP', transport: 'local', command: 'node', args: [],
|
||||
tools: [], createdAt: '2026-03-28T00:00:00.000Z', updatedAt: '2026-03-28T00:00:00.000Z',
|
||||
}],
|
||||
lspProfiles: [],
|
||||
};
|
||||
const names = listApprovalToolNames(tooling);
|
||||
expect(names).toContain('mcp_server:Git MCP');
|
||||
// Also includes builtins
|
||||
expect(names).toContain('read');
|
||||
expect(names).toContain('shell');
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user