solve_schedule
Assign tasks to time slots by matching their energy requirements with slot energy levels to maximize productivity. Ideal for deep-work scheduling, shift planning, and exercise scheduling.
Instructions
Assign tasks to time slots maximizing productivity, matching task energy requirements with slot energy levels. Use specifically for task→slot assignment with energy matching (deep-work scheduling, shift planning, exercise scheduling). For general resource allocation with arbitrary linear constraints, use solve_constraints. For sequence/route problems, use plan_pathfind. Deterministic.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| tasks | Yes | ||
| slots | Yes |
Output Schema
| Name | Required | Description | Default |
|---|---|---|---|
| assignments | Yes | ||
| unassignedTasks | No | Task IDs that did not fit. | |
| totalScore | No |
Implementation Reference
- The main handler function `optimizeSchedule` that implements the schedule optimization logic. Takes tasks and time slots, formulates a binary integer programming problem maximizing priority-weighted completion with energy matching, solves it via HiGHS, and returns assignments with total score.
export async function optimizeSchedule( tasks: Task[], slots: TimeSlot[], ): Promise<ScheduleResult> { const energyMatch: Record<string, Record<string, number>> = { high: { high: 1.0, medium: 0.5, low: 0.2 }, medium: { high: 0.8, medium: 1.0, low: 0.6 }, low: { high: 0.4, medium: 0.7, low: 1.0 }, }; const variables: OptimizationVariable[] = []; const objective: Record<string, number> = {}; const constraints: Constraint[] = []; // Sanitize IDs for LP format (no hyphens, spaces, or special chars) const sanitize = (s: string) => s.replace(/[^a-zA-Z0-9]/g, ""); const taskIdMap = new Map(tasks.map((t) => [t.id, sanitize(t.id)])); const slotIdMap = new Map(slots.map((s) => [s.id, sanitize(s.id)])); // Binary variable x_i_j = 1 if task i assigned to slot j for (const task of tasks) { for (const slot of slots) { if (slot.durationMinutes >= task.durationMinutes) { const varName = `x${taskIdMap.get(task.id)}${slotIdMap.get(slot.id)}`; variables.push({ name: varName, type: "binary" }); // Objective: priority × energy match const match = energyMatch[task.energyRequired]?.[slot.energyLevel] ?? 0.5; objective[varName] = task.priority * match; } } } // Each task assigned at most once for (const task of tasks) { const coefficients: Record<string, number> = {}; for (const slot of slots) { const varName = `x${taskIdMap.get(task.id)}${slotIdMap.get(slot.id)}`; if (variables.some((v) => v.name === varName)) { coefficients[varName] = 1; } } if (Object.keys(coefficients).length > 0) { constraints.push({ name: `task${taskIdMap.get(task.id)}`, coefficients, upper: 1 }); } } // Each slot used at most once for (const slot of slots) { const coefficients: Record<string, number> = {}; for (const task of tasks) { const varName = `x${taskIdMap.get(task.id)}${slotIdMap.get(slot.id)}`; if (variables.some((v) => v.name === varName)) { coefficients[varName] = 1; } } if (Object.keys(coefficients).length > 0) { constraints.push({ name: `slot${slotIdMap.get(slot.id)}`, coefficients, upper: 1 }); } } if (variables.length === 0) { return { assignments: [], unscheduled: tasks.map((t) => t.id), totalScore: 0 }; } const result = await solve({ direction: "maximize", objective, variables, constraints, }); // Build reverse maps: sanitized → original ID const reverseTaskMap = new Map(tasks.map((t) => [sanitize(t.id), t.id])); const reverseSlotMap = new Map(slots.map((s) => [sanitize(s.id), s.id])); const assignments: Array<{ taskId: string; slotId: string; score: number }> = []; const scheduledTasks = new Set<string>(); if (result.status === "optimal") { for (const [varName, value] of Object.entries(result.solution)) { if (value > 0.5 && varName.startsWith("x")) { // Find which task+slot this variable represents const suffix = varName.slice(1); // remove leading "x" for (const [sanTask, origTask] of reverseTaskMap) { if (suffix.startsWith(sanTask)) { const sanSlot = suffix.slice(sanTask.length); const origSlot = reverseSlotMap.get(sanSlot); if (origSlot) { scheduledTasks.add(origTask); assignments.push({ taskId: origTask, slotId: origSlot, score: objective[varName] ?? 0, }); break; } } } } } } const unscheduled = tasks.filter((t) => !scheduledTasks.has(t.id)).map((t) => t.id); return { assignments, unscheduled, totalScore: result.objectiveValue, }; } - Type definitions: Task, TimeSlot, and ScheduleResult interfaces used by optimizeSchedule.
export interface Task { id: string; name: string; durationMinutes: number; priority: number; // Higher = more important deadline?: number; // Unix timestamp energyRequired: "high" | "medium" | "low"; category?: string; } export interface TimeSlot { id: string; startTime: number; // Unix timestamp durationMinutes: number; energyLevel: "high" | "medium" | "low"; } export interface ScheduleResult { assignments: Array<{ taskId: string; slotId: string; score: number }>; unscheduled: string[]; totalScore: number; } - mission-control/packages/mcp-server/src/index.ts:530-534 (registration)The MCP tool handler case that dispatches 'solve_schedule' requests, extracts tasks and slots from args, and calls optimizeSchedule.
case "solve_schedule": { const { tasks, slots } = args as { tasks: Parameters<typeof optimizeSchedule>[0]; slots: Parameters<typeof optimizeSchedule>[1] }; const result = await optimizeSchedule(tasks, slots); return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] }; } - Input schema for the 'solve_schedule' tool with tasks (id, name, durationMinutes, priority, energyRequired) and slots (id, startTime, durationMinutes, energyLevel) properties.
{ name: "solve_schedule", description: "Optimal task scheduling. Given tasks (with priority, duration, energy requirement) and time slots (with available energy), finds the assignment that maximizes priority-weighted completion with energy matching. Use for daily planning, sprint planning, or resource scheduling.", inputSchema: { type: "object" as const, properties: { tasks: { type: "array", items: { type: "object", properties: { id: { type: "string" }, name: { type: "string" }, durationMinutes: { type: "number" }, priority: { type: "number", description: "Higher = more important" }, energyRequired: { type: "string", enum: ["high", "medium", "low"] }, }, required: ["id", "name", "durationMinutes", "priority", "energyRequired"], }, }, slots: { type: "array", items: { type: "object", properties: { id: { type: "string" }, startTime: { type: "number" }, durationMinutes: { type: "number" }, energyLevel: { type: "string", enum: ["high", "medium", "low"] }, }, required: ["id", "startTime", "durationMinutes", "energyLevel"], }, }, }, required: ["tasks", "slots"], }, - mission-control/packages/mcp-server/src/index.ts:44-45 (registration)Import of the optimizeSchedule function from constraintOptimizer into the MCP server.
optimizeSchedule, } from "../../apps/api/src/services/oracle/algorithms/constraintOptimizer.js";