feat: add config for mcp server (#8331)

This commit is contained in:
zhengkunwang
2025-04-07 15:41:51 +08:00
committed by GitHub
parent 9050680115
commit a12ceaee6e
8 changed files with 87 additions and 22 deletions

View File

@@ -118,8 +118,12 @@ func (m McpServerService) Update(req request.McpServerUpdate) error {
if err := files.NewFileOp().SaveFile(dockerComposePath, mcpServer.DockerCompose, 0644); err != nil {
return err
}
startMcp(mcpServer)
return syncMcpServerContainerStatus(mcpServer)
mcpServer.Status = constant.RuntimeStarting
if err := mcpServerRepo.Save(mcpServer); err != nil {
return err
}
go startMcp(mcpServer)
return nil
}
func (m McpServerService) Create(create request.McpServerCreate) error {
@@ -150,7 +154,7 @@ func (m McpServerService) Create(create request.McpServerCreate) error {
ContainerName: create.ContainerName,
Port: create.Port,
Command: create.Command,
Status: constant.RuntimeNormal,
Status: constant.RuntimeStarting,
BaseURL: create.BaseURL,
SsePath: create.SsePath,
Dir: mcpDir,
@@ -175,9 +179,9 @@ func (m McpServerService) Create(create request.McpServerCreate) error {
if err := mcpServerRepo.Create(mcpServer); err != nil {
return err
}
startMcp(mcpServer)
addProxy(mcpServer)
return syncMcpServerContainerStatus(mcpServer)
go startMcp(mcpServer)
return nil
}
func (m McpServerService) Delete(id uint) error {
@@ -584,6 +588,7 @@ func startMcp(mcpServer *model.McpServer) {
mcpServer.Status = constant.RuntimeRunning
mcpServer.Message = ""
}
_ = syncMcpServerContainerStatus(mcpServer)
}
func syncMcpServerContainerStatus(mcpServer *model.McpServer) error {

View File

@@ -35,6 +35,7 @@ var WebUrlMap = map[string]struct{}{
"/ai": {},
"/ai/model": {},
"/ai/gpu": {},
"/ai/mcp": {},
"/containers": {},
"/containers/container": {},

View File

@@ -2543,8 +2543,8 @@ const message = {
env: 'Environments',
noenv: 'None',
net: 'Network connections',
laddr: 'Source address/port',
raddr: 'Destination address/port',
laddr: 'Local address/port',
raddr: 'Remote address/port',
stopProcess: 'End',
viewDetails: 'Details',
stopProcessWarn: 'Are you sure you want to end this process (PID:{0})?',

View File

@@ -2380,8 +2380,8 @@ const message = {
env: '環境變數',
noenv: '無',
net: '網路連線',
laddr: '地址/',
raddr: '目標地址/',
laddr: '本地地址/',
raddr: '远程地址/',
stopProcess: '結束',
viewDetails: '查看詳情',
stopProcessWarn: '是否確定結束此行程 (PID:{0})',

View File

@@ -2382,8 +2382,8 @@ const message = {
env: '环境变量',
noenv: '无',
net: '网络连接',
laddr: '地址/端口',
raddr: '目标地址/端口',
laddr: '本地地址/端口',
raddr: '远程地址/端口',
stopProcess: '结束',
viewDetails: '查看详情',
stopProcessWarn: '是否确定结束此进程 (PID:{0})',

View File

@@ -0,0 +1,51 @@
<template>
<el-drawer
:destroy-on-close="true"
:close-on-click-modal="false"
:close-on-press-escape="false"
v-model="open"
size="50%"
>
<template #header>
<DrawerHeader :header="$t('menu.config')" :back="handleClose" />
</template>
<el-input type="textarea" :autosize="{ minRows: 10, maxRows: 20 }" v-model="prettyJson" readonly />
<CopyButton :content="prettyJson" class="mt-2" />
<template #footer>
<el-button @click="handleClose">{{ $t('commons.button.cancel') }}</el-button>
</template>
</el-drawer>
</template>
<script lang="ts" setup>
import { AI } from '@/api/interface/ai';
import { ref } from 'vue';
const open = ref(false);
const jsonObj = ref({
mcpServers: {},
});
const prettyJson = ref('');
const handleClose = () => {
open.value = false;
};
const acceptParams = (mcpServer: AI.McpServer) => {
jsonObj.value.mcpServers = {};
jsonObj.value.mcpServers[mcpServer.name] = {
url: mcpServer.baseUrl + mcpServer.ssePath,
};
if (mcpServer.environments) {
jsonObj.value.mcpServers[mcpServer.name].env = {};
for (const env of mcpServer.environments) {
jsonObj.value.mcpServers[mcpServer.name].env[env.key] = env.value;
}
}
prettyJson.value = JSON.stringify(jsonObj.value, null, 4);
open.value = true;
};
defineExpose({
acceptParams,
});
</script>

View File

@@ -18,7 +18,7 @@
:label="$t('commons.table.name')"
fix
prop="name"
min-width="120px"
width="200px"
show-overflow-tooltip
>
<template #default="{ row }">
@@ -27,18 +27,13 @@
</el-text>
</template>
</el-table-column>
<el-table-column :label="$t('commons.table.port')" prop="port" max-width="50px">
<template #default="{ row }">
{{ row.port }}
</template>
</el-table-column>
<el-table-column :label="$t('mcp.externalUrl')" prop="baseUrl" min-width="200px">
<template #default="{ row }">
{{ row.baseUrl + row.ssePath }}
<CopyButton :content="row.baseUrl + row.ssePath" type="icon" />
</template>
</el-table-column>
<el-table-column :label="$t('commons.table.status')" prop="status" max-width="50px">
<el-table-column :label="$t('commons.table.status')" prop="status" width="100px">
<template #default="{ row }">
<el-popover
v-if="row.status === 'error'"
@@ -57,7 +52,7 @@
</div>
</template>
</el-table-column>
<el-table-column :label="$t('commons.button.log')" prop="path" min-width="90px">
<el-table-column :label="$t('commons.button.log')" prop="path" width="100px">
<template #default="{ row }">
<el-button
@click="openLog(row)"
@@ -74,12 +69,12 @@
:label="$t('commons.table.date')"
:formatter="dateFormat"
show-overflow-tooltip
min-width="120"
width="180"
fix
/>
<fu-table-operations
:ellipsis="mobile ? 0 : 10"
:min-width="mobile ? 'auto' : 400"
:min-width="mobile ? 'auto' : 300"
:buttons="buttons"
:label="$t('commons.table.operate')"
fixed="right"
@@ -92,6 +87,7 @@
<OpDialog ref="opRef" @search="search" />
<ComposeLogs ref="composeLogRef" />
<BindDomain ref="bindDomainRef" @close="searchWithTimeOut" />
<Config ref="configRef" />
</div>
</template>
@@ -107,6 +103,7 @@ import { GlobalStore } from '@/store';
import i18n from '@/lang';
import { MsgError, MsgSuccess } from '@/utils/message';
import BindDomain from './bind/index.vue';
import Config from './config/index.vue';
const globalStore = GlobalStore();
const loading = ref(false);
@@ -114,6 +111,7 @@ const createRef = ref();
const opRef = ref();
const composeLogRef = ref();
const bindDomainRef = ref();
const configRef = ref();
const items = ref<AI.McpServer[]>([]);
const paginationConfig = reactive({
cacheSizeKey: 'mcp-server-page-size',
@@ -126,6 +124,12 @@ const mobile = computed(() => {
});
const buttons = [
{
label: i18n.global.t('menu.config'),
click: (row: AI.McpServer) => {
openConfig(row);
},
},
{
label: i18n.global.t('commons.button.edit'),
click: (row: AI.McpServer) => {
@@ -241,6 +245,10 @@ const openDomain = () => {
bindDomainRef.value.acceptParams();
};
const openConfig = (row: AI.McpServer) => {
configRef.value.acceptParams(row);
};
onMounted(() => {
search();
});

View File

@@ -65,7 +65,7 @@
</el-row>
</div>
</div>
<Volumes :volumes="mcpServer.volumes" />
<Volumes :volumes="mcpServer.volumes" class="mb-2" />
<el-row :gutter="20">
<el-col :span="6">
<el-form-item :label="$t('commons.table.port')" prop="port">