From 14b2e5bd0d4b0e0b20ec40c683cebaa418836754 Mon Sep 17 00:00:00 2001
From: yyh <92089059+lyzno1@users.noreply.github.com>
Date: Wed, 14 Jan 2026 13:40:16 +0800
Subject: [PATCH] refactor(web): MCP tool availability to context-based version
gating (#30955)
---
.../config/agent/agent-tools/index.tsx | 1 -
.../model-provider-page/model-modal/Form.tsx | 3 -
.../multiple-tool-selector/index.spec.tsx | 33 ++++-----
.../multiple-tool-selector/index.tsx | 8 +--
.../tool-selector/index.tsx | 4 --
.../tool-selector/tool-item.tsx | 10 ++-
.../workflow/block-selector/all-tools.tsx | 4 --
.../block-selector/featured-tools.tsx | 3 -
.../workflow/block-selector/tabs.tsx | 1 -
.../workflow/block-selector/tool-picker.tsx | 3 -
.../tool/tool-list-flat-view/list.tsx | 3 -
.../tool/tool-list-tree-view/item.tsx | 3 -
.../tool/tool-list-tree-view/list.tsx | 3 -
.../workflow/block-selector/tool/tool.tsx | 6 +-
.../workflow/block-selector/tools.tsx | 4 --
.../components/agent-strategy-selector.tsx | 4 +-
.../nodes/_base/components/agent-strategy.tsx | 8 +--
.../components/mcp-tool-availability.tsx | 38 +++++++++++
.../components/workflow/nodes/agent/panel.tsx | 67 ++++++++++---------
.../workflow/nodes/agent/use-config.ts | 2 -
20 files changed, 100 insertions(+), 108 deletions(-)
create mode 100644 web/app/components/workflow/nodes/_base/components/mcp-tool-availability.tsx
diff --git a/web/app/components/app/configuration/config/agent/agent-tools/index.tsx b/web/app/components/app/configuration/config/agent/agent-tools/index.tsx
index 02179822c9..7139ba66e0 100644
--- a/web/app/components/app/configuration/config/agent/agent-tools/index.tsx
+++ b/web/app/components/app/configuration/config/agent/agent-tools/index.tsx
@@ -183,7 +183,6 @@ const AgentTools: FC = () => {
onSelect={handleSelectTool}
onSelectMultiple={handleSelectMultipleTool}
selectedTools={tools as unknown as ToolValue[]}
- canChooseMCPTool
/>
>
)}
diff --git a/web/app/components/header/account-setting/model-provider-page/model-modal/Form.tsx b/web/app/components/header/account-setting/model-provider-page/model-modal/Form.tsx
index 5d0a1e5dd1..2927abe549 100644
--- a/web/app/components/header/account-setting/model-provider-page/model-modal/Form.tsx
+++ b/web/app/components/header/account-setting/model-provider-page/model-modal/Form.tsx
@@ -55,7 +55,6 @@ type FormProps<
nodeId?: string
nodeOutputVars?: NodeOutPutVar[]
availableNodes?: Node[]
- canChooseMCPTool?: boolean
}
function Form<
@@ -81,7 +80,6 @@ function Form<
nodeId,
nodeOutputVars,
availableNodes,
- canChooseMCPTool,
}: FormProps) {
const language = useLanguage()
const [changeKey, setChangeKey] = useState('')
@@ -407,7 +405,6 @@ function Form<
value={value[variable] || []}
onChange={item => handleFormChange(variable, item as any)}
supportCollapse
- canChooseMCPTool={canChooseMCPTool}
/>
{fieldMoreInfo?.(formSchema)}
{validating && changeKey === variable && }
diff --git a/web/app/components/plugins/plugin-detail-panel/multiple-tool-selector/index.spec.tsx b/web/app/components/plugins/plugin-detail-panel/multiple-tool-selector/index.spec.tsx
index 658c40c13c..288289b64d 100644
--- a/web/app/components/plugins/plugin-detail-panel/multiple-tool-selector/index.spec.tsx
+++ b/web/app/components/plugins/plugin-detail-panel/multiple-tool-selector/index.spec.tsx
@@ -7,6 +7,7 @@ import { beforeEach, describe, expect, it, vi } from 'vitest'
// ==================== Imports (after mocks) ====================
+import { MCPToolAvailabilityProvider } from '@/app/components/workflow/nodes/_base/components/mcp-tool-availability'
import MultipleToolSelector from './index'
// ==================== Mock Setup ====================
@@ -190,10 +191,11 @@ type RenderOptions = {
nodeOutputVars?: NodeOutPutVar[]
availableNodes?: Node[]
nodeId?: string
- canChooseMCPTool?: boolean
+ versionSupported?: boolean
}
const renderComponent = (options: RenderOptions = {}) => {
+ const { versionSupported, ...overrides } = options
const defaultProps = {
disabled: false,
value: [],
@@ -206,16 +208,17 @@ const renderComponent = (options: RenderOptions = {}) => {
nodeOutputVars: [createNodeOutputVar()],
availableNodes: [createNode()],
nodeId: 'test-node-id',
- canChooseMCPTool: false,
}
- const props = { ...defaultProps, ...options }
+ const props = { ...defaultProps, ...overrides }
const queryClient = createQueryClient()
return {
...render(
-
+
+
+
,
),
props,
@@ -410,7 +413,7 @@ describe('MultipleToolSelector', () => {
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
const mcpTools = [createMCPTool({ id: 'mcp-provider' })]
mockMCPToolsData.mockReturnValue(mcpTools)
@@ -421,13 +424,13 @@ describe('MultipleToolSelector', () => {
]
// Act
- renderComponent({ value: tools, canChooseMCPTool: true })
+ renderComponent({ value: tools, versionSupported: true })
// Assert
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
const mcpTools = [createMCPTool({ id: 'mcp-provider' })]
mockMCPToolsData.mockReturnValue(mcpTools)
@@ -438,7 +441,7 @@ describe('MultipleToolSelector', () => {
]
// Act
- renderComponent({ value: tools, canChooseMCPTool: false })
+ renderComponent({ value: tools, versionSupported: false })
// Assert
expect(screen.getByText('1/2')).toBeInTheDocument()
@@ -721,14 +724,6 @@ describe('MultipleToolSelector', () => {
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', () => {
// Arrange
const tools = [createToolValue()]
@@ -771,13 +766,13 @@ describe('MultipleToolSelector', () => {
]
// Act
- renderComponent({ value: tools, canChooseMCPTool: true })
+ renderComponent({ value: tools, versionSupported: true })
// Assert
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
const mcpTools = [createMCPTool({ id: 'mcp-provider' })]
mockMCPToolsData.mockReturnValue(mcpTools)
@@ -788,7 +783,7 @@ describe('MultipleToolSelector', () => {
]
// Act
- renderComponent({ value: tools, canChooseMCPTool: false })
+ renderComponent({ value: tools, versionSupported: false })
// Assert - Only regular tool should be counted
expect(screen.getByText('1/2')).toBeInTheDocument()
diff --git a/web/app/components/plugins/plugin-detail-panel/multiple-tool-selector/index.tsx b/web/app/components/plugins/plugin-detail-panel/multiple-tool-selector/index.tsx
index 4a7a6bdc6d..27efea8e08 100644
--- a/web/app/components/plugins/plugin-detail-panel/multiple-tool-selector/index.tsx
+++ b/web/app/components/plugins/plugin-detail-panel/multiple-tool-selector/index.tsx
@@ -12,6 +12,7 @@ import Divider from '@/app/components/base/divider'
import { ArrowDownRoundFill } from '@/app/components/base/icons/src/vender/solid/general'
import Tooltip from '@/app/components/base/tooltip'
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 { cn } from '@/utils/classnames'
@@ -27,7 +28,6 @@ type Props = {
nodeOutputVars: NodeOutPutVar[]
availableNodes: Node[]
nodeId?: string
- canChooseMCPTool?: boolean
}
const MultipleToolSelector = ({
@@ -42,14 +42,14 @@ const MultipleToolSelector = ({
nodeOutputVars,
availableNodes,
nodeId,
- canChooseMCPTool,
}: Props) => {
const { t } = useTranslation()
+ const { allowed: isMCPToolAllowed } = useMCPToolAvailability()
const { data: mcpTools } = useAllMCPTools()
const enabledCount = value.filter((item) => {
const isMCPTool = mcpTools?.find(tool => tool.id === item.provider_name)
if (isMCPTool)
- return item.enabled && canChooseMCPTool
+ return item.enabled && isMCPToolAllowed
return item.enabled
}).length
// collapse control
@@ -167,7 +167,6 @@ const MultipleToolSelector = ({
onSelectMultiple={handleAddMultiple}
onDelete={() => handleDelete(index)}
supportEnableSwitch
- canChooseMCPTool={canChooseMCPTool}
isEdit
/>
@@ -190,7 +189,6 @@ const MultipleToolSelector = ({
panelShowState={panelShowState}
onPanelShowStateChange={setPanelShowState}
isEdit={false}
- canChooseMCPTool={canChooseMCPTool}
onSelectMultiple={handleAddMultiple}
/>
>
diff --git a/web/app/components/plugins/plugin-detail-panel/tool-selector/index.tsx b/web/app/components/plugins/plugin-detail-panel/tool-selector/index.tsx
index 62c6fa3cd3..6c2c81a916 100644
--- a/web/app/components/plugins/plugin-detail-panel/tool-selector/index.tsx
+++ b/web/app/components/plugins/plugin-detail-panel/tool-selector/index.tsx
@@ -64,7 +64,6 @@ type Props = {
nodeOutputVars: NodeOutPutVar[]
availableNodes: Node[]
nodeId?: string
- canChooseMCPTool?: boolean
}
const ToolSelector: FC = ({
value,
@@ -86,7 +85,6 @@ const ToolSelector: FC = ({
nodeOutputVars,
availableNodes,
nodeId = '',
- canChooseMCPTool,
}) => {
const { t } = useTranslation()
const [isShow, onShowChange] = useState(false)
@@ -267,7 +265,6 @@ const ToolSelector: FC = ({
)}
- canChooseMCPTool={canChooseMCPTool}
/>
)}
@@ -300,7 +297,6 @@ const ToolSelector: FC = ({
onSelectMultiple={handleSelectMultipleTool}
scope={scope}
selectedTools={selectedTools}
- canChooseMCPTool={canChooseMCPTool}
/>
diff --git a/web/app/components/plugins/plugin-detail-panel/tool-selector/tool-item.tsx b/web/app/components/plugins/plugin-detail-panel/tool-selector/tool-item.tsx
index 896697c00d..995175c5ea 100644
--- a/web/app/components/plugins/plugin-detail-panel/tool-selector/tool-item.tsx
+++ b/web/app/components/plugins/plugin-detail-panel/tool-selector/tool-item.tsx
@@ -16,6 +16,7 @@ import Tooltip from '@/app/components/base/tooltip'
import { ToolTipContent } from '@/app/components/base/tooltip/content'
import Indicator from '@/app/components/header/indicator'
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 { SwitchPluginVersion } from '@/app/components/workflow/nodes/_base/components/switch-plugin-version'
import { cn } from '@/utils/classnames'
@@ -39,7 +40,6 @@ type Props = {
versionMismatch?: boolean
open: boolean
authRemoved?: boolean
- canChooseMCPTool?: boolean
}
const ToolItem = ({
@@ -61,13 +61,13 @@ const ToolItem = ({
errorTip,
versionMismatch,
authRemoved,
- canChooseMCPTool,
}: Props) => {
const { t } = useTranslation()
+ const { allowed: isMCPToolAllowed } = useMCPToolAvailability()
const providerNameText = isMCPTool ? providerShowName : providerName?.split('/').pop()
const isTransparent = uninstalled || versionMismatch || isError
const [isDeleting, setIsDeleting] = useState(false)
- const isShowCanNotChooseMCPTip = isMCPTool && !canChooseMCPTool
+ const isShowCanNotChooseMCPTip = isMCPTool && !isMCPToolAllowed
return (
)}
- {isShowCanNotChooseMCPTip && (
-
- )}
+ {isShowCanNotChooseMCPTip && }
{!isError && !uninstalled && !versionMismatch && noAuth && (