@damper/mcp 0.1.8 → 0.1.10

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 CHANGED
@@ -71,10 +71,10 @@ The AI will see tools from both servers and can distinguish between them:
71
71
 
72
72
  | Tool | Description |
73
73
  |------|-------------|
74
- | `list_tasks` | Get roadmap tasks (filter by `status`, `type`) |
74
+ | `list_tasks` | Get roadmap tasks (filter by `status`, `type`, `quarter`) |
75
75
  | `get_task` | Task details + subtasks + linked feedback |
76
76
  | `create_task` | Create task with type (bug, feature, improvement, task) |
77
- | `update_task` | Update description, plan, priority, effort, labels |
77
+ | `update_task` | Update description, plan, priority, effort, quarter, labels |
78
78
  | `start_task` | Lock and start task (use `force` to take over) |
79
79
  | `add_note` | Add progress note |
80
80
  | `create_subtask` | Add subtask to a task |
@@ -102,6 +102,7 @@ Tasks include project management fields visible in `list_tasks` and `get_task`:
102
102
 
103
103
  - **Priority**: high 🔴, medium 🟡, low ⚪
104
104
  - **Effort**: xs, s, m, l, xl
105
+ - **Quarter**: Target quarter (Q1 2025, Q2 2025, etc.)
105
106
  - **Labels**: Custom tags
106
107
  - **Due date**: Target completion date
107
108
 
@@ -114,6 +115,7 @@ Refine task details as you work:
114
115
  > Update the implementation plan with what I've learned
115
116
  > Add labels: backend, api, auth
116
117
  > Set effort to medium
118
+ > Schedule this for Q2 2025
117
119
  ```
118
120
 
119
121
  ### Subtasks
@@ -148,6 +150,8 @@ When you start a task, it's locked to prevent other agents from working on it si
148
150
  ```
149
151
  > What tasks are available?
150
152
  > List bugs I can fix
153
+ > Show tasks for Q2 2025
154
+ > What's unscheduled? (quarter: none)
151
155
  > Import my TODO.md into Damper
152
156
  > Work on the dark mode feature
153
157
  > Show me the subtasks for this task
