Skip to main content
Glama
goals-tools.ts19 kB
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; import { z } from 'zod'; import { createClickUpClient } from '../clickup-client/index.js'; import { createEnhancedGoalsClient } from '../clickup-client/goals-enhanced.js'; import { // TeamIdSchema, // GoalIdSchema, // TargetIdSchema, // CreateGoalSchema, // UpdateGoalSchema, // GoalColorSchema } from '../schemas/goals-schemas.js'; // Create clients const clickUpClient = createClickUpClient(); const goalsClient = createEnhancedGoalsClient(clickUpClient); export function setupGoalsTools(server: McpServer): void { // ======================================== // GOAL MANAGEMENT OPERATIONS // ======================================== server.tool( 'clickup_get_goals', 'Get goals for a team with optional filtering. Returns goal details including progress, targets, and team members.', { team_id: z.string().min(1).describe('The ID of the team to get goals for'), include_completed: z.boolean().optional().default(false).describe('Whether to include completed goals') }, async ({ team_id, include_completed }) => { try { const goals = await goalsClient.getGoals(team_id, include_completed); return { content: [{ type: 'text', text: `Goals for team ${team_id}:\n\n${JSON.stringify(goals, null, 2)}` }] }; } catch (error: any) { console.error('Error getting goals:', error); return { content: [{ type: 'text', text: `Error getting goals: ${error.message}` }], isError: true }; } } ); server.tool( 'clickup_create_goal', 'Create a new goal with targets and deadlines. Supports team collaboration with multiple owners.', { team_id: z.string().min(1).describe('The ID of the team to create the goal for'), name: z.string().min(1).max(255).describe('The name of the goal'), due_date: z.number().positive().describe('Goal due date (Unix timestamp)'), description: z.string().optional().describe('Detailed description of the goal'), multiple_owners: z.boolean().default(false).describe('Whether the goal can have multiple owners'), owners: z.array(z.number().positive()).min(1).describe('Array of user IDs who own this goal'), color: z.string().regex(/^#[0-9A-Fa-f]{6}$/).optional().default('#007cff').describe('Goal color (hex format)') }, async ({ team_id, name, due_date, description, multiple_owners, owners, color }) => { try { // Validate due date is in the future if (!goalsClient.validateGoalDate(due_date)) { return { content: [{ type: 'text', text: 'Error: Due date must be in the future' }], isError: true }; } const params = { name, due_date, description, multiple_owners, owners, color }; const goal = await goalsClient.createGoal(team_id, params); return { content: [{ type: 'text', text: `Goal created successfully!\n\n${JSON.stringify(goal, null, 2)}` }] }; } catch (error: any) { console.error('Error creating goal:', error); return { content: [{ type: 'text', text: `Error creating goal: ${error.message}` }], isError: true }; } } ); server.tool( 'clickup_update_goal', 'Update an existing goal. Can modify name, description, due date, owners, and color.', { goal_id: z.string().min(1).describe('The ID of the goal to update'), name: z.string().min(1).max(255).optional().describe('New name for the goal'), due_date: z.number().positive().optional().describe('New due date (Unix timestamp)'), description: z.string().optional().describe('New description for the goal'), rem_owners: z.array(z.number().positive()).optional().describe('Array of user IDs to remove as owners'), add_owners: z.array(z.number().positive()).optional().describe('Array of user IDs to add as owners'), color: z.string().regex(/^#[0-9A-Fa-f]{6}$/).optional().describe('New goal color (hex format)') }, async ({ goal_id, name, due_date, description, rem_owners, add_owners, color }) => { try { // Validate due date if provided if (due_date && !goalsClient.validateGoalDate(due_date)) { return { content: [{ type: 'text', text: 'Error: Due date must be in the future' }], isError: true }; } const params = { name, due_date, description, rem_owners, add_owners, color }; const updatedGoal = await goalsClient.updateGoal(goal_id, params); return { content: [{ type: 'text', text: `Goal updated successfully!\n\n${JSON.stringify(updatedGoal, null, 2)}` }] }; } catch (error: any) { console.error('Error updating goal:', error); return { content: [{ type: 'text', text: `Error updating goal: ${error.message}` }], isError: true }; } } ); server.tool( 'clickup_delete_goal', 'Delete a goal from ClickUp. This action cannot be undone and will remove all associated targets.', { goal_id: z.string().min(1).describe('The ID of the goal to delete') }, async ({ goal_id }) => { try { await goalsClient.deleteGoal(goal_id); return { content: [{ type: 'text', text: `Goal ${goal_id} deleted successfully. All associated targets have been removed.` }] }; } catch (error: any) { console.error('Error deleting goal:', error); return { content: [{ type: 'text', text: `Error deleting goal: ${error.message}` }], isError: true }; } } ); server.tool( 'clickup_get_goal', 'Get detailed information about a specific goal including all targets and progress data.', { goal_id: z.string().min(1).describe('The ID of the goal to retrieve') }, async ({ goal_id }) => { try { const goal = await goalsClient.getGoal(goal_id); // Add formatted progress information const formattedGoal = { ...goal, progress_summary: { overall_progress: `${goal.percent_completed}%`, days_until_due: goalsClient.getDaysUntilDue(goal.due_date), status: goalsClient.getGoalStatus(goal.percent_completed, goal.due_date), targets_count: goal.key_results.length, completed_targets: goal.key_results.filter(t => t.completed).length } }; return { content: [{ type: 'text', text: `Goal details:\n\n${JSON.stringify(formattedGoal, null, 2)}` }] }; } catch (error: any) { console.error('Error getting goal:', error); return { content: [{ type: 'text', text: `Error getting goal: ${error.message}` }], isError: true }; } } ); // ======================================== // GOAL TARGET MANAGEMENT // ======================================== server.tool( 'clickup_create_goal_target', 'Create a target (key result) for a goal. Supports different target types: number, currency, boolean, task, and list.', { goal_id: z.string().min(1).describe('The ID of the goal to add the target to'), name: z.string().min(1).max(255).describe('The name of the target'), type: z.enum(['number', 'currency', 'boolean', 'task', 'list']).describe('The type of target'), target_value: z.number().min(0).describe('The target value to achieve'), start_value: z.number().optional().default(0).describe('The starting value (defaults to 0)'), unit: z.string().optional().describe('Unit of measurement (e.g., "USD", "tasks", "users")'), task_statuses: z.array(z.string()).optional().describe('Task statuses to track (for task type targets)'), list_ids: z.array(z.string()).optional().describe('List IDs to track (for list type targets)') }, async ({ goal_id, name, type, target_value, start_value, unit, task_statuses, list_ids }) => { try { const params = { name, type, target_value, start_value, unit, task_statuses, list_ids }; const target = await goalsClient.createGoalTarget(goal_id, params); return { content: [{ type: 'text', text: `Goal target created successfully!\n\n${JSON.stringify(target, null, 2)}` }] }; } catch (error: any) { console.error('Error creating goal target:', error); return { content: [{ type: 'text', text: `Error creating goal target: ${error.message}` }], isError: true }; } } ); server.tool( 'clickup_update_goal_target', 'Update an existing goal target. Can modify name, target value, unit, and tracking parameters.', { goal_id: z.string().min(1).describe('The ID of the goal'), target_id: z.string().min(1).describe('The ID of the target to update'), name: z.string().min(1).max(255).optional().describe('New name for the target'), target_value: z.number().min(0).optional().describe('New target value'), unit: z.string().optional().describe('New unit of measurement'), task_statuses: z.array(z.string()).optional().describe('New task statuses to track'), list_ids: z.array(z.string()).optional().describe('New list IDs to track') }, async ({ goal_id, target_id, name, target_value, unit, task_statuses, list_ids }) => { try { const params = { name, target_value, unit, task_statuses, list_ids }; const updatedTarget = await goalsClient.updateGoalTarget(goal_id, target_id, params); return { content: [{ type: 'text', text: `Goal target updated successfully!\n\n${JSON.stringify(updatedTarget, null, 2)}` }] }; } catch (error: any) { console.error('Error updating goal target:', error); return { content: [{ type: 'text', text: `Error updating goal target: ${error.message}` }], isError: true }; } } ); server.tool( 'clickup_delete_goal_target', 'Delete a target from a goal. This action cannot be undone.', { goal_id: z.string().min(1).describe('The ID of the goal'), target_id: z.string().min(1).describe('The ID of the target to delete') }, async ({ goal_id, target_id }) => { try { await goalsClient.deleteGoalTarget(goal_id, target_id); return { content: [{ type: 'text', text: `Goal target ${target_id} deleted successfully from goal ${goal_id}.` }] }; } catch (error: any) { console.error('Error deleting goal target:', error); return { content: [{ type: 'text', text: `Error deleting goal target: ${error.message}` }], isError: true }; } } ); // ======================================== // GOAL ANALYTICS & REPORTING // ======================================== server.tool( 'clickup_get_goal_summary', 'Get comprehensive goal analytics and summary for a team. Includes progress statistics, status breakdown, and upcoming deadlines.', { team_id: z.string().min(1).describe('The ID of the team to get goal summary for') }, async ({ team_id }) => { try { const summary = await goalsClient.getGoalSummary(team_id); return { content: [{ type: 'text', text: `Goal summary for team ${team_id}:\n\n${JSON.stringify(summary, null, 2)}` }] }; } catch (error: any) { console.error('Error getting goal summary:', error); return { content: [{ type: 'text', text: `Error getting goal summary: ${error.message}` }], isError: true }; } } ); // ======================================== // HELPER TOOLS // ======================================== server.tool( 'clickup_create_number_goal', 'Create a number-based goal with a target. Convenient helper for creating numeric goals like task counts or metrics.', { team_id: z.string().min(1).describe('The ID of the team'), goal_name: z.string().min(1).max(255).describe('The name of the goal'), target_name: z.string().min(1).max(255).describe('The name of the target'), target_value: z.number().min(1).describe('The numeric target to achieve'), unit: z.string().optional().describe('Unit of measurement (e.g., "tasks", "users", "points")'), due_date: z.number().positive().describe('Goal due date (Unix timestamp)'), description: z.string().optional().describe('Goal description'), owners: z.array(z.number().positive()).min(1).describe('Array of user IDs who own this goal'), color: z.string().regex(/^#[0-9A-Fa-f]{6}$/).optional().default('#007cff').describe('Goal color') }, async ({ team_id, goal_name, target_name, target_value, unit, due_date, description, owners, color }) => { try { // Create the goal const goal = await goalsClient.createGoal(team_id, { name: goal_name, due_date, description, multiple_owners: owners.length > 1, owners, color }); // Create the number target const target = await goalsClient.createGoalTarget(goal.id, { name: target_name, type: 'number', target_value, start_value: 0, unit }); return { content: [{ type: 'text', text: `Number goal created successfully!\n\nGoal: ${JSON.stringify(goal, null, 2)}\n\nTarget: ${JSON.stringify(target, null, 2)}` }] }; } catch (error: any) { console.error('Error creating number goal:', error); return { content: [{ type: 'text', text: `Error creating number goal: ${error.message}` }], isError: true }; } } ); server.tool( 'clickup_create_currency_goal', 'Create a currency-based goal with a monetary target. Convenient helper for creating revenue or budget goals.', { team_id: z.string().min(1).describe('The ID of the team'), goal_name: z.string().min(1).max(255).describe('The name of the goal'), target_name: z.string().min(1).max(255).describe('The name of the target'), target_value: z.number().min(0).describe('The monetary target to achieve'), currency: z.string().optional().default('USD').describe('Currency code (e.g., "USD", "EUR", "GBP")'), due_date: z.number().positive().describe('Goal due date (Unix timestamp)'), description: z.string().optional().describe('Goal description'), owners: z.array(z.number().positive()).min(1).describe('Array of user IDs who own this goal'), color: z.string().regex(/^#[0-9A-Fa-f]{6}$/).optional().default('#00c851').describe('Goal color') }, async ({ team_id, goal_name, target_name, target_value, currency, due_date, description, owners, color }) => { try { // Create the goal const goal = await goalsClient.createGoal(team_id, { name: goal_name, due_date, description, multiple_owners: owners.length > 1, owners, color }); // Create the currency target const target = await goalsClient.createGoalTarget(goal.id, { name: target_name, type: 'currency', target_value, start_value: 0, unit: currency }); // Format the target value for display const formattedValue = goalsClient.formatCurrencyValue(target_value, currency); return { content: [{ type: 'text', text: `Currency goal created successfully!\n\nGoal: ${goal_name}\nTarget: ${formattedValue}\n\nDetails:\nGoal: ${JSON.stringify(goal, null, 2)}\n\nTarget: ${JSON.stringify(target, null, 2)}` }] }; } catch (error: any) { console.error('Error creating currency goal:', error); return { content: [{ type: 'text', text: `Error creating currency goal: ${error.message}` }], isError: true }; } } ); server.tool( 'clickup_format_goal_progress', 'Format goal progress information for human-readable display. Useful for reporting and dashboards.', { goal_id: z.string().min(1).describe('The ID of the goal to format') }, async ({ goal_id }) => { try { const goal = await goalsClient.getGoal(goal_id); const status = goalsClient.getGoalStatus(goal.percent_completed, goal.due_date); const daysUntilDue = goalsClient.getDaysUntilDue(goal.due_date); let formattedTargets = ''; for (const target of goal.key_results) { const progress = goalsClient.calculateTargetProgress(target.start_value, target.current_value, target.target_value); let valueDisplay = ''; if (target.type === 'currency') { valueDisplay = `${goalsClient.formatCurrencyValue(target.current_value, target.unit || 'USD')} / ${goalsClient.formatCurrencyValue(target.target_value, target.unit || 'USD')}`; } else { valueDisplay = `${goalsClient.formatNumberValue(target.current_value, target.unit || undefined)} / ${goalsClient.formatNumberValue(target.target_value, target.unit || undefined)}`; } formattedTargets += `\n • ${target.name}: ${valueDisplay} (${progress.toFixed(1)}%)`; } const formattedProgress = ` 📊 Goal Progress Report 🎯 Goal: ${goal.name} 📈 Overall Progress: ${goal.percent_completed}% 📅 Status: ${status.toUpperCase()} ⏰ Days Until Due: ${daysUntilDue} 👥 Owners: ${goal.owners.map(o => o.username).join(', ')} 🎯 Targets:${formattedTargets} 📝 Description: ${goal.description || 'No description'} `; return { content: [{ type: 'text', text: formattedProgress }] }; } catch (error: any) { console.error('Error formatting goal progress:', error); return { content: [{ type: 'text', text: `Error formatting goal progress: ${error.message}` }], isError: true }; } } ); }

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/Chykalophia/ClickUp-MCP-Server---Enhanced'

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