// 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'
import { z } from 'zod'
// Input schemas for context aggregation tools
const GetSmartContextSchema = z.object({
query: z.string().min(1),
project_id: z.string().uuid().optional(),
context_types: z.array(z.enum(['projects', 'tasks', 'documents', 'conversations'])).default(['projects', 'tasks', 'documents']),
max_results_per_type: z.number().int().positive().max(20).default(5),
include_related: z.boolean().default(true)
})
const GetWorkspaceOverviewSchema = z.object({
include_analytics: z.boolean().default(true),
time_range: z.enum(['today', 'week', 'month', 'all']).default('week'),
focus_areas: z.array(z.enum(['productivity', 'collaboration', 'documentation', 'blockers'])).optional()
})
const GetProjectInsightsSchema = z.object({
project_id: z.string().uuid(),
insight_types: z.array(z.enum(['progress', 'bottlenecks', 'team_performance', 'documentation_health', 'ai_readiness'])).default(['progress', 'bottlenecks']),
include_recommendations: z.boolean().default(true)
})
const FindRelatedContentSchema = z.object({
entity_type: z.enum(['project', 'task', 'document']),
entity_id: z.string().uuid(),
relation_types: z.array(z.enum(['similar', 'dependent', 'linked', 'recent'])).default(['similar', 'linked']),
max_results: z.number().int().positive().max(50).default(10)
})
const GenerateContextSummarySchema = z.object({
context_data: z.object({
projects: z.array(z.any()).optional(),
tasks: z.array(z.any()).optional(),
documents: z.array(z.any()).optional(),
conversations: z.array(z.any()).optional()
}),
summary_focus: z.enum(['overview', 'action_items', 'blockers', 'opportunities']).default('overview'),
target_audience: z.enum(['developer', 'manager', 'ai_agent']).default('ai_agent')
})
/**
* Get smart context based on natural language query
*/
export const getSmartContextTool: MCPTool = {
name: 'get_smart_context',
description: 'Get intelligent context aggregation based on natural language query across projects, tasks, and documents',
inputSchema: {
type: 'object',
properties: {
query: {
type: 'string',
description: 'Natural language query describing what context you need (e.g., "authentication tasks", "API documentation", "blocked items")'
},
project_id: {
type: 'string',
format: 'uuid',
description: 'Optional project ID to scope the search'
},
context_types: {
type: 'array',
items: {
type: 'string',
enum: ['projects', 'tasks', 'documents', 'conversations']
},
default: ['projects', 'tasks', 'documents'],
description: 'Types of content to include in context'
},
max_results_per_type: {
type: 'number',
minimum: 1,
maximum: 20,
default: 5,
description: 'Maximum results to return per content type'
},
include_related: {
type: 'boolean',
default: true,
description: 'Whether to include related/linked content'
}
},
required: ['query']
}
}
export const getSmartContext = requireAuth(async (args: any) => {
const { query, project_id, context_types, max_results_per_type, include_related } = GetSmartContextSchema.parse(args)
logger.info('Getting smart context', { query, project_id, context_types })
// Analyze query to understand intent and extract keywords
const queryAnalysis = analyzeContextQuery(query)
const context: any = {
query_analysis: queryAnalysis,
results: {},
related_content: {},
insights: {}
}
// Get relevant content for each requested type
for (const type of context_types) {
try {
switch (type) {
case 'projects':
context.results.projects = await getRelevantProjects(queryAnalysis, project_id, max_results_per_type)
break
case 'tasks':
context.results.tasks = await getRelevantTasks(queryAnalysis, project_id, max_results_per_type)
break
case 'documents':
context.results.documents = await getRelevantDocuments(queryAnalysis, project_id, max_results_per_type)
break
case 'conversations':
context.results.conversations = await getRelevantConversations(queryAnalysis, project_id, max_results_per_type)
break
}
} catch (error) {
logger.error(`Error getting ${type} context:`, error)
context.results[type] = []
}
}
// Get related content if requested
if (include_related) {
context.related_content = await findRelatedContentInternal(context.results, queryAnalysis)
}
// Generate insights and recommendations
context.insights = generateContextInsights(context.results, queryAnalysis)
context.summary = generateSmartContextSummary(context, query)
logger.info('Smart context generated', {
query,
total_results: Object.values(context.results).flat().length
})
return context
})
/**
* Get comprehensive workspace overview
*/
export const getWorkspaceOverviewTool: MCPTool = {
name: 'get_workspace_overview',
description: 'Get comprehensive overview of entire workspace with analytics and insights',
inputSchema: {
type: 'object',
properties: {
include_analytics: {
type: 'boolean',
default: true,
description: 'Whether to include detailed analytics'
},
time_range: {
type: 'string',
enum: ['today', 'week', 'month', 'all'],
default: 'week',
description: 'Time range for activity analysis'
},
focus_areas: {
type: 'array',
items: {
type: 'string',
enum: ['productivity', 'collaboration', 'documentation', 'blockers']
},
description: 'Specific areas to focus analysis on'
}
}
}
}
export const getWorkspaceOverview = requireAuth(async (args: any) => {
const { include_analytics, time_range, focus_areas } = GetWorkspaceOverviewSchema.parse(args)
logger.info('Getting workspace overview', { time_range, focus_areas })
// Get all workspace data
const [projects, tasks, documents] = await Promise.all([
supabaseService.getProjects({}, { limit: 50 }),
supabaseService.getTasks({}, { limit: 100 }),
supabaseService.getDocuments({}, { limit: 100 })
])
const overview: any = {
summary: {
total_projects: projects.length,
active_projects: projects.filter(p => p.status === 'active').length,
total_tasks: tasks.length,
completed_tasks: tasks.filter(t => t.status === 'done').length,
total_documents: documents.length,
documentation_coverage: calculateDocumentationCoverage(projects, documents)
},
project_health: analyzeProjectHealth(projects, tasks, documents),
productivity_metrics: calculateProductivityMetrics(tasks, time_range),
collaboration_insights: analyzeCollaboration(tasks, documents),
ai_readiness: assessAIReadiness(documents),
recommendations: generateWorkspaceRecommendations(projects, tasks, documents)
}
if (include_analytics) {
overview.analytics = {
task_distribution: analyzeTaskDistribution(tasks),
document_metrics: analyzeDocumentMetrics(documents),
project_velocity: calculateProjectVelocity(projects, tasks, time_range),
bottleneck_analysis: identifyBottlenecks(projects, tasks)
}
}
if (focus_areas) {
overview.focused_insights = generateFocusedInsights(overview, focus_areas)
}
return overview
})
/**
* Get deep project insights
*/
export const getProjectInsightsTool: MCPTool = {
name: 'get_project_insights',
description: 'Get deep analytics and insights for a specific project',
inputSchema: {
type: 'object',
properties: {
project_id: {
type: 'string',
format: 'uuid',
description: 'The project ID to analyze'
},
insight_types: {
type: 'array',
items: {
type: 'string',
enum: ['progress', 'bottlenecks', 'team_performance', 'documentation_health', 'ai_readiness']
},
default: ['progress', 'bottlenecks'],
description: 'Types of insights to generate'
},
include_recommendations: {
type: 'boolean',
default: true,
description: 'Whether to include actionable recommendations'
}
},
required: ['project_id']
}
}
export const getProjectInsights = requireAuth(async (args: any) => {
const { project_id, insight_types, include_recommendations } = GetProjectInsightsSchema.parse(args)
logger.info('Getting project insights', { project_id, insight_types })
const project = await supabaseService.getProject(project_id)
const projectContext = await supabaseService.getProjectContext(project_id)
const insights: any = {
project_overview: {
id: project.id,
name: project.name,
status: project.status,
created_at: project.created_at,
updated_at: project.updated_at
}
}
// Generate requested insights
for (const insightType of insight_types) {
switch (insightType) {
case 'progress':
insights.progress_analysis = analyzeProjectProgress(projectContext)
break
case 'bottlenecks':
insights.bottleneck_analysis = identifyProjectBottlenecks(projectContext)
break
case 'team_performance':
insights.team_performance = analyzeTeamPerformance(projectContext)
break
case 'documentation_health':
insights.documentation_health = analyzeDocumentationHealth(projectContext)
break
case 'ai_readiness':
insights.ai_readiness = assessProjectAIReadiness(projectContext)
break
}
}
if (include_recommendations) {
insights.recommendations = generateProjectRecommendations(insights, projectContext)
}
insights.overall_health_score = calculateOverallHealthScore(insights)
return insights
})
/**
* Find related content across the workspace
*/
export const findRelatedContentTool: MCPTool = {
name: 'find_related_content',
description: 'Find content related to a specific entity (project, task, or document)',
inputSchema: {
type: 'object',
properties: {
entity_type: {
type: 'string',
enum: ['project', 'task', 'document'],
description: 'Type of entity to find related content for'
},
entity_id: {
type: 'string',
format: 'uuid',
description: 'ID of the entity'
},
relation_types: {
type: 'array',
items: {
type: 'string',
enum: ['similar', 'dependent', 'linked', 'recent']
},
default: ['similar', 'linked'],
description: 'Types of relationships to find'
},
max_results: {
type: 'number',
minimum: 1,
maximum: 50,
default: 10,
description: 'Maximum number of related items to return'
}
},
required: ['entity_type', 'entity_id']
}
}
// Using the schema defined earlier
export const findRelatedContent = requireAuth(async (args: any) => {
const { entity_type, entity_id, relation_types, max_results } = FindRelatedContentSchema.parse(args)
logger.info('Finding related content', { entity_type, entity_id, relation_types })
const relatedContent: any = {
source_entity: await getSourceEntity(entity_type, entity_id),
relationships: {}
}
for (const relationType of relation_types) {
try {
relatedContent.relationships[relationType] = await findRelationshipsByType(
entity_type,
entity_id,
relationType,
max_results
)
} catch (error) {
logger.error(`Error finding ${relationType} relationships:`, error)
relatedContent.relationships[relationType] = []
}
}
// Calculate relationship strength and add metadata
relatedContent.analysis = analyzeRelationships(relatedContent.relationships)
relatedContent.suggestions = generateRelationshipSuggestions(relatedContent)
return relatedContent
})
/**
* Generate context summary from aggregated data
*/
export const generateContextSummaryTool: MCPTool = {
name: 'generate_context_summary',
description: 'Generate intelligent summary from aggregated context data',
inputSchema: {
type: 'object',
properties: {
context_data: {
type: 'object',
properties: {
projects: { type: 'array' },
tasks: { type: 'array' },
documents: { type: 'array' },
conversations: { type: 'array' }
},
description: 'Aggregated context data to summarize'
},
summary_focus: {
type: 'string',
enum: ['overview', 'action_items', 'blockers', 'opportunities'],
default: 'overview',
description: 'Focus of the summary'
},
target_audience: {
type: 'string',
enum: ['developer', 'manager', 'ai_agent'],
default: 'ai_agent',
description: 'Target audience for the summary'
}
},
required: ['context_data']
}
}
export const generateContextSummary = requireAuth(async (args: any) => {
const { context_data, summary_focus, target_audience } = GenerateContextSummarySchema.parse(args)
logger.info('Generating context summary', { summary_focus, target_audience })
const summary = {
executive_summary: generateExecutiveSummary(context_data, summary_focus),
key_metrics: extractKeyMetrics(context_data),
insights: generateInsights(context_data, summary_focus),
action_items: extractActionItems(context_data),
recommendations: generateRecommendations(context_data, target_audience),
metadata: {
generated_at: new Date().toISOString(),
summary_focus,
target_audience,
data_sources: Object.keys(context_data).filter(key => (context_data as any)[key]?.length > 0)
}
}
return summary
})
// Helper functions for context analysis
function analyzeContextQuery(query: string): any {
const keywords = extractKeywords(query)
const intent = detectIntent(query)
const entities = extractEntities(query)
const urgency = detectUrgency(query)
return {
original_query: query,
keywords,
intent,
entities,
urgency,
search_terms: generateSearchTerms(keywords, entities)
}
}
function extractKeywords(query: string): string[] {
// Simple keyword extraction
const stopWords = ['the', 'a', 'an', 'and', 'or', 'but', 'in', 'on', 'at', 'to', 'for', 'of', 'with', 'by']
return query
.toLowerCase()
.split(/\s+/)
.filter(word => word.length > 2 && !stopWords.includes(word))
.filter(word => /^[a-zA-Z]+$/.test(word))
}
function detectIntent(query: string): string {
const lowerQuery = query.toLowerCase()
if (lowerQuery.includes('block') || lowerQuery.includes('stuck') || lowerQuery.includes('issue')) {
return 'troubleshooting'
}
if (lowerQuery.includes('task') || lowerQuery.includes('todo') || lowerQuery.includes('work')) {
return 'task_management'
}
if (lowerQuery.includes('document') || lowerQuery.includes('doc') || lowerQuery.includes('readme')) {
return 'documentation'
}
if (lowerQuery.includes('project') || lowerQuery.includes('overview')) {
return 'project_overview'
}
if (lowerQuery.includes('team') || lowerQuery.includes('collaboration')) {
return 'collaboration'
}
return 'general_search'
}
function extractEntities(query: string): string[] {
// Simple entity extraction based on patterns
const entities = []
// Look for quoted terms
const quotedTerms = query.match(/"([^"]+)"/g)
if (quotedTerms) {
entities.push(...quotedTerms.map(term => term.replace(/"/g, '')))
}
// Look for technical terms (capitalized or with special chars)
const techTerms = query.match(/[A-Z][a-zA-Z]+|[a-zA-Z]+[-_][a-zA-Z]+/g)
if (techTerms) {
entities.push(...techTerms)
}
return entities
}
function detectUrgency(query: string): 'low' | 'medium' | 'high' {
const urgentWords = ['urgent', 'asap', 'emergency', 'critical', 'immediately', 'now']
const highWords = ['important', 'priority', 'soon', 'quick', 'fast']
const lowerQuery = query.toLowerCase()
if (urgentWords.some(word => lowerQuery.includes(word))) return 'high'
if (highWords.some(word => lowerQuery.includes(word))) return 'medium'
return 'low'
}
function generateSearchTerms(keywords: string[], entities: string[]): string[] {
return [...new Set([...keywords, ...entities])]
}
async function getRelevantProjects(queryAnalysis: any, projectId?: string, limit: number = 5): Promise<any[]> {
const searchTerms = queryAnalysis.search_terms.join(' ')
return await supabaseService.getProjects(
{ search: searchTerms },
{ limit },
{ field: 'updated_at', order: 'desc' }
)
}
async function getRelevantTasks(queryAnalysis: any, projectId?: string, limit: number = 5): Promise<any[]> {
const searchTerms = queryAnalysis.search_terms.join(' ')
return await supabaseService.getTasks(
{ search: searchTerms, project_id: projectId },
{ limit },
{ field: 'updated_at', order: 'desc' }
)
}
async function getRelevantDocuments(queryAnalysis: any, projectId?: string, limit: number = 5): Promise<any[]> {
const searchTerms = queryAnalysis.search_terms.join(' ')
return await supabaseService.getDocuments(
{ search: searchTerms, project_id: projectId },
{ limit },
{ field: 'updated_at', order: 'desc' }
)
}
async function getRelevantConversations(queryAnalysis: any, projectId?: string, limit: number = 5): Promise<any[]> {
// Placeholder - would implement conversation search
return []
}
async function findRelatedContentInternal(results: any, queryAnalysis: any): Promise<any> {
// Find connections between different types of content
const related = {
cross_references: [],
common_topics: [],
related_projects: []
}
// This would implement more sophisticated relationship finding
return related
}
function generateContextInsights(results: any, queryAnalysis: any): any {
return {
total_items_found: Object.values(results).flat().length,
coverage: {
projects: results.projects?.length || 0,
tasks: results.tasks?.length || 0,
documents: results.documents?.length || 0,
conversations: results.conversations?.length || 0
},
relevance_score: calculateRelevanceScore(results, queryAnalysis),
patterns: identifyPatterns(results),
suggestions: generateSearchSuggestions(results, queryAnalysis)
}
}
function generateSmartContextSummary(context: any, originalQuery: string): string {
const totalResults = Object.values(context.results).flat().length
const topTypes = Object.entries(context.results)
.sort(([,a], [,b]) => (b as any[]).length - (a as any[]).length)
.slice(0, 2)
.map(([type]) => type)
return `Found ${totalResults} relevant items for "${originalQuery}". Primary matches in ${topTypes.join(' and ')}. ${context.insights.relevance_score > 0.7 ? 'High relevance' : 'Moderate relevance'} to query intent.`
}
function calculateDocumentationCoverage(projects: any[], documents: any[]): number {
if (projects.length === 0) return 0
const projectsWithDocs = projects.filter(project =>
documents.some(doc => doc.project_id === project.id)
).length
return Math.round((projectsWithDocs / projects.length) * 100)
}
function analyzeProjectHealth(projects: any[], tasks: any[], documents: any[]): any {
return {
healthy_projects: projects.filter(p => p.status === 'active').length,
at_risk_projects: projects.filter(p => {
const projectTasks = tasks.filter(t => t.project_id === p.id)
const todoTasks = projectTasks.filter(t => t.status === 'todo').length
return todoTasks > projectTasks.length * 0.5 // More than 50% not started
}).length,
stale_projects: projects.filter(p => {
const daysSinceUpdate = (Date.now() - new Date(p.updated_at).getTime()) / (1000 * 60 * 60 * 24)
return daysSinceUpdate > 30
}).length
}
}
function calculateProductivityMetrics(tasks: any[], timeRange: string): any {
const now = new Date()
let startDate: Date
switch (timeRange) {
case 'today':
startDate = new Date(now.getFullYear(), now.getMonth(), now.getDate())
break
case 'week':
startDate = new Date(now.getTime() - 7 * 24 * 60 * 60 * 1000)
break
case 'month':
startDate = new Date(now.getFullYear(), now.getMonth(), 1)
break
default:
startDate = new Date(0)
}
const recentTasks = tasks.filter(task =>
new Date(task.updated_at).getTime() >= startDate.getTime()
)
const completedTasks = recentTasks.filter(task => task.status === 'done')
return {
tasks_completed: completedTasks.length,
tasks_in_progress: recentTasks.filter(task => task.status === 'in_progress').length,
completion_rate: recentTasks.length > 0 ? completedTasks.length / recentTasks.length : 0,
average_completion_time: calculateAverageCompletionTime(completedTasks)
}
}
function analyzeCollaboration(tasks: any[], documents: any[]): any {
const assignedTasks = tasks.filter(task => task.assignee_id)
// Since metadata doesn't exist in the schema, we'll count collaborative documents differently
const collaborativeDocuments = documents.filter(doc =>
doc.title.toLowerCase().includes('collaborative') ||
doc.title.toLowerCase().includes('team') ||
doc.document_type === 'meeting_notes'
)
return {
task_assignment_rate: tasks.length > 0 ? assignedTasks.length / tasks.length : 0,
collaborative_documents: collaborativeDocuments.length,
team_distribution: analyzeTeamDistribution(assignedTasks)
}
}
function assessAIReadiness(documents: any[]): any {
// Since metadata doesn't exist in the schema, we'll assess AI readiness based on document type and content
const aiReadyDocs = documents.filter(doc =>
doc.document_type === 'technical' ||
doc.document_type === 'design' ||
doc.content.includes('AI') ||
doc.content.includes('ai_instructions')
)
return {
ai_ready_documents: aiReadyDocs.length,
readiness_percentage: documents.length > 0 ? (aiReadyDocs.length / documents.length) * 100 : 0,
missing_ai_metadata: documents.length - aiReadyDocs.length,
recommendations: generateAIReadinessRecommendations(documents, aiReadyDocs)
}
}
function generateWorkspaceRecommendations(projects: any[], tasks: any[], documents: any[]): string[] {
const recommendations = []
const activeTasks = tasks.filter(t => t.status === 'in_progress')
const completedTasks = tasks.filter(t => t.status === 'done')
if (activeTasks.length > completedTasks.length * 2) {
recommendations.push('Consider focusing on completing existing tasks before starting new ones')
}
const undocumentedProjects = projects.filter(p =>
!documents.some(d => d.project_id === p.id && d.document_type === 'other')
)
if (undocumentedProjects.length > 0) {
recommendations.push(`Add README documentation for ${undocumentedProjects.length} project(s)`)
}
const todoTasks = tasks.filter(t => t.status === 'todo')
if (todoTasks.length > tasks.filter(t => t.status === 'in_progress').length * 2) {
recommendations.push(`Focus on starting ${todoTasks.length} pending task(s) to improve team velocity`)
}
return recommendations
}
// Additional helper functions would continue here...
// (For brevity, I'm showing the structure - the full implementation would include all remaining functions)
// Export all context aggregation tools
export const contextAggregationTools = {
getSmartContextTool,
getWorkspaceOverviewTool,
getProjectInsightsTool,
findRelatedContentTool,
generateContextSummaryTool
}
export const contextAggregationHandlers = {
get_smart_context: getSmartContext,
get_workspace_overview: getWorkspaceOverview,
get_project_insights: getProjectInsights,
find_related_content: findRelatedContent,
generate_context_summary: generateContextSummary
}
// Stub implementations for remaining helper functions
function calculateRelevanceScore(results: any, queryAnalysis: any): number {
// Calculate relevance based on query analysis and results
return 0.8
}
function identifyPatterns(results: any): any[] {
// Identify patterns across results
return []
}
function generateSearchSuggestions(results: any, queryAnalysis: any): string[] {
// Generate suggestions for better search
return []
}
async function getSourceEntity(entityType: string, entityId: string): Promise<any> {
// Get the source entity based on type
switch (entityType) {
case 'project':
return await supabaseService.getProject(entityId)
case 'task':
const tasks = await supabaseService.getTasks({ search: entityId })
return tasks.find(t => t.id === entityId)
case 'document':
return await supabaseService.getDocument(entityId)
default:
return null
}
}
async function findRelationshipsByType(entityType: string, entityId: string, relationType: string, maxResults: number): Promise<any[]> {
// Find relationships by type
return []
}
function analyzeRelationships(relationships: any): any {
// Analyze relationship strength and patterns
return {}
}
function generateRelationshipSuggestions(relatedContent: any): string[] {
// Generate suggestions based on relationships
return []
}
function generateExecutiveSummary(contextData: any, summaryFocus: string): string {
// Generate executive summary
return 'Executive summary of context data'
}
function extractKeyMetrics(contextData: any): any {
// Extract key metrics from context data
return {}
}
function generateInsights(contextData: any, summaryFocus: string): any[] {
// Generate insights from context data
return []
}
function extractActionItems(contextData: any): any[] {
// Extract action items from context data
return []
}
function generateRecommendations(contextData: any, targetAudience: string): string[] {
// Generate recommendations based on context
return []
}
function analyzeProjectProgress(projectContext: any): any {
// Analyze project progress
return {}
}
function identifyProjectBottlenecks(projectContext: any): any {
// Identify project bottlenecks
return {}
}
function analyzeTeamPerformance(projectContext: any): any {
// Analyze team performance
return {}
}
function analyzeDocumentationHealth(projectContext: any): any {
// Analyze documentation health
return {}
}
function assessProjectAIReadiness(projectContext: any): any {
// Assess project AI readiness
return {}
}
function generateProjectRecommendations(insights: any, projectContext: any): string[] {
// Generate project recommendations
return []
}
function calculateOverallHealthScore(insights: any): number {
// Calculate overall health score
return 85
}
function analyzeTaskDistribution(tasks: any[]): any {
// Analyze task distribution
return {}
}
function analyzeDocumentMetrics(documents: any[]): any {
// Analyze document metrics
return {}
}
function calculateProjectVelocity(projects: any[], tasks: any[], timeRange: string): any {
// Calculate project velocity
return {}
}
function identifyBottlenecks(projects: any[], tasks: any[]): any {
// Identify bottlenecks
return {}
}
function generateFocusedInsights(overview: any, focusAreas: string[]): any {
// Generate focused insights
return {}
}
function calculateAverageCompletionTime(completedTasks: any[]): number {
// Calculate average completion time
return 0
}
function analyzeTeamDistribution(assignedTasks: any[]): any {
// Analyze team distribution
return {}
}
function generateAIReadinessRecommendations(documents: any[], aiReadyDocs: any[]): string[] {
// Generate AI readiness recommendations
return []
}