@taazkareem/clickup-mcp-server 0.6.9 → 0.6.10

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,149 +0,0 @@
1
- /**
2
- * Remove a tag from a task
3
- * @param taskId Task ID or custom ID
4
- * @param taskName Task name (for lookup if ID not provided)
5
- * @param listName List name (optional, for improved task name lookup)
6
- * @param tagName Tag name to remove
7
- * @returns Success response
8
- */
9
- export async function removeTagFromTask(taskId, taskName, listName, tagName) {
10
- logger.info(`[TAG REMOVAL] Starting tag removal for ${taskId || taskName}`);
11
- try {
12
- // Step 1: Find the task
13
- let resolvedTaskId;
14
- if (taskId) {
15
- // If task ID is provided, use it directly
16
- resolvedTaskId = taskId;
17
- logger.info(`[TAG REMOVAL] Using provided task ID: ${resolvedTaskId}`);
18
- }
19
- else if (taskName) {
20
- // If task name is provided, let's use a more direct approach
21
- // First try to get tasks in the list if list name is provided
22
- if (listName) {
23
- logger.info(`[TAG REMOVAL] Looking up task by name in list "${listName}"`);
24
- const list = await findListIDByName(clickUpServices.workspace, listName);
25
- if (list) {
26
- const tasks = await clickUpServices.task.getTasks(list.id);
27
- // Try to find the task by name using a direct case-insensitive comparison
28
- const foundTask = tasks.find(t => t.name.toLowerCase() === taskName.toLowerCase());
29
- if (foundTask) {
30
- resolvedTaskId = foundTask.id;
31
- logger.info(`[TAG REMOVAL] Found task in list "${listName}": ${resolvedTaskId}`);
32
- }
33
- else {
34
- // Try to find by substring
35
- const substringMatch = tasks.find(t => t.name.toLowerCase().includes(taskName.toLowerCase()) ||
36
- taskName.toLowerCase().includes(t.name.toLowerCase()));
37
- if (substringMatch) {
38
- resolvedTaskId = substringMatch.id;
39
- logger.info(`[TAG REMOVAL] Found task in list "${listName}" by substring: ${resolvedTaskId}`);
40
- }
41
- else {
42
- // If not found, try the alternative approach
43
- logger.info(`[TAG REMOVAL] Task not found in list, trying global lookup`);
44
- // Get all tasks in workspace
45
- const filters = { list_ids: [list.id], include_closed: true };
46
- const response = await clickUpServices.task.getWorkspaceTasks({ ...filters, detail_level: 'detailed' });
47
- // Direct comparison of all tasks
48
- if ('tasks' in response && response.tasks.length > 0) {
49
- // Log first few tasks to see what's available
50
- logger.info(`[TAG REMOVAL] Workspace search found ${response.tasks.length} tasks in list "${listName}"`);
51
- // Try to find by exact name
52
- const exactMatch = response.tasks.find(t => t.name === taskName);
53
- if (exactMatch) {
54
- resolvedTaskId = exactMatch.id;
55
- logger.info(`[TAG REMOVAL] Found exact match: ${resolvedTaskId}`);
56
- }
57
- else {
58
- // Try case-insensitive
59
- const caseInsensitiveMatch = response.tasks.find(t => t.name.toLowerCase() === taskName.toLowerCase());
60
- if (caseInsensitiveMatch) {
61
- resolvedTaskId = caseInsensitiveMatch.id;
62
- logger.info(`[TAG REMOVAL] Found case-insensitive match: ${resolvedTaskId}`);
63
- }
64
- else {
65
- throw new Error(`Task "${taskName}" not found in list "${listName}"`);
66
- }
67
- }
68
- }
69
- else {
70
- throw new Error(`No tasks found in list "${listName}"`);
71
- }
72
- }
73
- }
74
- }
75
- else {
76
- throw new Error(`List "${listName}" not found`);
77
- }
78
- }
79
- else {
80
- // If no list name, search across the workspace
81
- logger.info(`[TAG REMOVAL] Searching for task by name across workspace: "${taskName}"`);
82
- // Get all tasks across all lists instead of using global lookup
83
- const response = await clickUpServices.task.getWorkspaceTasks({ include_closed: true, detail_level: 'detailed' });
84
- if ('tasks' in response && response.tasks.length > 0) {
85
- // Try exact match first
86
- const exactMatch = response.tasks.find(t => t.name === taskName);
87
- if (exactMatch) {
88
- resolvedTaskId = exactMatch.id;
89
- logger.info(`[TAG REMOVAL] Found exact match: ${resolvedTaskId}`);
90
- }
91
- else {
92
- // Try case-insensitive
93
- const caseInsensitiveMatch = response.tasks.find(t => t.name.toLowerCase() === taskName.toLowerCase());
94
- if (caseInsensitiveMatch) {
95
- resolvedTaskId = caseInsensitiveMatch.id;
96
- logger.info(`[TAG REMOVAL] Found case-insensitive match: ${resolvedTaskId}`);
97
- }
98
- else {
99
- // Try substring match
100
- const substringMatch = response.tasks.find(t => t.name.toLowerCase().includes(taskName.toLowerCase()) ||
101
- taskName.toLowerCase().includes(t.name.toLowerCase()));
102
- if (substringMatch) {
103
- resolvedTaskId = substringMatch.id;
104
- logger.info(`[TAG REMOVAL] Found substring match: ${resolvedTaskId}`);
105
- }
106
- else {
107
- throw new Error(`Task "${taskName}" not found in workspace`);
108
- }
109
- }
110
- }
111
- }
112
- else {
113
- throw new Error(`No tasks found in workspace`);
114
- }
115
- }
116
- }
117
- else {
118
- throw new Error("Either taskId or taskName must be provided");
119
- }
120
- // Step 2: Get the current task to check for tags
121
- const task = await clickUpServices.task.getTask(resolvedTaskId);
122
- logger.info(`[TAG REMOVAL] Retrieved task ${resolvedTaskId}, current tags: ${task.tags.map(t => t.name).join(', ')}`);
123
- // Step 3: Check if the tag exists on the task
124
- const hasTag = task.tags.some(tag => tag.name.toLowerCase() === tagName.toLowerCase());
125
- if (!hasTag) {
126
- logger.info(`[TAG REMOVAL] Tag "${tagName}" not found on task ${resolvedTaskId}`);
127
- return {
128
- success: true,
129
- message: "Tag removed from task successfully"
130
- };
131
- }
132
- // Step 4: Remove the tag by updating the task
133
- // Filter out the tag we want to remove
134
- const updatedTags = task.tags.filter(tag => tag.name.toLowerCase() !== tagName.toLowerCase());
135
- logger.info(`[TAG REMOVAL] Filtered tags from ${task.tags.length} to ${updatedTags.length}`);
136
- // Use directly the ClickUp API to remove tag
137
- const encodedTagName = encodeURIComponent(tagName);
138
- await clickUpServices.task.client.delete(`/task/${resolvedTaskId}/tag/${encodedTagName}`);
139
- logger.info(`[TAG REMOVAL] Successfully removed tag "${tagName}" from task ${resolvedTaskId}`);
140
- return {
141
- success: true,
142
- message: "Tag removed from task successfully"
143
- };
144
- }
145
- catch (error) {
146
- logger.error(`[TAG REMOVAL] Error removing tag: ${error instanceof Error ? error.message : String(error)}`);
147
- throw clickUpServices.task.handleError(error, `Failed to remove tag: ${error instanceof Error ? error.message : String(error)}`);
148
- }
149
- }
@@ -1,36 +0,0 @@
1
- /**
2
- * Alternative implementation for bulk tasks creation
3
- */
4
- import { handleCreateBulkTasks } from './task.js';
5
- /**
6
- * Alternative tool definition for bulk task creation to work around MCP validation issues
7
- */
8
- export const createTasksBulkTool = {
9
- name: "create_tasks_bulk",
10
- description: "Create multiple tasks in a list efficiently. You MUST provide:\n1. An array of tasks with required properties\n2. Either listId or listName to specify the target list",
11
- inputSchema: {
12
- type: "object",
13
- properties: {
14
- listId: {
15
- type: "string",
16
- description: "ID of list for new tasks (preferred). Use this instead of listName if you have it."
17
- },
18
- listName: {
19
- type: "string",
20
- description: "Name of list for new tasks. Only use if you don't have listId."
21
- },
22
- tasks: {
23
- // Define minimally to avoid validation issues
24
- description: "Array of tasks to create. Each task must have at least a name."
25
- }
26
- },
27
- required: ["tasks"]
28
- }
29
- };
30
- /**
31
- * Handler for create_tasks_bulk tool
32
- */
33
- export async function handleCreateTasksBulk(parameters) {
34
- // Use the same implementation as handleCreateBulkTasks
35
- return handleCreateBulkTasks(parameters);
36
- }
@@ -1,76 +0,0 @@
1
- /**
2
- * Debug Tools
3
- *
4
- * This module provides tools for debugging and monitoring the ClickUp MCP server.
5
- */
6
- // In-memory log storage
7
- const DEBUG_LOGS = [];
8
- /**
9
- * Log a message to the in-memory debug logs
10
- */
11
- export function logDebug(message, level = 'info') {
12
- const timestamp = new Date().toISOString();
13
- DEBUG_LOGS.push({ timestamp, message, level });
14
- // Optional: Also print to console for development
15
- if (level === 'error') {
16
- console.error(`[${timestamp}] ERROR: ${message}`);
17
- }
18
- else if (level === 'warn') {
19
- console.warn(`[${timestamp}] WARN: ${message}`);
20
- }
21
- else {
22
- console.log(`[${timestamp}] INFO: ${message}`);
23
- }
24
- }
25
- /**
26
- * Tool definition for checking debug logs
27
- */
28
- export const checkDebugLogsTool = {
29
- name: 'check_debug_logs',
30
- description: 'Check the server debug logs collected since the server started.',
31
- inputSchema: {
32
- type: 'object',
33
- properties: {}
34
- }
35
- };
36
- /**
37
- * Tool definition for clearing debug logs
38
- */
39
- export const clearDebugLogsTool = {
40
- name: 'clear_debug_logs',
41
- description: 'Clear all server debug logs.',
42
- inputSchema: {
43
- type: 'object',
44
- properties: {}
45
- }
46
- };
47
- /**
48
- * Handler for the check_debug_logs tool
49
- */
50
- export function handleCheckDebugLogs() {
51
- return {
52
- content: [
53
- {
54
- type: "text",
55
- text: DEBUG_LOGS.length > 0
56
- ? DEBUG_LOGS.map(log => `[${log.timestamp}] [${log.level.toUpperCase()}] ${log.message}`).join('\n')
57
- : "No debug logs available."
58
- }
59
- ]
60
- };
61
- }
62
- /**
63
- * Handler for the clear_debug_logs tool
64
- */
65
- export function handleClearDebugLogs() {
66
- const count = DEBUG_LOGS.length;
67
- DEBUG_LOGS.length = 0;
68
- return {
69
- content: [
70
- {
71
- type: "text",
72
- text: `Cleared ${count} debug log entries.`
73
- }
74
- ]
75
- };
76
- }
@@ -1,55 +0,0 @@
1
- /**
2
- * Simple Logs Tool
3
- *
4
- * Provides a basic tool for reading server logs.
5
- */
6
- import { promises as fs } from 'fs';
7
- import { join, dirname } from 'path';
8
- // Define the log file path - using __dirname to get absolute path
9
- const LOG_FILE = join(dirname(__dirname), 'server.log');
10
- // Tool definition
11
- export const checkLogsTool = {
12
- name: "check_logs",
13
- description: "Check server logs with optional filtering by log level",
14
- parameters: {
15
- type: "object",
16
- properties: {
17
- level: {
18
- type: "string",
19
- enum: ["TRACE", "DEBUG", "INFO", "WARN", "ERROR"],
20
- description: "Filter logs by level"
21
- },
22
- limit: {
23
- type: "number",
24
- description: "Maximum number of log entries to return (default: 50)"
25
- }
26
- }
27
- }
28
- };
29
- // Simple handler implementation using async/await with promises
30
- export async function handleCheckLogs(params) {
31
- try {
32
- // Read log file using promises
33
- const content = await fs.readFile(LOG_FILE, 'utf-8');
34
- // Split into lines and filter out empty lines
35
- let lines = content.split('\n').filter(Boolean);
36
- // Apply level filtering if specified
37
- if (params?.level) {
38
- lines = lines.filter(line => line.includes(`${params.level}:`));
39
- }
40
- // Apply limit (default to 50)
41
- const limit = params?.limit || 50;
42
- const result = lines.slice(-limit);
43
- // Return results
44
- return {
45
- logs: result.length > 0 ? result : ['No matching logs found.']
46
- };
47
- }
48
- catch (error) {
49
- // Simple error handling
50
- if (error.code === 'ENOENT') {
51
- return { logs: ['Log file not found.'] };
52
- }
53
- return { logs: [`Error reading logs: ${error.message}`] };
54
- }
55
- }