@damper/mcp 0.1.12 → 0.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.
- package/README.md +50 -3
- package/dist/index.js +233 -6
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -69,21 +69,45 @@ The AI will see tools from both servers and can distinguish between them:
|
|
|
69
69
|
|
|
70
70
|
## Tools
|
|
71
71
|
|
|
72
|
+
### Task Management
|
|
73
|
+
|
|
72
74
|
| Tool | Description |
|
|
73
75
|
|------|-------------|
|
|
74
|
-
| `list_tasks` | Get roadmap tasks (filter by `status`, `type`, `quarter`) |
|
|
76
|
+
| `list_tasks` | Get roadmap tasks (filter by `status`, `type`, `quarter`, sort by `importance`/`newest`/`votes`) |
|
|
75
77
|
| `get_task` | Task details + subtasks + linked feedback |
|
|
76
78
|
| `create_task` | Create task with type (bug, feature, improvement, task) |
|
|
77
79
|
| `update_task` | Update description, plan, priority, effort, quarter, labels |
|
|
78
|
-
| `start_task` | Lock and start task (use `force` to take over) |
|
|
80
|
+
| `start_task` | Lock and start task (use `force` to take over). Returns project context index. |
|
|
79
81
|
| `add_note` | Add progress note |
|
|
80
82
|
| `create_subtask` | Add subtask to a task |
|
|
81
83
|
| `update_subtask` | Mark subtask done/undone |
|
|
82
|
-
| `complete_task` | Mark done, release lock |
|
|
84
|
+
| `complete_task` | Mark done, release lock. Suggests context updates if needed. |
|
|
83
85
|
| `abandon_task` | Release lock, return to planned |
|
|
84
86
|
| `list_feedback` | Browse user feedback |
|
|
85
87
|
| `get_feedback` | Feedback details + votes |
|
|
86
88
|
|
|
89
|
+
### Project Context
|
|
90
|
+
|
|
91
|
+
AI agents can store and retrieve project documentation to help future agents work more effectively.
|
|
92
|
+
|
|
93
|
+
| Tool | Description |
|
|
94
|
+
|------|-------------|
|
|
95
|
+
| `get_project_context` | Get context index with section previews. Optionally highlights sections relevant to a task. |
|
|
96
|
+
| `list_context_sections` | List all available context sections |
|
|
97
|
+
| `get_context_section` | Get full content of a specific section |
|
|
98
|
+
| `update_context_section` | Upload/update a context section (requires user permission) |
|
|
99
|
+
| `sync_project_context` | Bulk upload multiple sections at once (requires user permission) |
|
|
100
|
+
|
|
101
|
+
**How it works:**
|
|
102
|
+
1. When you `start_task`, you receive a context index showing available documentation
|
|
103
|
+
2. Fetch relevant sections with `get_context_section` to understand the codebase
|
|
104
|
+
3. After completing work, `complete_task` reminds you to update docs if needed
|
|
105
|
+
4. Use `update_context_section` to share knowledge for future agents
|
|
106
|
+
|
|
107
|
+
**Common sections:** `overview`, `api`, `database`, `services`, `components`, `testing`, `auth`, `deployment`
|
|
108
|
+
|
|
109
|
+
⚠️ **Security:** Never include sensitive data (API keys, secrets, credentials, connection strings) in context uploads. Users can review and delete context from Settings → AI Context.
|
|
110
|
+
|
|
87
111
|
### Task Types
|
|
88
112
|
|
|
89
113
|
Filter tasks by type to prioritize work:
|
|
@@ -96,6 +120,19 @@ Filter tasks by type to prioritize work:
|
|
|
96
120
|
|
|
97
121
|
Types: `bug` 🐛, `feature` ✨, `improvement` 💡, `task` 📌
|
|
98
122
|
|
|
123
|
+
### Sorting
|
|
124
|
+
|
|
125
|
+
Tasks are sorted by **weighted importance** by default (priority weight + vote score), so high-priority items surface first even without votes. Override with `sort`:
|
|
126
|
+
|
|
127
|
+
- `importance` (default) - priority weight (high=100, medium=50, low=10) + votes
|
|
128
|
+
- `newest` - most recently created first
|
|
129
|
+
- `votes` - highest vote score first
|
|
130
|
+
|
|
131
|
+
```
|
|
132
|
+
> List tasks sorted by newest
|
|
133
|
+
> Show me the most voted features
|
|
134
|
+
```
|
|
135
|
+
|
|
99
136
|
### Task Metadata
|
|
100
137
|
|
|
101
138
|
Tasks include project management fields visible in `list_tasks` and `get_task`:
|
|
@@ -147,6 +184,7 @@ When you start a task, it's locked to prevent other agents from working on it si
|
|
|
147
184
|
|
|
148
185
|
## Usage Examples
|
|
149
186
|
|
|
187
|
+
### Tasks
|
|
150
188
|
```
|
|
151
189
|
> What tasks are available?
|
|
152
190
|
> List bugs I can fix
|
|
@@ -160,6 +198,15 @@ When you start a task, it's locked to prevent other agents from working on it si
|
|
|
160
198
|
> Abandon the current task, I'm blocked
|
|
161
199
|
```
|
|
162
200
|
|
|
201
|
+
### Project Context
|
|
202
|
+
```
|
|
203
|
+
> What documentation is available for this project?
|
|
204
|
+
> Get the API context section
|
|
205
|
+
> After analyzing this codebase, upload an overview
|
|
206
|
+
> Update the database section with the new schema
|
|
207
|
+
> Sync all my documentation to Damper
|
|
208
|
+
```
|
|
209
|
+
|
|
163
210
|
## Environment
|
|
164
211
|
|
|
165
212
|
| Variable | Required | Default |
|
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.
|
|
37
|
+
version: '0.2.0',
|
|
38
38
|
});
|
|
39
39
|
// Output schemas
|
|
40
40
|
const SubtaskProgressSchema = z.object({
|
|
@@ -109,6 +109,7 @@ server.registerTool('list_tasks', {
|
|
|
109
109
|
status: z.enum(['planned', 'in_progress', 'done', 'all']).optional(),
|
|
110
110
|
type: z.enum(['bug', 'feature', 'improvement', 'task']).optional().describe('Filter by task type'),
|
|
111
111
|
quarter: z.string().optional().describe('Filter by quarter (e.g., "Q1 2025") or "none" for unscheduled'),
|
|
112
|
+
sort: z.enum(['importance', 'newest', 'votes']).optional().describe('Sort order: importance (priority+votes, default), newest, or votes'),
|
|
112
113
|
limit: z.number().optional(),
|
|
113
114
|
}),
|
|
114
115
|
outputSchema: z.object({
|
|
@@ -121,7 +122,7 @@ server.registerTool('list_tasks', {
|
|
|
121
122
|
idempotentHint: true,
|
|
122
123
|
openWorldHint: false,
|
|
123
124
|
},
|
|
124
|
-
}, async ({ status, type, quarter, limit }) => {
|
|
125
|
+
}, async ({ status, type, quarter, sort, limit }) => {
|
|
125
126
|
const params = new URLSearchParams();
|
|
126
127
|
if (status)
|
|
127
128
|
params.set('status', status);
|
|
@@ -129,6 +130,8 @@ server.registerTool('list_tasks', {
|
|
|
129
130
|
params.set('type', type);
|
|
130
131
|
if (quarter)
|
|
131
132
|
params.set('quarter', quarter);
|
|
133
|
+
if (sort)
|
|
134
|
+
params.set('sort', sort);
|
|
132
135
|
if (limit)
|
|
133
136
|
params.set('limit', String(limit));
|
|
134
137
|
const query = params.toString();
|
|
@@ -312,11 +315,23 @@ server.registerTool('create_task', {
|
|
|
312
315
|
structuredContent: result,
|
|
313
316
|
};
|
|
314
317
|
});
|
|
318
|
+
// Context index schema for start_task response
|
|
319
|
+
const ContextIndexSchema = z.object({
|
|
320
|
+
isEmpty: z.boolean(),
|
|
321
|
+
index: z.array(z.object({
|
|
322
|
+
section: z.string(),
|
|
323
|
+
preview: z.string(),
|
|
324
|
+
updatedAt: z.string(),
|
|
325
|
+
})),
|
|
326
|
+
relevantSections: z.array(z.string()).optional(),
|
|
327
|
+
hint: z.string().optional(),
|
|
328
|
+
});
|
|
315
329
|
// Tool: Start task
|
|
316
330
|
server.registerTool('start_task', {
|
|
317
331
|
title: 'Start Task',
|
|
318
332
|
description: 'Lock and start a task. Fails if locked by another agent unless force=true. ' +
|
|
319
|
-
'Use force=true to take over a task from another agent (e.g., if they abandoned it).'
|
|
333
|
+
'Use force=true to take over a task from another agent (e.g., if they abandoned it). ' +
|
|
334
|
+
'Returns project context index with relevant sections for the task.',
|
|
320
335
|
inputSchema: z.object({
|
|
321
336
|
taskId: z.string(),
|
|
322
337
|
force: z.boolean().optional().describe('Take over lock from another agent'),
|
|
@@ -327,6 +342,7 @@ server.registerTool('start_task', {
|
|
|
327
342
|
message: z.string(),
|
|
328
343
|
lockedBy: z.string().optional(),
|
|
329
344
|
lockedAt: z.string().optional(),
|
|
345
|
+
context: ContextIndexSchema.optional(),
|
|
330
346
|
}),
|
|
331
347
|
annotations: {
|
|
332
348
|
readOnlyHint: false,
|
|
@@ -337,8 +353,26 @@ server.registerTool('start_task', {
|
|
|
337
353
|
}, async ({ taskId, force }) => {
|
|
338
354
|
try {
|
|
339
355
|
const result = await api('POST', `/api/agent/tasks/${taskId}/start`, force ? { force: true } : undefined);
|
|
356
|
+
// Build response text
|
|
357
|
+
const lines = [`Started ${result.id}: ${result.message}`];
|
|
358
|
+
if (result.context) {
|
|
359
|
+
if (result.context.isEmpty) {
|
|
360
|
+
lines.push(`\n📚 ${result.context.hint || 'No project context available.'}`);
|
|
361
|
+
}
|
|
362
|
+
else {
|
|
363
|
+
lines.push('\n**Project context available:**');
|
|
364
|
+
for (const s of result.context.index) {
|
|
365
|
+
const relevant = result.context.relevantSections?.includes(s.section) ? ' ⭐' : '';
|
|
366
|
+
lines.push(`• ${s.section}${relevant}`);
|
|
367
|
+
}
|
|
368
|
+
if (result.context.relevantSections && result.context.relevantSections.length > 0) {
|
|
369
|
+
lines.push(`\nRelevant for this task: ${result.context.relevantSections.join(', ')}`);
|
|
370
|
+
lines.push('Use get_context_section to fetch full content.');
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
}
|
|
340
374
|
return {
|
|
341
|
-
content: [{ type: 'text', text:
|
|
375
|
+
content: [{ type: 'text', text: lines.join('\n') }],
|
|
342
376
|
structuredContent: result,
|
|
343
377
|
};
|
|
344
378
|
}
|
|
@@ -463,10 +497,16 @@ server.registerTool('update_subtask', {
|
|
|
463
497
|
},
|
|
464
498
|
};
|
|
465
499
|
});
|
|
500
|
+
// Documentation reminder schema for complete_task response
|
|
501
|
+
const DocumentationSchema = z.object({
|
|
502
|
+
hasContext: z.boolean(),
|
|
503
|
+
affectedSections: z.array(z.string()),
|
|
504
|
+
reminder: z.string(),
|
|
505
|
+
});
|
|
466
506
|
// Tool: Complete task
|
|
467
507
|
server.registerTool('complete_task', {
|
|
468
508
|
title: 'Complete Task',
|
|
469
|
-
description: 'Mark task done with summary.',
|
|
509
|
+
description: 'Mark task done with summary. Returns documentation update suggestions.',
|
|
470
510
|
inputSchema: z.object({
|
|
471
511
|
taskId: z.string(),
|
|
472
512
|
summary: z.string().describe('What was implemented'),
|
|
@@ -474,6 +514,7 @@ server.registerTool('complete_task', {
|
|
|
474
514
|
outputSchema: z.object({
|
|
475
515
|
id: z.string(),
|
|
476
516
|
status: z.string(),
|
|
517
|
+
documentation: DocumentationSchema.optional(),
|
|
477
518
|
}),
|
|
478
519
|
annotations: {
|
|
479
520
|
readOnlyHint: false,
|
|
@@ -483,8 +524,12 @@ server.registerTool('complete_task', {
|
|
|
483
524
|
},
|
|
484
525
|
}, async ({ taskId, summary }) => {
|
|
485
526
|
const result = await api('POST', `/api/agent/tasks/${taskId}/complete`, { summary });
|
|
527
|
+
const lines = [`✅ Completed ${result.id}`];
|
|
528
|
+
if (result.documentation?.reminder) {
|
|
529
|
+
lines.push(`\n📝 ${result.documentation.reminder}`);
|
|
530
|
+
}
|
|
486
531
|
return {
|
|
487
|
-
content: [{ type: 'text', text:
|
|
532
|
+
content: [{ type: 'text', text: lines.join('\n') }],
|
|
488
533
|
structuredContent: result,
|
|
489
534
|
};
|
|
490
535
|
});
|
|
@@ -514,6 +559,188 @@ server.registerTool('abandon_task', {
|
|
|
514
559
|
structuredContent: result,
|
|
515
560
|
};
|
|
516
561
|
});
|
|
562
|
+
// ==================== Context Tools ====================
|
|
563
|
+
// Tool: List context sections
|
|
564
|
+
server.registerTool('list_context_sections', {
|
|
565
|
+
title: 'List Context Sections',
|
|
566
|
+
description: 'List available project context sections with brief previews.',
|
|
567
|
+
inputSchema: z.object({}),
|
|
568
|
+
outputSchema: z.object({
|
|
569
|
+
sections: z.array(z.object({
|
|
570
|
+
section: z.string(),
|
|
571
|
+
updatedAt: z.string(),
|
|
572
|
+
source: z.string(),
|
|
573
|
+
preview: z.string(),
|
|
574
|
+
})),
|
|
575
|
+
isEmpty: z.boolean(),
|
|
576
|
+
}),
|
|
577
|
+
annotations: {
|
|
578
|
+
readOnlyHint: true,
|
|
579
|
+
destructiveHint: false,
|
|
580
|
+
idempotentHint: true,
|
|
581
|
+
openWorldHint: false,
|
|
582
|
+
},
|
|
583
|
+
}, async () => {
|
|
584
|
+
const data = await api('GET', '/api/agent/context');
|
|
585
|
+
if (data.isEmpty) {
|
|
586
|
+
return {
|
|
587
|
+
content: [{ type: 'text', text: 'No project context available. Use sync_project_context to upload documentation.' }],
|
|
588
|
+
structuredContent: data,
|
|
589
|
+
};
|
|
590
|
+
}
|
|
591
|
+
const lines = data.sections.map((s) => `• ${s.section} (${s.source}, updated ${s.updatedAt.split('T')[0]})`);
|
|
592
|
+
return {
|
|
593
|
+
content: [{ type: 'text', text: `Context sections:\n${lines.join('\n')}` }],
|
|
594
|
+
structuredContent: data,
|
|
595
|
+
};
|
|
596
|
+
});
|
|
597
|
+
// Tool: Get context section
|
|
598
|
+
server.registerTool('get_context_section', {
|
|
599
|
+
title: 'Get Context Section',
|
|
600
|
+
description: 'Get full content of a specific context section.',
|
|
601
|
+
inputSchema: z.object({
|
|
602
|
+
section: z.string().describe('Section name (e.g., "overview", "api", "database")'),
|
|
603
|
+
}),
|
|
604
|
+
outputSchema: z.object({
|
|
605
|
+
section: z.string(),
|
|
606
|
+
content: z.string(),
|
|
607
|
+
updatedAt: z.string(),
|
|
608
|
+
source: z.string(),
|
|
609
|
+
}),
|
|
610
|
+
annotations: {
|
|
611
|
+
readOnlyHint: true,
|
|
612
|
+
destructiveHint: false,
|
|
613
|
+
idempotentHint: true,
|
|
614
|
+
openWorldHint: false,
|
|
615
|
+
},
|
|
616
|
+
}, async ({ section }) => {
|
|
617
|
+
const data = await api('GET', `/api/agent/context/${section}`);
|
|
618
|
+
return {
|
|
619
|
+
content: [{ type: 'text', text: `# ${data.section}\n\n${data.content}` }],
|
|
620
|
+
structuredContent: data,
|
|
621
|
+
};
|
|
622
|
+
});
|
|
623
|
+
// Tool: Update context section
|
|
624
|
+
server.registerTool('update_context_section', {
|
|
625
|
+
title: 'Update Context Section',
|
|
626
|
+
description: 'Upload/update a project context section. Use after analyzing local codebase. ' +
|
|
627
|
+
'This action shares documentation with the Damper project for future AI agents.\n\n' +
|
|
628
|
+
'⚠️ SECURITY WARNING: Do NOT include sensitive information:\n' +
|
|
629
|
+
'- API keys, secrets, passwords, tokens\n' +
|
|
630
|
+
'- Database connection strings\n' +
|
|
631
|
+
'- Private endpoints or internal URLs\n' +
|
|
632
|
+
'- Customer data or PII\n' +
|
|
633
|
+
'- .env file contents or credentials',
|
|
634
|
+
inputSchema: z.object({
|
|
635
|
+
section: z.string()
|
|
636
|
+
.regex(/^[a-z][a-z0-9-]{0,49}$/)
|
|
637
|
+
.describe('Section name (lowercase, alphanumeric, hyphens). Examples: overview, database, api, services, components, testing, conventions, deployment'),
|
|
638
|
+
content: z.string().describe('Markdown documentation for this section. Must NOT contain secrets, API keys, or sensitive data.'),
|
|
639
|
+
}),
|
|
640
|
+
outputSchema: z.object({
|
|
641
|
+
success: z.boolean(),
|
|
642
|
+
section: z.string(),
|
|
643
|
+
warnings: z.array(z.string()).optional(),
|
|
644
|
+
}),
|
|
645
|
+
annotations: {
|
|
646
|
+
readOnlyHint: false,
|
|
647
|
+
destructiveHint: false,
|
|
648
|
+
idempotentHint: true,
|
|
649
|
+
openWorldHint: false,
|
|
650
|
+
},
|
|
651
|
+
}, async ({ section, content }) => {
|
|
652
|
+
const result = await api('POST', `/api/agent/context/${section}`, { content });
|
|
653
|
+
let text = `📝 Updated context section: ${result.section}`;
|
|
654
|
+
if (result.warnings && result.warnings.length > 0) {
|
|
655
|
+
text += `\n⚠️ Warnings: ${result.warnings.join(', ')}`;
|
|
656
|
+
}
|
|
657
|
+
return {
|
|
658
|
+
content: [{ type: 'text', text }],
|
|
659
|
+
structuredContent: result,
|
|
660
|
+
};
|
|
661
|
+
});
|
|
662
|
+
// Tool: Sync project context (bulk upload)
|
|
663
|
+
server.registerTool('sync_project_context', {
|
|
664
|
+
title: 'Sync Project Context',
|
|
665
|
+
description: 'Bulk upload all project context sections at once. Use after analyzing a codebase. ' +
|
|
666
|
+
'This action shares documentation with the Damper project for future AI agents.\n\n' +
|
|
667
|
+
'⚠️ SECURITY WARNING: Do NOT include sensitive information:\n' +
|
|
668
|
+
'- API keys, secrets, passwords, tokens\n' +
|
|
669
|
+
'- Database connection strings\n' +
|
|
670
|
+
'- Private endpoints or internal URLs\n' +
|
|
671
|
+
'- Customer data or PII\n' +
|
|
672
|
+
'- .env file contents or credentials',
|
|
673
|
+
inputSchema: z.object({
|
|
674
|
+
sections: z.array(z.object({
|
|
675
|
+
section: z.string()
|
|
676
|
+
.regex(/^[a-z][a-z0-9-]{0,49}$/)
|
|
677
|
+
.describe('Section name'),
|
|
678
|
+
content: z.string().describe('Markdown documentation (no secrets!)'),
|
|
679
|
+
})).describe('Array of sections to upload'),
|
|
680
|
+
}),
|
|
681
|
+
outputSchema: z.object({
|
|
682
|
+
success: z.boolean(),
|
|
683
|
+
sectionsUpdated: z.number(),
|
|
684
|
+
}),
|
|
685
|
+
annotations: {
|
|
686
|
+
readOnlyHint: false,
|
|
687
|
+
destructiveHint: false,
|
|
688
|
+
idempotentHint: true,
|
|
689
|
+
openWorldHint: false,
|
|
690
|
+
},
|
|
691
|
+
}, async ({ sections }) => {
|
|
692
|
+
const result = await api('POST', '/api/agent/context/sync', { sections });
|
|
693
|
+
return {
|
|
694
|
+
content: [{ type: 'text', text: `📚 Synced ${result.sectionsUpdated} context sections` }],
|
|
695
|
+
structuredContent: result,
|
|
696
|
+
};
|
|
697
|
+
});
|
|
698
|
+
// Tool: Get project context (token-efficient index)
|
|
699
|
+
server.registerTool('get_project_context', {
|
|
700
|
+
title: 'Get Project Context',
|
|
701
|
+
description: 'Get project context index (token-efficient). Returns section list with previews. ' +
|
|
702
|
+
'Use get_context_section to fetch full content for sections you need.',
|
|
703
|
+
inputSchema: z.object({
|
|
704
|
+
taskId: z.string().optional().describe('If provided, highlights sections relevant to this task'),
|
|
705
|
+
}),
|
|
706
|
+
outputSchema: z.object({
|
|
707
|
+
isEmpty: z.boolean(),
|
|
708
|
+
index: z.array(z.object({
|
|
709
|
+
section: z.string(),
|
|
710
|
+
preview: z.string(),
|
|
711
|
+
updatedAt: z.string(),
|
|
712
|
+
})),
|
|
713
|
+
relevantSections: z.array(z.string()).optional(),
|
|
714
|
+
hint: z.string().optional(),
|
|
715
|
+
}),
|
|
716
|
+
annotations: {
|
|
717
|
+
readOnlyHint: true,
|
|
718
|
+
destructiveHint: false,
|
|
719
|
+
idempotentHint: true,
|
|
720
|
+
openWorldHint: false,
|
|
721
|
+
},
|
|
722
|
+
}, async ({ taskId }) => {
|
|
723
|
+
const params = taskId ? `?task_id=${taskId}` : '';
|
|
724
|
+
const data = await api('GET', `/api/agent/context/index${params}`);
|
|
725
|
+
if (data.isEmpty) {
|
|
726
|
+
return {
|
|
727
|
+
content: [{ type: 'text', text: data.hint || 'No project context available.' }],
|
|
728
|
+
structuredContent: data,
|
|
729
|
+
};
|
|
730
|
+
}
|
|
731
|
+
const lines = ['**Available context sections:**'];
|
|
732
|
+
for (const s of data.index) {
|
|
733
|
+
const relevant = data.relevantSections?.includes(s.section) ? ' ⭐' : '';
|
|
734
|
+
lines.push(`• ${s.section}${relevant} - ${s.preview}`);
|
|
735
|
+
}
|
|
736
|
+
if (data.relevantSections && data.relevantSections.length > 0) {
|
|
737
|
+
lines.push(`\n**Relevant for this task:** ${data.relevantSections.join(', ')}`);
|
|
738
|
+
}
|
|
739
|
+
return {
|
|
740
|
+
content: [{ type: 'text', text: lines.join('\n') }],
|
|
741
|
+
structuredContent: data,
|
|
742
|
+
};
|
|
743
|
+
});
|
|
517
744
|
// Tool: List feedback
|
|
518
745
|
server.registerTool('list_feedback', {
|
|
519
746
|
title: 'List Feedback',
|