@damper/mcp 0.3.22 → 0.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.
- package/README.md +20 -0
- package/dist/formatters.d.ts +1 -0
- package/dist/formatters.js +9 -1
- package/dist/index.js +113 -8
- package/package.json +1 -1
- package/dist/formatters.test.d.ts +0 -4
- package/dist/formatters.test.js +0 -126
package/README.md
CHANGED
|
@@ -118,6 +118,26 @@ AI agents can store and retrieve project documentation to help future agents wor
|
|
|
118
118
|
|
|
119
119
|
⚠️ **Security:** Never include sensitive data (API keys, secrets, credentials, connection strings) in context uploads. Users can review and delete context from Settings → AI Context.
|
|
120
120
|
|
|
121
|
+
### Project Settings
|
|
122
|
+
|
|
123
|
+
Configure agent-relevant project settings programmatically.
|
|
124
|
+
|
|
125
|
+
| Tool | Description |
|
|
126
|
+
|------|-------------|
|
|
127
|
+
| `get_project_settings` | View current settings (completion checklist, etc.) |
|
|
128
|
+
| `update_project_settings` | Configure completion checklist and other agent-relevant settings |
|
|
129
|
+
|
|
130
|
+
**Completion checklist workflow:**
|
|
131
|
+
1. `update_project_settings` with `completionChecklist: ["All tests pass", "Build succeeds"]`
|
|
132
|
+
2. Agents see the checklist when they call `start_task`
|
|
133
|
+
3. Agents must confirm all items via `confirmations` when calling `complete_task`
|
|
134
|
+
|
|
135
|
+
```
|
|
136
|
+
> View project settings
|
|
137
|
+
> Set completion checklist to require tests and builds
|
|
138
|
+
> Clear the completion checklist
|
|
139
|
+
```
|
|
140
|
+
|
|
121
141
|
### Hierarchical Sections
|
|
122
142
|
|
|
123
143
|
For monorepos or large projects, organize sections hierarchically:
|
package/dist/formatters.d.ts
CHANGED
package/dist/formatters.js
CHANGED
|
@@ -16,6 +16,14 @@ export function formatStartTaskResponse(result) {
|
|
|
16
16
|
lines.push(`• ${rule}`);
|
|
17
17
|
}
|
|
18
18
|
}
|
|
19
|
+
// Completion checklist - shown after critical rules so agent knows upfront
|
|
20
|
+
if (result.completionChecklist && result.completionChecklist.length > 0) {
|
|
21
|
+
lines.push('\n📋 **COMPLETION CHECKLIST (required for complete_task):**');
|
|
22
|
+
lines.push('You MUST verify each item and pass them as `confirmations` when calling `complete_task`:');
|
|
23
|
+
for (const item of result.completionChecklist) {
|
|
24
|
+
lines.push(` □ ${item}`);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
19
27
|
// Context section - emphatic about reading it first
|
|
20
28
|
if (result.context) {
|
|
21
29
|
if (result.context.isEmpty) {
|
|
@@ -43,7 +51,7 @@ export function formatStartTaskResponse(result) {
|
|
|
43
51
|
lines.push('1. `add_note`: "Session started: <goal>"');
|
|
44
52
|
lines.push('2. Work: use `add_commit` for commits, `add_note` for decisions');
|
|
45
53
|
lines.push('3. `add_note`: "Session end: <summary>"');
|
|
46
|
-
lines.push('4. `complete_task` or `abandon_task`');
|
|
54
|
+
lines.push('4. `complete_task` (with `confirmations` if checklist exists) or `abandon_task`');
|
|
47
55
|
return lines.join('\n');
|
|
48
56
|
}
|
|
49
57
|
/**
|
package/dist/index.js
CHANGED
|
@@ -28,6 +28,12 @@ async function api(method, path, body) {
|
|
|
28
28
|
lockErr.lockInfo = err;
|
|
29
29
|
throw lockErr;
|
|
30
30
|
}
|
|
31
|
+
// Preserve checklist info for 400 checklist failures
|
|
32
|
+
if (res.status === 400 && err.missingItems) {
|
|
33
|
+
const checklistErr = new Error(err.error || `HTTP ${res.status}`);
|
|
34
|
+
checklistErr.checklistInfo = err;
|
|
35
|
+
throw checklistErr;
|
|
36
|
+
}
|
|
31
37
|
throw new Error(err.error || `HTTP ${res.status}`);
|
|
32
38
|
}
|
|
33
39
|
return res.json();
|
|
@@ -35,7 +41,7 @@ async function api(method, path, body) {
|
|
|
35
41
|
// Server
|
|
36
42
|
const server = new McpServer({
|
|
37
43
|
name: 'damper',
|
|
38
|
-
version: '0.
|
|
44
|
+
version: '0.5.0',
|
|
39
45
|
});
|
|
40
46
|
// Output schemas
|
|
41
47
|
const SubtaskProgressSchema = z.object({
|
|
@@ -403,6 +409,7 @@ server.registerTool('start_task', {
|
|
|
403
409
|
lockedBy: z.string().optional(),
|
|
404
410
|
lockedAt: z.string().optional(),
|
|
405
411
|
context: ContextIndexSchema.optional(),
|
|
412
|
+
completionChecklist: z.array(z.string()).optional(),
|
|
406
413
|
}),
|
|
407
414
|
annotations: {
|
|
408
415
|
readOnlyHint: false,
|
|
@@ -587,6 +594,9 @@ server.registerTool('complete_task', {
|
|
|
587
594
|
'**Before calling:**\n' +
|
|
588
595
|
'1. Push all commits\n' +
|
|
589
596
|
'2. Check if project context docs need updating\n\n' +
|
|
597
|
+
'**Completion checklist:** If the project has a completion checklist (shown in `start_task` response), ' +
|
|
598
|
+
'you MUST pass `confirmations` — an array echoing back each checklist item you\'ve verified. ' +
|
|
599
|
+
'The server will reject completion if any items are missing.\n\n' +
|
|
590
600
|
'**Commits:** Pass commits array to log them at completion (convenience for final commits).\n\n' +
|
|
591
601
|
'Returns documentation update suggestions.',
|
|
592
602
|
inputSchema: z.object({
|
|
@@ -596,6 +606,7 @@ server.registerTool('complete_task', {
|
|
|
596
606
|
hash: z.string().describe('Commit hash (short or full)'),
|
|
597
607
|
message: z.string().describe('Commit message'),
|
|
598
608
|
})).optional().describe('Optional: commits to log at completion'),
|
|
609
|
+
confirmations: z.array(z.string()).optional().describe('Completion checklist confirmations — echo back each item from the checklist shown in start_task'),
|
|
599
610
|
}),
|
|
600
611
|
outputSchema: z.object({
|
|
601
612
|
id: z.string(),
|
|
@@ -608,12 +619,33 @@ server.registerTool('complete_task', {
|
|
|
608
619
|
idempotentHint: true,
|
|
609
620
|
openWorldHint: false,
|
|
610
621
|
},
|
|
611
|
-
}, async ({ taskId, summary, commits }) => {
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
622
|
+
}, async ({ taskId, summary, commits, confirmations }) => {
|
|
623
|
+
try {
|
|
624
|
+
const result = await api('POST', `/api/agent/tasks/${taskId}/complete`, { summary, commits, confirmations });
|
|
625
|
+
return {
|
|
626
|
+
content: [{ type: 'text', text: formatCompleteTaskResponse(result) }],
|
|
627
|
+
structuredContent: result,
|
|
628
|
+
};
|
|
629
|
+
}
|
|
630
|
+
catch (err) {
|
|
631
|
+
const error = err;
|
|
632
|
+
if (error.checklistInfo) {
|
|
633
|
+
const { missingItems, checklist } = error.checklistInfo;
|
|
634
|
+
const lines = [
|
|
635
|
+
'❌ Completion blocked — checklist not fully confirmed.',
|
|
636
|
+
'',
|
|
637
|
+
`**Missing ${missingItems.length} of ${checklist.length} items:**`,
|
|
638
|
+
...missingItems.map(item => ` • ${item}`),
|
|
639
|
+
'',
|
|
640
|
+
'Pass ALL checklist items in `confirmations` to complete the task.',
|
|
641
|
+
];
|
|
642
|
+
return {
|
|
643
|
+
content: [{ type: 'text', text: lines.join('\n') }],
|
|
644
|
+
isError: true,
|
|
645
|
+
};
|
|
646
|
+
}
|
|
647
|
+
throw error;
|
|
648
|
+
}
|
|
617
649
|
});
|
|
618
650
|
// Tool: Abandon task
|
|
619
651
|
server.registerTool('abandon_task', {
|
|
@@ -690,7 +722,7 @@ server.registerTool('get_agent_instructions', {
|
|
|
690
722
|
openWorldHint: false,
|
|
691
723
|
},
|
|
692
724
|
}, async ({ format = 'section' }) => {
|
|
693
|
-
const lastModified = '2025-02-
|
|
725
|
+
const lastModified = '2025-02-06';
|
|
694
726
|
const section = `## Task Management with Damper MCP
|
|
695
727
|
|
|
696
728
|
> Last updated: ${lastModified}
|
|
@@ -721,9 +753,14 @@ This project uses Damper MCP for task tracking. **You MUST follow this workflow.
|
|
|
721
753
|
- \`add_to_changelog\` - Manually add completed tasks to a changelog
|
|
722
754
|
- \`publish_changelog\` - Publish with optional email notifications and Twitter posting
|
|
723
755
|
|
|
756
|
+
### Project Settings
|
|
757
|
+
- \`get_project_settings\` - View current settings (completion checklist, etc.)
|
|
758
|
+
- \`update_project_settings\` - Configure completion checklist and other agent-relevant settings
|
|
759
|
+
|
|
724
760
|
### At Session End (MANDATORY)
|
|
725
761
|
- ALWAYS call \`complete_task\` (if done) or \`abandon_task\` (if stopping early)
|
|
726
762
|
- NEVER leave a started task without completing or abandoning it
|
|
763
|
+
- If the project has a **completion checklist** (shown in \`start_task\` response), you MUST pass all items as \`confirmations\` when calling \`complete_task\`
|
|
727
764
|
- If you learned something about the codebase, consider updating project context
|
|
728
765
|
|
|
729
766
|
### Why This Matters
|
|
@@ -1842,6 +1879,74 @@ server.registerTool('publish_changelog', {
|
|
|
1842
1879
|
},
|
|
1843
1880
|
};
|
|
1844
1881
|
});
|
|
1882
|
+
// ==================== Project Settings Tools ====================
|
|
1883
|
+
// Tool: Get project settings
|
|
1884
|
+
server.registerTool('get_project_settings', {
|
|
1885
|
+
title: 'Get Project Settings',
|
|
1886
|
+
description: 'View current project settings relevant to agents (completion checklist, etc.).\n\n' +
|
|
1887
|
+
'**When to use**: Check what completion checklist items are configured, or verify settings before updating them.',
|
|
1888
|
+
inputSchema: z.object({}),
|
|
1889
|
+
outputSchema: z.object({
|
|
1890
|
+
completionChecklist: z.array(z.string()),
|
|
1891
|
+
}),
|
|
1892
|
+
annotations: {
|
|
1893
|
+
readOnlyHint: true,
|
|
1894
|
+
destructiveHint: false,
|
|
1895
|
+
idempotentHint: true,
|
|
1896
|
+
openWorldHint: false,
|
|
1897
|
+
},
|
|
1898
|
+
}, async () => {
|
|
1899
|
+
const data = await api('GET', '/api/agent/project/settings');
|
|
1900
|
+
if (!data.completionChecklist || data.completionChecklist.length === 0) {
|
|
1901
|
+
return {
|
|
1902
|
+
content: [{ type: 'text', text: 'No completion checklist configured.' }],
|
|
1903
|
+
structuredContent: data,
|
|
1904
|
+
};
|
|
1905
|
+
}
|
|
1906
|
+
const lines = data.completionChecklist.map((item, i) => `${i + 1}. ${item}`);
|
|
1907
|
+
return {
|
|
1908
|
+
content: [{ type: 'text', text: `Completion checklist:\n${lines.join('\n')}` }],
|
|
1909
|
+
structuredContent: data,
|
|
1910
|
+
};
|
|
1911
|
+
});
|
|
1912
|
+
// Tool: Update project settings
|
|
1913
|
+
server.registerTool('update_project_settings', {
|
|
1914
|
+
title: 'Update Project Settings',
|
|
1915
|
+
description: 'Configure agent-relevant project settings.\n\n' +
|
|
1916
|
+
'**completionChecklist**: Items agents must confirm before completing tasks. ' +
|
|
1917
|
+
'Pass an empty array to clear the checklist.\n\n' +
|
|
1918
|
+
'Example items: "All tests pass", "Build succeeds", "Migration created if schema changed"',
|
|
1919
|
+
inputSchema: z.object({
|
|
1920
|
+
completionChecklist: z.array(z.string()).optional()
|
|
1921
|
+
.describe('Checklist items agents must confirm when completing tasks. Pass [] to clear.'),
|
|
1922
|
+
}),
|
|
1923
|
+
outputSchema: z.object({
|
|
1924
|
+
completionChecklist: z.array(z.string()),
|
|
1925
|
+
message: z.string(),
|
|
1926
|
+
}),
|
|
1927
|
+
annotations: {
|
|
1928
|
+
readOnlyHint: false,
|
|
1929
|
+
destructiveHint: false,
|
|
1930
|
+
idempotentHint: true,
|
|
1931
|
+
openWorldHint: false,
|
|
1932
|
+
},
|
|
1933
|
+
}, async ({ completionChecklist }) => {
|
|
1934
|
+
const body = {};
|
|
1935
|
+
if (completionChecklist !== undefined)
|
|
1936
|
+
body.completionChecklist = completionChecklist;
|
|
1937
|
+
const result = await api('PATCH', '/api/agent/project/settings', body);
|
|
1938
|
+
if (result.completionChecklist.length === 0) {
|
|
1939
|
+
return {
|
|
1940
|
+
content: [{ type: 'text', text: 'Completion checklist cleared.' }],
|
|
1941
|
+
structuredContent: result,
|
|
1942
|
+
};
|
|
1943
|
+
}
|
|
1944
|
+
const lines = result.completionChecklist.map((item, i) => `${i + 1}. ${item}`);
|
|
1945
|
+
return {
|
|
1946
|
+
content: [{ type: 'text', text: `${result.message}\n${lines.join('\n')}` }],
|
|
1947
|
+
structuredContent: result,
|
|
1948
|
+
};
|
|
1949
|
+
});
|
|
1845
1950
|
// Start
|
|
1846
1951
|
async function main() {
|
|
1847
1952
|
const transport = new StdioServerTransport();
|
package/package.json
CHANGED
package/dist/formatters.test.js
DELETED
|
@@ -1,126 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Tests for MCP response formatters
|
|
3
|
-
*/
|
|
4
|
-
import { describe, it, expect } from 'bun:test';
|
|
5
|
-
import { formatStartTaskResponse, formatCompleteTaskResponse, formatAbandonTaskResponse, } from './formatters';
|
|
6
|
-
describe('formatStartTaskResponse', () => {
|
|
7
|
-
it('includes workflow steps', () => {
|
|
8
|
-
const result = formatStartTaskResponse({
|
|
9
|
-
id: 'TASK-1',
|
|
10
|
-
status: 'in_progress',
|
|
11
|
-
message: 'Task started',
|
|
12
|
-
});
|
|
13
|
-
expect(result).toContain('**📋 Workflow:**');
|
|
14
|
-
expect(result).toContain('`add_note`: "Session started: <goal>"');
|
|
15
|
-
expect(result).toContain('`add_note`: "Session end: <summary, next steps>"');
|
|
16
|
-
expect(result).toContain('`complete_task` or `abandon_task`');
|
|
17
|
-
});
|
|
18
|
-
it('shows empty context hint when context is empty', () => {
|
|
19
|
-
const result = formatStartTaskResponse({
|
|
20
|
-
id: 'TASK-1',
|
|
21
|
-
status: 'in_progress',
|
|
22
|
-
message: 'Task started',
|
|
23
|
-
context: {
|
|
24
|
-
isEmpty: true,
|
|
25
|
-
index: [],
|
|
26
|
-
hint: 'No docs available yet',
|
|
27
|
-
},
|
|
28
|
-
});
|
|
29
|
-
expect(result).toContain('📚 No docs available yet');
|
|
30
|
-
});
|
|
31
|
-
it('lists context sections with star for relevant ones', () => {
|
|
32
|
-
const result = formatStartTaskResponse({
|
|
33
|
-
id: 'TASK-1',
|
|
34
|
-
status: 'in_progress',
|
|
35
|
-
message: 'Task started',
|
|
36
|
-
context: {
|
|
37
|
-
isEmpty: false,
|
|
38
|
-
index: [
|
|
39
|
-
{ section: 'overview', preview: 'Project overview', updatedAt: '2024-01-01' },
|
|
40
|
-
{ section: 'api', preview: 'API docs', updatedAt: '2024-01-01' },
|
|
41
|
-
{ section: 'testing', preview: 'Testing guide', updatedAt: '2024-01-01' },
|
|
42
|
-
],
|
|
43
|
-
relevantSections: ['api', 'testing'],
|
|
44
|
-
},
|
|
45
|
-
});
|
|
46
|
-
expect(result).toContain('**Project context available:**');
|
|
47
|
-
expect(result).toContain('• overview');
|
|
48
|
-
expect(result).toContain('• api ⭐');
|
|
49
|
-
expect(result).toContain('• testing ⭐');
|
|
50
|
-
expect(result).toContain('Relevant for this task: api, testing');
|
|
51
|
-
expect(result).toContain('Use get_context_section to fetch full content.');
|
|
52
|
-
});
|
|
53
|
-
it('shows basic message and task ID', () => {
|
|
54
|
-
const result = formatStartTaskResponse({
|
|
55
|
-
id: 'TASK-123',
|
|
56
|
-
status: 'in_progress',
|
|
57
|
-
message: 'Locked successfully',
|
|
58
|
-
});
|
|
59
|
-
expect(result).toContain('Started TASK-123: Locked successfully');
|
|
60
|
-
});
|
|
61
|
-
});
|
|
62
|
-
describe('formatCompleteTaskResponse', () => {
|
|
63
|
-
it('shows completion message', () => {
|
|
64
|
-
const result = formatCompleteTaskResponse({
|
|
65
|
-
id: 'TASK-1',
|
|
66
|
-
status: 'done',
|
|
67
|
-
});
|
|
68
|
-
expect(result).toContain('✅ Completed TASK-1');
|
|
69
|
-
});
|
|
70
|
-
it('shows documentation section with affected sections', () => {
|
|
71
|
-
const result = formatCompleteTaskResponse({
|
|
72
|
-
id: 'TASK-1',
|
|
73
|
-
status: 'done',
|
|
74
|
-
documentation: {
|
|
75
|
-
hasContext: true,
|
|
76
|
-
affectedSections: ['api', 'overview'],
|
|
77
|
-
reminder: 'Please review and update docs',
|
|
78
|
-
},
|
|
79
|
-
});
|
|
80
|
-
expect(result).toContain('**📚 Documentation:**');
|
|
81
|
-
expect(result).toContain('May need updates: api, overview');
|
|
82
|
-
expect(result).toContain('Please review and update docs');
|
|
83
|
-
});
|
|
84
|
-
it('skips affected sections line when empty', () => {
|
|
85
|
-
const result = formatCompleteTaskResponse({
|
|
86
|
-
id: 'TASK-1',
|
|
87
|
-
status: 'done',
|
|
88
|
-
documentation: {
|
|
89
|
-
hasContext: true,
|
|
90
|
-
affectedSections: [],
|
|
91
|
-
reminder: 'No changes needed',
|
|
92
|
-
},
|
|
93
|
-
});
|
|
94
|
-
expect(result).toContain('**📚 Documentation:**');
|
|
95
|
-
expect(result).not.toContain('May need updates:');
|
|
96
|
-
expect(result).toContain('No changes needed');
|
|
97
|
-
});
|
|
98
|
-
it('handles missing documentation gracefully', () => {
|
|
99
|
-
const result = formatCompleteTaskResponse({
|
|
100
|
-
id: 'TASK-1',
|
|
101
|
-
status: 'done',
|
|
102
|
-
});
|
|
103
|
-
expect(result).not.toContain('**📚 Documentation:**');
|
|
104
|
-
expect(result).toBe('✅ Completed TASK-1');
|
|
105
|
-
});
|
|
106
|
-
});
|
|
107
|
-
describe('formatAbandonTaskResponse', () => {
|
|
108
|
-
it('shows tip when no summary provided', () => {
|
|
109
|
-
const result = formatAbandonTaskResponse({ id: 'TASK-1', status: 'planned', message: 'Task abandoned' }, false);
|
|
110
|
-
expect(result).toContain('⏸️ Abandoned TASK-1: Task abandoned');
|
|
111
|
-
expect(result).toContain('⚠️ Tip: Include a summary next time for better handoff.');
|
|
112
|
-
expect(result).toContain('💡 Task returned to "planned". Notes preserved for next agent.');
|
|
113
|
-
});
|
|
114
|
-
it('does not show tip when summary provided', () => {
|
|
115
|
-
const result = formatAbandonTaskResponse({ id: 'TASK-1', status: 'planned', message: 'Task abandoned with handoff' }, true);
|
|
116
|
-
expect(result).toContain('⏸️ Abandoned TASK-1');
|
|
117
|
-
expect(result).not.toContain('⚠️ Tip:');
|
|
118
|
-
expect(result).toContain('💡 Task returned to "planned"');
|
|
119
|
-
});
|
|
120
|
-
it('always shows notes preserved message', () => {
|
|
121
|
-
const withSummary = formatAbandonTaskResponse({ id: 'TASK-1', status: 'planned', message: 'Done' }, true);
|
|
122
|
-
const withoutSummary = formatAbandonTaskResponse({ id: 'TASK-1', status: 'planned', message: 'Done' }, false);
|
|
123
|
-
expect(withSummary).toContain('Notes preserved for next agent');
|
|
124
|
-
expect(withoutSummary).toContain('Notes preserved for next agent');
|
|
125
|
-
});
|
|
126
|
-
});
|