feat: Add create MCP function (#3885)

Co-authored-by: wangdan-fit2cloud <dan.wang@fit2cloud.com>
This commit is contained in:
shaohuzhang1 2025-08-19 17:36:23 +08:00 committed by GitHub
parent 1e00a6d763
commit c631d775bf
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
26 changed files with 251 additions and 151 deletions

View File

@ -0,0 +1,3 @@
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M26.1563 2.66699L26.3594 2.68262C26.8258 2.75455 27.2257 3.06988 27.3994 3.51953L29.4004 8.7002C30.2431 10.8818 29.5573 13.1583 28.0215 14.5771C28.0235 14.6068 28.0264 14.6369 28.0264 14.667V28C28.0264 28.7364 27.4288 29.333 26.6924 29.333H5.35941C4.62303 29.333 4.0264 28.7364 4.0264 28V14.667C4.0264 14.6372 4.02838 14.6074 4.0303 14.5781C2.49502 13.1601 1.80856 10.8847 2.65042 8.7041L4.65335 3.51953L4.74026 3.33594C4.97526 2.92651 5.41431 2.66712 5.89651 2.66699H26.1563ZM20.3653 14.1885C19.388 15.2979 17.959 15.9999 16.3643 16H15.6846C14.0896 15.9999 12.6589 15.2983 11.6817 14.1885C10.7002 15.2988 9.26596 16 7.67288 16C7.33622 16 7.00883 15.9688 6.69241 15.9111V26.667H25.3594V15.9102C25.0425 15.968 24.7142 16 24.377 16C22.7837 15.9999 21.347 15.2993 20.3653 14.1885Z" fill="#FF8800"/>
</svg>

After

Width:  |  Height:  |  Size: 908 B

View File

@ -1,3 +0,0 @@
<svg width="22" height="22" viewBox="0 0 22 22" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M7.50959 19.3955C6.84272 20.0622 5.93829 20.4368 4.99527 20.4367C4.05226 20.4366 3.1479 20.0619 2.48114 19.395C1.81439 18.7282 1.43986 17.8238 1.43994 16.8807C1.44002 15.9377 1.81472 15.0334 2.48159 14.3666L4.6407 12.207C3.70381 9.41949 4.34648 6.21772 6.56692 3.99594C8.78914 1.77372 11.9927 1.13238 14.7816 2.07105C14.9607 2.13105 15.1758 2.21994 15.426 2.33683C15.5488 2.39405 15.656 2.48004 15.7385 2.5875C15.821 2.69496 15.8764 2.82073 15.8999 2.95414C15.9235 3.08754 15.9145 3.22467 15.8738 3.35388C15.8331 3.48309 15.7618 3.60057 15.666 3.69638L11.9096 7.45283L14.4238 9.96705L18.1318 6.25905C18.2333 6.15762 18.3578 6.08226 18.4947 6.0394C18.6316 5.99653 18.7768 5.98744 18.918 6.0129C19.0592 6.03836 19.1921 6.09761 19.3054 6.1856C19.4187 6.27358 19.5091 6.38769 19.5687 6.51816C19.6825 6.76616 19.7683 6.97949 19.8265 7.15861C20.7345 9.93283 20.0856 13.1048 17.8807 15.3097C15.6594 17.5306 12.4571 18.1728 9.66914 17.2359L7.50959 19.395V19.3955Z" fill="white"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -17,6 +17,7 @@
:source="prologue"
:send-message="sendMessage"
reasoning_content=""
:type="type"
></MdRenderer>
</el-card>
</div>
@ -35,7 +36,6 @@ const props = defineProps<{
sendMessage: (question: string, other_params_data?: any, chat?: chatType) => void
}>()
const showAvatar = computed(() => {
return props.application.show_avatar == undefined ? true : props.application.show_avatar
})

View File

@ -0,0 +1,21 @@
<template>
<el-avatar v-if="type == 'MCP'" shape="square" :size="size">
<img src="@/assets/workflow/icon_mcp.svg" style="width: 75%" alt="" />
</el-avatar>
<el-avatar v-else class="avatar-green" shape="square" :size="size">
<img src="@/assets/workflow/icon_tool.svg" style="width: 58%" alt="" />
</el-avatar>
</template>
<script setup lang="ts">
defineOptions({ name: 'ToolIcon' })
const props = defineProps({
type: {
type: [String, Number],
default: '',
},
size: {
type: [String, Number],
default: 32,
},
})
</script>

View File

@ -1,9 +1,9 @@
<template>
<el-breadcrumb separator-icon="ArrowRight" style="line-height: 22px">
<h2 v-if="breadcrumbData?.length === 1" class="ellipsis" :title="breadcrumbData[0]?.name">
{{ breadcrumbData[0]?.name }}
</h2>
<el-breadcrumb-item v-for="(item, index) in breadcrumbData" :key="index" v-else>
<h2 v-if="breadcrumbData?.length === 1" class="ellipsis" :title="breadcrumbData[0]?.name">
{{ breadcrumbData[0]?.name }}
</h2>
<el-breadcrumb separator-icon="ArrowRight" style="line-height: normal" class="mt-4" v-else>
<el-breadcrumb-item v-for="(item, index) in breadcrumbData" :key="index">
<h5 class="ml-4 ellipsis" v-if="index === breadcrumbData.length - 1" :title="item.name">
{{ item.name }}
</h5>

View File

@ -23,6 +23,7 @@ import TagEllipsis from './tag-ellipsis/index.vue'
import CardCheckbox from './card-checkbox/index.vue'
import AiChat from './ai-chat/index.vue'
import KnowledgeIcon from './app-icon/KnowledgeIcon.vue'
import ToolIcon from './app-icon/ToolIcon.vue'
import TagGroup from './tag-group/index.vue'
import WorkspaceDropdown from './workspace-dropdown/index.vue'
import FolderBreadcrumb from './folder-breadcrumb/index.vue'
@ -52,6 +53,7 @@ export default {
app.component('CardCheckbox', CardCheckbox)
app.component('AiChat', AiChat)
app.component('KnowledgeIcon', KnowledgeIcon)
app.component('ToolIcon', ToolIcon)
app.component('TagGroup', TagGroup)
app.component('WorkspaceDropdown', WorkspaceDropdown)
app.component('FolderBreadcrumb', FolderBreadcrumb)

View File

@ -5,9 +5,11 @@
<template v-for="(item, index) in md_view_list" :key="index">
<div
v-if="item.type === 'question'"
@click="sendMessage ? sendMessage(item.content, 'new') : (content: string) => {}"
@click="
sendMessage && type !== 'log' ? sendMessage(item.content, 'new') : (content: string) => {}
"
class="problem-button mt-4 mb-4 flex"
:class="sendMessage ? 'cursor' : 'disabled'"
:class="sendMessage && type !== 'log' ? 'cursor' : 'disabled'"
>
<AppIcon iconName="app-edit" class="mr-8" style="margin-top: 2px"></AppIcon>
{{ item.content }}
@ -61,6 +63,7 @@ config({
tokens[idx].attrSet('target', '_blank')
return md.renderer.renderToken(tokens, idx, options)
}
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
document.appendChild
},
})
@ -74,6 +77,7 @@ const props = withDefaults(
chat_record_id?: string
runtime_node_id?: string
disabled?: boolean
type?: 'log' | 'ai-chat' | 'debug-ai-chat'
}>(),
{
source: '',

View File

@ -11,12 +11,12 @@
>
<img :src="resetUrl(current?.icon, resetUrl('./favicon.ico'))" alt="" />
</el-avatar>
<LogoIcon
<!-- <LogoIcon
v-else-if="isApplication"
height="28px"
style="width: 28px; height: 28px; display: block"
class="mr-8"
/>
/> -->
<KnowledgeIcon v-else-if="isKnowledge" :type="current?.type" class="mr-8" />
<div class="ellipsis" :title="current?.name">{{ current?.name }}</div>

View File

@ -68,9 +68,11 @@ export default {
inputPlaceholder: 'Please enter parameter values',
},
mcp: {
title: 'MCP Service',
label: 'MCP Server Config',
placeholder: 'Please enter MCP Server config',
tip: 'Only supports SSE and Streamable HTTP calling methods',
requiredMessage: 'Please enter MCP Server Config',
},
debug: {
run: 'Run',

View File

@ -64,9 +64,11 @@ export default {
inputPlaceholder: '请输入参数值',
},
mcp: {
title: 'MCP 服务',
label: 'MCP Server Config',
placeholder: '请输入MCP Server配置',
tip: '仅支持SSE、Streamable HTTP调用方式',
requiredMessage: '请输入 MCP Server Config',
},
debug: {
run: '运行',

View File

@ -65,9 +65,11 @@ export default {
inputPlaceholder: '請輸入參數值',
},
mcp: {
title: 'MCP 服務',
label: 'MCP Server Config',
placeholder: '請輸入MCP Server配置',
tip: '僅支援SSE、Streamable HTTP呼叫方式',
requiredMessage: '請輸入 MCP Server Config',
},
debug: {
run: '運行',

View File

@ -155,6 +155,7 @@
// radio-button-group
.app-radio-button-group {
background: #ffffff;
border: 1px solid var(--app-border-color-dark);
border-radius: var(--el-border-radius-base);
.el-radio-button {

View File

@ -282,3 +282,8 @@
.el-tag {
padding: 0 6px;
}
// el-input
.el-input {
--el-input-text-color: var(--el-text-color-primary);
}

View File

@ -54,7 +54,7 @@
<el-scrollbar>
<div class="p-16-24 pt-0" style="height: calc(100vh - 200px)">
<el-row :gutter="12" v-loading="loading" v-if="filterData.length">
<el-row :gutter="12" v-loading="loading || apiLoading" v-if="filterData.length">
<el-col
:span="12"
v-for="(item, index) in filterData.filter((v: any) => v.resource_type !== 'folder')"
@ -132,7 +132,7 @@ const currentEmbedding = ref('')
const searchValue = ref('')
const searchData = ref<Array<any>>([])
const knowledgeList = ref<Array<any>>([])
const loading = ref(false)
const apiLoading = ref(false)
const filterData = computed(() => {
return currentEmbedding.value
@ -228,7 +228,7 @@ function getList() {
isShared: folder_id === 'share',
systemType: apiType.value,
})
.getKnowledgeList({ folder_id }, loading)
.getKnowledgeList({ folder_id }, apiLoading)
.then((res: any) => {
knowledgeList.value = uniqueArray([...knowledgeList.value, ...res.data], 'id')
searchData.value = res.data

View File

@ -13,6 +13,8 @@
ref="paramFormRef"
:model="form"
require-asterisk-position="right"
hide-required-asterisk
@submit.prevent
>
<el-form-item>
<el-radio-group v-model="form.mcp_source">
@ -22,7 +24,22 @@
<el-radio value="custom">{{ $t('common.custom') }}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item v-if="form.mcp_source === 'referencing'">
<el-form-item
v-if="form.mcp_source === 'referencing'"
:rules="[
{
required: true,
message:
$t('common.selectPlaceholder') +
` MCP ${$t('views.applicationWorkflow.nodes.mcpNode.tool')}`,
},
]"
prop="mcp_tool_id"
>
<template #label>
{{ `MCP ${$t('views.applicationWorkflow.nodes.mcpNode.tool')}` }}
<span class="color-danger">*</span>
</template>
<el-select v-model="form.mcp_tool_id" filterable>
<el-option
v-for="mcpTool in mcpToolSelectOptions"
@ -30,19 +47,35 @@
: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>
<div class="flex align-center">
<el-avatar shape="square" :size="20" class="mr-8">
<img src="@/assets/workflow/icon_mcp.svg" style="width: 75%" alt="" />
</el-avatar>
<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>
</div>
</el-option>
</el-select>
</el-form-item>
<el-form-item
v-else
:label="$t('views.applicationWorkflow.nodes.mcpNode.configLabel')"
prop="mcp_servers"
:rules="[{ required: true, message: $t('common.required') }]"
:rules="[
{
required: true,
message: $t('common.inputPlaceholder') + ' ' + $t('views.tool.form.mcp.label'),
},
]"
>
<template #label>
{{ $t('views.tool.form.mcp.label') }}
<span class="color-danger">*</span>
<el-text type="info" class="color-secondary">
{{ $t('views.tool.form.mcp.tip') }}
</el-text>
</template>
<el-input
v-model="form.mcp_servers"
:rows="6"
@ -63,9 +96,9 @@
</el-dialog>
</template>
<script setup lang="ts">
import {computed, inject, onMounted, ref, watch} from 'vue'
import {loadSharedApi} from "@/utils/dynamics-api/shared-api.ts";
import {useRoute} from "vue-router";
import { computed, inject, onMounted, ref, watch } from 'vue'
import { loadSharedApi } from '@/utils/dynamics-api/shared-api.ts'
import { useRoute } from 'vue-router'
const getApplicationDetail = inject('getApplicationDetail') as any
const applicationDetail = getApplicationDetail()
@ -106,38 +139,40 @@ watch(dialogVisible, (bool) => {
mcp_tool_id: '',
mcp_source: 'referencing',
}
paramFormRef.value?.clearValidate()
}
})
function getMcpToolSelectOptions() {
const obj =
apiType.value === 'systemManage'
? {
scope: 'WORKSPACE',
tool_type: 'MCP',
workspace_id: applicationDetail.value?.workspace_id,
}
scope: 'WORKSPACE',
tool_type: 'MCP',
workspace_id: applicationDetail.value?.workspace_id,
}
: {
scope: 'WORKSPACE',
tool_type: 'MCP',
}
scope: 'WORKSPACE',
tool_type: 'MCP',
}
loadSharedApi({type: 'tool', systemType: apiType.value})
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)
mcpToolSelectOptions.value = [...res.data.shared_tools, ...res.data.tools].filter(
(item: any) => item.is_active,
)
})
}
const open = (data: any) => {
form.value = {...form.value, ...data}
form.value = { ...form.value, ...data }
form.value.mcp_source = data.mcp_source || 'referencing'
dialogVisible.value = true
}
const submit = () => {
paramFormRef.value.validate().then((valid: any) => {
paramFormRef.value.validate((valid: any) => {
if (valid) {
emit('refresh', form.value)
dialogVisible.value = false
@ -149,6 +184,6 @@ onMounted(() => {
getMcpToolSelectOptions()
})
defineExpose({open})
defineExpose({ open })
</script>
<style lang="scss" scoped></style>

View File

@ -39,7 +39,12 @@
clearable
>
<template #prepend>
<el-select v-model="searchType" placeholder="Select" style="width: 80px">
<el-select
v-model="searchType"
placeholder="Select"
style="width: 80px"
@change="searchTypeChange"
>
<el-option :label="$t('common.title')" value="title" />
<el-option :label="$t('common.content')" value="content" />
</el-select>
@ -227,6 +232,11 @@ const title = ref('')
const search = ref('')
const searchType = ref('title')
const searchTypeChange = () => {
search.value = ''
}
const dialogVisible = ref(false)
watch(
() => ParagraphDialogRef.value?.dialogVisible,

View File

@ -52,18 +52,9 @@
<template #default="scope">
<div class="table-name flex align-center">
<el-icon size="24" class="mr-8">
<el-avatar
v-if="isAppIcon(scope.row?.icon)"
shape="square"
:size="24"
style="background: none"
class="mr-8"
>
<el-avatar shape="square" :size="24" style="background: none" class="mr-8">
<img :src="resetUrl(scope.row?.icon)" alt="" />
</el-avatar>
<el-avatar v-else class="avatar-green" shape="square" :size="24">
<img src="@/assets/workflow/icon_tool.svg" style="width: 58%" alt="" />
</el-avatar>
</el-icon>
{{ scope.row.name }}
</div>
@ -448,7 +439,6 @@ watch(
{ immediate: true },
)
function filterWorkspaceChange(val: string) {
if (val === 'clear') {
workspaceArr.value = []

View File

@ -61,9 +61,8 @@
>
<img :src="resetUrl(row?.icon)" alt="" />
</el-avatar>
<el-avatar v-else class="avatar-green" shape="square" :size="24">
<img src="@/assets/workflow/icon_tool.svg" style="width: 58%" alt="" />
</el-avatar>
<ToolIcon v-else :size="24" :type="row?.tool_type" />
</el-icon>
{{ row.name }}
</div>
@ -72,13 +71,15 @@
<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-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>

View File

@ -2,19 +2,20 @@
<div class="tool-shared">
<ToolListContainer>
<template #header>
<el-breadcrumb separator-icon="ArrowRight">
<el-breadcrumb-item>{{ t('views.shared.shared_resources') }}</el-breadcrumb-item>
<el-breadcrumb-item>
<h5 class="ml-4 color-text-primary">{{ t('views.tool.title') }}</h5>
</el-breadcrumb-item>
</el-breadcrumb>
<div class="mt-16 mb-16">
<el-space wrap>
<el-breadcrumb separator-icon="ArrowRight">
<el-breadcrumb-item>{{ t('views.shared.shared_resources') }}</el-breadcrumb-item>
<el-breadcrumb-item>
<h5 class="ml-4 color-text-primary">{{ t('views.tool.title') }}</h5>
</el-breadcrumb-item>
</el-breadcrumb>
<el-divider direction="vertical" />
<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>
</el-space>
</template>
</ToolListContainer>
</div>
@ -26,11 +27,10 @@ import { onMounted, ref, reactive, computed } from 'vue'
import ToolListContainer from '@/views/tool/component/ToolListContainer.vue'
import { t } from '@/locales'
import useStore from "@/stores";
import useStore from '@/stores'
const { tool } = useStore()
const toolType = ref('')
function radioChange() {

View File

@ -13,6 +13,7 @@
:rules="rules"
label-position="top"
require-asterisk-position="right"
hide-required-asterisk
v-loading="loading"
@submit.prevent
>
@ -31,10 +32,11 @@
:size="32"
style="background: none"
>
<img :src="String(form.icon)" alt=""/>
<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 v-else shape="square" :size="32">
<img src="@/assets/workflow/icon_mcp.svg" style="width: 75%" alt="" />
</el-avatar>
<el-Avatar
v-if="showEditIcon"
@ -47,8 +49,9 @@
<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 v-else shape="square" :size="32" class="mr-12">
<img src="@/assets/workflow/icon_mcp.svg" style="width: 75%" alt="" />
</el-avatar>
<el-input
v-model="form.name"
@ -71,24 +74,26 @@
@blur="form.desc = form.desc?.trim()"
/>
</el-form-item>
<h4 class="title-decoration-1 mb-16">
{{ $t('views.tool.form.mcp.title') }}
</h4>
<el-form-item :label="$t('views.tool.form.toolDescription.label')" prop="code">
<template #label>
{{ $t('views.tool.form.mcp.label') }}
<span class="color-danger">*</span>
<el-text type="info" class="color-secondary">
{{ $t('views.tool.form.mcp.tip') }}
</el-text>
</template>
<el-input
v-model="form.code"
:placeholder="mcpServerJson"
type="textarea"
:autosize="{ minRows: 5 }"
/>
</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>
@ -104,30 +109,30 @@
</el-button>
</div>
</template>
<EditAvatarDialog ref="EditAvatarDialogRef" @refresh="refreshTool"/>
<EditAvatarDialog ref="EditAvatarDialogRef" @refresh="refreshTool" />
</el-drawer>
</template>
<script setup lang="ts">
import {computed, reactive, ref, watch} from 'vue'
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 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'
import { loadSharedApi } from '@/utils/dynamics-api/shared-api'
const route = useRoute()
const props = defineProps({
title: String,
})
const {folder, user} = useStore()
const { folder, user } = useStore()
const apiType = computed(() => {
if (route.path.includes('shared')) {
@ -196,6 +201,13 @@ const rules = reactive({
trigger: 'blur',
},
],
code: [
{
required: true,
message: t('views.tool.form.mcp.requiredMessage'),
trigger: 'blur',
},
],
})
function close() {
@ -209,8 +221,7 @@ function close() {
.then(() => {
visible.value = false
})
.catch(() => {
})
.catch(() => {})
}
}
@ -226,7 +237,6 @@ function refreshTool(data: any) {
form.value.icon = data
}
function openEditAvatar() {
EditAvatarDialogRef.value.open(form.value)
}
@ -236,7 +246,7 @@ const submit = async (formEl: FormInstance | undefined) => {
await formEl.validate((valid: any) => {
if (valid) {
if (isEdit.value) {
loadSharedApi({type: 'tool', systemType: apiType.value})
loadSharedApi({ type: 'tool', systemType: apiType.value })
.putTool(form.value?.id as string, form.value, loading)
.then((res: any) => {
MsgSuccess(t('common.editSuccess'))
@ -251,7 +261,7 @@ const submit = async (formEl: FormInstance | undefined) => {
folder_id: folder.currentFolder?.id,
...form.value,
}
loadSharedApi({type: 'tool', systemType: apiType.value})
loadSharedApi({ type: 'tool', systemType: apiType.value })
.postTool(obj, loading)
.then((res: any) => {
MsgSuccess(t('common.createSuccess'))

View File

@ -1,5 +1,11 @@
<template>
<el-drawer v-model="debugVisible" size="60%" :append-to-body="true" :modal="false">
<el-drawer
v-model="debugVisible"
size="60%"
:append-to-body="true"
:modal="false"
:show-close="false"
>
<template #header>
<div class="flex align-center" style="margin-left: -8px">
<el-button class="cursor mr-4" link @click.prevent="debugVisible = false">
@ -54,7 +60,8 @@
<template #label>
<div class="flex">
<span
>{{ item.name }} <span class="color-danger" v-if="item.is_required">*</span></span
>{{ item.name }}
<span class="color-danger" v-if="item.is_required">*</span></span
>
<el-tag type="info" class="info-tag ml-4">{{ item.type }}</el-tag>
</div>
@ -95,11 +102,7 @@
<p class="lighter mb-8">{{ $t('views.tool.form.debug.output') }}</p>
<el-card
:class="isSuccess ? '' : 'color-danger'"
class="pre-wrap"
shadow="never"
>
<el-card :class="isSuccess ? '' : 'color-danger'" class="pre-wrap" shadow="never">
{{ String(result) == '0' ? 0 : result || '-' }}
</el-card>
</div>

View File

@ -196,7 +196,7 @@
</el-table>
<h4 class="title-decoration-1 mb-16">
{{ $t('views.tool.form.param.code') }}
<span style="color: red; margin-left: -10px">*</span>
<span class="color-danger" style="margin-left: -10px">*</span>
<el-text type="info" class="color-secondary">
{{ $t('views.tool.form.param.paramInfo2') }}
</el-text>

View File

@ -50,17 +50,19 @@
<img src="@/assets/workflow/icon_tool.svg" style="width: 58%" alt="" />
</el-avatar>
<div class="pre-wrap ml-8">
<div class="lighter">空白创建</div>
<div class="lighter">
{{ $t('views.application.form.appTemplate.blankApp.title') }}
</div>
</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 shape="square" :size="32">
<img src="@/assets/workflow/icon_mcp.svg" style="width: 75%" alt="" />
</el-avatar>
<div class="pre-wrap ml-8">
<div class="lighter">创建MCP</div>
<div class="lighter">{{ $t('common.create') }} MCP</div>
</div>
</div>
</el-dropdown-item>
@ -77,7 +79,7 @@
>
<el-dropdown-item v-if="permissionPrecise.import()">
<div class="flex align-center w-full">
<el-avatar shape="square" class="mt-4" :size="36" style="background: none">
<el-avatar shape="square" :size="36" style="background: none">
<img src="@/assets/icon_import.svg" alt="" />
</el-avatar>
<div class="pre-wrap ml-8">
@ -88,8 +90,8 @@
</el-upload>
<el-dropdown-item @click="openToolStoreDialog()">
<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 shape="square" :size="36" style="background: none">
<img src="@/assets/icon_tool_shop.svg" alt="" />
</el-avatar>
<div class="pre-wrap ml-8">
<div class="lighter">
@ -173,9 +175,7 @@
>
<img :src="resetUrl(item?.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>
<ToolIcon v-else :size="32" :type="item?.tool_type" />
</template>
<template #subTitle>
<el-text class="color-secondary lighter" size="small">

View File

@ -16,17 +16,18 @@
</template>
<ToolListContainer @refreshFolder="refreshFolder">
<template #header>
<h2 v-if="folder.currentFolder?.id === 'share'">
{{ $t('views.shared.shared_tool') }}
</h2>
<FolderBreadcrumb :folderList="folderList" @click="folderClickHandle" v-else />
<div class="mt-16 mb-16">
<el-space wrap>
<h2 v-if="folder.currentFolder?.id === 'share'">
{{ $t('views.shared.shared_tool') }}
</h2>
<FolderBreadcrumb :folderList="folderList" @click="folderClickHandle" v-else />
<el-divider direction="vertical" />
<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>
</el-space>
</template>
</ToolListContainer>
</LayoutContainer>

View File

@ -1,6 +1,6 @@
<template>
<el-avatar shape="square" style="background: #34c724">
<img src="@/assets/workflow/icon_mcp.svg" style="width: 65%" alt="" />
<el-avatar shape="square">
<img src="@/assets/workflow/icon_mcp.svg" style="width: 75%" alt="" />
</el-avatar>
</template>
<script setup lang="ts"></script>

View File

@ -117,7 +117,13 @@
<div class="flex-between mb-16">
<div class="lighter">MCP</div>
<div>
<el-button type="primary" link @click="openMcpServersDialog" @refreshForm="refreshParam">
<el-button
type="primary"
class="mr-4"
link
@click="openMcpServersDialog"
@refreshForm="refreshParam"
>
<AppIcon iconName="app-setting"></AppIcon>
</el-button>
<el-switch size="small" v-model="chat_data.mcp_enable" />
@ -126,7 +132,13 @@
<div class="flex-between mb-16">
<div class="lighter">{{ $t('views.applicationWorkflow.nodes.mcpNode.tool') }}</div>
<div>
<el-button type="primary" link @click="openToolDialog" @refreshForm="refreshParam">
<el-button
type="primary"
class="mr-4"
link
@click="openToolDialog"
@refreshForm="refreshParam"
>
<AppIcon iconName="app-setting"></AppIcon>
</el-button>
<el-switch size="small" v-model="chat_data.tool_enable" />
@ -178,7 +190,7 @@
@refresh="submitReasoningDialog"
/>
<McpServersDialog ref="mcpServersDialogRef" @refresh="submitMcpServersDialog" />
<ToolDialog ref="toolDialogRef" @refresh="submitToolDialog"/>
<ToolDialog ref="toolDialogRef" @refresh="submitToolDialog" />
</NodeContainer>
</template>
<script setup lang="ts">
@ -193,7 +205,7 @@ import ReasoningParamSettingDialog from '@/views/application/component/Reasoning
import McpServersDialog from '@/views/application/component/McpServersDialog.vue'
import { loadSharedApi } from '@/utils/dynamics-api/shared-api'
import { useRoute } from 'vue-router'
import ToolDialog from "@/views/application/component/ToolDialog.vue";
import ToolDialog from '@/views/application/component/ToolDialog.vue'
const getApplicationDetail = inject('getApplicationDetail') as any
const route = useRoute()
@ -366,25 +378,24 @@ function getToolSelectOptions() {
const obj =
apiType.value === 'systemManage'
? {
scope: 'WORKSPACE',
tool_type: 'CUSTOM',
workspace_id: application.value?.workspace_id,
}
scope: 'WORKSPACE',
tool_type: 'CUSTOM',
workspace_id: application.value?.workspace_id,
}
: {
scope: 'WORKSPACE',
tool_type: 'CUSTOM',
}
scope: 'WORKSPACE',
tool_type: 'CUSTOM',
}
loadSharedApi({type: 'tool', systemType: apiType.value})
loadSharedApi({ type: 'tool', systemType: apiType.value })
.getAllToolList(obj)
.then((res: any) => {
toolSelectOptions.value = [...res.data.shared_tools, ...res.data.tools]
.filter((item: any) => item.is_active)
toolSelectOptions.value = [...res.data.shared_tools, ...res.data.tools].filter(
(item: any) => item.is_active,
)
})
}
onMounted(() => {
getSelectModel()
if (typeof props.nodeModel.properties.node_data?.is_result === 'undefined') {