/ src / components / MCPServerApprovalDialog.tsx
MCPServerApprovalDialog.tsx
  1  import React from 'react'
  2  import { Box, Text, useInput } from 'ink'
  3  import { getTheme } from '../utils/theme.js'
  4  import { Select } from '@inkjs/ui'
  5  import {
  6    saveCurrentProjectConfig,
  7    getCurrentProjectConfig,
  8  } from '../utils/config.js'
  9  import { MCPServerDialogCopy } from './MCPServerDialogCopy.js'
 10  import { useExitOnCtrlCD } from '../hooks/useExitOnCtrlCD.js'
 11  
 12  type Props = {
 13    serverName: string
 14    onDone(): void
 15  }
 16  
 17  export function MCPServerApprovalDialog({
 18    serverName,
 19    onDone,
 20  }: Props): React.ReactNode {
 21    const theme = getTheme()
 22    function onChange(value: 'yes' | 'no') {
 23      const config = getCurrentProjectConfig()
 24      switch (value) {
 25        case 'yes': {
 26          if (!config.approvedMcprcServers) {
 27            config.approvedMcprcServers = []
 28          }
 29          if (!config.approvedMcprcServers.includes(serverName)) {
 30            config.approvedMcprcServers.push(serverName)
 31          }
 32          saveCurrentProjectConfig(config)
 33          onDone()
 34          break
 35        }
 36        case 'no': {
 37          if (!config.rejectedMcprcServers) {
 38            config.rejectedMcprcServers = []
 39          }
 40          if (!config.rejectedMcprcServers.includes(serverName)) {
 41            config.rejectedMcprcServers.push(serverName)
 42          }
 43          saveCurrentProjectConfig(config)
 44          onDone()
 45          break
 46        }
 47      }
 48    }
 49  
 50    const exitState = useExitOnCtrlCD(() => process.exit(0))
 51  
 52    useInput((_input, key) => {
 53      if (key.escape) {
 54        onDone()
 55        return
 56      }
 57    })
 58  
 59    return (
 60      <>
 61        <Box
 62          flexDirection="column"
 63          gap={1}
 64          padding={1}
 65          borderStyle="round"
 66          borderColor={theme.warning}
 67        >
 68          <Text bold color={theme.warning}>
 69            New MCP Server Detected
 70          </Text>
 71          <Text>
 72            This project contains a .mcprc file with an MCP server that requires
 73            your approval:
 74          </Text>
 75          <Text bold>{serverName}</Text>
 76  
 77          <MCPServerDialogCopy />
 78  
 79          <Text>Do you want to approve this MCP server?</Text>
 80  
 81          <Select
 82            options={[
 83              { label: 'Yes, approve this server', value: 'yes' },
 84              { label: 'No, reject this server', value: 'no' },
 85            ]}
 86            onChange={value => onChange(value as 'yes' | 'no')}
 87          />
 88        </Box>
 89        <Box marginLeft={3}>
 90          <Text dimColor>
 91            {exitState.pending ? (
 92              <>Press {exitState.keyName} again to exit</>
 93            ) : (
 94              <>Enter to confirm ยท Esc to reject</>
 95            )}
 96          </Text>
 97        </Box>
 98      </>
 99    )
100  }