@taazkareem/clickup-mcp-server 0.1.7
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/README.md +122 -0
- package/build/config.js +28 -0
- package/build/index.js +397 -0
- package/build/services/clickup.js +70 -0
- package/build/types/clickup.js +1 -0
- package/package.json +58 -0
package/README.md
ADDED
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
# ClickUp MCP Server
|
|
2
|
+
|
|
3
|
+
A Model Context Protocol (MCP) server for integrating ClickUp tasks with AI applications. This server allows AI agents to interact with ClickUp tasks, spaces, and lists through a standardized protocol.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- 🔄 List and read ClickUp tasks as resources
|
|
8
|
+
- ✨ Create and update tasks through tools
|
|
9
|
+
- 📊 Get spaces and lists with their IDs
|
|
10
|
+
- 📝 Generate task descriptions with AI
|
|
11
|
+
- 📋 Summarize tasks and analyze priorities
|
|
12
|
+
- 🔒 Secure API key management
|
|
13
|
+
|
|
14
|
+
## Installation
|
|
15
|
+
|
|
16
|
+
### Using npx (Recommended)
|
|
17
|
+
```bash
|
|
18
|
+
npx @taazkareem/clickup-mcp-server
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
### Global Installation
|
|
22
|
+
```bash
|
|
23
|
+
npm install -g @taazkareem/clickup-mcp-server
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Configuration
|
|
27
|
+
|
|
28
|
+
1. Get your ClickUp API key from [ClickUp Settings](https://app.clickup.com/settings/apps)
|
|
29
|
+
2. Create a `.env` file:
|
|
30
|
+
```env
|
|
31
|
+
CLICKUP_API_KEY=your_api_key_here
|
|
32
|
+
TEAM_ID=your_team_id_here
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Usage
|
|
36
|
+
|
|
37
|
+
### Starting the Server
|
|
38
|
+
```bash
|
|
39
|
+
clickup-mcp-server
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### Available Tools
|
|
43
|
+
|
|
44
|
+
1. **list_spaces**
|
|
45
|
+
- Lists all spaces and their lists with IDs
|
|
46
|
+
- No parameters required
|
|
47
|
+
|
|
48
|
+
2. **create_task**
|
|
49
|
+
- Creates a new task in ClickUp
|
|
50
|
+
- Required parameters:
|
|
51
|
+
- `listId`: ID of the list to create the task in
|
|
52
|
+
- `name`: Name of the task
|
|
53
|
+
- Optional parameters:
|
|
54
|
+
- `description`: Task description
|
|
55
|
+
- `status`: Task status
|
|
56
|
+
- `priority`: Priority level (1-4)
|
|
57
|
+
- `dueDate`: Due date (ISO string)
|
|
58
|
+
|
|
59
|
+
3. **update_task**
|
|
60
|
+
- Updates an existing task
|
|
61
|
+
- Required parameters:
|
|
62
|
+
- `taskId`: ID of the task to update
|
|
63
|
+
- Optional parameters:
|
|
64
|
+
- Same as create_task
|
|
65
|
+
|
|
66
|
+
### Available Prompts
|
|
67
|
+
|
|
68
|
+
1. **summarize_tasks**
|
|
69
|
+
- Provides a summary of all tasks
|
|
70
|
+
- Groups by status and highlights priorities
|
|
71
|
+
|
|
72
|
+
2. **analyze_priorities**
|
|
73
|
+
- Analyzes task priorities
|
|
74
|
+
- Suggests optimizations and sequencing
|
|
75
|
+
|
|
76
|
+
3. **generate_description**
|
|
77
|
+
- Helps generate detailed task descriptions
|
|
78
|
+
- Includes objectives, criteria, and dependencies
|
|
79
|
+
|
|
80
|
+
## Using with Cursor AI Composer
|
|
81
|
+
|
|
82
|
+
To add this server to Cursor AI Composer:
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
npx -y @taazkareem/clickup-mcp-server \
|
|
86
|
+
--env CLICKUP_API_KEY=your_api_key_here \
|
|
87
|
+
--env TEAM_ID=your_team_id_here
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
You can get these values from:
|
|
91
|
+
- `CLICKUP_API_KEY`: Get from [ClickUp Settings > Apps](https://app.clickup.com/settings/apps)
|
|
92
|
+
- `TEAM_ID`: Your ClickUp Team ID (found in the URL when viewing your workspace or via API)
|
|
93
|
+
|
|
94
|
+
> ⚠️ **Important**: Make sure to replace `your_api_key_here` and `your_team_id_here` with your actual ClickUp credentials.
|
|
95
|
+
|
|
96
|
+
> **Security Note**: Your API key will be stored securely and will not be exposed to AI models.
|
|
97
|
+
|
|
98
|
+
## Development
|
|
99
|
+
|
|
100
|
+
1. Clone the repository:
|
|
101
|
+
```bash
|
|
102
|
+
git clone https://github.com/yourusername/clickup-mcp-server.git
|
|
103
|
+
cd clickup-mcp-server
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
2. Install dependencies:
|
|
107
|
+
```bash
|
|
108
|
+
npm install
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
3. Start in development mode:
|
|
112
|
+
```bash
|
|
113
|
+
npm run dev
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
## Contributing
|
|
117
|
+
|
|
118
|
+
Contributions are welcome! Please read our [Contributing Guide](CONTRIBUTING.md) for details.
|
|
119
|
+
|
|
120
|
+
## License
|
|
121
|
+
|
|
122
|
+
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
|
package/build/config.js
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import dotenv from 'dotenv';
|
|
2
|
+
// Load environment variables from .env file
|
|
3
|
+
dotenv.config();
|
|
4
|
+
// Parse command line arguments for --env flags
|
|
5
|
+
const args = process.argv.slice(2);
|
|
6
|
+
const envArgs = {};
|
|
7
|
+
for (let i = 0; i < args.length; i++) {
|
|
8
|
+
if (args[i] === '--env' && i + 1 < args.length) {
|
|
9
|
+
const [key, value] = args[i + 1].split('=');
|
|
10
|
+
if (key === 'CLICKUP_API_KEY')
|
|
11
|
+
envArgs.clickupApiKey = value;
|
|
12
|
+
if (key === 'TEAM_ID')
|
|
13
|
+
envArgs.teamId = value;
|
|
14
|
+
i++; // Skip the next argument since we used it
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
const configuration = {
|
|
18
|
+
clickupApiKey: envArgs.clickupApiKey || process.env.CLICKUP_API_KEY || '',
|
|
19
|
+
teamId: envArgs.teamId || process.env.TEAM_ID || '',
|
|
20
|
+
};
|
|
21
|
+
// Check for missing environment variables
|
|
22
|
+
const missingEnvVars = Object.entries(configuration)
|
|
23
|
+
.filter(([_, value]) => !value)
|
|
24
|
+
.map(([key]) => key);
|
|
25
|
+
if (missingEnvVars.length > 0) {
|
|
26
|
+
throw new Error(`Missing required environment variables: ${missingEnvVars.join(', ')}`);
|
|
27
|
+
}
|
|
28
|
+
export default configuration;
|
package/build/index.js
ADDED
|
@@ -0,0 +1,397 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* This is a template MCP server that implements a simple ClickUp task management system.
|
|
4
|
+
* It demonstrates core MCP concepts like resources, tools, and prompts by allowing:
|
|
5
|
+
* - Listing ClickUp tasks as resources
|
|
6
|
+
* - Reading individual ClickUp tasks
|
|
7
|
+
* - Creating new ClickUp tasks via a tool
|
|
8
|
+
* - Updating existing ClickUp tasks via a tool
|
|
9
|
+
* - Summarizing all ClickUp tasks via a prompt
|
|
10
|
+
* - Analyzing task priorities via a prompt
|
|
11
|
+
* - Generating detailed descriptions for tasks via a prompt
|
|
12
|
+
*/
|
|
13
|
+
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
14
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
15
|
+
import { CallToolRequestSchema, ListResourcesRequestSchema, ListToolsRequestSchema, ReadResourceRequestSchema, ListPromptsRequestSchema, GetPromptRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
|
|
16
|
+
import { ClickUpService } from "./services/clickup.js";
|
|
17
|
+
import config from "./config.js";
|
|
18
|
+
/**
|
|
19
|
+
* Simple in-memory storage for notes.
|
|
20
|
+
* In a real implementation, this would likely be backed by a database.
|
|
21
|
+
*/
|
|
22
|
+
const notes = {
|
|
23
|
+
"1": { title: "First Note", content: "This is note 1" },
|
|
24
|
+
"2": { title: "Second Note", content: "This is note 2" }
|
|
25
|
+
};
|
|
26
|
+
// Initialize ClickUp service
|
|
27
|
+
const clickup = ClickUpService.initialize(config.clickupApiKey);
|
|
28
|
+
/**
|
|
29
|
+
* Create an MCP server with capabilities for resources (to list/read ClickUp tasks),
|
|
30
|
+
* tools (to create/update ClickUp tasks), and prompts (to summarize/analyze ClickUp tasks).
|
|
31
|
+
*/
|
|
32
|
+
const server = new Server({
|
|
33
|
+
name: "clickup-mcp-server",
|
|
34
|
+
version: "0.1.0",
|
|
35
|
+
}, {
|
|
36
|
+
capabilities: {
|
|
37
|
+
resources: {},
|
|
38
|
+
tools: {},
|
|
39
|
+
prompts: {},
|
|
40
|
+
},
|
|
41
|
+
});
|
|
42
|
+
/**
|
|
43
|
+
* Handler for listing available ClickUp tasks as resources.
|
|
44
|
+
* Each task is exposed as a resource with:
|
|
45
|
+
* - A clickup:// URI scheme
|
|
46
|
+
* - JSON MIME type
|
|
47
|
+
* - Human readable name and description (including the task name and description)
|
|
48
|
+
*/
|
|
49
|
+
server.setRequestHandler(ListResourcesRequestSchema, async () => {
|
|
50
|
+
try {
|
|
51
|
+
const spaces = await clickup.getSpaces(config.teamId);
|
|
52
|
+
const resources = [];
|
|
53
|
+
for (const space of spaces) {
|
|
54
|
+
const lists = await clickup.getLists(space.id);
|
|
55
|
+
for (const list of lists) {
|
|
56
|
+
const { tasks } = await clickup.getTasks(list.id);
|
|
57
|
+
resources.push(...tasks.map((task) => ({
|
|
58
|
+
uri: `clickup://task/${task.id}`,
|
|
59
|
+
mimeType: "application/json",
|
|
60
|
+
name: task.name,
|
|
61
|
+
description: task.description || `Task in ${list.name} (${space.name})`,
|
|
62
|
+
tags: []
|
|
63
|
+
})));
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return { resources };
|
|
67
|
+
}
|
|
68
|
+
catch (error) {
|
|
69
|
+
console.error('Error listing resources:', error);
|
|
70
|
+
throw error;
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
/**
|
|
74
|
+
* Handler for reading the contents of a specific ClickUp task.
|
|
75
|
+
* Takes a clickup:// URI and returns the task content as JSON.
|
|
76
|
+
*/
|
|
77
|
+
server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
|
|
78
|
+
try {
|
|
79
|
+
const url = new URL(request.params.uri);
|
|
80
|
+
const taskId = url.pathname.replace(/^\/task\//, '');
|
|
81
|
+
const task = await clickup.getTask(taskId);
|
|
82
|
+
return {
|
|
83
|
+
contents: [{
|
|
84
|
+
uri: request.params.uri,
|
|
85
|
+
mimeType: "application/json",
|
|
86
|
+
text: JSON.stringify(task, null, 2),
|
|
87
|
+
tags: []
|
|
88
|
+
}]
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
catch (error) {
|
|
92
|
+
console.error('Error reading resource:', error);
|
|
93
|
+
throw error;
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
/**
|
|
97
|
+
* Handler that lists available tools.
|
|
98
|
+
* Exposes tools for listing spaces, creating tasks, and updating tasks.
|
|
99
|
+
*/
|
|
100
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
101
|
+
return {
|
|
102
|
+
tools: [
|
|
103
|
+
{
|
|
104
|
+
name: "list_spaces",
|
|
105
|
+
description: "List all spaces and their lists with IDs",
|
|
106
|
+
inputSchema: {
|
|
107
|
+
type: "object",
|
|
108
|
+
properties: {},
|
|
109
|
+
required: []
|
|
110
|
+
}
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
name: "create_task",
|
|
114
|
+
description: "Create a new task in ClickUp",
|
|
115
|
+
inputSchema: {
|
|
116
|
+
type: "object",
|
|
117
|
+
properties: {
|
|
118
|
+
listId: {
|
|
119
|
+
type: "string",
|
|
120
|
+
description: "ID of the list to create the task in"
|
|
121
|
+
},
|
|
122
|
+
name: {
|
|
123
|
+
type: "string",
|
|
124
|
+
description: "Name of the task"
|
|
125
|
+
},
|
|
126
|
+
description: {
|
|
127
|
+
type: "string",
|
|
128
|
+
description: "Description of the task"
|
|
129
|
+
},
|
|
130
|
+
status: {
|
|
131
|
+
type: "string",
|
|
132
|
+
description: "Status of the task"
|
|
133
|
+
},
|
|
134
|
+
priority: {
|
|
135
|
+
type: "number",
|
|
136
|
+
description: "Priority of the task (1-4)"
|
|
137
|
+
},
|
|
138
|
+
dueDate: {
|
|
139
|
+
type: "string",
|
|
140
|
+
description: "Due date of the task (ISO string)"
|
|
141
|
+
}
|
|
142
|
+
},
|
|
143
|
+
required: ["listId", "name"]
|
|
144
|
+
}
|
|
145
|
+
},
|
|
146
|
+
{
|
|
147
|
+
name: "update_task",
|
|
148
|
+
description: "Update an existing task in ClickUp",
|
|
149
|
+
inputSchema: {
|
|
150
|
+
type: "object",
|
|
151
|
+
properties: {
|
|
152
|
+
taskId: {
|
|
153
|
+
type: "string",
|
|
154
|
+
description: "ID of the task to update"
|
|
155
|
+
},
|
|
156
|
+
name: {
|
|
157
|
+
type: "string",
|
|
158
|
+
description: "New name of the task"
|
|
159
|
+
},
|
|
160
|
+
description: {
|
|
161
|
+
type: "string",
|
|
162
|
+
description: "New description of the task"
|
|
163
|
+
},
|
|
164
|
+
status: {
|
|
165
|
+
type: "string",
|
|
166
|
+
description: "New status of the task"
|
|
167
|
+
},
|
|
168
|
+
priority: {
|
|
169
|
+
type: "number",
|
|
170
|
+
description: "New priority of the task (1-4)"
|
|
171
|
+
},
|
|
172
|
+
dueDate: {
|
|
173
|
+
type: "string",
|
|
174
|
+
description: "New due date of the task (ISO string)"
|
|
175
|
+
}
|
|
176
|
+
},
|
|
177
|
+
required: ["taskId"]
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
]
|
|
181
|
+
};
|
|
182
|
+
});
|
|
183
|
+
/**
|
|
184
|
+
* Handler for the CallToolRequestSchema.
|
|
185
|
+
* Handles the execution of tools like listing spaces, creating tasks, and updating tasks.
|
|
186
|
+
*/
|
|
187
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
188
|
+
try {
|
|
189
|
+
switch (request.params.name) {
|
|
190
|
+
case "list_spaces": {
|
|
191
|
+
const spaces = await clickup.getSpaces(config.teamId);
|
|
192
|
+
const allLists = await clickup.getAllLists(config.teamId);
|
|
193
|
+
let output = "Available Spaces and Lists:\n\n";
|
|
194
|
+
for (const space of spaces) {
|
|
195
|
+
output += `Space: ${space.name} (ID: ${space.id})\n`;
|
|
196
|
+
const spaceLists = allLists.filter(list => list.space.id === space.id);
|
|
197
|
+
for (const list of spaceLists) {
|
|
198
|
+
const { statuses } = await clickup.getTasks(list.id);
|
|
199
|
+
output += ` └─ List: ${list.name} (ID: ${list.id})\n`;
|
|
200
|
+
output += ` Available Statuses: ${statuses.join(', ')}\n`;
|
|
201
|
+
}
|
|
202
|
+
output += "\n";
|
|
203
|
+
}
|
|
204
|
+
// Add lists without spaces at the end
|
|
205
|
+
const listsWithoutSpace = allLists.filter(list => !list.space);
|
|
206
|
+
if (listsWithoutSpace.length > 0) {
|
|
207
|
+
output += "Lists without assigned spaces:\n";
|
|
208
|
+
for (const list of listsWithoutSpace) {
|
|
209
|
+
const { statuses } = await clickup.getTasks(list.id);
|
|
210
|
+
output += ` └─ List: ${list.name} (ID: ${list.id})\n`;
|
|
211
|
+
output += ` Available Statuses: ${statuses.join(', ')}\n`;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
return {
|
|
215
|
+
content: [{
|
|
216
|
+
type: "text",
|
|
217
|
+
text: output
|
|
218
|
+
}]
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
case "create_task": {
|
|
222
|
+
const args = request.params.arguments;
|
|
223
|
+
if (!args.listId || !args.name) {
|
|
224
|
+
throw new Error("listId and name are required");
|
|
225
|
+
}
|
|
226
|
+
const { listId, ...taskData } = args;
|
|
227
|
+
const task = await clickup.createTask(listId, taskData);
|
|
228
|
+
return {
|
|
229
|
+
content: [{
|
|
230
|
+
type: "text",
|
|
231
|
+
text: `Created task ${task.id}: ${task.name}`
|
|
232
|
+
}]
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
case "update_task": {
|
|
236
|
+
const args = request.params.arguments;
|
|
237
|
+
if (!args.taskId) {
|
|
238
|
+
throw new Error("taskId is required");
|
|
239
|
+
}
|
|
240
|
+
const { taskId, ...updateData } = args;
|
|
241
|
+
const task = await clickup.updateTask(taskId, updateData);
|
|
242
|
+
return {
|
|
243
|
+
content: [{
|
|
244
|
+
type: "text",
|
|
245
|
+
text: `Updated task ${task.id}: ${task.name}`
|
|
246
|
+
}]
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
default:
|
|
250
|
+
throw new Error("Unknown tool");
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
catch (error) {
|
|
254
|
+
console.error('Error handling tool call:', error);
|
|
255
|
+
throw error;
|
|
256
|
+
}
|
|
257
|
+
});
|
|
258
|
+
/**
|
|
259
|
+
* Add handlers for listing and getting prompts.
|
|
260
|
+
* Prompts include summarizing tasks, analyzing priorities, and generating task descriptions.
|
|
261
|
+
*/
|
|
262
|
+
server.setRequestHandler(ListPromptsRequestSchema, async () => {
|
|
263
|
+
return {
|
|
264
|
+
prompts: [
|
|
265
|
+
{
|
|
266
|
+
name: "summarize_tasks",
|
|
267
|
+
description: "Summarize all tasks in a list",
|
|
268
|
+
},
|
|
269
|
+
{
|
|
270
|
+
name: "analyze_priorities",
|
|
271
|
+
description: "Analyze task priorities and suggest optimizations",
|
|
272
|
+
},
|
|
273
|
+
{
|
|
274
|
+
name: "generate_description",
|
|
275
|
+
description: "Generate a detailed description for a task",
|
|
276
|
+
}
|
|
277
|
+
]
|
|
278
|
+
};
|
|
279
|
+
});
|
|
280
|
+
server.setRequestHandler(GetPromptRequestSchema, async (request) => {
|
|
281
|
+
try {
|
|
282
|
+
switch (request.params.name) {
|
|
283
|
+
case "summarize_tasks": {
|
|
284
|
+
const spaces = await clickup.getSpaces(config.teamId);
|
|
285
|
+
const tasks = [];
|
|
286
|
+
// Gather all tasks
|
|
287
|
+
for (const space of spaces) {
|
|
288
|
+
const lists = await clickup.getLists(space.id);
|
|
289
|
+
for (const list of lists) {
|
|
290
|
+
const { tasks: listTasks } = await clickup.getTasks(list.id);
|
|
291
|
+
tasks.push(...listTasks.map((task) => ({
|
|
292
|
+
type: "resource",
|
|
293
|
+
resource: {
|
|
294
|
+
uri: `clickup://task/${task.id}`,
|
|
295
|
+
mimeType: "application/json",
|
|
296
|
+
text: JSON.stringify(task, null, 2)
|
|
297
|
+
}
|
|
298
|
+
})));
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
return {
|
|
302
|
+
messages: [
|
|
303
|
+
{
|
|
304
|
+
role: "user",
|
|
305
|
+
content: {
|
|
306
|
+
type: "text",
|
|
307
|
+
text: "Please provide a summary of the following ClickUp tasks:"
|
|
308
|
+
}
|
|
309
|
+
},
|
|
310
|
+
...tasks.map(task => ({
|
|
311
|
+
role: "user",
|
|
312
|
+
content: task
|
|
313
|
+
})),
|
|
314
|
+
{
|
|
315
|
+
role: "user",
|
|
316
|
+
content: {
|
|
317
|
+
type: "text",
|
|
318
|
+
text: "Please provide:\n1. A high-level overview of all tasks\n2. Group them by status\n3. Highlight any urgent or high-priority items\n4. Suggest any task dependencies or relationships"
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
]
|
|
322
|
+
};
|
|
323
|
+
}
|
|
324
|
+
case "analyze_priorities": {
|
|
325
|
+
const spaces = await clickup.getSpaces(config.teamId);
|
|
326
|
+
const tasks = [];
|
|
327
|
+
for (const space of spaces) {
|
|
328
|
+
const lists = await clickup.getLists(space.id);
|
|
329
|
+
for (const list of lists) {
|
|
330
|
+
const { tasks: listTasks } = await clickup.getTasks(list.id);
|
|
331
|
+
tasks.push(...listTasks.map((task) => ({
|
|
332
|
+
type: "resource",
|
|
333
|
+
resource: {
|
|
334
|
+
uri: `clickup://task/${task.id}`,
|
|
335
|
+
mimeType: "application/json",
|
|
336
|
+
text: JSON.stringify(task, null, 2)
|
|
337
|
+
}
|
|
338
|
+
})));
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
return {
|
|
342
|
+
messages: [
|
|
343
|
+
{
|
|
344
|
+
role: "user",
|
|
345
|
+
content: {
|
|
346
|
+
type: "text",
|
|
347
|
+
text: "Please analyze the priorities of the following ClickUp tasks:"
|
|
348
|
+
}
|
|
349
|
+
},
|
|
350
|
+
...tasks.map(task => ({
|
|
351
|
+
role: "user",
|
|
352
|
+
content: task
|
|
353
|
+
})),
|
|
354
|
+
{
|
|
355
|
+
role: "user",
|
|
356
|
+
content: {
|
|
357
|
+
type: "text",
|
|
358
|
+
text: "Please provide:\n1. Analysis of current priority distribution\n2. Identify any misaligned priorities\n3. Suggest priority adjustments\n4. Recommend task sequencing based on priorities"
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
]
|
|
362
|
+
};
|
|
363
|
+
}
|
|
364
|
+
case "generate_description": {
|
|
365
|
+
return {
|
|
366
|
+
messages: [
|
|
367
|
+
{
|
|
368
|
+
role: "user",
|
|
369
|
+
content: {
|
|
370
|
+
type: "text",
|
|
371
|
+
text: "Please help me generate a detailed description for a ClickUp task. The description should include:\n1. Clear objective\n2. Success criteria\n3. Required resources\n4. Dependencies\n5. Potential risks\n\nPlease ask me about the task details."
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
]
|
|
375
|
+
};
|
|
376
|
+
}
|
|
377
|
+
default:
|
|
378
|
+
throw new Error("Unknown prompt");
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
catch (error) {
|
|
382
|
+
console.error('Error handling prompt:', error);
|
|
383
|
+
throw error;
|
|
384
|
+
}
|
|
385
|
+
});
|
|
386
|
+
/**
|
|
387
|
+
* Start the server using stdio transport.
|
|
388
|
+
* This allows the server to communicate via standard input/output streams.
|
|
389
|
+
*/
|
|
390
|
+
async function main() {
|
|
391
|
+
const transport = new StdioServerTransport();
|
|
392
|
+
await server.connect(transport);
|
|
393
|
+
}
|
|
394
|
+
main().catch((error) => {
|
|
395
|
+
console.error("Server error:", error);
|
|
396
|
+
process.exit(1);
|
|
397
|
+
});
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import axios from 'axios';
|
|
2
|
+
export class ClickUpService {
|
|
3
|
+
client;
|
|
4
|
+
static instance;
|
|
5
|
+
constructor(apiKey) {
|
|
6
|
+
this.client = axios.create({
|
|
7
|
+
baseURL: 'https://api.clickup.com/api/v2',
|
|
8
|
+
headers: {
|
|
9
|
+
'Authorization': apiKey,
|
|
10
|
+
'Content-Type': 'application/json'
|
|
11
|
+
}
|
|
12
|
+
});
|
|
13
|
+
}
|
|
14
|
+
static initialize(apiKey) {
|
|
15
|
+
if (!ClickUpService.instance) {
|
|
16
|
+
ClickUpService.instance = new ClickUpService(apiKey);
|
|
17
|
+
}
|
|
18
|
+
return ClickUpService.instance;
|
|
19
|
+
}
|
|
20
|
+
static getInstance() {
|
|
21
|
+
if (!ClickUpService.instance) {
|
|
22
|
+
throw new Error('ClickUpService not initialized. Call initialize() first.');
|
|
23
|
+
}
|
|
24
|
+
return ClickUpService.instance;
|
|
25
|
+
}
|
|
26
|
+
// Tasks
|
|
27
|
+
async getTasks(listId) {
|
|
28
|
+
const response = await this.client.get(`/list/${listId}/task`);
|
|
29
|
+
const tasks = response.data.tasks;
|
|
30
|
+
const statuses = [...new Set(tasks.map((task) => task.status.status))];
|
|
31
|
+
return { tasks, statuses };
|
|
32
|
+
}
|
|
33
|
+
async getTask(taskId) {
|
|
34
|
+
const response = await this.client.get(`/task/${taskId}`);
|
|
35
|
+
return response.data;
|
|
36
|
+
}
|
|
37
|
+
async createTask(listId, data) {
|
|
38
|
+
const response = await this.client.post(`/list/${listId}/task`, data);
|
|
39
|
+
return response.data;
|
|
40
|
+
}
|
|
41
|
+
async updateTask(taskId, data) {
|
|
42
|
+
const response = await this.client.put(`/task/${taskId}`, data);
|
|
43
|
+
return response.data;
|
|
44
|
+
}
|
|
45
|
+
async deleteTask(taskId) {
|
|
46
|
+
await this.client.delete(`/task/${taskId}`);
|
|
47
|
+
}
|
|
48
|
+
// Lists
|
|
49
|
+
async getLists(spaceId) {
|
|
50
|
+
const response = await this.client.get(`/space/${spaceId}/list`);
|
|
51
|
+
return response.data.lists;
|
|
52
|
+
}
|
|
53
|
+
async getAllLists(teamId) {
|
|
54
|
+
const response = await this.client.get(`/team/${teamId}/list`);
|
|
55
|
+
return response.data.lists;
|
|
56
|
+
}
|
|
57
|
+
async getList(listId) {
|
|
58
|
+
const response = await this.client.get(`/list/${listId}`);
|
|
59
|
+
return response.data;
|
|
60
|
+
}
|
|
61
|
+
// Spaces
|
|
62
|
+
async getSpaces(teamId) {
|
|
63
|
+
const response = await this.client.get(`/team/${teamId}/space`);
|
|
64
|
+
return response.data.spaces;
|
|
65
|
+
}
|
|
66
|
+
async getSpace(spaceId) {
|
|
67
|
+
const response = await this.client.get(`/space/${spaceId}`);
|
|
68
|
+
return response.data;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/package.json
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@taazkareem/clickup-mcp-server",
|
|
3
|
+
"version": "0.1.7",
|
|
4
|
+
"description": "ClickUp MCP Server - Integrate ClickUp tasks with AI through Model Context Protocol",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "build/index.js",
|
|
7
|
+
"bin": {
|
|
8
|
+
"clickup-mcp-server": "build/index.js"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"build",
|
|
12
|
+
"README.md",
|
|
13
|
+
"LICENSE"
|
|
14
|
+
],
|
|
15
|
+
"scripts": {
|
|
16
|
+
"build": "tsc && node -e \"require('fs').chmodSync('build/index.js', '755')\"",
|
|
17
|
+
"start": "node build/index.js",
|
|
18
|
+
"dev": "tsc -w",
|
|
19
|
+
"prepare": "npm run build",
|
|
20
|
+
"prepublishOnly": "npm test",
|
|
21
|
+
"test": "echo \"No tests specified\" && exit 0"
|
|
22
|
+
},
|
|
23
|
+
"keywords": [
|
|
24
|
+
"clickup",
|
|
25
|
+
"mcp",
|
|
26
|
+
"ai",
|
|
27
|
+
"tasks",
|
|
28
|
+
"project-management",
|
|
29
|
+
"model-context-protocol",
|
|
30
|
+
"clickup-server",
|
|
31
|
+
"clickup-mcp-server"
|
|
32
|
+
],
|
|
33
|
+
"author": "Talib Kareem",
|
|
34
|
+
"license": "MIT",
|
|
35
|
+
"repository": {
|
|
36
|
+
"type": "git",
|
|
37
|
+
"url": "https://github.com/taazkareem/clickup-mcp-server.git"
|
|
38
|
+
},
|
|
39
|
+
"bugs": {
|
|
40
|
+
"url": "https://github.com/taazkareem/clickup-mcp-server/issues"
|
|
41
|
+
},
|
|
42
|
+
"homepage": "https://github.com/taazkareem/clickup-mcp-server#readme",
|
|
43
|
+
"dependencies": {
|
|
44
|
+
"@modelcontextprotocol/sdk": "0.6.0",
|
|
45
|
+
"axios": "^1.6.7",
|
|
46
|
+
"dotenv": "^16.4.1"
|
|
47
|
+
},
|
|
48
|
+
"devDependencies": {
|
|
49
|
+
"@types/node": "^20.11.16",
|
|
50
|
+
"typescript": "^5.3.3"
|
|
51
|
+
},
|
|
52
|
+
"engines": {
|
|
53
|
+
"node": ">=18.0.0"
|
|
54
|
+
},
|
|
55
|
+
"publishConfig": {
|
|
56
|
+
"access": "public"
|
|
57
|
+
}
|
|
58
|
+
}
|