zhou zhou
2 天以前 450c9d39c6eb3765642f977512202e3240ac9b03
#波次
6个文件已修改
395 ■■■■ 已修改文件
rsf-design/src/locales/langs/en.json 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-design/src/locales/langs/zh.json 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-design/src/views/orders/wave/index.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-design/src/views/orders/wave/wavePage.helpers.js 39 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-design/src/views/system/ai-mcp-mount/index.vue 85 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-design/src/views/system/ai-mcp-mount/modules/ai-mcp-tools-drawer.vue 259 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-design/src/locales/langs/en.json
@@ -4164,6 +4164,11 @@
          "toolsLoadFailed": "Failed to load tools",
          "toolsTimeout": "Tool loading timed out and waiting has stopped",
          "toolTestTimeout": "Tool call timed out and waiting has stopped",
          "schemaParseFailed": "Failed to parse schema",
          "structuredInput": "Structured Input",
          "formattedSchema": "Formatted Schema",
          "fieldCount": "{count} fields",
          "required": "Required",
          "inputSchema": "Input Schema",
          "inputJson": "Input JSON",
          "inputJsonPlaceholder": "Enter JSON, e.g. {\"keyword\":\"task\"}",
rsf-design/src/locales/langs/zh.json
@@ -4172,6 +4172,11 @@
          "toolsLoadFailed": "工具加载失败",
          "toolsTimeout": "工具加载超时,已停止等待",
          "toolTestTimeout": "工具调用超时,已停止等待",
          "schemaParseFailed": "Schema 解析失败",
          "structuredInput": "结构化入参",
          "formattedSchema": "格式化 Schema",
          "fieldCount": "字段数 {count}",
          "required": "必填",
          "inputSchema": "输入 Schema",
          "inputJson": "输入参数(JSON)",
          "inputJsonPlaceholder": "请输入 JSON 参数,例如 {\"keyword\":\"task\"}",
