// ====== MCP server editor — platform-level catalog (admin) ====== // // MCP store(2026-05-26):MCP servers 持久化到 .baizhi-data SQLite // (`mcp_servers` 表,平台级)。admin 增删改通过 8787 CRUD endpoints。 // 真 secret 不进 DB —— `auth_env_key` 只是 env 变量名,真值仍在 .env。 // // 字段 → backend: // server_id / name / description / transport // transport=http/sse: http_url / headers_template (dict) / auth_env_key // transport=stdio: command / args (list) function MCPEditor({ mcp, onUpdate, onDelete, onDuplicate, saveState, onSave, onCheck, checkState, userEnabled, bindingState, onSetUserEnabled, }) { // headers 在前端 UI 是 [{k,v}] 表格;decorateMcp 已转好。提交时 // updateMcpServer 把它 collapse 回 dict 形态。 const headers = mcp.headers || []; const runtimeOptions = mcp.runtime_options || {}; const setHeader = (i, patch) => { const next = headers.map((h, idx) => (idx === i ? { ...h, ...patch } : h)); const shouldPersist = Boolean(next[i]?.k) && Boolean(next[i].k.trim()); onUpdate(patchForHeaderRows(next, { persist: shouldPersist })); }; const addHeader = () => { const next = [...headers, { k: "", v: "" }]; onUpdate(patchForHeaderRows(next, { persist: false })); }; const removeHeader = (i) => { const next = headers.filter((_, idx) => idx !== i); onUpdate(patchForHeaderRows(next)); }; const setRuntimeOption = (key, value) => { const next = { ...runtimeOptions }; const raw = String(value || "").trim(); if (!raw) { delete next[key]; } else { const n = Number(raw); if (Number.isFinite(n) && n > 0) next[key] = n; } onUpdate({ runtime_options: next }); }; // 标准 Claude Desktop / Cursor MCP config JSON 形态(参考用,Copy 用) const configJson = { mcpServers: { [mcp.name]: { type: mcp.transport === "http" ? "streamableHttp" : mcp.transport, description: mcp.description, isActive: mcp.available, name: mcp.server_id, ...(mcp.transport === "http" ? { baseUrl: mcp.url } : {}), ...(mcp.transport === "sse" ? { url: mcp.url } : {}), ...(mcp.command ? { command: mcp.command } : {}), ...(mcp.args ? { args: typeof mcp.args === "string" ? mcp.args.split(/\s+/).filter(Boolean) : mcp.args } : {}), ...(headers.length > 0 ? { headers: dictFromKV(headers) } : {}), }, }, }; const latestCheck = checkState?.result || mcp.checkStatus || null; const configStatus = latestCheck?.config || mcp.configStatus || { configured: !!mcp.available, issues: mcp.available ? [] : ["env / config is not ready"], env_key: mcp.auth_env_key || "", env_present: !!mcp.available, }; const mcpProbe = latestCheck?.mcp || { status: mcp.available ? "not checked" : "skipped", ok: false, error: mcp.available ? null : "configuration unavailable", }; const toolsProbe = latestCheck?.tools || { status: "not checked", ok: false, count: Array.isArray(mcp.tools) ? mcp.tools.length : 0, items: mcp.tools || [], error: null, }; const checkBusy = checkState?.status === "checking"; const bindingBusy = bindingState?.status === "saving"; return (
${"{VAR}"} 在 runtime 从 env 解析
{mcp.auth_env_key} is set in runtime env
: ✗ {mcp.auth_env_key} missing — server will be unavailable}
{JSON.stringify(configJson, null, 2)}