{"record":{"id":"39352f2a4d821df1","repo":"FlowiseAI/Flowise","slug":"workspace-context-is-required-to-load-mcp-server","errorCode":null,"errorMessage":"Workspace context is required to load MCP server","messagePattern":"Workspace context is required to load MCP server","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/components/nodes/tools/MCP/CustomMcpServerTool/CustomMcpServerTool.ts","lineNumber":134,"sourceCode":"    }\n\n    async getTools(nodeData: INodeData, options: ICommonObject): Promise<Tool[]> {\n        const serverId = nodeData.inputs?.mcpServerId as string\n        if (!serverId) {\n            throw new Error('MCP Server is required')\n        }\n\n        const appDataSource = options.appDataSource as DataSource\n        const databaseEntities = options.databaseEntities as IDatabaseEntity\n        if (!appDataSource || !databaseEntities?.['CustomMcpServer']) {\n            throw new Error('Database not available')\n        }\n\n        const workspaceId =\n            (options.workspaceId as string | undefined) ??\n            ((options.searchOptions as ICommonObject | undefined)?.workspaceId as string | undefined)\n        if (!workspaceId) {\n            throw new Error('Workspace context is required to load MCP server')\n        }\n\n        const serverRecord = await appDataSource.getRepository(databaseEntities['CustomMcpServer']).findOneBy({ id: serverId, workspaceId })\n        if (!serverRecord) {\n            throw new Error(`MCP server ${serverId} not found`)\n        }\n        if (serverRecord.status !== 'AUTHORIZED') {\n            throw new Error(`MCP server \"${serverRecord.name}\" is not authorized. Please authorize it in the Tools page first.`)\n        }\n\n        // Build headers from encrypted authConfig — only when authType explicitly requires them\n        let headers: Record<string, string> = {}\n        if (serverRecord.authType === 'CUSTOM_HEADERS' && serverRecord.authConfig) {\n            try {\n                const decrypted = await decryptCredentialData(serverRecord.authConfig)\n                if (decrypted?.headers && typeof decrypted.headers === 'object') {\n                    headers = decrypted.headers as Record<string, string>\n                }","sourceCodeStart":116,"sourceCodeEnd":152,"githubUrl":"https://github.com/FlowiseAI/Flowise/blob/abe4a8601a058047b350c260676826e21dd14101/packages/components/nodes/tools/MCP/CustomMcpServerTool/CustomMcpServerTool.ts#L116-L152","documentation":"Thrown by CustomMcpServerTool.getTools when neither options.workspaceId nor options.searchOptions.workspaceId._value is set. Managed MCP servers are workspace-scoped (the findOneBy queries { id, workspaceId }), so a missing workspaceId is a hard precondition — without it the DB lookup would cross workspace boundaries. The check is explicit before the repository query.","triggerScenarios":"Running the tool in a context that lacks workspace context (CLI, test, or a scheduled job that didn't propagate workspaceId); a session/REST call missing the workspace header; a bug in the runtime that fails to thread workspaceId into options.","commonSituations":"API/programmatic invocation without workspace context; multi-tenant isolation bug; a custom runner script that omits workspaceId.","solutions":["Ensure the runtime thread passes workspaceId into options (the UI flow does this automatically).","For programmatic calls, set options.workspaceId (or options.searchOptions.workspaceId._value) to the target workspace UUID.","Confirm the user/session belongs to the workspace before setting it.","Do not bypass this check — it enforces tenant isolation."],"exampleFix":"// before\nawait tool.getTools(nodeData, { appDataSource, databaseEntities })\n// after\nawait tool.getTools(nodeData, { appDataSource, databaseEntities, workspaceId: 'ws-uuid' })","handlingStrategy":"validation","validationCode":"function resolveWorkspaceId(options: ICommonObject): string {\n  const id = (options.workspaceId as string | undefined) ?? (options.searchOptions as any)?.workspaceId?._value\n  if (!id) throw new Error('workspaceId missing — pass it in options for tenant scoping')\n  return id\n}","typeGuard":"function hasWorkspaceContext(o: unknown): o is { workspaceId: string } | { searchOptions: { workspaceId: { _value: string } } } {\n  if (!o) return false\n  if (typeof (o as any).workspaceId === 'string' && (o as any).workspaceId) return true\n  const sw = (o as any).searchOptions?.workspaceId?._value\n  return typeof sw === 'string' && !!sw\n}","tryCatchPattern":null,"preventionTips":["Always thread workspaceId through options in programmatic calls.","Treat this check as a tenant-isolation boundary — don't bypass it.","Confirm the session/user belongs to the workspace before setting it.","For CLI/scheduled jobs, set workspaceId explicitly."],"tags":["mcp","multi-tenant","workspace","validation","security"],"backgroundTag":null,"analyzedSha":"abe4a8601a058047b350c260676826e21dd14101","analyzedAt":"2026-08-12T16:04:40.823Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}