@pollychrome/joan-mcp 1.2.0 → 1.3.1

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.
Files changed (40) hide show
  1. package/dist/client/api-client.d.ts +55 -1
  2. package/dist/client/api-client.d.ts.map +1 -1
  3. package/dist/client/api-client.js +181 -3
  4. package/dist/client/api-client.js.map +1 -1
  5. package/dist/client/types.d.ts +147 -1
  6. package/dist/client/types.d.ts.map +1 -1
  7. package/dist/index.d.ts.map +1 -1
  8. package/dist/index.js +11 -1
  9. package/dist/index.js.map +1 -1
  10. package/dist/resources/attachments.d.ts +7 -0
  11. package/dist/resources/attachments.d.ts.map +1 -0
  12. package/dist/resources/attachments.js +145 -0
  13. package/dist/resources/attachments.js.map +1 -0
  14. package/dist/resources/comments.d.ts +7 -0
  15. package/dist/resources/comments.d.ts.map +1 -0
  16. package/dist/resources/comments.js +39 -0
  17. package/dist/resources/comments.js.map +1 -0
  18. package/dist/resources/index.d.ts +3 -1
  19. package/dist/resources/index.d.ts.map +1 -1
  20. package/dist/resources/index.js +5 -1
  21. package/dist/resources/index.js.map +1 -1
  22. package/dist/tools/attachments.d.ts +7 -0
  23. package/dist/tools/attachments.d.ts.map +1 -0
  24. package/dist/tools/attachments.js +428 -0
  25. package/dist/tools/attachments.js.map +1 -0
  26. package/dist/tools/comments.d.ts +7 -0
  27. package/dist/tools/comments.d.ts.map +1 -0
  28. package/dist/tools/comments.js +188 -0
  29. package/dist/tools/comments.js.map +1 -0
  30. package/dist/tools/index.d.ts +4 -1
  31. package/dist/tools/index.d.ts.map +1 -1
  32. package/dist/tools/index.js +7 -1
  33. package/dist/tools/index.js.map +1 -1
  34. package/dist/tools/resources.d.ts +7 -0
  35. package/dist/tools/resources.d.ts.map +1 -0
  36. package/dist/tools/resources.js +380 -0
  37. package/dist/tools/resources.js.map +1 -0
  38. package/dist/tools/tasks.js +4 -4
  39. package/dist/tools/tasks.js.map +1 -1
  40. package/package.json +1 -1
