Skip to content

Commit 5906ba3

Browse files
Merge pull request #20 from chriscarrollsmith/11-probably-should-make-the-request-approval-process-toggleable-on-or-off
make the request approval process toggleable on or off
2 parents 841a7d2 + da1ae97 commit 5906ba3

11 files changed

+780
-926
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# MCP Task Manager
22

3-
A Model Context Protocol (MCP) server for AI task management. This tool helps AI assistants handle multi-step tasks in a structured way, with user approval checkpoints.
3+
MCP Task Manager ([npm package: taskqueue-mcp](https://www.npmjs.com/package/taskqueue-mcp)) is a Model Context Protocol (MCP) server for AI task management. This tool helps AI assistants handle multi-step tasks in a structured way, with optional user approval checkpoints.
44

55
## Features
66

index.ts

Lines changed: 13 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import { ListToolsRequestSchema, CallToolRequestSchema } from "@modelcontextprot
1010
const server = new Server(
1111
{
1212
name: "task-manager-server",
13-
version: "1.0.7"
13+
version: "1.0.8"
1414
},
1515
{
1616
capabilities: {
@@ -178,40 +178,19 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
178178
throw new Error("Missing required parameters: projectId and/or taskId");
179179
}
180180

181-
const updates: { title?: string; description?: string } = {};
182-
if (args.title !== undefined) updates.title = String(args.title);
183-
if (args.description !== undefined) updates.description = String(args.description);
184-
181+
const updates = Object.fromEntries(
182+
Object.entries({
183+
title: args.title !== undefined ? String(args.title) : undefined,
184+
description: args.description !== undefined ? String(args.description) : undefined,
185+
status: args.status !== undefined ? String(args.status) as "not started" | "in progress" | "done" : undefined,
186+
completedDetails: args.completedDetails !== undefined ? String(args.completedDetails) : undefined,
187+
toolRecommendations: args.toolRecommendations !== undefined ? String(args.toolRecommendations) : undefined,
188+
ruleRecommendations: args.ruleRecommendations !== undefined ? String(args.ruleRecommendations) : undefined
189+
}).filter(([_, value]) => value !== undefined)
190+
);
191+
185192
const result = await taskManager.updateTask(projectId, taskId, updates);
186-
187-
// Handle status change separately if needed
188-
if (args.status) {
189-
const status = args.status as "not started" | "in progress" | "done";
190-
const proj = taskManager["data"].projects.find(p => p.projectId === projectId);
191-
if (proj) {
192-
const task = proj.tasks.find(t => t.id === taskId);
193-
if (task) {
194-
if (status === "done") {
195-
if (!args.completedDetails) {
196-
return {
197-
content: [{ type: "text", text: JSON.stringify({
198-
status: "error",
199-
message: "completedDetails is required when setting status to 'done'"
200-
}, null, 2) }],
201-
};
202-
}
203-
204-
// Use markTaskDone for proper transition to done status
205-
await taskManager.markTaskDone(projectId, taskId, String(args.completedDetails));
206-
} else {
207-
// For other status changes
208-
task.status = status;
209-
await taskManager["saveTasks"]();
210-
}
211-
}
212-
}
213-
}
214-
193+
215194
return {
216195
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
217196
};

jest.config.cjs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,4 +14,10 @@ module.exports = {
1414
],
1515
},
1616
modulePathIgnorePatterns: ['<rootDir>/dist/'],
17+
// Force Jest to exit after all tests have completed
18+
forceExit: true,
19+
// Detect open handles and warn about them
20+
detectOpenHandles: true,
21+
// Extend the timeout to allow sufficient time for tests to complete
22+
testTimeout: 30000,
1723
};

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "taskqueue-mcp",
3-
"version": "1.0.7",
3+
"version": "1.0.8",
44
"description": "Task Queue MCP Server",
55
"author": "Christopher C. Smith ([email protected])",
66
"main": "dist/index.js",

src/server/TaskManager.ts

Lines changed: 10 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,8 @@ export class TaskManager {
129129
public async createProject(
130130
initialPrompt: string,
131131
tasks: { title: string; description: string; toolRecommendations?: string; ruleRecommendations?: string }[],
132-
projectPlan?: string
132+
projectPlan?: string,
133+
autoApprove?: boolean
133134
) {
134135
await this.ensureInitialized();
135136
this.projectCounter += 1;
@@ -156,6 +157,7 @@ export class TaskManager {
156157
projectPlan: projectPlan || initialPrompt,
157158
tasks: newTasks,
158159
completed: false,
160+
autoApprove: autoApprove === true ? true : false,
159161
});
160162

161163
await this.saveTasks();
@@ -213,38 +215,6 @@ export class TaskManager {
213215
};
214216
}
215217

216-
public async markTaskDone(
217-
projectId: string,
218-
taskId: string,
219-
completedDetails?: string
220-
) {
221-
await this.ensureInitialized();
222-
const proj = this.data.projects.find((p) => p.projectId === projectId);
223-
if (!proj) return { status: "error", message: "Project not found" };
224-
const task = proj.tasks.find((t) => t.id === taskId);
225-
if (!task) return { status: "error", message: "Task not found" };
226-
if (task.status === "done")
227-
return {
228-
status: "already_done",
229-
message: "Task is already marked done.",
230-
};
231-
232-
task.status = "done";
233-
task.completedDetails = completedDetails || "";
234-
await this.saveTasks();
235-
return {
236-
status: "task_marked_done",
237-
projectId: proj.projectId,
238-
task: {
239-
id: task.id,
240-
title: task.title,
241-
description: task.description,
242-
completedDetails: task.completedDetails,
243-
approved: task.approved,
244-
},
245-
};
246-
}
247-
248218
public async approveTaskCompletion(projectId: string, taskId: string) {
249219
await this.ensureInitialized();
250220
const proj = this.data.projects.find((p) => p.projectId === projectId);
@@ -457,6 +427,8 @@ export class TaskManager {
457427
description?: string;
458428
toolRecommendations?: string;
459429
ruleRecommendations?: string;
430+
status?: "not started" | "in progress" | "done";
431+
completedDetails?: string;
460432
}
461433
) {
462434
await this.ensureInitialized();
@@ -473,6 +445,11 @@ export class TaskManager {
473445
// Update the task with the provided updates
474446
project.tasks[taskIndex] = { ...project.tasks[taskIndex], ...updates };
475447

448+
// Check if status was updated to 'done' and if project has autoApprove enabled
449+
if (updates.status === 'done' && project.autoApprove) {
450+
project.tasks[taskIndex].approved = true;
451+
}
452+
476453
await this.saveTasks();
477454
return project.tasks[taskIndex];
478455
}

src/server/tools.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,10 @@ const createProjectTool: Tool = {
7676
required: ["title", "description"],
7777
},
7878
},
79+
autoApprove: {
80+
type: "boolean",
81+
description: "If true, tasks will be automatically approved when marked as done. If false or not provided, tasks require manual approval.",
82+
},
7983
},
8084
required: ["initialPrompt", "tasks"],
8185
},

src/types/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ export interface Project {
1818
projectPlan: string;
1919
tasks: Task[];
2020
completed: boolean;
21+
autoApprove?: boolean;
2122
}
2223

2324
export interface TaskManagerFile {

0 commit comments

Comments
 (0)