Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
214 changes: 214 additions & 0 deletions src/core/auto-approval/__tests__/index.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,214 @@
import { describe, it, expect, vi } from "vitest"
import { checkAutoApproval } from "../index"
import type { ExtensionState } from "../../../shared/ExtensionMessage"
import type { McpServerUse } from "@roo-code/types"

describe("checkAutoApproval", () => {
describe("MCP auto-approval", () => {
it("should approve MCP tool when alwaysAllowMcp is enabled", async () => {
const mcpServerUse: McpServerUse = {
type: "use_mcp_tool",
serverName: "test-server",
toolName: "test-tool",
}

const state: Partial<ExtensionState> = {
autoApprovalEnabled: true,
alwaysAllowMcp: true,
mcpServers: [
{
name: "test-server",
config: "test-config",
status: "connected",
tools: [
{
name: "test-tool",
alwaysAllow: false, // Tool does NOT have alwaysAllow flag
},
],
},
] as any,
}

const result = await checkAutoApproval({
state: state as ExtensionState,
ask: "use_mcp_server",
text: JSON.stringify(mcpServerUse),
})

// Should approve because alwaysAllowMcp is true, regardless of tool's alwaysAllow flag
expect(result).toEqual({ decision: "approve" })
})

it("should approve MCP tool when tool has alwaysAllow flag even if alwaysAllowMcp is false", async () => {
const mcpServerUse: McpServerUse = {
type: "use_mcp_tool",
serverName: "test-server",
toolName: "test-tool",
}

const state: Partial<ExtensionState> = {
autoApprovalEnabled: true,
alwaysAllowMcp: false, // Global MCP auto-approval is disabled
mcpServers: [
{
name: "test-server",
config: "test-config",
status: "connected",
tools: [
{
name: "test-tool",
alwaysAllow: true, // Tool has individual alwaysAllow flag
},
],
},
] as any,
}

const result = await checkAutoApproval({
state: state as ExtensionState,
ask: "use_mcp_server",
text: JSON.stringify(mcpServerUse),
})

// Should approve because tool has alwaysAllow flag
expect(result).toEqual({ decision: "approve" })
})

it("should ask for approval when neither alwaysAllowMcp nor tool alwaysAllow is set", async () => {
const mcpServerUse: McpServerUse = {
type: "use_mcp_tool",
serverName: "test-server",
toolName: "test-tool",
}

const state: Partial<ExtensionState> = {
autoApprovalEnabled: true,
alwaysAllowMcp: false,
mcpServers: [
{
name: "test-server",
config: "test-config",
status: "connected",
tools: [
{
name: "test-tool",
alwaysAllow: false,
},
],
},
] as any,
}

const result = await checkAutoApproval({
state: state as ExtensionState,
ask: "use_mcp_server",
text: JSON.stringify(mcpServerUse),
})

// Should ask because neither condition is met
expect(result).toEqual({ decision: "ask" })
})

it("should approve MCP resource access when alwaysAllowMcp is enabled", async () => {
const mcpServerUse: McpServerUse = {
type: "access_mcp_resource",
serverName: "test-server",
uri: "test://resource",
}

const state: Partial<ExtensionState> = {
autoApprovalEnabled: true,
alwaysAllowMcp: true,
}

const result = await checkAutoApproval({
state: state as ExtensionState,
ask: "use_mcp_server",
text: JSON.stringify(mcpServerUse),
})

// Should approve resource access when alwaysAllowMcp is true
expect(result).toEqual({ decision: "approve" })
})

it("should ask for MCP resource access when alwaysAllowMcp is disabled", async () => {
const mcpServerUse: McpServerUse = {
type: "access_mcp_resource",
serverName: "test-server",
uri: "test://resource",
}

const state: Partial<ExtensionState> = {
autoApprovalEnabled: true,
alwaysAllowMcp: false,
}

const result = await checkAutoApproval({
state: state as ExtensionState,
ask: "use_mcp_server",
text: JSON.stringify(mcpServerUse),
})

// Should ask for resource access when alwaysAllowMcp is false
expect(result).toEqual({ decision: "ask" })
})

it("should handle missing text gracefully", async () => {
const state: Partial<ExtensionState> = {
autoApprovalEnabled: true,
alwaysAllowMcp: true,
}

const result = await checkAutoApproval({
state: state as ExtensionState,
ask: "use_mcp_server",
text: undefined,
})

expect(result).toEqual({ decision: "ask" })
})

it("should handle invalid JSON gracefully", async () => {
const state: Partial<ExtensionState> = {
autoApprovalEnabled: true,
alwaysAllowMcp: true,
}

const result = await checkAutoApproval({
state: state as ExtensionState,
ask: "use_mcp_server",
text: "invalid json",
})

expect(result).toEqual({ decision: "ask" })
})
})

describe("General auto-approval settings", () => {
it("should ask when autoApprovalEnabled is false", async () => {
const state: Partial<ExtensionState> = {
autoApprovalEnabled: false,
alwaysAllowMcp: true,
}

const result = await checkAutoApproval({
state: state as ExtensionState,
ask: "use_mcp_server",
text: JSON.stringify({ type: "use_mcp_tool", serverName: "test", toolName: "test" }),
})

expect(result).toEqual({ decision: "ask" })
})

it("should ask when state is undefined", async () => {
const result = await checkAutoApproval({
state: undefined,
ask: "use_mcp_server",
text: JSON.stringify({ type: "use_mcp_tool", serverName: "test", toolName: "test" }),
})

expect(result).toEqual({ decision: "ask" })
})
})
})
5 changes: 4 additions & 1 deletion src/core/auto-approval/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,10 @@ export async function checkAutoApproval({
const mcpServerUse = JSON.parse(text) as McpServerUse

if (mcpServerUse.type === "use_mcp_tool") {
return state.alwaysAllowMcp === true && isMcpToolAlwaysAllowed(mcpServerUse, state.mcpServers)
// Auto-approve if either:
// 1. alwaysAllowMcp is enabled (approves all MCP tools)
// 2. The specific tool has alwaysAllow flag set
return state.alwaysAllowMcp === true || isMcpToolAlwaysAllowed(mcpServerUse, state.mcpServers)
? { decision: "approve" }
: { decision: "ask" }
} else if (mcpServerUse.type === "access_mcp_resource") {
Expand Down