feat: 完成 Agent MCP 对接

- 增加 MCP 连接类型、环境检测接口和容器运行环境支持

- 将 Agent 编排改为绑定整体 MCP 并编译为 runtime McpSpec

- 优化 MCP 工具展示、审批、草稿试运行和画布回显稳定性
This commit is contained in:
2026-05-29 11:09:21 +08:00
parent e39f7521e2
commit cc3bb9cff0
33 changed files with 2405 additions and 127 deletions

View File

@@ -12,7 +12,7 @@ import type {
AgentToolBinding,
} from '../../types';
import {computed} from 'vue';
import { computed } from 'vue';
const BASE_NODE_ID = 'agent-base';
const BASE_POSITION = { x: 430, y: 260 };
@@ -57,19 +57,66 @@ function buildKnowledgeTitle(
);
}
function buildToolTitle(binding: AgentToolBinding) {
function findMatchedToolOption(
binding: AgentToolBinding,
options: AgentOption[],
) {
const targetId = String(binding.targetId || '');
if (!targetId) return undefined;
return options.find((item) => {
const raw = item.raw || {};
return (
String(item.value) === targetId ||
String(raw.id || '') === targetId ||
String(raw.mcpId || '') === targetId
);
});
}
function buildToolTitle(binding: AgentToolBinding, options: AgentOption[] = []) {
const matchedOption = findMatchedToolOption(binding, options);
return firstText(
binding.resourceSummary?.title,
binding.resourceSummary?.name,
binding.resourceSummary?.label,
binding.resourceSummary?.displayName,
binding.resourceSummary?.mcpTitle,
binding.resourceSnapshot?.title,
binding.resourceSnapshot?.name,
binding.resourceSnapshot?.label,
binding.resourceSnapshot?.displayName,
binding.resourceSnapshot?.mcpTitle,
matchedOption?.label,
matchedOption?.raw?.title,
matchedOption?.raw?.name,
matchedOption?.raw?.label,
matchedOption?.raw?.displayName,
matchedOption?.raw?.mcpTitle,
binding.toolName,
);
}
function buildToolDetail(binding: AgentToolBinding, fallback: string) {
function buildToolDetail(
binding: AgentToolBinding,
fallback: string,
options: AgentOption[] = [],
) {
if (String(binding.toolType || '').toUpperCase() === 'MCP') {
const matchedOption = findMatchedToolOption(binding, options);
const resourceName = buildToolTitle(binding, options);
const tools =
binding.resourceSummary?.tools ||
binding.resourceSnapshot?.tools ||
matchedOption?.raw?.tools ||
[];
const toolCount = Array.isArray(tools) ? tools.length : 0;
if (resourceName && toolCount > 0) {
return `${resourceName} · ${toolCount} 个工具`;
}
return resourceName || fallback;
}
const toolName = firstText(binding.toolName);
const resourceName = buildToolTitle(binding);
const resourceName = buildToolTitle(binding, options);
if (toolName && resourceName && toolName !== resourceName) {
return `${resourceName} / ${toolName}`;
}
@@ -157,6 +204,11 @@ export function useAgentStudioModel(
layout?: AgentStudioLayoutSnapshot,
canvasSize?: () => AgentStudioCanvasSize | undefined,
knowledgeOptions?: () => AgentOption[],
toolOptions?: () => {
mcp?: AgentOption[];
plugin?: AgentOption[];
workflow?: AgentOption[];
},
) {
return computed(() => {
const positionOf = (nodeId: string, fallback: { x: number; y: number }) =>
@@ -214,10 +266,20 @@ export function useAgentStudioModel(
});
const toolNodes = state.toolBindings.map((binding, index) => {
const nodeId = `tool:${binding.localId}`;
const isWorkflow =
String(binding.toolType || '').toUpperCase() === 'WORKFLOW';
const fallback = isWorkflow ? '待选择工作流' : '待选择插件工具';
const detail = buildToolDetail(binding, fallback);
const toolType = String(binding.toolType || '').toUpperCase();
const isWorkflow = toolType === 'WORKFLOW';
const isMcp = toolType === 'MCP';
const matchedOptions = isWorkflow
? toolOptions?.().workflow || []
: isMcp
? toolOptions?.().mcp || []
: toolOptions?.().plugin || [];
const fallback = isWorkflow
? '待选择工作流'
: isMcp
? '待选择 MCP'
: '待选择插件工具';
const detail = buildToolDetail(binding, fallback, matchedOptions);
const position = resolveCapabilityNodePosition({
canvasSize: size,
fallbackIndex: state.knowledgeBindings.length + index,
@@ -233,14 +295,20 @@ export function useAgentStudioModel(
width: CAPABILITY_NODE_WIDTH,
height: CAPABILITY_NODE_HEIGHT,
data: {
badge: isWorkflow ? '工作流' : '插件',
badge: isWorkflow ? '工作流' : isMcp ? 'MCP' : '插件',
detail,
iconKey: isWorkflow ? 'workflow' : 'plugin',
iconKey: isWorkflow ? 'workflow' : isMcp ? 'mcp' : 'plugin',
id: nodeId,
kind: isWorkflow ? 'workflow' : 'plugin',
kind: isWorkflow ? 'workflow' : isMcp ? 'mcp' : 'plugin',
selected: selectedNodeId() === nodeId,
title:
detail === fallback ? (isWorkflow ? '工作流' : '插件') : detail,
detail === fallback
? isWorkflow
? '工作流'
: isMcp
? 'MCP'
: '插件'
: detail,
} satisfies AgentStudioNodeData,
};
});