@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.
@@ -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
- let message = 'Unknown error occurred';
79
- let code = ErrorCode.UNKNOWN;
80
- let details = null;
81
- let status = undefined;
82
- if (error.response) {
83
- // Server responded with an error status code
84
- status = error.response.status;
85
- details = error.response.data;
86
- switch (status) {
87
- case 401:
88
- message = 'Unauthorized: Invalid API key';
89
- code = ErrorCode.UNAUTHORIZED;
90
- break;
91
- case 403:
92
- message = 'Forbidden: Insufficient permissions';
93
- code = ErrorCode.UNAUTHORIZED;
94
- break;
95
- case 404:
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.request) {
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
- details = { request: error.request };
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
- // Error setting up the request
126
- message = `Request setup error: ${error.message}`;
127
- details = { message: error.message };
128
+ code = ErrorCode.UNKNOWN;
129
+ logMessage = `Unknown API error: ${errorMsg}`;
128
130
  }
129
- throw new ClickUpServiceError(message, code, details, status);
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.processingQueue || this.requestQueue.length === 0) {
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.processingQueue = true;
146
+ this.logger.debug(`Processing request queue (${this.requestQueue.length} items)`);
147
+ const startTime = Date.now();
140
148
  try {
141
- while (this.requestQueue.length > 0) {
142
- const request = this.requestQueue.shift();
143
- if (request) {
144
- try {
145
- await request();
146
- }
147
- catch (error) {
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
- this.processingQueue = false;
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
- const limit = parseInt(headers['x-ratelimit-limit'], 10);
169
- const remaining = parseInt(headers['x-ratelimit-remaining'], 10);
170
- const reset = parseInt(headers['x-ratelimit-reset'], 10);
171
- if (!isNaN(reset)) {
172
- this.lastRateLimitReset = reset;
173
- }
174
- // If we're running low on remaining requests, increase spacing
175
- if (!isNaN(remaining) && remaining < 10) {
176
- const timeUntilReset = (this.lastRateLimitReset * 1000) - Date.now();
177
- if (timeUntilReset > 0) {
178
- this.requestSpacing = Math.max(this.defaultRequestSpacing, Math.floor(timeUntilReset / remaining));
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
- else {
182
- this.requestSpacing = this.defaultRequestSpacing; // Reset to default spacing
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
- return new Promise((resolve, reject) => {
193
- this.requestQueue.push(async () => {
194
- try {
195
- const result = await fn();
196
- // Handle rate limit headers if present
197
- if (result && typeof result === 'object' && 'headers' in result) {
198
- this.handleRateLimitHeaders(result.headers);
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
- resolve(result);
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
- catch (error) {
203
- if (axios.isAxiosError(error) && error.response?.status === 429) {
204
- const retryAfter = parseInt(error.response.headers['retry-after'] || '60', 10);
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
- else {
221
- reject(error);
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
- console.log(`[${new Date().toISOString()}] ${operation}:`, details);
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
- // Create the workspace service
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
- return {
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: new TaskService(apiKey, teamId, baseUrl, workspaceService),
33
- list: new ListService(apiKey, teamId, baseUrl, workspaceService),
34
- folder: new FolderService(apiKey, teamId, baseUrl, workspaceService)
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
  }