asana_download_attachment
Download Asana attachments to your local directory for offline access or backup.
Instructions
Download an attachment locally
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| attachment_gid | Yes | The attachment GID to download | |
| output_dir | No | Directory to save the file (defaults to ~/downloads) |
Implementation Reference
- src/tool-handler.ts:542-548 (handler)Tool handler switch case that destructures input parameters and delegates to AsanaClientWrapper.downloadAttachment method, returning JSON response.case "asana_download_attachment": { const { attachment_gid, output_dir } = args; const response = await asanaClient.downloadAttachment(attachment_gid, output_dir); return { content: [{ type: "text", text: JSON.stringify(response) }], }; }
- Core implementation that fetches attachment metadata, downloads the file from Asana's download_url using fetch, determines filename and MIME type, and saves to local directory (default ~/downloads).async downloadAttachment(attachmentId: string, outputDir?: string) { const fs = await import('fs'); const path = await import('path'); const os = await import('os'); const { pipeline } = await import('stream/promises'); outputDir = outputDir || path.join(os.homedir(), 'downloads'); const attachment = await this.getAttachment(attachmentId); const downloadUrl = attachment.download_url || attachment.downloadUrl; if (!downloadUrl) { throw new Error('Attachment does not have a download_url'); } await fs.promises.mkdir(outputDir, { recursive: true }); const res = await fetch(downloadUrl); if (!res.ok || !res.body) { throw new Error(`Failed to download attachment: ${res.status}`); } let filename: string = attachment.name || attachment.gid; const contentType = res.headers.get('content-type') || attachment.mime_type; if (!path.extname(filename) && contentType) { filename += this.extensionForMime(contentType); } const filePath = path.join(outputDir, filename); const fileStream = fs.createWriteStream(filePath); await pipeline(res.body, fileStream); return { attachment_id: attachmentId, file_path: filePath, mime_type: contentType }; }
- src/tools/attachment-tools.ts:59-76 (schema)Defines the Tool object with name, description, and inputSchema for validation in MCP.export const downloadAttachmentTool: Tool = { name: "asana_download_attachment", description: "Download an attachment locally", inputSchema: { type: "object", properties: { attachment_gid: { type: "string", description: "The attachment GID to download" }, output_dir: { type: "string", description: "Directory to save the file (defaults to ~/downloads)" } }, required: ["attachment_gid"] } };
- src/tool-handler.ts:100-103 (registration)Includes downloadAttachmentTool in the exported tools array for MCP tool registration.getAttachmentsForObjectTool, uploadAttachmentForObjectTool, downloadAttachmentTool ];
- src/utils/validation.ts:374-384 (schema)Validation case that checks parameters against the tool's schema.case 'asana_download_attachment': result = validateGid(params.attachment_gid, 'attachment_gid'); if (!result.valid) errors.push(...result.errors); break; } return { valid: errors.length === 0, errors }; }