@@ -0,0 +1,428 @@
1
+ /**
2
+ * MCP tools for file attachment management
3
+ */
4
+ import { readFile } from 'fs/promises';
5
+ import { basename, extname } from 'path';
6
+ import { z } from 'zod';
7
+ import { formatErrorForMcp } from '../utils/errors.js';
8
+ // Common MIME type mappings
9
+ const MIME_TYPES = {
10
+ // Documents
11
+ '.pdf': 'application/pdf',
12
+ '.doc': 'application/msword',
13
+ '.docx': 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
14
+ '.xls': 'application/vnd.ms-excel',
15
+ '.xlsx': 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
16
+ '.ppt': 'application/vnd.ms-powerpoint',
17
+ '.pptx': 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
18
+ '.txt': 'text/plain',
19
+ '.rtf': 'application/rtf',
20
+ '.csv': 'text/csv',
21
+ '.md': 'text/markdown',
22
+ // Images
23
+ '.png': 'image/png',
24
+ '.jpg': 'image/jpeg',
25
+ '.jpeg': 'image/jpeg',
26
+ '.gif': 'image/gif',
27
+ '.svg': 'image/svg+xml',
28
+ '.webp': 'image/webp',
29
+ '.bmp': 'image/bmp',
30
+ // Videos
31
+ '.mp4': 'video/mp4',
32
+ '.mov': 'video/quicktime',
33
+ '.avi': 'video/x-msvideo',
34
+ '.mkv': 'video/x-matroska',
35
+ '.webm': 'video/webm',
36
+ // Audio
37
+ '.mp3': 'audio/mpeg',
38
+ '.wav': 'audio/wav',
39
+ '.ogg': 'audio/ogg',
40
+ '.m4a': 'audio/mp4',
41
+ '.flac': 'audio/flac',
42
+ // Archives
43
+ '.zip': 'application/zip',
44
+ '.tar': 'application/x-tar',
45
+ '.gz': 'application/gzip',
46
+ '.rar': 'application/vnd.rar',
47
+ '.7z': 'application/x-7z-compressed',
48
+ // Code
49
+ '.json': 'application/json',
50
+ '.js': 'text/javascript',
51
+ '.ts': 'text/typescript',
52
+ '.html': 'text/html',
53
+ '.css': 'text/css',
54
+ '.py': 'text/x-python',
55
+ '.java': 'text/x-java-source',
56
+ '.go': 'text/x-go',
57
+ '.rs': 'text/x-rust',
58
+ '.rb': 'text/x-ruby',
59
+ '.php': 'text/x-php',
60
+ '.c': 'text/x-c',
61
+ '.cpp': 'text/x-c++src',
62
+ '.h': 'text/x-c',
63
+ '.yaml': 'text/yaml',
64
+ '.yml': 'text/yaml',
65
+ '.xml': 'application/xml',
66
+ };
67
+ function getMimeType(filename) {
68
+ const ext = extname(filename).toLowerCase();
69
+ return MIME_TYPES[ext] || 'application/octet-stream';
70
+ }
71
+ function formatFileSize(bytes) {
72
+ if (bytes < 1024)
73
+ return `${bytes} B`;
74
+ if (bytes < 1024 * 1024)
75
+ return `${(bytes / 1024).toFixed(1)} KB`;
76
+ if (bytes < 1024 * 1024 * 1024)
77
+ return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
78
+ return `${(bytes / (1024 * 1024 * 1024)).toFixed(2)} GB`;
79
+ }
80
+ export function registerAttachmentTools(server, client) {
81
+ // Upload Attachment
82
+ server.tool('upload_attachment', 'Upload a file attachment to Joan. Supports local file paths, base64-encoded content, or URLs to fetch from.', {
83
+ // File source (one of these is required)
84
+ file_path: z.string().optional().describe('Local file path to upload'),
85
+ file_base64: z.string().optional().describe('Base64-encoded file content'),
86
+ file_url: z.string().url().optional().describe('URL to fetch file from'),
87
+ // Required filename for base64/URL sources
88
+ filename: z.string().optional().describe('Filename (required for base64/URL, auto-detected for file_path)'),
89
+ // Attachment target (entity)
90
+ entity_type: z.enum(['project', 'milestone', 'task', 'note', 'folder', 'user']).optional()
91
+ .describe('Entity type to attach to'),
92
+ entity_id: z.string().uuid().optional().describe('Entity ID to attach to'),
93
+ // Metadata
94
+ display_name: z.string().optional().describe('Custom display name'),
95
+ description: z.string().optional().describe('Description of the attachment'),
96
+ category: z.enum(['document', 'image', 'video', 'audio', 'archive', 'code', 'other']).optional()
97
+ .describe('Category (auto-detected from MIME type if not specified)'),
98
+ tags: z.array(z.string()).optional().describe('Tags for organization'),
99
+ }, async (input) => {
100
+ try {
101
+ let fileBuffer;
102
+ let filename;
103
+ let mimeType;
104
+ // Determine file source and load content
105
+ if (input.file_path) {
106
+ // Local file path
107
+ fileBuffer = await readFile(input.file_path);
108
+ filename = input.filename || basename(input.file_path);
109
+ mimeType = getMimeType(filename);
110
+ }
111
+ else if (input.file_base64) {
112
+ // Base64 encoded content
113
+ if (!input.filename) {
114
+ return {
115
+ content: [{
116
+ type: 'text',
117
+ text: 'Error: filename is required when using file_base64',
118
+ }],
119
+ };
120
+ }
121
+ fileBuffer = Buffer.from(input.file_base64, 'base64');
122
+ filename = input.filename;
123
+ mimeType = getMimeType(filename);
124
+ }
125
+ else if (input.file_url) {
126
+ // Fetch from URL
127
+ const response = await fetch(input.file_url);
128
+ if (!response.ok) {
129
+ return {
130
+ content: [{
131
+ type: 'text',
132
+ text: `Error: Failed to fetch file from URL (${response.status})`,
133
+ }],
134
+ };
135
+ }
136
+ const arrayBuffer = await response.arrayBuffer();
137
+ fileBuffer = Buffer.from(arrayBuffer);
138
+ // Try to get filename from URL or Content-Disposition
139
+ const contentDisposition = response.headers.get('Content-Disposition');
140
+ if (input.filename) {
141
+ filename = input.filename;
142
+ }
143
+ else if (contentDisposition) {
144
+ const match = contentDisposition.match(/filename="?([^";\n]+)"?/);
145
+ filename = match?.[1] || basename(new URL(input.file_url).pathname) || 'downloaded_file';
146
+ }
147
+ else {
148
+ filename = basename(new URL(input.file_url).pathname) || 'downloaded_file';
149
+ }
150
+ // Try to get MIME type from response or filename
151
+ mimeType = response.headers.get('Content-Type')?.split(';')[0] || getMimeType(filename);
152
+ }
153
+ else {
154
+ return {
155
+ content: [{
156
+ type: 'text',
157
+ text: 'Error: Must provide one of file_path, file_base64, or file_url',
158
+ }],
159
+ };
160
+ }
161
+ // Validate entity_type and entity_id are both provided or both omitted
162
+ if ((input.entity_type && !input.entity_id) || (!input.entity_type && input.entity_id)) {
163
+ return {
164
+ content: [{
165
+ type: 'text',
166
+ text: 'Error: Both entity_type and entity_id must be provided together',
167
+ }],
168
+ };
169
+ }
170
+ // Build metadata
171
+ const metadata = {
172
+ entity_type: input.entity_type,
173
+ entity_id: input.entity_id,
174
+ display_name: input.display_name,
175
+ description: input.description,
176
+ category: input.category,
177
+ tags: input.tags,
178
+ };
179
+ const attachment = await client.uploadAttachment(fileBuffer, filename, mimeType, metadata);
180
+ return {
181
+ content: [{
182
+ type: 'text',
183
+ text: `Uploaded "${attachment.display_name}" (ID: ${attachment.id})\n` +
184
+ ` File: ${attachment.filename}\n` +
185
+ ` Size: ${formatFileSize(attachment.size)}\n` +
186
+ ` Type: ${attachment.mime_type}\n` +
187
+ ` Category: ${attachment.category}` +
188
+ (attachment.entity_type ? `\n Attached to: ${attachment.entity_type} (${attachment.entity_id})` : ''),
189
+ }],
190
+ };
191
+ }
192
+ catch (error) {
193
+ return { content: formatErrorForMcp(error) };
194
+ }
195
+ });
196
+ // List Attachments
197
+ server.tool('list_attachments', 'List file attachments for a specific entity (project, milestone, task, note, folder, or user).', {
198
+ entity_type: z.enum(['project', 'milestone', 'task', 'note', 'folder', 'user'])
199
+ .describe('Entity type'),
200
+ entity_id: z.string().uuid().describe('Entity ID'),
201
+ }, async (input) => {
202
+ try {
203
+ const attachments = await client.listAttachmentsByEntity(input.entity_type, input.entity_id);
204
+ if (attachments.length === 0) {
205
+ return {
206
+ content: [{
207
+ type: 'text',
208
+ text: `No attachments found for ${input.entity_type} ${input.entity_id}`,
209
+ }],
210
+ };
211
+ }
212
+ const lines = attachments.map(a => `- ${a.display_name} (ID: ${a.id})\n` +
213
+ ` File: ${a.filename} | Size: ${formatFileSize(a.size)} | Type: ${a.category}`);
214
+ return {
215
+ content: [{
216
+ type: 'text',
217
+ text: `Attachments for ${input.entity_type} ${input.entity_id}:\n\n${lines.join('\n\n')}`,
218
+ }],
219
+ };
220
+ }
221
+ catch (error) {
222
+ return { content: formatErrorForMcp(error) };
223
+ }
224
+ });
225
+ // Get Attachment
226
+ server.tool('get_attachment', 'Get metadata for a specific attachment.', {
227
+ attachment_id: z.string().uuid().describe('Attachment ID'),
228
+ }, async (input) => {
229
+ try {
230
+ const attachment = await client.getAttachmentMetadata(input.attachment_id);
231
+ const info = [
232
+ `Attachment: ${attachment.display_name}`,
233
+ `ID: ${attachment.id}`,
234
+ `Filename: ${attachment.filename}`,
235
+ `Size: ${formatFileSize(attachment.size)}`,
236
+ `MIME Type: ${attachment.mime_type}`,
237
+ `Category: ${attachment.category}`,
238
+ ];
239
+ if (attachment.description) {
240
+ info.push(`Description: ${attachment.description}`);
241
+ }
242
+ if (attachment.tags && attachment.tags.length > 0) {
243
+ info.push(`Tags: ${attachment.tags.join(', ')}`);
244
+ }
245
+ if (attachment.entity_type) {
246
+ info.push(`Attached to: ${attachment.entity_type} (${attachment.entity_id})`);
247
+ }
248
+ info.push(`Uploaded: ${attachment.uploaded_at}`);
249
+ info.push(`Download URL: ${attachment.url}`);
250
+ return {
251
+ content: [{
252
+ type: 'text',
253
+ text: info.join('\n'),
254
+ }],
255
+ };
256
+ }
257
+ catch (error) {
258
+ return { content: formatErrorForMcp(error) };
259
+ }
260
+ });
261
+ // Update Attachment
262
+ server.tool('update_attachment', 'Update metadata for an attachment.', {
263
+ attachment_id: z.string().uuid().describe('Attachment ID'),
264
+ display_name: z.string().optional().describe('New display name'),
265
+ description: z.string().optional().describe('New description'),
266
+ category: z.enum(['document', 'image', 'video', 'audio', 'archive', 'code', 'other']).optional()
267
+ .describe('New category'),
268
+ tags: z.array(z.string()).optional().describe('New tags'),
269
+ }, async (input) => {
270
+ try {
271
+ const updateData = {};
272
+ if (input.display_name !== undefined)
273
+ updateData.display_name = input.display_name;
274
+ if (input.description !== undefined)
275
+ updateData.description = input.description;
276
+ if (input.category !== undefined)
277
+ updateData.category = input.category;
278
+ if (input.tags !== undefined)
279
+ updateData.tags = input.tags;
280
+ const attachment = await client.updateAttachment(input.attachment_id, updateData);
281
+ return {
282
+ content: [{
283
+ type: 'text',
284
+ text: `Updated attachment "${attachment.display_name}" (ID: ${attachment.id})`,
285
+ }],
286
+ };
287
+ }
288
+ catch (error) {
289
+ return { content: formatErrorForMcp(error) };
290
+ }
291
+ });
292
+ // Delete Attachment
293
+ server.tool('delete_attachment', 'Delete an attachment.', {
294
+ attachment_id: z.string().uuid().describe('Attachment ID'),
295
+ }, async (input) => {
296
+ try {
297
+ await client.deleteAttachment(input.attachment_id);
298
+ return {
299
+ content: [{
300
+ type: 'text',
301
+ text: `Attachment ${input.attachment_id} deleted`,
302
+ }],
303
+ };
304
+ }
305
+ catch (error) {
306
+ return { content: formatErrorForMcp(error) };
307
+ }
308
+ });
309
+ // Get Attachment Download URL
310
+ server.tool('get_attachment_download_url', 'Get a download URL for an attachment.', {
311
+ attachment_id: z.string().uuid().describe('Attachment ID'),
312
+ expires_in: z.number().optional().describe('URL expiration time in seconds (default: 3600)'),
313
+ }, async (input) => {
314
+ try {
315
+ const info = await client.getAttachmentDownloadUrl(input.attachment_id, input.expires_in);
316
+ return {
317
+ content: [{
318
+ type: 'text',
319
+ text: `Download URL for "${info.display_name}":\n` +
320
+ ` URL: ${info.download_url}\n` +
321
+ ` Expires: ${info.expires_at}\n` +
322
+ ` Size: ${formatFileSize(info.file_size)}`,
323
+ }],
324
+ };
325
+ }
326
+ catch (error) {
327
+ return { content: formatErrorForMcp(error) };
328
+ }
329
+ });
330
+ // Get Project Attachment Hierarchy
331
+ server.tool('get_project_attachment_hierarchy', 'Get all attachments organized by project hierarchy (project → milestones → tasks).', {
332
+ project_id: z.string().uuid().describe('Project ID'),
333
+ }, async (input) => {
334
+ try {
335
+ const hierarchy = await client.getProjectAttachmentHierarchy(input.project_id);
336
+ const lines = [];
337
+ lines.push(`Project Attachments (${hierarchy.total_files} total files)`);
338
+ lines.push('');
339
+ // Project level
340
+ if (hierarchy.project.attachments.length > 0) {
341
+ lines.push(`📁 Project (${hierarchy.project.attachments.length} files)`);
342
+ for (const a of hierarchy.project.attachments) {
343
+ lines.push(` - ${a.display_name} (${formatFileSize(a.size)})`);
344
+ }
345
+ lines.push('');
346
+ }
347
+ // Milestones
348
+ for (const milestone of hierarchy.milestones) {
349
+ const milestoneFiles = milestone.attachments.length +
350
+ milestone.tasks.reduce((sum, t) => sum + t.attachments.length, 0);
351
+ if (milestoneFiles > 0) {
352
+ lines.push(`🎯 ${milestone.name} [${milestone.status}] (${milestoneFiles} files)`);
353
+ for (const a of milestone.attachments) {
354
+ lines.push(` - ${a.display_name} (${formatFileSize(a.size)})`);
355
+ }
356
+ for (const task of milestone.tasks) {
357
+ if (task.attachments.length > 0) {
358
+ lines.push(` 📋 ${task.title} [${task.status}]`);
359
+ for (const a of task.attachments) {
360
+ lines.push(` - ${a.display_name} (${formatFileSize(a.size)})`);
361
+ }
362
+ }
363
+ }
364
+ lines.push('');
365
+ }
366
+ }
367
+ // Unassigned tasks
368
+ if (hierarchy.unassigned_tasks.length > 0) {
369
+ const unassignedFiles = hierarchy.unassigned_tasks.reduce((sum, t) => sum + t.attachments.length, 0);
370
+ if (unassignedFiles > 0) {
371
+ lines.push(`📋 Unassigned Tasks (${unassignedFiles} files)`);
372
+ for (const task of hierarchy.unassigned_tasks) {
373
+ if (task.attachments.length > 0) {
374
+ lines.push(` ${task.title} [${task.status}]`);
375
+ for (const a of task.attachments) {
376
+ lines.push(` - ${a.display_name} (${formatFileSize(a.size)})`);
377
+ }
378
+ }
379
+ }
380
+ }
381
+ }
382
+ return {
383
+ content: [{
384
+ type: 'text',
385
+ text: lines.join('\n'),
386
+ }],
387
+ };
388
+ }
389
+ catch (error) {
390
+ return { content: formatErrorForMcp(error) };
391
+ }
392
+ });
393
+ // Get Storage Usage
394
+ server.tool('get_storage_usage', 'Get storage usage statistics for your account.', {}, async () => {
395
+ try {
396
+ const usage = await client.getStorageUsage();
397
+ const lines = [
398
+ 'Storage Usage',
399
+ '─'.repeat(30),
400
+ `Total Files: ${usage.total_files}`,
401
+ `Total Size: ${usage.total_size_mb.toFixed(2)} MB`,
402
+ `Usage: ${usage.usage_percentage}% of ${usage.storage_limit_mb} MB limit`,
403
+ '',
404
+ 'By Entity Type:',
405
+ ];
406
+ for (const [type, count] of Object.entries(usage.by_entity)) {
407
+ lines.push(` ${type}: ${count} files`);
408
+ }
409
+ if (usage.by_type.length > 0) {
410
+ lines.push('');
411
+ lines.push('By File Type:');
412
+ for (const item of usage.by_type) {
413
+ lines.push(` ${item.file_type}: ${item.count} files (${formatFileSize(item.total_size)})`);
414
+ }
415
+ }
416
+ return {
417
+ content: [{
418
+ type: 'text',
419
+ text: lines.join('\n'),
420
+ }],
421
+ };
422
+ }
423
+ catch (error) {
424
+ return { content: formatErrorForMcp(error) };
425
+ }
426
+ });
427
+ }
428
+ //# sourceMappingURL=attachments.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"attachments.js","sourceRoot":"","sources":["../../src/tools/attachments.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ /**
2
+ * MCP tools for comment management on tasks and milestones
3
+ */
4
+ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
5
+ import type { JoanApiClient } from '../client/api-client.js';
6
+ export declare function registerCommentTools(server: McpServer, client: JoanApiClient): void;
7
+ //# sourceMappingURL=comments.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"comments.d.ts","sourceRoot":"","sources":["../../src/tools/comments.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAEzE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AAG7D,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,aAAa,GAAG,IAAI,CA0PnF"}
@@ -0,0 +1,188 @@
1
+ /**
2
+ * MCP tools for comment management on tasks and milestones
3
+ */
4
+ import { z } from 'zod';
5
+ import { formatErrorForMcp } from '../utils/errors.js';
6
+ export function registerCommentTools(server, client) {
7
+ // ============ Task Comments ============
8
+ // List Task Comments
9
+ server.tool('list_task_comments', 'List all comments on a specific task.', {
10
+ task_id: z.string().uuid().describe('Task ID to list comments for'),
11
+ }, async (input) => {
12
+ try {
13
+ const comments = await client.listTaskComments(input.task_id);
14
+ if (comments.length === 0) {
15
+ return {
16
+ content: [{
17
+ type: 'text',
18
+ text: `No comments found for task ${input.task_id}.`,
19
+ }],
20
+ };
21
+ }
22
+ const commentList = comments.map(c => {
23
+ const author = c.user_name || 'Unknown';
24
+ const date = new Date(c.created_at).toLocaleString();
25
+ return `- **${author}** (${date}):\n "${c.content}"`;
26
+ }).join('\n\n');
27
+ return {
28
+ content: [{
29
+ type: 'text',
30
+ text: `Found ${comments.length} comment(s):\n\n${commentList}`,
31
+ }],
32
+ };
33
+ }
34
+ catch (error) {
35
+ return { content: formatErrorForMcp(error) };
36
+ }
37
+ });
38
+ // Create Task Comment
39
+ server.tool('create_task_comment', 'Add a comment to a task.', {
40
+ task_id: z.string().uuid().describe('Task ID to comment on'),
41
+ content: z.string().min(1).max(10000).describe('Comment content (supports markdown)'),
42
+ }, async (input) => {
43
+ try {
44
+ const comment = await client.createTaskComment(input.task_id, {
45
+ content: input.content,
46
+ });
47
+ return {
48
+ content: [{
49
+ type: 'text',
50
+ text: `Comment added to task ${input.task_id}.\nComment ID: ${comment.id}`,
51
+ }],
52
+ };
53
+ }
54
+ catch (error) {
55
+ return { content: formatErrorForMcp(error) };
56
+ }
57
+ });
58
+ // Update Task Comment
59
+ server.tool('update_task_comment', 'Edit an existing comment on a task. Only the comment author or project admins can edit.', {
60
+ task_id: z.string().uuid().describe('Task ID'),
61
+ comment_id: z.string().uuid().describe('Comment ID to update'),
62
+ content: z.string().min(1).max(10000).describe('New comment content'),
63
+ }, async (input) => {
64
+ try {
65
+ await client.updateTaskComment(input.task_id, input.comment_id, {
66
+ content: input.content,
67
+ });
68
+ return {
69
+ content: [{
70
+ type: 'text',
71
+ text: `Comment ${input.comment_id} updated successfully.`,
72
+ }],
73
+ };
74
+ }
75
+ catch (error) {
76
+ return { content: formatErrorForMcp(error) };
77
+ }
78
+ });
79
+ // Delete Task Comment
80
+ server.tool('delete_task_comment', 'Remove a comment from a task. Only the comment author or project admins can delete.', {
81
+ task_id: z.string().uuid().describe('Task ID'),
82
+ comment_id: z.string().uuid().describe('Comment ID to delete'),
83
+ }, async (input) => {
84
+ try {
85
+ await client.deleteTaskComment(input.task_id, input.comment_id);
86
+ return {
87
+ content: [{
88
+ type: 'text',
89
+ text: `Comment ${input.comment_id} deleted from task ${input.task_id}.`,
90
+ }],
91
+ };
92
+ }
93
+ catch (error) {
94
+ return { content: formatErrorForMcp(error) };
95
+ }
96
+ });
97
+ // ============ Milestone Comments ============
98
+ // List Milestone Comments
99
+ server.tool('list_milestone_comments', 'List all comments on a specific milestone.', {
100
+ project_id: z.string().uuid().describe('Project ID'),
101
+ milestone_id: z.string().uuid().describe('Milestone ID to list comments for'),
102
+ }, async (input) => {
103
+ try {
104
+ const comments = await client.listMilestoneComments(input.project_id, input.milestone_id);
105
+ if (comments.length === 0) {
106
+ return {
107
+ content: [{
108
+ type: 'text',
109
+ text: `No comments found for milestone ${input.milestone_id}.`,
110
+ }],
111
+ };
112
+ }
113
+ const commentList = comments.map(c => {
114
+ const author = c.user_name || 'Unknown';
115
+ const date = new Date(c.created_at).toLocaleString();
116
+ return `- **${author}** (${date}):\n "${c.content}"`;
117
+ }).join('\n\n');
118
+ return {
119
+ content: [{
120
+ type: 'text',
121
+ text: `Found ${comments.length} comment(s):\n\n${commentList}`,
122
+ }],
123
+ };
124
+ }
125
+ catch (error) {
126
+ return { content: formatErrorForMcp(error) };
127
+ }
128
+ });
129
+ // Create Milestone Comment
130
+ server.tool('create_milestone_comment', 'Add a comment to a milestone.', {
131
+ project_id: z.string().uuid().describe('Project ID'),
132
+ milestone_id: z.string().uuid().describe('Milestone ID to comment on'),
133
+ content: z.string().min(1).max(10000).describe('Comment content (supports markdown)'),
134
+ }, async (input) => {
135
+ try {
136
+ const comment = await client.createMilestoneComment(input.project_id, input.milestone_id, { content: input.content });
137
+ return {
138
+ content: [{
139
+ type: 'text',
140
+ text: `Comment added to milestone ${input.milestone_id}.\nComment ID: ${comment.id}`,
141
+ }],
142
+ };
143
+ }
144
+ catch (error) {
145
+ return { content: formatErrorForMcp(error) };
146
+ }
147
+ });
148
+ // Update Milestone Comment
149
+ server.tool('update_milestone_comment', 'Edit an existing comment on a milestone. Only the comment author or project admins can edit.', {
150
+ project_id: z.string().uuid().describe('Project ID'),
151
+ milestone_id: z.string().uuid().describe('Milestone ID'),
152
+ comment_id: z.string().uuid().describe('Comment ID to update'),
153
+ content: z.string().min(1).max(10000).describe('New comment content'),
154
+ }, async (input) => {
155
+ try {
156
+ await client.updateMilestoneComment(input.project_id, input.milestone_id, input.comment_id, { content: input.content });
157
+ return {
158
+ content: [{
159
+ type: 'text',
160
+ text: `Comment ${input.comment_id} updated successfully.`,
161
+ }],
162
+ };
163
+ }
164
+ catch (error) {
165
+ return { content: formatErrorForMcp(error) };
166
+ }
167
+ });
168
+ // Delete Milestone Comment
169
+ server.tool('delete_milestone_comment', 'Remove a comment from a milestone. Only the comment author or project admins can delete.', {
170
+ project_id: z.string().uuid().describe('Project ID'),
171
+ milestone_id: z.string().uuid().describe('Milestone ID'),
172
+ comment_id: z.string().uuid().describe('Comment ID to delete'),
173
+ }, async (input) => {
174
+ try {
175
+ await client.deleteMilestoneComment(input.project_id, input.milestone_id, input.comment_id);
176
+ return {
177
+ content: [{
178
+ type: 'text',
179
+ text: `Comment ${input.comment_id} deleted from milestone ${input.milestone_id}.`,
180
+ }],
181
+ };
182
+ }
183
+ catch (error) {
184
+ return { content: formatErrorForMcp(error) };
185
+ }
186
+ });
187
+ }
188
+ //# sourceMappingURL=comments.js.map