|
|
@@ -1,51 +1,59 @@
|
|
|
<template>
|
|
|
- <div class="panel">
|
|
|
- <div class="toolbar">
|
|
|
- <div class="toolbar-left">
|
|
|
- <el-input
|
|
|
- v-model="keyword"
|
|
|
- clearable
|
|
|
- placeholder="搜索 MCP 名称"
|
|
|
- class="search-input"
|
|
|
- @keyup.enter="loadList(1)"
|
|
|
- >
|
|
|
- <template #prefix>
|
|
|
+ <div class="management-page">
|
|
|
+ <div class="page-head">
|
|
|
+ <div>
|
|
|
+ <h1>MCP 服务</h1>
|
|
|
+ <p>维护 MCP 服务列表,并查看资源与工具详情。</p>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="panel">
|
|
|
+ <div class="toolbar">
|
|
|
+ <div class="toolbar-left">
|
|
|
+ <el-input
|
|
|
+ v-model="keyword"
|
|
|
+ clearable
|
|
|
+ placeholder="搜索 MCP 名称"
|
|
|
+ class="search-input"
|
|
|
+ @keyup.enter="loadList(1)"
|
|
|
+ >
|
|
|
+ <template #prefix>
|
|
|
+ <el-icon>
|
|
|
+ <Search />
|
|
|
+ </el-icon>
|
|
|
+ </template>
|
|
|
+ </el-input>
|
|
|
+ <el-select
|
|
|
+ v-model="transportType"
|
|
|
+ clearable
|
|
|
+ placeholder="传输类型"
|
|
|
+ style="width: 160px"
|
|
|
+ @change="loadList(1)"
|
|
|
+ >
|
|
|
+ <el-option label="stdio" value="stdio" />
|
|
|
+ <el-option label="sse" value="sse" />
|
|
|
+ <el-option label="streamable" value="streamable" />
|
|
|
+ <el-option label="http" value="http" />
|
|
|
+ </el-select>
|
|
|
+ <el-button @click="loadList(1)">查询</el-button>
|
|
|
+ <el-button @click="handleReset">重置</el-button>
|
|
|
+ </div>
|
|
|
+ <div class="toolbar-right">
|
|
|
+ <el-button type="primary" @click="openCreate">
|
|
|
<el-icon>
|
|
|
- <Search />
|
|
|
+ <Plus />
|
|
|
</el-icon>
|
|
|
- </template>
|
|
|
- </el-input>
|
|
|
- <el-select
|
|
|
- v-model="transportType"
|
|
|
- clearable
|
|
|
- placeholder="传输类型"
|
|
|
- style="width: 160px"
|
|
|
- @change="loadList(1)"
|
|
|
- >
|
|
|
- <el-option label="stdio" value="stdio" />
|
|
|
- <el-option label="sse" value="sse" />
|
|
|
- <el-option label="streamable" value="streamable" />
|
|
|
- <el-option label="http" value="http" />
|
|
|
- </el-select>
|
|
|
- <el-button @click="loadList(1)">查询</el-button>
|
|
|
- <el-button @click="handleReset">重置</el-button>
|
|
|
- </div>
|
|
|
- <div class="toolbar-right">
|
|
|
- <el-button type="primary" @click="openCreate">
|
|
|
- <el-icon>
|
|
|
- <Plus />
|
|
|
- </el-icon>
|
|
|
- 新建 MCP
|
|
|
- </el-button>
|
|
|
+ 新建 MCP
|
|
|
+ </el-button>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
- </div>
|
|
|
|
|
|
- <div class="toolbar-meta">
|
|
|
- <span class="pill">共 {{ pagination.totalCount }} 个服务</span>
|
|
|
- <span class="pill">已启用 {{ enabledCount }} 个</span>
|
|
|
- </div>
|
|
|
+ <div class="toolbar-meta">
|
|
|
+ <span class="pill">共 {{ pagination.totalCount }} 个服务</span>
|
|
|
+ <span class="pill">已启用 {{ enabledCount }} 个</span>
|
|
|
+ </div>
|
|
|
|
|
|
- <!-- <div class="summary-grid">
|
|
|
+ <!-- <div class="summary-grid">
|
|
|
<div class="summary-card">
|
|
|
<div class="summary-value">{{ pagination.totalCount }}</div>
|
|
|
<div class="summary-label">MCP 总数</div>
|
|
|
@@ -60,265 +68,272 @@
|
|
|
</div>
|
|
|
</div> -->
|
|
|
|
|
|
- <div v-loading="loading" class="grid">
|
|
|
- <el-empty class="empty" v-if="!list.length && !loading" description="暂无 MCP 服务" />
|
|
|
- <div v-for="row in list" :key="row.id" class="card">
|
|
|
- <div class="card-head">
|
|
|
- <div class="card-head__top">
|
|
|
- <div class="title-block">
|
|
|
- <div class="title">{{ row.name || '未命名 MCP' }}</div>
|
|
|
- <div class="subtitle">{{ row.url || '未配置地址' }}</div>
|
|
|
+ <div v-loading="loading" class="grid">
|
|
|
+ <el-empty class="empty" v-if="!list.length && !loading" description="暂无 MCP 服务" />
|
|
|
+ <div v-for="row in list" :key="row.id" class="card">
|
|
|
+ <div class="card-head">
|
|
|
+ <div class="card-head__top">
|
|
|
+ <div class="title-block">
|
|
|
+ <div class="title">{{ row.name || '未命名 MCP' }}</div>
|
|
|
+ <div class="subtitle">{{ row.url || '未配置地址' }}</div>
|
|
|
+ </div>
|
|
|
+ <div class="actions">
|
|
|
+ <el-dropdown>
|
|
|
+ <span class="actions-trigger">
|
|
|
+ <el-icon>
|
|
|
+ <MoreFilled />
|
|
|
+ </el-icon>
|
|
|
+ </span>
|
|
|
+ <template #dropdown>
|
|
|
+ <el-dropdown-menu>
|
|
|
+ <el-dropdown-item @click="checkItem(row.id!)">测试</el-dropdown-item>
|
|
|
+ <el-dropdown-item @click="openTools(row.id!)">工具</el-dropdown-item>
|
|
|
+ <el-dropdown-item @click="openEditById(row.id!)">编辑</el-dropdown-item>
|
|
|
+ <el-dropdown-item divided @click="removeItem(row.id!)">
|
|
|
+ <span class="danger-text">删除</span>
|
|
|
+ </el-dropdown-item>
|
|
|
+ </el-dropdown-menu>
|
|
|
+ </template>
|
|
|
+ </el-dropdown>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
- <div class="actions">
|
|
|
- <el-dropdown>
|
|
|
- <span class="actions-trigger">
|
|
|
- <el-icon>
|
|
|
- <MoreFilled />
|
|
|
- </el-icon>
|
|
|
- </span>
|
|
|
- <template #dropdown>
|
|
|
- <el-dropdown-menu>
|
|
|
- <el-dropdown-item @click="checkItem(row.id!)">测试</el-dropdown-item>
|
|
|
- <el-dropdown-item @click="openTools(row.id!)">工具</el-dropdown-item>
|
|
|
- <el-dropdown-item @click="openEditById(row.id!)">编辑</el-dropdown-item>
|
|
|
- <el-dropdown-item divided @click="removeItem(row.id!)">
|
|
|
- <span class="danger-text">删除</span>
|
|
|
- </el-dropdown-item>
|
|
|
- </el-dropdown-menu>
|
|
|
- </template>
|
|
|
- </el-dropdown>
|
|
|
+ <div class="badge-row">
|
|
|
+ <span class="badge">{{ row.transport_type || '未设置传输类型' }}</span>
|
|
|
+ <span class="badge subtle">{{ row.enabled ? '启用中' : '已禁用' }}</span>
|
|
|
</div>
|
|
|
</div>
|
|
|
- <div class="badge-row">
|
|
|
- <span class="badge">{{ row.transport_type || '未设置传输类型' }}</span>
|
|
|
- <span class="badge subtle">{{ row.enabled ? '启用中' : '已禁用' }}</span>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
|
|
|
- <div class="desc">{{ row.description || '暂无描述' }}</div>
|
|
|
+ <div class="desc">{{ row.description || '暂无描述' }}</div>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
- </div>
|
|
|
|
|
|
- <div v-if="pagination.totalCount > 0" class="pagination">
|
|
|
- <el-pagination
|
|
|
- v-model:current-page="pagination.pageIndex"
|
|
|
- v-model:page-size="pagination.pageSize"
|
|
|
- background
|
|
|
- layout="total, sizes, prev, pager, next, jumper"
|
|
|
- :page-sizes="[10, 20, 50, 100]"
|
|
|
- :total="pagination.totalCount"
|
|
|
- @current-change="handlePageChange"
|
|
|
- @size-change="handleSizeChange"
|
|
|
- />
|
|
|
- </div>
|
|
|
+ <div v-if="pagination.totalCount > 0" class="pagination">
|
|
|
+ <el-pagination
|
|
|
+ v-model:current-page="pagination.pageIndex"
|
|
|
+ v-model:page-size="pagination.pageSize"
|
|
|
+ background
|
|
|
+ layout="total, sizes, prev, pager, next, jumper"
|
|
|
+ :page-sizes="[10, 20, 50, 100]"
|
|
|
+ :total="pagination.totalCount"
|
|
|
+ @current-change="handlePageChange"
|
|
|
+ @size-change="handleSizeChange"
|
|
|
+ />
|
|
|
+ </div>
|
|
|
|
|
|
- <el-drawer
|
|
|
- v-model="drawerVisible"
|
|
|
- :title="currentId ? '编辑 MCP' : '新建 MCP'"
|
|
|
- direction="rtl"
|
|
|
- size="760px"
|
|
|
- >
|
|
|
- <el-form ref="formRef" :model="form" :rules="rules" label-position="top">
|
|
|
- <el-form-item label="名称" prop="name">
|
|
|
- <el-input v-model="form.name" />
|
|
|
- </el-form-item>
|
|
|
- <el-form-item label="描述" prop="description">
|
|
|
- <el-input v-model="form.description" type="textarea" :rows="2" />
|
|
|
- </el-form-item>
|
|
|
- <div class="grid-2">
|
|
|
- <el-form-item label="传输类型" prop="transport_type">
|
|
|
- <el-select v-model="form.transport_type" style="width: 100%">
|
|
|
- <el-option label="SSE(Server-Sent Events)" value="sse" />
|
|
|
- <el-option label="HTTP Streamable" value="http-streamable" />
|
|
|
- </el-select>
|
|
|
+ <el-drawer
|
|
|
+ v-model="drawerVisible"
|
|
|
+ :title="currentId ? '编辑 MCP' : '新建 MCP'"
|
|
|
+ direction="rtl"
|
|
|
+ size="760px"
|
|
|
+ >
|
|
|
+ <el-form ref="formRef" :model="form" :rules="rules" label-position="top">
|
|
|
+ <el-form-item label="名称" prop="name">
|
|
|
+ <el-input v-model="form.name" />
|
|
|
</el-form-item>
|
|
|
- <el-form-item label="启用" prop="enabled">
|
|
|
- <el-switch v-model="form.enabled" />
|
|
|
+ <el-form-item label="描述" prop="description">
|
|
|
+ <el-input v-model="form.description" type="textarea" :rows="2" />
|
|
|
</el-form-item>
|
|
|
- </div>
|
|
|
- <el-form-item label="地址" prop="url">
|
|
|
- <el-input v-model="form.url" />
|
|
|
- </el-form-item>
|
|
|
- <el-form-item label="超时(秒)" prop="advanced_config.timeout">
|
|
|
- <el-input-number v-model="form.advanced_config.timeout" :min="0" style="width: 100%" />
|
|
|
- </el-form-item>
|
|
|
- <div class="grid-2">
|
|
|
- <el-form-item label="重试次数" prop="advanced_config.retry_count">
|
|
|
- <el-input-number
|
|
|
- v-model="form.advanced_config.retry_count"
|
|
|
- :min="0"
|
|
|
- style="width: 100%"
|
|
|
- />
|
|
|
+ <div class="grid-2">
|
|
|
+ <el-form-item label="传输类型" prop="transport_type">
|
|
|
+ <el-select v-model="form.transport_type" style="width: 100%">
|
|
|
+ <el-option label="SSE(Server-Sent Events)" value="sse" />
|
|
|
+ <el-option label="HTTP Streamable" value="http-streamable" />
|
|
|
+ </el-select>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="启用" prop="enabled">
|
|
|
+ <el-switch v-model="form.enabled" />
|
|
|
+ </el-form-item>
|
|
|
+ </div>
|
|
|
+ <el-form-item label="地址" prop="url">
|
|
|
+ <el-input v-model="form.url" />
|
|
|
</el-form-item>
|
|
|
- <el-form-item label="重试间隔(秒)" prop="advanced_config.retry_delay">
|
|
|
- <el-input-number
|
|
|
- v-model="form.advanced_config.retry_delay"
|
|
|
- :min="0"
|
|
|
- style="width: 100%"
|
|
|
- />
|
|
|
+ <el-form-item label="超时(秒)" prop="advanced_config.timeout">
|
|
|
+ <el-input-number v-model="form.advanced_config.timeout" :min="0" style="width: 100%" />
|
|
|
</el-form-item>
|
|
|
- </div>
|
|
|
- <el-form-item label="Headers">
|
|
|
- <div class="kv-config">
|
|
|
- <div class="kv-config__top">
|
|
|
- <span>请求头</span>
|
|
|
- <el-button type="primary" link @click="addHeaderRow">
|
|
|
- <el-icon> <Plus /> </el-icon>添加
|
|
|
- </el-button>
|
|
|
- </div>
|
|
|
- <div class="kv-config__rows">
|
|
|
- <div
|
|
|
- v-for="(item, index) in headerList"
|
|
|
- :key="`header-${index}`"
|
|
|
- class="kv-config__row"
|
|
|
- >
|
|
|
- <el-input v-model="item.key" placeholder="Header 名称" />
|
|
|
- <el-input v-model="item.value" placeholder="Header 值" />
|
|
|
- <el-button
|
|
|
- link
|
|
|
- type="danger"
|
|
|
- :disabled="headerList.length === 1"
|
|
|
- @click="removeHeaderRow(index)"
|
|
|
- >
|
|
|
- 删除
|
|
|
+ <div class="grid-2">
|
|
|
+ <el-form-item label="重试次数" prop="advanced_config.retry_count">
|
|
|
+ <el-input-number
|
|
|
+ v-model="form.advanced_config.retry_count"
|
|
|
+ :min="0"
|
|
|
+ style="width: 100%"
|
|
|
+ />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="重试间隔(秒)" prop="advanced_config.retry_delay">
|
|
|
+ <el-input-number
|
|
|
+ v-model="form.advanced_config.retry_delay"
|
|
|
+ :min="0"
|
|
|
+ style="width: 100%"
|
|
|
+ />
|
|
|
+ </el-form-item>
|
|
|
+ </div>
|
|
|
+ <el-form-item label="Headers">
|
|
|
+ <div class="kv-config">
|
|
|
+ <div class="kv-config__top">
|
|
|
+ <span>请求头</span>
|
|
|
+ <el-button type="primary" link @click="addHeaderRow">
|
|
|
+ <el-icon> <Plus /> </el-icon>添加
|
|
|
</el-button>
|
|
|
</div>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- </el-form-item>
|
|
|
- <el-form-item label="Auth Config">
|
|
|
- <div class="kv-config">
|
|
|
- <div class="kv-config__top">
|
|
|
- <span>鉴权配置</span>
|
|
|
- <el-button type="primary" link @click="addAuthRow">
|
|
|
- <el-icon> <Plus /> </el-icon>添加
|
|
|
- </el-button>
|
|
|
- </div>
|
|
|
- <div class="kv-config__rows">
|
|
|
- <div v-for="(item, index) in authList" :key="`auth-${index}`" class="kv-config__row">
|
|
|
- <el-input v-model="item.key" placeholder="配置项名称" />
|
|
|
- <el-input v-model="item.value" placeholder="配置项值" />
|
|
|
- <el-button
|
|
|
- link
|
|
|
- type="danger"
|
|
|
- :disabled="authList.length === 1"
|
|
|
- @click="removeAuthRow(index)"
|
|
|
+ <div class="kv-config__rows">
|
|
|
+ <div
|
|
|
+ v-for="(item, index) in headerList"
|
|
|
+ :key="`header-${index}`"
|
|
|
+ class="kv-config__row"
|
|
|
>
|
|
|
- 删除
|
|
|
- </el-button>
|
|
|
+ <el-input v-model="item.key" placeholder="Header 名称" />
|
|
|
+ <el-input v-model="item.value" placeholder="Header 值" />
|
|
|
+ <el-button
|
|
|
+ link
|
|
|
+ type="danger"
|
|
|
+ :disabled="headerList.length === 1"
|
|
|
+ @click="removeHeaderRow(index)"
|
|
|
+ >
|
|
|
+ 删除
|
|
|
+ </el-button>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
</div>
|
|
|
- </div>
|
|
|
- </el-form-item>
|
|
|
- <el-form-item label="Env Vars">
|
|
|
- <div class="kv-config">
|
|
|
- <div class="kv-config__top">
|
|
|
- <span>环境变量</span>
|
|
|
- <el-button type="primary" link @click="addEnvRow">
|
|
|
- <el-icon> <Plus /> </el-icon>添加
|
|
|
- </el-button>
|
|
|
- </div>
|
|
|
- <div class="kv-config__rows">
|
|
|
- <div v-for="(item, index) in envList" :key="`env-${index}`" class="kv-config__row">
|
|
|
- <el-input v-model="item.key" placeholder="变量名" />
|
|
|
- <el-input v-model="item.value" placeholder="变量值" />
|
|
|
- <el-button
|
|
|
- link
|
|
|
- type="danger"
|
|
|
- :disabled="envList.length === 1"
|
|
|
- @click="removeEnvRow(index)"
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="Auth Config">
|
|
|
+ <div class="kv-config">
|
|
|
+ <div class="kv-config__top">
|
|
|
+ <span>鉴权配置</span>
|
|
|
+ <el-button type="primary" link @click="addAuthRow">
|
|
|
+ <el-icon> <Plus /> </el-icon>添加
|
|
|
+ </el-button>
|
|
|
+ </div>
|
|
|
+ <div class="kv-config__rows">
|
|
|
+ <div
|
|
|
+ v-for="(item, index) in authList"
|
|
|
+ :key="`auth-${index}`"
|
|
|
+ class="kv-config__row"
|
|
|
>
|
|
|
- 删除
|
|
|
+ <el-input v-model="item.key" placeholder="配置项名称" />
|
|
|
+ <el-input v-model="item.value" placeholder="配置项值" />
|
|
|
+ <el-button
|
|
|
+ link
|
|
|
+ type="danger"
|
|
|
+ :disabled="authList.length === 1"
|
|
|
+ @click="removeAuthRow(index)"
|
|
|
+ >
|
|
|
+ 删除
|
|
|
+ </el-button>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="Env Vars">
|
|
|
+ <div class="kv-config">
|
|
|
+ <div class="kv-config__top">
|
|
|
+ <span>环境变量</span>
|
|
|
+ <el-button type="primary" link @click="addEnvRow">
|
|
|
+ <el-icon> <Plus /> </el-icon>添加
|
|
|
</el-button>
|
|
|
</div>
|
|
|
+ <div class="kv-config__rows">
|
|
|
+ <div v-for="(item, index) in envList" :key="`env-${index}`" class="kv-config__row">
|
|
|
+ <el-input v-model="item.key" placeholder="变量名" />
|
|
|
+ <el-input v-model="item.value" placeholder="变量值" />
|
|
|
+ <el-button
|
|
|
+ link
|
|
|
+ type="danger"
|
|
|
+ :disabled="envList.length === 1"
|
|
|
+ @click="removeEnvRow(index)"
|
|
|
+ >
|
|
|
+ 删除
|
|
|
+ </el-button>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item>
|
|
|
+ <div class="check-box">
|
|
|
+ <el-button :loading="checkLoading" :disabled="!currentId" @click="checkItemByForm">
|
|
|
+ 测试连接
|
|
|
+ </el-button>
|
|
|
+ <el-alert
|
|
|
+ v-if="checkMessage"
|
|
|
+ :title="checkMessage"
|
|
|
+ :type="checkSuccess ? 'success' : 'error'"
|
|
|
+ :closable="false"
|
|
|
+ show-icon
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+ </el-form-item>
|
|
|
+ </el-form>
|
|
|
+ <template #footer>
|
|
|
+ <div class="drawer-footer">
|
|
|
+ <el-button @click="drawerVisible = false">取消</el-button>
|
|
|
+ <el-button type="primary" :loading="submitLoading" @click="handleSubmit"
|
|
|
+ >保存</el-button
|
|
|
+ >
|
|
|
</div>
|
|
|
- </el-form-item>
|
|
|
- <el-form-item>
|
|
|
- <div class="check-box">
|
|
|
- <el-button :loading="checkLoading" :disabled="!currentId" @click="checkItemByForm">
|
|
|
- 测试连接
|
|
|
- </el-button>
|
|
|
- <el-alert
|
|
|
- v-if="checkMessage"
|
|
|
- :title="checkMessage"
|
|
|
- :type="checkSuccess ? 'success' : 'error'"
|
|
|
- :closable="false"
|
|
|
- show-icon
|
|
|
- />
|
|
|
- </div>
|
|
|
- </el-form-item>
|
|
|
- </el-form>
|
|
|
- <template #footer>
|
|
|
- <div class="drawer-footer">
|
|
|
- <el-button @click="drawerVisible = false">取消</el-button>
|
|
|
- <el-button type="primary" :loading="submitLoading" @click="handleSubmit">保存</el-button>
|
|
|
+ </template>
|
|
|
+ </el-drawer>
|
|
|
+
|
|
|
+ <el-dialog v-model="detailVisible" title="MCP 详情" width="720px">
|
|
|
+ <el-descriptions v-if="detailItem" :column="1" border>
|
|
|
+ <el-descriptions-item label="名称">{{ detailItem.name }}</el-descriptions-item>
|
|
|
+ <el-descriptions-item label="传输类型">{{
|
|
|
+ detailItem.transport_type
|
|
|
+ }}</el-descriptions-item>
|
|
|
+ <el-descriptions-item label="地址">{{ detailItem.url || '-' }}</el-descriptions-item>
|
|
|
+ <el-descriptions-item label="描述">{{
|
|
|
+ detailItem.description || '-'
|
|
|
+ }}</el-descriptions-item>
|
|
|
+ </el-descriptions>
|
|
|
+ </el-dialog>
|
|
|
+
|
|
|
+ <el-dialog v-model="resourcesVisible" title="MCP 资源" width="720px">
|
|
|
+ <el-empty v-if="!resourceList.length" description="暂无资源" />
|
|
|
+ <el-space v-else wrap>
|
|
|
+ <el-tag v-for="item in resourceList" :key="item">{{ item }}</el-tag>
|
|
|
+ </el-space>
|
|
|
+ </el-dialog>
|
|
|
+
|
|
|
+ <el-dialog v-model="toolsVisible" title="MCP 工具" width="720px" class="tool-modal">
|
|
|
+ <div class="tool-dialog">
|
|
|
+ <el-input
|
|
|
+ v-model="toolKeyword"
|
|
|
+ clearable
|
|
|
+ placeholder="搜索工具名称或描述"
|
|
|
+ class="tool-search-input"
|
|
|
+ >
|
|
|
+ <template #prefix>
|
|
|
+ <el-icon>
|
|
|
+ <Search />
|
|
|
+ </el-icon>
|
|
|
+ </template>
|
|
|
+ </el-input>
|
|
|
+ <el-empty v-if="!filteredToolDetailList.length" description="暂无工具" />
|
|
|
+ <el-scrollbar v-else max-height="420px">
|
|
|
+ <el-collapse>
|
|
|
+ <el-collapse-item v-for="item in filteredToolDetailList" :key="item.name">
|
|
|
+ <template #title>
|
|
|
+ <div>
|
|
|
+ <div class="tool-desc text-16px text-primary mb-4px">{{ item.name }}</div>
|
|
|
+ <div class="tool-desc text-12px">{{ item.description }}</div>
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ <el-card>
|
|
|
+ <el-descriptions direction="vertical" :column="1">
|
|
|
+ <el-descriptions-item label="参数结构">
|
|
|
+ <CodeEditor
|
|
|
+ :model-value="JSON.stringify(item.inputSchema, null, 2)"
|
|
|
+ language="json"
|
|
|
+ :height="260"
|
|
|
+ :tools="false"
|
|
|
+ readonly
|
|
|
+ />
|
|
|
+ </el-descriptions-item>
|
|
|
+ </el-descriptions>
|
|
|
+ </el-card>
|
|
|
+ </el-collapse-item>
|
|
|
+ </el-collapse>
|
|
|
+ </el-scrollbar>
|
|
|
</div>
|
|
|
- </template>
|
|
|
- </el-drawer>
|
|
|
-
|
|
|
- <el-dialog v-model="detailVisible" title="MCP 详情" width="720px">
|
|
|
- <el-descriptions v-if="detailItem" :column="1" border>
|
|
|
- <el-descriptions-item label="名称">{{ detailItem.name }}</el-descriptions-item>
|
|
|
- <el-descriptions-item label="传输类型">{{
|
|
|
- detailItem.transport_type
|
|
|
- }}</el-descriptions-item>
|
|
|
- <el-descriptions-item label="地址">{{ detailItem.url || '-' }}</el-descriptions-item>
|
|
|
- <el-descriptions-item label="描述">{{
|
|
|
- detailItem.description || '-'
|
|
|
- }}</el-descriptions-item>
|
|
|
- </el-descriptions>
|
|
|
- </el-dialog>
|
|
|
-
|
|
|
- <el-dialog v-model="resourcesVisible" title="MCP 资源" width="720px">
|
|
|
- <el-empty v-if="!resourceList.length" description="暂无资源" />
|
|
|
- <el-space v-else wrap>
|
|
|
- <el-tag v-for="item in resourceList" :key="item">{{ item }}</el-tag>
|
|
|
- </el-space>
|
|
|
- </el-dialog>
|
|
|
-
|
|
|
- <el-dialog v-model="toolsVisible" title="MCP 工具" width="720px" class="tool-modal">
|
|
|
- <div class="tool-dialog">
|
|
|
- <el-input
|
|
|
- v-model="toolKeyword"
|
|
|
- clearable
|
|
|
- placeholder="搜索工具名称或描述"
|
|
|
- class="tool-search-input"
|
|
|
- >
|
|
|
- <template #prefix>
|
|
|
- <el-icon>
|
|
|
- <Search />
|
|
|
- </el-icon>
|
|
|
- </template>
|
|
|
- </el-input>
|
|
|
- <el-empty v-if="!filteredToolDetailList.length" description="暂无工具" />
|
|
|
- <el-scrollbar v-else max-height="420px">
|
|
|
- <el-collapse>
|
|
|
- <el-collapse-item v-for="item in filteredToolDetailList" :key="item.name">
|
|
|
- <template #title>
|
|
|
- <div>
|
|
|
- <div class="tool-desc text-16px text-primary mb-4px">{{ item.name }}</div>
|
|
|
- <div class="tool-desc text-12px">{{ item.description }}</div>
|
|
|
- </div>
|
|
|
- </template>
|
|
|
- <el-card>
|
|
|
- <el-descriptions direction="vertical" :column="1">
|
|
|
- <el-descriptions-item label="参数结构">
|
|
|
- <CodeEditor
|
|
|
- :model-value="JSON.stringify(item.inputSchema, null, 2)"
|
|
|
- language="json"
|
|
|
- :height="260"
|
|
|
- :tools="false"
|
|
|
- readonly
|
|
|
- />
|
|
|
- </el-descriptions-item>
|
|
|
- </el-descriptions>
|
|
|
- </el-card>
|
|
|
- </el-collapse-item>
|
|
|
- </el-collapse>
|
|
|
- </el-scrollbar>
|
|
|
- </div>
|
|
|
- </el-dialog>
|
|
|
+ </el-dialog>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
</template>
|
|
|
|
|
|
@@ -640,6 +655,26 @@ onMounted(() => {
|
|
|
</script>
|
|
|
|
|
|
<style scoped lang="less">
|
|
|
+.management-page {
|
|
|
+ padding: 24px;
|
|
|
+ min-height: 100%;
|
|
|
+ box-sizing: border-box;
|
|
|
+}
|
|
|
+
|
|
|
+.page-head {
|
|
|
+ margin-bottom: 18px;
|
|
|
+
|
|
|
+ h1 {
|
|
|
+ margin: 0;
|
|
|
+ font-size: 28px;
|
|
|
+ }
|
|
|
+
|
|
|
+ p {
|
|
|
+ margin: 6px 0 0;
|
|
|
+ color: #6b7280;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
:deep(.el-collapse-item__header) {
|
|
|
height: 80px;
|
|
|
}
|