danny-avila/LibreChat · error · Error

Forbidden: Insufficient MCP server permissions

Error message

Forbidden: Insufficient MCP server permissions

What it means

The MCP tool _call wrapper throws this at MCP.js:1058 when canUseMCP returns false — i.e. the effective user lacks the USE permission on PermissionTypes.MCP_SERVERS. The check goes through userCanUseMCPServers (role-based, request-cached) or an injected mcpPermissionContext; any thrown error there also returns false, so a broken permission check fails closed.

Source

Thrown at api/server/services/MCP.js:1058

      },
      required: [],
    };
  }

  const normalizedToolKey = `${toolName}${Constants.mcp_delimiter}${normalizeServerName(serverName)}`;

  /** @type {(toolArguments: Object | string, config?: GraphRunnableConfig) => Promise<unknown>} */
  const _call = async (toolArguments, config) => {
    const effectiveUser = config?.configurable?.user ?? capturedUser;
    const permissionUser = effectiveUser;
    const userId = effectiveUser?.id || config?.configurable?.user_id || capturedUser?.id;
    try {
      const provider = (config?.metadata?.provider || capturedProvider)?.toLowerCase();
      const canUseMCP = mcpPermissionContext
        ? await mcpPermissionContext.canUseServers(permissionUser)
        : await userCanUseMCPServers(permissionUser);
      if (!canUseMCP) {
        throw new Error('Forbidden: Insufficient MCP server permissions');
      }
      const flowsCache = getLogStores(CacheKeys.FLOWS);
      const flowManager = getFlowStateManager(flowsCache);
      const derivedSignal = config?.signal ? AbortSignal.any([config.signal]) : undefined;
      const mcpManager = getMCPManager(userId);

      const { args: _args, stepId, ...toolCall } = config.toolCall ?? {};
      const flowId = `${serverName}:oauth_login:${config.metadata.thread_id}:${config.metadata.run_id}`;
      const runStepDeltaEmitter = createRunStepDeltaEmitter({
        res,
        stepId,
        toolCall,
        streamId,
        jobCreatedAt,
      });
      const oauthStart = createOAuthStart({
        flowId,
        flowManager,

View on GitHub (pinned to 5ff282f900)

Solutions

  1. Grant the user's role the USE permission for MCP_SERVERS via the admin role/permission UI.
  2. Confirm req.user.role and req.user.id are populated by the auth middleware for this session.
  3. Check server logs for 'Failed MCP permission check' — if present, the permission subsystem itself errored and needs fixing, not just the role grant.
Defensive patterns

Strategy: try-catch

Validate before calling

async function ensureCanUseMCP(user) {
  if (!user?.id || !user?.role) throw new Error('authenticated user with role required');
  const ok = await userCanUseMCPServers(user);
  if (!ok) throw new Error('role lacks MCP_SERVERS:USE');
}

Type guard

const isAuthedUser = (u) => !!u?.id && !!u?.role;

Try / catch

try {
  await callMcpTool(...);
} catch (e) {
  if (e.message === 'Forbidden: Insufficient MCP server permissions') {
    return showUpgradeOrRolePrompt();
  }
  throw e;
}

Prevention

When it happens

Trigger: A user whose role does not grant USE on MCP_SERVERS invokes any MCP tool. Also fires if user.id or user.role is missing on the session, or if checkAccessWithRequestCache throws (caught and mapped to false).

Common situations: Role policy was tightened and MCP use was not granted to the user's role. A new SSO/role mapping leaves user.role unset. A custom role was created without the MCP capability. Test fixtures that omit role.

Understand the failure class

Related errors


AI-assisted analysis of danny-avila/LibreChat@5ff282f900 (2026-08-12). Data as JSON: /api/errors/e1b1631d9247c7a6. Report an issue: GitHub.