mirror of
https://github.com/Syngnat/GoNavi.git
synced 2026-07-03 00:41:24 +08:00
✨ feat(ai): 完善 MCP 新增字段填写提示
- 增加 command、args、env、timeout 的应填与勿填对照 - 抽取 MCP 字段指南卡片复用速查与表单说明 - 补充 AI 设置 MCP 区域渲染测试
This commit is contained in:
71
frontend/src/components/ai/AIMCPFieldGuideCard.tsx
Normal file
71
frontend/src/components/ai/AIMCPFieldGuideCard.tsx
Normal file
@@ -0,0 +1,71 @@
|
||||
import React from 'react';
|
||||
|
||||
import type { OverlayWorkbenchTheme } from '../../utils/overlayWorkbenchTheme';
|
||||
import type { MCPFieldGuide } from '../../utils/mcpServerGuidance';
|
||||
import { buildMCPFieldTone, buildMCPHintStyle } from './AIMCPHelpBlock';
|
||||
|
||||
interface AIMCPFieldGuideCardProps {
|
||||
item: MCPFieldGuide;
|
||||
cardBorder: string;
|
||||
darkMode: boolean;
|
||||
overlayTheme: OverlayWorkbenchTheme;
|
||||
compact?: boolean;
|
||||
}
|
||||
|
||||
const AIMCPFieldGuideCard: React.FC<AIMCPFieldGuideCardProps> = ({
|
||||
item,
|
||||
cardBorder,
|
||||
darkMode,
|
||||
overlayTheme,
|
||||
compact = false,
|
||||
}) => {
|
||||
const tone = buildMCPFieldTone(item.fieldState, darkMode);
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
padding: compact ? '10px 12px' : '10px 12px',
|
||||
borderRadius: compact ? 12 : 10,
|
||||
border: `1px solid ${cardBorder}`,
|
||||
background: darkMode ? 'rgba(255,255,255,0.025)' : 'rgba(255,255,255,0.78)',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
gap: 6,
|
||||
}}
|
||||
>
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: 8, flexWrap: 'wrap' }}>
|
||||
<div style={{ fontSize: 12, fontWeight: 700, color: overlayTheme.titleText }}>{item.title}</div>
|
||||
<span
|
||||
style={{
|
||||
padding: '2px 8px',
|
||||
borderRadius: 999,
|
||||
fontSize: 11,
|
||||
fontWeight: 700,
|
||||
color: tone.color,
|
||||
background: tone.bg,
|
||||
}}
|
||||
>
|
||||
{tone.label}
|
||||
</span>
|
||||
</div>
|
||||
<div style={{ fontSize: 12, lineHeight: 1.6, color: overlayTheme.titleText }}>{item.summary}</div>
|
||||
{!compact && <div style={buildMCPHintStyle(overlayTheme.mutedText)}>{item.detail}</div>}
|
||||
<div style={buildMCPHintStyle(overlayTheme.mutedText)}>
|
||||
<strong>应填:</strong>
|
||||
{item.fill}
|
||||
</div>
|
||||
<div style={buildMCPHintStyle(overlayTheme.mutedText)}>
|
||||
<strong>不要填:</strong>
|
||||
{item.avoid}
|
||||
</div>
|
||||
{item.example ? (
|
||||
<div style={buildMCPHintStyle(overlayTheme.mutedText)}>
|
||||
示例值:
|
||||
{' '}
|
||||
<code style={{ fontFamily: 'var(--gn-font-mono)' }}>{item.example}</code>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default AIMCPFieldGuideCard;
|
||||
@@ -11,7 +11,8 @@ import {
|
||||
MCP_TROUBLESHOOTING_GUIDES,
|
||||
} from '../../utils/mcpServerGuidance';
|
||||
import AIMCPCommandDraftPreview from './AIMCPCommandDraftPreview';
|
||||
import { buildMCPFieldTone, buildMCPHintStyle, mcpLabelStyle } from './AIMCPHelpBlock';
|
||||
import AIMCPFieldGuideCard from './AIMCPFieldGuideCard';
|
||||
import { buildMCPHintStyle, mcpLabelStyle } from './AIMCPHelpBlock';
|
||||
|
||||
interface AIMCPServerGuidePanelProps {
|
||||
cardBorder: string;
|
||||
@@ -73,48 +74,15 @@ const AIMCPServerGuidePanel: React.FC<AIMCPServerGuidePanelProps> = ({
|
||||
如果看到某个参数名不知道该填什么,先看这一块;下面每个字段也都有更具体的示例和注意事项。
|
||||
</div>
|
||||
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(210px, 1fr))', gap: 10 }}>
|
||||
{MCP_FIELD_GUIDES.map((item) => {
|
||||
const tone = buildMCPFieldTone(item.fieldState, darkMode);
|
||||
return (
|
||||
<div
|
||||
key={item.key}
|
||||
style={{
|
||||
padding: '10px 12px',
|
||||
borderRadius: 10,
|
||||
border: `1px solid ${cardBorder}`,
|
||||
background: darkMode ? 'rgba(255,255,255,0.025)' : 'rgba(255,255,255,0.78)',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
gap: 6,
|
||||
}}
|
||||
>
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: 8, flexWrap: 'wrap' }}>
|
||||
<div style={{ fontSize: 12, fontWeight: 700, color: overlayTheme.titleText }}>{item.title}</div>
|
||||
<span
|
||||
style={{
|
||||
padding: '2px 8px',
|
||||
borderRadius: 999,
|
||||
fontSize: 11,
|
||||
fontWeight: 700,
|
||||
color: tone.color,
|
||||
background: tone.bg,
|
||||
}}
|
||||
>
|
||||
{tone.label}
|
||||
</span>
|
||||
</div>
|
||||
<div style={{ fontSize: 12, lineHeight: 1.6, color: overlayTheme.titleText }}>{item.summary}</div>
|
||||
<div style={buildMCPHintStyle(overlayTheme.mutedText)}>{item.detail}</div>
|
||||
{item.example ? (
|
||||
<div style={buildMCPHintStyle(overlayTheme.mutedText)}>
|
||||
示例值:
|
||||
{' '}
|
||||
<code style={{ fontFamily: 'var(--gn-font-mono)' }}>{item.example}</code>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
{MCP_FIELD_GUIDES.map((item) => (
|
||||
<AIMCPFieldGuideCard
|
||||
key={item.key}
|
||||
item={item}
|
||||
cardBorder={cardBorder}
|
||||
darkMode={darkMode}
|
||||
overlayTheme={overlayTheme}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -107,8 +107,13 @@ describe('AISettingsMCPSection', () => {
|
||||
expect(markup).toContain('env');
|
||||
expect(markup).toContain('timeout');
|
||||
expect(markup).toContain('只填程序名或启动器本身');
|
||||
expect(markup).toContain('应填:');
|
||||
expect(markup).toContain('填 npx、node、uvx、python,或某个 exe 的绝对路径');
|
||||
expect(markup).toContain('不要填整行命令,例如不要填 npx -y pkg --stdio');
|
||||
expect(markup).toContain('把脚本名、模块名、开关参数拆开逐项填写');
|
||||
expect(markup).toContain('不要再填 npx/node/uvx/python');
|
||||
expect(markup).toContain('给 MCP Server 传入 KEY=VALUE 形式的配置');
|
||||
expect(markup).toContain('不要写 export、set 或 $env: 前缀');
|
||||
expect(markup).toContain('单次工具发现或调用最多等待多久');
|
||||
expect(markup).toContain('常见启动方式模板');
|
||||
expect(markup).toContain('npx 包');
|
||||
|
||||
@@ -8,6 +8,7 @@ import { MCP_FIELD_GUIDES } from '../../utils/mcpServerGuidance';
|
||||
import { MCP_SERVER_DRAFT_TEMPLATES } from '../../utils/mcpServerTemplates';
|
||||
import type { OverlayWorkbenchTheme } from '../../utils/overlayWorkbenchTheme';
|
||||
import AIMCPClientInstallPanel from './AIMCPClientInstallPanel';
|
||||
import AIMCPFieldGuideCard from './AIMCPFieldGuideCard';
|
||||
import AIMCPServerCard from './AIMCPServerCard';
|
||||
|
||||
export type { MCPClientKey } from '../../utils/mcpClientInstallStatus';
|
||||
@@ -98,25 +99,14 @@ const AISettingsMCPSection: React.FC<AISettingsMCPSectionProps> = ({
|
||||
</div>
|
||||
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(170px, 1fr))', gap: 10 }}>
|
||||
{MCP_FIELD_GUIDES.filter((item) => ['command', 'args', 'env', 'timeout'].includes(item.key)).map((item) => (
|
||||
<div
|
||||
<AIMCPFieldGuideCard
|
||||
key={item.key}
|
||||
style={{
|
||||
padding: '10px 12px',
|
||||
borderRadius: 12,
|
||||
border: `1px solid ${cardBorder}`,
|
||||
background: darkMode ? 'rgba(255,255,255,0.03)' : 'rgba(255,255,255,0.72)',
|
||||
}}
|
||||
>
|
||||
<div style={{ fontSize: 12, fontWeight: 700, color: overlayTheme.titleText }}>{item.title}</div>
|
||||
<div style={{ marginTop: 4, fontSize: 12, color: overlayTheme.mutedText, lineHeight: 1.6 }}>{item.summary}</div>
|
||||
{item.example ? (
|
||||
<div style={{ marginTop: 6, fontSize: 12, color: overlayTheme.mutedText, lineHeight: 1.6 }}>
|
||||
例:
|
||||
{' '}
|
||||
<code style={{ fontFamily: 'var(--gn-font-mono)' }}>{item.example}</code>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
item={item}
|
||||
cardBorder={cardBorder}
|
||||
darkMode={darkMode}
|
||||
overlayTheme={overlayTheme}
|
||||
compact
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -5,6 +5,8 @@ export interface MCPFieldGuide {
|
||||
title: string;
|
||||
summary: string;
|
||||
detail: string;
|
||||
fill: string;
|
||||
avoid: string;
|
||||
fieldState: MCPFieldState;
|
||||
example?: string;
|
||||
}
|
||||
@@ -46,6 +48,8 @@ export const MCP_FIELD_GUIDES: MCPFieldGuide[] = [
|
||||
title: '服务名称',
|
||||
summary: '保存后显示给你和 AI 看的名字。',
|
||||
detail: '按用途命名,建议写成 Browser、GitHub、Filesystem 这类一眼能认出的名字。',
|
||||
fill: '这个 MCP 的用途名,例如 GitHub 或 Filesystem。',
|
||||
avoid: '不要写 server、test、mcp1 这类看不出用途的名字。',
|
||||
fieldState: 'required',
|
||||
example: 'Filesystem / Browser / GitHub',
|
||||
},
|
||||
@@ -54,6 +58,8 @@ export const MCP_FIELD_GUIDES: MCPFieldGuide[] = [
|
||||
title: '启用状态',
|
||||
summary: '控制这条配置现在要不要参与工具发现和调用。',
|
||||
detail: '禁用只是不使用,不会删除下面填好的配置。',
|
||||
fill: '临时不用选已禁用;确认要给 AI 用时选已启用。',
|
||||
avoid: '不要用删除代替临时停用,避免重新配置 command、args、env。',
|
||||
fieldState: 'optional',
|
||||
example: '已启用 / 已禁用',
|
||||
},
|
||||
@@ -62,6 +68,8 @@ export const MCP_FIELD_GUIDES: MCPFieldGuide[] = [
|
||||
title: '传输方式',
|
||||
summary: 'GoNavi 用什么方式和这个 MCP Server 通信。',
|
||||
detail: '当前固定为 stdio,表示本机直接启动进程并通过标准输入输出交互。',
|
||||
fill: '保持 stdio。',
|
||||
avoid: '不要填写 HTTP、SSE、URL 或端口;当前新增入口不是远程 MCP URL 配置。',
|
||||
fieldState: 'fixed',
|
||||
example: 'stdio',
|
||||
},
|
||||
@@ -70,6 +78,8 @@ export const MCP_FIELD_GUIDES: MCPFieldGuide[] = [
|
||||
title: '启动命令',
|
||||
summary: '只填程序名或启动器本身。',
|
||||
detail: '常见是 npx、node、uvx、python;包名、脚本名和 --stdio 这类内容放到参数里。',
|
||||
fill: '填 npx、node、uvx、python,或某个 exe 的绝对路径。',
|
||||
avoid: '不要填整行命令,例如不要填 npx -y pkg --stdio。',
|
||||
fieldState: 'required',
|
||||
example: 'npx / node / uvx / python',
|
||||
},
|
||||
@@ -78,6 +88,8 @@ export const MCP_FIELD_GUIDES: MCPFieldGuide[] = [
|
||||
title: '命令参数',
|
||||
summary: '把脚本名、模块名、开关参数拆开逐项填写。',
|
||||
detail: '例如 npx -y pkg --stdio,要拆成 -y、pkg 和 --stdio;node server.js --stdio 要拆成 server.js 和 --stdio。',
|
||||
fill: '逐项填 -y、包名、脚本名、-m、--stdio 等参数。',
|
||||
avoid: '不要再填 npx/node/uvx/python,也不要把多个参数粘成一个长字符串。',
|
||||
fieldState: 'optional',
|
||||
example: '-y / @modelcontextprotocol/server-filesystem / --stdio / server.js',
|
||||
},
|
||||
@@ -86,6 +98,8 @@ export const MCP_FIELD_GUIDES: MCPFieldGuide[] = [
|
||||
title: '环境变量',
|
||||
summary: '给 MCP Server 传入 KEY=VALUE 形式的配置。',
|
||||
detail: '通常用来放 API Key、服务地址、工作目录等;每行一条,不要写 export。',
|
||||
fill: '每行一条 KEY=VALUE,例如 GITHUB_TOKEN=...。',
|
||||
avoid: '不要写 export、set 或 $env: 前缀;也不要把环境变量混进 command 或 args。',
|
||||
fieldState: 'optional',
|
||||
example: 'OPENAI_API_KEY=... / GITHUB_TOKEN=...',
|
||||
},
|
||||
@@ -94,6 +108,8 @@ export const MCP_FIELD_GUIDES: MCPFieldGuide[] = [
|
||||
title: '超时(秒)',
|
||||
summary: '单次工具发现或调用最多等待多久。',
|
||||
detail: '本机常规工具一般 20 秒就够,启动慢或远端链路再适当调大。',
|
||||
fill: '常规填 20;启动慢时填 45 或 60。',
|
||||
avoid: '不要随意填过小,3 秒以下很容易让工具发现误判失败。',
|
||||
fieldState: 'optional',
|
||||
example: '20 / 45 / 60',
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user