迭代18: QMT Bridge MCP 能力内建(R-020)—— 动态挂载 dsh-mcp-client + 状态出口(设置页/会话头部灯)
This commit is contained in:
@@ -520,6 +520,8 @@ function QmtConnectionsSettings() {
|
||||
const [editingId, setEditingId] = useState(null);
|
||||
const [formName, setFormName] = useState('');
|
||||
const [formUrl, setFormUrl] = useState('');
|
||||
const [mcpStatus, setMcpStatus] = useState(null); // R-020/迭代18:MCP 状态
|
||||
const [mcpChecking, setMcpChecking] = useState(false);
|
||||
const call = useRpc();
|
||||
const toast = useToast();
|
||||
|
||||
@@ -533,7 +535,14 @@ function QmtConnectionsSettings() {
|
||||
}
|
||||
}, [call]);
|
||||
|
||||
useEffect(() => { load(); }, [load]);
|
||||
const loadMcp = useCallback(async (refresh) => {
|
||||
try {
|
||||
const res = await call('one-divine-lot/qmt-connections/mcp-status', { args: { refresh: !!refresh } });
|
||||
if (res && res.ok) setMcpStatus(res.value);
|
||||
} catch { /* 静默 */ }
|
||||
}, [call]);
|
||||
|
||||
useEffect(() => { load(); loadMcp(false); }, [load, loadMcp]);
|
||||
|
||||
const openAdd = () => { setEditingId(null); setFormName(''); setFormUrl(''); setFormOpen(true); setTestResult(null); };
|
||||
const openEdit = (c) => { setEditingId(c.id); setFormName(c.name); setFormUrl(c.baseUrl); setFormOpen(true); setTestResult(null); };
|
||||
@@ -576,6 +585,7 @@ function QmtConnectionsSettings() {
|
||||
const res = await call('one-divine-lot/qmt-connections/activate', { args: { id: c.id } });
|
||||
if (res && res.ok) {
|
||||
setState(res.value);
|
||||
loadMcp(false); // R-020/迭代18:激活切换后刷新 MCP 状态
|
||||
toast.success('已激活:' + c.name + '(立即生效)');
|
||||
} else {
|
||||
toast.error((res && res.error && res.error.message) || '激活失败');
|
||||
@@ -679,6 +689,41 @@ function QmtConnectionsSettings() {
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* R-020/迭代18:MCP 连接状态条(随激活连接;绿=已连接+N工具 / 黄=连接中 / 红=断开 / 灰=未启用) */}
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 12, minWidth: 0, fontSize: 12 }}>
|
||||
<span style={{ flex: 'none', fontWeight: 600 }}>MCP 连接</span>
|
||||
<span style={{
|
||||
flex: 'none', width: 10, height: 10, borderRadius: '50%',
|
||||
background: mcpStatus?.state === 'connected'
|
||||
? 'var(--dsw-alias-state-success-primary, #2e7d32)'
|
||||
: mcpStatus?.state === 'connecting'
|
||||
? 'var(--dsw-alias-state-warn-primary, #f9a825)'
|
||||
: mcpStatus?.state === 'error'
|
||||
? 'var(--dsw-alias-state-error-primary, #c62828)'
|
||||
: 'var(--dsw-alias-label-tertiary, #bbb)',
|
||||
}} />
|
||||
<EllipsisText
|
||||
text={mcpStatus?.state === 'connected'
|
||||
? '已连接' + (mcpStatus.toolCount != null ? '(' + mcpStatus.toolCount + ' 个工具)' : '') + ' · ' + (mcpStatus.url || '')
|
||||
: mcpStatus?.state === 'connecting' ? '连接中…'
|
||||
: mcpStatus?.state === 'error' ? '断开 · ' + String(mcpStatus.error || '').slice(0, 80)
|
||||
: mcpStatus ? '未启用(无激活连接)' : '检测中…'}
|
||||
maxWidth={520}
|
||||
title={mcpStatus?.error ? String(mcpStatus.error) : (mcpStatus?.url || '')}
|
||||
/>
|
||||
<button
|
||||
onClick={async () => {
|
||||
if (mcpChecking) return;
|
||||
setMcpChecking(true);
|
||||
try { await loadMcp(true); } finally { setMcpChecking(false); }
|
||||
}}
|
||||
disabled={mcpChecking}
|
||||
style={{ flex: 'none', padding: '2px 10px', cursor: 'pointer', border: '1px solid var(--dsw-alias-border-l2, #ccc)', borderRadius: 4, background: 'var(--dsw-alias-bg-layer-1, #fff)', fontSize: 12 }}
|
||||
>
|
||||
{mcpChecking ? '检测中…' : '检测 MCP'}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{error && <div style={{ color: 'var(--dsw-alias-state-error-primary, #c62828)', marginBottom: 12 }}><EllipsisText text={error} maxWidth={560} /></div>}
|
||||
{!state && !error && <div style={{ color: 'var(--dsw-alias-label-secondary, #666)' }}>加载中...</div>}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user