import { z } from 'zod'
// Local type definitions
interface MCPTool {
name: string
description: string
inputSchema: any
}
import { supabaseService } from '../lib/api-client.js'
import { requireAuth } from '../lib/auth.js'
import { logger } from '../lib/logger.js'
/**
* Get comprehensive project analytics
*/
export const getProjectAnalyticsTool: MCPTool = {
name: 'get_project_analytics',
description: 'Get comprehensive analytics and insights for projects',
inputSchema: {
type: 'object',
properties: {
project_ids: {
type: 'array',
items: { type: 'string' },
description: 'Specific project IDs to analyze (optional)'
},
time_range: {
type: 'string',
enum: ['week', 'month', 'quarter', 'year', 'all'],
default: 'month',
description: 'Time range for analytics'
},
metrics: {
type: 'array',
items: {
type: 'string',
enum: ['completion_rate', 'velocity', 'team_performance', 'resource_utilization', 'quality_metrics', 'collaboration_index']
},
default: ['completion_rate', 'velocity', 'team_performance'],
description: 'Specific metrics to calculate'
},
include_predictions: {
type: 'boolean',
default: false,
description: 'Include predictive analytics and forecasts'
},
benchmark_comparison: {
type: 'boolean',
default: false,
description: 'Compare against historical benchmarks'
}
}
}
}
const GetProjectAnalyticsSchema = z.object({
project_ids: z.array(z.string()).optional(),
time_range: z.enum(['week', 'month', 'quarter', 'year', 'all']).default('month'),
metrics: z.array(z.enum(['completion_rate', 'velocity', 'team_performance', 'resource_utilization', 'quality_metrics', 'collaboration_index'])).default(['completion_rate', 'velocity', 'team_performance']),
include_predictions: z.boolean().default(false),
benchmark_comparison: z.boolean().default(false)
})
export const getProjectAnalytics = requireAuth(async (args: any) => {
const { project_ids, time_range, metrics, include_predictions, benchmark_comparison } = GetProjectAnalyticsSchema.parse(args)
logger.info('Getting project analytics', { project_ids, time_range, metrics })
// Get projects to analyze
const projects = project_ids
? await Promise.all(project_ids.map(id => supabaseService.getProject(id)))
: await supabaseService.getProjects({}, { limit: 100 })
const analytics: any = {
time_range,
projects_analyzed: projects.length,
generated_at: new Date().toISOString(),
metrics: {}
}
// Calculate requested metrics
for (const metric of metrics) {
try {
switch (metric) {
case 'completion_rate':
analytics.metrics.completion_rate = await calculateCompletionRate(projects, time_range)
break
case 'velocity':
analytics.metrics.velocity = await calculateVelocity(projects, time_range)
break
case 'team_performance':
analytics.metrics.team_performance = await calculateTeamPerformance(projects, time_range)
break
case 'resource_utilization':
analytics.metrics.resource_utilization = await calculateResourceUtilization(projects, time_range)
break
case 'quality_metrics':
analytics.metrics.quality_metrics = await calculateQualityMetrics(projects, time_range)
break
case 'collaboration_index':
analytics.metrics.collaboration_index = await calculateCollaborationIndex(projects, time_range)
break
}
} catch (error) {
logger.error(`Failed to calculate ${metric}:`, error)
analytics.metrics[metric] = { error: 'Calculation failed' }
}
}
// Add predictions if requested
if (include_predictions) {
analytics.predictions = await generatePredictions(projects, analytics.metrics, time_range)
}
// Add benchmark comparison if requested
if (benchmark_comparison) {
analytics.benchmarks = await getBenchmarkComparison(analytics.metrics, time_range)
}
// Generate insights and recommendations
analytics.insights = generateAnalyticsInsights(analytics.metrics, projects)
analytics.recommendations = generateAnalyticsRecommendations(analytics.metrics, analytics.insights)
return analytics
})
/**
* Get team productivity insights
*/
export const getTeamProductivityTool: MCPTool = {
name: 'get_team_productivity',
description: 'Analyze team productivity patterns and performance',
inputSchema: {
type: 'object',
properties: {
team_members: {
type: 'array',
items: { type: 'string' },
description: 'Specific team member IDs to analyze (optional)'
},
project_id: {
type: 'string',
description: 'Project context for analysis (optional)'
},
time_range: {
type: 'string',
enum: ['week', 'month', 'quarter'],
default: 'month',
description: 'Time range for analysis'
},
productivity_dimensions: {
type: 'array',
items: {
type: 'string',
enum: ['task_completion', 'collaboration', 'code_quality', 'documentation', 'mentoring', 'innovation']
},
default: ['task_completion', 'collaboration'],
description: 'Dimensions of productivity to measure'
}
}
}
}
const GetTeamProductivitySchema = z.object({
team_members: z.array(z.string()).optional(),
project_id: z.string().optional(),
time_range: z.enum(['week', 'month', 'quarter']).default('month'),
productivity_dimensions: z.array(z.enum(['task_completion', 'collaboration', 'code_quality', 'documentation', 'mentoring', 'innovation'])).default(['task_completion', 'collaboration'])
})
export const getTeamProductivity = requireAuth(async (args: any) => {
const { team_members, project_id, time_range, productivity_dimensions } = GetTeamProductivitySchema.parse(args)
logger.info('Analyzing team productivity', { team_members, project_id, time_range })
// Get team data
const teamData = await getTeamData(team_members, project_id, time_range)
const productivity: any = {
time_range,
team_size: teamData.members.length,
project_context: project_id,
analyzed_at: new Date().toISOString(),
dimensions: {}
}
// Analyze each productivity dimension
for (const dimension of productivity_dimensions) {
try {
switch (dimension) {
case 'task_completion':
productivity.dimensions.task_completion = analyzeTaskCompletion(teamData)
break
case 'collaboration':
productivity.dimensions.collaboration = analyzeCollaboration(teamData)
break
case 'code_quality':
productivity.dimensions.code_quality = analyzeCodeQuality(teamData)
break
case 'documentation':
productivity.dimensions.documentation = analyzeDocumentation(teamData)
break
case 'mentoring':
productivity.dimensions.mentoring = analyzeMentoring(teamData)
break
case 'innovation':
productivity.dimensions.innovation = analyzeInnovation(teamData)
break
}
} catch (error) {
logger.error(`Failed to analyze ${dimension}:`, error)
productivity.dimensions[dimension] = { error: 'Analysis failed' }
}
}
// Calculate overall productivity score
productivity.overall_score = calculateOverallProductivityScore(productivity.dimensions)
// Generate team insights
productivity.insights = generateTeamInsights(productivity.dimensions, teamData)
productivity.improvement_suggestions = generateImprovementSuggestions(productivity.dimensions)
return productivity
})
/**
* Get workspace health dashboard
*/
export const getWorkspaceHealthTool: MCPTool = {
name: 'get_workspace_health',
description: 'Get comprehensive workspace health metrics and indicators',
inputSchema: {
type: 'object',
properties: {
health_categories: {
type: 'array',
items: {
type: 'string',
enum: ['project_health', 'team_wellness', 'technical_debt', 'documentation_coverage', 'automation_efficiency', 'security_posture']
},
default: ['project_health', 'team_wellness', 'documentation_coverage'],
description: 'Categories of health metrics to assess'
},
alert_thresholds: {
type: 'object',
properties: {
critical: { type: 'number', default: 30 },
warning: { type: 'number', default: 60 },
good: { type: 'number', default: 80 }
},
description: 'Thresholds for health score alerts'
},
include_recommendations: {
type: 'boolean',
default: true,
description: 'Include actionable recommendations'
}
}
}
}
const GetWorkspaceHealthSchema = z.object({
health_categories: z.array(z.enum(['project_health', 'team_wellness', 'technical_debt', 'documentation_coverage', 'automation_efficiency', 'security_posture'])).default(['project_health', 'team_wellness', 'documentation_coverage']),
alert_thresholds: z.object({
critical: z.number().default(30),
warning: z.number().default(60),
good: z.number().default(80)
}).default({}),
include_recommendations: z.boolean().default(true)
})
export const getWorkspaceHealth = requireAuth(async (args: any) => {
const { health_categories, alert_thresholds, include_recommendations } = GetWorkspaceHealthSchema.parse(args)
logger.info('Assessing workspace health', { health_categories })
const health: any = {
overall_score: 0,
status: 'unknown',
assessed_at: new Date().toISOString(),
categories: {},
alerts: []
}
const categoryScores: number[] = []
// Assess each health category
for (const category of health_categories) {
try {
let categoryHealth
switch (category) {
case 'project_health':
categoryHealth = await assessProjectHealth()
break
case 'team_wellness':
categoryHealth = await assessTeamWellness()
break
case 'technical_debt':
categoryHealth = await assessTechnicalDebt()
break
case 'documentation_coverage':
categoryHealth = await assessDocumentationCoverage()
break
case 'automation_efficiency':
categoryHealth = await assessAutomationEfficiency()
break
case 'security_posture':
categoryHealth = await assessSecurityPosture()
break
default:
continue
}
health.categories[category] = categoryHealth
categoryScores.push(categoryHealth.score)
// Check for alerts
if (categoryHealth.score <= alert_thresholds.critical) {
health.alerts.push({
level: 'critical',
category,
message: `${category} score is critically low (${categoryHealth.score}%)`,
recommendations: categoryHealth.urgent_actions || []
})
} else if (categoryHealth.score <= alert_thresholds.warning) {
health.alerts.push({
level: 'warning',
category,
message: `${category} needs attention (${categoryHealth.score}%)`,
recommendations: categoryHealth.improvements || []
})
}
} catch (error) {
logger.error(`Failed to assess ${category}:`, error)
health.categories[category] = { error: 'Assessment failed', score: 0 }
}
}
// Calculate overall health score
health.overall_score = categoryScores.length > 0
? Math.round(categoryScores.reduce((sum, score) => sum + score, 0) / categoryScores.length)
: 0
// Determine overall status
if (health.overall_score >= alert_thresholds.good) {
health.status = 'healthy'
} else if (health.overall_score >= alert_thresholds.warning) {
health.status = 'needs_attention'
} else {
health.status = 'critical'
}
// Generate recommendations if requested
if (include_recommendations) {
health.recommendations = generateWorkspaceRecommendations(health.categories, health.alerts)
}
return health
})
/**
* Generate custom analytics report
*/
export const generateCustomReportTool: MCPTool = {
name: 'generate_custom_report',
description: 'Generate a custom analytics report with specified metrics and visualizations',
inputSchema: {
type: 'object',
properties: {
report_name: {
type: 'string',
description: 'Name for the custom report'
},
data_sources: {
type: 'array',
items: {
type: 'string',
enum: ['projects', 'tasks', 'documents', 'conversations', 'team_members', 'automations']
},
description: 'Data sources to include in the report'
},
metrics_config: {
type: 'object',
properties: {
time_grouping: { type: 'string', enum: ['day', 'week', 'month'], default: 'week' },
aggregation_method: { type: 'string', enum: ['sum', 'average', 'count'], default: 'count' },
filters: { type: 'object' }
},
description: 'Configuration for metrics calculation'
},
output_format: {
type: 'string',
enum: ['json', 'csv', 'summary'],
default: 'json',
description: 'Output format for the report'
},
schedule: {
type: 'object',
properties: {
frequency: { type: 'string', enum: ['once', 'daily', 'weekly', 'monthly'] },
recipients: { type: 'array', items: { type: 'string' } }
},
description: 'Optional scheduling configuration'
}
},
required: ['report_name', 'data_sources']
}
}
const GenerateCustomReportSchema = z.object({
report_name: z.string().min(1),
data_sources: z.array(z.enum(['projects', 'tasks', 'documents', 'conversations', 'team_members', 'automations'])).min(1),
metrics_config: z.object({
time_grouping: z.enum(['day', 'week', 'month']).default('week'),
aggregation_method: z.enum(['sum', 'average', 'count']).default('count'),
filters: z.record(z.any()).optional()
}).optional(),
output_format: z.enum(['json', 'csv', 'summary']).default('json'),
schedule: z.object({
frequency: z.enum(['once', 'daily', 'weekly', 'monthly']),
recipients: z.array(z.string())
}).optional()
})
export const generateCustomReport = requireAuth(async (args: any) => {
const { report_name, data_sources, metrics_config, output_format, schedule } = GenerateCustomReportSchema.parse(args)
logger.info('Generating custom report', { report_name, data_sources, output_format })
const report: any = {
name: report_name,
generated_at: new Date().toISOString(),
data_sources,
metrics_config: metrics_config || {},
data: {}
}
// Collect data from each source
for (const source of data_sources) {
try {
report.data[source] = await collectReportData(source, metrics_config)
} catch (error) {
logger.error(`Failed to collect data from ${source}:`, error)
report.data[source] = { error: 'Data collection failed' }
}
}
// Calculate summary statistics
report.summary = calculateReportSummary(report.data, metrics_config)
// Format output based on requested format
if (output_format === 'csv') {
report.csv_data = convertToCSV(report.data)
} else if (output_format === 'summary') {
return {
report_name,
summary: report.summary,
key_insights: generateReportInsights(report.data),
generated_at: report.generated_at
}
}
// Set up scheduling if requested
if (schedule) {
report.schedule_id = await scheduleReport(report_name, args, schedule)
}
return report
})
// Helper functions for analytics calculations
async function calculateCompletionRate(projects: any[], timeRange: string): Promise<any> {
const projectStats = await Promise.all(projects.map(async (project) => {
const tasks = await supabaseService.getTasks({ project_id: project.id })
const completedTasks = tasks.filter(t => t.status === 'done')
return {
project_id: project.id,
project_name: project.name,
total_tasks: tasks.length,
completed_tasks: completedTasks.length,
completion_rate: tasks.length > 0 ? (completedTasks.length / tasks.length) * 100 : 0
}
}))
const overallRate = projectStats.reduce((sum, stats) => sum + stats.completion_rate, 0) / projectStats.length
return {
overall_completion_rate: Math.round(overallRate * 10) / 10,
project_breakdown: projectStats,
trend: 'stable', // Would calculate actual trend
time_range: timeRange
}
}
async function calculateVelocity(projects: any[], timeRange: string): Promise<any> {
// Calculate task completion velocity
const now = new Date()
const periodStart = getPeriodStart(now, timeRange)
let totalCompleted = 0
let totalEstimated = 0
for (const project of projects) {
const tasks = await supabaseService.getTasks({ project_id: project.id })
const recentCompleted = tasks.filter(t =>
t.status === 'done' &&
new Date(t.updated_at) >= periodStart
)
totalCompleted += recentCompleted.length
totalEstimated += recentCompleted.reduce((sum, t) => sum + 8, 0) // Default 8 hours per task
}
return {
tasks_per_period: totalCompleted,
estimated_hours_per_period: totalEstimated,
velocity_trend: 'increasing', // Would calculate actual trend
period: timeRange
}
}
async function calculateTeamPerformance(projects: any[], timeRange: string): Promise<any> {
// Get all team members and their performance
const teamStats = new Map()
for (const project of projects) {
const tasks = await supabaseService.getTasks({ project_id: project.id })
tasks.forEach(task => {
if (task.assignee_id) {
if (!teamStats.has(task.assignee_id)) {
teamStats.set(task.assignee_id, {
assigned_tasks: 0,
completed_tasks: 0,
overdue_tasks: 0
})
}
const stats = teamStats.get(task.assignee_id)
stats.assigned_tasks++
if (task.status === 'done') {
stats.completed_tasks++
}
if (task.due_date && new Date(task.due_date) < new Date() && task.status !== 'done') {
stats.overdue_tasks++
}
}
})
}
const performanceData = Array.from(teamStats.entries()).map(([userId, stats]) => ({
user_id: userId,
completion_rate: stats.assigned_tasks > 0 ? (stats.completed_tasks / stats.assigned_tasks) * 100 : 0,
overdue_rate: stats.assigned_tasks > 0 ? (stats.overdue_tasks / stats.assigned_tasks) * 100 : 0,
total_tasks: stats.assigned_tasks
}))
return {
team_size: performanceData.length,
average_completion_rate: performanceData.reduce((sum, p) => sum + p.completion_rate, 0) / performanceData.length,
individual_performance: performanceData,
top_performers: performanceData.sort((a, b) => b.completion_rate - a.completion_rate).slice(0, 3)
}
}
async function calculateResourceUtilization(projects: any[], timeRange: string): Promise<any> {
// Placeholder implementation
return {
utilization_rate: 78.5,
peak_usage: 'Tuesday-Thursday',
bottlenecks: ['Design review', 'Testing phase']
}
}
async function calculateQualityMetrics(projects: any[], timeRange: string): Promise<any> {
// Placeholder implementation
return {
documentation_coverage: 65,
code_review_rate: 89,
bug_fix_rate: 92,
customer_satisfaction: 4.2
}
}
async function calculateCollaborationIndex(projects: any[], timeRange: string): Promise<any> {
// Placeholder implementation
return {
collaboration_score: 72,
cross_team_projects: 3,
knowledge_sharing_events: 8,
mentoring_relationships: 12
}
}
async function generatePredictions(projects: any[], metrics: any, timeRange: string): Promise<any> {
// Simple prediction based on current trends
return {
completion_forecast: {
next_period: Math.round(metrics.completion_rate?.overall_completion_rate * 1.05) || 0,
confidence: 75
},
velocity_forecast: {
next_period: Math.round(metrics.velocity?.tasks_per_period * 1.1) || 0,
confidence: 80
}
}
}
async function getBenchmarkComparison(metrics: any, timeRange: string): Promise<any> {
// Placeholder benchmark data
return {
industry_average: {
completion_rate: 68,
velocity: 15,
team_performance: 72
},
comparison: {
completion_rate: metrics.completion_rate ? 'above_average' : 'unknown',
velocity: metrics.velocity ? 'average' : 'unknown',
team_performance: metrics.team_performance ? 'above_average' : 'unknown'
}
}
}
function generateAnalyticsInsights(metrics: any, projects: any[]): string[] {
const insights = []
if (metrics.completion_rate?.overall_completion_rate > 80) {
insights.push('Excellent task completion rate indicates strong project execution')
}
if (metrics.team_performance?.average_completion_rate < 60) {
insights.push('Team performance below optimal - consider workload redistribution')
}
if (projects.length > 10 && metrics.velocity?.tasks_per_period < 20) {
insights.push('Low velocity relative to project count - may need process optimization')
}
return insights
}
function generateAnalyticsRecommendations(metrics: any, insights: string[]): string[] {
const recommendations = []
if (metrics.completion_rate?.overall_completion_rate < 70) {
recommendations.push('Focus on breaking down large tasks and improving estimation accuracy')
}
if (metrics.team_performance?.team_size < 3) {
recommendations.push('Consider expanding team capacity for better project coverage')
}
return recommendations
}
// Additional helper functions would be implemented here...
async function getTeamData(teamMembers?: string[], projectId?: string, timeRange?: string): Promise<any> {
return { members: [] } // Placeholder
}
function analyzeTaskCompletion(teamData: any): any {
return { score: 85, trends: 'positive' } // Placeholder
}
function analyzeCollaboration(teamData: any): any {
return { score: 72, interaction_frequency: 'high' } // Placeholder
}
function analyzeCodeQuality(teamData: any): any {
return { score: 78, review_coverage: 85 } // Placeholder
}
function analyzeDocumentation(teamData: any): any {
return { score: 65, coverage: 'medium' } // Placeholder
}
function analyzeMentoring(teamData: any): any {
return { score: 60, active_relationships: 3 } // Placeholder
}
function analyzeInnovation(teamData: any): any {
return { score: 70, new_ideas: 5 } // Placeholder
}
function calculateOverallProductivityScore(dimensions: any): number {
const scores = Object.values(dimensions).map((d: any) => d.score).filter(s => typeof s === 'number')
return scores.length > 0 ? Math.round(scores.reduce((sum: number, score: number) => sum + score, 0) / scores.length) : 0
}
function generateTeamInsights(dimensions: any, teamData: any): string[] {
return ['Team showing strong collaboration patterns', 'Documentation practices need improvement']
}
function generateImprovementSuggestions(dimensions: any): string[] {
return ['Implement pair programming sessions', 'Create documentation templates']
}
async function assessProjectHealth(): Promise<any> {
return { score: 82, active_projects: 5, blocked_projects: 1 }
}
async function assessTeamWellness(): Promise<any> {
return { score: 75, workload_balance: 'good', burnout_risk: 'low' }
}
async function assessTechnicalDebt(): Promise<any> {
return { score: 65, debt_items: 8, critical_issues: 2 }
}
async function assessDocumentationCoverage(): Promise<any> {
return { score: 70, coverage_percentage: 68, missing_docs: 12 }
}
async function assessAutomationEfficiency(): Promise<any> {
return { score: 85, automated_tasks: 15, manual_processes: 5 }
}
async function assessSecurityPosture(): Promise<any> {
return { score: 88, vulnerabilities: 2, compliance_score: 92 }
}
function generateWorkspaceRecommendations(categories: any, alerts: any[]): string[] {
const recommendations = []
if (alerts.some(a => a.level === 'critical')) {
recommendations.push('Address critical health issues immediately')
}
recommendations.push('Schedule regular health assessments')
recommendations.push('Implement automated monitoring for key metrics')
return recommendations
}
async function collectReportData(source: string, config?: any): Promise<any> {
// Placeholder for data collection
return { count: 10, trend: 'positive' }
}
function calculateReportSummary(data: any, config?: any): any {
return { total_items: 100, growth_rate: 5.2 }
}
function generateReportInsights(data: any): string[] {
return ['Strong growth in documentation', 'Task completion trending upward']
}
function convertToCSV(data: any): string {
return 'header1,header2\nvalue1,value2' // Placeholder
}
async function scheduleReport(name: string, config: any, schedule: any): Promise<string> {
return 'schedule_123' // Placeholder
}
function getPeriodStart(date: Date, period: string): Date {
const start = new Date(date)
switch (period) {
case 'week':
start.setDate(date.getDate() - 7)
break
case 'month':
start.setMonth(date.getMonth() - 1)
break
case 'quarter':
start.setMonth(date.getMonth() - 3)
break
case 'year':
start.setFullYear(date.getFullYear() - 1)
break
}
return start
}
// Export all analytics tools
export const analyticsInsightsTools = {
getProjectAnalyticsTool,
getTeamProductivityTool,
getWorkspaceHealthTool,
generateCustomReportTool
}
export const analyticsInsightsHandlers = {
get_project_analytics: getProjectAnalytics,
get_team_productivity: getTeamProductivity,
get_workspace_health: getWorkspaceHealth,
generate_custom_report: generateCustomReport
}