UNPKG

mcp-chain-of-draft-server

Version:

A Model Context Protocol server which provides Chain of Draft style thinking

283 lines (282 loc) 12.9 kB
import { McpError, ErrorCode } from "@modelcontextprotocol/sdk/types.js"; import { TOOL_NAME, TOOL_SCHEMA, TOOL_DESCRIPTION } from "./implementationStrategyPlannerParams.js"; const strategyHistory = {}; const activeCritiques = {}; export const implementationStrategyPlannerTool = (server) => { const processStrategyRequest = async (input) => { try { const validatedInput = validateImplementationStrategyData(input); if (!validatedInput) { throw new McpError(ErrorCode.InvalidParams, "Invalid implementation strategy data"); } // Validate draft progression if (validatedInput.draft_number > validatedInput.total_drafts) { validatedInput.total_drafts = validatedInput.draft_number; } // Initialize history for this strategy if needed const strategyId = validatedInput.strategy_id; if (!strategyHistory[strategyId]) { strategyHistory[strategyId] = []; } // Store the implementation strategy in history strategyHistory[strategyId].push(validatedInput); // Handle critique tracking if (validatedInput.is_critique && validatedInput.critique_focus) { if (!activeCritiques[strategyId]) { activeCritiques[strategyId] = []; } activeCritiques[strategyId].push(validatedInput.critique_focus); } // Format response return { content: [{ type: "text", text: JSON.stringify({ strategyId: validatedInput.strategy_id, featureName: validatedInput.feature_name, draftNumber: validatedInput.draft_number, totalDrafts: validatedInput.total_drafts, approachCount: validatedInput.implementation_approaches.length, selectedApproach: validatedInput.selected_approach, nextStepNeeded: validatedInput.next_step_needed, isCritique: validatedInput.is_critique, critiqueFocus: validatedInput.critique_focus, revisionInstructions: validatedInput.revision_instructions, activeCritiques: activeCritiques[strategyId] || [], strategyHistoryLength: strategyHistory[strategyId].length, isFinalDraft: validatedInput.is_final_draft }, null, 2) }] }; } catch (error) { if (error instanceof McpError) { throw error; } throw new McpError(ErrorCode.InternalError, error instanceof Error ? error.message : String(error)); } }; server.tool(TOOL_NAME, TOOL_DESCRIPTION, TOOL_SCHEMA, async (args, extra) => ({ content: [{ type: "text", text: JSON.stringify(await processStrategyRequest(args)) }] })); }; const validateImplementationStrategyData = (input) => { const data = input; // Check required fields first if (typeof data !== "object" || data === null || !("strategy_id" in data) || !("feature_name" in data) || !("feature_description" in data) || !("implementation_approaches" in data) || !("dependencies" in data) || !("constraints" in data) || !("success_criteria" in data) || !("draft_number" in data) || !("total_drafts" in data) || !("next_step_needed" in data)) { throw new Error("Missing required fields"); } // Validate approaches if (!Array.isArray(data.implementation_approaches)) { throw new Error("Implementation approaches must be an array"); } const validImpactLevels = ["low", "medium", "high"]; const validDependencyTypes = ["feature", "component", "system", "external", "other"]; const validDependencyStatuses = ["not_started", "in_progress", "completed", "blocked"]; const validConstraintCategories = ["time", "resource", "technical", "organizational", "other"]; const approaches = []; for (const approach of data.implementation_approaches) { if (typeof approach !== "object" || approach === null || !("name" in approach) || !("description" in approach) || !("phases" in approach) || !("pros" in approach) || !("cons" in approach)) { throw new Error("Each approach must have name, description, phases, pros, and cons"); } // Validate phases if (!Array.isArray(approach.phases)) { throw new Error("Phases must be an array"); } const phases = []; for (const phase of approach.phases) { if (typeof phase !== "object" || phase === null || !("name" in phase) || !("description" in phase) || !("tasks" in phase) || !("estimated_effort" in phase) || !("risks" in phase) || !("dependencies" in phase)) { throw new Error("Each phase must have name, description, tasks, estimated_effort, risks, and dependencies"); } // Validate tasks if (!Array.isArray(phase.tasks)) { throw new Error("Tasks must be an array"); } // Validate risks if (!Array.isArray(phase.risks)) { throw new Error("Risks must be an array"); } const risks = []; for (const risk of phase.risks) { if (typeof risk !== "object" || risk === null || !("description" in risk) || !("mitigation" in risk) || !("impact" in risk)) { throw new Error("Each risk must have description, mitigation, and impact"); } // Validate impact if (!validImpactLevels.includes(String(risk.impact))) { throw new Error(`Invalid impact level. Must be one of: ${validImpactLevels.join(", ")}`); } risks.push({ description: String(risk.description), mitigation: String(risk.mitigation), impact: String(risk.impact) }); } // Validate dependencies if (!Array.isArray(phase.dependencies)) { throw new Error("Dependencies must be an array"); } phases.push({ name: String(phase.name), description: String(phase.description), tasks: Array.isArray(phase.tasks) ? phase.tasks.map((t) => String(t)) : [], estimated_effort: Number(phase.estimated_effort), risks: risks, dependencies: Array.isArray(phase.dependencies) ? phase.dependencies.map((d) => String(d)) : [] }); } approaches.push({ name: String(approach.name), description: String(approach.description), phases: phases, pros: Array.isArray(approach.pros) ? approach.pros.map((p) => String(p)) : [], cons: Array.isArray(approach.cons) ? approach.cons.map((c) => String(c)) : [] }); } // Validate dependencies if (!Array.isArray(data.dependencies)) { throw new Error("Dependencies must be an array"); } const dependencies = []; for (const dep of data.dependencies) { if (typeof dep !== "object" || dep === null || !("name" in dep) || !("description" in dep) || !("type" in dep) || !("status" in dep)) { throw new Error("Each dependency must have name, description, type, and status"); } // Validate type if (!validDependencyTypes.includes(String(dep.type))) { throw new Error(`Invalid dependency type. Must be one of: ${validDependencyTypes.join(", ")}`); } // Validate status if (!validDependencyStatuses.includes(String(dep.status))) { throw new Error(`Invalid dependency status. Must be one of: ${validDependencyStatuses.join(", ")}`); } dependencies.push({ name: String(dep.name), description: String(dep.description), type: String(dep.type), status: String(dep.status) }); } // Validate constraints if (!Array.isArray(data.constraints)) { throw new Error("Constraints must be an array"); } const constraints = []; for (const constraint of data.constraints) { if (typeof constraint !== "object" || constraint === null || !("category" in constraint) || !("description" in constraint) || !("impact" in constraint)) { throw new Error("Each constraint must have category, description, and impact"); } // Validate category if (!validConstraintCategories.includes(String(constraint.category))) { throw new Error(`Invalid constraint category. Must be one of: ${validConstraintCategories.join(", ")}`); } // Validate impact if (!validImpactLevels.includes(String(constraint.impact))) { throw new Error(`Invalid impact level. Must be one of: ${validImpactLevels.join(", ")}`); } constraints.push({ category: String(constraint.category), description: String(constraint.description), impact: String(constraint.impact) }); } // Validate success criteria if (!Array.isArray(data.success_criteria)) { throw new Error("Success criteria must be an array"); } // Create and validate implementation strategy data object const strategyData = { strategy_id: String(data.strategy_id), feature_name: String(data.feature_name), feature_description: String(data.feature_description), implementation_approaches: approaches, dependencies: dependencies, constraints: constraints, success_criteria: Array.isArray(data.success_criteria) ? data.success_criteria.map(sc => String(sc)) : [], draft_number: Number(data.draft_number), total_drafts: Number(data.total_drafts), next_step_needed: Boolean(data.next_step_needed) }; // Optional fields if ("selected_approach" in data) strategyData.selected_approach = String(data.selected_approach); if ("is_critique" in data) strategyData.is_critique = Boolean(data.is_critique); if ("critique_focus" in data) strategyData.critique_focus = String(data.critique_focus); if ("revision_instructions" in data) strategyData.revision_instructions = String(data.revision_instructions); if ("is_final_draft" in data) strategyData.is_final_draft = Boolean(data.is_final_draft); // Additional validations if (strategyData.draft_number <= 0) { throw new Error("Draft number must be positive"); } if (strategyData.total_drafts <= 0) { throw new Error("Total drafts must be positive"); } if (strategyData.draft_number > strategyData.total_drafts) { throw new Error("Draft number cannot exceed total drafts"); } // Validate critique-specific fields if (strategyData.is_critique && !strategyData.critique_focus) { throw new Error("Critique focus required when is_critique is true"); } // Validate revision-specific fields if (strategyData.is_critique === false && !strategyData.revision_instructions) { throw new Error("Revision instructions required when is_critique is false"); } // Validate selected approach if (strategyData.selected_approach) { const approachNames = strategyData.implementation_approaches.map(a => a.name); if (!approachNames.includes(strategyData.selected_approach)) { throw new Error(`Selected approach '${strategyData.selected_approach}' not found in implementation approaches`); } } return strategyData; }; const formatImplementationStrategy = (strategy) => { return JSON.stringify({ strategyId: strategy.strategy_id, featureName: strategy.feature_name, selectedApproach: strategy.selected_approach, approachCount: strategy.implementation_approaches.length, dependencyCount: strategy.dependencies.length, constraintCount: strategy.constraints.length, successCriteriaCount: strategy.success_criteria.length, draftNumber: strategy.draft_number }, null, 2); };