mirror of
https://github.com/1Panel-dev/MaxKB.git
synced 2025-12-26 01:33:05 +00:00
Merge remote-tracking branch 'origin/v2' into v2
This commit is contained in:
commit
b1a70b7b99
|
|
@ -1,5 +1,5 @@
|
|||
# coding=utf-8
|
||||
|
||||
import ast
|
||||
import os
|
||||
import pickle
|
||||
import subprocess
|
||||
|
|
@ -83,6 +83,39 @@ except Exception as e:
|
|||
return result.get('data')
|
||||
raise Exception(result.get('msg'))
|
||||
|
||||
def generate_mcp_server_code(self, _code):
|
||||
self.validate_banned_keywords(_code)
|
||||
|
||||
# 解析代码,提取导入语句和函数定义
|
||||
try:
|
||||
tree = ast.parse(_code)
|
||||
except SyntaxError:
|
||||
return _code
|
||||
|
||||
imports = []
|
||||
functions = []
|
||||
other_code = []
|
||||
|
||||
for node in tree.body:
|
||||
if isinstance(node, ast.Import) or isinstance(node, ast.ImportFrom):
|
||||
imports.append(ast.unparse(node))
|
||||
elif isinstance(node, ast.FunctionDef):
|
||||
# 为函数添加 @mcp.tool() 装饰器
|
||||
func_code = ast.unparse(node)
|
||||
functions.append(f"@mcp.tool()\n{func_code}\n")
|
||||
else:
|
||||
other_code.append(ast.unparse(node))
|
||||
|
||||
# 构建完整的 MCP 服务器代码
|
||||
code_parts = ["from mcp.server.fastmcp import FastMCP"]
|
||||
code_parts.extend(imports)
|
||||
code_parts.append(f"\nmcp = FastMCP(\"{uuid.uuid7()}\")\n")
|
||||
code_parts.extend(other_code)
|
||||
code_parts.extend(functions)
|
||||
code_parts.append("\nmcp.run(transport=\"stdio\")\n")
|
||||
|
||||
return "\n".join(code_parts)
|
||||
|
||||
def _exec_sandbox(self, _code, _id):
|
||||
exec_python_file = f'{self.sandbox_path}/execute/{_id}.py'
|
||||
with open(exec_python_file, 'w') as file:
|
||||
|
|
|
|||
|
|
@ -0,0 +1,18 @@
|
|||
# Generated by Django 5.2.4 on 2025-08-11 09:45
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('knowledge', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='file',
|
||||
name='source_type',
|
||||
field=models.CharField(choices=[('KNOWLEDGE', 'Knowledge'), ('APPLICATION', 'Application'), ('TOOL', 'Tool'), ('DOCUMENT', 'Document'), ('CHAT', 'Chat'), ('SYSTEM', 'System'), ('TEMPORARY_30_MINUTE', 'Temporary 30 Minute'), ('TEMPORARY_120_MINUTE', 'Temporary 120 Minute'), ('TEMPORARY_1_DAY', 'Temporary 1 Day')], db_index=True, default='TEMPORARY_120_MINUTE', verbose_name='资源类型'),
|
||||
),
|
||||
]
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
# Generated by Django 5.2.4 on 2025-08-11 09:37
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('tools', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='tool',
|
||||
name='tool_type',
|
||||
field=models.CharField(choices=[('INTERNAL', '内置'), ('CUSTOM', '自定义'), ('MCP', 'MCP工具')], db_index=True, default='CUSTOM', max_length=20, verbose_name='工具类型'),
|
||||
),
|
||||
]
|
||||
|
|
@ -31,6 +31,7 @@ class ToolScope(models.TextChoices):
|
|||
class ToolType(models.TextChoices):
|
||||
INTERNAL = "INTERNAL", '内置'
|
||||
CUSTOM = "CUSTOM", "自定义"
|
||||
MCP = "MCP", "MCP工具"
|
||||
|
||||
|
||||
class Tool(AppModelMixin):
|
||||
|
|
|
|||
|
|
@ -1,9 +1,11 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
import asyncio
|
||||
import io
|
||||
import json
|
||||
import os
|
||||
import pickle
|
||||
import re
|
||||
from typing import Dict
|
||||
|
||||
import uuid_utils.compat as uuid
|
||||
from django.core import validators
|
||||
|
|
@ -12,6 +14,7 @@ from django.db.models import QuerySet, Q
|
|||
from django.http import HttpResponse
|
||||
from django.utils import timezone
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from langchain_mcp_adapters.client import MultiServerMCPClient
|
||||
from pylint.lint import Run
|
||||
from pylint.reporters import JSON2Reporter
|
||||
from rest_framework import serializers, status
|
||||
|
|
@ -22,6 +25,7 @@ from common.exception.app_exception import AppApiException
|
|||
from common.field.common import UploadedImageField
|
||||
from common.result import result
|
||||
from common.utils.common import get_file_content
|
||||
from common.utils.logger import maxkb_logger
|
||||
from common.utils.rsa_util import rsa_long_decrypt, rsa_long_encrypt
|
||||
from common.utils.tool_code import ToolExecutor
|
||||
from knowledge.models import File, FileSourceType
|
||||
|
|
@ -103,6 +107,18 @@ def encryption(message: str):
|
|||
return pre_str + content + end_str
|
||||
|
||||
|
||||
def validate_mcp_config(servers: Dict):
|
||||
async def validate():
|
||||
client = MultiServerMCPClient(servers)
|
||||
await client.get_tools()
|
||||
|
||||
try:
|
||||
asyncio.run(validate())
|
||||
except Exception as e:
|
||||
maxkb_logger.error(f"validate mcp config error: {e}, servers: {servers}")
|
||||
raise serializers.ValidationError(_('MCP configuration is invalid'))
|
||||
|
||||
|
||||
class ToolModelSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = Tool
|
||||
|
|
@ -201,6 +217,131 @@ class PylintInstance(serializers.Serializer):
|
|||
|
||||
|
||||
class ToolSerializer(serializers.Serializer):
|
||||
class Query(serializers.Serializer):
|
||||
workspace_id = serializers.CharField(required=True, label=_('workspace id'))
|
||||
folder_id = serializers.CharField(required=False, allow_blank=True, allow_null=True, label=_('folder id'))
|
||||
name = serializers.CharField(required=False, allow_null=True, allow_blank=True, label=_('tool name'))
|
||||
user_id = serializers.UUIDField(required=False, allow_null=True, label=_('user id'))
|
||||
scope = serializers.CharField(required=True, label=_('scope'))
|
||||
tool_type = serializers.CharField(required=False, label=_('tool type'), allow_null=True, allow_blank=True)
|
||||
create_user = serializers.UUIDField(required=False, label=_('create user'), allow_null=True)
|
||||
|
||||
def get_query_set(self, workspace_manage, is_x_pack_ee):
|
||||
tool_query_set = QuerySet(Tool).filter(workspace_id=self.data.get('workspace_id'))
|
||||
folder_query_set = QuerySet(ToolFolder)
|
||||
default_query_set = QuerySet(Tool)
|
||||
|
||||
workspace_id = self.data.get('workspace_id')
|
||||
user_id = self.data.get('user_id')
|
||||
scope = self.data.get('scope')
|
||||
tool_type = self.data.get('tool_type')
|
||||
desc = self.data.get('desc')
|
||||
name = self.data.get('name')
|
||||
folder_id = self.data.get('folder_id')
|
||||
create_user = self.data.get('create_user')
|
||||
|
||||
if workspace_id is not None:
|
||||
folder_query_set = folder_query_set.filter(workspace_id=workspace_id)
|
||||
default_query_set = default_query_set.filter(workspace_id=workspace_id)
|
||||
if folder_id is not None:
|
||||
folder_query_set = folder_query_set.filter(parent=folder_id)
|
||||
default_query_set = default_query_set.filter(folder_id=folder_id)
|
||||
if name is not None:
|
||||
folder_query_set = folder_query_set.filter(name__icontains=name)
|
||||
default_query_set = default_query_set.filter(name__icontains=name)
|
||||
if desc is not None:
|
||||
folder_query_set = folder_query_set.filter(desc__icontains=desc)
|
||||
default_query_set = default_query_set.filter(desc__icontains=desc)
|
||||
if create_user is not None:
|
||||
tool_query_set = tool_query_set.filter(user_id=create_user)
|
||||
folder_query_set = folder_query_set.filter(user_id=create_user)
|
||||
|
||||
default_query_set = default_query_set.order_by("-create_time")
|
||||
|
||||
if scope is not None:
|
||||
tool_query_set = tool_query_set.filter(scope=scope)
|
||||
if tool_type:
|
||||
tool_query_set = tool_query_set.filter(tool_type=tool_type)
|
||||
|
||||
query_set_dict = {
|
||||
'folder_query_set': folder_query_set,
|
||||
'tool_query_set': tool_query_set,
|
||||
'default_query_set': default_query_set,
|
||||
}
|
||||
if not workspace_manage:
|
||||
query_set_dict['workspace_user_resource_permission_query_set'] = QuerySet(
|
||||
WorkspaceUserResourcePermission).filter(
|
||||
auth_target_type="TOOL",
|
||||
workspace_id=workspace_id,
|
||||
user_id=user_id
|
||||
)
|
||||
return query_set_dict
|
||||
|
||||
def get_authorized_query_set(self):
|
||||
default_query_set = QuerySet(Tool)
|
||||
tool_type = self.data.get('tool_type')
|
||||
desc = self.data.get('desc')
|
||||
name = self.data.get('name')
|
||||
create_user = self.data.get('create_user')
|
||||
|
||||
default_query_set = default_query_set.filter(workspace_id='None')
|
||||
default_query_set = default_query_set.filter(scope=ToolScope.SHARED)
|
||||
if name is not None:
|
||||
default_query_set = default_query_set.filter(name__icontains=name)
|
||||
if desc is not None:
|
||||
default_query_set = default_query_set.filter(desc__icontains=desc)
|
||||
if create_user is not None:
|
||||
default_query_set = default_query_set.filter(user_id=create_user)
|
||||
if tool_type:
|
||||
default_query_set = default_query_set.filter(tool_type=tool_type)
|
||||
|
||||
default_query_set = default_query_set.order_by("-create_time")
|
||||
|
||||
return default_query_set
|
||||
|
||||
@staticmethod
|
||||
def is_x_pack_ee():
|
||||
workspace_user_role_mapping_model = DatabaseModelManage.get_model("workspace_user_role_mapping")
|
||||
role_permission_mapping_model = DatabaseModelManage.get_model("role_permission_mapping_model")
|
||||
return workspace_user_role_mapping_model is not None and role_permission_mapping_model is not None
|
||||
|
||||
def get_tools(self):
|
||||
self.is_valid(raise_exception=True)
|
||||
|
||||
workspace_manage = is_workspace_manage(self.data.get('user_id'), self.data.get('workspace_id'))
|
||||
is_x_pack_ee = self.is_x_pack_ee()
|
||||
results = native_search(
|
||||
self.get_query_set(workspace_manage, is_x_pack_ee),
|
||||
get_file_content(
|
||||
os.path.join(
|
||||
PROJECT_DIR,
|
||||
"apps", "tools", 'sql',
|
||||
'list_tool.sql' if workspace_manage else (
|
||||
'list_tool_user_ee.sql' if is_x_pack_ee else 'list_tool_user.sql'
|
||||
)
|
||||
)
|
||||
),
|
||||
)
|
||||
|
||||
get_authorized_tool = DatabaseModelManage.get_model("get_authorized_tool")
|
||||
shared_queryset = QuerySet(Tool).none()
|
||||
if get_authorized_tool is not None:
|
||||
shared_queryset = self.get_authorized_query_set()
|
||||
shared_queryset = get_authorized_tool(shared_queryset, self.data.get('workspace_id'))
|
||||
|
||||
return {
|
||||
'shared_tools': [
|
||||
ToolModelSerializer(data).data for data in shared_queryset
|
||||
],
|
||||
'tools': [
|
||||
{
|
||||
**tool,
|
||||
'input_field_list': json.loads(tool.get('input_field_list', '[]')),
|
||||
'init_field_list': json.loads(tool.get('init_field_list', '[]')),
|
||||
} for tool in results if tool['resource_type'] == 'tool'
|
||||
],
|
||||
}
|
||||
|
||||
class Create(serializers.Serializer):
|
||||
user_id = serializers.UUIDField(required=True, label=_('user id'))
|
||||
workspace_id = serializers.CharField(required=True, label=_('workspace id'))
|
||||
|
|
@ -212,6 +353,10 @@ class ToolSerializer(serializers.Serializer):
|
|||
ToolCreateRequest(data=instance).is_valid(raise_exception=True)
|
||||
# 校验代码是否包括禁止的关键字
|
||||
ToolExecutor().validate_banned_keywords(instance.get('code', ''))
|
||||
# 校验mcp json
|
||||
if instance.get('tool_type') == ToolType.MCP.value:
|
||||
validate_mcp_config(json.loads(instance.get('code')))
|
||||
|
||||
tool_id = uuid.uuid7()
|
||||
Tool(
|
||||
id=tool_id,
|
||||
|
|
@ -223,6 +368,7 @@ class ToolSerializer(serializers.Serializer):
|
|||
input_field_list=instance.get('input_field_list', []),
|
||||
init_field_list=instance.get('init_field_list', []),
|
||||
scope=instance.get('scope', ToolScope.WORKSPACE),
|
||||
tool_type=instance.get('tool_type', ToolType.CUSTOM),
|
||||
folder_id=instance.get('folder_id', self.data.get('workspace_id')),
|
||||
is_active=False
|
||||
).save()
|
||||
|
|
@ -326,6 +472,10 @@ class ToolSerializer(serializers.Serializer):
|
|||
ToolEditRequest(data=instance).is_valid(raise_exception=True)
|
||||
# 校验代码是否包括禁止的关键字
|
||||
ToolExecutor().validate_banned_keywords(instance.get('code', ''))
|
||||
# 校验mcp json
|
||||
if instance.get('tool_type') == ToolType.MCP.value:
|
||||
validate_mcp_config(json.loads(instance.get('code')))
|
||||
|
||||
if not QuerySet(Tool).filter(id=self.data.get('id')).exists():
|
||||
raise serializers.ValidationError(_('Tool not found'))
|
||||
|
||||
|
|
@ -574,6 +724,7 @@ class ToolTreeSerializer(serializers.Serializer):
|
|||
name = serializers.CharField(required=False, allow_null=True, allow_blank=True, label=_('tool name'))
|
||||
user_id = serializers.UUIDField(required=False, allow_null=True, label=_('user id'))
|
||||
scope = serializers.CharField(required=True, label=_('scope'))
|
||||
tool_type = serializers.CharField(required=False, label=_('tool type'), allow_null=True, allow_blank=True)
|
||||
create_user = serializers.UUIDField(required=False, label=_('create user'), allow_null=True)
|
||||
|
||||
def page_tool(self, current_page: int, page_size: int):
|
||||
|
|
@ -609,6 +760,7 @@ class ToolTreeSerializer(serializers.Serializer):
|
|||
workspace_id = self.data.get('workspace_id')
|
||||
user_id = self.data.get('user_id')
|
||||
scope = self.data.get('scope')
|
||||
tool_type = self.data.get('tool_type')
|
||||
desc = self.data.get('desc')
|
||||
name = self.data.get('name')
|
||||
folder_id = self.data.get('folder_id')
|
||||
|
|
@ -634,6 +786,8 @@ class ToolTreeSerializer(serializers.Serializer):
|
|||
|
||||
if scope is not None:
|
||||
tool_query_set = tool_query_set.filter(scope=scope)
|
||||
if tool_type:
|
||||
tool_query_set = tool_query_set.filter(tool_type=tool_type)
|
||||
|
||||
query_set_dict = {
|
||||
'folder_query_set': folder_query_set,
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ urlpatterns = [
|
|||
path('workspace/<str:workspace_id>/tool/import', views.ToolView.Import.as_view()),
|
||||
path('workspace/<str:workspace_id>/tool/pylint', views.ToolView.Pylint.as_view()),
|
||||
path('workspace/<str:workspace_id>/tool/debug', views.ToolView.Debug.as_view()),
|
||||
path('workspace/<str:workspace_id>/tool/tool_list', views.ToolView.Query.as_view()),
|
||||
path('workspace/<str:workspace_id>/tool/<str:tool_id>', views.ToolView.Operate.as_view()),
|
||||
path('workspace/<str:workspace_id>/tool/<str:tool_id>/edit_icon', views.ToolView.EditIcon.as_view()),
|
||||
path('workspace/<str:workspace_id>/tool/<str:tool_id>/export', views.ToolView.Export.as_view()),
|
||||
|
|
|
|||
|
|
@ -73,6 +73,7 @@ class ToolView(APIView):
|
|||
'folder_id': request.query_params.get('folder_id'),
|
||||
'name': request.query_params.get('name'),
|
||||
'scope': request.query_params.get('scope', ToolScope.WORKSPACE),
|
||||
'tool_type': request.query_params.get('tool_type'),
|
||||
'user_id': request.user.id,
|
||||
'create_user': request.query_params.get('create_user'),
|
||||
}
|
||||
|
|
@ -209,11 +210,43 @@ class ToolView(APIView):
|
|||
'folder_id': request.query_params.get('folder_id'),
|
||||
'name': request.query_params.get('name'),
|
||||
'scope': request.query_params.get('scope'),
|
||||
'tool_type': request.query_params.get('tool_type'),
|
||||
'user_id': request.user.id,
|
||||
'create_user': request.query_params.get('create_user'),
|
||||
}
|
||||
).page_tool_with_folders(current_page, page_size))
|
||||
|
||||
class Query(APIView):
|
||||
authentication_classes = [TokenAuth]
|
||||
|
||||
@extend_schema(
|
||||
methods=['GET'],
|
||||
description=_('Get tool list '),
|
||||
summary=_('Get tool list'),
|
||||
operation_id=_('Get tool list'), # type: ignore
|
||||
parameters=ToolReadAPI.get_parameters(),
|
||||
responses=ToolReadAPI.get_response(),
|
||||
tags=[_('Tool')] # type: ignore
|
||||
)
|
||||
@has_permissions(
|
||||
PermissionConstants.TOOL_READ.get_workspace_permission(),
|
||||
PermissionConstants.TOOL_READ.get_workspace_permission_workspace_manage_role(),
|
||||
RoleConstants.WORKSPACE_MANAGE.get_workspace_role(), RoleConstants.USER.get_workspace_role()
|
||||
)
|
||||
@log(menu='Tool', operate='Get tool list')
|
||||
def get(self, request: Request, workspace_id: str):
|
||||
return result.success(ToolSerializer.Query(
|
||||
data={
|
||||
'workspace_id': workspace_id,
|
||||
'folder_id': request.query_params.get('folder_id'),
|
||||
'name': request.query_params.get('name'),
|
||||
'scope': request.query_params.get('scope'),
|
||||
'tool_type': request.query_params.get('tool_type'),
|
||||
'user_id': request.user.id,
|
||||
'create_user': request.query_params.get('create_user'),
|
||||
}
|
||||
).get_tools())
|
||||
|
||||
class Import(APIView):
|
||||
authentication_classes = [TokenAuth]
|
||||
parser_classes = [MultiPartParser]
|
||||
|
|
|
|||
|
|
@ -21,6 +21,21 @@ const getToolList: (data?: any, loading?: Ref<boolean>) => Promise<Result<Array<
|
|||
return get(`${prefix}`, data, loading)
|
||||
}
|
||||
|
||||
/**
|
||||
* 工具列表带分页(无分页)
|
||||
* @params 参数
|
||||
* param {
|
||||
"name": "string",
|
||||
"tool_type": "string",
|
||||
}
|
||||
*/
|
||||
const getAllToolList: (data?: any, loading?: Ref<boolean>) => Promise<Result<Array<any>>> = (
|
||||
data,
|
||||
loading,
|
||||
) => {
|
||||
return get(`${prefix}/tool_list`, data, loading)
|
||||
}
|
||||
|
||||
/**
|
||||
* 工具列表带分页
|
||||
* @param 参数
|
||||
|
|
@ -110,6 +125,7 @@ const postPylint: (code: string, loading?: Ref<boolean>) => Promise<Result<any>>
|
|||
export default {
|
||||
getToolListPage,
|
||||
getToolList,
|
||||
getAllToolList,
|
||||
putTool,
|
||||
getToolById,
|
||||
postToolDebug,
|
||||
|
|
|
|||
|
|
@ -17,6 +17,16 @@ const getToolList: (data?: any, loading?: Ref<boolean>) => Promise<Result<Array<
|
|||
return get(`${prefix}`, data, loading)
|
||||
}
|
||||
|
||||
/**
|
||||
* 工具列表带分页(无分页)
|
||||
*/
|
||||
const getAllToolList: (data?: any, loading?: Ref<boolean>) => Promise<Result<Array<any>>> = (
|
||||
data,
|
||||
loading,
|
||||
) => {
|
||||
return get(`${prefix}/tool_list`, data, loading)
|
||||
}
|
||||
|
||||
/**
|
||||
* 工具列表带分页
|
||||
* @param 参数
|
||||
|
|
@ -135,6 +145,7 @@ const addInternalTool: (
|
|||
|
||||
export default {
|
||||
getToolList,
|
||||
getAllToolList,
|
||||
getToolListPage,
|
||||
putTool,
|
||||
getToolById,
|
||||
|
|
|
|||
|
|
@ -24,6 +24,16 @@ const getToolList: (
|
|||
return get(`${prefix.value}`, data, loading)
|
||||
}
|
||||
|
||||
/**
|
||||
* 工具列表带分页(无分页)
|
||||
*/
|
||||
const getAllToolList: (
|
||||
data?: any,
|
||||
loading?: Ref<boolean>,
|
||||
) => Promise<Result<{ tools: any[]; folders: any[] }>> = (data, loading) => {
|
||||
return get(`${prefix.value}/tool_list`, data, loading)
|
||||
}
|
||||
|
||||
/**
|
||||
* 工具列表带分页
|
||||
* @param 参数
|
||||
|
|
@ -140,6 +150,7 @@ const addInternalTool: (
|
|||
|
||||
export default {
|
||||
getToolList,
|
||||
getAllToolList,
|
||||
getToolListPage,
|
||||
putTool,
|
||||
getToolById,
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ interface toolData {
|
|||
init_field_list?: Array<any>
|
||||
is_active?: boolean
|
||||
folder_id?: string
|
||||
tool_type?: string
|
||||
}
|
||||
|
||||
interface AddInternalToolParam {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
export default {
|
||||
title: 'Tool',
|
||||
all: 'All',
|
||||
createTool: 'Create Tool',
|
||||
editTool: 'Edit Tool',
|
||||
copyTool: 'Copy Tool',
|
||||
|
|
@ -66,6 +67,11 @@ export default {
|
|||
selectPlaceholder: 'Please select parameter',
|
||||
inputPlaceholder: 'Please enter parameter values',
|
||||
},
|
||||
mcp: {
|
||||
label: 'MCP Server Config',
|
||||
placeholder: 'Please enter MCP Server config',
|
||||
tip: 'Only supports SSE and Streamable HTTP calling methods',
|
||||
},
|
||||
debug: {
|
||||
run: 'Run',
|
||||
output: 'Output',
|
||||
|
|
|
|||
|
|
@ -238,6 +238,7 @@ export default {
|
|||
mcpServerTip: '请输入JSON格式的MCP服务器配置',
|
||||
mcpToolTip: '请选择工具',
|
||||
configLabel: 'MCP Server Config (仅支持SSE/Streamable HTTP调用方式)',
|
||||
reference: '引用MCP',
|
||||
},
|
||||
imageGenerateNode: {
|
||||
label: '图片生成',
|
||||
|
|
|
|||
|
|
@ -1,7 +1,10 @@
|
|||
export default {
|
||||
title: '工具',
|
||||
all: '全部',
|
||||
createTool: '创建工具',
|
||||
editTool: '编辑工具',
|
||||
createMcpTool: '创建MCP',
|
||||
editMcpTool: '编辑MCP',
|
||||
copyTool: '复制工具',
|
||||
importTool: '导入工具',
|
||||
toolStore: {
|
||||
|
|
@ -60,6 +63,11 @@ export default {
|
|||
selectPlaceholder: '请选择参数',
|
||||
inputPlaceholder: '请输入参数值',
|
||||
},
|
||||
mcp: {
|
||||
label: 'MCP Server Config',
|
||||
placeholder: '请输入MCP Server配置',
|
||||
tip: '仅支持SSE、Streamable HTTP调用方式',
|
||||
},
|
||||
debug: {
|
||||
run: '运行',
|
||||
output: '输出',
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
export default {
|
||||
title: '工具',
|
||||
all: '全部',
|
||||
createTool: '建立工具',
|
||||
editTool: '編輯工具',
|
||||
copyTool: '複製工具',
|
||||
|
|
@ -63,6 +64,11 @@ export default {
|
|||
selectPlaceholder: '請选择參數',
|
||||
inputPlaceholder: '請輸入參數值',
|
||||
},
|
||||
mcp: {
|
||||
label: 'MCP Server Config',
|
||||
placeholder: '請輸入MCP Server配置',
|
||||
tip: '僅支援SSE、Streamable HTTP呼叫方式',
|
||||
},
|
||||
debug: {
|
||||
run: '運行',
|
||||
output: '輸出',
|
||||
|
|
|
|||
|
|
@ -8,11 +8,15 @@ import useFolderStore from './folder'
|
|||
const useToolStore = defineStore('tool', {
|
||||
state: () => ({
|
||||
toolList: [] as any[],
|
||||
tool_type: '' as string,
|
||||
}),
|
||||
actions: {
|
||||
setToolList(list: any[]) {
|
||||
this.toolList = list
|
||||
},
|
||||
setToolType(type: string) {
|
||||
this.tool_type = type
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
|
|
|
|||
|
|
@ -72,9 +72,14 @@
|
|||
|
||||
<el-table-column prop="tool_type" :label="$t('views.system.resource_management.type')">
|
||||
<template #default="scope">
|
||||
<span v-if="scope.row.tool_type === 'MCP'">
|
||||
MCP
|
||||
</span>
|
||||
<span v-else>
|
||||
{{
|
||||
$t(ToolType[scope.row.template_id ? 'INTERNAL' : ('CUSTOM' as keyof typeof ToolType)])
|
||||
$t(ToolType[scope.row.template_id ? 'INTERNAL' : ('CUSTOM' as keyof typeof ToolType)])
|
||||
}}
|
||||
</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="$t('common.status.label')" width="120">
|
||||
|
|
|
|||
|
|
@ -8,6 +8,13 @@
|
|||
<h5 class="ml-4 color-text-primary">{{ t('views.tool.title') }}</h5>
|
||||
</el-breadcrumb-item>
|
||||
</el-breadcrumb>
|
||||
<div class="mt-16 mb-16">
|
||||
<el-radio-group v-model="toolType" @change="radioChange" class="app-radio-button-group">
|
||||
<el-radio-button value="">{{ $t('views.tool.all') }}</el-radio-button>
|
||||
<el-radio-button value="CUSTOM">{{ $t('views.tool.title') }}</el-radio-button>
|
||||
<el-radio-button value="MCP">MCP</el-radio-button>
|
||||
</el-radio-group>
|
||||
</div>
|
||||
</template>
|
||||
</ToolListContainer>
|
||||
</div>
|
||||
|
|
@ -19,6 +26,16 @@ import { onMounted, ref, reactive, computed } from 'vue'
|
|||
import ToolListContainer from '@/views/tool/component/ToolListContainer.vue'
|
||||
|
||||
import { t } from '@/locales'
|
||||
import useStore from "@/stores";
|
||||
|
||||
const { tool } = useStore()
|
||||
|
||||
|
||||
const toolType = ref('')
|
||||
|
||||
function radioChange() {
|
||||
tool.setToolType(toolType.value)
|
||||
}
|
||||
|
||||
onMounted(() => {})
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,284 @@
|
|||
<template>
|
||||
<el-drawer v-model="visible" size="60%" :before-close="close">
|
||||
<template #header>
|
||||
<h4>{{ title }}</h4>
|
||||
</template>
|
||||
<div>
|
||||
<h4 class="title-decoration-1 mb-16">
|
||||
{{ $t('views.model.modelForm.title.baseInfo') }}
|
||||
</h4>
|
||||
<el-form
|
||||
ref="FormRef"
|
||||
:model="form"
|
||||
:rules="rules"
|
||||
label-position="top"
|
||||
require-asterisk-position="right"
|
||||
v-loading="loading"
|
||||
@submit.prevent
|
||||
>
|
||||
<el-form-item :label="$t('views.tool.form.toolName.label')" prop="name">
|
||||
<div class="flex w-full">
|
||||
<div
|
||||
v-if="form.id"
|
||||
class="edit-avatar mr-12"
|
||||
@mouseenter="showEditIcon = true"
|
||||
@mouseleave="showEditIcon = false"
|
||||
>
|
||||
<el-Avatar
|
||||
v-if="isAppIcon(form.icon)"
|
||||
:id="form.id"
|
||||
shape="square"
|
||||
:size="32"
|
||||
style="background: none"
|
||||
>
|
||||
<img :src="String(form.icon)" alt=""/>
|
||||
</el-Avatar>
|
||||
<el-avatar v-else class="avatar-green" shape="square" :size="32">
|
||||
<img src="@/assets/workflow/icon_tool.svg" style="width: 58%" alt=""/>
|
||||
</el-avatar>
|
||||
<el-Avatar
|
||||
v-if="showEditIcon"
|
||||
:id="form.id"
|
||||
shape="square"
|
||||
class="edit-mask"
|
||||
:size="32"
|
||||
@click="openEditAvatar"
|
||||
>
|
||||
<AppIcon iconName="app-edit"></AppIcon>
|
||||
</el-Avatar>
|
||||
</div>
|
||||
<el-avatar v-else class="avatar-green mr-12" shape="square" :size="32">
|
||||
<img src="@/assets/workflow/icon_tool.svg" style="width: 58%" alt=""/>
|
||||
</el-avatar>
|
||||
<el-input
|
||||
v-model="form.name"
|
||||
:placeholder="$t('views.tool.form.toolName.placeholder')"
|
||||
maxlength="64"
|
||||
show-word-limit
|
||||
@blur="form.name = form.name?.trim()"
|
||||
/>
|
||||
</div>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item :label="$t('views.tool.form.toolDescription.label')">
|
||||
<el-input
|
||||
v-model="form.desc"
|
||||
type="textarea"
|
||||
:placeholder="$t('views.tool.form.toolDescription.placeholder')"
|
||||
maxlength="128"
|
||||
show-word-limit
|
||||
:autosize="{ minRows: 3 }"
|
||||
@blur="form.desc = form.desc?.trim()"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<h4 class="title-decoration-1 mb-16">
|
||||
{{ $t('views.tool.form.mcp.label') }}
|
||||
<span style="color: red; margin-left: -10px">*</span>
|
||||
<el-text type="info" class="color-secondary">
|
||||
{{ $t('views.tool.form.mcp.tip') }}
|
||||
</el-text>
|
||||
</h4>
|
||||
|
||||
<div class="mb-8">
|
||||
<el-input
|
||||
v-model="form.code"
|
||||
:placeholder="mcpServerJson"
|
||||
type="textarea"
|
||||
:autosize="{ minRows: 5 }"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<template #footer>
|
||||
<div>
|
||||
<el-button :loading="loading" @click="visible = false">{{ $t('common.cancel') }}</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
@click="submit(FormRef)"
|
||||
:loading="loading"
|
||||
v-if="isEdit ? permissionPrecise.edit(form?.id as string) : permissionPrecise.create()"
|
||||
>
|
||||
{{ isEdit ? $t('common.save') : $t('common.create') }}
|
||||
</el-button>
|
||||
</div>
|
||||
</template>
|
||||
<EditAvatarDialog ref="EditAvatarDialogRef" @refresh="refreshTool"/>
|
||||
</el-drawer>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {computed, reactive, ref, watch} from 'vue'
|
||||
import EditAvatarDialog from '@/views/tool/component/EditAvatarDialog.vue'
|
||||
import type {toolData} from '@/api/type/tool'
|
||||
import type {FormInstance} from 'element-plus'
|
||||
import {MsgConfirm, MsgSuccess} from '@/utils/message'
|
||||
import {cloneDeep} from 'lodash'
|
||||
import {t} from '@/locales'
|
||||
import {isAppIcon} from '@/utils/common'
|
||||
import {useRoute} from 'vue-router'
|
||||
import useStore from '@/stores'
|
||||
import permissionMap from '@/permission'
|
||||
import {loadSharedApi} from '@/utils/dynamics-api/shared-api'
|
||||
|
||||
const route = useRoute()
|
||||
|
||||
const props = defineProps({
|
||||
title: String,
|
||||
})
|
||||
const {folder, user} = useStore()
|
||||
|
||||
const apiType = computed(() => {
|
||||
if (route.path.includes('shared')) {
|
||||
return 'systemShare'
|
||||
} else if (route.path.includes('resource-management')) {
|
||||
return 'systemManage'
|
||||
} else {
|
||||
return 'workspace'
|
||||
}
|
||||
})
|
||||
const permissionPrecise = computed(() => {
|
||||
return permissionMap['tool'][apiType.value]
|
||||
})
|
||||
|
||||
const emit = defineEmits(['refresh'])
|
||||
const EditAvatarDialogRef = ref()
|
||||
const mcpServerJson = `{
|
||||
"math": {
|
||||
"url": "your_server",
|
||||
"transport": "sse"
|
||||
}
|
||||
}`
|
||||
|
||||
const FormRef = ref()
|
||||
|
||||
const isEdit = ref(false)
|
||||
const loading = ref(false)
|
||||
const visible = ref(false)
|
||||
const showEditor = ref(false)
|
||||
const currentIndex = ref<any>(null)
|
||||
const showEditIcon = ref(false)
|
||||
|
||||
const form = ref<toolData>({
|
||||
name: '',
|
||||
desc: '',
|
||||
code: '',
|
||||
icon: '',
|
||||
input_field_list: [],
|
||||
init_field_list: [],
|
||||
tool_type: 'MCP',
|
||||
})
|
||||
|
||||
watch(visible, (bool) => {
|
||||
if (!bool) {
|
||||
isEdit.value = false
|
||||
showEditor.value = false
|
||||
currentIndex.value = null
|
||||
form.value = {
|
||||
name: '',
|
||||
desc: '',
|
||||
code: '',
|
||||
icon: '',
|
||||
input_field_list: [],
|
||||
init_field_list: [],
|
||||
tool_type: 'MCP',
|
||||
}
|
||||
FormRef.value?.clearValidate()
|
||||
}
|
||||
})
|
||||
|
||||
const rules = reactive({
|
||||
name: [
|
||||
{
|
||||
required: true,
|
||||
message: t('views.tool.form.toolName.requiredMessage'),
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
})
|
||||
|
||||
function close() {
|
||||
if (isEdit.value || !areAllValuesNonEmpty(form.value)) {
|
||||
visible.value = false
|
||||
} else {
|
||||
MsgConfirm(t('common.tip'), t('views.tool.tip.saveMessage'), {
|
||||
confirmButtonText: t('common.confirm'),
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => {
|
||||
visible.value = false
|
||||
})
|
||||
.catch(() => {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
function areAllValuesNonEmpty(obj: any) {
|
||||
return Object.values(obj).some((value) => {
|
||||
return Array.isArray(value)
|
||||
? value.length !== 0
|
||||
: value !== null && value !== undefined && value !== ''
|
||||
})
|
||||
}
|
||||
|
||||
function refreshTool(data: any) {
|
||||
form.value.icon = data
|
||||
}
|
||||
|
||||
|
||||
function openEditAvatar() {
|
||||
EditAvatarDialogRef.value.open(form.value)
|
||||
}
|
||||
|
||||
const submit = async (formEl: FormInstance | undefined) => {
|
||||
if (!formEl) return
|
||||
await formEl.validate((valid: any) => {
|
||||
if (valid) {
|
||||
if (isEdit.value) {
|
||||
loadSharedApi({type: 'tool', systemType: apiType.value})
|
||||
.putTool(form.value?.id as string, form.value, loading)
|
||||
.then((res: any) => {
|
||||
MsgSuccess(t('common.editSuccess'))
|
||||
emit('refresh', res.data)
|
||||
return user.profile()
|
||||
})
|
||||
.then(() => {
|
||||
visible.value = false
|
||||
})
|
||||
} else {
|
||||
const obj = {
|
||||
folder_id: folder.currentFolder?.id,
|
||||
...form.value,
|
||||
}
|
||||
loadSharedApi({type: 'tool', systemType: apiType.value})
|
||||
.postTool(obj, loading)
|
||||
.then((res: any) => {
|
||||
MsgSuccess(t('common.createSuccess'))
|
||||
emit('refresh')
|
||||
return user.profile()
|
||||
})
|
||||
.then(() => {
|
||||
visible.value = false
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const open = (data: any) => {
|
||||
if (data) {
|
||||
isEdit.value = data?.id ? true : false
|
||||
form.value = cloneDeep(data)
|
||||
}
|
||||
visible.value = true
|
||||
setTimeout(() => {
|
||||
showEditor.value = true
|
||||
}, 100)
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
open,
|
||||
})
|
||||
</script>
|
||||
<style lang="scss" scoped></style>
|
||||
|
|
@ -54,6 +54,16 @@
|
|||
</div>
|
||||
</div>
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item @click="openCreateMcpDialog()">
|
||||
<div class="flex align-center">
|
||||
<el-avatar class="avatar-green" shape="square" :size="32">
|
||||
<img src="@/assets/workflow/icon_tool.svg" style="width: 58%" alt="" />
|
||||
</el-avatar>
|
||||
<div class="pre-wrap ml-8">
|
||||
<div class="lighter">创建MCP</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-dropdown-item>
|
||||
<el-upload
|
||||
ref="elUploadRef"
|
||||
:file-list="[]"
|
||||
|
|
@ -284,6 +294,7 @@
|
|||
</ContentContainer>
|
||||
<InitParamDrawer ref="InitParamDrawerRef" @refresh="refresh" />
|
||||
<ToolFormDrawer ref="ToolFormDrawerRef" @refresh="refresh" :title="ToolDrawertitle" />
|
||||
<McpToolFormDrawer ref="McpToolFormDrawerRef" @refresh="refresh" :title="McpToolDrawertitle" />
|
||||
<CreateFolderDialog ref="CreateFolderDialogRef" v-if="!isShared" @refresh="refreshFolder" />
|
||||
<ToolStoreDialog ref="toolStoreDialogRef" :api-type="apiType" @refresh="refresh" />
|
||||
<AddInternalToolDialog ref="AddInternalToolDialogRef" @refresh="confirmAddInternalTool" />
|
||||
|
|
@ -305,6 +316,7 @@ import { cloneDeep } from 'lodash'
|
|||
import { useRoute, onBeforeRouteLeave } from 'vue-router'
|
||||
import InitParamDrawer from '@/views/tool/component/InitParamDrawer.vue'
|
||||
import ToolFormDrawer from '@/views/tool/ToolFormDrawer.vue'
|
||||
import McpToolFormDrawer from '@/views/tool/McpToolFormDrawer.vue'
|
||||
import CreateFolderDialog from '@/components/folder-tree/CreateFolderDialog.vue'
|
||||
import AuthorizedWorkspace from '@/views/system-shared/AuthorizedWorkspaceDialog.vue'
|
||||
import ToolStoreDialog from '@/views/tool/toolStore/ToolStoreDialog.vue'
|
||||
|
|
@ -374,7 +386,9 @@ const search_type_change = () => {
|
|||
search_form.value = { name: '', create_user: '' }
|
||||
}
|
||||
const ToolFormDrawerRef = ref()
|
||||
const McpToolFormDrawerRef = ref()
|
||||
const ToolDrawertitle = ref('')
|
||||
const McpToolDrawertitle = ref('')
|
||||
|
||||
const MoveToDialogRef = ref()
|
||||
function openMoveToDialog(data: any) {
|
||||
|
|
@ -400,6 +414,11 @@ function openAuthorizedWorkspaceDialog(row: any) {
|
|||
}
|
||||
|
||||
function openCreateDialog(data?: any) {
|
||||
// mcp工具
|
||||
if (data?.tool_type === 'MCP') {
|
||||
openCreateMcpDialog(data)
|
||||
return
|
||||
}
|
||||
// 有template_id的不允许编辑,是模板转换来的
|
||||
if (data?.template_id) {
|
||||
return
|
||||
|
|
@ -420,6 +439,27 @@ function openCreateDialog(data?: any) {
|
|||
}
|
||||
}
|
||||
|
||||
function openCreateMcpDialog(data?: any) {
|
||||
// 有template_id的不允许编辑,是模板转换来的
|
||||
if (data?.template_id) {
|
||||
return
|
||||
}
|
||||
// 共享过来的工具不让编辑
|
||||
if (isShared.value) {
|
||||
return
|
||||
}
|
||||
McpToolDrawertitle.value = data ? t('views.tool.editMcpTool') : t('views.tool.createMcpTool')
|
||||
if (data) {
|
||||
loadSharedApi({ type: 'tool', systemType: apiType.value })
|
||||
.getToolById(data?.id, loading)
|
||||
.then((res: any) => {
|
||||
McpToolFormDrawerRef.value.open(res.data)
|
||||
})
|
||||
} else {
|
||||
McpToolFormDrawerRef.value.open(data)
|
||||
}
|
||||
}
|
||||
|
||||
async function changeState(row: any) {
|
||||
if (row.is_active) {
|
||||
MsgConfirm(
|
||||
|
|
@ -616,10 +656,21 @@ watch(
|
|||
},
|
||||
{ deep: true, immediate: true },
|
||||
)
|
||||
|
||||
watch(
|
||||
() => tool.tool_type,
|
||||
() => {
|
||||
paginationConfig.current_page = 1
|
||||
tool.setToolList([])
|
||||
getList()
|
||||
},
|
||||
)
|
||||
|
||||
function getList() {
|
||||
const params: any = {
|
||||
folder_id: folder.currentFolder?.id || user.getWorkspaceId(),
|
||||
scope: apiType.value === 'systemShare' ? 'SHARED' : 'WORKSPACE',
|
||||
tool_type: tool.tool_type || '',
|
||||
}
|
||||
if (search_form.value[search_type.value]) {
|
||||
params[search_type.value] = search_form.value[search_type.value]
|
||||
|
|
|
|||
|
|
@ -20,6 +20,13 @@
|
|||
{{ $t('views.shared.shared_tool') }}
|
||||
</h2>
|
||||
<FolderBreadcrumb :folderList="folderList" @click="folderClickHandle" v-else />
|
||||
<div class="mt-16 mb-16">
|
||||
<el-radio-group v-model="toolType" @change="radioChange" class="app-radio-button-group">
|
||||
<el-radio-button value="">{{ $t('views.tool.all') }}</el-radio-button>
|
||||
<el-radio-button value="CUSTOM">{{ $t('views.tool.title') }}</el-radio-button>
|
||||
<el-radio-button value="MCP">MCP</el-radio-button>
|
||||
</el-radio-group>
|
||||
</div>
|
||||
</template>
|
||||
</ToolListContainer>
|
||||
</LayoutContainer>
|
||||
|
|
@ -49,6 +56,7 @@ const permissionPrecise = computed(() => {
|
|||
})
|
||||
|
||||
const loading = ref(false)
|
||||
const toolType = ref('')
|
||||
|
||||
const folderList = ref<any[]>([])
|
||||
|
||||
|
|
@ -71,6 +79,10 @@ function folderClickHandle(row: any) {
|
|||
tool.setToolList([])
|
||||
}
|
||||
|
||||
function radioChange() {
|
||||
tool.setToolType(toolType.value)
|
||||
}
|
||||
|
||||
function refreshFolder() {
|
||||
getFolder()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,7 +12,28 @@
|
|||
hide-required-asterisk
|
||||
>
|
||||
<el-form-item label="MCP Server Config">
|
||||
<template #label>
|
||||
<div class="flex-between">
|
||||
<div>
|
||||
MCP Server Config
|
||||
<span class="color-danger">*</span>
|
||||
</div>
|
||||
<el-select
|
||||
:teleported="false"
|
||||
v-model="form_data.mcp_source"
|
||||
size="small"
|
||||
style="width: 85px"
|
||||
>
|
||||
<el-option
|
||||
:label="$t('views.applicationWorkflow.nodes.mcpNode.reference')"
|
||||
value="referencing"
|
||||
/>
|
||||
<el-option :label="$t('common.custom')" value="custom" />
|
||||
</el-select>
|
||||
</div>
|
||||
</template>
|
||||
<MdEditorMagnify
|
||||
v-if="form_data.mcp_source === 'custom'"
|
||||
@wheel="wheel"
|
||||
title="MCP Server Config"
|
||||
v-model="form_data.mcp_servers"
|
||||
|
|
@ -20,6 +41,19 @@
|
|||
@submitDialog="submitDialog"
|
||||
:placeholder="mcpServerJson"
|
||||
/>
|
||||
<el-select v-else v-model="form_data.mcp_tool_id" filterable @change="mcpToolSelectChange">
|
||||
<el-option
|
||||
v-for="mcpTool in mcpToolSelectOptions"
|
||||
:key="mcpTool.id"
|
||||
:label="mcpTool.name"
|
||||
:value="mcpTool.id"
|
||||
>
|
||||
<span>{{ mcpTool.name }}</span>
|
||||
<el-tag v-if="mcpTool.scope === 'SHARED'" type="info" class="info-tag ml-8 mt-4">
|
||||
{{ t('views.shared.title') }}
|
||||
</el-tag>
|
||||
</el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<template v-slot:label>
|
||||
|
|
@ -201,7 +235,7 @@
|
|||
<script setup lang="ts">
|
||||
import { cloneDeep, set } from 'lodash'
|
||||
import NodeContainer from '@/workflow/common/NodeContainer.vue'
|
||||
import { computed, onMounted, ref } from 'vue'
|
||||
import { computed, onMounted, ref, inject } from 'vue'
|
||||
import { isLastNode } from '@/workflow/common/data'
|
||||
import { t } from '@/locales'
|
||||
import { MsgError, MsgSuccess } from '@/utils/message'
|
||||
|
|
@ -209,12 +243,16 @@ import TooltipLabel from '@/components/dynamics-form/items/label/TooltipLabel.vu
|
|||
import NodeCascader from '@/workflow/common/NodeCascader.vue'
|
||||
import { useRoute } from 'vue-router'
|
||||
import { loadSharedApi } from '@/utils/dynamics-api/shared-api'
|
||||
import useStore from "@/stores";
|
||||
const props = defineProps<{ nodeModel: any }>()
|
||||
const { user } = useStore()
|
||||
|
||||
const route = useRoute()
|
||||
const {
|
||||
params: { id },
|
||||
} = route as any
|
||||
const getApplicationDetail = inject('getApplicationDetail') as any
|
||||
const applicationDetail = getApplicationDetail()
|
||||
|
||||
const apiType = computed(() => {
|
||||
if (route.path.includes('resource-management')) {
|
||||
|
|
@ -248,17 +286,31 @@ const form = {
|
|||
mcp_tools: [],
|
||||
mcp_servers: '',
|
||||
mcp_server: '',
|
||||
mcp_source: 'referencing',
|
||||
mcp_tool_id: '',
|
||||
tool_params: {},
|
||||
tool_form_field: [],
|
||||
params_nested: '',
|
||||
}
|
||||
|
||||
const mcpToolSelectOptions = ref<any[]>([])
|
||||
|
||||
function submitDialog(val: string) {
|
||||
set(props.nodeModel.properties.node_data, 'mcp_servers', val)
|
||||
}
|
||||
|
||||
async function mcpToolSelectChange() {
|
||||
const tool = await loadSharedApi({ type: 'tool', systemType: apiType.value })
|
||||
.getToolById(form_data.value.mcp_tool_id, loading)
|
||||
form_data.value.mcp_servers = tool.data.code
|
||||
}
|
||||
|
||||
function getTools() {
|
||||
if (!form_data.value.mcp_servers) {
|
||||
if (form_data.value.mcp_source === 'referencing' && !form_data.value.mcp_tool_id) {
|
||||
MsgError(t('views.applicationWorkflow.nodes.mcpNode.mcpToolTip'))
|
||||
return
|
||||
}
|
||||
if (form_data.value.mcp_source === 'custom' && !form_data.value.mcp_servers) {
|
||||
MsgError(t('views.applicationWorkflow.nodes.mcpNode.mcpServerTip'))
|
||||
return
|
||||
}
|
||||
|
|
@ -434,13 +486,34 @@ const validate = async () => {
|
|||
}
|
||||
}
|
||||
|
||||
function getMcpToolSelectOptions() {
|
||||
const obj =
|
||||
apiType.value === 'systemManage'
|
||||
? {
|
||||
scope: 'WORKSPACE',
|
||||
tool_type: 'MCP',
|
||||
workspace_id: applicationDetail.value?.workspace_id,
|
||||
}
|
||||
: {
|
||||
scope: 'WORKSPACE',
|
||||
tool_type: 'MCP',
|
||||
}
|
||||
|
||||
loadSharedApi({type: 'tool', systemType: apiType.value})
|
||||
.getAllToolList(obj, loading)
|
||||
.then((res: any) => {
|
||||
mcpToolSelectOptions.value = [...res.data.shared_tools, ...res.data.tools]
|
||||
.filter((item: any) => item.is_active)
|
||||
})
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
if (typeof props.nodeModel.properties.node_data?.is_result === 'undefined') {
|
||||
if (isLastNode(props.nodeModel)) {
|
||||
set(props.nodeModel.properties.node_data, 'is_result', true)
|
||||
}
|
||||
}
|
||||
|
||||
getMcpToolSelectOptions()
|
||||
set(props.nodeModel, 'validate', validate)
|
||||
})
|
||||
</script>
|
||||
|
|
|
|||
Loading…
Reference in New Issue