@codai/glass-mcp
Version:
High-Performance Enterprise AI Project Management MCP Server with advanced optimization and multi-agent coordination
833 lines (827 loc) • 36.4 kB
JavaScript
#!/usr/bin/env node
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import { CallToolRequestSchema, ListResourcesRequestSchema, ListToolsRequestSchema, ReadResourceRequestSchema, } from '@modelcontextprotocol/sdk/types.js';
import express from 'express';
import { WebSocketServer } from 'ws';
import cors from 'cors';
import dotenv from 'dotenv';
import { v4 as uuidv4 } from 'uuid';
import { DatabaseService } from './database/DatabaseService.js';
import { AIService } from './ai/AIService.js';
import { CoordinationService } from './coordination/CoordinationService.js';
import { Logger } from './utils/logger.js';
import { handleAdvancedAnalytics } from './tools/analytics.js';
import { TaskStatus, AgentStatus, ProjectStatus, Priority, AgentType, AgentCapability, MessageType } from './types/index.js';
// Load environment variables
dotenv.config({ path: process.env.DOTENV_CONFIG_PATH });
// Configuration
const config = {
server: {
port: parseInt(process.env.CONTROLAI_PORT || '7002'),
host: process.env.CONTROLAI_HOST || 'localhost',
cors: {
origin: process.env.CONTROLAI_CORS_ORIGIN || '*',
},
},
database: {
path: process.env.CONTROLAI_DATABASE_PATH || undefined,
},
ai: {
provider: 'azure-openai',
endpoint: process.env.AZURE_OPENAI_ENDPOINT || '',
apiKey: process.env.AZURE_OPENAI_API_KEY || '',
deploymentName: process.env.AZURE_OPENAI_DEPLOYMENT || 'gpt-4o',
},
websocket: {
enabled: process.env.CONTROLAI_WEBSOCKET_ENABLED === 'true' || false,
heartbeatInterval: 30000,
},
};
/**
* Enterprise AI Project Management MCP Server
*
* Provides intelligent task management, multi-agent coordination,
* and real-time project monitoring capabilities.
*/
class ControlAIMCPServer {
server;
database;
isMCPMode;
aiService;
coordinationService;
httpServer;
wsServer;
clients = new Set();
constructor() {
// Detect MCP mode (when running via npx or stdio transport)
this.isMCPMode = process.argv.includes('--stdio') ||
process.argv[0].includes('npx') ||
process.env.npm_lifecycle_event === 'start';
if (this.isMCPMode) {
Logger.setMCPMode(true);
}
this.server = new Server({
name: 'glass-mcp',
version: '11.4.1',
}, {
capabilities: {
resources: {},
tools: {},
},
});
this.database = new DatabaseService();
this.aiService = new AIService();
this.coordinationService = new CoordinationService(this.database, this.aiService);
this.setupHandlers();
}
setupHandlers() {
// List available tools
this.server.setRequestHandler(ListToolsRequestSchema, async () => {
return {
tools: [
{
name: 'create_project',
description: 'Create a new project with intelligent analysis and task breakdown',
inputSchema: {
type: 'object',
properties: {
name: { type: 'string', description: 'Project name' },
description: { type: 'string', description: 'Detailed project description' },
priority: {
type: 'string',
enum: Object.values(Priority),
description: 'Project priority level'
},
tags: {
type: 'array',
items: { type: 'string' },
description: 'Project tags for organization'
}
},
required: ['name', 'description'],
},
},
{
name: 'analyze_plan',
description: 'Intelligently analyze a project plan and break it down into tasks',
inputSchema: {
type: 'object',
properties: {
projectId: { type: 'string', description: 'Project ID to analyze' },
plan: { type: 'string', description: 'Natural language project plan description' },
},
required: ['projectId', 'plan'],
},
},
{
name: 'get_project_status',
description: 'Get comprehensive status of a project including tasks and agent assignments',
inputSchema: {
type: 'object',
properties: {
projectId: { type: 'string', description: 'Project ID to get status for' },
},
required: ['projectId'],
},
},
{
name: 'assign_task',
description: 'Intelligently assign a task to the most suitable agent',
inputSchema: {
type: 'object',
properties: {
taskId: { type: 'string', description: 'Task ID to assign' },
agentId: { type: 'string', description: 'Specific agent ID (optional for manual assignment)' },
},
required: ['taskId'],
},
},
{
name: 'register_agent',
description: 'Register a new AI agent with the coordination system',
inputSchema: {
type: 'object',
properties: {
name: { type: 'string', description: 'Agent name' },
type: {
type: 'string',
enum: Object.values(AgentType),
description: 'Agent type'
},
capabilities: {
type: 'array',
items: {
type: 'string',
enum: Object.values(AgentCapability)
},
description: 'Agent capabilities'
},
workspaceId: { type: 'string', description: 'Workspace identifier' },
maxConcurrentTasks: { type: 'number', description: 'Maximum concurrent tasks', default: 1 },
},
required: ['name', 'type', 'capabilities', 'workspaceId'],
},
},
{
name: 'get_dashboard_data',
description: 'Get real-time dashboard data for project monitoring',
inputSchema: {
type: 'object',
properties: {
workspaceId: { type: 'string', description: 'Workspace ID to get data for' },
},
required: ['workspaceId'],
},
},
{
name: 'update_task_status',
description: 'Update task status with intelligent notifications',
inputSchema: {
type: 'object',
properties: {
taskId: { type: 'string', description: 'Task ID to update' },
status: {
type: 'string',
enum: Object.values(TaskStatus),
description: 'New task status'
},
actualHours: { type: 'number', description: 'Actual hours spent (for completed tasks)' },
notes: { type: 'string', description: 'Status update notes' },
},
required: ['taskId', 'status'],
},
},
{
name: 'get_advanced_analytics',
description: 'Get comprehensive advanced analytics including project trends, agent performance, predictive insights, and custom metrics',
inputSchema: {
type: 'object',
properties: {
workspaceId: {
type: 'string',
description: 'Workspace ID to get analytics for'
},
timeRange: {
type: 'object',
properties: {
start: { type: 'string', description: 'Start date (ISO string)' },
end: { type: 'string', description: 'End date (ISO string)' }
},
description: 'Time range for analytics (defaults to last 30 days)'
},
metrics: {
type: 'array',
items: {
type: 'string',
enum: [
'project_trends',
'agent_performance',
'task_analytics',
'productivity_metrics',
'collaboration_metrics',
'quality_metrics',
'predictive_insights',
'custom_metrics'
]
},
description: 'Specific metrics to include (defaults to all)'
},
filters: {
type: 'object',
properties: {
projectIds: {
type: 'array',
items: { type: 'string' },
description: 'Filter by specific project IDs'
},
agentTypes: {
type: 'array',
items: { type: 'string' },
description: 'Filter by agent types'
},
priority: {
type: 'string',
enum: ['low', 'medium', 'high', 'critical'],
description: 'Filter by priority level'
}
},
description: 'Optional filters to apply to analytics'
},
aggregation: {
type: 'string',
enum: ['daily', 'weekly', 'monthly'],
description: 'Data aggregation level (defaults to weekly)'
}
},
required: ['workspaceId'],
},
},
],
};
});
// Handle tool calls
this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
const { name, arguments: args } = request.params;
try {
switch (name) {
case 'create_project': {
const project = await this.handleCreateProject(args);
return { content: [{ type: 'text', text: JSON.stringify(project, null, 2) }] };
}
case 'analyze_plan': {
const analysis = await this.handleAnalyzePlan(args);
return { content: [{ type: 'text', text: JSON.stringify(analysis, null, 2) }] };
}
case 'get_project_status': {
const status = await this.handleGetProjectStatus(args);
return { content: [{ type: 'text', text: JSON.stringify(status, null, 2) }] };
}
case 'assign_task': {
const assignment = await this.handleAssignTask(args);
return { content: [{ type: 'text', text: JSON.stringify(assignment, null, 2) }] };
}
case 'register_agent': {
const agent = await this.handleRegisterAgent(args);
return { content: [{ type: 'text', text: JSON.stringify(agent, null, 2) }] };
}
case 'get_dashboard_data': {
const dashboard = await this.handleGetDashboardData(args);
return { content: [{ type: 'text', text: JSON.stringify(dashboard, null, 2) }] };
}
case 'update_task_status': {
const result = await this.handleUpdateTaskStatus(args);
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
}
case 'get_advanced_analytics': {
const analytics = await this.handleGetAdvancedAnalytics(args);
return analytics;
}
default:
throw new Error(`Unknown tool: ${name}`);
}
}
catch (error) {
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
return {
content: [{
type: 'text',
text: `Error executing ${name}: ${errorMessage}`
}],
isError: true
};
}
});
// List resources
this.server.setRequestHandler(ListResourcesRequestSchema, async () => {
return {
resources: [
{
uri: 'controlai://projects',
mimeType: 'application/json',
name: 'All Projects',
description: 'List of all projects in the system',
},
{
uri: 'controlai://agents',
mimeType: 'application/json',
name: 'All Agents',
description: 'List of all registered agents',
},
{
uri: 'controlai://tasks/available',
mimeType: 'application/json',
name: 'Available Tasks',
description: 'Tasks available for assignment',
},
],
};
});
// Read resources
this.server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
const { uri } = request.params;
switch (uri) {
case 'controlai://projects': {
const projects = await this.database.getAllProjects();
return {
contents: [
{
uri,
mimeType: 'application/json',
text: JSON.stringify(projects, null, 2),
},
],
};
}
case 'controlai://agents': {
const agents = await this.database.getAllAgents();
return {
contents: [
{
uri,
mimeType: 'application/json',
text: JSON.stringify(agents, null, 2),
},
],
};
}
case 'controlai://tasks/available': {
const tasks = await this.database.getAvailableTasks();
return {
contents: [
{
uri,
mimeType: 'application/json',
text: JSON.stringify(tasks, null, 2),
},
],
};
}
default:
throw new Error(`Unknown resource: ${uri}`);
}
});
}
// Tool handlers
async handleCreateProject(args) {
const { name, description, priority = Priority.MEDIUM, tags = [] } = args;
const project = {
id: uuidv4(),
name,
description,
status: ProjectStatus.PLANNING,
priority,
tags,
metadata: {}
};
const createdProject = await this.database.createProject(project);
// Broadcast project creation
this.broadcast({
type: MessageType.PROJECT_CREATED,
payload: createdProject,
timestamp: new Date()
});
return createdProject;
}
async handleAnalyzePlan(args) {
const { projectId, plan } = args;
const project = await this.database.getProject(projectId);
if (!project) {
throw new Error(`Project ${projectId} not found`);
}
// Use AI service to analyze the plan and create tasks
const analysis = await this.aiService.analyzePlan(plan);
const tasks = [];
for (const taskSuggestion of analysis.suggestedTasks) {
const task = {
id: uuidv4(),
projectId,
title: taskSuggestion.title,
description: taskSuggestion.description,
status: TaskStatus.TODO,
priority: taskSuggestion.priority,
category: taskSuggestion.category,
estimatedHours: taskSuggestion.estimatedHours,
dependencies: [],
tags: taskSuggestion.tags || [],
metadata: {
aiGenerated: true,
confidence: taskSuggestion.confidence
}
};
const createdTask = await this.database.createTask(task);
tasks.push(createdTask);
}
// Update project status to active if it was in planning
if (project.status === ProjectStatus.PLANNING) {
await this.database.updateProject(projectId, { status: ProjectStatus.ACTIVE });
}
// Broadcast plan analysis completion
this.broadcast({
type: MessageType.PLAN_ANALYZED,
payload: { projectId, analysis, tasks },
timestamp: new Date()
});
return { analysis, tasks };
}
async handleGetProjectStatus(args) {
const { projectId } = args;
const project = await this.database.getProject(projectId);
if (!project) {
throw new Error(`Project ${projectId} not found`);
}
const tasks = await this.database.getTasksByProject(projectId);
// Calculate project metrics
const totalTasks = tasks.length;
const completedTasks = tasks.filter((t) => t.status === TaskStatus.COMPLETED).length;
const inProgressTasks = tasks.filter((t) => t.status === TaskStatus.IN_PROGRESS).length;
const todoTasks = tasks.filter((t) => t.status === TaskStatus.TODO).length;
const blockedTasks = tasks.filter((t) => t.status === TaskStatus.BLOCKED).length;
const progress = totalTasks > 0 ? (completedTasks / totalTasks) * 100 : 0;
const totalEstimated = tasks.reduce((sum, task) => sum + (task.estimatedHours || 0), 0);
const totalActual = tasks.reduce((sum, task) => sum + (task.actualHours || 0), 0);
return {
project,
tasks,
metrics: {
totalTasks,
completedTasks,
inProgressTasks,
todoTasks,
blockedTasks,
progress: Math.round(progress * 100) / 100,
totalEstimatedHours: totalEstimated,
totalActualHours: totalActual
}
};
}
async handleAssignTask(args) {
const { taskId, agentId } = args;
const task = await this.database.getTask(taskId);
if (!task) {
throw new Error(`Task ${taskId} not found`);
}
let targetAgent = null;
if (agentId) {
// Manual assignment to specific agent
targetAgent = await this.database.getAgent(agentId);
if (!targetAgent) {
throw new Error(`Agent ${agentId} not found`);
}
}
else {
// Use coordination service for intelligent assignment
const suggestion = await this.coordinationService.suggestTaskAssignment(taskId);
if (suggestion.suggestedAgent) {
targetAgent = await this.database.getAgent(suggestion.suggestedAgent.id);
}
}
if (!targetAgent) {
throw new Error('No suitable agent found for task assignment');
}
// Update task assignment
await this.database.updateTask(taskId, {
assignedAgentId: targetAgent.id,
status: TaskStatus.ASSIGNED
});
// Update agent current task
await this.database.updateAgent(targetAgent.id, {
currentTaskId: taskId,
status: AgentStatus.BUSY
});
const assignment = {
taskId,
agentId: targetAgent.id,
agentName: targetAgent.name,
assignedAt: new Date()
};
// Broadcast task assignment
this.broadcast({
type: MessageType.TASK_ASSIGNED,
payload: assignment,
timestamp: new Date()
});
return assignment;
}
async handleRegisterAgent(args) {
const { name, type, capabilities, workspaceId, maxConcurrentTasks = 1 } = args;
const agent = {
id: uuidv4(),
name,
type,
capabilities,
status: AgentStatus.AVAILABLE,
workspaceId,
maxConcurrentTasks,
performance: {
tasksCompleted: 0,
averageCompletionTime: 0,
qualityScore: 100,
reliabilityScore: 100,
efficiencyScore: 100,
successRate: 100,
lastUpdated: new Date()
},
metadata: {}
};
const registeredAgent = await this.database.registerAgent(agent);
// Broadcast agent registration
this.broadcast({
type: MessageType.AGENT_REGISTERED,
payload: registeredAgent,
timestamp: new Date()
});
return registeredAgent;
}
async handleGetDashboardData(args) {
const { workspaceId } = args;
const agents = await this.database.getAllAgents();
const workspaceAgents = agents.filter((a) => a.workspaceId === workspaceId);
const projects = await this.database.getAllProjects();
const availableTasks = await this.database.getAvailableTasks();
// Calculate workspace metrics
const activeAgents = workspaceAgents.filter((a) => a.status !== AgentStatus.OFFLINE).length;
const busyAgents = workspaceAgents.filter((a) => a.status === AgentStatus.BUSY).length;
const availableAgents = workspaceAgents.filter((a) => a.status === AgentStatus.AVAILABLE).length;
const activeProjects = projects.filter((p) => p.status === ProjectStatus.ACTIVE).length;
const completedProjects = projects.filter((p) => p.status === ProjectStatus.COMPLETED).length;
return {
workspaceId,
metrics: {
totalAgents: workspaceAgents.length,
activeAgents,
busyAgents,
availableAgents,
totalProjects: projects.length,
activeProjects,
completedProjects,
availableTasks: availableTasks.length
},
agents: workspaceAgents,
recentProjects: projects.slice(0, 10),
availableTasks: availableTasks.slice(0, 20)
};
}
async handleUpdateTaskStatus(args) {
const { taskId, status, actualHours, notes } = args;
const task = await this.database.getTask(taskId);
if (!task) {
throw new Error(`Task ${taskId} not found`);
}
const updates = { status };
if (status === TaskStatus.IN_PROGRESS && !task.startedAt) {
updates.startedAt = new Date();
}
if (status === TaskStatus.COMPLETED) {
updates.completedAt = new Date();
if (actualHours !== undefined) {
updates.actualHours = actualHours;
}
}
const updatedTask = await this.database.updateTask(taskId, updates);
// Update agent status if task is completed
if (status === TaskStatus.COMPLETED && task.assignedAgentId) {
const agent = await this.database.getAgent(task.assignedAgentId);
if (agent) {
// Update agent performance metrics
const newTasksCompleted = agent.performance.tasksCompleted + 1;
const completionTime = actualHours || task.estimatedHours || 0;
const avgTime = (agent.performance.averageCompletionTime * agent.performance.tasksCompleted + completionTime) / newTasksCompleted;
await this.database.updateAgent(task.assignedAgentId, {
status: AgentStatus.AVAILABLE,
currentTaskId: undefined,
performance: {
...agent.performance,
tasksCompleted: newTasksCompleted,
averageCompletionTime: avgTime,
lastUpdated: new Date()
}
});
}
}
const result = {
task: updatedTask,
notes,
updatedAt: new Date()
};
// Broadcast task status update
this.broadcast({
type: MessageType.TASK_STATUS_UPDATED,
payload: result,
timestamp: new Date()
});
return result;
}
async handleGetAdvancedAnalytics(args) {
return await handleAdvancedAnalytics(args, this.database, this.aiService);
}
// WebSocket broadcasting
broadcast(message) {
if (!this.wsServer || this.clients.size === 0)
return;
const messageStr = JSON.stringify(message);
this.clients.forEach(client => {
if (client.readyState === 1) { // WebSocket.OPEN
client.send(messageStr);
}
});
}
// Initialize HTTP server with WebSocket support
async startHTTPServer() {
if (!config.server.port)
return;
this.httpServer = express();
this.httpServer.use(cors(config.server.cors));
this.httpServer.use(express.json());
// Health check endpoint
this.httpServer.get('/health', (req, res) => {
res.json({
status: 'healthy',
service: 'glass-mcp',
version: '11.4.1',
timestamp: new Date().toISOString()
});
});
// Start HTTP server only if not in pure MCP mode
const isMCPMode = process.argv.includes('--mcp') || process.stdin.isTTY === false;
if (!isMCPMode) {
try {
const server = this.httpServer.listen(config.server.port, config.server.host, () => {
console.log(`ControlAI MCP Server running on http://${config.server.host}:${config.server.port}`);
});
server.on('error', (error) => {
if (error.code === 'EADDRINUSE') {
console.warn(`Port ${config.server.port} is in use, continuing with MCP-only mode`);
}
else {
console.error('HTTP Server error:', error);
}
});
// Set up WebSocket server
if (config.websocket.enabled) {
this.wsServer = new WebSocketServer({ server });
this.wsServer.on('connection', (ws) => {
console.log('WebSocket client connected');
this.clients.add(ws);
ws.on('close', () => {
console.log('WebSocket client disconnected');
this.clients.delete(ws);
});
ws.on('error', (error) => {
console.error('WebSocket error:', error);
this.clients.delete(ws);
});
// Send initial connection message
ws.send(JSON.stringify({
type: MessageType.CONNECTION_ESTABLISHED,
payload: { message: 'Connected to ControlAI MCP' },
timestamp: new Date()
}));
});
// Heartbeat to keep connections alive
setInterval(() => {
this.broadcast({
type: MessageType.HEARTBEAT,
payload: { timestamp: Date.now() },
timestamp: new Date()
});
}, config.websocket.heartbeatInterval);
}
}
catch (error) {
console.warn('Failed to start HTTP/WebSocket server, continuing with MCP-only mode:', error);
}
}
else {
console.log('Running in MCP-only mode (no HTTP/WebSocket server)');
}
}
async start() {
try {
if (!this.isMCPMode) {
console.log('Initializing ControlAI MCP Server...');
}
// Initialize database
await this.database.initialize();
if (!this.isMCPMode) {
console.log('Database initialized successfully');
}
// Start HTTP server if port is configured and not in MCP-only mode
if (config.server.port && !this.isMCPMode) {
try {
await this.startHTTPServer();
}
catch (error) {
console.warn('HTTP server startup failed, continuing with MCP-only mode:', error);
}
}
else if (this.isMCPMode) {
// Suppress startup messages in MCP mode to avoid stdout interference
}
// Start MCP server transport
const transport = new StdioServerTransport();
await this.server.connect(transport);
// Only log configuration in non-MCP mode to avoid stdout interference
if (!this.isMCPMode) {
console.log('ControlAI MCP Server started successfully');
console.log('Configuration:');
console.log(`- Database: ${config.database.path || 'Default location'}`);
console.log(`- AI Provider: ${config.ai.provider}`);
console.log(`- HTTP Server: ${config.server.port ? `http://${config.server.host}:${config.server.port}` : 'Disabled'}`);
console.log(`- WebSocket: ${config.websocket.enabled ? 'Enabled' : 'Disabled'}`);
}
}
catch (error) {
console.error('Failed to start ControlAI MCP Server:', error);
process.exit(1);
}
}
async shutdown() {
console.log('Shutting down ControlAI MCP Server...');
if (this.database) {
await this.database.close();
}
if (this.wsServer) {
this.wsServer.close();
}
console.log('ControlAI MCP Server shut down complete');
}
}
// Handle graceful shutdown
let server = null;
process.on('SIGINT', async () => {
if (server) {
await server.shutdown();
}
process.exit(0);
});
process.on('SIGTERM', async () => {
if (server) {
await server.shutdown();
}
process.exit(0);
});
// Check command line arguments
const args = process.argv.slice(2);
const isHelpMode = args.includes('--help') || args.includes('-h');
if (isHelpMode) {
console.log(`
🤖 ControlAI MCP Server v1.0.1
Enterprise AI Project Management MCP Server for multi-agent coordination
USAGE:
glass-mcp Start the MCP server
glass-mcp --help Show this help message
FEATURES:
🎯 Intelligent project creation with AI analysis
📊 Dynamic task breakdown and assignment
🤝 Multi-agent coordination and conflict resolution
📈 Real-time dashboard and progress tracking
🔧 Enterprise-grade SQLite persistence
⚡ WebSocket real-time updates
MCP TOOLS:
create_project Create projects with intelligent analysis
analyze_plan Break down project plans into tasks
assign_task Intelligently assign tasks to agents
get_project_status Get comprehensive project status
update_task_status Update task status with notifications
register_agent Register new AI agents
get_dashboard_data Get real-time dashboard data
ENVIRONMENT VARIABLES:
AZURE_OPENAI_API_KEY Azure OpenAI API key
AZURE_OPENAI_ENDPOINT Azure OpenAI endpoint URL
AZURE_OPENAI_DEPLOYMENT_NAME Deployment name (default: gpt-4o)
AZURE_OPENAI_API_VERSION API version (default: 2024-02-01)
DOTENV_CONFIG_PATH Path to .env file (optional)
For more information: https://github.com/codai-ecosystem/codai-project
`);
process.exit(0);
}
else {
// Start the server only if not in help mode
server = new ControlAIMCPServer();
server.start().catch(error => {
console.error('Fatal error starting server:', error);
process.exit(1);
});
}
//# sourceMappingURL=server.js.map