Skip to content
37 changes: 37 additions & 0 deletions packages/components/nodes/tools/MCP/CustomMCP/CustomMCP.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,9 @@ class Custom_MCP implements INode {
credential: INodeParams
inputs: INodeParams[]

// This cache is shared across all nodes with the SAME logical configuration.
static toolkitCache: Map<string, { toolkit: MCPToolkit; tools: Tool[] }> = new Map()

constructor() {
this.label = 'Custom MCP'
this.name = 'customMCP'
Expand Down Expand Up @@ -140,6 +143,25 @@ class Custom_MCP implements INode {
sandbox['$vars'] = prepareSandboxVars(variables)
}

// Get the workspaceId from the options object for multi-tenant isolation.
const workspaceId = options?.searchOptions?.workspaceId?._value

// Create a canonical cache key by parsing and re-stringifying the JSON config.
// This makes the cache immune to formatting changes (spaces, newlines, etc.).
let canonicalConfig
try {
// Try to parse the config to remove formatting.
canonicalConfig = JSON.parse(mcpServerConfig)
} catch (e) {
// If parsing fails (e.g., invalid JSON), use the raw string as a fallback.
canonicalConfig = mcpServerConfig
}
const cacheKey = JSON.stringify({ workspaceId, canonicalConfig, sandbox })
Comment thread
toi500 marked this conversation as resolved.

if (Custom_MCP.toolkitCache.has(cacheKey)) {
return Custom_MCP.toolkitCache.get(cacheKey)!.tools
}

try {
let serverParams
if (typeof mcpServerConfig === 'object') {
Expand All @@ -162,6 +184,21 @@ class Custom_MCP implements INode {

const tools = toolkit.tools ?? []

// After initializing a new toolkit, clear any stale actions
// from the previous configuration to prevent UI inconsistencies.
const availableToolNames = new Set(tools.map((t) => t.name))
const selectedActions = nodeData.inputs?.mcpActions
if (selectedActions && Array.isArray(selectedActions) && selectedActions.length > 0) {
const hasInvalidSelections = selectedActions.some((action) => !availableToolNames.has(action))
if (hasInvalidSelections) {
if (nodeData.inputs) {
nodeData.inputs.mcpActions = []
}
}
}

Custom_MCP.toolkitCache.set(cacheKey, { toolkit, tools })

return tools as Tool[]
} catch (error) {
throw new Error(`Invalid MCP Server Config: ${error}`)
Expand Down