@taazkareem/clickup-mcp-server 0.4.41 → 0.4.42

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.
@@ -1,25 +1,7 @@
1
- /**
2
- * ClickUp API Service Module
3
- * Provides a singleton service for interacting with the ClickUp API.
4
- * Handles all API operations including tasks, lists, spaces, and folders management.
5
- * Implements caching and connection pooling through axios instance.
6
- * @module services/clickup
7
- */
8
1
  import axios from 'axios';
9
- /**
10
- * ClickUp Service Class
11
- * Singleton service that manages all interactions with the ClickUp API.
12
- * Provides methods for CRUD operations on tasks, lists, spaces, and folders.
13
- * Implements helper methods for searching and managing ClickUp resources.
14
- */
15
2
  export class ClickUpService {
16
3
  client;
17
4
  static instance;
18
- /**
19
- * Private constructor to enforce singleton pattern
20
- * Creates an axios instance with base configuration for ClickUp API
21
- * @param apiKey - ClickUp API key for authentication
22
- */
23
5
  constructor(apiKey) {
24
6
  this.client = axios.create({
25
7
  baseURL: 'https://api.clickup.com/api/v2',
@@ -29,231 +11,105 @@ export class ClickUpService {
29
11
  }
30
12
  });
31
13
  }
32
- /**
33
- * Initializes the ClickUpService singleton
34
- * Creates a new instance if one doesn't exist
35
- * @param apiKey - ClickUp API key for authentication
36
- * @returns The singleton ClickUpService instance
37
- * @throws Error if API key is invalid
38
- */
39
14
  static initialize(apiKey) {
40
15
  if (!ClickUpService.instance) {
41
16
  ClickUpService.instance = new ClickUpService(apiKey);
42
17
  }
43
18
  return ClickUpService.instance;
44
19
  }
45
- /**
46
- * Gets the existing ClickUpService instance
47
- * @returns The singleton ClickUpService instance
48
- * @throws Error if service hasn't been initialized
49
- */
50
20
  static getInstance() {
51
21
  if (!ClickUpService.instance) {
52
22
  throw new Error('ClickUpService not initialized. Call initialize() first.');
53
23
  }
54
24
  return ClickUpService.instance;
55
25
  }
56
- // Task Operations
57
- /**
58
- * Retrieves all tasks from a specific list
59
- * Also returns available statuses in the list
60
- * @param listId - ID of the list to fetch tasks from
61
- * @returns Promise resolving to tasks array and unique statuses
62
- */
26
+ // Tasks
63
27
  async getTasks(listId) {
64
28
  const response = await this.client.get(`/list/${listId}/task`);
65
29
  const tasks = response.data.tasks;
66
30
  const statuses = [...new Set(tasks.map((task) => task.status.status))];
67
31
  return { tasks, statuses };
68
32
  }
69
- /**
70
- * Retrieves a specific task by ID
71
- * @param taskId - ID of the task to fetch
72
- * @returns Promise resolving to task details
73
- */
74
33
  async getTask(taskId) {
75
34
  const response = await this.client.get(`/task/${taskId}`);
76
35
  return response.data;
77
36
  }
78
- /**
79
- * Creates a new task in a specified list
80
- * @param listId - ID of the list to create task in
81
- * @param data - Task creation data
82
- * @returns Promise resolving to created task
83
- */
84
37
  async createTask(listId, data) {
85
38
  const response = await this.client.post(`/list/${listId}/task`, data);
86
39
  return response.data;
87
40
  }
88
- /**
89
- * Creates multiple tasks in a list simultaneously
90
- * @param listId - ID of the list to create tasks in
91
- * @param data - Bulk task creation data
92
- * @returns Promise resolving to array of created tasks
93
- */
94
41
  async createBulkTasks(listId, data) {
95
42
  const tasks = await Promise.all(data.tasks.map(taskData => this.createTask(listId, taskData)));
96
43
  return tasks;
97
44
  }
98
- /**
99
- * Updates an existing task
100
- * @param taskId - ID of the task to update
101
- * @param data - Task update data
102
- * @returns Promise resolving to updated task
103
- */
104
45
  async updateTask(taskId, data) {
105
46
  const response = await this.client.put(`/task/${taskId}`, data);
106
47
  return response.data;
107
48
  }
108
- /**
109
- * Deletes a task
110
- * @param taskId - ID of the task to delete
111
- */
112
49
  async deleteTask(taskId) {
113
50
  await this.client.delete(`/task/${taskId}`);
114
51
  }
115
- // List Operations
116
- /**
117
- * Retrieves all lists in a space
118
- * @param spaceId - ID of the space to fetch lists from
119
- * @returns Promise resolving to array of lists
120
- */
52
+ // Lists
121
53
  async getLists(spaceId) {
122
54
  const response = await this.client.get(`/space/${spaceId}/list`);
123
55
  return response.data.lists;
124
56
  }
125
- /**
126
- * Retrieves all lists across all spaces in a team
127
- * @param teamId - ID of the team to fetch lists from
128
- * @returns Promise resolving to array of lists
129
- */
130
57
  async getAllLists(teamId) {
131
58
  const response = await this.client.get(`/team/${teamId}/list`);
132
59
  return response.data.lists;
133
60
  }
134
- /**
135
- * Retrieves a specific list by ID
136
- * @param listId - ID of the list to fetch
137
- * @returns Promise resolving to list details
138
- */
139
61
  async getList(listId) {
140
62
  const response = await this.client.get(`/list/${listId}`);
141
63
  return response.data;
142
64
  }
143
- // Space Operations
144
- /**
145
- * Retrieves all spaces in a team
146
- * @param teamId - ID of the team to fetch spaces from
147
- * @returns Promise resolving to array of spaces
148
- */
65
+ // Spaces
149
66
  async getSpaces(teamId) {
150
67
  const response = await this.client.get(`/team/${teamId}/space`);
151
68
  return response.data.spaces;
152
69
  }
153
- /**
154
- * Retrieves a specific space by ID
155
- * @param spaceId - ID of the space to fetch
156
- * @returns Promise resolving to space details
157
- */
158
70
  async getSpace(spaceId) {
159
71
  const response = await this.client.get(`/space/${spaceId}`);
160
72
  return response.data;
161
73
  }
162
- /**
163
- * Finds a space by name (case-insensitive)
164
- * @param teamId - ID of the team to search in
165
- * @param spaceName - Name of the space to find
166
- * @returns Promise resolving to space if found, null otherwise
167
- */
168
74
  async findSpaceByName(teamId, spaceName) {
169
75
  const spaces = await this.getSpaces(teamId);
170
76
  return spaces.find(space => space.name.toLowerCase() === spaceName.toLowerCase()) || null;
171
77
  }
172
- /**
173
- * Creates a new list in a space
174
- * @param spaceId - ID of the space to create list in
175
- * @param data - List creation data
176
- * @returns Promise resolving to created list
177
- */
178
78
  async createList(spaceId, data) {
179
79
  const response = await this.client.post(`/space/${spaceId}/list`, data);
180
80
  return response.data;
181
81
  }
182
- // Folder Operations
183
- /**
184
- * Retrieves all folders in a space
185
- * @param spaceId - ID of the space to fetch folders from
186
- * @returns Promise resolving to array of folders
187
- */
82
+ // Folders
188
83
  async getFolders(spaceId) {
189
84
  const response = await this.client.get(`/space/${spaceId}/folder`);
190
85
  return response.data.folders;
191
86
  }
192
- /**
193
- * Retrieves a specific folder by ID
194
- * @param folderId - ID of the folder to fetch
195
- * @returns Promise resolving to folder details
196
- */
197
87
  async getFolder(folderId) {
198
88
  const response = await this.client.get(`/folder/${folderId}`);
199
89
  return response.data;
200
90
  }
201
- /**
202
- * Creates a new folder in a space
203
- * @param spaceId - ID of the space to create folder in
204
- * @param data - Folder creation data
205
- * @returns Promise resolving to created folder
206
- */
207
91
  async createFolder(spaceId, data) {
208
92
  const response = await this.client.post(`/space/${spaceId}/folder`, data);
209
93
  return response.data;
210
94
  }
211
- /**
212
- * Deletes a folder
213
- * @param folderId - ID of the folder to delete
214
- */
215
95
  async deleteFolder(folderId) {
216
96
  await this.client.delete(`/folder/${folderId}`);
217
97
  }
218
- /**
219
- * Creates a new list within a folder
220
- * @param folderId - ID of the folder to create list in
221
- * @param data - List creation data
222
- * @returns Promise resolving to created list
223
- */
224
98
  async createListInFolder(folderId, data) {
225
99
  const response = await this.client.post(`/folder/${folderId}/list`, data);
226
100
  return response.data;
227
101
  }
228
- /**
229
- * Finds a folder by name within a space (case-insensitive)
230
- * @param spaceId - ID of the space to search in
231
- * @param folderName - Name of the folder to find
232
- * @returns Promise resolving to folder if found, null otherwise
233
- */
234
102
  async findFolderByName(spaceId, folderName) {
235
103
  const folders = await this.getFolders(spaceId);
236
104
  return folders.find(folder => folder.name.toLowerCase() === folderName.toLowerCase()) || null;
237
105
  }
238
- // Helper Methods
239
- /**
240
- * Moves a task to a different list
241
- * @param taskId - ID of the task to move
242
- * @param listId - ID of the destination list
243
- * @returns Promise resolving to updated task
244
- */
106
+ // Additional helper methods
245
107
  async moveTask(taskId, listId) {
246
108
  const response = await this.client.post(`/task/${taskId}`, {
247
109
  list: listId
248
110
  });
249
111
  return response.data;
250
112
  }
251
- /**
252
- * Finds a list by name across all spaces and folders (case-insensitive)
253
- * @param teamId - ID of the team to search in
254
- * @param listName - Name of the list to find
255
- * @returns Promise resolving to list details with context if found, null otherwise
256
- */
257
113
  async findListByNameGlobally(teamId, listName) {
258
114
  const spaces = await this.getSpaces(teamId);
259
115
  for (const space of spaces) {
@@ -280,12 +136,6 @@ export class ClickUpService {
280
136
  }
281
137
  return null;
282
138
  }
283
- /**
284
- * Finds a folder by name across all spaces (case-insensitive)
285
- * @param teamId - ID of the team to search in
286
- * @param folderName - Name of the folder to find
287
- * @returns Promise resolving to folder details with context if found, null otherwise
288
- */
289
139
  async findFolderByNameGlobally(teamId, folderName) {
290
140
  const spaces = await this.getSpaces(teamId);
291
141
  for (const space of spaces) {
@@ -297,31 +147,15 @@ export class ClickUpService {
297
147
  }
298
148
  return null;
299
149
  }
300
- /**
301
- * Creates a duplicate of a task in a specified list
302
- * @param taskId - ID of the task to duplicate
303
- * @param listId - ID of the destination list
304
- * @returns Promise resolving to duplicated task
305
- */
306
150
  async duplicateTask(taskId, listId) {
307
151
  const response = await this.client.post(`/task/${taskId}/duplicate`, {
308
152
  list: listId
309
153
  });
310
154
  return response.data;
311
155
  }
312
- /**
313
- * Deletes a list
314
- * @param listId - ID of the list to delete
315
- */
316
156
  async deleteList(listId) {
317
157
  await this.client.delete(`/list/${listId}`);
318
158
  }
319
- /**
320
- * Updates an existing list
321
- * @param listId - ID of the list to update
322
- * @param data - List update data
323
- * @returns Promise resolving to updated list
324
- */
325
159
  async updateList(listId, data) {
326
160
  const response = await this.client.put(`/list/${listId}`, data);
327
161
  return response.data;
@@ -1,8 +1 @@
1
- /**
2
- * Type definitions for ClickUp API entities and operations.
3
- * Defines interfaces for tasks, lists, spaces, folders and their associated
4
- * creation/update data structures. These types match the ClickUp API's
5
- * expected request/response formats.
6
- * @module types/clickup
7
- */
8
1
  export {};
@@ -1,18 +1,3 @@
1
- /**
2
- * Resolver module for ClickUp entity resolution.
3
- * Provides utilities to resolve IDs from names and fetch entities from the ClickUp API.
4
- * @module resolvers
5
- */
6
- import { logMessage } from '../utils/logger.js';
7
- /**
8
- * Resolves a ClickUp list ID from either a direct ID or list name
9
- * @param clickup - ClickUp service instance
10
- * @param teamId - Team ID to search within
11
- * @param listId - Optional direct list ID
12
- * @param listName - Optional list name to search for
13
- * @returns Promise resolving to the list ID
14
- * @throws Error if neither listId nor listName is provided, or if list is not found
15
- */
16
1
  export async function resolveListId(clickup, teamId, listId, listName) {
17
2
  if (listId)
18
3
  return listId;
@@ -25,15 +10,6 @@ export async function resolveListId(clickup, teamId, listId, listName) {
25
10
  }
26
11
  return result.list.id;
27
12
  }
28
- /**
29
- * Resolves a ClickUp space ID from either a direct ID or space name
30
- * @param clickup - ClickUp service instance
31
- * @param teamId - Team ID to search within
32
- * @param spaceId - Optional direct space ID
33
- * @param spaceName - Optional space name to search for
34
- * @returns Promise resolving to the space ID
35
- * @throws Error if neither spaceId nor spaceName is provided, or if space is not found
36
- */
37
13
  export async function resolveSpaceId(clickup, teamId, spaceId, spaceName) {
38
14
  if (spaceId)
39
15
  return spaceId;
@@ -46,15 +22,6 @@ export async function resolveSpaceId(clickup, teamId, spaceId, spaceName) {
46
22
  }
47
23
  return space.id;
48
24
  }
49
- /**
50
- * Resolves a ClickUp folder ID from either a direct ID or folder name
51
- * @param clickup - ClickUp service instance
52
- * @param teamId - Team ID to search within
53
- * @param folderId - Optional direct folder ID
54
- * @param folderName - Optional folder name to search for
55
- * @returns Promise resolving to the folder ID
56
- * @throws Error if neither folderId nor folderName is provided, or if folder is not found
57
- */
58
25
  export async function resolveFolderId(clickup, teamId, folderId, folderName) {
59
26
  if (folderId)
60
27
  return folderId;
@@ -67,12 +34,6 @@ export async function resolveFolderId(clickup, teamId, folderId, folderName) {
67
34
  }
68
35
  return result.folder.id;
69
36
  }
70
- /**
71
- * Retrieves all tasks across all spaces in a team
72
- * @param clickup - ClickUp service instance
73
- * @param teamId - Team ID to fetch tasks for
74
- * @returns Promise resolving to an object containing all tasks and spaces
75
- */
76
37
  export async function getAllTasks(clickup, teamId) {
77
38
  const spaces = await clickup.getSpaces(teamId);
78
39
  const spacePromises = spaces.map(async (space) => {
@@ -85,14 +46,3 @@ export async function getAllTasks(clickup, teamId) {
85
46
  const allTasks = tasksPerSpace.flat();
86
47
  return { tasks: allTasks, spaces };
87
48
  }
88
- /**
89
- * Generic error handler that formats and logs errors before re-throwing
90
- * @param context - Context where the error occurred
91
- * @param error - The error to handle
92
- * @throws The original error after logging
93
- */
94
- export function handleError(context, error) {
95
- const errorMessage = error instanceof Error ? error.message : String(error);
96
- logMessage(`Error in ${context}`, { error: errorMessage });
97
- throw error;
98
- }
package/package.json CHANGED
@@ -1,11 +1,12 @@
1
1
  {
2
2
  "name": "@taazkareem/clickup-mcp-server",
3
- "version": "0.4.41",
3
+ "version": "0.4.42",
4
4
  "description": "ClickUp MCP Server - Integrate ClickUp tasks with AI through Model Context Protocol",
5
5
  "type": "module",
6
6
  "main": "./build/index.js",
7
7
  "bin": {
8
- "clickup-mcp-server": "build/index.js"
8
+ "clickup-mcp-server": "build/index.js",
9
+ "@taazkareem/clickup-mcp-server": "build/index.js"
9
10
  },
10
11
  "files": [
11
12
  "build",
@@ -42,7 +43,7 @@
42
43
  },
43
44
  "homepage": "https://github.com/taazkareem/clickup-mcp-server#readme",
44
45
  "dependencies": {
45
- "@modelcontextprotocol/sdk": "^1.0.1",
46
+ "@modelcontextprotocol/sdk": "^1.4.1",
46
47
  "@modelcontextprotocol/server-github": "^2025.1.23",
47
48
  "axios": "^1.6.7",
48
49
  "dotenv": "^16.4.1",
@@ -1,47 +0,0 @@
1
- /**
2
- * Logger module for MCP server that provides structured logging capabilities.
3
- * All logs are formatted as JSON-RPC 2.0 messages and written to stderr.
4
- * @module logger
5
- */
6
- /**
7
- * Creates a JSON-RPC 2.0 formatted log message
8
- * @param method - The logging method/category (e.g., 'server.error', 'mcp.info')
9
- * @param params - Additional data to include in the log message
10
- */
11
- export function logMessage(method, params) {
12
- const jsonRpcMessage = {
13
- jsonrpc: "2.0",
14
- method,
15
- params,
16
- id: Date.now() // Using timestamp as a unique ID
17
- };
18
- console.error(JSON.stringify(jsonRpcMessage)); // Use stderr for logging
19
- }
20
- /**
21
- * Logs an error with optional stack trace
22
- * @param context - The context where the error occurred (e.g., 'server', 'mcp')
23
- * @param error - The error object or error message
24
- */
25
- export function logError(context, error) {
26
- const errorMessage = error instanceof Error ? error.message : String(error);
27
- logMessage(`${context}.error`, { error: errorMessage });
28
- if (error instanceof Error && error.stack) {
29
- logMessage(`${context}.stack`, { stack: error.stack });
30
- }
31
- }
32
- /**
33
- * Logs debug information
34
- * @param context - The context of the debug message
35
- * @param data - Debug data to log
36
- */
37
- export function logDebug(context, data) {
38
- logMessage(`${context}.debug`, { data });
39
- }
40
- /**
41
- * Logs informational messages
42
- * @param context - The context of the info message
43
- * @param data - Information to log
44
- */
45
- export function logInfo(context, data) {
46
- logMessage(`${context}.info`, { data });
47
- }