@alanse/clickup-multi-mcp-server 1.0.1 → 1.0.2

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 CHANGED
@@ -178,17 +178,77 @@ await getTasks({ workspace: "personal", list_id: "987654321" });
178
178
  await getWorkspaceHierarchy({ workspace: "work" });
179
179
  ```
180
180
 
181
+ ## Local Development Setup
182
+
183
+ For local development or when running the server directly from source code:
184
+
185
+ ### 1. Clone and Install
186
+
187
+ ```bash
188
+ git clone https://github.com/da-okazaki/clickup-multi-mcp-server.git
189
+ cd clickup-multi-mcp-server
190
+ npm install
191
+ ```
192
+
193
+ ### 2. Configure Environment Variables
194
+
195
+ Copy `.env.example` to `.env` and configure your ClickUp credentials:
196
+
197
+ ```bash
198
+ cp .env.example .env
199
+ ```
200
+
201
+ Edit `.env` file:
202
+
203
+ ```bash
204
+ # Multi-workspace configuration
205
+ CLICKUP_WORKSPACES={"default":"alanse","workspaces":{"alanse":{"token":"pk_YOUR_TOKEN_1","teamId":"YOUR_TEAM_ID_1","description":"Alanse workspace"},"potz":{"token":"pk_YOUR_TOKEN_2","teamId":"YOUR_TEAM_ID_2","description":"Potz workspace"}}}
206
+
207
+ # Or use single workspace (legacy)
208
+ # CLICKUP_API_KEY=your_api_key_here
209
+ # CLICKUP_TEAM_ID=your_team_id_here
210
+ ```
211
+
212
+ **Note**: The `.env` file is automatically loaded when the server starts. Environment variables in `.env` are automatically picked up without needing to pass them via command line.
213
+
214
+ ### 3. Build and Run
215
+
216
+ ```bash
217
+ # Build the project
218
+ npm run build
219
+
220
+ # Run locally
221
+ node build/index.js
222
+ ```
223
+
224
+ ### 4. Configure Claude Code for Local Development
225
+
226
+ If you want to use your local build with Claude Code, update `~/.claude.json`:
227
+
228
+ ```json
229
+ {
230
+ "mcpServers": {
231
+ "clickup": {
232
+ "type": "stdio",
233
+ "command": "node",
234
+ "args": ["/absolute/path/to/clickup-multi-mcp-server/build/index.js"]
235
+ }
236
+ }
237
+ }
238
+ ```
239
+
240
+ **Important**: When using local build with Claude Code:
241
+ - Environment variables are loaded from `.env` file in the project root
242
+ - No need to specify `env` in `~/.claude.json` (unless you want to override `.env` values)
243
+ - Rebuild after making changes: `npm run build`
244
+
181
245
  ## Smithery Installation (Quick Start)
182
246
 
183
247
  [![smithery badge](https://smithery.ai/badge/@taazkareem/clickup-mcp-server)](https://smithery.ai/server/@TaazKareem/clickup-mcp-server)
184
248
 
185
249
  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.
186
250
 
187
- ## NPX Installation
188
251
 
189
- [![NPM Version](https://img.shields.io/npm/v/@taazkareem/clickup-mcp-server.svg?style=flat&logo=npm)](https://www.npmjs.com/package/@taazkareem/clickup-mcp-server)
190
- [![Dependency Status](https://img.shields.io/badge/dependencies-up%20to%20date-brightgreen)](https://github.com/TaazKareem/clickup-mcp-server/blob/main/package.json)
191
- [![NPM Downloads](https://img.shields.io/npm/dm/@taazkareem/clickup-mcp-server.svg?style=flat&logo=npm)](https://npmcharts.com/compare/@taazkareem/clickup-mcp-server?minimal=true)
192
252
 
193
253
  Add this entry to your client's MCP settings JSON file:
194
254
 
@@ -368,11 +428,12 @@ npm run sse-client
368
428
  | ⚡ **Integration Features** | 🏗️ **Architecture & Performance** |
369
429
  | • 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 | • **70% codebase reduction** for improved performance<br>• **Unified architecture** across all transport types<br>• **Zero code duplication**<br>• **HTTP Streamable transport** (MCP Inspector compatible)<br>• **Legacy SSE support** for backwards compatibility |
370
430
 
371
- ## Available Tools (36 Total)
431
+ ## Available Tools (42 Total)
372
432
 
373
433
  | Tool | Description | Required Parameters |
374
434
  | ------------------------------------------------------------------ | ------------------------------- | ---------------------------------------------------------------------------------------------------------------------------- |
375
435
  | [get_workspace_hierarchy](docs/user-guide.md#workspace-navigation) | Get workspace structure | None |
436
+ | [get_available_workspaces](docs/user-guide.md#workspace-navigation) | Get all available workspaces | None |
376
437
  | [create_task](docs/user-guide.md#task-management) | Create a task | `name`, (`listId`/`listName`) |
377
438
  | [create_bulk_tasks](docs/user-guide.md#task-management) | Create multiple tasks | `tasks[]` |
378
439
  | [update_task](docs/user-guide.md#task-management) | Modify task | `taskId`/`taskName` |
@@ -388,6 +449,11 @@ npm run sse-client
388
449
  | [move_task](docs/user-guide.md#task-management) | Move task | `taskId`/`taskName`, `listId`/`listName` |
389
450
  | [move_bulk_tasks](docs/user-guide.md#task-management) | Move multiple tasks | `tasks[]` with IDs or names, target list |
390
451
  | [duplicate_task](docs/user-guide.md#task-management) | Copy task | `taskId`/`taskName`, `listId`/`listName` |
452
+ | [merge_task](docs/user-guide.md#task-management) | Merge two tasks | `taskId`, `mergeFromId` |
453
+ | [get_task_time_in_status](docs/user-guide.md#task-management) | Get time in status for a task | `taskId`/`taskName` |
454
+ | [get_bulk_tasks_time_in_status](docs/user-guide.md#task-management)| Get bulk time in status | `taskIds[]` |
455
+ | [add_task_to_list](docs/user-guide.md#list-management) | Add task to additional list | `listId`, `taskId` |
456
+ | [remove_task_from_list](docs/user-guide.md#list-management) | Remove task from list | `listId`, `taskId` |
391
457
  | [create_list](docs/user-guide.md#list-management) | Create list in space | `name`, `spaceId`/`spaceName` |
392
458
  | [create_folder](docs/user-guide.md#folder-management) | Create folder | `name`, `spaceId`/`spaceName` |
393
459
  | [create_list_in_folder](docs/user-guide.md#list-management) | Create list in folder | `name`, `folderId`/`folderName` |
@@ -469,35 +535,9 @@ The server provides clear error messages for:
469
535
  The `LOG_LEVEL` environment variable can be specified to control the verbosity of server logs. Valid values are `trace`, `debug`, `info`, `warn`, and `error` (default).
470
536
  This can be also be specified on the command line as, e.g. `--env LOG_LEVEL=info`.
471
537
 
472
- ## Support the Developer
473
-
474
- When using this server, you may occasionally see a small sponsor message with a link to this repository included in tool responses. I hope you can support the project!
475
- If you find this project useful, please consider supporting:
476
-
477
- [![Sponsor TaazKareem](https://img.shields.io/badge/Sponsor-TaazKareem-orange?logo=github)](https://github.com/sponsors/TaazKareem)
478
-
479
- <a href="https://buymeacoffee.com/taazkareem">
480
- <img src="https://cdn.buymeacoffee.com/buttons/v2/default-yellow.png" width="200" alt="Buy Me A Coffee">
481
- </a>
482
-
483
- ## Acknowledgements
484
-
485
- Special thanks to [ClickUp](https://clickup.com) for their excellent API and services that make this integration possible.
486
-
487
- ## Contributing
488
-
489
- Contributions are welcome! Please read our [Contributing Guide](CONTRIBUTING.md) for details.
490
538
 
491
539
  ## License
492
540
 
493
541
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
494
542
 
495
543
  This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
496
-
497
- ## Disclaimer
498
-
499
- This software makes use of third-party APIs and may reference trademarks
500
- or brands owned by third parties. The use of such APIs or references does not imply
501
- any affiliation with or endorsement by the respective companies. All trademarks and
502
- brand names are the property of their respective owners. This project is an independent
503
- work and is not officially associated with or sponsored by any third-party company mentioned.
package/build/config.js CHANGED
@@ -21,6 +21,9 @@
21
21
  * - SSE_PORT: Port for SSE server (default: 3000)
22
22
  * - ENABLE_STDIO: Enable STDIO transport (default: true)
23
23
  */
24
+ // Load environment variables from .env file
25
+ import dotenv from 'dotenv';
26
+ dotenv.config();
24
27
  // Parse any command line environment arguments
25
28
  const args = process.argv.slice(2);
26
29
  const envArgs = {};
package/build/server.js CHANGED
@@ -7,8 +7,8 @@
7
7
  import { Server } from "@modelcontextprotocol/sdk/server/index.js";
8
8
  import { CallToolRequestSchema, ListToolsRequestSchema, ListPromptsRequestSchema, GetPromptRequestSchema, ListResourcesRequestSchema } from "@modelcontextprotocol/sdk/types.js";
9
9
  import config from "./config.js";
10
- import { workspaceHierarchyTool, handleGetWorkspaceHierarchy } from "./tools/workspace.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";
10
+ import { workspaceHierarchyTool, handleGetWorkspaceHierarchy, availableWorkspacesTool, handleGetAvailableWorkspaces } from "./tools/workspace.js";
11
+ import { createTaskTool, updateTaskTool, moveTaskTool, duplicateTaskTool, getTaskTool, deleteTaskTool, getTaskCommentsTool, createTaskCommentTool, createBulkTasksTool, updateBulkTasksTool, moveBulkTasksTool, deleteBulkTasksTool, attachTaskFileTool, getWorkspaceTasksTool, getTaskTimeEntriesTool, startTimeTrackingTool, stopTimeTrackingTool, addTimeEntryTool, deleteTimeEntryTool, getCurrentTimeEntryTool, mergeTaskTool, getTimeInStatusTool, getBulkTimeInStatusTool, addTaskToListTool, removeTaskFromListTool, handleCreateTask, handleUpdateTask, handleMoveTask, handleDuplicateTask, handleDeleteTask, handleGetTaskComments, handleCreateTaskComment, handleCreateBulkTasks, handleUpdateBulkTasks, handleMoveBulkTasks, handleDeleteBulkTasks, handleGetTask, handleAttachTaskFile, handleGetWorkspaceTasks, handleGetTaskTimeEntries, handleStartTimeTracking, handleStopTimeTracking, handleAddTimeEntry, handleDeleteTimeEntry, handleGetCurrentTimeEntry, mergeTaskHandler, getTimeInStatusHandler, getBulkTimeInStatusHandler, addTaskToListHandler, removeTaskFromListHandler } from "./tools/task/index.js";
12
12
  import { createListTool, handleCreateList, createListInFolderTool, handleCreateListInFolder, getListTool, handleGetList, updateListTool, handleUpdateList, deleteListTool, handleDeleteList } from "./tools/list.js";
13
13
  import { createFolderTool, handleCreateFolder, getFolderTool, handleGetFolder, updateFolderTool, handleUpdateFolder, deleteFolderTool, handleDeleteFolder } from "./tools/folder.js";
14
14
  import { getSpaceTagsTool, handleGetSpaceTags, addTagToTaskTool, handleAddTagToTask, removeTagFromTaskTool, handleRemoveTagFromTask } from "./tools/tag.js";
@@ -81,6 +81,7 @@ export function configureServer() {
81
81
  // Collect all tool definitions
82
82
  const allTools = [
83
83
  workspaceHierarchyTool,
84
+ availableWorkspacesTool,
84
85
  createTaskTool,
85
86
  getTaskTool,
86
87
  updateTaskTool,
@@ -101,6 +102,11 @@ export function configureServer() {
101
102
  addTimeEntryTool,
102
103
  deleteTimeEntryTool,
103
104
  getCurrentTimeEntryTool,
105
+ mergeTaskTool,
106
+ getTimeInStatusTool,
107
+ getBulkTimeInStatusTool,
108
+ addTaskToListTool,
109
+ removeTaskFromListTool,
104
110
  createListTool,
105
111
  createListInFolderTool,
106
112
  getListTool,
@@ -156,7 +162,9 @@ export function configureServer() {
156
162
  // Handle tool calls by routing to the appropriate handler
157
163
  switch (name) {
158
164
  case "get_workspace_hierarchy":
159
- return handleGetWorkspaceHierarchy();
165
+ return handleGetWorkspaceHierarchy(params);
166
+ case "get_available_workspaces":
167
+ return handleGetAvailableWorkspaces();
160
168
  case "create_task":
161
169
  return handleCreateTask(params);
162
170
  case "update_task":
@@ -221,6 +229,16 @@ export function configureServer() {
221
229
  return handleDeleteTimeEntry(params);
222
230
  case "get_current_time_entry":
223
231
  return handleGetCurrentTimeEntry(params);
232
+ case "merge_task":
233
+ return mergeTaskHandler(params);
234
+ case "get_task_time_in_status":
235
+ return getTimeInStatusHandler(params);
236
+ case "get_bulk_tasks_time_in_status":
237
+ return getBulkTimeInStatusHandler(params);
238
+ case "add_task_to_list":
239
+ return addTaskToListHandler(params);
240
+ case "remove_task_from_list":
241
+ return removeTaskFromListHandler(params);
224
242
  case "create_document":
225
243
  return handleCreateDocument(params);
226
244
  case "get_document":
@@ -0,0 +1 @@
1
+ Logging initialized to /Users/daichiokazaki/Documents/project/alanse/clickup-multi-mcp-server/build/server.log
@@ -601,4 +601,103 @@ export class TaskServiceCore extends BaseClickUpService {
601
601
  });
602
602
  this.logger.debug('Cached task name to ID mapping', { taskName, taskId, listId });
603
603
  }
604
+ /**
605
+ * Merge two tasks together
606
+ * @param taskId ID of the task to merge into (destination)
607
+ * @param data Merge configuration including merge_from_id
608
+ * @returns The merged task
609
+ */
610
+ async mergeTask(taskId, data) {
611
+ try {
612
+ this.logOperation('mergeTask', { taskId, ...data });
613
+ const path = `/task/${taskId}/merge`;
614
+ this.traceRequest('POST', path, data);
615
+ return await this.makeRequest(async () => {
616
+ const response = await this.client.post(path, data);
617
+ return response.data;
618
+ });
619
+ }
620
+ catch (error) {
621
+ throw this.handleError(error, `Failed to merge task ${taskId}`);
622
+ }
623
+ }
624
+ /**
625
+ * Get time in status for a task
626
+ * @param taskId ID of the task
627
+ * @returns Time in status data
628
+ */
629
+ async getTimeInStatus(taskId) {
630
+ try {
631
+ this.logOperation('getTimeInStatus', { taskId });
632
+ const path = `/task/${taskId}/time_in_status`;
633
+ this.traceRequest('GET', path);
634
+ return await this.makeRequest(async () => {
635
+ const response = await this.client.get(path);
636
+ return response.data;
637
+ });
638
+ }
639
+ catch (error) {
640
+ throw this.handleError(error, `Failed to get time in status for task ${taskId}`);
641
+ }
642
+ }
643
+ /**
644
+ * Get time in status for multiple tasks
645
+ * @param taskIds Array of task IDs
646
+ * @returns Bulk time in status data
647
+ */
648
+ async getBulkTimeInStatus(taskIds) {
649
+ try {
650
+ this.logOperation('getBulkTimeInStatus', { taskIds, count: taskIds.length });
651
+ // Convert array to query string: ?task_ids=id1&task_ids=id2
652
+ const queryParams = taskIds.map(id => `task_ids=${id}`).join('&');
653
+ const path = `/task/bulk_time_in_status/task_ids?${queryParams}`;
654
+ this.traceRequest('GET', path);
655
+ return await this.makeRequest(async () => {
656
+ const response = await this.client.get(path);
657
+ return response.data;
658
+ });
659
+ }
660
+ catch (error) {
661
+ throw this.handleError(error, `Failed to get bulk time in status`);
662
+ }
663
+ }
664
+ /**
665
+ * Add a task to an additional list (multiple list membership)
666
+ * @param listId ID of the list to add the task to
667
+ * @param taskId ID of the task to add
668
+ * @returns The updated task
669
+ */
670
+ async addTaskToList(listId, taskId) {
671
+ try {
672
+ this.logOperation('addTaskToList', { listId, taskId });
673
+ const path = `/list/${listId}/task/${taskId}`;
674
+ this.traceRequest('POST', path);
675
+ return await this.makeRequest(async () => {
676
+ const response = await this.client.post(path);
677
+ return response.data;
678
+ });
679
+ }
680
+ catch (error) {
681
+ throw this.handleError(error, `Failed to add task ${taskId} to list ${listId}`);
682
+ }
683
+ }
684
+ /**
685
+ * Remove a task from a list
686
+ * @param listId ID of the list to remove the task from
687
+ * @param taskId ID of the task to remove
688
+ * @returns Success indicator
689
+ */
690
+ async removeTaskFromList(listId, taskId) {
691
+ try {
692
+ this.logOperation('removeTaskFromList', { listId, taskId });
693
+ const path = `/list/${listId}/task/${taskId}`;
694
+ this.traceRequest('DELETE', path);
695
+ await this.makeRequest(async () => {
696
+ await this.client.delete(path);
697
+ });
698
+ }
699
+ catch (error) {
700
+ throw this.handleError(error, `Failed to remove task ${taskId} from list ${listId}`);
701
+ }
702
+ }
604
703
  }
@@ -21,12 +21,15 @@ const workspaceServicesMap = new Map();
21
21
  */
22
22
  export function getClickUpServices(workspaceId) {
23
23
  const wsId = workspaceId || getDefaultWorkspace();
24
+ console.error('[DEBUG] getClickUpServices called with workspaceId:', workspaceId);
25
+ console.error('[DEBUG] Resolved workspace ID:', wsId);
24
26
  // Check if services already exist for this workspace
25
27
  let services = workspaceServicesMap.get(wsId);
26
28
  if (!services) {
27
29
  logger.info(`Creating ClickUp services for workspace: ${wsId}`);
28
30
  // Get workspace configuration
29
31
  const workspaceConfig = getWorkspaceConfig(wsId);
32
+ console.error('[DEBUG] Workspace config:', JSON.stringify(workspaceConfig));
30
33
  // Create the services instance
31
34
  services = createClickUpServices({
32
35
  apiKey: workspaceConfig.token,
@@ -15,6 +15,7 @@ import { validateTaskIdentification, validateListIdentification, validateTaskUpd
15
15
  import { handleResolveAssignees } from '../member.js';
16
16
  import { isNameMatch } from '../../utils/resolver-utils.js';
17
17
  import { Logger } from '../../logger.js';
18
+ import { sponsorService } from '../../utils/sponsor-service.js';
18
19
  // Use default workspace services for backwards compatibility
19
20
  const defaultServices = getClickUpServices();
20
21
  const { task: taskService, list: listService, workspace: workspaceService } = defaultServices;
@@ -917,3 +918,88 @@ export async function deleteTaskHandler(params) {
917
918
  await taskService.deleteTask(taskId);
918
919
  return true;
919
920
  }
921
+ /**
922
+ * Handler for merging two tasks
923
+ */
924
+ export async function mergeTaskHandler(params) {
925
+ try {
926
+ const { taskId, mergeFromId } = params;
927
+ if (!taskId || !mergeFromId) {
928
+ throw new Error('taskId and mergeFromId are required');
929
+ }
930
+ const result = await taskService.mergeTask(taskId, {
931
+ merge_from_id: mergeFromId
932
+ });
933
+ return sponsorService.createResponse(result, true);
934
+ }
935
+ catch (error) {
936
+ return sponsorService.createErrorResponse(error, params);
937
+ }
938
+ }
939
+ /**
940
+ * Handler for getting time in status for a task
941
+ */
942
+ export async function getTimeInStatusHandler(params) {
943
+ try {
944
+ const { taskId } = params;
945
+ if (!taskId) {
946
+ throw new Error('taskId is required');
947
+ }
948
+ const result = await taskService.getTimeInStatus(taskId);
949
+ return sponsorService.createResponse(result, true);
950
+ }
951
+ catch (error) {
952
+ return sponsorService.createErrorResponse(error, params);
953
+ }
954
+ }
955
+ /**
956
+ * Handler for getting time in status for multiple tasks
957
+ */
958
+ export async function getBulkTimeInStatusHandler(params) {
959
+ try {
960
+ const { taskIds } = params;
961
+ if (!taskIds || !Array.isArray(taskIds) || taskIds.length === 0) {
962
+ throw new Error('taskIds array is required and must not be empty');
963
+ }
964
+ const result = await taskService.getBulkTimeInStatus(taskIds);
965
+ return sponsorService.createResponse(result, true);
966
+ }
967
+ catch (error) {
968
+ return sponsorService.createErrorResponse(error, params);
969
+ }
970
+ }
971
+ /**
972
+ * Handler for adding a task to an additional list
973
+ */
974
+ export async function addTaskToListHandler(params) {
975
+ try {
976
+ const { listId, taskId } = params;
977
+ if (!listId || !taskId) {
978
+ throw new Error('listId and taskId are required');
979
+ }
980
+ const result = await taskService.addTaskToList(listId, taskId);
981
+ return sponsorService.createResponse(result, true);
982
+ }
983
+ catch (error) {
984
+ return sponsorService.createErrorResponse(error, params);
985
+ }
986
+ }
987
+ /**
988
+ * Handler for removing a task from a list
989
+ */
990
+ export async function removeTaskFromListHandler(params) {
991
+ try {
992
+ const { listId, taskId } = params;
993
+ if (!listId || !taskId) {
994
+ throw new Error('listId and taskId are required');
995
+ }
996
+ await taskService.removeTaskFromList(listId, taskId);
997
+ return sponsorService.createResponse({
998
+ success: true,
999
+ message: `Task ${taskId} removed from list ${listId}`
1000
+ }, true);
1001
+ }
1002
+ catch (error) {
1003
+ return sponsorService.createErrorResponse(error, params);
1004
+ }
1005
+ }
@@ -9,7 +9,7 @@
9
9
  // Re-export from main module
10
10
  export * from './main.js';
11
11
  // Re-export single task operation tools
12
- export { createTaskTool, getTaskTool, getTasksTool, updateTaskTool, moveTaskTool, duplicateTaskTool, deleteTaskTool, getTaskCommentsTool, createTaskCommentTool } from './single-operations.js';
12
+ export { createTaskTool, getTaskTool, getTasksTool, updateTaskTool, moveTaskTool, duplicateTaskTool, deleteTaskTool, getTaskCommentsTool, createTaskCommentTool, mergeTaskTool, getTimeInStatusTool, getBulkTimeInStatusTool, addTaskToListTool, removeTaskFromListTool } from './single-operations.js';
13
13
  // Re-export bulk task operation tools
14
14
  export { createBulkTasksTool, updateBulkTasksTool, moveBulkTasksTool, deleteBulkTasksTool } from './bulk-operations.js';
15
15
  // Re-export workspace task operation tools
@@ -21,7 +21,7 @@ export { attachTaskFileTool, handleAttachTaskFile } from './attachments.js';
21
21
  // Re-export handlers
22
22
  export {
23
23
  // Single task operation handlers
24
- createTaskHandler, getTaskHandler, getTasksHandler, updateTaskHandler, moveTaskHandler, duplicateTaskHandler, deleteTaskHandler, getTaskCommentsHandler, createTaskCommentHandler,
24
+ createTaskHandler, getTaskHandler, getTasksHandler, updateTaskHandler, moveTaskHandler, duplicateTaskHandler, deleteTaskHandler, getTaskCommentsHandler, createTaskCommentHandler, mergeTaskHandler, getTimeInStatusHandler, getBulkTimeInStatusHandler, addTaskToListHandler, removeTaskFromListHandler,
25
25
  // Bulk task operation handlers
26
26
  createBulkTasksHandler, updateBulkTasksHandler, moveBulkTasksHandler, deleteBulkTasksHandler,
27
27
  // Team task operation handlers
@@ -467,3 +467,142 @@ export const deleteTaskTool = {
467
467
  }
468
468
  }
469
469
  };
470
+ /**
471
+ * Tool definition for merging two tasks
472
+ */
473
+ export const mergeTaskTool = {
474
+ name: "merge_task",
475
+ description: `Merges two ClickUp tasks together. The merge_from task will be merged into the destination task.
476
+
477
+ Example usage:
478
+ {
479
+ "taskId": "abc123", // Destination task ID (merge into this)
480
+ "mergeFromId": "xyz789" // Source task ID (merge from this)
481
+ }`,
482
+ inputSchema: {
483
+ type: "object",
484
+ properties: {
485
+ taskId: {
486
+ type: "string",
487
+ description: "REQUIRED: ID of the destination task (merge into this task)"
488
+ },
489
+ mergeFromId: {
490
+ type: "string",
491
+ description: "REQUIRED: ID of the source task (merge from this task)"
492
+ }
493
+ },
494
+ required: ["taskId", "mergeFromId"]
495
+ }
496
+ };
497
+ /**
498
+ * Tool definition for getting time in status for a task
499
+ */
500
+ export const getTimeInStatusTool = {
501
+ name: "get_task_time_in_status",
502
+ description: `Gets the time tracking information for how long a task has been in each status.
503
+
504
+ Example usage:
505
+ {
506
+ "taskId": "abc123"
507
+ }
508
+
509
+ Returns:
510
+ - current_status: Current status with time spent
511
+ - status_history: Array of all status changes with timestamps`,
512
+ inputSchema: {
513
+ type: "object",
514
+ properties: {
515
+ taskId: {
516
+ type: "string",
517
+ description: "REQUIRED: ID of the task to get time in status for"
518
+ }
519
+ },
520
+ required: ["taskId"]
521
+ }
522
+ };
523
+ /**
524
+ * Tool definition for getting time in status for multiple tasks
525
+ */
526
+ export const getBulkTimeInStatusTool = {
527
+ name: "get_bulk_tasks_time_in_status",
528
+ description: `Gets the time in status information for multiple tasks at once. More efficient than calling get_task_time_in_status multiple times.
529
+
530
+ Example usage:
531
+ {
532
+ "taskIds": ["abc123", "xyz789", "def456"]
533
+ }
534
+
535
+ Returns an object with task IDs as keys and their time in status data as values.`,
536
+ inputSchema: {
537
+ type: "object",
538
+ properties: {
539
+ taskIds: {
540
+ type: "array",
541
+ description: "REQUIRED: Array of task IDs to get time in status for",
542
+ items: {
543
+ type: "string"
544
+ },
545
+ minItems: 1
546
+ }
547
+ },
548
+ required: ["taskIds"]
549
+ }
550
+ };
551
+ /**
552
+ * Tool definition for adding a task to an additional list
553
+ */
554
+ export const addTaskToListTool = {
555
+ name: "add_task_to_list",
556
+ description: `Adds a task to an additional list, enabling multiple list membership for the task.
557
+
558
+ Example usage:
559
+ {
560
+ "listId": "12345",
561
+ "taskId": "abc123"
562
+ }
563
+
564
+ Note: This does NOT move the task. The task will belong to multiple lists after this operation.`,
565
+ inputSchema: {
566
+ type: "object",
567
+ properties: {
568
+ listId: {
569
+ type: "string",
570
+ description: "REQUIRED: ID of the list to add the task to"
571
+ },
572
+ taskId: {
573
+ type: "string",
574
+ description: "REQUIRED: ID of the task to add to the list"
575
+ }
576
+ },
577
+ required: ["listId", "taskId"]
578
+ }
579
+ };
580
+ /**
581
+ * Tool definition for removing a task from a list
582
+ */
583
+ export const removeTaskFromListTool = {
584
+ name: "remove_task_from_list",
585
+ description: `Removes a task from a specific list. If the task belongs to multiple lists, it will only be removed from the specified list.
586
+
587
+ Example usage:
588
+ {
589
+ "listId": "12345",
590
+ "taskId": "abc123"
591
+ }
592
+
593
+ Warning: If this is the task's only list, the task may be deleted or moved to an archive depending on ClickUp settings.`,
594
+ inputSchema: {
595
+ type: "object",
596
+ properties: {
597
+ listId: {
598
+ type: "string",
599
+ description: "REQUIRED: ID of the list to remove the task from"
600
+ },
601
+ taskId: {
602
+ type: "string",
603
+ description: "REQUIRED: ID of the task to remove from the list"
604
+ }
605
+ },
606
+ required: ["listId", "taskId"]
607
+ }
608
+ };
@@ -25,7 +25,10 @@ export const workspaceParameter = {
25
25
  */
26
26
  export function getServicesForWorkspace(params) {
27
27
  try {
28
- return getClickUpServices(params.workspace);
28
+ console.error('[DEBUG] getServicesForWorkspace called with workspace:', params.workspace);
29
+ const services = getClickUpServices(params.workspace);
30
+ console.error('[DEBUG] Retrieved services for workspace:', params.workspace || 'default');
31
+ return services;
29
32
  }
30
33
  catch (error) {
31
34
  // Enhance error message with available workspaces
@@ -30,6 +30,9 @@ export const workspaceHierarchyTool = {
30
30
  */
31
31
  export async function handleGetWorkspaceHierarchy(params = {}) {
32
32
  try {
33
+ // DEBUG: Log received parameters
34
+ console.error('[DEBUG] handleGetWorkspaceHierarchy called with params:', JSON.stringify(params));
35
+ console.error('[DEBUG] params.workspace =', params.workspace);
33
36
  // Get services for the specified workspace
34
37
  const services = getServicesForWorkspace(params);
35
38
  const { workspace: workspaceService } = services;
@@ -44,6 +47,49 @@ export async function handleGetWorkspaceHierarchy(params = {}) {
44
47
  return sponsorService.createErrorResponse(`Error getting workspace hierarchy: ${error.message}`);
45
48
  }
46
49
  }
50
+ /**
51
+ * Tool definition for retrieving available workspaces
52
+ */
53
+ export const availableWorkspacesTool = {
54
+ name: 'get_available_workspaces',
55
+ description: `Gets list of all available workspaces configured in the ClickUp MCP server. Returns workspace identifiers, default workspace, and descriptions if available.`,
56
+ inputSchema: {
57
+ type: 'object',
58
+ properties: {}
59
+ }
60
+ };
61
+ /**
62
+ * Handler for the get_available_workspaces tool
63
+ */
64
+ export async function handleGetAvailableWorkspaces() {
65
+ try {
66
+ // Import config functions
67
+ const { getAvailableWorkspaces, getDefaultWorkspace, getWorkspaceConfig } = await import('../config.js');
68
+ // Get list of available workspace identifiers
69
+ const workspaceIds = getAvailableWorkspaces();
70
+ const defaultWorkspace = getDefaultWorkspace();
71
+ // Get detailed information for each workspace
72
+ const workspaces = workspaceIds.map(id => {
73
+ const config = getWorkspaceConfig(id);
74
+ return {
75
+ id,
76
+ teamId: config.teamId,
77
+ description: config.description || (id === 'default' ? 'Default workspace' : undefined),
78
+ isDefault: id === defaultWorkspace
79
+ };
80
+ });
81
+ const result = {
82
+ default: defaultWorkspace,
83
+ workspaces,
84
+ count: workspaces.length
85
+ };
86
+ // Use sponsor service to create the response
87
+ return sponsorService.createResponse(result, true);
88
+ }
89
+ catch (error) {
90
+ return sponsorService.createErrorResponse(`Error getting available workspaces: ${error.message}`);
91
+ }
92
+ }
47
93
  /**
48
94
  * Format the hierarchy as a tree string
49
95
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alanse/clickup-multi-mcp-server",
3
- "version": "1.0.1",
3
+ "version": "1.0.2",
4
4
  "description": "ClickUp MCP Server with Multi-Workspace Support - Integrate multiple ClickUp workspaces with AI through Model Context Protocol",
5
5
  "type": "module",
6
6
  "main": "build/index.js",