feat(ai): 增强 MCP 远程接入与上下文诊断

This commit is contained in:
Syngnat
2026-06-11 07:29:04 +08:00
parent d3278bb4c4
commit 26fb650e04
25 changed files with 923 additions and 8 deletions

View File

@@ -701,6 +701,30 @@ export const BUILTIN_AI_INSPECTION_TOOL_INFO: AIBuiltinToolInfo[] = [
},
},
},
{
name: "inspect_ai_context_budget",
icon: "📦",
desc: "诊断 AI 上下文体量与稳定性风险",
detail:
"统计当前或指定 AI 会话的最近消息、工具结果、已挂载表结构、MCP 工具 schema、用户提示词和 Skills 体量,返回 low/medium/high/critical 风险、主要膨胀来源和收窄建议。适合用户反馈 AI 变慢、乱答、上下文太大、工具结果过长或表结构挂太多时先做预算体检。",
params: "sessionId?(默认当前会话), messageLimit?(默认 40), includeDetails?(默认 true)",
tool: {
type: "function",
function: {
name: "inspect_ai_context_budget",
description:
"读取当前 AI 上下文体量与稳定性风险快照包括最近消息窗口、tool 结果长度、已挂载表结构 DDL、MCP 工具 schema、用户提示词和启用 Skills 的估算体量,并返回风险级别、告警和收窄建议。适用于用户提到 AI 回复变慢、上下文过大、表结构带太多、工具结果过长、模型开始乱答或复杂任务前需要判断是否应拆小上下文时优先调用。",
parameters: {
type: "object",
properties: {
sessionId: { type: "string", description: "可选,指定要诊断的 AI 会话 ID不传时读取当前活动会话" },
messageLimit: { type: "number", description: "可选,最多统计最近多少条消息,默认 40最大 120" },
includeDetails: { type: "boolean", description: "可选,是否返回最大消息、最大 DDL 表和最大 MCP schema 明细,默认 true" },
},
},
},
},
},
{
name: "inspect_sql_snippets",
icon: "🧩",

View File

@@ -184,6 +184,11 @@ export const BUILTIN_TOOL_FLOWS: AIBuiltinToolFlow[] = [
steps: 'inspect_ai_message_flow -> inspect_ai_last_render_error / inspect_app_logs',
description: '适合用户反馈回复被拆成多个气泡、工具调用后没继续回答、消息流状态不对时,先读取当前会话的真实消息结构和异常信号。',
},
{
title: '诊断 AI 上下文体量',
steps: 'inspect_ai_context_budget -> inspect_ai_context / inspect_ai_message_flow / inspect_ai_tool_catalog',
description: '适合用户反馈 AI 变慢、乱答、上下文太大、工具结果过长或表结构挂太多时先看消息、DDL、MCP schema、提示词和 Skills 的体量来源,再决定收窄上下文或拆任务。',
},
{
title: '复用历史 SQL',
steps: 'inspect_saved_queries -> get_columns / execute_sql',

View File

@@ -162,6 +162,14 @@ describe('aiToolRegistry', () => {
expect(info?.tool.function.description).toContain('连续 assistant 消息');
});
it('registers the ai-context-budget inspector as a builtin tool', () => {
const info = BUILTIN_AI_TOOL_INFO.find((item) => item.name === 'inspect_ai_context_budget');
expect(info).toBeTruthy();
expect(info?.desc).toContain('上下文体量');
expect(info?.tool.function.description).toContain('MCP 工具 schema');
expect(info?.tool.function.parameters?.properties?.messageLimit?.description).toContain('最大 120');
});
it('registers the recent-sql-activity, saved-query, and sql-snippet inspectors as builtin tools', () => {
const recentActivityTool = BUILTIN_AI_TOOL_INFO.find((item) => item.name === 'inspect_recent_sql_activity');
const sqlEditorTransactionTool = BUILTIN_AI_TOOL_INFO.find((item) => item.name === 'inspect_sql_editor_transaction');
@@ -236,6 +244,7 @@ describe('aiToolRegistry', () => {
expect(tools.some((item) => item.function.name === 'inspect_recent_connection_failures')).toBe(true);
expect(tools.some((item) => item.function.name === 'inspect_ai_last_render_error')).toBe(true);
expect(tools.some((item) => item.function.name === 'inspect_ai_message_flow')).toBe(true);
expect(tools.some((item) => item.function.name === 'inspect_ai_context_budget')).toBe(true);
expect(tools.some((item) => item.function.name === 'inspect_saved_queries')).toBe(true);
expect(tools.some((item) => item.function.name === 'inspect_ai_sessions')).toBe(true);
expect(tools.some((item) => item.function.name === 'inspect_sql_snippets')).toBe(true);

View File

@@ -137,11 +137,13 @@ describe('mcpClientInstallStatus helpers', () => {
expect(guide).toContain('allowMutating=true');
expect(guide).toContain('"type": "streamable-http"');
expect(guide).toContain('"Authorization": "Bearer <随机token>"');
expect(guide).toContain('GoNavi.exe mcp-server remote-config --client openclaw --url https://<你的域名或隧道地址>/mcp --token <随机token>');
expect(guide).toContain('GoNavi.exe mcp-server http --addr 127.0.0.1:8765 --path /mcp --token <随机token>');
});
it('builds remote quick-start snippets for cloud agents without database secrets', () => {
const quickStart = buildRemoteMCPClientQuickStart({
client: 'hermans',
displayName: 'OpenClaw',
});
@@ -150,6 +152,7 @@ describe('mcpClientInstallStatus helpers', () => {
expect(quickStart.configJson).toContain('"url": "https://<你的域名或隧道地址>/mcp"');
expect(quickStart.configJson).toContain('"Authorization": "Bearer <随机token>"');
expect(quickStart.configJson).not.toContain('password');
expect(quickStart.configCommand).toBe('GoNavi.exe mcp-server remote-config --client hermans --url https://<你的域名或隧道地址>/mcp --token <随机token>');
expect(quickStart.launchCommand).toBe('GoNavi.exe mcp-server http --addr 127.0.0.1:8765 --path /mcp --token <随机token>');
expect(quickStart.standaloneCommand).toBe('gonavi-mcp-server http --addr 127.0.0.1:8765 --path /mcp --token <随机token>');
expect(quickStart.verificationSteps.join('\n')).toContain('get_connections');

View File

@@ -11,6 +11,7 @@ const DEFAULT_REMOTE_MCP_PATH = '/mcp';
export interface RemoteMCPClientQuickStart {
displayName: string;
configJson: string;
configCommand: string;
launchCommand: string;
standaloneCommand: string;
verificationSteps: string[];
@@ -170,7 +171,7 @@ export const formatMCPLaunchCommand = (
};
export const buildRemoteMCPClientGuide = (
status?: Pick<AIMCPClientInstallStatus, 'displayName' | 'message'> | null,
status?: Partial<Pick<AIMCPClientInstallStatus, 'client' | 'displayName' | 'message'>> | null,
): string => {
const quickStart = buildRemoteMCPClientQuickStart(status);
return [
@@ -194,6 +195,9 @@ export const buildRemoteMCPClientGuide = (
'可复制配置片段(适用于支持 mcpServers JSON 的 Agent',
...quickStart.configJson.split('\n'),
'',
'无 GUI / CLI 生成配置命令:',
quickStart.configCommand,
'',
'CLI / 服务启动命令:',
quickStart.launchCommand,
`或设置环境变量GONAVI_MCP_HTTP_TOKEN=<随机token> 后运行 ${quickStart.standaloneCommand.replace(' --token <随机token>', '')}`,
@@ -203,11 +207,13 @@ export const buildRemoteMCPClientGuide = (
};
export const buildRemoteMCPClientQuickStart = (
status?: Pick<AIMCPClientInstallStatus, 'displayName'> | null,
status?: Partial<Pick<AIMCPClientInstallStatus, 'client' | 'displayName'>> | null,
): RemoteMCPClientQuickStart => {
const displayName = String(status?.displayName || '远程 Agent').trim();
const client = isMCPClientKey(String(status?.client || '')) ? String(status?.client || '').trim() : 'openclaw';
const launchCommand = `GoNavi.exe mcp-server http --addr ${DEFAULT_REMOTE_MCP_LOCAL_ADDR} --path ${DEFAULT_REMOTE_MCP_PATH} --token <随机token>`;
const standaloneCommand = `gonavi-mcp-server http --addr ${DEFAULT_REMOTE_MCP_LOCAL_ADDR} --path ${DEFAULT_REMOTE_MCP_PATH} --token <随机token>`;
const configCommand = `GoNavi.exe mcp-server remote-config --client ${client} --url ${DEFAULT_REMOTE_MCP_PUBLIC_URL} --token <随机token>`;
const configJson = JSON.stringify({
mcpServers: {
gonavi: {
@@ -223,6 +229,7 @@ export const buildRemoteMCPClientQuickStart = (
return {
displayName,
configJson,
configCommand,
launchCommand,
standaloneCommand,
verificationSteps: [