From 2e0661aa9035673dd242c9559efd191c3fa2fa53 Mon Sep 17 00:00:00 2001 From: yyh Date: Mon, 23 Feb 2026 22:46:31 +0800 Subject: [PATCH] refactor(web): align MCP availability context migration - move MCP availability context to block-selector/context and update imports - preserve sandbox gating, parent-provider inheritance, and blockedBy semantics - add context tests on top of refactor baseline cases - regenerate and prune eslint suppressions --- .../__tests__/index.spec.tsx | 2 +- .../multiple-tool-selector/index.tsx | 2 +- .../tool-selector/__tests__/index.spec.tsx | 2 +- .../tool-selector/components/tool-item.tsx | 2 +- .../workflow-app/components/workflow-main.tsx | 2 +- .../mcp-tool-availability-context.spec.tsx | 88 +++++++++++++++++++ .../mcp-tool-availability-context.tsx} | 6 +- .../workflow/block-selector/tool/tool.tsx | 2 +- .../mcp-tool-not-support-tooltip.tsx | 2 +- .../components/workflow/nodes/agent/panel.tsx | 2 +- web/eslint-suppressions.json | 5 -- 11 files changed, 101 insertions(+), 14 deletions(-) create mode 100644 web/app/components/workflow/block-selector/context/__tests__/mcp-tool-availability-context.spec.tsx rename web/app/components/workflow/{nodes/_base/components/mcp-tool-availability.tsx => block-selector/context/mcp-tool-availability-context.tsx} (91%) diff --git a/web/app/components/plugins/plugin-detail-panel/multiple-tool-selector/__tests__/index.spec.tsx b/web/app/components/plugins/plugin-detail-panel/multiple-tool-selector/__tests__/index.spec.tsx index 7460f0834b..7ce53a60d4 100644 --- a/web/app/components/plugins/plugin-detail-panel/multiple-tool-selector/__tests__/index.spec.tsx +++ b/web/app/components/plugins/plugin-detail-panel/multiple-tool-selector/__tests__/index.spec.tsx @@ -7,7 +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 { MCPToolAvailabilityProvider } from '@/app/components/workflow/block-selector/context/mcp-tool-availability-context' import MultipleToolSelector from '../index' // ==================== Mock Setup ==================== 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 274f76b17b..556ebc99cf 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,7 +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 { useMCPToolAvailability } from '@/app/components/workflow/block-selector/context/mcp-tool-availability-context' import { useAllMCPTools } from '@/service/use-tools' import { cn } from '@/utils/classnames' diff --git a/web/app/components/plugins/plugin-detail-panel/tool-selector/__tests__/index.spec.tsx b/web/app/components/plugins/plugin-detail-panel/tool-selector/__tests__/index.spec.tsx index 26e4de0fd7..06fe61865d 100644 --- a/web/app/components/plugins/plugin-detail-panel/tool-selector/__tests__/index.spec.tsx +++ b/web/app/components/plugins/plugin-detail-panel/tool-selector/__tests__/index.spec.tsx @@ -208,7 +208,7 @@ vi.mock('../components/reasoning-config-form', () => ({ // Track MCP availability mock state let mockMCPToolAllowed = true -vi.mock('@/app/components/workflow/nodes/_base/components/mcp-tool-availability', () => ({ +vi.mock('@/app/components/workflow/block-selector/context/mcp-tool-availability-context', () => ({ useMCPToolAvailability: () => ({ allowed: mockMCPToolAllowed }), })) diff --git a/web/app/components/plugins/plugin-detail-panel/tool-selector/components/tool-item.tsx b/web/app/components/plugins/plugin-detail-panel/tool-selector/components/tool-item.tsx index 7fac245f09..cd5c40a720 100644 --- a/web/app/components/plugins/plugin-detail-panel/tool-selector/components/tool-item.tsx +++ b/web/app/components/plugins/plugin-detail-panel/tool-selector/components/tool-item.tsx @@ -15,8 +15,8 @@ import Switch from '@/app/components/base/switch' import Tooltip from '@/app/components/base/tooltip' import { ToolTipContent } from '@/app/components/base/tooltip/content' import Indicator from '@/app/components/header/indicator' +import { useMCPToolAvailability } from '@/app/components/workflow/block-selector/context/mcp-tool-availability-context' 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' diff --git a/web/app/components/workflow-app/components/workflow-main.tsx b/web/app/components/workflow-app/components/workflow-main.tsx index f64aaa09b5..9b7752ebf8 100644 --- a/web/app/components/workflow-app/components/workflow-main.tsx +++ b/web/app/components/workflow-app/components/workflow-main.tsx @@ -15,9 +15,9 @@ import { useReactFlow } from 'reactflow' import { useFeatures, useFeaturesStore } from '@/app/components/base/features/hooks' import { FILE_EXTS } from '@/app/components/base/prompt-editor/constants' import { WorkflowWithInnerContext } from '@/app/components/workflow' +import { MCPToolAvailabilityProvider } from '@/app/components/workflow/block-selector/context/mcp-tool-availability-context' import { collaborationManager, useCollaboration } from '@/app/components/workflow/collaboration' import { useWorkflowUpdate } from '@/app/components/workflow/hooks/use-workflow-interactions' -import { MCPToolAvailabilityProvider } from '@/app/components/workflow/nodes/_base/components/mcp-tool-availability' import { useStore, useWorkflowStore } from '@/app/components/workflow/store' import { SupportUploadFileTypes } from '@/app/components/workflow/types' import { fetchWorkflowDraft } from '@/service/workflow' diff --git a/web/app/components/workflow/block-selector/context/__tests__/mcp-tool-availability-context.spec.tsx b/web/app/components/workflow/block-selector/context/__tests__/mcp-tool-availability-context.spec.tsx new file mode 100644 index 0000000000..64e05442ab --- /dev/null +++ b/web/app/components/workflow/block-selector/context/__tests__/mcp-tool-availability-context.spec.tsx @@ -0,0 +1,88 @@ +import type { ReactNode } from 'react' +import { renderHook } from '@testing-library/react' +import { describe, expect, it } from 'vitest' +import { MCPToolAvailabilityProvider, useMCPToolAvailability } from '../mcp-tool-availability-context' + +describe('useMCPToolAvailability', () => { + it('returns allowed=true without provider', () => { + const { result } = renderHook(() => useMCPToolAvailability()) + + expect(result.current).toEqual({ allowed: true }) + }) + + it('returns allowed=true when version is not provided to provider', () => { + const wrapper = ({ children }: { children: ReactNode }) => ( + + {children} + + ) + + const { result } = renderHook(() => useMCPToolAvailability(), { wrapper }) + + expect(result.current).toEqual({ allowed: true }) + }) + + it('returns allowed=false when version is not supported', () => { + const wrapper = ({ children }: { children: ReactNode }) => ( + + {children} + + ) + + const { result } = renderHook(() => useMCPToolAvailability(), { wrapper }) + + expect(result.current).toEqual({ allowed: false, blockedBy: 'version' }) + }) + + it('returns allowed=true when version is supported', () => { + const wrapper = ({ children }: { children: ReactNode }) => ( + + {children} + + ) + + const { result } = renderHook(() => useMCPToolAvailability(), { wrapper }) + + expect(result.current).toEqual({ allowed: true }) + }) + + it('returns allowed=false when sandbox is not enabled', () => { + const wrapper = ({ children }: { children: ReactNode }) => ( + + {children} + + ) + + const { result } = renderHook(() => useMCPToolAvailability(), { wrapper }) + + expect(result.current).toEqual({ allowed: false, blockedBy: 'sandbox' }) + }) + + it('inherits parent provider values when child omits them', () => { + const wrapper = ({ children }: { children: ReactNode }) => ( + + + {children} + + + ) + + const { result } = renderHook(() => useMCPToolAvailability(), { wrapper }) + + expect(result.current).toEqual({ allowed: false, blockedBy: 'sandbox' }) + }) + + it('allows access when child provider overrides parent sandbox value', () => { + const wrapper = ({ children }: { children: ReactNode }) => ( + + + {children} + + + ) + + const { result } = renderHook(() => useMCPToolAvailability(), { wrapper }) + + expect(result.current).toEqual({ allowed: true }) + }) +}) diff --git a/web/app/components/workflow/nodes/_base/components/mcp-tool-availability.tsx b/web/app/components/workflow/block-selector/context/mcp-tool-availability-context.tsx similarity index 91% rename from web/app/components/workflow/nodes/_base/components/mcp-tool-availability.tsx rename to web/app/components/workflow/block-selector/context/mcp-tool-availability-context.tsx index 1bda3d0606..dd01ba3475 100644 --- a/web/app/components/workflow/nodes/_base/components/mcp-tool-availability.tsx +++ b/web/app/components/workflow/block-selector/context/mcp-tool-availability-context.tsx @@ -39,6 +39,7 @@ export function MCPToolAvailabilityProvider({ ) } +// eslint-disable-next-line react-refresh/only-export-components export function useMCPToolAvailability(): MCPToolAvailability { const context = useContext(MCPToolAvailabilityContext) @@ -55,5 +56,8 @@ export function useMCPToolAvailability(): MCPToolAvailability { else if (!sandboxAllowed) blockedBy = 'sandbox' - return { allowed, blockedBy } + if (blockedBy) + return { allowed, blockedBy } + + return { allowed } } diff --git a/web/app/components/workflow/block-selector/tool/tool.tsx b/web/app/components/workflow/block-selector/tool/tool.tsx index dbe9da668b..9d7e193e16 100644 --- a/web/app/components/workflow/block-selector/tool/tool.tsx +++ b/web/app/components/workflow/block-selector/tool/tool.tsx @@ -9,7 +9,7 @@ import * as React from 'react' import { useCallback, useEffect, useMemo, useRef } from 'react' import { useTranslation } from 'react-i18next' import { Mcp } from '@/app/components/base/icons/src/vender/other' -import { useMCPToolAvailability } from '@/app/components/workflow/nodes/_base/components/mcp-tool-availability' +import { useMCPToolAvailability } from '@/app/components/workflow/block-selector/context/mcp-tool-availability-context' import { useGetLanguage } from '@/context/i18n' import useTheme from '@/hooks/use-theme' import { Theme } from '@/types/app' diff --git a/web/app/components/workflow/nodes/_base/components/mcp-tool-not-support-tooltip.tsx b/web/app/components/workflow/nodes/_base/components/mcp-tool-not-support-tooltip.tsx index 1c628c749e..7d57437d5b 100644 --- a/web/app/components/workflow/nodes/_base/components/mcp-tool-not-support-tooltip.tsx +++ b/web/app/components/workflow/nodes/_base/components/mcp-tool-not-support-tooltip.tsx @@ -4,7 +4,7 @@ import { RiAlertFill } from '@remixicon/react' import * as React from 'react' import { useTranslation } from 'react-i18next' import Tooltip from '@/app/components/base/tooltip' -import { useMCPToolAvailability } from './mcp-tool-availability' +import { useMCPToolAvailability } from '@/app/components/workflow/block-selector/context/mcp-tool-availability-context' const McpToolNotSupportTooltip: FC = () => { const { t } = useTranslation() diff --git a/web/app/components/workflow/nodes/agent/panel.tsx b/web/app/components/workflow/nodes/agent/panel.tsx index 14232bc639..b9800bdab6 100644 --- a/web/app/components/workflow/nodes/agent/panel.tsx +++ b/web/app/components/workflow/nodes/agent/panel.tsx @@ -6,11 +6,11 @@ import type { StrategyParamItem } from '@/app/components/plugins/types' import { memo } from 'react' import { useTranslation } from 'react-i18next' import { toType } from '@/app/components/tools/utils/to-form-schema' +import { MCPToolAvailabilityProvider } from '@/app/components/workflow/block-selector/context/mcp-tool-availability-context' import { isSupportMCP } from '@/utils/plugin-version-feature' import { useStore } from '../../store' import { AgentStrategy } from '../_base/components/agent-strategy' import Field from '../_base/components/field' -import { MCPToolAvailabilityProvider } from '../_base/components/mcp-tool-availability' import MemoryConfig from '../_base/components/memory-config' import OutputVars, { VarItem } from '../_base/components/output-vars' import Split from '../_base/components/split' diff --git a/web/eslint-suppressions.json b/web/eslint-suppressions.json index 46384cf7b4..7bbcc876b1 100644 --- a/web/eslint-suppressions.json +++ b/web/eslint-suppressions.json @@ -2970,11 +2970,6 @@ "count": 7 } }, - "app/components/workflow/nodes/_base/components/mcp-tool-availability.tsx": { - "react-refresh/only-export-components": { - "count": 1 - } - }, "app/components/workflow/nodes/_base/components/memory-config.tsx": { "unicorn/prefer-number-properties": { "count": 1