rsf-design/src/views/orders/wave/index.vue
@@ -433,7 +433,7 @@
        { timeoutMessage: t('pages.orders.wave.messages.publicTaskTimeout') }
      )
      publicTaskRows.value = Array.isArray(previewResponse?.records)
        ? previewResponse.records.map((item) => normalizeWaveItemRow(item, t))
        ? previewResponse.records.map((item) => normalizeWaveItemRow(item, t, { placeholder: '' }))
        : []
      updatePaginationState(
        publicTaskPagination,
rsf-design/src/views/orders/wave/wavePage.helpers.js
@@ -187,36 +187,37 @@
  }
}
export function normalizeWaveItemRow(record = {}, t) {
export function normalizeWaveItemRow(record = {}, t, options = {}) {
  const placeholder = options.placeholder ?? '-'
  const statusConfig = getItemStatusConfig(record.exceStatus, record['exceStatus$'], t)
  return {
    ...record,
    id: record.id ?? null,
    waveId: record.waveId ?? '-',
    waveCode: record.waveCode || '-',
    orderCode: record.orderCode || '-',
    orderItemId: record.orderItemId ?? '-',
    matnrId: record.matnrId ?? '-',
    matnrCode: record.matnrCode || '-',
    maktx: record.maktx || '-',
    batch: record.batch || '-',
    splrBatch: record.splrBatch || '-',
    unit: record.unit || '-',
    memo: record.memo || '-',
    trackCode: record.trackCode || '-',
    fieldsIndex: record.fieldsIndex || '-',
    waveId: record.waveId ?? placeholder,
    waveCode: record.waveCode || placeholder,
    orderCode: record.orderCode || placeholder,
    orderItemId: record.orderItemId ?? placeholder,
    matnrId: record.matnrId ?? placeholder,
    matnrCode: record.matnrCode || placeholder,
    maktx: record.maktx || placeholder,
    batch: record.batch || placeholder,
    splrBatch: record.splrBatch || placeholder,
    unit: record.unit || placeholder,
    memo: record.memo || placeholder,
    trackCode: record.trackCode || placeholder,
    fieldsIndex: record.fieldsIndex || placeholder,
    anfme: normalizeNumber(record.anfme),
    qty: normalizeNumber(record.qty),
    workQty: normalizeNumber(record.workQty),
    stockQty: normalizeNumber(record.stockQty),
    stockLocsText: normalizeStockLocs(record.stockLocs),
    updateByText: record['updateBy$'] || record.updateBy || '-',
    createByText: record['createBy$'] || record.createBy || '-',
    statusLabel: record['status$'] || record.status || '-',
    updateByText: record['updateBy$'] || record.updateBy || placeholder,
    createByText: record['createBy$'] || record.createBy || placeholder,
    statusLabel: record['status$'] || record.status || placeholder,
    exceStatusText: statusConfig.label,
    exceStatusTagType: statusConfig.tagType,
    updateTimeText: record['updateTime$'] || record.updateTime || '-',
    createTimeText: record['createTime$'] || record.createTime || '-'
    updateTimeText: record['updateTime$'] || record.updateTime || placeholder,
    createTimeText: record['createTime$'] || record.createTime || placeholder
  }
}
rsf-design/src/views/system/ai-mcp-mount/index.vue
@@ -11,18 +11,30 @@
    <ElCard class="art-table-card">
      <div class="mb-5 flex flex-wrap items-center justify-between gap-4">
        <div>
          <h3 class="text-lg font-semibold text-[var(--art-gray-900)]">{{ t('pages.system.aiMcpMount.title') }}</h3>
          <p class="mt-1 text-sm text-[var(--art-gray-500)]">{{ t('pages.system.aiMcpMount.subtitle') }}</p>
          <h3 class="text-lg font-semibold text-[var(--art-gray-900)]">{{
            t('pages.system.aiMcpMount.title')
          }}</h3>
          <p class="mt-1 text-sm text-[var(--art-gray-500)]">{{
            t('pages.system.aiMcpMount.subtitle')
          }}</p>
        </div>
        <ElSpace wrap>
          <ElButton v-auth="'save'" @click="openCreateDialog" v-ripple>{{ t('pages.system.aiMcpMount.buttons.add') }}</ElButton>
          <ElButton :loading="loading" @click="refreshData" v-ripple>{{ t('common.actions.refresh') }}</ElButton>
          <ElButton @click="openCreateDialog" v-ripple>{{
            t('pages.system.aiMcpMount.buttons.add')
          }}</ElButton>
          <ElButton :loading="loading" @click="refreshData" v-ripple>{{
            t('common.actions.refresh')
          }}</ElButton>
        </ElSpace>
      </div>
      <div v-loading="loading" class="space-y-6">
        <ElEmpty v-if="!groupedRecords.length" :description="t('pages.system.aiMcpMount.empty')" :image-size="110" />
        <ElEmpty
          v-if="!groupedRecords.length"
          :description="t('pages.system.aiMcpMount.empty')"
          :image-size="110"
        />
        <section v-for="group in groupedRecords" :key="group.key" class="space-y-4">
          <div>
@@ -65,7 +77,9 @@
                <div
                  class="rounded-2xl bg-[var(--art-main-bg-color)]/70 p-3 ring-1 ring-inset ring-[var(--art-border-color)]"
                >
                  <p class="text-xs text-[var(--art-gray-500)]">{{ t('pages.system.aiMcpMount.fields.target') }}</p>
                  <p class="text-xs text-[var(--art-gray-500)]">{{
                    t('pages.system.aiMcpMount.fields.target')
                  }}</p>
                  <p class="mt-2 break-all text-[var(--art-gray-900)]">{{
                    item.targetLabel || '--'
                  }}</p>
@@ -73,7 +87,9 @@
                <div
                  class="rounded-2xl bg-[var(--art-main-bg-color)]/70 p-3 ring-1 ring-inset ring-[var(--art-border-color)]"
                >
                  <p class="text-xs text-[var(--art-gray-500)]">{{ t('pages.system.aiMcpMount.fields.lastTestTime') }}</p>
                  <p class="text-xs text-[var(--art-gray-500)]">{{
                    t('pages.system.aiMcpMount.fields.lastTestTime')
                  }}</p>
                  <p class="mt-2 text-[var(--art-gray-900)]">{{
                    item['lastTestTime$'] || t('pages.system.aiMcpMount.health.notTested')
                  }}</p>
@@ -82,7 +98,9 @@
              <div class="mt-4 grid gap-3 text-sm sm:grid-cols-3">
                <div class="rounded-2xl bg-slate-50 px-3 py-2">
                  <p class="text-xs text-[var(--art-gray-500)]">{{ t('pages.system.aiMcpMount.fields.timeoutMs') }}</p>
                  <p class="text-xs text-[var(--art-gray-500)]">{{
                    t('pages.system.aiMcpMount.fields.timeoutMs')
                  }}</p>
                  <p class="mt-1 font-medium text-[var(--art-gray-900)]"
                    >{{ item.requestTimeoutMs ?? '--' }} ms</p
                  >
@@ -92,7 +110,9 @@
                  <p class="mt-1 font-medium text-[var(--art-gray-900)]">{{ item.sort ?? '--' }}</p>
                </div>
                <div class="rounded-2xl bg-slate-50 px-3 py-2">
                  <p class="text-xs text-[var(--art-gray-500)]">{{ t('pages.system.aiMcpMount.fields.lastInitElapsedMs') }}</p>
                  <p class="text-xs text-[var(--art-gray-500)]">{{
                    t('pages.system.aiMcpMount.fields.lastInitElapsedMs')
                  }}</p>
                  <p class="mt-1 font-medium text-[var(--art-gray-900)]">{{
                    item.lastInitElapsedMs ?? '--'
                  }}</p>
@@ -114,8 +134,12 @@
                }}</div>
                <ElSpace wrap>
                  <ElButton text @click="openDetailDialog(item)">{{ t('common.actions.detail') }}</ElButton>
                  <ElButton v-auth="'update'" text @click="openEditDialog(item)">{{ t('common.actions.edit') }}</ElButton>
                  <ElButton text @click="openDetailDialog(item)">{{
                    t('common.actions.detail')
                  }}</ElButton>
                  <ElButton v-auth="'update'" text @click="openEditDialog(item)">{{
                    t('common.actions.edit')
                  }}</ElButton>
                  <ElButton
                    v-auth="'update'"
                    text
