{"record":{"id":"d559c96cee222db7","repo":"FlowiseAI/Flowise","slug":"invalid-mcp-server-config-error","errorCode":null,"errorMessage":"Invalid MCP Server Config: ${error}","messagePattern":"Invalid MCP Server Config: (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/components/nodes/tools/MCP/CustomMCP/CustomMCP.ts","lineNumber":199,"sourceCode":"                }\n            }\n\n            // Compatible with stdio and SSE\n            let toolkit: MCPToolkit\n            if (process.env.CUSTOM_MCP_PROTOCOL === 'stdio' && serverParams!.command) toolkit = new MCPToolkit(serverParams, 'stdio')\n            else toolkit = new MCPToolkit(serverParams, 'sse')\n\n            await toolkit.initialize()\n\n            const tools = toolkit.tools ?? []\n\n            if (options.cachePool) {\n                await options.cachePool.addMCPCache(cacheKey, { toolkit, tools })\n            }\n\n            return tools as Tool[]\n        } catch (error) {\n            throw new Error(`Invalid MCP Server Config: ${error}`)\n        }\n    }\n}\n\nfunction substituteVariablesInObject(obj: any, sandbox: any): any {\n    if (typeof obj === 'string') {\n        // Replace variables in string values\n        return substituteVariablesInString(obj, sandbox)\n    } else if (Array.isArray(obj)) {\n        // Recursively process arrays\n        return obj.map((item) => substituteVariablesInObject(item, sandbox))\n    } else if (obj !== null && typeof obj === 'object') {\n        // Recursively process object properties\n        const result: any = {}\n        for (const [key, value] of Object.entries(obj)) {\n            result[key] = substituteVariablesInObject(value, sandbox)\n        }\n        return result","sourceCodeStart":181,"sourceCodeEnd":217,"githubUrl":"https://github.com/FlowiseAI/Flowise/blob/abe4a8601a058047b350c260676826e21dd14101/packages/components/nodes/tools/MCP/CustomMCP/CustomMCP.ts#L181-L217","documentation":"Thrown by the outer catch in CustomMCP.getTools for ANY error during server-param substitution, security validation, MCPToolkit construction, or toolkit.initialize() that wasn't already re-thrown as a more specific error (392, 393). It is a catch-all wrapper that prefixes the underlying error. Common underlying causes: JSON.parse failure on the substituted config string (convertToValidJSONString couldn't repair it), toolkit.initialize() network/DNS failure, or a security-check re-throw nesting.","triggerScenarios":"The substituted mcpServerConfig is not valid JSON (e.g. unbalanced braces after variable injection); the MCPToolkit fails to connect to the SSE url (DNS, TLS, 401); a stdio command's binary is not found; or error 393 propagates through this catch (producing a doubly-wrapped message). The wrapper obscures the root cause, so the inner message must be read.","commonSituations":"Variable substitution injecting a value with unescaped quotes breaking JSON; MCP server URL wrong/offline; stdio command not on PATH; env vars missing for the stdio process; cascading from a security-validation failure.","solutions":["Read the inner error string after 'Invalid MCP Server Config:' — it is the real cause (parse position, connect ECONNREFUSED, spawn ENOENT, etc.).","If the inner error is 'Security validation failed', apply the 393 fix.","If JSON parse, validate the substituted config string with JSON.parse in isolation and fix the variable value.","If connect/spawn, verify the URL is reachable / the command binary exists before the tool call.","Test the config in a plain MCPToolkit.initialize() call outside Flowise to isolate."],"exampleFix":"// before — variable injection broke JSON\nmcpServerConfig: '{\"url\":\"{{$vars.endpoint}}\"}' with $vars.endpoint = 'https://x\".com'\n// after — sanitize variable values\n$vars.endpoint = 'https://x.com'","handlingStrategy":"try-catch","validationCode":"function preflightMcpConfig(raw: string, sandbox: any) {\n  const subbed = substituteVariablesInString(raw, sandbox)\n  const repaired = convertToValidJSONString(subbed)\n  let obj: any\n  try { obj = JSON.parse(repaired) } catch (e) { throw new Error(`config won't parse: ${(e as Error).message}`) }\n  if (process.env.CUSTOM_MCP_SECURITY_CHECK !== 'false') validateMCPServerConfig(obj)\n  return obj\n}","typeGuard":null,"tryCatchPattern":"try {\n  return await customMcp.getTools(nodeData, options)\n} catch (e) {\n  // strip the wrapper to get the inner cause\n  const inner = e instanceof Error ? e.message.replace(/^Invalid MCP Server Config: /, '') : String(e)\n  if (/Security validation/.test(inner)) return fixConfigAndRetry()\n  if (/ECONNREFUSED|ENOTFOUND|spawn ENOENT/.test(inner)) return reportUnreachable(inner)\n  throw e\n}","preventionTips":["Read the inner cause after the wrapper prefix — that's the real failure.","Sanitize $vars values to avoid quote-breaking the JSON.","Test the config with a plain MCPToolkit.initialize() in isolation.","Confirm the command binary exists (stdio) or the URL is reachable (SSE)."],"tags":["mcp","custom-mcp","json-parse","network","wrapper"],"backgroundTag":null,"analyzedSha":"abe4a8601a058047b350c260676826e21dd14101","analyzedAt":"2026-08-12T16:04:40.823Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}