MCP checkbox for always allow

This commit is contained in:
Matt Rubens
2024-12-13 14:23:31 -05:00
parent 6ee118e0a2
commit 1346f1280c
26 changed files with 744 additions and 22 deletions

View File

@@ -1,19 +1,45 @@
import { VSCodeCheckbox } from "@vscode/webview-ui-toolkit/react"
import { McpTool } from "../../../../src/shared/mcp"
import { vscode } from "../../utils/vscode"
type McpToolRowProps = {
tool: McpTool
serverName?: string
}
const McpToolRow = ({ tool }: McpToolRowProps) => {
const McpToolRow = ({ tool, serverName }: McpToolRowProps) => {
const handleAlwaysAllowChange = () => {
if (!serverName) return;
vscode.postMessage({
type: "toggleToolAlwaysAllow",
serverName,
toolName: tool.name,
alwaysAllow: !tool.alwaysAllow
});
}
return (
<div
key={tool.name}
style={{
padding: "3px 0",
}}>
<div style={{ display: "flex" }}>
<span className="codicon codicon-symbol-method" style={{ marginRight: "6px" }}></span>
<span style={{ fontWeight: 500 }}>{tool.name}</span>
<div
style={{ display: "flex", alignItems: "center", justifyContent: "space-between" }}
onClick={(e) => e.stopPropagation()}>
<div style={{ display: "flex", alignItems: "center" }}>
<span className="codicon codicon-symbol-method" style={{ marginRight: "6px" }}></span>
<span style={{ fontWeight: 500 }}>{tool.name}</span>
</div>
{serverName && (
<VSCodeCheckbox
checked={tool.alwaysAllow}
onChange={handleAlwaysAllowChange}
data-tool={tool.name}>
Always allow
</VSCodeCheckbox>
)}
</div>
{tool.description && (
<div

View File

@@ -256,7 +256,11 @@ const ServerRow = ({ server }: { server: McpServer }) => {
<div
style={{ display: "flex", flexDirection: "column", gap: "8px", width: "100%" }}>
{server.tools.map((tool) => (
<McpToolRow key={tool.name} tool={tool} />
<McpToolRow
key={tool.name}
tool={tool}
serverName={server.name}
/>
))}
</div>
) : (

View File

@@ -0,0 +1,107 @@
import React from 'react'
import { render, fireEvent, screen } from '@testing-library/react'
import McpToolRow from '../McpToolRow'
import { vscode } from '../../../utils/vscode'
jest.mock('../../../utils/vscode', () => ({
vscode: {
postMessage: jest.fn()
}
}))
describe('McpToolRow', () => {
const mockTool = {
name: 'test-tool',
description: 'A test tool',
alwaysAllow: false
}
beforeEach(() => {
jest.clearAllMocks()
})
it('renders tool name and description', () => {
render(<McpToolRow tool={mockTool} />)
expect(screen.getByText('test-tool')).toBeInTheDocument()
expect(screen.getByText('A test tool')).toBeInTheDocument()
})
it('does not show always allow checkbox when serverName is not provided', () => {
render(<McpToolRow tool={mockTool} />)
expect(screen.queryByText('Always allow')).not.toBeInTheDocument()
})
it('shows always allow checkbox when serverName is provided', () => {
render(<McpToolRow tool={mockTool} serverName="test-server" />)
expect(screen.getByText('Always allow')).toBeInTheDocument()
})
it('sends message to toggle always allow when checkbox is clicked', () => {
render(<McpToolRow tool={mockTool} serverName="test-server" />)
const checkbox = screen.getByRole('checkbox')
fireEvent.click(checkbox)
expect(vscode.postMessage).toHaveBeenCalledWith({
type: 'toggleToolAlwaysAllow',
serverName: 'test-server',
toolName: 'test-tool',
alwaysAllow: true
})
})
it('reflects always allow state in checkbox', () => {
const alwaysAllowedTool = {
...mockTool,
alwaysAllow: true
}
render(<McpToolRow tool={alwaysAllowedTool} serverName="test-server" />)
const checkbox = screen.getByRole('checkbox')
expect(checkbox).toBeChecked()
})
it('prevents event propagation when clicking the checkbox', () => {
const mockStopPropagation = jest.fn()
render(<McpToolRow tool={mockTool} serverName="test-server" />)
const container = screen.getByTestId('tool-row-container')
fireEvent.click(container, {
stopPropagation: mockStopPropagation
})
expect(mockStopPropagation).toHaveBeenCalled()
})
it('displays input schema parameters when provided', () => {
const toolWithSchema = {
...mockTool,
inputSchema: {
type: 'object',
properties: {
param1: {
type: 'string',
description: 'First parameter'
},
param2: {
type: 'number',
description: 'Second parameter'
}
},
required: ['param1']
}
}
render(<McpToolRow tool={toolWithSchema} serverName="test-server" />)
expect(screen.getByText('Parameters')).toBeInTheDocument()
expect(screen.getByText('param1')).toBeInTheDocument()
expect(screen.getByText('param2')).toBeInTheDocument()
expect(screen.getByText('First parameter')).toBeInTheDocument()
expect(screen.getByText('Second parameter')).toBeInTheDocument()
})
})