feat: semi frontend

This commit is contained in:
WangCham
2025-10-21 16:18:03 +08:00
parent 7be226d3fa
commit 760db38c11
3 changed files with 54 additions and 75 deletions

View File

@@ -1,5 +1,6 @@
from __future__ import annotations
import time
import traceback
import uuid
import quart
@@ -48,31 +49,37 @@ class MCPRouterGroup(group.RouterGroup):
elif quart.request.method == 'POST':
data = await quart.request.json
data = data['source']
try:
# 检查服务器名称是否重复
result = await self.ap.persistence_mgr.execute_async(
sqlalchemy.select(MCPServer).where(MCPServer.name == data['name'])
)
if result.first() is not None:
return self.http_status(400, -1, 'Server name already exists')
# 创建新服务器配置
new_server = {
'uuid': str(uuid.uuid4()),
'name': data['name'],
'mode': data['mode'],
'enable': data.get('enable', False),
'description': data.get('description',''),
'extra_args': {
'url':data.get('url',''),
'headers':data.get('headers',{}),
'timeout':data.get('timeout',60),
},
}
result = await self.ap.persistence_mgr.execute_async(
sqlalchemy.select(MCPServer).where(MCPServer.name == data['name'])
)
if result.first() is not None:
return self.http_status(400, -1, 'Server name already exists')
# 创建新服务器配置
new_server = {
'uuid': str(uuid.uuid4()),
'name': data['name'],
'mode': 'sse',
'enable': data.get('enable', False),
'description': data.get('description',''),
'extra_args': {
'url':data.get('url',''),
'headers':data.get('headers',{}),
'timeout':data.get('timeout',60),
},
}
await self.ap.persistence_mgr.execute_async(
sqlalchemy.insert(MCPServer).values(new_server)
)
await self.ap.persistence_mgr.execute_async(
sqlalchemy.insert(MCPServer).values(new_server)
)
return self.success()
except Exception as e:
print(traceback.format_exc())
@self.route('/servers/<server_name>', methods=['GET', 'PUT', 'DELETE'], auth_type=group.AuthType.USER_TOKEN)
async def _(server_name: str) -> str:

View File

@@ -43,7 +43,7 @@ import { systemInfo } from '@/app/infra/http/HttpClient';
import { ApiRespPluginSystemStatus } from '@/app/infra/entities/api';
import { set } from 'lodash';
import { passiveEventSupported } from '@tanstack/react-table';
import { config } from 'process';
enum PluginInstallStatus {
WAIT_INPUT = 'wait_input',
@@ -324,36 +324,21 @@ export default function PluginConfigPage() {
setMcpInstallStatus(PluginInstallStatus.INSTALLING);
console.log('installing mcp server from sse with config:', config);
httpClient.installMCPServerFromSSE(config ?? {})
.then((resp) => {
const taskId = resp.task_id;
let alreadySuccess = false;
console.log('taskId:', taskId);
// 每秒拉取一次任务状态
const interval = setInterval(() => {
httpClient.getAsyncTask(taskId).then((resp) => {
console.log('task status:', resp);
if (resp.runtime.done) {
clearInterval(interval);
if (resp.runtime.exception) {
setMcpInstallError(resp.runtime.exception);
setMcpInstallStatus(PluginInstallStatus.ERROR);
} else {
// success
if (!alreadySuccess) {
toast.success(t('mcp.installSuccess'));
alreadySuccess = true;
}
setMcpSSEInstallModalOpen(false);
setMcpName('');
setMcpDescription('');
setMcpSSEURL('');
setMcpSSEHeaders('');
setMcpTimeout(60);
mcpComponentRef.current?.refreshServerList();
}
}
});
}, 1000);
.then((resp:any) => {
if (resp && resp.status === 'success') {
console.log('MCP server installed successfully');
toast.success(t('mcp.installSuccess'));
setMcpSSEURL('');
setMcpName('');
setMcpDescription('');
setMcpSSEHeaders('');
setMcpTimeout(60);
setMcpSSEInstallModalOpen(false);
mcpComponentRef.current?.refreshServerList();
} else {
setMcpInstallError(t('mcp.installFailed'));
setMcpInstallStatus(PluginInstallStatus.ERROR);
}
})
.catch((err) => {
console.log('error when install mcp server:', err);
@@ -672,19 +657,7 @@ export default function PluginConfigPage() {
</div>
</div>
<div className="mt-4">
<div>
<label className="text-sm text-muted-foreground block mb-1">
{t('mcp.headers')}
</label>
<Input
placeholder={t('mcp.headersExample')}
value={mcpSSEHeaders}
onChange={(e) => setMcpSSEHeaders(e.target.value)}
className="mb-1"
/>
</div>
</div>
<div className='mt-4'>
<div>
@@ -746,14 +719,13 @@ export default function PluginConfigPage() {
toast.error(t('mcp.timeoutRequired'));
}
const configToSend = {
name: mcpSSEConfig?.name,
description: mcpSSEConfig?.description,
sse_url: mcpSSEConfig?.sse_url,
sse_headers: mcpSSEConfig?.sse_headers,
timeout: Number(mcpSSEConfig?.timeout) || 60,
name: mcpName,
description: mcpDescription,
sse_url: mcpSSEURL,
sse_headers: mcpSSEHeaders,
timeout: Number(mcpTimeout) || 60,
};
handleMcpModalConfirm();
// handleMcpModalConfirm();
// call installer (for now installMcpServer will log config and call backend with url only)
installMcpServerFromSSE(configToSend);
}}

View File

@@ -555,7 +555,7 @@ export class BackendClient extends BaseHttpClient {
public installMCPServerFromSSE(
source: {},
): Promise<AsyncTaskCreatedResp> {
return this.post('/api/v1/mcp/install/sse', { source });
return this.post('/api/v1/mcp/servers', { source });
}
// ============ System API ============