feat(Inputbar, MCPToolsButton, AssistantMCPSettings): integrate active MCP server handling and UI updates

- Added active MCP server filtering in Inputbar for message sending.
- Updated MCPToolsButton to reflect availability of enabled MCPs.
- Refactored AssistantMCPSettings to streamline MCP server updates and adjusted UI styles for consistency.
This commit is contained in:
kangfenmao 2025-04-01 15:55:21 +08:00 committed by 亢奋猫
parent 8b9929cc7b
commit bfbfba13fe
3 changed files with 24 additions and 23 deletions

View File

@ -13,6 +13,7 @@ import TranslateButton from '@renderer/components/TranslateButton'
import { isFunctionCallingModel, isGenerateImageModel, isVisionModel, isWebSearchModel } from '@renderer/config/models' import { isFunctionCallingModel, isGenerateImageModel, isVisionModel, isWebSearchModel } from '@renderer/config/models'
import db from '@renderer/databases' import db from '@renderer/databases'
import { useAssistant } from '@renderer/hooks/useAssistant' import { useAssistant } from '@renderer/hooks/useAssistant'
import { useMCPServers } from '@renderer/hooks/useMCPServers'
import { useMessageOperations, useTopicLoading } from '@renderer/hooks/useMessageOperations' import { useMessageOperations, useTopicLoading } from '@renderer/hooks/useMessageOperations'
import { modelGenerating, useRuntime } from '@renderer/hooks/useRuntime' import { modelGenerating, useRuntime } from '@renderer/hooks/useRuntime'
import { useMessageStyle, useSettings } from '@renderer/hooks/useSettings' import { useMessageStyle, useSettings } from '@renderer/hooks/useSettings'
@ -101,6 +102,7 @@ const Inputbar: FC<Props> = ({ assistant: _assistant, setActiveTopic, topic }) =
const isVision = useMemo(() => isVisionModel(model), [model]) const isVision = useMemo(() => isVisionModel(model), [model])
const supportExts = useMemo(() => [...textExts, ...documentExts, ...(isVision ? imageExts : [])], [isVision]) const supportExts = useMemo(() => [...textExts, ...documentExts, ...(isVision ? imageExts : [])], [isVision])
const navigate = useNavigate() const navigate = useNavigate()
const { activedMcpServers } = useMCPServers()
const showKnowledgeIcon = useSidebarIconShow('knowledge') const showKnowledgeIcon = useSidebarIconShow('knowledge')
const showMCPToolsIcon = isFunctionCallingModel(model) const showMCPToolsIcon = isFunctionCallingModel(model)
@ -148,7 +150,7 @@ const Inputbar: FC<Props> = ({ assistant: _assistant, setActiveTopic, topic }) =
// Reset to assistant knowledge mcp servers // Reset to assistant knowledge mcp servers
useEffect(() => { useEffect(() => {
setEnabledMCPs(assistant.mcpServers || []) setEnabledMCPs(assistant.mcpServers || [])
}, [assistant]) }, [assistant.mcpServers])
const sendMessage = useCallback(async () => { const sendMessage = useCallback(async () => {
if (inputEmpty || loading) { if (inputEmpty || loading) {
@ -179,8 +181,12 @@ const Inputbar: FC<Props> = ({ assistant: _assistant, setActiveTopic, topic }) =
userMessage.mentions = mentionModels userMessage.mentions = mentionModels
} }
if (enabledMCPs) { if (isFunctionCallingModel(model)) {
userMessage.enabledMCPs = enabledMCPs if (!isEmpty(assistant.mcpServers) && !isEmpty(activedMcpServers)) {
userMessage.enabledMCPs = activedMcpServers.filter((server) =>
assistant.mcpServers?.some((s) => s.id === server.id)
)
}
} }
userMessage.usage = await estimateMessageUsage(userMessage) userMessage.usage = await estimateMessageUsage(userMessage)
@ -202,13 +208,14 @@ const Inputbar: FC<Props> = ({ assistant: _assistant, setActiveTopic, topic }) =
console.error('Failed to send message:', error) console.error('Failed to send message:', error)
} }
}, [ }, [
activedMcpServers,
assistant, assistant,
dispatch, dispatch,
enabledMCPs,
files, files,
inputEmpty, inputEmpty,
loading, loading,
mentionModels, mentionModels,
model,
resizeTextArea, resizeTextArea,
selectedKnowledgeBases, selectedKnowledgeBases,
text, text,

View File

@ -19,6 +19,10 @@ const MCPToolsButton: FC<Props> = ({ enabledMCPs, toggelEnableMCP, ToolbarButton
const menuRef = useRef<HTMLDivElement>(null) const menuRef = useRef<HTMLDivElement>(null)
const { t } = useTranslation() const { t } = useTranslation()
const availableMCPs = activedMcpServers.filter((server) => enabledMCPs.some((s) => s.id === server.id))
const buttonEnabled = availableMCPs.length > 0
const truncateText = (text: string, maxLength: number = 50) => { const truncateText = (text: string, maxLength: number = 50) => {
if (!text || text.length <= maxLength) return text if (!text || text.length <= maxLength) return text
return text.substring(0, maxLength) + '...' return text.substring(0, maxLength) + '...'
@ -102,7 +106,7 @@ const MCPToolsButton: FC<Props> = ({ enabledMCPs, toggelEnableMCP, ToolbarButton
overlayClassName="mention-models-dropdown"> overlayClassName="mention-models-dropdown">
<Tooltip placement="top" title={t('settings.mcp.title')} arrow> <Tooltip placement="top" title={t('settings.mcp.title')} arrow>
<ToolbarButton type="text" ref={dropdownRef}> <ToolbarButton type="text" ref={dropdownRef}>
<CodeOutlined style={{ color: enabledMCPs.length > 0 ? 'var(--color-primary)' : 'var(--color-icon)' }} /> <CodeOutlined style={{ color: buttonEnabled ? 'var(--color-primary)' : 'var(--color-icon)' }} />
</ToolbarButton> </ToolbarButton>
</Tooltip> </Tooltip>
</Dropdown> </Dropdown>

View File

@ -25,14 +25,14 @@ interface Props {
const AssistantMCPSettings: React.FC<Props> = ({ assistant, updateAssistant }) => { const AssistantMCPSettings: React.FC<Props> = ({ assistant, updateAssistant }) => {
const { t } = useTranslation() const { t } = useTranslation()
const { mcpServers: allMcpServers } = useMCPServers() const { mcpServers: allMcpServers } = useMCPServers()
const onUpdate = (ids: string[]) => { const onUpdate = (ids: string[]) => {
const mcpServers = ids const mcpServers = ids
.map((id) => allMcpServers.find((server) => server.id === id)) .map((id) => allMcpServers.find((server) => server.id === id))
.filter((server): server is MCPServer => server !== undefined && server.isActive) .filter((server): server is MCPServer => server !== undefined && server.isActive)
const _assistant = { ...assistant, mcpServers }
updateAssistant(_assistant) updateAssistant({ ...assistant, mcpServers })
} }
const handleServerToggle = (serverId: string) => { const handleServerToggle = (serverId: string) => {
@ -52,7 +52,7 @@ const AssistantMCPSettings: React.FC<Props> = ({ assistant, updateAssistant }) =
return ( return (
<Container> <Container>
<HeaderContainer> <HeaderContainer>
<Box style={{ fontWeight: 'bold', fontSize: '16px' }}> <Box style={{ fontWeight: 'bold', fontSize: '14px' }}>
{t('assistants.settings.mcp.title')} {t('assistants.settings.mcp.title')}
<Tooltip title={t('assistants.settings.mcp.description', 'Select MCP servers to use with this assistant')}> <Tooltip title={t('assistants.settings.mcp.description', 'Select MCP servers to use with this assistant')}>
<InfoIcon /> <InfoIcon />
@ -111,9 +111,6 @@ const Container = styled.div`
flex: 1; flex: 1;
flex-direction: column; flex-direction: column;
overflow: hidden; overflow: hidden;
padding: 12px;
background-color: ${(props) => props.theme.colors?.background || '#f9f9f9'};
border-radius: 8px;
` `
const HeaderContainer = styled.div` const HeaderContainer = styled.div`
@ -126,13 +123,13 @@ const HeaderContainer = styled.div`
const InfoIcon = styled(InfoCircleOutlined)` const InfoIcon = styled(InfoCircleOutlined)`
margin-left: 6px; margin-left: 6px;
font-size: 14px; font-size: 14px;
color: ${(props) => props.theme.colors?.textSecondary || '#8c8c8c'}; color: var(--color-text-2);
cursor: help; cursor: help;
` `
const EnabledCount = styled.span` const EnabledCount = styled.span`
font-size: 12px; font-size: 12px;
color: ${(props) => props.theme.colors?.textSecondary || '#8c8c8c'}; color: var(--color-text-2);
` `
const EmptyContainer = styled.div` const EmptyContainer = styled.div`
@ -148,7 +145,6 @@ const ServerList = styled.div`
flex-direction: column; flex-direction: column;
gap: 8px; gap: 8px;
overflow-y: auto; overflow-y: auto;
padding: 4px;
` `
const ServerItem = styled.div<{ isEnabled: boolean }>` const ServerItem = styled.div<{ isEnabled: boolean }>`
@ -157,15 +153,9 @@ const ServerItem = styled.div<{ isEnabled: boolean }>`
align-items: center; align-items: center;
padding: 12px 16px; padding: 12px 16px;
border-radius: 8px; border-radius: 8px;
background-color: ${(props) => props.theme.colors?.cardBackground || '#fff'}; background-color: var(--color-background-mute);
border: 1px solid ${(props) => props.theme.colors?.border || '#e6e6e6'}; border: 1px solid var(--color-border);
transition: all 0.2s ease; transition: all 0.2s ease;
&:hover {
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
transform: translateY(-1px);
}
opacity: ${(props) => (props.isEnabled ? 1 : 0.7)}; opacity: ${(props) => (props.isEnabled ? 1 : 0.7)};
` `