@taazkareem/clickup-mcp-server 0.4.60 → 0.4.63

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,150 @@
1
+ /**
2
+ * Utility functions for ClickUp MCP tools
3
+ */
4
+ /**
5
+ * Get a timestamp for a relative time
6
+ *
7
+ * @param hours Hours from now
8
+ * @param days Days from now
9
+ * @param weeks Weeks from now
10
+ * @param months Months from now
11
+ * @returns Timestamp in milliseconds
12
+ */
13
+ export function getRelativeTimestamp(hours = 0, days = 0, weeks = 0, months = 0) {
14
+ const now = new Date();
15
+ if (hours)
16
+ now.setHours(now.getHours() + hours);
17
+ if (days)
18
+ now.setDate(now.getDate() + days);
19
+ if (weeks)
20
+ now.setDate(now.getDate() + (weeks * 7));
21
+ if (months)
22
+ now.setMonth(now.getMonth() + months);
23
+ return now.getTime();
24
+ }
25
+ /**
26
+ * Parse a due date string into a timestamp
27
+ * Supports ISO 8601 format or natural language like "tomorrow"
28
+ *
29
+ * @param dateString Date string to parse
30
+ * @returns Timestamp in milliseconds or undefined if parsing fails
31
+ */
32
+ export function parseDueDate(dateString) {
33
+ if (!dateString)
34
+ return undefined;
35
+ try {
36
+ // Handle natural language dates
37
+ const lowerDate = dateString.toLowerCase();
38
+ const now = new Date();
39
+ if (lowerDate === 'today') {
40
+ const today = new Date();
41
+ today.setHours(23, 59, 59, 999);
42
+ return today.getTime();
43
+ }
44
+ // Handle relative dates with specific times
45
+ const relativeTimeRegex = /(?:(\d+)\s*(days?|weeks?|months?)\s*from\s*now|tomorrow|next\s+(?:week|month))\s*(?:at\s+(\d+)(?::(\d+))?\s*(am|pm)?)?/i;
46
+ const match = lowerDate.match(relativeTimeRegex);
47
+ if (match) {
48
+ const date = new Date();
49
+ const [_, amount, unit, hours, minutes, meridian] = match;
50
+ // Calculate the future date
51
+ if (amount && unit) {
52
+ const value = parseInt(amount);
53
+ if (unit.startsWith('day')) {
54
+ date.setDate(date.getDate() + value);
55
+ }
56
+ else if (unit.startsWith('week')) {
57
+ date.setDate(date.getDate() + (value * 7));
58
+ }
59
+ else if (unit.startsWith('month')) {
60
+ date.setMonth(date.getMonth() + value);
61
+ }
62
+ }
63
+ else if (lowerDate.startsWith('tomorrow')) {
64
+ date.setDate(date.getDate() + 1);
65
+ }
66
+ else if (lowerDate.includes('next week')) {
67
+ date.setDate(date.getDate() + 7);
68
+ }
69
+ else if (lowerDate.includes('next month')) {
70
+ date.setMonth(date.getMonth() + 1);
71
+ }
72
+ // Set the time if specified
73
+ if (hours) {
74
+ let parsedHours = parseInt(hours);
75
+ const parsedMinutes = minutes ? parseInt(minutes) : 0;
76
+ // Convert to 24-hour format if meridian is specified
77
+ if (meridian?.toLowerCase() === 'pm' && parsedHours < 12)
78
+ parsedHours += 12;
79
+ if (meridian?.toLowerCase() === 'am' && parsedHours === 12)
80
+ parsedHours = 0;
81
+ date.setHours(parsedHours, parsedMinutes, 0, 0);
82
+ }
83
+ else {
84
+ // Default to end of day if no time specified
85
+ date.setHours(23, 59, 59, 999);
86
+ }
87
+ return date.getTime();
88
+ }
89
+ // Handle hours from now
90
+ const hoursRegex = /(\d+)\s*hours?\s*from\s*now/i;
91
+ const daysRegex = /(\d+)\s*days?\s*from\s*now/i;
92
+ const weeksRegex = /(\d+)\s*weeks?\s*from\s*now/i;
93
+ const monthsRegex = /(\d+)\s*months?\s*from\s*now/i;
94
+ if (hoursRegex.test(lowerDate)) {
95
+ const hours = parseInt(lowerDate.match(hoursRegex)[1]);
96
+ return getRelativeTimestamp(hours);
97
+ }
98
+ if (daysRegex.test(lowerDate)) {
99
+ const days = parseInt(lowerDate.match(daysRegex)[1]);
100
+ return getRelativeTimestamp(0, days);
101
+ }
102
+ if (weeksRegex.test(lowerDate)) {
103
+ const weeks = parseInt(lowerDate.match(weeksRegex)[1]);
104
+ return getRelativeTimestamp(0, 0, weeks);
105
+ }
106
+ if (monthsRegex.test(lowerDate)) {
107
+ const months = parseInt(lowerDate.match(monthsRegex)[1]);
108
+ return getRelativeTimestamp(0, 0, 0, months);
109
+ }
110
+ // Try to parse as a date string
111
+ const date = new Date(dateString);
112
+ if (!isNaN(date.getTime())) {
113
+ return date.getTime();
114
+ }
115
+ // If all parsing fails, return undefined
116
+ return undefined;
117
+ }
118
+ catch (error) {
119
+ console.warn(`Failed to parse due date: ${dateString}`, error);
120
+ return undefined;
121
+ }
122
+ }
123
+ /**
124
+ * Format a due date timestamp into a human-readable string
125
+ *
126
+ * @param timestamp Unix timestamp in milliseconds
127
+ * @returns Formatted date string or undefined if timestamp is invalid
128
+ */
129
+ export function formatDueDate(timestamp) {
130
+ if (!timestamp)
131
+ return undefined;
132
+ try {
133
+ const date = new Date(timestamp);
134
+ if (isNaN(date.getTime()))
135
+ return undefined;
136
+ // Format: "March 10, 2025 at 10:56 PM"
137
+ return date.toLocaleString('en-US', {
138
+ year: 'numeric',
139
+ month: 'long',
140
+ day: 'numeric',
141
+ hour: 'numeric',
142
+ minute: '2-digit',
143
+ hour12: true
144
+ }).replace(' at', ',');
145
+ }
146
+ catch (error) {
147
+ console.warn(`Failed to format due date: ${timestamp}`, error);
148
+ return undefined;
149
+ }
150
+ }
@@ -0,0 +1,132 @@
1
+ /**
2
+ * ClickUp MCP Workspace Tools
3
+ *
4
+ * This module defines workspace-related tools like retrieving workspace hierarchy.
5
+ * It handles the workspace tool definitions and the implementation of their handlers.
6
+ */
7
+ // Use the workspace service imported from the server
8
+ // This is defined when server.ts imports this module
9
+ let workspaceService;
10
+ /**
11
+ * Tool definition for retrieving the complete workspace hierarchy
12
+ */
13
+ export const workspaceHierarchyTool = {
14
+ name: 'get_workspace_hierarchy',
15
+ description: 'Get the complete workspace hierarchy including spaces, folders, and lists.',
16
+ inputSchema: {
17
+ type: 'object',
18
+ properties: {}
19
+ }
20
+ };
21
+ /**
22
+ * Initialize the tool with services
23
+ */
24
+ export function initializeWorkspaceTool(services) {
25
+ workspaceService = services.workspace;
26
+ }
27
+ /**
28
+ * Handler for the get_workspace_hierarchy tool
29
+ */
30
+ export async function handleGetWorkspaceHierarchy() {
31
+ try {
32
+ // Get workspace hierarchy from the workspace service
33
+ const hierarchy = await workspaceService.getWorkspaceHierarchy();
34
+ const response = formatHierarchyResponse(hierarchy);
35
+ return response;
36
+ }
37
+ catch (error) {
38
+ return {
39
+ content: [
40
+ {
41
+ type: "text",
42
+ text: `Error getting workspace hierarchy: ${error.message}`
43
+ }
44
+ ]
45
+ };
46
+ }
47
+ }
48
+ /**
49
+ * Format the hierarchy for the response
50
+ */
51
+ function formatHierarchyResponse(hierarchy) {
52
+ try {
53
+ // Helper function to format nodes by type
54
+ const formatNodesByType = (nodes = []) => {
55
+ const result = {
56
+ spaces: [],
57
+ folders: [],
58
+ lists: []
59
+ };
60
+ for (const node of nodes) {
61
+ if (node.type === 'space') {
62
+ const spaceResult = {
63
+ id: node.id,
64
+ name: node.name,
65
+ type: node.type,
66
+ lists: [],
67
+ folders: []
68
+ };
69
+ // Process children of space
70
+ if (node.children && node.children.length > 0) {
71
+ const childrenByType = formatNodesByType(node.children);
72
+ spaceResult.lists = childrenByType.lists;
73
+ spaceResult.folders = childrenByType.folders;
74
+ }
75
+ result.spaces.push(spaceResult);
76
+ }
77
+ else if (node.type === 'folder') {
78
+ const folderResult = {
79
+ id: node.id,
80
+ name: node.name,
81
+ type: node.type,
82
+ lists: []
83
+ };
84
+ // Process children of folder (only lists)
85
+ if (node.children && node.children.length > 0) {
86
+ folderResult.lists = node.children
87
+ .filter(child => child.type === 'list')
88
+ .map(list => ({
89
+ id: list.id,
90
+ name: list.name,
91
+ type: list.type
92
+ }));
93
+ }
94
+ result.folders.push(folderResult);
95
+ }
96
+ else if (node.type === 'list') {
97
+ result.lists.push({
98
+ id: node.id,
99
+ name: node.name,
100
+ type: node.type
101
+ });
102
+ }
103
+ }
104
+ return result;
105
+ };
106
+ // Convert the workspace hierarchy to a simplified format
107
+ const rootChildren = formatNodesByType(hierarchy.root.children);
108
+ const formattedHierarchy = {
109
+ workspaceId: hierarchy.root.id,
110
+ workspaceName: hierarchy.root.name,
111
+ spaces: rootChildren.spaces
112
+ };
113
+ return {
114
+ content: [
115
+ {
116
+ type: "text",
117
+ text: JSON.stringify(formattedHierarchy, null, 2)
118
+ }
119
+ ]
120
+ };
121
+ }
122
+ catch (error) {
123
+ return {
124
+ content: [
125
+ {
126
+ type: "text",
127
+ text: `Error formatting workspace hierarchy: ${error.message}`
128
+ }
129
+ ]
130
+ };
131
+ }
132
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@taazkareem/clickup-mcp-server",
3
- "version": "0.4.60",
3
+ "version": "0.4.63",
4
4
  "description": "ClickUp MCP Server - Integrate ClickUp tasks with AI through Model Context Protocol",
5
5
  "type": "module",
6
6
  "main": "build/index.js",
@@ -44,6 +44,8 @@
44
44
  "homepage": "https://github.com/taazkareem/clickup-mcp-server#readme",
45
45
  "dependencies": {
46
46
  "@modelcontextprotocol/sdk": "0.6.0",
47
+ "@modelcontextprotocol/server-github": "^2025.1.23",
48
+ "@taazkareem/clickup-mcp-server": "^0.4.60",
47
49
  "axios": "^1.6.7",
48
50
  "dotenv": "^16.4.1"
49
51
  },