Skip to content

Commit 1875368

Browse files
committed
feat: enhance MCP tool dialog with source selection and dynamic tool options
1 parent 870e7f2 commit 1875368

File tree

3 files changed

+219
-14
lines changed

3 files changed

+219
-14
lines changed

ui/src/views/application/component/McpServersDialog.vue

Lines changed: 72 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,31 @@
1414
:model="form"
1515
require-asterisk-position="right"
1616
>
17-
<el-form-item label="MCP" prop="mcp_enable" @click.prevent>
18-
<el-switch v-model="form.mcp_enable" />
17+
<el-form-item>
18+
<el-radio-group v-model="form.mcp_source">
19+
<el-radio value="referencing">
20+
{{ $t('views.applicationWorkflow.nodes.mcpNode.reference') }}
21+
</el-radio>
22+
<el-radio value="custom">{{ $t('common.custom') }}</el-radio>
23+
</el-radio-group>
24+
</el-form-item>
25+
<el-form-item v-if="form.mcp_source === 'referencing'">
26+
<el-select v-model="form.mcp_tool_id" filterable>
27+
<el-option
28+
v-for="mcpTool in mcpToolSelectOptions"
29+
:key="mcpTool.id"
30+
:label="mcpTool.name"
31+
:value="mcpTool.id"
32+
>
33+
<span>{{ mcpTool.name }}</span>
34+
<el-tag v-if="mcpTool.scope === 'SHARED'" type="info" class="info-tag ml-8 mt-4">
35+
{{ $t('views.shared.title') }}
36+
</el-tag>
37+
</el-option>
38+
</el-select>
1939
</el-form-item>
2040
<el-form-item
21-
v-if="form.mcp_enable"
41+
v-else
2242
:label="$t('views.applicationWorkflow.nodes.mcpNode.configLabel')"
2343
prop="mcp_servers"
2444
:rules="[{ required: true, message: $t('common.required') }]"
@@ -43,10 +63,21 @@
4363
</el-dialog>
4464
</template>
4565
<script setup lang="ts">
46-
import { ref, watch } from 'vue'
66+
import {computed, inject, onMounted, ref, watch} from 'vue'
67+
import {loadSharedApi} from "@/utils/dynamics-api/shared-api.ts";
68+
import {useRoute} from "vue-router";
4769
70+
const getApplicationDetail = inject('getApplicationDetail') as any
71+
const applicationDetail = getApplicationDetail()
4872
const emit = defineEmits(['refresh'])
49-
73+
const route = useRoute()
74+
const apiType = computed(() => {
75+
if (route.path.includes('resource-management')) {
76+
return 'systemManage'
77+
} else {
78+
return 'workspace'
79+
}
80+
})
5081
const paramFormRef = ref()
5182
5283
const mcpServerJson = `{
@@ -58,22 +89,50 @@ const mcpServerJson = `{
5889
5990
const form = ref<any>({
6091
mcp_servers: '',
61-
mcp_enable: false,
92+
mcp_tool_id: '',
93+
mcp_source: 'referencing',
6294
})
6395
96+
const mcpToolSelectOptions = ref<any[]>([])
97+
6498
const dialogVisible = ref<boolean>(false)
99+
65100
const loading = ref(false)
101+
66102
watch(dialogVisible, (bool) => {
67103
if (!bool) {
68104
form.value = {
69105
mcp_servers: '',
70-
mcp_enable: false,
106+
mcp_tool_id: '',
107+
mcp_source: 'referencing',
71108
}
72109
}
73110
})
74111
112+
113+
function getMcpToolSelectOptions() {
114+
const obj =
115+
apiType.value === 'systemManage'
116+
? {
117+
scope: 'WORKSPACE',
118+
tool_type: 'MCP',
119+
workspace_id: applicationDetail.value?.workspace_id,
120+
}
121+
: {
122+
scope: 'WORKSPACE',
123+
tool_type: 'MCP',
124+
}
125+
126+
loadSharedApi({type: 'tool', systemType: apiType.value})
127+
.getAllToolList(obj, loading)
128+
.then((res: any) => {
129+
mcpToolSelectOptions.value = [...res.data.shared_tools, ...res.data.tools]
130+
.filter((item: any) => item.is_active)
131+
})
132+
}
133+
75134
const open = (data: any) => {
76-
form.value = { ...form.value, ...data }
135+
form.value = {...form.value, ...data}
77136
dialogVisible.value = true
78137
}
79138
@@ -86,6 +145,10 @@ const submit = () => {
86145
})
87146
}
88147
89-
defineExpose({ open })
148+
onMounted(() => {
149+
getMcpToolSelectOptions()
150+
})
151+
152+
defineExpose({open})
90153
</script>
91154
<style lang="scss" scoped></style>
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
<template>
2+
<el-dialog
3+
align-center
4+
:title="$t('common.setting')"
5+
v-model="dialogVisible"
6+
style="width: 550px"
7+
append-to-body
8+
:close-on-click-modal="false"
9+
:close-on-press-escape="false"
10+
>
11+
<el-form
12+
label-position="top"
13+
ref="paramFormRef"
14+
:model="form"
15+
require-asterisk-position="right"
16+
>
17+
<el-form-item>
18+
<el-select v-model="form.tool_ids" filterable multiple>
19+
<el-option
20+
v-for="mcpTool in toolSelectOptions"
21+
:key="mcpTool.id"
22+
:label="mcpTool.name"
23+
:value="mcpTool.id"
24+
>
25+
<span>{{ mcpTool.name }}</span>
26+
<el-tag v-if="mcpTool.scope === 'SHARED'" type="info" class="info-tag ml-8 mt-4">
27+
{{ $t('views.shared.title') }}
28+
</el-tag>
29+
</el-option>
30+
</el-select>
31+
</el-form-item>
32+
</el-form>
33+
34+
<template #footer>
35+
<span class="dialog-footer">
36+
<el-button @click.prevent="dialogVisible = false">{{ $t('common.cancel') }}</el-button>
37+
<el-button type="primary" @click="submit()" :loading="loading">
38+
{{ $t('common.save') }}
39+
</el-button>
40+
</span>
41+
</template>
42+
</el-dialog>
43+
</template>
44+
<script setup lang="ts">
45+
import {ref, watch} from 'vue'
46+
47+
const emit = defineEmits(['refresh'])
48+
49+
const paramFormRef = ref()
50+
51+
const form = ref<any>({
52+
tool_ids: [],
53+
})
54+
55+
const toolSelectOptions = ref<any[]>([])
56+
57+
const dialogVisible = ref<boolean>(false)
58+
59+
const loading = ref(false)
60+
61+
watch(dialogVisible, (bool) => {
62+
if (!bool) {
63+
form.value = {
64+
tool_ids: [],
65+
}
66+
}
67+
})
68+
69+
70+
const open = (data: any, selectOptions: any) => {
71+
form.value = {...form.value, ...data}
72+
dialogVisible.value = true
73+
toolSelectOptions.value = selectOptions
74+
}
75+
76+
const submit = () => {
77+
paramFormRef.value.validate().then((valid: any) => {
78+
if (valid) {
79+
emit('refresh', form.value)
80+
dialogVisible.value = false
81+
}
82+
})
83+
}
84+
85+
86+
87+
defineExpose({open})
88+
</script>
89+
<style lang="scss" scoped></style>

