feat: Add epic_close MCP tool

- New epic_close(id) function to mark epics as completed
- Added tool definition and handler
- Usage: epic_close({ id: 'CF-E1' })

Task: CF-258

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Christian Gick
2026-01-09 15:25:05 +02:00
parent e21072ea54
commit d39e82a767
3 changed files with 42 additions and 1 deletions

View File

@@ -21,7 +21,7 @@ import { toolDefinitions } from './tools/index.js';
import { taskAdd, taskList, taskShow, taskClose, taskUpdate } from './tools/crud.js'; import { taskAdd, taskList, taskShow, taskClose, taskUpdate } from './tools/crud.js';
import { taskSimilar, taskContext } from './tools/search.js'; import { taskSimilar, taskContext } from './tools/search.js';
import { taskLink, checklistAdd, checklistToggle, taskResolveDuplicate } from './tools/relations.js'; import { taskLink, checklistAdd, checklistToggle, taskResolveDuplicate } from './tools/relations.js';
import { epicAdd, epicList, epicShow, epicAssign } from './tools/epics.js'; import { epicAdd, epicList, epicShow, epicAssign, epicClose } from './tools/epics.js';
import { taskDelegations, taskDelegationQuery } from './tools/delegations.js'; import { taskDelegations, taskDelegationQuery } from './tools/delegations.js';
// Create MCP server // Create MCP server
@@ -147,6 +147,9 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
epic_id: a.epic_id, epic_id: a.epic_id,
}); });
break; break;
case 'epic_close':
result = await epicClose(a.id);
break;
// Delegations // Delegations
case 'task_delegations': case 'task_delegations':

View File

@@ -209,3 +209,30 @@ export async function epicUnassign(task_id: string): Promise<string> {
return `Unassigned ${task_id} from its epic`; return `Unassigned ${task_id} from its epic`;
} }
/**
* Close an epic (mark as completed)
*/
export async function epicClose(id: string): Promise<string> {
// Verify epic exists
const epic = await queryOne<{ id: string; title: string; status: string }>(
`SELECT id, title, status FROM epics WHERE id = $1`,
[id]
);
if (!epic) {
return `Epic not found: ${id}`;
}
if (epic.status === 'completed') {
return `Epic already completed: ${id}`;
}
// Update epic status
await execute(
`UPDATE epics SET status = 'completed', updated_at = NOW() WHERE id = $1`,
[id]
);
return `Closed: ${id} (${epic.title})`;
}

View File

@@ -197,6 +197,17 @@ export const toolDefinitions = [
required: ['task_id', 'epic_id'], required: ['task_id', 'epic_id'],
}, },
}, },
{
name: 'epic_close',
description: 'Close an epic (mark as completed)',
inputSchema: {
type: 'object',
properties: {
id: { type: 'string', description: 'Epic ID to close (e.g., VPN-E1, ST-E3)' },
},
required: ['id'],
},
},
// Delegation Tools // Delegation Tools
{ {