UNPKG

@taazkareem/clickup-mcp-server

Version:

ClickUp MCP Server - Integrate ClickUp tasks with AI through Model Context Protocol

240 lines (239 loc) 8.61 kB
/** * SPDX-FileCopyrightText: © 2025 Talib Kareem <taazkareem@icloud.com> * SPDX-License-Identifier: MIT * * ClickUp Tag Service * * Provides access to ClickUp API endpoints for tag management: * - Space tags (get, create, update, delete) * - Task tags (add, remove) */ import { BaseClickUpService } from './base.js'; /** * ClickUp Tag Service class for managing tags */ export class ClickUpTagService extends BaseClickUpService { /** * Get all tags in a space * @param spaceId - ID of the space to get tags from * @returns Promise with tags array */ async getSpaceTags(spaceId) { try { this.logger.debug(`Getting tags for space: ${spaceId}`); const response = await this.client.get(`/space/${spaceId}/tag`); return { success: true, data: response.data.tags }; } catch (error) { this.logger.error(`Failed to get tags for space: ${spaceId}`, error); return { success: false, error: { message: error.message || 'Failed to get space tags', code: error.code, details: error.data } }; } } /** * Create a new tag in a space * @param spaceId - ID of the space * @param tagData - Tag data (name, background color, foreground color) * @returns Promise with created tag */ async createSpaceTag(spaceId, tagData) { try { this.logger.debug(`Creating tag "${tagData.tag_name}" in space: ${spaceId}`); // Send tag data wrapped in a 'tag' object const response = await this.client.post(`/space/${spaceId}/tag`, { tag: { name: tagData.tag_name, tag_bg: tagData.tag_bg, tag_fg: tagData.tag_fg } }); return { success: true, data: response.data.tag }; } catch (error) { this.logger.error(`Failed to create tag in space: ${spaceId}`, error); return { success: false, error: { message: error.message || 'Failed to create space tag', code: error.code, details: error.data } }; } } /** * Update an existing tag in a space * @param spaceId - ID of the space * @param tagName - Current name of the tag to update * @param updateData - Tag data to update (name, colors) * @returns Promise with updated tag */ async updateSpaceTag(spaceId, tagName, updateData) { try { this.logger.debug(`Updating tag "${tagName}" in space: ${spaceId}`); // Encode the tag name in the URL const encodedTagName = encodeURIComponent(tagName); const response = await this.client.put(`/space/${spaceId}/tag/${encodedTagName}`, updateData); return { success: true, data: response.data.tag }; } catch (error) { this.logger.error(`Failed to update tag "${tagName}" in space: ${spaceId}`, error); return { success: false, error: { message: error.message || 'Failed to update space tag', code: error.code, details: error.data } }; } } /** * Delete a tag from a space * @param spaceId - ID of the space * @param tagName - Name of the tag to delete * @returns Promise with success status */ async deleteSpaceTag(spaceId, tagName) { try { this.logger.debug(`Deleting tag "${tagName}" from space: ${spaceId}`); // Encode the tag name in the URL const encodedTagName = encodeURIComponent(tagName); await this.client.delete(`/space/${spaceId}/tag/${encodedTagName}`); return { success: true }; } catch (error) { this.logger.error(`Failed to delete tag "${tagName}" from space: ${spaceId}`, error); return { success: false, error: { message: error.message || 'Failed to delete space tag', code: error.code, details: error.data } }; } } /** * Add a tag to a task * @param taskId - ID of the task * @param tagName - Name of the tag to add * @returns Promise with success status and tag data */ async addTagToTask(taskId, tagName) { try { this.logger.debug(`Adding tag "${tagName}" to task: ${taskId}`); // First get the task to get its space ID const taskResponse = await this.client.get(`/task/${taskId}`); if (!taskResponse.data?.space?.id) { return { success: false, error: { message: 'Could not determine space ID from task', code: 'SPACE_NOT_FOUND' } }; } // Get space tags to verify tag exists const spaceId = taskResponse.data.space.id; const spaceTags = await this.getSpaceTags(spaceId); if (!spaceTags.success || !spaceTags.data) { return { success: false, error: { message: 'Failed to verify tag existence in space', code: 'TAG_VERIFICATION_FAILED', details: spaceTags.error } }; } // Check if tag exists const tagExists = spaceTags.data.some(tag => tag.name === tagName); if (!tagExists) { return { success: false, error: { message: `Tag "${tagName}" does not exist in the space`, code: 'TAG_NOT_FOUND' } }; } // Encode the tag name in the URL const encodedTagName = encodeURIComponent(tagName); // Add the tag await this.client.post(`/task/${taskId}/tag/${encodedTagName}`, {}); // Verify the tag was added by getting the task again const verifyResponse = await this.client.get(`/task/${taskId}`); const tagAdded = verifyResponse.data?.tags?.some(tag => tag.name === tagName) ?? false; if (!tagAdded) { return { success: false, error: { message: 'Tag addition failed verification', code: 'TAG_VERIFICATION_FAILED' } }; } return { success: true, data: { tagAdded: true } }; } catch (error) { this.logger.error(`Failed to add tag "${tagName}" to task: ${taskId}`, error); return { success: false, error: { message: error.message || 'Failed to add tag to task', code: error.code, details: error.data } }; } } /** * Remove a tag from a task * @param taskId - ID of the task * @param tagName - Name of the tag to remove * @returns Promise with success status */ async removeTagFromTask(taskId, tagName) { try { this.logger.debug(`Removing tag "${tagName}" from task: ${taskId}`); // Encode the tag name in the URL const encodedTagName = encodeURIComponent(tagName); await this.client.delete(`/task/${taskId}/tag/${encodedTagName}`); return { success: true }; } catch (error) { this.logger.error(`Failed to remove tag "${tagName}" from task: ${taskId}`, error); return { success: false, error: { message: error.message || 'Failed to remove tag from task', code: error.code, details: error.data } }; } } }