ui/src/workflow/nodes/ai-chat-node/index.vue

Lines changed: 58 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -114,11 +114,23 @@
114114
/>
115115
</el-form-item>
116116

117+
<div class="flex-between mb-16">
118+
<div class="lighter">MCP</div>
119+
<div>
120+
<el-button type="primary" link @click="openMcpServersDialog" @refreshForm="refreshParam">
121+
<AppIcon iconName="app-setting"></AppIcon>
122+
</el-button>
123+
<el-switch size="small" v-model="chat_data.mcp_enable" />
124+
</div>
125+
</div>
117126
<div class="flex-between mb-16">
118127
<div class="lighter">{{ $t('views.applicationWorkflow.nodes.mcpNode.tool') }}</div>
119-
<el-button type="primary" link @click="openMcpServersDialog" @refreshForm="refreshParam">
120-
<AppIcon iconName="app-setting"></AppIcon>
121-
</el-button>
128+
<div>
129+
<el-button type="primary" link @click="openToolDialog" @refreshForm="refreshParam">
130+
<AppIcon iconName="app-setting"></AppIcon>
131+
</el-button>
132+
<el-switch size="small" v-model="chat_data.tool_enable" />
133+
</div>
122134
</div>
123135

124136
<el-form-item @click.prevent>
@@ -166,6 +178,7 @@
166178
@refresh="submitReasoningDialog"
167179
/>
168180
<McpServersDialog ref="mcpServersDialogRef" @refresh="submitMcpServersDialog" />
181+
<ToolDialog ref="toolDialogRef" @refresh="submitToolDialog"/>
169182
</NodeContainer>
170183
</template>
171184
<script setup lang="ts">
@@ -180,6 +193,7 @@ import ReasoningParamSettingDialog from '@/views/application/component/Reasoning
180193
import McpServersDialog from '@/views/application/component/McpServersDialog.vue'
181194
import { loadSharedApi } from '@/utils/dynamics-api/shared-api'
182195
import { useRoute } from 'vue-router'
196+
import ToolDialog from "@/views/application/component/ToolDialog.vue";
183197
const getApplicationDetail = inject('getApplicationDetail') as any
184198
const route = useRoute()
185199
@@ -324,16 +338,53 @@ const mcpServersDialogRef = ref()
324338
function openMcpServersDialog() {
325339
const config = {
326340
mcp_servers: chat_data.value.mcp_servers,
327-
mcp_enable: chat_data.value.mcp_enable,
341+
mcp_tool_id: chat_data.value.mcp_tool_id,
342+
mcp_source: chat_data.value.mcp_source,
328343
}
329344
mcpServersDialogRef.value.open(config)
330345
}
331346
332347
function submitMcpServersDialog(config: any) {
333348
set(props.nodeModel.properties.node_data, 'mcp_servers', config.mcp_servers)
334-
set(props.nodeModel.properties.node_data, 'mcp_enable', config.mcp_enable)
349+
set(props.nodeModel.properties.node_data, 'mcp_tool_id', config.mcp_tool_id)
350+
set(props.nodeModel.properties.node_data, 'mcp_source', config.mcp_source)
335351
}
336352
353+
const toolDialogRef = ref()
354+
function openToolDialog() {
355+
const config = {
356+
tool_ids: chat_data.value.tool_ids,
357+
}
358+
toolDialogRef.value.open(config, toolSelectOptions.value)
359+
}
360+
function submitToolDialog(config: any) {
361+
set(props.nodeModel.properties.node_data, 'tool_ids', config.tool_ids)
362+
}
363+
364+
const toolSelectOptions = ref<any[]>([])
365+
function getToolSelectOptions() {
366+
const obj =
367+
apiType.value === 'systemManage'
368+
? {
369+
scope: 'WORKSPACE',
370+
tool_type: 'CUSTOM',
371+
workspace_id: application.value?.workspace_id,
372+
}
373+
: {
374+
scope: 'WORKSPACE',
375+
tool_type: 'CUSTOM',
376+
}
377+
378+
loadSharedApi({type: 'tool', systemType: apiType.value})
379+
.getAllToolList(obj)
380+
.then((res: any) => {
381+
toolSelectOptions.value = [...res.data.shared_tools, ...res.data.tools]
382+
.filter((item: any) => item.is_active)
383+
})
384+
}
385+
386+
387+
337388
onMounted(() => {
338389
getSelectModel()
339390
if (typeof props.nodeModel.properties.node_data?.is_result === 'undefined') {
@@ -345,6 +396,8 @@ onMounted(() => {
345396
if (!chat_data.value.dialogue_type) {
346397
chat_data.value.dialogue_type = 'WORKFLOW'
347398
}
399+
400+
getToolSelectOptions()
348401
})
349402
</script>
350403
<style lang="scss" scoped></style>

0 commit comments

Comments
 (0)