feat(mcp): pre-fill wizard with existing configuration

Allow MCP wizard to load and edit existing server configuration:
- Parse current config (JSON/TOML) and pass to wizard as initial data
- Auto-detect server type (stdio/http) and populate form fields
- Convert objects (env/headers) to multi-line text format for editing
- Improves UX by avoiding manual re-entry of existing settings
This commit is contained in:
Jason
2025-10-13 23:37:33 +08:00
parent e77eab2116
commit 06010ff78e
2 changed files with 82 additions and 2 deletions

View File

@@ -1,4 +1,4 @@
import React, { useState } from "react";
import React, { useMemo, useState } from "react";
import { useTranslation } from "react-i18next";
import { X, Save, AlertCircle, ChevronDown, ChevronUp } from "lucide-react";
import { McpServer, McpServerSpec } from "../../types";
@@ -117,6 +117,31 @@ const McpFormModal: React.FC<McpFormModalProps> = ({
// 判断是否使用 TOML 格式
const useToml = appType === "codex";
const wizardInitialSpec = useMemo(() => {
const fallback = initialData?.server;
if (!formConfig.trim()) {
return fallback;
}
if (useToml) {
try {
return tomlToMcpServer(formConfig);
} catch {
return fallback;
}
}
try {
const parsed = JSON.parse(formConfig);
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
return parsed as McpServerSpec;
}
return fallback;
} catch {
return fallback;
}
}, [formConfig, initialData, useToml]);
// 预设选择状态(仅新增模式显示;-1 表示自定义)
const [selectedPreset, setSelectedPreset] = useState<number | null>(
isEditing ? null : -1,
@@ -661,6 +686,8 @@ const McpFormModal: React.FC<McpFormModalProps> = ({
onClose={() => setIsWizardOpen(false)}
onApply={handleWizardApply}
onNotify={onNotify}
initialTitle={formId}
initialServer={wizardInitialSpec}
/>
</div>
);

View File

@@ -1,4 +1,4 @@
import React, { useState } from "react";
import React, { useEffect, useState } from "react";
import { useTranslation } from "react-i18next";
import { X, Save } from "lucide-react";
import { McpServerSpec } from "../../types";
@@ -13,6 +13,8 @@ interface McpWizardModalProps {
type: "success" | "error",
duration?: number,
) => void;
initialTitle?: string;
initialServer?: McpServerSpec;
}
/**
@@ -72,6 +74,8 @@ const McpWizardModal: React.FC<McpWizardModalProps> = ({
onClose,
onApply,
onNotify,
initialTitle,
initialServer,
}) => {
const { t } = useTranslation();
const [wizardType, setWizardType] = useState<"stdio" | "http">("stdio");
@@ -162,6 +166,55 @@ const McpWizardModal: React.FC<McpWizardModalProps> = ({
}
};
useEffect(() => {
if (!isOpen) return;
const title = initialTitle ?? "";
setWizardTitle(title);
const resolvedType =
initialServer?.type ??
(initialServer?.url ? "http" : "stdio");
setWizardType(resolvedType);
if (resolvedType === "http") {
setWizardUrl(initialServer?.url ?? "");
const headersCandidate = initialServer?.headers;
const headers =
headersCandidate && typeof headersCandidate === "object"
? headersCandidate
: undefined;
setWizardHeaders(
headers
? Object.entries(headers)
.map(([k, v]) => `${k}: ${v ?? ""}`)
.join("\n")
: "",
);
setWizardCommand("");
setWizardArgs("");
setWizardEnv("");
return;
}
setWizardCommand(initialServer?.command ?? "");
const argsValue = initialServer?.args;
setWizardArgs(Array.isArray(argsValue) ? argsValue.join("\n") : "");
const envCandidate = initialServer?.env;
const env =
envCandidate && typeof envCandidate === "object" ? envCandidate : undefined;
setWizardEnv(
env
? Object.entries(env)
.map(([k, v]) => `${k}=${v ?? ""}`)
.join("\n")
: "",
);
setWizardUrl("");
setWizardHeaders("");
}, [isOpen]);
if (!isOpen) return null;
const preview = generatePreview();