FlowiseAI/Flowise · error · Error

Workspace context is required to load MCP server

Error message

Workspace context is required to load MCP server

What it means

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.

Solutions

  1. Ensure the runtime thread passes workspaceId into options (the UI flow does this automatically).
  2. For programmatic calls, set options.workspaceId (or options.searchOptions.workspaceId._value) to the target workspace UUID.
  3. Confirm the user/session belongs to the workspace before setting it.
  4. Do not bypass this check — it enforces tenant isolation.

Example fix

// before
await tool.getTools(nodeData, { appDataSource, databaseEntities })
// after
await tool.getTools(nodeData, { appDataSource, databaseEntities, workspaceId: 'ws-uuid' })
Defensive patterns

Strategy: validation

Validate before calling

function resolveWorkspaceId(options: ICommonObject): string {
  const id = (options.workspaceId as string | undefined) ?? (options.searchOptions as any)?.workspaceId?._value
  if (!id) throw new Error('workspaceId missing — pass it in options for tenant scoping')
  return id
}

Type guard

function hasWorkspaceContext(o: unknown): o is { workspaceId: string } | { searchOptions: { workspaceId: { _value: string } } } {
  if (!o) return false
  if (typeof (o as any).workspaceId === 'string' && (o as any).workspaceId) return true
  const sw = (o as any).searchOptions?.workspaceId?._value
  return typeof sw === 'string' && !!sw
}

Prevention

When it happens

Trigger: 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.

Common situations: API/programmatic invocation without workspace context; multi-tenant isolation bug; a custom runner script that omits workspaceId.

Related errors


AI-assisted analysis of FlowiseAI/Flowise@abe4a8601a (2026-08-12). Data as JSON: /api/errors/39352f2a4d821df1. Report an issue: GitHub.

Appendix: source

Thrown at packages/components/nodes/tools/MCP/CustomMcpServerTool/CustomMcpServerTool.ts:134

    }

    async getTools(nodeData: INodeData, options: ICommonObject): Promise<Tool[]> {
        const serverId = nodeData.inputs?.mcpServerId as string
        if (!serverId) {
            throw new Error('MCP Server is required')
        }

        const appDataSource = options.appDataSource as DataSource
        const databaseEntities = options.databaseEntities as IDatabaseEntity
        if (!appDataSource || !databaseEntities?.['CustomMcpServer']) {
            throw new Error('Database not available')
        }

        const workspaceId =
            (options.workspaceId as string | undefined) ??
            ((options.searchOptions as ICommonObject | undefined)?.workspaceId as string | undefined)
        if (!workspaceId) {
            throw new Error('Workspace context is required to load MCP server')
        }

        const serverRecord = await appDataSource.getRepository(databaseEntities['CustomMcpServer']).findOneBy({ id: serverId, workspaceId })
        if (!serverRecord) {
            throw new Error(`MCP server ${serverId} not found`)
        }
        if (serverRecord.status !== 'AUTHORIZED') {
            throw new Error(`MCP server "${serverRecord.name}" is not authorized. Please authorize it in the Tools page first.`)
        }

        // Build headers from encrypted authConfig — only when authType explicitly requires them
        let headers: Record<string, string> = {}
        if (serverRecord.authType === 'CUSTOM_HEADERS' && serverRecord.authConfig) {
            try {
                const decrypted = await decryptCredentialData(serverRecord.authConfig)
                if (decrypted?.headers && typeof decrypted.headers === 'object') {
                    headers = decrypted.headers as Record<string, string>
                }

View on GitHub (pinned to abe4a8601a)