Skip to main content
Glama

Interactive MCP

MIT License
97
299
  • Apple
  • Linux
InteractiveInput.tsx4.04 kB
import React, { FC, useState, useEffect } from 'react'; import { Box, Text, useInput } from 'ink'; import { TextInput } from '@inkjs/ui'; import logger from '@/utils/logger.js'; interface InteractiveInputProps { question: string; questionId: string; predefinedOptions?: string[]; onSubmit: (questionId: string, value: string) => void; } export const InteractiveInput: FC<InteractiveInputProps> = ({ question, questionId, predefinedOptions = [], onSubmit, }) => { const [mode, setMode] = useState<'option' | 'input'>( predefinedOptions.length > 0 ? 'option' : 'input', ); const [selectedIndex, setSelectedIndex] = useState<number>(0); const [inputValue, setInputValue] = useState<string>(''); useInput((input, key) => { if (predefinedOptions.length > 0) { if (key.upArrow) { setMode('option'); setSelectedIndex( (prev) => (prev - 1 + predefinedOptions.length) % predefinedOptions.length, ); return; } if (key.downArrow) { setMode('option'); setSelectedIndex((prev) => (prev + 1) % predefinedOptions.length); return; } } if (key.return) { if (mode === 'option' && predefinedOptions.length > 0) { onSubmit(questionId, predefinedOptions[selectedIndex]); } else { onSubmit(questionId, inputValue); } return; } // Any other key press switches to input mode if ( !key.ctrl && !key.meta && !key.escape && !key.tab && !key.shift && !key.leftArrow && !key.rightArrow && input ) { setMode('input'); // Update inputValue only if switching to input mode via typing // TextInput's onChange will handle subsequent typing if (mode === 'option') { setInputValue(input); // Start input with the typed character } } }); const handleInputChange = (value: string) => { if (value !== inputValue) { setInputValue(value); // If user starts typing, switch to input mode if (value.length > 0 && mode === 'option') { setMode('input'); } else if (value.length === 0 && predefinedOptions.length > 0) { // Optionally switch back to option mode if input is cleared // setMode('option'); } } }; const handleSubmit = (value: string) => { // The primary submit logic is now handled in useInput via Enter key // This might still be called by TextInput's internal onSubmit, ensure consistency if (mode === 'option' && predefinedOptions.length > 0) { onSubmit(questionId, predefinedOptions[selectedIndex]); } else { onSubmit(questionId, value); // Use the value from TextInput in case it triggered submit } }; return ( <> <Box flexDirection="column" marginBottom={1}> <Text bold color="cyan" wrap="wrap"> {question} </Text> </Box> {predefinedOptions.length > 0 && ( <Box flexDirection="column" marginBottom={1}> <Text dimColor={true}> Use ↑/↓ to select options, type for custom input, Enter to submit </Text> {predefinedOptions.map((opt, i) => ( <Text key={i} color={ i === selectedIndex && mode === 'option' ? 'greenBright' : undefined } > {i === selectedIndex && mode === 'option' ? '› ' : ' '} {opt} </Text> ))} </Box> )} <Box> <Text color={mode === 'input' ? 'greenBright' : undefined}> {mode === 'input' ? '✎ ' : '› '} <TextInput placeholder={ predefinedOptions.length > 0 ? 'Type or select an option...' : 'Type your answer...' } onChange={handleInputChange} onSubmit={handleSubmit} /> </Text> </Box> </> ); };

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/ttommyth/interactive-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server