package/dist/index.js CHANGED
@@ -34,7 +34,7 @@ async function api(method, path, body) {
34
34
  // Server
35
35
  const server = new McpServer({
36
36
  name: 'damper',
37
- version: '0.1.8',
37
+ version: '0.1.10',
38
38
  });
39
39
  // Output schemas
40
40
  const SubtaskProgressSchema = z.object({
@@ -48,6 +48,7 @@ const TaskSummarySchema = z.object({
48
48
  status: z.string(),
49
49
  priority: z.string(),
50
50
  effort: z.string().nullable().optional(),
51
+ quarter: z.string().nullable().optional(),
51
52
  labels: z.array(z.string()).optional(),
52
53
  dueDate: z.string().nullable().optional(),
53
54
  feedbackCount: z.number(),
@@ -68,6 +69,7 @@ const TaskDetailSchema = z.object({
68
69
  status: z.string(),
69
70
  priority: z.string().nullable().optional(),
70
71
  effort: z.string().nullable().optional(),
72
+ quarter: z.string().nullable().optional(),
71
73
  labels: z.array(z.string()).optional(),
72
74
  dueDate: z.string().nullable().optional(),
73
75
  voteScore: z.number(),
@@ -102,10 +104,11 @@ const FeedbackDetailSchema = z.object({
102
104
  server.registerTool('list_tasks', {
103
105
  title: 'List Tasks',
104
106
  description: 'Get roadmap tasks. Returns planned/in-progress by default. ' +
105
- 'Filter by type to get bugs, features, etc.',
107
+ 'Filter by type, quarter, etc.',
106
108
  inputSchema: z.object({
107
109
  status: z.enum(['planned', 'in_progress', 'done', 'all']).optional(),
108
110
  type: z.enum(['bug', 'feature', 'improvement', 'task']).optional().describe('Filter by task type'),
111
+ quarter: z.string().optional().describe('Filter by quarter (e.g., "Q1 2025") or "none" for unscheduled'),
109
112
  limit: z.number().optional(),
110
113
  }),
111
114
  outputSchema: z.object({
@@ -118,19 +121,22 @@ server.registerTool('list_tasks', {
118
121
  idempotentHint: true,
119
122
  openWorldHint: false,
120
123
  },
121
- }, async ({ status, type, limit }) => {
124
+ }, async ({ status, type, quarter, limit }) => {
122
125
  const params = new URLSearchParams();
123
126
  if (status)
124
127
  params.set('status', status);
125
128
  if (type)
126
129
  params.set('type', type);
130
+ if (quarter)
131
+ params.set('quarter', quarter);
127
132
  if (limit)
128
133
  params.set('limit', String(limit));
129
134
  const query = params.toString();
130
135
  const data = await api('GET', `/api/agent/tasks${query ? `?${query}` : ''}`);
131
136
  if (!data.tasks.length) {
137
+ const filters = [type && `type: ${type}`, quarter && `quarter: ${quarter}`].filter(Boolean).join(', ');
132
138
  return {
133
- content: [{ type: 'text', text: `No tasks in "${data.project.name}"${type ? ` (type: ${type})` : ''}` }],
139
+ content: [{ type: 'text', text: `No tasks in "${data.project.name}"${filters ? ` (${filters})` : ''}` }],
134
140
  structuredContent: { project: data.project.name, tasks: [] },
135
141
  };
136
142
  }
@@ -138,7 +144,8 @@ server.registerTool('list_tasks', {
138
144
  const p = t.priority === 'high' ? '🔴' : t.priority === 'medium' ? '🟡' : '⚪';
139
145
  const typeIcon = t.type === 'bug' ? '🐛' : t.type === 'feature' ? '✨' : t.type === 'improvement' ? '💡' : '📌';
140
146
  const subtaskInfo = t.subtaskProgress ? ` [${t.subtaskProgress.done}/${t.subtaskProgress.total}]` : '';
141
- return `• ${t.id}: ${typeIcon} ${t.title} [${t.status}] ${p}${t.hasImplementationPlan ? ' 📋' : ''}${subtaskInfo}`;
147
+ const quarterInfo = t.quarter ? ` 📅${t.quarter}` : '';
148
+ return `• ${t.id}: ${typeIcon} ${t.title} [${t.status}] ${p}${quarterInfo}${t.hasImplementationPlan ? ' 📋' : ''}${subtaskInfo}`;
142
149
  });
143
150
  return {
144
151
  content: [{ type: 'text', text: `Tasks in "${data.project.name}":\n${lines.join('\n')}` }],
@@ -166,12 +173,14 @@ server.registerTool('get_task', {
166
173
  `# ${typeIcon} ${t.title}`,
167
174
  `Type: ${t.type} | Status: ${t.status} | Score: ${t.voteScore}`,
168
175
  ];
169
- // Add priority/effort/labels if present
176
+ // Add priority/effort/quarter/labels if present
170
177
  const meta = [];
171
178
  if (t.priority)
172
179
  meta.push(`Priority: ${t.priority}`);
173
180
  if (t.effort)
174
181
  meta.push(`Effort: ${t.effort}`);
182
+ if (t.quarter)
183
+ meta.push(`Quarter: ${t.quarter}`);
175
184
  if (t.dueDate)
176
185
  meta.push(`Due: ${t.dueDate}`);
177
186
  if (t.labels && t.labels.length)
@@ -202,7 +211,7 @@ server.registerTool('get_task', {
202
211
  // Tool: Update task
203
212
  server.registerTool('update_task', {
204
213
  title: 'Update Task',
205
- description: 'Update task fields like description, implementation plan, priority, effort, or labels. ' +
214
+ description: 'Update task fields like description, implementation plan, priority, effort, quarter, or labels. ' +
206
215
  'Useful for refining task details as you learn more about the work.',
207
216
  inputSchema: z.object({
208
217
  taskId: z.string().describe('Task ID'),
@@ -210,6 +219,7 @@ server.registerTool('update_task', {
210
219
  implementationPlan: z.string().optional().describe('Implementation plan (markdown)'),
211
220
  priority: z.enum(['high', 'medium', 'low']).nullable().optional().describe('Task priority'),
212
221
  effort: z.enum(['xs', 's', 'm', 'l', 'xl']).nullable().optional().describe('Estimated effort'),
222
+ quarter: z.string().nullable().optional().describe('Target quarter (e.g., "Q1 2025", "Q2 2025")'),
213
223
  labels: z.array(z.string()).optional().describe('Labels/tags'),
214
224
  }),
215
225
  outputSchema: z.object({
@@ -219,6 +229,7 @@ server.registerTool('update_task', {
219
229
  implementationPlan: z.string().nullable().optional(),
220
230
  priority: z.string().nullable().optional(),
221
231
  effort: z.string().nullable().optional(),
232
+ quarter: z.string().nullable().optional(),
222
233
  labels: z.array(z.string()).optional(),
223
234
  }),
224
235
  annotations: {
@@ -227,7 +238,7 @@ server.registerTool('update_task', {
227
238
  idempotentHint: true,
228
239
  openWorldHint: false,
229
240
  },
230
- }, async ({ taskId, description, implementationPlan, priority, effort, labels }) => {
241
+ }, async ({ taskId, description, implementationPlan, priority, effort, quarter, labels }) => {
231
242
  const body = {};
232
243
  if (description !== undefined)
233
244
  body.description = description;
@@ -237,6 +248,8 @@ server.registerTool('update_task', {
237
248
  body.priority = priority;
238
249
  if (effort !== undefined)
239
250
  body.effort = effort;
251
+ if (quarter !== undefined)
252
+ body.quarter = quarter;
240
253
  if (labels !== undefined)
241
254
  body.labels = labels;
242
255
  const result = await api('PATCH', `/api/agent/tasks/${taskId}`, body);
@@ -249,6 +262,8 @@ server.registerTool('update_task', {
249
262
  updates.push(`priority=${priority || 'none'}`);
250
263
  if (effort !== undefined)
251
264
  updates.push(`effort=${effort || 'none'}`);
265
+ if (quarter !== undefined)
266
+ updates.push(`quarter=${quarter || 'none'}`);
252
267
  if (labels !== undefined)
253
268
  updates.push(`labels=[${labels.join(', ')}]`);
254
269
  return {
@@ -260,6 +275,7 @@ server.registerTool('update_task', {
260
275
  implementationPlan: result.implementationPlan,
261
276
  priority: result.priority,
262
277
  effort: result.effort,
278
+ quarter: result.quarter,
263
279
  labels: result.labels,
264
280
  },
265
281
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@damper/mcp",
3
- "version": "0.1.8",
3
+ "version": "0.1.10",
4
4
  "description": "MCP server for Damper task management",
5
5
  "author": "Damper <hello@usedamper.com>",
6
6
  "repository": {