feat(ee): MCP OAuth (#2432)

* feat: mcp oauth

* fix: small refactor

* fix: cleanup consent
This commit is contained in:
Philip Okugbe
2026-08-26 12:55:30 +01:00
committed by GitHub
parent bda802e5df
commit e56de8eb6c
44 changed files with 1530 additions and 71 deletions
@@ -24,6 +24,7 @@ import {
RevokeInviteDto,
} from '../dto/invitation.dto';
import { JwtAuthGuard } from '../../../common/guards/jwt-auth.guard';
import { OAuthScope } from '../../../common/decorators/oauth-scope.decorator';
import { User, Workspace } from '@docmost/db/types/entity.types';
import WorkspaceAbilityFactory from '../../casl/abilities/workspace-ability.factory';
import {
@@ -58,6 +59,7 @@ export class WorkspaceController {
@HttpCode(HttpStatus.OK)
@Post('/info')
@OAuthScope('read')
async getWorkspace(@AuthWorkspace() workspace: Workspace) {
return this.workspaceService.getWorkspaceInfo(workspace.id);
}
@@ -113,6 +115,7 @@ export class WorkspaceController {
@HttpCode(HttpStatus.OK)
@Post('members')
@OAuthScope('read')
async getWorkspaceMembers(
@Body()
pagination: PaginationOptions,
@@ -76,4 +76,8 @@ export class UpdateWorkspaceDto extends PartialType(CreateWorkspaceDto) {
@IsOptional()
@IsBoolean()
aiChatWorkspaceKnowledgeOnly: boolean;
@IsOptional()
@IsBoolean()
mcpOauthOnly: boolean;
}
@@ -336,7 +336,8 @@ export class WorkspaceService {
typeof updateWorkspaceDto.isScimEnabled !== 'undefined' ||
typeof updateWorkspaceDto.allowPersonalSpaces !== 'undefined' ||
typeof updateWorkspaceDto.aiChatReadOnly !== 'undefined' ||
typeof updateWorkspaceDto.aiChatWorkspaceKnowledgeOnly !== 'undefined'
typeof updateWorkspaceDto.aiChatWorkspaceKnowledgeOnly !== 'undefined' ||
typeof updateWorkspaceDto.mcpOauthOnly !== 'undefined'
) {
const ws = await this.db
.selectFrom('workspaces')
@@ -391,6 +392,18 @@ export class WorkspaceService {
}
}
if (typeof updateWorkspaceDto.mcpOauthOnly !== 'undefined') {
if (
!this.licenseCheckService.hasFeature(
ws.licenseKey,
Feature.MCP_CONTROLS,
ws.plan,
)
) {
throw new ForbiddenException('This feature requires a valid license');
}
}
if (
typeof updateWorkspaceDto.disablePublicSharing !== 'undefined' ||
typeof updateWorkspaceDto.trashRetentionDays !== 'undefined' ||
@@ -561,6 +574,20 @@ export class WorkspaceService {
);
}
if (typeof updateWorkspaceDto.mcpOauthOnly !== 'undefined') {
const prev = settingsBefore?.ai?.mcpOauthOnly ?? false;
if (prev !== updateWorkspaceDto.mcpOauthOnly) {
before.mcpOauthOnly = prev;
after.mcpOauthOnly = updateWorkspaceDto.mcpOauthOnly;
}
await this.workspaceRepo.updateAiSettings(
workspaceId,
'mcpOauthOnly',
updateWorkspaceDto.mcpOauthOnly,
trx,
);
}
if (typeof updateWorkspaceDto.allowPersonalSpaces !== 'undefined') {
const prev = settingsBefore?.spaces?.allowPersonal ?? false;
if (prev !== updateWorkspaceDto.allowPersonalSpaces) {
@@ -600,6 +627,7 @@ export class WorkspaceService {
delete updateWorkspaceDto.defaultPageEditMode;
delete updateWorkspaceDto.aiChatReadOnly;
delete updateWorkspaceDto.aiChatWorkspaceKnowledgeOnly;
delete updateWorkspaceDto.mcpOauthOnly;
await this.workspaceRepo.updateWorkspace(
updateWorkspaceDto,