Skip to main content
Glama

createTask

Create and manage new tasks in a task list with customizable options, attachments, assignees, and due dates for efficient project coordination.

Instructions

Creates a task. Create a new task in the provided task list.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
taskRequestYesRequest body: taskRequest
tasklistIdYesPath parameter: tasklistId

Implementation Reference

  • The primary handler for the 'createTask' MCP tool. Processes user input, handles tasklistId resolution from input or .teamwork file, validates task data, calls the underlying service, and returns a formatted MCP response with the created task details.
    export async function handleCreateTask(input: any) { logger.info("=== createTask tool called ==="); logger.info(`Input: ${JSON.stringify(input || {})}`); try { // Get the tasklistId from input let tasklistId = input.tasklistId ? String(input.tasklistId) : null; // If tasklistId is not provided, try to get it from .teamwork file if (!tasklistId) { try { const fs = require("fs"); if (fs.existsSync(".teamwork")) { const teamworkConfig = fs.readFileSync(".teamwork", "utf8"); const tasklistIdMatch = teamworkConfig.match(/TASKLISTID=(\d+)/); if (tasklistIdMatch) { tasklistId = String(parseInt(tasklistIdMatch[1])); logger.info(`Using tasklistId ${tasklistId} from .teamwork file`); } else { // Check if there"s a project ID and try to get tasklists const projectIdMatch = teamworkConfig.match(/PROJECTID=(\d+)/); if (projectIdMatch) { const projectId = parseInt(projectIdMatch[1]); logger.info(`Found projectId ${projectId} in .teamwork file, fetching tasklists`); const tasklists = await teamworkService.getTaskListsByProjectId(projectId); if (tasklists && tasklists.length === 1) { // If there"s only one tasklist, use it and update .teamwork file tasklistId = String(tasklists[0].id); logger.info(`Found single tasklist ${tasklistId}, using it and updating .teamwork file`); // Update .teamwork file with tasklistId const updatedConfig = teamworkConfig + `\nTASKLISTID=${tasklistId}`; fs.writeFileSync(".teamwork", updatedConfig); } else if (tasklists && tasklists.length > 1) { // If there are multiple tasklists, we can"t automatically choose logger.info(`Multiple tasklists found for project ${projectId}`); return { content: [{ type: "text", text: `Multiple tasklists found for project ${projectId}. Please specify a tasklistId in your request.` }] }; } } } } } catch (error: unknown) { const errorMessage = error instanceof Error ? error.message : String(error); logger.error(`Error reading .teamwork file: ${errorMessage}`); } } // If we still don"t have a tasklistId, return an error if (!tasklistId) { logger.info("No tasklistId provided and couldn't find one in .teamwork file"); return { content: [{ type: "text", text: "No tasklistId provided and couldn't find one in .teamwork file. Please provide a tasklistId." }] }; } // Prepare the task request const taskRequest = input.taskRequest as TaskRequest; // Validate task request if (!taskRequest || !taskRequest.task) { logger.error("Invalid task request: missing task object"); return { content: [{ type: "text", text: "Invalid task request: missing task object. Please provide a taskRequest.task object." }] }; } // Ensure task has name (Teamwork API requires 'name' for the task title) if (!taskRequest.task.name) { logger.error("Invalid task request: missing task name"); return { content: [{ type: "text", text: "Invalid task request: missing task name. Please provide taskRequest.task.name." }] }; } logger.info(`Creating task "${taskRequest.task.name}" in tasklist ${tasklistId}`); // Call the service to create the task const createdTask = await teamworkService.createTask(String(tasklistId), taskRequest); logger.info("Task created successfully"); logger.info(`Created task response: ${JSON.stringify(createdTask).substring(0, 200)}...`); // Ensure we return a properly formatted response const response = { content: [{ type: "text", text: JSON.stringify(createdTask, null, 2) }] }; // Validate that the response can be serialized try { JSON.stringify(response); logger.info("Response is valid JSON"); } catch (jsonError: any) { logger.error(`Invalid JSON in response: ${jsonError.message}`); // Return a safe response return { content: [{ type: "text", text: "Task created successfully, but there was an error formatting the response." }] }; } logger.info("=== createTask tool completed successfully ==="); return response; } catch (error: any) { logger.error(`Error in createTask handler: ${error.message}`); if (error.stack) { logger.error(`Stack trace: ${error.stack}`); } if (error.response) { logger.error(`API response error: ${JSON.stringify({ status: error.response.status, statusText: error.response.statusText, data: error.response.data })}`); } // Return a properly formatted error response return { content: [{ type: "text", text: `Error creating task: ${error.message}` }] }; } }
  • The input schema and metadata definition for the 'createTask' tool, defining the structure for taskRequest (including task details, assignees, due dates, etc.) and required tasklistId parameter.
    export const createTaskDefinition = { name: "createTask", description: "Creates a task. Create a new task in the provided task list. ", inputSchema: { type: "object", properties: { taskRequest: { type: "object", properties: { attachmentOptions: { type: "object", properties: { removeOtherFiles: { type: "boolean" } }, required: [] }, attachments: { type: "object", properties: { files: { type: "array", items: { type: "object", properties: { categoryId: { type: "integer" }, id: { type: "integer" } }, required: [], description: "File stores information about a uploaded file." } }, pendingFiles: { type: "array", items: { type: "object", properties: { categoryId: { type: "integer" }, reference: { type: "string" } }, required: [], description: "PendingFile stores information about a file uploaded on-the-fly." } } }, required: [] }, card: { type: "object", properties: { columnId: { type: "integer" } }, required: [], description: "Card stores information about the card created with the task." }, predecessors: { type: "array", items: { type: "object", properties: { id: { type: "integer" }, type: { type: "string" } }, required: [], description: "Predecessor stores information about task predecessors." } }, tags: { type: "array", items: { type: "object", properties: { color: { type: "string" }, name: { type: "string" }, projectId: { type: "integer" } }, required: [], description: "Tag contains all the information returned from a tag." } }, task: { type: "object", properties: { assignees: { type: "object", properties: { companyIds: { type: "array", items: { type: "integer" }, description: "NullableInt64Slice implements json.Unmarshaler to allow testing between a value that explicitly set to null or omitted." }, teamIds: { type: "array", items: { type: "integer" }, description: "NullableInt64Slice implements json.Unmarshaler to allow testing between a value that explicitly set to null or omitted." }, userIds: { type: "array", items: { type: "integer" }, description: "NullableInt64Slice implements json.Unmarshaler to allow testing between a value that explicitly set to null or omitted." } }, required: [], description: "UserGroups are common lists for storing users, companies and teams ids together." }, attachmentIds: { type: "array", items: { type: "integer" } }, changeFollowers: { type: "object", properties: { companyIds: { type: "array", items: { type: "integer" }, description: "NullableInt64Slice implements json.Unmarshaler to allow testing between a value that explicitly set to null or omitted." }, teamIds: { type: "array", items: { type: "integer" }, description: "NullableInt64Slice implements json.Unmarshaler to allow testing between a value that explicitly set to null or omitted." }, userIds: { type: "array", items: { type: "integer" }, description: "NullableInt64Slice implements json.Unmarshaler to allow testing between a value that explicitly set to null or omitted." } }, required: [], description: "UserGroups are common lists for storing users, companies and teams ids together." }, commentFollowers: { type: "object", properties: { companyIds: { type: "array", items: { type: "integer" }, description: "NullableInt64Slice implements json.Unmarshaler to allow testing between a value that explicitly set to null or omitted." }, teamIds: { type: "array", items: { type: "integer" }, description: "NullableInt64Slice implements json.Unmarshaler to allow testing between a value that explicitly set to null or omitted." }, userIds: { type: "array", items: { type: "integer" }, description: "NullableInt64Slice implements json.Unmarshaler to allow testing between a value that explicitly set to null or omitted." } }, required: [], description: "UserGroups are common lists for storing users, companies and teams ids together." }, completedAt: { type: "string" }, completedBy: { type: "integer" }, createdAt: { type: "string" }, createdBy: { type: "integer" }, crmDealIds: { type: "array", items: { type: "integer" } }, customFields: { type: "object", properties: { Values: { type: "array", items: { type: "object", properties: { countryCode: { type: "string" }, currencySymbol: { type: "string" }, customfieldId: { type: "integer" }, urlTextToDisplay: { type: "string" }, value: { type: "string" } }, required: [], description: "CustomFieldValue contains all the information returned from a customfield." } } }, required: [], description: "CustomFields is the custom fields type." }, description: { type: "string" }, descriptionContentType: { type: "string" }, dueAt: { type: "string", format: "date", description: "NullableDate implements json.Unmarshaler to allow testing between a value that explicitly set to null or omitted. Date format `2006-01-02`" }, estimatedMinutes: { type: "integer" }, grantAccessTo: { type: "object", properties: { companyIds: { type: "array", items: { type: "integer" }, description: "NullableInt64Slice implements json.Unmarshaler to allow testing between a value that explicitly set to null or omitted." }, teamIds: { type: "array", items: { type: "integer" }, description: "NullableInt64Slice implements json.Unmarshaler to allow testing between a value that explicitly set to null or omitted." }, userIds: { type: "array", items: { type: "integer" }, description: "NullableInt64Slice implements json.Unmarshaler to allow testing between a value that explicitly set to null or omitted." } }, required: [], description: "UserGroups are common lists for storing users, companies and teams ids together." }, hasDeskTickets: { type: "boolean" }, name: { type: "string" }, originalDueDate: { type: "string", format: "date", description: "NullableDate implements json.Unmarshaler to allow testing between a value that explicitly set to null or omitted. Date format `2006-01-02`" }, parentTaskId: { type: "integer" }, priority: { type: "string", enum: [ "low", "normal", "high" ], description: "NullableTaskPriority implements json.Unmarshaler to allow testing between a value that explicitly set to null or omitted." }, private: { type: "boolean" }, progress: { type: "integer" }, reminders: { type: "array", items: { type: "object", properties: { isRelative: { type: "boolean" }, note: { type: "string" }, relativeNumberDays: { type: "integer" }, remindAt: { type: "string" }, type: { type: "string" }, userId: { type: "integer" } }, required: [], description: "Reminder stores all necessary information to create a task reminder." } }, repeatOptions: { type: "object", properties: { duration: { type: "integer" }, editOption: { type: "string" }, endsAt: { type: "string", format: "date", description: "NullableDate implements json.Unmarshaler to allow testing between a value that explicitly set to null or omitted. Date format `2006-01-02`" }, frequency: { type: "object", description: "NullableTaskRepeatFrequency implements json.Unmarshaler to allow testing between a value that explicitly set to null or omitted." }, monthlyRepeatType: { type: "object", description: "NullableTaskRepeatMonthlyType implements json.Unmarshaler to allow testing between a value that explicitly set to null or omitted." }, rrule: { type: "string", description: "Adds the RRule definition for repeating tasks. It replaces all other repeating fields." }, selectedDays: { type: "object", description: "NullableWorkingHourEntryWeekdays implements json.Unmarshaler to allow testing between a value that explicitly set to null or omitted." } }, required: [], description: "RepeatOptions stores recurring information for the task." }, startAt: { type: "string", format: "date", description: "NullableDate implements json.Unmarshaler to allow testing between a value that explicitly set to null or omitted. Date format `2006-01-02`" }, status: { type: "string" }, tagIds: { type: "array", items: { type: "integer" } }, taskgroupId: { type: "integer" }, tasklistId: { type: "integer" }, templateRoleName: { type: "string" }, ticketId: { type: "integer" } }, required: [], description: "Task contains all the information returned from a task." }, taskOptions: { type: "object", properties: { appendAssignees: { type: "boolean" }, checkInvalidusers: { type: "boolean" }, everyoneMustDo: { type: "boolean" }, fireWebhook: { type: "boolean" }, isTemplate: { type: "boolean" }, logActivity: { type: "boolean" }, notify: { type: "boolean" }, parseInlineTags: { type: "boolean" }, positionAfterTaskId: { type: "integer" }, pushDependents: { type: "boolean" }, pushSubtasks: { type: "boolean" }, shiftProjectDates: { type: "boolean" }, useDefaults: { type: "boolean" }, useNotifyViaTWIM: { type: "boolean" } }, required: [], description: "Options contains any options which can be set for the task request" }, workflows: { type: "object", properties: { positionAfterTask: { type: "integer" }, stageId: { type: "integer" }, workflowId: { type: "integer" } }, required: [], description: "Workflows stores information about where the task lives in the workflow" } }, required: [], description: "Request body: taskRequest" }, tasklistId: { type: "integer", description: "Path parameter: tasklistId" } }, required: [ "taskRequest", "tasklistId" ] }, annotations: { title: "Create a Task", readOnlyHint: false, destructiveHint: false, openWorldHint: false } };
  • Registration of the 'createTask' tool in the main tools index: imports definition and handler, adds to toolPairs array used for toolDefinitions and toolHandlersMap.
    import { createTaskDefinition as createTask, handleCreateTask } from './tasks/createTask.js'; import { createSubTaskDefinition as createSubTask, handleCreateSubTask } from './tasks/createSubTask.js'; import { updateTaskDefinition as updateTask, handleUpdateTask } from './tasks/updateTask.js'; import { deleteTaskDefinition as deleteTask, handleDeleteTask } from './tasks/deleteTask.js'; import { getTasksMetricsCompleteDefinition as getTasksMetricsComplete, handleGetTasksMetricsComplete } from './tasks/getTasksMetricsComplete.js'; import { getTasksMetricsLateDefinition as getTasksMetricsLate, handleGetTasksMetricsLate } from './tasks/getTasksMetricsLate.js'; import { getTaskSubtasksDefinition as getTaskSubtasks, handleGetTaskSubtasks } from './tasks/getTaskSubtasks.js'; import { getTaskCommentsDefinition as getTaskComments, handleGetTaskComments } from './tasks/getTaskComments.js'; // Comments import { createCommentDefinition as createComment, handleCreateComment } from './comments/createComment.js'; // People import { getPeopleDefinition as getPeople, handleGetPeople } from './people/getPeople.js'; import { getPersonByIdDefinition as getPersonById, handleGetPersonById } from './people/getPersonById.js'; import { getProjectPeopleDefinition as getProjectPeople, handleGetProjectPeople } from './people/getProjectPeople.js'; import { addPeopleToProjectDefinition as addPeopleToProject, handleAddPeopleToProject } from './people/addPeopleToProject.js'; import { deletePersonDefinition as deletePerson, handleDeletePerson } from './people/deletePerson.js'; import { updatePersonDefinition as updatePerson, handleUpdatePerson } from './people/updatePerson.js'; // Companies import { createCompanyDefinition as createCompany, handleCreateCompany } from './companies/createCompany.js'; import { updateCompanyDefinition as updateCompany, handleUpdateCompany } from './companies/updateCompany.js'; import { deleteCompanyDefinition as deleteCompany, handleDeleteCompany } from './companies/deleteCompany.js'; import { getCompaniesDefinition as getCompanies, handleGetCompanies } from './companies/getCompanies.js'; import { getCompanyByIdDefinition as getCompanyById, handleGetCompanyById } from './companies/getCompanyById.js'; // Reporting import { getProjectsPeopleMetricsPerformanceDefinition as getProjectsPeopleMetricsPerformance, handleGetProjectsPeopleMetricsPerformance } from './people/getPeopleMetricsPerformance.js'; import { getProjectsPeopleUtilizationDefinition as getProjectsPeopleUtilization, handleGetProjectsPeopleUtilization } from './people/getPeopleUtilization.js'; import { getProjectPersonDefinition as getProjectPerson, handleGetProjectPerson } from './people/getProjectPerson.js'; import { getProjectsReportingUserTaskCompletionDefinition as getProjectsReportingUserTaskCompletion, handleGetProjectsReportingUserTaskCompletion } from './reporting/getUserTaskCompletion.js'; import { getProjectsReportingUtilizationDefinition as getProjectsReportingUtilization, handleGetProjectsReportingUtilization } from './people/getUtilization.js'; // Time-related imports import { getTimeDefinition as getTime, handleGetTime } from './time/getTime.js'; import { getProjectsAllocationsTimeDefinition as getAllocationTime, handleGetProjectsAllocationsTime } from './time/getAllocationTime.js'; // Core import { getTimezonesDefinition as getTimezones, handleGetTimezones } from './core/getTimezones.js'; // Define a structure that pairs tool definitions with their handlers interface ToolPair { definition: any; handler: Function; } // Create an array of tool pairs const toolPairs: ToolPair[] = [ { definition: getProjects, handler: handleGetProjects }, { definition: getCurrentProject, handler: handleGetCurrentProject }, { definition: createProject, handler: handleCreateProject }, { definition: getTasks, handler: handleGetTasks }, { definition: getTasksByProjectId, handler: handleGetTasksByProjectId }, { definition: getTaskListsByProjectId, handler: handleGetTaskListsByProjectId }, { definition: getTasksByTaskListId, handler: handleGetTasksByTaskListId }, { definition: getTaskById, handler: handleGetTaskById }, { definition: createTask, handler: handleCreateTask }, { definition: createSubTask, handler: handleCreateSubTask }, { definition: updateTask, handler: handleUpdateTask }, { definition: deleteTask, handler: handleDeleteTask }, { definition: getTasksMetricsComplete, handler: handleGetTasksMetricsComplete }, { definition: getTasksMetricsLate, handler: handleGetTasksMetricsLate }, { definition: getTaskSubtasks, handler: handleGetTaskSubtasks }, { definition: getTaskComments, handler: handleGetTaskComments }, { definition: createComment, handler: handleCreateComment }, { definition: getPeople, handler: handleGetPeople }, { definition: getPersonById, handler: handleGetPersonById }, { definition: getProjectPeople, handler: handleGetProjectPeople }, { definition: addPeopleToProject, handler: handleAddPeopleToProject }, { definition: deletePerson, handler: handleDeletePerson }, { definition: updatePerson, handler: handleUpdatePerson }, { definition: createCompany, handler: handleCreateCompany }, { definition: updateCompany, handler: handleUpdateCompany }, { definition: deleteCompany, handler: handleDeleteCompany }, { definition: getCompanies, handler: handleGetCompanies }, { definition: getCompanyById, handler: handleGetCompanyById }, { definition: getProjectsPeopleMetricsPerformance, handler: handleGetProjectsPeopleMetricsPerformance }, { definition: getProjectsPeopleUtilization, handler: handleGetProjectsPeopleUtilization }, { definition: getAllocationTime, handler: handleGetProjectsAllocationsTime }, { definition: getTime, handler: handleGetTime }, { definition: getProjectPerson, handler: handleGetProjectPerson }, { definition: getProjectsReportingUserTaskCompletion, handler: handleGetProjectsReportingUserTaskCompletion }, { definition: getProjectsReportingUtilization, handler: handleGetProjectsReportingUtilization }, { definition: getTimezones, handler: handleGetTimezones } ]; // Extract just the definitions for the toolDefinitions array export const toolDefinitions = toolPairs.map(pair => pair.definition); // Create a map of tool names to their handler functions export const toolHandlersMap: Record<string, Function> = toolPairs.reduce((map, pair) => { map[pair.definition.name] = pair.handler; return map; }, {} as Record<string, Function>);
  • Underlying service function called by the tool handler, performs the actual HTTP POST to Teamwork API to create the task.
    export const createTask = async (tasklistId: string, taskData: TaskRequest) => { try { logger.info(`Creating task in tasklist ${tasklistId}`); // Ensure we have a valid task object if (!taskData || !taskData.task) { throw new Error('Invalid task data: missing task object'); } // Ensure task has name field (Teamwork API requires 'name' for the task title) // Note: In the API docs it might be called 'content', but in our model it's 'name' if (!taskData.task.name) { throw new Error('Invalid task data: missing task name'); } logger.info(`Task data: ${JSON.stringify(taskData).substring(0, 200)}...`); const api = ensureApiClient(); const response = await api.post(`/tasklists/${tasklistId}/tasks.json`, taskData); logger.info(`Task creation successful, status: ${response.status}`); // Validate response data if (!response.data) { logger.warn('Task created but response data is empty'); return { success: true, message: 'Task created successfully, but no details returned' }; } // Ensure response data is serializable try { JSON.stringify(response.data); logger.info('Response data is valid JSON'); } catch (error: any) { logger.error(`Response data is not valid JSON: ${error.message}`); return { success: true, message: 'Task created successfully, but response contains non-serializable data', partial: typeof response.data === 'object' ? Object.keys(response.data) : typeof response.data }; } return response.data; } catch (error: any) { logger.error(`Error creating task in tasklist ${tasklistId}: ${error.message}`); // Log detailed error information if (error.response) { logger.error(`API error status: ${error.response.status}`); logger.error(`API error data: ${JSON.stringify(error.response.data || {})}`); } if (error.stack) { logger.error(`Stack trace: ${error.stack}`); } throw new Error(`Failed to create task in tasklist ${tasklistId}: ${error.message}`); } };

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/Vizioz/Teamwork-MCP'

If you have feedback or need assistance with the MCP directory API, please join our Discord server