UNPKG

@taazkareem/clickup-mcp-server

Version:

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

98 lines (97 loc) 4.43 kB
/** * SPDX-FileCopyrightText: © 2025 Talib Kareem <taazkareem@icloud.com> * SPDX-License-Identifier: MIT * * ClickUp Task Service - Attachments Module * * Handles file attachment operations for ClickUp tasks, supporting three methods: * - Uploading file attachments from base64/buffer data * - Uploading file attachments from a URL (web URLs like http/https) * - Uploading file attachments from local file paths (absolute paths) */ import { TaskServiceSearch } from './task-search.js'; /** * Attachment functionality for the TaskService */ export class TaskServiceAttachments extends TaskServiceSearch { /** * Upload a file attachment to a ClickUp task * @param taskId The ID of the task to attach the file to * @param fileData The file data as a Buffer * @param fileName The name of the file * @returns Promise resolving to the attachment response from ClickUp */ async uploadTaskAttachment(taskId, fileData, fileName) { this.logOperation('uploadTaskAttachment', { taskId, fileName, fileSize: fileData.length }); try { return await this.makeRequest(async () => { // Create FormData for multipart/form-data upload const FormData = (await import('form-data')).default; const formData = new FormData(); // Add the file to the form data formData.append('attachment', fileData, { filename: fileName, contentType: 'application/octet-stream' // Let ClickUp determine the content type }); // Use the raw axios client for this request since we need to handle FormData const response = await this.client.post(`/task/${taskId}/attachment`, formData, { headers: { ...formData.getHeaders(), 'Authorization': this.apiKey } }); return response.data; }); } catch (error) { throw this.handleError(error, `Failed to upload attachment to task ${taskId}`); } } /** * Upload a file attachment to a ClickUp task from a URL * @param taskId The ID of the task to attach the file to * @param fileUrl The URL of the file to download and attach * @param fileName Optional file name (if not provided, it will be extracted from the URL) * @param authHeader Optional authorization header for the URL * @returns Promise resolving to the attachment response from ClickUp */ async uploadTaskAttachmentFromUrl(taskId, fileUrl, fileName, authHeader) { this.logOperation('uploadTaskAttachmentFromUrl', { taskId, fileUrl, fileName }); try { return await this.makeRequest(async () => { // Import required modules const axios = (await import('axios')).default; const FormData = (await import('form-data')).default; // Download the file from the URL const headers = {}; if (authHeader) { headers['Authorization'] = authHeader; } const response = await axios.get(fileUrl, { responseType: 'arraybuffer', headers }); // Extract filename from URL if not provided const actualFileName = fileName || fileUrl.split('/').pop() || 'downloaded-file'; // Create FormData for multipart/form-data upload const formData = new FormData(); // Add the file to the form data formData.append('attachment', Buffer.from(response.data), { filename: actualFileName, contentType: 'application/octet-stream' }); // Upload the file to ClickUp const uploadResponse = await this.client.post(`/task/${taskId}/attachment`, formData, { headers: { ...formData.getHeaders(), 'Authorization': this.apiKey } }); return uploadResponse.data; }); } catch (error) { throw this.handleError(error, `Failed to upload attachment from URL to task ${taskId}`); } } }