/ src / components / MCPServerMultiselectDialog.tsx
MCPServerMultiselectDialog.tsx
  1  import React from 'react'
  2  import { Box, Text, useInput } from 'ink'
  3  import { getTheme } from '../utils/theme.js'
  4  import { MultiSelect } from '@inkjs/ui'
  5  import {
  6    saveCurrentProjectConfig,
  7    getCurrentProjectConfig,
  8  } from '../utils/config.js'
  9  import { partition } from 'lodash-es'
 10  import { MCPServerDialogCopy } from './MCPServerDialogCopy.js'
 11  import { useExitOnCtrlCD } from '../hooks/useExitOnCtrlCD.js'
 12  
 13  type Props = {
 14    serverNames: string[]
 15    onDone(): void
 16  }
 17  
 18  export function MCPServerMultiselectDialog({
 19    serverNames,
 20    onDone,
 21  }: Props): React.ReactNode {
 22    const theme = getTheme()
 23    function onSubmit(selectedServers: string[]) {
 24      const config = getCurrentProjectConfig()
 25  
 26      // Initialize arrays if they don't exist
 27      if (!config.approvedMcprcServers) {
 28        config.approvedMcprcServers = []
 29      }
 30      if (!config.rejectedMcprcServers) {
 31        config.rejectedMcprcServers = []
 32      }
 33  
 34      // Use partition to separate approved and rejected servers
 35      const [approvedServers, rejectedServers] = partition(serverNames, server =>
 36        selectedServers.includes(server),
 37      )
 38  
 39      // Add new servers directly to the respective lists
 40      config.approvedMcprcServers.push(...approvedServers)
 41      config.rejectedMcprcServers.push(...rejectedServers)
 42  
 43      saveCurrentProjectConfig(config)
 44      onDone()
 45    }
 46  
 47    const exitState = useExitOnCtrlCD(() => process.exit())
 48  
 49    useInput((_input, key) => {
 50      if (key.escape) {
 51        // On escape, treat all servers as rejected
 52        const config = getCurrentProjectConfig()
 53        if (!config.rejectedMcprcServers) {
 54          config.rejectedMcprcServers = []
 55        }
 56  
 57        for (const server of serverNames) {
 58          if (!config.rejectedMcprcServers.includes(server)) {
 59            config.rejectedMcprcServers.push(server)
 60          }
 61        }
 62  
 63        saveCurrentProjectConfig(config)
 64        onDone()
 65        return
 66      }
 67    })
 68  
 69    return (
 70      <>
 71        <Box
 72          flexDirection="column"
 73          gap={1}
 74          padding={1}
 75          borderStyle="round"
 76          borderColor={theme.warning}
 77        >
 78          <Text bold color={theme.warning}>
 79            New MCP Servers Detected
 80          </Text>
 81          <Text>
 82            This project contains a .mcprc file with {serverNames.length} MCP
 83            servers that require your approval.
 84          </Text>
 85          <MCPServerDialogCopy />
 86  
 87          <Text>Please select the servers you want to enable:</Text>
 88  
 89          <MultiSelect
 90            options={serverNames.map(server => ({
 91              label: server,
 92              value: server,
 93            }))}
 94            defaultValue={serverNames}
 95            onSubmit={onSubmit}
 96          />
 97        </Box>
 98        <Box marginLeft={3}>
 99          <Text dimColor>
100            {exitState.pending ? (
101              <>Press {exitState.keyName} again to exit</>
102            ) : (
103              <>Space to select · Enter to confirm · Esc to reject all</>
104            )}
105          </Text>
106        </Box>
107      </>
108    )
109  }