motion_comments
Add or view comments on Motion tasks to track updates and collaborate within the task management platform.
Instructions
Manage comments on tasks
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| operation | Yes | Operation to perform | |
| taskId | Yes | Task ID to comment on or fetch comments from (required) | |
| content | No | Comment content (required for create operation) | |
| cursor | No | Pagination cursor for list operation (optional) |
Implementation Reference
- src/handlers/CommentHandler.ts:1-74 (handler)Main handler for motion_comments tool. Implements two operations: 'list' (retrieves comments with pagination) and 'create' (creates new comments with content sanitization and validation). Extends BaseHandler and uses MotionService for API calls.import { BaseHandler } from './base/BaseHandler'; import { McpToolResponse } from '../types/mcp'; import { MotionCommentsArgs } from '../types/mcp-tool-args'; import { CreateCommentData } from '../types/motion'; import { formatCommentList, formatCommentDetail, LIMITS } from '../utils'; import { sanitizeCommentContent } from '../utils/sanitize'; export class CommentHandler extends BaseHandler { async handle(args: MotionCommentsArgs): Promise<McpToolResponse> { try { const { operation, taskId, content, cursor } = args; switch(operation) { case 'list': return await this.handleList(taskId, cursor); case 'create': return await this.handleCreate(taskId, content); default: return this.handleUnknownOperation(operation); } } catch (error: unknown) { return this.handleError(error); } } private async handleList(taskId?: string, cursor?: string): Promise<McpToolResponse> { if (!taskId) { return this.handleError(new Error('taskId is required for list operation')); } const commentsResponse = await this.motionService.getComments(taskId, cursor); const commentsResult = formatCommentList(commentsResponse.data); // Add pagination info if there's more data if (commentsResponse.meta.nextCursor && commentsResult.content[0]) { const firstContent = commentsResult.content[0]; if ('text' in firstContent && typeof firstContent.text === 'string') { firstContent.text += `\n\nπ More comments available. Use cursor: ${commentsResponse.meta.nextCursor}`; } } return commentsResult; } private async handleCreate(taskId?: string, content?: string): Promise<McpToolResponse> { if (!taskId) { return this.handleError(new Error('taskId is required for create operation')); } if (!content) { return this.handleError(new Error('content is required for create operation')); } // Reject oversized content before sanitization so the check is never dead code if (content.length > LIMITS.COMMENT_MAX_LENGTH) { return this.handleError(new Error(`Comment content exceeds maximum length of ${LIMITS.COMMENT_MAX_LENGTH} characters`)); } // Sanitize and validate comment content const sanitizationResult = sanitizeCommentContent(content); if (!sanitizationResult.isValid) { return this.handleError(new Error(sanitizationResult.error || 'Invalid comment content')); } const sanitizedContent = sanitizationResult.sanitized; const commentData: CreateCommentData = { taskId, content: sanitizedContent }; const newComment = await this.motionService.createComment(commentData); return formatCommentDetail(newComment); } }
- src/tools/ToolDefinitions.ts:261-287 (registration)Tool definition for motion_comments. Defines the tool name, description, and input schema with properties: operation (enum: list/create), taskId (required), content (for create), and cursor (for pagination).export const commentsToolDefinition: McpToolDefinition = { name: TOOL_NAMES.COMMENTS, description: "Manage comments on tasks", inputSchema: { type: "object", properties: { operation: { type: "string", enum: ["list", "create"], description: "Operation to perform" }, taskId: { type: "string", description: "Task ID to comment on or fetch comments from (required)" }, content: { type: "string", description: "Comment content (required for create operation)" }, cursor: { type: "string", description: "Pagination cursor for list operation (optional)" } }, required: ["operation", "taskId"] } };
- src/handlers/HandlerFactory.ts:27-38 (registration)Registers CommentHandler with the TOOL_NAMES.COMMENTS key ('motion_comments') in the handler factory, mapping the tool name to its handler class.private registerHandlers(): void { this.handlers.set(TOOL_NAMES.TASKS, TaskHandler); this.handlers.set(TOOL_NAMES.PROJECTS, ProjectHandler); this.handlers.set(TOOL_NAMES.WORKSPACES, WorkspaceHandler); this.handlers.set(TOOL_NAMES.USERS, UserHandler); this.handlers.set(TOOL_NAMES.SEARCH, SearchHandler); this.handlers.set(TOOL_NAMES.COMMENTS, CommentHandler); this.handlers.set(TOOL_NAMES.CUSTOM_FIELDS, CustomFieldHandler); this.handlers.set(TOOL_NAMES.RECURRING_TASKS, RecurringTaskHandler); this.handlers.set(TOOL_NAMES.SCHEDULES, ScheduleHandler); this.handlers.set(TOOL_NAMES.STATUSES, StatusHandler); }
- src/types/mcp-tool-args.ts:71-78 (schema)TypeScript interface for MotionCommentsArgs defining the operation type ('list' | 'create'), required taskId string, optional content string, and optional cursor for pagination.export type CommentsOperation = 'list' | 'create'; export interface MotionCommentsArgs { operation: CommentsOperation; taskId: string; content?: string; cursor?: string; }
- Helper functions for formatting comment responses: formatCommentList formats multiple comments with truncation and display limits, formatCommentDetail formats single comment creation responses with metadata.export function formatCommentList(comments: MotionComment[]): CallToolResult { if (comments.length === 0) { return formatMcpSuccess("No comments found."); } const commentFormatter = (comment: MotionComment) => { const location = `Task ${comment.taskId}`; const timestamp = comment.createdAt; const author = comment.creator.name || comment.creator.email || comment.creator.id; // Truncate long comments for display const displayContent = comment.content.length > LIMITS.COMMENT_DISPLAY_LENGTH ? comment.content.substring(0, LIMITS.COMMENT_DISPLAY_LENGTH) + '...' : comment.content; // Keep as single line for proper list formatting return `- [${comment.id}] ${location} | Author: ${author} | ${timestamp} | "${displayContent}"`; }; return formatListResponse( comments, `Found ${comments.length} comment${comments.length === 1 ? '' : 's'}`, commentFormatter ); } /** * Format single comment response */ export function formatCommentDetail(comment: MotionComment): CallToolResult { const location = `Task ${comment.taskId}`; const timestamp = comment.createdAt; const author = comment.creator.name || comment.creator.email || comment.creator.id; const details = [ `Comment created successfully:`, `- ID: ${comment.id}`, `- Location: ${location}`, `- Author: ${author}`, `- Created: ${timestamp}`, `- Content: "${comment.content}"` ].join('\n'); return formatMcpSuccess(details); }