@damper/mcp 0.1.5 → 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.
Files changed (3) hide show
  1. package/README.md +33 -1
  2. package/dist/index.js +113 -5
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -72,10 +72,12 @@ The AI will see tools from both servers and can distinguish between them:
72
72
  | Tool | Description |
73
73
  |------|-------------|
74
74
  | `list_tasks` | Get roadmap tasks (filter by `status`, `type`) |
75
- | `get_task` | Task details + linked feedback |
75
+ | `get_task` | Task details + subtasks + linked feedback |
76
76
  | `create_task` | Create task with type (bug, feature, improvement, task) |
77
77
  | `start_task` | Lock and start task (use `force` to take over) |
78
78
  | `add_note` | Add progress note |
79
+ | `create_subtask` | Add subtask to a task |
80
+ | `update_subtask` | Mark subtask done/undone |
79
81
  | `complete_task` | Mark done, release lock |
80
82
  | `abandon_task` | Release lock, return to planned |
81
83
  | `list_feedback` | Browse user feedback |
@@ -93,6 +95,34 @@ Filter tasks by type to prioritize work:
93
95
 
94
96
  Types: `bug` 🐛, `feature` ✨, `improvement` 💡, `task` 📌
95
97
 
98
+ ### Task Metadata
99
+
100
+ Tasks include project management fields visible in `list_tasks` and `get_task`:
101
+
102
+ - **Priority**: high 🔴, medium 🟡, low ⚪
103
+ - **Effort**: xs, s, m, l, xl
104
+ - **Labels**: Custom tags
105
+ - **Due date**: Target completion date
106
+
107
+ ### Subtasks
108
+
109
+ Tasks can have subtasks for tracking incremental progress. Use `get_task` to see subtasks with their IDs:
110
+
111
+ ```
112
+ ## Subtasks (2/5)
113
+ - [x] Setup database schema (id: abc123)
114
+ - [ ] Implement API endpoints (id: def456)
115
+ ```
116
+
117
+ Create and update subtasks as you work:
118
+
119
+ ```
120
+ > Add a subtask "Write unit tests" to this task
121
+ > Create subtask for database migrations
122
+ > Mark the database schema subtask as done
123
+ > Update subtask def456 to done
124
+ ```
125
+
96
126
  ### Task Locking
97
127
 
98
128
  When you start a task, it's locked to prevent other agents from working on it simultaneously:
@@ -108,6 +138,8 @@ When you start a task, it's locked to prevent other agents from working on it si
108
138
  > List bugs I can fix
109
139
  > Import my TODO.md into Damper
110
140
  > Work on the dark mode feature
141
+ > Show me the subtasks for this task
142
+ > Mark the first subtask as done
111
143
  > I'm done with the auth task - tests pass
112
144
  > Abandon the current task, I'm blocked
113
145
  ```
package/dist/index.js CHANGED
@@ -34,27 +34,45 @@ async function api(method, path, body) {
34
34
  // Server
35
35
  const server = new McpServer({
36
36
  name: 'damper',
37
- version: '0.1.5',
37
+ version: '0.1.7',
38
38
  });
39
39
  // Output schemas
40
+ const SubtaskProgressSchema = z.object({
41
+ done: z.number(),
42
+ total: z.number(),
43
+ }).nullable();
40
44
  const TaskSummarySchema = z.object({
41
45
  id: z.string(),
42
46
  title: z.string(),
43
47
  type: z.string(),
44
48
  status: z.string(),
45
49
  priority: z.string(),
50
+ effort: z.string().nullable().optional(),
51
+ labels: z.array(z.string()).optional(),
52
+ dueDate: z.string().nullable().optional(),
46
53
  feedbackCount: z.number(),
47
54
  hasImplementationPlan: z.boolean(),
55
+ subtaskProgress: SubtaskProgressSchema.optional(),
56
+ });
57
+ const SubtaskSchema = z.object({
58
+ id: z.string(),
59
+ title: z.string(),
60
+ done: z.boolean(),
48
61
  });
49
62
  const TaskDetailSchema = z.object({
50
63
  id: z.string(),
51
64
  title: z.string(),
52
- description: z.string().optional(),
65
+ description: z.string().nullable().optional(),
53
66
  type: z.string(),
54
- implementationPlan: z.string().optional(),
67
+ implementationPlan: z.string().nullable().optional(),
55
68
  status: z.string(),
69
+ priority: z.string().nullable().optional(),
70
+ effort: z.string().nullable().optional(),
71
+ labels: z.array(z.string()).optional(),
72
+ dueDate: z.string().nullable().optional(),
56
73
  voteScore: z.number(),
57
- agentNotes: z.string().optional(),
74
+ subtasks: z.array(SubtaskSchema).optional(),
75
+ agentNotes: z.string().nullable().optional(),
58
76
  feedback: z.array(z.object({
59
77
  id: z.string(),
60
78
  title: z.string(),
@@ -119,7 +137,8 @@ server.registerTool('list_tasks', {
119
137
  const lines = data.tasks.map((t) => {
120
138
  const p = t.priority === 'high' ? '🔴' : t.priority === 'medium' ? '🟡' : '⚪';
121
139
  const typeIcon = t.type === 'bug' ? '🐛' : t.type === 'feature' ? '✨' : t.type === 'improvement' ? '💡' : '📌';
122
- return `• ${t.id}: ${typeIcon} ${t.title} [${t.status}] ${p}${t.hasImplementationPlan ? ' 📋' : ''}`;
140
+ const subtaskInfo = t.subtaskProgress ? ` [${t.subtaskProgress.done}/${t.subtaskProgress.total}]` : '';
141
+ return `• ${t.id}: ${typeIcon} ${t.title} [${t.status}] ${p}${t.hasImplementationPlan ? ' 📋' : ''}${subtaskInfo}`;
123
142
  });
124
143
  return {
125
144
  content: [{ type: 'text', text: `Tasks in "${data.project.name}":\n${lines.join('\n')}` }],
@@ -147,10 +166,28 @@ server.registerTool('get_task', {
147
166
  `# ${typeIcon} ${t.title}`,
