Skip to content

Return full task details on read_task and get_next_task tool calls #41

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { ListToolsRequestSchema, CallToolRequestSchema } from "@modelcontextprot
const server = new Server(
{
name: "task-manager-server",
version: "1.3.1"
version: "1.3.2"
},
{
capabilities: {
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "taskqueue-mcp",
"version": "1.3.1",
"version": "1.3.2",
"description": "Task Queue MCP Server",
"author": "Christopher C. Smith ([email protected])",
"main": "dist/index.js",
Expand Down
2 changes: 1 addition & 1 deletion src/client/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ const program = new Command();
program
.name("taskqueue")
.description("CLI for the Task Manager MCP Server")
.version("1.3.1")
.version("1.3.2")
.option(
'-f, --file-path <path>',
'Specify the path to the tasks JSON file. Overrides TASK_MANAGER_FILE_PATH env var.'
Expand Down
31 changes: 9 additions & 22 deletions src/server/TaskManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -280,7 +280,7 @@ export class TaskManager {
}
}

public async getNextTask(projectId: string): Promise<StandardResponse> {
public async getNextTask(projectId: string): Promise<StandardResponse<OpenTaskSuccessData | { message: string }>> {
await this.ensureInitialized();
// Reload from disk to ensure we have the latest data
await this.reloadFromDisk();
Expand Down Expand Up @@ -316,15 +316,11 @@ export class TaskManager {
);
}

return {
status: "next_task",
data: {
id: nextTask.id,
title: nextTask.title,
description: nextTask.description,
message: `Next task is ready. Task approval will be required after completion.\n`
}
};
// Return the full task details similar to openTaskDetails
return createSuccessResponse<OpenTaskSuccessData>({
projectId: proj.projectId,
task: { ...nextTask },
});
}

public async approveTaskCompletion(projectId: string, taskId: string): Promise<StandardResponse<ApproveTaskSuccessData>> {
Expand Down Expand Up @@ -428,23 +424,14 @@ export class TaskManager {
await this.ensureInitialized();
// Reload from disk to ensure we have the latest data
await this.reloadFromDisk();

for (const proj of this.data.projects) {
const target = proj.tasks.find((t) => t.id === taskId);
if (target) {
// Return only projectId and the full task object
return createSuccessResponse({
projectId: proj.projectId,
initialPrompt: proj.initialPrompt,
projectPlan: proj.projectPlan,
completed: proj.completed,
task: {
id: target.id,
title: target.title,
description: target.description,
status: target.status,
approved: target.approved,
completedDetails: target.completedDetails,
},
task: { ...target }, // Return all fields from the found task
});
}
}
Expand Down
10 changes: 0 additions & 10 deletions src/server/toolExecutors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -237,16 +237,6 @@ const getNextTaskToolExecutor: ToolExecutor = {
async execute(taskManager, args) {
const projectId = validateProjectId(args.projectId);
const result = await taskManager.getNextTask(projectId);

// Ensure backward compatibility with integration tests
if (result.status === "next_task" && result.data) {
return formatToolResponse({
status: "next_task",
task: result.data,
message: result.data.message,
});
}

return formatToolResponse(result);
},
};
Expand Down
19 changes: 2 additions & 17 deletions src/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -111,10 +111,7 @@ export interface ApproveProjectSuccessData {

export interface OpenTaskSuccessData {
projectId: string;
initialPrompt: string;
projectPlan: string;
completed: boolean;
task: Task; // Use the full Task type
task: Task;
}

export interface ListProjectsSuccessData {
Expand Down Expand Up @@ -147,7 +144,7 @@ export interface ReadProjectSuccessData {
initialPrompt: string;
projectPlan: string;
completed: boolean;
tasks: Task[]; // Use the full Task type
tasks: Task[];
}

// --- End NEW Success Data Interfaces ---
Expand All @@ -169,17 +166,6 @@ export interface ErrorResponse {
};
}

// Next task response
export interface NextTaskResponse {
status: "next_task";
data: {
id: string;
title: string;
description: string;
message?: string;
};
}

// All tasks done response
export interface AllTasksDoneResponse {
status: "all_tasks_done";
Expand All @@ -192,5 +178,4 @@ export interface AllTasksDoneResponse {
export type StandardResponse<T = unknown> =
| SuccessResponse<T>
| ErrorResponse
| NextTaskResponse
| AllTasksDoneResponse;
12 changes: 6 additions & 6 deletions tests/integration/TaskManager.integration.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,9 +100,9 @@ describe('TaskManager Integration', () => {

// 2. Get the next task (first task)
const nextTaskResult = await server.getNextTask(projectId);
expect(nextTaskResult.status).toBe('next_task');
if (nextTaskResult.status === 'next_task' && nextTaskResult.data) {
expect(nextTaskResult.data.id).toBe(taskId1);
expect(nextTaskResult.status).toBe('success');
if (nextTaskResult.status === 'success' && 'task' in nextTaskResult.data) {
expect(nextTaskResult.data.task.id).toBe(taskId1);
}

// 3. Mark the first task as in progress
Expand All @@ -123,9 +123,9 @@ describe('TaskManager Integration', () => {

// 6. Get the next task (second task)
const nextTaskResult2 = await server.getNextTask(projectId);
expect(nextTaskResult2.status).toBe('next_task');
if (nextTaskResult2.status === 'next_task' && nextTaskResult2.data) {
expect(nextTaskResult2.data.id).toBe(taskId2);
expect(nextTaskResult2.status).toBe('success');
if (nextTaskResult2.status === 'success' && 'task' in nextTaskResult2.data) {
expect(nextTaskResult2.data.task.id).toBe(taskId2);
}

// 7. Mark the second task as in progress
Expand Down
13 changes: 6 additions & 7 deletions tests/integration/e2e.integration.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -159,9 +159,9 @@ describe('MCP Client Integration', () => {
}) as ToolResponse;
expect(nextTaskResult.isError).toBeFalsy();
const nextTask = JSON.parse((nextTaskResult.content[0] as { text: string }).text);
expect(nextTask.status).toBe("next_task");
expect(nextTask.task).toBeDefined();
const taskId = nextTask.task.id;
expect(nextTask.status).toBe("success");
expect(nextTask.data).toHaveProperty('task');
const taskId = nextTask.data.task.id;
console.log('Got next task with ID:', taskId);

// Mark task as done
Expand Down Expand Up @@ -202,7 +202,6 @@ describe('MCP Client Integration', () => {
it('should have accurate version', async () => {
console.log('Testing server version...');
const response = await client.getServerVersion();
expect(response).toBeDefined();
expect(response).toHaveProperty('version');
// Should match package.json version
const packageJson = JSON.parse(
Expand Down Expand Up @@ -242,9 +241,9 @@ describe('MCP Client Integration', () => {
}) as ToolResponse;
expect(nextTaskResult.isError).toBeFalsy();
const nextTask = JSON.parse((nextTaskResult.content[0] as { text: string }).text);
expect(nextTask.status).toBe("next_task");
expect(nextTask.task).toBeDefined();
const taskId = nextTask.task.id;
expect(nextTask.status).toBe("success");
expect(nextTask.data).toHaveProperty('task');
const taskId = nextTask.data.task.id;

// Mark task as done - we need to mark it as done using the update_task tool
const markDoneResult = await client.callTool({
Expand Down
6 changes: 3 additions & 3 deletions tests/unit/TaskManager.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -370,9 +370,9 @@ describe('TaskManager', () => {
// Get the next task
const nextTaskResult = await taskManager.getNextTask(projectId);

expect(nextTaskResult.status).toBe('next_task');
if (nextTaskResult.status === 'next_task') {
expect(nextTaskResult.data.id).toBe(createResult.data.tasks[0].id);
expect(nextTaskResult.status).toBe('success');
if (nextTaskResult.status === 'success' && 'task' in nextTaskResult.data) {
expect(nextTaskResult.data.task.id).toBe(createResult.data.tasks[0].id);
}
}
});
Expand Down
7 changes: 5 additions & 2 deletions tests/unit/toolExecutors.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -238,8 +238,11 @@ describe('Tool Executors', () => {
};

taskManager.getNextTask.mockResolvedValue({
status: 'next_task',
data: mockTask
status: 'success',
data: {
message: 'Next task retrieved successfully',
task: mockTask
}
});

const result = await executor.execute(taskManager, { projectId: 'proj-1' });
Expand Down