@taazkareem/clickup-mcp-server 0.6.9 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +73 -51
- package/build/config.js +17 -1
- package/build/server.js +65 -6
- package/build/services/clickup/document.js +159 -0
- package/build/services/clickup/index.js +11 -1
- package/build/services/clickup/task/task-core.js +14 -2
- package/build/services/clickup/time.js +244 -0
- package/build/services/clickup/types.js +11 -0
- package/build/services/shared.js +1 -1
- package/build/tools/documents.js +501 -0
- package/build/tools/task/bulk-operations.js +2 -1
- package/build/tools/task/index.js +2 -0
- package/build/tools/task/single-operations.js +2 -1
- package/build/tools/task/time-tracking.js +684 -0
- package/build/utils/sponsor-service.js +1 -1
- package/package.json +1 -1
- package/build/mcp-tools.js +0 -64
- package/build/server-state.js +0 -93
- package/build/server.log +0 -76
- package/build/services/clickup/task/handlers.js +0 -1
- package/build/services/clickup/task.js +0 -976
- package/build/services/clickup/tools/tag.js +0 -149
- package/build/tools/bulk-tasks.js +0 -36
- package/build/tools/debug.js +0 -76
- package/build/tools/logs.js +0 -55
- package/build/tools/task.js +0 -1554
- package/build/utils/params-utils.js +0 -39
- package/build/utils/sponsor-analytics.js +0 -100
- package/build/utils/sponsor-utils.js +0 -57
package/README.md
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
<img src="assets/images/clickup_mcp_server_social_image.png" alt="ClickUp MCP Server" width="100%">
|
|
2
2
|
|
|
3
|
-

