@systemprompt/mcp-taskchecker
Version:
Model Context Protocol (MCP) server for intelligent task management, evaluation scoring, and session-based workflow tracking. Seamlessly integrates with AI assistants to provide structured task orchestration, real-time progress monitoring, and comprehensi
129 lines (128 loc) • 4.38 kB
JavaScript
import { randomUUID } from 'node:crypto';
export class TaskService {
static instance;
taskLists = new Map();
cleanupInterval;
CLEANUP_INTERVAL = 60 * 60 * 1000; // 1 hour
MAX_AGE = 24 * 60 * 60 * 1000; // 24 hours
constructor() {
// Start cleanup interval
this.cleanupInterval = setInterval(() => {
this.cleanupOldTaskLists();
}, this.CLEANUP_INTERVAL);
}
static getInstance() {
if (!TaskService.instance) {
TaskService.instance = new TaskService();
}
return TaskService.instance;
}
createTaskList(sessionId, initialTasks) {
const taskListId = randomUUID();
const now = new Date();
const tasks = initialTasks?.map(task => ({
id: randomUUID(),
title: task.title,
status: 'pending',
acceptanceCriteria: task.acceptanceCriteria,
createdAt: now,
updatedAt: now,
})) || [];
const taskList = {
id: taskListId,
sessionId,
tasks,
createdAt: now,
lastAccessed: now,
};
this.taskLists.set(taskListId, taskList);
return taskList;
}
getTaskList(taskListId) {
const taskList = this.taskLists.get(taskListId);
if (taskList) {
// Update last accessed time
taskList.lastAccessed = new Date();
this.taskLists.set(taskListId, taskList);
}
return taskList || null;
}
updateTask(taskListId, taskId, updates) {
const taskList = this.getTaskList(taskListId);
if (!taskList)
return null;
const taskIndex = taskList.tasks.findIndex(task => task.id === taskId);
if (taskIndex === -1)
return null;
const task = taskList.tasks[taskIndex];
// Validate evaluation score
if (updates.evaluation !== undefined && (updates.evaluation < 0 || updates.evaluation > 100)) {
throw new Error('Evaluation must be between 0 and 100');
}
// Update task properties
const updatedTask = {
...task,
...(updates.title !== undefined && { title: updates.title }),
...(updates.status !== undefined && { status: updates.status }),
...(updates.acceptanceCriteria !== undefined && { acceptanceCriteria: updates.acceptanceCriteria }),
...(updates.evaluation !== undefined && { evaluation: updates.evaluation }),
updatedAt: new Date(),
};
taskList.tasks[taskIndex] = updatedTask;
taskList.lastAccessed = new Date();
this.taskLists.set(taskListId, taskList);
return updatedTask;
}
getTask(taskListId, taskId) {
const taskList = this.getTaskList(taskListId);
if (!taskList)
return null;
return taskList.tasks.find(task => task.id === taskId) || null;
}
getAllTasks(taskListId) {
const taskList = this.getTaskList(taskListId);
return taskList ? taskList.tasks : null;
}
deleteTaskList(taskListId) {
return this.taskLists.delete(taskListId);
}
deleteTaskListBySession(sessionId) {
let deletedCount = 0;
for (const [id, taskList] of this.taskLists.entries()) {
if (taskList.sessionId === sessionId) {
this.taskLists.delete(id);
deletedCount++;
}
}
return deletedCount;
}
cleanupOldTaskLists() {
const now = Date.now();
let cleanedCount = 0;
for (const [id, taskList] of this.taskLists.entries()) {
if (now - taskList.lastAccessed.getTime() > this.MAX_AGE) {
this.taskLists.delete(id);
cleanedCount++;
}
}
if (cleanedCount > 0) {
console.log(`Cleaned up ${cleanedCount} old task lists`);
}
}
getStats() {
let totalTasks = 0;
for (const taskList of this.taskLists.values()) {
totalTasks += taskList.tasks.length;
}
return {
totalTaskLists: this.taskLists.size,
totalTasks,
};
}
destroy() {
if (this.cleanupInterval) {
clearInterval(this.cleanupInterval);
}
this.taskLists.clear();
}
}