@pripanggalih/clickup-mcp 1.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +22 -0
- package/README.md +295 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +184 -0
- package/dist/clickup-text.d.ts +83 -0
- package/dist/clickup-text.d.ts.map +1 -0
- package/dist/clickup-text.js +563 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +135 -0
- package/dist/resources/space-resources.d.ts +6 -0
- package/dist/resources/space-resources.d.ts.map +1 -0
- package/dist/resources/space-resources.js +95 -0
- package/dist/shared/config.d.ts +11 -0
- package/dist/shared/config.d.ts.map +1 -0
- package/dist/shared/config.js +61 -0
- package/dist/shared/data-uri.d.ts +14 -0
- package/dist/shared/data-uri.d.ts.map +1 -0
- package/dist/shared/data-uri.js +34 -0
- package/dist/shared/image-processing.d.ts +13 -0
- package/dist/shared/image-processing.d.ts.map +1 -0
- package/dist/shared/image-processing.js +199 -0
- package/dist/shared/types.d.ts +21 -0
- package/dist/shared/types.d.ts.map +1 -0
- package/dist/shared/types.js +2 -0
- package/dist/shared/utils.d.ts +71 -0
- package/dist/shared/utils.d.ts.map +1 -0
- package/dist/shared/utils.js +508 -0
- package/dist/test-utils.d.ts +23 -0
- package/dist/test-utils.d.ts.map +1 -0
- package/dist/test-utils.js +44 -0
- package/dist/tools/admin-tools.d.ts +3 -0
- package/dist/tools/admin-tools.d.ts.map +1 -0
- package/dist/tools/admin-tools.js +288 -0
- package/dist/tools/doc-tools.d.ts +4 -0
- package/dist/tools/doc-tools.d.ts.map +1 -0
- package/dist/tools/doc-tools.js +436 -0
- package/dist/tools/list-tools.d.ts +4 -0
- package/dist/tools/list-tools.d.ts.map +1 -0
- package/dist/tools/list-tools.js +175 -0
- package/dist/tools/search-tools.d.ts +3 -0
- package/dist/tools/search-tools.d.ts.map +1 -0
- package/dist/tools/search-tools.js +161 -0
- package/dist/tools/space-tools.d.ts +3 -0
- package/dist/tools/space-tools.d.ts.map +1 -0
- package/dist/tools/space-tools.js +128 -0
- package/dist/tools/task-tools.d.ts +8 -0
- package/dist/tools/task-tools.d.ts.map +1 -0
- package/dist/tools/task-tools.js +329 -0
- package/dist/tools/task-write-tools.d.ts +3 -0
- package/dist/tools/task-write-tools.d.ts.map +1 -0
- package/dist/tools/task-write-tools.js +567 -0
- package/dist/tools/time-tools.d.ts +4 -0
- package/dist/tools/time-tools.d.ts.map +1 -0
- package/dist/tools/time-tools.js +338 -0
- package/package.json +74 -0
|
@@ -0,0 +1,338 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.registerTimeToolsRead = registerTimeToolsRead;
|
|
4
|
+
exports.registerTimeToolsWrite = registerTimeToolsWrite;
|
|
5
|
+
const zod_1 = require("zod");
|
|
6
|
+
const config_1 = require("../shared/config");
|
|
7
|
+
const utils_1 = require("../shared/utils");
|
|
8
|
+
/**
|
|
9
|
+
* Converts ISO date string to Unix timestamp in milliseconds
|
|
10
|
+
*/
|
|
11
|
+
function isoToTimestamp(isoString) {
|
|
12
|
+
return new Date(isoString).getTime();
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Formats timestamp to ISO string with local timezone (not UTC)
|
|
16
|
+
*/
|
|
17
|
+
function timestampToIso(timestamp) {
|
|
18
|
+
const date = new Date(timestamp);
|
|
19
|
+
const year = date.getFullYear();
|
|
20
|
+
const month = String(date.getMonth() + 1).padStart(2, '0');
|
|
21
|
+
const day = String(date.getDate()).padStart(2, '0');
|
|
22
|
+
const hours = String(date.getHours()).padStart(2, '0');
|
|
23
|
+
const minutes = String(date.getMinutes()).padStart(2, '0');
|
|
24
|
+
const seconds = String(date.getSeconds()).padStart(2, '0');
|
|
25
|
+
// Calculate timezone offset
|
|
26
|
+
const offset = date.getTimezoneOffset();
|
|
27
|
+
const offsetHours = Math.floor(Math.abs(offset) / 60);
|
|
28
|
+
const offsetMinutes = Math.abs(offset) % 60;
|
|
29
|
+
const sign = offset <= 0 ? '+' : '-';
|
|
30
|
+
const timezoneOffset = sign + String(offsetHours).padStart(2, '0') + ':' + String(offsetMinutes).padStart(2, '0');
|
|
31
|
+
return `${year}-${month}-${day}T${hours}:${minutes}:${seconds}${timezoneOffset}`;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Formats duration in milliseconds to human readable format
|
|
35
|
+
*/
|
|
36
|
+
function formatDuration(durationMs) {
|
|
37
|
+
const hours = durationMs / (1000 * 60 * 60);
|
|
38
|
+
const displayHours = Math.floor(hours);
|
|
39
|
+
const displayMinutes = Math.round((hours - displayHours) * 60);
|
|
40
|
+
return displayHours > 0 ? `${displayHours}h ${displayMinutes}m` : `${displayMinutes}m`;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Formats timestamp to simple date and time for entry display
|
|
44
|
+
*/
|
|
45
|
+
function formatEntryTime(timestamp) {
|
|
46
|
+
const date = new Date(timestamp);
|
|
47
|
+
const month = String(date.getMonth() + 1).padStart(2, '0');
|
|
48
|
+
const day = String(date.getDate()).padStart(2, '0');
|
|
49
|
+
const hours = String(date.getHours()).padStart(2, '0');
|
|
50
|
+
const minutes = String(date.getMinutes()).padStart(2, '0');
|
|
51
|
+
return `${date.getFullYear()}-${month}-${day} ${hours}:${minutes}`;
|
|
52
|
+
}
|
|
53
|
+
function registerTimeToolsRead(server) {
|
|
54
|
+
server.tool("getTimeEntries", "Gets time entries for a specific task or all user's time entries. Returns last 30 days by default if no dates specified.", {
|
|
55
|
+
task_id: zod_1.z.string().min(6).max(9).optional().describe("Optional 6-9 character task ID to filter entries. If not provided, returns all user's time entries."),
|
|
56
|
+
start_date: zod_1.z.string().optional().describe("Optional start date filter as ISO date string (e.g., '2024-10-06T00:00:00+02:00'). Defaults to 30 days ago."),
|
|
57
|
+
end_date: zod_1.z.string().optional().describe("Optional end date filter as ISO date string (e.g., '2024-10-06T23:59:59+02:00'). Defaults to current date."),
|
|
58
|
+
list_id: zod_1.z.string().optional().describe("Optional single list ID to filter time entries by a specific list"),
|
|
59
|
+
space_id: zod_1.z.string().optional().describe("Optional single space ID to filter time entries by a specific space"),
|
|
60
|
+
include_all_users: zod_1.z.boolean().optional().describe("Optional flag to include time entries from all team members (default: false, only current user)")
|
|
61
|
+
}, {
|
|
62
|
+
readOnlyHint: true
|
|
63
|
+
}, async ({ task_id, start_date, end_date, list_id, space_id, include_all_users }) => {
|
|
64
|
+
try {
|
|
65
|
+
// Build query parameters
|
|
66
|
+
const params = new URLSearchParams();
|
|
67
|
+
if (task_id) {
|
|
68
|
+
params.append('task_id', task_id);
|
|
69
|
+
}
|
|
70
|
+
if (start_date) {
|
|
71
|
+
params.append('start_date', isoToTimestamp(start_date).toString());
|
|
72
|
+
}
|
|
73
|
+
if (end_date) {
|
|
74
|
+
params.append('end_date', isoToTimestamp(end_date).toString());
|
|
75
|
+
}
|
|
76
|
+
// Add single list_id or space_id filter (not both)
|
|
77
|
+
if (list_id) {
|
|
78
|
+
params.append('list_id', list_id);
|
|
79
|
+
}
|
|
80
|
+
else if (space_id) {
|
|
81
|
+
params.append('space_id', space_id);
|
|
82
|
+
}
|
|
83
|
+
// Always include location names to get list information
|
|
84
|
+
params.append('include_location_names', 'true');
|
|
85
|
+
// Handle include_all_users by fetching all team members and adding them as assignees filter
|
|
86
|
+
// Note: This only works for Workspace Owners/Admins
|
|
87
|
+
if (include_all_users) {
|
|
88
|
+
try {
|
|
89
|
+
const teamMembers = await (0, utils_1.getAllTeamMembers)();
|
|
90
|
+
if (teamMembers.length > 0) {
|
|
91
|
+
params.append('assignee', teamMembers.join(','));
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
catch (error) {
|
|
95
|
+
console.error('Warning: Could not fetch all team members. This feature requires Workspace Owner/Admin permissions.');
|
|
96
|
+
// Continue without all users - will only show current user's entries
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
const response = await fetch(`https://api.clickup.com/api/v2/team/${config_1.CONFIG.teamId}/time_entries?${params}`, {
|
|
100
|
+
headers: { Authorization: config_1.CONFIG.apiKey },
|
|
101
|
+
});
|
|
102
|
+
if (!response.ok) {
|
|
103
|
+
throw new Error(`Error fetching time entries: ${response.status} ${response.statusText}`);
|
|
104
|
+
}
|
|
105
|
+
const data = await response.json();
|
|
106
|
+
return processTimeEntriesData(data, task_id, start_date, end_date, include_all_users);
|
|
107
|
+
}
|
|
108
|
+
catch (error) {
|
|
109
|
+
console.error('Error fetching time entries:', error);
|
|
110
|
+
return {
|
|
111
|
+
content: [
|
|
112
|
+
{
|
|
113
|
+
type: "text",
|
|
114
|
+
text: `Error fetching time entries: ${error instanceof Error ? error.message : 'Unknown error'}`,
|
|
115
|
+
},
|
|
116
|
+
],
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Process the time entries data and return formatted hierarchical output
|
|
123
|
+
*/
|
|
124
|
+
function processTimeEntriesData(data, task_id, start_date, end_date, include_all_users) {
|
|
125
|
+
if (!data.data || !Array.isArray(data.data)) {
|
|
126
|
+
const noEntriesMsg = task_id ?
|
|
127
|
+
`No time entries found for task ${task_id}.` :
|
|
128
|
+
'No time entries found.';
|
|
129
|
+
return {
|
|
130
|
+
content: [{ type: "text", text: noEntriesMsg }],
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
const filteredEntries = data.data;
|
|
134
|
+
// Create hierarchical structure: List → Task → User → Individual entries
|
|
135
|
+
const hierarchy = new Map();
|
|
136
|
+
let totalTimeMs = 0;
|
|
137
|
+
filteredEntries.forEach((entry) => {
|
|
138
|
+
const taskId = entry.task?.id || 'no-task';
|
|
139
|
+
// Use location names from include_location_names parameter
|
|
140
|
+
const listId = entry.task_location?.list_id || 'no-list';
|
|
141
|
+
const listName = entry.task_location?.list_name || 'No List';
|
|
142
|
+
const taskName = entry.task?.name || 'No Task';
|
|
143
|
+
const userId = entry.user?.id || 'no-user';
|
|
144
|
+
const userName = entry.user?.username || 'Unknown User';
|
|
145
|
+
// Handle running timers (negative duration)
|
|
146
|
+
let entryDurationMs = parseInt(entry.duration) || 0;
|
|
147
|
+
const isRunningTimer = entryDurationMs < 0;
|
|
148
|
+
if (isRunningTimer) {
|
|
149
|
+
// For running timers, calculate current duration from start time
|
|
150
|
+
entryDurationMs = Date.now() - parseInt(entry.start);
|
|
151
|
+
}
|
|
152
|
+
totalTimeMs += entryDurationMs;
|
|
153
|
+
// Initialize list level
|
|
154
|
+
if (!hierarchy.has(listId)) {
|
|
155
|
+
hierarchy.set(listId, {
|
|
156
|
+
name: listName,
|
|
157
|
+
id: listId,
|
|
158
|
+
totalTime: 0,
|
|
159
|
+
tasks: new Map()
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
const listData = hierarchy.get(listId);
|
|
163
|
+
listData.totalTime += entryDurationMs;
|
|
164
|
+
// Initialize task level
|
|
165
|
+
if (!listData.tasks.has(taskId)) {
|
|
166
|
+
listData.tasks.set(taskId, {
|
|
167
|
+
name: taskName,
|
|
168
|
+
id: taskId,
|
|
169
|
+
totalTime: 0,
|
|
170
|
+
users: new Map()
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
const taskData = listData.tasks.get(taskId);
|
|
174
|
+
taskData.totalTime += entryDurationMs;
|
|
175
|
+
// Initialize user level
|
|
176
|
+
if (!taskData.users.has(userId)) {
|
|
177
|
+
taskData.users.set(userId, {
|
|
178
|
+
name: userName,
|
|
179
|
+
id: userId,
|
|
180
|
+
totalTime: 0,
|
|
181
|
+
entries: []
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
const userData = taskData.users.get(userId);
|
|
185
|
+
userData.totalTime += entryDurationMs;
|
|
186
|
+
userData.entries.push(entry);
|
|
187
|
+
});
|
|
188
|
+
// Count total tasks across all lists
|
|
189
|
+
let totalTasks = 0;
|
|
190
|
+
for (const [listId, listData] of hierarchy.entries()) {
|
|
191
|
+
totalTasks += listData.tasks.size;
|
|
192
|
+
}
|
|
193
|
+
// Format the hierarchical output
|
|
194
|
+
const outputLines = [];
|
|
195
|
+
// Header with date range and total
|
|
196
|
+
const dateRange = start_date && end_date ?
|
|
197
|
+
` (${start_date.split('T')[0]} to ${end_date.split('T')[0]})` :
|
|
198
|
+
start_date ? ` (from ${start_date.split('T')[0]})` :
|
|
199
|
+
end_date ? ` (until ${end_date.split('T')[0]})` : '';
|
|
200
|
+
outputLines.push(`Time Entries Summary${dateRange}`);
|
|
201
|
+
outputLines.push(`Total: ${formatDuration(totalTimeMs)}`);
|
|
202
|
+
outputLines.push('');
|
|
203
|
+
// Check if result is too large (>100 tasks)
|
|
204
|
+
const TASK_LIMIT = 100;
|
|
205
|
+
const isTruncated = totalTasks > TASK_LIMIT;
|
|
206
|
+
if (isTruncated) {
|
|
207
|
+
// Show only list-level summary
|
|
208
|
+
outputLines.push(`⚠️ Large result detected (${totalTasks} tasks). Showing summary only.`);
|
|
209
|
+
outputLines.push(`💡 Use list_id, space_id, or date filters for detailed view.`);
|
|
210
|
+
outputLines.push('');
|
|
211
|
+
for (const [listId, listData] of hierarchy.entries()) {
|
|
212
|
+
const taskCount = listData.tasks.size;
|
|
213
|
+
outputLines.push(`📋 ${listData.name} (List: ${listId}) - ${formatDuration(listData.totalTime)} across ${taskCount} task${taskCount === 1 ? '' : 's'}`);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
else {
|
|
217
|
+
// Show full hierarchical display
|
|
218
|
+
for (const [listId, listData] of hierarchy.entries()) {
|
|
219
|
+
outputLines.push(`📋 ${listData.name} (List: ${listId}) - ${formatDuration(listData.totalTime)}`);
|
|
220
|
+
for (const [taskId, taskData] of listData.tasks.entries()) {
|
|
221
|
+
outputLines.push(` ├─ 🎯 ${taskData.name} (Task: ${taskId}) - ${formatDuration(taskData.totalTime)}`);
|
|
222
|
+
const userEntries = Array.from(taskData.users.entries());
|
|
223
|
+
for (let userIndex = 0; userIndex < userEntries.length; userIndex++) {
|
|
224
|
+
const [userId, userData] = userEntries[userIndex];
|
|
225
|
+
const isLastUser = userIndex === userEntries.length - 1;
|
|
226
|
+
const userPrefix = isLastUser ? ' └─' : ' ├─';
|
|
227
|
+
outputLines.push(`${userPrefix} ${userData.name}: ${formatDuration(userData.totalTime)}`);
|
|
228
|
+
// Add individual entries
|
|
229
|
+
userData.entries.forEach((entry, entryIndex) => {
|
|
230
|
+
const isLastEntry = entryIndex === userData.entries.length - 1;
|
|
231
|
+
const entryPrefix = isLastUser ?
|
|
232
|
+
(isLastEntry ? ' └─' : ' ├─') :
|
|
233
|
+
(isLastEntry ? ' │ └─' : ' │ ├─');
|
|
234
|
+
const entryStart = formatEntryTime(parseInt(entry.start));
|
|
235
|
+
// Handle running timers
|
|
236
|
+
const rawDuration = parseInt(entry.duration) || 0;
|
|
237
|
+
const isRunningTimer = rawDuration < 0;
|
|
238
|
+
let entryDuration;
|
|
239
|
+
if (isRunningTimer) {
|
|
240
|
+
const currentDuration = Date.now() - parseInt(entry.start);
|
|
241
|
+
entryDuration = `${formatDuration(currentDuration)} (running)`;
|
|
242
|
+
}
|
|
243
|
+
else {
|
|
244
|
+
entryDuration = formatDuration(rawDuration);
|
|
245
|
+
}
|
|
246
|
+
const entryDescription = entry.description ? ` | ${entry.description}` : '';
|
|
247
|
+
outputLines.push(`${entryPrefix} ${entryStart} - ${entryDuration}${entryDescription}`);
|
|
248
|
+
});
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
outputLines.push('');
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
return {
|
|
255
|
+
content: [
|
|
256
|
+
{
|
|
257
|
+
type: "text",
|
|
258
|
+
text: outputLines.join('\n')
|
|
259
|
+
}
|
|
260
|
+
],
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
function registerTimeToolsWrite(server) {
|
|
264
|
+
server.tool("createTimeEntry", [
|
|
265
|
+
"Creates a time entry (books time) on a task for the current user.",
|
|
266
|
+
"Use decimal hours (e.g., 0.25 for 15 minutes, 0.5 for 30 minutes, 2.5 for 2.5 hours).",
|
|
267
|
+
"IMPORTANT: Before booking time, check the task's status - booking time on tasks in 'backlog', 'closed', or similar inactive states usually doesn't make sense.",
|
|
268
|
+
"Suggest moving the task to an active status like 'in progress' first."
|
|
269
|
+
].join("\n"), {
|
|
270
|
+
task_id: zod_1.z.string().min(6).max(9).describe("The 6-9 character task ID to book time against"),
|
|
271
|
+
hours: zod_1.z.number().min(0.01).max(24).describe("Hours to book (decimal format, e.g., 0.25 = 15min, 1.5 = 1h 30min)"),
|
|
272
|
+
description: zod_1.z.string().optional().describe("Optional description for the time entry"),
|
|
273
|
+
start_time: zod_1.z.string().optional().describe("Optional start time as ISO date string (e.g., '2024-10-06T09:00:00+02:00', defaults to current time)")
|
|
274
|
+
}, {
|
|
275
|
+
readOnlyHint: false,
|
|
276
|
+
destructiveHint: false,
|
|
277
|
+
idempotentHint: false,
|
|
278
|
+
}, async ({ task_id, hours, description, start_time }) => {
|
|
279
|
+
try {
|
|
280
|
+
// Convert hours to milliseconds (ClickUp API uses milliseconds)
|
|
281
|
+
const durationMs = Math.round(hours * 60 * 60 * 1000);
|
|
282
|
+
// Convert ISO date to timestamp if provided, otherwise use current time
|
|
283
|
+
const startTimeMs = start_time ? isoToTimestamp(start_time) : Date.now();
|
|
284
|
+
const requestBody = {
|
|
285
|
+
tid: task_id,
|
|
286
|
+
start: startTimeMs,
|
|
287
|
+
duration: durationMs,
|
|
288
|
+
...(description && { description })
|
|
289
|
+
};
|
|
290
|
+
const response = await fetch(`https://api.clickup.com/api/v2/team/${config_1.CONFIG.teamId}/time_entries`, {
|
|
291
|
+
method: 'POST',
|
|
292
|
+
headers: {
|
|
293
|
+
Authorization: config_1.CONFIG.apiKey,
|
|
294
|
+
'Content-Type': 'application/json'
|
|
295
|
+
},
|
|
296
|
+
body: JSON.stringify(requestBody)
|
|
297
|
+
});
|
|
298
|
+
if (!response.ok) {
|
|
299
|
+
const errorData = await response.json().catch(() => ({}));
|
|
300
|
+
throw new Error(`Error creating time entry: ${response.status} ${response.statusText} - ${JSON.stringify(errorData)}`);
|
|
301
|
+
}
|
|
302
|
+
const timeEntry = await response.json();
|
|
303
|
+
// Format duration for display
|
|
304
|
+
const displayHours = Math.floor(hours);
|
|
305
|
+
const displayMinutes = Math.round((hours - displayHours) * 60);
|
|
306
|
+
const durationDisplay = displayHours > 0 ?
|
|
307
|
+
`${displayHours}h ${displayMinutes}m` :
|
|
308
|
+
`${displayMinutes}m`;
|
|
309
|
+
return {
|
|
310
|
+
content: [
|
|
311
|
+
{
|
|
312
|
+
type: "text",
|
|
313
|
+
text: [
|
|
314
|
+
`Time entry created successfully!`,
|
|
315
|
+
`entry_id: ${timeEntry.data?.id || 'N/A'}`,
|
|
316
|
+
`task_id: ${task_id}`,
|
|
317
|
+
`duration: ${durationDisplay}`,
|
|
318
|
+
`start_time: ${timestampToIso(startTimeMs)}`,
|
|
319
|
+
...(description ? [`description: ${description}`] : []),
|
|
320
|
+
`user: ${timeEntry.data?.user?.username || 'Current user'}`
|
|
321
|
+
].join('\n')
|
|
322
|
+
}
|
|
323
|
+
],
|
|
324
|
+
};
|
|
325
|
+
}
|
|
326
|
+
catch (error) {
|
|
327
|
+
console.error('Error creating time entry:', error);
|
|
328
|
+
return {
|
|
329
|
+
content: [
|
|
330
|
+
{
|
|
331
|
+
type: "text",
|
|
332
|
+
text: `Error creating time entry: ${error instanceof Error ? error.message : 'Unknown error'}`,
|
|
333
|
+
},
|
|
334
|
+
],
|
|
335
|
+
};
|
|
336
|
+
}
|
|
337
|
+
});
|
|
338
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@pripanggalih/clickup-mcp",
|
|
3
|
+
"version": "1.6.1",
|
|
4
|
+
"description": "Personal ClickUp MCP server for task context, documents, time tracking, and workspace administration.",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"bin": {
|
|
8
|
+
"clickup-mcp": "dist/index.js"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"dist",
|
|
12
|
+
"README.md"
|
|
13
|
+
],
|
|
14
|
+
"scripts": {
|
|
15
|
+
"build": "tsc",
|
|
16
|
+
"start": "node dist/index.js",
|
|
17
|
+
"dev": "npx tsc -w & nodemon dist/index.js",
|
|
18
|
+
"cli": "npx ts-node src/cli.ts",
|
|
19
|
+
"prettier": "prettier --write src/**/*.ts",
|
|
20
|
+
"prepublishOnly": "rm -r dist && npm run build",
|
|
21
|
+
"release": "npm run build && npm publish --access public && git add . && git commit -m \"Release v$(node -p 'require(\"./package.json\").version')\" && git tag -a v$(node -p 'require(\"./package.json\").version') -m \"Release v$(node -p 'require(\"./package.json\").version')\" && git push && git push --tags",
|
|
22
|
+
"mcpb": "npm run build && mcpb pack . ClickUp.mcpb",
|
|
23
|
+
"test": "node --test -r ts-node/register src/**/*.test.ts"
|
|
24
|
+
},
|
|
25
|
+
"keywords": [
|
|
26
|
+
"clickup",
|
|
27
|
+
"mcp",
|
|
28
|
+
"model-context-protocol",
|
|
29
|
+
"llm",
|
|
30
|
+
"ai",
|
|
31
|
+
"ticket",
|
|
32
|
+
"time-tracking",
|
|
33
|
+
"productivity",
|
|
34
|
+
"project-management",
|
|
35
|
+
"task-management",
|
|
36
|
+
"agentic-coding",
|
|
37
|
+
"automation"
|
|
38
|
+
],
|
|
39
|
+
"author": {
|
|
40
|
+
"name": "MW Pripanggalih",
|
|
41
|
+
"email": "galihcrew07@gmail.com"
|
|
42
|
+
},
|
|
43
|
+
"license": "MIT",
|
|
44
|
+
"dependencies": {
|
|
45
|
+
"@modelcontextprotocol/sdk": "1.15.1",
|
|
46
|
+
"fuse.js": "^7.1.0",
|
|
47
|
+
"remark-gfm": "^4.0.1",
|
|
48
|
+
"remark-parse": "^11.0.0",
|
|
49
|
+
"unified": "^11.0.5",
|
|
50
|
+
"zod": "^3.24.2"
|
|
51
|
+
},
|
|
52
|
+
"devDependencies": {
|
|
53
|
+
"@anthropic-ai/mcpb": "^1.1.1",
|
|
54
|
+
"@types/mdast": "^4.0.4",
|
|
55
|
+
"@types/node": "^22.14.1",
|
|
56
|
+
"dotenv": "^16.5.0",
|
|
57
|
+
"nodemon": "^3.1.9",
|
|
58
|
+
"prettier": "^3.5.3",
|
|
59
|
+
"ts-node": "^10.9.2",
|
|
60
|
+
"typescript": "^5.8.3",
|
|
61
|
+
"undici": "^7.16.0"
|
|
62
|
+
},
|
|
63
|
+
"engines": {
|
|
64
|
+
"node": ">=16.0.0"
|
|
65
|
+
},
|
|
66
|
+
"repository": {
|
|
67
|
+
"type": "git",
|
|
68
|
+
"url": "git+https://github.com/pripanggalih/clickup-mcp.git"
|
|
69
|
+
},
|
|
70
|
+
"bugs": {
|
|
71
|
+
"url": "https://github.com/pripanggalih/clickup-mcp/issues"
|
|
72
|
+
},
|
|
73
|
+
"homepage": "https://github.com/pripanggalih/clickup-mcp#readme"
|
|
74
|
+
}
|