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

@@ -7,10 +7,10 @@ import type {
AgentValidationIssue,
} from '../types';
import {computed, reactive} from 'vue';
import { computed, reactive } from 'vue';
const BASE_NODE_ID = 'agent-base';
const SAFE_TOOL_NAME_PATTERN = /^[a-zA-Z0-9_-]+$/;
const SAFE_TOOL_NAME_PATTERN = /^[\w-]+$/;
function createLocalId(prefix: string) {
return `${prefix}-${Date.now()}-${Math.random().toString(16).slice(2, 8)}`;
@@ -25,6 +25,15 @@ function buildFallbackToolName(prefix: string, resource?: Record<string, any>) {
return `${prefix}_${id}`;
}
function toolKindFromType(
toolType?: string,
): Exclude<AgentCapabilityKind, 'knowledge'> {
const normalized = String(toolType || '').toUpperCase();
if (normalized === 'WORKFLOW') return 'workflow';
if (normalized === 'MCP') return 'mcp';
return 'plugin';
}
function resolveToolName(
kind: Exclude<AgentCapabilityKind, 'knowledge'>,
resource?: Record<string, any>,
@@ -36,19 +45,19 @@ function resolveToolName(
return String(resource?.name);
}
return buildFallbackToolName(
kind === 'workflow' ? 'workflow' : 'plugin',
kind === 'workflow' ? 'workflow' : kind === 'mcp' ? 'mcp' : 'plugin',
resource,
);
}
function normalizeBindingToolName(binding: AgentToolBinding) {
if (String(binding.toolType || '').toUpperCase() === 'MCP') {
return '';
}
if (isSafeToolName(binding.toolName)) {
return String(binding.toolName);
}
const kind =
String(binding.toolType || '').toUpperCase() === 'WORKFLOW'
? 'workflow'
: 'plugin';
const kind = toolKindFromType(binding.toolType);
const resource = {
...(binding.resourceSnapshot || {}),
...(binding.resourceSummary || {}),
@@ -178,10 +187,7 @@ export function useAgentDesignerState() {
(item) => item.localId === localId,
);
return {
kind:
String(binding?.toolType || '').toUpperCase() === 'WORKFLOW'
? ('workflow' as AgentCapabilityKind)
: ('plugin' as AgentCapabilityKind),
kind: toolKindFromType(binding?.toolType) as AgentCapabilityKind,
binding,
};
}
@@ -237,12 +243,17 @@ export function useAgentDesignerState() {
kind: Exclude<AgentCapabilityKind, 'knowledge'>,
resource?: Record<string, any>,
) {
const toolType = kind === 'workflow' ? 'WORKFLOW' : 'PLUGIN';
const toolType =
kind === 'workflow' ? 'WORKFLOW' : kind === 'mcp' ? 'MCP' : 'PLUGIN';
const binding = normalizeToolBinding(
{
toolType,
targetId: resource?.id ? String(resource.id) : '',
toolName: resolveToolName(kind, resource),
targetId: resource?.mcpId
? String(resource.mcpId)
: resource?.id
? String(resource.id)
: '',
toolName: kind === 'mcp' ? '' : resolveToolName(kind, resource),
resourceSummary: resource || {},
},
state.toolBindings.length,
@@ -299,6 +310,9 @@ export function useAgentDesignerState() {
if (!binding.targetId) {
issues.push({ nodeId, field: 'targetId', message: '请选择能力资源' });
}
if (String(binding.toolType || '').toUpperCase() === 'MCP') {
return;
}
if (!String(binding.toolName || '').trim()) {
issues.push({ nodeId, field: 'toolName', message: '请填写工具名称' });
} else if (!isSafeToolName(binding.toolName)) {
@@ -340,13 +354,17 @@ export function useAgentDesignerState() {
}
function buildToolPayload(agentId?: number | string) {
return state.toolBindings.map((binding, index) => ({
...binding,
agentId,
enabled: binding.enabled !== false,
hitlEnabled: Boolean(binding.hitlEnabled),
sortNo: index + 1,
}));
return state.toolBindings.map((binding, index) => {
const isMcp = String(binding.toolType || '').toUpperCase() === 'MCP';
return {
...binding,
agentId,
enabled: binding.enabled !== false,
hitlEnabled: Boolean(binding.hitlEnabled),
toolName: isMcp ? '' : binding.toolName,
sortNo: index + 1,
};
});
}
reset();