@projora/mcp-server 1.1.0 → 1.2.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 +21 -6
  2. package/build/index.js +161 -1
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -4,18 +4,27 @@ 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 (specify project key, title, and optional details)
7
9
  - **start_task** - Start working on a task (sets status to "In Progress", adds comment, returns full context)
8
10
  - **complete_task** - Mark a task as done with an optional summary
9
11
  - **get_task** - Get a specific task by its key (e.g., "PRJ-123")
10
- - **list_projects** - List all projects
12
+ - **update_task** - Update task fields (title, description, priority, assignee, due date)
13
+ - **update_task_status** - Update task status
14
+
15
+ ### Task Lists
11
16
  - **list_tasks** - List tasks with optional filters (project, status, priority, assignee)
12
17
  - **my_tasks** - Get tasks assigned to you
13
18
  - **overdue_tasks** - Get all overdue tasks
14
19
  - **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
20
+
21
+ ### Collaboration
17
22
  - **add_comment** - Add a comment to a task
18
23
  - **log_time** - Log time worked on a task
24
+ - **link_branch** - Link a git branch or pull request to a task
25
+
26
+ ### Projects & Config
27
+ - **list_projects** - List all projects
19
28
  - **get_project** - Get project details including linked GitHub repository
20
29
  - **get_config** - Get available statuses, priorities, and task types
21
30
  - **dashboard_stats** - Get dashboard statistics
@@ -82,6 +91,8 @@ Add to your OpenCode configuration (`opencode.json`):
82
91
 
83
92
  Once configured, use prompts like:
84
93
 
94
+ - "Create a task in project PRJ: Fix the login button"
95
+ - "Create a task in PRJ with title 'Add unit tests' and assign it to me"
85
96
  - "Start working on task PRJ-123"
86
97
  - "Show me my tasks"
87
98
  - "What are the overdue tasks?"
@@ -92,14 +103,18 @@ Once configured, use prompts like:
92
103
 
93
104
  ## Workflow Example
94
105
 
95
- 1. **Start a task**: "Use projora to start working on task PRJ-123"
106
+ 1. **Create a task**: "Use projora to create a task in project PRJ: Implement user authentication"
107
+ - Task is created in the specified project
108
+ - Returns the task key (e.g., PRJ-123) for reference
109
+
110
+ 2. **Start the task**: "Use projora to start working on task PRJ-123"
96
111
  - Status automatically changes to "In Progress"
97
112
  - A comment is added noting work has started
98
113
  - You get the full task context including description, GitHub repo, and suggested branch name
99
114
 
100
- 2. **Work on the task**: Make your changes, commit code, etc.
115
+ 3. **Work on the task**: Make your changes, commit code, etc.
101
116
 
102
- 3. **Complete the task**: "Use projora to complete task PRJ-123 with summary: Implemented the new feature with tests"
117
+ 4. **Complete the task**: "Use projora to complete task PRJ-123 with summary: Implemented the new feature with tests"
103
118
  - Status changes to "Done"
104
119
  - A completion comment is added with your summary
105
120
 
package/build/index.js CHANGED
@@ -1066,7 +1066,167 @@ server.registerTool("update_task", {
1066
1066
  content: [{ type: "text", text: `Updated ${result.updateTask.taskKey}: ${updatedFields}` }],
1067
1067
  };
1068
1068
  });
