diff --git a/frontend/src/components/ai/AIMCPClientInstallPanel.test.tsx b/frontend/src/components/ai/AIMCPClientInstallPanel.test.tsx index bdcb48c..e2c727a 100644 --- a/frontend/src/components/ai/AIMCPClientInstallPanel.test.tsx +++ b/frontend/src/components/ai/AIMCPClientInstallPanel.test.tsx @@ -62,18 +62,21 @@ describe('AIMCPClientInstallPanel', () => { />, ); - expect(markup).toContain('这里不是给 GoNavi 自己安装 MCP'); + expect(markup).toContain('这里是在给外部客户端接入 GoNavi MCP'); expect(markup).toContain('接入外部客户端'); expect(markup).toContain('目标客户端'); - expect(markup).toContain('只会修改你选中的客户端用户级 MCP 配置'); + expect(markup).toContain('选择目标客户端'); + expect(markup).toContain('写入当前 GoNavi 配置'); + expect(markup).toContain('重启对应客户端'); expect(markup).toContain('未接入'); expect(markup).toContain('需更新'); expect(markup).toContain('复制配置路径'); expect(markup).toContain('复制启动命令'); - expect(markup).toContain('更新 Codex 配置'); + expect(markup).toContain('更新已选客户端配置'); expect(markup).toContain('Codex 状态'); expect(markup).toContain('CLI 检测:已检测到 codex'); - expect(markup).toContain('点击后切换到这个客户端'); + expect(markup).toContain('当前已选中,将只对这个客户端执行写入或更新'); + expect(markup).toContain('已选目标:Codex'); }); it('shows an already-connected label and supports prewriting config when the client command is not detected locally', () => { @@ -124,9 +127,62 @@ describe('AIMCPClientInstallPanel', () => { />, ); - expect(markup).toContain('接入到 Claude Code'); + expect(markup).toContain('写入到已选客户端'); expect(markup).toContain('CLI 检测:未检测到 claude'); expect(markup).toContain('未检测到本机 claude 命令'); expect(markup).toContain('已接入'); }); + + it('makes repeated install avoidance explicit when the selected client already matches current GoNavi', () => { + const markup = renderToStaticMarkup( + {}} + onRefreshStatus={() => {}} + onCopyConfigPath={() => {}} + onCopyLaunchCommand={() => {}} + onInstall={() => {}} + />, + ); + + expect(markup).toContain('当前状态:已接入当前 GoNavi,无需重复写入'); + expect(markup).toContain('当前已接入,无需重复写入'); + expect(markup).toContain('下面的主按钮会自动禁用,避免重复写入'); + }); }); diff --git a/frontend/src/components/ai/AIMCPClientInstallPanel.tsx b/frontend/src/components/ai/AIMCPClientInstallPanel.tsx index 71aedae..4b16974 100644 --- a/frontend/src/components/ai/AIMCPClientInstallPanel.tsx +++ b/frontend/src/components/ai/AIMCPClientInstallPanel.tsx @@ -100,15 +100,27 @@ const getClientDetectionSummary = (status: AIMCPClientInstallStatus | undefined) return `未检测到本机 ${commandName} 命令;如果 CLI 还没加入 PATH,也可以先写入 ${label} 配置,稍后再重启验证。`; }; -const resolveActionLabel = (status: AIMCPClientInstallStatus | undefined) => { - const label = status?.displayName || '客户端'; +const getSelectedClientStateLine = (status: AIMCPClientInstallStatus | undefined) => { if (status?.matchesCurrent) { - return `已接入到 ${label}`; + return '已接入当前 GoNavi,无需重复写入'; } if (status?.installed) { - return `更新 ${label} 配置`; + return '已存在旧记录,建议更新到当前 GoNavi 路径'; } - return `接入到 ${label}`; + if (hasStatusIssue(status)) { + return '状态读取异常,建议先刷新检测'; + } + return '当前还没有写入 GoNavi MCP 配置'; +}; + +const resolveActionLabel = (status: AIMCPClientInstallStatus | undefined) => { + if (status?.matchesCurrent) { + return '当前已接入,无需重复写入'; + } + if (status?.installed) { + return '更新已选客户端配置'; + } + return '写入到已选客户端'; }; const AIMCPClientInstallPanel: React.FC = ({ @@ -129,10 +141,6 @@ const AIMCPClientInstallPanel: React.FC = ({ onInstall, }) => (
-
- 这里不是给 GoNavi 自己安装 MCP,而是把 GoNavi 作为 MCP Server 接入 Claude Code 或 Codex 这类外部 AI 客户端。 -
-
= ({ gap: 14, }} > -
-
接入外部客户端
-
- 选择 1 个客户端后再执行接入或更新。GoNavi 会自动把当前安装路径写入它的用户级 MCP 配置文件,不需要你自己找本机 exe 或手动改配置。 -
-
- 只会修改你选中的客户端用户级 MCP 配置,不会安装新的 GoNavi,也不会替换 GoNavi 自己的程序文件。 +
+ 这里是在给外部客户端接入 GoNavi MCP,不是给 GoNavi 自己安装 MCP。 +
+
+ 你只需要选中 Claude Code 或 Codex 其中一个目标,GoNavi 就会把“如何启动当前这份 GoNavi MCP”的配置写入那个客户端的用户级配置文件。 +
+
+ +
+
接入外部客户端
+
+ 先选择 1 个目标客户端,再执行写入或更新。GoNavi 会自动把当前安装路径写入它的用户级 MCP 配置文件,不需要你自己找本机 exe 或手动改配置。 +
+
+
+ {[ + { step: '1', title: '选择目标客户端', detail: 'Claude Code 和 Codex 二选一即可。' }, + { step: '2', title: '写入当前 GoNavi 配置', detail: '只改用户级 MCP 配置,不会重装 GoNavi。' }, + { step: '3', title: '重启对应客户端', detail: '重启后就能在外部 CLI 里调用当前 GoNavi MCP。' }, + ].map((item) => ( +
+
+
+ {item.step} +
+
{item.title}
+
+
{item.detail}
+
+ ))}
@@ -222,8 +284,9 @@ const AIMCPClientInstallPanel: React.FC = ({ fontWeight: 700, color: tone.color, background: tone.bg, - minWidth: 76, + width: 80, textAlign: 'center', + flexShrink: 0, }} > {tone.label} @@ -233,7 +296,7 @@ const AIMCPClientInstallPanel: React.FC = ({ {getClientOptionSummary(status)}
- {active ? '当前已选中这个客户端。' : '点击后切换到这个客户端。'} + {active ? '当前已选中,将只对这个客户端执行写入或更新。' : '点击后切换到这个客户端。'}
); @@ -256,6 +319,9 @@ const AIMCPClientInstallPanel: React.FC = ({
{selectedStatus?.displayName || '客户端'} 状态
+
+ 已选目标:{selectedStatus?.displayName || '未选择客户端'} +
{getStatusSummary(selectedStatus)} @@ -277,11 +343,7 @@ const AIMCPClientInstallPanel: React.FC = ({
- 当前状态:{selectedStatus?.matchesCurrent - ? '当前 GoNavi MCP 已接入到这个客户端' - : selectedStatus?.installed - ? '检测到旧配置,建议更新到当前安装路径' - : '当前还没有把 GoNavi MCP 接入到这个客户端'} + 当前状态:{getSelectedClientStateLine(selectedStatus)}
CLI 检测:{selectedStatus?.clientDetected @@ -341,14 +403,14 @@ const AIMCPClientInstallPanel: React.FC = ({
{getClientDetectionSummary(selectedStatus)} {' '} - 已经是当前配置时按钮会自动禁用,避免重复写入。 + 已经是当前配置时,下面的主按钮会自动禁用,避免重复写入。