148
167
  `Type: ${t.type} | Status: ${t.status} | Score: ${t.voteScore}`,
149
168
  ];
169
+ // Add priority/effort/labels if present
170
+ const meta = [];
171
+ if (t.priority)
172
+ meta.push(`Priority: ${t.priority}`);
173
+ if (t.effort)
174
+ meta.push(`Effort: ${t.effort}`);
175
+ if (t.dueDate)
176
+ meta.push(`Due: ${t.dueDate}`);
177
+ if (t.labels && t.labels.length)
178
+ meta.push(`Labels: ${t.labels.join(', ')}`);
179
+ if (meta.length)
180
+ parts.push(meta.join(' | '));
150
181
  if (t.description)
151
182
  parts.push(`\n## Description\n${t.description}`);
152
183
  if (t.implementationPlan)
153
184
  parts.push(`\n## Plan\n${t.implementationPlan}`);
185
+ // Show subtasks
186
+ if (t.subtasks && t.subtasks.length) {
187
+ const done = t.subtasks.filter(s => s.done).length;
188
+ parts.push(`\n## Subtasks (${done}/${t.subtasks.length})`);
189
+ t.subtasks.forEach((s) => parts.push(`- [${s.done ? 'x' : ' '}] ${s.title} (id: ${s.id})`));
190
+ }
154
191
  if (t.agentNotes)
155
192
  parts.push(`\n## Notes\n${t.agentNotes}`);
156
193
  if (t.feedback.length) {
@@ -273,6 +310,77 @@ server.registerTool('add_note', {
273
310
  structuredContent: { taskId, success: true },
274
311
  };
275
312
  });
313
+ // Tool: Create subtask
314
+ server.registerTool('create_subtask', {
315
+ title: 'Create Subtask',
316
+ description: 'Add a new subtask to a task. Useful for breaking down work into smaller steps.',
317
+ inputSchema: z.object({
318
+ taskId: z.string().describe('Task ID'),
319
+ title: z.string().describe('Subtask title'),
320
+ }),
321
+ outputSchema: z.object({
322
+ taskId: z.string(),
323
+ subtask: z.object({
324
+ id: z.string(),
325
+ title: z.string(),
326
+ done: z.boolean(),
327
+ }),
328
+ totalSubtasks: z.number(),
329
+ }),
330
+ annotations: {
331
+ readOnlyHint: false,
332
+ destructiveHint: false,
333
+ idempotentHint: false,
334
+ openWorldHint: false,
335
+ },
336
+ }, async ({ taskId, title }) => {
337
+ const result = await api('POST', `/api/agent/tasks/${taskId}/subtasks`, { title });
338
+ return {
339
+ content: [{ type: 'text', text: `➕ Created subtask: "${result.subtask.title}" (${result.totalSubtasks} total)` }],
340
+ structuredContent: {
341
+ taskId,
342
+ subtask: result.subtask,
343
+ totalSubtasks: result.totalSubtasks,
344
+ },
345
+ };
346
+ });
347
+ // Tool: Update subtask
348
+ server.registerTool('update_subtask', {
349
+ title: 'Update Subtask',
350
+ description: 'Mark a subtask as done or not done. Use get_task to see subtask IDs.',
351
+ inputSchema: z.object({
352
+ taskId: z.string().describe('Task ID'),
353
+ subtaskId: z.string().describe('Subtask ID'),
354
+ done: z.boolean().describe('Whether the subtask is done'),
355
+ }),
356
+ outputSchema: z.object({
357
+ taskId: z.string(),
358
+ subtaskId: z.string(),
359
+ done: z.boolean(),
360
+ progress: z.object({
361
+ done: z.number(),
362
+ total: z.number(),
363
+ }),
364
+ }),
365
+ annotations: {
366
+ readOnlyHint: false,
367
+ destructiveHint: false,
368
+ idempotentHint: true,
369
+ openWorldHint: false,
370
+ },
371
+ }, async ({ taskId, subtaskId, done }) => {
372
+ const result = await api('PATCH', `/api/agent/tasks/${taskId}/subtasks/${subtaskId}`, { done });
373
+ const status = done ? '✅' : '⬜';
374
+ return {
375
+ content: [{ type: 'text', text: `${status} Subtask updated (${result.progress.done}/${result.progress.total} done)` }],
376
+ structuredContent: {
377
+ taskId,
378
+ subtaskId,
379
+ done: result.done,
380
+ progress: result.progress,
381
+ },
382
+ };
383
+ });
276
384
  // Tool: Complete task
277
385
  server.registerTool('complete_task', {
278
386
  title: 'Complete Task',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@damper/mcp",
3
- "version": "0.1.5",
3
+ "version": "0.1.7",
4
4
  "description": "MCP server for Damper task management",
5
5
  "author": "Damper <hello@usedamper.com>",
6
6
  "repository": {