Skip to main content
Glama

Vibe-Coder MCP Server

index.ts51.8 kB
#!/usr/bin/env node /** * @file Vibe-Coder MCP Server * @version 0.1.0 * * This MCP server implements a structured development workflow that helps * LLM-based coders build features in an organized, clean, and safe manner. * * Core functionalities: * - Feature request clarification through iterative questioning * - PRD and implementation plan generation * - Phased development with tasks and status tracking */ import { Server } from "@modelcontextprotocol/sdk/server/index.js"; import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; import { CallToolRequestSchema, ListResourcesRequestSchema, ListToolsRequestSchema, ReadResourceRequestSchema, ListPromptsRequestSchema, GetPromptRequestSchema, } from "@modelcontextprotocol/sdk/types.js"; // Import core modules import { Feature, FeatureStorage, PhaseStatus, Phase, Task, ClarificationResponse } from './types.js'; import { features, storeFeature, getFeature, updateFeature, listFeatures } from './storage.js'; import { DEFAULT_CLARIFICATION_QUESTIONS as CLARIFICATION_QUESTIONS, getNextClarificationQuestion, addClarificationResponse, formatClarificationResponses, isClarificationComplete, getClarificationStatus } from './clarification.js'; import { generatePRD, generateImplementationPlan, extractObjectivesFromClarifications, extractRequirementsFromClarifications, extractTechnicalSpecsFromClarifications } from './documentation.js'; import { createPhase, getPhase, updatePhaseStatus, getNextPhaseStatus, validatePhaseTransition, addTask, updateTaskStatus } from './phases.js'; import { generateId, createFeatureObject, createPhaseObject, createTaskObject, generateFeatureProgressSummary, isValidPhaseStatus } from './utils.js'; import { validateFeatureId, validatePhaseId, validateTaskId, validateFeaturePhaseTask, validateRequiredText, validatePhaseStatusValue } from './validators.js'; /** * Type alias for a note object. */ type Note = { title: string, content: string }; /** * Simple in-memory storage for notes. * In a real implementation, this would likely be backed by a database. */ const notes: { [id: string]: Note } = { "1": { title: "First Note", content: "This is note 1" }, "2": { title: "Second Note", content: "This is note 2" } }; /** * Create an MCP server with capabilities for resources, tools, and prompts */ const server = new Server( { name: "Vibe-Coder MCP Server", version: "0.1.0", }, { capabilities: { resources: {}, // Expose resources for features, PRDs, and implementation plans tools: {}, // Provide tools for feature clarification and development prompts: {}, // Supply prompts for guiding the development process }, } ); /** * Handler for listing available resources. * Exposes: * - A list of all features * - Individual feature details * - PRD and implementation plan documents * - Phase and task details */ server.setRequestHandler(ListResourcesRequestSchema, async () => { return { resources: [ { uri: "features://list", mimeType: "text/plain", name: "Features List", description: "Lists all features being developed" }, { uri: "features://status", mimeType: "text/markdown", name: "Project Status", description: "Provides a summary of all features and their development status" }, ...listFeatures().flatMap(feature => [ { uri: `feature://${feature.id}`, mimeType: "text/plain", name: feature.name, description: `Details about feature: ${feature.name}` }, { uri: `feature://${feature.id}/progress`, mimeType: "text/markdown", name: `${feature.name} Progress Report`, description: `Detailed progress report for feature: ${feature.name}` }, { uri: `feature://${feature.id}/prd`, mimeType: "text/markdown", name: `${feature.name} PRD`, description: `PRD document for feature: ${feature.name}` }, { uri: `feature://${feature.id}/implementation`, mimeType: "text/markdown", name: `${feature.name} Implementation Plan`, description: `Implementation plan for feature: ${feature.name}` }, { uri: `feature://${feature.id}/phases`, mimeType: "text/plain", name: `${feature.name} Phases`, description: `Lists all phases for feature: ${feature.name}` }, { uri: `feature://${feature.id}/tasks`, mimeType: "text/plain", name: `${feature.name} All Tasks`, description: `Lists all tasks across all phases for feature: ${feature.name}` }, ...feature.phases.flatMap(phase => [ { uri: `feature://${feature.id}/phase/${phase.id}`, mimeType: "text/plain", name: `${feature.name} - ${phase.name}`, description: `Details about phase: ${phase.name} for feature: ${feature.name}` }, { uri: `feature://${feature.id}/phase/${phase.id}/tasks`, mimeType: "text/plain", name: `${feature.name} - ${phase.name} Tasks`, description: `Lists all tasks for phase: ${phase.name}` }, ...phase.tasks.map(task => ({ uri: `feature://${feature.id}/phase/${phase.id}/task/${task.id}`, mimeType: "text/plain", name: `Task: ${task.description.substring(0, 30)}${task.description.length > 30 ? '...' : ''}`, description: `Details about task: ${task.description.substring(0, 50)}${task.description.length > 50 ? '...' : ''}` })) ]) ]) ] }; }); /** * Handler for reading feature resources. * Supports: * - List of all features * - Individual feature details * - PRD and implementation plan documents * - Phase and task details */ server.setRequestHandler(ReadResourceRequestSchema, async (request) => { try { const url = new URL(request.params.uri); // Handle list of all features if (url.protocol === "features:") { if (url.pathname === "/list") { return { contents: [{ uri: request.params.uri, mimeType: "text/plain", text: listFeatures().map(f => `${f.id}: ${f.name}`).join("\n") }] }; } if (url.pathname === "/status") { const features = listFeatures(); if (features.length === 0) { return { contents: [{ uri: request.params.uri, mimeType: "text/markdown", text: "# Project Status\n\nNo features have been created yet." }] }; } const featuresStatus = features.map(feature => { const totalPhases = feature.phases.length; const completedPhases = feature.phases.filter(p => p.status === 'completed' || p.status === 'reviewed').length; const totalTasks = feature.phases.reduce((acc, phase) => acc + phase.tasks.length, 0); const completedTasks = feature.phases.reduce( (acc, phase) => acc + phase.tasks.filter(t => t.completed).length, 0 ); return `## ${feature.name} - ID: ${feature.id} - Status: ${completedPhases === totalPhases && totalPhases > 0 ? 'Completed' : 'In Progress'} - Phases: ${completedPhases}/${totalPhases} completed - Tasks: ${completedTasks}/${totalTasks} completed - [View Details](feature://${feature.id}/progress) `; }).join('\n'); return { contents: [{ uri: request.params.uri, mimeType: "text/markdown", text: `# Project Status\n\n${featuresStatus}` }] }; } } // Handle feature-specific resources if (url.protocol === "feature:") { // The hostname part is actually our feature ID in feature:// URLs let featureId = ""; if (url.hostname) { featureId = url.hostname; } else { // Try to get feature ID from pathname for backward compatibility const parts = url.pathname.split('/').filter(Boolean); if (parts.length > 0) { featureId = parts[0]; } } if (!featureId) { throw new Error(`Invalid feature URI: ${request.params.uri}. Missing feature ID.`); } const feature = getFeature(featureId); if (!feature) { throw new Error(`Feature ${featureId} not found`); } // Extract path parts, excluding empty strings const parts = url.pathname.split('/').filter(Boolean); // Return feature details - no additional path parts beyond the feature ID if (parts.length === 0) { const timestamp = feature.updatedAt.toISOString(); const clarifications = formatClarificationResponses(feature.clarificationResponses); const phasesText = feature.phases.map(p => `- ${p.name} (${p.status}): ${p.tasks.filter(t => t.completed).length}/${p.tasks.length} tasks completed` ).join('\n'); const featureDetails = ` Feature: ${feature.name} ID: ${feature.id} Description: ${feature.description} Last Updated: ${timestamp} Clarification Responses: ${clarifications} Phases (${feature.phases.length}): ${phasesText} `; return { contents: [{ uri: request.params.uri, mimeType: "text/plain", text: featureDetails }] }; } // Resource is a sub-resource of the feature if (parts.length >= 1) { const subResource = parts[0]; // Return feature progress report if (subResource === "progress") { const progressReport = generateFeatureProgressSummary(feature); return { contents: [{ uri: request.params.uri, mimeType: "text/markdown", text: progressReport }] }; } // Return PRD document if (subResource === "prd") { const prdContent = feature.prdDoc || generatePRD(feature); return { contents: [{ uri: request.params.uri, mimeType: "text/markdown", text: prdContent }] }; } // Return implementation plan document if (subResource === "implementation") { const implContent = feature.implDoc || generateImplementationPlan(feature); return { contents: [{ uri: request.params.uri, mimeType: "text/markdown", text: implContent }] }; } // Return phase listing if (subResource === "phases") { if (feature.phases.length === 0) { return { contents: [{ uri: request.params.uri, mimeType: "text/plain", text: `No phases defined for feature: ${feature.name}` }] }; } const phasesContent = feature.phases.map(phase => { const completedTasks = phase.tasks.filter(t => t.completed).length; const totalTasks = phase.tasks.length; return `Phase: ${phase.name} (ID: ${phase.id}) Status: ${phase.status} Description: ${phase.description} Progress: ${completedTasks}/${totalTasks} tasks completed Last Updated: ${phase.updatedAt.toISOString()} `; }).join('\n---\n\n'); return { contents: [{ uri: request.params.uri, mimeType: "text/plain", text: `# Phases for Feature: ${feature.name}\n\n${phasesContent}` }] }; } // Handle list of all tasks for a feature if (subResource === "tasks") { const allTasks = feature.phases.flatMap(phase => phase.tasks.map(task => ({ ...task, phaseName: phase.name, phaseId: phase.id, phaseStatus: phase.status })) ); if (allTasks.length === 0) { return { contents: [{ uri: request.params.uri, mimeType: "text/plain", text: `No tasks defined for feature: ${feature.name}` }] }; } const pendingTasks = allTasks.filter(t => !t.completed); const completedTasks = allTasks.filter(t => t.completed); const pendingTasksText = pendingTasks.length > 0 ? pendingTasks.map(task => `- [ ] ${task.description} (ID: ${task.id}, Phase: ${task.phaseName})`).join('\n') : "No pending tasks."; const completedTasksText = completedTasks.length > 0 ? completedTasks.map(task => `- [x] ${task.description} (ID: ${task.id}, Phase: ${task.phaseName})`).join('\n') : "No completed tasks."; const tasksContent = `# All Tasks for Feature: ${feature.name} ## Pending Tasks (${pendingTasks.length}) ${pendingTasksText} ## Completed Tasks (${completedTasks.length}) ${completedTasksText} `; return { contents: [{ uri: request.params.uri, mimeType: "text/plain", text: tasksContent }] }; } // Handle phase-specific resources if (subResource === "phase" && parts.length >= 2) { const phaseId = parts[1]; const phase = feature.phases.find(p => p.id === phaseId); if (!phase) { throw new Error(`Phase ${phaseId} not found in feature ${feature.name}`); } // Return phase details if (parts.length === 2) { const completedTasks = phase.tasks.filter(t => t.completed).length; const totalTasks = phase.tasks.length; const taskList = phase.tasks.map(task => `- [${task.completed ? 'x' : ' '}] ${task.description} (ID: ${task.id})` ).join('\n'); const phaseDetails = ` Phase: ${phase.name} ID: ${phase.id} Status: ${phase.status} Description: ${phase.description} Created: ${phase.createdAt.toISOString()} Last Updated: ${phase.updatedAt.toISOString()} Progress: ${completedTasks}/${totalTasks} tasks completed Tasks: ${taskList} `; return { contents: [{ uri: request.params.uri, mimeType: "text/plain", text: phaseDetails }] }; } // Return task listing if (parts[2] === "tasks") { if (phase.tasks.length === 0) { return { contents: [{ uri: request.params.uri, mimeType: "text/plain", text: `No tasks defined for phase: ${phase.name}` }] }; } const tasksContent = phase.tasks.map(task => ` Task: ${task.description} ID: ${task.id} Status: ${task.completed ? 'Completed' : 'Pending'} Created: ${task.createdAt.toISOString()} Last Updated: ${task.updatedAt.toISOString()} `).join('\n---\n'); return { contents: [{ uri: request.params.uri, mimeType: "text/plain", text: `# Tasks for Phase: ${phase.name}\n\n${tasksContent}` }] }; } // Return individual task details if (parts[2] === "task" && parts.length === 4) { const taskId = parts[3]; const task = phase.tasks.find(t => t.id === taskId); if (!task) { throw new Error(`Task ${taskId} not found in phase ${phase.name}`); } const taskDetails = ` Task: ${task.description} ID: ${task.id} Status: ${task.completed ? 'Completed' : 'Pending'} Created: ${task.createdAt.toISOString()} Last Updated: ${task.updatedAt.toISOString()} `; return { contents: [{ uri: request.params.uri, mimeType: "text/plain", text: taskDetails }] }; } } } } throw new Error(`Resource not found: ${request.params.uri}`); } catch (error: any) { console.error(`Error reading resource ${request.params.uri}:`, error); return { contents: [{ uri: request.params.uri, mimeType: "text/plain", text: `Error: ${error.message || 'Unknown error'}` }] }; } }); /** * Handler that lists available tools. * Exposes tools for feature clarification and development. */ server.setRequestHandler(ListToolsRequestSchema, async () => { return { tools: [ { name: "start_feature_clarification", description: "Start the clarification process for a new feature", inputSchema: { type: "object", properties: { featureName: { type: "string", description: "Name of the feature" }, initialDescription: { type: "string", description: "Initial description of the feature" } }, required: ["featureName"], examples: [ { featureName: "User Authentication", initialDescription: "Add login and registration functionality to the application" }, { featureName: "Data Export", initialDescription: "Allow users to export their data in CSV and JSON formats" } ] } }, { name: "provide_clarification", description: "Provide answer to a clarification question", inputSchema: { type: "object", properties: { featureId: { type: "string", description: "ID of the feature" }, question: { type: "string", description: "Clarification question" }, answer: { type: "string", description: "Answer to the clarification question" } }, required: ["featureId", "question", "answer"], examples: [ { featureId: "feature-123", question: "What problem does this feature solve?", answer: "This feature solves the problem of users forgetting their passwords by providing a secure password reset flow." }, { featureId: "feature-456", question: "Who are the target users?", answer: "The target users are administrators who need to manage user accounts and permissions." } ] } }, { name: "generate_prd", description: "Generate a PRD document based on clarification responses", inputSchema: { type: "object", properties: { featureId: { type: "string", description: "ID of the feature" } }, required: ["featureId"], examples: [ { featureId: "feature-123" } ] }, handler: async (params: {featureId: string}) => { const { featureId } = params; const feature = getFeature(featureId); if (!feature) { return { error: `Feature with ID ${featureId} not found` }; } if (!isClarificationComplete(feature)) { return { error: 'Cannot generate PRD until clarification is complete', clarificationStatus: getClarificationStatus(feature) }; } // Generate the PRD const prd = generatePRD(feature); // Update the feature with the PRD updateFeature(featureId, { ...feature, prd, updatedAt: new Date() }); return { success: true, message: `PRD generated for feature ${feature.name}`, prd }; } }, { name: 'generate_implementation_plan', description: 'Generate an implementation plan for a feature based on clarifications and PRD', inputSchema: { type: 'object', properties: { featureId: { type: 'string', description: 'The ID of the feature to generate an implementation plan for' } }, required: ['featureId'] }, handler: async (params: {featureId: string}) => { const { featureId } = params; const feature = getFeature(featureId); if (!feature) { return { error: `Feature with ID ${featureId} not found` }; } if (!isClarificationComplete(feature)) { return { error: 'Cannot generate implementation plan until clarification is complete', clarificationStatus: getClarificationStatus(feature) }; } // Generate the implementation plan const implementationPlan = generateImplementationPlan(feature); // Update the feature with the implementation plan updateFeature(featureId, { ...feature, implementationPlan, updatedAt: new Date() }); return { success: true, message: `Implementation plan generated for feature ${feature.name}`, implementationPlan }; } }, { name: 'create_phase', description: 'Create a new development phase for a feature', inputSchema: { type: 'object', properties: { featureId: { type: 'string', description: 'ID of the feature to create a phase for' }, name: { type: 'string', description: 'Name of the phase' }, description: { type: 'string', description: 'Description of the phase' } }, required: ['featureId', 'name', 'description'], examples: [ { featureId: "feature-123", name: "Requirements Analysis", description: "Gather and analyze requirements for the feature" }, { featureId: "feature-123", name: "Implementation", description: "Implement the core functionality of the feature" } ] }, handler: async (params: {featureId: string, name: string, description: string}) => { const { featureId, name, description } = params; const feature = getFeature(featureId); if (!feature) { return { error: `Feature with ID ${featureId} not found` }; } // Create the phase with validated inputs const phaseId = generateId(); const phase = createPhase(feature.id, name, description); return { success: true, message: `Phase ${name} created for feature ${feature.name}`, phase }; } }, { name: 'update_phase_status', description: 'Update the status of a development phase', inputSchema: { type: 'object', properties: { featureId: { type: 'string', description: 'ID of the feature containing the phase' }, phaseId: { type: 'string', description: 'ID of the phase to update' }, status: { type: 'string', description: 'New status for the phase (pending, in_progress, completed, reviewed)' } }, required: ['featureId', 'phaseId', 'status'], examples: [ { featureId: "feature-123", phaseId: "phase-456", status: "in_progress" }, { featureId: "feature-123", phaseId: "phase-456", status: "completed" } ] }, handler: async (params: {featureId: string, phaseId: string, status: PhaseStatus}) => { const { featureId, phaseId, status } = params; const feature = getFeature(featureId); if (!feature) { return { error: `Feature with ID ${featureId} not found` }; } const phase = feature.phases.find(p => p.id === phaseId); if (!phase) { return { error: `Phase with ID ${phaseId} not found in feature ${feature.name}` }; } // Validate the status transition const validationResult = validatePhaseTransition(phase.status, status); if (!validationResult.valid) { return { error: validationResult.message }; } // Update the phase status const updatedPhase = updatePhaseStatus(featureId, phaseId, status); return { success: true, message: `Phase ${phase.name} status updated to ${status}`, phase: updatedPhase }; } }, { name: 'add_task', description: 'Add a task to a development phase', inputSchema: { type: 'object', properties: { featureId: { type: 'string', description: 'ID of the feature containing the phase' }, phaseId: { type: 'string', description: 'ID of the phase to add the task to' }, description: { type: 'string', description: 'Description of the task' } }, required: ['featureId', 'phaseId', 'description'], examples: [ { featureId: "feature-123", phaseId: "phase-456", description: "Create database migration scripts" }, { featureId: "feature-123", phaseId: "phase-456", description: "Implement user interface components" } ] }, handler: async (params: {featureId: string, phaseId: string, description: string}) => { const { featureId, phaseId, description } = params; const feature = getFeature(featureId); if (!feature) { return { error: `Feature with ID ${featureId} not found` }; } const phase = feature.phases.find(p => p.id === phaseId); if (!phase) { return { error: `Phase with ID ${phaseId} not found in feature ${feature.name}` }; } // Add the task to the phase const updatedPhase = addTask(featureId, phaseId, description); return { success: true, message: `Task added to phase ${phase.name}`, phase: updatedPhase }; } }, { name: 'update_task_status', description: 'Update the completion status of a task', inputSchema: { type: 'object', properties: { featureId: { type: 'string', description: 'ID of the feature containing the phase' }, phaseId: { type: 'string', description: 'ID of the phase containing the task' }, taskId: { type: 'string', description: 'ID of the task to update' }, completed: { type: 'boolean', description: 'Whether the task is completed' } }, required: ['featureId', 'phaseId', 'taskId', 'completed'], examples: [ { featureId: "feature-123", phaseId: "phase-456", taskId: "task-789", completed: true }, { featureId: "feature-123", phaseId: "phase-456", taskId: "task-789", completed: false } ] }, handler: async (params: {featureId: string, phaseId: string, taskId: string, completed: boolean}) => { const { featureId, phaseId, taskId, completed } = params; const feature = getFeature(featureId); if (!feature) { return { error: `Feature with ID ${featureId} not found` }; } const phase = feature.phases.find(p => p.id === phaseId); if (!phase) { return { error: `Phase with ID ${phaseId} not found in feature ${feature.name}` }; } const task = phase.tasks.find(t => t.id === taskId); if (!task) { return { error: `Task with ID ${taskId} not found in phase ${phase.name}` }; } // Update the task status const updatedTask = updateTaskStatus(featureId, phaseId, taskId, completed); // Check if all tasks are completed and suggest phase update if applicable let message = `Task status updated to ${completed ? 'completed' : 'not completed'}`; // @ts-ignore - We know the task structure from our validation if (completed && phase.tasks.every(t => t.id === taskId || t.completed)) { // All tasks are now completed message += `. All tasks in phase ${phase.name} are now completed. Consider updating the phase status to 'completed'.`; } return { success: true, message, task: updatedTask }; } }, { name: 'get_next_phase_action', description: 'Get guidance on what to do next in the current phase or whether to move to the next phase', inputSchema: { type: 'object', properties: { featureId: { type: 'string', description: 'ID of the feature' } }, required: ['featureId'], examples: [ { featureId: "feature-123" } ] }, handler: async (params: {featureId: string}) => { const { featureId } = params; const feature = getFeature(featureId); if (!feature) { return { error: `Feature with ID ${featureId} not found` }; } // Find the current active phase (first non-completed/reviewed phase) // @ts-ignore - We know the phase structure from our Feature type const currentPhase = feature.phases.find(p => p.status === 'pending' || p.status === 'in_progress'); if (!currentPhase) { // All phases are completed or reviewed return { content: [{ type: "text", text: 'All phases are completed or reviewed. The feature implementation is done!' }] }; } // Check task completion status // @ts-ignore - We know the task structure from our Phase type const completedTasks = currentPhase.tasks.filter(t => t.completed); // @ts-ignore - We know the task structure from our Phase type const pendingTasks = currentPhase.tasks.filter(t => !t.completed); // Determine next action based on phase and task status let message = ''; if (currentPhase.status === 'pending') { message = `Phase "${currentPhase.name}" is pending. Start working on this phase by setting its status to "in_progress".`; } else if (currentPhase.status === 'in_progress') { if (pendingTasks.length > 0) { message = `${completedTasks.length}/${currentPhase.tasks.length} tasks are completed in phase "${currentPhase.name}". Continue working on pending tasks.`; } else if (currentPhase.tasks.length === 0) { message = `Phase "${currentPhase.name}" has no tasks defined. Add tasks or mark the phase as completed if appropriate.`; } else { // All tasks are completed message = `All tasks in phase "${currentPhase.name}" are completed. Consider marking this phase as completed.`; } } return { content: [{ type: "text", text: message }] }; } } ] }; }); /** * Handler for implementing MCP tools. * Handles feature clarification, PRD generation, and more. */ server.setRequestHandler(CallToolRequestSchema, async (request) => { try { switch (request.params.name) { case "start_feature_clarification": { const featureName = String(request.params.arguments?.featureName || ""); const initialDescription = String(request.params.arguments?.initialDescription || ""); // Validate inputs const nameValidation = validateRequiredText(featureName, "Feature name", 2, 100); if (!nameValidation.valid) { return { content: [{ type: "text", text: `Error: ${nameValidation.message}` }] }; } // Initial description is optional, but if provided, validate its length if (initialDescription && initialDescription.trim() !== "") { const descValidation = validateRequiredText(initialDescription, "Initial description", 1, 5000); if (!descValidation.valid) { return { content: [{ type: "text", text: `Error: ${descValidation.message}` }] }; } } // Create a new feature const feature = createFeatureObject(nameValidation.data, initialDescription); storeFeature(feature); // Get the first clarification question const firstQuestion = getNextClarificationQuestion(feature); return { content: [{ type: "text", text: `Feature ID: ${feature.id}\n\nLet's clarify your feature request. ${firstQuestion}` }] }; } case "provide_clarification": { const featureId = String(request.params.arguments?.featureId || ""); const question = String(request.params.arguments?.question || ""); const answer = String(request.params.arguments?.answer || ""); console.log(`\n[CLARIFICATION] Received request for feature ${featureId}\n Question: "${question.substring(0, 50)}..."\n Answer: "${answer.substring(0, 50)}..."`); // Basic validation if (!featureId || !answer) { console.log(`[CLARIFICATION] Missing required fields: featureId=${!!featureId}, answer=${!!answer}`); return { content: [{ type: "text", text: "Error: Feature ID and answer are required" }] }; } // Get the feature const feature = getFeature(featureId); if (!feature) { console.log(`[CLARIFICATION] Feature ID ${featureId} not found`); return { content: [{ type: "text", text: `Error: Feature with ID ${featureId} not found` }] }; } console.log(`[CLARIFICATION] Found feature: ${feature.name} with ${feature.clarificationResponses.length} existing responses`); // Add the clarification response to the feature feature.clarificationResponses.push({ question, answer, timestamp: new Date() }); console.log(`[CLARIFICATION] Added response, now has ${feature.clarificationResponses.length} responses`); // Save the feature with the updated clarification response updateFeature(featureId, feature); // Determine which question to ask next const nextQuestionIndex = feature.clarificationResponses.length; console.log(`[CLARIFICATION] Next question index would be: ${nextQuestionIndex}`); console.log(`[CLARIFICATION] Total questions available: ${CLARIFICATION_QUESTIONS.length}`); if (nextQuestionIndex < CLARIFICATION_QUESTIONS.length) { // We have more questions to ask const nextQuestion = CLARIFICATION_QUESTIONS[nextQuestionIndex]; console.log(`[CLARIFICATION] Returning next question: "${nextQuestion.substring(0, 50)}..."`); return { content: [{ type: "text", text: `Response recorded. ${nextQuestion}` }] }; } else { // All questions answered console.log(`[CLARIFICATION] All questions answered`); return { content: [{ type: "text", text: "All clarification questions have been answered. You can now generate a PRD for this feature." }] }; } } case "generate_prd": { const featureId = String(request.params.arguments?.featureId || ""); // Validate feature ID const featureResult = validateFeatureId(featureId); if (!featureResult.valid) { return { content: [{ type: "text", text: `Error: ${featureResult.message}` }] }; } const feature = featureResult.data; // Check if clarifications are complete if (!isClarificationComplete(feature)) { return { content: [{ type: "text", text: "Error: Please complete all clarification questions before generating a PRD." }] }; } // Generate PRD and implementation plan const prdDoc = generatePRD(feature); const implementationPlan = generateImplementationPlan(feature); // Store the documents feature.prdDoc = prdDoc; feature.implementationPlan = implementationPlan; return { content: [{ type: "text", text: `PRD and Implementation Plan generated for feature ${feature.name}. You can view them using the resource URIs: feature://${feature.id}/prd and feature://${feature.id}/implementation` }] }; } case "create_phase": { const featureId = String(request.params.arguments?.featureId || ""); const phaseName = String(request.params.arguments?.name || ""); const phaseDescription = String(request.params.arguments?.description || ""); const tasksJson = String(request.params.arguments?.tasks || "[]"); // Validate feature ID const featureResult = validateFeatureId(featureId); if (!featureResult.valid) { return { content: [{ type: "text", text: `Error: ${featureResult.message}` }] }; } // Validate phase name const nameValidation = validateRequiredText(phaseName, "Phase name", 2, 100); if (!nameValidation.valid) { return { content: [{ type: "text", text: `Error: ${nameValidation.message}` }] }; } // Validate phase description (optional but if provided, validate) if (phaseDescription && phaseDescription.trim() !== "") { const descValidation = validateRequiredText(phaseDescription, "Phase description", 5, 1000); if (!descValidation.valid) { return { content: [{ type: "text", text: `Error: ${descValidation.message}` }] }; } } // Parse and validate tasks let tasks = []; try { tasks = JSON.parse(tasksJson); if (!Array.isArray(tasks)) { return { content: [{ type: "text", text: "Error: Tasks must be a valid JSON array of task descriptions" }] }; } // Validate each task description for (let i = 0; i < tasks.length; i++) { const taskDesc = tasks[i]; if (typeof taskDesc !== 'string' || taskDesc.trim().length < 3) { return { content: [{ type: "text", text: `Error: Task at index ${i} must be a string with at least 3 characters` }] }; } } } catch (e) { return { content: [{ type: "text", text: "Error: Could not parse tasks JSON. Please provide a valid JSON array." }] }; } const feature = featureResult.data; // Create the phase const phase = createPhase(featureId, nameValidation.data, phaseDescription); if (!phase) { return { content: [{ type: "text", text: `Error: Failed to create phase for feature ${feature.name}` }] }; } // Add tasks if provided if (tasks.length > 0) { tasks.forEach(taskDesc => { addTask(featureId, phase.id, taskDesc); }); } return { content: [{ type: "text", text: `Phase "${phaseName}" created with ID: ${phase.id}` }] }; } case "update_phase_status": { const featureId = String(request.params.arguments?.featureId || ""); const phaseId = String(request.params.arguments?.phaseId || ""); const status = String(request.params.arguments?.status || ""); // Validate feature and phase const validationResult = validateFeaturePhaseTask(featureId, phaseId); if (!validationResult.valid) { return { content: [{ type: "text", text: `Error: ${validationResult.message}` }] }; } // Validate status const statusValidation = validatePhaseStatusValue(status); if (!statusValidation.valid) { return { content: [{ type: "text", text: `Error: ${statusValidation.message}` }] }; } const { feature, phase } = validationResult.data; // Update the phase status const updatedPhase = updatePhaseStatus(featureId, phaseId, statusValidation.data); return { content: [{ type: "text", text: `Phase status updated to "${status}"` }] }; } case "add_task": { const featureId = String(request.params.arguments?.featureId || ""); const phaseId = String(request.params.arguments?.phaseId || ""); const taskDescription = String(request.params.arguments?.description || ""); // Validate feature and phase const validationResult = validateFeaturePhaseTask(featureId, phaseId); if (!validationResult.valid) { return { content: [{ type: "text", text: `Error: ${validationResult.message}` }] }; } // Validate task description const descValidation = validateRequiredText(taskDescription, "Task description", 3, 500); if (!descValidation.valid) { return { content: [{ type: "text", text: `Error: ${descValidation.message}` }] }; } const { feature, phase } = validationResult.data; // Add the task const taskId = addTask(featureId, phaseId, descValidation.data); return { content: [{ type: "text", text: `Task added to phase "${phase.name}" with ID: ${taskId}` }] }; } case "update_task_status": { const featureId = String(request.params.arguments?.featureId || ""); const phaseId = String(request.params.arguments?.phaseId || ""); const taskId = String(request.params.arguments?.taskId || ""); const completed = Boolean(request.params.arguments?.completed); // Validate feature, phase and task const validationResult = validateFeaturePhaseTask(featureId, phaseId, taskId); if (!validationResult.valid) { return { content: [{ type: "text", text: `Error: ${validationResult.message}` }] }; } const { feature, phase, task } = validationResult.data; // Update the task status const updatedTask = updateTaskStatus(featureId, phaseId, taskId, completed); // Check if all tasks are completed and suggest phase update if applicable let message = `Task status updated to ${completed ? 'completed' : 'not completed'}`; // @ts-ignore - We know the task structure from our validation if (completed && phase.tasks.every(t => t.id === taskId || t.completed)) { // All tasks are now completed message += `. All tasks in phase ${phase.name} are now completed. Consider updating the phase status to 'completed'.`; } return { content: [{ type: "text", text: message }] }; } case "get_next_phase_action": { const featureId = String(request.params.arguments?.featureId || ""); // Validate feature ID const featureResult = validateFeatureId(featureId); if (!featureResult.valid) { return { content: [{ type: "text", text: `Error: ${featureResult.message}` }] }; } const feature = featureResult.data; // Find the current active phase (first non-completed/reviewed phase) // @ts-ignore - We know the phase structure from our Feature type const currentPhase = feature.phases.find(p => p.status === 'pending' || p.status === 'in_progress'); if (!currentPhase) { // All phases are completed or reviewed return { content: [{ type: "text", text: 'All phases are completed or reviewed. The feature implementation is done!' }] }; } // Check task completion status // @ts-ignore - We know the task structure from our Phase type const completedTasks = currentPhase.tasks.filter(t => t.completed); // @ts-ignore - We know the task structure from our Phase type const pendingTasks = currentPhase.tasks.filter(t => !t.completed); // Determine next action based on phase and task status let message = ''; if (currentPhase.status === 'pending') { message = `Phase "${currentPhase.name}" is pending. Start working on this phase by setting its status to "in_progress".`; } else if (currentPhase.status === 'in_progress') { if (pendingTasks.length > 0) { message = `${completedTasks.length}/${currentPhase.tasks.length} tasks are completed in phase "${currentPhase.name}". Continue working on pending tasks.`; } else if (currentPhase.tasks.length === 0) { message = `Phase "${currentPhase.name}" has no tasks defined. Add tasks or mark the phase as completed if appropriate.`; } else { // All tasks are completed message = `All tasks in phase "${currentPhase.name}" are completed. Consider marking this phase as completed.`; } } return { content: [{ type: "text", text: message }] }; } default: return { content: [{ type: "text", text: `Error: Unknown tool "${request.params.name}"` }] }; } } catch (error: any) { console.error('Error executing tool:', error); return { content: [{ type: "text", text: `An unexpected error occurred: ${error.message || 'Unknown error'}` }] }; } }); /** * Handler that lists available prompts. */ server.setRequestHandler(ListPromptsRequestSchema, async () => { return { prompts: [ { name: "clarify_feature", description: "Guide to clarify a feature request through questioning" } ] }; }); /** * Handler for the clarify_feature prompt. * Returns a prompt that guides feature clarification. */ server.setRequestHandler(GetPromptRequestSchema, async (request) => { if (request.params.name === "clarify_feature") { return { messages: [ { role: "user", content: { type: "text", text: "Help me clarify this feature request by asking questions about:" } }, { role: "user", content: { type: "text", text: "1. The specific problem it solves\n2. The target users\n3. Key requirements\n4. Success criteria\n5. Technical constraints\n\nAsk one question at a time, analyze the response, then proceed to the next most relevant question." } } ] }; } throw new Error("Unknown prompt"); }); /** * Start the server using stdio transport. * This allows the server to communicate via standard input/output streams. */ async function main() { console.error("Starting Vibe-Coder MCP Server..."); const transport = new StdioServerTransport(); await server.connect(transport); } main().catch((error) => { console.error("Server error:", error); process.exit(1); });

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/crazyrabbitLTC/mcp-vibecoder'

If you have feedback or need assistance with the MCP directory API, please join our Discord server