import type { FastMCP } from 'fastmcp'; import { z } from 'zod'; import { getController } from '../controller'; import { jsonTextContent } from '../utils/format'; const toolContextSchema = { teamName: z.string().min(1), claudeDir: z.string().min(1).optional(), }; const relationshipTypeSchema = z.enum(['blocked-by', 'blocks', 'related']); export function registerTaskTools(server: Pick) { server.addTool({ name: 'task_create', description: 'Create a team task', parameters: z.object({ ...toolContextSchema, subject: z.string().min(1), description: z.string().optional(), owner: z.string().optional(), blockedBy: z.array(z.string().min(1)).optional(), related: z.array(z.string().min(1)).optional(), prompt: z.string().optional(), startImmediately: z.boolean().optional(), }), execute: async ({ teamName, claudeDir, subject, description, owner, blockedBy, related, prompt, startImmediately }) => { const controller = getController(teamName, claudeDir); return await Promise.resolve( jsonTextContent( controller.tasks.createTask({ subject, ...(description ? { description } : {}), ...(owner ? { owner } : {}), ...(blockedBy?.length ? { 'blocked-by': blockedBy.join(',') } : {}), ...(related?.length ? { related: related.join(',') } : {}), ...(prompt ? { prompt } : {}), ...(startImmediately !== undefined ? { startImmediately } : {}), }) ) ); }, }); server.addTool({ name: 'task_get', description: 'Get a task by id', parameters: z.object({ ...toolContextSchema, taskId: z.string().min(1), }), execute: async ({ teamName, claudeDir, taskId }) => await Promise.resolve(jsonTextContent(getController(teamName, claudeDir).tasks.getTask(taskId))), }); server.addTool({ name: 'task_list', description: 'List tasks for a team', parameters: z.object({ ...toolContextSchema, }), execute: async ({ teamName, claudeDir }) => await Promise.resolve(jsonTextContent(getController(teamName, claudeDir).tasks.listTasks())), }); server.addTool({ name: 'task_set_status', description: 'Set task work status', parameters: z.object({ ...toolContextSchema, taskId: z.string().min(1), status: z.enum(['pending', 'in_progress', 'completed', 'deleted']), actor: z.string().optional(), }), execute: async ({ teamName, claudeDir, taskId, status, actor }) => await Promise.resolve( jsonTextContent(getController(teamName, claudeDir).tasks.setTaskStatus(taskId, status, actor)) ), }); server.addTool({ name: 'task_start', description: 'Mark task as in progress', parameters: z.object({ ...toolContextSchema, taskId: z.string().min(1), actor: z.string().optional(), }), execute: async ({ teamName, claudeDir, taskId, actor }) => await Promise.resolve(jsonTextContent(getController(teamName, claudeDir).tasks.startTask(taskId, actor))), }); server.addTool({ name: 'task_complete', description: 'Mark task as completed', parameters: z.object({ ...toolContextSchema, taskId: z.string().min(1), actor: z.string().optional(), }), execute: async ({ teamName, claudeDir, taskId, actor }) => await Promise.resolve( jsonTextContent(getController(teamName, claudeDir).tasks.completeTask(taskId, actor)) ), }); server.addTool({ name: 'task_set_owner', description: 'Assign or clear task owner', parameters: z.object({ ...toolContextSchema, taskId: z.string().min(1), owner: z.string().nullable(), }), execute: async ({ teamName, claudeDir, taskId, owner }) => await Promise.resolve( jsonTextContent(getController(teamName, claudeDir).tasks.setTaskOwner(taskId, owner)) ), }); server.addTool({ name: 'task_add_comment', description: 'Add task comment', parameters: z.object({ ...toolContextSchema, taskId: z.string().min(1), text: z.string().min(1), from: z.string().optional(), }), execute: async ({ teamName, claudeDir, taskId, text, from }) => await Promise.resolve( jsonTextContent( getController(teamName, claudeDir).tasks.addTaskComment(taskId, { text, ...(from ? { from } : {}), }) ) ), }); server.addTool({ name: 'task_attach_file', description: 'Attach a file to a task', parameters: z.object({ ...toolContextSchema, taskId: z.string().min(1), filePath: z.string().min(1), mode: z.enum(['copy', 'link']).optional(), filename: z.string().optional(), mimeType: z.string().optional(), noFallback: z.boolean().optional(), }), execute: async ({ teamName, claudeDir, taskId, filePath, mode, filename, mimeType, noFallback, }) => await Promise.resolve( jsonTextContent( getController(teamName, claudeDir).tasks.attachTaskFile(taskId, { file: filePath, ...(mode ? { mode } : {}), ...(filename ? { filename } : {}), ...(mimeType ? { 'mime-type': mimeType } : {}), ...(noFallback ? { 'no-fallback': true } : {}), }) ) ), }); server.addTool({ name: 'task_attach_comment_file', description: 'Attach a file to a task comment', parameters: z.object({ ...toolContextSchema, taskId: z.string().min(1), commentId: z.string().min(1), filePath: z.string().min(1), mode: z.enum(['copy', 'link']).optional(), filename: z.string().optional(), mimeType: z.string().optional(), noFallback: z.boolean().optional(), }), execute: async ({ teamName, claudeDir, taskId, commentId, filePath, mode, filename, mimeType, noFallback, }) => await Promise.resolve( jsonTextContent( getController(teamName, claudeDir).tasks.attachCommentFile(taskId, commentId, { file: filePath, ...(mode ? { mode } : {}), ...(filename ? { filename } : {}), ...(mimeType ? { 'mime-type': mimeType } : {}), ...(noFallback ? { 'no-fallback': true } : {}), }) ) ), }); server.addTool({ name: 'task_set_clarification', description: 'Set or clear task clarification state', parameters: z.object({ ...toolContextSchema, taskId: z.string().min(1), value: z.enum(['lead', 'user', 'clear']), }), execute: async ({ teamName, claudeDir, taskId, value }) => await Promise.resolve( jsonTextContent( getController(teamName, claudeDir).tasks.setNeedsClarification( taskId, value === 'clear' ? null : value ) ) ), }); server.addTool({ name: 'task_link', description: 'Link tasks by blockedBy, blocks, or related relationship', parameters: z.object({ ...toolContextSchema, taskId: z.string().min(1), targetId: z.string().min(1), relationship: relationshipTypeSchema, }), execute: async ({ teamName, claudeDir, taskId, targetId, relationship }) => await Promise.resolve( jsonTextContent(getController(teamName, claudeDir).tasks.linkTask(taskId, targetId, relationship)) ), }); server.addTool({ name: 'task_unlink', description: 'Remove task relationship link', parameters: z.object({ ...toolContextSchema, taskId: z.string().min(1), targetId: z.string().min(1), relationship: relationshipTypeSchema, }), execute: async ({ teamName, claudeDir, taskId, targetId, relationship }) => await Promise.resolve( jsonTextContent( getController(teamName, claudeDir).tasks.unlinkTask(taskId, targetId, relationship) ) ), }); server.addTool({ name: 'task_briefing', description: 'Get formatted task briefing for a member', parameters: z.object({ ...toolContextSchema, memberName: z.string().min(1), }), execute: async ({ teamName, claudeDir, memberName }) => ({ content: [ { type: 'text' as const, text: await getController(teamName, claudeDir).tasks.taskBriefing(memberName), }, ], }), }); }