@boundless-oss/atlas
Version:
Atlas - MCP Server for comprehensive startup project management
651 lines (511 loc) • 20.9 kB
text/typescript
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { RoadmapManager } from './roadmap-manager.js';
import { PrioritizationEngine } from './prioritization-engine.js';
import {
ProductRoadmap,
RoadmapTheme,
Initiative,
Feature,
CreateRoadmapOptions,
AddThemeOptions,
CreateInitiativeOptions,
AddFeatureOptions,
ValueMetrics,
EffortEstimate,
BusinessValue,
Risk
} from './types.js';
interface WizardState {
step: number;
type: 'roadmap' | 'theme' | 'initiative' | 'feature';
data: any;
roadmapId?: string;
themeId?: string;
initiativeId?: string;
}
export class RoadmapWizard {
private roadmapManager: RoadmapManager;
private prioritizationEngine: PrioritizationEngine;
private wizardStates: Map<string, WizardState> = new Map();
constructor(roadmapManager: RoadmapManager, prioritizationEngine: PrioritizationEngine) {
this.roadmapManager = roadmapManager;
this.prioritizationEngine = prioritizationEngine;
}
async startRoadmapWizard(sessionId: string): Promise<string> {
const state: WizardState = {
step: 1,
type: 'roadmap',
data: {}
};
this.wizardStates.set(sessionId, state);
return `🚀 **Product Roadmap Creation Wizard**
Let's create a comprehensive product roadmap for your business!
**Step 1/4: Basic Information**
Please provide the following information:
1. **Product/Roadmap Name**: What is the name of your product or roadmap?
2. **Vision Statement**: What is the long-term vision for this product? (2-3 sentences)
3. **Time Horizon**: How far ahead are you planning?
- quarterly (3-12 months)
- annual (1-2 years)
- multi-year (2-5 years)
4. **Your Name/Role**: Who will be the owner of this roadmap?
Example response:
- Name: "Customer Portal 2.0"
- Vision: "Create a self-service portal that empowers customers to manage their accounts, access resources, and get support without human intervention."
- Time Horizon: annual
- Owner: "Jane Smith, Product Manager"`;
}
async continueRoadmapWizard(sessionId: string, input: any): Promise<string> {
const state = this.wizardStates.get(sessionId);
if (!state || state.type !== 'roadmap') {
return 'No active roadmap wizard found. Start one with start_roadmap_wizard.';
}
switch (state.step) {
case 1:
// Parse basic information
state.data.name = input.name;
state.data.vision = input.vision;
state.data.timeHorizon = input.timeHorizon;
state.data.owner = input.owner;
state.step = 2;
return `✅ Great! Basic information captured.
**Step 2/4: Key Stakeholders**
Who are the key stakeholders for this roadmap? Please list their names and roles.
Examples:
- Engineering Lead: John Doe
- Sales Director: Sarah Johnson
- Customer Success Manager: Mike Chen
- Marketing Head: Lisa Brown
You can provide as many stakeholders as needed, or type "none" if you want to skip this step.`;
case 2:
// Parse stakeholders
if (input.stakeholders && input.stakeholders !== 'none') {
state.data.stakeholders = Array.isArray(input.stakeholders)
? input.stakeholders
: [input.stakeholders];
} else {
state.data.stakeholders = [];
}
state.step = 3;
return `✅ Stakeholders added!
**Step 3/4: Strategic Context**
To help structure your roadmap effectively, please answer:
1. **Primary Goal**: What is the main business goal this roadmap serves?
- Revenue growth
- Cost reduction
- Market expansion
- Customer satisfaction
- Technical modernization
- Other (please specify)
2. **Key Challenges**: What are the main challenges you're trying to solve? (list 2-3)
3. **Success Metrics**: How will you measure success? (list 2-3 metrics)
Example:
- Goal: Customer satisfaction
- Challenges: High support ticket volume, Limited self-service options, Poor mobile experience
- Metrics: Support tickets reduced by 50%, Customer satisfaction score > 4.5, Self-service adoption > 70%`;
case 3:
// Parse strategic context
state.data.primaryGoal = input.goal;
state.data.challenges = input.challenges;
state.data.successMetrics = input.metrics;
state.step = 4;
const timelineHelp = state.data.timeHorizon === 'quarterly'
? 'next 4 quarters (e.g., Q1 2024, Q2 2024, etc.)'
: state.data.timeHorizon === 'annual'
? 'next 2 years by quarter'
: 'next 3-5 years by year';
return `✅ Strategic context captured!
**Step 4/4: Initial Themes**
Let's define 2-3 strategic themes for your roadmap. Themes are high-level focus areas that group related initiatives.
For each theme, provide:
1. **Name**: Short, memorable name
2. **Description**: What this theme encompasses
3. **Priority**: must-have, should-have, or nice-to-have
4. **Timeframe**: When will you work on this (${timelineHelp})
Example Theme:
- Name: "Self-Service Excellence"
- Description: "Build comprehensive self-service capabilities to reduce support burden and improve customer satisfaction"
- Priority: must-have
- Timeframe: Q1 2024 - Q4 2024
Please provide at least 2 themes to complete the wizard.`;
case 4:
// Create the roadmap
const roadmapOptions: CreateRoadmapOptions = {
name: state.data.name,
vision: state.data.vision,
timeHorizon: state.data.timeHorizon,
owner: state.data.owner,
stakeholders: state.data.stakeholders
};
const roadmap = await this.roadmapManager.createRoadmap(roadmapOptions);
// Add initial themes if provided
let themesAdded = 0;
if (input.themes && Array.isArray(input.themes)) {
for (const themeInput of input.themes) {
try {
const themeOptions: AddThemeOptions = {
roadmapId: roadmap.id,
name: themeInput.name,
description: themeInput.description,
objectives: this.deriveObjectivesFromContext(state.data, themeInput),
priority: themeInput.priority,
startQuarter: themeInput.startQuarter || this.getCurrentQuarter(),
endQuarter: themeInput.endQuarter || this.getQuarterOffset(this.getCurrentQuarter(), 4)
};
await this.roadmapManager.addTheme(themeOptions);
themesAdded++;
} catch (error) {
console.error('Error adding theme:', error);
}
}
}
// Clear wizard state
this.wizardStates.delete(sessionId);
return `🎉 **Roadmap Created Successfully!**
**${roadmap.name}**
- Vision: ${roadmap.vision}
- Time Horizon: ${roadmap.timeHorizon}
- Owner: ${roadmap.owner}
- Stakeholders: ${roadmap.stakeholders.length}
- Initial Themes: ${themesAdded}
**Roadmap ID**: ${roadmap.id}
**Strategic Context Captured**:
- Primary Goal: ${state.data.primaryGoal}
- Key Challenges: ${state.data.challenges.join(', ')}
- Success Metrics: ${state.data.successMetrics.join(', ')}
**Next Steps**:
1. Add more themes using \`add_roadmap_theme\`
2. Create initiatives within themes using \`create_initiative_wizard\`
3. Add features to initiatives
4. Set up milestones and releases
5. Prioritize features using \`prioritize_features\`
Would you like to start adding initiatives to your themes? Use \`create_initiative_wizard\` to begin!`;
default:
return 'Invalid wizard state. Please restart the wizard.';
}
}
async startInitiativeWizard(sessionId: string, roadmapId: string, themeId: string): Promise<string> {
const state: WizardState = {
step: 1,
type: 'initiative',
data: {},
roadmapId,
themeId
};
this.wizardStates.set(sessionId, state);
const roadmap = await this.roadmapManager.getRoadmap(roadmapId);
const themeDetails = await this.roadmapManager.getThemeDetails(themeId);
if (!roadmap || !themeDetails) {
this.wizardStates.delete(sessionId);
return 'Invalid roadmap or theme ID provided.';
}
return `🎯 **Initiative Creation Wizard**
**Roadmap**: ${roadmap.name}
**Theme**: ${themeDetails.theme.name}
Let's create a strategic initiative within this theme.
**Step 1/5: Initiative Overview**
Please provide:
1. **Title**: A clear, action-oriented title for the initiative
2. **Description**: What this initiative will accomplish (2-3 sentences)
3. **Key Outcomes**: What specific outcomes will this deliver? (list 2-3)
Example:
- Title: "Implement Smart Search and Knowledge Base"
- Description: "Build an AI-powered search system and comprehensive knowledge base to help customers find answers quickly without contacting support."
- Outcomes: ["Instant answers to common questions", "Reduced support ticket volume", "Improved customer satisfaction"]`;
}
async continueInitiativeWizard(sessionId: string, input: any): Promise<string> {
const state = this.wizardStates.get(sessionId);
if (!state || state.type !== 'initiative') {
return 'No active initiative wizard found. Start one with create_initiative_wizard.';
}
switch (state.step) {
case 1:
state.data.title = input.title;
state.data.description = input.description;
state.data.outcomes = input.outcomes;
state.step = 2;
return `✅ Initiative overview captured!
**Step 2/5: Business Value Assessment**
Let's quantify the value this initiative will deliver:
1. **User Impact**: How significant is the impact on users?
- low: Minor improvement
- medium: Noticeable improvement
- high: Significant improvement
- critical: Game-changing impact
2. **Revenue Impact**: Estimated annual revenue impact in dollars
3. **Cost Savings**: Estimated annual cost savings in dollars
4. **Strategic Value**: How well does this align with company strategy? (1-10, where 10 is perfectly aligned)
Example:
- User Impact: high
- Revenue Impact: 500000 (for $500k)
- Cost Savings: 200000 (for $200k)
- Strategic Value: 8`;
case 2:
const customerSat = await this.estimateCustomerSatisfactionImpact(input.userImpact);
state.data.estimatedValue = {
userImpact: input.userImpact,
revenueImpact: input.revenueImpact,
costSavings: input.costSavings,
strategicValue: input.strategicValue,
customerSatisfaction: customerSat
} as ValueMetrics;
state.step = 3;
return `✅ Business value assessed!
**Step 3/5: Effort Estimation**
Now let's estimate the effort required:
1. **Development Weeks**: How many weeks of development effort?
2. **Design Weeks**: How many weeks of design effort?
3. **QA/Testing Weeks**: How many weeks of QA effort?
4. **Confidence Level**: How confident are you in these estimates?
- low: Very uncertain, could be 50%+ off
- medium: Reasonably confident, within 25%
- high: Very confident, within 10%
Example:
- Development: 12
- Design: 4
- QA: 3
- Confidence: medium`;
case 3:
state.data.estimatedEffort = {
developmentWeeks: input.developmentWeeks,
designWeeks: input.designWeeks,
qaWeeks: input.qaWeeks,
confidence: input.confidence
} as EffortEstimate;
state.step = 4;
return `✅ Effort estimated!
**Step 4/5: Risk Assessment**
What are the main risks for this initiative? For each risk, provide:
1. **Description**: What could go wrong?
2. **Likelihood**: low, medium, or high
3. **Impact**: low, medium, or high
4. **Mitigation**: How will you address this risk?
Please provide 1-3 risks, or type "none" if there are no significant risks.
Example Risk:
- Description: "Integration with legacy system may be complex"
- Likelihood: medium
- Impact: high
- Mitigation: "Conduct technical spike early, plan for gradual migration"`;
case 4:
if (input.risks && input.risks !== 'none') {
state.data.risks = Array.isArray(input.risks) ? input.risks : [input.risks];
state.data.risks = state.data.risks.map((r: any, index: number) => ({
id: `risk-${Date.now()}-${index}`,
description: r.description,
likelihood: r.likelihood,
impact: r.impact,
mitigation: r.mitigation
}));
} else {
state.data.risks = [];
}
state.step = 5;
return `✅ Risks assessed!
**Step 5/5: Initial Features**
Let's define 2-3 key features for this initiative. For each feature:
1. **Name**: Feature name
2. **Description**: What it does
3. **Value Score**: Business value (1-100)
4. **Complexity**: technical complexity (low, medium, high, very-high)
Example Feature:
- Name: "AI-Powered Search"
- Description: "Natural language search that understands user intent and provides relevant results"
- Value: 85
- Complexity: high
Please provide at least 2 features to complete the initiative.`;
case 5:
// Create the initiative
const initiativeOptions: CreateInitiativeOptions = {
roadmapId: state.roadmapId!,
themeId: state.themeId!,
title: state.data.title,
description: state.data.description,
estimatedValue: state.data.estimatedValue,
estimatedEffort: state.data.estimatedEffort,
risks: state.data.risks
};
const initiative = await this.roadmapManager.createInitiative(initiativeOptions);
// Add initial features if provided
let featuresAdded = 0;
if (input.features && Array.isArray(input.features)) {
for (const featureInput of input.features) {
try {
const businessValue: BusinessValue = {
score: featureInput.value,
rationale: `Derived from initiative goals: ${state.data.outcomes.join(', ')}`,
metrics: this.deriveFeatureMetrics(featureInput, state.data.outcomes)
};
const featureOptions: AddFeatureOptions = {
roadmapId: state.roadmapId!,
initiativeId: initiative.id,
name: featureInput.name,
description: featureInput.description,
businessValue,
technicalComplexity: featureInput.complexity
};
await this.roadmapManager.addFeature(featureOptions);
featuresAdded++;
} catch (error) {
console.error('Error adding feature:', error);
}
}
}
// Calculate total effort
const totalEffort = state.data.estimatedEffort.developmentWeeks +
state.data.estimatedEffort.designWeeks +
state.data.estimatedEffort.qaWeeks;
// Clear wizard state
this.wizardStates.delete(sessionId);
return `🎉 **Initiative Created Successfully!**
**${initiative.title}**
${initiative.description}
**Key Outcomes**:
${state.data.outcomes.map((o: string, i: number) => `${i + 1}. ${o}`).join('\n')}
**Value Assessment**:
- User Impact: ${initiative.value.userImpact}
- Revenue Impact: $${initiative.value.revenueImpact.toLocaleString()}/year
- Cost Savings: $${initiative.value.costSavings.toLocaleString()}/year
- Strategic Value: ${initiative.value.strategicValue}/10
**Effort Estimate**: ${totalEffort} weeks (${initiative.effort.confidence} confidence)
**Risks Identified**: ${initiative.risks.length}
**Initial Features**: ${featuresAdded}
**Initiative ID**: ${initiative.id}
**Next Steps**:
1. Add more features using \`add_feature\`
2. Link to epics in your agile management system
3. Prioritize features using \`prioritize_features\`
4. Assign to a release using \`plan_release\`
Great work! Your initiative is now part of the roadmap.`;
default:
return 'Invalid wizard state. Please restart the wizard.';
}
}
async startQuickFeatureWizard(
sessionId: string,
roadmapId: string,
initiativeId: string
): Promise<string> {
const state: WizardState = {
step: 1,
type: 'feature',
data: {},
roadmapId,
initiativeId
};
this.wizardStates.set(sessionId, state);
return `⚡ **Quick Feature Addition**
Let's quickly add a feature to your initiative.
Please provide:
1. **Name**: Feature name
2. **Description**: What it does (1-2 sentences)
3. **Value**: Business value score (1-100)
4. **Complexity**: low, medium, high, or very-high
5. **Success Metrics**: How will you measure success? (list 2-3 metrics)
Example:
- Name: "Real-time Notifications"
- Description: "Push notifications for important account updates and alerts"
- Value: 75
- Complexity: medium
- Metrics: ["User engagement up 30%", "Response time < 2 seconds", "90% delivery rate"]`;
}
async continueQuickFeatureWizard(sessionId: string, input: any): Promise<string> {
const state = this.wizardStates.get(sessionId);
if (!state || state.type !== 'feature') {
return 'No active feature wizard found.';
}
const businessValue: BusinessValue = {
score: input.value,
rationale: input.rationale || 'Added via quick wizard',
metrics: input.metrics || []
};
const featureOptions: AddFeatureOptions = {
roadmapId: state.roadmapId!,
initiativeId: state.initiativeId!,
name: input.name,
description: input.description,
businessValue,
technicalComplexity: input.complexity,
targetRelease: input.targetRelease
};
const feature = await this.roadmapManager.addFeature(featureOptions);
this.wizardStates.delete(sessionId);
return `✅ **Feature Added Successfully!**
**${feature.name}**
${feature.description}
- Business Value: ${feature.businessValue.score}/100
- Technical Complexity: ${feature.technicalComplexity}
- Success Metrics: ${feature.businessValue.metrics.length}
**Feature ID**: ${feature.id}
Feature has been added to the initiative!`;
}
// Helper methods
private getCurrentQuarter(): string {
const now = new Date();
const quarter = Math.floor(now.getMonth() / 3) + 1;
const year = now.getFullYear();
return `Q${quarter} ${year}`;
}
private getQuarterOffset(startQuarter: string, offset: number): string {
const [q, year] = startQuarter.split(' ');
const quarterNum = parseInt(q.substring(1));
const yearNum = parseInt(year);
let newQuarter = quarterNum + offset;
let newYear = yearNum;
while (newQuarter > 4) {
newQuarter -= 4;
newYear++;
}
return `Q${newQuarter} ${newYear}`;
}
private deriveObjectivesFromContext(contextData: any, theme: any): string[] {
const objectives: string[] = [];
// Add objective based on primary goal
if (contextData.primaryGoal) {
objectives.push(`Drive ${contextData.primaryGoal} through ${theme.name.toLowerCase()}`);
}
// Add objectives based on challenges
if (contextData.challenges && contextData.challenges.length > 0) {
objectives.push(`Address key challenge: ${contextData.challenges[0]}`);
}
// Add metric-based objective
if (contextData.successMetrics && contextData.successMetrics.length > 0) {
objectives.push(`Achieve target: ${contextData.successMetrics[0]}`);
}
// Add theme-specific objective
objectives.push(`Deliver ${theme.priority} capabilities for ${theme.name}`);
return objectives;
}
private async estimateCustomerSatisfactionImpact(userImpact: string): Promise<number> {
const impactMap = {
'low': 2,
'medium': 5,
'high': 10,
'critical': 20
};
return impactMap[userImpact as keyof typeof impactMap] || 5;
}
private deriveFeatureMetrics(feature: any, outcomes: string[]): string[] {
const metrics: string[] = [];
// Add feature-specific metric
metrics.push(`${feature.name} adoption rate > 60%`);
// Add outcome-based metric
if (outcomes && outcomes.length > 0) {
metrics.push(`Contribute to: ${outcomes[0]}`);
}
// Add value-based metric
if (feature.value > 80) {
metrics.push('User satisfaction score > 4.5/5');
} else if (feature.value > 60) {
metrics.push('User satisfaction score > 4.0/5');
} else {
metrics.push('Feature completion rate > 80%');
}
return metrics;
}
getActiveWizards(): string[] {
return Array.from(this.wizardStates.keys());
}
cancelWizard(sessionId: string): boolean {
return this.wizardStates.delete(sessionId);
}
}