fix(mcp): remove SSE support; keep stdio default when type is omitted
- Backend: reject "sse" in validators; accept missing type as stdio; require url only for http (mcp.rs, claude_mcp.rs) - Frontend: McpServer.type narrowed to "stdio" | "http" (optional) (src/types.ts) - UI: avoid undefined in list item details (McpListItem) - Claude-only sync after delete to update ~/.claude.json (commands.rs) Notes: - Ran typecheck and cargo check: both pass - Clippy shows advisory warnings unrelated to this change - Prettier check warns on a few files; limited scope changes kept minimal
This commit is contained in:
@@ -30,9 +30,9 @@ const McpListItem: React.FC<McpListItemProps> = ({
|
||||
const enabled = server.enabled !== false;
|
||||
|
||||
// 构建详细信息文本
|
||||
const details = [server.type, server.command, ...(server.args || [])].join(
|
||||
" · ",
|
||||
);
|
||||
const details = ([server.type, server.command, ...(server.args || [])]
|
||||
.filter(Boolean) as string[])
|
||||
.join(" · ");
|
||||
|
||||
return (
|
||||
<div className={cn(cardStyles.interactive, "!p-4")}>
|
||||
|
||||
@@ -40,7 +40,7 @@ const McpPanel: React.FC<McpPanelProps> = ({ onClose, onNotify }) => {
|
||||
const reload = async () => {
|
||||
setLoading(true);
|
||||
try {
|
||||
const cfg = await window.api.getMcpConfig();
|
||||
const cfg = await window.api.getMcpConfig("claude");
|
||||
setStatus({
|
||||
userConfigPath: cfg.configPath,
|
||||
userConfigExists: true,
|
||||
@@ -59,7 +59,7 @@ const McpPanel: React.FC<McpPanelProps> = ({ onClose, onNotify }) => {
|
||||
await window.api.importMcpFromClaude();
|
||||
|
||||
// 读取现有 config.json 内容
|
||||
const cfg = await window.api.getMcpConfig();
|
||||
const cfg = await window.api.getMcpConfig("claude");
|
||||
const existing = cfg.servers || {};
|
||||
|
||||
// 将预设落库为禁用(若缺失)
|
||||
@@ -70,7 +70,7 @@ const McpPanel: React.FC<McpPanelProps> = ({ onClose, onNotify }) => {
|
||||
enabled: false,
|
||||
source: "preset",
|
||||
} as unknown as McpServer;
|
||||
await window.api.upsertMcpServerInConfig(p.id, seed);
|
||||
await window.api.upsertMcpServerInConfig("claude", p.id, seed);
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn("MCP 初始化导入/落库失败(忽略继续)", e);
|
||||
@@ -87,9 +87,9 @@ const McpPanel: React.FC<McpPanelProps> = ({ onClose, onNotify }) => {
|
||||
if (!server) {
|
||||
const preset = mcpPresets.find((p) => p.id === id);
|
||||
if (!preset) return;
|
||||
await window.api.upsertMcpServerInConfig(id, preset.server as McpServer);
|
||||
await window.api.upsertMcpServerInConfig("claude", id, preset.server as McpServer);
|
||||
}
|
||||
await window.api.setMcpEnabled(id, enabled);
|
||||
await window.api.setMcpEnabled("claude", id, enabled);
|
||||
await reload();
|
||||
onNotify?.(
|
||||
enabled ? t("mcp.msg.enabled") : t("mcp.msg.disabled"),
|
||||
@@ -123,7 +123,7 @@ const McpPanel: React.FC<McpPanelProps> = ({ onClose, onNotify }) => {
|
||||
message: t("mcp.confirm.deleteMessage", { id }),
|
||||
onConfirm: async () => {
|
||||
try {
|
||||
await window.api.deleteMcpServerInConfig(id);
|
||||
await window.api.deleteMcpServerInConfig("claude", id);
|
||||
await reload();
|
||||
setConfirmDialog(null);
|
||||
onNotify?.(t("mcp.msg.deleted"), "success", 1500);
|
||||
@@ -141,7 +141,7 @@ const McpPanel: React.FC<McpPanelProps> = ({ onClose, onNotify }) => {
|
||||
|
||||
const handleSave = async (id: string, server: McpServer) => {
|
||||
try {
|
||||
await window.api.upsertMcpServerInConfig(id, server);
|
||||
await window.api.upsertMcpServerInConfig("claude", id, server);
|
||||
await reload();
|
||||
setIsFormOpen(false);
|
||||
setEditingId(null);
|
||||
|
||||
@@ -339,10 +339,10 @@ export const tauriAPI = {
|
||||
}
|
||||
},
|
||||
|
||||
// 新:config.json 为 SSOT 的 MCP API
|
||||
getMcpConfig: async (): Promise<McpConfigResponse> => {
|
||||
// 新:config.json 为 SSOT 的 MCP API(按客户端)
|
||||
getMcpConfig: async (app: AppType = "claude"): Promise<McpConfigResponse> => {
|
||||
try {
|
||||
return await invoke<McpConfigResponse>("get_mcp_config");
|
||||
return await invoke<McpConfigResponse>("get_mcp_config", { app });
|
||||
} catch (error) {
|
||||
console.error("获取 MCP 配置失败:", error);
|
||||
throw error;
|
||||
@@ -350,29 +350,37 @@ export const tauriAPI = {
|
||||
},
|
||||
|
||||
upsertMcpServerInConfig: async (
|
||||
app: AppType = "claude",
|
||||
id: string,
|
||||
spec: McpServer | Record<string, any>,
|
||||
): Promise<boolean> => {
|
||||
try {
|
||||
return await invoke<boolean>("upsert_mcp_server_in_config", { id, spec });
|
||||
return await invoke<boolean>("upsert_mcp_server_in_config", { app, id, spec });
|
||||
} catch (error) {
|
||||
console.error("写入 MCP(config.json)失败:", error);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
|
||||
deleteMcpServerInConfig: async (id: string): Promise<boolean> => {
|
||||
deleteMcpServerInConfig: async (
|
||||
app: AppType = "claude",
|
||||
id: string,
|
||||
): Promise<boolean> => {
|
||||
try {
|
||||
return await invoke<boolean>("delete_mcp_server_in_config", { id });
|
||||
return await invoke<boolean>("delete_mcp_server_in_config", { app, id });
|
||||
} catch (error) {
|
||||
console.error("删除 MCP(config.json)失败:", error);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
|
||||
setMcpEnabled: async (id: string, enabled: boolean): Promise<boolean> => {
|
||||
setMcpEnabled: async (
|
||||
app: AppType = "claude",
|
||||
id: string,
|
||||
enabled: boolean,
|
||||
): Promise<boolean> => {
|
||||
try {
|
||||
return await invoke<boolean>("set_mcp_enabled", { id, enabled });
|
||||
return await invoke<boolean>("set_mcp_enabled", { app, id, enabled });
|
||||
} catch (error) {
|
||||
console.error("设置 MCP 启用状态失败:", error);
|
||||
throw error;
|
||||
|
||||
@@ -55,7 +55,8 @@ export interface Settings {
|
||||
|
||||
// MCP 服务器定义(宽松:允许扩展字段)
|
||||
export interface McpServer {
|
||||
type: "stdio" | "http";
|
||||
// 可选:社区常见 .mcp.json 中 stdio 配置可不写 type
|
||||
type?: "stdio" | "http";
|
||||
// stdio 字段
|
||||
command?: string;
|
||||
args?: string[];
|
||||
|
||||
11
src/vite-env.d.ts
vendored
11
src/vite-env.d.ts
vendored
@@ -71,13 +71,18 @@ declare global {
|
||||
deleteClaudeMcpServer: (id: string) => Promise<boolean>;
|
||||
validateMcpCommand: (cmd: string) => Promise<boolean>;
|
||||
// 新:config.json 为 SSOT 的 MCP API
|
||||
getMcpConfig: () => Promise<McpConfigResponse>;
|
||||
getMcpConfig: (app?: AppType) => Promise<McpConfigResponse>;
|
||||
upsertMcpServerInConfig: (
|
||||
app: AppType | undefined,
|
||||
id: string,
|
||||
spec: Record<string, any>,
|
||||
) => Promise<boolean>;
|
||||
deleteMcpServerInConfig: (id: string) => Promise<boolean>;
|
||||
setMcpEnabled: (id: string, enabled: boolean) => Promise<boolean>;
|
||||
deleteMcpServerInConfig: (app: AppType | undefined, id: string) => Promise<boolean>;
|
||||
setMcpEnabled: (
|
||||
app: AppType | undefined,
|
||||
id: string,
|
||||
enabled: boolean,
|
||||
) => Promise<boolean>;
|
||||
syncEnabledMcpToClaude: () => Promise<boolean>;
|
||||
importMcpFromClaude: () => Promise<number>;
|
||||
testApiEndpoints: (
|
||||
|
||||
Reference in New Issue
Block a user