|
|
4
4
|
[](https://github.com/TaazKareem/clickup-mcp-server/stargazers)
|
|
5
5
|
[](https://github.com/TaazKareem/clickup-mcp-server/graphs/commit-activity)
|
|
6
6
|
|
|
7
7
|
A Model Context Protocol (MCP) server for integrating ClickUp tasks with AI applications. This server allows AI agents to interact with ClickUp tasks, spaces, lists, and folders through a standardized protocol.
|
|
8
8
|
|
|
9
|
-
>
|
|
9
|
+
> 🚀 **Status Update:** v0.7.0 now available with complete Time Tracking support and Document Management features.
|
|
10
10
|
|
|
11
11
|
## Setup
|
|
12
12
|
|
|
@@ -20,7 +20,7 @@ A Model Context Protocol (MCP) server for integrating ClickUp tasks with AI appl
|
|
|
20
20
|
|
|
21
21
|
[](https://smithery.ai/server/@TaazKareem/clickup-mcp-server)
|
|
22
22
|
|
|
23
|
-
The server is hosted on [Smithery](https://smithery.ai/server/@taazkareem/clickup-mcp-server). There, you can preview the available tools or copy the commands to run on your specific client app.
|
|
23
|
+
The server is hosted on [Smithery](https://smithery.ai/server/@taazkareem/clickup-mcp-server). There, you can preview the available tools or copy the commands to run on your specific client app.
|
|
24
24
|
|
|
25
25
|
## NPX Installation
|
|
26
26
|
|
|
@@ -41,7 +41,8 @@ Add this entry to your client's MCP settings JSON file:
|
|
|
41
41
|
],
|
|
42
42
|
"env": {
|
|
43
43
|
"CLICKUP_API_KEY": "your-api-key",
|
|
44
|
-
"CLICKUP_TEAM_ID": "your-team-id"
|
|
44
|
+
"CLICKUP_TEAM_ID": "your-team-id",
|
|
45
|
+
"DOCUMENT_SUPPORT": "true"
|
|
45
46
|
}
|
|
46
47
|
}
|
|
47
48
|
}
|
|
@@ -52,64 +53,87 @@ Or use this npx command:
|
|
|
52
53
|
|
|
53
54
|
`npx -y @taazkareem/clickup-mcp-server@latest --env CLICKUP_API_KEY=your-api-key --env CLICKUP_TEAM_ID=your-team-id`
|
|
54
55
|
|
|
56
|
+
**Obs: if you don't pass "DOCUMENT_SUPPORT": "true", the default is false and document support will not be active.**
|
|
57
|
+
|
|
58
|
+
Additionally, you can use the `DISABLED_TOOLS` environment variable or `--env DISABLED_TOOLS` argument to disable specific tools. Provide a comma-separated list of tool names to disable (e.g., `create_task,delete_task`).
|
|
59
|
+
|
|
60
|
+
Please disable tools you don't need if you are having issues with the number of tools or any context limitations
|
|
61
|
+
|
|
55
62
|
## Features
|
|
56
63
|
|
|
57
|
-
| 📝 Task Management
|
|
58
|
-
|
|
59
|
-
| • Create, update, and delete tasks
|
|
60
|
-
|
|
|
61
|
-
| •
|
|
64
|
+
| 📝 Task Management | 🏷️ Tag Management |
|
|
65
|
+
| ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
66
|
+
| • Create, update, and delete tasks`<br>`• Move and duplicate tasks anywhere`<br>`• Support for single and bulk operations`<br>`• Set start/due dates with natural language`<br>`• Create and manage subtasks`<br>`• Add comments and attachments | • Create, update, and delete space tags`<br>`• Add and remove tags from tasks`<br>`• Use natural language color commands`<br>`• Automatic contrasting foreground colors`<br>`• View all space tags`<br>`• Tag-based task organization across workspace |
|
|
67
|
+
| ⏱️**Time Tracking** | 🌳**Workspace Organization** |
|
|
68
|
+
| • View time entries for tasks`<br>`• Start/stop time tracking on tasks`<br>`• Add manual time entries`<br>`• Delete time entries`<br>`• View currently running timer`<br>`• Track billable and non-billable time | • Navigate spaces, folders, and lists`<br>`• Create and manage folders`<br>`• Organize lists within spaces`<br>`• Create lists in folders`<br>`• View workspace hierarchy`<br>`• Efficient path navigation |
|
|
69
|
+
| ⚡**Integration Features** | **Document Listing, Creation and Updating!** |
|
|
70
|
+
| • Global name or ID-based lookups`<br>`• Case-insensitive matching`<br>`• Markdown formatting support`<br>`• Built-in rate limiting`<br>`• Error handling and validation`<br>`• Comprehensive API coverage | • Document Listing through all workspace`<br>` • Document Page listing `<br>` • Document Page Details `<br>` • Document Creation `<br>` • Document page update, modification (append and prepend) `<br>` |
|
|
62
71
|
|
|
63
72
|
## Available Tools
|
|
64
73
|
|
|
65
|
-
| Tool
|
|
66
|
-
|
|
67
|
-
| [get_workspace_hierarchy](docs/api-reference.md#workspace-navigation) | Get workspace structure
|
|
68
|
-
| [create_task](docs/api-reference.md#task-management)
|
|
69
|
-
| [create_bulk_tasks](docs/api-reference.md#task-management)
|
|
70
|
-
| [update_task](docs/api-reference.md#task-management)
|
|
71
|
-
| [update_bulk_tasks](docs/api-reference.md#task-management)
|
|
72
|
-
| [get_tasks](docs/api-reference.md#task-management)
|
|
73
|
-
| [get_task](docs/api-reference.md#task-management)
|
|
74
|
-
| [get_workspace_tasks](docs/api-reference.md#task-management)
|
|
75
|
-
| [get_task_comments](docs/api-reference.md#task-management)
|
|
76
|
-
| [create_task_comment](docs/api-reference.md#task-management)
|
|
77
|
-
| [attach_task_file](docs/api-reference.md#task-management)
|
|
78
|
-
| [delete_task](docs/api-reference.md#task-management)
|
|
79
|
-
| [delete_bulk_tasks](docs/api-reference.md#task-management)
|
|
80
|
-
| [move_task](docs/api-reference.md#task-management)
|
|
81
|
-
| [move_bulk_tasks](docs/api-reference.md#task-management)
|
|
82
|
-
| [duplicate_task](docs/api-reference.md#task-management)
|
|
83
|
-
| [create_list](docs/api-reference.md#list-management)
|
|
84
|
-
| [create_folder](docs/api-reference.md#folder-management)
|
|
85
|
-
| [create_list_in_folder](docs/api-reference.md#list-management)
|
|
86
|
-
| [get_folder](docs/api-reference.md#folder-management)
|
|
87
|
-
| [update_folder](docs/api-reference.md#folder-management)
|
|
88
|
-
| [delete_folder](docs/api-reference.md#folder-management)
|
|
89
|
-
| [get_list](docs/api-reference.md#list-management)
|
|
90
|
-
| [update_list](docs/api-reference.md#list-management)
|
|
91
|
-
| [delete_list](docs/api-reference.md#list-management)
|
|
92
|
-
| [get_space_tags](docs/api-reference.md#tag-management)
|
|
93
|
-
| [create_space_tag](docs/api-reference.md#tag-management)
|
|
94
|
-
| [update_space_tag](docs/api-reference.md#tag-management)
|
|
95
|
-
| [delete_space_tag](docs/api-reference.md#tag-management)
|
|
96
|
-
| [add_tag_to_task](docs/api-reference.md#tag-management)
|
|
97
|
-
| [remove_tag_from_task](docs/api-reference.md#tag-management)
|
|
74
|
+
| Tool | Description | Required Parameters |
|
|
75
|
+
| ------------------------------------------------------------------ | ------------------------------- | ---------------------------------------------------------------------------------------------------------------------------- |
|
|
76
|
+
| [get_workspace_hierarchy](docs/api-reference.md#workspace-navigation) | Get workspace structure | None |
|
|
77
|
+
| [create_task](docs/api-reference.md#task-management) | Create a task | `name`, (`listId`/`listName`) |
|
|
78
|
+
| [create_bulk_tasks](docs/api-reference.md#task-management) | Create multiple tasks | `tasks[]` |
|
|
79
|
+
| [update_task](docs/api-reference.md#task-management) | Modify task | `taskId`/`taskName` |
|
|
80
|
+
| [update_bulk_tasks](docs/api-reference.md#task-management) | Update multiple tasks | `tasks[]` with IDs or names |
|
|
81
|
+
| [get_tasks](docs/api-reference.md#task-management) | Get tasks from list | `listId`/`listName` |
|
|
82
|
+
| [get_task](docs/api-reference.md#task-management) | Get single task details | `taskId`/`taskName` (with smart disambiguation) |
|
|
83
|
+
| [get_workspace_tasks](docs/api-reference.md#task-management) | Get tasks with filtering | At least one filter (tags, list_ids, space_ids, etc.) |
|
|
84
|
+
| [get_task_comments](docs/api-reference.md#task-management) | Get comments on a task | `taskId`/`taskName` |
|
|
85
|
+
| [create_task_comment](docs/api-reference.md#task-management) | Add a comment to a task | `commentText`, (`taskId`/(`taskName`+`listName`)) |
|
|
86
|
+
| [attach_task_file](docs/api-reference.md#task-management) | Attach file to a task | `taskId`/`taskName`, (`file_data` or `file_url`) |
|
|
87
|
+
| [delete_task](docs/api-reference.md#task-management) | Remove task | `taskId`/`taskName` |
|
|
88
|
+
| [delete_bulk_tasks](docs/api-reference.md#task-management) | Remove multiple tasks | `tasks[]` with IDs or names |
|
|
89
|
+
| [move_task](docs/api-reference.md#task-management) | Move task | `taskId`/`taskName`, `listId`/`listName` |
|
|
90
|
+
| [move_bulk_tasks](docs/api-reference.md#task-management) | Move multiple tasks | `tasks[]` with IDs or names, target list |
|
|
91
|
+
| [duplicate_task](docs/api-reference.md#task-management) | Copy task | `taskId`/`taskName`, `listId`/`listName` |
|
|
92
|
+
| [create_list](docs/api-reference.md#list-management) | Create list in space | `name`, `spaceId`/`spaceName` |
|
|
93
|
+
| [create_folder](docs/api-reference.md#folder-management) | Create folder | `name`, `spaceId`/`spaceName` |
|
|
94
|
+
| [create_list_in_folder](docs/api-reference.md#list-management) | Create list in folder | `name`, `folderId`/`folderName` |
|
|
95
|
+
| [get_folder](docs/api-reference.md#folder-management) | Get folder details | `folderId`/`folderName` |
|
|
96
|
+
| [update_folder](docs/api-reference.md#folder-management) | Update folder properties | `folderId`/`folderName` |
|
|
97
|
+
| [delete_folder](docs/api-reference.md#folder-management) | Delete folder | `folderId`/`folderName` |
|
|
98
|
+
| [get_list](docs/api-reference.md#list-management) | Get list details | `listId`/`listName` |
|
|
99
|
+
| [update_list](docs/api-reference.md#list-management) | Update list properties | `listId`/`listName` |
|
|
100
|
+
| [delete_list](docs/api-reference.md#list-management) | Delete list | `listId`/`listName` |
|
|
101
|
+
| [get_space_tags](docs/api-reference.md#tag-management) | Get space tags | `spaceId`/`spaceName` |
|
|
102
|
+
| [create_space_tag](docs/api-reference.md#tag-management) | Create tag | `tagName`, `spaceId`/`spaceName` |
|
|
103
|
+
| [update_space_tag](docs/api-reference.md#tag-management) | Update tag | `tagName`, `spaceId`/`spaceName` |
|
|
104
|
+
| [delete_space_tag](docs/api-reference.md#tag-management) | Delete tag | `tagName`, `spaceId`/`spaceName` |
|
|
105
|
+
| [add_tag_to_task](docs/api-reference.md#tag-management) | Add tag to task | `tagName`, `taskId`/(`taskName`+`listName`) |
|
|
106
|
+
| [remove_tag_from_task](docs/api-reference.md#tag-management) | Remove tag from task | `tagName`, `taskId`/(`taskName`+`listName`) |
|
|
107
|
+
| [get_task_time_entries](docs/api-reference.md#time-tracking) | Get time entries for a task | `taskId`/`taskName` |
|
|
108
|
+
| [start_time_tracking](docs/api-reference.md#time-tracking) | Start time tracking on a task | `taskId`/`taskName` |
|
|
109
|
+
| [stop_time_tracking](docs/api-reference.md#time-tracking) | Stop current time tracking | None |
|
|
110
|
+
| [add_time_entry](docs/api-reference.md#time-tracking) | Add manual time entry to a task | `taskId`/`taskName`, `start`, `duration` |
|
|
111
|
+
| [delete_time_entry](docs/api-reference.md#time-tracking) | Delete a time entry | `timeEntryId` |
|
|
112
|
+
| [get_current_time_entry](docs/api-reference.md#time-tracking) | Get currently running timer | None |
|
|
113
|
+
| [create_document](docs/api-reference.md#document-management) | Create a document | `workspaceId`, `name`, `parentId`/`parentType`, `visibility`, `create_pages` |
|
|
114
|
+
| [get_document](docs/api-reference.md#document-management) | Get a document | `workspaceId`/`documentId` |
|
|
115
|
+
| [list_documents](docs/api-reference.md#document-management) | List documents | `workspaceId`, `documentId`/`creator`/`deleted`/`archived`/`parent_id`/`parent_type`/`limit`/`next_cursor` |
|
|
116
|
+
| [list_document_pages](docs/api-reference.md#document-management) | List document pages | `documentId`/`documentName` |
|
|
117
|
+
| [get_document_pages](docs/api-reference.md#document-management) | Get document pages | `documentId`/`documentName`, `pageIds` |
|
|
118
|
+
| [create_document_pages](docs/api-reference.md#document-management) | Create a document page | `workspaceId`/`documentId`, `parent_page_id`/`name`/`sub_title`,`content`/`content_format` |
|
|
119
|
+
| [update_document_page](docs/api-reference.md#document-management) | Update a document page | `workspaceId`/`documentId`, `name`/`sub_title`,`content`/`content_edit_mode`/`content_format` |
|
|
98
120
|
|
|
99
121
|
See [full documentation](docs/api-reference.md) for optional parameters and advanced usage.
|
|
100
122
|
|
|
101
123
|
## Prompts
|
|
124
|
+
|
|
102
125
|
Not yet implemented and not supported by all client apps. Request a feature for a Prompt implementation that would be most beneficial for your workflow (without it being too specific). Examples:
|
|
103
126
|
|
|
104
|
-
| Prompt
|
|
105
|
-
|
|
106
|
-
| [summarize_tasks](docs/api-reference.md#prompts)
|
|
107
|
-
| [analyze_priorities](docs/api-reference.md#prompts)
|
|
108
|
-
| [generate_description](docs/api-reference.md#prompts) | Task description creation | Objectives, criteria, dependencies
|
|
127
|
+
| Prompt | Purpose | Features |
|
|
128
|
+
| -------------------------------------------------- | ------------------------- | ----------------------------------------- |
|
|
129
|
+
| [summarize_tasks](docs/api-reference.md#prompts) | Task overview | Status summary, priorities, relationships |
|
|
130
|
+
| [analyze_priorities](docs/api-reference.md#prompts) | Priority optimization | Distribution analysis, sequencing |
|
|
131
|
+
| [generate_description](docs/api-reference.md#prompts) | Task description creation | Objectives, criteria, dependencies |
|
|
109
132
|
|
|
110
133
|
## Error Handling
|
|
111
134
|
|
|
112
135
|
The server provides clear error messages for:
|
|
136
|
+
|
|
113
137
|
- Missing required parameters
|
|
114
138
|
- Invalid IDs or names
|
|
115
139
|
- Items not found
|
|
@@ -127,12 +151,10 @@ If you find this project useful, please consider supporting:
|
|
|
127
151
|
|
|
128
152
|
[](https://github.com/sponsors/TaazKareem)
|
|
129
153
|
|
|
130
|
-
|
|
131
154
|
<a href="https://buymeacoffee.com/taazkareem">
|
|
132
155
|
<img src="https://cdn.buymeacoffee.com/buttons/v2/default-yellow.png" width="200" alt="Buy Me A Coffee">
|
|
133
156
|
</a>
|
|
134
157
|
|
|
135
|
-
|
|
136
158
|
## Acknowledgements
|
|
137
159
|
|
|
138
160
|
Special thanks to [ClickUp](https://clickup.com) for their excellent API and services that make this integration possible.
|
|
@@ -150,7 +172,7 @@ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file
|
|
|
150
172
|
## Disclaimer
|
|
151
173
|
|
|
152
174
|
This software makes use of third-party APIs and may reference trademarks
|
|
153
|
-
or brands owned by third parties. The use of such APIs or references does not imply
|
|
154
|
-
any affiliation with or endorsement by the respective companies. All trademarks and
|
|
175
|
+
or brands owned by third parties. The use of such APIs or references does not imply
|
|
176
|
+
any affiliation with or endorsement by the respective companies. All trademarks and
|
|
155
177
|
brand names are the property of their respective owners. This project is an independent
|
|
156
178
|
work and is not officially associated with or sponsored by any third-party company mentioned.
|
package/build/config.js
CHANGED
|
@@ -7,6 +7,10 @@
|
|
|
7
7
|
* The required environment variables (CLICKUP_API_KEY and CLICKUP_TEAM_ID) are passed
|
|
8
8
|
* securely to this file when running the hosted server at smithery.ai. Optionally,
|
|
9
9
|
* they can be parsed via command line arguments when running the server locally.
|
|
10
|
+
*
|
|
11
|
+
* The document support is optional and can be passed via command line arguments.
|
|
12
|
+
* The default value is 'false' (string), which means document support will be disabled if
|
|
13
|
+
* no parameter is passed. Pass it as 'true' (string) to enable it.
|
|
10
14
|
*/
|
|
11
15
|
// Parse any command line environment arguments
|
|
12
16
|
const args = process.argv.slice(2);
|
|
@@ -18,8 +22,18 @@ for (let i = 0; i < args.length; i++) {
|
|
|
18
22
|
envArgs.clickupApiKey = value;
|
|
19
23
|
if (key === 'CLICKUP_TEAM_ID')
|
|
20
24
|
envArgs.clickupTeamId = value;
|
|
25
|
+
if (key === 'DOCUMENT_SUPPORT')
|
|
26
|
+
envArgs.documentSupport = value;
|
|
27
|
+
if (key === 'DOCUMENT_MODEL')
|
|
28
|
+
envArgs.documentSupport = value; // Backward compatibility
|
|
29
|
+
if (key === 'DOCUMENT_MODULE')
|
|
30
|
+
envArgs.documentSupport = value; // Backward compatibility
|
|
21
31
|
if (key === 'LOG_LEVEL')
|
|
22
32
|
envArgs.logLevel = value;
|
|
33
|
+
if (key === 'DISABLED_TOOLS')
|
|
34
|
+
envArgs.disabledTools = value;
|
|
35
|
+
if (key === 'DISABLED_COMMANDS')
|
|
36
|
+
envArgs.disabledTools = value; // Backward compatibility
|
|
23
37
|
i++;
|
|
24
38
|
}
|
|
25
39
|
}
|
|
@@ -52,7 +66,9 @@ const configuration = {
|
|
|
52
66
|
clickupApiKey: envArgs.clickupApiKey || process.env.CLICKUP_API_KEY || '',
|
|
53
67
|
clickupTeamId: envArgs.clickupTeamId || process.env.CLICKUP_TEAM_ID || '',
|
|
54
68
|
enableSponsorMessage: process.env.ENABLE_SPONSOR_MESSAGE !== 'false',
|
|
55
|
-
|
|
69
|
+
documentSupport: envArgs.documentSupport || process.env.DOCUMENT_SUPPORT || process.env.DOCUMENT_MODULE || process.env.DOCUMENT_MODEL || 'false',
|
|
70
|
+
logLevel: parseLogLevel(envArgs.logLevel || process.env.LOG_LEVEL),
|
|
71
|
+
disabledTools: ((envArgs.disabledTools || process.env.DISABLED_TOOLS || process.env.DISABLED_COMMANDS)?.split(',').map(cmd => cmd.trim()).filter(cmd => cmd !== '') || []),
|
|
56
72
|
};
|
|
57
73
|
// Don't log to console as it interferes with JSON-RPC communication
|
|
58
74
|
// Validate only the required variables are present
|
package/build/server.js
CHANGED
|
@@ -6,11 +6,13 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
8
8
|
import { CallToolRequestSchema, ListToolsRequestSchema, ListPromptsRequestSchema, GetPromptRequestSchema, ListResourcesRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
|
|
9
|
+
import config from "./config.js";
|
|
9
10
|
import { workspaceHierarchyTool, handleGetWorkspaceHierarchy } from "./tools/workspace.js";
|
|
10
|
-
import { createTaskTool, updateTaskTool, moveTaskTool, duplicateTaskTool, getTaskTool, deleteTaskTool, getTaskCommentsTool, createTaskCommentTool, createBulkTasksTool, updateBulkTasksTool, moveBulkTasksTool, deleteBulkTasksTool, attachTaskFileTool, getWorkspaceTasksTool, handleCreateTask, handleUpdateTask, handleMoveTask, handleDuplicateTask, handleDeleteTask, handleGetTaskComments, handleCreateTaskComment, handleCreateBulkTasks, handleUpdateBulkTasks, handleMoveBulkTasks, handleDeleteBulkTasks, handleGetTask, handleAttachTaskFile, handleGetWorkspaceTasks } from "./tools/task/index.js";
|
|
11
|
+
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";
|
|
11
12
|
import { createListTool, handleCreateList, createListInFolderTool, handleCreateListInFolder, getListTool, handleGetList, updateListTool, handleUpdateList, deleteListTool, handleDeleteList } from "./tools/list.js";
|
|
12
13
|
import { createFolderTool, handleCreateFolder, getFolderTool, handleGetFolder, updateFolderTool, handleUpdateFolder, deleteFolderTool, handleDeleteFolder } from "./tools/folder.js";
|
|
13
14
|
import { getSpaceTagsTool, handleGetSpaceTags, addTagToTaskTool, handleAddTagToTask, removeTagFromTaskTool, handleRemoveTagFromTask } from "./tools/tag.js";
|
|
15
|
+
import { createDocumentTool, handleCreateDocument, getDocumentTool, handleGetDocument, listDocumentsTool, handleListDocuments, listDocumentPagesTool, handleListDocumentPages, getDocumentPagesTool, handleGetDocumentPages, createDocumentPageTool, handleCreateDocumentPage, updateDocumentPageTool, handleUpdateDocumentPage } from "./tools/documents.js";
|
|
14
16
|
import { Logger } from "./logger.js";
|
|
15
17
|
import { clickUpServices } from "./services/shared.js";
|
|
16
18
|
// Create a logger instance for server
|
|
@@ -19,7 +21,7 @@ const logger = new Logger('Server');
|
|
|
19
21
|
const { workspace } = clickUpServices;
|
|
20
22
|
export const server = new Server({
|
|
21
23
|
name: "clickup-mcp-server",
|
|
22
|
-
version: "0.
|
|
24
|
+
version: "0.7.0",
|
|
23
25
|
}, {
|
|
24
26
|
capabilities: {
|
|
25
27
|
tools: {},
|
|
@@ -27,6 +29,22 @@ export const server = new Server({
|
|
|
27
29
|
resources: {},
|
|
28
30
|
},
|
|
29
31
|
});
|
|
32
|
+
const documentModule = () => {
|
|
33
|
+
if (config.documentSupport === 'true') {
|
|
34
|
+
return [
|
|
35
|
+
createDocumentTool,
|
|
36
|
+
getDocumentTool,
|
|
37
|
+
listDocumentsTool,
|
|
38
|
+
listDocumentPagesTool,
|
|
39
|
+
getDocumentPagesTool,
|
|
40
|
+
createDocumentPageTool,
|
|
41
|
+
updateDocumentPageTool,
|
|
42
|
+
];
|
|
43
|
+
}
|
|
44
|
+
else {
|
|
45
|
+
return [];
|
|
46
|
+
}
|
|
47
|
+
};
|
|
30
48
|
/**
|
|
31
49
|
* Configure the server routes and handlers
|
|
32
50
|
*/
|
|
@@ -52,6 +70,12 @@ export function configureServer() {
|
|
|
52
70
|
moveBulkTasksTool,
|
|
53
71
|
deleteBulkTasksTool,
|
|
54
72
|
getWorkspaceTasksTool,
|
|
73
|
+
getTaskTimeEntriesTool,
|
|
74
|
+
startTimeTrackingTool,
|
|
75
|
+
stopTimeTrackingTool,
|
|
76
|
+
addTimeEntryTool,
|
|
77
|
+
deleteTimeEntryTool,
|
|
78
|
+
getCurrentTimeEntryTool,
|
|
55
79
|
createListTool,
|
|
56
80
|
createListInFolderTool,
|
|
57
81
|
getListTool,
|
|
@@ -63,8 +87,9 @@ export function configureServer() {
|
|
|
63
87
|
deleteFolderTool,
|
|
64
88
|
getSpaceTagsTool,
|
|
65
89
|
addTagToTaskTool,
|
|
66
|
-
removeTagFromTaskTool
|
|
67
|
-
|
|
90
|
+
removeTagFromTaskTool,
|
|
91
|
+
...documentModule()
|
|
92
|
+
].filter(tool => !config.disabledTools.includes(tool.name))
|
|
68
93
|
};
|
|
69
94
|
});
|
|
70
95
|
// Add handler for resources/list
|
|
@@ -74,8 +99,8 @@ export function configureServer() {
|
|
|
74
99
|
});
|
|
75
100
|
// Register CallTool handler with proper logging
|
|
76
101
|
logger.info("Registering tool handlers", {
|
|
77
|
-
toolCount:
|
|
78
|
-
categories: ["workspace", "task", "list", "folder", "tag"]
|
|
102
|
+
toolCount: 40,
|
|
103
|
+
categories: ["workspace", "task", "time-tracking", "list", "folder", "tag", "document"]
|
|
79
104
|
});
|
|
80
105
|
server.setRequestHandler(CallToolRequestSchema, async (req) => {
|
|
81
106
|
const { name, arguments: params } = req.params;
|
|
@@ -83,6 +108,14 @@ export function configureServer() {
|
|
|
83
108
|
logger.info(`Received CallTool request for tool: ${name}`, {
|
|
84
109
|
params
|
|
85
110
|
});
|
|
111
|
+
// Check if the tool is disabled
|
|
112
|
+
if (config.disabledTools.includes(name)) {
|
|
113
|
+
logger.warn(`Tool execution blocked: Tool '${name}' is disabled.`);
|
|
114
|
+
throw {
|
|
115
|
+
code: -32601,
|
|
116
|
+
message: `Tool '${name}' is disabled.`
|
|
117
|
+
};
|
|
118
|
+
}
|
|
86
119
|
try {
|
|
87
120
|
// Handle tool calls by routing to the appropriate handler
|
|
88
121
|
switch (name) {
|
|
@@ -140,6 +173,32 @@ export function configureServer() {
|
|
|
140
173
|
return handleAddTagToTask(params);
|
|
141
174
|
case "remove_tag_from_task":
|
|
142
175
|
return handleRemoveTagFromTask(params);
|
|
176
|
+
case "get_task_time_entries":
|
|
177
|
+
return handleGetTaskTimeEntries(params);
|
|
178
|
+
case "start_time_tracking":
|
|
179
|
+
return handleStartTimeTracking(params);
|
|
180
|
+
case "stop_time_tracking":
|
|
181
|
+
return handleStopTimeTracking(params);
|
|
182
|
+
case "add_time_entry":
|
|
183
|
+
return handleAddTimeEntry(params);
|
|
184
|
+
case "delete_time_entry":
|
|
185
|
+
return handleDeleteTimeEntry(params);
|
|
186
|
+
case "get_current_time_entry":
|
|
187
|
+
return handleGetCurrentTimeEntry(params);
|
|
188
|
+
case "create_document":
|
|
189
|
+
return handleCreateDocument(params);
|
|
190
|
+
case "get_document":
|
|
191
|
+
return handleGetDocument(params);
|
|
192
|
+
case "list_documents":
|
|
193
|
+
return handleListDocuments(params);
|
|
194
|
+
case "list_document_pages":
|
|
195
|
+
return handleListDocumentPages(params);
|
|
196
|
+
case "get_document_pages":
|
|
197
|
+
return handleGetDocumentPages(params);
|
|
198
|
+
case "create_document_page":
|
|
199
|
+
return handleCreateDocumentPage(params);
|
|
200
|
+
case "update_document_page":
|
|
201
|
+
return handleUpdateDocumentPage(params);
|
|
143
202
|
default:
|
|
144
203
|
logger.error(`Unknown tool requested: ${name}`);
|
|
145
204
|
const error = new Error(`Unknown tool: ${name}`);
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SPDX-FileCopyrightText: © 2025 João Santana <joaosantana@gmail.com>
|
|
3
|
+
* SPDX-License-Identifier: MIT
|
|
4
|
+
*
|
|
5
|
+
* Document service for ClickUp
|
|
6
|
+
*
|
|
7
|
+
* This service provides methods to manage ClickUp documents:
|
|
8
|
+
* - Create documents
|
|
9
|
+
* - Get document details
|
|
10
|
+
* - List documents in a container
|
|
11
|
+
* - List document pages
|
|
12
|
+
* - Get document pages content
|
|
13
|
+
* - Update document pages
|
|
14
|
+
*/
|
|
15
|
+
import { BaseClickUpService, ClickUpServiceError, ErrorCode } from './base.js';
|
|
16
|
+
export class DocumentService extends BaseClickUpService {
|
|
17
|
+
constructor(apiKey, teamId, baseUrl) {
|
|
18
|
+
// Override baseUrl to use v3 API, since Docs are only available in v3
|
|
19
|
+
super(apiKey, teamId, baseUrl || 'https://api.clickup.com/api/v3');
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Helper method to handle errors consistently
|
|
23
|
+
* @param error The error that occurred
|
|
24
|
+
* @param message Optional custom error message
|
|
25
|
+
* @returns A ClickUpServiceError
|
|
26
|
+
*/
|
|
27
|
+
handleError(error, message) {
|
|
28
|
+
if (error instanceof ClickUpServiceError) {
|
|
29
|
+
return error;
|
|
30
|
+
}
|
|
31
|
+
return new ClickUpServiceError(message || `Document service error: ${error.message}`, ErrorCode.UNKNOWN, error);
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Creates a new document in a space, folder, or list
|
|
35
|
+
* @param data - Document data with parent object structured as {id: string, type: number}
|
|
36
|
+
* @returns Created document
|
|
37
|
+
*/
|
|
38
|
+
async createDocument(data) {
|
|
39
|
+
try {
|
|
40
|
+
// Log the request data for debugging
|
|
41
|
+
this.logOperation('Creating document with data:', { data });
|
|
42
|
+
const response = await this.client.post(`/workspaces/${this.teamId}/docs`, {
|
|
43
|
+
name: data.name,
|
|
44
|
+
parent: data.parent,
|
|
45
|
+
visibility: data.visibility || 'PRIVATE',
|
|
46
|
+
create_page: data.create_page !== undefined ? data.create_page : true
|
|
47
|
+
});
|
|
48
|
+
return response.data;
|
|
49
|
+
}
|
|
50
|
+
catch (error) {
|
|
51
|
+
throw this.handleError(error, 'Failed to create document');
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Creates a new page in a document
|
|
56
|
+
* @param documentId - ID of the document to create the page in
|
|
57
|
+
* @param data - Page data
|
|
58
|
+
* @returns Created page
|
|
59
|
+
*/
|
|
60
|
+
async createPage(documentId, data) {
|
|
61
|
+
try {
|
|
62
|
+
this.logOperation('Creating page in document with data:', { documentId, data });
|
|
63
|
+
const response = await this.client.post(`/workspaces/${this.teamId}/docs/${documentId}/pages`, data);
|
|
64
|
+
return response.data;
|
|
65
|
+
}
|
|
66
|
+
catch (error) {
|
|
67
|
+
throw this.handleError(error, `Failed to create page in document ${documentId}`);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Gets a document by ID
|
|
72
|
+
* @param documentId - ID of the document to retrieve
|
|
73
|
+
* @returns Document details
|
|
74
|
+
*/
|
|
75
|
+
async getDocument(documentId) {
|
|
76
|
+
try {
|
|
77
|
+
this.logOperation('Getting document with ID:', { documentId });
|
|
78
|
+
const response = await this.client.get(`/workspaces/${this.teamId}/docs/${documentId}`);
|
|
79
|
+
return response.data;
|
|
80
|
+
}
|
|
81
|
+
catch (error) {
|
|
82
|
+
throw this.handleError(error, `Failed to get document ${documentId}`);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Lists documents in the workspace with optional filters
|
|
87
|
+
* @param options - Search and filter options
|
|
88
|
+
* @returns List of documents
|
|
89
|
+
*/
|
|
90
|
+
async listDocuments(options = {}) {
|
|
91
|
+
try {
|
|
92
|
+
this.logOperation('Listing documents with options:', { options });
|
|
93
|
+
const response = await this.client.get(`/workspaces/${this.teamId}/docs`, {
|
|
94
|
+
params: options
|
|
95
|
+
});
|
|
96
|
+
return response.data;
|
|
97
|
+
}
|
|
98
|
+
catch (error) {
|
|
99
|
+
throw this.handleError(error, 'Failed to list documents');
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Lists all pages in a document with optional depth control
|
|
104
|
+
* @param documentId - ID of the document
|
|
105
|
+
* @param options - Options for page listing
|
|
106
|
+
* @returns List of document pages
|
|
107
|
+
*/
|
|
108
|
+
async listDocumentPages(documentId, options = {}) {
|
|
109
|
+
try {
|
|
110
|
+
this.logOperation('Listing pages for document with ID:', { documentId, options });
|
|
111
|
+
const response = await this.client.get(`/workspaces/${this.teamId}/docs/${documentId}/pageListing`, { params: options });
|
|
112
|
+
return response.data;
|
|
113
|
+
}
|
|
114
|
+
catch (error) {
|
|
115
|
+
throw this.handleError(error, `Failed to list pages for document ${documentId}`);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Gets the content of specific pages in a document
|
|
120
|
+
* @param documentId - ID of the document
|
|
121
|
+
* @param pageIds - Array of page IDs to retrieve
|
|
122
|
+
* @param options - Options for retrieving pages content
|
|
123
|
+
* @returns Document pages with content
|
|
124
|
+
*/
|
|
125
|
+
async getDocumentPages(documentId, pageIds, options = {}) {
|
|
126
|
+
try {
|
|
127
|
+
// Get pages in parallel
|
|
128
|
+
this.logOperation('Getting pages for document with ID:', { documentId, pageIds, options });
|
|
129
|
+
const pagePromises = pageIds.map(pageId => this.client.get(`/workspaces/${this.teamId}/docs/${documentId}/pages/${pageId}`, { params: { ...options, pageIds } }));
|
|
130
|
+
const responses = await Promise.all(pagePromises);
|
|
131
|
+
const pages = responses.map(response => response.data);
|
|
132
|
+
return { pages };
|
|
133
|
+
}
|
|
134
|
+
catch (error) {
|
|
135
|
+
throw this.handleError(error, `Failed to get pages for document ${documentId}`);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Updates an existing page in a document
|
|
140
|
+
* @param documentId - ID of the document containing the page
|
|
141
|
+
* @param pageId - ID of the page to update
|
|
142
|
+
* @param data - Updated page data
|
|
143
|
+
* @returns Updated page
|
|
144
|
+
*/
|
|
145
|
+
async updatePage(documentId, pageId, data) {
|
|
146
|
+
try {
|
|
147
|
+
this.logOperation('Updating page in document with ID:', { documentId, pageId, data });
|
|
148
|
+
const response = await this.client.put(`/workspaces/${this.teamId}/docs/${documentId}/pages/${pageId}`, {
|
|
149
|
+
...data,
|
|
150
|
+
content_format: data.content_format || 'text/md',
|
|
151
|
+
content_edit_mode: data.content_edit_mode || 'append'
|
|
152
|
+
});
|
|
153
|
+
return response.data;
|
|
154
|
+
}
|
|
155
|
+
catch (error) {
|
|
156
|
+
throw this.handleError(error, `Failed to update page ${pageId} in document ${documentId}`);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
@@ -17,13 +17,17 @@ export { TaskService } from './task/index.js';
|
|
|
17
17
|
export { ListService } from './list.js';
|
|
18
18
|
export { FolderService } from './folder.js';
|
|
19
19
|
export { ClickUpTagService } from './tag.js';
|
|
20
|
+
export { TimeTrackingService } from './time.js';
|
|
21
|
+
export { DocumentService } from './document.js';
|
|
20
22
|
// Import service classes for the factory function
|
|
21
23
|
import { WorkspaceService } from './workspace.js';
|
|
22
24
|
import { TaskService } from './task/index.js';
|
|
23
25
|
import { ListService } from './list.js';
|
|
24
26
|
import { FolderService } from './folder.js';
|
|
25
27
|
import { ClickUpTagService } from './tag.js';
|
|
28
|
+
import { TimeTrackingService } from './time.js';
|
|
26
29
|
import { Logger } from '../../logger.js';
|
|
30
|
+
import { DocumentService } from './document.js';
|
|
27
31
|
// Singleton logger for ClickUp services
|
|
28
32
|
const logger = new Logger('ClickUpServices');
|
|
29
33
|
/**
|
|
@@ -50,12 +54,18 @@ export function createClickUpServices(config) {
|
|
|
50
54
|
const folderService = new FolderService(apiKey, teamId, baseUrl, workspaceService);
|
|
51
55
|
logger.info('Initializing ClickUp Tag service');
|
|
52
56
|
const tagService = new ClickUpTagService(apiKey, teamId, baseUrl);
|
|
57
|
+
logger.info('Initializing ClickUp Time Tracking service');
|
|
58
|
+
const timeTrackingService = new TimeTrackingService(apiKey, teamId, baseUrl);
|
|
59
|
+
logger.info('Initializing ClickUp Document service');
|
|
60
|
+
const documentService = new DocumentService(apiKey, teamId, baseUrl);
|
|
53
61
|
const services = {
|
|
54
62
|
workspace: workspaceService,
|
|
55
63
|
task: taskService,
|
|
56
64
|
list: listService,
|
|
57
65
|
folder: folderService,
|
|
58
|
-
tag: tagService
|
|
66
|
+
tag: tagService,
|
|
67
|
+
timeTracking: timeTrackingService,
|
|
68
|
+
document: documentService
|
|
59
69
|
};
|
|
60
70
|
// Log successful completion
|
|
61
71
|
logger.info('All ClickUp services initialized successfully', {
|
|
@@ -268,8 +268,11 @@ export class TaskServiceCore extends BaseClickUpService {
|
|
|
268
268
|
async updateTask(taskId, updateData) {
|
|
269
269
|
this.logOperation('updateTask', { taskId, ...updateData });
|
|
270
270
|
try {
|
|
271
|
-
|
|
272
|
-
|
|
271
|
+
// Extract custom fields from updateData
|
|
272
|
+
const { custom_fields, ...standardFields } = updateData;
|
|
273
|
+
// First update the standard fields
|
|
274
|
+
const updatedTask = await this.makeRequest(async () => {
|
|
275
|
+
const response = await this.client.put(`/task/${taskId}`, standardFields);
|
|
273
276
|
// Handle both JSON and text responses
|
|
274
277
|
const data = response.data;
|
|
275
278
|
if (typeof data === 'string') {
|
|
@@ -283,6 +286,15 @@ export class TaskServiceCore extends BaseClickUpService {
|
|
|
283
286
|
}
|
|
284
287
|
return data;
|
|
285
288
|
});
|
|
289
|
+
// Then update custom fields if provided
|
|
290
|
+
if (custom_fields && Array.isArray(custom_fields) && custom_fields.length > 0) {
|
|
291
|
+
// Use the setCustomFieldValues method from the inherited class
|
|
292
|
+
// This will be available in TaskServiceCustomFields which extends this class
|
|
293
|
+
await this.setCustomFieldValues(taskId, custom_fields);
|
|
294
|
+
// Fetch the task again to get the updated version with custom fields
|
|
295
|
+
return await this.getTask(taskId);
|
|
296
|
+
}
|
|
297
|
+
return updatedTask;
|
|
286
298
|
}
|
|
287
299
|
catch (error) {
|
|
288
300
|
throw this.handleError(error, `Failed to update task ${taskId}`);
|