diff --git a/src/renderer/src/components/mcp-config/mcpServerForm.vue b/src/renderer/src/components/mcp-config/mcpServerForm.vue index 9d92f4059..361f47c5a 100644 --- a/src/renderer/src/components/mcp-config/mcpServerForm.vue +++ b/src/renderer/src/components/mcp-config/mcpServerForm.vue @@ -20,7 +20,6 @@ import { useToast } from '@/components/use-toast' import { Icon } from '@iconify/vue' import { Popover, PopoverContent, PopoverTrigger } from '@shadcn/components/ui/popover' import { ChevronDown, X } from 'lucide-vue-next' -import { Badge } from '@shadcn/components/ui/badge' import ModelSelect from '@/components/ModelSelect.vue' import ModelIcon from '@/components/icons/ModelIcon.vue' import { useModelStore } from '@/stores/modelStore' @@ -29,6 +28,7 @@ import { MCP_MARKETPLACE_URL, HIGRESS_MCP_MARKETPLACE_URL } from './const' import { usePresenter } from '@/composables/usePresenter' import { useThemeStore } from '@/stores/theme' import { ModelType } from '@shared/model' +import { nanoid } from 'nanoid' const { t } = useI18n() const { toast } = useToast() @@ -49,7 +49,7 @@ const emit = defineEmits<{ // 表单状态 const name = ref(props.serverName || '') const command = ref(props.initialConfig?.command || 'npx') -const args = ref(props.initialConfig?.args?.join(' ') || '') +const args = ref(props.initialConfig?.args?.join('\n') || '') const env = ref(JSON.stringify(props.initialConfig?.env || {}, null, 2)) const descriptions = ref(props.initialConfig?.descriptions || '') type MCPServerTypeOption = 'sse' | 'stdio' | 'inmemory' | 'http' @@ -99,7 +99,7 @@ const handleImageModelSelect = (model: RENDERER_MODEL_META, providerId: string): selectedImageModel.value = model selectedImageModelProvider.value = providerId // 将provider和modelId以空格分隔拼接成args的值 - args.value = `${providerId} ${model.id}` + setArgsRowsFromArray([providerId, model.id]) modelSelectOpen.value = false } @@ -186,16 +186,23 @@ const parseJsonConfig = (): void => { // 填充表单数据 name.value = serverName command.value = serverConfig.command || 'npx' - args.value = serverConfig.args?.join(' ') || '' env.value = JSON.stringify(serverConfig.env || {}, null, 2) descriptions.value = serverConfig.descriptions || '' icons.value = serverConfig.icons || '📁' + const incomingArgs = Array.isArray(serverConfig.args) ? serverConfig.args : [] const incomingType = serverConfig.type as MCPServerTypeOption | undefined baseUrl.value = serverConfig.url || serverConfig.baseUrl || '' const fallbackType: MCPServerTypeOption = baseUrl.value ? 'http' : 'stdio' type.value = incomingType && VALID_MCP_TYPES.includes(incomingType) ? incomingType : fallbackType console.log('type', type.value, baseUrl.value) + // 根据类型填充参数 + if (isBuildInFileSystem.value) { + foldersList.value = incomingArgs + args.value = incomingArgs.join('\n') + } else { + setArgsRowsFromArray(incomingArgs) + } // 填充 customHeaders (如果存在) const headersFromConfig = @@ -310,10 +317,33 @@ const isFormValid = computed(() => { return isNameValid.value && isCommandValid.value && isEnvValid.value }) -// 参数输入相关状态 (用于标签式输入) -const argumentsList = ref([]) -const currentArgumentInput = ref('') -const argsInputRef = ref(null) // 用于聚焦输入框 +// 参数输入相关状态 (列表式输入) +const argsRows = ref>([]) +const createArgsRows = (values: string[]): Array<{ id: string; value: string }> => + values.map((value) => ({ + id: nanoid(), + value + })) +const syncArgsRowsFromString = (value: string): void => { + const parsedValues = value ? value.split(/\r?\n/) : [] + const currentValues = argsRows.value.map((row) => row.value) + if ( + parsedValues.length === currentValues.length && + parsedValues.every((val, index) => val === currentValues[index]) + ) { + return + } + argsRows.value = createArgsRows(parsedValues) +} +const setArgsRowsFromArray = (values: string[]): void => { + syncArgsRowsFromString(values.join('\n')) +} +const addArgsRow = (): void => { + argsRows.value.push({ id: nanoid(), value: '' }) +} +const removeArgsRow = (id: string): void => { + argsRows.value = argsRows.value.filter((row) => row.id !== id) +} // 文件夹选择相关状态 (用于 buildInFileSystem) const foldersList = ref([]) @@ -351,17 +381,12 @@ watch( if (isBuildInFileSystem.value) { // 对于 buildInFileSystem,args 是文件夹路径列表 if (newArgs) { - foldersList.value = newArgs.split(/\s+/).filter(Boolean) + foldersList.value = newArgs.split(/\r?\n/).filter((item) => item.trim().length > 0) } else { foldersList.value = [] } } else { - // 对于其他类型,使用标签式输入 - if (newArgs) { - argumentsList.value = newArgs.split(/\s+/).filter(Boolean) - } else { - argumentsList.value = [] - } + syncArgsRowsFromString(newArgs || '') } }, { immediate: true } @@ -369,11 +394,11 @@ watch( // 监听内部列表变化,更新外部 args 字符串 watch( - argumentsList, - (newList) => { - if (!isBuildInFileSystem.value) { - args.value = newList.join(' ') - } + argsRows, + (newRows) => { + if (isBuildInFileSystem.value) return + const joinedArgs = newRows.map((row) => row.value).join('\n') + if (args.value !== joinedArgs) args.value = joinedArgs }, { deep: true } ) @@ -383,49 +408,12 @@ watch( foldersList, (newList) => { if (isBuildInFileSystem.value) { - args.value = newList.join(' ') + args.value = newList.join('\n') } }, { deep: true } ) -// 添加参数到列表 -const addArgument = (): void => { - const value = currentArgumentInput.value.trim() - if (value) { - argumentsList.value.push(value) - } - currentArgumentInput.value = '' // 清空输入框 -} - -// 移除指定索引的参数 -const removeArgument = (index: number): void => { - argumentsList.value.splice(index, 1) -} - -// 处理输入框键盘事件 -const handleArgumentInputKeydown = (event: KeyboardEvent): void => { - switch (event.key) { - case 'Enter': - case ' ': // 按下空格也添加 - event.preventDefault() // 阻止默认行为 (如换行或输入空格) - addArgument() - break - case 'Backspace': - // 如果输入框为空,且参数列表不为空,则将最后一个tag的内容移回输入框,并从列表中移除 - if (currentArgumentInput.value === '' && argumentsList.value.length > 0) { - event.preventDefault() // 阻止默认的退格行为 - currentArgumentInput.value = argumentsList.value.pop() || '' - } - break - } -} - -// 点击容器时聚焦输入框 -const focusArgsInput = (): void => { - argsInputRef.value?.focus() -} - // 提交表单 const handleSubmit = (): void => { if (!isFormValid.value) return @@ -502,11 +490,13 @@ const handleSubmit = (): void => { } } else { // STDIO 或 inmemory 类型的服务器 + const normalizedArgs = isBuildInFileSystem.value + ? foldersList.value.filter((folder) => folder.trim().length > 0) + : argsRows.value.map((row) => row.value.trim()).filter((value) => value.length > 0) serverConfig = { ...baseConfig, command: command.value.trim(), - // args 从 argumentsList 更新,所以直接使用 split 即可,或者直接使用 argumentsList.value - args: args.value.split(/\s+/).filter(Boolean), + args: normalizedArgs, env: parsedEnv, baseUrl: baseUrl.value.trim() } @@ -619,11 +609,11 @@ watch( // 初始化时解析args中的provider和modelId(针对imageServer) watch( - [() => name.value, () => args.value, () => type.value], + [() => name.value, () => argsRows.value.map((row) => row.value), () => type.value], ([newName, newArgs, newType]) => { - if (newType === 'inmemory' && newName === 'imageServer' && newArgs) { + if (newType === 'inmemory' && newName === 'imageServer' && newArgs.length > 0) { // 从args中解析出provider和modelId - const argsParts = newArgs.split(/\s+/) + const argsParts = newArgs.filter((value) => value.trim().length > 0) if (argsParts.length >= 2) { const providerId = argsParts[0] const modelId = argsParts[1] @@ -651,13 +641,19 @@ watch( // Reset fields based on initialConfig // name.value = props.serverName || ''; // Name is usually passed separately and kept disabled command.value = newConfig.command || 'npx' - args.value = newConfig.args?.join(' ') || '' + const incomingArgs = Array.isArray(newConfig.args) ? newConfig.args : [] env.value = JSON.stringify(newConfig.env || {}, null, 2) descriptions.value = newConfig.descriptions || '' icons.value = newConfig.icons || '📁' type.value = newConfig.type || 'stdio' baseUrl.value = newConfig.baseUrl || '' npmRegistry.value = newConfig.customNpmRegistry || '' + if (isBuildInFileSystem.value) { + foldersList.value = incomingArgs + args.value = incomingArgs.join('\n') + } else { + setArgsRowsFromArray(incomingArgs) + } // 解析 E2B 配置(仅针对 powerpack 服务器) if (props.serverName === 'powerpack' && newConfig.env) { @@ -948,39 +944,25 @@ HTTP-Referer=deepchatai.cn`
- -
- - {{ arg }} - - - +
+ + +
+
+
+ + +
diff --git a/src/renderer/src/i18n/en-US/mcp.json b/src/renderer/src/i18n/en-US/mcp.json index 027f3cb2d..81f8b9ed2 100644 --- a/src/renderer/src/i18n/en-US/mcp.json +++ b/src/renderer/src/i18n/en-US/mcp.json @@ -81,7 +81,9 @@ "serverForm": { "add": "Add", "args": "Arguments", - "argsPlaceholder": "Enter parameters, separated by spaces", + "argsPlaceholder": "Enter one argument per line", + "addArg": "Add Argument", + "argPlaceholder": "Enter argument value", "argsRequired": "Parameters cannot be empty", "autoApprove": "Auto-approve", "autoApproveAll": "All", diff --git a/src/renderer/src/i18n/en-US/settings.json b/src/renderer/src/i18n/en-US/settings.json index 3134591ae..96f694546 100644 --- a/src/renderer/src/i18n/en-US/settings.json +++ b/src/renderer/src/i18n/en-US/settings.json @@ -630,7 +630,9 @@ "commandPlaceholder": "Enter command", "commandRequired": "Command is required", "args": "Arguments", - "argsPlaceholder": "Enter arguments separated by spaces", + "argsPlaceholder": "Enter one argument per line", + "addArg": "Add Argument", + "argPlaceholder": "Enter argument value", "argsRequired": "Arguments are required", "env": "Environment Variables", "envPlaceholder": "Enter environment variables in JSON format", diff --git a/src/renderer/src/i18n/fa-IR/mcp.json b/src/renderer/src/i18n/fa-IR/mcp.json index 23ef7cedf..04ebff972 100644 --- a/src/renderer/src/i18n/fa-IR/mcp.json +++ b/src/renderer/src/i18n/fa-IR/mcp.json @@ -81,7 +81,9 @@ "serverForm": { "add": "افزودن به", "args": "داده‌ها", - "argsPlaceholder": "داده‌ها را با فاصله وارد کنید", + "argsPlaceholder": "هر خط یک آرگومان وارد کنید", + "addArg": "افزودن آرگومان", + "argPlaceholder": "مقدار آرگومان را وارد کنید", "argsRequired": "داده‌ها نمی‌توانند خالی باشند", "autoApprove": "پذیرش خودکار", "autoApproveAll": "همه", diff --git a/src/renderer/src/i18n/fa-IR/settings.json b/src/renderer/src/i18n/fa-IR/settings.json index 71d86a5b6..27bda641b 100644 --- a/src/renderer/src/i18n/fa-IR/settings.json +++ b/src/renderer/src/i18n/fa-IR/settings.json @@ -630,7 +630,9 @@ "commandPlaceholder": "دستور را وارد کنید", "commandRequired": "دستور لازم است", "args": "ورودی‌ها", - "argsPlaceholder": "ورودی‌ها را با فاصله وارد کنید", + "argsPlaceholder": "هر ورودی را در یک خط وارد کنید", + "addArg": "افزودن ورودی", + "argPlaceholder": "مقدار ورودی را وارد کنید", "argsRequired": "ورودی‌ها لازم اند", "env": "متغیرهای محیط", "envPlaceholder": "متغیرهای محیط را در قالب JSON وارد کنید", diff --git a/src/renderer/src/i18n/fr-FR/mcp.json b/src/renderer/src/i18n/fr-FR/mcp.json index 672dea456..a16987015 100644 --- a/src/renderer/src/i18n/fr-FR/mcp.json +++ b/src/renderer/src/i18n/fr-FR/mcp.json @@ -81,7 +81,9 @@ "serverForm": { "add": "Ajouter", "args": "Arguments", - "argsPlaceholder": "Entrez les paramètres, séparés par des espaces", + "argsPlaceholder": "Saisir un argument par ligne", + "addArg": "Ajouter un argument", + "argPlaceholder": "Saisir la valeur de l'argument", "argsRequired": "Les paramètres ne peuvent pas être vides", "autoApprove": "Autorisation automatique", "autoApproveAll": "Tout", diff --git a/src/renderer/src/i18n/fr-FR/settings.json b/src/renderer/src/i18n/fr-FR/settings.json index abf2a40b4..aab8e3a24 100644 --- a/src/renderer/src/i18n/fr-FR/settings.json +++ b/src/renderer/src/i18n/fr-FR/settings.json @@ -621,7 +621,9 @@ "commandPlaceholder": "Entrer la commande", "commandRequired": "La commande est requise", "args": "Arguments", - "argsPlaceholder": "Entrer les arguments séparés par des espaces", + "argsPlaceholder": "Saisir un argument par ligne", + "addArg": "Ajouter un argument", + "argPlaceholder": "Saisir la valeur de l'argument", "argsRequired": "Les arguments sont requis", "env": "Variables d'environnement", "envPlaceholder": "Entrer les variables d'environnement au format JSON", diff --git a/src/renderer/src/i18n/ja-JP/mcp.json b/src/renderer/src/i18n/ja-JP/mcp.json index 44a8c1e2d..163fcc1a1 100644 --- a/src/renderer/src/i18n/ja-JP/mcp.json +++ b/src/renderer/src/i18n/ja-JP/mcp.json @@ -81,7 +81,9 @@ "serverForm": { "add": "追加", "args": "パラメーター", - "argsPlaceholder": "スペースで区切られたパラメーターを入力します", + "argsPlaceholder": "1行に1つのパラメーターを入力", + "addArg": "パラメーターを追加", + "argPlaceholder": "パラメーター値を入力", "argsRequired": "パラメーターを空にすることはできません", "autoApprove": "自動認証", "autoApproveAll": "全て", diff --git a/src/renderer/src/i18n/ja-JP/settings.json b/src/renderer/src/i18n/ja-JP/settings.json index f7892644b..283575a8a 100644 --- a/src/renderer/src/i18n/ja-JP/settings.json +++ b/src/renderer/src/i18n/ja-JP/settings.json @@ -620,7 +620,9 @@ "commandPlaceholder": "コマンドを入力", "commandRequired": "命令は空にできません", "args": "パラメータ", - "argsPlaceholder": "パラメータを入力してください", + "argsPlaceholder": "1行に1つのパラメータを入力", + "addArg": "パラメータを追加", + "argPlaceholder": "パラメータ値を入力", "argsRequired": "パラメータは空にできません", "env": "環境変数", "envPlaceholder": "JSON形式の環境変数を入力してください", diff --git a/src/renderer/src/i18n/ko-KR/mcp.json b/src/renderer/src/i18n/ko-KR/mcp.json index bb8d8b78e..72f04f5a8 100644 --- a/src/renderer/src/i18n/ko-KR/mcp.json +++ b/src/renderer/src/i18n/ko-KR/mcp.json @@ -38,7 +38,9 @@ "commandPlaceholder": "명령어 입력", "commandRequired": "명령어는 비어 있을 수 없습니다", "args": "매개변수", - "argsPlaceholder": "매개변수 입력, 공백으로 구분", + "argsPlaceholder": "한 줄에 하나씩 매개변수를 입력하세요", + "addArg": "매개변수 추가", + "argPlaceholder": "매개변수 값을 입력", "argsRequired": "매개변수는 비어 있을 수 없습니다", "env": "환경 변수", "envPlaceholder": "JSON 형식의 환경 변수 입력", diff --git a/src/renderer/src/i18n/ko-KR/settings.json b/src/renderer/src/i18n/ko-KR/settings.json index 7039d931d..0bcb2b134 100644 --- a/src/renderer/src/i18n/ko-KR/settings.json +++ b/src/renderer/src/i18n/ko-KR/settings.json @@ -621,7 +621,9 @@ "commandPlaceholder": "명령어 입력", "commandRequired": "명령어는 비어 있을 수 없습니다", "args": "매개변수", - "argsPlaceholder": "매개변수 입력, 공백으로 구분", + "argsPlaceholder": "한 줄에 하나씩 매개변수를 입력하세요", + "addArg": "매개변수 추가", + "argPlaceholder": "매개변수 값을 입력", "argsRequired": "매개변수는 비어 있을 수 없습니다", "env": "환경 변수", "envPlaceholder": "JSON 형식의 환경 변수 입력", diff --git a/src/renderer/src/i18n/pt-BR/mcp.json b/src/renderer/src/i18n/pt-BR/mcp.json index 47d3c446c..4347c3819 100644 --- a/src/renderer/src/i18n/pt-BR/mcp.json +++ b/src/renderer/src/i18n/pt-BR/mcp.json @@ -81,7 +81,9 @@ "serverForm": { "add": "Adicionar", "args": "Argumentos", - "argsPlaceholder": "Digite os parâmetros, separados por espaços", + "argsPlaceholder": "Digite um argumento por linha", + "addArg": "Adicionar argumento", + "argPlaceholder": "Digite o valor do argumento", "argsRequired": "Os parâmetros não podem estar vazios", "autoApprove": "Aprovação automática", "autoApproveAll": "Todos", diff --git a/src/renderer/src/i18n/pt-BR/settings.json b/src/renderer/src/i18n/pt-BR/settings.json index d0e4d5a88..cc09384a3 100644 --- a/src/renderer/src/i18n/pt-BR/settings.json +++ b/src/renderer/src/i18n/pt-BR/settings.json @@ -630,7 +630,9 @@ "commandPlaceholder": "Digite o comando", "commandRequired": "O comando é obrigatório", "args": "Argumentos", - "argsPlaceholder": "Digite os argumentos separados por espaços", + "argsPlaceholder": "Digite um argumento por linha", + "addArg": "Adicionar argumento", + "argPlaceholder": "Digite o valor do argumento", "argsRequired": "Argumentos são obrigatórios", "env": "Variáveis de Ambiente", "envPlaceholder": "Digite as variáveis de ambiente em formato JSON", diff --git a/src/renderer/src/i18n/ru-RU/mcp.json b/src/renderer/src/i18n/ru-RU/mcp.json index 83b3e78fe..92696bbc6 100644 --- a/src/renderer/src/i18n/ru-RU/mcp.json +++ b/src/renderer/src/i18n/ru-RU/mcp.json @@ -81,7 +81,9 @@ "serverForm": { "add": "добавить в", "args": "параметр", - "argsPlaceholder": "Введите параметры, разделенные пространствами", + "argsPlaceholder": "Введите по одному параметру в строке", + "addArg": "Добавить параметр", + "argPlaceholder": "Введите значение параметра", "argsRequired": "Параметры не могут быть пустыми", "autoApprove": "Автоматическое авторизация", "autoApproveAll": "все", diff --git a/src/renderer/src/i18n/ru-RU/settings.json b/src/renderer/src/i18n/ru-RU/settings.json index acf181859..ab31dccaa 100644 --- a/src/renderer/src/i18n/ru-RU/settings.json +++ b/src/renderer/src/i18n/ru-RU/settings.json @@ -621,7 +621,9 @@ "commandPlaceholder": "Введите команду", "commandRequired": "Команда не может быть пустой", "args": "Параметры", - "argsPlaceholder": "Введите параметры, разделенные пробелами", + "argsPlaceholder": "Введите по одному параметру в строке", + "addArg": "Добавить параметр", + "argPlaceholder": "Введите значение параметра", "argsRequired": "Параметры не могут быть пустыми", "env": "Переменные окружения", "envPlaceholder": "Введите переменные окружения в формате JSON", diff --git a/src/renderer/src/i18n/zh-CN/mcp.json b/src/renderer/src/i18n/zh-CN/mcp.json index 639764174..f25719983 100644 --- a/src/renderer/src/i18n/zh-CN/mcp.json +++ b/src/renderer/src/i18n/zh-CN/mcp.json @@ -62,7 +62,9 @@ "commandPlaceholder": "输入命令", "commandRequired": "命令不能为空", "args": "参数", - "argsPlaceholder": "输入参数,用空格分隔", + "argsPlaceholder": "每行一个参数", + "addArg": "添加参数", + "argPlaceholder": "输入参数值", "argsRequired": "参数不能为空", "env": "环境变量", "envPlaceholder": "输入JSON格式的环境变量", diff --git a/src/renderer/src/i18n/zh-CN/settings.json b/src/renderer/src/i18n/zh-CN/settings.json index 0ab502751..6bbf10d7b 100644 --- a/src/renderer/src/i18n/zh-CN/settings.json +++ b/src/renderer/src/i18n/zh-CN/settings.json @@ -629,7 +629,9 @@ "commandPlaceholder": "输入命令", "commandRequired": "命令不能为空", "args": "参数", - "argsPlaceholder": "输入参数,用空格分隔", + "argsPlaceholder": "每行一个参数", + "addArg": "添加参数", + "argPlaceholder": "输入参数值", "argsRequired": "参数不能为空", "env": "环境变量", "envPlaceholder": "输入JSON格式的环境变量", diff --git a/src/renderer/src/i18n/zh-HK/mcp.json b/src/renderer/src/i18n/zh-HK/mcp.json index 01a20588f..7cd8669df 100644 --- a/src/renderer/src/i18n/zh-HK/mcp.json +++ b/src/renderer/src/i18n/zh-HK/mcp.json @@ -81,7 +81,9 @@ "serverForm": { "add": "添加", "args": "參數", - "argsPlaceholder": "輸入參數,用空格分隔", + "argsPlaceholder": "每行一個參數", + "addArg": "添加參數", + "argPlaceholder": "輸入參數值", "argsRequired": "參數不能為空", "autoApprove": "自動授權", "autoApproveAll": "全部", diff --git a/src/renderer/src/i18n/zh-HK/settings.json b/src/renderer/src/i18n/zh-HK/settings.json index 898e0e06e..171ea0660 100644 --- a/src/renderer/src/i18n/zh-HK/settings.json +++ b/src/renderer/src/i18n/zh-HK/settings.json @@ -621,7 +621,9 @@ "commandPlaceholder": "輸入命令", "commandRequired": "命令不能為空", "args": "參數", - "argsPlaceholder": "輸入參數,用空格分隔", + "argsPlaceholder": "每行一個參數", + "addArg": "添加參數", + "argPlaceholder": "輸入參數值", "argsRequired": "參數不能為空", "env": "環境變量", "envPlaceholder": "輸入JSON格式的環境變量", diff --git a/src/renderer/src/i18n/zh-TW/mcp.json b/src/renderer/src/i18n/zh-TW/mcp.json index ef9abaec9..b7895f75b 100644 --- a/src/renderer/src/i18n/zh-TW/mcp.json +++ b/src/renderer/src/i18n/zh-TW/mcp.json @@ -62,7 +62,9 @@ "commandPlaceholder": "輸入命令", "commandRequired": "命令不能為空", "args": "參數", - "argsPlaceholder": "輸入參數,用空格分隔", + "argsPlaceholder": "每行一個參數", + "addArg": "新增參數", + "argPlaceholder": "輸入參數值", "argsRequired": "參數不能為空", "env": "環境變量", "envPlaceholder": "輸入JSON格式的環境變量", diff --git a/src/renderer/src/i18n/zh-TW/settings.json b/src/renderer/src/i18n/zh-TW/settings.json index a43d2b223..1131fd5de 100644 --- a/src/renderer/src/i18n/zh-TW/settings.json +++ b/src/renderer/src/i18n/zh-TW/settings.json @@ -620,7 +620,9 @@ "commandPlaceholder": "輸入命令", "commandRequired": "命令不能為空", "args": "參數", - "argsPlaceholder": "輸入參數,用空格分隔", + "argsPlaceholder": "每行一個參數", + "addArg": "新增參數", + "argPlaceholder": "輸入參數值", "argsRequired": "參數不能為空", "env": "環境變數", "envPlaceholder": "輸入JSON格式的環境變數",