@damper/mcp 0.2.0 → 0.3.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 +79 -2
- package/dist/index.js +355 -16
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -85,6 +85,14 @@ The AI will see tools from both servers and can distinguish between them:
|
|
|
85
85
|
| `abandon_task` | Release lock, return to planned |
|
|
86
86
|
| `list_feedback` | Browse user feedback |
|
|
87
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 |
|
|
88
96
|
|
|
89
97
|
### Project Context
|
|
90
98
|
|
|
@@ -94,8 +102,8 @@ AI agents can store and retrieve project documentation to help future agents wor
|
|
|
94
102
|
|------|-------------|
|
|
95
103
|
| `get_project_context` | Get context index with section previews. Optionally highlights sections relevant to a task. |
|
|
96
104
|
| `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) |
|
|
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) |
|
|
99
107
|
| `sync_project_context` | Bulk upload multiple sections at once (requires user permission) |
|
|
100
108
|
|
|
101
109
|
**How it works:**
|
|
@@ -108,6 +116,75 @@ AI agents can store and retrieve project documentation to help future agents wor
|
|
|
108
116
|
|
|
109
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.
|
|
110
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.
|
|
187
|
+
|
|
111
188
|
### Task Types
|
|
112
189
|
|
|
113
190
|
Filter tasks by type to prioritize work:
|
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({
|
|
@@ -571,6 +571,8 @@ server.registerTool('list_context_sections', {
|
|
|
571
571
|
updatedAt: z.string(),
|
|
572
572
|
source: z.string(),
|
|
573
573
|
preview: z.string(),
|
|
574
|
+
tags: z.array(z.string()).optional(),
|
|
575
|
+
appliesTo: z.array(z.string()).optional(),
|
|
574
576
|
})),
|
|
575
577
|
isEmpty: z.boolean(),
|
|
576
578
|
}),
|
|
@@ -588,7 +590,10 @@ server.registerTool('list_context_sections', {
|
|
|
588
590
|
structuredContent: data,
|
|
589
591
|
};
|
|
590
592
|
}
|
|
591
|
-
const lines = data.sections.map((s) =>
|
|
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
|
+
});
|
|
592
597
|
return {
|
|
593
598
|
content: [{ type: 'text', text: `Context sections:\n${lines.join('\n')}` }],
|
|
594
599
|
structuredContent: data,
|
|
@@ -597,15 +602,32 @@ server.registerTool('list_context_sections', {
|
|
|
597
602
|
// Tool: Get context section
|
|
598
603
|
server.registerTool('get_context_section', {
|
|
599
604
|
title: 'Get Context Section',
|
|
600
|
-
description: 'Get full content of a specific 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)',
|
|
601
610
|
inputSchema: z.object({
|
|
602
|
-
section: z.string().describe('Section name (e.g., "overview", "api", "
|
|
611
|
+
section: z.string().describe('Section name or glob pattern (e.g., "overview", "api/architecture", "api/*", "api/**")'),
|
|
603
612
|
}),
|
|
604
613
|
outputSchema: z.object({
|
|
605
|
-
section
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
614
|
+
// Single section response
|
|
615
|
+
section: z.string().optional(),
|
|
616
|
+
content: z.string().optional(),
|
|
617
|
+
updatedAt: z.string().optional(),
|
|
618
|
+
source: z.string().optional(),
|
|
619
|
+
tags: z.array(z.string()).optional(),
|
|
620
|
+
appliesTo: z.array(z.string()).optional(),
|
|
621
|
+
// Glob pattern response
|
|
622
|
+
pattern: z.string().optional(),
|
|
623
|
+
sections: z.array(z.object({
|
|
624
|
+
section: z.string(),
|
|
625
|
+
content: z.string(),
|
|
626
|
+
updatedAt: z.string(),
|
|
627
|
+
source: z.string(),
|
|
628
|
+
tags: z.array(z.string()).optional(),
|
|
629
|
+
appliesTo: z.array(z.string()).optional(),
|
|
630
|
+
})).optional(),
|
|
609
631
|
}),
|
|
610
632
|
annotations: {
|
|
611
633
|
readOnlyHint: true,
|
|
@@ -614,7 +636,19 @@ server.registerTool('get_context_section', {
|
|
|
614
636
|
openWorldHint: false,
|
|
615
637
|
},
|
|
616
638
|
}, async ({ section }) => {
|
|
617
|
-
const data = await api('GET', `/api/agent/context/${section}`);
|
|
639
|
+
const data = await api('GET', `/api/agent/context/${encodeURIComponent(section)}`);
|
|
640
|
+
// Handle glob pattern response (multiple sections)
|
|
641
|
+
if (data.pattern && data.sections) {
|
|
642
|
+
const lines = [`**Sections matching "${data.pattern}":**\n`];
|
|
643
|
+
for (const s of data.sections) {
|
|
644
|
+
lines.push(`# ${s.section}\n\n${s.content}\n\n---\n`);
|
|
645
|
+
}
|
|
646
|
+
return {
|
|
647
|
+
content: [{ type: 'text', text: lines.join('\n') }],
|
|
648
|
+
structuredContent: data,
|
|
649
|
+
};
|
|
650
|
+
}
|
|
651
|
+
// Single section response
|
|
618
652
|
return {
|
|
619
653
|
content: [{ type: 'text', text: `# ${data.section}\n\n${data.content}` }],
|
|
620
654
|
structuredContent: data,
|
|
@@ -625,6 +659,11 @@ server.registerTool('update_context_section', {
|
|
|
625
659
|
title: 'Update Context Section',
|
|
626
660
|
description: 'Upload/update a project context section. Use after analyzing local codebase. ' +
|
|
627
661
|
'This action shares documentation with the Damper project for future AI agents.\n\n' +
|
|
662
|
+
'**When to use hierarchical names**: For monorepos or large projects, use paths like ' +
|
|
663
|
+
'`api/architecture`, `api/testing` to organize by module. For simple projects, flat ' +
|
|
664
|
+
'names like `overview`, `conventions` are sufficient.\n\n' +
|
|
665
|
+
'**When to add tags**: Only if you have multiple related sections that should surface ' +
|
|
666
|
+
'together (e.g., all "backend" docs). Skip for simple projects.\n\n' +
|
|
628
667
|
'⚠️ SECURITY WARNING: Do NOT include sensitive information:\n' +
|
|
629
668
|
'- API keys, secrets, passwords, tokens\n' +
|
|
630
669
|
'- Database connection strings\n' +
|
|
@@ -633,13 +672,17 @@ server.registerTool('update_context_section', {
|
|
|
633
672
|
'- .env file contents or credentials',
|
|
634
673
|
inputSchema: z.object({
|
|
635
674
|
section: z.string()
|
|
636
|
-
.regex(/^[a-z][a-z0-9-]{0,49}$/)
|
|
637
|
-
.describe('Section name
|
|
675
|
+
.regex(/^[a-z][a-z0-9-]{0,49}(\/[a-z][a-z0-9-]{0,49}){0,2}$/)
|
|
676
|
+
.describe('Section name or path (e.g., "overview", "api/architecture"). Max 3 levels.'),
|
|
638
677
|
content: z.string().describe('Markdown documentation for this section. Must NOT contain secrets, API keys, or sensitive data.'),
|
|
678
|
+
tags: z.array(z.string()).optional().describe('Categorization tags (e.g., ["backend", "critical"]). Helps match sections to task labels.'),
|
|
679
|
+
appliesTo: z.array(z.string()).optional().describe('Module names this section applies to (e.g., ["server", "dashboard"]).'),
|
|
639
680
|
}),
|
|
640
681
|
outputSchema: z.object({
|
|
641
682
|
success: z.boolean(),
|
|
642
683
|
section: z.string(),
|
|
684
|
+
tags: z.array(z.string()).optional(),
|
|
685
|
+
appliesTo: z.array(z.string()).optional(),
|
|
643
686
|
warnings: z.array(z.string()).optional(),
|
|
644
687
|
}),
|
|
645
688
|
annotations: {
|
|
@@ -648,9 +691,12 @@ server.registerTool('update_context_section', {
|
|
|
648
691
|
idempotentHint: true,
|
|
649
692
|
openWorldHint: false,
|
|
650
693
|
},
|
|
651
|
-
}, async ({ section, content }) => {
|
|
652
|
-
const result = await api('POST', `/api/agent/context/${section}`, { content });
|
|
694
|
+
}, async ({ section, content, tags, appliesTo }) => {
|
|
695
|
+
const result = await api('POST', `/api/agent/context/${encodeURIComponent(section)}`, { content, tags, appliesTo });
|
|
653
696
|
let text = `📝 Updated context section: ${result.section}`;
|
|
697
|
+
if (result.tags && result.tags.length > 0) {
|
|
698
|
+
text += ` [${result.tags.join(', ')}]`;
|
|
699
|
+
}
|
|
654
700
|
if (result.warnings && result.warnings.length > 0) {
|
|
655
701
|
text += `\n⚠️ Warnings: ${result.warnings.join(', ')}`;
|
|
656
702
|
}
|
|
@@ -673,9 +719,11 @@ server.registerTool('sync_project_context', {
|
|
|
673
719
|
inputSchema: z.object({
|
|
674
720
|
sections: z.array(z.object({
|
|
675
721
|
section: z.string()
|
|
676
|
-
.regex(/^[a-z][a-z0-9-]{0,49}$/)
|
|
677
|
-
.describe('Section name'),
|
|
722
|
+
.regex(/^[a-z][a-z0-9-]{0,49}(\/[a-z][a-z0-9-]{0,49}){0,2}$/)
|
|
723
|
+
.describe('Section name or path (e.g., "overview", "api/architecture")'),
|
|
678
724
|
content: z.string().describe('Markdown documentation (no secrets!)'),
|
|
725
|
+
tags: z.array(z.string()).optional().describe('Categorization tags'),
|
|
726
|
+
appliesTo: z.array(z.string()).optional().describe('Module names this applies to'),
|
|
679
727
|
})).describe('Array of sections to upload'),
|
|
680
728
|
}),
|
|
681
729
|
outputSchema: z.object({
|
|
@@ -709,6 +757,8 @@ server.registerTool('get_project_context', {
|
|
|
709
757
|
section: z.string(),
|
|
710
758
|
preview: z.string(),
|
|
711
759
|
updatedAt: z.string(),
|
|
760
|
+
tags: z.array(z.string()).optional(),
|
|
761
|
+
appliesTo: z.array(z.string()).optional(),
|
|
712
762
|
})),
|
|
713
763
|
relevantSections: z.array(z.string()).optional(),
|
|
714
764
|
hint: z.string().optional(),
|
|
@@ -731,7 +781,8 @@ server.registerTool('get_project_context', {
|
|
|
731
781
|
const lines = ['**Available context sections:**'];
|
|
732
782
|
for (const s of data.index) {
|
|
733
783
|
const relevant = data.relevantSections?.includes(s.section) ? ' ⭐' : '';
|
|
734
|
-
|
|
784
|
+
const tags = s.tags && s.tags.length > 0 ? ` [${s.tags.join(', ')}]` : '';
|
|
785
|
+
lines.push(`• ${s.section}${relevant}${tags} - ${s.preview}`);
|
|
735
786
|
}
|
|
736
787
|
if (data.relevantSections && data.relevantSections.length > 0) {
|
|
737
788
|
lines.push(`\n**Relevant for this task:** ${data.relevantSections.join(', ')}`);
|
|
@@ -821,6 +872,294 @@ server.registerTool('get_feedback', {
|
|
|
821
872
|
structuredContent: f,
|
|
822
873
|
};
|
|
823
874
|
});
|
|
875
|
+
// ==================== Template Tools ====================
|
|
876
|
+
// Tool: List templates
|
|
877
|
+
server.registerTool('list_templates', {
|
|
878
|
+
title: 'List Templates',
|
|
879
|
+
description: 'List available code templates for this project. Templates help maintain consistency ' +
|
|
880
|
+
'when creating new files.\n\n' +
|
|
881
|
+
'**When to use**: Check for templates before creating new service files, components, ' +
|
|
882
|
+
'or test files. If no templates exist, create files using patterns you observe in the codebase.\n\n' +
|
|
883
|
+
'**When to skip**: If the list is empty, the project doesn\'t use templates - that\'s fine.',
|
|
884
|
+
inputSchema: z.object({}),
|
|
885
|
+
outputSchema: z.object({
|
|
886
|
+
templates: z.array(z.object({
|
|
887
|
+
name: z.string(),
|
|
888
|
+
description: z.string().nullable().optional(),
|
|
889
|
+
filePattern: z.string().nullable().optional(),
|
|
890
|
+
tags: z.array(z.string()).optional(),
|
|
891
|
+
preview: z.string(),
|
|
892
|
+
updatedAt: z.string(),
|
|
893
|
+
})),
|
|
894
|
+
isEmpty: z.boolean(),
|
|
895
|
+
}),
|
|
896
|
+
annotations: {
|
|
897
|
+
readOnlyHint: true,
|
|
898
|
+
destructiveHint: false,
|
|
899
|
+
idempotentHint: true,
|
|
900
|
+
openWorldHint: false,
|
|
901
|
+
},
|
|
902
|
+
}, async () => {
|
|
903
|
+
const data = await api('GET', '/api/agent/templates');
|
|
904
|
+
if (data.isEmpty) {
|
|
905
|
+
return {
|
|
906
|
+
content: [{ type: 'text', text: 'No templates available for this project.' }],
|
|
907
|
+
structuredContent: data,
|
|
908
|
+
};
|
|
909
|
+
}
|
|
910
|
+
const lines = data.templates.map((t) => {
|
|
911
|
+
const pattern = t.filePattern ? ` → ${t.filePattern}` : '';
|
|
912
|
+
const desc = t.description ? `: ${t.description}` : '';
|
|
913
|
+
return `• ${t.name}${pattern}${desc}`;
|
|
914
|
+
});
|
|
915
|
+
return {
|
|
916
|
+
content: [{ type: 'text', text: `Templates:\n${lines.join('\n')}` }],
|
|
917
|
+
structuredContent: data,
|
|
918
|
+
};
|
|
919
|
+
});
|
|
920
|
+
// Tool: Get template
|
|
921
|
+
server.registerTool('get_template', {
|
|
922
|
+
title: 'Get Template',
|
|
923
|
+
description: 'Get full content of a specific code template.',
|
|
924
|
+
inputSchema: z.object({
|
|
925
|
+
name: z.string().describe('Template name'),
|
|
926
|
+
}),
|
|
927
|
+
outputSchema: z.object({
|
|
928
|
+
name: z.string(),
|
|
929
|
+
description: z.string().nullable().optional(),
|
|
930
|
+
content: z.string(),
|
|
931
|
+
filePattern: z.string().nullable().optional(),
|
|
932
|
+
tags: z.array(z.string()).optional(),
|
|
933
|
+
updatedAt: z.string(),
|
|
934
|
+
}),
|
|
935
|
+
annotations: {
|
|
936
|
+
readOnlyHint: true,
|
|
937
|
+
destructiveHint: false,
|
|
938
|
+
idempotentHint: true,
|
|
939
|
+
openWorldHint: false,
|
|
940
|
+
},
|
|
941
|
+
}, async ({ name }) => {
|
|
942
|
+
const t = await api('GET', `/api/agent/templates/${name}`);
|
|
943
|
+
const header = t.description ? `${t.name}: ${t.description}` : t.name;
|
|
944
|
+
const pattern = t.filePattern ? `\nFile pattern: ${t.filePattern}` : '';
|
|
945
|
+
return {
|
|
946
|
+
content: [{ type: 'text', text: `# Template: ${header}${pattern}\n\n\`\`\`\n${t.content}\n\`\`\`` }],
|
|
947
|
+
structuredContent: t,
|
|
948
|
+
};
|
|
949
|
+
});
|
|
950
|
+
// Tool: Update template
|
|
951
|
+
server.registerTool('update_template', {
|
|
952
|
+
title: 'Update Template',
|
|
953
|
+
description: 'Create or update a code template. Templates help maintain consistency across similar files.\n\n' +
|
|
954
|
+
'**When to create templates**: When you notice repetitive file patterns in a project ' +
|
|
955
|
+
'(e.g., all services follow same structure). Extract the pattern and store it.\n\n' +
|
|
956
|
+
'**File patterns**: Use `{name}` as a placeholder in the file pattern ' +
|
|
957
|
+
'(e.g., "src/services/{name}.ts").',
|
|
958
|
+
inputSchema: z.object({
|
|
959
|
+
name: z.string().regex(/^[a-z][a-z0-9-]{0,49}$/).describe('Template name (lowercase, alphanumeric, hyphens)'),
|
|
960
|
+
content: z.string().describe('Template content (can include placeholders like {name}, {description})'),
|
|
961
|
+
description: z.string().optional().describe('Brief description of when to use this template'),
|
|
962
|
+
filePattern: z.string().optional().describe('File path pattern (e.g., "src/services/{name}.ts")'),
|
|
963
|
+
tags: z.array(z.string()).optional().describe('Categorization tags'),
|
|
964
|
+
}),
|
|
965
|
+
outputSchema: z.object({
|
|
966
|
+
success: z.boolean(),
|
|
967
|
+
name: z.string(),
|
|
968
|
+
description: z.string().nullable().optional(),
|
|
969
|
+
filePattern: z.string().nullable().optional(),
|
|
970
|
+
tags: z.array(z.string()).optional(),
|
|
971
|
+
}),
|
|
972
|
+
annotations: {
|
|
973
|
+
readOnlyHint: false,
|
|
974
|
+
destructiveHint: false,
|
|
975
|
+
idempotentHint: true,
|
|
976
|
+
openWorldHint: false,
|
|
977
|
+
},
|
|
978
|
+
}, async ({ name, content, description, filePattern, tags }) => {
|
|
979
|
+
const result = await api('POST', `/api/agent/templates/${name}`, { content, description, filePattern, tags });
|
|
980
|
+
return {
|
|
981
|
+
content: [{ type: 'text', text: `📝 Updated template: ${result.name}` }],
|
|
982
|
+
structuredContent: result,
|
|
983
|
+
};
|
|
984
|
+
});
|
|
985
|
+
// Tool: Sync templates
|
|
986
|
+
server.registerTool('sync_templates', {
|
|
987
|
+
title: 'Sync Templates',
|
|
988
|
+
description: 'Bulk upload multiple templates at once.',
|
|
989
|
+
inputSchema: z.object({
|
|
990
|
+
templates: z.array(z.object({
|
|
991
|
+
name: z.string().regex(/^[a-z][a-z0-9-]{0,49}$/).describe('Template name'),
|
|
992
|
+
content: z.string().describe('Template content'),
|
|
993
|
+
description: z.string().optional().describe('Description'),
|
|
994
|
+
filePattern: z.string().optional().describe('File path pattern'),
|
|
995
|
+
tags: z.array(z.string()).optional().describe('Tags'),
|
|
996
|
+
})).describe('Array of templates to upload'),
|
|
997
|
+
}),
|
|
998
|
+
outputSchema: z.object({
|
|
999
|
+
success: z.boolean(),
|
|
1000
|
+
templatesUpdated: z.number(),
|
|
1001
|
+
}),
|
|
1002
|
+
annotations: {
|
|
1003
|
+
readOnlyHint: false,
|
|
1004
|
+
destructiveHint: false,
|
|
1005
|
+
idempotentHint: true,
|
|
1006
|
+
openWorldHint: false,
|
|
1007
|
+
},
|
|
1008
|
+
}, async ({ templates }) => {
|
|
1009
|
+
const result = await api('POST', '/api/agent/templates/sync', { templates });
|
|
1010
|
+
return {
|
|
1011
|
+
content: [{ type: 'text', text: `📚 Synced ${result.templatesUpdated} templates` }],
|
|
1012
|
+
structuredContent: result,
|
|
1013
|
+
};
|
|
1014
|
+
});
|
|
1015
|
+
// ==================== Module Tools ====================
|
|
1016
|
+
// Tool: List modules
|
|
1017
|
+
server.registerTool('list_modules', {
|
|
1018
|
+
title: 'List Modules',
|
|
1019
|
+
description: 'List registered modules in this monorepo. Shows package structure, ports, and dependencies.\n\n' +
|
|
1020
|
+
'**When to use**: For monorepos with multiple packages. Helps understand which packages ' +
|
|
1021
|
+
'exist and how they relate before making cross-package changes.\n\n' +
|
|
1022
|
+
'**When to skip**: For single-package projects or when you already understand the structure.',
|
|
1023
|
+
inputSchema: z.object({}),
|
|
1024
|
+
outputSchema: z.object({
|
|
1025
|
+
modules: z.array(z.object({
|
|
1026
|
+
name: z.string(),
|
|
1027
|
+
path: z.string(),
|
|
1028
|
+
port: z.number().nullable().optional(),
|
|
1029
|
+
dependsOn: z.array(z.string()).optional(),
|
|
1030
|
+
description: z.string().nullable().optional(),
|
|
1031
|
+
tags: z.array(z.string()).optional(),
|
|
1032
|
+
updatedAt: z.string(),
|
|
1033
|
+
})),
|
|
1034
|
+
isEmpty: z.boolean(),
|
|
1035
|
+
}),
|
|
1036
|
+
annotations: {
|
|
1037
|
+
readOnlyHint: true,
|
|
1038
|
+
destructiveHint: false,
|
|
1039
|
+
idempotentHint: true,
|
|
1040
|
+
openWorldHint: false,
|
|
1041
|
+
},
|
|
1042
|
+
}, async () => {
|
|
1043
|
+
const data = await api('GET', '/api/agent/modules');
|
|
1044
|
+
if (data.isEmpty) {
|
|
1045
|
+
return {
|
|
1046
|
+
content: [{ type: 'text', text: 'No modules registered. This may be a single-package project.' }],
|
|
1047
|
+
structuredContent: data,
|
|
1048
|
+
};
|
|
1049
|
+
}
|
|
1050
|
+
const lines = data.modules.map((m) => {
|
|
1051
|
+
const port = m.port ? ` :${m.port}` : '';
|
|
1052
|
+
const deps = m.dependsOn && m.dependsOn.length > 0 ? ` → ${m.dependsOn.join(', ')}` : '';
|
|
1053
|
+
return `• ${m.name}${port} (${m.path})${deps}`;
|
|
1054
|
+
});
|
|
1055
|
+
return {
|
|
1056
|
+
content: [{ type: 'text', text: `Modules:\n${lines.join('\n')}` }],
|
|
1057
|
+
structuredContent: data,
|
|
1058
|
+
};
|
|
1059
|
+
});
|
|
1060
|
+
// Tool: Get module
|
|
1061
|
+
server.registerTool('get_module', {
|
|
1062
|
+
title: 'Get Module',
|
|
1063
|
+
description: 'Get details of a specific module.',
|
|
1064
|
+
inputSchema: z.object({
|
|
1065
|
+
name: z.string().describe('Module name'),
|
|
1066
|
+
}),
|
|
1067
|
+
outputSchema: z.object({
|
|
1068
|
+
name: z.string(),
|
|
1069
|
+
path: z.string(),
|
|
1070
|
+
port: z.number().nullable().optional(),
|
|
1071
|
+
dependsOn: z.array(z.string()).optional(),
|
|
1072
|
+
description: z.string().nullable().optional(),
|
|
1073
|
+
tags: z.array(z.string()).optional(),
|
|
1074
|
+
updatedAt: z.string(),
|
|
1075
|
+
}),
|
|
1076
|
+
annotations: {
|
|
1077
|
+
readOnlyHint: true,
|
|
1078
|
+
destructiveHint: false,
|
|
1079
|
+
idempotentHint: true,
|
|
1080
|
+
openWorldHint: false,
|
|
1081
|
+
},
|
|
1082
|
+
}, async ({ name }) => {
|
|
1083
|
+
const m = await api('GET', `/api/agent/modules/${name}`);
|
|
1084
|
+
const parts = [`# Module: ${m.name}`, `Path: ${m.path}`];
|
|
1085
|
+
if (m.port)
|
|
1086
|
+
parts.push(`Port: ${m.port}`);
|
|
1087
|
+
if (m.dependsOn && m.dependsOn.length > 0)
|
|
1088
|
+
parts.push(`Depends on: ${m.dependsOn.join(', ')}`);
|
|
1089
|
+
if (m.description)
|
|
1090
|
+
parts.push(`\n${m.description}`);
|
|
1091
|
+
return {
|
|
1092
|
+
content: [{ type: 'text', text: parts.join('\n') }],
|
|
1093
|
+
structuredContent: m,
|
|
1094
|
+
};
|
|
1095
|
+
});
|
|
1096
|
+
// Tool: Update module
|
|
1097
|
+
server.registerTool('update_module', {
|
|
1098
|
+
title: 'Update Module',
|
|
1099
|
+
description: 'Register or update a module in the registry. Use to track monorepo structure.\n\n' +
|
|
1100
|
+
'**When to use**: After analyzing a monorepo, register each package so future agents ' +
|
|
1101
|
+
'understand the structure without re-analyzing.',
|
|
1102
|
+
inputSchema: z.object({
|
|
1103
|
+
name: z.string().regex(/^[a-z][a-z0-9-]{0,49}$/).describe('Module name (e.g., "server", "dashboard")'),
|
|
1104
|
+
path: z.string().describe('Path to module (e.g., "packages/server")'),
|
|
1105
|
+
port: z.number().nullable().optional().describe('Dev server port if applicable'),
|
|
1106
|
+
dependsOn: z.array(z.string()).optional().describe('Names of modules this depends on'),
|
|
1107
|
+
description: z.string().optional().describe('Brief description of the module'),
|
|
1108
|
+
tags: z.array(z.string()).optional().describe('Categorization tags'),
|
|
1109
|
+
}),
|
|
1110
|
+
outputSchema: z.object({
|
|
1111
|
+
success: z.boolean(),
|
|
1112
|
+
name: z.string(),
|
|
1113
|
+
path: z.string(),
|
|
1114
|
+
port: z.number().nullable().optional(),
|
|
1115
|
+
dependsOn: z.array(z.string()).optional(),
|
|
1116
|
+
description: z.string().nullable().optional(),
|
|
1117
|
+
tags: z.array(z.string()).optional(),
|
|
1118
|
+
}),
|
|
1119
|
+
annotations: {
|
|
1120
|
+
readOnlyHint: false,
|
|
1121
|
+
destructiveHint: false,
|
|
1122
|
+
idempotentHint: true,
|
|
1123
|
+
openWorldHint: false,
|
|
1124
|
+
},
|
|
1125
|
+
}, async ({ name, path, port, dependsOn, description, tags }) => {
|
|
1126
|
+
const result = await api('POST', `/api/agent/modules/${name}`, { path, port, dependsOn, description, tags });
|
|
1127
|
+
return {
|
|
1128
|
+
content: [{ type: 'text', text: `📦 Updated module: ${result.name} (${result.path})` }],
|
|
1129
|
+
structuredContent: result,
|
|
1130
|
+
};
|
|
1131
|
+
});
|
|
1132
|
+
// Tool: Sync modules
|
|
1133
|
+
server.registerTool('sync_modules', {
|
|
1134
|
+
title: 'Sync Modules',
|
|
1135
|
+
description: 'Bulk upload multiple modules at once. Use after analyzing a monorepo.',
|
|
1136
|
+
inputSchema: z.object({
|
|
1137
|
+
modules: z.array(z.object({
|
|
1138
|
+
name: z.string().regex(/^[a-z][a-z0-9-]{0,49}$/).describe('Module name'),
|
|
1139
|
+
path: z.string().describe('Path to module'),
|
|
1140
|
+
port: z.number().nullable().optional().describe('Dev server port'),
|
|
1141
|
+
dependsOn: z.array(z.string()).optional().describe('Dependencies'),
|
|
1142
|
+
description: z.string().optional().describe('Description'),
|
|
1143
|
+
tags: z.array(z.string()).optional().describe('Tags'),
|
|
1144
|
+
})).describe('Array of modules to upload'),
|
|
1145
|
+
}),
|
|
1146
|
+
outputSchema: z.object({
|
|
1147
|
+
success: z.boolean(),
|
|
1148
|
+
modulesUpdated: z.number(),
|
|
1149
|
+
}),
|
|
1150
|
+
annotations: {
|
|
1151
|
+
readOnlyHint: false,
|
|
1152
|
+
destructiveHint: false,
|
|
1153
|
+
idempotentHint: true,
|
|
1154
|
+
openWorldHint: false,
|
|
1155
|
+
},
|
|
1156
|
+
}, async ({ modules }) => {
|
|
1157
|
+
const result = await api('POST', '/api/agent/modules/sync', { modules });
|
|
1158
|
+
return {
|
|
1159
|
+
content: [{ type: 'text', text: `📦 Synced ${result.modulesUpdated} modules` }],
|
|
1160
|
+
structuredContent: result,
|
|
1161
|
+
};
|
|
1162
|
+
});
|
|
824
1163
|
// Start
|
|
825
1164
|
async function main() {
|
|
826
1165
|
const transport = new StdioServerTransport();
|