Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions backend/src/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import { AdminModule } from './modules/admin/admin.module';
import { AnalyticsModule } from './modules/analytics/analytics.module';
import { MCPModule } from './modules/mcp/mcp.module';
import { McpSlackModule } from './modules/mcp-slack/mcp-slack.module';
import { McpTeamsModule } from './modules/mcp-teams/mcp-teams.module';

@Module({
imports: [
Expand Down Expand Up @@ -57,6 +58,7 @@ import { McpSlackModule } from './modules/mcp-slack/mcp-slack.module';
AnalyticsModule,
MCPModule,
McpSlackModule,
McpTeamsModule,
],
controllers: [AppController],
providers: [AppService],
Expand Down
347 changes: 347 additions & 0 deletions backend/src/modules/mcp-teams/controllers/mcp-teams.controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,347 @@
import {
Controller,
Get,
Post,
Body,
Query,
Param,
HttpException,
HttpStatus,
} from '@nestjs/common';
import { ApiTags, ApiOperation, ApiResponse } from '@nestjs/swagger';
import { McpTeamsService } from '../services/mcp-teams.service';
import {
SyncChannelDto,
GetMessagesDto,
GetMeetingsDto,
ProcessRecordingDto,
SyncRecordingsDto,
GetTeamMembersDto,
} from '../dto/teams-mcp.dto';

/**
* Teams MCP Controller
* REST API endpoints for Microsoft Teams MCP integration
*/
@ApiTags('teams-mcp')
@Controller('teams-mcp')
export class McpTeamsController {
constructor(private readonly mcpTeamsService: McpTeamsService) {}

/**
* Check if Teams MCP is available
*/
@Get('status')
@ApiOperation({ summary: 'Check Teams MCP status' })
@ApiResponse({ status: 200, description: 'MCP status retrieved' })
async getStatus() {
const isAvailable = this.mcpTeamsService.isAvailable();

return {
available: isAvailable,
server: 'teams',
timestamp: new Date().toISOString(),
};
}

/**
* List all Teams
*/
@Get('teams')
@ApiOperation({ summary: 'List all Microsoft Teams' })
@ApiResponse({ status: 200, description: 'Teams retrieved successfully' })
async listTeams() {
try {
const teams = await this.mcpTeamsService.listTeams();

return {
success: true,
count: teams.length,
teams,
};
} catch (error) {
throw new HttpException(
{
success: false,
message: 'Failed to list teams',
error: error instanceof Error ? error.message : 'Unknown error',
},
HttpStatus.INTERNAL_SERVER_ERROR,
);
}
}

/**
* List channels in a team
*/
@Get('teams/:teamId/channels')
@ApiOperation({ summary: 'List channels in a Microsoft Team' })
@ApiResponse({ status: 200, description: 'Channels retrieved successfully' })
async listChannels(@Param('teamId') teamId: string) {
try {
const channels = await this.mcpTeamsService.listChannels(teamId);

return {
success: true,
teamId,
count: channels.length,
channels,
};
} catch (error) {
throw new HttpException(
{
success: false,
message: `Failed to list channels for team ${teamId}`,
error: error instanceof Error ? error.message : 'Unknown error',
},
HttpStatus.INTERNAL_SERVER_ERROR,
);
}
}

/**
* Get channel messages
*/
@Get('messages')
@ApiOperation({ summary: 'Get messages from a Teams channel' })
@ApiResponse({ status: 200, description: 'Messages retrieved successfully' })
async getMessages(@Query() query: GetMessagesDto) {
try {
const messages = await this.mcpTeamsService.getChannelMessages(
query.teamId,
query.channelId,
{
top: query.top,
skip: query.skip,
},
);

return {
success: true,
teamId: query.teamId,
channelId: query.channelId,
count: messages.length,
messages,
};
} catch (error) {
throw new HttpException(
{
success: false,
message: 'Failed to get messages',
error: error instanceof Error ? error.message : 'Unknown error',
},
HttpStatus.INTERNAL_SERVER_ERROR,
);
}
}

/**
* Sync channel messages to database
*/
@Post('sync')
@ApiOperation({ summary: 'Sync Teams channel messages to database' })
@ApiResponse({ status: 200, description: 'Sync completed successfully' })
async syncChannel(@Body() dto: SyncChannelDto) {
try {
const result = await this.mcpTeamsService.syncChannelHistory(dto.teamId, dto.channelId, {
top: dto.top,
filter: dto.filter,
});

return {
success: true,
result,
};
} catch (error) {
throw new HttpException(
{
success: false,
message: 'Sync failed',
error: error instanceof Error ? error.message : 'Unknown error',
},
HttpStatus.INTERNAL_SERVER_ERROR,
);
}
}

/**
* Get team members
*/
@Get('teams/:teamId/members')
@ApiOperation({ summary: 'Get members of a Microsoft Team' })
@ApiResponse({ status: 200, description: 'Members retrieved successfully' })
async getTeamMembers(@Param() params: GetTeamMembersDto) {
try {
const members = await this.mcpTeamsService.getTeamMembers(params.teamId);

return {
success: true,
teamId: params.teamId,
count: members.length,
members,
};
} catch (error) {
throw new HttpException(
{
success: false,
message: `Failed to get members for team ${params.teamId}`,
error: error instanceof Error ? error.message : 'Unknown error',
},
HttpStatus.INTERNAL_SERVER_ERROR,
);
}
}

/**
* Get user info
*/
@Get('users/:userId')
@ApiOperation({ summary: 'Get Microsoft Teams user information' })
@ApiResponse({ status: 200, description: 'User info retrieved' })
async getUserInfo(@Param('userId') userId: string) {
try {
const user = await this.mcpTeamsService.getUserInfo(userId);

return {
success: true,
user,
};
} catch (error) {
throw new HttpException(
{
success: false,
message: `Failed to get user info for ${userId}`,
error: error instanceof Error ? error.message : 'Unknown error',
},
HttpStatus.INTERNAL_SERVER_ERROR,
);
}
}

/**
* Get online meetings
*/
@Get('meetings')
@ApiOperation({ summary: 'Get online meetings' })
@ApiResponse({ status: 200, description: 'Meetings retrieved successfully' })
async getMeetings(@Query() query: GetMeetingsDto) {
try {
const meetings = await this.mcpTeamsService.getOnlineMeetings({
startDateTime: query.startDateTime,
endDateTime: query.endDateTime,
top: query.top,
});

return {
success: true,
count: meetings.length,
meetings,
};
} catch (error) {
throw new HttpException(
{
success: false,
message: 'Failed to get meetings',
error: error instanceof Error ? error.message : 'Unknown error',
},
HttpStatus.INTERNAL_SERVER_ERROR,
);
}
}

/**
* Get meeting recordings
*/
@Get('recordings')
@ApiOperation({ summary: 'Get meeting recordings' })
@ApiResponse({ status: 200, description: 'Recordings retrieved successfully' })
async getRecordings(@Query('since') since?: string) {
try {
const sinceDate = since ? new Date(since) : undefined;
const recordings = await this.mcpTeamsService.getMeetingRecordings(sinceDate);

return {
success: true,
count: recordings.length,
recordings,
};
} catch (error) {
throw new HttpException(
{
success: false,
message: 'Failed to get recordings',
error: error instanceof Error ? error.message : 'Unknown error',
},
HttpStatus.INTERNAL_SERVER_ERROR,
);
}
}

/**
* Process a specific recording
*/
@Post('recordings/process')
@ApiOperation({ summary: 'Process a Teams meeting recording' })
@ApiResponse({ status: 200, description: 'Recording processing queued' })
async processRecording(@Body() dto: ProcessRecordingDto) {
try {
await this.mcpTeamsService.processRecording(dto.recordingId);

return {
success: true,
message: `Recording ${dto.recordingId} queued for processing`,
recordingId: dto.recordingId,
};
} catch (error) {
throw new HttpException(
{
success: false,
message: 'Failed to process recording',
error: error instanceof Error ? error.message : 'Unknown error',
},
HttpStatus.INTERNAL_SERVER_ERROR,
);
}
}

/**
* Sync meeting recordings
*/
@Post('recordings/sync')
@ApiOperation({ summary: 'Sync meeting recordings' })
@ApiResponse({ status: 200, description: 'Recording sync started' })
async syncRecordings(@Body() dto: SyncRecordingsDto) {
try {
const sinceDate = dto.since ? new Date(dto.since) : undefined;
const recordings = await this.mcpTeamsService.getMeetingRecordings(sinceDate);

// Process each recording (limit to dto.limit if specified)
const toProcess = dto.limit ? recordings.slice(0, dto.limit) : recordings;

for (const recording of toProcess) {
try {
await this.mcpTeamsService.processRecording(recording.recordingId);
} catch (error) {
// Continue processing other recordings even if one fails
continue;
}
}

return {
success: true,
message: 'Recording sync started',
totalFound: recordings.length,
queued: toProcess.length,
};
} catch (error) {
throw new HttpException(
{
success: false,
message: 'Recording sync failed',
error: error instanceof Error ? error.message : 'Unknown error',
},
HttpStatus.INTERNAL_SERVER_ERROR,
);
}
}
}
Loading