mirror of
https://github.com/Syngnat/GoNavi.git
synced 2026-06-14 10:29:52 +08:00
✨ feat(ai): 完善 MCP 新增参数指引
- 新增 npx MCP 服务模板和启动预览 - 补充 command/args 表单说明与内置指引快照 - 覆盖 MCP 参数草稿、指引和 AI 配置检查测试
This commit is contained in:
@@ -40,7 +40,7 @@ describe('AIMCPServerCard', () => {
|
||||
expect(markup).toContain('保存后显示给你和 AI 看的名字');
|
||||
expect(markup).toContain('示例值:');
|
||||
expect(markup).toContain('Filesystem / Browser / GitHub');
|
||||
expect(markup).toContain('server.js / --stdio / -m / your_mcp_server');
|
||||
expect(markup).toContain('-y / @modelcontextprotocol/server-filesystem / --stdio / server.js');
|
||||
expect(markup).toContain('当前固定为 stdio');
|
||||
expect(markup).toContain('单次工具发现或调用最多等待多久');
|
||||
expect(markup).toContain('必填');
|
||||
@@ -50,10 +50,12 @@ describe('AIMCPServerCard', () => {
|
||||
expect(markup).toContain('自动拆分到下方字段');
|
||||
expect(markup).toContain('$env:KEY=VALUE;');
|
||||
expect(markup).toContain('set KEY=VALUE &&');
|
||||
expect(markup).toContain('npx -y package --stdio');
|
||||
expect(markup).toContain('-y、@modelcontextprotocol/server-filesystem、--stdio、server.js');
|
||||
expect(markup).toContain('每个参数单独录入一个标签');
|
||||
expect(markup).toContain('每行一个 KEY=VALUE');
|
||||
expect(markup).toContain('没有等号或 key 含空格的行不会保存');
|
||||
expect(markup).toContain('不要把 node server.js --stdio 整串都塞进这里');
|
||||
expect(markup).toContain('不要把 npx -y package --stdio 或 node server.js --stdio 整串都塞进这里');
|
||||
expect(markup).toContain('不要写 export');
|
||||
expect(markup).toContain('当前阶段只支持 stdio');
|
||||
expect(markup).toContain('实际启动命令预览');
|
||||
@@ -67,6 +69,7 @@ describe('AIMCPServerCard', () => {
|
||||
expect(markup).toContain('默认 20 秒');
|
||||
expect(markup).toContain('稍宽松 45 秒');
|
||||
expect(markup).toContain('慢启动 60 秒');
|
||||
expect(markup).toContain('npx -y @modelcontextprotocol/server-filesystem --stdio');
|
||||
expect(markup).toContain('node server.js --stdio');
|
||||
expect(markup).toContain('$env:GITHUB_TOKEN=...; uvx mcp-server-github --stdio');
|
||||
});
|
||||
|
||||
@@ -73,11 +73,11 @@ const AIMCPServerFormPanel: React.FC<AIMCPServerFormPanelProps> = ({
|
||||
options={[{ label: 'stdio', value: 'stdio' }]}
|
||||
/>
|
||||
</AIMCPHelpBlock>
|
||||
<AIMCPHelpBlock title="启动命令" description="这里只填命令本身;如果是 node/uvx/python 这类启动器,把脚本名或模块名放到下面的参数里。不要把 node server.js --stdio 整串都塞进这里。" overlayTheme={overlayTheme} darkMode={darkMode} fieldState="required" example="node / uvx / python">
|
||||
<AIMCPHelpBlock title="启动命令" description="这里只填命令本身;如果是 npx/node/uvx/python 这类启动器,把包名、脚本名或模块名放到下面的参数里。不要把 npx -y package --stdio 或 node server.js --stdio 整串都塞进这里。" overlayTheme={overlayTheme} darkMode={darkMode} fieldState="required" example="npx / node / uvx / python">
|
||||
<Input
|
||||
value={server.command}
|
||||
onChange={(event) => onChange({ command: event.target.value })}
|
||||
placeholder="启动命令,例如:node / uvx / python"
|
||||
placeholder="启动命令,例如:npx / node / uvx / python"
|
||||
style={{ borderRadius: 10, background: inputBg, border: `1px solid ${cardBorder}` }}
|
||||
/>
|
||||
</AIMCPHelpBlock>
|
||||
@@ -120,12 +120,12 @@ const AIMCPServerFormPanel: React.FC<AIMCPServerFormPanelProps> = ({
|
||||
</AIMCPHelpBlock>
|
||||
</div>
|
||||
|
||||
<AIMCPHelpBlock title="命令参数" description="每个参数单独录入一个标签;命令本体不要填在这里。比如 node server.js --stdio,要把 server.js 和 --stdio 分开填。不确定怎么拆时,优先回到上面的“完整命令”框自动拆分。" overlayTheme={overlayTheme} darkMode={darkMode} fieldState="optional" example="server.js、--stdio、-m、your_mcp_server">
|
||||
<AIMCPHelpBlock title="命令参数" description="每个参数单独录入一个标签;命令本体不要填在这里。比如 npx -y package --stdio,要把 -y、package 和 --stdio 分开填;node server.js --stdio 要把 server.js 和 --stdio 分开填。不确定怎么拆时,优先回到上面的“完整命令”框自动拆分。" overlayTheme={overlayTheme} darkMode={darkMode} fieldState="optional" example="-y、@modelcontextprotocol/server-filesystem、--stdio、server.js">
|
||||
<Select
|
||||
mode="tags"
|
||||
value={server.args || []}
|
||||
onChange={(value) => onChange({ args: value })}
|
||||
placeholder="命令参数,回车录入,例如:server.js、--stdio"
|
||||
placeholder="命令参数,回车录入,例如:-y、包名、--stdio"
|
||||
style={{ width: '100%' }}
|
||||
/>
|
||||
</AIMCPHelpBlock>
|
||||
|
||||
@@ -111,9 +111,12 @@ describe('AISettingsMCPSection', () => {
|
||||
expect(markup).toContain('给 MCP Server 传入 KEY=VALUE 形式的配置');
|
||||
expect(markup).toContain('单次工具发现或调用最多等待多久');
|
||||
expect(markup).toContain('常见启动方式模板');
|
||||
expect(markup).toContain('npx 包');
|
||||
expect(markup).toContain('npx -y @modelcontextprotocol/server-filesystem --stdio');
|
||||
expect(markup).toContain('Node 脚本');
|
||||
expect(markup).toContain('新增 MCP 服务');
|
||||
expect(markup).toContain('还没有 MCP 服务');
|
||||
expect(markup).toContain('npx -y package --stdio');
|
||||
});
|
||||
|
||||
it('renders troubleshooting hints when a server draft exists', () => {
|
||||
@@ -149,16 +152,16 @@ describe('AISettingsMCPSection', () => {
|
||||
onAddServer,
|
||||
}));
|
||||
|
||||
const nodeTemplateButton = findElement(
|
||||
const npxTemplateButton = findElement(
|
||||
tree,
|
||||
(node) => node.type === 'button' && flattenElementText(node.props?.children).includes('Node 脚本'),
|
||||
(node) => node.type === 'button' && flattenElementText(node.props?.children).includes('npx 包'),
|
||||
);
|
||||
expect(nodeTemplateButton).toBeTruthy();
|
||||
nodeTemplateButton.props.onClick();
|
||||
expect(npxTemplateButton).toBeTruthy();
|
||||
npxTemplateButton.props.onClick();
|
||||
|
||||
expect(onAddServer).toHaveBeenCalledWith(expect.objectContaining({
|
||||
command: 'node',
|
||||
args: ['server.js', '--stdio'],
|
||||
command: 'npx',
|
||||
args: ['-y', '@modelcontextprotocol/server-filesystem', '--stdio'],
|
||||
}));
|
||||
});
|
||||
});
|
||||
|
||||
@@ -164,7 +164,7 @@ const AISettingsMCPSection: React.FC<AISettingsMCPSectionProps> = ({
|
||||
</div>
|
||||
{mcpServers.length === 0 && (
|
||||
<div style={{ padding: '18px 16px', borderRadius: 14, border: `1px dashed ${cardBorder}`, background: cardBg, color: overlayTheme.mutedText }}>
|
||||
还没有 MCP 服务。常见形式是 `node server.js`、`uvx some-mcp-server`、`python -m server`。
|
||||
还没有 MCP 服务。常见形式是 `npx -y package --stdio`、`node server.js`、`uvx some-mcp-server`、`python -m server`。
|
||||
</div>
|
||||
)}
|
||||
{mcpServers.map((server) => (
|
||||
|
||||
@@ -315,8 +315,10 @@ describe('aiLocalToolExecutor AI config inspection tools', () => {
|
||||
expect(result.content).toContain('"supportsWholeCommandAutoSplit":true');
|
||||
expect(result.content).toContain('"fullCommandPasteExample":"$env:GITHUB_TOKEN=...; uvx mcp-server-github --stdio"');
|
||||
expect(result.content).toContain('"title":"启动命令"');
|
||||
expect(result.content).toContain('"example":"node / uvx / python"');
|
||||
expect(result.content).toContain('"example":"npx / node / uvx / python"');
|
||||
expect(result.content).toContain('PowerShell $env:KEY=VALUE;');
|
||||
expect(result.content).toContain('"title":"npx 包"');
|
||||
expect(result.content).toContain('"exampleLaunchPreview":"npx -y @modelcontextprotocol/server-filesystem --stdio"');
|
||||
expect(result.content).toContain('"title":"uvx 工具"');
|
||||
expect(result.content).toContain('"exampleLaunchPreview":"uvx some-mcp-server"');
|
||||
});
|
||||
|
||||
@@ -133,14 +133,14 @@ export const BUILTIN_AI_INSPECTION_TOOL_INFO: AIBuiltinToolInfo[] = [
|
||||
icon: "🧭",
|
||||
desc: "查看新增 MCP 的填写指引",
|
||||
detail:
|
||||
"返回新增 MCP 表单里各字段的作用、推荐填写顺序、完整命令自动拆分规则,以及 Node / uvx / Python / EXE 模板样例。适合用户问“command/args/env 到底怎么填”“给我一个 node / uvx / python 示例”“为什么启动命令不能整行填”时,先读这份真实接入指引。",
|
||||
"返回新增 MCP 表单里各字段的作用、推荐填写顺序、完整命令自动拆分规则,以及 npx / Node / uvx / Python / EXE 模板样例。适合用户问“command/args/env 到底怎么填”“给我一个 npx / node / uvx / python 示例”“为什么启动命令不能整行填”时,先读这份真实接入指引。",
|
||||
params: "无参数",
|
||||
tool: {
|
||||
type: "function",
|
||||
function: {
|
||||
name: "inspect_mcp_authoring_guide",
|
||||
description:
|
||||
"读取 GoNavi 当前内置的 MCP 新增指引,包括推荐填写顺序、字段作用、常见命令示例、完整命令自动拆分规则,以及 Node / uvx / Python / EXE 模板样例。适用于用户提到新增 MCP 不知道 command、args、env、timeout 怎么填,或想要一个最接近的模板时,先读取这份真实前端接入指南,不要凭记忆口述。",
|
||||
"读取 GoNavi 当前内置的 MCP 新增指引,包括推荐填写顺序、字段作用、常见命令示例、完整命令自动拆分规则,以及 npx / Node / uvx / Python / EXE 模板样例。适用于用户提到新增 MCP 不知道 command、args、env、timeout 怎么填,或想要一个最接近的模板时,先读取这份真实前端接入指南,不要凭记忆口述。",
|
||||
parameters: { type: "object", properties: {} },
|
||||
},
|
||||
},
|
||||
|
||||
@@ -70,6 +70,17 @@ describe('mcpCommandDraft helpers', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('keeps npx package launches as command plus split args', () => {
|
||||
const result = parseMCPCommandDraft('npx -y @modelcontextprotocol/server-filesystem --stdio C:\\Users\\me\\workspace');
|
||||
|
||||
expect(result.ok).toBe(true);
|
||||
expect(result.draft).toEqual({
|
||||
command: 'npx',
|
||||
args: ['-y', '@modelcontextprotocol/server-filesystem', '--stdio', 'C:\\Users\\me\\workspace'],
|
||||
env: {},
|
||||
});
|
||||
});
|
||||
|
||||
it('reports unclosed quotes instead of producing a broken parse', () => {
|
||||
expect(splitShellLikeCommand('uvx "broken command')).toEqual({
|
||||
tokens: ['uvx'],
|
||||
|
||||
@@ -15,6 +15,7 @@ describe('mcpServerGuidance', () => {
|
||||
expect(symptoms).toContain('测试提示找不到命令');
|
||||
expect(symptoms).toContain('认证失败、401 或 403');
|
||||
expect(allGuidance).toContain('命令参数');
|
||||
expect(allGuidance).toContain('command=npx');
|
||||
expect(allGuidance).toContain('KEY=VALUE');
|
||||
expect(allGuidance).toContain('当前只支持 stdio');
|
||||
});
|
||||
@@ -24,6 +25,7 @@ describe('mcpServerGuidance', () => {
|
||||
|
||||
expect(notes).toContain('本机配置');
|
||||
expect(notes).toContain('不要把密钥写进聊天内容');
|
||||
expect(notes).toContain('command 填 npx');
|
||||
expect(notes).toContain('PowerShell $env:KEY=VALUE;');
|
||||
expect(notes).toContain('Windows set KEY=VALUE &&');
|
||||
});
|
||||
|
||||
@@ -24,6 +24,7 @@ export interface MCPTroubleshootingGuide {
|
||||
}
|
||||
|
||||
export const MCP_COMMAND_EXAMPLES = [
|
||||
'npx -y @modelcontextprotocol/server-filesystem --stdio',
|
||||
'uvx mcp-server-fetch',
|
||||
'node server.js --stdio',
|
||||
'python -m your_mcp_server',
|
||||
@@ -68,17 +69,17 @@ export const MCP_FIELD_GUIDES: MCPFieldGuide[] = [
|
||||
key: 'command',
|
||||
title: '启动命令',
|
||||
summary: '只填程序名或启动器本身。',
|
||||
detail: '常见是 node、uvx、python;脚本名和 --stdio 这类内容放到参数里。',
|
||||
detail: '常见是 npx、node、uvx、python;包名、脚本名和 --stdio 这类内容放到参数里。',
|
||||
fieldState: 'required',
|
||||
example: 'node / uvx / python',
|
||||
example: 'npx / node / uvx / python',
|
||||
},
|
||||
{
|
||||
key: 'args',
|
||||
title: '命令参数',
|
||||
summary: '把脚本名、模块名、开关参数拆开逐项填写。',
|
||||
detail: '例如 node server.js --stdio,要拆成 server.js 和 --stdio 两项。',
|
||||
detail: '例如 npx -y pkg --stdio,要拆成 -y、pkg 和 --stdio;node server.js --stdio 要拆成 server.js 和 --stdio。',
|
||||
fieldState: 'optional',
|
||||
example: 'server.js / --stdio / -m / your_mcp_server',
|
||||
example: '-y / @modelcontextprotocol/server-filesystem / --stdio / server.js',
|
||||
},
|
||||
{
|
||||
key: 'env',
|
||||
@@ -100,6 +101,7 @@ export const MCP_FIELD_GUIDES: MCPFieldGuide[] = [
|
||||
|
||||
export const MCP_AUTHORING_NOTES = [
|
||||
'启动命令只填程序本身,不要把脚本名、模块名和 --stdio 混进去。',
|
||||
'README 给 npx 示例时,command 填 npx,args 逐项填 -y、包名和 --stdio;不要把整行 npx 命令放进 command。',
|
||||
'如果 README 里只给了一整行命令,优先粘到完整命令框自动拆分;支持 KEY=VALUE、env KEY=VALUE、PowerShell $env:KEY=VALUE; 和 Windows set KEY=VALUE && 这几类前缀环境变量写法。',
|
||||
'环境变量每行一条 KEY=VALUE,不要写 export,也不要和启动命令混成一行保存。',
|
||||
'密钥类环境变量会保存到本机配置,并只在启动 MCP 进程时作为进程环境传入;不要把密钥写进聊天内容。',
|
||||
@@ -112,7 +114,7 @@ export const MCP_TROUBLESHOOTING_GUIDES: MCPTroubleshootingGuide[] = [
|
||||
symptom: '测试提示找不到命令',
|
||||
likelyCause: '启动命令填了整串命令、命令没加入 PATH,或 Windows 路径里有空格但没有用真实 exe 路径。',
|
||||
fix: '启动命令只填可执行程序本身;脚本名和 --stdio 放到命令参数里。命令不在 PATH 时,直接填绝对路径。',
|
||||
example: 'command=node, args=server.js / --stdio',
|
||||
example: 'command=npx, args=-y / @modelcontextprotocol/server-filesystem / --stdio',
|
||||
},
|
||||
{
|
||||
key: 'timeout-or-no-tools',
|
||||
|
||||
@@ -9,6 +9,19 @@ export interface MCPServerDraftTemplate {
|
||||
}
|
||||
|
||||
export const MCP_SERVER_DRAFT_TEMPLATES: MCPServerDraftTemplate[] = [
|
||||
{
|
||||
key: 'npx',
|
||||
title: 'npx 包',
|
||||
description: '适合 README 里写着 `npx -y xxx --stdio` 的 npm MCP 包。',
|
||||
detail: '示例会填成 `npx -y @modelcontextprotocol/server-filesystem --stdio`,把包名和路径参数改成实际值。',
|
||||
seed: {
|
||||
name: 'npx 包',
|
||||
command: 'npx',
|
||||
args: ['-y', '@modelcontextprotocol/server-filesystem', '--stdio'],
|
||||
env: {},
|
||||
timeoutSeconds: 20,
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'uvx',
|
||||
title: 'uvx 工具',
|
||||
|
||||
Reference in New Issue
Block a user