@projora/mcp-server 1.3.2 → 1.5.0
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 +39 -17
- package/build/index.js +149 -0
- package/package.json +4 -2
package/README.md
CHANGED
|
@@ -4,46 +4,66 @@ A Model Context Protocol (MCP) server for [Projora](https://projora.app) task ma
|
|
|
4
4
|
|
|
5
5
|
## Features
|
|
6
6
|
|
|
7
|
+
### Task Management
|
|
8
|
+
- **create_task** - Create a new task in a project with optional fields (priority, assignee, due date, etc.)
|
|
7
9
|
- **start_task** - Start working on a task (sets status to "In Progress", adds comment, returns full context)
|
|
8
|
-
- **complete_task** - Mark a task as done with
|
|
9
|
-
- **get_task** - Get
|
|
10
|
-
- **
|
|
10
|
+
- **complete_task** - Mark a task as done with optional summary and automatic time logging
|
|
11
|
+
- **get_task** - Get detailed task information including comments, sub-tasks, and dependencies
|
|
12
|
+
- **update_task** - Update task fields (title, description, priority, assignee, due date, estimate)
|
|
13
|
+
- **update_task_status** - Update task status
|
|
14
|
+
- **link_branch** - Link a git branch or pull request to a task
|
|
15
|
+
|
|
16
|
+
### Time Tracking
|
|
17
|
+
- **log_time** - Log time worked on a task with optional description
|
|
18
|
+
- **add_comment** - Add a comment to a task
|
|
19
|
+
|
|
20
|
+
### Queries & Search
|
|
21
|
+
- **list_projects** - List all projects with task counts
|
|
11
22
|
- **list_tasks** - List tasks with optional filters (project, status, priority, assignee)
|
|
12
23
|
- **my_tasks** - Get tasks assigned to you
|
|
13
24
|
- **overdue_tasks** - Get all overdue tasks
|
|
14
25
|
- **search_tasks** - Search for tasks and projects by keyword
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
- **add_comment** - Add a comment to a task
|
|
18
|
-
- **log_time** - Log time worked on a task
|
|
26
|
+
|
|
27
|
+
### Configuration & Stats
|
|
19
28
|
- **get_project** - Get project details including linked GitHub repository
|
|
20
29
|
- **get_config** - Get available statuses, priorities, and task types
|
|
21
|
-
- **dashboard_stats** - Get dashboard statistics
|
|
30
|
+
- **dashboard_stats** - Get dashboard statistics overview
|
|
22
31
|
|
|
23
32
|
## Installation
|
|
24
33
|
|
|
25
34
|
```bash
|
|
26
|
-
|
|
35
|
+
npm install -g @projora/mcp-server
|
|
27
36
|
```
|
|
28
37
|
|
|
29
|
-
Or
|
|
38
|
+
Or run directly with npx (no installation needed):
|
|
30
39
|
|
|
31
40
|
```bash
|
|
32
|
-
|
|
41
|
+
npx projora-mcp-server setup
|
|
33
42
|
```
|
|
34
43
|
|
|
35
44
|
## Quick Setup
|
|
36
45
|
|
|
37
|
-
|
|
46
|
+
### For Claude Code (Recommended)
|
|
47
|
+
|
|
48
|
+
Run this single command (replace `YOUR_TOKEN` with your API token):
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
claude mcp add projora \
|
|
52
|
+
--env PROJORA_AUTH_TOKEN="YOUR_TOKEN" \
|
|
53
|
+
-- npx -y projora-mcp-server
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Get your API token from [Projora Settings](https://projora.app/settings/integrations).
|
|
57
|
+
|
|
58
|
+
### For OpenCode
|
|
59
|
+
|
|
60
|
+
Run the setup wizard:
|
|
38
61
|
|
|
39
62
|
```bash
|
|
40
|
-
npx
|
|
63
|
+
npx projora-mcp-server setup
|
|
41
64
|
```
|
|
42
65
|
|
|
43
|
-
The
|
|
44
|
-
1. Prompt for your API token (get it from [Projora Settings](https://projora.app/settings/integrations))
|
|
45
|
-
2. Automatically configure your AI coding assistant
|
|
46
|
-
3. No manual editing of config files required!
|
|
66
|
+
The wizard will prompt for your API token and automatically configure OpenCode.
|
|
47
67
|
|
|
48
68
|
### Manual Configuration
|
|
49
69
|
|
|
@@ -97,6 +117,7 @@ Add to your `opencode.json`:
|
|
|
97
117
|
|
|
98
118
|
Once configured, use prompts like:
|
|
99
119
|
|
|
120
|
+
- "Create a task in project PROJ: Fix authentication bug"
|
|
100
121
|
- "Start working on task PRJ-123"
|
|
101
122
|
- "Show me my tasks"
|
|
102
123
|
- "What are the overdue tasks?"
|
|
@@ -104,6 +125,7 @@ Once configured, use prompts like:
|
|
|
104
125
|
- "Mark task PRJ-123 as done with summary: Fixed the login bug"
|
|
105
126
|
- "Add a comment to PRJ-123: Need to review the test coverage"
|
|
106
127
|
- "Log 2 hours on task PRJ-123"
|
|
128
|
+
- "Link branch feature/auth-fix to task PRJ-123"
|
|
107
129
|
|
|
108
130
|
## Workflow Example
|
|
109
131
|
|
package/build/index.js
CHANGED
|
@@ -1119,6 +1119,155 @@ server.registerTool("get_project", {
|
|
|
1119
1119
|
content: [{ type: "text", text: output }],
|
|
1120
1120
|
};
|
|
1121
1121
|
});
|
|
1122
|
+
// 15. Create a new task
|
|
1123
|
+
server.registerTool("create_task", {
|
|
1124
|
+
description: "Create a new task in a project. You can specify the project by key and optionally the task list name, or provide IDs directly. Returns the created task details.",
|
|
1125
|
+
inputSchema: {
|
|
1126
|
+
title: z.string().describe("The task title (required)"),
|
|
1127
|
+
projectKey: z.string().optional().describe("The project key (e.g., 'PRJ'). Either projectKey or taskListId is required."),
|
|
1128
|
+
taskListId: z.string().optional().describe("The task list ID. If provided, projectKey and taskListName are ignored."),
|
|
1129
|
+
taskListName: z.string().optional().describe("Name of the task list to add the task to (e.g., 'To Do', 'Backlog'). If not provided, uses the first task list."),
|
|
1130
|
+
description: z.string().optional().describe("Task description (supports markdown)"),
|
|
1131
|
+
statusId: z.string().optional().describe("Status ID (use get_config to see available statuses)"),
|
|
1132
|
+
priorityId: z.string().optional().describe("Priority ID (use get_config to see available priorities)"),
|
|
1133
|
+
taskTypeId: z.string().optional().describe("Task type ID (use get_config to see available task types)"),
|
|
1134
|
+
assigneeId: z.string().optional().describe("User ID to assign the task to"),
|
|
1135
|
+
dueDate: z.string().optional().describe("Due date in YYYY-MM-DD format"),
|
|
1136
|
+
startDate: z.string().optional().describe("Start date in YYYY-MM-DD format"),
|
|
1137
|
+
estimatedHours: z.number().optional().describe("Estimated hours to complete the task"),
|
|
1138
|
+
},
|
|
1139
|
+
}, async ({ title, projectKey, taskListId, taskListName, description, statusId, priorityId, taskTypeId, assigneeId, dueDate, startDate, estimatedHours }) => {
|
|
1140
|
+
// If taskListId is not provided, we need to look it up from the project
|
|
1141
|
+
let finalTaskListId = taskListId;
|
|
1142
|
+
if (!finalTaskListId && projectKey) {
|
|
1143
|
+
// Get the project's task lists
|
|
1144
|
+
const projectQuery = `
|
|
1145
|
+
query GetProject($projectKey: String!) {
|
|
1146
|
+
projects {
|
|
1147
|
+
id
|
|
1148
|
+
key
|
|
1149
|
+
name
|
|
1150
|
+
taskLists {
|
|
1151
|
+
id
|
|
1152
|
+
name
|
|
1153
|
+
}
|
|
1154
|
+
}
|
|
1155
|
+
}
|
|
1156
|
+
`;
|
|
1157
|
+
const projectData = await graphqlQuery(projectQuery);
|
|
1158
|
+
if (!projectData) {
|
|
1159
|
+
return {
|
|
1160
|
+
content: [{ type: "text", text: "Failed to fetch project. Check your authentication token." }],
|
|
1161
|
+
};
|
|
1162
|
+
}
|
|
1163
|
+
const project = projectData.projects.find(p => p.key.toLowerCase() === projectKey.toLowerCase());
|
|
1164
|
+
if (!project) {
|
|
1165
|
+
return {
|
|
1166
|
+
content: [{ type: "text", text: `Project with key '${projectKey}' not found.` }],
|
|
1167
|
+
};
|
|
1168
|
+
}
|
|
1169
|
+
if (project.taskLists.length === 0) {
|
|
1170
|
+
return {
|
|
1171
|
+
content: [{ type: "text", text: `Project '${projectKey}' has no task lists.` }],
|
|
1172
|
+
};
|
|
1173
|
+
}
|
|
1174
|
+
// Find the task list by name, or use the first one
|
|
1175
|
+
if (taskListName) {
|
|
1176
|
+
const taskList = project.taskLists.find(tl => tl.name.toLowerCase() === taskListName.toLowerCase());
|
|
1177
|
+
if (!taskList) {
|
|
1178
|
+
const availableLists = project.taskLists.map(tl => tl.name).join(', ');
|
|
1179
|
+
return {
|
|
1180
|
+
content: [{ type: "text", text: `Task list '${taskListName}' not found in project '${projectKey}'. Available lists: ${availableLists}` }],
|
|
1181
|
+
};
|
|
1182
|
+
}
|
|
1183
|
+
finalTaskListId = taskList.id;
|
|
1184
|
+
}
|
|
1185
|
+
else {
|
|
1186
|
+
finalTaskListId = project.taskLists[0].id;
|
|
1187
|
+
}
|
|
1188
|
+
}
|
|
1189
|
+
if (!finalTaskListId) {
|
|
1190
|
+
return {
|
|
1191
|
+
content: [{ type: "text", text: "Either taskListId or projectKey must be provided." }],
|
|
1192
|
+
};
|
|
1193
|
+
}
|
|
1194
|
+
// Create the task
|
|
1195
|
+
const input = {
|
|
1196
|
+
taskListId: finalTaskListId,
|
|
1197
|
+
title,
|
|
1198
|
+
};
|
|
1199
|
+
if (description !== undefined)
|
|
1200
|
+
input.description = description;
|
|
1201
|
+
if (statusId !== undefined)
|
|
1202
|
+
input.statusId = statusId;
|
|
1203
|
+
if (priorityId !== undefined)
|
|
1204
|
+
input.priorityId = priorityId;
|
|
1205
|
+
if (taskTypeId !== undefined)
|
|
1206
|
+
input.taskTypeId = taskTypeId;
|
|
1207
|
+
if (assigneeId !== undefined)
|
|
1208
|
+
input.assigneeId = assigneeId;
|
|
1209
|
+
if (dueDate !== undefined)
|
|
1210
|
+
input.dueDate = dueDate;
|
|
1211
|
+
if (startDate !== undefined)
|
|
1212
|
+
input.startDate = startDate;
|
|
1213
|
+
if (estimatedHours !== undefined)
|
|
1214
|
+
input.estimatedHours = estimatedHours;
|
|
1215
|
+
const mutation = `
|
|
1216
|
+
mutation CreateTask($input: CreateTaskInput!) {
|
|
1217
|
+
createTask(input: $input) {
|
|
1218
|
+
id
|
|
1219
|
+
taskKey
|
|
1220
|
+
title
|
|
1221
|
+
description
|
|
1222
|
+
dueDate
|
|
1223
|
+
startDate
|
|
1224
|
+
estimatedHours
|
|
1225
|
+
createdAt
|
|
1226
|
+
status { id name }
|
|
1227
|
+
priority { id name }
|
|
1228
|
+
taskType { id name }
|
|
1229
|
+
assignee { id name }
|
|
1230
|
+
project { id name key }
|
|
1231
|
+
taskList { id name }
|
|
1232
|
+
}
|
|
1233
|
+
}
|
|
1234
|
+
`;
|
|
1235
|
+
const result = await graphqlQuery(mutation, { input });
|
|
1236
|
+
if (!result) {
|
|
1237
|
+
return {
|
|
1238
|
+
content: [{ type: "text", text: "Failed to create task. Check your permissions and input data." }],
|
|
1239
|
+
};
|
|
1240
|
+
}
|
|
1241
|
+
const task = result.createTask;
|
|
1242
|
+
let output = `# Task Created: ${task.taskKey}\n\n`;
|
|
1243
|
+
output += `**${task.title}**\n\n`;
|
|
1244
|
+
output += `- **Project**: ${task.project.name} (${task.project.key})\n`;
|
|
1245
|
+
output += `- **List**: ${task.taskList.name}\n`;
|
|
1246
|
+
output += `- **Status**: ${task.status?.name || 'No status'}\n`;
|
|
1247
|
+
if (task.priority) {
|
|
1248
|
+
output += `- **Priority**: ${task.priority.name}\n`;
|
|
1249
|
+
}
|
|
1250
|
+
if (task.taskType) {
|
|
1251
|
+
output += `- **Type**: ${task.taskType.name}\n`;
|
|
1252
|
+
}
|
|
1253
|
+
if (task.assignee) {
|
|
1254
|
+
output += `- **Assignee**: ${task.assignee.name}\n`;
|
|
1255
|
+
}
|
|
1256
|
+
if (task.dueDate) {
|
|
1257
|
+
output += `- **Due Date**: ${task.dueDate}\n`;
|
|
1258
|
+
}
|
|
1259
|
+
if (task.estimatedHours) {
|
|
1260
|
+
output += `- **Estimate**: ${task.estimatedHours} hours\n`;
|
|
1261
|
+
}
|
|
1262
|
+
if (task.description) {
|
|
1263
|
+
output += `\n## Description\n${task.description}\n`;
|
|
1264
|
+
}
|
|
1265
|
+
output += `\n---\nCreated at: ${task.createdAt}\n\n`;
|
|
1266
|
+
output += `You can now use \`start_task\` with taskKey "${task.taskKey}" to begin working on it.`;
|
|
1267
|
+
return {
|
|
1268
|
+
content: [{ type: "text", text: output }],
|
|
1269
|
+
};
|
|
1270
|
+
});
|
|
1122
1271
|
// Main function to run the server
|
|
1123
1272
|
async function main() {
|
|
1124
1273
|
// Check if "setup" command was passed
|
package/package.json
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@projora/mcp-server",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.5.0",
|
|
4
4
|
"description": "MCP server for Projora task management - integrate with Claude Code and OpenCode to manage tasks, update statuses, and track work",
|
|
5
5
|
"type": "module",
|
|
6
|
-
"bin":
|
|
6
|
+
"bin": {
|
|
7
|
+
"projora-mcp-server": "./build/index.js"
|
|
8
|
+
},
|
|
7
9
|
"main": "./build/index.js",
|
|
8
10
|
"scripts": {
|
|
9
11
|
"build": "tsc && chmod 755 build/index.js build/setup.js",
|