@@ -124,8 +148,12 @@
                  >
                    {{ t('pages.system.aiMcpMount.actions.connectivityTest') }}
                  </ElButton>
                  <ElButton v-auth="'list'" text @click="openToolsDrawer(item)">{{ t('pages.system.aiMcpMount.actions.toolsPreview') }}</ElButton>
                  <ElButton v-auth="'remove'" text type="danger" @click="handleDelete(item)">{{ t('common.actions.delete') }}</ElButton>
                  <ElButton v-auth="'list'" text @click="openToolsDrawer(item)">{{
                    t('pages.system.aiMcpMount.actions.toolsPreview')
                  }}</ElButton>
                  <ElButton v-auth="'remove'" text type="danger" @click="handleDelete(item)">{{
                    t('common.actions.delete')
                  }}</ElButton>
                </ElSpace>
              </div>
            </article>
@@ -265,9 +293,21 @@
  const groupedRecords = computed(() => {
    const groups = [
      { key: 'BUILTIN', title: t('pages.system.aiMcpMount.groups.builtin.title'), description: t('pages.system.aiMcpMount.groups.builtin.description') },
      { key: 'SSE_HTTP', title: t('pages.system.aiMcpMount.groups.sse.title'), description: t('pages.system.aiMcpMount.groups.sse.description') },
      { key: 'STDIO', title: t('pages.system.aiMcpMount.groups.stdio.title'), description: t('pages.system.aiMcpMount.groups.stdio.description') }
      {
        key: 'BUILTIN',
        title: t('pages.system.aiMcpMount.groups.builtin.title'),
        description: t('pages.system.aiMcpMount.groups.builtin.description')
      },
      {
        key: 'SSE_HTTP',
        title: t('pages.system.aiMcpMount.groups.sse.title'),
        description: t('pages.system.aiMcpMount.groups.sse.description')
      },
      {
        key: 'STDIO',
        title: t('pages.system.aiMcpMount.groups.stdio.title'),
        description: t('pages.system.aiMcpMount.groups.stdio.description')
      }
    ]
    return groups
      .map((group) => ({
@@ -342,10 +382,11 @@
        }),
        t('crud.confirm.deleteTitle'),
        {
        confirmButtonText: t('common.confirm'),
        cancelButtonText: t('common.cancel'),
        type: 'warning'
      })
          confirmButtonText: t('common.confirm'),
          cancelButtonText: t('common.cancel'),
          type: 'warning'
        }
      )
      await fetchDeleteAiMcpMount(record.id)
      ElMessage.success(t('crud.messages.deleteSuccess'))
      await refreshRemove()
