@boundless-oss/atlas
Version:
Atlas - MCP Server for comprehensive startup project management
220 lines (175 loc) • 7.41 kB
text/typescript
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { CallToolRequestSchema } from '@modelcontextprotocol/sdk/types.js';
import { setupProductRoadmapTools } from './tools.js';
import { SQLiteManager } from '../../storage/sqlite-manager.js';
import { RequestContext } from '../../core/types.js';
/**
* Adapter to convert 12-factor tools to MCP SDK format
*/
export async function setupProductRoadmapMCP(server: Server, dbManager: SQLiteManager, projectId: string = 'default') {
const toolRegistration = await setupProductRoadmapTools();
// Register the composite handler for all tools
server.setRequestHandler(CallToolRequestSchema, async (request) => {
const { name, arguments: args } = request.params;
if (!args) {
throw new Error('No arguments provided');
}
// Find the matching tool
const tool = toolRegistration.tools.find(t => t.name === name);
if (!tool) {
throw new Error(`Unknown tool: ${name}`);
}
// Create request context
const context: RequestContext = {
toolName: name,
requestId: `req-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
timestamp: Date.now(),
projectId,
db: dbManager
};
// Execute the tool
const result = await tool.execute(args as any, context);
// Convert result to MCP format
if (result.success) {
return {
content: [{
type: 'text',
text: formatResultText(name, result.data)
}]
};
} else {
// Return error in MCP format
return {
isError: true,
content: [{
type: 'text',
text: `Error: ${result.error?.message || 'Unknown error'}\n\nDetails: ${JSON.stringify(result.error?.details || {}, null, 2)}`
}]
};
}
});
return toolRegistration;
}
/**
* Format result data as text for MCP response
*/
function formatResultText(toolName: string, data: any): string {
switch (toolName) {
case 'create_roadmap':
return `✅ Product roadmap "${data.roadmap.name}" created successfully!
**Vision**: ${data.roadmap.vision}
**Time Horizon**: ${data.roadmap.timeHorizon}
**Owner**: ${data.roadmap.owner}
**Status**: ${data.roadmap.status}
**Roadmap ID**: ${data.roadmap.id}
${data.nextSteps ? `Next steps:\n${data.nextSteps.map((s: string, i: number) => `${i + 1}. ${s}`).join('\n')}` : ''}`;
case 'add_roadmap_theme':
return `✅ Theme "${data.theme.name}" added to roadmap!
**Description**: ${data.theme.description}
**Priority**: ${data.theme.priority}
**Timeframe**: ${data.theme.timeframe.startQuarter} - ${data.theme.timeframe.endQuarter}
**Objectives**:
${data.theme.objectives.map((obj: string, i: number) => `${i + 1}. ${obj}`).join('\n')}
**Theme ID**: ${data.theme.id}
${data.nextSteps ? `Next: ${data.nextSteps.join(', ')}` : ''}`;
case 'create_initiative':
return `✅ Initiative "${data.initiative.title}" created!
**Description**: ${data.initiative.description}
**Estimated Value**:
- User Impact: ${data.initiative.value.userImpact}
- Revenue Impact: $${data.initiative.value.revenueImpact.toLocaleString()}
- Cost Savings: $${data.initiative.value.costSavings.toLocaleString()}
- Strategic Value: ${data.initiative.value.strategicValue}/10
- Customer Satisfaction: ${data.initiative.value.customerSatisfaction > 0 ? '+' : ''}${data.initiative.value.customerSatisfaction} NPS
**Estimated Effort**: ${data.initiative.totalEffortWeeks} weeks total
- Development: ${data.initiative.effort.developmentWeeks} weeks
- Design: ${data.initiative.effort.designWeeks} weeks
- QA: ${data.initiative.effort.qaWeeks} weeks
- Confidence: ${data.initiative.effort.confidence}
**Risks**: ${data.initiative.risks.length} identified
**Initiative ID**: ${data.initiative.id}`;
case 'add_feature':
return `✅ Feature "${data.feature.name}" added!
**Description**: ${data.feature.description}
**Business Value**: ${data.feature.businessValue.score}/100
**Technical Complexity**: ${data.feature.technicalComplexity}
**Status**: ${data.feature.status}
${data.feature.targetRelease ? `**Target Release**: ${data.feature.targetRelease}` : ''}
**Value Rationale**: ${data.feature.businessValue.rationale}
**Success Metrics**:
${data.feature.businessValue.metrics.map((metric: string, i: number) => `${i + 1}. ${metric}`).join('\n')}
**Feature ID**: ${data.feature.id}`;
case 'list_roadmaps':
if (data.roadmaps.length === 0) {
return 'No product roadmaps found. Create one using create_roadmap.';
}
return `📋 Product Roadmaps (${data.roadmaps.length}):
${data.roadmaps.map((r: any) => `
**${r.name}** (${r.id})
- Vision: ${r.vision}
- Time Horizon: ${r.timeHorizon}
- Status: ${r.status}
- Owner: ${r.owner}
- Themes: ${r.themes}
- Updated: ${r.updatedAt}`
).join('\n')}`;
case 'get_roadmap':
const roadmap = data.roadmap;
return `# ${roadmap.name}
**Vision**: ${roadmap.vision}
**Time Horizon**: ${roadmap.timeHorizon}
**Status**: ${roadmap.status}
**Owner**: ${roadmap.owner}
## Overview
- Themes: ${roadmap.statistics.themes || 0}
- Initiatives: ${roadmap.statistics.initiatives || 0}
- Features: ${roadmap.statistics.features || 0}
- Milestones: ${roadmap.statistics.milestones || 0}
- Releases: ${roadmap.statistics.releases || 0}
## Themes
${roadmap.themes.map((theme: any) => `
### ${theme.name}
- Priority: ${theme.priority}
- Timeframe: ${theme.timeframe}
- Status: ${theme.status}
- Initiatives: ${theme.initiatives}
- Features: ${theme.features}`
).join('\n')}
## Upcoming Milestones
${roadmap.upcomingMilestones.length > 0 ?
roadmap.upcomingMilestones.map((m: any) => `- ${m.name} (${m.date})`).join('\n') :
'No upcoming milestones'}`;
case 'prioritize_features':
return `📊 Feature Prioritization Results (${data.method.toUpperCase()})
**Scope**: ${data.scope}
**Features Analyzed**: ${data.totalFeatures}
**Top Features**:
${data.topFeatures.map((f: any) =>
`${f.rank}. **${f.name}**\n Score: ${f.score.toFixed(2)}\n ${f.rationale}\n Status: ${f.currentStatus}\n Complexity: ${f.complexity}`
).join('\n\n')}
${data.insights ? `\n**Insights**:\n${data.insights.map((i: string) => `- ${i}`).join('\n')}` : ''}`;
case 'get_roadmap_health':
const healthEmoji: Record<string, string> = {
'excellent': '🟢',
'good': '🟡',
'at-risk': '🟠',
'critical': '🔴'
};
return `${healthEmoji[data.health] || '⚪'} Roadmap Health: **${data.health.toUpperCase()}**
**Metrics**:
- Features Planned: ${data.metrics.featuresPlanned}
- Features Completed: ${data.metrics.featuresCompleted} (${
data.metrics.featuresPlanned > 0 ?
Math.round((data.metrics.featuresCompleted / data.metrics.featuresPlanned) * 100) : 0
}%)
- Active Initiatives: ${data.metrics.initiativesActive}
- Velocity Trend: ${data.metrics.velocityTrend}
- On-Time Delivery: ${data.metrics.onTimeDelivery}%
- Value Delivered: ${data.metrics.valueDelivered}%
${data.risks.length > 0 ? `**Risks**:\n${data.risks.map((r: string) => `- ⚠️ ${r}`).join('\n')}` : ''}
${data.recommendations.length > 0 ? `\n**Recommendations**:\n${data.recommendations.map((r: string) => `- 💡 ${r}`).join('\n')}` : ''}`;
default:
// Generic formatting for other tools
return data.message || JSON.stringify(data, null, 2);
}
}