@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.
Files changed (3) hide show
  1. package/README.md +39 -17
  2. package/build/index.js +149 -0
  3. 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 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
 
25
34
  ```bash
26
- npx @projora/mcp-server
35
+ npm install -g @projora/mcp-server
27
36
  ```
28
37
 
29
- Or install globally:
38
+ Or run directly with npx (no installation needed):
30
39
 
31
40
  ```bash
32
- npm install -g @projora/mcp-server
41
+ npx projora-mcp-server setup
33
42
  ```
34
43
 
35
44
  ## Quick Setup
36
45
 
37
- Run this single command to automatically configure Claude Code or OpenCode:
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 @projora/mcp-server setup
63
+ npx projora-mcp-server setup
41
64
  ```
42
65
 
43
- The setup wizard will:
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.2",
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": "./build/index.js",
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",