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
This commit is contained in:
yyh
2026-02-23 22:46:31 +08:00
parent 2dc86363e3
commit 2e0661aa90
11 changed files with 101 additions and 14 deletions

View File

@@ -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 ====================

View File

@@ -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'

View File

@@ -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 }),
}))

View File

@@ -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'

View File

@@ -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'

View File

@@ -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 }) => (
<MCPToolAvailabilityProvider>
{children}
</MCPToolAvailabilityProvider>
)
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 }) => (
<MCPToolAvailabilityProvider versionSupported={false}>
{children}
</MCPToolAvailabilityProvider>
)
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 }) => (
<MCPToolAvailabilityProvider versionSupported={true}>
{children}
</MCPToolAvailabilityProvider>
)
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 }) => (
<MCPToolAvailabilityProvider sandboxEnabled={false}>
{children}
</MCPToolAvailabilityProvider>
)
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 }) => (
<MCPToolAvailabilityProvider sandboxEnabled={false}>
<MCPToolAvailabilityProvider versionSupported={true}>
{children}
</MCPToolAvailabilityProvider>
</MCPToolAvailabilityProvider>
)
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 }) => (
<MCPToolAvailabilityProvider sandboxEnabled={false}>
<MCPToolAvailabilityProvider versionSupported={true} sandboxEnabled={true}>
{children}
</MCPToolAvailabilityProvider>
</MCPToolAvailabilityProvider>
)
const { result } = renderHook(() => useMCPToolAvailability(), { wrapper })
expect(result.current).toEqual({ allowed: true })
})
})

View File

@@ -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 }
}

View File

@@ -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'

View File

@@ -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()

View File

@@ -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'

View File

@@ -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