mirror of
https://github.com/Syngnat/GoNavi.git
synced 2026-06-14 10:29:52 +08:00
✨ feat(ai-mcp): 优化外部客户端接入引导与状态识别
- 调整 Claude Code 和 Codex 的安装文案与状态标签,明确是把 GoNavi 暴露给外部客户端使用 - 优化 MCP 客户端默认选择逻辑,优先聚焦未接入或需更新的目标并避免刷新后乱跳 - 同步补齐前端 mock、后端状态文案和定向测试,确保安装区交互与状态展示一致
This commit is contained in:
@@ -46,6 +46,11 @@ describe('AISettingsModal edit password behavior', () => {
|
||||
it('wires the external MCP client install panel actions back to the modal handlers', () => {
|
||||
expect(source).toContain('mcpClientStatuses={mcpClientStatuses}');
|
||||
expect(source).toContain('selectedMCPClient={selectedMCPClient}');
|
||||
expect(source).toContain('const [mcpClientSelectionTouched, setMCPClientSelectionTouched] = useState(false);');
|
||||
expect(source).toContain('const handleSelectMCPClient = useCallback((client: MCPClientKey) => {');
|
||||
expect(source).toContain('pickPreferredMCPClient(normalizedStatuses, mcpClientSelectionTouched ? prev : undefined)');
|
||||
expect(source).toContain('setMCPClientSelectionTouched(true);');
|
||||
expect(source).toContain('onSelectClient={handleSelectMCPClient}');
|
||||
expect(source).toContain('onRefreshStatus={() => void loadMCPClientStatuses()}');
|
||||
expect(source).toContain('onCopyConfigPath={() => void handleCopySelectedMCPConfigPath()}');
|
||||
expect(source).toContain('onCopyLaunchCommand={() => void handleCopySelectedMCPLaunchCommand()}');
|
||||
|
||||
@@ -112,14 +112,14 @@ const EMPTY_MCP_CLIENT_STATUSES: AIMCPClientInstallStatus[] = [
|
||||
displayName: 'Claude Code',
|
||||
installed: false,
|
||||
matchesCurrent: false,
|
||||
message: '未安装到 Claude Code 用户级配置',
|
||||
message: '未检测到 Claude Code 用户级 GoNavi MCP 配置',
|
||||
},
|
||||
{
|
||||
client: 'codex',
|
||||
displayName: 'Codex',
|
||||
installed: false,
|
||||
matchesCurrent: false,
|
||||
message: '未安装到 Codex 用户级配置',
|
||||
message: '未检测到 Codex 用户级 GoNavi MCP 配置',
|
||||
},
|
||||
];
|
||||
|
||||
@@ -219,6 +219,7 @@ const AISettingsModal: React.FC<AISettingsModalProps> = ({ open, onClose, darkMo
|
||||
const [mcpTools, setMCPTools] = useState<AIMCPToolDescriptor[]>([]);
|
||||
const [mcpClientStatuses, setMCPClientStatuses] = useState<AIMCPClientInstallStatus[]>(EMPTY_MCP_CLIENT_STATUSES);
|
||||
const [selectedMCPClient, setSelectedMCPClient] = useState<MCPClientKey>('claude-code');
|
||||
const [mcpClientSelectionTouched, setMCPClientSelectionTouched] = useState(false);
|
||||
const [mcpClientStatusLoading, setMCPClientStatusLoading] = useState(false);
|
||||
const [skills, setSkills] = useState<AISkillConfig[]>([]);
|
||||
const [editingProvider, setEditingProvider] = useState<AIProviderConfig | null>(null);
|
||||
@@ -263,6 +264,10 @@ const AISettingsModal: React.FC<AISettingsModalProps> = ({ open, onClose, darkMo
|
||||
() => formatMCPLaunchCommand(selectedMCPClientStatus),
|
||||
[selectedMCPClientStatus],
|
||||
);
|
||||
const handleSelectMCPClient = useCallback((client: MCPClientKey) => {
|
||||
setMCPClientSelectionTouched(true);
|
||||
setSelectedMCPClient(client);
|
||||
}, []);
|
||||
|
||||
const resolveAIService = useCallback(async () => {
|
||||
const service = await waitForAIService();
|
||||
@@ -291,7 +296,7 @@ const AISettingsModal: React.FC<AISettingsModalProps> = ({ open, onClose, darkMo
|
||||
if (Array.isArray(result)) {
|
||||
const normalizedStatuses = normalizeMCPClientStatuses(result);
|
||||
setMCPClientStatuses(normalizedStatuses);
|
||||
setSelectedMCPClient((prev) => pickPreferredMCPClient(normalizedStatuses, prev));
|
||||
setSelectedMCPClient((prev) => pickPreferredMCPClient(normalizedStatuses, mcpClientSelectionTouched ? prev : undefined));
|
||||
}
|
||||
} catch (e: any) {
|
||||
if (silent) {
|
||||
@@ -304,7 +309,7 @@ const AISettingsModal: React.FC<AISettingsModalProps> = ({ open, onClose, darkMo
|
||||
setMCPClientStatusLoading(false);
|
||||
}
|
||||
}
|
||||
}, [messageApi, resolveAIService]);
|
||||
}, [mcpClientSelectionTouched, messageApi, resolveAIService]);
|
||||
|
||||
const copyTextToClipboard = useCallback(async (text: string, successMessage: string) => {
|
||||
if (typeof navigator?.clipboard?.writeText !== 'function') {
|
||||
@@ -362,13 +367,19 @@ const AISettingsModal: React.FC<AISettingsModalProps> = ({ open, onClose, darkMo
|
||||
if (Array.isArray(mcpClientStatusesRes)) {
|
||||
const normalizedStatuses = normalizeMCPClientStatuses(mcpClientStatusesRes);
|
||||
setMCPClientStatuses(normalizedStatuses);
|
||||
setSelectedMCPClient((prev) => pickPreferredMCPClient(normalizedStatuses, prev));
|
||||
setSelectedMCPClient((prev) => pickPreferredMCPClient(normalizedStatuses, mcpClientSelectionTouched ? prev : undefined));
|
||||
}
|
||||
} catch (e) { console.warn('Failed to load AI config', e); }
|
||||
}, [resolveAIService]);
|
||||
}, [mcpClientSelectionTouched, resolveAIService]);
|
||||
|
||||
useEffect(() => { if (open) void loadConfig(); }, [open, loadConfig]);
|
||||
|
||||
useEffect(() => {
|
||||
if (open) {
|
||||
setMCPClientSelectionTouched(false);
|
||||
}
|
||||
}, [open]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!open || !focusProviderId) {
|
||||
return;
|
||||
@@ -641,6 +652,7 @@ const AISettingsModal: React.FC<AISettingsModalProps> = ({ open, onClose, darkMo
|
||||
}
|
||||
try {
|
||||
setLoading(true);
|
||||
setMCPClientSelectionTouched(true);
|
||||
const Service = await resolveAIService();
|
||||
let result: MCPClientInstallResult;
|
||||
if (targetClient === 'codex') {
|
||||
@@ -904,7 +916,7 @@ const AISettingsModal: React.FC<AISettingsModalProps> = ({ open, onClose, darkMo
|
||||
inputBg={inputBg}
|
||||
loading={loading}
|
||||
mcpClientStatusLoading={mcpClientStatusLoading}
|
||||
onSelectClient={setSelectedMCPClient}
|
||||
onSelectClient={handleSelectMCPClient}
|
||||
onRefreshStatus={() => void loadMCPClientStatuses()}
|
||||
onCopyConfigPath={() => void handleCopySelectedMCPConfigPath()}
|
||||
onCopyLaunchCommand={() => void handleCopySelectedMCPLaunchCommand()}
|
||||
|
||||
@@ -15,14 +15,14 @@ describe('AIMCPClientInstallPanel', () => {
|
||||
displayName: 'Claude Code',
|
||||
installed: false,
|
||||
matchesCurrent: false,
|
||||
message: '未安装到 Claude Code 用户级配置',
|
||||
message: '未检测到 Claude Code 用户级 GoNavi MCP 配置',
|
||||
},
|
||||
{
|
||||
client: 'codex',
|
||||
displayName: 'Codex',
|
||||
installed: true,
|
||||
matchesCurrent: false,
|
||||
message: '检测到旧的 Codex 配置,建议更新',
|
||||
message: '已检测到 Codex 中的 GoNavi MCP 记录,但与当前 GoNavi 安装路径不一致,建议更新',
|
||||
configPath: '~/.codex/config.toml',
|
||||
command: 'gonavi-mcp-server',
|
||||
args: ['stdio'],
|
||||
@@ -34,7 +34,7 @@ describe('AIMCPClientInstallPanel', () => {
|
||||
displayName: 'Codex',
|
||||
installed: true,
|
||||
matchesCurrent: false,
|
||||
message: '检测到旧的 Codex 配置,建议更新',
|
||||
message: '已检测到 Codex 中的 GoNavi MCP 记录,但与当前 GoNavi 安装路径不一致,建议更新',
|
||||
configPath: '~/.codex/config.toml',
|
||||
command: 'gonavi-mcp-server',
|
||||
args: ['stdio'],
|
||||
@@ -54,14 +54,54 @@ describe('AIMCPClientInstallPanel', () => {
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(markup).toContain('不是给 GoNavi 自己安装 MCP');
|
||||
expect(markup).toContain('安装到外部 AI 客户端');
|
||||
expect(markup).toContain('第 1 步:选择安装目标');
|
||||
expect(markup).toContain('第 2 步:确认当前状态并安装');
|
||||
expect(markup).toContain('待安装');
|
||||
expect(markup).toContain('不是给 GoNavi 自己再装一个 MCP');
|
||||
expect(markup).toContain('把 GoNavi MCP 接入外部 AI 客户端');
|
||||
expect(markup).toContain('第 1 步:选择目标客户端');
|
||||
expect(markup).toContain('第 2 步:确认状态后写入');
|
||||
expect(markup).toContain('未接入');
|
||||
expect(markup).toContain('需更新');
|
||||
expect(markup).toContain('复制配置路径');
|
||||
expect(markup).toContain('复制启动命令');
|
||||
expect(markup).toContain('更新 Codex 配置');
|
||||
expect(markup).toContain('不会下载 Claude Code / Codex');
|
||||
});
|
||||
|
||||
it('shows an already-connected label when the selected client matches the current GoNavi path', () => {
|
||||
const markup = renderToStaticMarkup(
|
||||
<AIMCPClientInstallPanel
|
||||
statuses={[
|
||||
{
|
||||
client: 'claude-code',
|
||||
displayName: 'Claude Code',
|
||||
installed: true,
|
||||
matchesCurrent: true,
|
||||
message: '已检测到 Claude Code 用户级 GoNavi MCP 配置,且与当前 GoNavi 安装路径一致',
|
||||
},
|
||||
]}
|
||||
selectedClient="claude-code"
|
||||
selectedStatus={{
|
||||
client: 'claude-code',
|
||||
displayName: 'Claude Code',
|
||||
installed: true,
|
||||
matchesCurrent: true,
|
||||
message: '已检测到 Claude Code 用户级 GoNavi MCP 配置,且与当前 GoNavi 安装路径一致',
|
||||
}}
|
||||
selectedCommandText="gonavi-mcp-server stdio"
|
||||
darkMode={false}
|
||||
overlayTheme={buildOverlayWorkbenchTheme(false)}
|
||||
cardBg="#fff"
|
||||
cardBorder="rgba(0,0,0,0.08)"
|
||||
loading={false}
|
||||
statusLoading={false}
|
||||
onSelectClient={() => {}}
|
||||
onRefreshStatus={() => {}}
|
||||
onCopyConfigPath={() => {}}
|
||||
onCopyLaunchCommand={() => {}}
|
||||
onInstall={() => {}}
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(markup).toContain('已接入');
|
||||
expect(markup).toContain('已接入 Claude Code');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -29,7 +29,7 @@ const getStatusTone = (status: AIMCPClientInstallStatus | undefined, darkMode: b
|
||||
const messageText = String(status?.message || '');
|
||||
if (status?.matchesCurrent) {
|
||||
return {
|
||||
label: '已安装',
|
||||
label: '已接入',
|
||||
color: '#16a34a',
|
||||
bg: darkMode ? 'rgba(34,197,94,0.18)' : 'rgba(34,197,94,0.12)',
|
||||
};
|
||||
@@ -49,24 +49,25 @@ const getStatusTone = (status: AIMCPClientInstallStatus | undefined, darkMode: b
|
||||
};
|
||||
}
|
||||
return {
|
||||
label: '待安装',
|
||||
label: '未接入',
|
||||
color: darkMode ? 'rgba(255,255,255,0.72)' : '#64748b',
|
||||
bg: darkMode ? 'rgba(255,255,255,0.08)' : 'rgba(100,116,139,0.08)',
|
||||
};
|
||||
};
|
||||
|
||||
const getStatusSummary = (status: AIMCPClientInstallStatus | undefined) => {
|
||||
const label = status?.displayName || '这个客户端';
|
||||
const messageText = String(status?.message || '');
|
||||
if (status?.matchesCurrent) {
|
||||
return '这个客户端已经安装当前 GoNavi MCP,不需要再装一遍。';
|
||||
return `${label} 已经写入当前 GoNavi 路径,可直接把 GoNavi 当作 MCP Server 使用。`;
|
||||
}
|
||||
if (status?.installed) {
|
||||
return '这个客户端已经有旧配置,更新后会改成当前 GoNavi 安装路径。';
|
||||
return `${label} 已检测到旧的 GoNavi 路径,更新后会改成当前这份 GoNavi。`;
|
||||
}
|
||||
if (messageText.includes('失败') || messageText.includes('异常')) {
|
||||
return '状态读取异常,建议先刷新,再决定是否安装。';
|
||||
return '状态读取异常,建议先刷新,再决定是否写入。';
|
||||
}
|
||||
return '这个客户端还没有安装 GoNavi MCP。';
|
||||
return `${label} 还没有写入 GoNavi MCP 配置。`;
|
||||
};
|
||||
|
||||
const getClientCardDescription = (status: AIMCPClientInstallStatus | undefined) => {
|
||||
@@ -82,12 +83,12 @@ const getClientCardDescription = (status: AIMCPClientInstallStatus | undefined)
|
||||
const resolveActionLabel = (status: AIMCPClientInstallStatus | undefined) => {
|
||||
const label = status?.displayName || '客户端';
|
||||
if (status?.matchesCurrent) {
|
||||
return `已安装到 ${label}`;
|
||||
return `已接入 ${label}`;
|
||||
}
|
||||
if (status?.installed) {
|
||||
return `更新 ${label} 配置`;
|
||||
}
|
||||
return `安装到 ${label}`;
|
||||
return `写入 ${label} 配置`;
|
||||
};
|
||||
|
||||
const AIMCPClientInstallPanel: React.FC<AIMCPClientInstallPanelProps> = ({
|
||||
@@ -109,7 +110,7 @@ const AIMCPClientInstallPanel: React.FC<AIMCPClientInstallPanelProps> = ({
|
||||
}) => (
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: 12 }}>
|
||||
<div style={{ fontSize: 13, color: overlayTheme.mutedText, marginBottom: 4, lineHeight: 1.7 }}>
|
||||
这里不是给 GoNavi 自己安装 MCP,而是把 GoNavi 作为 MCP Server 安装到 Claude Code、Codex 这类外部 AI 客户端里使用。
|
||||
这里的“安装”不是给 GoNavi 自己再装一个 MCP,而是把 GoNavi 暴露成 MCP Server,写入 Claude Code、Codex 这类外部 AI 客户端里使用。
|
||||
</div>
|
||||
|
||||
<div
|
||||
@@ -124,9 +125,9 @@ const AIMCPClientInstallPanel: React.FC<AIMCPClientInstallPanelProps> = ({
|
||||
}}
|
||||
>
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: 4 }}>
|
||||
<div style={{ fontWeight: 700, fontSize: 14, color: overlayTheme.titleText }}>安装到外部 AI 客户端</div>
|
||||
<div style={{ fontWeight: 700, fontSize: 14, color: overlayTheme.titleText }}>把 GoNavi MCP 接入外部 AI 客户端</div>
|
||||
<div style={{ fontSize: 12, color: overlayTheme.mutedText, lineHeight: 1.7 }}>
|
||||
先选 1 个要安装到的目标客户端,GoNavi 会自动把当前安装路径写入它的用户级 MCP 配置文件,不需要你自己找本机 exe,也不用手改配置。
|
||||
先选 1 个要接入的目标客户端,GoNavi 会自动把当前安装路径写入它的用户级 MCP 配置文件,不需要你自己找本机 exe,也不用手改配置。
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -141,14 +142,14 @@ const AIMCPClientInstallPanel: React.FC<AIMCPClientInstallPanelProps> = ({
|
||||
lineHeight: 1.7,
|
||||
}}
|
||||
>
|
||||
只会修改你选中的外部客户端用户级 MCP 配置,不会下载新的 GoNavi,也不会替换 GoNavi 自己的程序文件。
|
||||
只会修改你选中的外部客户端用户级 MCP 配置,不会下载 Claude Code / Codex,也不会重新安装 GoNavi 自己的程序文件。
|
||||
</div>
|
||||
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: 4 }}>
|
||||
<div style={{ fontWeight: 700, fontSize: 13, color: overlayTheme.titleText }}>第 1 步:选择安装目标</div>
|
||||
<div style={{ fontWeight: 700, fontSize: 13, color: overlayTheme.titleText }}>第 1 步:选择目标客户端</div>
|
||||
<div style={{ fontSize: 12, color: overlayTheme.mutedText, lineHeight: 1.6 }}>
|
||||
每次只安装到一个外部客户端,避免重复写入。
|
||||
每次只处理一个外部客户端,先看状态,再决定是否写入。
|
||||
</div>
|
||||
</div>
|
||||
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(220px, 1fr))', gap: 10 }}>
|
||||
@@ -171,6 +172,7 @@ const AIMCPClientInstallPanel: React.FC<AIMCPClientInstallPanelProps> = ({
|
||||
flexDirection: 'column',
|
||||
gap: 10,
|
||||
textAlign: 'left',
|
||||
minHeight: 118,
|
||||
transition: 'all 0.2s ease',
|
||||
opacity: statusLoading ? 0.72 : 1,
|
||||
}}
|
||||
@@ -234,7 +236,7 @@ const AIMCPClientInstallPanel: React.FC<AIMCPClientInstallPanelProps> = ({
|
||||
>
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: 6 }}>
|
||||
<div style={{ fontWeight: 700, fontSize: 13, color: overlayTheme.titleText }}>
|
||||
第 2 步:确认当前状态并安装
|
||||
第 2 步:确认状态后写入
|
||||
</div>
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: 8, flexWrap: 'wrap' }}>
|
||||
<div style={{ fontWeight: 700, fontSize: 13, color: overlayTheme.titleText }}>
|
||||
@@ -305,7 +307,7 @@ const AIMCPClientInstallPanel: React.FC<AIMCPClientInstallPanelProps> = ({
|
||||
|
||||
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', gap: 12, flexWrap: 'wrap' }}>
|
||||
<div style={{ fontSize: 12, color: overlayTheme.mutedText, lineHeight: 1.6 }}>
|
||||
写入后重启对应客户端即可生效;如果已经是当前路径,按钮会自动禁用,避免重复安装。
|
||||
写入后重启对应客户端即可生效;如果当前路径已经一致,按钮会自动禁用,避免重复写入。
|
||||
</div>
|
||||
<Button
|
||||
type={selectedStatus?.matchesCurrent ? 'default' : 'primary'}
|
||||
|
||||
@@ -47,14 +47,14 @@ describe('AISettingsMCPSection', () => {
|
||||
displayName: 'Claude Code',
|
||||
installed: false,
|
||||
matchesCurrent: false,
|
||||
message: '未安装到 Claude Code 用户级配置',
|
||||
message: '未检测到 Claude Code 用户级 GoNavi MCP 配置',
|
||||
},
|
||||
{
|
||||
client: 'codex',
|
||||
displayName: 'Codex',
|
||||
installed: false,
|
||||
matchesCurrent: false,
|
||||
message: '未安装到 Codex 用户级配置',
|
||||
message: '未检测到 Codex 用户级 GoNavi MCP 配置',
|
||||
},
|
||||
]}
|
||||
selectedMCPClient="claude-code"
|
||||
@@ -63,7 +63,7 @@ describe('AISettingsMCPSection', () => {
|
||||
displayName: 'Claude Code',
|
||||
installed: false,
|
||||
matchesCurrent: false,
|
||||
message: '未安装到 Claude Code 用户级配置',
|
||||
message: '未检测到 Claude Code 用户级 GoNavi MCP 配置',
|
||||
}}
|
||||
selectedMCPClientCommandText=""
|
||||
mcpServers={[]}
|
||||
@@ -88,7 +88,7 @@ describe('AISettingsMCPSection', () => {
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(markup).toContain('安装到外部 AI 客户端');
|
||||
expect(markup).toContain('把 GoNavi MCP 接入外部 AI 客户端');
|
||||
expect(markup).toContain('常见启动方式模板');
|
||||
expect(markup).toContain('Node 脚本');
|
||||
expect(markup).toContain('新增 MCP 服务');
|
||||
|
||||
@@ -43,7 +43,7 @@ if (typeof window !== 'undefined' && (!(window as any).go?.app?.App || !(window
|
||||
displayName: 'Claude Code',
|
||||
installed: false,
|
||||
matchesCurrent: false,
|
||||
message: '未安装到 Claude Code 用户级配置',
|
||||
message: '未检测到 Claude Code 用户级 GoNavi MCP 配置',
|
||||
configPath: 'C:/Users/mock/.claude.json',
|
||||
command: 'C:/Program Files/GoNavi/GoNavi.exe',
|
||||
args: ['mcp-server'],
|
||||
@@ -53,7 +53,7 @@ if (typeof window !== 'undefined' && (!(window as any).go?.app?.App || !(window
|
||||
displayName: 'Codex',
|
||||
installed: true,
|
||||
matchesCurrent: false,
|
||||
message: '已检测到 Codex 安装记录,但与当前 GoNavi 安装包路径不一致,建议更新安装',
|
||||
message: '已检测到 Codex 中的 GoNavi MCP 记录,但与当前 GoNavi 安装路径不一致,建议更新',
|
||||
configPath: 'C:/Users/mock/.codex/config.toml',
|
||||
command: 'C:/Old/GoNavi.exe',
|
||||
args: ['mcp-server'],
|
||||
|
||||
@@ -172,7 +172,7 @@ func inspectClaudeCodeMCPInstallStatus(expectedCommand string, expectedArgs []st
|
||||
Client: "claude-code",
|
||||
DisplayName: "Claude Code",
|
||||
ConfigPath: strings.TrimSpace(configPath),
|
||||
Message: "未安装到 Claude Code 用户级配置",
|
||||
Message: "未检测到 Claude Code 用户级 GoNavi MCP 配置",
|
||||
}
|
||||
if pathErr != nil {
|
||||
status.Message = fmt.Sprintf("定位 Claude Code 配置失败: %v", pathErr)
|
||||
@@ -197,18 +197,18 @@ func inspectClaudeCodeMCPInstallStatus(expectedCommand string, expectedArgs []st
|
||||
status.Command = strings.TrimSpace(serverConfig.Command)
|
||||
status.Args = append([]string(nil), serverConfig.Args...)
|
||||
if expectedErr != nil {
|
||||
status.Message = fmt.Sprintf("已检测到 Claude Code 安装记录,但当前 GoNavi 安装路径校验失败:%v", expectedErr)
|
||||
status.Message = fmt.Sprintf("已检测到 Claude Code 中的 GoNavi MCP 记录,但当前 GoNavi 安装路径校验失败:%v", expectedErr)
|
||||
return status
|
||||
}
|
||||
|
||||
status.MatchesCurrent = strings.EqualFold(strings.TrimSpace(serverConfig.Type), "stdio") &&
|
||||
sameMCPCommand(serverConfig.Command, serverConfig.Args, expectedCommand, expectedArgs)
|
||||
if status.MatchesCurrent {
|
||||
status.Message = "已安装到 Claude Code 用户级配置"
|
||||
status.Message = "已检测到 Claude Code 用户级 GoNavi MCP 配置,且与当前 GoNavi 安装路径一致"
|
||||
return status
|
||||
}
|
||||
|
||||
status.Message = "已检测到 Claude Code 安装记录,但与当前 GoNavi 安装包路径不一致,建议更新安装"
|
||||
status.Message = "已检测到 Claude Code 中的 GoNavi MCP 记录,但与当前 GoNavi 安装路径不一致,建议更新"
|
||||
return status
|
||||
}
|
||||
|
||||
@@ -218,7 +218,7 @@ func inspectCodexMCPInstallStatus(expectedCommand string, expectedArgs []string,
|
||||
Client: "codex",
|
||||
DisplayName: "Codex",
|
||||
ConfigPath: strings.TrimSpace(configPath),
|
||||
Message: "未安装到 Codex 用户级配置",
|
||||
Message: "未检测到 Codex 用户级 GoNavi MCP 配置",
|
||||
}
|
||||
if pathErr != nil {
|
||||
status.Message = fmt.Sprintf("定位 Codex 配置失败: %v", pathErr)
|
||||
@@ -243,18 +243,18 @@ func inspectCodexMCPInstallStatus(expectedCommand string, expectedArgs []string,
|
||||
status.Command = strings.TrimSpace(serverConfig.Command)
|
||||
status.Args = append([]string(nil), serverConfig.Args...)
|
||||
if expectedErr != nil {
|
||||
status.Message = fmt.Sprintf("已检测到 Codex 安装记录,但当前 GoNavi 安装路径校验失败:%v", expectedErr)
|
||||
status.Message = fmt.Sprintf("已检测到 Codex 中的 GoNavi MCP 记录,但当前 GoNavi 安装路径校验失败:%v", expectedErr)
|
||||
return status
|
||||
}
|
||||
|
||||
status.MatchesCurrent = sameMCPCommand(serverConfig.Command, serverConfig.Args, expectedCommand, expectedArgs) &&
|
||||
(serverConfig.StartupTimeoutSec == 0 || serverConfig.StartupTimeoutSec == defaultCodexMCPStartupTimeoutSecond)
|
||||
if status.MatchesCurrent {
|
||||
status.Message = "已安装到 Codex 用户级配置"
|
||||
status.Message = "已检测到 Codex 用户级 GoNavi MCP 配置,且与当前 GoNavi 安装路径一致"
|
||||
return status
|
||||
}
|
||||
|
||||
status.Message = "已检测到 Codex 安装记录,但与当前 GoNavi 安装包路径不一致,建议更新安装"
|
||||
status.Message = "已检测到 Codex 中的 GoNavi MCP 记录,但与当前 GoNavi 安装路径不一致,建议更新"
|
||||
return status
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user