@projora/mcp-server 1.3.3 → 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.
Files changed (3) hide show
  1. package/README.md +20 -9
  2. package/build/index.js +149 -0
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -4,21 +4,30 @@ 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 an optional summary
9
- - **get_task** - Get a specific task by its key (e.g., "PRJ-123")
10
- - **list_projects** - List all projects
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
- - **update_task** - Update task fields (title, description, priority, assignee, due date)
16
- - **update_task_status** - Update task status
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
 
@@ -40,7 +49,7 @@ Run this single command (replace `YOUR_TOKEN` with your API token):
40
49
 
41
50
  ```bash
42
51
  claude mcp add projora \
43
- --env PROJORA_AUTH_TOKEN=YOUR_TOKEN \
52
+ --env PROJORA_AUTH_TOKEN="YOUR_TOKEN" \
44
53
  -- npx -y projora-mcp-server
45
54
  ```
46
55
 
@@ -108,6 +117,7 @@ Add to your `opencode.json`:
108
117
 
109
118
  Once configured, use prompts like:
110
119
 
120
+ - "Create a task in project PROJ: Fix authentication bug"
111
121
  - "Start working on task PRJ-123"
112
122
  - "Show me my tasks"
113
123
  - "What are the overdue tasks?"
@@ -115,6 +125,7 @@ Once configured, use prompts like:
115
125
  - "Mark task PRJ-123 as done with summary: Fixed the login bug"
116
126
  - "Add a comment to PRJ-123: Need to review the test coverage"
117
127
  - "Log 2 hours on task PRJ-123"
128
+ - "Link branch feature/auth-fix to task PRJ-123"
118
129
 
119
130
  ## Workflow Example
120
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,6 +1,6 @@
1
1
  {
2
2
  "name": "@projora/mcp-server",
3
- "version": "1.3.3",
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
6
  "bin": {