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
- Grant the user's role the USE permission for MCP_SERVERS via the admin role/permission UI.
- Confirm req.user.role and req.user.id are populated by the auth middleware for this session.
- 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
- Grant USE on MCP_SERVERS to roles that need MCP tools.
- Ensure auth middleware always populates user.id and user.role.
- Monitor logs for 'Failed MCP permission check' to catch permission-subsystem failures.
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
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
- Invalid role ID: ${principalId}
- Role ${accessRoleId} not found
- Role ${accessRoleId} is for ${role.resourceType} resources,
- Invalid role ID: ${principalId}
- Role ${accessRoleId} not found
AI-assisted analysis of danny-avila/LibreChat@5ff282f900 (2026-08-12).
Data as JSON: /api/errors/e1b1631d9247c7a6.
Report an issue: GitHub.