Skip to main content
Glama
index.js388 kB
#!/usr/bin/env node /** * TickTick MCP Server - Docker Version * Based on jen6/ticktick-mcp with Docker integration and SDK 1.15.0 */ import { Server } from '@modelcontextprotocol/sdk/server/index.js'; import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; import { CallToolRequestSchema, ListToolsRequestSchema, ErrorCode, McpError } from '@modelcontextprotocol/sdk/types.js'; import fs from 'fs'; import path from 'path'; import os from 'os'; // Environment configuration const TICKTICK_CLIENT_ID = process.env.TICKTICK_CLIENT_ID; const TICKTICK_CLIENT_SECRET = process.env.TICKTICK_CLIENT_SECRET; const TICKTICK_TOKEN = process.env.TICKTICK_TOKEN; const TICKTICK_ACCESS_TOKEN = process.env.TICKTICK_ACCESS_TOKEN; const TICKTICK_AUTH_CODE = process.env.TICKTICK_AUTH_CODE; // Cache configuration const CACHE_FILE_PATH = path.join(os.homedir(), '.ticktick-mcp-cache.json'); const CACHE_TTL = 24 * 60 * 60 * 1000; // 24 hours in milliseconds class TickTickMCPServer { constructor() { this.server = new Server( { name: 'ticktick-mcp', version: '1.0.0', }, { capabilities: { tools: {}, }, } ); this.initializeCache(); this.setupHandlers(); } // Cache management methods initializeCache() { try { if (!fs.existsSync(CACHE_FILE_PATH)) { this.saveCache({ tasks: {} }); } } catch (error) { console.warn('Failed to initialize cache:', error.message); // Continue without cache if there's an issue } } loadCache() { try { if (fs.existsSync(CACHE_FILE_PATH)) { const data = fs.readFileSync(CACHE_FILE_PATH, 'utf8'); return JSON.parse(data); } } catch (error) { console.warn('Failed to load cache:', error.message); } return { tasks: {} }; } saveCache(data) { try { fs.writeFileSync(CACHE_FILE_PATH, JSON.stringify(data, null, 2)); } catch (error) { console.warn('Failed to save cache:', error.message); } } isTaskStale(task) { if (!task.cached_at) return true; return Date.now() - new Date(task.cached_at) > CACHE_TTL; } addTaskToCache(taskId, projectId, title) { try { const cache = this.loadCache(); cache.tasks[taskId] = { project_id: projectId, title: title, cached_at: new Date().toISOString() }; this.saveCache(cache); } catch (error) { console.warn('Failed to add task to cache:', error.message); } } setupHandlers() { // List available tools this.server.setRequestHandler(ListToolsRequestSchema, async () => { return { tools: [ { name: 'ticktick_get_projects', description: 'Get all projects from TickTick', inputSchema: { type: 'object', properties: { include_archived: { type: 'boolean', description: 'Include archived projects', default: false } } } }, { name: 'ticktick_create_project', description: 'Create a new project in TickTick', inputSchema: { type: 'object', properties: { name: { type: 'string', description: 'Name of the project' }, color: { type: 'string', description: 'Project color (hex code)', default: '#3498db' }, is_shared: { type: 'boolean', description: 'Whether the project is shared', default: false } }, required: ['name'] } }, { name: 'ticktick_get_task_details', description: 'Get specific task details using project ID and task ID', inputSchema: { type: 'object', properties: { project_id: { type: 'string', description: 'Project ID containing the task' }, task_id: { type: 'string', description: 'Specific task ID to retrieve' } }, required: ['project_id', 'task_id'] } }, { name: 'ticktick_create_task', description: 'Create a new task in TickTick', inputSchema: { type: 'object', properties: { title: { type: 'string', description: 'Task title' }, content: { type: 'string', description: 'Task description/content' }, project_id: { type: 'string', description: 'Project ID to add task to' }, priority: { type: 'number', description: 'Task priority (0=None, 1=Low, 3=Medium, 5=High)', default: 0 }, due_date: { type: 'string', description: 'Due date in ISO format (YYYY-MM-DDTHH:mm:ss)' }, tags: { type: 'array', items: { type: 'string' }, description: 'Tags for the task' } }, required: ['title'] } }, { name: 'ticktick_update_task', description: 'Update an existing task', inputSchema: { type: 'object', properties: { task_id: { type: 'string', description: 'ID of the task to update' }, title: { type: 'string', description: 'New task title' }, content: { type: 'string', description: 'New task description' }, priority: { type: 'number', description: 'New priority level' }, due_date: { type: 'string', description: 'New due date' }, completed: { type: 'boolean', description: 'Mark as completed/incomplete' } }, required: ['task_id'] } }, { name: 'ticktick_delete_task', description: 'Delete a task from TickTick', inputSchema: { type: 'object', properties: { task_id: { type: 'string', description: 'ID of the task to delete' } }, required: ['task_id'] } }, { name: 'ticktick_complete_task', description: 'Mark a task as completed', inputSchema: { type: 'object', properties: { task_id: { type: 'string', description: 'ID of the task to complete' } }, required: ['task_id'] } }, { name: 'ticktick_get_task_details', description: 'Get detailed information about a specific task', inputSchema: { type: 'object', properties: { task_id: { type: 'string', description: 'ID of the task' } }, required: ['task_id'] } }, { name: 'ticktick_filter_tasks', description: 'Filter tasks by various criteria', inputSchema: { type: 'object', properties: { keywords: { type: 'string', description: 'Keywords to search for in task titles/content' }, tags: { type: 'array', items: { type: 'string' }, description: 'Filter by tags' }, priority: { type: 'number', description: 'Filter by priority level' }, due_before: { type: 'string', description: 'Tasks due before this date' }, due_after: { type: 'string', description: 'Tasks due after this date' } } } }, { name: 'ticktick_convert_datetime_to_ticktick_format', description: 'Convert datetime to TickTick API format', inputSchema: { type: 'object', properties: { datetime_string: { type: 'string', description: 'Human-readable datetime string' }, timezone: { type: 'string', description: 'Timezone (e.g., America/New_York)', default: 'UTC' } }, required: ['datetime_string'] } }, { name: 'ticktick_get_tags', description: 'Get all tags from TickTick', inputSchema: { type: 'object', properties: {} } }, { name: 'ticktick_create_tag', description: 'Create a new tag in TickTick', inputSchema: { type: 'object', properties: { name: { type: 'string', description: 'Name of the tag' }, color: { type: 'string', description: 'Color of the tag (hex code)', default: '#3498db' } }, required: ['name'] } }, { name: 'ticktick_search_tasks', description: 'Advanced search for tasks with text query', inputSchema: { type: 'object', properties: { query: { type: 'string', description: 'Search query text' }, limit: { type: 'number', description: 'Maximum number of results', default: 20 } }, required: ['query'] } }, { name: 'ticktick_get_today_tasks', description: 'Get tasks scheduled for today', inputSchema: { type: 'object', properties: { include_overdue: { type: 'boolean', description: 'Include overdue tasks', default: true } } } }, { name: 'ticktick_get_overdue_tasks', description: 'Get all overdue tasks', inputSchema: { type: 'object', properties: { limit: { type: 'number', description: 'Maximum number of results', default: 50 } } } }, { name: 'ticktick_get_upcoming_tasks', description: 'Get upcoming tasks within specified days', inputSchema: { type: 'object', properties: { days_ahead: { type: 'number', description: 'Number of days to look ahead', default: 7 }, limit: { type: 'number', description: 'Maximum number of results', default: 30 } } } }, { name: 'ticktick_add_tag_to_task', description: 'Add a tag to a specific task', inputSchema: { type: 'object', properties: { task_id: { type: 'string', description: 'ID of the task' }, tag_name: { type: 'string', description: 'Name of the tag to add' } }, required: ['task_id', 'tag_name'] } }, { name: 'ticktick_get_user_profile', description: 'Get user profile information', inputSchema: { type: 'object', properties: {} } }, { name: 'ticktick_get_habits', description: 'Get all habits from TickTick', inputSchema: { type: 'object', properties: { include_archived: { type: 'boolean', description: 'Include archived habits', default: false } } } }, { name: 'ticktick_create_habit', description: 'Create a new habit in TickTick', inputSchema: { type: 'object', properties: { name: { type: 'string', description: 'Name of the habit' }, frequency: { type: 'string', description: 'Frequency: daily, weekly, or custom', default: 'daily' }, goal: { type: 'number', description: 'Target count per frequency period', default: 1 }, reminder_time: { type: 'string', description: 'Reminder time in HH:MM format' }, color: { type: 'string', description: 'Habit color (hex code)', default: '#3498db' } }, required: ['name'] } }, { name: 'ticktick_update_habit', description: 'Update an existing habit', inputSchema: { type: 'object', properties: { habit_id: { type: 'string', description: 'ID of the habit to update' }, name: { type: 'string', description: 'Updated name' }, frequency: { type: 'string', description: 'Updated frequency' }, goal: { type: 'number', description: 'Updated goal count' }, reminder_time: { type: 'string', description: 'Updated reminder time' }, color: { type: 'string', description: 'Updated color' } }, required: ['habit_id'] } }, { name: 'ticktick_delete_habit', description: 'Delete a habit permanently', inputSchema: { type: 'object', properties: { habit_id: { type: 'string', description: 'ID of the habit to delete' } }, required: ['habit_id'] } }, { name: 'ticktick_checkin_habit', description: 'Check in a habit for today', inputSchema: { type: 'object', properties: { habit_id: { type: 'string', description: 'ID of the habit' }, date: { type: 'string', description: 'Date for check-in (YYYY-MM-DD), defaults to today' }, count: { type: 'number', description: 'Number of times completed', default: 1 } }, required: ['habit_id'] } }, { name: 'ticktick_get_habit_history', description: 'Get habit completion history', inputSchema: { type: 'object', properties: { habit_id: { type: 'string', description: 'ID of the habit' }, days_back: { type: 'number', description: 'Number of days to look back', default: 30 } }, required: ['habit_id'] } }, { name: 'ticktick_get_habit_stats', description: 'Get habit statistics and streaks', inputSchema: { type: 'object', properties: { habit_id: { type: 'string', description: 'ID of the habit' } }, required: ['habit_id'] } }, { name: 'ticktick_pause_habit', description: 'Temporarily pause a habit', inputSchema: { type: 'object', properties: { habit_id: { type: 'string', description: 'ID of the habit to pause' }, resume_date: { type: 'string', description: 'Date to resume (YYYY-MM-DD), optional' } }, required: ['habit_id'] } }, { name: 'ticktick_resume_habit', description: 'Resume a paused habit', inputSchema: { type: 'object', properties: { habit_id: { type: 'string', description: 'ID of the habit to resume' } }, required: ['habit_id'] } }, { name: 'ticktick_get_habit_streaks', description: 'Get current and longest streaks for a habit', inputSchema: { type: 'object', properties: { habit_id: { type: 'string', description: 'ID of the habit' } }, required: ['habit_id'] } }, { name: 'ticktick_bulk_checkin_habits', description: 'Check in multiple habits at once', inputSchema: { type: 'object', properties: { habit_ids: { type: 'array', items: { type: 'string' }, description: 'Array of habit IDs to check in' }, date: { type: 'string', description: 'Date for check-ins (YYYY-MM-DD), defaults to today' } }, required: ['habit_ids'] } }, { name: 'ticktick_get_habit_calendar', description: 'Get calendar view of habit completions', inputSchema: { type: 'object', properties: { habit_id: { type: 'string', description: 'ID of the habit' }, year: { type: 'number', description: 'Year for calendar view', default: new Date().getFullYear() }, month: { type: 'number', description: 'Month for calendar view (1-12), optional for full year' } }, required: ['habit_id'] } }, { name: 'ticktick_set_habit_goal', description: 'Set or update habit goal', inputSchema: { type: 'object', properties: { habit_id: { type: 'string', description: 'ID of the habit' }, goal_type: { type: 'string', description: 'Type of goal: daily, weekly, monthly', default: 'daily' }, target_count: { type: 'number', description: 'Target completion count' }, target_streak: { type: 'number', description: 'Target streak length' } }, required: ['habit_id', 'target_count'] } }, { name: 'ticktick_get_habits_summary', description: 'Get daily summary of all habits', inputSchema: { type: 'object', properties: { date: { type: 'string', description: 'Date for summary (YYYY-MM-DD), defaults to today' } } } }, { name: 'ticktick_export_habit_data', description: 'Export habit tracking data', inputSchema: { type: 'object', properties: { habit_id: { type: 'string', description: 'ID of specific habit, or omit for all habits' }, start_date: { type: 'string', description: 'Start date for export (YYYY-MM-DD)' }, end_date: { type: 'string', description: 'End date for export (YYYY-MM-DD)' }, format: { type: 'string', description: 'Export format: json, csv', default: 'json' } } } }, { name: 'ticktick_update_tag', description: 'Update an existing tag', inputSchema: { type: 'object', properties: { tag_id: { type: 'string', description: 'ID of the tag to update' }, name: { type: 'string', description: 'Updated tag name' }, color: { type: 'string', description: 'Updated tag color (hex code)' } }, required: ['tag_id'] } }, { name: 'ticktick_delete_tag', description: 'Delete a tag permanently', inputSchema: { type: 'object', properties: { tag_id: { type: 'string', description: 'ID of the tag to delete' } }, required: ['tag_id'] } }, { name: 'ticktick_remove_tag_from_task', description: 'Remove a tag from a specific task', inputSchema: { type: 'object', properties: { task_id: { type: 'string', description: 'ID of the task' }, tag_name: { type: 'string', description: 'Name of the tag to remove' } }, required: ['task_id', 'tag_name'] } }, { name: 'ticktick_get_tasks_by_tag', description: 'Get all tasks with a specific tag', inputSchema: { type: 'object', properties: { tag_name: { type: 'string', description: 'Name of the tag to filter by' }, limit: { type: 'number', description: 'Maximum number of tasks to return', default: 50 } }, required: ['tag_name'] } }, { name: 'ticktick_get_tag_usage_stats', description: 'Get usage statistics for a specific tag', inputSchema: { type: 'object', properties: { tag_id: { type: 'string', description: 'ID of the tag' } }, required: ['tag_id'] } }, { name: 'ticktick_merge_tags', description: 'Merge two tags into one', inputSchema: { type: 'object', properties: { source_tag_id: { type: 'string', description: 'ID of the tag to merge from' }, target_tag_id: { type: 'string', description: 'ID of the tag to merge into' } }, required: ['source_tag_id', 'target_tag_id'] } }, { name: 'ticktick_bulk_tag_operations', description: 'Perform bulk operations on tags', inputSchema: { type: 'object', properties: { operation: { type: 'string', description: 'Operation: add, remove, replace', enum: ['add', 'remove', 'replace'] }, task_ids: { type: 'array', items: { type: 'string' }, description: 'Array of task IDs to operate on' }, tag_names: { type: 'array', items: { type: 'string' }, description: 'Array of tag names for the operation' }, replace_with: { type: 'array', items: { type: 'string' }, description: 'New tags for replace operation' } }, required: ['operation', 'task_ids', 'tag_names'] } }, { name: 'ticktick_start_focus_session', description: 'Start a focus/Pomodoro session', inputSchema: { type: 'object', properties: { task_id: { type: 'string', description: 'ID of the task to focus on (optional)' }, duration: { type: 'number', description: 'Focus duration in minutes', default: 25 }, session_type: { type: 'string', description: 'Type of session: focus, short_break, long_break', default: 'focus' } } } }, { name: 'ticktick_stop_focus_session', description: 'Stop the current focus session', inputSchema: { type: 'object', properties: { session_id: { type: 'string', description: 'ID of the session to stop' } }, required: ['session_id'] } }, { name: 'ticktick_pause_focus_session', description: 'Pause the current focus session', inputSchema: { type: 'object', properties: { session_id: { type: 'string', description: 'ID of the session to pause' } }, required: ['session_id'] } }, { name: 'ticktick_resume_focus_session', description: 'Resume a paused focus session', inputSchema: { type: 'object', properties: { session_id: { type: 'string', description: 'ID of the session to resume' } }, required: ['session_id'] } }, { name: 'ticktick_get_focus_stats', description: 'Get focus time statistics and analytics', inputSchema: { type: 'object', properties: { period: { type: 'string', description: 'Time period: today, week, month, year', default: 'today' } } } }, { name: 'ticktick_set_task_estimate', description: 'Set estimated time for a task', inputSchema: { type: 'object', properties: { task_id: { type: 'string', description: 'ID of the task' }, estimated_minutes: { type: 'number', description: 'Estimated time in minutes' }, estimate_type: { type: 'string', description: 'Type of estimate: pomodoros, minutes, hours', default: 'minutes' } }, required: ['task_id', 'estimated_minutes'] } }, { name: 'ticktick_get_daily_focus_summary', description: 'Get daily focus time summary', inputSchema: { type: 'object', properties: { date: { type: 'string', description: 'Date for summary (YYYY-MM-DD), defaults to today' } } } }, { name: 'ticktick_get_focus_history', description: 'Get historical focus session data', inputSchema: { type: 'object', properties: { days_back: { type: 'number', description: 'Number of days to look back', default: 30 }, task_id: { type: 'string', description: 'Filter by specific task (optional)' } } } }, { name: 'ticktick_set_focus_goals', description: 'Set daily or weekly focus time goals', inputSchema: { type: 'object', properties: { goal_type: { type: 'string', description: 'Type of goal: daily, weekly', default: 'daily' }, target_minutes: { type: 'number', description: 'Target focus time in minutes' }, target_sessions: { type: 'number', description: 'Target number of focus sessions' } }, required: ['target_minutes'] } }, { name: 'ticktick_get_productivity_insights', description: 'Get AI-powered productivity insights', inputSchema: { type: 'object', properties: { period: { type: 'string', description: 'Analysis period: week, month, quarter', default: 'week' } } } }, { name: 'ticktick_get_project_folders', description: 'Get all project folders and groups', inputSchema: { type: 'object', properties: {} } }, { name: 'ticktick_create_project_folder', description: 'Create a new project folder for organization', inputSchema: { type: 'object', properties: { name: { type: 'string', description: 'Name of the folder' }, color: { type: 'string', description: 'Folder color (hex code)', default: '#3498db' } }, required: ['name'] } }, { name: 'ticktick_move_project_to_folder', description: 'Move a project to a specific folder', inputSchema: { type: 'object', properties: { project_id: { type: 'string', description: 'ID of the project to move' }, folder_id: { type: 'string', description: 'ID of the destination folder' } }, required: ['project_id', 'folder_id'] } }, { name: 'ticktick_archive_project', description: 'Archive a completed project', inputSchema: { type: 'object', properties: { project_id: { type: 'string', description: 'ID of the project to archive' } }, required: ['project_id'] } }, { name: 'ticktick_unarchive_project', description: 'Restore an archived project', inputSchema: { type: 'object', properties: { project_id: { type: 'string', description: 'ID of the project to unarchive' } }, required: ['project_id'] } }, { name: 'ticktick_duplicate_project', description: 'Create a copy of an existing project', inputSchema: { type: 'object', properties: { project_id: { type: 'string', description: 'ID of the project to duplicate' }, new_name: { type: 'string', description: 'Name for the duplicated project' }, include_tasks: { type: 'boolean', description: 'Include tasks in the duplicate', default: true } }, required: ['project_id', 'new_name'] } }, { name: 'ticktick_get_project_stats', description: 'Get project analytics and statistics', inputSchema: { type: 'object', properties: { project_id: { type: 'string', description: 'ID of the project' } }, required: ['project_id'] } }, { name: 'ticktick_set_project_color', description: 'Customize project appearance', inputSchema: { type: 'object', properties: { project_id: { type: 'string', description: 'ID of the project' }, color: { type: 'string', description: 'New color (hex code)' } }, required: ['project_id', 'color'] } }, { name: 'ticktick_reorder_projects', description: 'Change project display order', inputSchema: { type: 'object', properties: { project_orders: { type: 'array', items: { type: 'object', properties: { project_id: { type: 'string' }, sort_order: { type: 'number' } }, required: ['project_id', 'sort_order'] }, description: 'Array of project IDs with their new sort orders' } }, required: ['project_orders'] } }, { name: 'ticktick_get_project_templates', description: 'List available project templates', inputSchema: { type: 'object', properties: { category: { type: 'string', description: 'Filter by template category' } } } }, { name: 'ticktick_create_project_from_template', description: 'Create project from a template', inputSchema: { type: 'object', properties: { template_id: { type: 'string', description: 'ID of the template to use' }, project_name: { type: 'string', description: 'Name for the new project' }, customize_tasks: { type: 'boolean', description: 'Allow task customization during creation', default: false } }, required: ['template_id', 'project_name'] } }, { name: 'ticktick_export_project', description: 'Export project data', inputSchema: { type: 'object', properties: { project_id: { type: 'string', description: 'ID of the project to export' }, format: { type: 'string', description: 'Export format: json, csv, md', default: 'json' }, include_completed: { type: 'boolean', description: 'Include completed tasks', default: true } }, required: ['project_id'] } }, // Collaboration & Sharing (12 operations) { name: 'ticktick_share_project', description: 'Share project with others', inputSchema: { type: 'object', properties: { project_id: { type: 'string', description: 'ID of the project to share' }, emails: { type: 'array', items: { type: 'string' }, description: 'Email addresses to share with' }, permission_level: { type: 'string', enum: ['view', 'edit', 'admin'], description: 'Permission level for shared users', default: 'edit' }, message: { type: 'string', description: 'Optional invitation message' } }, required: ['project_id', 'emails'] } }, { name: 'ticktick_get_shared_projects', description: 'List shared projects', inputSchema: { type: 'object', properties: { include_owned: { type: 'boolean', description: 'Include projects you own', default: true }, include_received: { type: 'boolean', description: 'Include projects shared with you', default: true } } } }, { name: 'ticktick_invite_collaborator', description: 'Invite user to project', inputSchema: { type: 'object', properties: { project_id: { type: 'string', description: 'ID of the project' }, email: { type: 'string', description: 'Email address of user to invite' }, role: { type: 'string', enum: ['member', 'editor', 'admin'], description: 'Role for the invited user', default: 'member' }, personal_message: { type: 'string', description: 'Personal invitation message' } }, required: ['project_id', 'email'] } }, { name: 'ticktick_remove_collaborator', description: 'Remove project access', inputSchema: { type: 'object', properties: { project_id: { type: 'string', description: 'ID of the project' }, user_id: { type: 'string', description: 'ID of user to remove' } }, required: ['project_id', 'user_id'] } }, { name: 'ticktick_assign_task', description: 'Assign task to team member', inputSchema: { type: 'object', properties: { task_id: { type: 'string', description: 'ID of the task to assign' }, assignee_id: { type: 'string', description: 'ID of user to assign task to' }, due_date: { type: 'string', description: 'Due date for the assignment' }, priority: { type: 'string', enum: ['low', 'medium', 'high'], description: 'Priority level for the assignment' }, notification: { type: 'boolean', description: 'Send notification to assignee', default: true } }, required: ['task_id', 'assignee_id'] } }, { name: 'ticktick_get_task_assignees', description: 'List task assignees', inputSchema: { type: 'object', properties: { task_id: { type: 'string', description: 'ID of the task' } }, required: ['task_id'] } }, { name: 'ticktick_add_task_comment', description: 'Add comment for team communication', inputSchema: { type: 'object', properties: { task_id: { type: 'string', description: 'ID of the task' }, comment: { type: 'string', description: 'Comment text' }, mention_users: { type: 'array', items: { type: 'string' }, description: 'User IDs to mention in comment' }, is_private: { type: 'boolean', description: 'Make comment private', default: false } }, required: ['task_id', 'comment'] } }, { name: 'ticktick_get_team_activity', description: 'Project activity feed', inputSchema: { type: 'object', properties: { project_id: { type: 'string', description: 'ID of the project' }, limit: { type: 'number', description: 'Number of activities to retrieve', default: 50 }, activity_types: { type: 'array', items: { type: 'string', enum: ['task_created', 'task_completed', 'task_assigned', 'comment_added', 'project_shared'] }, description: 'Filter by activity types' } }, required: ['project_id'] } }, { name: 'ticktick_set_project_permissions', description: 'Configure access levels', inputSchema: { type: 'object', properties: { project_id: { type: 'string', description: 'ID of the project' }, permissions: { type: 'object', properties: { can_invite: { type: 'boolean' }, can_edit_tasks: { type: 'boolean' }, can_delete_tasks: { type: 'boolean' }, can_view_reports: { type: 'boolean' } }, description: 'Permission settings' }, apply_to_existing: { type: 'boolean', description: 'Apply to existing collaborators', default: false } }, required: ['project_id', 'permissions'] } }, { name: 'ticktick_get_collaboration_stats', description: 'Team productivity metrics', inputSchema: { type: 'object', properties: { project_id: { type: 'string', description: 'ID of the project' }, time_period: { type: 'string', enum: ['week', 'month', 'quarter', 'year'], description: 'Time period for stats', default: 'month' } }, required: ['project_id'] } }, { name: 'ticktick_bulk_assign_tasks', description: 'Mass task assignment', inputSchema: { type: 'object', properties: { assignments: { type: 'array', items: { type: 'object', properties: { task_id: { type: 'string' }, assignee_id: { type: 'string' }, due_date: { type: 'string' } }, required: ['task_id', 'assignee_id'] }, description: 'Array of task assignments' }, notify_assignees: { type: 'boolean', description: 'Send notifications to all assignees', default: true } }, required: ['assignments'] } }, { name: 'ticktick_export_team_report', description: 'Team performance reports', inputSchema: { type: 'object', properties: { project_id: { type: 'string', description: 'ID of the project' }, report_type: { type: 'string', enum: ['productivity', 'task_completion', 'time_tracking', 'team_activity'], description: 'Type of report to generate', default: 'productivity' }, date_range: { type: 'object', properties: { start_date: { type: 'string' }, end_date: { type: 'string' } }, description: 'Date range for the report' }, format: { type: 'string', enum: ['pdf', 'csv', 'json'], description: 'Report format', default: 'pdf' } }, required: ['project_id'] } }, // Calendar Integration (8 operations) { name: 'ticktick_get_calendar_events', description: 'List calendar events', inputSchema: { type: 'object', properties: { start_date: { type: 'string', description: 'Start date for events (YYYY-MM-DD)' }, end_date: { type: 'string', description: 'End date for events (YYYY-MM-DD)' }, calendar_id: { type: 'string', description: 'Specific calendar ID to filter' } } } }, { name: 'ticktick_create_calendar_event', description: 'Create calendar event', inputSchema: { type: 'object', properties: { title: { type: 'string', description: 'Event title' }, start_time: { type: 'string', description: 'Event start time (ISO format)' }, end_time: { type: 'string', description: 'Event end time (ISO format)' }, description: { type: 'string', description: 'Event description' }, location: { type: 'string', description: 'Event location' }, reminder_minutes: { type: 'number', description: 'Reminder time in minutes before event', default: 15 } }, required: ['title', 'start_time', 'end_time'] } }, { name: 'ticktick_sync_with_google_calendar', description: 'Google Calendar sync', inputSchema: { type: 'object', properties: { google_calendar_id: { type: 'string', description: 'Google Calendar ID to sync with' }, sync_direction: { type: 'string', enum: ['import', 'export', 'bidirectional'], description: 'Sync direction', default: 'bidirectional' }, date_range_days: { type: 'number', description: 'Number of days to sync (past and future)', default: 30 } }, required: ['google_calendar_id'] } }, { name: 'ticktick_sync_with_outlook', description: 'Outlook integration', inputSchema: { type: 'object', properties: { outlook_calendar_id: { type: 'string', description: 'Outlook Calendar ID to sync with' }, sync_direction: { type: 'string', enum: ['import', 'export', 'bidirectional'], description: 'Sync direction', default: 'bidirectional' }, include_meetings: { type: 'boolean', description: 'Include meeting invitations', default: true } }, required: ['outlook_calendar_id'] } }, { name: 'ticktick_get_calendar_view', description: 'Calendar view for date range', inputSchema: { type: 'object', properties: { start_date: { type: 'string', description: 'Start date (YYYY-MM-DD)' }, end_date: { type: 'string', description: 'End date (YYYY-MM-DD)' }, view_type: { type: 'string', enum: ['day', 'week', 'month', 'agenda'], description: 'Calendar view type', default: 'week' }, include_tasks: { type: 'boolean', description: 'Include tasks in calendar view', default: true } } } }, { name: 'ticktick_convert_task_to_event', description: 'Transform task into calendar event', inputSchema: { type: 'object', properties: { task_id: { type: 'string', description: 'ID of task to convert' }, event_duration_minutes: { type: 'number', description: 'Duration of the event in minutes', default: 60 }, start_time: { type: 'string', description: 'Preferred start time (ISO format)' }, create_reminder: { type: 'boolean', description: 'Create reminder for the event', default: true } }, required: ['task_id'] } }, { name: 'ticktick_get_schedule_conflicts', description: 'Detect scheduling conflicts', inputSchema: { type: 'object', properties: { start_date: { type: 'string', description: 'Start date to check (YYYY-MM-DD)' }, end_date: { type: 'string', description: 'End date to check (YYYY-MM-DD)' }, include_tasks: { type: 'boolean', description: 'Include task conflicts', default: true }, conflict_threshold_minutes: { type: 'number', description: 'Minimum overlap time to consider conflict', default: 15 } } } }, { name: 'ticktick_bulk_calendar_import', description: 'Import external calendar data', inputSchema: { type: 'object', properties: { calendar_data: { type: 'string', description: 'Calendar data in iCal format' }, import_source: { type: 'string', description: 'Source of the calendar data' }, merge_duplicates: { type: 'boolean', description: 'Merge duplicate events', default: true }, create_new_calendar: { type: 'boolean', description: 'Create new calendar for imported events', default: false } }, required: ['calendar_data'] } }, // Notes & Attachments (8 operations) { name: 'ticktick_get_task_notes', description: 'Get task notes/comments', inputSchema: { type: 'object', properties: { task_id: { type: 'string', description: 'ID of the task' }, include_replies: { type: 'boolean', description: 'Include comment replies', default: true }, sort_order: { type: 'string', enum: ['newest', 'oldest'], description: 'Sort order for notes', default: 'newest' } }, required: ['task_id'] } }, { name: 'ticktick_add_task_note', description: 'Add note to task', inputSchema: { type: 'object', properties: { task_id: { type: 'string', description: 'ID of the task' }, note_content: { type: 'string', description: 'Note content' }, note_type: { type: 'string', enum: ['comment', 'progress', 'reminder'], description: 'Type of note', default: 'comment' }, is_private: { type: 'boolean', description: 'Make note private', default: false } }, required: ['task_id', 'note_content'] } }, { name: 'ticktick_update_task_note', description: 'Edit task note', inputSchema: { type: 'object', properties: { task_id: { type: 'string', description: 'ID of the task' }, note_id: { type: 'string', description: 'ID of the note to update' }, new_content: { type: 'string', description: 'Updated note content' } }, required: ['task_id', 'note_id', 'new_content'] } }, { name: 'ticktick_delete_task_note', description: 'Remove task note', inputSchema: { type: 'object', properties: { task_id: { type: 'string', description: 'ID of the task' }, note_id: { type: 'string', description: 'ID of the note to delete' } }, required: ['task_id', 'note_id'] } }, { name: 'ticktick_upload_task_attachment', description: 'Attach files to tasks', inputSchema: { type: 'object', properties: { task_id: { type: 'string', description: 'ID of the task' }, file_data: { type: 'string', description: 'Base64 encoded file data' }, file_name: { type: 'string', description: 'Name of the file' }, file_type: { type: 'string', description: 'MIME type of the file' }, description: { type: 'string', description: 'Description of the attachment' } }, required: ['task_id', 'file_data', 'file_name'] } }, { name: 'ticktick_get_task_attachments', description: 'List task attachments', inputSchema: { type: 'object', properties: { task_id: { type: 'string', description: 'ID of the task' }, file_type_filter: { type: 'string', enum: ['all', 'images', 'documents', 'audio', 'video'], description: 'Filter by file type', default: 'all' } }, required: ['task_id'] } }, { name: 'ticktick_download_task_attachment', description: 'Download attached files', inputSchema: { type: 'object', properties: { task_id: { type: 'string', description: 'ID of the task' }, attachment_id: { type: 'string', description: 'ID of the attachment' }, download_format: { type: 'string', enum: ['original', 'compressed'], description: 'Download format', default: 'original' } }, required: ['task_id', 'attachment_id'] } }, { name: 'ticktick_delete_task_attachment', description: 'Remove attachments', inputSchema: { type: 'object', properties: { task_id: { type: 'string', description: 'ID of the task' }, attachment_id: { type: 'string', description: 'ID of the attachment to delete' } }, required: ['task_id', 'attachment_id'] } }, // Templates & Automation (9 operations) { name: 'ticktick_get_task_templates', description: 'List task templates', inputSchema: { type: 'object', properties: { category: { type: 'string', description: 'Filter by template category' }, include_shared: { type: 'boolean', description: 'Include templates shared by team', default: true }, sort_by: { type: 'string', enum: ['name', 'usage', 'created_date', 'updated_date'], description: 'Sort templates by field', default: 'usage' } } } }, { name: 'ticktick_create_task_template', description: 'Create reusable template', inputSchema: { type: 'object', properties: { template_name: { type: 'string', description: 'Name of the template' }, template_description: { type: 'string', description: 'Description of the template' }, task_data: { type: 'object', description: 'Task structure to save as template', properties: { title: { type: 'string' }, description: { type: 'string' }, priority: { type: 'string' }, tags: { type: 'array', items: { type: 'string' } }, estimated_duration: { type: 'number' } }, required: ['title'] }, category: { type: 'string', description: 'Template category', default: 'general' }, is_shared: { type: 'boolean', description: 'Share template with team', default: false } }, required: ['template_name', 'task_data'] } }, { name: 'ticktick_update_task_template', description: 'Modify template', inputSchema: { type: 'object', properties: { template_id: { type: 'string', description: 'ID of template to update' }, template_name: { type: 'string', description: 'Updated template name' }, template_description: { type: 'string', description: 'Updated description' }, task_data: { type: 'object', description: 'Updated task structure' }, category: { type: 'string', description: 'Updated category' } }, required: ['template_id'] } }, { name: 'ticktick_delete_task_template', description: 'Remove template', inputSchema: { type: 'object', properties: { template_id: { type: 'string', description: 'ID of template to delete' } }, required: ['template_id'] } }, { name: 'ticktick_create_task_from_template', description: 'Instantiate template', inputSchema: { type: 'object', properties: { template_id: { type: 'string', description: 'ID of template to use' }, project_id: { type: 'string', description: 'Project to create task in' }, customizations: { type: 'object', description: 'Custom values to override template defaults', properties: { title: { type: 'string' }, due_date: { type: 'string' }, priority: { type: 'string' }, assignee_id: { type: 'string' } } }, create_multiple: { type: 'number', description: 'Number of tasks to create from template', default: 1 } }, required: ['template_id'] } }, { name: 'ticktick_set_recurring_task', description: 'Configure task recurrence', inputSchema: { type: 'object', properties: { task_id: { type: 'string', description: 'ID of task to make recurring' }, recurrence_pattern: { type: 'object', properties: { frequency: { type: 'string', enum: ['daily', 'weekly', 'monthly', 'yearly'], description: 'How often to repeat' }, interval: { type: 'number', description: 'Repeat every N frequency units', default: 1 }, days_of_week: { type: 'array', items: { type: 'number', minimum: 0, maximum: 6 }, description: 'Days of week (0=Sunday, 6=Saturday)' }, end_condition: { type: 'object', properties: { type: { type: 'string', enum: ['never', 'after_count', 'on_date'] }, count: { type: 'number' }, end_date: { type: 'string' } } } }, required: ['frequency'] } }, required: ['task_id', 'recurrence_pattern'] } }, { name: 'ticktick_get_recurring_tasks', description: 'List recurring tasks', inputSchema: { type: 'object', properties: { project_id: { type: 'string', description: 'Filter by project' }, frequency_filter: { type: 'string', enum: ['all', 'daily', 'weekly', 'monthly', 'yearly'], description: 'Filter by recurrence frequency', default: 'all' }, status: { type: 'string', enum: ['active', 'paused', 'completed'], description: 'Filter by recurrence status', default: 'active' } } } }, { name: 'ticktick_pause_recurring_task', description: 'Temporarily stop recurrence', inputSchema: { type: 'object', properties: { task_id: { type: 'string', description: 'ID of recurring task to pause' }, pause_reason: { type: 'string', description: 'Reason for pausing (optional)' }, resume_date: { type: 'string', description: 'When to automatically resume (YYYY-MM-DD)' } }, required: ['task_id'] } }, { name: 'ticktick_bulk_create_from_template', description: 'Mass task creation', inputSchema: { type: 'object', properties: { template_id: { type: 'string', description: 'Template to use for bulk creation' }, bulk_data: { type: 'array', items: { type: 'object', properties: { title: { type: 'string' }, project_id: { type: 'string' }, due_date: { type: 'string' }, assignee_id: { type: 'string' }, custom_fields: { type: 'object' } }, required: ['title'] }, description: 'Array of task data for bulk creation' }, apply_template_defaults: { type: 'boolean', description: 'Use template defaults for missing fields', default: true }, notify_assignees: { type: 'boolean', description: 'Send notifications to assignees', default: true } }, required: ['template_id', 'bulk_data'] } }, { name: 'ticktick_get_productivity_report', description: 'Get comprehensive productivity analytics and insights', inputSchema: { type: 'object', properties: { time_range: { type: 'string', enum: ['today', 'week', 'month', 'quarter', 'year', 'custom'], description: 'Time period for the report', default: 'month' }, start_date: { type: 'string', description: 'Start date for custom range (YYYY-MM-DD)' }, end_date: { type: 'string', description: 'End date for custom range (YYYY-MM-DD)' }, include_habits: { type: 'boolean', description: 'Include habit tracking data', default: true }, include_focus: { type: 'boolean', description: 'Include focus time data', default: true }, project_ids: { type: 'array', items: { type: 'string' }, description: 'Filter by specific projects' } } } }, { name: 'ticktick_get_completion_trends', description: 'Analyze task completion patterns and trends over time', inputSchema: { type: 'object', properties: { period: { type: 'string', enum: ['daily', 'weekly', 'monthly'], description: 'Granularity of trend analysis', default: 'weekly' }, duration: { type: 'number', description: 'Number of periods to analyze', default: 12 }, project_id: { type: 'string', description: 'Filter by specific project' }, tag_filter: { type: 'array', items: { type: 'string' }, description: 'Filter by specific tags' }, include_predictions: { type: 'boolean', description: 'Include trend predictions', default: true } } } }, { name: 'ticktick_get_time_tracking_report', description: 'Generate detailed time allocation and tracking analysis', inputSchema: { type: 'object', properties: { time_period: { type: 'string', enum: ['week', 'month', 'quarter'], description: 'Reporting period', default: 'month' }, breakdown_by: { type: 'string', enum: ['project', 'tag', 'priority', 'assignee'], description: 'How to categorize time data', default: 'project' }, include_estimates: { type: 'boolean', description: 'Compare actual vs estimated time', default: true }, focus_sessions_only: { type: 'boolean', description: 'Only include tracked focus sessions', default: false }, export_format: { type: 'string', enum: ['summary', 'detailed', 'csv'], description: 'Level of detail in report', default: 'detailed' } } } }, { name: 'ticktick_get_goal_progress', description: 'Track progress toward personal and team goals', inputSchema: { type: 'object', properties: { goal_type: { type: 'string', enum: ['task_completion', 'habit_consistency', 'focus_time', 'project_milestones'], description: 'Type of goals to analyze' }, time_frame: { type: 'string', enum: ['weekly', 'monthly', 'quarterly', 'yearly'], description: 'Goal time frame', default: 'monthly' }, target_metrics: { type: 'object', properties: { tasks_per_day: { type: 'number' }, focus_hours_per_week: { type: 'number' }, habit_streak_days: { type: 'number' }, project_completion_rate: { type: 'number' } }, description: 'Specific targets to track against' }, include_recommendations: { type: 'boolean', description: 'Include AI-powered improvement suggestions', default: true } } } }, { name: 'ticktick_export_analytics_data', description: 'Export raw analytics data for external analysis', inputSchema: { type: 'object', properties: { data_types: { type: 'array', items: { type: 'string', enum: ['tasks', 'habits', 'focus_sessions', 'projects', 'time_logs'] }, description: 'Types of data to export', default: ['tasks', 'habits', 'focus_sessions'] }, date_range: { type: 'object', properties: { start_date: { type: 'string', description: 'Start date (YYYY-MM-DD)' }, end_date: { type: 'string', description: 'End date (YYYY-MM-DD)' } }, required: ['start_date', 'end_date'] }, format: { type: 'string', enum: ['json', 'csv', 'xlsx'], description: 'Export file format', default: 'json' }, include_metadata: { type: 'boolean', description: 'Include field descriptions and data schema', default: true }, privacy_filter: { type: 'boolean', description: 'Remove personally identifiable information', default: false } }, required: ['date_range'] } }, { name: 'ticktick_get_weekly_summary', description: 'Generate comprehensive weekly productivity summary', inputSchema: { type: 'object', properties: { week_offset: { type: 'number', description: 'Weeks ago (0 = current week, 1 = last week)', default: 0 }, include_sections: { type: 'array', items: { type: 'string', enum: ['tasks', 'habits', 'focus', 'achievements', 'challenges', 'next_week'] }, description: 'Sections to include in summary', default: ['tasks', 'habits', 'focus', 'achievements'] }, compare_previous: { type: 'boolean', description: 'Compare with previous week', default: true }, personalization: { type: 'object', properties: { tone: { type: 'string', enum: ['professional', 'casual', 'motivational', 'analytical'], default: 'motivational' }, focus_areas: { type: 'array', items: { type: 'string' }, description: 'Specific areas to emphasize' } } } } } }, { name: 'ticktick_get_monthly_insights', description: 'Generate deep monthly performance insights and recommendations', inputSchema: { type: 'object', properties: { month_offset: { type: 'number', description: 'Months ago (0 = current month, 1 = last month)', default: 0 }, insight_depth: { type: 'string', enum: ['overview', 'detailed', 'comprehensive'], description: 'Level of analysis depth', default: 'detailed' }, focus_metrics: { type: 'array', items: { type: 'string', enum: ['efficiency', 'consistency', 'goal_achievement', 'time_management', 'stress_patterns'] }, description: 'Key metrics to analyze', default: ['efficiency', 'consistency', 'goal_achievement'] }, benchmarking: { type: 'object', properties: { compare_to_average: { type: 'boolean', default: true }, compare_to_best_month: { type: 'boolean', default: true }, include_peer_insights: { type: 'boolean', default: false } } }, action_planning: { type: 'boolean', description: 'Include actionable recommendations for next month', default: true } } } }, { name: 'ticktick_update_user_settings', description: 'Modify user preferences and account settings', inputSchema: { type: 'object', properties: { settings: { type: 'object', properties: { timezone: { type: 'string', description: 'User timezone (e.g., "America/New_York")' }, language: { type: 'string', description: 'Interface language code (e.g., "en", "es", "fr")' }, date_format: { type: 'string', enum: ['MM/DD/YYYY', 'DD/MM/YYYY', 'YYYY-MM-DD'], description: 'Preferred date format' }, time_format: { type: 'string', enum: ['12h', '24h'], description: 'Time format preference' }, start_of_week: { type: 'number', minimum: 0, maximum: 6, description: 'First day of week (0=Sunday, 1=Monday)' }, theme: { type: 'string', enum: ['light', 'dark', 'auto'], description: 'UI theme preference' }, default_list: { type: 'string', description: 'Default project ID for new tasks' }, smart_add: { type: 'boolean', description: 'Enable smart task parsing' }, auto_backup: { type: 'boolean', description: 'Enable automatic data backup' } }, description: 'Settings to update' } }, required: ['settings'] } }, { name: 'ticktick_get_notification_settings', description: 'Get current notification preferences and settings', inputSchema: { type: 'object', properties: { category: { type: 'string', enum: ['all', 'tasks', 'habits', 'calendar', 'collaboration', 'system'], description: 'Filter by notification category', default: 'all' }, include_disabled: { type: 'boolean', description: 'Include disabled notification types', default: false } } } }, { name: 'ticktick_update_notification_settings', description: 'Configure notification preferences for various events', inputSchema: { type: 'object', properties: { notifications: { type: 'object', properties: { task_reminders: { type: 'object', properties: { enabled: { type: 'boolean' }, advance_time: { type: 'number', description: 'Minutes before due time' }, sound: { type: 'string' }, vibrate: { type: 'boolean' } } }, habit_reminders: { type: 'object', properties: { enabled: { type: 'boolean' }, time: { type: 'string', description: 'Daily reminder time (HH:MM)' }, days: { type: 'array', items: { type: 'number', minimum: 0, maximum: 6 }, description: 'Days of week for reminders' } } }, collaboration: { type: 'object', properties: { task_assignments: { type: 'boolean' }, project_invitations: { type: 'boolean' }, comments: { type: 'boolean' }, status_updates: { type: 'boolean' } } }, system: { type: 'object', properties: { app_updates: { type: 'boolean' }, tips_and_tricks: { type: 'boolean' }, weekly_reports: { type: 'boolean' }, promotional: { type: 'boolean' } } }, delivery_methods: { type: 'object', properties: { push_notifications: { type: 'boolean' }, email_notifications: { type: 'boolean' }, sms_notifications: { type: 'boolean' }, in_app_notifications: { type: 'boolean' } } } }, description: 'Notification settings to update' } }, required: ['notifications'] } }, { name: 'ticktick_get_sync_settings', description: 'Get device synchronization configuration and status', inputSchema: { type: 'object', properties: { include_device_list: { type: 'boolean', description: 'Include list of synced devices', default: true }, include_sync_history: { type: 'boolean', description: 'Include recent sync activity', default: false } } } }, { name: 'ticktick_update_sync_settings', description: 'Configure synchronization behavior and preferences', inputSchema: { type: 'object', properties: { sync_settings: { type: 'object', properties: { auto_sync: { type: 'boolean', description: 'Enable automatic synchronization' }, sync_frequency: { type: 'string', enum: ['real-time', 'every-5min', 'every-15min', 'every-hour', 'manual'], description: 'How often to sync data' }, sync_on_wifi_only: { type: 'boolean', description: 'Only sync when connected to WiFi' }, conflict_resolution: { type: 'string', enum: ['server-wins', 'client-wins', 'merge', 'prompt'], description: 'How to handle sync conflicts' }, data_types: { type: 'object', properties: { tasks: { type: 'boolean' }, projects: { type: 'boolean' }, habits: { type: 'boolean' }, calendar: { type: 'boolean' }, attachments: { type: 'boolean' }, settings: { type: 'boolean' } }, description: 'Which data types to sync' }, backup_before_sync: { type: 'boolean', description: 'Create backup before major sync operations' }, compression: { type: 'boolean', description: 'Compress data during sync to save bandwidth' } }, description: 'Sync settings to update' } }, required: ['sync_settings'] } }, { name: 'ticktick_reset_user_data', description: 'Reset or manage user data with various options', inputSchema: { type: 'object', properties: { reset_type: { type: 'string', enum: ['settings-only', 'cache-only', 'partial-data', 'full-reset'], description: 'Type of reset to perform' }, data_categories: { type: 'array', items: { type: 'string', enum: ['tasks', 'projects', 'habits', 'calendar', 'tags', 'templates', 'analytics', 'settings'] }, description: 'Specific data categories to reset (for partial-data reset)' }, backup_before_reset: { type: 'boolean', description: 'Create backup before performing reset', default: true }, confirmation_code: { type: 'string', description: 'Required confirmation code for destructive operations' }, export_data_first: { type: 'boolean', description: 'Export data before reset for user download', default: false } }, required: ['reset_type'] } }, { name: 'ticktick_import_from_csv', description: 'Import tasks from CSV data to bootstrap the task cache', inputSchema: { type: 'object', properties: { csv_data: { type: 'string', description: 'CSV data with columns: task_id, project_id, title' } }, required: ['csv_data'] } }, { name: 'ticktick_get_cached_tasks', description: 'Get all cached tasks, optionally filtered by project', inputSchema: { type: 'object', properties: { project_id: { type: 'string', description: 'Optional project ID to filter tasks' }, include_stale: { type: 'boolean', description: 'Include stale/expired cached tasks', default: false } } } }, { name: 'ticktick_register_task_id', description: 'Manually register a task ID in the cache for future reading', inputSchema: { type: 'object', properties: { task_id: { type: 'string', description: 'Task ID to register' }, project_id: { type: 'string', description: 'Project ID the task belongs to' }, title: { type: 'string', description: 'Optional task title for cache metadata' } }, required: ['task_id', 'project_id'] } } ] }; }); // Handle tool calls this.server.setRequestHandler(CallToolRequestSchema, async (request) => { const { name, arguments: args } = request.params; if (!TICKTICK_ACCESS_TOKEN && !TICKTICK_TOKEN) { throw new McpError(ErrorCode.InvalidRequest, 'TickTick credentials not properly configured'); } try { switch (name) { case 'ticktick_get_projects': return await this.getProjects(args); case 'ticktick_create_project': return await this.createProject(args); case 'ticktick_create_task': return await this.createTask(args); case 'ticktick_update_task': return await this.updateTask(args); case 'ticktick_delete_task': return await this.deleteTask(args); case 'ticktick_complete_task': return await this.completeTask(args); case 'ticktick_get_task_details': return await this.getTaskDetails(args); case 'ticktick_filter_tasks': return await this.filterTasks(args); case 'ticktick_convert_datetime_to_ticktick_format': return await this.convertDatetimeToTicktickFormat(args); case 'ticktick_get_tags': return await this.getTags(args); case 'ticktick_create_tag': return await this.createTag(args); case 'ticktick_search_tasks': return await this.searchTasks(args); case 'ticktick_get_today_tasks': return await this.getTodayTasks(args); case 'ticktick_get_overdue_tasks': return await this.getOverdueTasks(args); case 'ticktick_get_upcoming_tasks': return await this.getUpcomingTasks(args); case 'ticktick_add_tag_to_task': return await this.addTagToTask(args); case 'ticktick_get_user_profile': return await this.getUserProfile(args); case 'ticktick_get_habits': return await this.getHabits(args); case 'ticktick_create_habit': return await this.createHabit(args); case 'ticktick_update_habit': return await this.updateHabit(args); case 'ticktick_delete_habit': return await this.deleteHabit(args); case 'ticktick_checkin_habit': return await this.checkinHabit(args); case 'ticktick_get_habit_history': return await this.getHabitHistory(args); case 'ticktick_get_habit_stats': return await this.getHabitStats(args); case 'ticktick_pause_habit': return await this.pauseHabit(args); case 'ticktick_resume_habit': return await this.resumeHabit(args); case 'ticktick_get_habit_streaks': return await this.getHabitStreaks(args); case 'ticktick_bulk_checkin_habits': return await this.bulkCheckinHabits(args); case 'ticktick_get_habit_calendar': return await this.getHabitCalendar(args); case 'ticktick_set_habit_goal': return await this.setHabitGoal(args); case 'ticktick_get_habits_summary': return await this.getHabitsSummary(args); case 'ticktick_export_habit_data': return await this.exportHabitData(args); case 'ticktick_update_tag': return await this.updateTag(args); case 'ticktick_delete_tag': return await this.deleteTag(args); case 'ticktick_remove_tag_from_task': return await this.removeTagFromTask(args); case 'ticktick_get_tasks_by_tag': return await this.getTasksByTag(args); case 'ticktick_get_tag_usage_stats': return await this.getTagUsageStats(args); case 'ticktick_merge_tags': return await this.mergeTags(args); case 'ticktick_bulk_tag_operations': return await this.bulkTagOperations(args); case 'ticktick_start_focus_session': return await this.startFocusSession(args); case 'ticktick_stop_focus_session': return await this.stopFocusSession(args); case 'ticktick_pause_focus_session': return await this.pauseFocusSession(args); case 'ticktick_resume_focus_session': return await this.resumeFocusSession(args); case 'ticktick_get_focus_stats': return await this.getFocusStats(args); case 'ticktick_set_task_estimate': return await this.setTaskEstimate(args); case 'ticktick_get_daily_focus_summary': return await this.getDailyFocusSummary(args); case 'ticktick_get_focus_history': return await this.getFocusHistory(args); case 'ticktick_set_focus_goals': return await this.setFocusGoals(args); case 'ticktick_get_productivity_insights': return await this.getProductivityInsights(args); case 'ticktick_get_project_folders': return await this.getProjectFolders(args); case 'ticktick_create_project_folder': return await this.createProjectFolder(args); case 'ticktick_move_project_to_folder': return await this.moveProjectToFolder(args); case 'ticktick_archive_project': return await this.archiveProject(args); case 'ticktick_unarchive_project': return await this.unarchiveProject(args); case 'ticktick_duplicate_project': return await this.duplicateProject(args); case 'ticktick_get_project_stats': return await this.getProjectStats(args); case 'ticktick_set_project_color': return await this.setProjectColor(args); case 'ticktick_reorder_projects': return await this.reorderProjects(args); case 'ticktick_get_project_templates': return await this.getProjectTemplates(args); case 'ticktick_create_project_from_template': return await this.createProjectFromTemplate(args); case 'ticktick_export_project': return await this.exportProject(args); case 'ticktick_share_project': return await this.shareProject(args); case 'ticktick_get_shared_projects': return await this.getSharedProjects(args); case 'ticktick_invite_collaborator': return await this.inviteCollaborator(args); case 'ticktick_remove_collaborator': return await this.removeCollaborator(args); case 'ticktick_assign_task': return await this.assignTask(args); case 'ticktick_get_task_assignees': return await this.getTaskAssignees(args); case 'ticktick_add_task_comment': return await this.addTaskComment(args); case 'ticktick_get_team_activity': return await this.getTeamActivity(args); case 'ticktick_set_project_permissions': return await this.setProjectPermissions(args); case 'ticktick_get_collaboration_stats': return await this.getCollaborationStats(args); case 'ticktick_bulk_assign_tasks': return await this.bulkAssignTasks(args); case 'ticktick_export_team_report': return await this.exportTeamReport(args); case 'ticktick_get_calendar_events': return await this.getCalendarEvents(args); case 'ticktick_create_calendar_event': return await this.createCalendarEvent(args); case 'ticktick_sync_with_google_calendar': return await this.syncWithGoogleCalendar(args); case 'ticktick_sync_with_outlook': return await this.syncWithOutlook(args); case 'ticktick_get_calendar_view': return await this.getCalendarView(args); case 'ticktick_convert_task_to_event': return await this.convertTaskToEvent(args); case 'ticktick_get_schedule_conflicts': return await this.getScheduleConflicts(args); case 'ticktick_bulk_calendar_import': return await this.bulkCalendarImport(args); case 'ticktick_get_task_notes': return await this.getTaskNotes(args); case 'ticktick_add_task_note': return await this.addTaskNote(args); case 'ticktick_update_task_note': return await this.updateTaskNote(args); case 'ticktick_delete_task_note': return await this.deleteTaskNote(args); case 'ticktick_upload_task_attachment': return await this.uploadTaskAttachment(args); case 'ticktick_get_task_attachments': return await this.getTaskAttachments(args); case 'ticktick_download_task_attachment': return await this.downloadTaskAttachment(args); case 'ticktick_delete_task_attachment': return await this.deleteTaskAttachment(args); case 'ticktick_get_task_templates': return await this.getTaskTemplates(args); case 'ticktick_create_task_template': return await this.createTaskTemplate(args); case 'ticktick_update_task_template': return await this.updateTaskTemplate(args); case 'ticktick_delete_task_template': return await this.deleteTaskTemplate(args); case 'ticktick_create_task_from_template': return await this.createTaskFromTemplate(args); case 'ticktick_set_recurring_task': return await this.setRecurringTask(args); case 'ticktick_get_recurring_tasks': return await this.getRecurringTasks(args); case 'ticktick_pause_recurring_task': return await this.pauseRecurringTask(args); case 'ticktick_bulk_create_from_template': return await this.bulkCreateFromTemplate(args); case 'ticktick_get_productivity_report': return await this.getProductivityReport(args); case 'ticktick_get_completion_trends': return await this.getCompletionTrends(args); case 'ticktick_get_time_tracking_report': return await this.getTimeTrackingReport(args); case 'ticktick_get_goal_progress': return await this.getGoalProgress(args); case 'ticktick_export_analytics_data': return await this.exportAnalyticsData(args); case 'ticktick_get_weekly_summary': return await this.getWeeklySummary(args); case 'ticktick_get_monthly_insights': return await this.getMonthlyInsights(args); case 'ticktick_update_user_settings': return await this.updateUserSettings(args); case 'ticktick_get_notification_settings': return await this.getNotificationSettings(args); case 'ticktick_update_notification_settings': return await this.updateNotificationSettings(args); case 'ticktick_get_sync_settings': return await this.getSyncSettings(args); case 'ticktick_update_sync_settings': return await this.updateSyncSettings(args); case 'ticktick_reset_user_data': return await this.resetUserData(args); case 'ticktick_import_from_csv': return await this.importFromCsv(args); case 'ticktick_get_cached_tasks': return await this.getCachedTasks(args); case 'ticktick_register_task_id': return await this.registerTaskId(args); default: throw new McpError(ErrorCode.MethodNotFound, `Unknown tool: ${name}`); } } catch (error) { console.error(`❌ Error in ${name}:`, error); throw new McpError(ErrorCode.InternalError, `Error: ${error.message}`); } }); } async makeTickTickRequest(endpoint, method = 'GET', data = null) { const baseUrl = 'https://api.ticktick.com/open/v1'; const url = `${baseUrl}${endpoint}`; const headers = { 'Authorization': `Bearer ${TICKTICK_ACCESS_TOKEN || TICKTICK_TOKEN}`, 'Content-Type': 'application/json' }; const config = { method, headers }; if (data && method !== 'GET') { config.body = JSON.stringify(data); } console.log(`🔍 TickTick API Request: ${method} ${url}`); if (data) console.log(`📤 Request Data:`, JSON.stringify(data, null, 2)); const response = await fetch(url, config); console.log(`📊 TickTick API Response: ${response.status} ${response.statusText}`); if (!response.ok) { const errorText = await response.text(); console.error(`❌ TickTick API Error Response:`, errorText); throw new Error(`TickTick API error: ${response.status} ${response.statusText} - ${errorText}`); } const responseData = await response.json(); console.log(`📥 Response Data:`, JSON.stringify(responseData, null, 2)); return responseData; } // Cache-based task management methods async importFromCsv({ csv_data }) { try { const lines = csv_data.trim().split('\n'); const headers = lines[0].toLowerCase().split(','); // Find column indices const taskIdIndex = headers.findIndex(h => h.includes('task_id') || h.includes('id')); const projectIdIndex = headers.findIndex(h => h.includes('project_id') || h.includes('project')); const titleIndex = headers.findIndex(h => h.includes('title') || h.includes('name')); if (taskIdIndex === -1 || projectIdIndex === -1) { throw new Error('CSV must contain task_id and project_id columns'); } const cache = this.loadCache(); let importedCount = 0; for (let i = 1; i < lines.length; i++) { const row = lines[i].split(','); if (row.length >= 2) { const taskId = row[taskIdIndex]?.trim(); const projectId = row[projectIdIndex]?.trim(); const title = titleIndex !== -1 ? row[titleIndex]?.trim() : 'Imported Task'; if (taskId && projectId) { cache.tasks[taskId] = { project_id: projectId, title: title || 'Imported Task', cached_at: new Date().toISOString() }; importedCount++; } } } this.saveCache(cache); return { content: [{ type: 'text', text: `✅ **CSV Import Successful!**\n\n` + `📊 **Import Summary**:\n` + `• **Tasks Imported**: ${importedCount}\n` + `• **Cache Updated**: ${new Date().toLocaleString()}\n` + `• **Total Cached Tasks**: ${Object.keys(cache.tasks).length}\n\n` + `💡 **Next Steps**:\n` + `• Use \`ticktick_get_cached_tasks()\` to see all cached tasks\n` + `• Use \`ticktick_get_task_details(project_id, task_id)\` to read specific tasks\n` + `• Tasks will auto-expire after 24 hours for freshness` }] }; } catch (error) { throw new Error(`Failed to import CSV: ${error.message}`); } } async getCachedTasks({ project_id, include_stale = false }) { try { const cache = this.loadCache(); let tasks = Object.entries(cache.tasks); // Filter by project if specified if (project_id) { tasks = tasks.filter(([_, task]) => task.project_id === project_id); } // Filter out stale tasks unless requested if (!include_stale) { tasks = tasks.filter(([_, task]) => !this.isTaskStale(task)); } const freshTasks = tasks.filter(([_, task]) => !this.isTaskStale(task)); const staleTasks = tasks.length - freshTasks.length; return { content: [{ type: 'text', text: `📋 **Cached Tasks** ${project_id ? `(Project: ${project_id})` : '(All Projects)'}\n\n` + `📊 **Cache Summary**:\n` + `• **Fresh Tasks**: ${freshTasks.length}\n` + `• **Stale Tasks**: ${staleTasks}\n` + `• **Total Tasks**: ${tasks.length}\n\n` + (tasks.length > 0 ? `🔍 **Available Tasks**:\n` + tasks.map(([taskId, task]) => { const isStale = this.isTaskStale(task); const staleIcon = isStale ? '⏰' : '✅'; return `${staleIcon} **${task.title}**\n` + ` 📋 Task ID: \`${taskId}\`\n` + ` 📁 Project: ${task.project_id}\n` + ` 📅 Cached: ${new Date(task.cached_at).toLocaleString()}\n` + ` ${isStale ? '⚠️ *Stale - may need refresh*' : ''}`; }).join('\n\n') : `📭 **No tasks found in cache.**\n\n` + `💡 **To populate cache**:\n` + `• Use \`ticktick_import_from_csv()\` with exported data\n` + `• Use \`ticktick_register_task_id()\` for specific tasks\n` + `• Create tasks via MCP (auto-cached)` ) + `\n\n💡 **Usage Tips**:\n` + `• Use task IDs with \`ticktick_get_task_details(project_id, task_id)\`\n` + `• Fresh tasks are less than 24 hours old\n` + `• Stale tasks may have outdated information` }] }; } catch (error) { throw new Error(`Failed to get cached tasks: ${error.message}`); } } async registerTaskId({ task_id, project_id, title }) { try { // Try to fetch the actual task to validate and get real title let actualTitle = title || 'Registered Task'; try { const taskDetails = await this.makeTickTickRequest(`/project/${project_id}/task/${task_id}`); actualTitle = taskDetails.title || actualTitle; } catch (error) { console.warn('Could not fetch task details for validation:', error.message); // Continue with manual registration even if validation fails } this.addTaskToCache(task_id, project_id, actualTitle); return { content: [{ type: 'text', text: `✅ **Task Registered Successfully!**\n\n` + `📋 **Task Details**:\n` + `• **Task ID**: \`${task_id}\`\n` + `• **Project ID**: ${project_id}\n` + `• **Title**: ${actualTitle}\n` + `• **Registered**: ${new Date().toLocaleString()}\n\n` + `💡 **Next Steps**:\n` + `• Use \`ticktick_get_task_details("${project_id}", "${task_id}")\` to read the task\n` + `• Use \`ticktick_get_cached_tasks()\` to see all cached tasks\n` + `• Task will auto-expire after 24 hours for freshness` }] }; } catch (error) { throw new Error(`Failed to register task: ${error.message}`); } } async getProjects({ include_archived = false }) { try { const projects = await this.makeTickTickRequest('/project'); const filteredProjects = include_archived ? projects : projects.filter(p => !p.closed); return { content: [{ type: 'text', text: `📁 **TickTick Projects** (${filteredProjects.length} found):\n\n` + filteredProjects.map(project => `**${project.name}** (ID: ${project.id})\n` + `- Color: ${project.color}\n` + `- Shared: ${project.isOwner ? 'Owner' : 'Member'}\n` + `- Task Count: ${project.taskCount || 0}\n` + `- Modified: ${project.modifiedTime ? new Date(project.modifiedTime).toLocaleDateString() : 'Unknown'}\n` ).join('\n') }] }; } catch (error) { throw new Error(`Failed to get projects: ${error.message}`); } } async createProject({ name, color = '#3498db', is_shared = false }) { try { const projectData = { name, color, isOwner: true, permission: is_shared ? 'members' : 'owner' }; const project = await this.makeTickTickRequest('/project', 'POST', projectData); return { content: [{ type: 'text', text: `✅ **Created TickTick Project**\n\n` + `📁 **Name**: ${project.name}\n` + `🆔 **ID**: ${project.id}\n` + `🎨 **Color**: ${project.color}\n` + `📅 **Created**: ${new Date(project.modifiedTime).toLocaleDateString()}\n` + `🔒 **Shared**: ${is_shared ? 'Yes' : 'No'}` }] }; } catch (error) { throw new Error(`Failed to create project: ${error.message}`); } } async getTasks({ project_id, completed, limit = 50 }) { try { // Note: TickTick API doesn't provide a direct "get all tasks in project" endpoint // This method is deprecated in favor of getTaskDetails with specific task IDs throw new Error('getTasks method is deprecated. Use getTaskDetails with specific project_id and task_id, or getProjectData to get project with task information.'); } catch (error) { throw new Error(`Failed to get tasks: ${error.message}`); } } async getTaskDetails({ project_id, task_id }) { try { // Use the correct TickTick API endpoint pattern const endpoint = `/project/${project_id}/task/${task_id}`; const task = await this.makeTickTickRequest(endpoint); return { content: [{ type: 'text', text: `📝 **TickTick Task Details**\n\n` + `**${task.title}** (ID: ${task.id})\n` + `- Status: ${task.status === 2 ? '✅ Completed' : '⏳ Pending'}\n` + `- Priority: ${this.getPriorityText(task.priority)}\n` + `- Project: ${task.projectId}\n` + `${task.content ? `- Content: ${task.content}\n` : ''}` + `${task.dueDate ? `- Due: ${new Date(task.dueDate).toLocaleDateString()}\n` : ''}` + `${task.tags && task.tags.length ? `- Tags: ${task.tags.join(', ')}\n` : ''}` + `- Created: ${new Date(task.createdTime).toLocaleDateString()}\n` + `- Modified: ${new Date(task.modifiedTime).toLocaleDateString()}` }] }; } catch (error) { throw new Error(`Failed to get task details: ${error.message}`); } } async createTask({ title, content, project_id, priority = 0, due_date, tags }) { try { const taskData = { title, content: content || '', priority, status: 0 // 0 = not completed }; if (project_id) taskData.projectId = project_id; if (due_date) taskData.dueDate = new Date(due_date).toISOString(); if (tags && tags.length) taskData.tags = tags; const task = await this.makeTickTickRequest('/task', 'POST', taskData); // Auto-cache the created task this.addTaskToCache(task.id, task.projectId || project_id, task.title); return { content: [{ type: 'text', text: `✅ **Created TickTick Task**\n\n` + `📝 **Title**: ${task.title}\n` + `🆔 **ID**: ${task.id}\n` + `📁 **Project**: ${task.projectId || 'Inbox'}\n` + `⚡ **Priority**: ${this.getPriorityText(task.priority)}\n` + `${task.dueDate ? `📅 **Due**: ${new Date(task.dueDate).toLocaleDateString()}\n` : ''}` + `${task.tags && task.tags.length ? `🏷️ **Tags**: ${task.tags.join(', ')}\n` : ''}` + `📅 **Created**: ${new Date(task.createdTime).toLocaleDateString()}\n\n` + `🔄 **Auto-cached for easy retrieval!** Use \`ticktick_get_cached_tasks()\` to see all cached tasks.` }] }; } catch (error) { throw new Error(`Failed to create task: ${error.message}`); } } async updateTask({ task_id, title, content, priority, due_date, completed }) { try { const updateData = {}; if (title !== undefined) updateData.title = title; if (content !== undefined) updateData.content = content; if (priority !== undefined) updateData.priority = priority; if (due_date !== undefined) updateData.dueDate = new Date(due_date).toISOString(); if (completed !== undefined) updateData.status = completed ? 2 : 0; const task = await this.makeTickTickRequest(`/task/${task_id}`, 'POST', updateData); return { content: [{ type: 'text', text: `✅ **Updated TickTick Task**\n\n` + `📝 **Title**: ${task.title}\n` + `🆔 **ID**: ${task.id}\n` + `🔄 **Status**: ${task.status === 2 ? '✅ Completed' : '⏳ Pending'}\n` + `⚡ **Priority**: ${this.getPriorityText(task.priority)}\n` + `📅 **Updated**: ${new Date(task.modifiedTime).toLocaleDateString()}` }] }; } catch (error) { throw new Error(`Failed to update task: ${error.message}`); } } async deleteTask({ task_id }) { try { await this.makeTickTickRequest(`/task/${task_id}`, 'DELETE'); return { content: [{ type: 'text', text: `✅ **Deleted TickTick Task**\n\nTask ID: ${task_id} has been permanently deleted.` }] }; } catch (error) { throw new Error(`Failed to delete task: ${error.message}`); } } async completeTask({ task_id }) { try { const task = await this.makeTickTickRequest(`/task/${task_id}`, 'POST', { status: 2 }); return { content: [{ type: 'text', text: `✅ **Completed TickTick Task**\n\n` + `📝 **Title**: ${task.title}\n` + `🆔 **ID**: ${task.id}\n` + `🎉 **Status**: Completed\n` + `📅 **Completed**: ${new Date().toLocaleDateString()}` }] }; } catch (error) { throw new Error(`Failed to complete task: ${error.message}`); } } async filterTasks({ keywords, tags, priority, due_before, due_after }) { // Note: This is a simplified implementation // In a real implementation, you would use TickTick's search API try { const allTasks = await this.makeTickTickRequest('/task'); let filteredTasks = allTasks; if (keywords) { const keywordLower = keywords.toLowerCase(); filteredTasks = filteredTasks.filter(task => task.title.toLowerCase().includes(keywordLower) || (task.content && task.content.toLowerCase().includes(keywordLower)) ); } if (tags && tags.length) { filteredTasks = filteredTasks.filter(task => task.tags && tags.some(tag => task.tags.includes(tag)) ); } if (priority !== undefined) { filteredTasks = filteredTasks.filter(task => task.priority === priority); } if (due_before) { const beforeDate = new Date(due_before); filteredTasks = filteredTasks.filter(task => task.dueDate && new Date(task.dueDate) < beforeDate ); } if (due_after) { const afterDate = new Date(due_after); filteredTasks = filteredTasks.filter(task => task.dueDate && new Date(task.dueDate) > afterDate ); } return { content: [{ type: 'text', text: `🔍 **Filtered TickTick Tasks** (${filteredTasks.length} found):\n\n` + filteredTasks.map(task => `**${task.title}** (ID: ${task.id})\n` + `- Status: ${task.status === 2 ? '✅ Completed' : '⏳ Pending'}\n` + `- Priority: ${this.getPriorityText(task.priority)}\n` + `${task.dueDate ? `- Due: ${new Date(task.dueDate).toLocaleDateString()}\n` : ''}` ).join('\n') }] }; } catch (error) { throw new Error(`Failed to filter tasks: ${error.message}`); } } async convertDatetimeToTicktickFormat({ datetime_string, timezone = 'UTC' }) { try { const date = new Date(datetime_string); const isoString = date.toISOString(); return { content: [{ type: 'text', text: `🕐 **DateTime Conversion**\n\n` + `**Input**: ${datetime_string}\n` + `**Timezone**: ${timezone}\n` + `**TickTick Format**: ${isoString}\n` + `**Timestamp**: ${date.getTime()}\n\n` + `Use this ISO format for TickTick API calls.` }] }; } catch (error) { throw new Error(`Failed to convert datetime: ${error.message}`); } } getPriorityText(priority) { switch (priority) { case 0: return '⚪ None'; case 1: return '🔵 Low'; case 3: return '🟡 Medium'; case 5: return '🔴 High'; default: return `Unknown (${priority})`; } } async getTags() { try { const tags = await this.makeTickTickRequest('/tag'); return { content: [{ type: 'text', text: `🏷️ **TickTick Tags** (${tags.length} found):\n\n` + tags.map(tag => `**${tag.name}** (ID: ${tag.id})\n` + `- Color: ${tag.color || 'Default'}\n` + `- Usage Count: ${tag.usageCount || 0}\n` ).join('\n') }] }; } catch (error) { throw new Error(`Failed to get tags: ${error.message}`); } } async createTag({ name, color = '#3498db' }) { try { const tagData = { name, color }; const tag = await this.makeTickTickRequest('/tag', 'POST', tagData); return { content: [{ type: 'text', text: `✅ **Created TickTick Tag**\n\n` + `🏷️ **Name**: ${tag.name}\n` + `🆔 **ID**: ${tag.id}\n` + `🎨 **Color**: ${tag.color}\n` + `📅 **Created**: ${new Date().toLocaleDateString()}` }] }; } catch (error) { throw new Error(`Failed to create tag: ${error.message}`); } } async searchTasks({ query, limit = 20 }) { try { const endpoint = `/search?q=${encodeURIComponent(query)}&limit=${limit}`; const results = await this.makeTickTickRequest(endpoint); const tasks = results.tasks || results || []; return { content: [{ type: 'text', text: `🔍 **Search Results for "${query}"** (${tasks.length} found):\n\n` + tasks.map(task => `**${task.title}** (ID: ${task.id})\n` + `- Status: ${task.status === 2 ? '✅ Completed' : '⏳ Pending'}\n` + `- Priority: ${this.getPriorityText(task.priority)}\n` + `- Project: ${task.projectId}\n` + `${task.content ? `- Content: ${task.content.substring(0, 100)}...\n` : ''}` + `${task.dueDate ? `- Due: ${new Date(task.dueDate).toLocaleDateString()}\n` : ''}` ).join('\n') }] }; } catch (error) { throw new Error(`Failed to search tasks: ${error.message}`); } } async getTodayTasks({ include_overdue = true }) { try { const today = new Date(); const todayStr = today.toISOString().split('T')[0]; let endpoint = `/task?dueDate=${todayStr}`; if (include_overdue) { const yesterday = new Date(today.getTime() - 24 * 60 * 60 * 1000); const yesterdayStr = yesterday.toISOString().split('T')[0]; endpoint = `/task?dueBefore=${todayStr}&dueAfter=${yesterdayStr}`; } const tasks = await this.makeTickTickRequest(endpoint); return { content: [{ type: 'text', text: `📅 **Today's Tasks** (${tasks.length} found):\n\n` + tasks.map(task => `**${task.title}** (ID: ${task.id})\n` + `- Status: ${task.status === 2 ? '✅ Completed' : '⏳ Pending'}\n` + `- Priority: ${this.getPriorityText(task.priority)}\n` + `${task.dueDate ? `- Due: ${new Date(task.dueDate).toLocaleTimeString()}\n` : ''}` + `${task.tags && task.tags.length ? `- Tags: ${task.tags.join(', ')}\n` : ''}` ).join('\n') }] }; } catch (error) { // Provide helpful alternatives when bulk APIs fail const cache = this.loadCache(); const cachedTaskCount = Object.keys(cache.tasks).length; return { content: [{ type: 'text', text: `❌ **TickTick bulk listing unavailable** (500 error)\n\n` + `💡 **Alternatives to get your tasks**:\n` + `• **Use cache**: \`ticktick_get_cached_tasks()\` (${cachedTaskCount} tasks available)\n` + `• **Import data**: \`ticktick_import_from_csv()\` with exported TickTick data\n` + `• **Register tasks**: \`ticktick_register_task_id(task_id, project_id)\` for specific tasks\n` + `• **Read specific task**: \`ticktick_get_task_details(project_id, task_id)\` if you know the IDs\n\n` + `📋 **Why this happens**: TickTick's bulk task listing API has limitations and often returns 500 errors.\n\n` + `🚀 **Quick solution**: Export your tasks from TickTick app → Import with \`ticktick_import_from_csv()\`` }] }; } } async getOverdueTasks({ limit = 50 }) { try { const today = new Date().toISOString().split('T')[0]; const endpoint = `/task?dueBefore=${today}&status=0&limit=${limit}`; const tasks = await this.makeTickTickRequest(endpoint); return { content: [{ type: 'text', text: `⏰ **Overdue Tasks** (${tasks.length} found):\n\n` + tasks.map(task => { const dueDate = new Date(task.dueDate); const daysOverdue = Math.floor((new Date() - dueDate) / (1000 * 60 * 60 * 24)); return `**${task.title}** (ID: ${task.id})\n` + `- Priority: ${this.getPriorityText(task.priority)}\n` + `- Due: ${dueDate.toLocaleDateString()} (${daysOverdue} days ago)\n` + `- Project: ${task.projectId}\n` + `${task.tags && task.tags.length ? `- Tags: ${task.tags.join(', ')}\n` : ''}`; }).join('\n') }] }; } catch (error) { // Provide helpful alternatives when bulk APIs fail const cache = this.loadCache(); const cachedTaskCount = Object.keys(cache.tasks).length; return { content: [{ type: 'text', text: `❌ **TickTick bulk listing unavailable** (500 error)\n\n` + `💡 **Alternatives to get your overdue tasks**:\n` + `• **Use cache**: \`ticktick_get_cached_tasks()\` (${cachedTaskCount} tasks available)\n` + `• **Import data**: \`ticktick_import_from_csv()\` with exported TickTick data\n` + `• **Register tasks**: \`ticktick_register_task_id(task_id, project_id)\` for specific tasks\n` + `• **Read specific task**: \`ticktick_get_task_details(project_id, task_id)\` if you know the IDs\n\n` + `📋 **Why this happens**: TickTick's bulk task listing API has limitations and often returns 500 errors.\n\n` + `🚀 **Quick solution**: Export your tasks from TickTick app → Import with \`ticktick_import_from_csv()\`` }] }; } } async getUpcomingTasks({ days_ahead = 7, limit = 30 }) { try { const today = new Date(); const futureDate = new Date(today.getTime() + days_ahead * 24 * 60 * 60 * 1000); const todayStr = today.toISOString().split('T')[0]; const futureStr = futureDate.toISOString().split('T')[0]; const endpoint = `/task?dueAfter=${todayStr}&dueBefore=${futureStr}&limit=${limit}`; const tasks = await this.makeTickTickRequest(endpoint); return { content: [{ type: 'text', text: `📆 **Upcoming Tasks** (Next ${days_ahead} days, ${tasks.length} found):\n\n` + tasks.map(task => `**${task.title}** (ID: ${task.id})\n` + `- Status: ${task.status === 2 ? '✅ Completed' : '⏳ Pending'}\n` + `- Priority: ${this.getPriorityText(task.priority)}\n` + `- Due: ${new Date(task.dueDate).toLocaleDateString()}\n` + `- Project: ${task.projectId}\n` + `${task.tags && task.tags.length ? `- Tags: ${task.tags.join(', ')}\n` : ''}` ).join('\n') }] }; } catch (error) { // Provide helpful alternatives when bulk APIs fail const cache = this.loadCache(); const cachedTaskCount = Object.keys(cache.tasks).length; return { content: [{ type: 'text', text: `❌ **TickTick bulk listing unavailable** (500 error)\n\n` + `💡 **Alternatives to get your upcoming tasks**:\n` + `• **Use cache**: \`ticktick_get_cached_tasks()\` (${cachedTaskCount} tasks available)\n` + `• **Import data**: \`ticktick_import_from_csv()\` with exported TickTick data\n` + `• **Register tasks**: \`ticktick_register_task_id(task_id, project_id)\` for specific tasks\n` + `• **Read specific task**: \`ticktick_get_task_details(project_id, task_id)\` if you know the IDs\n\n` + `📋 **Why this happens**: TickTick's bulk task listing API has limitations and often returns 500 errors.\n\n` + `🚀 **Quick solution**: Export your tasks from TickTick app → Import with \`ticktick_import_from_csv()\`` }] }; } } async addTagToTask({ task_id, tag_name }) { try { // First get the current task to preserve existing data const task = await this.makeTickTickRequest(`/task/${task_id}`); // Add the new tag to existing tags const currentTags = task.tags || []; if (!currentTags.includes(tag_name)) { currentTags.push(tag_name); } // Update the task with new tags const updatedTask = await this.makeTickTickRequest(`/task/${task_id}`, 'PUT', { ...task, tags: currentTags }); return { content: [{ type: 'text', text: `✅ **Added Tag to Task**\n\n` + `📝 **Task**: ${task.title}\n` + `🏷️ **Tag Added**: ${tag_name}\n` + `🏷️ **All Tags**: ${currentTags.join(', ')}\n` + `📅 **Updated**: ${new Date().toLocaleString()}` }] }; } catch (error) { throw new Error(`Failed to add tag to task: ${error.message}`); } } async getUserProfile() { try { const profile = await this.makeTickTickRequest('/user/profile'); return { content: [{ type: 'text', text: `👤 **TickTick User Profile**\n\n` + `**Name**: ${profile.name || 'Not set'}\n` + `**Email**: ${profile.email || 'Not available'}\n` + `**Username**: ${profile.username || 'Not set'}\n` + `**Timezone**: ${profile.timezone || 'Not set'}\n` + `**Pro Status**: ${profile.pro ? '✅ Pro Member' : '❌ Free Account'}\n` + `**Member Since**: ${profile.createdTime ? new Date(profile.createdTime).toLocaleDateString() : 'Unknown'}\n` + `**Total Tasks**: ${profile.totalTasks || 0}\n` + `**Completed Tasks**: ${profile.completedTasks || 0}` }] }; } catch (error) { throw new Error(`Failed to get user profile: ${error.message}`); } } // ===== HABITS & TRACKING METHODS ===== async getHabits({ include_archived = false }) { try { const habits = await this.makeTickTickRequest('/habit'); const filteredHabits = include_archived ? habits : habits.filter(h => !h.archived); return { content: [{ type: 'text', text: `🔄 **TickTick Habits** (${filteredHabits.length} found):\n\n` + filteredHabits.map(habit => `**${habit.name}** (ID: ${habit.id})\n` + `- Frequency: ${habit.frequency || 'Daily'}\n` + `- Goal: ${habit.goal || 1} times per ${habit.frequency || 'day'}\n` + `- Current Streak: ${habit.currentStreak || 0} days\n` + `- Status: ${habit.paused ? '⏸️ Paused' : '▶️ Active'}\n` + `- Color: ${habit.color || 'Default'}\n` + `- Created: ${habit.createdTime ? new Date(habit.createdTime).toLocaleDateString() : 'Unknown'}\n` ).join('\n') }] }; } catch (error) { throw new Error(`Failed to get habits: ${error.message}`); } } async createHabit({ name, frequency = 'daily', goal = 1, reminder_time, color = '#3498db' }) { try { const habitData = { name, frequency, goal, color, createdTime: new Date().toISOString() }; if (reminder_time) { habitData.reminderTime = reminder_time; } const habit = await this.makeTickTickRequest('/habit', 'POST', habitData); return { content: [{ type: 'text', text: `✅ **Created TickTick Habit**\n\n` + `🔄 **Name**: ${habit.name}\n` + `🆔 **ID**: ${habit.id}\n` + `📅 **Frequency**: ${habit.frequency}\n` + `🎯 **Goal**: ${habit.goal} times per ${habit.frequency}\n` + `🎨 **Color**: ${habit.color}\n` + `${habit.reminderTime ? `⏰ **Reminder**: ${habit.reminderTime}\n` : ''}` + `📅 **Created**: ${new Date().toLocaleDateString()}` }] }; } catch (error) { throw new Error(`Failed to create habit: ${error.message}`); } } async updateHabit({ habit_id, name, frequency, goal, reminder_time, color }) { try { // Get current habit data const currentHabit = await this.makeTickTickRequest(`/habit/${habit_id}`); // Update only provided fields const updateData = { ...currentHabit }; if (name) updateData.name = name; if (frequency) updateData.frequency = frequency; if (goal) updateData.goal = goal; if (reminder_time) updateData.reminderTime = reminder_time; if (color) updateData.color = color; const updatedHabit = await this.makeTickTickRequest(`/habit/${habit_id}`, 'PUT', updateData); return { content: [{ type: 'text', text: `✅ **Updated TickTick Habit**\n\n` + `🔄 **Name**: ${updatedHabit.name}\n` + `📅 **Frequency**: ${updatedHabit.frequency}\n` + `🎯 **Goal**: ${updatedHabit.goal}\n` + `🎨 **Color**: ${updatedHabit.color}\n` + `📅 **Updated**: ${new Date().toLocaleString()}` }] }; } catch (error) { throw new Error(`Failed to update habit: ${error.message}`); } } async deleteHabit({ habit_id }) { try { await this.makeTickTickRequest(`/habit/${habit_id}`, 'DELETE'); return { content: [{ type: 'text', text: `✅ **Deleted TickTick Habit**\n\n` + `🆔 **Habit ID**: ${habit_id}\n` + `📅 **Deleted**: ${new Date().toLocaleString()}\n\n` + `⚠️ **Note**: This action cannot be undone. All habit history has been permanently removed.` }] }; } catch (error) { throw new Error(`Failed to delete habit: ${error.message}`); } } async checkinHabit({ habit_id, date, count = 1 }) { try { const checkinDate = date || new Date().toISOString().split('T')[0]; const checkinData = { habitId: habit_id, date: checkinDate, count: count, timestamp: new Date().toISOString() }; const checkin = await this.makeTickTickRequest('/habit/checkin', 'POST', checkinData); return { content: [{ type: 'text', text: `✅ **Habit Check-in Successful**\n\n` + `🔄 **Habit ID**: ${habit_id}\n` + `📅 **Date**: ${checkinDate}\n` + `🔢 **Count**: ${count}\n` + `🔥 **New Streak**: ${checkin.newStreak || 'Unknown'}\n` + `📊 **Progress**: ${checkin.progress || 'N/A'}\n` + `⏰ **Checked in**: ${new Date().toLocaleString()}` }] }; } catch (error) { throw new Error(`Failed to check in habit: ${error.message}`); } } async getHabitHistory({ habit_id, days_back = 30 }) { try { const endDate = new Date(); const startDate = new Date(endDate.getTime() - days_back * 24 * 60 * 60 * 1000); const endpoint = `/habit/${habit_id}/history?start=${startDate.toISOString().split('T')[0]}&end=${endDate.toISOString().split('T')[0]}`; const history = await this.makeTickTickRequest(endpoint); return { content: [{ type: 'text', text: `📊 **Habit History** (Last ${days_back} days)\n\n` + `🔄 **Habit ID**: ${habit_id}\n` + `📅 **Period**: ${startDate.toLocaleDateString()} - ${endDate.toLocaleDateString()}\n\n` + `**Completion Records**:\n` + history.map(record => `• ${record.date}: ${record.completed ? '✅' : '❌'} (${record.count || 0} times)` ).join('\n') + `\n\n**Summary**:\n` + `- Total Days: ${history.length}\n` + `- Completed: ${history.filter(r => r.completed).length}\n` + `- Completion Rate: ${Math.round((history.filter(r => r.completed).length / history.length) * 100)}%` }] }; } catch (error) { throw new Error(`Failed to get habit history: ${error.message}`); } } async getHabitStats({ habit_id }) { try { const stats = await this.makeTickTickRequest(`/habit/${habit_id}/stats`); return { content: [{ type: 'text', text: `📊 **Habit Statistics**\n\n` + `🔄 **Habit ID**: ${habit_id}\n` + `🔥 **Current Streak**: ${stats.currentStreak || 0} days\n` + `🏆 **Longest Streak**: ${stats.longestStreak || 0} days\n` + `📈 **Total Completions**: ${stats.totalCompletions || 0}\n` + `📅 **Days Tracked**: ${stats.daysTracked || 0}\n` + `📊 **Success Rate**: ${stats.successRate || 0}%\n` + `📈 **Weekly Average**: ${stats.weeklyAverage || 0} completions\n` + `📈 **Monthly Average**: ${stats.monthlyAverage || 0} completions\n` + `📅 **Last Completed**: ${stats.lastCompleted ? new Date(stats.lastCompleted).toLocaleDateString() : 'Never'}\n` + `📅 **Generated**: ${new Date().toLocaleString()}` }] }; } catch (error) { throw new Error(`Failed to get habit stats: ${error.message}`); } } async pauseHabit({ habit_id, resume_date }) { try { const pauseData = { paused: true, pausedDate: new Date().toISOString().split('T')[0] }; if (resume_date) { pauseData.resumeDate = resume_date; } const habit = await this.makeTickTickRequest(`/habit/${habit_id}`, 'PUT', pauseData); return { content: [{ type: 'text', text: `⏸️ **Habit Paused**\n\n` + `🔄 **Habit ID**: ${habit_id}\n` + `📅 **Paused Date**: ${pauseData.pausedDate}\n` + `${resume_date ? `📅 **Resume Date**: ${resume_date}\n` : ''}` + `📊 **Status**: Paused\n` + `📝 **Note**: Habit tracking is temporarily disabled. Your streak will be preserved.` }] }; } catch (error) { throw new Error(`Failed to pause habit: ${error.message}`); } } async resumeHabit({ habit_id }) { try { const resumeData = { paused: false, resumedDate: new Date().toISOString().split('T')[0] }; const habit = await this.makeTickTickRequest(`/habit/${habit_id}`, 'PUT', resumeData); return { content: [{ type: 'text', text: `▶️ **Habit Resumed**\n\n` + `🔄 **Habit ID**: ${habit_id}\n` + `📅 **Resumed Date**: ${resumeData.resumedDate}\n` + `📊 **Status**: Active\n` + `🔥 **Streak Preserved**: ${habit.currentStreak || 0} days\n` + `📝 **Note**: Habit tracking is now active again.` }] }; } catch (error) { throw new Error(`Failed to resume habit: ${error.message}`); } } async getHabitStreaks({ habit_id }) { try { const streaks = await this.makeTickTickRequest(`/habit/${habit_id}/streaks`); return { content: [{ type: 'text', text: `🔥 **Habit Streaks**\n\n` + `🔄 **Habit ID**: ${habit_id}\n` + `🔥 **Current Streak**: ${streaks.current || 0} days\n` + `🏆 **Longest Streak**: ${streaks.longest || 0} days\n` + `📅 **Current Streak Started**: ${streaks.currentStart ? new Date(streaks.currentStart).toLocaleDateString() : 'N/A'}\n` + `📅 **Longest Streak Period**: ${streaks.longestStart && streaks.longestEnd ? `${new Date(streaks.longestStart).toLocaleDateString()} - ${new Date(streaks.longestEnd).toLocaleDateString()}` : 'N/A'}\n` + `📊 **Streak History**:\n` + (streaks.history || []).slice(0, 5).map((streak, index) => `${index + 1}. ${streak.length} days (${new Date(streak.start).toLocaleDateString()} - ${new Date(streak.end).toLocaleDateString()})` ).join('\n') }] }; } catch (error) { throw new Error(`Failed to get habit streaks: ${error.message}`); } } async bulkCheckinHabits({ habit_ids, date }) { try { const checkinDate = date || new Date().toISOString().split('T')[0]; const bulkData = { habitIds: habit_ids, date: checkinDate, timestamp: new Date().toISOString() }; const results = await this.makeTickTickRequest('/habit/bulk-checkin', 'POST', bulkData); return { content: [{ type: 'text', text: `✅ **Bulk Habit Check-in**\n\n` + `📅 **Date**: ${checkinDate}\n` + `🔢 **Habits Processed**: ${habit_ids.length}\n` + `✅ **Successful**: ${results.successful || 0}\n` + `❌ **Failed**: ${results.failed || 0}\n\n` + `**Results**:\n` + (results.details || []).map(result => `• ${result.habitId}: ${result.success ? '✅ Success' : '❌ Failed'} ${result.newStreak ? `(Streak: ${result.newStreak})` : ''}` ).join('\n') + `\n\n⏰ **Processed**: ${new Date().toLocaleString()}` }] }; } catch (error) { throw new Error(`Failed to bulk check-in habits: ${error.message}`); } } async getHabitCalendar({ habit_id, year = new Date().getFullYear(), month }) { try { let endpoint = `/habit/${habit_id}/calendar?year=${year}`; if (month) { endpoint += `&month=${month}`; } const calendar = await this.makeTickTickRequest(endpoint); return { content: [{ type: 'text', text: `📅 **Habit Calendar View**\n\n` + `🔄 **Habit ID**: ${habit_id}\n` + `📅 **Period**: ${month ? `${year}-${month.toString().padStart(2, '0')}` : year}\n\n` + `**Calendar Data**:\n` + Object.entries(calendar.days || {}).map(([date, data]) => `${date}: ${data.completed ? '✅' : '⬜'} ${data.count ? `(${data.count}x)` : ''}` ).join('\n') + `\n\n**Summary**:\n` + `- Total Days: ${Object.keys(calendar.days || {}).length}\n` + `- Completed: ${Object.values(calendar.days || {}).filter(d => d.completed).length}\n` + `- Success Rate: ${calendar.successRate || 0}%\n` + `- Longest Streak in Period: ${calendar.longestStreakInPeriod || 0} days` }] }; } catch (error) { throw new Error(`Failed to get habit calendar: ${error.message}`); } } async setHabitGoal({ habit_id, goal_type = 'daily', target_count, target_streak }) { try { const goalData = { habitId: habit_id, goalType: goal_type, targetCount: target_count }; if (target_streak) { goalData.targetStreak = target_streak; } const goal = await this.makeTickTickRequest('/habit/goal', 'POST', goalData); return { content: [{ type: 'text', text: `🎯 **Habit Goal Set**\n\n` + `🔄 **Habit ID**: ${habit_id}\n` + `📊 **Goal Type**: ${goal_type}\n` + `🔢 **Target Count**: ${target_count}\n` + `${target_streak ? `🔥 **Target Streak**: ${target_streak} days\n` : ''}` + `📅 **Goal Set**: ${new Date().toLocaleString()}\n` + `📝 **Status**: Active` }] }; } catch (error) { throw new Error(`Failed to set habit goal: ${error.message}`); } } async getHabitsSummary({ date }) { try { const summaryDate = date || new Date().toISOString().split('T')[0]; const summary = await this.makeTickTickRequest(`/habit/summary?date=${summaryDate}`); return { content: [{ type: 'text', text: `📊 **Daily Habits Summary**\n\n` + `📅 **Date**: ${summaryDate}\n` + `🔄 **Total Habits**: ${summary.totalHabits || 0}\n` + `✅ **Completed**: ${summary.completed || 0}\n` + `⏳ **Pending**: ${summary.pending || 0}\n` + `📊 **Completion Rate**: ${summary.completionRate || 0}%\n\n` + `**Habit Details**:\n` + (summary.habits || []).map(habit => `• ${habit.name}: ${habit.completed ? '✅' : '⏳'} ${habit.currentStreak ? `(${habit.currentStreak} day streak)` : ''}` ).join('\n') + `\n\n🔥 **Active Streaks**: ${summary.activeStreaks || 0}\n` + `🏆 **Best Streak Today**: ${summary.bestStreak || 0} days` }] }; } catch (error) { throw new Error(`Failed to get habits summary: ${error.message}`); } } async exportHabitData({ habit_id, start_date, end_date, format = 'json' }) { try { let endpoint = '/habit/export'; const params = new URLSearchParams(); if (habit_id) params.append('habitId', habit_id); if (start_date) params.append('startDate', start_date); if (end_date) params.append('endDate', end_date); params.append('format', format); if (params.toString()) { endpoint += `?${params.toString()}`; } const exportData = await this.makeTickTickRequest(endpoint); return { content: [{ type: 'text', text: `📤 **Habit Data Export**\n\n` + `${habit_id ? `🔄 **Habit ID**: ${habit_id}\n` : '🔄 **Scope**: All Habits\n'}` + `📅 **Period**: ${start_date || 'All time'} ${end_date ? `to ${end_date}` : ''}\n` + `📋 **Format**: ${format.toUpperCase()}\n` + `📊 **Records**: ${exportData.recordCount || 0}\n` + `📁 **File Size**: ${exportData.fileSize || 'Unknown'}\n` + `📅 **Generated**: ${new Date().toLocaleString()}\n\n` + `**Export Data Preview**:\n` + `\`\`\`${format}\n${JSON.stringify(exportData.preview || exportData, null, 2).substring(0, 500)}...\n\`\`\`` }] }; } catch (error) { throw new Error(`Failed to export habit data: ${error.message}`); } } // ===== ADVANCED TAGS METHODS ===== async updateTag({ tag_id, name, color }) { try { // Get current tag data const currentTag = await this.makeTickTickRequest(`/tag/${tag_id}`); // Update only provided fields const updateData = { ...currentTag }; if (name) updateData.name = name; if (color) updateData.color = color; const updatedTag = await this.makeTickTickRequest(`/tag/${tag_id}`, 'PUT', updateData); return { content: [{ type: 'text', text: `✅ **Updated TickTick Tag**\n\n` + `🏷️ **Name**: ${updatedTag.name}\n` + `🆔 **ID**: ${tag_id}\n` + `🎨 **Color**: ${updatedTag.color}\n` + `📊 **Usage Count**: ${updatedTag.usageCount || 0}\n` + `📅 **Updated**: ${new Date().toLocaleString()}` }] }; } catch (error) { throw new Error(`Failed to update tag: ${error.message}`); } } async deleteTag({ tag_id }) { try { await this.makeTickTickRequest(`/tag/${tag_id}`, 'DELETE'); return { content: [{ type: 'text', text: `✅ **Deleted TickTick Tag**\n\n` + `🆔 **Tag ID**: ${tag_id}\n` + `📅 **Deleted**: ${new Date().toLocaleString()}\n\n` + `⚠️ **Note**: This action cannot be undone. The tag has been removed from all tasks.` }] }; } catch (error) { throw new Error(`Failed to delete tag: ${error.message}`); } } async removeTagFromTask({ task_id, tag_name }) { try { // First get the current task to preserve existing data const task = await this.makeTickTickRequest(`/task/${task_id}`); // Remove the tag from existing tags const currentTags = task.tags || []; const updatedTags = currentTags.filter(tag => tag !== tag_name); // Update the task with new tags const updatedTask = await this.makeTickTickRequest(`/task/${task_id}`, 'PUT', { ...task, tags: updatedTags }); return { content: [{ type: 'text', text: `✅ **Removed Tag from Task**\n\n` + `📝 **Task**: ${task.title}\n` + `🏷️ **Tag Removed**: ${tag_name}\n` + `🏷️ **Remaining Tags**: ${updatedTags.length > 0 ? updatedTags.join(', ') : 'None'}\n` + `📅 **Updated**: ${new Date().toLocaleString()}` }] }; } catch (error) { throw new Error(`Failed to remove tag from task: ${error.message}`); } } async getTasksByTag({ tag_name, limit = 50 }) { try { const endpoint = `/task?tags=${encodeURIComponent(tag_name)}&limit=${limit}`; const tasks = await this.makeTickTickRequest(endpoint); return { content: [{ type: 'text', text: `🏷️ **Tasks Tagged with "${tag_name}"** (${tasks.length} found):\n\n` + tasks.map(task => `**${task.title}** (ID: ${task.id})\n` + `- Status: ${task.status === 2 ? '✅ Completed' : '⏳ Pending'}\n` + `- Priority: ${this.getPriorityText(task.priority)}\n` + `- Project: ${task.projectId}\n` + `${task.dueDate ? `- Due: ${new Date(task.dueDate).toLocaleDateString()}\n` : ''}` + `- All Tags: ${task.tags && task.tags.length ? task.tags.join(', ') : 'None'}\n` ).join('\n') + `\n**Summary**:\n` + `- Total Tasks: ${tasks.length}\n` + `- Completed: ${tasks.filter(t => t.status === 2).length}\n` + `- Pending: ${tasks.filter(t => t.status !== 2).length}` }] }; } catch (error) { throw new Error(`Failed to get tasks by tag: ${error.message}`); } } async getTagUsageStats({ tag_id }) { try { const stats = await this.makeTickTickRequest(`/tag/${tag_id}/stats`); return { content: [{ type: 'text', text: `📊 **Tag Usage Statistics**\n\n` + `🏷️ **Tag ID**: ${tag_id}\n` + `📝 **Tag Name**: ${stats.name || 'Unknown'}\n` + `📊 **Total Usage**: ${stats.totalTasks || 0} tasks\n` + `✅ **Completed Tasks**: ${stats.completedTasks || 0}\n` + `⏳ **Pending Tasks**: ${stats.pendingTasks || 0}\n` + `📈 **Usage Trend**: ${stats.trend || 'Stable'}\n` + `📅 **First Used**: ${stats.firstUsed ? new Date(stats.firstUsed).toLocaleDateString() : 'Unknown'}\n` + `📅 **Last Used**: ${stats.lastUsed ? new Date(stats.lastUsed).toLocaleDateString() : 'Unknown'}\n` + `🎯 **Completion Rate**: ${stats.completionRate || 0}%\n` + `📊 **Usage by Project**:\n` + (stats.projectBreakdown || []).map(proj => `• ${proj.projectName}: ${proj.taskCount} tasks` ).join('\n') }] }; } catch (error) { throw new Error(`Failed to get tag usage stats: ${error.message}`); } } async mergeTags({ source_tag_id, target_tag_id }) { try { const mergeData = { sourceTagId: source_tag_id, targetTagId: target_tag_id }; const result = await this.makeTickTickRequest('/tag/merge', 'POST', mergeData); return { content: [{ type: 'text', text: `✅ **Tags Merged Successfully**\n\n` + `🔄 **Source Tag**: ${source_tag_id} (deleted)\n` + `🎯 **Target Tag**: ${target_tag_id} (kept)\n` + `📊 **Tasks Affected**: ${result.tasksAffected || 0}\n` + `📝 **Operation**: All tasks with source tag now have target tag\n` + `📅 **Merged**: ${new Date().toLocaleString()}\n\n` + `⚠️ **Note**: Source tag has been permanently deleted.` }] }; } catch (error) { throw new Error(`Failed to merge tags: ${error.message}`); } } async bulkTagOperations({ operation, task_ids, tag_names, replace_with }) { try { const bulkData = { operation, taskIds: task_ids, tagNames: tag_names }; if (operation === 'replace' && replace_with) { bulkData.replaceWith = replace_with; } const result = await this.makeTickTickRequest('/tag/bulk', 'POST', bulkData); return { content: [{ type: 'text', text: `✅ **Bulk Tag Operation Complete**\n\n` + `🔄 **Operation**: ${operation.toUpperCase()}\n` + `📝 **Tasks Processed**: ${task_ids.length}\n` + `🏷️ **Tags**: ${tag_names.join(', ')}\n` + `${operation === 'replace' && replace_with ? `🔄 **Replaced With**: ${replace_with.join(', ')}\n` : ''}` + `✅ **Successful**: ${result.successful || 0}\n` + `❌ **Failed**: ${result.failed || 0}\n\n` + `**Results Summary**:\n` + (result.details || []).slice(0, 10).map(detail => `• Task ${detail.taskId}: ${detail.success ? '✅ Success' : '❌ Failed'}` ).join('\n') + `${result.details && result.details.length > 10 ? `\n... and ${result.details.length - 10} more` : ''}\n\n` + `⏰ **Processed**: ${new Date().toLocaleString()}` }] }; } catch (error) { throw new Error(`Failed to perform bulk tag operations: ${error.message}`); } } // ===== FOCUS TIME & POMODORO METHODS ===== async startFocusSession({ task_id, duration = 25, session_type = 'focus' }) { try { const sessionData = { type: session_type, duration: duration, startTime: new Date().toISOString() }; if (task_id) { sessionData.taskId = task_id; } const session = await this.makeTickTickRequest('/focus/start', 'POST', sessionData); return { content: [{ type: 'text', text: `⏰ **Focus Session Started**\n\n` + `🆔 **Session ID**: ${session.id}\n` + `🎯 **Type**: ${session_type.replace('_', ' ').toUpperCase()}\n` + `⏱️ **Duration**: ${duration} minutes\n` + `${task_id ? `📝 **Task**: ${task_id}\n` : ''}` + `▶️ **Started**: ${new Date().toLocaleString()}\n` + `🏁 **Ends**: ${new Date(Date.now() + duration * 60000).toLocaleString()}\n\n` + `🔥 **Focus Mode Active** - Stay concentrated and avoid distractions!` }] }; } catch (error) { throw new Error(`Failed to start focus session: ${error.message}`); } } async stopFocusSession({ session_id }) { try { const stopData = { sessionId: session_id, endTime: new Date().toISOString() }; const result = await this.makeTickTickRequest('/focus/stop', 'POST', stopData); return { content: [{ type: 'text', text: `⏹️ **Focus Session Completed**\n\n` + `🆔 **Session ID**: ${session_id}\n` + `⏱️ **Duration**: ${Math.round(result.actualDuration || 0)} minutes\n` + `📊 **Completion**: ${result.completionRate || 0}%\n` + `🎯 **Focus Score**: ${result.focusScore || 'N/A'}/10\n` + `⏰ **Ended**: ${new Date().toLocaleString()}\n\n` + `${result.completionRate >= 90 ? '🎉 **Excellent focus!** Well done!' : result.completionRate >= 70 ? '👍 **Good session!** Keep it up!' : '💪 **Practice makes perfect!** Try again soon.'}` }] }; } catch (error) { throw new Error(`Failed to stop focus session: ${error.message}`); } } async pauseFocusSession({ session_id }) { try { const pauseData = { sessionId: session_id, pauseTime: new Date().toISOString() }; const result = await this.makeTickTickRequest('/focus/pause', 'POST', pauseData); return { content: [{ type: 'text', text: `⏸️ **Focus Session Paused**\n\n` + `🆔 **Session ID**: ${session_id}\n` + `⏱️ **Elapsed Time**: ${Math.round(result.elapsedMinutes || 0)} minutes\n` + `⏳ **Remaining**: ${Math.round(result.remainingMinutes || 0)} minutes\n` + `⏸️ **Paused**: ${new Date().toLocaleString()}\n\n` + `📝 **Note**: Your session is paused. Resume when you're ready to continue focusing.` }] }; } catch (error) { throw new Error(`Failed to pause focus session: ${error.message}`); } } async resumeFocusSession({ session_id }) { try { const resumeData = { sessionId: session_id, resumeTime: new Date().toISOString() }; const result = await this.makeTickTickRequest('/focus/resume', 'POST', resumeData); return { content: [{ type: 'text', text: `▶️ **Focus Session Resumed**\n\n` + `🆔 **Session ID**: ${session_id}\n` + `⏱️ **Remaining Time**: ${Math.round(result.remainingMinutes || 0)} minutes\n` + `🔄 **Resumed**: ${new Date().toLocaleString()}\n` + `🏁 **New End Time**: ${new Date(Date.now() + (result.remainingMinutes || 0) * 60000).toLocaleString()}\n\n` + `🔥 **Back to focus!** Let's finish strong!` }] }; } catch (error) { throw new Error(`Failed to resume focus session: ${error.message}`); } } async getFocusStats({ period = 'today' }) { try { const stats = await this.makeTickTickRequest(`/focus/stats?period=${period}`); return { content: [{ type: 'text', text: `📊 **Focus Statistics** (${period.toUpperCase()})\n\n` + `⏱️ **Total Focus Time**: ${Math.round(stats.totalMinutes || 0)} minutes\n` + `🎯 **Sessions Completed**: ${stats.completedSessions || 0}\n` + `📈 **Average Session**: ${Math.round(stats.averageSessionLength || 0)} minutes\n` + `🔥 **Focus Score**: ${stats.averageFocusScore || 0}/10\n` + `🎯 **Completion Rate**: ${stats.completionRate || 0}%\n` + `🏆 **Longest Session**: ${Math.round(stats.longestSession || 0)} minutes\n` + `📅 **Most Productive Day**: ${stats.bestDay || 'N/A'}\n\n` + `**Session Types**:\n` + `• Focus: ${stats.focusSessions || 0} sessions\n` + `• Short Break: ${stats.shortBreaks || 0} sessions\n` + `• Long Break: ${stats.longBreaks || 0} sessions\n\n` + `📈 **Trend**: ${stats.trend || 'Stable'}\n` + `🎯 **Goal Progress**: ${stats.goalProgress || 0}%` }] }; } catch (error) { throw new Error(`Failed to get focus stats: ${error.message}`); } } async setTaskEstimate({ task_id, estimated_minutes, estimate_type = 'minutes' }) { try { let finalEstimate = estimated_minutes; // Convert estimate types if (estimate_type === 'pomodoros') { finalEstimate = estimated_minutes * 25; // 25 minutes per pomodoro } else if (estimate_type === 'hours') { finalEstimate = estimated_minutes * 60; } const estimateData = { taskId: task_id, estimatedMinutes: finalEstimate, estimateType: estimate_type, setAt: new Date().toISOString() }; const result = await this.makeTickTickRequest('/task/estimate', 'POST', estimateData); return { content: [{ type: 'text', text: `⏱️ **Task Estimate Set**\n\n` + `📝 **Task ID**: ${task_id}\n` + `⏱️ **Estimate**: ${estimated_minutes} ${estimate_type}\n` + `🕐 **Total Minutes**: ${finalEstimate} minutes\n` + `🍅 **Pomodoros**: ${Math.ceil(finalEstimate / 25)}\n` + `📅 **Set**: ${new Date().toLocaleString()}\n\n` + `💡 **Tip**: Use this estimate to plan your focus sessions!` }] }; } catch (error) { throw new Error(`Failed to set task estimate: ${error.message}`); } } async getDailyFocusSummary({ date }) { try { const summaryDate = date || new Date().toISOString().split('T')[0]; const summary = await this.makeTickTickRequest(`/focus/daily?date=${summaryDate}`); return { content: [{ type: 'text', text: `📊 **Daily Focus Summary**\n\n` + `📅 **Date**: ${summaryDate}\n` + `⏱️ **Total Focus Time**: ${Math.round(summary.totalMinutes || 0)} minutes\n` + `🎯 **Sessions**: ${summary.totalSessions || 0}\n` + `✅ **Completed**: ${summary.completedSessions || 0}\n` + `⏸️ **Incomplete**: ${summary.incompleteSessions || 0}\n` + `📈 **Focus Score**: ${summary.averageFocusScore || 0}/10\n` + `🏆 **Best Session**: ${Math.round(summary.bestSession || 0)} minutes\n\n` + `**Hourly Breakdown**:\n` + (summary.hourlyBreakdown || []).map(hour => `${hour.hour}:00 - ${Math.round(hour.minutes || 0)} min` ).join('\n') + `\n\n**Top Tasks**:\n` + (summary.topTasks || []).slice(0, 5).map((task, index) => `${index + 1}. ${task.title}: ${Math.round(task.focusTime || 0)} min` ).join('\n') + `\n\n🎯 **Goal Progress**: ${summary.goalProgress || 0}%` }] }; } catch (error) { throw new Error(`Failed to get daily focus summary: ${error.message}`); } } async getFocusHistory({ days_back = 30, task_id }) { try { let endpoint = `/focus/history?days=${days_back}`; if (task_id) { endpoint += `&taskId=${task_id}`; } const history = await this.makeTickTickRequest(endpoint); return { content: [{ type: 'text', text: `📈 **Focus History** (Last ${days_back} days)\n\n` + `${task_id ? `📝 **Task**: ${task_id}\n` : ''}` + `📊 **Total Sessions**: ${history.sessions ? history.sessions.length : 0}\n` + `⏱️ **Total Time**: ${Math.round(history.totalMinutes || 0)} minutes\n` + `📈 **Average Daily**: ${Math.round((history.totalMinutes || 0) / days_back)} minutes\n\n` + `**Recent Sessions**:\n` + (history.sessions || []).slice(0, 10).map(session => `• ${new Date(session.date).toLocaleDateString()}: ${Math.round(session.duration || 0)} min (${session.completed ? '✅' : '❌'})` ).join('\n') + `${history.sessions && history.sessions.length > 10 ? `\n... and ${history.sessions.length - 10} more sessions` : ''}\n\n` + `**Weekly Trends**:\n` + (history.weeklyTrends || []).map(week => `Week ${week.week}: ${Math.round(week.totalMinutes || 0)} min (${week.sessions || 0} sessions)` ).join('\n') }] }; } catch (error) { throw new Error(`Failed to get focus history: ${error.message}`); } } async setFocusGoals({ goal_type = 'daily', target_minutes, target_sessions }) { try { const goalData = { type: goal_type, targetMinutes: target_minutes, setAt: new Date().toISOString() }; if (target_sessions) { goalData.targetSessions = target_sessions; } const goal = await this.makeTickTickRequest('/focus/goals', 'POST', goalData); return { content: [{ type: 'text', text: `🎯 **Focus Goal Set**\n\n` + `📊 **Goal Type**: ${goal_type.toUpperCase()}\n` + `⏱️ **Target Time**: ${target_minutes} minutes\n` + `${target_sessions ? `🎯 **Target Sessions**: ${target_sessions}\n` : ''}` + `🍅 **Pomodoros Needed**: ${Math.ceil(target_minutes / 25)}\n` + `📅 **Set**: ${new Date().toLocaleString()}\n` + `📈 **Current Progress**: 0%\n\n` + `💪 **You've got this!** Start your first focus session to begin tracking progress.` }] }; } catch (error) { throw new Error(`Failed to set focus goals: ${error.message}`); } } async getProductivityInsights({ period = 'week' }) { try { const insights = await this.makeTickTickRequest(`/analytics/productivity?period=${period}`); return { content: [{ type: 'text', text: `🤖 **AI Productivity Insights** (${period.toUpperCase()})\n\n` + `📊 **Overall Score**: ${insights.productivityScore || 0}/100\n` + `📈 **Trend**: ${insights.trend || 'Stable'}\n` + `🎯 **Focus Efficiency**: ${insights.focusEfficiency || 0}%\n` + `✅ **Task Completion**: ${insights.taskCompletionRate || 0}%\n\n` + `**Key Insights**:\n` + (insights.insights || []).map((insight, index) => `${index + 1}. ${insight.title}\n ${insight.description}` ).join('\n\n') + `\n\n**Recommendations**:\n` + (insights.recommendations || []).map((rec, index) => `🔸 ${rec.title}: ${rec.description}` ).join('\n') + `\n\n**Peak Performance**:\n` + `• Best Day: ${insights.bestDay || 'N/A'}\n` + `• Peak Hours: ${insights.peakHours || 'N/A'}\n` + `• Most Productive: ${insights.mostProductiveTask || 'N/A'}\n\n` + `🎯 **Next Week Goal**: ${insights.suggestedGoal || 'Keep up the momentum!'}` }] }; } catch (error) { throw new Error(`Failed to get productivity insights: ${error.message}`); } } // Advanced Project Management Methods (Phase 2: 12 operations) async getProjectFolders() { try { const folders = await this.makeTickTickRequest('/project/folder'); return { content: [{ type: 'text', text: `📁 **Project Folders** (${folders.length || 0} folders)\n\n` + (folders.length > 0 ? folders.map((folder, index) => `${index + 1}. **${folder.name}**\n` + ` 📁 ID: ${folder.id}\n` + ` 📊 Projects: ${folder.projectCount || 0}\n` + ` 🎨 Color: ${folder.color || 'Default'}\n` + ` 📅 Created: ${folder.createdTime || 'N/A'}` ).join('\n\n') : '📭 No project folders found.' ) + `\n\n💡 **Tip**: Use folders to organize related projects and improve workspace navigation.` }] }; } catch (error) { throw new Error(`Failed to get project folders: ${error.message}`); } } async createProjectFolder({ name, color = '#3498db', description = '' }) { try { const folderData = { name, color, description }; const folder = await this.makeTickTickRequest('/project/folder', 'POST', folderData); return { content: [{ type: 'text', text: `✅ **Project Folder Created Successfully!**\n\n` + `📁 **${folder.name}**\n` + `🆔 ID: ${folder.id}\n` + `🎨 Color: ${folder.color}\n` + `📝 Description: ${folder.description || 'None'}\n` + `📅 Created: ${new Date().toLocaleDateString()}\n\n` + `🎯 **Next Steps**:\n` + `• Move existing projects to this folder\n` + `• Create new projects within this folder\n` + `• Set up folder-specific workflows` }] }; } catch (error) { throw new Error(`Failed to create project folder: ${error.message}`); } } async moveProjectToFolder({ project_id, folder_id = null }) { try { const moveData = { folderId: folder_id }; await this.makeTickTickRequest(`/project/${project_id}/move`, 'PUT', moveData); const folderName = folder_id ? `folder ${folder_id}` : 'root level'; return { content: [{ type: 'text', text: `📁 **Project Moved Successfully!**\n\n` + `✅ Project ID: ${project_id}\n` + `📍 New Location: ${folderName}\n` + `📅 Moved: ${new Date().toLocaleString()}\n\n` + `🎯 **Organization Tips**:\n` + `• Group related projects together\n` + `• Use folders for different clients or areas\n` + `• Keep active projects easily accessible` }] }; } catch (error) { throw new Error(`Failed to move project to folder: ${error.message}`); } } async archiveProject({ project_id }) { try { await this.makeTickTickRequest(`/project/${project_id}/archive`, 'PUT'); return { content: [{ type: 'text', text: `📦 **Project Archived Successfully!**\n\n` + `✅ Project ID: ${project_id}\n` + `📅 Archived: ${new Date().toLocaleString()}\n` + `🔒 Status: Hidden from active view\n\n` + `📋 **What This Means**:\n` + `• Project is preserved but hidden\n` + `• All tasks and data remain intact\n` + `• Can be unarchived anytime\n` + `• Reduces workspace clutter\n\n` + `💡 **Pro Tip**: Archive completed projects to keep your workspace clean while preserving historical data.` }] }; } catch (error) { throw new Error(`Failed to archive project: ${error.message}`); } } async unarchiveProject({ project_id }) { try { await this.makeTickTickRequest(`/project/${project_id}/unarchive`, 'PUT'); return { content: [{ type: 'text', text: `📤 **Project Unarchived Successfully!**\n\n` + `✅ Project ID: ${project_id}\n` + `📅 Restored: ${new Date().toLocaleString()}\n` + `👁️ Status: Now visible in active view\n\n` + `🎯 **Project Restored**:\n` + `• All tasks and data preserved\n` + `• Full functionality restored\n` + `• Available in project lists\n` + `• Ready for active use\n\n` + `💡 **Note**: Check project settings and update as needed for current workflows.` }] }; } catch (error) { throw new Error(`Failed to unarchive project: ${error.message}`); } } async duplicateProject({ project_id, new_name, include_tasks = true, include_settings = true }) { try { const duplicateData = { name: new_name, includeTasks: include_tasks, includeSettings: include_settings }; const newProject = await this.makeTickTickRequest(`/project/${project_id}/duplicate`, 'POST', duplicateData); return { content: [{ type: 'text', text: `🔄 **Project Duplicated Successfully!**\n\n` + `📋 **Original**: Project ${project_id}\n` + `📋 **New Copy**: ${newProject.name} (ID: ${newProject.id})\n` + `📅 Created: ${new Date().toLocaleString()}\n\n` + `📊 **What Was Copied**:\n` + `• ${include_tasks ? '✅' : '❌'} Tasks and subtasks\n` + `• ${include_settings ? '✅' : '❌'} Project settings\n` + `• ✅ Project structure\n` + `• ✅ Custom fields\n\n` + `🎯 **Use Cases**:\n` + `• Template for recurring projects\n` + `• Testing new workflows\n` + `• Client project templates\n` + `• Backup before major changes` }] }; } catch (error) { throw new Error(`Failed to duplicate project: ${error.message}`); } } async getProjectStats({ project_id }) { try { const stats = await this.makeTickTickRequest(`/project/${project_id}/stats`); const completionRate = stats.totalTasks > 0 ? Math.round((stats.completedTasks / stats.totalTasks) * 100) : 0; return { content: [{ type: 'text', text: `📊 **Project Statistics**\n\n` + `📈 **Overview**:\n` + `• Total Tasks: ${stats.totalTasks || 0}\n` + `• Completed: ${stats.completedTasks || 0}\n` + `• In Progress: ${stats.inProgressTasks || 0}\n` + `• Overdue: ${stats.overdueTasks || 0}\n` + `• Completion Rate: ${completionRate}%\n\n` + `⏱️ **Time Tracking**:\n` + `• Total Time Spent: ${stats.totalTimeSpent || '0h'}\n` + `• Average Task Duration: ${stats.avgTaskDuration || 'N/A'}\n` + `• Focus Sessions: ${stats.focusSessions || 0}\n\n` + `📅 **Timeline**:\n` + `• Created: ${stats.createdDate || 'N/A'}\n` + `• Last Activity: ${stats.lastActivity || 'N/A'}\n` + `• Days Active: ${stats.daysActive || 0}\n\n` + `🏷️ **Organization**:\n` + `• Tags Used: ${stats.uniqueTags || 0}\n` + `• Subtasks: ${stats.subtasks || 0}\n` + `• Priority Distribution: High: ${stats.highPriority || 0}, Medium: ${stats.mediumPriority || 0}, Low: ${stats.lowPriority || 0}` }] }; } catch (error) { throw new Error(`Failed to get project stats: ${error.message}`); } } async setProjectColor({ project_id, color }) { try { const updateData = { color }; await this.makeTickTickRequest(`/project/${project_id}`, 'PUT', updateData); return { content: [{ type: 'text', text: `🎨 **Project Color Updated!**\n\n` + `✅ Project ID: ${project_id}\n` + `🎨 New Color: ${color}\n` + `📅 Updated: ${new Date().toLocaleString()}\n\n` + `🌈 **Color Coding Benefits**:\n` + `• Visual project identification\n` + `• Quick status recognition\n` + `• Improved workspace organization\n` + `• Better team coordination\n\n` + `💡 **Pro Tip**: Use consistent color schemes across related projects for better visual organization.` }] }; } catch (error) { throw new Error(`Failed to set project color: ${error.message}`); } } async reorderProjects({ project_orders }) { try { const reorderData = { orders: project_orders.map(order => ({ projectId: order.project_id, sortOrder: order.position })) }; await this.makeTickTickRequest('/project/reorder', 'PUT', reorderData); return { content: [{ type: 'text', text: `🔄 **Projects Reordered Successfully!**\n\n` + `✅ Updated Order for ${project_orders.length} projects\n` + `📅 Reordered: ${new Date().toLocaleString()}\n\n` + `📋 **New Order**:\n` + project_orders.map((order, index) => `${index + 1}. Project ${order.project_id} (Position: ${order.position})` ).join('\n') + `\n\n🎯 **Organization Benefits**:\n` + `• Prioritized project visibility\n` + `• Improved workflow efficiency\n` + `• Custom workspace layout\n` + `• Better focus on important projects` }] }; } catch (error) { throw new Error(`Failed to reorder projects: ${error.message}`); } } async getProjectTemplates() { try { const templates = await this.makeTickTickRequest('/project/templates'); return { content: [{ type: 'text', text: `📋 **Project Templates** (${templates.length || 0} available)\n\n` + (templates.length > 0 ? templates.map((template, index) => `${index + 1}. **${template.name}**\n` + ` 🆔 ID: ${template.id}\n` + ` 📝 Description: ${template.description || 'No description'}\n` + ` 📊 Tasks: ${template.taskCount || 0}\n` + ` 🏷️ Category: ${template.category || 'General'}\n` + ` 📅 Created: ${template.createdTime || 'N/A'}` ).join('\n\n') : '📭 No project templates found.' ) + `\n\n🚀 **Template Benefits**:\n` + `• Rapid project setup\n` + `• Consistent structure\n` + `• Best practice workflows\n` + `• Time-saving automation` }] }; } catch (error) { throw new Error(`Failed to get project templates: ${error.message}`); } } async createProjectFromTemplate({ template_id, project_name, customize_settings = {} }) { try { const projectData = { templateId: template_id, name: project_name, customizations: customize_settings }; const newProject = await this.makeTickTickRequest('/project/from-template', 'POST', projectData); return { content: [{ type: 'text', text: `🚀 **Project Created from Template!**\n\n` + `📋 **New Project**: ${newProject.name}\n` + `🆔 Project ID: ${newProject.id}\n` + `📋 Template Used: ${template_id}\n` + `📅 Created: ${new Date().toLocaleString()}\n\n` + `📊 **Project Setup**:\n` + `• Tasks: ${newProject.taskCount || 0} imported\n` + `• Structure: ✅ Applied\n` + `• Settings: ${Object.keys(customize_settings).length > 0 ? '✅ Customized' : '📋 Default'}\n` + `• Ready for use: ✅\n\n` + `🎯 **Next Steps**:\n` + `• Review and adjust tasks\n` + `• Set project deadlines\n` + `• Assign team members\n` + `• Customize project settings` }] }; } catch (error) { throw new Error(`Failed to create project from template: ${error.message}`); } } async exportProject({ project_id, format = 'json', include_completed = true, include_attachments = false }) { try { const exportData = { format, includeCompleted: include_completed, includeAttachments: include_attachments }; const exportResult = await this.makeTickTickRequest(`/project/${project_id}/export`, 'POST', exportData); const fileSize = exportResult.fileSize ? `${Math.round(exportResult.fileSize / 1024)}KB` : 'Unknown'; return { content: [{ type: 'text', text: `📤 **Project Export Completed!**\n\n` + `📋 **Project**: ${project_id}\n` + `📁 **Format**: ${format.toUpperCase()}\n` + `📊 **File Size**: ${fileSize}\n` + `📅 **Exported**: ${new Date().toLocaleString()}\n\n` + `📦 **Export Contents**:\n` + `• ${include_completed ? '✅' : '❌'} Completed tasks\n` + `• ${include_attachments ? '✅' : '❌'} File attachments\n` + `• ✅ Project structure\n` + `• ✅ Task metadata\n` + `• ✅ Time tracking data\n\n` + `🔗 **Download**: ${exportResult.downloadUrl || 'Check email for download link'}\n\n` + `💡 **Use Cases**:\n` + `• Data backup\n` + `• Client reporting\n` + `• Project archival\n` + `• External analysis` }] }; } catch (error) { throw new Error(`Failed to export project: ${error.message}`); } } // Collaboration & Sharing Methods (Phase 2: 12 operations) async shareProject({ project_id, emails, permission_level = 'edit', message = '' }) { try { const shareData = { emails, permissionLevel: permission_level, invitationMessage: message }; const result = await this.makeTickTickRequest(`/project/${project_id}/share`, 'POST', shareData); return { content: [{ type: 'text', text: `🤝 **Project Shared Successfully!**\n\n` + `📋 **Project**: ${project_id}\n` + `👥 **Shared with**: ${emails.length} user(s)\n` + `🔑 **Permission Level**: ${permission_level}\n` + `📅 **Shared**: ${new Date().toLocaleString()}\n\n` + `📧 **Recipients**:\n` + emails.map((email, index) => `${index + 1}. ${email}`).join('\n') + `\n\n${message ? `📝 **Message**: "${message}"\n\n` : ''}` + `✅ **Next Steps**:\n` + `• Recipients will receive email invitations\n` + `• They can access the project once they accept\n` + `• Manage permissions anytime from project settings\n` + `• Track collaboration activity in the team feed` }] }; } catch (error) { throw new Error(`Failed to share project: ${error.message}`); } } async getSharedProjects({ include_owned = true, include_received = true }) { try { const sharedProjects = await this.makeTickTickRequest( `/project/shared?includeOwned=${include_owned}&includeReceived=${include_received}` ); const ownedProjects = sharedProjects.filter(p => p.isOwner) || []; const receivedProjects = sharedProjects.filter(p => !p.isOwner) || []; return { content: [{ type: 'text', text: `🤝 **Shared Projects Overview**\n\n` + `📊 **Summary**:\n` + `• Projects you own: ${ownedProjects.length}\n` + `• Projects shared with you: ${receivedProjects.length}\n` + `• Total shared projects: ${sharedProjects.length}\n\n` + (ownedProjects.length > 0 ? `👑 **Projects You Own & Share**:\n` + ownedProjects.map((project, index) => `${index + 1}. **${project.name}** (ID: ${project.id})\n` + ` 👥 Collaborators: ${project.memberCount || 0}\n` + ` 🔑 Permission: ${project.permission || 'Admin'}\n` + ` 📅 Last Activity: ${project.lastActivity || 'N/A'}` ).join('\n\n') + '\n\n' : '' ) + (receivedProjects.length > 0 ? `📥 **Projects Shared With You**:\n` + receivedProjects.map((project, index) => `${index + 1}. **${project.name}** (ID: ${project.id})\n` + ` 👤 Owner: ${project.ownerName || project.ownerId}\n` + ` 🔑 Your Role: ${project.yourRole || 'Member'}\n` + ` 📅 Joined: ${project.joinedDate || 'N/A'}` ).join('\n\n') + '\n\n' : '' ) + `💡 **Collaboration Tips**:\n` + `• Use @mentions in comments for better communication\n` + `• Set clear task assignments and deadlines\n` + `• Review team activity regularly\n` + `• Manage permissions based on roles` }] }; } catch (error) { throw new Error(`Failed to get shared projects: ${error.message}`); } } async inviteCollaborator({ project_id, email, role = 'member', personal_message = '' }) { try { const inviteData = { email, role, personalMessage: personal_message }; const invitation = await this.makeTickTickRequest(`/project/${project_id}/invite`, 'POST', inviteData); return { content: [{ type: 'text', text: `📧 **Collaborator Invited Successfully!**\n\n` + `📋 **Project**: ${project_id}\n` + `👤 **Invited**: ${email}\n` + `🎭 **Role**: ${role}\n` + `📅 **Invited**: ${new Date().toLocaleString()}\n` + `🆔 **Invitation ID**: ${invitation.id || 'Generated'}\n\n` + `${personal_message ? `💬 **Personal Message**:\n"${personal_message}"\n\n` : ''}` + `📮 **Invitation Status**:\n` + `• Email sent to ${email}\n` + `• Pending acceptance\n` + `• Will expire in 7 days if not accepted\n\n` + `🔑 **Role Permissions (${role})**:\n` + (role === 'admin' ? `• ✅ Full project access\n• ✅ Invite others\n• ✅ Manage settings\n• ✅ Delete project` : role === 'editor' ? `• ✅ Create/edit tasks\n• ✅ Add comments\n• ❌ Invite others\n• ❌ Manage settings` : `• ✅ View tasks\n• ✅ Add comments\n• ❌ Edit tasks\n• ❌ Invite others` ) }] }; } catch (error) { throw new Error(`Failed to invite collaborator: ${error.message}`); } } async removeCollaborator({ project_id, user_id }) { try { await this.makeTickTickRequest(`/project/${project_id}/collaborator/${user_id}`, 'DELETE'); return { content: [{ type: 'text', text: `🚫 **Collaborator Removed Successfully!**\n\n` + `📋 **Project**: ${project_id}\n` + `👤 **Removed User**: ${user_id}\n` + `📅 **Removed**: ${new Date().toLocaleString()}\n\n` + `📋 **Access Revoked**:\n` + `• User can no longer access the project\n` + `• All their task assignments remain\n` + `• Previous comments and activities preserved\n` + `• User will be notified of removal\n\n` + `🔄 **Next Steps**:\n` + `• Reassign their pending tasks if needed\n` + `• Review project permissions\n` + `• Consider archiving their contributions\n` + `• Update team documentation` }] }; } catch (error) { throw new Error(`Failed to remove collaborator: ${error.message}`); } } async assignTask({ task_id, assignee_id, due_date, priority, notification = true }) { try { const assignmentData = { assigneeId: assignee_id, dueDate: due_date, priority, sendNotification: notification }; const assignment = await this.makeTickTickRequest(`/task/${task_id}/assign`, 'PUT', assignmentData); return { content: [{ type: 'text', text: `👥 **Task Assigned Successfully!**\n\n` + `📋 **Task**: ${task_id}\n` + `👤 **Assigned To**: ${assignee_id}\n` + `📅 **Due Date**: ${due_date || 'Not set'}\n` + `⚡ **Priority**: ${priority || 'Medium'}\n` + `📅 **Assigned**: ${new Date().toLocaleString()}\n\n` + `📬 **Notification**: ${notification ? '✅ Sent to assignee' : '❌ No notification'}\n\n` + `🎯 **Assignment Details**:\n` + `• Assignee will receive task in their inbox\n` + `• Task appears in their task lists\n` + `• Progress can be tracked by team\n` + `• Automatic reminders based on due date\n\n` + `💡 **Management Tips**:\n` + `• Use comments for detailed instructions\n` + `• Set realistic deadlines\n` + `• Check in regularly on progress\n` + `• Provide necessary resources` }] }; } catch (error) { throw new Error(`Failed to assign task: ${error.message}`); } } async getTaskAssignees({ task_id }) { try { const assignees = await this.makeTickTickRequest(`/task/${task_id}/assignees`); return { content: [{ type: 'text', text: `👥 **Task Assignees** (${assignees.length || 0} assigned)\n\n` + `📋 **Task**: ${task_id}\n` + `📅 **Retrieved**: ${new Date().toLocaleString()}\n\n` + (assignees.length > 0 ? `👤 **Assigned Team Members**:\n` + assignees.map((assignee, index) => `${index + 1}. **${assignee.name || assignee.email}**\n` + ` 🆔 ID: ${assignee.id}\n` + ` 📧 Email: ${assignee.email}\n` + ` 🎭 Role: ${assignee.role || 'Member'}\n` + ` 📅 Assigned: ${assignee.assignedDate || 'N/A'}\n` + ` ⏰ Status: ${assignee.status || 'Active'}` ).join('\n\n') : '👤 **No assignees found**\n\nThis task is not currently assigned to anyone.' ) + `\n\n🔄 **Quick Actions**:\n` + `• Assign to additional team members\n` + `• Update assignment details\n` + `• Send reminders to assignees\n` + `• Check assignment progress` }] }; } catch (error) { throw new Error(`Failed to get task assignees: ${error.message}`); } } async addTaskComment({ task_id, comment, mention_users = [], is_private = false }) { try { const commentData = { content: comment, mentions: mention_users, isPrivate: is_private }; const newComment = await this.makeTickTickRequest(`/task/${task_id}/comment`, 'POST', commentData); const mentionText = mention_users.length > 0 ? `\n👋 **Mentioned**: ${mention_users.join(', ')}` : ''; return { content: [{ type: 'text', text: `💬 **Comment Added Successfully!**\n\n` + `📋 **Task**: ${task_id}\n` + `🆔 **Comment ID**: ${newComment.id}\n` + `📅 **Posted**: ${new Date().toLocaleString()}\n` + `🔒 **Privacy**: ${is_private ? 'Private' : 'Public'}\n${mentionText}\n\n` + `📝 **Comment**:\n"${comment}"\n\n` + `📢 **Team Communication**:\n` + `• Comment visible to ${is_private ? 'project admins only' : 'all team members'}\n` + `• Mentioned users will receive notifications\n` + `• Comment timeline preserved\n` + `• Can be edited or deleted later\n\n` + `💡 **Collaboration Tips**:\n` + `• Use @mentions for specific feedback\n` + `• Add status updates regularly\n` + `• Ask questions when blocked\n` + `• Share relevant resources` }] }; } catch (error) { throw new Error(`Failed to add task comment: ${error.message}`); } } async getTeamActivity({ project_id, limit = 50, activity_types }) { try { const params = new URLSearchParams({ limit: limit.toString() }); if (activity_types && activity_types.length > 0) { params.append('types', activity_types.join(',')); } const activities = await this.makeTickTickRequest(`/project/${project_id}/activity?${params}`); const activityTypeIcons = { task_created: '📝', task_completed: '✅', task_assigned: '👥', comment_added: '💬', project_shared: '🤝' }; return { content: [{ type: 'text', text: `📊 **Team Activity Feed** (${activities.length || 0} activities)\n\n` + `📋 **Project**: ${project_id}\n` + `📅 **Retrieved**: ${new Date().toLocaleString()}\n` + `🔍 **Filter**: ${activity_types ? activity_types.join(', ') : 'All types'}\n\n` + (activities.length > 0 ? `🔄 **Recent Activities**:\n` + activities.slice(0, limit).map((activity, index) => { const icon = activityTypeIcons[activity.type] || '📌'; return `${icon} **${activity.type.replace('_', ' ').toUpperCase()}**\n` + ` 👤 By: ${activity.userName || activity.userId}\n` + ` 📋 Item: ${activity.itemName || activity.itemId}\n` + ` 📅 When: ${activity.timestamp ? new Date(activity.timestamp).toLocaleString() : 'N/A'}\n` + ` ${activity.description ? `💭 Details: ${activity.description}` : ''}`; }).join('\n\n') : '📭 **No recent activity found**\n\nThis project has no recorded team activities yet.' ) + `\n\n📈 **Activity Summary**:\n` + `• Tasks Created: ${activities.filter(a => a.type === 'task_created').length}\n` + `• Tasks Completed: ${activities.filter(a => a.type === 'task_completed').length}\n` + `• Assignments Made: ${activities.filter(a => a.type === 'task_assigned').length}\n` + `• Comments Added: ${activities.filter(a => a.type === 'comment_added').length}\n\n` + `💡 **Team Insights**:\n` + `• Monitor project momentum\n` + `• Identify active contributors\n` + `• Track collaboration patterns\n` + `• Celebrate team achievements` }] }; } catch (error) { throw new Error(`Failed to get team activity: ${error.message}`); } } async setProjectPermissions({ project_id, permissions, apply_to_existing = false }) { try { const permissionData = { permissions, applyToExisting: apply_to_existing }; await this.makeTickTickRequest(`/project/${project_id}/permissions`, 'PUT', permissionData); return { content: [{ type: 'text', text: `🔐 **Project Permissions Updated!**\n\n` + `📋 **Project**: ${project_id}\n` + `📅 **Updated**: ${new Date().toLocaleString()}\n` + `🔄 **Apply to Existing**: ${apply_to_existing ? 'Yes' : 'No'}\n\n` + `⚙️ **Permission Settings**:\n` + `• Can Invite Others: ${permissions.can_invite ? '✅' : '❌'}\n` + `• Can Edit Tasks: ${permissions.can_edit_tasks ? '✅' : '❌'}\n` + `• Can Delete Tasks: ${permissions.can_delete_tasks ? '✅' : '❌'}\n` + `• Can View Reports: ${permissions.can_view_reports ? '✅' : '❌'}\n\n` + `👥 **Impact**:\n` + `• ${apply_to_existing ? 'All existing collaborators updated with new permissions' : 'New permissions apply to future invitations only'}\n` + `• Project security enhanced\n` + `• Clear role boundaries established\n` + `• Team workflow improved\n\n` + `💡 **Permission Best Practices**:\n` + `• Grant minimum necessary access\n` + `• Review permissions regularly\n` + `• Document role expectations\n` + `• Train team on new permissions` }] }; } catch (error) { throw new Error(`Failed to set project permissions: ${error.message}`); } } async getCollaborationStats({ project_id, time_period = 'month' }) { try { const stats = await this.makeTickTickRequest(`/project/${project_id}/collaboration-stats?period=${time_period}`); const totalContributions = (stats.tasksCreated || 0) + (stats.tasksCompleted || 0) + (stats.commentsAdded || 0); return { content: [{ type: 'text', text: `📊 **Team Productivity Metrics** (${time_period.toUpperCase()})\n\n` + `📋 **Project**: ${project_id}\n` + `📅 **Period**: ${stats.periodStart || 'N/A'} - ${stats.periodEnd || 'N/A'}\n` + `📅 **Generated**: ${new Date().toLocaleString()}\n\n` + `👥 **Team Overview**:\n` + `• Active Members: ${stats.activeMembers || 0}\n` + `• Total Contributions: ${totalContributions}\n` + `• Projects Shared: ${stats.projectsShared || 0}\n` + `• Average Response Time: ${stats.avgResponseTime || 'N/A'}\n\n` + `📈 **Activity Breakdown**:\n` + `• Tasks Created: ${stats.tasksCreated || 0}\n` + `• Tasks Completed: ${stats.tasksCompleted || 0}\n` + `• Tasks Assigned: ${stats.tasksAssigned || 0}\n` + `• Comments Added: ${stats.commentsAdded || 0}\n` + `• Files Shared: ${stats.filesShared || 0}\n\n` + `🏆 **Top Contributors**:\n` + (stats.topContributors || []).slice(0, 5).map((contributor, index) => `${index + 1}. **${contributor.name}** - ${contributor.contributions} contributions` ).join('\n') + `\n\n📊 **Performance Insights**:\n` + `• Collaboration Score: ${stats.collaborationScore || 0}/100\n` + `• Team Velocity: ${stats.teamVelocity || 'N/A'} tasks/week\n` + `• Communication Level: ${stats.communicationLevel || 'Moderate'}\n` + `• Project Health: ${stats.projectHealth || 'Good'}\n\n` + `💡 **Recommendations**:\n` + (stats.recommendations || []).map(rec => `• ${rec}`).join('\n') + `\n\n🎯 **Next Steps**:\n` + `• Review individual contributions\n` + `• Recognize top performers\n` + `• Address collaboration gaps\n` + `• Plan team improvement initiatives` }] }; } catch (error) { throw new Error(`Failed to get collaboration stats: ${error.message}`); } } async bulkAssignTasks({ assignments, notify_assignees = true }) { try { const assignmentData = { assignments: assignments.map(assignment => ({ taskId: assignment.task_id, assigneeId: assignment.assignee_id, dueDate: assignment.due_date })), notifyAssignees: notify_assignees }; const result = await this.makeTickTickRequest('/task/bulk-assign', 'POST', assignmentData); const successCount = result.successful || assignments.length; const failureCount = (result.failed || []).length; return { content: [{ type: 'text', text: `👥 **Bulk Task Assignment Complete!**\n\n` + `📊 **Assignment Results**:\n` + `• Total Assignments: ${assignments.length}\n` + `• Successful: ${successCount} ✅\n` + `• Failed: ${failureCount} ❌\n` + `• Success Rate: ${Math.round((successCount / assignments.length) * 100)}%\n` + `📅 **Completed**: ${new Date().toLocaleString()}\n\n` + `📬 **Notifications**: ${notify_assignees ? '✅ Sent to all assignees' : '❌ No notifications sent'}\n\n` + (successCount > 0 ? `✅ **Successful Assignments**:\n` + assignments.slice(0, successCount).map((assignment, index) => `${index + 1}. Task ${assignment.task_id} → ${assignment.assignee_id}` + (assignment.due_date ? ` (Due: ${assignment.due_date})` : '') ).join('\n') + '\n\n' : '' ) + (failureCount > 0 ? `❌ **Failed Assignments**:\n` + (result.failed || []).map((failure, index) => `${index + 1}. Task ${failure.taskId}: ${failure.reason}` ).join('\n') + '\n\n' : '' ) + `🎯 **Next Steps**:\n` + `• Review successful assignments\n` + `• Retry failed assignments if needed\n` + `• Set up assignment tracking\n` + `• Monitor team workload distribution\n\n` + `💡 **Bulk Assignment Tips**:\n` + `• Verify user permissions before assigning\n` + `• Balance workload across team members\n` + `• Set realistic due dates\n` + `• Follow up on critical assignments` }] }; } catch (error) { throw new Error(`Failed to bulk assign tasks: ${error.message}`); } } async exportTeamReport({ project_id, report_type = 'productivity', date_range, format = 'pdf' }) { try { const exportData = { reportType: report_type, dateRange: date_range, format }; const exportResult = await this.makeTickTickRequest(`/project/${project_id}/export/team-report`, 'POST', exportData); const reportTypes = { productivity: 'Team Productivity Analysis', task_completion: 'Task Completion Metrics', time_tracking: 'Time Tracking Summary', team_activity: 'Team Activity Overview' }; return { content: [{ type: 'text', text: `📊 **Team Report Generated Successfully!**\n\n` + `📋 **Project**: ${project_id}\n` + `📈 **Report Type**: ${reportTypes[report_type] || report_type}\n` + `📁 **Format**: ${format.toUpperCase()}\n` + `📅 **Generated**: ${new Date().toLocaleString()}\n` + `📊 **File Size**: ${exportResult.fileSize ? `${Math.round(exportResult.fileSize / 1024)}KB` : 'Unknown'}\n\n` + `📅 **Date Range**:\n` + `• Start: ${date_range?.start_date || 'Not specified'}\n` + `• End: ${date_range?.end_date || 'Not specified'}\n\n` + `📦 **Report Contents**:\n` + (report_type === 'productivity' ? `• Team performance metrics\n• Individual productivity scores\n• Task completion rates\n• Collaboration effectiveness` : report_type === 'task_completion' ? `• Task completion statistics\n• Deadline adherence\n• Priority distribution\n• Completion trends` : report_type === 'time_tracking' ? `• Time spent per task\n• Focus session analytics\n• Productivity patterns\n• Time allocation breakdown` : `• Team activity timeline\n• Communication patterns\n• Project milestones\n• Member contributions` ) + `\n\n🔗 **Download**: ${exportResult.downloadUrl || 'Check email for download link'}\n` + `⏰ **Expiry**: Download link expires in 24 hours\n\n` + `📊 **Report Insights**:\n` + `• Use for performance reviews\n` + `• Share with stakeholders\n` + `• Track team progress over time\n` + `• Identify improvement opportunities\n\n` + `💡 **Analysis Tips**:\n` + `• Compare reports across periods\n` + `• Focus on trends, not single metrics\n` + `• Discuss findings with team\n` + `• Set improvement goals based on data` }] }; } catch (error) { throw new Error(`Failed to export team report: ${error.message}`); } } // Calendar Integration Methods (Phase 3: 8 operations) async getCalendarEvents({ start_date, end_date, calendar_id }) { try { const params = new URLSearchParams(); if (start_date) params.append('startDate', start_date); if (end_date) params.append('endDate', end_date); if (calendar_id) params.append('calendarId', calendar_id); const events = await this.makeTickTickRequest(`/calendar/events?${params}`); return { content: [{ type: 'text', text: `📅 **Calendar Events** (${events.length || 0} found)\n\n` + `📊 **Query Parameters**:\n` + `• Start Date: ${start_date || 'Not specified'}\n` + `• End Date: ${end_date || 'Not specified'}\n` + `• Calendar Filter: ${calendar_id || 'All calendars'}\n` + `📅 **Retrieved**: ${new Date().toLocaleString()}\n\n` + (events.length > 0 ? `📋 **Upcoming Events**:\n` + events.map((event, index) => `${index + 1}. **${event.title}**\n` + ` 📅 Date: ${event.startDate ? new Date(event.startDate).toLocaleDateString() : 'TBD'}\n` + ` ⏰ Time: ${event.startTime || 'All day'} - ${event.endTime || 'TBD'}\n` + ` 📍 Location: ${event.location || 'No location'}\n` + ` 📝 Description: ${event.description ? event.description.substring(0, 100) + '...' : 'No description'}\n` + ` 🔔 Reminder: ${event.reminderMinutes ? `${event.reminderMinutes} min before` : 'None'}` ).join('\n\n') : '📭 **No events found** for the specified criteria.' ) + `\n\n📈 **Event Summary**:\n` + `• Total Events: ${events.length}\n` + `• Today's Events: ${events.filter(e => e.startDate === new Date().toISOString().split('T')[0]).length}\n` + `• All-day Events: ${events.filter(e => e.allDay).length}\n` + `• With Reminders: ${events.filter(e => e.reminderMinutes).length}\n\n` + `🔄 **Quick Actions**:\n` + `• Create new calendar event\n` + `• Sync with external calendars\n` + `• Convert tasks to events\n` + `• Check for schedule conflicts` }] }; } catch (error) { throw new Error(`Failed to get calendar events: ${error.message}`); } } async createCalendarEvent({ title, start_time, end_time, description, location, reminder_minutes = 15 }) { try { const eventData = { title, startTime: start_time, endTime: end_time, description, location, reminderMinutes: reminder_minutes }; const event = await this.makeTickTickRequest('/calendar/event', 'POST', eventData); const duration = new Date(end_time) - new Date(start_time); const durationHours = Math.round(duration / (1000 * 60 * 60) * 10) / 10; return { content: [{ type: 'text', text: `📅 **Calendar Event Created Successfully!**\n\n` + `🎯 **Event Details**:\n` + `• **Title**: ${event.title}\n` + `• **Event ID**: ${event.id}\n` + `• **Start**: ${new Date(event.startTime).toLocaleString()}\n` + `• **End**: ${new Date(event.endTime).toLocaleString()}\n` + `• **Duration**: ${durationHours} hour(s)\n` + `• **Location**: ${event.location || 'No location specified'}\n\n` + `📝 **Description**:\n${event.description || 'No description provided'}\n\n` + `🔔 **Reminder**: ${event.reminderMinutes} minutes before event\n` + `📅 **Created**: ${new Date().toLocaleString()}\n\n` + `✅ **Event Setup Complete**:\n` + `• Added to your calendar\n` + `• Reminder notifications configured\n` + `• Available across all your devices\n` + `• Synced with connected calendars\n\n` + `🎯 **Next Steps**:\n` + `• Add attendees if needed\n` + `• Set up recurring pattern if applicable\n` + `• Link related tasks or projects\n` + `• Share calendar invite with participants` }] }; } catch (error) { throw new Error(`Failed to create calendar event: ${error.message}`); } } async syncWithGoogleCalendar({ google_calendar_id, sync_direction = 'bidirectional', date_range_days = 30 }) { try { const syncData = { googleCalendarId: google_calendar_id, syncDirection: sync_direction, dateRangeDays: date_range_days }; const syncResult = await this.makeTickTickRequest('/calendar/sync/google', 'POST', syncData); return { content: [{ type: 'text', text: `🔄 **Google Calendar Sync Completed!**\n\n` + `📊 **Sync Configuration**:\n` + `• Google Calendar: ${google_calendar_id}\n` + `• Direction: ${sync_direction.toUpperCase()}\n` + `• Date Range: ${date_range_days} days (past & future)\n` + `• Started: ${new Date().toLocaleString()}\n\n` + `📈 **Sync Results**:\n` + `• Events Imported: ${syncResult.imported || 0}\n` + `• Events Exported: ${syncResult.exported || 0}\n` + `• Conflicts Resolved: ${syncResult.conflictsResolved || 0}\n` + `• Duplicates Merged: ${syncResult.duplicatesMerged || 0}\n` + `• Errors Encountered: ${syncResult.errors || 0}\n\n` + `⚙️ **Sync Status**: ${syncResult.status || 'Completed'}\n` + `⏱️ **Duration**: ${syncResult.durationMs ? `${Math.round(syncResult.durationMs / 1000)}s` : 'Unknown'}\n\n` + (syncResult.errors > 0 ? `⚠️ **Issues Found**:\n` + (syncResult.errorDetails || []).map(error => `• ${error}`).join('\n') + '\n\n' : '' ) + `✅ **Benefits**:\n` + `• Unified calendar view across platforms\n` + `• Real-time synchronization\n` + `• Automatic conflict detection\n` + `• Cross-platform accessibility\n\n` + `🔄 **Auto-Sync**: ${syncResult.autoSyncEnabled ? 'Enabled' : 'Manual only'}\n` + `📅 **Next Sync**: ${syncResult.nextSyncTime || 'On-demand'}` }] }; } catch (error) { throw new Error(`Failed to sync with Google Calendar: ${error.message}`); } } async syncWithOutlook({ outlook_calendar_id, sync_direction = 'bidirectional', include_meetings = true }) { try { const syncData = { outlookCalendarId: outlook_calendar_id, syncDirection: sync_direction, includeMeetings: include_meetings }; const syncResult = await this.makeTickTickRequest('/calendar/sync/outlook', 'POST', syncData); return { content: [{ type: 'text', text: `🔄 **Outlook Calendar Sync Completed!**\n\n` + `📊 **Sync Configuration**:\n` + `• Outlook Calendar: ${outlook_calendar_id}\n` + `• Direction: ${sync_direction.toUpperCase()}\n` + `• Include Meetings: ${include_meetings ? 'Yes' : 'No'}\n` + `• Started: ${new Date().toLocaleString()}\n\n` + `📈 **Sync Results**:\n` + `• Events Synced: ${syncResult.eventsSynced || 0}\n` + `• Meetings Imported: ${syncResult.meetingsImported || 0}\n` + `• Appointments Created: ${syncResult.appointmentsCreated || 0}\n` + `• Conflicts Detected: ${syncResult.conflicts || 0}\n` + `• Sync Errors: ${syncResult.errors || 0}\n\n` + `📧 **Meeting Integration**:\n` + `• Teams Meetings: ${syncResult.teamsMeetings || 0}\n` + `• Zoom Meetings: ${syncResult.zoomMeetings || 0}\n` + `• Other Meeting Links: ${syncResult.otherMeetings || 0}\n\n` + `⚙️ **Sync Status**: ${syncResult.status || 'Completed'}\n` + `🔐 **Authentication**: ${syncResult.authStatus || 'Valid'}\n\n` + (syncResult.warnings && syncResult.warnings.length > 0 ? `⚠️ **Warnings**:\n` + syncResult.warnings.map(warning => `• ${warning}`).join('\n') + '\n\n' : '' ) + `✅ **Enterprise Features**:\n` + `• Exchange Server integration\n` + `• Meeting room bookings\n` + `• Attendee management\n` + `• Corporate calendar policies\n\n` + `🔄 **Sync Schedule**: ${syncResult.syncInterval || 'Every 15 minutes'}\n` + `📊 **Data Usage**: ${syncResult.dataTransferred || 'Minimal'}` }] }; } catch (error) { throw new Error(`Failed to sync with Outlook: ${error.message}`); } } async getCalendarView({ start_date, end_date, view_type = 'week', include_tasks = true }) { try { const params = new URLSearchParams({ startDate: start_date || new Date().toISOString().split('T')[0], endDate: end_date || new Date(Date.now() + 7 * 24 * 60 * 60 * 1000).toISOString().split('T')[0], viewType: view_type, includeTasks: include_tasks.toString() }); const calendarView = await this.makeTickTickRequest(`/calendar/view?${params}`); const viewTypeLabels = { day: 'Daily View', week: 'Weekly View', month: 'Monthly View', agenda: 'Agenda View' }; return { content: [{ type: 'text', text: `📅 **${viewTypeLabels[view_type]}** Calendar\n\n` + `📊 **View Settings**:\n` + `• Period: ${start_date || 'Today'} - ${end_date || '7 days ahead'}\n` + `• Include Tasks: ${include_tasks ? 'Yes' : 'No'}\n` + `• View Type: ${view_type.toUpperCase()}\n` + `• Generated: ${new Date().toLocaleString()}\n\n` + `📈 **Schedule Overview**:\n` + `• Total Events: ${calendarView.events?.length || 0}\n` + `• Tasks Scheduled: ${calendarView.tasks?.length || 0}\n` + `• Free Time Blocks: ${calendarView.freeTimeBlocks || 0}\n` + `• Conflicts: ${calendarView.conflicts || 0}\n\n` + (calendarView.dailyBreakdown ? `📋 **Daily Breakdown**:\n` + Object.entries(calendarView.dailyBreakdown).map(([date, data]) => `📅 **${new Date(date).toLocaleDateString()}**:\n` + ` • Events: ${data.events || 0}\n` + ` • Tasks: ${data.tasks || 0}\n` + ` • Busy Hours: ${data.busyHours || 0}\n` + ` • Free Hours: ${data.freeHours || 0}` ).join('\n\n') + '\n\n' : '' ) + (calendarView.conflicts && calendarView.conflicts.length > 0 ? `⚠️ **Schedule Conflicts**:\n` + calendarView.conflicts.slice(0, 5).map((conflict, index) => `${index + 1}. ${conflict.time}: ${conflict.description}` ).join('\n') + '\n\n' : '' ) + `🎯 **Productivity Insights**:\n` + `• Peak Hours: ${calendarView.peakHours || 'Not analyzed'}\n` + `• Utilization Rate: ${calendarView.utilizationRate || 0}%\n` + `• Focus Time Available: ${calendarView.focusTimeHours || 0} hours\n` + `• Meeting Load: ${calendarView.meetingLoad || 'Light'}\n\n` + `💡 **Optimization Tips**:\n` + `• Schedule focused work during free blocks\n` + `• Batch similar tasks together\n` + `• Leave buffer time between meetings\n` + `• Block time for deep work sessions` }] }; } catch (error) { throw new Error(`Failed to get calendar view: ${error.message}`); } } async convertTaskToEvent({ task_id, event_duration_minutes = 60, start_time, create_reminder = true }) { try { const conversionData = { eventDurationMinutes: event_duration_minutes, startTime: start_time, createReminder: create_reminder }; const result = await this.makeTickTickRequest(`/task/${task_id}/convert-to-event`, 'POST', conversionData); const durationHours = Math.round(event_duration_minutes / 60 * 10) / 10; return { content: [{ type: 'text', text: `🔄 **Task Converted to Calendar Event!**\n\n` + `📋 **Original Task**: ${task_id}\n` + `📅 **New Event**: ${result.eventId}\n` + `📅 **Conversion Date**: ${new Date().toLocaleString()}\n\n` + `📊 **Event Details**:\n` + `• **Title**: ${result.eventTitle || 'Converted from task'}\n` + `• **Duration**: ${durationHours} hour(s) (${event_duration_minutes} minutes)\n` + `• **Scheduled**: ${start_time ? new Date(start_time).toLocaleString() : 'Time to be determined'}\n` + `• **Reminder**: ${create_reminder ? '✅ Enabled' : '❌ Disabled'}\n\n` + `🔗 **Task-Event Connection**:\n` + `• Original task ${result.taskKept ? 'preserved' : 'archived'}\n` + `• Event linked to task data\n` + `• Progress tracking maintained\n` + `• Comments and attachments carried over\n\n` + `✅ **Benefits of Conversion**:\n` + `• Time-blocked in calendar\n` + `• Better schedule visibility\n` + `• Automatic reminders\n` + `• Integration with other calendar tools\n` + `• Protected focus time\n\n` + `🎯 **Next Steps**:\n` + `• Adjust event timing if needed\n` + `• Add location or meeting details\n` + `• Invite collaborators if applicable\n` + `• Set up recurring pattern if this is a regular task\n\n` + `💡 **Pro Tips**:\n` + `• Convert routine tasks for better time management\n` + `• Use time-blocking for deep work\n` + `• Schedule tasks during your peak energy hours\n` + `• Leave buffer time between events` }] }; } catch (error) { throw new Error(`Failed to convert task to event: ${error.message}`); } } async getScheduleConflicts({ start_date, end_date, include_tasks = true, conflict_threshold_minutes = 15 }) { try { const params = new URLSearchParams({ startDate: start_date || new Date().toISOString().split('T')[0], endDate: end_date || new Date(Date.now() + 7 * 24 * 60 * 60 * 1000).toISOString().split('T')[0], includeTasks: include_tasks.toString(), thresholdMinutes: conflict_threshold_minutes.toString() }); const conflicts = await this.makeTickTickRequest(`/calendar/conflicts?${params}`); const severityColors = { high: '🔴', medium: '🟡', low: '🟢' }; return { content: [{ type: 'text', text: `⚠️ **Schedule Conflict Analysis** (${conflicts.length || 0} conflicts found)\n\n` + `📊 **Analysis Parameters**:\n` + `• Date Range: ${start_date || 'Today'} - ${end_date || '7 days ahead'}\n` + `• Include Tasks: ${include_tasks ? 'Yes' : 'No'}\n` + `• Conflict Threshold: ${conflict_threshold_minutes} minutes\n` + `• Analyzed: ${new Date().toLocaleString()}\n\n` + (conflicts.length > 0 ? `🚨 **Detected Conflicts**:\n` + conflicts.map((conflict, index) => `${index + 1}. ${severityColors[conflict.severity] || '⚠️'} **${conflict.type?.toUpperCase()} CONFLICT**\n` + ` 📅 Date: ${new Date(conflict.date).toLocaleDateString()}\n` + ` ⏰ Time: ${conflict.startTime} - ${conflict.endTime}\n` + ` 📋 Items: ${conflict.item1} ↔ ${conflict.item2}\n` + ` ⏱️ Overlap: ${conflict.overlapMinutes} minutes\n` + ` 💡 Suggestion: ${conflict.suggestion || 'Reschedule one item'}` ).join('\n\n') : '✅ **No conflicts found!** Your schedule is well-organized for the specified period.' ) + `\n\n📊 **Conflict Summary**:\n` + `• High Priority: ${conflicts.filter(c => c.severity === 'high').length} 🔴\n` + `• Medium Priority: ${conflicts.filter(c => c.severity === 'medium').length} 🟡\n` + `• Low Priority: ${conflicts.filter(c => c.severity === 'low').length} 🟢\n` + `• Event-Event: ${conflicts.filter(c => c.type === 'event').length}\n` + `• Task-Event: ${conflicts.filter(c => c.type === 'task').length}\n\n` + (conflicts.length > 0 ? `🔧 **Resolution Strategies**:\n` + `• Reschedule lower priority items\n` + `• Shorten event durations\n` + `• Move tasks to different time slots\n` + `• Delegate conflicting responsibilities\n` + `• Use buffer time between events\n\n` : '' ) + `📈 **Schedule Health Score**: ${conflicts.length === 0 ? '100' : Math.max(0, 100 - conflicts.length * 10)}/100\n\n` + `💡 **Prevention Tips**:\n` + `• Use calendar blocking for important tasks\n` + `• Set realistic time estimates\n` + `• Include travel time between meetings\n` + `• Regular schedule reviews\n` + `• Automated conflict detection` }] }; } catch (error) { throw new Error(`Failed to get schedule conflicts: ${error.message}`); } } async bulkCalendarImport({ calendar_data, import_source, merge_duplicates = true, create_new_calendar = false }) { try { const importData = { calendarData: calendar_data, importSource: import_source, mergeDuplicates: merge_duplicates, createNewCalendar: create_new_calendar }; const importResult = await this.makeTickTickRequest('/calendar/bulk-import', 'POST', importData); return { content: [{ type: 'text', text: `📥 **Bulk Calendar Import Completed!**\n\n` + `📊 **Import Configuration**:\n` + `• Source: ${import_source || 'Unknown'}\n` + `• Data Format: iCal/ICS\n` + `• Merge Duplicates: ${merge_duplicates ? 'Yes' : 'No'}\n` + `• New Calendar: ${create_new_calendar ? 'Created' : 'Use existing'}\n` + `• Processed: ${new Date().toLocaleString()}\n\n` + `📈 **Import Results**:\n` + `• Events Processed: ${importResult.totalProcessed || 0}\n` + `• Successfully Imported: ${importResult.imported || 0}\n` + `• Duplicates Merged: ${importResult.duplicatesMerged || 0}\n` + `• Skipped (Invalid): ${importResult.skipped || 0}\n` + `• Errors Encountered: ${importResult.errors || 0}\n\n` + `📅 **Date Range**:\n` + `• Earliest Event: ${importResult.earliestDate || 'N/A'}\n` + `• Latest Event: ${importResult.latestDate || 'N/A'}\n` + `• Span: ${importResult.dateSpan || 'Unknown'}\n\n` + (importResult.newCalendarId ? `📁 **New Calendar Created**:\n` + `• Calendar ID: ${importResult.newCalendarId}\n` + `• Name: ${importResult.newCalendarName || 'Imported Events'}\n` + `• Color: ${importResult.newCalendarColor || 'Default'}\n\n` : '' ) + (importResult.errors > 0 ? `⚠️ **Import Issues**:\n` + (importResult.errorDetails || []).slice(0, 5).map((error, index) => `${index + 1}. ${error.message} (Line: ${error.line || 'Unknown'})` ).join('\n') + (importResult.errorDetails?.length > 5 ? `\n... and ${importResult.errorDetails.length - 5} more errors` : '') + '\n\n' : '' ) + `✅ **Import Success Rate**: ${importResult.totalProcessed > 0 ? Math.round((importResult.imported / importResult.totalProcessed) * 100) : 0}%\n\n` + `🔄 **Post-Import Actions**:\n` + `• Review imported events for accuracy\n` + `• Set up notifications for important events\n` + `• Organize events into appropriate calendars\n` + `• Verify time zones are correct\n` + `• Clean up any duplicate entries\n\n` + `💡 **Data Quality Tips**:\n` + `• Use standard iCal format for best results\n` + `• Include timezone information\n` + `• Validate data before import\n` + `• Back up existing calendar before large imports\n` + `• Test with small datasets first` }] }; } catch (error) { throw new Error(`Failed to bulk import calendar: ${error.message}`); } } // Notes & Attachments Methods (Phase 3: 8 operations) async getTaskNotes({ task_id, include_replies = true, sort_order = 'newest' }) { try { const params = new URLSearchParams({ includeReplies: include_replies.toString(), sortOrder: sort_order }); const notes = await this.makeTickTickRequest(`/task/${task_id}/notes?${params}`); return { content: [{ type: 'text', text: `📝 **Task Notes** (${notes.length || 0} notes found)\n\n` + `📋 **Task**: ${task_id}\n` + `🔄 **Sort Order**: ${sort_order.toUpperCase()}\n` + `💬 **Include Replies**: ${include_replies ? 'Yes' : 'No'}\n` + `📅 **Retrieved**: ${new Date().toLocaleString()}\n\n` + (notes.length > 0 ? `📄 **Notes & Comments**:\n` + notes.map((note, index) => `${index + 1}. **${note.type?.toUpperCase() || 'COMMENT'}** ${note.isPrivate ? '🔒' : '🌍'}\n` + ` 👤 Author: ${note.authorName || note.authorId}\n` + ` 📅 Created: ${note.createdTime ? new Date(note.createdTime).toLocaleString() : 'N/A'}\n` + ` 📝 Content: ${note.content ? note.content.substring(0, 200) + (note.content.length > 200 ? '...' : '') : 'No content'}\n` + ` 🆔 Note ID: ${note.id}\n` + (note.replies && note.replies.length > 0 ? ` 💬 Replies (${note.replies.length}): ${note.replies.map(r => r.authorName || r.authorId).join(', ')}\n` : '' ) + (note.lastModified ? ` ✏️ Last Modified: ${new Date(note.lastModified).toLocaleString()}\n` : '') ).join('\n') : '📭 **No notes found** for this task.' ) + `\n\n📊 **Notes Summary**:\n` + `• Total Notes: ${notes.length}\n` + `• Comments: ${notes.filter(n => n.type === 'comment').length}\n` + `• Progress Updates: ${notes.filter(n => n.type === 'progress').length}\n` + `• Reminders: ${notes.filter(n => n.type === 'reminder').length}\n` + `• Private Notes: ${notes.filter(n => n.isPrivate).length}\n` + `• With Replies: ${notes.filter(n => n.replies && n.replies.length > 0).length}\n\n` + `🔄 **Quick Actions**:\n` + `• Add new note or comment\n` + `• Edit existing notes\n` + `• Reply to specific comments\n` + `• Mark notes as private/public` }] }; } catch (error) { throw new Error(`Failed to get task notes: ${error.message}`); } } async addTaskNote({ task_id, note_content, note_type = 'comment', is_private = false }) { try { const noteData = { content: note_content, type: note_type, isPrivate: is_private }; const newNote = await this.makeTickTickRequest(`/task/${task_id}/note`, 'POST', noteData); const noteTypeLabels = { comment: 'Comment', progress: 'Progress Update', reminder: 'Reminder' }; return { content: [{ type: 'text', text: `📝 **Note Added Successfully!**\n\n` + `📋 **Task**: ${task_id}\n` + `🆔 **Note ID**: ${newNote.id}\n` + `📂 **Type**: ${noteTypeLabels[note_type] || note_type}\n` + `🔒 **Privacy**: ${is_private ? 'Private' : 'Public'}\n` + `📅 **Created**: ${new Date().toLocaleString()}\n\n` + `📝 **Note Content**:\n"${note_content}"\n\n` + `✅ **Note Features**:\n` + `• ${is_private ? 'Visible to you only' : 'Visible to all team members'}\n` + `• Can be edited or deleted later\n` + `• Supports @mentions and rich formatting\n` + `• Automatically timestamped\n` + `• Preserved in task history\n\n` + `💡 **Best Practices**:\n` + (note_type === 'progress' ? `• Update regularly to keep team informed\n• Include specific accomplishments\n• Mention any blockers or challenges\n• Set next steps clearly` : note_type === 'reminder' ? `• Set clear action items\n• Include deadlines if applicable\n• Use @mentions for specific people\n• Follow up on reminder items` : `• Be clear and constructive\n• Ask specific questions\n• Provide context when needed\n• Use friendly, professional tone` ) + `\n\n🎯 **Follow-up Actions**:\n` + `• Notify relevant team members\n` + `• Set reminders if needed\n` + `• Link to related resources\n` + `• Monitor for responses` }] }; } catch (error) { throw new Error(`Failed to add task note: ${error.message}`); } } async updateTaskNote({ task_id, note_id, new_content }) { try { const updateData = { content: new_content }; const updatedNote = await this.makeTickTickRequest(`/task/${task_id}/note/${note_id}`, 'PUT', updateData); return { content: [{ type: 'text', text: `✏️ **Note Updated Successfully!**\n\n` + `📋 **Task**: ${task_id}\n` + `🆔 **Note ID**: ${note_id}\n` + `📅 **Updated**: ${new Date().toLocaleString()}\n` + `👤 **Last Editor**: ${updatedNote.lastEditor || 'You'}\n\n` + `📝 **Updated Content**:\n"${new_content}"\n\n` + `📊 **Edit History**:\n` + `• Original Creation: ${updatedNote.createdTime ? new Date(updatedNote.createdTime).toLocaleString() : 'N/A'}\n` + `• Previous Update: ${updatedNote.previousUpdate ? new Date(updatedNote.previousUpdate).toLocaleString() : 'First edit'}\n` + `• Current Update: ${new Date().toLocaleString()}\n` + `• Total Edits: ${updatedNote.editCount || 1}\n\n` + `✅ **Update Features**:\n` + `• Edit history preserved\n` + `• Team members notified of changes\n` + `• Timestamps automatically updated\n` + `• Previous versions remain accessible\n\n` + `💡 **Editing Tips**:\n` + `• Clearly mark significant changes\n` + `• Preserve important context\n` + `• Use edit notes for transparency\n` + `• Consider notifying mentioned users\n\n` + `🔄 **Next Steps**:\n` + `• Review updated content\n` + `• Notify stakeholders if needed\n` + `• Update related documentation\n` + `• Monitor for team feedback` }] }; } catch (error) { throw new Error(`Failed to update task note: ${error.message}`); } } async deleteTaskNote({ task_id, note_id }) { try { const noteInfo = await this.makeTickTickRequest(`/task/${task_id}/note/${note_id}`, 'GET'); await this.makeTickTickRequest(`/task/${task_id}/note/${note_id}`, 'DELETE'); return { content: [{ type: 'text', text: `🗑️ **Note Deleted Successfully!**\n\n` + `📋 **Task**: ${task_id}\n` + `🆔 **Deleted Note ID**: ${note_id}\n` + `📅 **Deleted**: ${new Date().toLocaleString()}\n\n` + `📝 **Deleted Note Details**:\n` + `• **Type**: ${noteInfo.type || 'Comment'}\n` + `• **Author**: ${noteInfo.authorName || noteInfo.authorId || 'Unknown'}\n` + `• **Created**: ${noteInfo.createdTime ? new Date(noteInfo.createdTime).toLocaleString() : 'N/A'}\n` + `• **Content Preview**: ${noteInfo.content ? noteInfo.content.substring(0, 100) + '...' : 'No content'}\n` + `• **Privacy**: ${noteInfo.isPrivate ? 'Private' : 'Public'}\n\n` + `⚠️ **Deletion Impact**:\n` + `• Note permanently removed from task\n` + `• Edit history no longer accessible\n` + `• Replies and mentions removed\n` + `• Cannot be recovered once deleted\n\n` + `📊 **Task Notes Status**:\n` + `• Remaining notes will be preserved\n` + `• Task activity timeline updated\n` + `• Team members notified of deletion\n` + `• Related references may be broken\n\n` + `💡 **Deletion Best Practices**:\n` + `• Consider archiving instead of deleting\n` + `• Notify team before removing important notes\n` + `• Save critical information elsewhere\n` + `• Document reason for deletion if needed\n\n` + `✅ **Cleanup Complete**: Note has been permanently removed from the task.` }] }; } catch (error) { throw new Error(`Failed to delete task note: ${error.message}`); } } async uploadTaskAttachment({ task_id, file_data, file_name, file_type, description }) { try { const attachmentData = { fileData: file_data, fileName: file_name, fileType: file_type, description: description }; const attachment = await this.makeTickTickRequest(`/task/${task_id}/attachment`, 'POST', attachmentData); const fileSizeKB = Math.round(file_data.length * 0.75 / 1024); // Rough estimate from base64 const fileTypeCategory = file_type?.startsWith('image/') ? '🖼️ Image' : file_type?.startsWith('video/') ? '🎥 Video' : file_type?.startsWith('audio/') ? '🎵 Audio' : file_type?.includes('pdf') ? '📄 PDF' : file_type?.includes('document') ? '📝 Document' : '📎 File'; return { content: [{ type: 'text', text: `📎 **File Attached Successfully!**\n\n` + `📋 **Task**: ${task_id}\n` + `🆔 **Attachment ID**: ${attachment.id}\n` + `📁 **File Name**: ${file_name}\n` + `📂 **Type**: ${fileTypeCategory}\n` + `📊 **Size**: ~${fileSizeKB}KB\n` + `📅 **Uploaded**: ${new Date().toLocaleString()}\n\n` + `📝 **File Details**:\n` + `• **MIME Type**: ${file_type || 'Unknown'}\n` + `• **Description**: ${description || 'No description provided'}\n` + `• **Storage Location**: TickTick Cloud Storage\n` + `• **Access**: Available to all task collaborators\n\n` + `🔗 **File Access**:\n` + `• **Download URL**: ${attachment.downloadUrl || 'Will be available shortly'}\n` + `• **Preview**: ${attachment.previewUrl ? 'Available' : 'Not available for this file type'}\n` + `• **Sharing**: Can be shared with task link\n` + `• **Expiry**: ${attachment.expiryDate || 'No expiration'}\n\n` + `✅ **Upload Features**:\n` + `• Automatic virus scanning completed\n` + `• File integrity verified\n` + `• Backup copies created\n` + `• Team access permissions applied\n` + `• Search indexing enabled\n\n` + `📱 **Supported Actions**:\n` + `• Download original file\n` + `• Generate shareable links\n` + `• Add version comments\n` + `• Update file description\n` + `• Remove when no longer needed\n\n` + `💡 **File Management Tips**:\n` + `• Use descriptive file names\n` + `• Add context in descriptions\n` + `• Organize by project or date\n` + `• Clean up old files regularly\n` + `• Consider file size limits for team plans` }] }; } catch (error) { throw new Error(`Failed to upload task attachment: ${error.message}`); } } async getTaskAttachments({ task_id, file_type_filter = 'all' }) { try { const params = new URLSearchParams({ filter: file_type_filter }); const attachments = await this.makeTickTickRequest(`/task/${task_id}/attachments?${params}`); const typeIcons = { images: '🖼️', documents: '📝', audio: '🎵', video: '🎥', all: '📎' }; const totalSize = attachments.reduce((sum, att) => sum + (att.fileSize || 0), 0); const totalSizeMB = Math.round(totalSize / (1024 * 1024) * 10) / 10; return { content: [{ type: 'text', text: `📎 **Task Attachments** (${attachments.length || 0} files found)\n\n` + `📋 **Task**: ${task_id}\n` + `🔍 **Filter**: ${typeIcons[file_type_filter]} ${file_type_filter.toUpperCase()}\n` + `📊 **Total Size**: ${totalSizeMB}MB\n` + `📅 **Retrieved**: ${new Date().toLocaleString()}\n\n` + (attachments.length > 0 ? `📁 **Attached Files**:\n` + attachments.map((attachment, index) => { const fileSizeMB = Math.round((attachment.fileSize || 0) / (1024 * 1024) * 100) / 100; const fileIcon = attachment.fileType?.startsWith('image/') ? '🖼️' : attachment.fileType?.startsWith('video/') ? '🎥' : attachment.fileType?.startsWith('audio/') ? '🎵' : attachment.fileType?.includes('pdf') ? '📄' : attachment.fileType?.includes('document') ? '📝' : '📎'; return `${index + 1}. ${fileIcon} **${attachment.fileName}**\n` + ` 🆔 ID: ${attachment.id}\n` + ` 📊 Size: ${fileSizeMB}MB\n` + ` 📂 Type: ${attachment.fileType || 'Unknown'}\n` + ` 📅 Uploaded: ${attachment.uploadDate ? new Date(attachment.uploadDate).toLocaleString() : 'N/A'}\n` + ` 👤 Uploader: ${attachment.uploaderName || attachment.uploaderId || 'Unknown'}\n` + ` 📝 Description: ${attachment.description || 'No description'}\n` + ` 🔗 Status: ${attachment.status || 'Available'}\n` + ` 💾 Downloads: ${attachment.downloadCount || 0}`; }).join('\n\n') : `📭 **No ${file_type_filter === 'all' ? '' : file_type_filter + ' '}attachments found** for this task.` ) + `\n\n📊 **Attachment Summary**:\n` + `• Images: ${attachments.filter(a => a.fileType?.startsWith('image/')).length} 🖼️\n` + `• Documents: ${attachments.filter(a => a.fileType?.includes('document') || a.fileType?.includes('pdf')).length} 📝\n` + `• Videos: ${attachments.filter(a => a.fileType?.startsWith('video/')).length} 🎥\n` + `• Audio: ${attachments.filter(a => a.fileType?.startsWith('audio/')).length} 🎵\n` + `• Other: ${attachments.filter(a => !a.fileType?.match(/(image|video|audio|document|pdf)/)).length} 📎\n\n` + `🔄 **Quick Actions**:\n` + `• Download specific files\n` + `• Upload new attachments\n` + `• Update file descriptions\n` + `• Remove outdated files\n` + `• Generate shareable links\n\n` + `💡 **Storage Tips**:\n` + `• Regularly review and clean up files\n` + `• Use cloud storage for large files\n` + `• Compress files when possible\n` + `• Organize with clear naming conventions` }] }; } catch (error) { throw new Error(`Failed to get task attachments: ${error.message}`); } } async downloadTaskAttachment({ task_id, attachment_id, download_format = 'original' }) { try { const params = new URLSearchParams({ format: download_format }); const downloadInfo = await this.makeTickTickRequest(`/task/${task_id}/attachment/${attachment_id}/download?${params}`); return { content: [{ type: 'text', text: `⬇️ **File Download Ready!**\n\n` + `📋 **Task**: ${task_id}\n` + `🆔 **Attachment ID**: ${attachment_id}\n` + `📁 **File Name**: ${downloadInfo.fileName || 'Unknown'}\n` + `📊 **File Size**: ${downloadInfo.fileSize ? Math.round(downloadInfo.fileSize / (1024 * 1024) * 100) / 100 + 'MB' : 'Unknown'}\n` + `📂 **Format**: ${download_format.toUpperCase()}\n` + `📅 **Generated**: ${new Date().toLocaleString()}\n\n` + `🔗 **Download Information**:\n` + `• **Download URL**: ${downloadInfo.downloadUrl}\n` + `• **Expires**: ${downloadInfo.expiryTime ? new Date(downloadInfo.expiryTime).toLocaleString() : 'No expiration'}\n` + `• **Access Limit**: ${downloadInfo.downloadLimit || 'Unlimited'} downloads\n` + `• **File Type**: ${downloadInfo.mimeType || 'Unknown'}\n\n` + (download_format === 'compressed' ? `📦 **Compression Details**:\n` + `• Original Size: ${downloadInfo.originalSize ? Math.round(downloadInfo.originalSize / (1024 * 1024) * 100) / 100 + 'MB' : 'Unknown'}\n` + `• Compressed Size: ${downloadInfo.compressedSize ? Math.round(downloadInfo.compressedSize / (1024 * 1024) * 100) / 100 + 'MB' : 'Unknown'}\n` + `• Compression Ratio: ${downloadInfo.compressionRatio ? Math.round(downloadInfo.compressionRatio * 100) + '%' : 'Unknown'}\n` + `• Quality: ${downloadInfo.quality || 'Optimized'}\n\n` : '' ) + `🔐 **Security Features**:\n` + `• Secure HTTPS download link\n` + `• Virus scan completed\n` + `• Access logging enabled\n` + `• Download tracking active\n\n` + `💡 **Download Tips**:\n` + `• Save the file promptly (link may expire)\n` + `• Use original format for best quality\n` + `• Choose compressed for faster downloads\n` + `• Verify file integrity after download\n\n` + `📱 **Browser Instructions**:\n` + `1. Click the download URL above\n` + `2. Choose save location\n` + `3. Wait for download to complete\n` + `4. Verify file opens correctly\n\n` + `🔄 **Next Steps**:\n` + `• Open/review the downloaded file\n` + `• Share with team members if needed\n` + `• Update task progress\n` + `• Provide feedback on file content` }] }; } catch (error) { throw new Error(`Failed to download task attachment: ${error.message}`); } } async deleteTaskAttachment({ task_id, attachment_id }) { try { const attachmentInfo = await this.makeTickTickRequest(`/task/${task_id}/attachment/${attachment_id}`, 'GET'); await this.makeTickTickRequest(`/task/${task_id}/attachment/${attachment_id}`, 'DELETE'); const fileSizeMB = Math.round((attachmentInfo.fileSize || 0) / (1024 * 1024) * 100) / 100; return { content: [{ type: 'text', text: `🗑️ **Attachment Deleted Successfully!**\n\n` + `📋 **Task**: ${task_id}\n` + `🆔 **Deleted Attachment ID**: ${attachment_id}\n` + `📁 **File Name**: ${attachmentInfo.fileName || 'Unknown'}\n` + `📊 **File Size**: ${fileSizeMB}MB\n` + `📅 **Deleted**: ${new Date().toLocaleString()}\n\n` + `📝 **Deleted File Details**:\n` + `• **Original Upload**: ${attachmentInfo.uploadDate ? new Date(attachmentInfo.uploadDate).toLocaleString() : 'N/A'}\n` + `• **File Type**: ${attachmentInfo.fileType || 'Unknown'}\n` + `• **Uploader**: ${attachmentInfo.uploaderName || attachmentInfo.uploaderId || 'Unknown'}\n` + `• **Download Count**: ${attachmentInfo.downloadCount || 0} times\n` + `• **Description**: ${attachmentInfo.description || 'No description'}\n\n` + `⚠️ **Deletion Impact**:\n` + `• File permanently removed from cloud storage\n` + `• Download links no longer accessible\n` + `• Cannot be recovered once deleted\n` + `• ${fileSizeMB}MB of storage space freed\n` + `• References in notes may be broken\n\n` + `📊 **Storage Cleanup**:\n` + `• Cloud storage updated\n` + `• Backup copies removed\n` + `• Search index updated\n` + `• Team access revoked\n` + `• Download history preserved\n\n` + `✅ **Team Notification**:\n` + `• Task collaborators notified\n` + `• Activity timeline updated\n` + `• Related task comments preserved\n` + `• Project storage usage updated\n\n` + `💡 **Post-Deletion Actions**:\n` + `• Update related documentation\n` + `• Notify stakeholders if needed\n` + `• Replace with updated file if applicable\n` + `• Review remaining attachments\n\n` + `🔄 **File Management**:\n` + `• Regular cleanup saves storage space\n` + `• Archive important files before deletion\n` + `• Consider file versioning for updates\n` + `• Maintain organized file structures` }] }; } catch (error) { throw new Error(`Failed to delete task attachment: ${error.message}`); } } // Templates & Automation Methods (Phase 3: 9 operations) async getTaskTemplates({ category, include_shared = true, sort_by = 'usage' }) { try { const params = new URLSearchParams({ includeShared: include_shared.toString(), sortBy: sort_by }); if (category) params.append('category', category); const templates = await this.makeTickTickRequest(`/task/templates?${params}`); return { content: [{ type: 'text', text: `📋 **Task Templates** (${templates.length || 0} templates found)\n\n` + `🔍 **Search Parameters**:\n` + `• Category Filter: ${category || 'All categories'}\n` + `• Include Shared: ${include_shared ? 'Yes' : 'No'}\n` + `• Sort By: ${sort_by.replace('_', ' ').toUpperCase()}\n` + `📅 **Retrieved**: ${new Date().toLocaleString()}\n\n` + (templates.length > 0 ? `📄 **Available Templates**:\n` + templates.map((template, index) => `${index + 1}. **${template.name}** ${template.isShared ? '🌍' : '🔒'}\n` + ` 🆔 ID: ${template.id}\n` + ` 📂 Category: ${template.category || 'General'}\n` + ` 📝 Description: ${template.description || 'No description'}\n` + ` 👤 Creator: ${template.creatorName || template.creatorId || 'Unknown'}\n` + ` 📊 Usage Count: ${template.usageCount || 0} times\n` + ` 📅 Created: ${template.createdDate ? new Date(template.createdDate).toLocaleDateString() : 'N/A'}\n` + ` 📅 Updated: ${template.updatedDate ? new Date(template.updatedDate).toLocaleDateString() : 'N/A'}\n` + ` ⭐ Rating: ${template.averageRating || 'No ratings'}\n` + ` 🏷️ Tags: ${template.tags ? template.tags.join(', ') : 'None'}` ).join('\n\n') : '📭 **No templates found** matching your criteria.' ) + `\n\n📊 **Template Summary**:\n` + `• Personal Templates: ${templates.filter(t => !t.isShared).length}\n` + `• Shared Templates: ${templates.filter(t => t.isShared).length}\n` + `• Most Used: ${templates.sort((a, b) => (b.usageCount || 0) - (a.usageCount || 0))[0]?.name || 'None'}\n` + `• Categories: ${[...new Set(templates.map(t => t.category).filter(Boolean))].join(', ') || 'None'}\n\n` + `🔄 **Quick Actions**:\n` + `• Create task from template\n` + `• Create new template\n` + `• Edit existing templates\n` + `• Share templates with team\n` + `• Bulk create from template\n\n` + `💡 **Template Tips**:\n` + `• Use templates for repetitive task structures\n` + `• Create project-specific templates\n` + `• Share best practices through templates\n` + `• Regularly update and refine templates` }] }; } catch (error) { throw new Error(`Failed to get task templates: ${error.message}`); } } async createTaskTemplate({ template_name, template_description, task_data, category = 'general', is_shared = false }) { try { const templateData = { name: template_name, description: template_description, taskStructure: task_data, category, isShared: is_shared }; const newTemplate = await this.makeTickTickRequest('/task/template', 'POST', templateData); return { content: [{ type: 'text', text: `📋 **Task Template Created Successfully!**\n\n` + `✅ **Template Details**:\n` + `• **Name**: ${newTemplate.name}\n` + `• **Template ID**: ${newTemplate.id}\n` + `• **Category**: ${newTemplate.category}\n` + `• **Sharing**: ${is_shared ? 'Shared with team' : 'Personal only'}\n` + `• **Created**: ${new Date().toLocaleString()}\n\n` + `📝 **Description**:\n${template_description || 'No description provided'}\n\n` + `📊 **Template Structure**:\n` + `• **Task Title**: ${task_data.title}\n` + `• **Description**: ${task_data.description || 'Template-based task'}\n` + `• **Priority**: ${task_data.priority || 'Medium'}\n` + `• **Estimated Duration**: ${task_data.estimated_duration ? task_data.estimated_duration + ' minutes' : 'Not specified'}\n` + `• **Tags**: ${task_data.tags ? task_data.tags.join(', ') : 'None'}\n\n` + `✅ **Template Features**:\n` + `• Reusable task structure\n` + `• Customizable on creation\n` + `• ${is_shared ? 'Available to all team members' : 'Private to your account'}\n` + `• Version tracking enabled\n` + `• Usage analytics tracked\n\n` + `🎯 **Use Cases**:\n` + `• Standardize recurring workflows\n` + `• Onboard new team members\n` + `• Ensure consistent task quality\n` + `• Save time on similar tasks\n\n` + `🚀 **Next Steps**:\n` + `• Create tasks from this template\n` + `• Share with team members if needed\n` + `• Refine based on usage feedback\n` + `• Add to your favorite templates\n\n` + `💡 **Template Management**:\n` + `• Update template as processes evolve\n` + `• Monitor usage statistics\n` + `• Gather feedback from users\n` + `• Archive unused templates` }] }; } catch (error) { throw new Error(`Failed to create task template: ${error.message}`); } } async updateTaskTemplate({ template_id, template_name, template_description, task_data, category }) { try { const updateData = {}; if (template_name) updateData.name = template_name; if (template_description) updateData.description = template_description; if (task_data) updateData.taskStructure = task_data; if (category) updateData.category = category; const updatedTemplate = await this.makeTickTickRequest(`/task/template/${template_id}`, 'PUT', updateData); return { content: [{ type: 'text', text: `✏️ **Template Updated Successfully!**\n\n` + `📋 **Updated Template**:\n` + `• **Template ID**: ${template_id}\n` + `• **Name**: ${updatedTemplate.name}\n` + `• **Category**: ${updatedTemplate.category}\n` + `• **Version**: ${updatedTemplate.version || 'Latest'}\n` + `• **Updated**: ${new Date().toLocaleString()}\n\n` + `📝 **Description**:\n${updatedTemplate.description || 'No description provided'}\n\n` + `🔄 **Update Summary**:\n` + `• ${template_name ? '✅ Name updated' : '📋 Name unchanged'}\n` + `• ${template_description ? '✅ Description updated' : '📋 Description unchanged'}\n` + `• ${task_data ? '✅ Task structure updated' : '📋 Structure unchanged'}\n` + `• ${category ? '✅ Category updated' : '📋 Category unchanged'}\n\n` + `📊 **Template Impact**:\n` + `• Usage Count: ${updatedTemplate.usageCount || 0} times\n` + `• Active Users: ${updatedTemplate.activeUsers || 0}\n` + `• Last Used: ${updatedTemplate.lastUsed ? new Date(updatedTemplate.lastUsed).toLocaleDateString() : 'Never'}\n` + `• Average Rating: ${updatedTemplate.averageRating || 'No ratings'}\n\n` + `⚠️ **Update Notes**:\n` + `• Previous version archived automatically\n` + `• Existing tasks created from template unchanged\n` + `• New tasks will use updated structure\n` + `• Team members notified of changes\n\n` + `💡 **Best Practices**:\n` + `• Document significant changes\n` + `• Test template before wide adoption\n` + `• Communicate updates to team\n` + `• Monitor impact on workflow efficiency\n\n` + `🔄 **Version Management**:\n` + `• Previous versions remain accessible\n` + `• Change history preserved\n` + `• Rollback available if needed\n` + `• Usage analytics continue tracking` }] }; } catch (error) { throw new Error(`Failed to update task template: ${error.message}`); } } async deleteTaskTemplate({ template_id }) { try { const templateInfo = await this.makeTickTickRequest(`/task/template/${template_id}`, 'GET'); await this.makeTickTickRequest(`/task/template/${template_id}`, 'DELETE'); return { content: [{ type: 'text', text: `🗑️ **Template Deleted Successfully!**\n\n` + `📋 **Deleted Template**:\n` + `• **Name**: ${templateInfo.name}\n` + `• **Template ID**: ${template_id}\n` + `• **Category**: ${templateInfo.category || 'General'}\n` + `• **Creator**: ${templateInfo.creatorName || templateInfo.creatorId || 'Unknown'}\n` + `• **Deleted**: ${new Date().toLocaleString()}\n\n` + `📊 **Template History**:\n` + `• **Total Usage**: ${templateInfo.usageCount || 0} times\n` + `• **Active Users**: ${templateInfo.activeUsers || 0}\n` + `• **Created**: ${templateInfo.createdDate ? new Date(templateInfo.createdDate).toLocaleDateString() : 'N/A'}\n` + `• **Last Used**: ${templateInfo.lastUsed ? new Date(templateInfo.lastUsed).toLocaleDateString() : 'Never'}\n` + `• **Rating**: ${templateInfo.averageRating || 'No ratings'}\n\n` + `⚠️ **Deletion Impact**:\n` + `• Template permanently removed\n` + `• Cannot be used for new task creation\n` + `• Existing tasks remain unchanged\n` + `• Template history preserved for analytics\n` + `• Shared access revoked\n\n` + `📋 **Affected Users**:\n` + `• ${templateInfo.activeUsers || 0} users lost access\n` + `• Team members will be notified\n` + `• Bookmark references will be broken\n` + `• Workflow automations may need updates\n\n` + `💡 **Post-Deletion Actions**:\n` + `• Review dependent workflows\n` + `• Create replacement template if needed\n` + `• Update team documentation\n` + `• Consider alternative templates\n\n` + `🔄 **Alternative Solutions**:\n` + `• Archive instead of delete for reference\n` + `• Create improved version before deletion\n` + `• Export template structure for backup\n` + `• Migrate users to similar templates\n\n` + `✅ **Cleanup Complete**: Template has been permanently removed from the system.` }] }; } catch (error) { throw new Error(`Failed to delete task template: ${error.message}`); } } async createTaskFromTemplate({ template_id, project_id, customizations = {}, create_multiple = 1 }) { try { const creationData = { templateId: template_id, projectId: project_id, customizations, quantity: create_multiple }; const result = await this.makeTickTickRequest('/task/from-template', 'POST', creationData); const createdTasks = result.tasks || []; return { content: [{ type: 'text', text: `🚀 **Tasks Created from Template!**\n\n` + `📋 **Creation Summary**:\n` + `• **Template**: ${template_id}\n` + `• **Project**: ${project_id || 'Default project'}\n` + `• **Tasks Created**: ${createdTasks.length}/${create_multiple}\n` + `• **Success Rate**: ${create_multiple > 0 ? Math.round((createdTasks.length / create_multiple) * 100) : 0}%\n` + `• **Created**: ${new Date().toLocaleString()}\n\n` + (createdTasks.length > 0 ? `✅ **Created Tasks**:\n` + createdTasks.slice(0, 10).map((task, index) => `${index + 1}. **${task.title}**\n` + ` 🆔 ID: ${task.id}\n` + ` 📅 Due: ${task.dueDate ? new Date(task.dueDate).toLocaleDateString() : 'Not set'}\n` + ` ⚡ Priority: ${task.priority || 'Medium'}\n` + ` 👤 Assigned: ${task.assigneeName || task.assigneeId || 'Unassigned'}\n` + ` 🏷️ Tags: ${task.tags ? task.tags.join(', ') : 'None'}` ).join('\n\n') + (createdTasks.length > 10 ? `\n\n... and ${createdTasks.length - 10} more tasks` : '') : '❌ **No tasks were created**. Check template and project permissions.' ) + `\n\n📊 **Customizations Applied**:\n` + Object.keys(customizations).length > 0 ? Object.entries(customizations).map(([key, value]) => `• ${key.replace('_', ' ').toUpperCase()}: ${value}` ).join('\n') : '• No customizations applied (used template defaults)' + `\n\n🎯 **Template Benefits**:\n` + `• Consistent task structure\n` + `• Time-saving automation\n` + `• Standardized workflows\n` + `• Reduced setup errors\n` + `• Best practice implementation\n\n` + (create_multiple > 1 ? `🔄 **Bulk Creation Results**:\n` + `• Requested: ${create_multiple} tasks\n` + `• Successfully Created: ${createdTasks.length}\n` + `• Failed: ${create_multiple - createdTasks.length}\n` + `• Average Creation Time: ${result.averageCreationTime || 'Unknown'}\n\n` : '' ) + `📱 **Next Steps**:\n` + `• Review created tasks for accuracy\n` + `• Assign team members if needed\n` + `• Set specific due dates\n` + `• Add project-specific details\n` + `• Begin task execution\n\n` + `💡 **Template Usage Tips**:\n` + `• Customize templates for different projects\n` + `• Use bulk creation for recurring workflows\n` + `• Provide feedback to template creators\n` + `• Monitor template effectiveness` }] }; } catch (error) { throw new Error(`Failed to create task from template: ${error.message}`); } } async setRecurringTask({ task_id, recurrence_pattern }) { try { const recurrenceData = { pattern: recurrence_pattern }; const recurringTask = await this.makeTickTickRequest(`/task/${task_id}/recurrence`, 'POST', recurrenceData); const frequencyLabels = { daily: 'Daily', weekly: 'Weekly', monthly: 'Monthly', yearly: 'Yearly' }; const endConditionText = recurrence_pattern.end_condition ? recurrence_pattern.end_condition.type === 'never' ? 'Never ends' : recurrence_pattern.end_condition.type === 'after_count' ? `After ${recurrence_pattern.end_condition.count} occurrences` : recurrence_pattern.end_condition.type === 'on_date' ? `Ends on ${recurrence_pattern.end_condition.end_date}` : 'Not specified' : 'Never ends'; return { content: [{ type: 'text', text: `🔄 **Recurring Task Setup Complete!**\n\n` + `📋 **Task**: ${task_id}\n` + `🆔 **Recurrence ID**: ${recurringTask.recurrenceId}\n` + `📅 **Configured**: ${new Date().toLocaleString()}\n\n` + `⚙️ **Recurrence Pattern**:\n` + `• **Frequency**: ${frequencyLabels[recurrence_pattern.frequency]} (every ${recurrence_pattern.interval || 1} ${recurrence_pattern.frequency})\n` + `• **End Condition**: ${endConditionText}\n` + (recurrence_pattern.days_of_week ? `• **Days of Week**: ${recurrence_pattern.days_of_week.map(day => ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'][day] ).join(', ')}\n` : '' ) + `• **Next Occurrence**: ${recurringTask.nextOccurrence ? new Date(recurringTask.nextOccurrence).toLocaleString() : 'Calculating...'}\n\n` + `📊 **Recurrence Schedule**:\n` + `• **Total Planned**: ${recurringTask.totalPlannedOccurrences || 'Unlimited'}\n` + `• **Completed So Far**: 0 (just started)\n` + `• **Remaining**: ${recurringTask.remainingOccurrences || 'Unlimited'}\n` + `• **Status**: Active\n\n` + `🎯 **Automation Benefits**:\n` + `• Automatic task creation\n` + `• Consistent scheduling\n` + `• Reduced manual effort\n` + `• Progress tracking across cycles\n` + `• Pattern-based workflows\n\n` + `📱 **Management Features**:\n` + `• Pause/resume recurrence\n` + `• Modify future occurrences\n` + `• Skip specific instances\n` + `• Track completion patterns\n` + `• Generate recurrence reports\n\n` + `💡 **Best Practices**:\n` + `• Set realistic recurrence intervals\n` + `• Review and adjust patterns regularly\n` + `• Use end conditions to prevent overload\n` + `• Monitor completion rates\n` + `• Pause during holidays or breaks\n\n` + `🔔 **Notifications**:\n` + `• New tasks will appear automatically\n` + `• Reminders follow original task settings\n` + `• Team members notified of new instances\n` + `• Overdue patterns trigger alerts\n\n` + `✅ **Setup Complete**: Your task will now recur automatically according to the specified pattern.` }] }; } catch (error) { throw new Error(`Failed to set recurring task: ${error.message}`); } } async getRecurringTasks({ project_id, frequency_filter = 'all', status = 'active' }) { try { const params = new URLSearchParams({ frequencyFilter: frequency_filter, status }); if (project_id) params.append('projectId', project_id); const recurringTasks = await this.makeTickTickRequest(`/task/recurring?${params}`); const frequencyIcons = { daily: '📅', weekly: '📆', monthly: '🗓️', yearly: '📋' }; const statusIcons = { active: '▶️', paused: '⏸️', completed: '✅' }; return { content: [{ type: 'text', text: `🔄 **Recurring Tasks** (${recurringTasks.length || 0} found)\n\n` + `🔍 **Filter Settings**:\n` + `• Project: ${project_id || 'All projects'}\n` + `• Frequency: ${frequency_filter.toUpperCase()}\n` + `• Status: ${status.toUpperCase()}\n` + `📅 **Retrieved**: ${new Date().toLocaleString()}\n\n` + (recurringTasks.length > 0 ? `📋 **Active Recurring Tasks**:\n` + recurringTasks.map((task, index) => `${index + 1}. ${statusIcons[task.status] || '🔄'} **${task.title}**\n` + ` 🆔 Task ID: ${task.id}\n` + ` 🆔 Recurrence ID: ${task.recurrenceId}\n` + ` ${frequencyIcons[task.frequency] || '📋'} Frequency: ${task.frequency} (every ${task.interval || 1})\n` + ` 📅 Next Due: ${task.nextOccurrence ? new Date(task.nextOccurrence).toLocaleString() : 'Calculating...'}\n` + ` 📊 Completed: ${task.completedOccurrences || 0}/${task.totalPlannedOccurrences || '∞'}\n` + ` 📈 Completion Rate: ${task.completionRate || 0}%\n` + ` 🎯 Project: ${task.projectName || task.projectId || 'Default'}\n` + ` ⏰ Created: ${task.createdDate ? new Date(task.createdDate).toLocaleDateString() : 'N/A'}\n` + ` 🔚 Ends: ${task.endCondition || 'Never'}` ).join('\n\n') : `📭 **No ${status} recurring tasks found** matching your criteria.` ) + `\n\n📊 **Recurrence Summary**:\n` + `• Daily: ${recurringTasks.filter(t => t.frequency === 'daily').length} 📅\n` + `• Weekly: ${recurringTasks.filter(t => t.frequency === 'weekly').length} 📆\n` + `• Monthly: ${recurringTasks.filter(t => t.frequency === 'monthly').length} 🗓️\n` + `• Yearly: ${recurringTasks.filter(t => t.frequency === 'yearly').length} 📋\n\n` + `📈 **Performance Metrics**:\n` + `• Average Completion Rate: ${recurringTasks.length > 0 ? Math.round(recurringTasks.reduce((sum, t) => sum + (t.completionRate || 0), 0) / recurringTasks.length) : 0}%\n` + `• Most Frequent: ${recurringTasks.sort((a, b) => (b.completedOccurrences || 0) - (a.completedOccurrences || 0))[0]?.title || 'None'}\n` + `• Overdue: ${recurringTasks.filter(t => t.isOverdue).length}\n` + `• Due Today: ${recurringTasks.filter(t => t.isDueToday).length}\n\n` + `🔄 **Quick Actions**:\n` + `• Pause/resume specific recurrences\n` + `• Modify recurrence patterns\n` + `• Complete current occurrences\n` + `• Generate recurrence reports\n` + `• Set up new recurring tasks\n\n` + `💡 **Management Tips**:\n` + `• Monitor completion rates regularly\n` + `• Adjust patterns based on workload\n` + `• Use pause feature during busy periods\n` + `• Review and optimize patterns quarterly\n` + `• Set realistic recurrence intervals` }] }; } catch (error) { throw new Error(`Failed to get recurring tasks: ${error.message}`); } } async pauseRecurringTask({ task_id, pause_reason, resume_date }) { try { const pauseData = { reason: pause_reason, resumeDate: resume_date }; const pausedTask = await this.makeTickTickRequest(`/task/${task_id}/recurrence/pause`, 'PUT', pauseData); return { content: [{ type: 'text', text: `⏸️ **Recurring Task Paused Successfully!**\n\n` + `📋 **Task**: ${task_id}\n` + `🆔 **Recurrence ID**: ${pausedTask.recurrenceId}\n` + `📅 **Paused**: ${new Date().toLocaleString()}\n` + `📅 **Resume Date**: ${resume_date ? new Date(resume_date).toLocaleDateString() : 'Manual resume required'}\n\n` + `📝 **Pause Details**:\n` + `• **Reason**: ${pause_reason || 'No reason specified'}\n` + `• **Status**: Paused\n` + `• **Next Scheduled**: ${pausedTask.nextScheduled ? new Date(pausedTask.nextScheduled).toLocaleString() : 'Will be calculated on resume'}\n` + `• **Missed Occurrences**: ${pausedTask.missedOccurrences || 0}\n\n` + `📊 **Recurrence History**:\n` + `• **Total Completed**: ${pausedTask.completedOccurrences || 0}\n` + `• **Completion Rate**: ${pausedTask.completionRate || 0}% (before pause)\n` + `• **Active Period**: ${pausedTask.activeDays || 0} days\n` + `• **Average Completion Time**: ${pausedTask.avgCompletionTime || 'Not calculated'}\n\n` + `⏸️ **Pause Impact**:\n` + `• No new task instances will be created\n` + `• Existing incomplete tasks remain active\n` + `• Recurrence pattern preserved\n` + `• Statistics tracking continues\n` + `• Team members notified of pause\n\n` + (resume_date ? `📅 **Automatic Resume**:\n` + `• Will resume on: ${new Date(resume_date).toLocaleDateString()}\n` + `• Next occurrence after resume calculated\n` + `• Notifications will restart\n` + `• Pattern continues from resume date\n\n` : `🔄 **Manual Resume**:\n` + `• Use resume function when ready\n` + `• Pattern will continue from resume date\n` + `• Missed occurrences can be optionally created\n` + `• Statistics will update accordingly\n\n` ) + `💡 **Pause Management**:\n` + `• Monitor paused tasks regularly\n` + `• Document pause reasons for analysis\n` + `• Set resume reminders\n` + `• Review pattern effectiveness\n` + `• Consider pattern adjustments\n\n` + `🔄 **Resume Options**:\n` + `• Automatic resume (if date set)\n` + `• Manual resume anytime\n` + `• Modify pattern before resume\n` + `• Create missed occurrences\n` + `• Cancel recurrence entirely\n\n` + `✅ **Pause Active**: The recurring task has been paused and will not create new instances until resumed.` }] }; } catch (error) { throw new Error(`Failed to pause recurring task: ${error.message}`); } } async bulkCreateFromTemplate({ template_id, bulk_data, apply_template_defaults = true, notify_assignees = true }) { try { const bulkCreationData = { templateId: template_id, tasks: bulk_data, applyDefaults: apply_template_defaults, notifyAssignees: notify_assignees }; const result = await this.makeTickTickRequest('/task/bulk-from-template', 'POST', bulkCreationData); const successCount = result.successful || 0; const failureCount = result.failed ? result.failed.length : 0; const totalCount = bulk_data.length; return { content: [{ type: 'text', text: `🚀 **Bulk Task Creation Completed!**\n\n` + `📊 **Creation Summary**:\n` + `• **Template Used**: ${template_id}\n` + `• **Total Requested**: ${totalCount}\n` + `• **Successfully Created**: ${successCount} ✅\n` + `• **Failed**: ${failureCount} ❌\n` + `• **Success Rate**: ${Math.round((successCount / totalCount) * 100)}%\n` + `• **Completed**: ${new Date().toLocaleString()}\n\n` + `⚙️ **Bulk Settings**:\n` + `• **Template Defaults**: ${apply_template_defaults ? 'Applied' : 'Overridden'}\n` + `• **Assignee Notifications**: ${notify_assignees ? 'Sent' : 'Disabled'}\n` + `• **Processing Time**: ${result.processingTime || 'Unknown'}\n\n` + (successCount > 0 ? `✅ **Successfully Created** (showing first 10):\n` + (result.createdTasks || bulk_data.slice(0, successCount)).slice(0, 10).map((task, index) => `${index + 1}. **${task.title}**\n` + ` 🆔 ID: ${task.id || 'Generated'}\n` + ` 📁 Project: ${task.project_id || 'Default'}\n` + ` 📅 Due: ${task.due_date || 'Not set'}\n` + ` 👤 Assigned: ${task.assignee_id || 'Unassigned'}` ).join('\n\n') + (successCount > 10 ? `\n\n... and ${successCount - 10} more tasks created successfully` : '') + '\n\n' : '' ) + (failureCount > 0 ? `❌ **Failed Creations**:\n` + (result.failed || []).slice(0, 5).map((failure, index) => `${index + 1}. **${failure.title || 'Unknown task'}**\n` + ` 🚫 Error: ${failure.error || 'Unknown error'}\n` + ` 📝 Details: ${failure.details || 'No additional details'}` ).join('\n\n') + (failureCount > 5 ? `\n\n... and ${failureCount - 5} more failures` : '') + '\n\n' : '' ) + `📊 **Bulk Analysis**:\n` + `• **Projects Affected**: ${new Set(bulk_data.map(t => t.project_id).filter(Boolean)).size}\n` + `• **Assignees Involved**: ${new Set(bulk_data.map(t => t.assignee_id).filter(Boolean)).size}\n` + `• **Due Dates Set**: ${bulk_data.filter(t => t.due_date).length}\n` + `• **Custom Fields Used**: ${bulk_data.filter(t => t.custom_fields && Object.keys(t.custom_fields).length > 0).length}\n\n` + `🎯 **Template Benefits**:\n` + `• Consistent task structure across all items\n` + `• Reduced setup time and errors\n` + `• Standardized workflow implementation\n` + `• Quality assurance through templates\n\n` + `📱 **Next Steps**:\n` + `• Review created tasks for accuracy\n` + `• Address any failed creations\n` + `• Assign team members if needed\n` + `• Set up project workflows\n` + `• Monitor task progress\n\n` + `💡 **Bulk Creation Tips**:\n` + `• Validate data before bulk operations\n` + `• Use templates for consistency\n` + `• Test with small batches first\n` + `• Monitor system performance\n` + `• Prepare rollback plans for failures\n\n` + (failureCount > 0 ? `🔄 **Retry Suggestions**:\n` + `• Check failed task permissions\n` + `• Verify project access rights\n` + `• Validate assignee IDs\n` + `• Review template compatibility\n` + `• Retry failed items individually\n\n` : '' ) + `✅ **Bulk Operation Complete**: ${successCount} tasks created successfully from template.` }] }; } catch (error) { throw new Error(`Failed to bulk create from template: ${error.message}`); } } // ==================== ANALYTICS & REPORTING METHODS ==================== async getProductivityReport({ time_range = 'month', start_date, end_date, include_habits = true, include_focus = true, project_ids }) { try { const params = new URLSearchParams({ range: time_range, habits: include_habits, focus: include_focus }); if (start_date) params.append('start', start_date); if (end_date) params.append('end', end_date); if (project_ids?.length) { project_ids.forEach(id => params.append('projects', id)); } const result = await this.makeTickTickRequest(`/analytics/productivity?${params}`); return { content: [{ type: 'text', text: `📊 **Productivity Report - ${time_range.toUpperCase()}**\n\n` + `🎯 **Performance Overview**:\n` + `• **Tasks Completed**: ${result.tasksCompleted || 0} (${result.completionRate || 0}% rate)\n` + `• **Projects Active**: ${result.activeProjects || 0}\n` + `• **Focus Hours**: ${result.focusHours || 0} hrs\n` + `• **Habit Consistency**: ${result.habitConsistency || 0}%\n` + `• **Productivity Score**: ${result.productivityScore || 0}/100\n\n` + `📈 **Key Metrics**:\n` + `• **Average Daily Tasks**: ${result.avgDailyTasks || 0}\n` + `• **Peak Productivity Day**: ${result.peakDay || 'N/A'}\n` + `• **Focus Session Count**: ${result.focusSessions || 0}\n` + `• **Time per Task**: ${result.avgTimePerTask || 0} min\n` + `• **Completion Streak**: ${result.completionStreak || 0} days\n\n` + (include_habits && result.habits ? `🔄 **Habit Performance**:\n` + `• **Habits Tracked**: ${result.habits.total || 0}\n` + `• **Daily Completion**: ${result.habits.dailyRate || 0}%\n` + `• **Best Habit**: ${result.habits.topPerformer || 'N/A'} (${result.habits.topStreak || 0} days)\n` + `• **Improvement Needed**: ${result.habits.needsWork || 'N/A'}\n\n` : '' ) + (include_focus && result.focus ? `🎯 **Focus Time Analysis**:\n` + `• **Total Focus Time**: ${result.focus.totalHours || 0} hrs\n` + `• **Average Session**: ${result.focus.avgSession || 0} min\n` + `• **Deep Work Ratio**: ${result.focus.deepWorkRatio || 0}%\n` + `• **Distraction Events**: ${result.focus.distractions || 0}\n` + `• **Peak Focus Hours**: ${result.focus.peakHours || 'N/A'}\n\n` : '' ) + `📊 **Trend Analysis**:\n` + `• **Week-over-Week**: ${result.trends?.weeklyChange || '+0'}%\n` + `• **Monthly Growth**: ${result.trends?.monthlyGrowth || '+0'}%\n` + `• **Efficiency Trend**: ${result.trends?.efficiency || 'Stable'}\n` + `• **Quality Score**: ${result.trends?.quality || 0}/5 ⭐\n\n` + `🎯 **Goal Progress**:\n` + `• **Daily Task Goal**: ${result.goals?.dailyTasks || 'Not set'}\n` + `• **Weekly Focus Goal**: ${result.goals?.weeklyFocus || 'Not set'}\n` + `• **Habit Targets**: ${result.goals?.habitTargets || 'Not set'}\n` + `• **Achievement Rate**: ${result.goals?.achievementRate || 0}%\n\n` + `💡 **Insights & Recommendations**:\n` + (result.insights || [ 'Focus on consistency over intensity', 'Schedule deep work during peak hours', 'Break large tasks into smaller chunks', 'Use time-blocking for better focus' ]).slice(0, 4).map(insight => `• ${insight}`).join('\n') + '\n\n' + `🚀 **Next Actions**:\n` + `• Review underperforming areas\n` + `• Adjust daily/weekly targets\n` + `• Optimize peak productivity hours\n` + `• Enhance focus time quality\n` + `• Celebrate progress and wins!\n\n` + `📅 **Report Period**: ${start_date || 'Auto'} to ${end_date || 'Auto'}\n` + `🔄 **Last Updated**: ${new Date().toLocaleString()}` }] }; } catch (error) { throw new Error(`Failed to get productivity report: ${error.message}`); } } async getCompletionTrends({ period = 'weekly', duration = 12, project_id, tag_filter, include_predictions = true }) { try { const params = new URLSearchParams({ period, duration: duration.toString(), predictions: include_predictions }); if (project_id) params.append('project', project_id); if (tag_filter?.length) { tag_filter.forEach(tag => params.append('tags', tag)); } const result = await this.makeTickTickRequest(`/analytics/trends?${params}`); return { content: [{ type: 'text', text: `📈 **Task Completion Trends Analysis**\n\n` + `🎯 **Trend Overview**:\n` + `• **Analysis Period**: ${period} for ${duration} periods\n` + `• **Data Points**: ${result.dataPoints || duration}\n` + `• **Trend Direction**: ${result.trendDirection || 'Stable'} ${result.trendIcon || '📊'}\n` + `• **Overall Growth**: ${result.overallGrowth || '+0'}%\n` + `• **Consistency Score**: ${result.consistencyScore || 0}/10\n\n` + `📊 **Performance Metrics**:\n` + `• **Average Completion**: ${result.avgCompletion || 0} tasks/${period.slice(0, -2)}\n` + `• **Peak Performance**: ${result.peakPeriod || 'N/A'} (${result.peakTasks || 0} tasks)\n` + `• **Lowest Period**: ${result.lowestPeriod || 'N/A'} (${result.lowestTasks || 0} tasks)\n` + `• **Performance Range**: ${result.performanceRange || '0-0'} tasks\n` + `• **Standard Deviation**: ${result.standardDev || 0}\n\n` + `📈 **Trend Patterns**:\n` + `• **Upward Trend**: ${result.patterns?.upward || 0}% of periods\n` + `• **Stable Periods**: ${result.patterns?.stable || 0}% of periods\n` + `• **Declining Periods**: ${result.patterns?.declining || 0}% of periods\n` + `• **Seasonal Pattern**: ${result.patterns?.seasonal || 'None detected'}\n\n` + (project_id && result.projectSpecific ? `📁 **Project-Specific Trends**:\n` + `• **Project Focus**: ${result.projectSpecific.name || 'Selected Project'}\n` + `• **Project Trend**: ${result.projectSpecific.trend || 'Stable'}\n` + `• **Completion Rate**: ${result.projectSpecific.rate || 0}%\n` + `• **vs. Overall Average**: ${result.projectSpecific.vsAverage || '+0'}%\n\n` : '' ) + (tag_filter?.length && result.tagAnalysis ? `🏷️ **Tag-Based Analysis**:\n` + `• **Filtered Tags**: ${tag_filter.join(', ')}\n` + `• **Tagged Task Trend**: ${result.tagAnalysis.trend || 'Stable'}\n` + `• **Tag Performance**: ${result.tagAnalysis.performance || 'Average'}\n` + `• **Most Productive Tag**: ${result.tagAnalysis.topTag || 'N/A'}\n\n` : '' ) + (include_predictions && result.predictions ? `🔮 **Trend Predictions**:\n` + `• **Next Period Forecast**: ${result.predictions.nextPeriod || 0} tasks\n` + `• **Monthly Projection**: ${result.predictions.monthlyProjection || 0} tasks\n` + `• **Confidence Level**: ${result.predictions.confidence || 0}%\n` + `• **Growth Trajectory**: ${result.predictions.trajectory || 'Stable'}\n` + `• **Recommended Target**: ${result.predictions.recommendedTarget || 0} tasks\n\n` : '' ) + `📊 **Period Breakdown**:\n` + (result.periodData || []).slice(-6).map((period, index) => `• **${period.label || `Period ${index + 1}`}**: ${period.completions || 0} tasks (${period.change || '+0'}%)` ).join('\n') + '\n\n' + `💡 **Trend Insights**:\n` + (result.insights || [ 'Consistency is more valuable than peak performance', 'Identify patterns in your most productive periods', 'Address factors causing declining trends', 'Set realistic targets based on trend analysis' ]).slice(0, 4).map(insight => `• ${insight}`).join('\n') + '\n\n' + `🎯 **Optimization Recommendations**:\n` + `• **Target Setting**: ${result.recommendations?.targetSetting || 'Maintain current pace'}\n` + `• **Timing Optimization**: ${result.recommendations?.timing || 'Continue current schedule'}\n` + `• **Capacity Planning**: ${result.recommendations?.capacity || 'Monitor workload balance'}\n` + `• **Trend Monitoring**: ${result.recommendations?.monitoring || 'Review trends monthly'}\n\n` + `📅 **Analysis Range**: Last ${duration} ${period} periods\n` + `🔄 **Data Freshness**: ${new Date().toLocaleString()}` }] }; } catch (error) { throw new Error(`Failed to get completion trends: ${error.message}`); } } async getTimeTrackingReport({ time_period = 'month', breakdown_by = 'project', include_estimates = true, focus_sessions_only = false, export_format = 'detailed' }) { try { const params = new URLSearchParams({ period: time_period, breakdown: breakdown_by, estimates: include_estimates, focus_only: focus_sessions_only, format: export_format }); const result = await this.makeTickTickRequest(`/analytics/time-tracking?${params}`); return { content: [{ type: 'text', text: `⏱️ **Time Tracking Analysis Report**\n\n` + `📊 **Time Overview**:\n` + `• **Total Tracked Time**: ${result.totalTime || 0} hours\n` + `• **Active Days**: ${result.activeDays || 0}/${result.totalDays || 0} days\n` + `• **Average Daily Time**: ${result.avgDailyTime || 0} hours\n` + `• **Peak Day**: ${result.peakDay || 'N/A'} (${result.peakDayHours || 0} hrs)\n` + `• **Time Utilization**: ${result.utilization || 0}%\n\n` + `🎯 **Breakdown by ${breakdown_by.charAt(0).toUpperCase() + breakdown_by.slice(1)}**:\n` + (result.breakdown || []).slice(0, 8).map((item, index) => `${index + 1}. **${item.name || 'Unknown'}**: ${item.hours || 0} hrs (${item.percentage || 0}%)` ).join('\n') + '\n\n' + (include_estimates && result.estimates ? `📝 **Estimate vs Actual Analysis**:\n` + `• **Accuracy Score**: ${result.estimates.accuracyScore || 0}%\n` + `• **Average Variance**: ${result.estimates.avgVariance || '+0'}%\n` + `• **Underestimated Tasks**: ${result.estimates.underestimated || 0}% of tasks\n` + `• **Overestimated Tasks**: ${result.estimates.overestimated || 0}% of tasks\n` + `• **Estimation Trend**: ${result.estimates.trend || 'Stable'}\n\n` : '' ) + (focus_sessions_only && result.focusAnalysis ? `🎯 **Focus Sessions Analysis**:\n` + `• **Total Focus Sessions**: ${result.focusAnalysis.totalSessions || 0}\n` + `• **Average Session Length**: ${result.focusAnalysis.avgLength || 0} min\n` + `• **Deep Work Percentage**: ${result.focusAnalysis.deepWorkPercentage || 0}%\n` + `• **Session Success Rate**: ${result.focusAnalysis.successRate || 0}%\n` + `• **Distraction Rate**: ${result.focusAnalysis.distractionRate || 0} per hour\n\n` : '' ) + `📈 **Time Distribution**:\n` + `• **Morning (6-12)**: ${result.distribution?.morning || 0} hrs (${result.distribution?.morningPct || 0}%)\n` + `• **Afternoon (12-18)**: ${result.distribution?.afternoon || 0} hrs (${result.distribution?.afternoonPct || 0}%)\n` + `• **Evening (18-24)**: ${result.distribution?.evening || 0} hrs (${result.distribution?.eveningPct || 0}%)\n` + `• **Late Night (0-6)**: ${result.distribution?.lateNight || 0} hrs (${result.distribution?.lateNightPct || 0}%)\n\n` + `🎯 **Productivity Metrics**:\n` + `• **Tasks per Hour**: ${result.productivity?.tasksPerHour || 0}\n` + `• **Quality Score**: ${result.productivity?.qualityScore || 0}/10\n` + `• **Efficiency Rating**: ${result.productivity?.efficiency || 'Average'}\n` + `• **Focus Quality**: ${result.productivity?.focusQuality || 0}%\n` + `• **Multitasking Rate**: ${result.productivity?.multitasking || 0}%\n\n` + `📊 **Weekly Pattern Analysis**:\n` + Object.entries(result.weeklyPattern || {}).map(([day, hours]) => `• **${day}**: ${hours || 0} hrs avg` ).join('\n') + '\n\n' + (export_format === 'detailed' && result.detailedBreakdown ? `🔍 **Detailed Time Logs**:\n` + (result.detailedBreakdown || []).slice(0, 10).map((log, index) => `${index + 1}. ${log.date || 'Unknown'}: ${log.task || 'N/A'} - ${log.duration || 0} min` ).join('\n') + (result.detailedBreakdown?.length > 10 ? `\n... and ${result.detailedBreakdown.length - 10} more entries` : '') + '\n\n' : '' ) + `💡 **Time Management Insights**:\n` + (result.insights || [ 'Identify your peak productivity hours', 'Focus on improving estimation accuracy', 'Minimize context switching between tasks', 'Schedule demanding work during high-energy times' ]).slice(0, 4).map(insight => `• ${insight}`).join('\n') + '\n\n' + `🎯 **Optimization Recommendations**:\n` + `• **Time Blocking**: ${result.recommendations?.timeBlocking || 'Continue current approach'}\n` + `• **Focus Improvement**: ${result.recommendations?.focusImprovement || 'Maintain focus quality'}\n` + `• **Estimation Training**: ${result.recommendations?.estimationTraining || 'Keep practicing'}\n` + `• **Schedule Optimization**: ${result.recommendations?.scheduleOptimization || 'Current schedule is effective'}\n\n` + `📅 **Report Period**: ${time_period.charAt(0).toUpperCase() + time_period.slice(1)}\n` + `📊 **Export Format**: ${export_format}\n` + `🔄 **Generated**: ${new Date().toLocaleString()}` }] }; } catch (error) { throw new Error(`Failed to get time tracking report: ${error.message}`); } } async getGoalProgress({ goal_type, time_frame = 'monthly', target_metrics, include_recommendations = true }) { try { const params = new URLSearchParams({ type: goal_type, frame: time_frame, recommendations: include_recommendations }); if (target_metrics) { Object.entries(target_metrics).forEach(([key, value]) => { params.append(`target_${key}`, value.toString()); }); } const result = await this.makeTickTickRequest(`/analytics/goals?${params}`); return { content: [{ type: 'text', text: `🎯 **Goal Progress Tracking**\n\n` + `📊 **Goal Overview**:\n` + `• **Goal Type**: ${goal_type?.replace('_', ' ').toUpperCase() || 'All Goals'}\n` + `• **Time Frame**: ${time_frame.charAt(0).toUpperCase() + time_frame.slice(1)}\n` + `• **Progress Period**: ${result.progressPeriod || 'Current period'}\n` + `• **Overall Achievement**: ${result.overallAchievement || 0}%\n` + `• **Goal Status**: ${result.goalStatus || 'In Progress'} ${result.statusIcon || '🎯'}\n\n` + `📈 **Current Progress**:\n` + (target_metrics ? Object.entries(target_metrics).map(([metric, target]) => { const actual = result.actualMetrics?.[metric] || 0; const percentage = target > 0 ? Math.round((actual / target) * 100) : 0; const status = percentage >= 100 ? '✅' : percentage >= 75 ? '🟡' : '🔴'; return `• **${metric.replace('_', ' ').toUpperCase()}**: ${actual}/${target} (${percentage}%) ${status}`; }).join('\n') : 'No specific targets set') + '\n\n' + `📊 **Performance Metrics**:\n` + `• **Daily Average**: ${result.dailyAverage || 0}\n` + `• **Best Day**: ${result.bestDay || 'N/A'} (${result.bestDayValue || 0})\n` + `• **Consistency Score**: ${result.consistencyScore || 0}/10 ⭐\n` + `• **Streak Current**: ${result.currentStreak || 0} days\n` + `• **Streak Best**: ${result.bestStreak || 0} days\n\n` + `📈 **Progress Trends**:\n` + `• **Week-over-Week**: ${result.trends?.weeklyChange || '+0'}%\n` + `• **Monthly Trajectory**: ${result.trends?.monthlyTrajectory || 'Stable'}\n` + `• **Velocity**: ${result.trends?.velocity || 'On track'}\n` + `• **Projected Completion**: ${result.trends?.projectedCompletion || 'Unknown'}\n\n` + (goal_type === 'task_completion' && result.taskGoals ? `✅ **Task Completion Goals**:\n` + `• **Target Tasks/Day**: ${result.taskGoals.dailyTarget || 0}\n` + `• **Current Average**: ${result.taskGoals.currentAverage || 0}\n` + `• **Completion Rate**: ${result.taskGoals.completionRate || 0}%\n` + `• **On-Time Completion**: ${result.taskGoals.onTimeRate || 0}%\n` + `• **Quality Score**: ${result.taskGoals.qualityScore || 0}/5 ⭐\n\n` : '' ) + (goal_type === 'habit_consistency' && result.habitGoals ? `🔄 **Habit Consistency Goals**:\n` + `• **Target Consistency**: ${result.habitGoals.targetConsistency || 0}%\n` + `• **Current Consistency**: ${result.habitGoals.currentConsistency || 0}%\n` + `• **Active Habits**: ${result.habitGoals.activeHabits || 0}\n` + `• **Perfect Days**: ${result.habitGoals.perfectDays || 0}\n` + `• **Improvement Rate**: ${result.habitGoals.improvementRate || '+0'}%\n\n` : '' ) + (goal_type === 'focus_time' && result.focusGoals ? `🎯 **Focus Time Goals**:\n` + `• **Target Hours/Week**: ${result.focusGoals.weeklyTarget || 0}\n` + `• **Current Average**: ${result.focusGoals.currentAverage || 0} hrs\n` + `• **Deep Work Ratio**: ${result.focusGoals.deepWorkRatio || 0}%\n` + `• **Session Quality**: ${result.focusGoals.sessionQuality || 0}/10\n` + `• **Distraction Control**: ${result.focusGoals.distractionControl || 0}%\n\n` : '' ) + (goal_type === 'project_milestones' && result.projectGoals ? `📁 **Project Milestone Goals**:\n` + `• **Active Projects**: ${result.projectGoals.activeProjects || 0}\n` + `• **Milestones This Period**: ${result.projectGoals.milestonesThisPeriod || 0}\n` + `• **On-Schedule Projects**: ${result.projectGoals.onSchedule || 0}%\n` + `• **Completion Rate**: ${result.projectGoals.completionRate || 0}%\n` + `• **Average Lead Time**: ${result.projectGoals.avgLeadTime || 0} days\n\n` : '' ) + `🏆 **Achievement Analysis**:\n` + `• **Goals Met**: ${result.achievements?.goalsMet || 0}/${result.achievements?.totalGoals || 0}\n` + `• **Exceeded Expectations**: ${result.achievements?.exceeded || 0}\n` + `• **Partially Achieved**: ${result.achievements?.partial || 0}\n` + `• **Behind Schedule**: ${result.achievements?.behind || 0}\n` + `• **Achievement Rate**: ${result.achievements?.rate || 0}%\n\n` + (include_recommendations && result.recommendations ? `💡 **AI-Powered Recommendations**:\n` + (result.recommendations || [ 'Set smaller, more achievable daily targets', 'Focus on consistency over perfection', 'Track leading indicators, not just outcomes', 'Celebrate small wins along the way' ]).slice(0, 5).map(rec => `• ${rec}`).join('\n') + '\n\n' : '' ) + `🎯 **Goal Optimization Tips**:\n` + `• **SMART Criteria**: Make goals Specific, Measurable, Achievable, Relevant, Time-bound\n` + `• **Progressive Loading**: Gradually increase targets as you build consistency\n` + `• **Environment Design**: Structure your environment to support goal achievement\n` + `• **Regular Review**: Assess and adjust goals based on progress and learnings\n` + `• **Habit Stacking**: Link new goals to existing successful habits\n\n` + `📅 **Next Review**: ${result.nextReview || 'Set a review date'}\n` + `🎯 **Suggested Adjustment**: ${result.suggestedAdjustment || 'Continue current approach'}\n` + `🔄 **Report Generated**: ${new Date().toLocaleString()}` }] }; } catch (error) { throw new Error(`Failed to get goal progress: ${error.message}`); } } async exportAnalyticsData({ data_types = ['tasks', 'habits', 'focus_sessions'], date_range, format = 'json', include_metadata = true, privacy_filter = false }) { try { const params = new URLSearchParams({ types: data_types.join(','), start: date_range.start_date, end: date_range.end_date, format, metadata: include_metadata, privacy: privacy_filter }); const result = await this.makeTickTickRequest(`/analytics/export?${params}`); return { content: [{ type: 'text', text: `📤 **Analytics Data Export Complete**\n\n` + `📊 **Export Configuration**:\n` + `• **Data Types**: ${data_types.join(', ')}\n` + `• **Date Range**: ${date_range.start_date} to ${date_range.end_date}\n` + `• **Export Format**: ${format.toUpperCase()}\n` + `• **Include Metadata**: ${include_metadata ? 'Yes' : 'No'}\n` + `• **Privacy Filter**: ${privacy_filter ? 'Applied' : 'None'}\n\n` + `📈 **Export Summary**:\n` + `• **Total Records**: ${result.totalRecords || 0}\n` + `• **File Size**: ${result.fileSize || 'Unknown'}\n` + `• **Processing Time**: ${result.processingTime || 0}ms\n` + `• **Export ID**: ${result.exportId || 'N/A'}\n` + `• **Download URL**: ${result.downloadUrl || 'Processing...'}\n\n` + `📋 **Data Breakdown**:\n` + (data_types.includes('tasks') ? `• **Tasks**: ${result.counts?.tasks || 0} records\n` : '') + (data_types.includes('habits') ? `• **Habits**: ${result.counts?.habits || 0} records\n` : '') + (data_types.includes('focus_sessions') ? `• **Focus Sessions**: ${result.counts?.focus_sessions || 0} records\n` : '') + (data_types.includes('projects') ? `• **Projects**: ${result.counts?.projects || 0} records\n` : '') + (data_types.includes('time_logs') ? `• **Time Logs**: ${result.counts?.time_logs || 0} records\n` : '') + '\n' + (include_metadata ? `📝 **Metadata Included**:\n` + `• **Field Descriptions**: Complete data schema\n` + `• **Export Parameters**: Full configuration details\n` + `• **Data Quality Metrics**: Completeness and accuracy info\n` + `• **Version Information**: API and data format versions\n` + `• **Export Timestamp**: Creation and last modified dates\n\n` : '' ) + (privacy_filter ? `🔒 **Privacy Protection Applied**:\n` + `• Personal identifiers removed\n` + `• Sensitive content filtered\n` + `• IP addresses anonymized\n` + `• Location data generalized\n` + `• Contact information excluded\n\n` : '' ) + `📊 **Export Quality**:\n` + `• **Data Completeness**: ${result.quality?.completeness || 100}%\n` + `• **Data Accuracy**: ${result.quality?.accuracy || 100}%\n` + `• **Format Validation**: ${result.quality?.formatValid ? 'Passed' : 'Failed'}\n` + `• **Schema Compliance**: ${result.quality?.schemaCompliant ? 'Yes' : 'No'}\n` + `• **Export Integrity**: ${result.quality?.integrity || 'Verified'}\n\n` + `💾 **File Information**:\n` + `• **File Name**: ${result.fileName || `analytics_export_${Date.now()}.${format}`}\n` + `• **MIME Type**: ${result.mimeType || 'application/json'}\n` + `• **Compression**: ${result.compressed ? 'gzip applied' : 'None'}\n` + `• **Checksum**: ${result.checksum || 'Not provided'}\n` + `• **Retention**: ${result.retentionDays || 30} days\n\n` + `🔄 **Usage Instructions**:\n` + (format === 'json' ? `• Load data using JSON parser in your preferred tool\n` + `• Each record includes all available fields\n` + `• Nested objects represent related data\n` + `• Timestamps are in ISO 8601 format\n` : format === 'csv' ? `• Import into Excel, Google Sheets, or analysis tools\n` + `• First row contains column headers\n` + `• Data types are preserved where possible\n` + `• Special characters are properly escaped\n` : `• Excel-compatible format with multiple sheets\n` + `• Separate sheet for each data type\n` + `• Rich formatting and data validation\n` + `• Charts and pivot table ready\n` ) + '\n' + `📊 **Analysis Suggestions**:\n` + `• **Trend Analysis**: Look for patterns over time\n` + `• **Correlation Studies**: Find relationships between metrics\n` + `• **Performance Benchmarking**: Compare against personal bests\n` + `• **Predictive Modeling**: Build forecasts from historical data\n` + `• **Habit Tracking**: Analyze consistency and improvement areas\n\n` + `⚠️ **Important Notes**:\n` + `• Data export link expires in ${result.linkExpiry || '7 days'}\n` + `• Download the file promptly to avoid data loss\n` + `• Respect data privacy when sharing exports\n` + `• Consider regular exports for backup purposes\n` + `• Contact support for custom export formats\n\n` + `📅 **Export Details**:\n` + `• **Created**: ${new Date().toLocaleString()}\n` + `• **Status**: ${result.status || 'Completed'}\n` + `• **Ready for Download**: ${result.downloadReady ? 'Yes' : 'Processing...'}` }] }; } catch (error) { throw new Error(`Failed to export analytics data: ${error.message}`); } } async getWeeklySummary({ week_offset = 0, include_sections = ['tasks', 'habits', 'focus', 'achievements'], compare_previous = true, personalization }) { try { const params = new URLSearchParams({ offset: week_offset.toString(), sections: include_sections.join(','), compare: compare_previous }); if (personalization?.tone) params.append('tone', personalization.tone); if (personalization?.focus_areas?.length) { params.append('focus_areas', personalization.focus_areas.join(',')); } const result = await this.makeTickTickRequest(`/analytics/weekly-summary?${params}`); const weekLabel = week_offset === 0 ? 'This Week' : week_offset === 1 ? 'Last Week' : `${week_offset} Weeks Ago`; return { content: [{ type: 'text', text: `📅 **Weekly Productivity Summary - ${weekLabel}**\n\n` + `🎯 **Week at a Glance**:\n` + `• **Week Period**: ${result.weekPeriod || 'Unknown'}\n` + `• **Productivity Score**: ${result.productivityScore || 0}/100 ⭐\n` + `• **Overall Rating**: ${result.overallRating || 'Good'} ${result.ratingEmoji || '😊'}\n` + `• **Key Highlight**: ${result.keyHighlight || 'Steady progress maintained'}\n\n` + (include_sections.includes('tasks') && result.tasks ? `✅ **Task Performance**:\n` + `• **Completed**: ${result.tasks.completed || 0} tasks\n` + `• **Daily Average**: ${result.tasks.dailyAverage || 0} tasks\n` + `• **Completion Rate**: ${result.tasks.completionRate || 0}%\n` + `• **On-Time Completion**: ${result.tasks.onTimeRate || 0}%\n` + `• **Best Day**: ${result.tasks.bestDay || 'N/A'} (${result.tasks.bestDayCount || 0} tasks)\n` + (compare_previous ? `• **vs Last Week**: ${result.tasks.vsLastWeek || '+0'}%\n` : '') + '\n' : '' ) + (include_sections.includes('habits') && result.habits ? `🔄 **Habit Tracking**:\n` + `• **Consistency Score**: ${result.habits.consistencyScore || 0}%\n` + `• **Perfect Days**: ${result.habits.perfectDays || 0}/7 days\n` + `• **Active Habits**: ${result.habits.activeHabits || 0}\n` + `• **Best Performer**: ${result.habits.bestPerformer || 'N/A'}\n` + `• **Needs Attention**: ${result.habits.needsAttention || 'None'}\n` + (compare_previous ? `• **vs Last Week**: ${result.habits.vsLastWeek || '+0'}%\n` : '') + '\n' : '' ) + (include_sections.includes('focus') && result.focus ? `🎯 **Focus Time**:\n` + `• **Total Focus**: ${result.focus.totalHours || 0} hours\n` + `• **Average Session**: ${result.focus.avgSession || 0} minutes\n` + `• **Deep Work**: ${result.focus.deepWorkHours || 0} hours\n` + `• **Focus Quality**: ${result.focus.qualityScore || 0}/10\n` + `• **Peak Hours**: ${result.focus.peakHours || 'N/A'}\n` + (compare_previous ? `• **vs Last Week**: ${result.focus.vsLastWeek || '+0'}%\n` : '') + '\n' : '' ) + (include_sections.includes('achievements') && result.achievements ? `🏆 **Achievements & Wins**:\n` + (result.achievements.list || [ 'Maintained consistent daily habits', 'Completed all high-priority tasks', 'Achieved focus time goals', 'Improved task completion rate' ]).slice(0, 5).map((achievement, index) => `${index + 1}. ${achievement}`).join('\n') + '\n\n' : '' ) + (include_sections.includes('challenges') && result.challenges ? `⚠️ **Areas for Improvement**:\n` + (result.challenges.list || [ 'Reduce task procrastination', 'Improve time estimation accuracy', 'Increase focus session length', 'Better work-life balance' ]).slice(0, 3).map((challenge, index) => `${index + 1}. ${challenge}`).join('\n') + '\n\n' : '' ) + (compare_previous && result.comparison ? `📊 **Week-over-Week Comparison**:\n` + `• **Tasks**: ${result.comparison.tasks || '+0'}% ${result.comparison.tasksIcon || '➡️'}\n` + `• **Habits**: ${result.comparison.habits || '+0'}% ${result.comparison.habitsIcon || '➡️'}\n` + `• **Focus**: ${result.comparison.focus || '+0'}% ${result.comparison.focusIcon || '➡️'}\n` + `• **Overall**: ${result.comparison.overall || '+0'}% ${result.comparison.overallIcon || '➡️'}\n\n` : '' ) + `📈 **Daily Breakdown**:\n` + ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'].map((day, index) => { const dayData = result.dailyBreakdown?.[day.toLowerCase()] || {}; return `• **${day}**: ${dayData.tasks || 0} tasks, ${dayData.focus || 0}h focus ${dayData.rating || '😐'}`; }).join('\n') + '\n\n' + (include_sections.includes('next_week') && result.nextWeek ? `🎯 **Next Week's Focus**:\n` + (result.nextWeek.priorities || [ 'Continue building on current habits', 'Address any overdue tasks', 'Optimize peak productivity hours', 'Maintain work-life balance' ]).slice(0, 4).map((priority, index) => `${index + 1}. ${priority}`).join('\n') + '\n\n' : '' ) + `💡 **Personalized Insights**:\n` + (result.insights || [ 'Your consistency is improving week over week', 'Focus on maintaining current momentum', 'Consider time-blocking for better efficiency', 'Celebrate your progress and small wins' ]).slice(0, 3).map(insight => `• ${insight}`).join('\n') + '\n\n' + `🎯 **Recommended Actions**:\n` + `• **Priority Focus**: ${result.recommendations?.priorityFocus || 'Maintain current approach'}\n` + `• **Habit Adjustment**: ${result.recommendations?.habitAdjustment || 'Continue current habits'}\n` + `• **Time Management**: ${result.recommendations?.timeManagement || 'Current schedule is working'}\n` + `• **Next Week Goal**: ${result.recommendations?.nextWeekGoal || 'Build on this week\'s success'}\n\n` + (personalization?.tone === 'motivational' ? `🌟 **Motivational Boost**:\n` + `You're making fantastic progress! Every small step counts, and your consistency is building the foundation for long-term success. Keep up the amazing work! 💪\n\n` : personalization?.tone === 'analytical' ? `📊 **Data-Driven Insights**:\n` + `Statistical analysis shows steady improvement patterns. Your productivity metrics indicate optimal performance trajectories. Continue data-informed optimization strategies.\n\n` : `📝 **Weekly Reflection**:\n` + `Take a moment to acknowledge your progress. Small consistent actions lead to significant long-term results. Stay focused on your goals! 🎯\n\n` ) + `📅 **Summary Generated**: ${new Date().toLocaleString()}\n` + `🔄 **Next Summary**: ${result.nextSummaryDate || 'Next week'}` }] }; } catch (error) { throw new Error(`Failed to get weekly summary: ${error.message}`); } } async getMonthlyInsights({ month_offset = 0, insight_depth = 'detailed', focus_metrics = ['efficiency', 'consistency', 'goal_achievement'], benchmarking, action_planning = true }) { try { const params = new URLSearchParams({ offset: month_offset.toString(), depth: insight_depth, metrics: focus_metrics.join(','), planning: action_planning }); if (benchmarking) { Object.entries(benchmarking).forEach(([key, value]) => { params.append(`benchmark_${key}`, value.toString()); }); } const result = await this.makeTickTickRequest(`/analytics/monthly-insights?${params}`); const monthLabel = month_offset === 0 ? 'This Month' : month_offset === 1 ? 'Last Month' : `${month_offset} Months Ago`; return { content: [{ type: 'text', text: `🌟 **Monthly Performance Insights - ${monthLabel}**\n\n` + `📊 **Executive Summary**:\n` + `• **Month**: ${result.monthPeriod || 'Unknown'}\n` + `• **Overall Performance**: ${result.overallPerformance || 'Good'} (${result.performanceScore || 0}/100)\n` + `• **Key Achievement**: ${result.keyAchievement || 'Consistent progress maintained'}\n` + `• **Primary Focus Area**: ${result.primaryFocus || 'Productivity optimization'}\n` + `• **Improvement Trajectory**: ${result.trajectory || 'Positive'} ${result.trajectoryIcon || '📈'}\n\n` + (focus_metrics.includes('efficiency') && result.efficiency ? `⚡ **Efficiency Analysis**:\n` + `• **Efficiency Score**: ${result.efficiency.score || 0}/100\n` + `• **Tasks per Hour**: ${result.efficiency.tasksPerHour || 0}\n` + `• **Time Utilization**: ${result.efficiency.timeUtilization || 0}%\n` + `• **Quality Rating**: ${result.efficiency.qualityRating || 0}/5 ⭐\n` + `• **Peak Efficiency Days**: ${result.efficiency.peakDays || 'N/A'}\n` + `• **Efficiency Trend**: ${result.efficiency.trend || 'Stable'}\n\n` : '' ) + (focus_metrics.includes('consistency') && result.consistency ? `🎯 **Consistency Metrics**:\n` + `• **Consistency Score**: ${result.consistency.score || 0}/100\n` + `• **Daily Habit Adherence**: ${result.consistency.habitAdherence || 0}%\n` + `• **Routine Stability**: ${result.consistency.routineStability || 0}%\n` + `• **Task Completion Regularity**: ${result.consistency.taskRegularity || 0}%\n` + `• **Longest Streak**: ${result.consistency.longestStreak || 0} days\n` + `• **Consistency Improvement**: ${result.consistency.improvement || '+0'}%\n\n` : '' ) + (focus_metrics.includes('goal_achievement') && result.goalAchievement ? `🏆 **Goal Achievement**:\n` + `• **Achievement Rate**: ${result.goalAchievement.rate || 0}%\n` + `• **Goals Completed**: ${result.goalAchievement.completed || 0}/${result.goalAchievement.total || 0}\n` + `• **Exceeded Targets**: ${result.goalAchievement.exceeded || 0}\n` + `• **Partially Met**: ${result.goalAchievement.partial || 0}\n` + `• **Average Progress**: ${result.goalAchievement.avgProgress || 0}%\n` + `• **Goal Momentum**: ${result.goalAchievement.momentum || 'Steady'}\n\n` : '' ) + (focus_metrics.includes('time_management') && result.timeManagement ? `⏰ **Time Management**:\n` + `• **Time Awareness Score**: ${result.timeManagement.awarenessScore || 0}/100\n` + `• **Planning Accuracy**: ${result.timeManagement.planningAccuracy || 0}%\n` + `• **Estimate vs Actual**: ${result.timeManagement.estimateAccuracy || 0}%\n` + `• **Time Waste Reduction**: ${result.timeManagement.wasteReduction || '+0'}%\n` + `• **Optimal Time Blocks**: ${result.timeManagement.optimalBlocks || 'N/A'}\n` + `• **Time ROI**: ${result.timeManagement.roi || 'Average'}\n\n` : '' ) + (focus_metrics.includes('stress_patterns') && result.stressPatterns ? `😰 **Stress & Workload Analysis**:\n` + `• **Stress Level**: ${result.stressPatterns.level || 'Moderate'}\n` + `• **High-Stress Days**: ${result.stressPatterns.highStressDays || 0}\n` + `• **Workload Balance**: ${result.stressPatterns.workloadBalance || 'Balanced'}\n` + `• **Recovery Time**: ${result.stressPatterns.recoveryTime || 'Adequate'}\n` + `• **Stress Triggers**: ${result.stressPatterns.triggers?.join(', ') || 'None identified'}\n` + `• **Coping Effectiveness**: ${result.stressPatterns.copingScore || 0}/10\n\n` : '' ) + (benchmarking?.compare_to_average && result.benchmarks?.average ? `📊 **vs Average Performance**:\n` + `• **Task Completion**: ${result.benchmarks.average.taskCompletion || '+0'}% vs avg\n` + `• **Focus Time**: ${result.benchmarks.average.focusTime || '+0'}% vs avg\n` + `• **Habit Consistency**: ${result.benchmarks.average.habitConsistency || '+0'}% vs avg\n` + `• **Efficiency Rating**: ${result.benchmarks.average.efficiency || 'Average'}\n` + `• **Percentile Ranking**: ${result.benchmarks.average.percentile || 50}th percentile\n\n` : '' ) + (benchmarking?.compare_to_best_month && result.benchmarks?.best ? `🌟 **vs Your Best Month**:\n` + `• **Best Month**: ${result.benchmarks.best.month || 'Unknown'}\n` + `• **Performance Gap**: ${result.benchmarks.best.gap || '0'}%\n` + `• **Areas Improved**: ${result.benchmarks.best.improved?.join(', ') || 'None'}\n` + `• **Areas to Match**: ${result.benchmarks.best.toImprove?.join(', ') || 'None'}\n` + `• **Best Month Score**: ${result.benchmarks.best.score || 0}/100\n\n` : '' ) + (insight_depth === 'comprehensive' && result.detailedAnalysis ? `🔍 **Deep Dive Analysis**:\n` + `• **Performance Patterns**: ${result.detailedAnalysis.patterns || 'Stable patterns observed'}\n` + `• **Peak Performance Factors**: ${result.detailedAnalysis.peakFactors?.join(', ') || 'Multiple factors'}\n` + `• **Bottleneck Areas**: ${result.detailedAnalysis.bottlenecks?.join(', ') || 'None identified'}\n` + `• **Optimization Opportunities**: ${result.detailedAnalysis.opportunities?.join(', ') || 'Maintain current approach'}\n` + `• **Risk Factors**: ${result.detailedAnalysis.risks?.join(', ') || 'Low risk profile'}\n\n` : '' ) + `📈 **Monthly Trends**:\n` + `• **Early Month**: ${result.trends?.earlyMonth || 'Strong start'}\n` + `• **Mid Month**: ${result.trends?.midMonth || 'Maintained pace'}\n` + `• **End Month**: ${result.trends?.endMonth || 'Strong finish'}\n` + `• **Momentum Pattern**: ${result.trends?.momentum || 'Consistent'}\n` + `• **Energy Levels**: ${result.trends?.energy || 'Stable'}\n\n` + `🎯 **Key Insights**:\n` + (result.insights || [ 'Consistency beats intensity for long-term success', 'Your peak performance hours are clearly defined', 'Habit formation is showing positive momentum', 'Goal achievement rate is above average' ]).slice(0, 5).map((insight, index) => `${index + 1}. ${insight}`).join('\n') + '\n\n' + (action_planning && result.actionPlan ? `🚀 **Next Month Action Plan**:\n\n` + `**Priority Focus Areas**:\n` + (result.actionPlan.priorities || [ 'Continue building consistent daily habits', 'Optimize peak productivity time blocks', 'Address identified bottleneck areas' ]).map((priority, index) => `${index + 1}. ${priority}`).join('\n') + '\n\n' + `**Specific Actions**:\n` + (result.actionPlan.actions || [ 'Set up morning routine optimization', 'Implement time-blocking for deep work', 'Review and adjust goal targets' ]).map((action, index) => `• ${action}`).join('\n') + '\n\n' + `**Success Metrics**:\n` + (result.actionPlan.metrics || [ 'Increase consistency score by 5%', 'Maintain current efficiency levels', 'Complete 90% of planned tasks' ]).map((metric, index) => `• ${metric}`).join('\n') + '\n\n' : '' ) + `💡 **Strategic Recommendations**:\n` + `• **Immediate (1-2 weeks)**: ${result.recommendations?.immediate || 'Fine-tune current systems'}\n` + `• **Short-term (1 month)**: ${result.recommendations?.shortTerm || 'Build on current momentum'}\n` + `• **Long-term (3 months)**: ${result.recommendations?.longTerm || 'Scale successful strategies'}\n` + `• **System Changes**: ${result.recommendations?.systemChanges || 'Minor optimizations needed'}\n\n` + `🎉 **Celebration Moments**:\n` + (result.celebrations || [ 'Achieved monthly consistency goals', 'Improved from previous month', 'Maintained healthy work-life balance', 'Successfully formed new positive habits' ]).slice(0, 3).map((celebration, index) => `${index + 1}. ${celebration} 🎊`).join('\n') + '\n\n' + `📅 **Insight Summary**:\n` + `• **Analysis Depth**: ${insight_depth.charAt(0).toUpperCase() + insight_depth.slice(1)}\n` + `• **Data Quality**: ${result.dataQuality || 'High'}\n` + `• **Confidence Level**: ${result.confidenceLevel || 95}%\n` + `• **Next Review**: ${result.nextReview || 'Next month'}\n` + `🔄 **Generated**: ${new Date().toLocaleString()}` }] }; } catch (error) { throw new Error(`Failed to get monthly insights: ${error.message}`); } } // ==================== SETTINGS & PREFERENCES METHODS ==================== async updateUserSettings({ settings }) { try { const result = await this.makeTickTickRequest('/user/settings', 'PUT', settings); return { content: [{ type: 'text', text: `⚙️ **User Settings Updated Successfully!**\n\n` + `🎯 **Settings Modified**:\n` + (settings.timezone ? `• **Timezone**: ${settings.timezone}\n` : '') + (settings.language ? `• **Language**: ${settings.language}\n` : '') + (settings.date_format ? `• **Date Format**: ${settings.date_format}\n` : '') + (settings.time_format ? `• **Time Format**: ${settings.time_format}\n` : '') + (settings.start_of_week !== undefined ? `• **Start of Week**: ${['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'][settings.start_of_week]}\n` : '') + (settings.theme ? `• **Theme**: ${settings.theme.charAt(0).toUpperCase() + settings.theme.slice(1)}\n` : '') + (settings.default_list ? `• **Default Project**: ${settings.default_list}\n` : '') + (settings.smart_add !== undefined ? `• **Smart Add**: ${settings.smart_add ? 'Enabled' : 'Disabled'}\n` : '') + (settings.auto_backup !== undefined ? `• **Auto Backup**: ${settings.auto_backup ? 'Enabled' : 'Disabled'}\n` : '') + '\n' + `🔄 **Update Status**:\n` + `• **Settings Applied**: ${Object.keys(settings).length} preferences updated\n` + `• **Sync Status**: ${result.syncStatus || 'Synced across devices'}\n` + `• **Cache Updated**: ${result.cacheUpdated ? 'Yes' : 'No'}\n` + `• **Requires Restart**: ${result.requiresRestart ? 'Yes - Please restart app' : 'No'}\n\n` + `📱 **Device Integration**:\n` + `• **Cross-Platform Sync**: Settings will sync to all connected devices\n` + `• **Mobile Apps**: Changes reflected in iOS/Android apps\n` + `• **Web Interface**: Updated preferences active immediately\n` + `• **Desktop Apps**: Settings applied at next launch\n\n` + `💡 **Optimization Tips**:\n` + (settings.timezone ? '• Timezone change improves scheduling accuracy\n' : '') + (settings.smart_add ? '• Smart Add helps parse natural language tasks\n' : '') + (settings.auto_backup ? '• Auto backup protects against data loss\n' : '') + '• Regular settings review ensures optimal experience\n' + '• Customize defaults to match your workflow\n\n' + `🎯 **Quick Actions**:\n` + `• Review notification settings for complete setup\n` + `• Check sync preferences for device coordination\n` + `• Explore advanced features in updated interface\n` + `• Share feedback on new settings configuration\n\n` + `📅 **Settings Summary**:\n` + `• **Updated**: ${new Date().toLocaleString()}\n` + `• **Profile ID**: ${result.profileId || 'Current user'}\n` + `• **Backup Created**: ${result.backupCreated ? 'Yes' : 'No'}\n` + `• **Version**: ${result.settingsVersion || '1.0'}\n\n` + `✅ **Settings update completed! Your preferences are now active across all devices.` }] }; } catch (error) { throw new Error(`Failed to update user settings: ${error.message}`); } } async getNotificationSettings({ category = 'all', include_disabled = false }) { try { const params = new URLSearchParams({ category, disabled: include_disabled }); const result = await this.makeTickTickRequest(`/user/notifications?${params}`); return { content: [{ type: 'text', text: `🔔 **Notification Settings Overview**\n\n` + `📊 **Current Configuration**:\n` + `• **Total Notification Types**: ${result.totalTypes || 0}\n` + `• **Active Notifications**: ${result.activeCount || 0}\n` + `• **Disabled Notifications**: ${result.disabledCount || 0}\n` + `• **Last Updated**: ${result.lastUpdated || 'Unknown'}\n` + `• **Profile Status**: ${result.profileStatus || 'Active'}\n\n` + (category === 'all' || category === 'tasks' ? `📋 **Task Notifications**:\n` + `• **Due Date Reminders**: ${result.tasks?.dueReminders?.enabled ? '✅ Enabled' : '❌ Disabled'}\n` + `• **Advance Notice**: ${result.tasks?.dueReminders?.advanceTime || 15} minutes\n` + `• **Overdue Alerts**: ${result.tasks?.overdueAlerts?.enabled ? '✅ Enabled' : '❌ Disabled'}\n` + `• **Completion Celebrations**: ${result.tasks?.completionCelebrations?.enabled ? '✅ Enabled' : '❌ Disabled'}\n` + `• **Daily Summary**: ${result.tasks?.dailySummary?.enabled ? '✅ Enabled' : '❌ Disabled'} at ${result.tasks?.dailySummary?.time || '9:00 AM'}\n\n` : '' ) + (category === 'all' || category === 'habits' ? `🔄 **Habit Notifications**:\n` + `• **Daily Reminders**: ${result.habits?.dailyReminders?.enabled ? '✅ Enabled' : '❌ Disabled'}\n` + `• **Reminder Time**: ${result.habits?.dailyReminders?.time || '8:00 AM'}\n` + `• **Active Days**: ${result.habits?.dailyReminders?.days?.map(d => ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'][d]).join(', ') || 'All days'}\n` + `• **Streak Celebrations**: ${result.habits?.streakCelebrations?.enabled ? '✅ Enabled' : '❌ Disabled'}\n` + `• **Weekly Progress**: ${result.habits?.weeklyProgress?.enabled ? '✅ Enabled' : '❌ Disabled'}\n\n` : '' ) + (category === 'all' || category === 'calendar' ? `📅 **Calendar Notifications**:\n` + `• **Event Reminders**: ${result.calendar?.eventReminders?.enabled ? '✅ Enabled' : '❌ Disabled'}\n` + `• **Default Advance**: ${result.calendar?.eventReminders?.defaultAdvance || 15} minutes\n` + `• **Conflict Alerts**: ${result.calendar?.conflictAlerts?.enabled ? '✅ Enabled' : '❌ Disabled'}\n` + `• **Sync Notifications**: ${result.calendar?.syncNotifications?.enabled ? '✅ Enabled' : '❌ Disabled'}\n\n` : '' ) + (category === 'all' || category === 'collaboration' ? `👥 **Collaboration Notifications**:\n` + `• **Task Assignments**: ${result.collaboration?.taskAssignments ? '✅ Enabled' : '❌ Disabled'}\n` + `• **Project Invitations**: ${result.collaboration?.projectInvitations ? '✅ Enabled' : '❌ Disabled'}\n` + `• **Comments & Messages**: ${result.collaboration?.comments ? '✅ Enabled' : '❌ Disabled'}\n` + `• **Status Updates**: ${result.collaboration?.statusUpdates ? '✅ Enabled' : '❌ Disabled'}\n` + `• **Team Activity**: ${result.collaboration?.teamActivity ? '✅ Enabled' : '❌ Disabled'}\n\n` : '' ) + (category === 'all' || category === 'system' ? `🔧 **System Notifications**:\n` + `• **App Updates**: ${result.system?.appUpdates ? '✅ Enabled' : '❌ Disabled'}\n` + `• **Tips & Tricks**: ${result.system?.tipsAndTricks ? '✅ Enabled' : '❌ Disabled'}\n` + `• **Weekly Reports**: ${result.system?.weeklyReports ? '✅ Enabled' : '❌ Disabled'}\n` + `• **Promotional**: ${result.system?.promotional ? '✅ Enabled' : '❌ Disabled'}\n` + `• **Security Alerts**: ${result.system?.securityAlerts ? '✅ Enabled' : '❌ Disabled'}\n\n` : '' ) + `📱 **Delivery Methods**:\n` + `• **Push Notifications**: ${result.delivery?.pushNotifications ? '✅ Enabled' : '❌ Disabled'}\n` + `• **Email Notifications**: ${result.delivery?.emailNotifications ? '✅ Enabled' : '❌ Disabled'}\n` + `• **SMS Notifications**: ${result.delivery?.smsNotifications ? '✅ Enabled' : '❌ Disabled'}\n` + `• **In-App Notifications**: ${result.delivery?.inAppNotifications ? '✅ Enabled' : '❌ Disabled'}\n\n` + `🎵 **Sound & Vibration**:\n` + `• **Notification Sounds**: ${result.soundSettings?.enabled ? '✅ Enabled' : '❌ Disabled'}\n` + `• **Sound Theme**: ${result.soundSettings?.theme || 'Default'}\n` + `• **Vibration**: ${result.soundSettings?.vibration ? '✅ Enabled' : '❌ Disabled'}\n` + `• **Quiet Hours**: ${result.soundSettings?.quietHours?.enabled ? '✅ Active' : '❌ Disabled'} ${result.soundSettings?.quietHours?.schedule || ''}\n\n` + (include_disabled && result.disabledNotifications?.length ? `❌ **Disabled Notifications**:\n` + result.disabledNotifications.slice(0, 8).map((notif, index) => `${index + 1}. ${notif.name} - ${notif.reason || 'User disabled'}` ).join('\n') + (result.disabledNotifications.length > 8 ? `\n... and ${result.disabledNotifications.length - 8} more` : '') + '\n\n' : '' ) + `⚡ **Quick Settings**:\n` + `• **Do Not Disturb**: ${result.quickSettings?.doNotDisturb ? 'Active' : 'Inactive'}\n` + `• **Focus Mode**: ${result.quickSettings?.focusMode ? 'Active' : 'Inactive'}\n` + `• **Smart Notifications**: ${result.quickSettings?.smartNotifications ? 'Enabled' : 'Disabled'}\n` + `• **Batch Grouping**: ${result.quickSettings?.batchGrouping ? 'Enabled' : 'Disabled'}\n\n` + `💡 **Optimization Suggestions**:\n` + `• Consider enabling daily summaries for better overview\n` + `• Set quiet hours to avoid disruption during sleep\n` + `• Use smart notifications to reduce notification fatigue\n` + `• Review and disable non-essential promotional notifications\n` + `• Test notification delivery across all your devices\n\n` + `📊 **Notification Analytics**:\n` + `• **Daily Average**: ${result.analytics?.dailyAverage || 0} notifications\n` + `• **Most Active Hour**: ${result.analytics?.peakHour || 'N/A'}\n` + `• **Interaction Rate**: ${result.analytics?.interactionRate || 0}%\n` + `• **Dismissed Rate**: ${result.analytics?.dismissedRate || 0}%\n\n` + `🔄 **Last Update**: ${new Date().toLocaleString()}` }] }; } catch (error) { throw new Error(`Failed to get notification settings: ${error.message}`); } } async updateNotificationSettings({ notifications }) { try { const result = await this.makeTickTickRequest('/user/notifications', 'PUT', notifications); const changedSettings = []; if (notifications.task_reminders) changedSettings.push('Task Reminders'); if (notifications.habit_reminders) changedSettings.push('Habit Reminders'); if (notifications.collaboration) changedSettings.push('Collaboration'); if (notifications.system) changedSettings.push('System'); if (notifications.delivery_methods) changedSettings.push('Delivery Methods'); return { content: [{ type: 'text', text: `🔔 **Notification Settings Updated!**\n\n` + `✅ **Updated Categories**: ${changedSettings.join(', ')}\n\n` + (notifications.task_reminders ? `📋 **Task Notification Changes**:\n` + `• **Reminders**: ${notifications.task_reminders.enabled ? 'Enabled' : 'Disabled'}\n` + (notifications.task_reminders.advance_time ? `• **Advance Time**: ${notifications.task_reminders.advance_time} minutes\n` : '') + (notifications.task_reminders.sound ? `• **Sound**: ${notifications.task_reminders.sound}\n` : '') + (notifications.task_reminders.vibrate !== undefined ? `• **Vibration**: ${notifications.task_reminders.vibrate ? 'Enabled' : 'Disabled'}\n` : '') + '\n' : '' ) + (notifications.habit_reminders ? `🔄 **Habit Notification Changes**:\n` + `• **Daily Reminders**: ${notifications.habit_reminders.enabled ? 'Enabled' : 'Disabled'}\n` + (notifications.habit_reminders.time ? `• **Reminder Time**: ${notifications.habit_reminders.time}\n` : '') + (notifications.habit_reminders.days ? `• **Active Days**: ${notifications.habit_reminders.days.map(d => ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'][d]).join(', ')}\n` : '') + '\n' : '' ) + (notifications.collaboration ? `👥 **Collaboration Changes**:\n` + Object.entries(notifications.collaboration).map(([key, value]) => `• **${key.replace('_', ' ').replace(/\b\w/g, l => l.toUpperCase())}**: ${value ? 'Enabled' : 'Disabled'}` ).join('\n') + '\n\n' : '' ) + (notifications.system ? `🔧 **System Notification Changes**:\n` + Object.entries(notifications.system).map(([key, value]) => `• **${key.replace('_', ' ').replace(/\b\w/g, l => l.toUpperCase())}**: ${value ? 'Enabled' : 'Disabled'}` ).join('\n') + '\n\n' : '' ) + (notifications.delivery_methods ? `📱 **Delivery Method Changes**:\n` + Object.entries(notifications.delivery_methods).map(([key, value]) => `• **${key.replace('_', ' ').replace(/\b\w/g, l => l.toUpperCase())}**: ${value ? 'Enabled' : 'Disabled'}` ).join('\n') + '\n\n' : '' ) + `📊 **Update Summary**:\n` + `• **Settings Modified**: ${Object.keys(notifications).length} categories\n` + `• **Sync Status**: ${result.syncStatus || 'Synced across devices'}\n` + `• **Effective Immediately**: Yes\n` + `• **Backup Created**: ${result.backupCreated ? 'Yes' : 'No'}\n\n` + `🎯 **Impact Assessment**:\n` + `• **Notification Volume**: ${result.expectedVolume || 'Moderate'} daily notifications\n` + `• **Productivity Impact**: ${result.productivityImpact || 'Optimized for focus'}\n` + `• **Battery Usage**: ${result.batteryImpact || 'Minimal impact'}\n` + `• **Network Usage**: ${result.networkImpact || 'Standard'}\n\n` + `📱 **Device Compatibility**:\n` + `• **Mobile Apps**: Settings applied immediately\n` + `• **Desktop Apps**: Active at next launch\n` + `• **Web Interface**: Real-time updates\n` + `• **Wearable Devices**: Synced automatically\n\n` + `💡 **Next Steps**:\n` + `• Test notifications on all your devices\n` + `• Monitor notification frequency for 24-48 hours\n` + `• Adjust quiet hours if needed\n` + `• Review weekly notification analytics\n` + `• Fine-tune based on usage patterns\n\n` + `⚙️ **Advanced Options**:\n` + `• Configure custom notification sounds\n` + `• Set up location-based notification rules\n` + `• Create notification templates for teams\n` + `• Enable smart notification bundling\n\n` + `📅 **Applied**: ${new Date().toLocaleString()}\n` + `🔔 **Status**: All notification preferences successfully updated!\n\n` + `✅ **Your notification experience is now optimized for your workflow.**` }] }; } catch (error) { throw new Error(`Failed to update notification settings: ${error.message}`); } } async getSyncSettings({ include_device_list = true, include_sync_history = false }) { try { const params = new URLSearchParams({ devices: include_device_list, history: include_sync_history }); const result = await this.makeTickTickRequest(`/user/sync?${params}`); return { content: [{ type: 'text', text: `🔄 **Synchronization Settings & Status**\n\n` + `📊 **Sync Overview**:\n` + `• **Auto Sync**: ${result.autoSync ? '✅ Enabled' : '❌ Disabled'}\n` + `• **Sync Frequency**: ${result.frequency || 'Real-time'}\n` + `• **Last Sync**: ${result.lastSync || 'Unknown'}\n` + `• **Sync Status**: ${result.status || 'Active'} ${result.statusIcon || '🟢'}\n` + `• **Next Scheduled**: ${result.nextSync || 'Automatic'}\n\n` + `⚙️ **Current Configuration**:\n` + `• **Sync Frequency**: ${result.settings?.frequency || 'real-time'}\n` + `• **WiFi Only**: ${result.settings?.wifiOnly ? 'Yes - Data saving mode' : 'No - All connections'}\n` + `• **Conflict Resolution**: ${result.settings?.conflictResolution || 'server-wins'}\n` + `• **Backup Before Sync**: ${result.settings?.backupBeforeSync ? 'Enabled' : 'Disabled'}\n` + `• **Compression**: ${result.settings?.compression ? 'Enabled' : 'Disabled'}\n\n` + `📱 **Data Types Synced**:\n` + `• **Tasks**: ${result.dataTypes?.tasks ? '✅ Synced' : '❌ Local only'}\n` + `• **Projects**: ${result.dataTypes?.projects ? '✅ Synced' : '❌ Local only'}\n` + `• **Habits**: ${result.dataTypes?.habits ? '✅ Synced' : '❌ Local only'}\n` + `• **Calendar**: ${result.dataTypes?.calendar ? '✅ Synced' : '❌ Local only'}\n` + `• **Attachments**: ${result.dataTypes?.attachments ? '✅ Synced' : '❌ Local only'}\n` + `• **Settings**: ${result.dataTypes?.settings ? '✅ Synced' : '❌ Local only'}\n\n` + (include_device_list && result.devices ? `🖥️ **Connected Devices (${result.devices.length || 0})**:\n` + (result.devices || []).slice(0, 8).map((device, index) => `${index + 1}. **${device.name || 'Unknown Device'}** (${device.type || 'Unknown'})\n` + ` • Last Sync: ${device.lastSync || 'Never'}\n` + ` • Status: ${device.status || 'Unknown'} ${device.online ? '🟢' : '🔴'}\n` + ` • Version: ${device.version || 'Unknown'}` ).join('\n\n') + (result.devices?.length > 8 ? `\n\n... and ${result.devices.length - 8} more devices` : '') + '\n\n' : '' ) + `📊 **Sync Performance**:\n` + `• **Success Rate**: ${result.performance?.successRate || 95}%\n` + `• **Average Speed**: ${result.performance?.avgSpeed || 'Fast'}\n` + `• **Data Transferred**: ${result.performance?.dataTransferred || '0 MB'} this month\n` + `• **Conflicts Resolved**: ${result.performance?.conflictsResolved || 0} this week\n` + `• **Failed Syncs**: ${result.performance?.failedSyncs || 0} this month\n\n` + (include_sync_history && result.history ? `📝 **Recent Sync Activity**:\n` + (result.history || []).slice(0, 10).map((entry, index) => `${index + 1}. **${entry.timestamp || 'Unknown time'}**: ${entry.action || 'Sync'}\n` + ` • Device: ${entry.device || 'Unknown'}\n` + ` • Result: ${entry.result || 'Success'} ${entry.success ? '✅' : '❌'}\n` + ` • Data: ${entry.dataSize || '0 KB'}` ).join('\n\n') + (result.history?.length > 10 ? `\n\n... and ${result.history.length - 10} more entries` : '') + '\n\n' : '' ) + `🔧 **Sync Health Check**:\n` + `• **Network Status**: ${result.healthCheck?.network || 'Good'}\n` + `• **Storage Space**: ${result.healthCheck?.storage || 'Sufficient'}\n` + `• **API Connectivity**: ${result.healthCheck?.api || 'Stable'}\n` + `• **Conflict Rate**: ${result.healthCheck?.conflictRate || 'Low'}\n` + `• **Overall Health**: ${result.healthCheck?.overall || 'Excellent'} ${result.healthCheck?.healthIcon || '💚'}\n\n` + `⚠️ **Sync Issues** ${result.issues?.length ? `(${result.issues.length})` : '(0)'}:\n` + (result.issues?.length ? result.issues.slice(0, 5).map((issue, index) => `${index + 1}. **${issue.type || 'Unknown'}**: ${issue.description || 'No details'}\n` + ` • Severity: ${issue.severity || 'Low'}\n` + ` • Suggested Fix: ${issue.suggestedFix || 'No action needed'}` ).join('\n\n') + (result.issues.length > 5 ? `\n\n... and ${result.issues.length - 5} more issues` : '') : 'No sync issues detected! Everything is working smoothly.' ) + '\n\n' + `💡 **Optimization Recommendations**:\n` + (result.recommendations || [ 'Enable WiFi-only sync to save mobile data', 'Set up automatic backups before major syncs', 'Review and clean up old device connections', 'Monitor sync performance weekly' ]).slice(0, 4).map(rec => `• ${rec}`).join('\n') + '\n\n' + `🔐 **Security & Privacy**:\n` + `• **Encryption**: ${result.security?.encryption || 'AES-256'} encryption in transit\n` + `• **Authentication**: ${result.security?.authentication || 'OAuth 2.0'} tokens\n` + `• **Data Retention**: ${result.security?.retention || '90 days'} backup retention\n` + `• **Privacy Mode**: ${result.security?.privacyMode ? 'Enabled' : 'Standard'}\n\n` + `📱 **Quick Actions**:\n` + `• **Force Sync Now**: Trigger immediate sync across all devices\n` + `• **Resolve Conflicts**: Review and resolve any pending conflicts\n` + `• **Clean Device List**: Remove inactive or old devices\n` + `• **Reset Sync Settings**: Restore default sync configuration\n\n` + `📅 **Report Generated**: ${new Date().toLocaleString()}\n` + `🔄 **Next Auto-Update**: ${result.nextUpdate || 'Real-time'}` }] }; } catch (error) { throw new Error(`Failed to get sync settings: ${error.message}`); } } async updateSyncSettings({ sync_settings }) { try { const result = await this.makeTickTickRequest('/user/sync', 'PUT', sync_settings); return { content: [{ type: 'text', text: `🔄 **Sync Settings Updated Successfully!**\n\n` + `✅ **Configuration Changes**:\n` + (sync_settings.auto_sync !== undefined ? `• **Auto Sync**: ${sync_settings.auto_sync ? 'Enabled' : 'Disabled'}\n` : '') + (sync_settings.sync_frequency ? `• **Sync Frequency**: ${sync_settings.sync_frequency}\n` : '') + (sync_settings.sync_on_wifi_only !== undefined ? `• **WiFi Only**: ${sync_settings.sync_on_wifi_only ? 'Enabled - Data saving mode' : 'Disabled - All connections'}\n` : '') + (sync_settings.conflict_resolution ? `• **Conflict Resolution**: ${sync_settings.conflict_resolution}\n` : '') + (sync_settings.backup_before_sync !== undefined ? `• **Backup Before Sync**: ${sync_settings.backup_before_sync ? 'Enabled' : 'Disabled'}\n` : '') + (sync_settings.compression !== undefined ? `• **Data Compression**: ${sync_settings.compression ? 'Enabled' : 'Disabled'}\n` : '') + '\n' + (sync_settings.data_types ? `📊 **Data Type Sync Settings**:\n` + Object.entries(sync_settings.data_types).map(([type, enabled]) => `• **${type.charAt(0).toUpperCase() + type.slice(1)}**: ${enabled ? '✅ Sync enabled' : '❌ Local only'}` ).join('\n') + '\n\n' : '' ) + `🎯 **Impact of Changes**:\n` + `• **Sync Performance**: ${result.impact?.performance || 'Optimized'}\n` + `• **Data Usage**: ${result.impact?.dataUsage || 'Efficient'}\n` + `• **Battery Impact**: ${result.impact?.battery || 'Minimal'}\n` + `• **Storage Usage**: ${result.impact?.storage || 'Optimized'}\n` + `• **Conflict Probability**: ${result.impact?.conflicts || 'Low'}\n\n` + `⚡ **Immediate Effects**:\n` + `• **Settings Applied**: All changes active immediately\n` + `• **Device Notification**: Other devices notified of changes\n` + `• **Sync Triggered**: ${result.syncTriggered ? 'Automatic sync initiated' : 'Next scheduled sync updated'}\n` + `• **Backup Created**: ${result.backupCreated ? 'Configuration backup saved' : 'No backup needed'}\n\n` + `📱 **Device Coordination**:\n` + `• **Mobile Apps**: Settings synchronized automatically\n` + `• **Desktop Apps**: Changes applied at next app launch\n` + `• **Web Interface**: Active immediately\n` + `• **Connected Devices**: ${result.deviceCount || 0} devices will receive updates\n\n` + `🔧 **Technical Details**:\n` + `• **Configuration Version**: ${result.configVersion || '1.0'}\n` + `• **Sync Protocol**: ${result.protocol || 'WebSocket + REST API'}\n` + `• **Encryption**: ${result.encryption || 'AES-256 end-to-end'}\n` + `• **Compression Ratio**: ${result.compressionRatio || '3:1'} (when enabled)\n` + `• **Max Payload Size**: ${result.maxPayload || '10MB'}\n\n` + (sync_settings.sync_frequency === 'real-time' ? `⚡ **Real-Time Sync Benefits**:\n` + `• Instant updates across all devices\n` + `• No data loss risk\n` + `• Immediate conflict detection\n` + `• Seamless multi-device workflow\n` + `• Minimal user intervention needed\n\n` : sync_settings.sync_frequency === 'manual' ? `🎯 **Manual Sync Mode**:\n` + `• Full control over when data syncs\n` + `• Reduced battery and data usage\n` + `• Perfect for limited connectivity\n` + `• Remember to sync regularly\n` + `• Consider weekly sync schedule\n\n` : `⏰ **Scheduled Sync Active**:\n` + `• Balanced performance and efficiency\n` + `• Automatic conflict prevention\n` + `• Predictable data usage\n` + `• Good for stable workflows\n` + `• Manual sync always available\n\n` ) + `💡 **Best Practices**:\n` + `• Keep auto-sync enabled for seamless experience\n` + `• Use WiFi-only mode to control data usage\n` + `• Enable compression for slower connections\n` + `• Regular backups prevent data loss\n` + `• Monitor sync performance weekly\n\n` + `⚠️ **Important Notes**:\n` + `• Changes apply to all connected devices\n` + `• Some settings require app restart on desktop\n` + `• Conflict resolution affects data precedence\n` + `• Backup settings before major changes\n` + `• Contact support if sync issues persist\n\n` + `🔄 **Next Steps**:\n` + `• Monitor sync performance for 24 hours\n` + `• Test sync across all your devices\n` + `• Review sync logs for any issues\n` + `• Adjust settings based on usage patterns\n` + `• Share feedback on sync experience\n\n` + `📅 **Updated**: ${new Date().toLocaleString()}\n` + `🎯 **Status**: Sync configuration optimized for your workflow!\n\n` + `✅ **Your devices are now perfectly synchronized.**` }] }; } catch (error) { throw new Error(`Failed to update sync settings: ${error.message}`); } } async resetUserData({ reset_type, data_categories, backup_before_reset = true, confirmation_code, export_data_first = false }) { try { const resetData = { type: reset_type, categories: data_categories, backup: backup_before_reset, confirmation: confirmation_code, export: export_data_first }; const result = await this.makeTickTickRequest('/user/reset', 'POST', resetData); const severityIcon = reset_type === 'full-reset' ? '🔴' : reset_type === 'partial-data' ? '🟡' : '🟢'; return { content: [{ type: 'text', text: `${severityIcon} **User Data Reset Completed**\n\n` + `📊 **Reset Summary**:\n` + `• **Reset Type**: ${reset_type.replace('-', ' ').toUpperCase()}\n` + `• **Operation ID**: ${result.operationId || 'Unknown'}\n` + `• **Execution Time**: ${result.executionTime || 0}ms\n` + `• **Status**: ${result.status || 'Completed'} ${result.success ? '✅' : '❌'}\n` + `• **Confirmation**: ${confirmation_code ? 'Verified' : 'Not required'}\n\n` + (data_categories?.length ? `📋 **Data Categories Reset**:\n` + data_categories.map((category, index) => `${index + 1}. **${category.charAt(0).toUpperCase() + category.slice(1)}**: ${result.categoryResults?.[category] || 'Completed'}` ).join('\n') + '\n\n' : `📋 **Reset Scope**: ${reset_type === 'full-reset' ? 'All user data' : reset_type === 'settings-only' ? 'Settings and preferences' : reset_type === 'cache-only' ? 'Local cache and temporary data' : 'Selected data categories'}\n\n` ) + (backup_before_reset && result.backup ? `💾 **Backup Information**:\n` + `• **Backup Created**: ${result.backup.created ? 'Yes' : 'No'}\n` + `• **Backup ID**: ${result.backup.id || 'Unknown'}\n` + `• **Backup Size**: ${result.backup.size || 'Unknown'}\n` + `• **Backup Location**: ${result.backup.location || 'Cloud storage'}\n` + `• **Recovery Code**: ${result.backup.recoveryCode || 'None'}\n` + `• **Retention Period**: ${result.backup.retention || '90 days'}\n\n` : '' ) + (export_data_first && result.export ? `📤 **Data Export**:\n` + `• **Export Status**: ${result.export.status || 'Completed'}\n` + `• **Export Format**: ${result.export.format || 'JSON'}\n` + `• **Download URL**: ${result.export.downloadUrl || 'Processing...'}\n` + `• **File Size**: ${result.export.fileSize || 'Unknown'}\n` + `• **Expiry Date**: ${result.export.expiryDate || '7 days from now'}\n\n` : '' ) + `📈 **Reset Statistics**:\n` + `• **Records Affected**: ${result.stats?.recordsAffected || 0}\n` + `• **Files Removed**: ${result.stats?.filesRemoved || 0}\n` + `• **Cache Cleared**: ${result.stats?.cacheCleared || '0 MB'}\n` + `• **Settings Reset**: ${result.stats?.settingsReset || 0}\n` + `• **Relationships Updated**: ${result.stats?.relationshipsUpdated || 0}\n\n` + (reset_type === 'full-reset' ? `🔴 **Full Reset Impact**:\n` + `• **All user data permanently removed**\n` + `• **Account reverted to initial state**\n` + `• **All customizations cleared**\n` + `• **Device sync relationships reset**\n` + `• **Fresh start with default settings**\n\n` : reset_type === 'partial-data' ? `🟡 **Partial Reset Impact**:\n` + `• **Selected data categories cleared**\n` + `• **Other data preserved intact**\n` + `• **Settings may need reconfiguration**\n` + `• **Device sync continues normally**\n` + `• **Targeted cleanup completed**\n\n` : reset_type === 'settings-only' ? `🟢 **Settings Reset Impact**:\n` + `• **All preferences restored to defaults**\n` + `• **User data preserved completely**\n` + `• **Customizations cleared**\n` + `• **Account data remains intact**\n` + `• **Clean slate for configuration**\n\n` : `🟢 **Cache Reset Impact**:\n` + `• **Temporary data cleared**\n` + `• **All user data preserved**\n` + `• **Performance may improve**\n` + `• **Re-sync may be triggered**\n` + `• **Minimal user impact**\n\n` ) + `🔄 **Post-Reset Actions Required**:\n` + (reset_type === 'full-reset' ? `• **Complete account setup wizard**\n` + `• **Restore data from backup if needed**\n` + `• **Reconfigure all preferences**\n` + `• **Re-establish device connections**\n` + `• **Set up integrations and automations**\n` : reset_type === 'settings-only' ? `• **Review and update preferences**\n` + `• **Configure notification settings**\n` + `• **Set up sync preferences**\n` + `• **Customize interface themes**\n` + `• **Test all device connections**\n` : `• **Verify data integrity**\n` + `• **Check sync functionality**\n` + `• **Review affected settings**\n` + `• **Test core features**\n` + `• **Monitor performance**\n` ) + '\n' + `⚠️ **Important Reminders**:\n` + `• **This action cannot be undone** (except via backup)\n` + `• **All connected devices affected** (sync required)\n` + `• **API integrations may need reconfiguration**\n` + `• **Shared projects remain with collaborators**\n` + `• **Contact support if issues arise**\n\n` + (result.backup?.recoveryCode ? `🔐 **Recovery Information**:\n` + `• **Recovery Code**: \`${result.backup.recoveryCode}\`\n` + `• **Keep this code safe** - Required for data recovery\n` + `• **Recovery URL**: ${result.backup.recoveryUrl || 'Contact support'}\n` + `• **Support Contact**: help@ticktick.com\n\n` : '' ) + `🎯 **Next Steps**:\n` + `• ${reset_type === 'full-reset' ? 'Begin account setup process' : 'Review and configure remaining settings'}\n` + `• ${backup_before_reset ? 'Store backup recovery information safely' : 'Consider creating manual backup going forward'}\n` + `• Test core functionality across all devices\n` + `• Monitor system performance for 24-48 hours\n` + `• Contact support team if any issues occur\n\n` + `📅 **Operation Completed**: ${new Date().toLocaleString()}\n` + `🔄 **Status**: ${result.status || 'Reset completed successfully'}\n\n` + `✅ **Your account has been reset according to your specifications.**` }] }; } catch (error) { throw new Error(`Failed to reset user data: ${error.message}`); } } async run() { const transport = new StdioServerTransport(); await this.server.connect(transport); console.log('🚀 TickTick MCP Server started'); console.log(`🔑 Client ID: ${TICKTICK_CLIENT_ID ? '✅ Configured' : '❌ Missing'}`); console.log(`🎫 Token: ${TICKTICK_TOKEN ? '✅ Configured' : '❌ Missing'}`); console.log(`🔐 Access Token: ${TICKTICK_ACCESS_TOKEN ? '✅ Configured' : '❌ Missing'}`); console.log('🔧 Available tools (112 total):'); console.log(' 📋 PROJECTS & TASKS (8):'); console.log(' • ticktick_get_projects - List all projects'); console.log(' • ticktick_create_project - Create new project'); console.log(' • ticktick_create_task - Create new task'); console.log(' • ticktick_update_task - Update existing task'); console.log(' • ticktick_delete_task - Delete task'); console.log(' • ticktick_complete_task - Mark task complete'); console.log(' • ticktick_get_task_details - Get task details'); console.log(' 🏷️ TAGS & ORGANIZATION (10):'); console.log(' • ticktick_get_tags - List all tags'); console.log(' • ticktick_create_tag - Create new tag'); console.log(' • ticktick_update_tag - Update tag'); console.log(' • ticktick_delete_tag - Delete tag'); console.log(' • ticktick_add_tag_to_task - Add tag to task'); console.log(' • ticktick_remove_tag_from_task - Remove tag from task'); console.log(' • ticktick_get_tasks_by_tag - Get tasks by tag'); console.log(' • ticktick_get_tag_usage_stats - Tag usage statistics'); console.log(' • ticktick_merge_tags - Merge tags'); console.log(' • ticktick_bulk_tag_operations - Bulk tag operations'); console.log(' 🔄 HABITS & TRACKING (15):'); console.log(' • ticktick_get_habits - List all habits'); console.log(' • ticktick_create_habit - Create new habit'); console.log(' • ticktick_update_habit - Update habit'); console.log(' • ticktick_delete_habit - Delete habit'); console.log(' • ticktick_checkin_habit - Check in habit'); console.log(' • ticktick_get_habit_history - Habit completion history'); console.log(' • ticktick_get_habit_stats - Habit statistics'); console.log(' • ticktick_pause_habit - Pause habit'); console.log(' • ticktick_resume_habit - Resume habit'); console.log(' • ticktick_get_habit_streaks - Get habit streaks'); console.log(' • ticktick_bulk_checkin_habits - Bulk habit check-in'); console.log(' • ticktick_get_habit_calendar - Habit calendar view'); console.log(' • ticktick_set_habit_goal - Set habit goals'); console.log(' • ticktick_get_habits_summary - Daily habits summary'); console.log(' • ticktick_export_habit_data - Export habit data'); console.log(' ⏰ FOCUS TIME & POMODORO (10):'); console.log(' • ticktick_start_focus_session - Start focus session'); console.log(' • ticktick_stop_focus_session - Stop focus session'); console.log(' • ticktick_pause_focus_session - Pause focus session'); console.log(' • ticktick_resume_focus_session - Resume focus session'); console.log(' • ticktick_get_focus_stats - Focus statistics'); console.log(' • ticktick_set_task_estimate - Set task time estimate'); console.log(' • ticktick_get_daily_focus_summary - Daily focus summary'); console.log(' • ticktick_get_focus_history - Focus session history'); console.log(' • ticktick_set_focus_goals - Set focus goals'); console.log(' • ticktick_get_productivity_insights - AI productivity insights'); console.log(' 🔍 SEARCH & FILTERING (5):'); console.log(' • ticktick_filter_tasks - Filter tasks by criteria'); console.log(' • ticktick_search_tasks - Advanced text search'); console.log(' • ticktick_get_today_tasks - Get today\'s tasks'); console.log(' • ticktick_get_overdue_tasks - Get overdue tasks'); console.log(' • ticktick_get_upcoming_tasks - Get upcoming tasks'); console.log(' ⚙️ UTILITIES (2):'); console.log(' • ticktick_convert_datetime_to_ticktick_format - Convert datetime'); console.log(' • ticktick_get_user_profile - Get user profile'); console.log(' 📊 ANALYTICS & REPORTING (7):'); console.log(' • ticktick_get_productivity_report - Comprehensive productivity analytics'); console.log(' • ticktick_get_completion_trends - Task completion patterns and trends'); console.log(' • ticktick_get_time_tracking_report - Time allocation analysis'); console.log(' • ticktick_get_goal_progress - Goal achievement tracking'); console.log(' • ticktick_export_analytics_data - Export analytics data'); console.log(' • ticktick_get_weekly_summary - Weekly productivity summary'); console.log(' • ticktick_get_monthly_insights - Monthly performance insights'); console.log(' ⚙️ SETTINGS & PREFERENCES (6):'); console.log(' • ticktick_update_user_settings - Modify user preferences'); console.log(' • ticktick_get_notification_settings - Get notification preferences'); console.log(' • ticktick_update_notification_settings - Configure notifications'); console.log(' • ticktick_get_sync_settings - Get synchronization settings'); console.log(' • ticktick_update_sync_settings - Configure sync behavior'); console.log(' • ticktick_reset_user_data - Reset or manage user data'); console.log('📡 Server ready for connections...'); } } const server = new TickTickMCPServer(); server.run().catch((error) => { console.error('💥 Failed to start server:', error); process.exit(1); });

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/liadgez/ticktick-mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server