1069
- // 14. Get project with GitHub info
1069
+ // 14. Create a new task
1070
+ server.registerTool("create_task", {
1071
+ 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.",
1072
+ inputSchema: {
1073
+ projectKey: z.string().optional().describe("The project key (e.g., 'PRJ'). Either projectKey or taskListId is required."),
1074
+ 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."),
1075
+ taskListId: z.string().optional().describe("The task list ID. If provided, projectKey and taskListName are ignored."),
1076
+ title: z.string().describe("The task title (required)"),
1077
+ description: z.string().optional().describe("Task description (supports markdown)"),
1078
+ statusId: z.string().optional().describe("Status ID (use get_config to see available statuses)"),
1079
+ priorityId: z.string().optional().describe("Priority ID (use get_config to see available priorities)"),
1080
+ taskTypeId: z.string().optional().describe("Task type ID (use get_config to see available task types)"),
1081
+ assigneeId: z.string().optional().describe("User ID to assign the task to"),
1082
+ dueDate: z.string().optional().describe("Due date in YYYY-MM-DD format"),
1083
+ startDate: z.string().optional().describe("Start date in YYYY-MM-DD format"),
1084
+ estimatedHours: z.number().optional().describe("Estimated hours to complete the task"),
1085
+ },
1086
+ }, async ({ projectKey, taskListName, taskListId, title, description, statusId, priorityId, taskTypeId, assigneeId, dueDate, startDate, estimatedHours }) => {
1087
+ let resolvedTaskListId = taskListId;
1088
+ let projectName = "";
1089
+ // If taskListId is not provided, we need to look it up from projectKey
1090
+ if (!resolvedTaskListId) {
1091
+ if (!projectKey) {
1092
+ return {
1093
+ content: [{ type: "text", text: "Either projectKey or taskListId is required to create a task." }],
1094
+ };
1095
+ }
1096
+ // Get project and its task lists
1097
+ const projectQuery = `
1098
+ query GetProjectWithTaskLists {
1099
+ projects {
1100
+ id
1101
+ name
1102
+ key
1103
+ taskLists {
1104
+ id
1105
+ name
1106
+ sortOrder
1107
+ }
1108
+ }
1109
+ }
1110
+ `;
1111
+ const projectData = await graphqlQuery(projectQuery);
1112
+ if (!projectData) {
1113
+ return {
1114
+ content: [{ type: "text", text: "Failed to fetch projects. Check your authentication token." }],
1115
+ };
1116
+ }
1117
+ const project = projectData.projects.find(p => p.key.toLowerCase() === projectKey.toLowerCase());
1118
+ if (!project) {
1119
+ const availableProjects = projectData.projects.map(p => p.key).join(", ");
1120
+ return {
1121
+ content: [{ type: "text", text: `Project '${projectKey}' not found. Available projects: ${availableProjects}` }],
1122
+ };
1123
+ }
1124
+ projectName = project.name;
1125
+ if (project.taskLists.length === 0) {
1126
+ return {
1127
+ content: [{ type: "text", text: `Project '${projectKey}' has no task lists. Please create a task list first.` }],
1128
+ };
1129
+ }
1130
+ // Find the task list by name or use the first one
1131
+ if (taskListName) {
1132
+ const taskList = project.taskLists.find(tl => tl.name.toLowerCase() === taskListName.toLowerCase());
1133
+ if (!taskList) {
1134
+ const availableLists = project.taskLists.map(tl => tl.name).join(", ");
1135
+ return {
1136
+ content: [{ type: "text", text: `Task list '${taskListName}' not found in project '${projectKey}'. Available lists: ${availableLists}` }],
1137
+ };
1138
+ }
1139
+ resolvedTaskListId = taskList.id;
1140
+ }
1141
+ else {
1142
+ // Use the first task list (sorted by sortOrder)
1143
+ const sortedLists = [...project.taskLists].sort((a, b) => a.sortOrder - b.sortOrder);
1144
+ resolvedTaskListId = sortedLists[0].id;
1145
+ }
1146
+ }
1147
+ // Build the input object
1148
+ const input = {
1149
+ taskListId: resolvedTaskListId,
1150
+ title,
1151
+ };
1152
+ if (description !== undefined)
1153
+ input.description = description;
1154
+ if (statusId !== undefined)
1155
+ input.statusId = statusId;
1156
+ if (priorityId !== undefined)
1157
+ input.priorityId = priorityId;
1158
+ if (taskTypeId !== undefined)
1159
+ input.taskTypeId = taskTypeId;
1160
+ if (assigneeId !== undefined)
1161
+ input.assigneeId = assigneeId;
1162
+ if (dueDate !== undefined)
1163
+ input.dueDate = dueDate;
1164
+ if (startDate !== undefined)
1165
+ input.startDate = startDate;
1166
+ if (estimatedHours !== undefined)
1167
+ input.estimatedHours = estimatedHours;
1168
+ // Create the task
1169
+ const mutation = `
1170
+ mutation CreateTask($input: CreateTaskInput!) {
1171
+ createTask(input: $input) {
1172
+ id
1173
+ taskKey
1174
+ title
1175
+ description
1176
+ dueDate
1177
+ startDate
1178
+ estimatedHours
1179
+ status { id name }
1180
+ priority { id name }
1181
+ taskType { id name }
1182
+ assignee { id name }
1183
+ project { id name key }
1184
+ taskList { id name }
1185
+ createdAt
1186
+ }
1187
+ }
1188
+ `;
1189
+ const result = await graphqlQuery(mutation, { input });
1190
+ if (!result || !result.createTask) {
1191
+ return {
1192
+ content: [{ type: "text", text: "Failed to create task. Check your permissions and input values." }],
1193
+ };
1194
+ }
1195
+ const task = result.createTask;
1196
+ let output = `# Task Created: ${task.taskKey}\n\n`;
1197
+ output += `**${task.title}**\n\n`;
1198
+ output += `- **Project**: ${task.project.name} (${task.project.key})\n`;
1199
+ output += `- **List**: ${task.taskList.name}\n`;
1200
+ output += `- **Status**: ${task.status?.name || 'Default'}\n`;
1201
+ if (task.priority) {
1202
+ output += `- **Priority**: ${task.priority.name}\n`;
1203
+ }
1204
+ if (task.taskType) {
1205
+ output += `- **Type**: ${task.taskType.name}\n`;
1206
+ }
1207
+ if (task.assignee) {
1208
+ output += `- **Assignee**: ${task.assignee.name}\n`;
1209
+ }
1210
+ if (task.dueDate) {
1211
+ output += `- **Due Date**: ${task.dueDate}\n`;
1212
+ }
1213
+ if (task.startDate) {
1214
+ output += `- **Start Date**: ${task.startDate}\n`;
1215
+ }
1216
+ if (task.estimatedHours) {
1217
+ output += `- **Estimate**: ${task.estimatedHours} hours\n`;
1218
+ }
1219
+ if (task.description) {
1220
+ output += `\n## Description\n${task.description}\n`;
1221
+ }
1222
+ output += `\n---\n`;
1223
+ output += `Created at: ${task.createdAt}\n`;
1224
+ output += `\nYou can now use \`start_task\` with taskKey "${task.taskKey}" to begin working on it.`;
1225
+ return {
1226
+ content: [{ type: "text", text: output }],
1227
+ };
1228
+ });
1229
+ // 15. Get project with GitHub info
1070
1230
  server.registerTool("get_project", {
1071
1231
  description: "Get project details including linked GitHub repository",
1072
1232
  inputSchema: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@projora/mcp-server",
3
- "version": "1.1.0",
3
+ "version": "1.2.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": {