@taazkareem/clickup-mcp-server 0.4.68 → 0.4.70
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 +7 -6
- package/build/index.js +58 -9
- package/build/logger.js +166 -0
- package/build/server-state.js +93 -0
- package/build/server.js +77 -56
- package/build/server.log +265 -0
- package/build/services/clickup/base.js +158 -105
- package/build/services/clickup/index.js +27 -5
- package/build/services/clickup/task.js +46 -2
- package/build/services/clickup/workspace.js +22 -17
- package/build/services/shared.js +24 -4
- package/build/tools/cache.js +452 -0
- package/build/tools/debug.js +76 -0
- package/build/tools/folder.js +3 -9
- package/build/tools/list.js +3 -8
- package/build/tools/logs.js +55 -0
- package/build/tools/task.js +171 -97
- package/build/tools/workspace.js +11 -0
- package/package.json +1 -1
package/build/server.log
ADDED
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
[2025-03-18T04:13:46.822Z] [PID:60411] INFO: [SharedServices] Creating shared ClickUp services singleton
|
|
2
|
+
[2025-03-18T04:13:46.823Z] [PID:60411] INFO: [ClickUpServices] Starting ClickUp services initialization
|
|
3
|
+
{
|
|
4
|
+
"teamId": "9014370478",
|
|
5
|
+
"baseUrl": "https://api.clickup.com/api/v2"
|
|
6
|
+
}
|
|
7
|
+
[2025-03-18T04:13:46.824Z] [PID:60411] INFO: [ClickUpServices] Initializing ClickUp Workspace service
|
|
8
|
+
[2025-03-18T04:13:46.825Z] [PID:60411] DEBUG: [ClickUp:WorkspaceService] Initialized WorkspaceService
|
|
9
|
+
{
|
|
10
|
+
"teamId": "9014370478",
|
|
11
|
+
"baseUrl": "https://api.clickup.com/api/v2"
|
|
12
|
+
}
|
|
13
|
+
[2025-03-18T04:13:46.825Z] [PID:60411] INFO: [ClickUpServices] Initializing ClickUp Task service
|
|
14
|
+
[2025-03-18T04:13:46.826Z] [PID:60411] DEBUG: [ClickUp:TaskService] Initialized TaskService
|
|
15
|
+
{
|
|
16
|
+
"teamId": "9014370478",
|
|
17
|
+
"baseUrl": "https://api.clickup.com/api/v2"
|
|
18
|
+
}
|
|
19
|
+
[2025-03-18T04:13:46.826Z] [PID:60411] DEBUG: [ClickUp:ListService] Initialized ListService
|
|
20
|
+
{
|
|
21
|
+
"teamId": "9014370478",
|
|
22
|
+
"baseUrl": "https://api.clickup.com/api/v2"
|
|
23
|
+
}
|
|
24
|
+
[2025-03-18T04:13:46.826Z] [PID:60411] INFO: [ClickUpServices] Initializing ClickUp List service
|
|
25
|
+
[2025-03-18T04:13:46.827Z] [PID:60411] DEBUG: [ClickUp:ListService] Initialized ListService
|
|
26
|
+
{
|
|
27
|
+
"teamId": "9014370478",
|
|
28
|
+
"baseUrl": "https://api.clickup.com/api/v2"
|
|
29
|
+
}
|
|
30
|
+
[2025-03-18T04:13:46.827Z] [PID:60411] INFO: [ClickUpServices] Initializing ClickUp Folder service
|
|
31
|
+
[2025-03-18T04:13:46.827Z] [PID:60411] DEBUG: [ClickUp:FolderService] Initialized FolderService
|
|
32
|
+
{
|
|
33
|
+
"teamId": "9014370478",
|
|
34
|
+
"baseUrl": "https://api.clickup.com/api/v2"
|
|
35
|
+
}
|
|
36
|
+
[2025-03-18T04:13:46.827Z] [PID:60411] INFO: [ClickUpServices] All ClickUp services initialized successfully
|
|
37
|
+
{
|
|
38
|
+
"services": [
|
|
39
|
+
"workspace",
|
|
40
|
+
"task",
|
|
41
|
+
"list",
|
|
42
|
+
"folder"
|
|
43
|
+
],
|
|
44
|
+
"baseUrl": "https://api.clickup.com/api/v2"
|
|
45
|
+
}
|
|
46
|
+
[2025-03-18T04:13:46.827Z] [PID:60411] INFO: [SharedServices] Services initialization complete
|
|
47
|
+
{
|
|
48
|
+
"services": "workspace, task, list, folder",
|
|
49
|
+
"teamId": "9014370478"
|
|
50
|
+
}
|
|
51
|
+
[2025-03-18T04:13:46.828Z] [PID:60411] INFO: Starting ClickUp MCP Server...
|
|
52
|
+
[2025-03-18T04:13:47.270Z] [PID:60411] INFO: Server environment
|
|
53
|
+
{
|
|
54
|
+
"pid": 60411,
|
|
55
|
+
"node": "v23.5.0",
|
|
56
|
+
"os": "darwin",
|
|
57
|
+
"arch": "x64"
|
|
58
|
+
}
|
|
59
|
+
[2025-03-18T04:13:47.271Z] [PID:60411] INFO: Initializing workspace tools
|
|
60
|
+
[2025-03-18T04:13:47.271Z] [PID:60411] INFO: [WorkspaceTool] Initializing workspace tool
|
|
61
|
+
[2025-03-18T04:13:47.271Z] [PID:60411] INFO: [WorkspaceTool] Workspace tool initialized successfully
|
|
62
|
+
{
|
|
63
|
+
"serviceType": "WorkspaceService"
|
|
64
|
+
}
|
|
65
|
+
[2025-03-18T04:13:47.271Z] [PID:60411] INFO: Configuring server request handlers
|
|
66
|
+
[2025-03-18T04:13:47.271Z] [PID:60411] INFO: [Server] Registering server request handlers
|
|
67
|
+
[2025-03-18T04:13:47.271Z] [PID:60411] INFO: [Server] Registering tool handlers
|
|
68
|
+
{
|
|
69
|
+
"toolCount": 22,
|
|
70
|
+
"categories": [
|
|
71
|
+
"workspace",
|
|
72
|
+
"task",
|
|
73
|
+
"list",
|
|
74
|
+
"folder"
|
|
75
|
+
]
|
|
76
|
+
}
|
|
77
|
+
[2025-03-18T04:13:47.271Z] [PID:60411] INFO: Connecting to MCP stdio transport
|
|
78
|
+
[2025-03-18T04:13:47.272Z] [PID:60411] INFO: Server startup complete - ready to handle requests
|
|
79
|
+
[2025-03-18T04:13:47.731Z] [PID:60411] DEBUG: [Server] Received ListTools request
|
|
80
|
+
[2025-03-18T04:14:04.121Z] [PID:60411] INFO: [Server] Received CallTool request for tool: get_workspace_hierarchy
|
|
81
|
+
{
|
|
82
|
+
"params": {}
|
|
83
|
+
}
|
|
84
|
+
[2025-03-18T04:14:04.578Z] [PID:60411] DEBUG: [ClickUp:WorkspaceService] Request completed successfully in 456ms
|
|
85
|
+
[2025-03-18T04:14:04.982Z] [PID:60411] DEBUG: [ClickUp:WorkspaceService] Request completed successfully in 403ms
|
|
86
|
+
[2025-03-18T04:14:05.440Z] [PID:60411] DEBUG: [ClickUp:WorkspaceService] Request completed successfully in 458ms
|
|
87
|
+
[2025-03-18T04:14:05.954Z] [PID:60411] DEBUG: [ClickUp:WorkspaceService] Request completed successfully in 513ms
|
|
88
|
+
[2025-03-18T04:14:05.954Z] [PID:60411] DEBUG: [ClickUp:WorkspaceService] Found 2 folderless lists in space 90141365861
|
|
89
|
+
[2025-03-18T04:14:05.954Z] [PID:60411] DEBUG: [ClickUp:WorkspaceService] Adding 2 lists directly to space Talib's Space (90141365861)
|
|
90
|
+
[2025-03-18T04:14:05.954Z] [PID:60411] DEBUG: [ClickUp:WorkspaceService] Adding list directly to space: Personal List (901403617613)
|
|
91
|
+
[2025-03-18T04:14:05.954Z] [PID:60411] DEBUG: [ClickUp:WorkspaceService] Adding list directly to space: Work List (901403621899)
|
|
92
|
+
[2025-03-18T04:14:06.318Z] [PID:60411] DEBUG: [ClickUp:WorkspaceService] Request completed successfully in 364ms
|
|
93
|
+
[2025-03-18T04:14:06.775Z] [PID:60411] DEBUG: [ClickUp:WorkspaceService] Request completed successfully in 457ms
|
|
94
|
+
[2025-03-18T04:14:07.146Z] [PID:60411] DEBUG: [ClickUp:WorkspaceService] Request completed successfully in 371ms
|
|
95
|
+
[2025-03-18T04:14:07.147Z] [PID:60411] DEBUG: [ClickUp:WorkspaceService] Found 0 folderless lists in space 90141864154
|
|
96
|
+
[2025-03-18T04:14:07.147Z] [PID:60411] DEBUG: [ClickUp:WorkspaceService] Adding 0 lists directly to space Education (90141864154)
|
|
97
|
+
[2025-03-18T04:14:07.517Z] [PID:60411] DEBUG: [ClickUp:WorkspaceService] Request completed successfully in 370ms
|
|
98
|
+
[2025-03-18T04:14:07.899Z] [PID:60411] DEBUG: [ClickUp:WorkspaceService] Request completed successfully in 382ms
|
|
99
|
+
[2025-03-18T04:14:07.899Z] [PID:60411] DEBUG: [ClickUp:WorkspaceService] Found 1 folderless lists in space 90141369187
|
|
100
|
+
[2025-03-18T04:14:07.899Z] [PID:60411] DEBUG: [ClickUp:WorkspaceService] Adding 1 lists directly to space Social Media Content (90141369187)
|
|
101
|
+
[2025-03-18T04:14:07.899Z] [PID:60411] DEBUG: [ClickUp:WorkspaceService] Adding list directly to space: VibeCase (901403679582)
|
|
102
|
+
[2025-03-18T04:14:08.412Z] [PID:60411] DEBUG: [ClickUp:WorkspaceService] Request completed successfully in 512ms
|
|
103
|
+
[2025-03-18T04:14:08.821Z] [PID:60411] DEBUG: [ClickUp:WorkspaceService] Request completed successfully in 409ms
|
|
104
|
+
[2025-03-18T04:14:09.218Z] [PID:60411] DEBUG: [ClickUp:WorkspaceService] Request completed successfully in 397ms
|
|
105
|
+
[2025-03-18T04:14:09.641Z] [PID:60411] DEBUG: [ClickUp:WorkspaceService] Request completed successfully in 423ms
|
|
106
|
+
[2025-03-18T04:14:10.363Z] [PID:60411] DEBUG: [ClickUp:WorkspaceService] Request completed successfully in 722ms
|
|
107
|
+
[2025-03-18T04:14:11.075Z] [PID:60411] DEBUG: [ClickUp:WorkspaceService] Request completed successfully in 712ms
|
|
108
|
+
[2025-03-18T04:14:11.485Z] [PID:60411] DEBUG: [ClickUp:WorkspaceService] Request completed successfully in 410ms
|
|
109
|
+
[2025-03-18T04:14:12.099Z] [PID:60411] DEBUG: [ClickUp:WorkspaceService] Request completed successfully in 614ms
|
|
110
|
+
[2025-03-18T04:14:12.562Z] [PID:60411] DEBUG: [ClickUp:WorkspaceService] Request completed successfully in 463ms
|
|
111
|
+
[2025-03-18T04:14:12.977Z] [PID:60411] DEBUG: [ClickUp:WorkspaceService] Request completed successfully in 415ms
|
|
112
|
+
[2025-03-18T04:14:12.977Z] [PID:60411] DEBUG: [ClickUp:WorkspaceService] Found 7 folderless lists in space 90141392755
|
|
113
|
+
[2025-03-18T04:14:12.977Z] [PID:60411] DEBUG: [ClickUp:WorkspaceService] Adding 7 lists directly to space Custom Space (90141392755)
|
|
114
|
+
[2025-03-18T04:14:12.977Z] [PID:60411] DEBUG: [ClickUp:WorkspaceService] Adding list directly to space: Job Applications (901404823810)
|
|
115
|
+
[2025-03-18T04:14:12.977Z] [PID:60411] DEBUG: [ClickUp:WorkspaceService] Adding list directly to space: Prompts | Snippets | Commands (901407112060)
|
|
116
|
+
[2025-03-18T04:14:12.977Z] [PID:60411] DEBUG: [ClickUp:WorkspaceService] Adding list directly to space: Style Scraper (901408105509)
|
|
117
|
+
[2025-03-18T04:14:12.977Z] [PID:60411] DEBUG: [ClickUp:WorkspaceService] Adding list directly to space: goal-tracker (901408127809)
|
|
118
|
+
[2025-03-18T04:14:12.977Z] [PID:60411] DEBUG: [ClickUp:WorkspaceService] Adding list directly to space: cursor-rules-mcp-server (901408144363)
|
|
119
|
+
[2025-03-18T04:14:12.977Z] [PID:60411] DEBUG: [ClickUp:WorkspaceService] Adding list directly to space: Items to Sell Online (901404691843)
|
|
120
|
+
[2025-03-18T04:14:12.977Z] [PID:60411] DEBUG: [ClickUp:WorkspaceService] Adding list directly to space: clickup-mcp-server (901408020907)
|
|
121
|
+
[2025-03-18T04:14:15.996Z] [PID:60411] INFO: [Server] Received CallTool request for tool: get_tasks
|
|
122
|
+
{
|
|
123
|
+
"params": {
|
|
124
|
+
"listName": "clickup-mcp-server"
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
[2025-03-18T04:14:15.997Z] [PID:60411] INFO: [ClickUp:TaskService] Operation: getTasks
|
|
128
|
+
{
|
|
129
|
+
"listId": "901407953112",
|
|
130
|
+
"filters": {}
|
|
131
|
+
}
|
|
132
|
+
[2025-03-18T04:14:16.466Z] [PID:60411] DEBUG: [ClickUp:TaskService] Request completed successfully in 469ms
|
|
133
|
+
[2025-03-18T04:14:19.277Z] [PID:60411] INFO: [Server] Received CallTool request for tool: get_tasks
|
|
134
|
+
{
|
|
135
|
+
"params": {
|
|
136
|
+
"listId": "901407953112"
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
[2025-03-18T04:14:19.277Z] [PID:60411] INFO: [ClickUp:TaskService] Operation: getTasks
|
|
140
|
+
{
|
|
141
|
+
"listId": "901407953112",
|
|
142
|
+
"filters": {}
|
|
143
|
+
}
|
|
144
|
+
[2025-03-18T04:14:20.289Z] [PID:60411] DEBUG: [ClickUp:TaskService] Request completed successfully in 1012ms
|
|
145
|
+
[2025-03-18T04:14:23.404Z] [PID:60411] INFO: [Server] Received CallTool request for tool: get_tasks
|
|
146
|
+
{
|
|
147
|
+
"params": {
|
|
148
|
+
"listName": "MCP Server TypeScript Template"
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
[2025-03-18T04:14:23.405Z] [PID:60411] INFO: [ClickUp:TaskService] Operation: getTasks
|
|
152
|
+
{
|
|
153
|
+
"listId": "901407973154",
|
|
154
|
+
"filters": {}
|
|
155
|
+
}
|
|
156
|
+
[2025-03-18T04:14:23.846Z] [PID:60411] DEBUG: [ClickUp:TaskService] Request completed successfully in 441ms
|
|
157
|
+
[2025-03-18T04:14:25.784Z] [PID:60411] INFO: [Server] Received CallTool request for tool: get_tasks
|
|
158
|
+
{
|
|
159
|
+
"params": {
|
|
160
|
+
"listName": "style-scraper"
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
[2025-03-18T04:14:25.784Z] [PID:60411] INFO: [ClickUp:TaskService] Operation: getTasks
|
|
164
|
+
{
|
|
165
|
+
"listId": "901408160190",
|
|
166
|
+
"filters": {}
|
|
167
|
+
}
|
|
168
|
+
[2025-03-18T04:14:26.539Z] [PID:60411] DEBUG: [ClickUp:TaskService] Request completed successfully in 755ms
|
|
169
|
+
[2025-03-18T04:14:28.915Z] [PID:60411] INFO: [Server] Received CallTool request for tool: get_tasks
|
|
170
|
+
{
|
|
171
|
+
"params": {
|
|
172
|
+
"listName": "Style Scraper"
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
[2025-03-18T04:14:28.915Z] [PID:60411] INFO: [ClickUp:TaskService] Operation: getTasks
|
|
176
|
+
{
|
|
177
|
+
"listId": "901408105509",
|
|
178
|
+
"filters": {}
|
|
179
|
+
}
|
|
180
|
+
[2025-03-18T04:14:30.493Z] [PID:60411] DEBUG: [ClickUp:TaskService] Request completed successfully in 1578ms
|
|
181
|
+
[2025-03-18T04:14:32.848Z] [PID:60411] INFO: [Server] Received CallTool request for tool: get_tasks
|
|
182
|
+
{
|
|
183
|
+
"params": {
|
|
184
|
+
"listId": "901407112060"
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
[2025-03-18T04:14:32.848Z] [PID:60411] INFO: [ClickUp:TaskService] Operation: getTasks
|
|
188
|
+
{
|
|
189
|
+
"listId": "901407112060",
|
|
190
|
+
"filters": {}
|
|
191
|
+
}
|
|
192
|
+
[2025-03-18T04:14:34.933Z] [PID:60411] DEBUG: [ClickUp:TaskService] Request completed successfully in 2085ms
|
|
193
|
+
[2025-03-18T04:14:37.867Z] [PID:60411] INFO: [Server] Received CallTool request for tool: get_tasks
|
|
194
|
+
{
|
|
195
|
+
"params": {
|
|
196
|
+
"listName": "cursor-rules-mcp-server"
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
[2025-03-18T04:14:37.867Z] [PID:60411] INFO: [ClickUp:TaskService] Operation: getTasks
|
|
200
|
+
{
|
|
201
|
+
"listId": "901408144363",
|
|
202
|
+
"filters": {}
|
|
203
|
+
}
|
|
204
|
+
[2025-03-18T04:14:38.209Z] [PID:60411] DEBUG: [ClickUp:TaskService] Request completed successfully in 342ms
|
|
205
|
+
[2025-03-18T04:14:41.460Z] [PID:60411] INFO: [Server] Received CallTool request for tool: get_tasks
|
|
206
|
+
{
|
|
207
|
+
"params": {
|
|
208
|
+
"listName": "goal-tracker"
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
[2025-03-18T04:14:41.460Z] [PID:60411] INFO: [ClickUp:TaskService] Operation: getTasks
|
|
212
|
+
{
|
|
213
|
+
"listId": "901408127809",
|
|
214
|
+
"filters": {}
|
|
215
|
+
}
|
|
216
|
+
[2025-03-18T04:14:41.997Z] [PID:60411] DEBUG: [ClickUp:TaskService] Request completed successfully in 537ms
|
|
217
|
+
[2025-03-18T04:14:44.058Z] [PID:60411] INFO: [Server] Received CallTool request for tool: get_tasks
|
|
218
|
+
{
|
|
219
|
+
"params": {
|
|
220
|
+
"listName": "youtube-mcp-server"
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
[2025-03-18T04:14:44.058Z] [PID:60411] INFO: [ClickUp:TaskService] Operation: getTasks
|
|
224
|
+
{
|
|
225
|
+
"listId": "901407973619",
|
|
226
|
+
"filters": {}
|
|
227
|
+
}
|
|
228
|
+
[2025-03-18T04:14:44.660Z] [PID:60411] DEBUG: [ClickUp:TaskService] Request completed successfully in 602ms
|
|
229
|
+
[2025-03-18T04:14:48.562Z] [PID:60411] INFO: [Server] Received CallTool request for tool: get_task
|
|
230
|
+
{
|
|
231
|
+
"params": {
|
|
232
|
+
"taskId": "86b48gaha"
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
[2025-03-18T04:14:48.562Z] [PID:60411] INFO: [ClickUp:TaskService] Operation: getTask
|
|
236
|
+
{
|
|
237
|
+
"taskId": "86b48gaha"
|
|
238
|
+
}
|
|
239
|
+
[2025-03-18T04:14:49.063Z] [PID:60411] DEBUG: [ClickUp:TaskService] Request completed successfully in 501ms
|
|
240
|
+
[2025-03-18T04:14:52.146Z] [PID:60411] INFO: [Server] Received CallTool request for tool: create_task
|
|
241
|
+
{
|
|
242
|
+
"params": {
|
|
243
|
+
"name": "📊 Project Setup",
|
|
244
|
+
"listName": "clickup-mcp-server",
|
|
245
|
+
"description": "Initial setup of the ClickUp MCP server project including repository configuration, environment setup, and dependency management."
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
[2025-03-18T04:14:52.147Z] [PID:60411] INFO: [ClickUp:TaskService] Operation: createTask
|
|
249
|
+
{
|
|
250
|
+
"listId": "901407953112",
|
|
251
|
+
"name": "📊 Project Setup",
|
|
252
|
+
"description": "Initial setup of the ClickUp MCP server project including repository configuration, environment setup, and dependency management."
|
|
253
|
+
}
|
|
254
|
+
[2025-03-18T04:14:52.702Z] [PID:60411] DEBUG: [ClickUp:TaskService] Request completed successfully in 555ms
|
|
255
|
+
[2025-03-18T04:14:54.911Z] [PID:60411] INFO: [Server] Received CallTool request for tool: get_task
|
|
256
|
+
{
|
|
257
|
+
"params": {
|
|
258
|
+
"taskId": "86b4a6cpf"
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
[2025-03-18T04:14:54.911Z] [PID:60411] INFO: [ClickUp:TaskService] Operation: getTask
|
|
262
|
+
{
|
|
263
|
+
"taskId": "86b4a6cpf"
|
|
264
|
+
}
|
|
265
|
+
[2025-03-18T04:14:55.469Z] [PID:60411] DEBUG: [ClickUp:TaskService] Request completed successfully in 558ms
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
* - Common request methods
|
|
9
9
|
*/
|
|
10
10
|
import axios from 'axios';
|
|
11
|
+
import { Logger, LogLevel } from '../../logger.js';
|
|
11
12
|
/**
|
|
12
13
|
* Error types for better error handling
|
|
13
14
|
*/
|
|
@@ -56,6 +57,9 @@ export class BaseClickUpService {
|
|
|
56
57
|
this.apiKey = apiKey;
|
|
57
58
|
this.teamId = teamId;
|
|
58
59
|
this.requestSpacing = this.defaultRequestSpacing;
|
|
60
|
+
// Create a logger with the actual class name for better context
|
|
61
|
+
const className = this.constructor.name;
|
|
62
|
+
this.logger = new Logger(`ClickUp:${className}`);
|
|
59
63
|
// Configure the Axios client with default settings
|
|
60
64
|
this.client = axios.create({
|
|
61
65
|
baseURL: baseUrl,
|
|
@@ -65,6 +69,7 @@ export class BaseClickUpService {
|
|
|
65
69
|
},
|
|
66
70
|
timeout: this.timeout
|
|
67
71
|
});
|
|
72
|
+
this.logger.debug(`Initialized ${className}`, { teamId, baseUrl });
|
|
68
73
|
// Add response interceptor for error handling
|
|
69
74
|
this.client.interceptors.response.use(response => response, error => this.handleAxiosError(error));
|
|
70
75
|
}
|
|
@@ -75,88 +80,89 @@ export class BaseClickUpService {
|
|
|
75
80
|
* @returns Never - always throws an error
|
|
76
81
|
*/
|
|
77
82
|
handleAxiosError(error) {
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
message = 'Resource not found';
|
|
97
|
-
code = ErrorCode.NOT_FOUND;
|
|
98
|
-
break;
|
|
99
|
-
case 429:
|
|
100
|
-
message = 'Rate limit exceeded';
|
|
101
|
-
code = ErrorCode.RATE_LIMIT;
|
|
102
|
-
break;
|
|
103
|
-
case 400:
|
|
104
|
-
message = 'Invalid request: ' + (error.response.data?.err || 'Validation error');
|
|
105
|
-
code = ErrorCode.VALIDATION;
|
|
106
|
-
break;
|
|
107
|
-
case 500:
|
|
108
|
-
case 502:
|
|
109
|
-
case 503:
|
|
110
|
-
case 504:
|
|
111
|
-
message = 'ClickUp server error';
|
|
112
|
-
code = ErrorCode.SERVER_ERROR;
|
|
113
|
-
break;
|
|
114
|
-
default:
|
|
115
|
-
message = `ClickUp API error (${status}): ${error.response.data?.err || 'Unknown error'}`;
|
|
116
|
-
}
|
|
83
|
+
// Determine error details
|
|
84
|
+
const status = error.response?.status;
|
|
85
|
+
const responseData = error.response?.data;
|
|
86
|
+
const errorMsg = responseData?.err || responseData?.error || error.message || 'Unknown API error';
|
|
87
|
+
const path = error.config?.url || 'unknown path';
|
|
88
|
+
// Context object for providing more detailed log information
|
|
89
|
+
const errorContext = {
|
|
90
|
+
path,
|
|
91
|
+
status,
|
|
92
|
+
method: error.config?.method?.toUpperCase() || 'UNKNOWN',
|
|
93
|
+
requestData: error.config?.data ? JSON.parse(error.config.data) : undefined
|
|
94
|
+
};
|
|
95
|
+
// Pick the appropriate error code based on status
|
|
96
|
+
let code;
|
|
97
|
+
let logMessage;
|
|
98
|
+
if (error.code === 'ECONNABORTED' || error.message?.includes('timeout')) {
|
|
99
|
+
code = ErrorCode.NETWORK_ERROR;
|
|
100
|
+
logMessage = `Request timeout for ${path}`;
|
|
117
101
|
}
|
|
118
|
-
else if (error.
|
|
119
|
-
// Request was made but no response received
|
|
120
|
-
message = 'Network error: No response received from ClickUp';
|
|
102
|
+
else if (!error.response) {
|
|
121
103
|
code = ErrorCode.NETWORK_ERROR;
|
|
122
|
-
|
|
104
|
+
logMessage = `Network error accessing ${path}: ${error.message}`;
|
|
105
|
+
}
|
|
106
|
+
else if (status === 429) {
|
|
107
|
+
code = ErrorCode.RATE_LIMIT;
|
|
108
|
+
this.handleRateLimitHeaders(error.response.headers);
|
|
109
|
+
logMessage = `Rate limit exceeded for ${path}`;
|
|
110
|
+
}
|
|
111
|
+
else if (status === 401 || status === 403) {
|
|
112
|
+
code = ErrorCode.UNAUTHORIZED;
|
|
113
|
+
logMessage = `Authorization failed for ${path}`;
|
|
114
|
+
}
|
|
115
|
+
else if (status === 404) {
|
|
116
|
+
code = ErrorCode.NOT_FOUND;
|
|
117
|
+
logMessage = `Resource not found: ${path}`;
|
|
118
|
+
}
|
|
119
|
+
else if (status >= 400 && status < 500) {
|
|
120
|
+
code = ErrorCode.VALIDATION;
|
|
121
|
+
logMessage = `Validation error for ${path}: ${errorMsg}`;
|
|
122
|
+
}
|
|
123
|
+
else if (status >= 500) {
|
|
124
|
+
code = ErrorCode.SERVER_ERROR;
|
|
125
|
+
logMessage = `ClickUp server error: ${errorMsg}`;
|
|
123
126
|
}
|
|
124
127
|
else {
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
details = { message: error.message };
|
|
128
|
+
code = ErrorCode.UNKNOWN;
|
|
129
|
+
logMessage = `Unknown API error: ${errorMsg}`;
|
|
128
130
|
}
|
|
129
|
-
|
|
131
|
+
// Log the error with context
|
|
132
|
+
this.logger.error(logMessage, errorContext);
|
|
133
|
+
// Throw a well-structured error
|
|
134
|
+
throw new ClickUpServiceError(errorMsg, code, responseData, status, errorContext);
|
|
130
135
|
}
|
|
131
136
|
/**
|
|
132
137
|
* Process the request queue, respecting rate limits by spacing out requests
|
|
133
138
|
* @private
|
|
134
139
|
*/
|
|
135
140
|
async processQueue() {
|
|
136
|
-
if (this.
|
|
141
|
+
if (this.requestQueue.length === 0) {
|
|
142
|
+
this.logger.debug('Queue empty, exiting queue processing mode');
|
|
143
|
+
this.processingQueue = false;
|
|
137
144
|
return;
|
|
138
145
|
}
|
|
139
|
-
this.
|
|
146
|
+
this.logger.debug(`Processing request queue (${this.requestQueue.length} items)`);
|
|
147
|
+
const startTime = Date.now();
|
|
140
148
|
try {
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
console.error('Request failed:', error);
|
|
149
|
-
// Continue processing queue even if one request fails
|
|
150
|
-
}
|
|
151
|
-
// Space out requests to stay within rate limit
|
|
152
|
-
if (this.requestQueue.length > 0) {
|
|
153
|
-
await new Promise(resolve => setTimeout(resolve, this.requestSpacing));
|
|
154
|
-
}
|
|
155
|
-
}
|
|
149
|
+
// Take the first request from the queue
|
|
150
|
+
const request = this.requestQueue.shift();
|
|
151
|
+
if (request) {
|
|
152
|
+
// Wait for the request spacing interval
|
|
153
|
+
await new Promise(resolve => setTimeout(resolve, this.requestSpacing));
|
|
154
|
+
// Run the request
|
|
155
|
+
await request();
|
|
156
156
|
}
|
|
157
157
|
}
|
|
158
|
+
catch (error) {
|
|
159
|
+
this.logger.error('Error executing queued request', error);
|
|
160
|
+
}
|
|
158
161
|
finally {
|
|
159
|
-
|
|
162
|
+
const duration = Date.now() - startTime;
|
|
163
|
+
this.logger.trace(`Queue item processed in ${duration}ms, ${this.requestQueue.length} items remaining`);
|
|
164
|
+
// Continue processing the queue after a short delay
|
|
165
|
+
setTimeout(() => this.processQueue(), this.requestSpacing);
|
|
160
166
|
}
|
|
161
167
|
}
|
|
162
168
|
/**
|
|
@@ -165,21 +171,38 @@ export class BaseClickUpService {
|
|
|
165
171
|
* @param headers Response headers from ClickUp
|
|
166
172
|
*/
|
|
167
173
|
handleRateLimitHeaders(headers) {
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
this.
|
|
174
|
+
try {
|
|
175
|
+
// Parse the rate limit headers
|
|
176
|
+
const limit = headers['x-ratelimit-limit'];
|
|
177
|
+
const remaining = headers['x-ratelimit-remaining'];
|
|
178
|
+
const reset = headers['x-ratelimit-reset'];
|
|
179
|
+
// Only log if we're getting close to the limit
|
|
180
|
+
if (remaining < limit * 0.2) {
|
|
181
|
+
this.logger.warn('Approaching rate limit', { remaining, limit, reset });
|
|
182
|
+
}
|
|
183
|
+
else {
|
|
184
|
+
this.logger.debug('Rate limit status', { remaining, limit, reset });
|
|
185
|
+
}
|
|
186
|
+
if (reset) {
|
|
187
|
+
this.lastRateLimitReset = reset;
|
|
188
|
+
// If reset is in the future, calculate a safe request spacing
|
|
189
|
+
const now = Date.now();
|
|
190
|
+
const resetTime = reset * 1000; // convert to milliseconds
|
|
191
|
+
const timeToReset = Math.max(0, resetTime - now);
|
|
192
|
+
if (timeToReset > 0 && remaining > 0) {
|
|
193
|
+
// Calculate time between requests to stay under limit
|
|
194
|
+
// Add 10% buffer to be safe
|
|
195
|
+
const safeSpacing = Math.ceil((timeToReset / remaining) * 1.1);
|
|
196
|
+
// Only adjust if it's greater than our current spacing
|
|
197
|
+
if (safeSpacing > this.requestSpacing) {
|
|
198
|
+
this.logger.debug(`Adjusting request spacing: ${this.requestSpacing}ms → ${safeSpacing}ms`);
|
|
199
|
+
this.requestSpacing = safeSpacing;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
179
202
|
}
|
|
180
203
|
}
|
|
181
|
-
|
|
182
|
-
this.
|
|
204
|
+
catch (error) {
|
|
205
|
+
this.logger.warn('Failed to parse rate limit headers', error);
|
|
183
206
|
}
|
|
184
207
|
}
|
|
185
208
|
/**
|
|
@@ -189,41 +212,59 @@ export class BaseClickUpService {
|
|
|
189
212
|
* @returns Promise that resolves with the result of the API request
|
|
190
213
|
*/
|
|
191
214
|
async makeRequest(fn) {
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
215
|
+
// If we're being rate limited, queue the request rather than executing immediately
|
|
216
|
+
if (this.processingQueue) {
|
|
217
|
+
this.logger.debug('Queue active, adding request to queue');
|
|
218
|
+
return new Promise((resolve, reject) => {
|
|
219
|
+
this.requestQueue.push(async () => {
|
|
220
|
+
try {
|
|
221
|
+
const result = await fn();
|
|
222
|
+
resolve(result);
|
|
223
|
+
}
|
|
224
|
+
catch (error) {
|
|
225
|
+
reject(error);
|
|
199
226
|
}
|
|
200
|
-
|
|
227
|
+
});
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
const startTime = Date.now();
|
|
231
|
+
try {
|
|
232
|
+
// Execute the request function
|
|
233
|
+
const result = await fn();
|
|
234
|
+
// Debug log for successful requests with timing information
|
|
235
|
+
const duration = Date.now() - startTime;
|
|
236
|
+
this.logger.debug(`Request completed successfully in ${duration}ms`);
|
|
237
|
+
return result;
|
|
238
|
+
}
|
|
239
|
+
catch (error) {
|
|
240
|
+
// If we hit a rate limit, start processing the queue
|
|
241
|
+
if (error instanceof ClickUpServiceError && error.code === ErrorCode.RATE_LIMIT) {
|
|
242
|
+
this.logger.warn('Rate limit reached, switching to queue mode', {
|
|
243
|
+
reset: this.lastRateLimitReset,
|
|
244
|
+
queueLength: this.requestQueue.length
|
|
245
|
+
});
|
|
246
|
+
if (!this.processingQueue) {
|
|
247
|
+
this.processingQueue = true;
|
|
248
|
+
this.processQueue().catch(err => {
|
|
249
|
+
this.logger.error('Error processing request queue', err);
|
|
250
|
+
});
|
|
201
251
|
}
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
const resetTime = parseInt(error.response.headers['x-ratelimit-reset'] || '0', 10);
|
|
206
|
-
// Use the more precise reset time if available
|
|
207
|
-
const waitTime = resetTime > 0 ?
|
|
208
|
-
(resetTime * 1000) - Date.now() :
|
|
209
|
-
retryAfter * 1000;
|
|
210
|
-
await new Promise(resolve => setTimeout(resolve, waitTime));
|
|
252
|
+
// Queue this failed request and return a promise that will resolve when it's retried
|
|
253
|
+
return new Promise((resolve, reject) => {
|
|
254
|
+
this.requestQueue.push(async () => {
|
|
211
255
|
try {
|
|
212
|
-
// Retry the request once after waiting
|
|
213
256
|
const result = await fn();
|
|
214
257
|
resolve(result);
|
|
215
258
|
}
|
|
216
259
|
catch (retryError) {
|
|
217
260
|
reject(retryError);
|
|
218
261
|
}
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
this.processQueue().catch(reject);
|
|
226
|
-
});
|
|
262
|
+
});
|
|
263
|
+
});
|
|
264
|
+
}
|
|
265
|
+
// For other errors, just throw
|
|
266
|
+
throw error;
|
|
267
|
+
}
|
|
227
268
|
}
|
|
228
269
|
/**
|
|
229
270
|
* Gets the ClickUp team ID associated with this service instance
|
|
@@ -239,6 +280,18 @@ export class BaseClickUpService {
|
|
|
239
280
|
* @param details - Details about the operation
|
|
240
281
|
*/
|
|
241
282
|
logOperation(operation, details) {
|
|
242
|
-
|
|
283
|
+
this.logger.info(`Operation: ${operation}`, details);
|
|
284
|
+
}
|
|
285
|
+
/**
|
|
286
|
+
* Log detailed information about a request (path and payload)
|
|
287
|
+
* For trace level logging only
|
|
288
|
+
*/
|
|
289
|
+
traceRequest(method, url, data) {
|
|
290
|
+
if (this.logger.isLevelEnabled(LogLevel.TRACE)) {
|
|
291
|
+
this.logger.trace(`${method} ${url}`, {
|
|
292
|
+
payload: data,
|
|
293
|
+
teamId: this.teamId
|
|
294
|
+
});
|
|
295
|
+
}
|
|
243
296
|
}
|
|
244
297
|
}
|
|
@@ -18,6 +18,9 @@ import { WorkspaceService } from './workspace.js';
|
|
|
18
18
|
import { TaskService } from './task.js';
|
|
19
19
|
import { ListService } from './list.js';
|
|
20
20
|
import { FolderService } from './folder.js';
|
|
21
|
+
import { Logger } from '../../logger.js';
|
|
22
|
+
// Singleton logger for ClickUp services
|
|
23
|
+
const logger = new Logger('ClickUpServices');
|
|
21
24
|
/**
|
|
22
25
|
* Factory function to create instances of all ClickUp services
|
|
23
26
|
* @param config Configuration for the services
|
|
@@ -25,12 +28,31 @@ import { FolderService } from './folder.js';
|
|
|
25
28
|
*/
|
|
26
29
|
export function createClickUpServices(config) {
|
|
27
30
|
const { apiKey, teamId, baseUrl } = config;
|
|
28
|
-
//
|
|
31
|
+
// Log start of overall initialization
|
|
32
|
+
logger.info('Starting ClickUp services initialization', {
|
|
33
|
+
teamId,
|
|
34
|
+
baseUrl: baseUrl || 'https://api.clickup.com/api/v2'
|
|
35
|
+
});
|
|
36
|
+
// Create workspace service first since others depend on it
|
|
37
|
+
logger.info('Initializing ClickUp Workspace service');
|
|
29
38
|
const workspaceService = new WorkspaceService(apiKey, teamId, baseUrl);
|
|
30
|
-
|
|
39
|
+
// Initialize remaining services with workspace dependency
|
|
40
|
+
logger.info('Initializing ClickUp Task service');
|
|
41
|
+
const taskService = new TaskService(apiKey, teamId, baseUrl, workspaceService);
|
|
42
|
+
logger.info('Initializing ClickUp List service');
|
|
43
|
+
const listService = new ListService(apiKey, teamId, baseUrl, workspaceService);
|
|
44
|
+
logger.info('Initializing ClickUp Folder service');
|
|
45
|
+
const folderService = new FolderService(apiKey, teamId, baseUrl, workspaceService);
|
|
46
|
+
const services = {
|
|
31
47
|
workspace: workspaceService,
|
|
32
|
-
task:
|
|
33
|
-
list:
|
|
34
|
-
folder:
|
|
48
|
+
task: taskService,
|
|
49
|
+
list: listService,
|
|
50
|
+
folder: folderService
|
|
35
51
|
};
|
|
52
|
+
// Log successful completion
|
|
53
|
+
logger.info('All ClickUp services initialized successfully', {
|
|
54
|
+
services: Object.keys(services),
|
|
55
|
+
baseUrl: baseUrl || 'https://api.clickup.com/api/v2'
|
|
56
|
+
});
|
|
57
|
+
return services;
|
|
36
58
|
}
|