@damper/mcp 0.1.1

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 ADDED
@@ -0,0 +1,73 @@
1
+ # @damper/mcp
2
+
3
+ MCP server for [Damper](https://usedamper.com) - enables AI agents to manage roadmap tasks.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm install -g @damper/mcp
9
+ ```
10
+
11
+ ## Setup
12
+
13
+ ### 1. Get API Key
14
+ Damper → Settings → API Keys → Generate
15
+
16
+ ### 2. Configure Your AI Agent
17
+
18
+ **Claude Code** (`~/.claude.json`):
19
+ ```json
20
+ {
21
+ "mcpServers": {
22
+ "damper": {
23
+ "command": "damper",
24
+ "env": { "DAMPER_API_KEY": "dmp_..." }
25
+ }
26
+ }
27
+ }
28
+ ```
29
+
30
+ **Cursor** (`.cursor/mcp.json`):
31
+ ```json
32
+ {
33
+ "mcpServers": {
34
+ "damper": {
35
+ "command": "npx",
36
+ "args": ["@damper/mcp"],
37
+ "env": { "DAMPER_API_KEY": "dmp_..." }
38
+ }
39
+ }
40
+ }
41
+ ```
42
+
43
+ ## Tools
44
+
45
+ | Tool | Description |
46
+ |------|-------------|
47
+ | `list_tasks` | Get roadmap tasks |
48
+ | `get_task` | Task details + feedback |
49
+ | `create_task` | Create task (for TODO imports) |
50
+ | `start_task` | Mark in-progress |
51
+ | `add_note` | Add progress note |
52
+ | `complete_task` | Mark done |
53
+ | `list_feedback` | Browse user feedback |
54
+ | `get_feedback` | Feedback details |
55
+
56
+ ## Usage
57
+
58
+ ```
59
+ > What tasks are available?
60
+ > Import my TODO.md into Damper
61
+ > Work on the dark mode task
62
+ ```
63
+
64
+ ## Environment
65
+
66
+ | Variable | Required | Default |
67
+ |----------|----------|---------|
68
+ | `DAMPER_API_KEY` | Yes | - |
69
+ | `DAMPER_API_URL` | No | https://api.usedamper.com |
70
+
71
+ ## License
72
+
73
+ MIT
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/index.js ADDED
@@ -0,0 +1,198 @@
1
+ #!/usr/bin/env node
2
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
3
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
4
+ import { z } from 'zod';
5
+ // Config
6
+ const API_KEY = process.env.DAMPER_API_KEY;
7
+ const API_URL = process.env.DAMPER_API_URL || 'https://api.usedamper.com';
8
+ if (!API_KEY) {
9
+ console.error('DAMPER_API_KEY required. Get one from Damper → Settings → API Keys');
10
+ process.exit(1);
11
+ }
12
+ // API Client
13
+ async function api(method, path, body) {
14
+ const res = await fetch(`${API_URL}${path}`, {
15
+ method,
16
+ headers: {
17
+ Authorization: `Bearer ${API_KEY}`,
18
+ 'Content-Type': 'application/json',
19
+ },
20
+ body: body ? JSON.stringify(body) : undefined,
21
+ });
22
+ if (!res.ok) {
23
+ const err = await res.json().catch(() => ({}));
24
+ throw new Error(err.error || `HTTP ${res.status}`);
25
+ }
26
+ return res.json();
27
+ }
28
+ // Server
29
+ const server = new McpServer({
30
+ name: 'damper',
31
+ version: '0.1.0',
32
+ });
33
+ // Tool: List tasks
34
+ server.registerTool('list_tasks', {
35
+ title: 'List Tasks',
36
+ description: 'Get roadmap tasks. Returns planned/in-progress by default.',
37
+ inputSchema: z.object({
38
+ status: z.enum(['planned', 'in_progress', 'done', 'all']).optional(),
39
+ limit: z.number().optional(),
40
+ }),
41
+ }, async ({ status, limit }) => {
42
+ const params = new URLSearchParams();
43
+ if (status)
44
+ params.set('status', status);
45
+ if (limit)
46
+ params.set('limit', String(limit));
47
+ const query = params.toString();
48
+ const data = await api('GET', `/api/agent/tasks${query ? `?${query}` : ''}`);
49
+ if (!data.tasks.length) {
50
+ return { content: [{ type: 'text', text: `No tasks in "${data.project.name}"` }] };
51
+ }
52
+ const lines = data.tasks.map((t) => {
53
+ const p = t.priority === 'high' ? '🔴' : t.priority === 'medium' ? '🟡' : '⚪';
54
+ return `• ${t.id}: ${t.title} [${t.status}] ${p}${t.hasImplementationPlan ? ' 📋' : ''}`;
55
+ });
56
+ return {
57
+ content: [{ type: 'text', text: `Tasks in "${data.project.name}":\n${lines.join('\n')}` }],
58
+ };
59
+ });
60
+ // Tool: Get task
61
+ server.registerTool('get_task', {
62
+ title: 'Get Task',
63
+ description: 'Get task details including description, plan, and linked feedback.',
64
+ inputSchema: z.object({
65
+ taskId: z.string().describe('Task ID'),
66
+ }),
67
+ }, async ({ taskId }) => {
68
+ const t = await api('GET', `/api/agent/tasks/${taskId}`);
69
+ const parts = [
70
+ `# ${t.title}`,
71
+ `Status: ${t.status} | Score: ${t.voteScore}`,
72
+ ];
73
+ if (t.description)
74
+ parts.push(`\n## Description\n${t.description}`);
75
+ if (t.implementationPlan)
76
+ parts.push(`\n## Plan\n${t.implementationPlan}`);
77
+ if (t.agentNotes)
78
+ parts.push(`\n## Notes\n${t.agentNotes}`);
79
+ if (t.feedback.length) {
80
+ parts.push(`\n## Feedback (${t.feedback.length})`);
81
+ t.feedback.forEach((f) => parts.push(`- ${f.title} (${f.voterCount} votes)`));
82
+ }
83
+ return { content: [{ type: 'text', text: parts.join('\n') }] };
84
+ });
85
+ // Tool: Create task
86
+ server.registerTool('create_task', {
87
+ title: 'Create Task',
88
+ description: 'Create a new roadmap task. Use for importing from TODO files.',
89
+ inputSchema: z.object({
90
+ title: z.string(),
91
+ description: z.string().optional(),
92
+ status: z.enum(['planned', 'in_progress']).optional(),
93
+ implementationPlan: z.string().optional(),
94
+ }),
95
+ }, async (args) => {
96
+ const result = await api('POST', '/api/agent/tasks', args);
97
+ return {
98
+ content: [{ type: 'text', text: `Created: ${result.id} "${result.title}" [${result.status}]` }],
99
+ };
100
+ });
101
+ // Tool: Start task
102
+ server.registerTool('start_task', {
103
+ title: 'Start Task',
104
+ description: 'Mark task as in-progress. Call before working on it.',
105
+ inputSchema: z.object({
106
+ taskId: z.string(),
107
+ }),
108
+ }, async ({ taskId }) => {
109
+ const result = await api('POST', `/api/agent/tasks/${taskId}/start`);
110
+ return { content: [{ type: 'text', text: `Started ${result.id}: ${result.message}` }] };
111
+ });
112
+ // Tool: Add note
113
+ server.registerTool('add_note', {
114
+ title: 'Add Note',
115
+ description: 'Add progress note to task.',
116
+ inputSchema: z.object({
117
+ taskId: z.string(),
118
+ note: z.string(),
119
+ }),
120
+ }, async ({ taskId, note }) => {
121
+ await api('POST', `/api/agent/tasks/${taskId}/notes`, { note });
122
+ return { content: [{ type: 'text', text: `Note added to ${taskId}` }] };
123
+ });
124
+ // Tool: Complete task
125
+ server.registerTool('complete_task', {
126
+ title: 'Complete Task',
127
+ description: 'Mark task done with summary.',
128
+ inputSchema: z.object({
129
+ taskId: z.string(),
130
+ summary: z.string().describe('What was implemented'),
131
+ }),
132
+ }, async ({ taskId, summary }) => {
133
+ const result = await api('POST', `/api/agent/tasks/${taskId}/complete`, { summary });
134
+ return { content: [{ type: 'text', text: `Completed ${result.id}` }] };
135
+ });
136
+ // Tool: List feedback
137
+ server.registerTool('list_feedback', {
138
+ title: 'List Feedback',
139
+ description: 'Get user feedback. Useful for understanding user needs.',
140
+ inputSchema: z.object({
141
+ status: z.enum(['new', 'under_review', 'planned', 'in_progress', 'done', 'closed']).optional(),
142
+ limit: z.number().optional(),
143
+ }),
144
+ }, async ({ status, limit }) => {
145
+ const params = new URLSearchParams();
146
+ if (status)
147
+ params.set('status', status);
148
+ if (limit)
149
+ params.set('limit', String(limit));
150
+ const query = params.toString();
151
+ const data = await api('GET', `/api/agent/feedback${query ? `?${query}` : ''}`);
152
+ if (!data.feedback.length) {
153
+ return { content: [{ type: 'text', text: 'No feedback found' }] };
154
+ }
155
+ const lines = data.feedback.map((f) => {
156
+ const link = f.linkedTaskId ? ` → ${f.linkedTaskId}` : '';
157
+ return `• ${f.id}: ${f.title} [${f.type}] (${f.voterCount} votes)${link}`;
158
+ });
159
+ return { content: [{ type: 'text', text: `Feedback:\n${lines.join('\n')}` }] };
160
+ });
161
+ // Tool: Get feedback
162
+ server.registerTool('get_feedback', {
163
+ title: 'Get Feedback',
164
+ description: 'Get feedback details with votes and comments.',
165
+ inputSchema: z.object({
166
+ feedbackId: z.string(),
167
+ }),
168
+ }, async ({ feedbackId }) => {
169
+ const f = await api('GET', `/api/agent/feedback/${feedbackId}`);
170
+ const parts = [
171
+ `# ${f.title}`,
172
+ `Type: ${f.type} | Status: ${f.status} | Score: ${f.voteScore}`,
173
+ f.linkedTaskId ? `Linked: ${f.linkedTaskId}` : '',
174
+ `\n${f.description}`,
175
+ ].filter(Boolean);
176
+ if (f.voters.length) {
177
+ parts.push(`\n## Voters (${f.voters.length})`);
178
+ f.voters.slice(0, 5).forEach((v) => parts.push(`- ${v.email} (${v.plan})`));
179
+ if (f.voters.length > 5)
180
+ parts.push(`... and ${f.voters.length - 5} more`);
181
+ }
182
+ if (f.comments.length) {
183
+ parts.push(`\n## Comments (${f.comments.length})`);
184
+ f.comments.slice(0, 3).forEach((c) => parts.push(`**${c.author}**: ${c.body}`));
185
+ if (f.comments.length > 3)
186
+ parts.push(`... and ${f.comments.length - 3} more`);
187
+ }
188
+ return { content: [{ type: 'text', text: parts.join('\n') }] };
189
+ });
190
+ // Start
191
+ async function main() {
192
+ const transport = new StdioServerTransport();
193
+ await server.connect(transport);
194
+ }
195
+ main().catch((e) => {
196
+ console.error('Fatal:', e);
197
+ process.exit(1);
198
+ });
package/package.json ADDED
@@ -0,0 +1,38 @@
1
+ {
2
+ "name": "@damper/mcp",
3
+ "version": "0.1.1",
4
+ "description": "MCP server for Damper task management",
5
+ "author": "Damper <hello@usedamper.com>",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "https://github.com/usedamper/damper.git",
9
+ "directory": "packages/mcp"
10
+ },
11
+ "homepage": "https://usedamper.com",
12
+ "type": "module",
13
+ "bin": {
14
+ "damper": "./dist/index.js"
15
+ },
16
+ "main": "./dist/index.js",
17
+ "types": "./dist/index.d.ts",
18
+ "files": ["dist"],
19
+ "scripts": {
20
+ "build": "tsc",
21
+ "dev": "tsx src/index.ts",
22
+ "prepublishOnly": "bun run build"
23
+ },
24
+ "dependencies": {
25
+ "@modelcontextprotocol/sdk": "^1.25.0",
26
+ "zod": "^3.24.0"
27
+ },
28
+ "devDependencies": {
29
+ "@types/node": "^22.0.0",
30
+ "tsx": "^4.0.0",
31
+ "typescript": "^5.3.0"
32
+ },
33
+ "engines": {
34
+ "node": ">=18"
35
+ },
36
+ "keywords": ["mcp", "damper", "ai", "task-management"],
37
+ "license": "MIT"
38
+ }