refactor(web): MCP tool availability to context-based version gating (#30955)

This commit is contained in:
yyh
2026-01-14 13:40:16 +08:00
committed by GitHub
parent d095bd413b
commit 14b2e5bd0d
20 changed files with 100 additions and 108 deletions

View File

@ -183,7 +183,6 @@ const AgentTools: FC = () => {
onSelect={handleSelectTool} onSelect={handleSelectTool}
onSelectMultiple={handleSelectMultipleTool} onSelectMultiple={handleSelectMultipleTool}
selectedTools={tools as unknown as ToolValue[]} selectedTools={tools as unknown as ToolValue[]}
canChooseMCPTool
/> />
</> </>
)} )}

View File

@ -55,7 +55,6 @@ type FormProps<
nodeId?: string nodeId?: string
nodeOutputVars?: NodeOutPutVar[] nodeOutputVars?: NodeOutPutVar[]
availableNodes?: Node[] availableNodes?: Node[]
canChooseMCPTool?: boolean
} }
function Form< function Form<
@ -81,7 +80,6 @@ function Form<
nodeId, nodeId,
nodeOutputVars, nodeOutputVars,
availableNodes, availableNodes,
canChooseMCPTool,
}: FormProps<CustomFormSchema>) { }: FormProps<CustomFormSchema>) {
const language = useLanguage() const language = useLanguage()
const [changeKey, setChangeKey] = useState('') const [changeKey, setChangeKey] = useState('')
@ -407,7 +405,6 @@ function Form<
value={value[variable] || []} value={value[variable] || []}
onChange={item => handleFormChange(variable, item as any)} onChange={item => handleFormChange(variable, item as any)}
supportCollapse supportCollapse
canChooseMCPTool={canChooseMCPTool}
/> />
{fieldMoreInfo?.(formSchema)} {fieldMoreInfo?.(formSchema)}
{validating && changeKey === variable && <ValidatingTip />} {validating && changeKey === variable && <ValidatingTip />}

View File

@ -7,6 +7,7 @@ import { beforeEach, describe, expect, it, vi } from 'vitest'
// ==================== Imports (after mocks) ==================== // ==================== Imports (after mocks) ====================
import { MCPToolAvailabilityProvider } from '@/app/components/workflow/nodes/_base/components/mcp-tool-availability'
import MultipleToolSelector from './index' import MultipleToolSelector from './index'
// ==================== Mock Setup ==================== // ==================== Mock Setup ====================
@ -190,10 +191,11 @@ type RenderOptions = {
nodeOutputVars?: NodeOutPutVar[] nodeOutputVars?: NodeOutPutVar[]
availableNodes?: Node[] availableNodes?: Node[]
nodeId?: string nodeId?: string
canChooseMCPTool?: boolean versionSupported?: boolean
} }
const renderComponent = (options: RenderOptions = {}) => { const renderComponent = (options: RenderOptions = {}) => {
const { versionSupported, ...overrides } = options
const defaultProps = { const defaultProps = {
disabled: false, disabled: false,
value: [], value: [],
@ -206,16 +208,17 @@ const renderComponent = (options: RenderOptions = {}) => {
nodeOutputVars: [createNodeOutputVar()], nodeOutputVars: [createNodeOutputVar()],
availableNodes: [createNode()], availableNodes: [createNode()],
nodeId: 'test-node-id', nodeId: 'test-node-id',
canChooseMCPTool: false,
} }
const props = { ...defaultProps, ...options } const props = { ...defaultProps, ...overrides }
const queryClient = createQueryClient() const queryClient = createQueryClient()
return { return {
...render( ...render(
<QueryClientProvider client={queryClient}> <QueryClientProvider client={queryClient}>
<MultipleToolSelector {...props} /> <MCPToolAvailabilityProvider versionSupported={versionSupported}>
<MultipleToolSelector {...props} />
</MCPToolAvailabilityProvider>
</QueryClientProvider>, </QueryClientProvider>,
), ),
props, props,
@ -410,7 +413,7 @@ describe('MultipleToolSelector', () => {
expect(screen.getByText('2/3')).toBeInTheDocument() expect(screen.getByText('2/3')).toBeInTheDocument()
}) })
it('should track enabled count with MCP tools when canChooseMCPTool is true', () => { it('should track enabled count with MCP tools when version is supported', () => {
// Arrange // Arrange
const mcpTools = [createMCPTool({ id: 'mcp-provider' })] const mcpTools = [createMCPTool({ id: 'mcp-provider' })]
mockMCPToolsData.mockReturnValue(mcpTools) mockMCPToolsData.mockReturnValue(mcpTools)
@ -421,13 +424,13 @@ describe('MultipleToolSelector', () => {
] ]
// Act // Act
renderComponent({ value: tools, canChooseMCPTool: true }) renderComponent({ value: tools, versionSupported: true })
// Assert // Assert
expect(screen.getByText('2/2')).toBeInTheDocument() expect(screen.getByText('2/2')).toBeInTheDocument()
}) })
it('should not count MCP tools when canChooseMCPTool is false', () => { it('should not count MCP tools when version is unsupported', () => {
// Arrange // Arrange
const mcpTools = [createMCPTool({ id: 'mcp-provider' })] const mcpTools = [createMCPTool({ id: 'mcp-provider' })]
mockMCPToolsData.mockReturnValue(mcpTools) mockMCPToolsData.mockReturnValue(mcpTools)
@ -438,7 +441,7 @@ describe('MultipleToolSelector', () => {
] ]
// Act // Act
renderComponent({ value: tools, canChooseMCPTool: false }) renderComponent({ value: tools, versionSupported: false })
// Assert // Assert
expect(screen.getByText('1/2')).toBeInTheDocument() expect(screen.getByText('1/2')).toBeInTheDocument()
@ -721,14 +724,6 @@ describe('MultipleToolSelector', () => {
expect(screen.getByTestId('tool-selector-add')).toBeInTheDocument() expect(screen.getByTestId('tool-selector-add')).toBeInTheDocument()
}) })
it('should pass canChooseMCPTool prop correctly', () => {
// Arrange & Act
renderComponent({ canChooseMCPTool: true })
// Assert
expect(screen.getByTestId('tool-selector-add')).toBeInTheDocument()
})
it('should render with supportEnableSwitch for edit selectors', () => { it('should render with supportEnableSwitch for edit selectors', () => {
// Arrange // Arrange
const tools = [createToolValue()] const tools = [createToolValue()]
@ -771,13 +766,13 @@ describe('MultipleToolSelector', () => {
] ]
// Act // Act
renderComponent({ value: tools, canChooseMCPTool: true }) renderComponent({ value: tools, versionSupported: true })
// Assert // Assert
expect(screen.getByText('2/2')).toBeInTheDocument() expect(screen.getByText('2/2')).toBeInTheDocument()
}) })
it('should exclude MCP tools from enabled count when canChooseMCPTool is false', () => { it('should exclude MCP tools from enabled count when strategy version is unsupported', () => {
// Arrange // Arrange
const mcpTools = [createMCPTool({ id: 'mcp-provider' })] const mcpTools = [createMCPTool({ id: 'mcp-provider' })]
mockMCPToolsData.mockReturnValue(mcpTools) mockMCPToolsData.mockReturnValue(mcpTools)
@ -788,7 +783,7 @@ describe('MultipleToolSelector', () => {
] ]
// Act // Act
renderComponent({ value: tools, canChooseMCPTool: false }) renderComponent({ value: tools, versionSupported: false })
// Assert - Only regular tool should be counted // Assert - Only regular tool should be counted
expect(screen.getByText('1/2')).toBeInTheDocument() expect(screen.getByText('1/2')).toBeInTheDocument()

View File

@ -12,6 +12,7 @@ import Divider from '@/app/components/base/divider'
import { ArrowDownRoundFill } from '@/app/components/base/icons/src/vender/solid/general' import { ArrowDownRoundFill } from '@/app/components/base/icons/src/vender/solid/general'
import Tooltip from '@/app/components/base/tooltip' import Tooltip from '@/app/components/base/tooltip'
import ToolSelector from '@/app/components/plugins/plugin-detail-panel/tool-selector' import ToolSelector from '@/app/components/plugins/plugin-detail-panel/tool-selector'
import { useMCPToolAvailability } from '@/app/components/workflow/nodes/_base/components/mcp-tool-availability'
import { useAllMCPTools } from '@/service/use-tools' import { useAllMCPTools } from '@/service/use-tools'
import { cn } from '@/utils/classnames' import { cn } from '@/utils/classnames'
@ -27,7 +28,6 @@ type Props = {
nodeOutputVars: NodeOutPutVar[] nodeOutputVars: NodeOutPutVar[]
availableNodes: Node[] availableNodes: Node[]
nodeId?: string nodeId?: string
canChooseMCPTool?: boolean
} }
const MultipleToolSelector = ({ const MultipleToolSelector = ({
@ -42,14 +42,14 @@ const MultipleToolSelector = ({
nodeOutputVars, nodeOutputVars,
availableNodes, availableNodes,
nodeId, nodeId,
canChooseMCPTool,
}: Props) => { }: Props) => {
const { t } = useTranslation() const { t } = useTranslation()
const { allowed: isMCPToolAllowed } = useMCPToolAvailability()
const { data: mcpTools } = useAllMCPTools() const { data: mcpTools } = useAllMCPTools()
const enabledCount = value.filter((item) => { const enabledCount = value.filter((item) => {
const isMCPTool = mcpTools?.find(tool => tool.id === item.provider_name) const isMCPTool = mcpTools?.find(tool => tool.id === item.provider_name)
if (isMCPTool) if (isMCPTool)
return item.enabled && canChooseMCPTool return item.enabled && isMCPToolAllowed
return item.enabled return item.enabled
}).length }).length
// collapse control // collapse control
@ -167,7 +167,6 @@ const MultipleToolSelector = ({
onSelectMultiple={handleAddMultiple} onSelectMultiple={handleAddMultiple}
onDelete={() => handleDelete(index)} onDelete={() => handleDelete(index)}
supportEnableSwitch supportEnableSwitch
canChooseMCPTool={canChooseMCPTool}
isEdit isEdit
/> />
</div> </div>
@ -190,7 +189,6 @@ const MultipleToolSelector = ({
panelShowState={panelShowState} panelShowState={panelShowState}
onPanelShowStateChange={setPanelShowState} onPanelShowStateChange={setPanelShowState}
isEdit={false} isEdit={false}
canChooseMCPTool={canChooseMCPTool}
onSelectMultiple={handleAddMultiple} onSelectMultiple={handleAddMultiple}
/> />
</> </>

View File

@ -64,7 +64,6 @@ type Props = {
nodeOutputVars: NodeOutPutVar[] nodeOutputVars: NodeOutPutVar[]
availableNodes: Node[] availableNodes: Node[]
nodeId?: string nodeId?: string
canChooseMCPTool?: boolean
} }
const ToolSelector: FC<Props> = ({ const ToolSelector: FC<Props> = ({
value, value,
@ -86,7 +85,6 @@ const ToolSelector: FC<Props> = ({
nodeOutputVars, nodeOutputVars,
availableNodes, availableNodes,
nodeId = '', nodeId = '',
canChooseMCPTool,
}) => { }) => {
const { t } = useTranslation() const { t } = useTranslation()
const [isShow, onShowChange] = useState(false) const [isShow, onShowChange] = useState(false)
@ -267,7 +265,6 @@ const ToolSelector: FC<Props> = ({
</p> </p>
</div> </div>
)} )}
canChooseMCPTool={canChooseMCPTool}
/> />
)} )}
</PortalToFollowElemTrigger> </PortalToFollowElemTrigger>
@ -300,7 +297,6 @@ const ToolSelector: FC<Props> = ({
onSelectMultiple={handleSelectMultipleTool} onSelectMultiple={handleSelectMultipleTool}
scope={scope} scope={scope}
selectedTools={selectedTools} selectedTools={selectedTools}
canChooseMCPTool={canChooseMCPTool}
/> />
</div> </div>
<div className="flex flex-col gap-1"> <div className="flex flex-col gap-1">

View File

@ -16,6 +16,7 @@ import Tooltip from '@/app/components/base/tooltip'
import { ToolTipContent } from '@/app/components/base/tooltip/content' import { ToolTipContent } from '@/app/components/base/tooltip/content'
import Indicator from '@/app/components/header/indicator' import Indicator from '@/app/components/header/indicator'
import { InstallPluginButton } from '@/app/components/workflow/nodes/_base/components/install-plugin-button' import { InstallPluginButton } from '@/app/components/workflow/nodes/_base/components/install-plugin-button'
import { useMCPToolAvailability } from '@/app/components/workflow/nodes/_base/components/mcp-tool-availability'
import McpToolNotSupportTooltip from '@/app/components/workflow/nodes/_base/components/mcp-tool-not-support-tooltip' import McpToolNotSupportTooltip from '@/app/components/workflow/nodes/_base/components/mcp-tool-not-support-tooltip'
import { SwitchPluginVersion } from '@/app/components/workflow/nodes/_base/components/switch-plugin-version' import { SwitchPluginVersion } from '@/app/components/workflow/nodes/_base/components/switch-plugin-version'
import { cn } from '@/utils/classnames' import { cn } from '@/utils/classnames'
@ -39,7 +40,6 @@ type Props = {
versionMismatch?: boolean versionMismatch?: boolean
open: boolean open: boolean
authRemoved?: boolean authRemoved?: boolean
canChooseMCPTool?: boolean
} }
const ToolItem = ({ const ToolItem = ({
@ -61,13 +61,13 @@ const ToolItem = ({
errorTip, errorTip,
versionMismatch, versionMismatch,
authRemoved, authRemoved,
canChooseMCPTool,
}: Props) => { }: Props) => {
const { t } = useTranslation() const { t } = useTranslation()
const { allowed: isMCPToolAllowed } = useMCPToolAvailability()
const providerNameText = isMCPTool ? providerShowName : providerName?.split('/').pop() const providerNameText = isMCPTool ? providerShowName : providerName?.split('/').pop()
const isTransparent = uninstalled || versionMismatch || isError const isTransparent = uninstalled || versionMismatch || isError
const [isDeleting, setIsDeleting] = useState(false) const [isDeleting, setIsDeleting] = useState(false)
const isShowCanNotChooseMCPTip = isMCPTool && !canChooseMCPTool const isShowCanNotChooseMCPTip = isMCPTool && !isMCPToolAllowed
return ( return (
<div className={cn( <div className={cn(
@ -125,9 +125,7 @@ const ToolItem = ({
/> />
</div> </div>
)} )}
{isShowCanNotChooseMCPTip && ( {isShowCanNotChooseMCPTip && <McpToolNotSupportTooltip />}
<McpToolNotSupportTooltip />
)}
{!isError && !uninstalled && !versionMismatch && noAuth && ( {!isError && !uninstalled && !versionMismatch && noAuth && (
<Button variant="secondary" size="small"> <Button variant="secondary" size="small">
{t('notAuthorized', { ns: 'tools' })} {t('notAuthorized', { ns: 'tools' })}

View File

@ -47,7 +47,6 @@ type AllToolsProps = {
canNotSelectMultiple?: boolean canNotSelectMultiple?: boolean
onSelectMultiple?: (type: BlockEnum, tools: ToolDefaultValue[]) => void onSelectMultiple?: (type: BlockEnum, tools: ToolDefaultValue[]) => void
selectedTools?: ToolValue[] selectedTools?: ToolValue[]
canChooseMCPTool?: boolean
onTagsChange?: Dispatch<SetStateAction<string[]>> onTagsChange?: Dispatch<SetStateAction<string[]>>
isInRAGPipeline?: boolean isInRAGPipeline?: boolean
featuredPlugins?: Plugin[] featuredPlugins?: Plugin[]
@ -71,7 +70,6 @@ const AllTools = ({
customTools, customTools,
mcpTools = [], mcpTools = [],
selectedTools, selectedTools,
canChooseMCPTool,
onTagsChange, onTagsChange,
isInRAGPipeline = false, isInRAGPipeline = false,
featuredPlugins = [], featuredPlugins = [],
@ -249,7 +247,6 @@ const AllTools = ({
providerMap={providerMap} providerMap={providerMap}
onSelect={onSelect} onSelect={onSelect}
selectedTools={selectedTools} selectedTools={selectedTools}
canChooseMCPTool={canChooseMCPTool}
isLoading={featuredLoading} isLoading={featuredLoading}
onInstallSuccess={async () => { onInstallSuccess={async () => {
await onFeaturedInstallSuccess?.() await onFeaturedInstallSuccess?.()
@ -275,7 +272,6 @@ const AllTools = ({
viewType={isSupportGroupView ? activeView : ViewType.flat} viewType={isSupportGroupView ? activeView : ViewType.flat}
hasSearchText={hasSearchText} hasSearchText={hasSearchText}
selectedTools={selectedTools} selectedTools={selectedTools}
canChooseMCPTool={canChooseMCPTool}
/> />
</> </>
)} )}

View File

@ -30,7 +30,6 @@ type FeaturedToolsProps = {
providerMap: Map<string, ToolWithProvider> providerMap: Map<string, ToolWithProvider>
onSelect: (type: BlockEnum, tool: ToolDefaultValue) => void onSelect: (type: BlockEnum, tool: ToolDefaultValue) => void
selectedTools?: ToolValue[] selectedTools?: ToolValue[]
canChooseMCPTool?: boolean
isLoading?: boolean isLoading?: boolean
onInstallSuccess?: () => void onInstallSuccess?: () => void
} }
@ -42,7 +41,6 @@ const FeaturedTools = ({
providerMap, providerMap,
onSelect, onSelect,
selectedTools, selectedTools,
canChooseMCPTool,
isLoading = false, isLoading = false,
onInstallSuccess, onInstallSuccess,
}: FeaturedToolsProps) => { }: FeaturedToolsProps) => {
@ -166,7 +164,6 @@ const FeaturedTools = ({
viewType={ViewType.flat} viewType={ViewType.flat}
hasSearchText={false} hasSearchText={false}
selectedTools={selectedTools} selectedTools={selectedTools}
canChooseMCPTool={canChooseMCPTool}
/> />
)} )}

View File

@ -223,7 +223,6 @@ const Tabs: FC<TabsProps> = ({
customTools={customTools || []} customTools={customTools || []}
workflowTools={workflowTools || []} workflowTools={workflowTools || []}
mcpTools={mcpTools || []} mcpTools={mcpTools || []}
canChooseMCPTool
onTagsChange={onTagsChange} onTagsChange={onTagsChange}
isInRAGPipeline={inRAGPipeline} isInRAGPipeline={inRAGPipeline}
featuredPlugins={featuredPlugins} featuredPlugins={featuredPlugins}

View File

@ -50,7 +50,6 @@ type Props = {
supportAddCustomTool?: boolean supportAddCustomTool?: boolean
scope?: string scope?: string
selectedTools?: ToolValue[] selectedTools?: ToolValue[]
canChooseMCPTool?: boolean
} }
const ToolPicker: FC<Props> = ({ const ToolPicker: FC<Props> = ({
@ -66,7 +65,6 @@ const ToolPicker: FC<Props> = ({
scope = 'all', scope = 'all',
selectedTools, selectedTools,
panelClassName, panelClassName,
canChooseMCPTool,
}) => { }) => {
const { t } = useTranslation() const { t } = useTranslation()
const [searchText, setSearchText] = useState('') const [searchText, setSearchText] = useState('')
@ -198,7 +196,6 @@ const ToolPicker: FC<Props> = ({
workflowTools={workflowToolList || []} workflowTools={workflowToolList || []}
mcpTools={mcpTools || []} mcpTools={mcpTools || []}
selectedTools={selectedTools} selectedTools={selectedTools}
canChooseMCPTool={canChooseMCPTool}
onTagsChange={setTags} onTagsChange={setTags}
featuredPlugins={featuredPlugins} featuredPlugins={featuredPlugins}
featuredLoading={isFeaturedLoading} featuredLoading={isFeaturedLoading}

View File

@ -18,7 +18,6 @@ type Props = {
letters: string[] letters: string[]
toolRefs: any toolRefs: any
selectedTools?: ToolValue[] selectedTools?: ToolValue[]
canChooseMCPTool?: boolean
} }
const ToolViewFlatView: FC<Props> = ({ const ToolViewFlatView: FC<Props> = ({
@ -32,7 +31,6 @@ const ToolViewFlatView: FC<Props> = ({
onSelectMultiple, onSelectMultiple,
toolRefs, toolRefs,
selectedTools, selectedTools,
canChooseMCPTool,
}) => { }) => {
const firstLetterToolIds = useMemo(() => { const firstLetterToolIds = useMemo(() => {
const res: Record<string, string> = {} const res: Record<string, string> = {}
@ -63,7 +61,6 @@ const ToolViewFlatView: FC<Props> = ({
canNotSelectMultiple={canNotSelectMultiple} canNotSelectMultiple={canNotSelectMultiple}
onSelectMultiple={onSelectMultiple} onSelectMultiple={onSelectMultiple}
selectedTools={selectedTools} selectedTools={selectedTools}
canChooseMCPTool={canChooseMCPTool}
/> />
</div> </div>
))} ))}

View File

@ -14,7 +14,6 @@ type Props = {
canNotSelectMultiple?: boolean canNotSelectMultiple?: boolean
onSelectMultiple?: (type: BlockEnum, tools: ToolDefaultValue[]) => void onSelectMultiple?: (type: BlockEnum, tools: ToolDefaultValue[]) => void
selectedTools?: ToolValue[] selectedTools?: ToolValue[]
canChooseMCPTool?: boolean
} }
const Item: FC<Props> = ({ const Item: FC<Props> = ({
@ -25,7 +24,6 @@ const Item: FC<Props> = ({
canNotSelectMultiple, canNotSelectMultiple,
onSelectMultiple, onSelectMultiple,
selectedTools, selectedTools,
canChooseMCPTool,
}) => { }) => {
return ( return (
<div> <div>
@ -43,7 +41,6 @@ const Item: FC<Props> = ({
canNotSelectMultiple={canNotSelectMultiple} canNotSelectMultiple={canNotSelectMultiple}
onSelectMultiple={onSelectMultiple} onSelectMultiple={onSelectMultiple}
selectedTools={selectedTools} selectedTools={selectedTools}
canChooseMCPTool={canChooseMCPTool}
/> />
))} ))}
</div> </div>

View File

@ -15,7 +15,6 @@ type Props = {
canNotSelectMultiple?: boolean canNotSelectMultiple?: boolean
onSelectMultiple?: (type: BlockEnum, tools: ToolDefaultValue[]) => void onSelectMultiple?: (type: BlockEnum, tools: ToolDefaultValue[]) => void
selectedTools?: ToolValue[] selectedTools?: ToolValue[]
canChooseMCPTool?: boolean
} }
const ToolListTreeView: FC<Props> = ({ const ToolListTreeView: FC<Props> = ({
@ -25,7 +24,6 @@ const ToolListTreeView: FC<Props> = ({
canNotSelectMultiple, canNotSelectMultiple,
onSelectMultiple, onSelectMultiple,
selectedTools, selectedTools,
canChooseMCPTool,
}) => { }) => {
const { t } = useTranslation() const { t } = useTranslation()
const getI18nGroupName = useCallback((name: string) => { const getI18nGroupName = useCallback((name: string) => {
@ -56,7 +54,6 @@ const ToolListTreeView: FC<Props> = ({
canNotSelectMultiple={canNotSelectMultiple} canNotSelectMultiple={canNotSelectMultiple}
onSelectMultiple={onSelectMultiple} onSelectMultiple={onSelectMultiple}
selectedTools={selectedTools} selectedTools={selectedTools}
canChooseMCPTool={canChooseMCPTool}
/> />
))} ))}
</div> </div>

View File

@ -9,6 +9,7 @@ import * as React from 'react'
import { useCallback, useEffect, useMemo, useRef } from 'react' import { useCallback, useEffect, useMemo, useRef } from 'react'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import { Mcp } from '@/app/components/base/icons/src/vender/other' import { Mcp } from '@/app/components/base/icons/src/vender/other'
import { useMCPToolAvailability } from '@/app/components/workflow/nodes/_base/components/mcp-tool-availability'
import { useGetLanguage } from '@/context/i18n' import { useGetLanguage } from '@/context/i18n'
import useTheme from '@/hooks/use-theme' import useTheme from '@/hooks/use-theme'
import { Theme } from '@/types/app' import { Theme } from '@/types/app'
@ -38,7 +39,6 @@ type Props = {
canNotSelectMultiple?: boolean canNotSelectMultiple?: boolean
onSelectMultiple?: (type: BlockEnum, tools: ToolDefaultValue[]) => void onSelectMultiple?: (type: BlockEnum, tools: ToolDefaultValue[]) => void
selectedTools?: ToolValue[] selectedTools?: ToolValue[]
canChooseMCPTool?: boolean
isShowLetterIndex?: boolean isShowLetterIndex?: boolean
} }
@ -51,9 +51,9 @@ const Tool: FC<Props> = ({
canNotSelectMultiple, canNotSelectMultiple,
onSelectMultiple, onSelectMultiple,
selectedTools, selectedTools,
canChooseMCPTool,
}) => { }) => {
const { t } = useTranslation() const { t } = useTranslation()
const { allowed: isMCPToolAllowed } = useMCPToolAvailability()
const language = useGetLanguage() const language = useGetLanguage()
const isFlatView = viewType === ViewType.flat const isFlatView = viewType === ViewType.flat
const notShowProvider = payload.type === CollectionType.workflow const notShowProvider = payload.type === CollectionType.workflow
@ -63,7 +63,7 @@ const Tool: FC<Props> = ({
const ref = useRef(null) const ref = useRef(null)
const isHovering = useHover(ref) const isHovering = useHover(ref)
const isMCPTool = payload.type === CollectionType.mcp const isMCPTool = payload.type === CollectionType.mcp
const isShowCanNotChooseMCPTip = !canChooseMCPTool && isMCPTool const isShowCanNotChooseMCPTip = !isMCPToolAllowed && isMCPTool
const { theme } = useTheme() const { theme } = useTheme()
const normalizedIcon = useMemo<ToolWithProvider['icon']>(() => { const normalizedIcon = useMemo<ToolWithProvider['icon']>(() => {
return normalizeProviderIcon(payload.icon) ?? payload.icon return normalizeProviderIcon(payload.icon) ?? payload.icon

View File

@ -21,7 +21,6 @@ type ToolsProps = {
className?: string className?: string
indexBarClassName?: string indexBarClassName?: string
selectedTools?: ToolValue[] selectedTools?: ToolValue[]
canChooseMCPTool?: boolean
} }
const Tools = ({ const Tools = ({
onSelect, onSelect,
@ -35,7 +34,6 @@ const Tools = ({
className, className,
indexBarClassName, indexBarClassName,
selectedTools, selectedTools,
canChooseMCPTool,
}: ToolsProps) => { }: ToolsProps) => {
// const tools: any = [] // const tools: any = []
const language = useGetLanguage() const language = useGetLanguage()
@ -109,7 +107,6 @@ const Tools = ({
canNotSelectMultiple={canNotSelectMultiple} canNotSelectMultiple={canNotSelectMultiple}
onSelectMultiple={onSelectMultiple} onSelectMultiple={onSelectMultiple}
selectedTools={selectedTools} selectedTools={selectedTools}
canChooseMCPTool={canChooseMCPTool}
indexBar={<IndexBar letters={letters} itemRefs={toolRefs} className={indexBarClassName} />} indexBar={<IndexBar letters={letters} itemRefs={toolRefs} className={indexBarClassName} />}
/> />
) )
@ -121,7 +118,6 @@ const Tools = ({
canNotSelectMultiple={canNotSelectMultiple} canNotSelectMultiple={canNotSelectMultiple}
onSelectMultiple={onSelectMultiple} onSelectMultiple={onSelectMultiple}
selectedTools={selectedTools} selectedTools={selectedTools}
canChooseMCPTool={canChooseMCPTool}
/> />
) )
)} )}

View File

@ -92,13 +92,12 @@ function formatStrategy(input: StrategyPluginDetail[], getIcon: (i: string) => s
export type AgentStrategySelectorProps = { export type AgentStrategySelectorProps = {
value?: Strategy value?: Strategy
onChange: (value?: Strategy) => void onChange: (value?: Strategy) => void
canChooseMCPTool: boolean
} }
export const AgentStrategySelector = memo((props: AgentStrategySelectorProps) => { export const AgentStrategySelector = memo((props: AgentStrategySelectorProps) => {
const { enable_marketplace } = useGlobalPublicStore(s => s.systemFeatures) const { enable_marketplace } = useGlobalPublicStore(s => s.systemFeatures)
const { value, onChange, canChooseMCPTool } = props const { value, onChange } = props
const [open, setOpen] = useState(false) const [open, setOpen] = useState(false)
const [viewType, setViewType] = useState<ViewType>(ViewType.flat) const [viewType, setViewType] = useState<ViewType>(ViewType.flat)
const [query, setQuery] = useState('') const [query, setQuery] = useState('')
@ -242,7 +241,6 @@ export const AgentStrategySelector = memo((props: AgentStrategySelectorProps) =>
indexBarClassName="top-0 xl:top-36" indexBarClassName="top-0 xl:top-36"
hasSearchText={false} hasSearchText={false}
canNotSelectMultiple canNotSelectMultiple
canChooseMCPTool={canChooseMCPTool}
isAgent isAgent
/> />
{enable_marketplace && ( {enable_marketplace && (

View File

@ -43,7 +43,6 @@ export type AgentStrategyProps = {
nodeOutputVars?: NodeOutPutVar[] nodeOutputVars?: NodeOutPutVar[]
availableNodes?: Node[] availableNodes?: Node[]
nodeId?: string nodeId?: string
canChooseMCPTool: boolean
} }
type CustomSchema<Type, Field = {}> = Omit<CredentialFormSchema, 'type'> & { type: Type } & Field type CustomSchema<Type, Field = {}> = Omit<CredentialFormSchema, 'type'> & { type: Type } & Field
@ -54,7 +53,7 @@ type MultipleToolSelectorSchema = CustomSchema<'array[tools]'>
type CustomField = ToolSelectorSchema | MultipleToolSelectorSchema type CustomField = ToolSelectorSchema | MultipleToolSelectorSchema
export const AgentStrategy = memo((props: AgentStrategyProps) => { export const AgentStrategy = memo((props: AgentStrategyProps) => {
const { strategy, onStrategyChange, formSchema, formValue, onFormValueChange, nodeOutputVars, availableNodes, nodeId, canChooseMCPTool } = props const { strategy, onStrategyChange, formSchema, formValue, onFormValueChange, nodeOutputVars, availableNodes, nodeId } = props
const { t } = useTranslation() const { t } = useTranslation()
const docLink = useDocLink() const docLink = useDocLink()
const defaultModel = useDefaultModel(ModelTypeEnum.textGeneration) const defaultModel = useDefaultModel(ModelTypeEnum.textGeneration)
@ -189,7 +188,6 @@ export const AgentStrategy = memo((props: AgentStrategyProps) => {
value={value} value={value}
onSelect={item => onChange(item)} onSelect={item => onChange(item)}
onDelete={() => onChange(null)} onDelete={() => onChange(null)}
canChooseMCPTool={canChooseMCPTool}
onSelectMultiple={noop} onSelectMultiple={noop}
/> />
</Field> </Field>
@ -212,7 +210,6 @@ export const AgentStrategy = memo((props: AgentStrategyProps) => {
onChange={onChange} onChange={onChange}
supportCollapse supportCollapse
required={schema.required} required={schema.required}
canChooseMCPTool={canChooseMCPTool}
/> />
) )
} }
@ -220,7 +217,7 @@ export const AgentStrategy = memo((props: AgentStrategyProps) => {
} }
return ( return (
<div className="space-y-2"> <div className="space-y-2">
<AgentStrategySelector value={strategy} onChange={onStrategyChange} canChooseMCPTool={canChooseMCPTool} /> <AgentStrategySelector value={strategy} onChange={onStrategyChange} />
{ {
strategy strategy
? ( ? (
@ -241,7 +238,6 @@ export const AgentStrategy = memo((props: AgentStrategyProps) => {
nodeId={nodeId} nodeId={nodeId}
nodeOutputVars={nodeOutputVars || []} nodeOutputVars={nodeOutputVars || []}
availableNodes={availableNodes || []} availableNodes={availableNodes || []}
canChooseMCPTool={canChooseMCPTool}
/> />
</div> </div>
) )

View File

@ -0,0 +1,38 @@
'use client'
import type { ReactNode } from 'react'
import { createContext, useContext } from 'react'
type MCPToolAvailabilityContextValue = {
versionSupported?: boolean
}
const MCPToolAvailabilityContext = createContext<MCPToolAvailabilityContextValue | undefined>(undefined)
export type MCPToolAvailability = {
allowed: boolean
versionSupported?: boolean
}
export const MCPToolAvailabilityProvider = ({
versionSupported,
children,
}: {
versionSupported?: boolean
children: ReactNode
}) => (
<MCPToolAvailabilityContext.Provider value={{ versionSupported }}>
{children}
</MCPToolAvailabilityContext.Provider>
)
export const useMCPToolAvailability = (): MCPToolAvailability => {
const context = useContext(MCPToolAvailabilityContext)
if (context === undefined)
return { allowed: true }
const { versionSupported } = context
return {
allowed: versionSupported === true,
versionSupported,
}
}

View File

@ -6,9 +6,11 @@ import type { StrategyParamItem } from '@/app/components/plugins/types'
import { memo } from 'react' import { memo } from 'react'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import { toType } from '@/app/components/tools/utils/to-form-schema' import { toType } from '@/app/components/tools/utils/to-form-schema'
import { isSupportMCP } from '@/utils/plugin-version-feature'
import { useStore } from '../../store' import { useStore } from '../../store'
import { AgentStrategy } from '../_base/components/agent-strategy' import { AgentStrategy } from '../_base/components/agent-strategy'
import Field from '../_base/components/field' import Field from '../_base/components/field'
import { MCPToolAvailabilityProvider } from '../_base/components/mcp-tool-availability'
import MemoryConfig from '../_base/components/memory-config' import MemoryConfig from '../_base/components/memory-config'
import OutputVars, { VarItem } from '../_base/components/output-vars' import OutputVars, { VarItem } from '../_base/components/output-vars'
import Split from '../_base/components/split' import Split from '../_base/components/split'
@ -40,9 +42,9 @@ const AgentPanel: FC<NodePanelProps<AgentNodeType>> = (props) => {
readOnly, readOnly,
outputSchema, outputSchema,
handleMemoryChange, handleMemoryChange,
canChooseMCPTool,
} = useConfig(props.id, props.data) } = useConfig(props.id, props.data)
const { t } = useTranslation() const { t } = useTranslation()
const isMCPVersionSupported = isSupportMCP(inputs.meta?.version)
const resetEditor = useStore(s => s.setControlPromptEditorRerenderKey) const resetEditor = useStore(s => s.setControlPromptEditorRerenderKey)
return ( return (
@ -53,37 +55,38 @@ const AgentPanel: FC<NodePanelProps<AgentNodeType>> = (props) => {
className="px-4 py-2" className="px-4 py-2"
tooltip={t('nodes.agent.strategy.tooltip', { ns: 'workflow' })} tooltip={t('nodes.agent.strategy.tooltip', { ns: 'workflow' })}
> >
<AgentStrategy <MCPToolAvailabilityProvider versionSupported={isMCPVersionSupported}>
strategy={inputs.agent_strategy_name <AgentStrategy
? { strategy={inputs.agent_strategy_name
agent_strategy_provider_name: inputs.agent_strategy_provider_name!, ? {
agent_strategy_name: inputs.agent_strategy_name!, agent_strategy_provider_name: inputs.agent_strategy_provider_name!,
agent_strategy_label: inputs.agent_strategy_label!, agent_strategy_name: inputs.agent_strategy_name!,
agent_output_schema: inputs.output_schema, agent_strategy_label: inputs.agent_strategy_label!,
plugin_unique_identifier: inputs.plugin_unique_identifier!, agent_output_schema: inputs.output_schema,
meta: inputs.meta, plugin_unique_identifier: inputs.plugin_unique_identifier!,
} meta: inputs.meta,
: undefined} }
onStrategyChange={(strategy) => { : undefined}
setInputs({ onStrategyChange={(strategy) => {
...inputs, setInputs({
agent_strategy_provider_name: strategy?.agent_strategy_provider_name, ...inputs,
agent_strategy_name: strategy?.agent_strategy_name, agent_strategy_provider_name: strategy?.agent_strategy_provider_name,
agent_strategy_label: strategy?.agent_strategy_label, agent_strategy_name: strategy?.agent_strategy_name,
output_schema: strategy!.agent_output_schema, agent_strategy_label: strategy?.agent_strategy_label,
plugin_unique_identifier: strategy!.plugin_unique_identifier, output_schema: strategy!.agent_output_schema,
meta: strategy?.meta, plugin_unique_identifier: strategy!.plugin_unique_identifier,
}) meta: strategy?.meta,
resetEditor(Date.now()) })
}} resetEditor(Date.now())
formSchema={currentStrategy?.parameters?.map(strategyParamToCredientialForm) || []} }}
formValue={formData} formSchema={currentStrategy?.parameters?.map(strategyParamToCredientialForm) || []}
onFormValueChange={onFormChange} formValue={formData}
nodeOutputVars={availableVars} onFormValueChange={onFormChange}
availableNodes={availableNodesWithParent} nodeOutputVars={availableVars}
nodeId={props.id} availableNodes={availableNodesWithParent}
canChooseMCPTool={canChooseMCPTool} nodeId={props.id}
/> />
</MCPToolAvailabilityProvider>
</Field> </Field>
<div className="px-4 py-2"> <div className="px-4 py-2">
{isChatMode && currentStrategy?.features?.includes(AgentFeature.HISTORY_MESSAGES) && ( {isChatMode && currentStrategy?.features?.includes(AgentFeature.HISTORY_MESSAGES) && (

View File

@ -11,7 +11,6 @@ import {
} from '@/app/components/workflow/hooks' } from '@/app/components/workflow/hooks'
import { useCheckInstalled, useFetchPluginsInMarketPlaceByIds } from '@/service/use-plugins' import { useCheckInstalled, useFetchPluginsInMarketPlaceByIds } from '@/service/use-plugins'
import { useStrategyProviderDetail } from '@/service/use-strategy' import { useStrategyProviderDetail } from '@/service/use-strategy'
import { isSupportMCP } from '@/utils/plugin-version-feature'
import { VarType as VarKindType } from '../../types' import { VarType as VarKindType } from '../../types'
import useAvailableVarList from '../_base/hooks/use-available-var-list' import useAvailableVarList from '../_base/hooks/use-available-var-list'
import useNodeCrud from '../_base/hooks/use-node-crud' import useNodeCrud from '../_base/hooks/use-node-crud'
@ -222,7 +221,6 @@ const useConfig = (id: string, payload: AgentNodeType) => {
outputSchema, outputSchema,
handleMemoryChange, handleMemoryChange,
isChatMode, isChatMode,
canChooseMCPTool: isSupportMCP(inputs.meta?.version),
} }
} }