mcp-product-manager
Version:
MCP Orchestrator for task and project management with web interface
210 lines • 8.95 kB
JavaScript
// orchestratorPurpose.js - Middleware for purpose-driven orchestration
import { query, run } from '../utils/database.js';
// Purpose-driven middleware for orchestrator agents
export const orchestratorPurposeMiddleware = async (req, res, next) => {
// Only apply to orchestrator-specific endpoints or when agent type is orchestrator
const isOrchestratorEndpoint = req.path.includes('/orchestrate') ||
req.path.includes('/review') ||
req.path.includes('/approve');
const agentType = req.headers['x-agent-type'] || req.body.agent_type;
const isOrchestratorAgent = agentType === 'orchestrator';
if (!isOrchestratorEndpoint && !isOrchestratorAgent) {
return next();
}
const project = req.params.project || req.body.project || req.query.project;
if (!project) {
return next();
}
try {
// Get project purpose
const projectData = await query('SELECT purpose, purpose_updated_at, purpose_context FROM projects WHERE name = ?', [project]);
if (!projectData || projectData.length === 0) {
// Create project entry if it doesn't exist
await run('INSERT INTO projects (name, created_at) VALUES (?, datetime("now"))', [project]);
req.projectPurpose = null;
}
else {
req.projectPurpose = {
purpose: projectData[0].purpose,
context: projectData[0].purpose_context,
updated_at: projectData[0].purpose_updated_at
};
}
// Add purpose filter to response
const originalJson = res.json;
res.json = function (data) {
if (req.projectPurpose && req.projectPurpose.purpose && data.body) {
// Add purpose context to responses
data.body.purpose_context = {
current_purpose: req.projectPurpose.purpose,
purpose_filter: `All decisions should align with: ${req.projectPurpose.purpose}`,
evaluation_criteria: [
'Does this task/decision advance the stated purpose?',
'Is this the most efficient path toward the purpose?',
'Are there higher-priority items that better serve the purpose?'
]
};
// If returning tasks, add purpose scoring
if (data.body.tasks && Array.isArray(data.body.tasks)) {
data.body.tasks = data.body.tasks.map(task => ({
...task,
purpose_alignment: calculatePurposeAlignment(task, req.projectPurpose.purpose)
}));
}
}
return originalJson.call(this, data);
};
next();
}
catch (err) {
console.error('Purpose middleware error:', err);
next(); // Continue even if purpose lookup fails
}
};
// Calculate how well a task aligns with the stated purpose
const calculatePurposeAlignment = (task, purpose) => {
if (!purpose)
return 0.5; // Neutral if no purpose defined
// Simple keyword matching for now - can be enhanced with better NLP
const purposeKeywords = purpose.toLowerCase().split(/\s+/);
const taskKeywords = (task.description + ' ' + (task.technical_context || '')).toLowerCase().split(/\s+/);
let matches = 0;
for (const keyword of purposeKeywords) {
if (keyword.length > 3 && taskKeywords.includes(keyword)) {
matches++;
}
}
const score = Math.min(matches / purposeKeywords.length, 1);
// Boost score for critical/high priority tasks
if (task.priority === 'critical')
return Math.min(score + 0.3, 1);
if (task.priority === 'high')
return Math.min(score + 0.1, 1);
return score;
};
// Endpoint to update project purpose
export const updateProjectPurpose = async (req, res) => {
const { project, purpose, context, updated_by } = req.body;
if (!project || !purpose) {
return res.status(400).json({
error: 'Project and purpose are required'
});
}
try {
const result = await run(`UPDATE projects
SET purpose = ?,
purpose_context = ?,
purpose_updated_at = datetime('now'),
purpose_updated_by = ?,
updated_at = datetime('now')
WHERE name = ?`, [purpose, context || null, updated_by || 'orchestrator', project]);
if (result.changes === 0) {
// Project doesn't exist, create it
await run(`INSERT INTO projects (name, purpose, purpose_context, purpose_updated_at, purpose_updated_by, created_at)
VALUES (?, ?, ?, datetime('now'), ?, datetime('now'))`, [project, purpose, context || null, updated_by || 'orchestrator']);
}
// Log purpose change as orchestrator decision
await run(`INSERT INTO orchestrator_decisions (
id, project, orchestrator_agent, decision_type,
rationale, purpose_context, created_at
) VALUES (?, ?, ?, ?, ?, ?, datetime('now'))`, [
`PURPOSE-${Date.now()}`,
project,
updated_by || 'orchestrator',
'purpose_updated',
`Updated project purpose to: ${purpose}`,
purpose
]);
res.json({
success: true,
message: 'Project purpose updated',
purpose: {
project,
purpose,
context,
updated_by: updated_by || 'orchestrator'
}
});
}
catch (err) {
console.error('Failed to update purpose:', err);
res.status(500).json({
error: 'Failed to update project purpose',
details: err.message
});
}
};
// Analyze project alignment with purpose
export const analyzeProjectAlignment = async (req, res) => {
const { project } = req.params;
try {
// Get project purpose
const projectData = await query('SELECT purpose FROM projects WHERE name = ?', [project]);
if (!projectData || !projectData[0]?.purpose) {
return res.status(400).json({
error: 'Project purpose not defined'
});
}
const purpose = projectData[0].purpose;
// Get all active tasks
const tasks = await query(`SELECT id, description, priority, status, category, technical_context
FROM tasks
WHERE project = ? AND status NOT IN ('completed', 'archived')`, [project]);
// Calculate alignment scores
const alignmentAnalysis = tasks.map(task => ({
task_id: task.id,
description: task.description,
status: task.status,
priority: task.priority,
alignment_score: calculatePurposeAlignment(task, purpose),
recommendation: getAlignmentRecommendation(task, calculatePurposeAlignment(task, purpose))
}));
// Sort by alignment score
alignmentAnalysis.sort((a, b) => b.alignment_score - a.alignment_score);
// Calculate overall metrics
const avgAlignment = alignmentAnalysis.reduce((sum, t) => sum + t.alignment_score, 0) / alignmentAnalysis.length;
const highAlignmentTasks = alignmentAnalysis.filter(t => t.alignment_score >= 0.7).length;
const lowAlignmentTasks = alignmentAnalysis.filter(t => t.alignment_score < 0.3).length;
res.json({
success: true,
project,
purpose,
metrics: {
total_tasks: alignmentAnalysis.length,
average_alignment: avgAlignment.toFixed(2),
high_alignment_tasks: highAlignmentTasks,
low_alignment_tasks: lowAlignmentTasks
},
recommendations: {
prioritize: alignmentAnalysis.filter(t => t.recommendation === 'prioritize').slice(0, 5),
deprioritize: alignmentAnalysis.filter(t => t.recommendation === 'deprioritize').slice(0, 5),
clarify: alignmentAnalysis.filter(t => t.recommendation === 'clarify').slice(0, 5)
},
full_analysis: alignmentAnalysis
});
}
catch (err) {
console.error('Failed to analyze alignment:', err);
res.status(500).json({
error: 'Failed to analyze project alignment',
details: err.message
});
}
};
const getAlignmentRecommendation = (task, score) => {
if (score >= 0.7) {
return task.priority === 'critical' || task.priority === 'high'
? 'maintain'
: 'prioritize';
}
else if (score < 0.3) {
return task.status === 'in_progress'
? 'complete_and_archive'
: 'deprioritize';
}
else {
return 'clarify';
}
};
export default orchestratorPurposeMiddleware;
//# sourceMappingURL=orchestratorPurpose.js.map