@damper/mcp 0.1.13 → 0.3.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 +113 -2
- package/dist/index.js +570 -5
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -69,20 +69,121 @@ 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 |
|
|
88
|
+
| `list_templates` | List available code templates |
|
|
89
|
+
| `get_template` | Get template content |
|
|
90
|
+
| `update_template` | Create/update a template |
|
|
91
|
+
| `sync_templates` | Bulk upload templates |
|
|
92
|
+
| `list_modules` | List monorepo modules |
|
|
93
|
+
| `get_module` | Get module details |
|
|
94
|
+
| `update_module` | Register/update a module |
|
|
95
|
+
| `sync_modules` | Bulk upload module registry |
|
|
96
|
+
|
|
97
|
+
### Project Context
|
|
98
|
+
|
|
99
|
+
AI agents can store and retrieve project documentation to help future agents work more effectively.
|
|
100
|
+
|
|
101
|
+
| Tool | Description |
|
|
102
|
+
|------|-------------|
|
|
103
|
+
| `get_project_context` | Get context index with section previews. Optionally highlights sections relevant to a task. |
|
|
104
|
+
| `list_context_sections` | List all available context sections |
|
|
105
|
+
| `get_context_section` | Get full content of a specific section. Supports glob patterns (`api/*`, `api/**`). |
|
|
106
|
+
| `update_context_section` | Upload/update a context section with optional tags (requires user permission) |
|
|
107
|
+
| `sync_project_context` | Bulk upload multiple sections at once (requires user permission) |
|
|
108
|
+
|
|
109
|
+
**How it works:**
|
|
110
|
+
1. When you `start_task`, you receive a context index showing available documentation
|
|
111
|
+
2. Fetch relevant sections with `get_context_section` to understand the codebase
|
|
112
|
+
3. After completing work, `complete_task` reminds you to update docs if needed
|
|
113
|
+
4. Use `update_context_section` to share knowledge for future agents
|
|
114
|
+
|
|
115
|
+
**Common sections:** `overview`, `api`, `database`, `services`, `components`, `testing`, `auth`, `deployment`
|
|
116
|
+
|
|
117
|
+
⚠️ **Security:** Never include sensitive data (API keys, secrets, credentials, connection strings) in context uploads. Users can review and delete context from Settings → AI Context.
|
|
118
|
+
|
|
119
|
+
### Hierarchical Sections
|
|
120
|
+
|
|
121
|
+
For monorepos or large projects, organize sections hierarchically:
|
|
122
|
+
|
|
123
|
+
```
|
|
124
|
+
> Update the api/architecture section
|
|
125
|
+
> Get all api sections: api/**
|
|
126
|
+
> Get direct children only: api/*
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
**When to use**: When a project has many related sections that benefit from organization (e.g., `api/architecture`, `api/endpoints`, `api/testing`).
|
|
130
|
+
|
|
131
|
+
**When to skip**: For simple projects, flat section names like `overview`, `api`, `testing` are sufficient.
|
|
132
|
+
|
|
133
|
+
### Section Tags
|
|
134
|
+
|
|
135
|
+
Add tags to sections for better automatic matching with task labels:
|
|
136
|
+
|
|
137
|
+
```
|
|
138
|
+
> Update api section with tags: backend, critical
|
|
139
|
+
> Sections tagged "backend" will auto-surface for tasks labeled "backend"
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
**When to use**: When you have multiple related sections that should surface together (e.g., all docs tagged "backend" for backend tasks).
|
|
143
|
+
|
|
144
|
+
**When to skip**: For simple projects with few sections. Tags add complexity without much benefit.
|
|
145
|
+
|
|
146
|
+
### Code Templates
|
|
147
|
+
|
|
148
|
+
Store and retrieve code templates for maintaining consistency.
|
|
149
|
+
|
|
150
|
+
| Tool | Description |
|
|
151
|
+
|------|-------------|
|
|
152
|
+
| `list_templates` | List available templates with previews |
|
|
153
|
+
| `get_template` | Get full template content |
|
|
154
|
+
| `update_template` | Create/update a template |
|
|
155
|
+
| `sync_templates` | Bulk upload templates |
|
|
156
|
+
|
|
157
|
+
```
|
|
158
|
+
> List available templates
|
|
159
|
+
> Get the service-module template
|
|
160
|
+
> Create a template for API controllers
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
**When to use**: Check for templates before creating new service files, components, or test files. If you notice repetitive patterns, extract them into templates.
|
|
164
|
+
|
|
165
|
+
**When to skip**: If the project doesn't have templates defined, create files using patterns you observe in the existing codebase.
|
|
166
|
+
|
|
167
|
+
### Module Registry
|
|
168
|
+
|
|
169
|
+
Track monorepo package structure for better cross-package awareness.
|
|
170
|
+
|
|
171
|
+
| Tool | Description |
|
|
172
|
+
|------|-------------|
|
|
173
|
+
| `list_modules` | List registered modules with paths and ports |
|
|
174
|
+
| `get_module` | Get module details including dependencies |
|
|
175
|
+
| `update_module` | Register/update a module |
|
|
176
|
+
| `sync_modules` | Bulk upload module registry |
|
|
177
|
+
|
|
178
|
+
```
|
|
179
|
+
> List all modules in this monorepo
|
|
180
|
+
> What port does the dashboard run on?
|
|
181
|
+
> Register the new shared package
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
**When to use**: For monorepos with multiple packages. Helps understand package structure and dependencies before making cross-package changes.
|
|
185
|
+
|
|
186
|
+
**When to skip**: For single-package projects, the module registry isn't needed.
|
|
86
187
|
|
|
87
188
|
### Task Types
|
|
88
189
|
|
|
@@ -160,6 +261,7 @@ When you start a task, it's locked to prevent other agents from working on it si
|
|
|
160
261
|
|
|
161
262
|
## Usage Examples
|
|
162
263
|
|
|
264
|
+
### Tasks
|
|
163
265
|
```
|
|
164
266
|
> What tasks are available?
|
|
165
267
|
> List bugs I can fix
|
|
@@ -173,6 +275,15 @@ When you start a task, it's locked to prevent other agents from working on it si
|
|
|
173
275
|
> Abandon the current task, I'm blocked
|
|
174
276
|
```
|
|
175
277
|
|
|
278
|
+
### Project Context
|
|
279
|
+
```
|
|
280
|
+
> What documentation is available for this project?
|
|
281
|
+
> Get the API context section
|
|
282
|
+
> After analyzing this codebase, upload an overview
|
|
283
|
+
> Update the database section with the new schema
|
|
284
|
+
> Sync all my documentation to Damper
|
|
285
|
+
```
|
|
286
|
+
|
|
176
287
|
## Environment
|
|
177
288
|
|
|
178
289
|
| 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.3.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:
|
|
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:
|
|
532
|
+
content: [{ type: 'text', text: lines.join('\n') }],
|
|
491
533
|
structuredContent: result,
|
|
492
534
|
};
|
|
493
535
|
});
|
|
@@ -517,6 +559,241 @@ 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
|
+
tags: z.array(z.string()).optional(),
|
|
575
|
+
appliesTo: z.array(z.string()).optional(),
|
|
576
|
+
})),
|
|
577
|
+
isEmpty: z.boolean(),
|
|
578
|
+
}),
|
|
579
|
+
annotations: {
|
|
580
|
+
readOnlyHint: true,
|
|
581
|
+
destructiveHint: false,
|
|
582
|
+
idempotentHint: true,
|
|
583
|
+
openWorldHint: false,
|
|
584
|
+
},
|
|
585
|
+
}, async () => {
|
|
586
|
+
const data = await api('GET', '/api/agent/context');
|
|
587
|
+
if (data.isEmpty) {
|
|
588
|
+
return {
|
|
589
|
+
content: [{ type: 'text', text: 'No project context available. Use sync_project_context to upload documentation.' }],
|
|
590
|
+
structuredContent: data,
|
|
591
|
+
};
|
|
592
|
+
}
|
|
593
|
+
const lines = data.sections.map((s) => {
|
|
594
|
+
const tags = s.tags && s.tags.length > 0 ? ` [${s.tags.join(', ')}]` : '';
|
|
595
|
+
return `• ${s.section}${tags} (${s.source}, updated ${s.updatedAt.split('T')[0]})`;
|
|
596
|
+
});
|
|
597
|
+
return {
|
|
598
|
+
content: [{ type: 'text', text: `Context sections:\n${lines.join('\n')}` }],
|
|
599
|
+
structuredContent: data,
|
|
600
|
+
};
|
|
601
|
+
});
|
|
602
|
+
// Tool: Get context section
|
|
603
|
+
server.registerTool('get_context_section', {
|
|
604
|
+
title: 'Get Context Section',
|
|
605
|
+
description: 'Get full content of a specific context section. Supports glob patterns for hierarchical sections.\n\n' +
|
|
606
|
+
'**Hierarchical paths**: For monorepos or large projects, sections can be organized hierarchically ' +
|
|
607
|
+
'(e.g., "api/architecture", "api/endpoints/auth"). Use glob patterns to fetch multiple:\n' +
|
|
608
|
+
'- `api/*` - direct children only (api/architecture, api/testing)\n' +
|
|
609
|
+
'- `api/**` - all descendants (api/architecture, api/endpoints/auth)',
|
|
610
|
+
inputSchema: z.object({
|
|
611
|
+
section: z.string().describe('Section name or glob pattern (e.g., "overview", "api/architecture", "api/*", "api/**")'),
|
|
612
|
+
}),
|
|
613
|
+
outputSchema: z.union([
|
|
614
|
+
z.object({
|
|
615
|
+
section: z.string(),
|
|
616
|
+
content: z.string(),
|
|
617
|
+
updatedAt: z.string(),
|
|
618
|
+
source: z.string(),
|
|
619
|
+
tags: z.array(z.string()).optional(),
|
|
620
|
+
appliesTo: z.array(z.string()).optional(),
|
|
621
|
+
}),
|
|
622
|
+
z.object({
|
|
623
|
+
pattern: z.string(),
|
|
624
|
+
sections: z.array(z.object({
|
|
625
|
+
section: z.string(),
|
|
626
|
+
content: z.string(),
|
|
627
|
+
updatedAt: z.string(),
|
|
628
|
+
source: z.string(),
|
|
629
|
+
tags: z.array(z.string()).optional(),
|
|
630
|
+
appliesTo: z.array(z.string()).optional(),
|
|
631
|
+
})),
|
|
632
|
+
}),
|
|
633
|
+
]),
|
|
634
|
+
annotations: {
|
|
635
|
+
readOnlyHint: true,
|
|
636
|
+
destructiveHint: false,
|
|
637
|
+
idempotentHint: true,
|
|
638
|
+
openWorldHint: false,
|
|
639
|
+
},
|
|
640
|
+
}, async ({ section }) => {
|
|
641
|
+
const data = await api('GET', `/api/agent/context/${encodeURIComponent(section)}`);
|
|
642
|
+
// Handle glob pattern response (multiple sections)
|
|
643
|
+
if (data.pattern && data.sections) {
|
|
644
|
+
const lines = [`**Sections matching "${data.pattern}":**\n`];
|
|
645
|
+
for (const s of data.sections) {
|
|
646
|
+
lines.push(`# ${s.section}\n\n${s.content}\n\n---\n`);
|
|
647
|
+
}
|
|
648
|
+
return {
|
|
649
|
+
content: [{ type: 'text', text: lines.join('\n') }],
|
|
650
|
+
structuredContent: data,
|
|
651
|
+
};
|
|
652
|
+
}
|
|
653
|
+
// Single section response
|
|
654
|
+
return {
|
|
655
|
+
content: [{ type: 'text', text: `# ${data.section}\n\n${data.content}` }],
|
|
656
|
+
structuredContent: data,
|
|
657
|
+
};
|
|
658
|
+
});
|
|
659
|
+
// Tool: Update context section
|
|
660
|
+
server.registerTool('update_context_section', {
|
|
661
|
+
title: 'Update Context Section',
|
|
662
|
+
description: 'Upload/update a project context section. Use after analyzing local codebase. ' +
|
|
663
|
+
'This action shares documentation with the Damper project for future AI agents.\n\n' +
|
|
664
|
+
'**When to use hierarchical names**: For monorepos or large projects, use paths like ' +
|
|
665
|
+
'`api/architecture`, `api/testing` to organize by module. For simple projects, flat ' +
|
|
666
|
+
'names like `overview`, `conventions` are sufficient.\n\n' +
|
|
667
|
+
'**When to add tags**: Only if you have multiple related sections that should surface ' +
|
|
668
|
+
'together (e.g., all "backend" docs). Skip for simple projects.\n\n' +
|
|
669
|
+
'⚠️ SECURITY WARNING: Do NOT include sensitive information:\n' +
|
|
670
|
+
'- API keys, secrets, passwords, tokens\n' +
|
|
671
|
+
'- Database connection strings\n' +
|
|
672
|
+
'- Private endpoints or internal URLs\n' +
|
|
673
|
+
'- Customer data or PII\n' +
|
|
674
|
+
'- .env file contents or credentials',
|
|
675
|
+
inputSchema: z.object({
|
|
676
|
+
section: z.string()
|
|
677
|
+
.regex(/^[a-z][a-z0-9-]{0,49}(\/[a-z][a-z0-9-]{0,49}){0,2}$/)
|
|
678
|
+
.describe('Section name or path (e.g., "overview", "api/architecture"). Max 3 levels.'),
|
|
679
|
+
content: z.string().describe('Markdown documentation for this section. Must NOT contain secrets, API keys, or sensitive data.'),
|
|
680
|
+
tags: z.array(z.string()).optional().describe('Categorization tags (e.g., ["backend", "critical"]). Helps match sections to task labels.'),
|
|
681
|
+
appliesTo: z.array(z.string()).optional().describe('Module names this section applies to (e.g., ["server", "dashboard"]).'),
|
|
682
|
+
}),
|
|
683
|
+
outputSchema: z.object({
|
|
684
|
+
success: z.boolean(),
|
|
685
|
+
section: z.string(),
|
|
686
|
+
tags: z.array(z.string()).optional(),
|
|
687
|
+
appliesTo: z.array(z.string()).optional(),
|
|
688
|
+
warnings: z.array(z.string()).optional(),
|
|
689
|
+
}),
|
|
690
|
+
annotations: {
|
|
691
|
+
readOnlyHint: false,
|
|
692
|
+
destructiveHint: false,
|
|
693
|
+
idempotentHint: true,
|
|
694
|
+
openWorldHint: false,
|
|
695
|
+
},
|
|
696
|
+
}, async ({ section, content, tags, appliesTo }) => {
|
|
697
|
+
const result = await api('POST', `/api/agent/context/${encodeURIComponent(section)}`, { content, tags, appliesTo });
|
|
698
|
+
let text = `📝 Updated context section: ${result.section}`;
|
|
699
|
+
if (result.tags && result.tags.length > 0) {
|
|
700
|
+
text += ` [${result.tags.join(', ')}]`;
|
|
701
|
+
}
|
|
702
|
+
if (result.warnings && result.warnings.length > 0) {
|
|
703
|
+
text += `\n⚠️ Warnings: ${result.warnings.join(', ')}`;
|
|
704
|
+
}
|
|
705
|
+
return {
|
|
706
|
+
content: [{ type: 'text', text }],
|
|
707
|
+
structuredContent: result,
|
|
708
|
+
};
|
|
709
|
+
});
|
|
710
|
+
// Tool: Sync project context (bulk upload)
|
|
711
|
+
server.registerTool('sync_project_context', {
|
|
712
|
+
title: 'Sync Project Context',
|
|
713
|
+
description: 'Bulk upload all project context sections at once. Use after analyzing a codebase. ' +
|
|
714
|
+
'This action shares documentation with the Damper project for future AI agents.\n\n' +
|
|
715
|
+
'⚠️ SECURITY WARNING: Do NOT include sensitive information:\n' +
|
|
716
|
+
'- API keys, secrets, passwords, tokens\n' +
|
|
717
|
+
'- Database connection strings\n' +
|
|
718
|
+
'- Private endpoints or internal URLs\n' +
|
|
719
|
+
'- Customer data or PII\n' +
|
|
720
|
+
'- .env file contents or credentials',
|
|
721
|
+
inputSchema: z.object({
|
|
722
|
+
sections: z.array(z.object({
|
|
723
|
+
section: z.string()
|
|
724
|
+
.regex(/^[a-z][a-z0-9-]{0,49}(\/[a-z][a-z0-9-]{0,49}){0,2}$/)
|
|
725
|
+
.describe('Section name or path (e.g., "overview", "api/architecture")'),
|
|
726
|
+
content: z.string().describe('Markdown documentation (no secrets!)'),
|
|
727
|
+
tags: z.array(z.string()).optional().describe('Categorization tags'),
|
|
728
|
+
appliesTo: z.array(z.string()).optional().describe('Module names this applies to'),
|
|
729
|
+
})).describe('Array of sections to upload'),
|
|
730
|
+
}),
|
|
731
|
+
outputSchema: z.object({
|
|
732
|
+
success: z.boolean(),
|
|
733
|
+
sectionsUpdated: z.number(),
|
|
734
|
+
}),
|
|
735
|
+
annotations: {
|
|
736
|
+
readOnlyHint: false,
|
|
737
|
+
destructiveHint: false,
|
|
738
|
+
idempotentHint: true,
|
|
739
|
+
openWorldHint: false,
|
|
740
|
+
},
|
|
741
|
+
}, async ({ sections }) => {
|
|
742
|
+
const result = await api('POST', '/api/agent/context/sync', { sections });
|
|
743
|
+
return {
|
|
744
|
+
content: [{ type: 'text', text: `📚 Synced ${result.sectionsUpdated} context sections` }],
|
|
745
|
+
structuredContent: result,
|
|
746
|
+
};
|
|
747
|
+
});
|
|
748
|
+
// Tool: Get project context (token-efficient index)
|
|
749
|
+
server.registerTool('get_project_context', {
|
|
750
|
+
title: 'Get Project Context',
|
|
751
|
+
description: 'Get project context index (token-efficient). Returns section list with previews. ' +
|
|
752
|
+
'Use get_context_section to fetch full content for sections you need.',
|
|
753
|
+
inputSchema: z.object({
|
|
754
|
+
taskId: z.string().optional().describe('If provided, highlights sections relevant to this task'),
|
|
755
|
+
}),
|
|
756
|
+
outputSchema: z.object({
|
|
757
|
+
isEmpty: z.boolean(),
|
|
758
|
+
index: z.array(z.object({
|
|
759
|
+
section: z.string(),
|
|
760
|
+
preview: z.string(),
|
|
761
|
+
updatedAt: z.string(),
|
|
762
|
+
tags: z.array(z.string()).optional(),
|
|
763
|
+
appliesTo: z.array(z.string()).optional(),
|
|
764
|
+
})),
|
|
765
|
+
relevantSections: z.array(z.string()).optional(),
|
|
766
|
+
hint: z.string().optional(),
|
|
767
|
+
}),
|
|
768
|
+
annotations: {
|
|
769
|
+
readOnlyHint: true,
|
|
770
|
+
destructiveHint: false,
|
|
771
|
+
idempotentHint: true,
|
|
772
|
+
openWorldHint: false,
|
|
773
|
+
},
|
|
774
|
+
}, async ({ taskId }) => {
|
|
775
|
+
const params = taskId ? `?task_id=${taskId}` : '';
|
|
776
|
+
const data = await api('GET', `/api/agent/context/index${params}`);
|
|
777
|
+
if (data.isEmpty) {
|
|
778
|
+
return {
|
|
779
|
+
content: [{ type: 'text', text: data.hint || 'No project context available.' }],
|
|
780
|
+
structuredContent: data,
|
|
781
|
+
};
|
|
782
|
+
}
|
|
783
|
+
const lines = ['**Available context sections:**'];
|
|
784
|
+
for (const s of data.index) {
|
|
785
|
+
const relevant = data.relevantSections?.includes(s.section) ? ' ⭐' : '';
|
|
786
|
+
const tags = s.tags && s.tags.length > 0 ? ` [${s.tags.join(', ')}]` : '';
|
|
787
|
+
lines.push(`• ${s.section}${relevant}${tags} - ${s.preview}`);
|
|
788
|
+
}
|
|
789
|
+
if (data.relevantSections && data.relevantSections.length > 0) {
|
|
790
|
+
lines.push(`\n**Relevant for this task:** ${data.relevantSections.join(', ')}`);
|
|
791
|
+
}
|
|
792
|
+
return {
|
|
793
|
+
content: [{ type: 'text', text: lines.join('\n') }],
|
|
794
|
+
structuredContent: data,
|
|
795
|
+
};
|
|
796
|
+
});
|
|
520
797
|
// Tool: List feedback
|
|
521
798
|
server.registerTool('list_feedback', {
|
|
522
799
|
title: 'List Feedback',
|
|
@@ -597,6 +874,294 @@ server.registerTool('get_feedback', {
|
|
|
597
874
|
structuredContent: f,
|
|
598
875
|
};
|
|
599
876
|
});
|
|
877
|
+
// ==================== Template Tools ====================
|
|
878
|
+
// Tool: List templates
|
|
879
|
+
server.registerTool('list_templates', {
|
|
880
|
+
title: 'List Templates',
|
|
881
|
+
description: 'List available code templates for this project. Templates help maintain consistency ' +
|
|
882
|
+
'when creating new files.\n\n' +
|
|
883
|
+
'**When to use**: Check for templates before creating new service files, components, ' +
|
|
884
|
+
'or test files. If no templates exist, create files using patterns you observe in the codebase.\n\n' +
|
|
885
|
+
'**When to skip**: If the list is empty, the project doesn\'t use templates - that\'s fine.',
|
|
886
|
+
inputSchema: z.object({}),
|
|
887
|
+
outputSchema: z.object({
|
|
888
|
+
templates: z.array(z.object({
|
|
889
|
+
name: z.string(),
|
|
890
|
+
description: z.string().nullable().optional(),
|
|
891
|
+
filePattern: z.string().nullable().optional(),
|
|
892
|
+
tags: z.array(z.string()).optional(),
|
|
893
|
+
preview: z.string(),
|
|
894
|
+
updatedAt: z.string(),
|
|
895
|
+
})),
|
|
896
|
+
isEmpty: z.boolean(),
|
|
897
|
+
}),
|
|
898
|
+
annotations: {
|
|
899
|
+
readOnlyHint: true,
|
|
900
|
+
destructiveHint: false,
|
|
901
|
+
idempotentHint: true,
|
|
902
|
+
openWorldHint: false,
|
|
903
|
+
},
|
|
904
|
+
}, async () => {
|
|
905
|
+
const data = await api('GET', '/api/agent/templates');
|
|
906
|
+
if (data.isEmpty) {
|
|
907
|
+
return {
|
|
908
|
+
content: [{ type: 'text', text: 'No templates available for this project.' }],
|
|
909
|
+
structuredContent: data,
|
|
910
|
+
};
|
|
911
|
+
}
|
|
912
|
+
const lines = data.templates.map((t) => {
|
|
913
|
+
const pattern = t.filePattern ? ` → ${t.filePattern}` : '';
|
|
914
|
+
const desc = t.description ? `: ${t.description}` : '';
|
|
915
|
+
return `• ${t.name}${pattern}${desc}`;
|
|
916
|
+
});
|
|
917
|
+
return {
|
|
918
|
+
content: [{ type: 'text', text: `Templates:\n${lines.join('\n')}` }],
|
|
919
|
+
structuredContent: data,
|
|
920
|
+
};
|
|
921
|
+
});
|
|
922
|
+
// Tool: Get template
|
|
923
|
+
server.registerTool('get_template', {
|
|
924
|
+
title: 'Get Template',
|
|
925
|
+
description: 'Get full content of a specific code template.',
|
|
926
|
+
inputSchema: z.object({
|
|
927
|
+
name: z.string().describe('Template name'),
|
|
928
|
+
}),
|
|
929
|
+
outputSchema: z.object({
|
|
930
|
+
name: z.string(),
|
|
931
|
+
description: z.string().nullable().optional(),
|
|
932
|
+
content: z.string(),
|
|
933
|
+
filePattern: z.string().nullable().optional(),
|
|
934
|
+
tags: z.array(z.string()).optional(),
|
|
935
|
+
updatedAt: z.string(),
|
|
936
|
+
}),
|
|
937
|
+
annotations: {
|
|
938
|
+
readOnlyHint: true,
|
|
939
|
+
destructiveHint: false,
|
|
940
|
+
idempotentHint: true,
|
|
941
|
+
openWorldHint: false,
|
|
942
|
+
},
|
|
943
|
+
}, async ({ name }) => {
|
|
944
|
+
const t = await api('GET', `/api/agent/templates/${name}`);
|
|
945
|
+
const header = t.description ? `${t.name}: ${t.description}` : t.name;
|
|
946
|
+
const pattern = t.filePattern ? `\nFile pattern: ${t.filePattern}` : '';
|
|
947
|
+
return {
|
|
948
|
+
content: [{ type: 'text', text: `# Template: ${header}${pattern}\n\n\`\`\`\n${t.content}\n\`\`\`` }],
|
|
949
|
+
structuredContent: t,
|
|
950
|
+
};
|
|
951
|
+
});
|
|
952
|
+
// Tool: Update template
|
|
953
|
+
server.registerTool('update_template', {
|
|
954
|
+
title: 'Update Template',
|
|
955
|
+
description: 'Create or update a code template. Templates help maintain consistency across similar files.\n\n' +
|
|
956
|
+
'**When to create templates**: When you notice repetitive file patterns in a project ' +
|
|
957
|
+
'(e.g., all services follow same structure). Extract the pattern and store it.\n\n' +
|
|
958
|
+
'**File patterns**: Use `{name}` as a placeholder in the file pattern ' +
|
|
959
|
+
'(e.g., "src/services/{name}.ts").',
|
|
960
|
+
inputSchema: z.object({
|
|
961
|
+
name: z.string().regex(/^[a-z][a-z0-9-]{0,49}$/).describe('Template name (lowercase, alphanumeric, hyphens)'),
|
|
962
|
+
content: z.string().describe('Template content (can include placeholders like {name}, {description})'),
|
|
963
|
+
description: z.string().optional().describe('Brief description of when to use this template'),
|
|
964
|
+
filePattern: z.string().optional().describe('File path pattern (e.g., "src/services/{name}.ts")'),
|
|
965
|
+
tags: z.array(z.string()).optional().describe('Categorization tags'),
|
|
966
|
+
}),
|
|
967
|
+
outputSchema: z.object({
|
|
968
|
+
success: z.boolean(),
|
|
969
|
+
name: z.string(),
|
|
970
|
+
description: z.string().nullable().optional(),
|
|
971
|
+
filePattern: z.string().nullable().optional(),
|
|
972
|
+
tags: z.array(z.string()).optional(),
|
|
973
|
+
}),
|
|
974
|
+
annotations: {
|
|
975
|
+
readOnlyHint: false,
|
|
976
|
+
destructiveHint: false,
|
|
977
|
+
idempotentHint: true,
|
|
978
|
+
openWorldHint: false,
|
|
979
|
+
},
|
|
980
|
+
}, async ({ name, content, description, filePattern, tags }) => {
|
|
981
|
+
const result = await api('POST', `/api/agent/templates/${name}`, { content, description, filePattern, tags });
|
|
982
|
+
return {
|
|
983
|
+
content: [{ type: 'text', text: `📝 Updated template: ${result.name}` }],
|
|
984
|
+
structuredContent: result,
|
|
985
|
+
};
|
|
986
|
+
});
|
|
987
|
+
// Tool: Sync templates
|
|
988
|
+
server.registerTool('sync_templates', {
|
|
989
|
+
title: 'Sync Templates',
|
|
990
|
+
description: 'Bulk upload multiple templates at once.',
|
|
991
|
+
inputSchema: z.object({
|
|
992
|
+
templates: z.array(z.object({
|
|
993
|
+
name: z.string().regex(/^[a-z][a-z0-9-]{0,49}$/).describe('Template name'),
|
|
994
|
+
content: z.string().describe('Template content'),
|
|
995
|
+
description: z.string().optional().describe('Description'),
|
|
996
|
+
filePattern: z.string().optional().describe('File path pattern'),
|
|
997
|
+
tags: z.array(z.string()).optional().describe('Tags'),
|
|
998
|
+
})).describe('Array of templates to upload'),
|
|
999
|
+
}),
|
|
1000
|
+
outputSchema: z.object({
|
|
1001
|
+
success: z.boolean(),
|
|
1002
|
+
templatesUpdated: z.number(),
|
|
1003
|
+
}),
|
|
1004
|
+
annotations: {
|
|
1005
|
+
readOnlyHint: false,
|
|
1006
|
+
destructiveHint: false,
|
|
1007
|
+
idempotentHint: true,
|
|
1008
|
+
openWorldHint: false,
|
|
1009
|
+
},
|
|
1010
|
+
}, async ({ templates }) => {
|
|
1011
|
+
const result = await api('POST', '/api/agent/templates/sync', { templates });
|
|
1012
|
+
return {
|
|
1013
|
+
content: [{ type: 'text', text: `📚 Synced ${result.templatesUpdated} templates` }],
|
|
1014
|
+
structuredContent: result,
|
|
1015
|
+
};
|
|
1016
|
+
});
|
|
1017
|
+
// ==================== Module Tools ====================
|
|
1018
|
+
// Tool: List modules
|
|
1019
|
+
server.registerTool('list_modules', {
|
|
1020
|
+
title: 'List Modules',
|
|
1021
|
+
description: 'List registered modules in this monorepo. Shows package structure, ports, and dependencies.\n\n' +
|
|
1022
|
+
'**When to use**: For monorepos with multiple packages. Helps understand which packages ' +
|
|
1023
|
+
'exist and how they relate before making cross-package changes.\n\n' +
|
|
1024
|
+
'**When to skip**: For single-package projects or when you already understand the structure.',
|
|
1025
|
+
inputSchema: z.object({}),
|
|
1026
|
+
outputSchema: z.object({
|
|
1027
|
+
modules: z.array(z.object({
|
|
1028
|
+
name: z.string(),
|
|
1029
|
+
path: z.string(),
|
|
1030
|
+
port: z.number().nullable().optional(),
|
|
1031
|
+
dependsOn: z.array(z.string()).optional(),
|
|
1032
|
+
description: z.string().nullable().optional(),
|
|
1033
|
+
tags: z.array(z.string()).optional(),
|
|
1034
|
+
updatedAt: z.string(),
|
|
1035
|
+
})),
|
|
1036
|
+
isEmpty: z.boolean(),
|
|
1037
|
+
}),
|
|
1038
|
+
annotations: {
|
|
1039
|
+
readOnlyHint: true,
|
|
1040
|
+
destructiveHint: false,
|
|
1041
|
+
idempotentHint: true,
|
|
1042
|
+
openWorldHint: false,
|
|
1043
|
+
},
|
|
1044
|
+
}, async () => {
|
|
1045
|
+
const data = await api('GET', '/api/agent/modules');
|
|
1046
|
+
if (data.isEmpty) {
|
|
1047
|
+
return {
|
|
1048
|
+
content: [{ type: 'text', text: 'No modules registered. This may be a single-package project.' }],
|
|
1049
|
+
structuredContent: data,
|
|
1050
|
+
};
|
|
1051
|
+
}
|
|
1052
|
+
const lines = data.modules.map((m) => {
|
|
1053
|
+
const port = m.port ? ` :${m.port}` : '';
|
|
1054
|
+
const deps = m.dependsOn && m.dependsOn.length > 0 ? ` → ${m.dependsOn.join(', ')}` : '';
|
|
1055
|
+
return `• ${m.name}${port} (${m.path})${deps}`;
|
|
1056
|
+
});
|
|
1057
|
+
return {
|
|
1058
|
+
content: [{ type: 'text', text: `Modules:\n${lines.join('\n')}` }],
|
|
1059
|
+
structuredContent: data,
|
|
1060
|
+
};
|
|
1061
|
+
});
|
|
1062
|
+
// Tool: Get module
|
|
1063
|
+
server.registerTool('get_module', {
|
|
1064
|
+
title: 'Get Module',
|
|
1065
|
+
description: 'Get details of a specific module.',
|
|
1066
|
+
inputSchema: z.object({
|
|
1067
|
+
name: z.string().describe('Module name'),
|
|
1068
|
+
}),
|
|
1069
|
+
outputSchema: z.object({
|
|
1070
|
+
name: z.string(),
|
|
1071
|
+
path: z.string(),
|
|
1072
|
+
port: z.number().nullable().optional(),
|
|
1073
|
+
dependsOn: z.array(z.string()).optional(),
|
|
1074
|
+
description: z.string().nullable().optional(),
|
|
1075
|
+
tags: z.array(z.string()).optional(),
|
|
1076
|
+
updatedAt: z.string(),
|
|
1077
|
+
}),
|
|
1078
|
+
annotations: {
|
|
1079
|
+
readOnlyHint: true,
|
|
1080
|
+
destructiveHint: false,
|
|
1081
|
+
idempotentHint: true,
|
|
1082
|
+
openWorldHint: false,
|
|
1083
|
+
},
|
|
1084
|
+
}, async ({ name }) => {
|
|
1085
|
+
const m = await api('GET', `/api/agent/modules/${name}`);
|
|
1086
|
+
const parts = [`# Module: ${m.name}`, `Path: ${m.path}`];
|
|
1087
|
+
if (m.port)
|
|
1088
|
+
parts.push(`Port: ${m.port}`);
|
|
1089
|
+
if (m.dependsOn && m.dependsOn.length > 0)
|
|
1090
|
+
parts.push(`Depends on: ${m.dependsOn.join(', ')}`);
|
|
1091
|
+
if (m.description)
|
|
1092
|
+
parts.push(`\n${m.description}`);
|
|
1093
|
+
return {
|
|
1094
|
+
content: [{ type: 'text', text: parts.join('\n') }],
|
|
1095
|
+
structuredContent: m,
|
|
1096
|
+
};
|
|
1097
|
+
});
|
|
1098
|
+
// Tool: Update module
|
|
1099
|
+
server.registerTool('update_module', {
|
|
1100
|
+
title: 'Update Module',
|
|
1101
|
+
description: 'Register or update a module in the registry. Use to track monorepo structure.\n\n' +
|
|
1102
|
+
'**When to use**: After analyzing a monorepo, register each package so future agents ' +
|
|
1103
|
+
'understand the structure without re-analyzing.',
|
|
1104
|
+
inputSchema: z.object({
|
|
1105
|
+
name: z.string().regex(/^[a-z][a-z0-9-]{0,49}$/).describe('Module name (e.g., "server", "dashboard")'),
|
|
1106
|
+
path: z.string().describe('Path to module (e.g., "packages/server")'),
|
|
1107
|
+
port: z.number().nullable().optional().describe('Dev server port if applicable'),
|
|
1108
|
+
dependsOn: z.array(z.string()).optional().describe('Names of modules this depends on'),
|
|
1109
|
+
description: z.string().optional().describe('Brief description of the module'),
|
|
1110
|
+
tags: z.array(z.string()).optional().describe('Categorization tags'),
|
|
1111
|
+
}),
|
|
1112
|
+
outputSchema: z.object({
|
|
1113
|
+
success: z.boolean(),
|
|
1114
|
+
name: z.string(),
|
|
1115
|
+
path: z.string(),
|
|
1116
|
+
port: z.number().nullable().optional(),
|
|
1117
|
+
dependsOn: z.array(z.string()).optional(),
|
|
1118
|
+
description: z.string().nullable().optional(),
|
|
1119
|
+
tags: z.array(z.string()).optional(),
|
|
1120
|
+
}),
|
|
1121
|
+
annotations: {
|
|
1122
|
+
readOnlyHint: false,
|
|
1123
|
+
destructiveHint: false,
|
|
1124
|
+
idempotentHint: true,
|
|
1125
|
+
openWorldHint: false,
|
|
1126
|
+
},
|
|
1127
|
+
}, async ({ name, path, port, dependsOn, description, tags }) => {
|
|
1128
|
+
const result = await api('POST', `/api/agent/modules/${name}`, { path, port, dependsOn, description, tags });
|
|
1129
|
+
return {
|
|
1130
|
+
content: [{ type: 'text', text: `📦 Updated module: ${result.name} (${result.path})` }],
|
|
1131
|
+
structuredContent: result,
|
|
1132
|
+
};
|
|
1133
|
+
});
|
|
1134
|
+
// Tool: Sync modules
|
|
1135
|
+
server.registerTool('sync_modules', {
|
|
1136
|
+
title: 'Sync Modules',
|
|
1137
|
+
description: 'Bulk upload multiple modules at once. Use after analyzing a monorepo.',
|
|
1138
|
+
inputSchema: z.object({
|
|
1139
|
+
modules: z.array(z.object({
|
|
1140
|
+
name: z.string().regex(/^[a-z][a-z0-9-]{0,49}$/).describe('Module name'),
|
|
1141
|
+
path: z.string().describe('Path to module'),
|
|
1142
|
+
port: z.number().nullable().optional().describe('Dev server port'),
|
|
1143
|
+
dependsOn: z.array(z.string()).optional().describe('Dependencies'),
|
|
1144
|
+
description: z.string().optional().describe('Description'),
|
|
1145
|
+
tags: z.array(z.string()).optional().describe('Tags'),
|
|
1146
|
+
})).describe('Array of modules to upload'),
|
|
1147
|
+
}),
|
|
1148
|
+
outputSchema: z.object({
|
|
1149
|
+
success: z.boolean(),
|
|
1150
|
+
modulesUpdated: z.number(),
|
|
1151
|
+
}),
|
|
1152
|
+
annotations: {
|
|
1153
|
+
readOnlyHint: false,
|
|
1154
|
+
destructiveHint: false,
|
|
1155
|
+
idempotentHint: true,
|
|
1156
|
+
openWorldHint: false,
|
|
1157
|
+
},
|
|
1158
|
+
}, async ({ modules }) => {
|
|
1159
|
+
const result = await api('POST', '/api/agent/modules/sync', { modules });
|
|
1160
|
+
return {
|
|
1161
|
+
content: [{ type: 'text', text: `📦 Synced ${result.modulesUpdated} modules` }],
|
|
1162
|
+
structuredContent: result,
|
|
1163
|
+
};
|
|
1164
|
+
});
|
|
600
1165
|
// Start
|
|
601
1166
|
async function main() {
|
|
602
1167
|
const transport = new StdioServerTransport();
|