memtask
Version:
Memory and task management MCP Server with Goal-Task-Memory architecture
900 lines (899 loc) • 59.1 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.MemoryContextServer = void 0;
/**
* Server Class
*
* Updated to use unified CacheService instead of individual cache implementations.
* Creates CacheService instances for each Manager to eliminate code duplication.
*/
const index_js_1 = require("@modelcontextprotocol/sdk/server/index.js");
const types_js_1 = require("@modelcontextprotocol/sdk/types.js");
const stdio_js_1 = require("@modelcontextprotocol/sdk/server/stdio.js");
const memory_1 = require("./memory");
const task_1 = require("./task");
const goal_1 = require("./goal");
const cache_1 = require("./cache");
/**
* Memory Context Server Class
*/
class MemoryContextServer {
/**
* Constructor
* @param config System configuration
* @param logger Logger
*/
constructor(config, logger) {
this.config = config;
this.logger = logger;
// Create server
this.server = new index_js_1.Server({
name: 'memory-context-server',
version: '2.0.0',
}, {
capabilities: {
resources: {},
tools: {},
},
});
// Create cache service factory
const cacheFactory = new cache_1.CacheServiceFactory(logger);
// Create cache services for each manager
const memoryCacheService = cacheFactory.create(this.config.cache.memory);
const taskCacheService = cacheFactory.create(this.config.cache.task);
const goalCacheService = cacheFactory.create(this.config.cache.goal);
// Create managers with injected cache services
this.memoryManager = new memory_1.MemoryManager(this.config.memoriesPath, memoryCacheService, logger);
this.taskManager = new task_1.TaskManager(this.config.tasksPath, taskCacheService, logger);
this.goalManager = new goal_1.GoalManager(this.config.goalsPath, goalCacheService, logger);
}
/**
* Initialize server
*/
async initialize() {
this.logger.info('Initializing Memory Context Server');
// Initialize managers
await this.memoryManager.initialize();
await this.taskManager.initialize();
await this.goalManager.initialize();
// Set up request handlers
this.setupHandlers();
this.logger.info('Memory Context Server initialization complete');
}
/**
* Set up request handlers
*/
setupHandlers() {
// Handle list resources request
this.server.setRequestHandler(types_js_1.ListResourcesRequestSchema, async () => {
this.logger.debug('Handling list resources request');
try {
const memories = await this.memoryManager.listMemories();
const tasks = await this.taskManager.listTasks();
const goals = await this.goalManager.listGoals();
const resources = [
// // Memory resources
// ...memories.map(memory => ({
// uri: `memory://${memory.id}`,
// name: `Memory: ${memory.summary}`,
// description: `Memory created at ${memory.metadata.created_at}. Tags: ${memory.metadata.tags.join(', ')}`,
// mimeType: 'application/json'
// })),
// // Task resources
// ...tasks.map(task => ({
// uri: `task://${task.id}`,
// name: `Task: ${task.title}`,
// description: `${task.status} task, priority ${task.priority}. Created at ${task.created_at}`,
// mimeType: 'application/json'
// })),
// // Goal resources
// ...goals.map(goal => ({
// uri: `goal://${goal.id}`,
// name: `Goal: ${goal.title}`,
// description: `${goal.status} goal, priority ${goal.priority}. Created at ${goal.created_at}`,
// mimeType: 'application/json'
// })),
// Aggregate resources
{
uri: 'memory://all',
name: 'All Memories',
description: `All ${memories.length} memories in the system`,
mimeType: 'application/json'
},
{
uri: 'task://all',
name: 'All Tasks',
description: `All ${tasks.length} tasks in the system`,
mimeType: 'application/json'
},
{
uri: 'goal://all',
name: 'All Goals',
description: `All ${goals.length} goals in the system`,
mimeType: 'application/json'
}
];
this.logger.info(`Listed ${resources.length} resources`);
return { resources };
}
catch (error) {
this.logger.error('Failed to handle list resources request', error instanceof Error ? error : undefined);
throw error;
}
});
// Handle resource read request
this.server.setRequestHandler(types_js_1.ReadResourceRequestSchema, async (request) => {
const { uri } = request.params;
this.logger.debug('Handling resource read request', { uri });
try {
// Parse URI to determine resource type and ID
const url = new URL(uri);
const protocol = url.protocol.slice(0, -1); // Remove trailing ':'
const resourceId = url.hostname || url.pathname.slice(1);
switch (protocol) {
case 'memory': {
if (resourceId === 'all') {
const memories = await this.memoryManager.listMemories();
return {
contents: [
{
uri,
mimeType: 'application/json',
text: JSON.stringify(memories, null, 2)
}
]
};
}
else {
const memory = await this.memoryManager.getMemory(resourceId);
if (!memory) {
throw new Error(`Memory ${resourceId} does not exist`);
}
return {
contents: [
{
uri,
mimeType: 'application/json',
text: JSON.stringify(memory, null, 2)
}
]
};
}
}
case 'task': {
if (resourceId === 'all') {
const tasks = await this.taskManager.listTasks();
return {
contents: [
{
uri,
mimeType: 'application/json',
text: JSON.stringify(tasks, null, 2)
}
]
};
}
else {
const task = await this.taskManager.getTaskStatus({ id: resourceId });
if (!task) {
throw new Error(`Task ${resourceId} does not exist`);
}
return {
contents: [
{
uri,
mimeType: 'application/json',
text: JSON.stringify(task, null, 2)
}
]
};
}
}
case 'goal': {
if (resourceId === 'all') {
const goals = await this.goalManager.listGoals();
return {
contents: [
{
uri,
mimeType: 'application/json',
text: JSON.stringify(goals, null, 2)
}
]
};
}
else {
const goal = await this.goalManager.getGoal({ id: resourceId });
if (!goal) {
throw new Error(`Goal ${resourceId} does not exist`);
}
return {
contents: [
{
uri,
mimeType: 'application/json',
text: JSON.stringify(goal, null, 2)
}
]
};
}
}
default:
throw new Error(`Unknown resource protocol: ${protocol}`);
}
}
catch (error) {
this.logger.error(`Failed to read resource ${uri}`, error instanceof Error ? error : undefined);
throw new Error(`Failed to read resource ${uri}: ${error instanceof Error ? error.message : String(error)}`);
}
});
// List available tools
this.server.setRequestHandler(types_js_1.ListToolsRequestSchema, async () => {
this.logger.debug('Handling tool list request');
try {
const tools = [
// Memory management tools
{
name: 'create_memory',
description: 'Create new memory entry',
inputSchema: {
type: 'object',
properties: {
content: { type: 'string', description: 'Memory content' },
summary: { type: 'string', description: 'Memory summary' },
tags: { type: 'array', items: { type: 'string' }, description: 'Tags (optional)' },
context_id: { type: 'string', description: 'Related context ID (optional)' }
},
required: ['content', 'summary']
}
},
{
name: 'read_memory',
description: 'Retrieve specific memory by ID',
inputSchema: {
type: 'object',
properties: {
id: { type: 'string', description: 'Memory ID' }
},
required: ['id']
}
},
{
name: 'search_memories',
description: 'Search memories by query',
inputSchema: {
type: 'object',
properties: {
query: { type: 'string', description: 'Search query' },
limit: { type: 'number', description: 'Result limit', default: 10 }
},
required: ['query']
}
},
{
name: 'list_memories',
description: 'List memories with optional filtering',
inputSchema: {
type: 'object',
properties: {
tags: { type: 'array', items: { type: 'string' }, description: 'Filter by tags (optional)' }
}
}
},
{
name: 'delete_memory',
description: 'Delete memory by ID',
inputSchema: {
type: 'object',
properties: {
id: { type: 'string', description: 'Memory ID' }
},
required: ['id']
}
},
// Goal management tools
{
name: 'create_goal',
description: 'Create new goal',
inputSchema: {
type: 'object',
properties: {
title: { type: 'string', description: 'Goal title' },
description: { type: 'string', description: 'Goal description' },
priority: { type: 'string', enum: ['low', 'medium', 'high'], description: 'Priority (optional)', default: 'medium' },
tags: { type: 'array', items: { type: 'string' }, description: 'Tags (optional)' },
target_date: { type: 'string', description: 'Target date in ISO format (optional)' },
success_criteria: { type: 'array', items: { type: 'string' }, description: 'Success criteria (optional)' },
linked_tasks: { type: 'array', items: { type: 'string' }, description: 'Related task IDs (optional)' }
},
required: ['title', 'description']
}
},
{
name: 'read_goal',
description: 'Retrieve specific goal by ID',
inputSchema: {
type: 'object',
properties: {
id: { type: 'string', description: 'Goal ID' }
},
required: ['id']
}
},
{
name: 'update_goal',
description: 'Update existing goal',
inputSchema: {
type: 'object',
properties: {
id: { type: 'string', description: 'Goal ID' },
status: { type: 'string', enum: ['planning', 'active', 'completed', 'on_hold', 'cancelled'], description: 'Goal status (optional)' },
title: { type: 'string', description: 'Goal title (optional)' },
description: { type: 'string', description: 'Goal description (optional)' },
priority: { type: 'string', enum: ['low', 'medium', 'high'], description: 'Priority (optional)' },
target_date: { type: 'string', description: 'Target date in ISO format (optional)' },
progress_note: { type: 'string', description: 'Progress note (optional)' },
success_criteria: { type: 'array', items: { type: 'string' }, description: 'Success criteria (optional)' },
linked_tasks: { type: 'array', items: { type: 'string' }, description: 'Related task IDs (optional)' }
},
required: ['id']
}
},
{
name: 'search_goals',
description: 'Search goals by query',
inputSchema: {
type: 'object',
properties: {
query: { type: 'string', description: 'Search query' },
limit: { type: 'number', description: 'Result limit', default: 10 }
},
required: ['query']
}
},
{
name: 'list_goals',
description: 'List goals with optional filtering',
inputSchema: {
type: 'object',
properties: {
status: { type: 'string', enum: ['planning', 'active', 'completed', 'on_hold', 'cancelled'], description: 'Filter by status (optional)' },
priority: { type: 'string', enum: ['low', 'medium', 'high'], description: 'Filter by priority (optional)' },
tags: { type: 'array', items: { type: 'string' }, description: 'Filter by tags (optional)' }
}
}
},
{
name: 'delete_goal',
description: 'Delete goal by ID',
inputSchema: {
type: 'object',
properties: {
id: { type: 'string', description: 'Goal ID' }
},
required: ['id']
}
},
// Task management tools
{
name: 'create_task',
description: 'Create new task',
inputSchema: {
type: 'object',
properties: {
title: { type: 'string', description: 'Task title' },
description: { type: 'string', description: 'Task description' },
goal_id: { type: 'string', description: 'Goal ID to associate this task with (required)' },
priority: { type: 'string', enum: ['low', 'medium', 'high'], description: 'Priority (optional)', default: 'medium' },
tags: { type: 'array', items: { type: 'string' }, description: 'Tags (optional)' },
due_date: { type: 'string', description: 'Due date in ISO format (optional)' },
linked_memories: { type: 'array', items: { type: 'string' }, description: 'Related memory IDs (optional)' },
depends_on: { type: 'array', items: { type: 'string' }, description: 'Task IDs this task depends on (optional)' }
},
required: ['title', 'description', 'goal_id']
}
},
{
name: 'read_task',
description: 'Retrieve specific task by ID',
inputSchema: {
type: 'object',
properties: {
id: { type: 'string', description: 'Task ID' }
},
required: ['id']
}
},
{
name: 'update_task',
description: 'Update existing task',
inputSchema: {
type: 'object',
properties: {
id: { type: 'string', description: 'Task ID' },
status: { type: 'string', enum: ['todo', 'in_progress', 'completed', 'cancelled'], description: 'Task status (optional)' },
title: { type: 'string', description: 'Task title (optional)' },
description: { type: 'string', description: 'Task description (optional)' },
priority: { type: 'string', enum: ['low', 'medium', 'high'], description: 'Priority (optional)' },
goal_id: { type: 'string', description: 'Goal ID to associate this task with (optional)' },
progress_note: { type: 'string', description: 'Progress note (optional)' },
depends_on: { type: 'array', items: { type: 'string' }, description: 'Task IDs this task depends on (optional)' }
},
required: ['id']
}
},
{
name: 'search_tasks',
description: 'Search tasks by query',
inputSchema: {
type: 'object',
properties: {
query: { type: 'string', description: 'Search query' },
limit: { type: 'number', description: 'Result limit', default: 10 }
},
required: ['query']
}
},
{
name: 'list_tasks',
description: 'List tasks with optional filtering',
inputSchema: {
type: 'object',
properties: {
status: { type: 'string', enum: ['todo', 'in_progress', 'completed', 'cancelled'], description: 'Filter by status (optional)' },
priority: { type: 'string', enum: ['low', 'medium', 'high'], description: 'Filter by priority (optional)' },
goal_id: { type: 'string', description: 'Filter by goal ID (optional)' },
tags: { type: 'array', items: { type: 'string' }, description: 'Filter by tags (optional)' }
}
}
},
{
name: 'delete_task',
description: 'Delete task by ID',
inputSchema: {
type: 'object',
properties: {
id: { type: 'string', description: 'Task ID' }
},
required: ['id']
}
},
// System overview tool
{
name: 'overview',
description: 'Display complete system overview. IMPORTANT: Always use this tool first to understand the current system state.',
inputSchema: {
type: 'object',
properties: {}
}
}
];
this.logger.info(`Listed ${tools.length} tools`);
return { tools };
}
catch (error) {
this.logger.error('Failed to handle tool list request', error instanceof Error ? error : undefined);
throw error;
}
});
// Handle tool calls
this.server.setRequestHandler(types_js_1.CallToolRequestSchema, async (request) => {
const { name, arguments: args } = request.params;
this.logger.debug('Handling tool call request', { name, args });
try {
switch (name) {
case 'create_memory': {
const memory = await this.memoryManager.addMemory(args);
return {
content: [
{
type: 'text',
text: `Memory added successfully. ID: ${memory.id}`
}
]
};
}
case 'read_memory': {
const { id } = args;
const memory = await this.memoryManager.getMemory(id);
if (!memory) {
return {
content: [
{
type: 'text',
text: `Memory ${id} does not exist.`
}
]
};
}
return {
content: [
{
type: 'text',
text: `Memory details:\nID: ${memory.id}\nSummary: ${memory.summary}\nContent: ${memory.content}\nTags: ${memory.metadata.tags.join(', ')}\nCreated at: ${memory.metadata.created_at}\nUpdated at: ${memory.metadata.updated_at}`
}
]
};
}
case 'search_memories': {
const results = await this.memoryManager.searchMemory(args);
const truncateContent = (content, maxLength = 100) => {
return content.length > maxLength ? content.substring(0, maxLength) + '...' : content;
};
return {
content: [
{
type: 'text',
text: `Found ${results.length} related memories:\n\n${results.map(r => `ID: ${r.memory.id}\nSummary: ${r.memory.summary}\nContent Preview: ${truncateContent(r.memory.content)}\nTags: ${r.memory.metadata.tags.join(', ')}\nCreated at: ${r.memory.metadata.created_at}\nSimilarity: ${r.similarity.toFixed(2)}\n---`).join('\n')}`
}
]
};
}
case 'list_memories': {
const memories = await this.memoryManager.listMemories(args);
const truncateContent = (content, maxLength = 100) => {
return content.length > maxLength ? content.substring(0, maxLength) + '...' : content;
};
return {
content: [
{
type: 'text',
text: `Total ${memories.length} memories:\n\n${memories.map(m => `ID: ${m.id}\nSummary: ${m.summary}\nContent Preview: ${truncateContent(m.content)}\nTags: ${m.metadata.tags.join(', ')}\nCreated at: ${m.metadata.created_at}\n---`).join('\n')}`
}
]
};
}
case 'delete_memory': {
const deleteArgs = args;
const result = await this.memoryManager.deleteMemory(deleteArgs);
return {
content: [
{
type: 'text',
text: result ? `Memory ${deleteArgs.id} deleted successfully.` : `Memory ${deleteArgs.id} does not exist.`
}
]
};
}
case 'create_goal': {
const goal = await this.goalManager.createGoal(args);
return {
content: [
{
type: 'text',
text: `Goal ${goal.id} created successfully.\nTitle: ${goal.title}\nPriority: ${goal.priority}\nStatus: ${goal.status}\nNext: Use update_goal to set status to "active" when ready to start`
}
]
};
}
case 'read_goal': {
const { id } = args;
const goal = await this.goalManager.getGoal({ id });
if (!goal) {
return {
content: [
{
type: 'text',
text: `Goal ${id} does not exist.`
}
]
};
}
const linkedTasksText = goal.linked_tasks.length > 0 ?
`Linked tasks:\n${goal.linked_tasks.map(id => ` - ${id}`).join('\n')}` :
'Linked tasks: None';
const progressNotesText = goal.progress_notes.length > 0 ?
`Progress notes:\n${goal.progress_notes.map((note, i) => ` ${i + 1}. ${note}`).join('\n')}` :
'Progress notes: None';
const successCriteriaText = goal.success_criteria.length > 0 ?
`Success criteria:\n${goal.success_criteria.map((criteria, i) => ` ${i + 1}. ${criteria}`).join('\n')}` :
'Success criteria: None';
return {
content: [
{
type: 'text',
text: `Goal details:\nID: ${goal.id}\nTitle: ${goal.title}\nDescription: ${goal.description}\nStatus: ${goal.status}\nPriority: ${goal.priority}\nTags: ${goal.tags.join(', ')}\nCreated at: ${goal.created_at}\nLast updated: ${goal.updated_at}\nTarget date: ${goal.target_date || 'Not set'}\n${linkedTasksText}\n${progressNotesText}\n${successCriteriaText}`
}
]
};
}
case 'update_goal': {
const goal = await this.goalManager.updateGoal(args);
if (!goal) {
return {
content: [
{
type: 'text',
text: `Goal ${args.id} does not exist.`
}
]
};
}
return {
content: [
{
type: 'text',
text: `Goal ${goal.id} updated successfully.\nCurrent status: ${goal.status}\nLast updated: ${goal.updated_at}`
}
]
};
}
case 'search_goals': {
const results = await this.goalManager.searchGoal(args);
const truncateContent = (content, maxLength = 100) => {
return content.length > maxLength ? content.substring(0, maxLength) + '...' : content;
};
return {
content: [
{
type: 'text',
text: `Found ${results.length} related goals:\n\n${results.map(r => `ID: ${r.goal.id}\nTitle: ${r.goal.title}\nDescription: ${truncateContent(r.goal.description)}\nStatus: ${r.goal.status}\nPriority: ${r.goal.priority}\nTags: ${r.goal.tags.join(', ')}\nCreated at: ${r.goal.created_at}\nSimilarity: ${r.similarity.toFixed(2)}\n---`).join('\n')}`
}
]
};
}
case 'list_goals': {
const goals = await this.goalManager.listGoals(args);
const truncateContent = (content, maxLength = 100) => {
return content.length > maxLength ? content.substring(0, maxLength) + '...' : content;
};
return {
content: [
{
type: 'text',
text: `Total ${goals.length} goals:\n\n${goals.map(g => `ID: ${g.id}\nTitle: ${g.title}\nDescription: ${truncateContent(g.description)}\nStatus: ${g.status}\nPriority: ${g.priority}\nTags: ${g.tags.join(', ')}\nCreated at: ${g.created_at}\n---`).join('\n')}`
}
]
};
}
case 'delete_goal': {
const deleteArgs = args;
// First, check if goal exists
const goalExists = await this.goalManager.getGoal({ id: deleteArgs.id });
if (!goalExists) {
return {
content: [
{
type: 'text',
text: `Goal ${deleteArgs.id} does not exist.`
}
]
};
}
// Find all tasks associated with this goal
const allTasks = await this.taskManager.listTasks();
const relatedTasks = allTasks.filter(task => task.goal_id === deleteArgs.id);
// Clear goal_id from related tasks (make them orphaned)
for (const task of relatedTasks) {
await this.taskManager.updateTask({
id: task.id,
goal_id: '' // Clear the goal_id
});
}
// Now delete the goal
const result = await this.goalManager.deleteGoal(deleteArgs);
const orphanedMessage = relatedTasks.length > 0 ?
` ${relatedTasks.length} related tasks have been orphaned.` : '';
return {
content: [
{
type: 'text',
text: `Goal ${deleteArgs.id} deleted successfully.${orphanedMessage}`
}
]
};
}
case 'create_task': {
const createArgs = args;
// Validate goal_id exists
if (createArgs.goal_id) {
const goalExists = await this.goalManager.getGoal({ id: createArgs.goal_id });
if (!goalExists) {
return {
content: [
{
type: 'text',
text: `Error: Goal ${createArgs.goal_id} does not exist. Please create the goal first or use an existing goal ID.`
}
]
};
}
}
const task = await this.taskManager.createTask(createArgs);
const executableTasks = await this.taskManager.getExecutableTasks();
const isExecutable = executableTasks.some(t => t.id === task.id);
const dependsText = task.depends_on && task.depends_on.length > 0 ?
`\nDependencies: ${task.depends_on.join(', ')}` : '';
const statusText = isExecutable ?
'\nStatus: Ready to execute' :
(task.depends_on && task.depends_on.length > 0 ?
'\nStatus: Waiting for dependencies to complete' :
'\nStatus: Ready to execute');
const nextAction = isExecutable ?
'\nNext: Use update_task to set status to "in_progress" when ready to start' :
'\nNext: Complete dependent tasks first, then use overview to check executable tasks';
return {
content: [
{
type: 'text',
text: `Task ${task.id} created successfully.\nTitle: ${task.title}\nPriority: ${task.priority}${dependsText}${statusText}${nextAction}`
}
]
};
}
case 'read_task': {
const { id } = args;
const task = await this.taskManager.getTaskStatus({ id });
if (!task) {
return {
content: [
{
type: 'text',
text: `Task ${id} does not exist.`
}
]
};
}
const linkedMemoriesText = task.linked_memories.length > 0 ?
`Linked memories:\n${task.linked_memories.map(id => ` - ${id}`).join('\n')}` :
'Linked memories: None';
const progressNotesText = task.progress_notes.length > 0 ?
`Progress notes:\n${task.progress_notes.map((note, i) => ` ${i + 1}. ${note}`).join('\n')}` :
'Progress notes: None';
return {
content: [
{
type: 'text',
text: `Task details:\nID: ${task.id}\nTitle: ${task.title}\nDescription: ${task.description}\nStatus: ${task.status}\nPriority: ${task.priority}\nTags: ${task.tags.join(', ')}\nCreated at: ${task.created_at}\nLast updated: ${task.updated_at}\nDue date: ${task.due_date || 'Not set'}\n${linkedMemoriesText}\n${progressNotesText}`
}
]
};
}
case 'update_task': {
const updateArgs = args;
// Validate goal_id exists if being updated
if (updateArgs.goal_id !== undefined && updateArgs.goal_id !== null && updateArgs.goal_id !== '') {
const goalExists = await this.goalManager.getGoal({ id: updateArgs.goal_id });
if (!goalExists) {
return {
content: [
{
type: 'text',
text: `Error: Goal ${updateArgs.goal_id} does not exist. Please create the goal first or use an existing goal ID.`
}
]
};
}
}
const task = await this.taskManager.updateTask(updateArgs);
if (!task) {
return {
content: [
{
type: 'text',
text: `Task ${updateArgs.id} does not exist.`
}
]
};
}
// Check for unlocked tasks if this task was completed
let unlockedTasksText = '';
if (task.status === 'completed') {
const allTasks = await this.taskManager.getTasksInOrder();
const unlockedTasks = allTasks.filter(t => t.depends_on && t.depends_on.includes(task.id) &&
t.status !== 'completed' && t.status !== 'cancelled');
if (unlockedTasks.length > 0) {
unlockedTasksText = `\nUnlocked tasks: ${unlockedTasks.map(t => `Task ${t.id}`).join(', ')}`;
}
}
// Get next action suggestion
const executableTasks = await this.taskManager.getExecutableTasks();
const nextActionText = executableTasks.length > 0 ?
`\nNext: ${executableTasks.length} task(s) ready to execute. Use overview to see them.` :
'\nNext: Use overview to check system status and plan next steps.';
return {
content: [
{
type: 'text',
text: `Task ${task.id} updated successfully.\nStatus: ${task.status}\nLast updated: ${task.updated_at}${unlockedTasksText}${nextActionText}`
}
]
};
}
case 'delete_task': {
const deleteArgs = args;
const result = await this.taskManager.deleteTask(deleteArgs);
return {
content: [
{
type: 'text',
text: result ? `Task ${deleteArgs.id} deleted successfully.` : `Task ${deleteArgs.id} does not exist.`
}
]
};
}
case 'list_tasks': {
const tasks = await this.taskManager.listTasks(args);
const executableTasks = await this.taskManager.getExecutableTasks();
const truncateDescription = (description, maxLength = 100) => {
return description.length > maxLength ? description.substring(0, maxLength) + '...' : description;
};
// Sort tasks by numeric ID for better order display
const sortedTasks = tasks.sort((a, b) => parseInt(a.id) - parseInt(b.id));
const taskList = sortedTasks.map(t => {
const isExecutable = executableTasks.some(et => et.id === t.id);
const statusIcon = t.status === 'completed' ? 'DONE' :
t.status === 'in_progress' ? 'ACTIVE' :
t.status === 'cancelled' ? 'CANCELLED' :
isExecutable ? 'READY' : 'WAITING';
const dependsText = t.depends_on && t.depends_on.length > 0 ?
`\nDepends on: ${t.depends_on.join(', ')}` : '';
return `Task ${t.id}: ${t.title}\nStatus: ${statusIcon} | Priority: ${t.priority}\nDescription: ${truncateDescription(t.description)}${dependsText}\n---`;
}).join('\n');
const executableCount = executableTasks.filter(t => tasks.some(lt => lt.id === t.id)).length;
const suggestion = executableCount > 0 ?
`\n${executableCount} task(s) ready to execute. Consider starting with update_task.` :
'\nUse overview to see all tasks with dependency information.';
return {
content: [
{
type: 'text',
text: `Total ${tasks.length} tasks:\n\n${taskList}${suggestion}`
}
]
};
}
case 'search_tasks': {
const tasks = await this.taskManager.searchTask(args);
const truncateDescription = (description, maxLength = 100) => {
return description.length > maxLength ? description.substring(0, maxLength) + '...' : description;
};
return {
content: [
{
type: 'text',
text: `Found ${tasks.length} related tasks:\n\n${tasks.map(t => `ID: ${t.task.id}\nTitle: ${t.task.title}\nDescription Preview: ${truncateDescription(t.task.description)}\nStatus: ${t.task.status}\nPriority: ${t.task.priority}\nTags: ${t.task.tags.join(', ')}\nSimilarity: ${t.similarity.toFixed(2)}\n---`).join('\n')}`
}
]
};
}
case 'overview': {
// Reuse the logic from "overview://summary" resource
const memories = await this.memoryManager.listMemories();
const tasks = await this.taskManager.listTasks();
const goals = await this.goalManager.listGoals();
const truncateText = (text, maxLength = 80) => {
return text.length > maxLength ? text.substring(0, maxLength) + '...' : text;
};
// Calculate statistics
const activeTasks = tasks.filter(t => t.status !== 'completed' && t.status !== 'cancelled');
const executableTasks = await this.taskManager.getExecutableTasks();
const tasksInOrder = await this.taskManager.getTasksInOrder();
const taskStats = {
todo: tasks.filter(t => t.status === 'todo').length,
inProgress: tasks.filter(t => t.status === 'in_progress').length,
completed: tasks.filter(t => t.status === 'completed').length,
cancelled: tasks.filter(t => t.status === 'cancelled').length
};
// Calculate label statistics
const tagCounts = new Map();
memories.forEach(m => {
m.metadata.tags.forEach(tag => {
tagCounts.set(tag, (tagCounts.get(tag) || 0) + 1);
});
});
const topTags = Array.from(tagCounts.entries())
.sort((a, b) => b[1] - a[1])
.slice(0, 10);
// Cache Statistics
const memoryCache = this.memoryManager.getCacheStats();
const taskCache = this.taskManager.getCacheStats();
const goalCache = this.goalManager.getCacheStats();
const totalHits = memoryCache.hits + taskCache.hits + goalCache.hits;
const totalMisses = memoryCache.misses + taskCache.misses + goalCache.misses;
const overallHitRate = totalHits + totalMisses > 0 ? (totalHits / (totalHits + totalMisses) * 100).toFixed(1) : '0.0';
// Goal-Task relationship processing functions
const buildGoalTaskMap = (goals, tasks) => {
const goalTaskMap = new Map();
// Initialize map with all goals
goals.forEach(goal => {