UNPKG

@rigo-m/clickup-mcp-server

Version:

ClickUp MCP Server - Integrate ClickUp tasks with AI through Model Context Protocol

248 lines (247 loc) 11.4 kB
/** * SPDX-FileCopyrightText: © 2025 Talib Kareem <taazkareem@icloud.com> * SPDX-License-Identifier: MIT * * MCP Server for ClickUp integration */ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import { CallToolRequestSchema, ListToolsRequestSchema, ListPromptsRequestSchema, GetPromptRequestSchema, ListResourcesRequestSchema, } from "@modelcontextprotocol/sdk/types.js"; import { getConfig } from "./config.js"; import { workspaceHierarchyTool, handleGetWorkspaceHierarchy } from "./tools/workspace.js"; import { createTaskTool, updateTaskTool, moveTaskTool, duplicateTaskTool, getTaskTool, deleteTaskTool, getTaskCommentsTool, createTaskCommentTool, createBulkTasksTool, updateBulkTasksTool, moveBulkTasksTool, deleteBulkTasksTool, attachTaskFileTool, getWorkspaceTasksTool, getTaskTimeEntriesTool, startTimeTrackingTool, stopTimeTrackingTool, addTimeEntryTool, deleteTimeEntryTool, getCurrentTimeEntryTool, handleCreateTask, handleUpdateTask, handleMoveTask, handleDuplicateTask, handleDeleteTask, handleGetTaskComments, handleCreateTaskComment, handleCreateBulkTasks, handleUpdateBulkTasks, handleMoveBulkTasks, handleDeleteBulkTasks, handleGetTask, handleAttachTaskFile, handleGetWorkspaceTasks, handleGetTaskTimeEntries, handleStartTimeTracking, handleStopTimeTracking, handleAddTimeEntry, handleDeleteTimeEntry, handleGetCurrentTimeEntry } from "./tools/task/index.js"; import { createListTool, handleCreateList, createListInFolderTool, handleCreateListInFolder, getListTool, handleGetList, updateListTool, handleUpdateList, deleteListTool, handleDeleteList } from "./tools/list.js"; import { createFolderTool, handleCreateFolder, getFolderTool, handleGetFolder, updateFolderTool, handleUpdateFolder, deleteFolderTool, handleDeleteFolder } from "./tools/folder.js"; import { getSpaceTagsTool, handleGetSpaceTags, addTagToTaskTool, handleAddTagToTask, removeTagFromTaskTool, handleRemoveTagFromTask } from "./tools/tag.js"; import { createDocumentTool, handleCreateDocument, getDocumentTool, handleGetDocument, listDocumentsTool, handleListDocuments, listDocumentPagesTool, handleListDocumentPages, getDocumentPagesTool, handleGetDocumentPages, createDocumentPageTool, handleCreateDocumentPage, updateDocumentPageTool, handleUpdateDocumentPage } from "./tools/documents.js"; import { Logger } from "./logger.js"; import { clickUpServices } from "./services/shared.js"; // Create a logger instance for server const logger = new Logger('Server'); // Use existing services from shared module instead of creating new ones const { workspace } = clickUpServices; export const McpServerInstance = new McpServer({ name: "clickup-mcp-server", version: "0.7.2", }, { capabilities: { tools: {}, prompts: {}, resources: {}, }, }); export const server = McpServerInstance.server; const documentModule = () => { if (getConfig().documentSupport === 'true') { return [ createDocumentTool, getDocumentTool, listDocumentsTool, listDocumentPagesTool, getDocumentPagesTool, createDocumentPageTool, updateDocumentPageTool, ]; } else { return []; } }; /** * Configure the server routes and handlers */ export function configureServer() { logger.info("Registering server request handlers"); // Register ListTools handler server.setRequestHandler(ListToolsRequestSchema, async () => { logger.debug("Received ListTools request"); return { tools: [ workspaceHierarchyTool, createTaskTool, getTaskTool, updateTaskTool, moveTaskTool, duplicateTaskTool, deleteTaskTool, getTaskCommentsTool, createTaskCommentTool, attachTaskFileTool, createBulkTasksTool, updateBulkTasksTool, moveBulkTasksTool, deleteBulkTasksTool, getWorkspaceTasksTool, getTaskTimeEntriesTool, startTimeTrackingTool, stopTimeTrackingTool, addTimeEntryTool, deleteTimeEntryTool, getCurrentTimeEntryTool, createListTool, createListInFolderTool, getListTool, updateListTool, deleteListTool, createFolderTool, getFolderTool, updateFolderTool, deleteFolderTool, getSpaceTagsTool, addTagToTaskTool, removeTagFromTaskTool, ...documentModule() ].filter(tool => !getConfig().disabledTools.includes(tool.name)) }; }); // Add handler for resources/list server.setRequestHandler(ListResourcesRequestSchema, async (req) => { logger.debug("Received ListResources request"); return { resources: [] }; }); // Register CallTool handler with proper logging logger.info("Registering tool handlers", { toolCount: 40, categories: ["workspace", "task", "time-tracking", "list", "folder", "tag", "document"] }); server.setRequestHandler(CallToolRequestSchema, async (req) => { const { name, arguments: params } = req.params; // Improved logging with more context logger.info(`Received CallTool request for tool: ${name}`, { params }); // Check if the tool is disabled if (getConfig().disabledTools.includes(name)) { logger.warn(`Tool execution blocked: Tool '${name}' is disabled.`); throw { code: -32601, message: `Tool '${name}' is disabled.` }; } try { // Handle tool calls by routing to the appropriate handler switch (name) { case "get_workspace_hierarchy": return handleGetWorkspaceHierarchy(); case "create_task": return handleCreateTask(params); case "update_task": return handleUpdateTask(params); case "move_task": return handleMoveTask(params); case "duplicate_task": return handleDuplicateTask(params); case "get_task": return handleGetTask(params); case "delete_task": return handleDeleteTask(params); case "get_task_comments": return handleGetTaskComments(params); case "create_task_comment": return handleCreateTaskComment(params); case "attach_task_file": return handleAttachTaskFile(params); case "create_bulk_tasks": return handleCreateBulkTasks(params); case "update_bulk_tasks": return handleUpdateBulkTasks(params); case "move_bulk_tasks": return handleMoveBulkTasks(params); case "delete_bulk_tasks": return handleDeleteBulkTasks(params); case "get_workspace_tasks": return handleGetWorkspaceTasks(params); case "create_list": return handleCreateList(params); case "create_list_in_folder": return handleCreateListInFolder(params); case "get_list": return handleGetList(params); case "update_list": return handleUpdateList(params); case "delete_list": return handleDeleteList(params); case "create_folder": return handleCreateFolder(params); case "get_folder": return handleGetFolder(params); case "update_folder": return handleUpdateFolder(params); case "delete_folder": return handleDeleteFolder(params); case "get_space_tags": return handleGetSpaceTags(params); case "add_tag_to_task": return handleAddTagToTask(params); case "remove_tag_from_task": return handleRemoveTagFromTask(params); case "get_task_time_entries": return handleGetTaskTimeEntries(params); case "start_time_tracking": return handleStartTimeTracking(params); case "stop_time_tracking": return handleStopTimeTracking(params); case "add_time_entry": return handleAddTimeEntry(params); case "delete_time_entry": return handleDeleteTimeEntry(params); case "get_current_time_entry": return handleGetCurrentTimeEntry(params); case "create_document": return handleCreateDocument(params); case "get_document": return handleGetDocument(params); case "list_documents": return handleListDocuments(params); case "list_document_pages": return handleListDocumentPages(params); case "get_document_pages": return handleGetDocumentPages(params); case "create_document_page": return handleCreateDocumentPage(params); case "update_document_page": return handleUpdateDocumentPage(params); default: logger.error(`Unknown tool requested: ${name}`); const error = new Error(`Unknown tool: ${name}`); error.name = "UnknownToolError"; throw error; } } catch (err) { logger.error(`Error executing tool: ${name}`, err); // Transform error to a more descriptive JSON-RPC error if (err.name === "UnknownToolError") { throw { code: -32601, message: `Method not found: ${name}` }; } else if (err.name === "ValidationError") { throw { code: -32602, message: `Invalid params for tool ${name}: ${err.message}` }; } else { // Generic server error throw { code: -32000, message: `Error executing tool ${name}: ${err.message}` }; } } }); server.setRequestHandler(ListPromptsRequestSchema, async () => { logger.info("Received ListPrompts request"); return { prompts: [] }; }); server.setRequestHandler(GetPromptRequestSchema, async () => { logger.error("Received GetPrompt request, but prompts are not supported"); throw new Error("Prompt not found"); }); return server; } /** * Export the clickup service for use in tool handlers */ export { workspace };