Skip to main content
Glama

ClickUp MCP Server

by windalfin
list.ts15 kB
/** * ClickUp MCP List Tools * * This module defines list-related tools including creating, updating, * retrieving, and deleting lists. It supports creating lists both in spaces * and in folders. */ import { CreateListData, ClickUpList } from '../services/clickup/types.js'; import { createClickUpServices } from '../services/clickup/index.js'; import config from '../config.js'; // Initialize ClickUp services using the factory function const services = createClickUpServices({ apiKey: config.clickupApiKey, teamId: config.clickupTeamId }); // Extract the services we need for list operations const { list: listService, workspace: workspaceService } = services; /** * Tool definition for creating a list directly in a space */ export const createListTool = { name: "create_list", description: "Create a new list directly in a ClickUp space (not in a folder). You MUST provide either spaceId or spaceName. For creating lists inside folders, use create_list_in_folder instead.", inputSchema: { type: "object", properties: { name: { type: "string", description: "Name of the list" }, spaceId: { type: "string", description: "ID of the space to create the list in. Use this instead of spaceName if you have the ID." }, spaceName: { type: "string", description: "Name of the space to create the list in. Alternative to spaceId - one of them MUST be provided." }, content: { type: "string", description: "Description or content of the list" }, dueDate: { type: "string", description: "Due date for the list (Unix timestamp in milliseconds)" }, priority: { type: "number", description: "Priority level: 1 (urgent), 2 (high), 3 (normal), 4 (low)" }, assignee: { type: "number", description: "User ID to assign the list to" }, status: { type: "string", description: "Status of the list" } }, required: ["name"] } }; /** * Tool definition for creating a list within a folder */ export const createListInFolderTool = { name: "create_list_in_folder", description: "Create a new list within a ClickUp folder. You MUST provide either: 1) folderId alone, or 2) folderName WITH either spaceName or spaceId. Folder names may not be unique across spaces, which is why space information is required when using folderName.", inputSchema: { type: "object", properties: { name: { type: "string", description: "Name of the list" }, folderId: { type: "string", description: "ID of the folder to create the list in. If you have this, you don't need folderName or space information." }, folderName: { type: "string", description: "Name of the folder to create the list in. When using this, you MUST also provide either spaceName or spaceId." }, spaceId: { type: "string", description: "ID of the space containing the folder. Required when using folderName instead of folderId." }, spaceName: { type: "string", description: "Name of the space containing the folder. Required when using folderName instead of folderId." }, content: { type: "string", description: "Description or content of the list" }, status: { type: "string", description: "Status of the list (uses folder default if not specified)" } }, required: ["name"] } }; /** * Tool definition for retrieving list details */ export const getListTool = { name: "get_list", description: "Retrieve details about a specific ClickUp list. You MUST provide either listId or listName. Using listId is more reliable as list names might not be unique.", inputSchema: { type: "object", properties: { listId: { type: "string", description: "ID of the list to retrieve. Use this instead of listName if you have the ID." }, listName: { type: "string", description: "Name of the list to retrieve. May be ambiguous if multiple lists have the same name." } }, required: [] } }; /** * Tool definition for updating a list */ export const updateListTool = { name: "update_list", description: "Update an existing ClickUp list's properties. You MUST provide either listId or listName, and at least one field to update (name, content, or status).", inputSchema: { type: "object", properties: { listId: { type: "string", description: "ID of the list to update. Use this instead of listName if you have the ID." }, listName: { type: "string", description: "Name of the list to update. May be ambiguous if multiple lists have the same name." }, name: { type: "string", description: "New name for the list" }, content: { type: "string", description: "New description or content for the list" }, status: { type: "string", description: "New status for the list" } }, required: [] } }; /** * Tool definition for deleting a list */ export const deleteListTool = { name: "delete_list", description: "Permanently delete a ClickUp list and all its tasks. You MUST provide either listId or listName. WARNING: This action cannot be undone.", inputSchema: { type: "object", properties: { listId: { type: "string", description: "ID of the list to delete. Use this instead of listName if you have the ID." }, listName: { type: "string", description: "Name of the list to delete. May be ambiguous if multiple lists have the same name." } }, required: [] } }; /** * Helper function to find a list ID by name * Uses the ClickUp service's global list search functionality */ export async function findListIDByName(workspaceService: any, listName: string): Promise<{ id: string; name: string } | null> { // Use workspace service to find the list in the hierarchy const hierarchy = await workspaceService.getWorkspaceHierarchy(); const listInfo = workspaceService.findIDByNameInHierarchy(hierarchy, listName, 'list'); if (!listInfo) return null; return { id: listInfo.id, name: listName }; } /** * Handler for the create_list tool * Creates a new list directly in a space */ export async function handleCreateList(parameters: any) { const { name, spaceId, spaceName, content, dueDate, priority, assignee, status } = parameters; // Validate required fields if (!name) { throw new Error("List name is required"); } let targetSpaceId = spaceId; // If no spaceId but spaceName is provided, look up the space ID if (!targetSpaceId && spaceName) { const spaceIdResult = await workspaceService.findSpaceIDByName(spaceName); if (!spaceIdResult) { throw new Error(`Space "${spaceName}" not found`); } targetSpaceId = spaceIdResult; } if (!targetSpaceId) { throw new Error("Either spaceId or spaceName must be provided"); } // Prepare list data const listData: CreateListData = { name }; // Add optional fields if provided if (content) listData.content = content; if (dueDate) listData.due_date = parseInt(dueDate); if (priority) listData.priority = priority; if (assignee) listData.assignee = assignee; if (status) listData.status = status; try { // Create the list const newList = await listService.createList(targetSpaceId, listData); return { content: [{ type: "text", text: JSON.stringify( { id: newList.id, name: newList.name, content: newList.content, space: { id: newList.space.id, name: newList.space.name }, message: `List "${newList.name}" created successfully` }, null, 2 ) }] }; } catch (error: any) { throw new Error(`Failed to create list: ${error.message}`); } } /** * Handler for the create_list_in_folder tool * Creates a new list inside a folder */ export async function handleCreateListInFolder(parameters: any) { const { name, folderId, folderName, spaceId, spaceName, content, status } = parameters; // Validate required fields if (!name) { throw new Error("List name is required"); } let targetFolderId = folderId; // If no folderId but folderName is provided, look up the folder ID if (!targetFolderId && folderName) { let targetSpaceId = spaceId; // If no spaceId provided but spaceName is, look up the space ID first if (!targetSpaceId && spaceName) { const spaceIdResult = await workspaceService.findSpaceByName(spaceName); if (!spaceIdResult) { throw new Error(`Space "${spaceName}" not found`); } targetSpaceId = spaceIdResult.id; } if (!targetSpaceId) { throw new Error("When using folderName to identify a folder, you must also provide either spaceId or spaceName to locate the correct folder. This is because folder names might not be unique across different spaces."); } // Find the folder in the workspace hierarchy const hierarchy = await workspaceService.getWorkspaceHierarchy(); const folderInfo = workspaceService.findIDByNameInHierarchy(hierarchy, folderName, 'folder'); if (!folderInfo) { throw new Error(`Folder "${folderName}" not found in space`); } targetFolderId = folderInfo.id; } if (!targetFolderId) { throw new Error("Either folderId or folderName must be provided"); } // Prepare list data const listData: CreateListData = { name }; // Add optional fields if provided if (content) listData.content = content; if (status) listData.status = status; try { // Create the list in the folder const newList = await listService.createListInFolder(targetFolderId, listData); return { content: [{ type: "text", text: JSON.stringify( { id: newList.id, name: newList.name, content: newList.content, space: { id: newList.space.id, name: newList.space.name }, message: `List "${newList.name}" created successfully in folder` }, null, 2 ) }] }; } catch (error: any) { throw new Error(`Failed to create list in folder: ${error.message}`); } } /** * Handler for the get_list tool * Retrieves details about a specific list */ export async function handleGetList(parameters: any) { const { listId, listName } = parameters; let targetListId = listId; // If no listId provided but listName is, look up the list ID if (!targetListId && listName) { const listResult = await findListIDByName(workspaceService, listName); if (!listResult) { throw new Error(`List "${listName}" not found`); } targetListId = listResult.id; } if (!targetListId) { throw new Error("Either listId or listName must be provided"); } try { // Get the list const list = await listService.getList(targetListId); return { content: [{ type: "text", text: JSON.stringify( { id: list.id, name: list.name, content: list.content, space: { id: list.space.id, name: list.space.name }, status: list.status, url: `https://app.clickup.com/${config.clickupTeamId}/v/l/${list.id}` }, null, 2 ) }] }; } catch (error: any) { throw new Error(`Failed to retrieve list: ${error.message}`); } } /** * Handler for the update_list tool * Updates an existing list's properties */ export async function handleUpdateList(parameters: any) { const { listId, listName, name, content, status } = parameters; let targetListId = listId; // If no listId provided but listName is, look up the list ID if (!targetListId && listName) { const listResult = await findListIDByName(workspaceService, listName); if (!listResult) { throw new Error(`List "${listName}" not found`); } targetListId = listResult.id; } if (!targetListId) { throw new Error("Either listId or listName must be provided"); } // Ensure at least one update field is provided if (!name && !content && !status) { throw new Error("At least one of name, content, or status must be provided for update"); } // Prepare update data const updateData: Partial<CreateListData> = {}; if (name) updateData.name = name; if (content) updateData.content = content; if (status) updateData.status = status; try { // Update the list const updatedList = await listService.updateList(targetListId, updateData); return { content: [{ type: "text", text: JSON.stringify( { id: updatedList.id, name: updatedList.name, content: updatedList.content, space: { id: updatedList.space.id, name: updatedList.space.name }, status: updatedList.status, url: `https://app.clickup.com/${config.clickupTeamId}/v/l/${updatedList.id}`, message: `List "${updatedList.name}" updated successfully` }, null, 2 ) }] }; } catch (error: any) { throw new Error(`Failed to update list: ${error.message}`); } } /** * Handler for the delete_list tool * Permanently removes a list from the workspace */ export async function handleDeleteList(parameters: any) { const { listId, listName } = parameters; let targetListId = listId; // If no listId provided but listName is, look up the list ID if (!targetListId && listName) { const listResult = await findListIDByName(workspaceService, listName); if (!listResult) { throw new Error(`List "${listName}" not found`); } targetListId = listResult.id; } if (!targetListId) { throw new Error("Either listId or listName must be provided"); } try { // Get list details before deletion for confirmation message const list = await listService.getList(targetListId); const listName = list.name; // Delete the list await listService.deleteList(targetListId); return { content: [{ type: "text", text: JSON.stringify( { message: `List "${listName}" deleted successfully`, url: `https://app.clickup.com/${config.clickupTeamId}/v/l/${targetListId}` }, null, 2 ) }] }; } catch (error: any) { throw new Error(`Failed to delete list: ${error.message}`); } }

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/windalfin/clickup-mcp-server'

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