@taazkareem/clickup-mcp-server 0.5.1 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,18 +1,12 @@
1
- <img src="https://clickup.com/assets/brand/logo-v3-clickup-dark.svg" alt="ClickUp" height="40" style="vertical-align: middle; margin-top: -4px;">
2
-
3
- # MCP Server
1
+ <img src="assets/images/clickup_mcp_server_social_image.png" alt="ClickUp MCP Server" width="100%">
4
2
 
3
+ ![Total Supporters](https://img.shields.io/badge/šŸ†%20Total%20Supporters-2-gold)
5
4
  [![GitHub Stars](https://img.shields.io/github/stars/TaazKareem/clickup-mcp-server?style=flat&logo=github)](https://github.com/TaazKareem/clickup-mcp-server/stargazers)
6
- [![Maintenance](https://img.shields.io/badge/Maintained%3F-yes-green.svg)](https://github.com/TaazKareem/clickup-mcp-server/graphs/commit-activity)
5
+ [![Maintenance](https://img.shields.io/badge/Maintained%3F-yes-brightgreen.svg)](https://github.com/TaazKareem/clickup-mcp-server/graphs/commit-activity)
7
6
 
8
7
  A Model Context Protocol (MCP) server for integrating ClickUp tasks with AI applications. This server allows AI agents to interact with ClickUp tasks, spaces, lists, and folders through a standardized protocol.
9
8
 
10
-
11
- > 🚧 **Status Update:** -v0.5.1 beta release with new Attach Task Files tool, Create Task Comments tool, Get Task Comments tool, and Custom ID support across all tools.
12
-
13
-
14
-
15
-
9
+ > 🚧 **Status Update:** v0.5.2 release with new Attach Task Files tool, Create Task Comments tool, Get Task Comments tool, Custom ID support across all tools, and Subtasks support.
16
10
 
17
11
  ## Setup
18
12
 
@@ -26,13 +20,13 @@ A Model Context Protocol (MCP) server for integrating ClickUp tasks with AI appl
26
20
 
27
21
  [![smithery badge](https://smithery.ai/badge/@TaazKareem/clickup-mcp-server)](https://smithery.ai/server/@TaazKareem/clickup-mcp-server)
28
22
 
29
- The server is hosted on Smithery. There, you can preview the available tools or copy the commands to run on your specific client app.
23
+ The server is hosted on [Smithery](https://smithery.ai/server/@TaazKareem/clickup-mcp-server). There, you can preview the available tools or copy the commands to run on your specific client app.
30
24
 
31
25
 
32
26
  ## NPX Installation
33
27
 
34
28
  [![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)
35
- [![Dependency Status](https://img.shields.io/librariesio/github/TaazKareem/clickup-mcp-server)](https://libraries.io/github/TaazKareem/clickup-mcp-server)
29
+ [![Dependency Status](https://img.shields.io/badge/dependencies-up%20to%20date-brightgreen)](https://github.com/TaazKareem/clickup-mcp-server/blob/main/package.json)
36
30
  [![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)
37
31
 
38
32
  Add this entry to your client's MCP settings JSON file:
@@ -69,6 +63,7 @@ Or use this npx command:
69
63
  - Get task comments
70
64
  - Set due dates using natural language and relative time expressions
71
65
  - Attach files to tasks using local file paths, URL, base64, or chunked uploads
66
+ - Create and manage subtasks with support for multi-level nesting
72
67
 
73
68
  - šŸ“‚ **Workspace Organization**
74
69
  - Complete workspace hierarchy (spaces, folders, lists)
@@ -86,12 +81,12 @@ Or use this npx command:
86
81
  | Tool | Description | Required Parameters |
87
82
  |------|-------------|-------------------|
88
83
  | [get_workspace_hierarchy](docs/api-reference.md#workspace-navigation) | Get workspace structure | None |
89
- | [create_task](docs/api-reference.md#task-management) | Create a task | `name`, (`listId`/`listName`) |
84
+ | [create_task](docs/api-reference.md#task-management) | Create a task | `name`, (`listId`/`listName`), optional `parent` |
90
85
  | [create_bulk_tasks](docs/api-reference.md#task-management) | Create multiple tasks | `tasks[]` |
91
86
  | [update_task](docs/api-reference.md#task-management) | Modify task | `taskId`/`taskName` |
92
87
  | [update_bulk_tasks](docs/api-reference.md#task-management) | Update multiple tasks | `tasks[]` with IDs or names |
93
- | [get_tasks](docs/api-reference.md#task-management) | Get tasks from list | `listId`/`listName` |
94
- | [get_task](docs/api-reference.md#task-management) | Get task details | `taskId`/`taskName` |
88
+ | [get_tasks](docs/api-reference.md#task-management) | Get tasks from list | `listId`/`listName`, optional `subtasks` |
89
+ | [get_task](docs/api-reference.md#task-management) | Get task details | `taskId`/`taskName`, optional `subtasks` |
95
90
  | [get_task_comments](docs/api-reference.md#task-management) | Get comments on a task | `taskId`/`taskName` |
96
91
  | [create_task_comment](docs/api-reference.md#task-management) | Add a comment to a task | `commentText`, (`taskId`/(`taskName`+`listName`)) |
97
92
  | [attach_task_file](docs/api-reference.md#task-management) | Attach file to a task | `taskId`/`taskName`, (`file_data` or `file_url`) |
@@ -112,8 +107,8 @@ Or use this npx command:
112
107
 
113
108
  See [full documentation](docs/api-reference.md) for optional parameters and advanced usage.
114
109
 
115
- ## Available Prompts
116
- Not yet implemented (or needed) For now, you can send a follow up prompt after the tool result.
110
+ ## Prompts
111
+ Not yet implemented and not supported by all client apps. Request a feature for a Prompt implementation that would be most beneficial for your workflow (without it being too specific). Examples:
117
112
 
118
113
  | Prompt | Purpose | Features |
119
114
  |--------|---------|----------|
@@ -131,6 +126,9 @@ The server provides clear error messages for:
131
126
  - API errors
132
127
  - Rate limiting
133
128
 
129
+ 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).
130
+ This can be also be specified on the command line as, e.g. `--env LOG_LEVEL=info`.
131
+
134
132
  ## Support the Developer
135
133
 
136
134
  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!
@@ -138,10 +136,12 @@ If you find this project useful, please consider supporting:
138
136
 
139
137
  [![Sponsor TaazKareem](https://img.shields.io/badge/Sponsor-TaazKareem-orange?logo=github)](https://github.com/sponsors/TaazKareem)
140
138
 
139
+
141
140
  <a href="https://buymeacoffee.com/taazkareem">
142
141
  <img src="https://cdn.buymeacoffee.com/buttons/v2/default-yellow.png" width="200" alt="Buy Me A Coffee">
143
142
  </a>
144
143
 
144
+
145
145
  ## Acknowledgements
146
146
 
147
147
  Special thanks to [ClickUp](https://clickup.com) for their excellent API and services that make this integration possible.
@@ -158,7 +158,7 @@ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file
158
158
 
159
159
  ## Disclaimer
160
160
 
161
- Disclaimer: This software makes use of third-party APIs and may reference trademarks
161
+ This software makes use of third-party APIs and may reference trademarks
162
162
  or brands owned by third parties. The use of such APIs or references does not imply
163
163
  any affiliation with or endorsement by the respective companies. All trademarks and
164
164
  brand names are the property of their respective owners. This project is an independent
package/build/config.js CHANGED
@@ -22,8 +22,7 @@ for (let i = 0; i < args.length; i++) {
22
22
  const configuration = {
23
23
  clickupApiKey: envArgs.clickupApiKey || process.env.CLICKUP_API_KEY || '',
24
24
  clickupTeamId: envArgs.clickupTeamId || process.env.CLICKUP_TEAM_ID || '',
25
- enableSponsorMessage: process.env.ENABLE_SPONSOR_MESSAGE !== 'false',
26
- sponsorUrl: process.env.SPONSOR_URL || 'https://github.com/sponsors/taazkareem'
25
+ enableSponsorMessage: process.env.ENABLE_SPONSOR_MESSAGE !== 'false'
27
26
  };
28
27
  // Validate only the required variables are present
29
28
  const requiredVars = ['clickupApiKey', 'clickupTeamId'];
package/build/index.js CHANGED
@@ -1,37 +1,25 @@
1
1
  #!/usr/bin/env node
2
2
  /**
3
- * ClickUp MCP Server - A Model Context Protocol server for ClickUp integration
3
+ * ClickUp MCP Server
4
4
  *
5
- * This server enables AI applications to interact with ClickUp through a standardized protocol,
6
- * allowing AI assistants to manage tasks, lists, and folders in ClickUp workspaces.
5
+ * This custom server implements the Model Context Protocol (MCP) specification to enable
6
+ * AI applications to interact with ClickUp workspaces. It provides a standardized
7
+ * interface for managing tasks, lists, folders and other ClickUp entities using Natural Language.
7
8
  *
8
- * Key capabilities include:
9
+ * Key Features:
10
+ * - Complete task management (CRUD operations, moving, duplicating)
11
+ * - Workspace organization (spaces, folders, lists)
12
+ * - Bulk operations with concurrent processing
13
+ * - Natural language date parsing
14
+ * - File attachments support
15
+ * - Name-based entity resolution
16
+ * - Markdown formatting
17
+ * - Built-in rate limiting
9
18
  *
10
- * Task Management:
11
- * - Create, update, move and duplicate tasks with rich description support
12
- * - Find tasks by name with smart disambiguation
13
- * - Optimized bulk task operations with concurrent processing
14
- * - Comprehensive filtering and sorting options
15
- *
16
- * Workspace Organization:
17
- * - Navigate and discover workspace structure with hierarchical views
18
- * - Create and manage lists and folders with proper nesting
19
- * - Smart name-based lookups that eliminate the need for IDs
20
- * - Support for priorities, statuses, and due dates
21
- *
22
- * Technical Features:
23
- * - Full markdown support for rich text content
24
- * - Secure credential handling through configuration
25
- * - Comprehensive error reporting and validation
26
- * - Name-based entity resolution with fuzzy matching
27
- *
28
- * This implementation follows the Model Context Protocol specification and
29
- * is designed to be used with AI assistants that support MCP.
19
+ * For full documentation and usage examples, please refer to the README.md file.
30
20
  */
31
21
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
32
22
  import { configureServer, server } from "./server.js";
33
- import { clickUpServices } from "./services/shared.js";
34
- import { initializeWorkspaceTool } from "./tools/workspace.js";
35
23
  import { info, error, warn } from "./logger.js";
36
24
  import { exec } from 'child_process';
37
25
  import { promisify } from 'util';
@@ -76,9 +64,6 @@ async function main() {
76
64
  os: process.platform,
77
65
  arch: process.arch
78
66
  });
79
- // Initialize tools with services
80
- info("Initializing workspace tools");
81
- initializeWorkspaceTool(clickUpServices);
82
67
  // Configure the server with all handlers
83
68
  info("Configuring server request handlers");
84
69
  await configureServer();
package/build/logger.js CHANGED
@@ -24,8 +24,35 @@ export var LogLevel;
24
24
  LogLevel[LogLevel["WARN"] = 3] = "WARN";
25
25
  LogLevel[LogLevel["ERROR"] = 4] = "ERROR";
26
26
  })(LogLevel || (LogLevel = {}));
27
- // Fixed log level - always use TRACE as the minimum level for complete logging
28
- const configuredLevel = LogLevel.TRACE;
27
+ // Parse LOG_LEVEL environment variable or command line argument
28
+ const parseLogLevel = (levelStr) => {
29
+ if (!levelStr)
30
+ return LogLevel.ERROR; // Default to ERROR if not specified
31
+ switch (levelStr.toUpperCase()) {
32
+ case 'TRACE': return LogLevel.TRACE;
33
+ case 'DEBUG': return LogLevel.DEBUG;
34
+ case 'INFO': return LogLevel.INFO;
35
+ case 'WARN': return LogLevel.WARN;
36
+ case 'ERROR': return LogLevel.ERROR;
37
+ default:
38
+ console.error(`Invalid LOG_LEVEL: ${levelStr}, defaulting to ERROR`);
39
+ return LogLevel.ERROR;
40
+ }
41
+ };
42
+ // Parse command line arguments for LOG_LEVEL
43
+ const args = process.argv.slice(2);
44
+ let argLogLevel;
45
+ for (let i = 0; i < args.length; i++) {
46
+ if (args[i] === '--env' && i + 1 < args.length) {
47
+ const [key, value] = args[i + 1].split('=');
48
+ if (key === 'LOG_LEVEL')
49
+ argLogLevel = value;
50
+ i++;
51
+ }
52
+ }
53
+ // Get log level from environment variable or command line, default to ERROR
54
+ const configuredLevel = parseLogLevel(argLogLevel || process.env.LOG_LEVEL);
55
+ console.debug(`Log level set to: ${LogLevel[configuredLevel]}`);
29
56
  /**
30
57
  * Check if a log level is enabled based on the configured level
31
58
  * @param level The log level to check
package/build/server.js CHANGED
@@ -15,7 +15,7 @@ const { workspace } = clickUpServices;
15
15
  */
16
16
  export const server = new Server({
17
17
  name: "clickup-mcp-server",
18
- version: "0.5.1",
18
+ version: "0.6.0",
19
19
  }, {
20
20
  capabilities: {
21
21
  tools: {},
@@ -61,7 +61,7 @@ export function configureServer() {
61
61
  });
62
62
  // Register CallTool handler with proper logging
63
63
  logger.info("Registering tool handlers", {
64
- toolCount: 23,
64
+ toolCount: 24,
65
65
  categories: ["workspace", "task", "list", "folder"]
66
66
  });
67
67
  server.setRequestHandler(CallToolRequestSchema, async (req) => {
@@ -204,6 +204,28 @@ export class TaskService extends BaseClickUpService {
204
204
  throw this.handleError(error, `Failed to get task ${taskId}`);
205
205
  }
206
206
  }
207
+ /**
208
+ * Get subtasks of a specific task
209
+ * @param taskId The ID of the parent task
210
+ * @returns Array of subtask details
211
+ */
212
+ async getSubtasks(taskId) {
213
+ this.logOperation('getSubtasks', { taskId });
214
+ try {
215
+ return await this.makeRequest(async () => {
216
+ // First, get the task to get its list ID
217
+ const task = await this.getTask(taskId);
218
+ const listId = task.list.id;
219
+ // Then get all tasks from the list
220
+ const allTasks = await this.getTasks(listId, { subtasks: true });
221
+ // Filter tasks that have the specified task as parent
222
+ return allTasks.filter(t => t.parent === taskId || t.top_level_parent === taskId);
223
+ });
224
+ }
225
+ catch (error) {
226
+ throw this.handleError(error, `Failed to get subtasks of task ${taskId}`);
227
+ }
228
+ }
207
229
  /**
208
230
  * Get a specific task by its custom ID
209
231
  * @param customTaskId The custom ID of the task (e.g., "DEV-1234")
@@ -214,8 +236,11 @@ export class TaskService extends BaseClickUpService {
214
236
  this.logOperation('getTaskByCustomId', { customTaskId, listId });
215
237
  try {
216
238
  return await this.makeRequest(async () => {
217
- // Build query with custom_task_ids=true
218
- const params = new URLSearchParams({ custom_task_ids: 'true' });
239
+ // Build query with custom_task_ids=true and team_id
240
+ const params = new URLSearchParams({
241
+ custom_task_ids: 'true',
242
+ team_id: this.teamId
243
+ });
219
244
  // Use the ClickUp API endpoint for retrieving tasks by ID
220
245
  // With custom_task_ids=true parameter, it will treat the ID as a custom ID
221
246
  const response = await this.client.get(`/task/${customTaskId}?${params.toString()}`);
@@ -83,7 +83,7 @@ async function mapTaskIds(tasks) {
83
83
  * Handler for creating a task
84
84
  */
85
85
  export async function createTaskHandler(params) {
86
- const { name, description, markdown_description, status, dueDate } = params;
86
+ const { name, description, markdown_description, status, dueDate, parent } = params;
87
87
  if (!name)
88
88
  throw new Error("Task name is required");
89
89
  // Use our helper function to validate and convert priority
@@ -95,7 +95,8 @@ export async function createTaskHandler(params) {
95
95
  markdown_description,
96
96
  status,
97
97
  priority,
98
- due_date: dueDate ? parseDueDate(dueDate) : undefined
98
+ due_date: dueDate ? parseDueDate(dueDate) : undefined,
99
+ parent
99
100
  });
100
101
  }
101
102
  /**
@@ -131,6 +132,12 @@ export async function duplicateTaskHandler(params) {
131
132
  export async function getTaskHandler(params) {
132
133
  // resolveTaskIdWithValidation now auto-detects whether taskId is a regular ID or custom ID
133
134
  const taskId = await getTaskId(params.taskId, params.taskName, params.listName, params.customTaskId);
135
+ // If subtasks parameter is provided and true, use the getTaskWithSubtasks method
136
+ if (params.subtasks) {
137
+ const task = await taskService.getTask(taskId);
138
+ const subtasks = await taskService.getSubtasks(taskId);
139
+ return { ...task, subtasks };
140
+ }
134
141
  return await taskService.getTask(taskId);
135
142
  }
136
143
  /**
@@ -140,14 +147,6 @@ export async function getTasksHandler(params) {
140
147
  const listId = await getListId(params.listId, params.listName);
141
148
  return await taskService.getTasks(listId, buildTaskFilters(params));
142
149
  }
143
- /**
144
- * Handler for deleting a task
145
- */
146
- export async function deleteTaskHandler(params) {
147
- const taskId = await getTaskId(params.taskId, params.taskName, params.listName);
148
- await taskService.deleteTask(taskId);
149
- return true;
150
- }
151
150
  /**
152
151
  * Handler for getting task comments
153
152
  */
@@ -242,3 +241,11 @@ export async function deleteBulkTasksHandler(params) {
242
241
  await bulkService.deleteTasks(taskIds, parseBulkOptions(params.options));
243
242
  return taskIds.map(() => true);
244
243
  }
244
+ /**
245
+ * Handler for deleting a task
246
+ */
247
+ export async function deleteTaskHandler(params) {
248
+ const taskId = await getTaskId(params.taskId, params.taskName, params.listName);
249
+ await taskService.deleteTask(taskId);
250
+ return true;
251
+ }
@@ -57,7 +57,8 @@ Requirements:
57
57
 
58
58
  Notes:
59
59
  - For multiple tasks, use create_bulk_tasks instead
60
- - Reuse list IDs from previous responses when possible to avoid redundant lookups`,
60
+ - Reuse list IDs from previous responses when possible to avoid redundant lookups
61
+ - To create a subtask, set the parent parameter to the ID of the parent task`,
61
62
  inputSchema: {
62
63
  type: "object",
63
64
  properties: {
@@ -92,6 +93,10 @@ Notes:
92
93
  dueDate: {
93
94
  type: "string",
94
95
  description: "Optional due date. Supports Unix timestamps (ms) or natural language like '1 hour from now', 'tomorrow', 'next week', etc."
96
+ },
97
+ parent: {
98
+ type: "string",
99
+ description: "Optional ID of the parent task. When specified, this task will be created as a subtask of the specified parent task."
95
100
  }
96
101
  }
97
102
  }
@@ -269,7 +274,8 @@ Requirements:
269
274
  Note:
270
275
  - Task names are only unique within a list, so the system needs to know which list to search in
271
276
  - Regular task IDs are always 9 characters long (e.g., "86b394eqa")
272
- - Custom IDs have an uppercase prefix followed by a hyphen and number (e.g., "DEV-1234")`,
277
+ - Custom IDs have an uppercase prefix followed by a hyphen and number (e.g., "DEV-1234")
278
+ - Set subtasks=true to include all subtasks in the response`,
273
279
  inputSchema: {
274
280
  type: "object",
275
281
  properties: {
@@ -288,6 +294,10 @@ Note:
288
294
  customTaskId: {
289
295
  type: "string",
290
296
  description: "Custom task ID (e.g., 'DEV-1234'). Only use this if you want to explicitly force custom ID lookup. In most cases, you can just use taskId which auto-detects ID format."
297
+ },
298
+ subtasks: {
299
+ type: "boolean",
300
+ description: "Whether to include subtasks in the response. Set to true to retrieve full details of all subtasks."
291
301
  }
292
302
  },
293
303
  required: []
@@ -322,6 +332,15 @@ Notes:
322
332
  type: "string",
323
333
  description: "Name of list to get tasks from. Only use if you don't have listId."
324
334
  },
335
+ subtasks: {
336
+ type: "boolean",
337
+ description: "Include subtasks"
338
+ },
339
+ statuses: {
340
+ type: "array",
341
+ items: { type: "string" },
342
+ description: "Filter by status names (e.g. ['To Do', 'In Progress'])"
343
+ },
325
344
  archived: {
326
345
  type: "boolean",
327
346
  description: "Include archived tasks"
@@ -337,55 +356,11 @@ Notes:
337
356
  reverse: {
338
357
  type: "boolean",
339
358
  description: "Reverse sort order (descending)"
340
- },
341
- subtasks: {
342
- type: "boolean",
343
- description: "Include subtasks"
344
- },
345
- statuses: {
346
- type: "array",
347
- items: {
348
- type: "string"
349
- },
350
- description: "Filter by status names (e.g. ['To Do', 'In Progress'])"
351
359
  }
352
360
  },
353
361
  required: []
354
362
  }
355
363
  };
356
- /**
357
- * Tool definition for deleting a task
358
- */
359
- export const deleteTaskTool = {
360
- name: "delete_task",
361
- description: `Purpose: PERMANENTLY DELETE a task.
362
-
363
- Valid Usage:
364
- 1. Use taskId alone (preferred and safest)
365
- 2. Use taskName + optional listName
366
-
367
- Warning:
368
- - This action CANNOT be undone
369
- - Using taskName is risky as names may not be unique
370
- - Provide listName when using taskName for more precise targeting`,
371
- inputSchema: {
372
- type: "object",
373
- properties: {
374
- taskId: {
375
- type: "string",
376
- description: "ID of task to delete (preferred). Works with both regular task IDs (9 characters) and custom IDs with uppercase prefixes (like 'DEV-1234')."
377
- },
378
- taskName: {
379
- type: "string",
380
- description: "Name of task to delete. Use with extreme caution as names may not be unique."
381
- },
382
- listName: {
383
- type: "string",
384
- description: "Name of list containing the task. Helps ensure correct task deletion when using taskName."
385
- }
386
- }
387
- }
388
- };
389
364
  /**
390
365
  * Tool definition for retrieving task comments
391
366
  */
@@ -477,3 +452,36 @@ Notes:
477
452
  required: ["commentText"]
478
453
  }
479
454
  };
455
+ /**
456
+ * Tool definition for deleting a task
457
+ */
458
+ export const deleteTaskTool = {
459
+ name: "delete_task",
460
+ description: `Purpose: PERMANENTLY DELETE a task.
461
+
462
+ Valid Usage:
463
+ 1. Use taskId alone (preferred and safest)
464
+ 2. Use taskName + optional listName
465
+
466
+ Warning:
467
+ - This action CANNOT be undone
468
+ - Using taskName is risky as names may not be unique
469
+ - Provide listName when using taskName for more precise targeting`,
470
+ inputSchema: {
471
+ type: "object",
472
+ properties: {
473
+ taskId: {
474
+ type: "string",
475
+ description: "ID of task to delete (preferred). Works with both regular task IDs (9 characters) and custom IDs with uppercase prefixes (like 'DEV-1234')."
476
+ },
477
+ taskName: {
478
+ type: "string",
479
+ description: "Name of task to delete. Use with extreme caution as names may not be unique."
480
+ },
481
+ listName: {
482
+ type: "string",
483
+ description: "Name of list containing the task. Helps ensure correct task deletion when using taskName."
484
+ }
485
+ }
486
+ }
487
+ };
@@ -6,11 +6,11 @@
6
6
  */
7
7
  import { Logger } from '../logger.js';
8
8
  import { sponsorService } from '../utils/sponsor-service.js';
9
+ import { clickUpServices } from '../services/shared.js';
9
10
  // Create a logger for workspace tools
10
11
  const logger = new Logger('WorkspaceTool');
11
- // Use the workspace service imported from the server
12
- // This is defined when server.ts imports this module
13
- let workspaceService;
12
+ // Use the workspace service from the shared services
13
+ const { workspace: workspaceService } = clickUpServices;
14
14
  /**
15
15
  * Tool definition for retrieving the complete workspace hierarchy
16
16
  */
@@ -33,20 +33,6 @@ Notes:
33
33
  properties: {}
34
34
  }
35
35
  };
36
- /**
37
- * Initialize the tool with services
38
- */
39
- export function initializeWorkspaceTool(services) {
40
- logger.info('Initializing workspace tool');
41
- if (!services || !services.workspace) {
42
- logger.error('Failed to initialize workspace tool: services not provided');
43
- throw new Error('Workspace service not available');
44
- }
45
- workspaceService = services.workspace;
46
- logger.info('Workspace tool initialized successfully', {
47
- serviceType: workspaceService.constructor.name
48
- });
49
- }
50
36
  /**
51
37
  * Handler for the get_workspace_hierarchy tool
52
38
  */
@@ -56,40 +42,13 @@ export async function handleGetWorkspaceHierarchy() {
56
42
  const hierarchy = await workspaceService.getWorkspaceHierarchy();
57
43
  // Generate tree representation
58
44
  const treeOutput = formatTreeOutput(hierarchy);
59
- return sponsorService.createResponse({
60
- hierarchy: treeOutput
61
- }, true);
45
+ // Use sponsor service to create the response with optional sponsor message
46
+ return sponsorService.createResponse({ hierarchy: treeOutput }, true);
62
47
  }
63
48
  catch (error) {
64
49
  return sponsorService.createErrorResponse(`Error getting workspace hierarchy: ${error.message}`);
65
50
  }
66
51
  }
67
- /**
68
- * Format the hierarchy for the response
69
- */
70
- function formatHierarchyResponse(hierarchy) {
71
- try {
72
- const treeOutput = formatTreeOutput(hierarchy);
73
- return {
74
- content: [
75
- {
76
- type: "text",
77
- text: treeOutput
78
- }
79
- ]
80
- };
81
- }
82
- catch (error) {
83
- return {
84
- content: [
85
- {
86
- type: "text",
87
- text: `Error formatting workspace hierarchy: ${error.message}`
88
- }
89
- ]
90
- };
91
- }
92
- }
93
52
  /**
94
53
  * Format the hierarchy as a tree string
95
54
  */
@@ -114,5 +73,6 @@ function formatTreeOutput(hierarchy) {
114
73
  };
115
74
  // Generate tree representation
116
75
  const treeLines = formatNodeAsTree(hierarchy.root);
76
+ // Return plain text instead of adding code block markers
117
77
  return treeLines.join('\n');
118
78
  }
@@ -12,8 +12,8 @@ const logger = new Logger('SponsorService');
12
12
  */
13
13
  export class SponsorService {
14
14
  constructor() {
15
+ this.sponsorUrl = 'https://github.com/sponsors/taazkareem';
15
16
  this.isEnabled = config.enableSponsorMessage;
16
- this.sponsorUrl = config.sponsorUrl;
17
17
  logger.info('SponsorService initialized', { enabled: this.isEnabled });
18
18
  }
19
19
  /**
@@ -56,7 +56,7 @@ export class SponsorService {
56
56
  if (this.isEnabled && includeSponsorMessage) {
57
57
  content.push({
58
58
  type: "text",
59
- text: `\n\nā¤ļø Support this project by sponsoring the developer at ${this.sponsorUrl}`
59
+ text: `āŽÆ Support this project by sponsoring the developer at ${this.sponsorUrl}`
60
60
  });
61
61
  }
62
62
  return { content };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@taazkareem/clickup-mcp-server",
3
- "version": "0.5.1",
3
+ "version": "0.6.0",
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",