@@ -362,7 +403,9 @@
      const result = await guardRequestWithMessage(fetchTestAiMcpConnectivity(record.id), null, {
        timeoutMessage: t('pages.system.aiMcpMount.messages.connectivityTimeout')
      })
      ElMessage.success(result?.message || t('pages.system.aiMcpMount.messages.connectivitySuccess'))
      ElMessage.success(
        result?.message || t('pages.system.aiMcpMount.messages.connectivitySuccess')
      )
      await refreshUpdate()
    } catch (error) {
      ElMessage.error(error?.message || t('pages.system.aiMcpMount.messages.connectivityFailed'))
rsf-design/src/views/system/ai-mcp-mount/modules/ai-mcp-tools-drawer.vue
@@ -65,12 +65,80 @@
          >
            <div class="flex flex-wrap items-start justify-between gap-3">
              <div>
                <div class="text-base font-semibold text-[var(--art-gray-900)]">{{ tool.name }}</div>
                <div class="mt-1 text-sm text-[var(--art-gray-500)]">{{ tool.description || emptyText }}</div>
                <div class="text-base font-semibold text-[var(--art-gray-900)]">{{
                  tool.name
                }}</div>
                <div class="mt-1 text-sm text-[var(--art-gray-500)]">{{
                  tool.description || emptyText
                }}</div>
              </div>
              <ElButton :loading="testingToolName === tool.name" @click="handleToolTest(tool.name)">
                {{ t('pages.system.aiMcpMount.toolsDrawer.toolTest') }}
              </ElButton>
              <div class="flex flex-wrap items-center gap-2">
                <span class="text-xs text-[var(--art-gray-500)]">
                  {{
                    t('pages.system.aiMcpMount.toolsDrawer.fieldCount', {
                      count: schemaInfoMap[tool.name]?.fields?.length || 0
                    })
                  }}
                </span>
                <ElButton
                  :loading="testingToolName === tool.name"
                  @click="handleToolTest(tool.name)"
                >
                  {{ t('pages.system.aiMcpMount.toolsDrawer.toolTest') }}
                </ElButton>
              </div>
            </div>
            <ElAlert
              v-if="schemaInfoMap[tool.name]?.error"
              class="mt-4"
              type="warning"
              :closable="false"
            >
              {{ schemaInfoMap[tool.name].error }}
            </ElAlert>
            <div
              v-if="schemaInfoMap[tool.name]?.fields?.length"
              class="mt-4 rounded-xl bg-[var(--art-main-bg-color)] p-3"
            >
              <div class="text-xs text-[var(--art-gray-500)]">
                {{ t('pages.system.aiMcpMount.toolsDrawer.structuredInput') }}
              </div>
              <div class="mt-3 grid gap-4 md:grid-cols-2">
                <div
                  v-for="field in schemaInfoMap[tool.name].fields"
                  :key="`${tool.name}-${field.name}`"
                >
                  <ElSelect
                    v-if="field.type === 'boolean' || field.enumValues.length"
                    :model-value="structuredInputs[tool.name]?.[field.name] ?? ''"
                    clearable
                    filterable
                    class="w-full"
                    :placeholder="field.title"
                    @update:model-value="handleStructuredFieldChange(tool.name, field.name, $event)"
                  >
                    <ElOption
                      v-for="option in getFieldOptions(field)"
                      :key="`${field.name}-${option.value}`"
                      :label="option.label"
                      :value="option.value"
                    />
                  </ElSelect>
                  <ElInput
                    v-else
                    :model-value="structuredInputs[tool.name]?.[field.name] ?? ''"
                    clearable
                    :type="field.type === 'integer' || field.type === 'number' ? 'number' : 'text'"
                    :placeholder="field.title"
                    @update:model-value="handleStructuredFieldChange(tool.name, field.name, $event)"
                  />
                  <div class="mt-1 text-xs text-[var(--art-gray-500)]">
                    {{ buildFieldHelperText(field, schemaInfoMap[tool.name].required) }}
                  </div>
                </div>
              </div>
            </div>
            <div class="mt-4 grid gap-4 md:grid-cols-2">
@@ -79,10 +147,11 @@
                  {{ t('pages.system.aiMcpMount.toolsDrawer.inputJson') }}
                </div>
                <ElInput
                  v-model="toolInputs[tool.name]"
                  :model-value="toolInputs[tool.name] || ''"
                  type="textarea"
                  :rows="8"
                  :placeholder="t('pages.system.aiMcpMount.toolsDrawer.inputJsonPlaceholder')"
                  @update:model-value="handleInputChange(tool.name, $event)"
                />
              </div>
              <div class="space-y-2">
@@ -101,11 +170,12 @@
            <div v-if="tool.inputSchema" class="mt-4 rounded-xl bg-[var(--art-main-bg-color)] p-3">
              <div class="text-xs text-[var(--art-gray-500)]">
                {{ t('pages.system.aiMcpMount.toolsDrawer.inputSchema') }}
                {{ t('pages.system.aiMcpMount.toolsDrawer.formattedSchema') }}
              </div>
              <pre class="mt-2 whitespace-pre-wrap break-all text-xs leading-6 text-[var(--art-gray-900)]">{{
                formatSchema(tool.inputSchema)
              }}</pre>
              <pre
                class="mt-2 whitespace-pre-wrap break-all text-xs leading-6 text-[var(--art-gray-900)]"
                >{{ schemaInfoMap[tool.name]?.pretty || formatSchema(tool.inputSchema) }}</pre
              >
            </div>
          </div>
        </div>
@@ -118,7 +188,11 @@
  import { useI18n } from 'vue-i18n'
  import { ElMessage } from 'element-plus'
  import { guardRequestWithMessage } from '@/utils/sys/requestGuard'
  import { fetchPreviewAiMcpTools, fetchTestAiMcpConnectivity, fetchTestAiMcpTool } from '@/api/ai-config'
  import {
    fetchPreviewAiMcpTools,
    fetchTestAiMcpConnectivity,
    fetchTestAiMcpTool
  } from '@/api/ai-config'
  const props = defineProps({
    visible: { type: Boolean, default: false },
@@ -133,16 +207,24 @@
  const connectivityLoading = ref(false)
  const connectivityResult = ref(null)
  const toolInputs = reactive({})
  const structuredInputs = reactive({})
  const toolOutputs = reactive({})
  const testingToolName = ref('')
  const emptyText = computed(() => t('common.placeholder.empty'))
  const schemaInfoMap = computed(() =>
    tools.value.reduce((result, tool) => {
      result[tool.name] = parseInputSchema(tool.inputSchema)
      return result
    }, {})
  )
  function resetState() {
    tools.value = []
    connectivityResult.value = null
    testingToolName.value = ''
    Object.keys(toolInputs).forEach((key) => delete toolInputs[key])
    Object.keys(structuredInputs).forEach((key) => delete structuredInputs[key])
    Object.keys(toolOutputs).forEach((key) => delete toolOutputs[key])
  }
@@ -154,6 +236,136 @@
    }
  }
  function parseInputSchema(inputSchema) {
    if (!inputSchema) {
      return { pretty: '', fields: [], required: [], error: '' }
    }
    try {
      const schema = JSON.parse(inputSchema)
      const properties = schema?.properties || {}
      const required = Array.isArray(schema?.required) ? schema.required : []
      return {
        pretty: JSON.stringify(schema, null, 2),
        required,
        error: '',
        fields: Object.entries(properties).map(([name, definition]) => ({
          name,
          title: definition?.title || name,
          description: definition?.description || '',
          type: definition?.type || 'string',
          enumValues: Array.isArray(definition?.enum) ? definition.enum : []
        }))
      }
    } catch (error) {
      return {
        pretty: inputSchema,
        fields: [],
        required: [],
        error: error?.message || t('pages.system.aiMcpMount.toolsDrawer.schemaParseFailed')
      }
    }
  }
  function normalizeFieldValue(field, rawValue) {
    if (rawValue === '' || rawValue === undefined || rawValue === null) {
      return undefined
    }
    if (field.type === 'integer') {
      const parsed = Number.parseInt(rawValue, 10)
      return Number.isNaN(parsed) ? rawValue : parsed
    }
    if (field.type === 'number') {
      const parsed = Number(rawValue)
      return Number.isNaN(parsed) ? rawValue : parsed
    }
    if (field.type === 'boolean') {
      return rawValue === true || rawValue === 'true'
    }
    return rawValue
  }
  function buildInputJson(schemaInfo, fieldValues) {
    if (!schemaInfo?.fields?.length) {
      return ''
    }
    const payload = {}
    schemaInfo.fields.forEach((field) => {
      const normalized = normalizeFieldValue(field, fieldValues?.[field.name])
      if (normalized !== undefined) {
        payload[field.name] = normalized
      }
    })
    return JSON.stringify(payload, null, 2)
  }
  function readStructuredValues(schemaInfo, inputJson) {
    if (!schemaInfo?.fields?.length || !inputJson || !inputJson.trim()) {
      return {}
    }
    try {
      const parsed = JSON.parse(inputJson)
      if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
        return {}
      }
      const values = {}
      schemaInfo.fields.forEach((field) => {
        const value = parsed[field.name]
        if (value === undefined || value === null) {
          return
        }
        values[field.name] = typeof value === 'boolean' ? String(value) : String(value)
      })
      return values
    } catch {
      return {}
    }
  }
  function getFieldOptions(field) {
    if (field.type === 'boolean') {
      return [
        { label: 'true', value: 'true' },
        { label: 'false', value: 'false' }
      ]
    }
    return field.enumValues.map((value) => ({
      label: String(value),
      value: String(value)
    }))
  }
  function buildFieldHelperText(field, requiredFields = []) {
    const tokens = []
    if (requiredFields.includes(field.name)) {
      tokens.push(t('pages.system.aiMcpMount.toolsDrawer.required'))
    }
    if (field.type) {
      tokens.push(field.type)
    }
    if (field.description) {
      tokens.push(field.description)
    }
    return tokens.join(' · ') || emptyText.value
  }
  function handleInputChange(toolName, value) {
    toolInputs[toolName] = value
    structuredInputs[toolName] = readStructuredValues(schemaInfoMap.value[toolName], value)
  }
  function handleStructuredFieldChange(toolName, fieldName, value) {
    const schemaInfo = schemaInfoMap.value[toolName]
    const nextToolValues = {
      ...(structuredInputs[toolName] || {}),
      [fieldName]: value
    }
    if (value === '' || value === undefined || value === null) {
      delete nextToolValues[fieldName]
    }
    structuredInputs[toolName] = nextToolValues
    toolInputs[toolName] = buildInputJson(schemaInfo, nextToolValues)
  }
  async function loadTools() {
    if (!props.mountId) return
    toolsLoading.value = true
@@ -162,6 +374,15 @@
        timeoutMessage: t('pages.system.aiMcpMount.messages.toolsTimeout')
      })
      tools.value = Array.isArray(response) ? response : []
      tools.value.forEach((tool) => {
        if (!(tool.name in toolInputs)) {
          toolInputs[tool.name] = ''
        }
        structuredInputs[tool.name] = readStructuredValues(
          schemaInfoMap.value[tool.name],
          toolInputs[tool.name]
        )
      })
    } catch (error) {
      tools.value = []
      ElMessage.error(error?.message || t('pages.system.aiMcpMount.toolsDrawer.toolsLoadFailed'))
@@ -174,11 +395,16 @@
    if (!props.mountId) return
    connectivityLoading.value = true
    try {
      connectivityResult.value = await guardRequestWithMessage(fetchTestAiMcpConnectivity(props.mountId), null, {
        timeoutMessage: t('pages.system.aiMcpMount.messages.connectivityTimeout')
      })
      connectivityResult.value = await guardRequestWithMessage(
        fetchTestAiMcpConnectivity(props.mountId),
        null,
        {
          timeoutMessage: t('pages.system.aiMcpMount.messages.connectivityTimeout')
        }
      )
      ElMessage.success(
        connectivityResult.value?.message || t('pages.system.aiMcpMount.messages.connectivitySuccess')
        connectivityResult.value?.message ||
          t('pages.system.aiMcpMount.messages.connectivitySuccess')
      )
    } catch (error) {
      ElMessage.error(error?.message || t('pages.system.aiMcpMount.messages.connectivityFailed'))
@@ -209,7 +435,8 @@
      toolOutputs[toolName] = result?.output || JSON.stringify(result || {}, null, 2)
      ElMessage.success(t('pages.system.aiMcpMount.toolsDrawer.toolTestSuccess'))
    } catch (error) {
      toolOutputs[toolName] = error?.message || t('pages.system.aiMcpMount.toolsDrawer.toolTestFailed')
      toolOutputs[toolName] =
        error?.message || t('pages.system.aiMcpMount.toolsDrawer.toolTestFailed')
      ElMessage.error(error?.message || t('pages.system.aiMcpMount.toolsDrawer.toolTestFailed'))
    } finally {
      testingToolName.value = ''