UNPKG

mcp-decisive

Version:

MCP server for WRAP decision-making framework with structured output

142 lines (140 loc) 5.96 kB
import { ok, err } from 'neverthrow'; // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ // Implementation Section // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ // Business rule: Validate plan creation request const validateCreateRequest = (request) => { if (request.name.trim().length === 0) { return err({ type: 'ValidationFailed', reason: 'Plan name cannot be empty' }); } if (request.tasks.length === 0) { return err({ type: 'ValidationFailed', reason: 'Plan must have at least one task' }); } return ok(request); }; // Command implementation using functional composition const createPlanCommand = (request) => validateCreateRequest(request) .andThen(validRequest => { // Create the plan entity const plan = { id: generateId(), name: validRequest.name, description: validRequest.description, tasks: validRequest.tasks.map((t, idx) => ({ id: `task-${idx}`, title: t.title, status: 'todo' })), createdAt: new Date(), updatedAt: new Date() }; // Return the event const event = { type: 'PlanCreated', planId: plan.id, plan }; return ok(event); }); const updatePlanCommand = (existingPlan, request) => { // Apply updates immutably const updatedPlan = { ...existingPlan, name: request.name ?? existingPlan.name, description: request.description ?? existingPlan.description, tasks: existingPlan.tasks.map(task => { const update = request.taskUpdates?.find(u => u.id === task.id); return update ? { ...task, ...update } : task; }), updatedAt: new Date() }; const event = { type: 'PlanUpdated', planId: updatedPlan.id, plan: updatedPlan }; return ok(event); }; // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ // Error Handling Utilities // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ const PlanErrorHandler = { // Smart constructor for validation errors validationFailed: (reason) => ({ type: 'ValidationFailed', reason }), // Convert errors to user-friendly messages toString: (error) => { switch (error.type) { case 'ValidationFailed': return `Validation failed: ${error.reason}`; case 'PlanNotFound': return `Plan not found: ${error.planId}`; case 'InvalidStateTransition': return `Invalid state transition from ${error.from} to ${error.to}`; default: // Exhaustive check - TypeScript will error if we miss a case const _exhaustive = error; throw new Error(`Unhandled error type: ${_exhaustive}`); } } }; // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ // Public API - Expose only what's needed // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ /** * Plan Aggregate - The public interface for plan-related commands * * @command createPlan - Create a new work plan * @command updatePlan - Update an existing plan * @utility toErrorMessage - Convert errors to user-friendly strings */ export const PlanAggregate = { createPlan: createPlanCommand, updatePlan: updatePlanCommand, toErrorMessage: PlanErrorHandler.toString, }; // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ // Usage Example // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ /* // Creating a plan const result = PlanAggregate.createPlan({ name: 'Q4 Feature Development', description: 'New features for Q4 release', tasks: [ { title: 'Design API', description: 'Design REST API endpoints' }, { title: 'Implement backend', description: 'Implement business logic' } ] }); // Handle the result monadically result .map(event => { console.log('Plan created:', event.plan.id); // Dispatch event to event store }) .mapErr(error => { console.error(PlanAggregate.toErrorMessage(error)); }); // Chaining operations const updateResult = result .andThen(createEvent => PlanAggregate.updatePlan(createEvent.plan, { taskUpdates: [ { id: 'task-0', status: 'in-progress' } ] }) ); */ // Helper function (would normally be in a separate utility) function generateId() { return `plan-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`; } //# sourceMappingURL=plan-aggregate-example.js.map