@damper/mcp 0.1.13 → 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.
Files changed (3) hide show
  1. package/README.md +36 -2
  2. package/dist/index.js +229 -5
  3. 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
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:
@@ -160,6 +184,7 @@ When you start a task, it's locked to prevent other agents from working on it si
160
184
 
161
185
  ## Usage Examples
162
186
 
187
+ ### Tasks
163
188
  ```
164
189
  > What tasks are available?
165
190
  > List bugs I can fix
@@ -173,6 +198,15 @@ When you start a task, it's locked to prevent other agents from working on it si
173
198
  > Abandon the current task, I'm blocked
174
199
  ```
175
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
+
176
210
  ## Environment
177
211
 
178
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.1.11',
37
+ version: '0.2.0',
38
38
  });
39
39
  // Output schemas
40
40
  const SubtaskProgressSchema = z.object({
@@ -315,11 +315,23 @@ server.registerTool('create_task', {
315
315
  structuredContent: result,
316
316
  };
317
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
+ });
318
329
  // Tool: Start task
319
330
  server.registerTool('start_task', {
320
331
  title: 'Start Task',
321
332
  description: 'Lock and start a task. Fails if locked by another agent unless force=true. ' +
322
- '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.',
323
335
  inputSchema: z.object({
324
336
  taskId: z.string(),
325
337
  force: z.boolean().optional().describe('Take over lock from another agent'),
@@ -330,6 +342,7 @@ server.registerTool('start_task', {
330
342
  message: z.string(),
331
343
  lockedBy: z.string().optional(),
332
344
  lockedAt: z.string().optional(),
345
+ context: ContextIndexSchema.optional(),
333
346
  }),
334
347
  annotations: {
335
348
  readOnlyHint: false,
@@ -340,8 +353,26 @@ server.registerTool('start_task', {
340
353
  }, async ({ taskId, force }) => {
341
354
  try {
342
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
+ }
343
374
  return {
344
- content: [{ type: 'text', text: `Started ${result.id}: ${result.message}` }],
375
+ content: [{ type: 'text', text: lines.join('\n') }],
345
376
  structuredContent: result,
346
377
  };
347
378
  }
@@ -466,10 +497,16 @@ server.registerTool('update_subtask', {
466
497
  },
467
498
  };
468
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
+ });
469
506
  // Tool: Complete task
470
507
  server.registerTool('complete_task', {
471
508
  title: 'Complete Task',
472
- description: 'Mark task done with summary.',
509
+ description: 'Mark task done with summary. Returns documentation update suggestions.',
473
510
  inputSchema: z.object({
474
511
  taskId: z.string(),
475
512
  summary: z.string().describe('What was implemented'),
@@ -477,6 +514,7 @@ server.registerTool('complete_task', {
477
514
  outputSchema: z.object({
478
515
  id: z.string(),
479
516
  status: z.string(),
517
+ documentation: DocumentationSchema.optional(),
480
518
  }),
481
519
  annotations: {
482
520
  readOnlyHint: false,
@@ -486,8 +524,12 @@ server.registerTool('complete_task', {
486
524
  },
487
525
  }, async ({ taskId, summary }) => {
488
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
+ }
489
531
  return {
490
- content: [{ type: 'text', text: `Completed ${result.id}` }],
532
+ content: [{ type: 'text', text: lines.join('\n') }],
491
533
  structuredContent: result,
492
534
  };
493
535
  });
@@ -517,6 +559,188 @@ server.registerTool('abandon_task', {
517
559
  structuredContent: result,
518
560
  };
519
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
+ });
520
744
  // Tool: List feedback
521
745
  server.registerTool('list_feedback', {
522
746
  title: 'List Feedback',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@damper/mcp",
3
- "version": "0.1.13",
3
+ "version": "0.2.0",
4
4
  "description": "MCP server for Damper task management",
5
5
  "author": "Damper <hello@usedamper.com>",
6
6
  "repository": {