mcp-adr-analysis-server 2.1.22 → 2.1.27
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 +63 -1
- package/dist/.tsbuildinfo +1 -1
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +370 -0
- package/dist/src/index.js.map +1 -1
- package/dist/src/tools/adr-aggregator-tools.d.ts +185 -0
- package/dist/src/tools/adr-aggregator-tools.d.ts.map +1 -0
- package/dist/src/tools/adr-aggregator-tools.js +872 -0
- package/dist/src/tools/adr-aggregator-tools.js.map +1 -0
- package/dist/src/tools/analyze-gaps-tool.d.ts +57 -0
- package/dist/src/tools/analyze-gaps-tool.d.ts.map +1 -0
- package/dist/src/tools/analyze-gaps-tool.js +727 -0
- package/dist/src/tools/analyze-gaps-tool.js.map +1 -0
- package/dist/src/tools/tool-catalog.d.ts +1 -1
- package/dist/src/tools/tool-catalog.d.ts.map +1 -1
- package/dist/src/tools/tool-catalog.js +382 -0
- package/dist/src/tools/tool-catalog.js.map +1 -1
- package/dist/src/tools/tool-dispatcher.d.ts.map +1 -1
- package/dist/src/tools/tool-dispatcher.js +4 -0
- package/dist/src/tools/tool-dispatcher.js.map +1 -1
- package/dist/src/types/adr-aggregator.d.ts +692 -0
- package/dist/src/types/adr-aggregator.d.ts.map +1 -0
- package/dist/src/types/adr-aggregator.js +8 -0
- package/dist/src/types/adr-aggregator.js.map +1 -0
- package/dist/src/types/index.d.ts +3 -2
- package/dist/src/types/index.d.ts.map +1 -1
- package/dist/src/types/memory-entities.d.ts +48 -48
- package/dist/src/utils/adr-aggregator-client.d.ts +177 -0
- package/dist/src/utils/adr-aggregator-client.d.ts.map +1 -0
- package/dist/src/utils/adr-aggregator-client.js +396 -0
- package/dist/src/utils/adr-aggregator-client.js.map +1 -0
- package/dist/src/utils/config.d.ts +3 -0
- package/dist/src/utils/config.d.ts.map +1 -1
- package/dist/src/utils/config.js +13 -0
- package/dist/src/utils/config.js.map +1 -1
- package/dist/src/utils/git-remote-detector.d.ts +67 -0
- package/dist/src/utils/git-remote-detector.d.ts.map +1 -0
- package/dist/src/utils/git-remote-detector.js +194 -0
- package/dist/src/utils/git-remote-detector.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,872 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ADR Aggregator Integration Tools
|
|
3
|
+
* MCP tools for bidirectional sync with https://adraggregator.com
|
|
4
|
+
*
|
|
5
|
+
* @see NEW_FEATURE.md for API documentation
|
|
6
|
+
*/
|
|
7
|
+
import { McpAdrError } from '../types/index.js';
|
|
8
|
+
import { getAdrAggregatorClient, } from '../utils/adr-aggregator-client.js';
|
|
9
|
+
import { loadConfig } from '../utils/config.js';
|
|
10
|
+
import { isGitRepository } from '../utils/git-remote-detector.js';
|
|
11
|
+
import { promises as fs } from 'fs';
|
|
12
|
+
import path from 'path';
|
|
13
|
+
// ============================================================================
|
|
14
|
+
// Helper Functions
|
|
15
|
+
// ============================================================================
|
|
16
|
+
/**
|
|
17
|
+
* Discover existing ADRs in the project
|
|
18
|
+
*/
|
|
19
|
+
async function discoverAdrs(projectPath, adrDirectory) {
|
|
20
|
+
const adrPath = path.resolve(projectPath, adrDirectory);
|
|
21
|
+
const adrFiles = [];
|
|
22
|
+
try {
|
|
23
|
+
const files = await fs.readdir(adrPath);
|
|
24
|
+
for (const file of files) {
|
|
25
|
+
if (file.endsWith('.md')) {
|
|
26
|
+
adrFiles.push(path.join(adrPath, file));
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
// Directory doesn't exist or not readable
|
|
32
|
+
}
|
|
33
|
+
return adrFiles;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Parse ADR content to extract metadata
|
|
37
|
+
*/
|
|
38
|
+
function parseAdrContent(content, filePath) {
|
|
39
|
+
const lines = content.split('\n');
|
|
40
|
+
let title = '';
|
|
41
|
+
let status = 'proposed';
|
|
42
|
+
for (const line of lines) {
|
|
43
|
+
// Extract title from first H1
|
|
44
|
+
if (line.startsWith('# ') && !title) {
|
|
45
|
+
title = line.slice(2).trim();
|
|
46
|
+
}
|
|
47
|
+
// Extract status
|
|
48
|
+
const statusMatch = line.match(/^\*?\*?Status\*?\*?:\s*(.+)/i);
|
|
49
|
+
if (statusMatch && statusMatch[1]) {
|
|
50
|
+
const statusValue = statusMatch[1].toLowerCase().trim();
|
|
51
|
+
if (statusValue.includes('accepted'))
|
|
52
|
+
status = 'accepted';
|
|
53
|
+
else if (statusValue.includes('deprecated'))
|
|
54
|
+
status = 'deprecated';
|
|
55
|
+
else if (statusValue.includes('superseded'))
|
|
56
|
+
status = 'superseded';
|
|
57
|
+
else if (statusValue.includes('proposed'))
|
|
58
|
+
status = 'proposed';
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return {
|
|
62
|
+
title: title || path.basename(filePath, '.md'),
|
|
63
|
+
status,
|
|
64
|
+
filePath,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Build ADR payloads for sync
|
|
69
|
+
*/
|
|
70
|
+
async function buildAdrPayloads(adrPaths, projectPath, includeMetadata) {
|
|
71
|
+
const payloads = [];
|
|
72
|
+
for (const adrPath of adrPaths) {
|
|
73
|
+
try {
|
|
74
|
+
const content = await fs.readFile(adrPath, 'utf-8');
|
|
75
|
+
const relativePath = path.relative(projectPath, adrPath);
|
|
76
|
+
const parsed = parseAdrContent(content, adrPath);
|
|
77
|
+
const payload = {
|
|
78
|
+
path: relativePath,
|
|
79
|
+
title: parsed.title || path.basename(adrPath, '.md'),
|
|
80
|
+
status: parsed.status || 'proposed',
|
|
81
|
+
content,
|
|
82
|
+
};
|
|
83
|
+
if (includeMetadata) {
|
|
84
|
+
payload.analysis_metadata = {
|
|
85
|
+
last_analyzed: new Date().toISOString(),
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
payloads.push(payload);
|
|
89
|
+
}
|
|
90
|
+
catch (error) {
|
|
91
|
+
// Skip files that can't be read
|
|
92
|
+
console.error(`Failed to read ADR: ${adrPath}`, error);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
return payloads;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Ensure client is configured
|
|
99
|
+
*/
|
|
100
|
+
function ensureConfigured(client) {
|
|
101
|
+
if (!client.isConfigured()) {
|
|
102
|
+
throw new McpAdrError('ADR Aggregator API key not configured. Set ADR_AGGREGATOR_API_KEY environment variable.', 'NOT_CONFIGURED');
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Ensure git repository
|
|
107
|
+
*/
|
|
108
|
+
function ensureGitRepo(projectPath) {
|
|
109
|
+
if (!isGitRepository(projectPath)) {
|
|
110
|
+
throw new McpAdrError('Not a git repository. ADR Aggregator requires a git repository to auto-detect the repository name.', 'NOT_GIT_REPO');
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
// ============================================================================
|
|
114
|
+
// Tool Implementations
|
|
115
|
+
// ============================================================================
|
|
116
|
+
/**
|
|
117
|
+
* Sync ADRs to ADR Aggregator platform
|
|
118
|
+
*
|
|
119
|
+
* POST /functions/v1/mcp-sync-adr
|
|
120
|
+
*/
|
|
121
|
+
export async function syncToAggregator(args, context) {
|
|
122
|
+
const { full_sync = false, include_metadata = true, adr_paths, projectPath } = args;
|
|
123
|
+
const client = getAdrAggregatorClient();
|
|
124
|
+
ensureConfigured(client);
|
|
125
|
+
const config = loadConfig();
|
|
126
|
+
const resolvedProjectPath = projectPath || config.projectPath;
|
|
127
|
+
ensureGitRepo(resolvedProjectPath);
|
|
128
|
+
try {
|
|
129
|
+
context?.info('Starting ADR sync to aggregator...');
|
|
130
|
+
context?.report_progress(10, 100);
|
|
131
|
+
// Auto-detect repository name
|
|
132
|
+
const repositoryName = client.getRepositoryName(resolvedProjectPath);
|
|
133
|
+
context?.info(`Repository: ${repositoryName}`);
|
|
134
|
+
// Discover ADRs
|
|
135
|
+
context?.report_progress(20, 100);
|
|
136
|
+
let adrFilePaths;
|
|
137
|
+
if (adr_paths && adr_paths.length > 0) {
|
|
138
|
+
// Use specified paths
|
|
139
|
+
adrFilePaths = adr_paths.map(p => path.resolve(resolvedProjectPath, p));
|
|
140
|
+
}
|
|
141
|
+
else {
|
|
142
|
+
// Discover all ADRs
|
|
143
|
+
adrFilePaths = await discoverAdrs(resolvedProjectPath, config.adrDirectory);
|
|
144
|
+
}
|
|
145
|
+
context?.info(`Found ${adrFilePaths.length} ADRs to sync`);
|
|
146
|
+
context?.report_progress(40, 100);
|
|
147
|
+
if (adrFilePaths.length === 0) {
|
|
148
|
+
return {
|
|
149
|
+
content: [
|
|
150
|
+
{
|
|
151
|
+
type: 'text',
|
|
152
|
+
text: `# ADR Aggregator Sync
|
|
153
|
+
|
|
154
|
+
No ADRs found in ${config.adrDirectory}. Create ADRs first using \`suggest_adrs\` or \`generate_adr_from_decision\` tools.`,
|
|
155
|
+
},
|
|
156
|
+
],
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
// Build payloads
|
|
160
|
+
const adrs = await buildAdrPayloads(adrFilePaths, resolvedProjectPath, include_metadata);
|
|
161
|
+
context?.report_progress(60, 100);
|
|
162
|
+
// Send to aggregator
|
|
163
|
+
const request = {
|
|
164
|
+
repository_name: repositoryName,
|
|
165
|
+
adrs,
|
|
166
|
+
full_sync,
|
|
167
|
+
include_metadata,
|
|
168
|
+
};
|
|
169
|
+
const response = await client.syncAdrs(request);
|
|
170
|
+
context?.report_progress(100, 100);
|
|
171
|
+
context?.info(`Synced ${response.synced_count} ADRs successfully`);
|
|
172
|
+
return {
|
|
173
|
+
content: [
|
|
174
|
+
{
|
|
175
|
+
type: 'text',
|
|
176
|
+
text: `# ADR Aggregator Sync Complete
|
|
177
|
+
|
|
178
|
+
## Summary
|
|
179
|
+
- **Repository:** ${response.repository}
|
|
180
|
+
- **ADRs Synced:** ${response.synced_count}
|
|
181
|
+
- **Sync ID:** ${response.sync_id}
|
|
182
|
+
- **Timestamp:** ${response.timestamp}
|
|
183
|
+
${response.tier ? `- **Tier:** ${response.tier}` : ''}
|
|
184
|
+
${response.truncated ? `\n**Warning:** Some ADRs were truncated due to tier limits. Upgrade at /pricing to sync more.` : ''}
|
|
185
|
+
|
|
186
|
+
${response.errors && response.errors.length > 0
|
|
187
|
+
? `## Errors
|
|
188
|
+
${response.errors.map(e => `- ${e.code}: ${e.message}`).join('\n')}`
|
|
189
|
+
: ''}
|
|
190
|
+
|
|
191
|
+
## Synced ADRs
|
|
192
|
+
${adrs.map(adr => `- ${adr.path} (${adr.status})`).join('\n')}
|
|
193
|
+
|
|
194
|
+
## Options
|
|
195
|
+
- Full Sync: ${full_sync}
|
|
196
|
+
- Include Metadata: ${include_metadata}
|
|
197
|
+
|
|
198
|
+
View in dashboard: ${client.getBaseUrl()}/mcp-dashboard`,
|
|
199
|
+
},
|
|
200
|
+
],
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
catch (error) {
|
|
204
|
+
const errorMessage = error instanceof McpAdrError ? error.message : String(error);
|
|
205
|
+
return {
|
|
206
|
+
content: [
|
|
207
|
+
{
|
|
208
|
+
type: 'text',
|
|
209
|
+
text: `# ADR Sync Failed
|
|
210
|
+
|
|
211
|
+
**Error:** ${errorMessage}
|
|
212
|
+
|
|
213
|
+
Please check:
|
|
214
|
+
1. ADR_AGGREGATOR_API_KEY is set correctly
|
|
215
|
+
2. You have an active internet connection
|
|
216
|
+
3. The ADR Aggregator service is available`,
|
|
217
|
+
},
|
|
218
|
+
],
|
|
219
|
+
isError: true,
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* Get ADR context from aggregator
|
|
225
|
+
*
|
|
226
|
+
* GET /functions/v1/mcp-get-context
|
|
227
|
+
*/
|
|
228
|
+
export async function getAdrContext(args, context) {
|
|
229
|
+
const client = getAdrAggregatorClient();
|
|
230
|
+
ensureConfigured(client);
|
|
231
|
+
const config = loadConfig();
|
|
232
|
+
const resolvedProjectPath = args.projectPath || config.projectPath;
|
|
233
|
+
ensureGitRepo(resolvedProjectPath);
|
|
234
|
+
try {
|
|
235
|
+
const repositoryName = client.getRepositoryName(resolvedProjectPath);
|
|
236
|
+
context?.info(`Fetching ADR context for ${repositoryName}...`);
|
|
237
|
+
context?.report_progress(30, 100);
|
|
238
|
+
const request = {
|
|
239
|
+
repository_name: repositoryName,
|
|
240
|
+
include_timeline: args.include_timeline ?? true,
|
|
241
|
+
...(args.include_diagrams !== undefined && { include_diagrams: args.include_diagrams }),
|
|
242
|
+
...(args.include_code_links !== undefined && { include_code_links: args.include_code_links }),
|
|
243
|
+
...(args.include_research !== undefined && { include_research: args.include_research }),
|
|
244
|
+
...(args.staleness_filter !== undefined && { staleness_filter: args.staleness_filter }),
|
|
245
|
+
...(args.graph_depth !== undefined && { graph_depth: args.graph_depth }),
|
|
246
|
+
};
|
|
247
|
+
const response = await client.getContext(request);
|
|
248
|
+
context?.report_progress(100, 100);
|
|
249
|
+
return {
|
|
250
|
+
content: [
|
|
251
|
+
{
|
|
252
|
+
type: 'text',
|
|
253
|
+
text: `# ADR Context for ${response.repository}
|
|
254
|
+
|
|
255
|
+
## Summary
|
|
256
|
+
- **Total ADRs:** ${response.summary.total_adrs}
|
|
257
|
+
- **By Status:** ${JSON.stringify(response.summary.by_status)}
|
|
258
|
+
- **Average Staleness:** ${response.summary.average_staleness.toFixed(2)}
|
|
259
|
+
|
|
260
|
+
## ADRs
|
|
261
|
+
${response.adrs
|
|
262
|
+
.map(adr => `
|
|
263
|
+
### ${adr.title}
|
|
264
|
+
- **Path:** ${adr.path}
|
|
265
|
+
- **Status:** ${adr.status}
|
|
266
|
+
${adr.timeline ? `- **Staleness:** ${adr.timeline.staleness} (${adr.timeline.days_since_modified} days)` : ''}
|
|
267
|
+
${adr.summary ? `- **Summary:** ${adr.summary}` : ''}
|
|
268
|
+
`)
|
|
269
|
+
.join('\n')}
|
|
270
|
+
`,
|
|
271
|
+
},
|
|
272
|
+
],
|
|
273
|
+
};
|
|
274
|
+
}
|
|
275
|
+
catch (error) {
|
|
276
|
+
const errorMessage = error instanceof McpAdrError ? error.message : String(error);
|
|
277
|
+
return {
|
|
278
|
+
content: [
|
|
279
|
+
{ type: 'text', text: `# Failed to Get ADR Context\n\n**Error:** ${errorMessage}` },
|
|
280
|
+
],
|
|
281
|
+
isError: true,
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
/**
|
|
286
|
+
* Get staleness report from aggregator
|
|
287
|
+
*
|
|
288
|
+
* GET /functions/v1/mcp-staleness-report
|
|
289
|
+
*/
|
|
290
|
+
export async function getStalenessReport(args, context) {
|
|
291
|
+
const client = getAdrAggregatorClient();
|
|
292
|
+
ensureConfigured(client);
|
|
293
|
+
const config = loadConfig();
|
|
294
|
+
const resolvedProjectPath = args.projectPath || config.projectPath;
|
|
295
|
+
ensureGitRepo(resolvedProjectPath);
|
|
296
|
+
try {
|
|
297
|
+
const repositoryName = client.getRepositoryName(resolvedProjectPath);
|
|
298
|
+
context?.info(`Fetching staleness report for ${repositoryName}...`);
|
|
299
|
+
context?.report_progress(30, 100);
|
|
300
|
+
const request = {
|
|
301
|
+
repository_name: repositoryName,
|
|
302
|
+
threshold: args.threshold ?? 90,
|
|
303
|
+
};
|
|
304
|
+
const response = await client.getStalenessReport(request);
|
|
305
|
+
context?.report_progress(100, 100);
|
|
306
|
+
return {
|
|
307
|
+
content: [
|
|
308
|
+
{
|
|
309
|
+
type: 'text',
|
|
310
|
+
text: `# ADR Staleness Report
|
|
311
|
+
|
|
312
|
+
## Repository: ${response.repository}
|
|
313
|
+
**Report Date:** ${response.report_date}
|
|
314
|
+
|
|
315
|
+
## Summary
|
|
316
|
+
| Status | Count |
|
|
317
|
+
|--------|-------|
|
|
318
|
+
| Fresh | ${response.summary.fresh} |
|
|
319
|
+
| Recent | ${response.summary.recent} |
|
|
320
|
+
| Stale | ${response.summary.stale} |
|
|
321
|
+
| Very Stale | ${response.summary.very_stale} |
|
|
322
|
+
| **Total** | **${response.summary.total_adrs}** |
|
|
323
|
+
|
|
324
|
+
## Governance
|
|
325
|
+
- **Review Cycle Compliance:** ${(response.governance.review_cycle_compliance * 100).toFixed(1)}%
|
|
326
|
+
- **Overdue Reviews:** ${response.governance.overdue_reviews}
|
|
327
|
+
|
|
328
|
+
${response.stale_adrs.length > 0
|
|
329
|
+
? `## Stale ADRs Requiring Attention
|
|
330
|
+
${response.stale_adrs
|
|
331
|
+
.map(adr => `
|
|
332
|
+
### ${adr.title}
|
|
333
|
+
- **Path:** ${adr.path}
|
|
334
|
+
- **Days Since Modified:** ${adr.days_since_modified}
|
|
335
|
+
- **Staleness Level:** ${adr.staleness}
|
|
336
|
+
- **Recommended Action:** ${adr.recommended_action}
|
|
337
|
+
`)
|
|
338
|
+
.join('\n')}`
|
|
339
|
+
: '## All ADRs are Fresh! ✓'}
|
|
340
|
+
`,
|
|
341
|
+
},
|
|
342
|
+
],
|
|
343
|
+
};
|
|
344
|
+
}
|
|
345
|
+
catch (error) {
|
|
346
|
+
const errorMessage = error instanceof McpAdrError ? error.message : String(error);
|
|
347
|
+
return {
|
|
348
|
+
content: [
|
|
349
|
+
{ type: 'text', text: `# Failed to Get Staleness Report\n\n**Error:** ${errorMessage}` },
|
|
350
|
+
],
|
|
351
|
+
isError: true,
|
|
352
|
+
};
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
/**
|
|
356
|
+
* Get ADR templates from aggregator
|
|
357
|
+
*
|
|
358
|
+
* GET /functions/v1/mcp-get-templates
|
|
359
|
+
*/
|
|
360
|
+
export async function getAdrTemplates(args, context) {
|
|
361
|
+
const client = getAdrAggregatorClient();
|
|
362
|
+
// Templates don't require authentication
|
|
363
|
+
// ensureConfigured(client);
|
|
364
|
+
try {
|
|
365
|
+
context?.info(`Fetching ADR templates${args.domain ? ` for domain: ${args.domain}` : ''}...`);
|
|
366
|
+
context?.report_progress(30, 100);
|
|
367
|
+
const request = {
|
|
368
|
+
...(args.domain !== undefined && { domain: args.domain }),
|
|
369
|
+
};
|
|
370
|
+
const response = await client.getTemplates(request);
|
|
371
|
+
context?.report_progress(100, 100);
|
|
372
|
+
const templateSections = Object.entries(response.templates)
|
|
373
|
+
.map(([domain, template]) => `
|
|
374
|
+
## ${domain.replace(/_/g, ' ').toUpperCase()}
|
|
375
|
+
|
|
376
|
+
### Best Practices
|
|
377
|
+
${template.best_practices
|
|
378
|
+
.map(bp => `
|
|
379
|
+
#### ${bp.name}
|
|
380
|
+
- **ID:** ${bp.id}
|
|
381
|
+
- **Description:** ${bp.description}
|
|
382
|
+
|
|
383
|
+
<details>
|
|
384
|
+
<summary>ADR Template</summary>
|
|
385
|
+
|
|
386
|
+
\`\`\`markdown
|
|
387
|
+
${bp.adr_template}
|
|
388
|
+
\`\`\`
|
|
389
|
+
|
|
390
|
+
</details>
|
|
391
|
+
`)
|
|
392
|
+
.join('\n')}
|
|
393
|
+
|
|
394
|
+
### Anti-Patterns to Avoid
|
|
395
|
+
${template.anti_patterns
|
|
396
|
+
.map(ap => `
|
|
397
|
+
#### ${ap.name}
|
|
398
|
+
- **ID:** ${ap.id}
|
|
399
|
+
- **Description:** ${ap.description}
|
|
400
|
+
- **Detection Hints:** ${ap.detection_hints.join(', ')}
|
|
401
|
+
- **Recommendation:** ${ap.recommendation}
|
|
402
|
+
`)
|
|
403
|
+
.join('\n')}
|
|
404
|
+
`)
|
|
405
|
+
.join('\n---\n');
|
|
406
|
+
return {
|
|
407
|
+
content: [
|
|
408
|
+
{
|
|
409
|
+
type: 'text',
|
|
410
|
+
text: `# ADR Templates from ADR Aggregator
|
|
411
|
+
|
|
412
|
+
${templateSections}
|
|
413
|
+
`,
|
|
414
|
+
},
|
|
415
|
+
],
|
|
416
|
+
};
|
|
417
|
+
}
|
|
418
|
+
catch (error) {
|
|
419
|
+
const errorMessage = error instanceof McpAdrError ? error.message : String(error);
|
|
420
|
+
return {
|
|
421
|
+
content: [{ type: 'text', text: `# Failed to Get Templates\n\n**Error:** ${errorMessage}` }],
|
|
422
|
+
isError: true,
|
|
423
|
+
};
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
/**
|
|
427
|
+
* Get Mermaid diagrams from aggregator (Pro+ tier)
|
|
428
|
+
*
|
|
429
|
+
* GET /functions/v1/mcp-get-diagrams
|
|
430
|
+
*/
|
|
431
|
+
export async function getAdrDiagrams(args, context) {
|
|
432
|
+
const client = getAdrAggregatorClient();
|
|
433
|
+
ensureConfigured(client);
|
|
434
|
+
const config = loadConfig();
|
|
435
|
+
const resolvedProjectPath = args.projectPath || config.projectPath;
|
|
436
|
+
ensureGitRepo(resolvedProjectPath);
|
|
437
|
+
try {
|
|
438
|
+
const repositoryName = client.getRepositoryName(resolvedProjectPath);
|
|
439
|
+
context?.info(`Fetching Mermaid diagrams for ${repositoryName}...`);
|
|
440
|
+
context?.report_progress(30, 100);
|
|
441
|
+
const request = {
|
|
442
|
+
repository_name: repositoryName,
|
|
443
|
+
...(args.adr_path !== undefined && { adr_path: args.adr_path }),
|
|
444
|
+
};
|
|
445
|
+
const response = await client.getDiagrams(request);
|
|
446
|
+
context?.report_progress(100, 100);
|
|
447
|
+
return {
|
|
448
|
+
content: [
|
|
449
|
+
{
|
|
450
|
+
type: 'text',
|
|
451
|
+
text: `# ADR Diagrams for ${response.repository}
|
|
452
|
+
|
|
453
|
+
${response.diagrams
|
|
454
|
+
.map(diagram => `
|
|
455
|
+
## ${diagram.adr_path}
|
|
456
|
+
**Generated:** ${diagram.generated_at}
|
|
457
|
+
|
|
458
|
+
${diagram.workflow_diagram
|
|
459
|
+
? `### Workflow Diagram
|
|
460
|
+
\`\`\`mermaid
|
|
461
|
+
${diagram.workflow_diagram}
|
|
462
|
+
\`\`\`
|
|
463
|
+
`
|
|
464
|
+
: ''}
|
|
465
|
+
|
|
466
|
+
${diagram.relationship_diagram
|
|
467
|
+
? `### Relationship Diagram
|
|
468
|
+
\`\`\`mermaid
|
|
469
|
+
${diagram.relationship_diagram}
|
|
470
|
+
\`\`\`
|
|
471
|
+
`
|
|
472
|
+
: ''}
|
|
473
|
+
|
|
474
|
+
${diagram.impact_diagram
|
|
475
|
+
? `### Impact Flow Diagram
|
|
476
|
+
\`\`\`mermaid
|
|
477
|
+
${diagram.impact_diagram}
|
|
478
|
+
\`\`\`
|
|
479
|
+
`
|
|
480
|
+
: ''}
|
|
481
|
+
`)
|
|
482
|
+
.join('\n---\n')}
|
|
483
|
+
`,
|
|
484
|
+
},
|
|
485
|
+
],
|
|
486
|
+
};
|
|
487
|
+
}
|
|
488
|
+
catch (error) {
|
|
489
|
+
const errorMessage = error instanceof McpAdrError ? error.message : String(error);
|
|
490
|
+
const tierRequired = error instanceof McpAdrError && error.details?.['tier_required'];
|
|
491
|
+
return {
|
|
492
|
+
content: [
|
|
493
|
+
{
|
|
494
|
+
type: 'text',
|
|
495
|
+
text: `# Failed to Get Diagrams
|
|
496
|
+
|
|
497
|
+
**Error:** ${errorMessage}
|
|
498
|
+
${tierRequired ? `\n**Note:** This feature requires ${tierRequired} tier. Upgrade at /pricing` : ''}`,
|
|
499
|
+
},
|
|
500
|
+
],
|
|
501
|
+
isError: true,
|
|
502
|
+
};
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
/**
|
|
506
|
+
* Validate ADR compliance via aggregator (Pro+ tier)
|
|
507
|
+
*
|
|
508
|
+
* POST /functions/v1/mcp-validate-compliance
|
|
509
|
+
*/
|
|
510
|
+
export async function validateAdrCompliance(args, context) {
|
|
511
|
+
const client = getAdrAggregatorClient();
|
|
512
|
+
ensureConfigured(client);
|
|
513
|
+
const config = loadConfig();
|
|
514
|
+
const resolvedProjectPath = args.projectPath || config.projectPath;
|
|
515
|
+
ensureGitRepo(resolvedProjectPath);
|
|
516
|
+
try {
|
|
517
|
+
const repositoryName = client.getRepositoryName(resolvedProjectPath);
|
|
518
|
+
context?.info(`Validating ADR compliance for ${repositoryName}...`);
|
|
519
|
+
context?.report_progress(30, 100);
|
|
520
|
+
const request = {
|
|
521
|
+
repository_name: repositoryName,
|
|
522
|
+
validation_type: args.validation_type ?? 'all',
|
|
523
|
+
...(args.adr_paths !== undefined && { adr_paths: args.adr_paths }),
|
|
524
|
+
};
|
|
525
|
+
const response = await client.validateCompliance(request);
|
|
526
|
+
context?.report_progress(100, 100);
|
|
527
|
+
return {
|
|
528
|
+
content: [
|
|
529
|
+
{
|
|
530
|
+
type: 'text',
|
|
531
|
+
text: `# ADR Compliance Validation Results
|
|
532
|
+
|
|
533
|
+
## Repository: ${response.repository}
|
|
534
|
+
|
|
535
|
+
${response.validation_results
|
|
536
|
+
.map(result => `
|
|
537
|
+
## ${result.adr_path}
|
|
538
|
+
|
|
539
|
+
| Metric | Value |
|
|
540
|
+
|--------|-------|
|
|
541
|
+
| Compliance Score | **${result.compliance_score}%** |
|
|
542
|
+
| Status | ${result.status === 'compliant' ? '✅' : result.status === 'partial' ? '⚠️' : '❌'} ${result.status} |
|
|
543
|
+
| Files Validated | ${result.linked_files_validated} |
|
|
544
|
+
|
|
545
|
+
### Findings
|
|
546
|
+
${result.findings.map(f => `- ${f.type === 'success' ? '✅' : f.type === 'warning' ? '⚠️' : '❌'} ${f.description}`).join('\n')}
|
|
547
|
+
|
|
548
|
+
### Recommendations
|
|
549
|
+
${result.recommendations.map(r => `- ${r}`).join('\n')}
|
|
550
|
+
`)
|
|
551
|
+
.join('\n---\n')}
|
|
552
|
+
`,
|
|
553
|
+
},
|
|
554
|
+
],
|
|
555
|
+
};
|
|
556
|
+
}
|
|
557
|
+
catch (error) {
|
|
558
|
+
const errorMessage = error instanceof McpAdrError ? error.message : String(error);
|
|
559
|
+
const tierRequired = error instanceof McpAdrError && error.details?.['tier_required'];
|
|
560
|
+
return {
|
|
561
|
+
content: [
|
|
562
|
+
{
|
|
563
|
+
type: 'text',
|
|
564
|
+
text: `# Failed to Validate Compliance
|
|
565
|
+
|
|
566
|
+
**Error:** ${errorMessage}
|
|
567
|
+
${tierRequired ? `\n**Note:** This feature requires ${tierRequired} tier. Upgrade at /pricing` : ''}`,
|
|
568
|
+
},
|
|
569
|
+
],
|
|
570
|
+
isError: true,
|
|
571
|
+
};
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
/**
|
|
575
|
+
* Get knowledge graph from aggregator (Team tier)
|
|
576
|
+
*
|
|
577
|
+
* GET /functions/v1/mcp-get-knowledge-graph
|
|
578
|
+
*/
|
|
579
|
+
export async function getKnowledgeGraph(args, context) {
|
|
580
|
+
const client = getAdrAggregatorClient();
|
|
581
|
+
ensureConfigured(client);
|
|
582
|
+
try {
|
|
583
|
+
const request = {
|
|
584
|
+
scope: args.scope ?? 'repository',
|
|
585
|
+
include_analytics: args.include_analytics ?? true,
|
|
586
|
+
};
|
|
587
|
+
// Only need repository name for repository scope
|
|
588
|
+
if (args.scope !== 'organization') {
|
|
589
|
+
const config = loadConfig();
|
|
590
|
+
const resolvedProjectPath = args.projectPath || config.projectPath;
|
|
591
|
+
ensureGitRepo(resolvedProjectPath);
|
|
592
|
+
request.repository_name = client.getRepositoryName(resolvedProjectPath);
|
|
593
|
+
}
|
|
594
|
+
context?.info(`Fetching knowledge graph (scope: ${request.scope})...`);
|
|
595
|
+
context?.report_progress(30, 100);
|
|
596
|
+
const response = await client.getKnowledgeGraph(request);
|
|
597
|
+
context?.report_progress(100, 100);
|
|
598
|
+
return {
|
|
599
|
+
content: [
|
|
600
|
+
{
|
|
601
|
+
type: 'text',
|
|
602
|
+
text: `# Knowledge Graph
|
|
603
|
+
|
|
604
|
+
${response.organization_id ? `**Organization:** ${response.organization_id}` : ''}
|
|
605
|
+
${response.repository ? `**Repository:** ${response.repository}` : ''}
|
|
606
|
+
${response.total_repositories ? `**Total Repositories:** ${response.total_repositories}` : ''}
|
|
607
|
+
|
|
608
|
+
## Graph Statistics
|
|
609
|
+
- **Nodes:** ${response.graph.nodes.length}
|
|
610
|
+
- **Relationships:** ${response.graph.relationships.length}
|
|
611
|
+
|
|
612
|
+
${response.graph.cross_repo_patterns && response.graph.cross_repo_patterns.length > 0
|
|
613
|
+
? `## Cross-Repository Patterns
|
|
614
|
+
${response.graph.cross_repo_patterns
|
|
615
|
+
.map(p => `
|
|
616
|
+
### ${p.pattern}
|
|
617
|
+
- **Repositories:** ${p.repositories.join(', ')}
|
|
618
|
+
- **ADR Count:** ${p.adr_count}
|
|
619
|
+
`)
|
|
620
|
+
.join('\n')}`
|
|
621
|
+
: ''}
|
|
622
|
+
|
|
623
|
+
${response.insights
|
|
624
|
+
? `## Insights
|
|
625
|
+
|
|
626
|
+
### Most Connected ADRs
|
|
627
|
+
${response.insights.most_connected_adrs.map(a => `- ${a}`).join('\n')}
|
|
628
|
+
|
|
629
|
+
### Orphan Decisions (No Links)
|
|
630
|
+
${response.insights.orphan_decisions.length > 0 ? response.insights.orphan_decisions.map(d => `- ${d}`).join('\n') : '- None detected'}
|
|
631
|
+
|
|
632
|
+
### Pattern Trends
|
|
633
|
+
${response.insights.pattern_trends.map(t => `- ${t.pattern}: ${t.trend} (${t.count})`).join('\n')}`
|
|
634
|
+
: ''}
|
|
635
|
+
`,
|
|
636
|
+
},
|
|
637
|
+
],
|
|
638
|
+
};
|
|
639
|
+
}
|
|
640
|
+
catch (error) {
|
|
641
|
+
const errorMessage = error instanceof McpAdrError ? error.message : String(error);
|
|
642
|
+
const tierRequired = error instanceof McpAdrError && error.details?.['tier_required'];
|
|
643
|
+
return {
|
|
644
|
+
content: [
|
|
645
|
+
{
|
|
646
|
+
type: 'text',
|
|
647
|
+
text: `# Failed to Get Knowledge Graph
|
|
648
|
+
|
|
649
|
+
**Error:** ${errorMessage}
|
|
650
|
+
${tierRequired ? `\n**Note:** This feature requires ${tierRequired} tier. Upgrade at /pricing` : ''}`,
|
|
651
|
+
},
|
|
652
|
+
],
|
|
653
|
+
isError: true,
|
|
654
|
+
};
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
/**
|
|
658
|
+
* Update implementation status of synced ADRs (Pro+ tier)
|
|
659
|
+
*
|
|
660
|
+
* POST /functions/v1/mcp-update-implementation-status
|
|
661
|
+
*/
|
|
662
|
+
export async function updateAdrImplementationStatus(args, context) {
|
|
663
|
+
const client = getAdrAggregatorClient();
|
|
664
|
+
ensureConfigured(client);
|
|
665
|
+
const config = loadConfig();
|
|
666
|
+
const resolvedProjectPath = args.projectPath || config.projectPath;
|
|
667
|
+
ensureGitRepo(resolvedProjectPath);
|
|
668
|
+
try {
|
|
669
|
+
const repositoryName = client.getRepositoryName(resolvedProjectPath);
|
|
670
|
+
context?.info(`Updating implementation status for ${repositoryName}...`);
|
|
671
|
+
context?.report_progress(30, 100);
|
|
672
|
+
// Validate updates array
|
|
673
|
+
if (!args.updates || args.updates.length === 0) {
|
|
674
|
+
return {
|
|
675
|
+
content: [
|
|
676
|
+
{
|
|
677
|
+
type: 'text',
|
|
678
|
+
text: `# Update Implementation Status
|
|
679
|
+
|
|
680
|
+
**Error:** No updates provided. Please specify at least one ADR path and status to update.
|
|
681
|
+
|
|
682
|
+
## Example Usage
|
|
683
|
+
\`\`\`json
|
|
684
|
+
{
|
|
685
|
+
"updates": [
|
|
686
|
+
{
|
|
687
|
+
"adr_path": "docs/adrs/001-use-typescript.md",
|
|
688
|
+
"implementation_status": "implemented",
|
|
689
|
+
"notes": "Completed migration"
|
|
690
|
+
}
|
|
691
|
+
]
|
|
692
|
+
}
|
|
693
|
+
\`\`\`
|
|
694
|
+
|
|
695
|
+
**Valid Status Values:**
|
|
696
|
+
- \`not_started\` - Implementation has not begun
|
|
697
|
+
- \`in_progress\` - Implementation is underway
|
|
698
|
+
- \`implemented\` - Decision has been fully implemented
|
|
699
|
+
- \`deprecated\` - Decision is no longer relevant
|
|
700
|
+
- \`blocked\` - Implementation is blocked`,
|
|
701
|
+
},
|
|
702
|
+
],
|
|
703
|
+
isError: true,
|
|
704
|
+
};
|
|
705
|
+
}
|
|
706
|
+
const request = {
|
|
707
|
+
repository_name: repositoryName,
|
|
708
|
+
updates: args.updates,
|
|
709
|
+
};
|
|
710
|
+
const response = await client.updateImplementationStatus(request);
|
|
711
|
+
context?.report_progress(100, 100);
|
|
712
|
+
context?.info(`Updated ${response.updated_count} ADR status(es) successfully`);
|
|
713
|
+
return {
|
|
714
|
+
content: [
|
|
715
|
+
{
|
|
716
|
+
type: 'text',
|
|
717
|
+
text: `# ADR Implementation Status Updated
|
|
718
|
+
|
|
719
|
+
## Summary
|
|
720
|
+
- **Repository:** ${response.repository}
|
|
721
|
+
- **Updates Applied:** ${response.updated_count}
|
|
722
|
+
- **Timestamp:** ${response.timestamp}
|
|
723
|
+
${response.tier ? `- **Tier:** ${response.tier}` : ''}
|
|
724
|
+
|
|
725
|
+
## Updated ADRs
|
|
726
|
+
${args.updates.map(u => `- **${u.adr_path}**: ${u.implementation_status}${u.notes ? ` (${u.notes})` : ''}`).join('\n')}
|
|
727
|
+
|
|
728
|
+
${response.errors && response.errors.length > 0
|
|
729
|
+
? `## Errors
|
|
730
|
+
${response.errors.map(e => `- ${e.code}: ${e.message}`).join('\n')}`
|
|
731
|
+
: ''}
|
|
732
|
+
|
|
733
|
+
View in dashboard: ${client.getBaseUrl()}/mcp-dashboard`,
|
|
734
|
+
},
|
|
735
|
+
],
|
|
736
|
+
};
|
|
737
|
+
}
|
|
738
|
+
catch (error) {
|
|
739
|
+
const errorMessage = error instanceof McpAdrError ? error.message : String(error);
|
|
740
|
+
const tierRequired = error instanceof McpAdrError && error.details?.['tier_required'];
|
|
741
|
+
return {
|
|
742
|
+
content: [
|
|
743
|
+
{
|
|
744
|
+
type: 'text',
|
|
745
|
+
text: `# Failed to Update Implementation Status
|
|
746
|
+
|
|
747
|
+
**Error:** ${errorMessage}
|
|
748
|
+
${tierRequired ? `\n**Note:** This feature requires ${tierRequired} tier. Upgrade at /pricing` : ''}
|
|
749
|
+
|
|
750
|
+
## Troubleshooting
|
|
751
|
+
1. Ensure ADR_AGGREGATOR_API_KEY is set correctly
|
|
752
|
+
2. Verify you have Pro+ tier subscription
|
|
753
|
+
3. Check that the ADR paths are valid and synced`,
|
|
754
|
+
},
|
|
755
|
+
],
|
|
756
|
+
isError: true,
|
|
757
|
+
};
|
|
758
|
+
}
|
|
759
|
+
}
|
|
760
|
+
/**
|
|
761
|
+
* Get ADR priorities for roadmap/backlog planning
|
|
762
|
+
*
|
|
763
|
+
* GET /functions/v1/mcp-get-priorities
|
|
764
|
+
*/
|
|
765
|
+
export async function getAdrPriorities(args, context) {
|
|
766
|
+
const client = getAdrAggregatorClient();
|
|
767
|
+
ensureConfigured(client);
|
|
768
|
+
const config = loadConfig();
|
|
769
|
+
const resolvedProjectPath = args.projectPath || config.projectPath;
|
|
770
|
+
ensureGitRepo(resolvedProjectPath);
|
|
771
|
+
try {
|
|
772
|
+
const repositoryName = client.getRepositoryName(resolvedProjectPath);
|
|
773
|
+
context?.info(`Fetching ADR priorities for ${repositoryName}...`);
|
|
774
|
+
context?.report_progress(30, 100);
|
|
775
|
+
const request = {
|
|
776
|
+
repository_name: repositoryName,
|
|
777
|
+
...(args.include_ai !== undefined && { include_ai: args.include_ai }),
|
|
778
|
+
};
|
|
779
|
+
const response = await client.getPriorities(request);
|
|
780
|
+
context?.report_progress(100, 100);
|
|
781
|
+
// Sort priorities by score (highest first)
|
|
782
|
+
const sortedPriorities = [...response.priorities].sort((a, b) => b.priority_score - a.priority_score);
|
|
783
|
+
return {
|
|
784
|
+
content: [
|
|
785
|
+
{
|
|
786
|
+
type: 'text',
|
|
787
|
+
text: `# ADR Priorities for ${response.repository}
|
|
788
|
+
|
|
789
|
+
## Summary
|
|
790
|
+
- **Total ADRs:** ${response.summary.total_adrs}
|
|
791
|
+
- **Implemented:** ${response.summary.implemented}
|
|
792
|
+
- **In Progress:** ${response.summary.in_progress}
|
|
793
|
+
- **Not Started:** ${response.summary.not_started}
|
|
794
|
+
- **Blocked:** ${response.summary.blocked}
|
|
795
|
+
- **Total Gaps:** ${response.summary.total_gaps}
|
|
796
|
+
${response.tier ? `- **Tier:** ${response.tier}` : ''}
|
|
797
|
+
|
|
798
|
+
---
|
|
799
|
+
|
|
800
|
+
## Prioritized Roadmap
|
|
801
|
+
|
|
802
|
+
${sortedPriorities.length > 0
|
|
803
|
+
? sortedPriorities
|
|
804
|
+
.map((p, index) => `
|
|
805
|
+
### ${index + 1}. ${p.title}
|
|
806
|
+
- **Path:** ${p.adr_path}
|
|
807
|
+
- **Priority Score:** ${p.priority_score}/100 ${p.ai_prioritized ? '(AI)' : ''}
|
|
808
|
+
- **Status:** ${p.implementation_status === 'implemented' ? '✅' : p.implementation_status === 'in_progress' ? '🔄' : p.implementation_status === 'blocked' ? '🚫' : '⏳'} ${p.implementation_status}
|
|
809
|
+
- **Dependencies:** ${p.dependencies.length > 0 ? p.dependencies.join(', ') : 'None'}
|
|
810
|
+
- **Blockers:** ${p.blockers.length > 0 ? p.blockers.join(', ') : 'None'}
|
|
811
|
+
- **Gap Count:** ${p.gap_count}
|
|
812
|
+
`)
|
|
813
|
+
.join('\n')
|
|
814
|
+
: '*No ADRs found in this repository*'}
|
|
815
|
+
|
|
816
|
+
---
|
|
817
|
+
|
|
818
|
+
## Recommendations
|
|
819
|
+
|
|
820
|
+
${sortedPriorities.length === 0
|
|
821
|
+
? 'Sync ADRs to the aggregator first using `sync_to_aggregator` to get priority analysis.'
|
|
822
|
+
: `
|
|
823
|
+
### High Priority Items
|
|
824
|
+
${sortedPriorities
|
|
825
|
+
.filter(p => p.priority_score >= 70 && p.implementation_status !== 'implemented')
|
|
826
|
+
.map(p => `- **${p.title}** (Score: ${p.priority_score}) - ${p.blockers.length > 0 ? 'Blocked by: ' + p.blockers.join(', ') : 'Ready to implement'}`)
|
|
827
|
+
.join('\n') || '- No high-priority items pending'}
|
|
828
|
+
|
|
829
|
+
### Blocked Items
|
|
830
|
+
${sortedPriorities
|
|
831
|
+
.filter(p => p.implementation_status === 'blocked' || p.blockers.length > 0)
|
|
832
|
+
.map(p => `- **${p.title}** blocked by: ${p.blockers.join(', ')}`)
|
|
833
|
+
.join('\n') || '- No blocked items'}
|
|
834
|
+
|
|
835
|
+
### Items with Gaps
|
|
836
|
+
${sortedPriorities
|
|
837
|
+
.filter(p => p.gap_count > 0)
|
|
838
|
+
.map(p => `- **${p.title}** has ${p.gap_count} gap(s) to address`)
|
|
839
|
+
.join('\n') || '- No items with gaps'}
|
|
840
|
+
`}
|
|
841
|
+
|
|
842
|
+
---
|
|
843
|
+
|
|
844
|
+
*Priority analysis performed at ${new Date().toISOString()}*
|
|
845
|
+
View in dashboard: ${client.getBaseUrl()}/mcp-dashboard`,
|
|
846
|
+
},
|
|
847
|
+
],
|
|
848
|
+
};
|
|
849
|
+
}
|
|
850
|
+
catch (error) {
|
|
851
|
+
const errorMessage = error instanceof McpAdrError ? error.message : String(error);
|
|
852
|
+
const tierRequired = error instanceof McpAdrError && error.details?.['tier_required'];
|
|
853
|
+
return {
|
|
854
|
+
content: [
|
|
855
|
+
{
|
|
856
|
+
type: 'text',
|
|
857
|
+
text: `# Failed to Get ADR Priorities
|
|
858
|
+
|
|
859
|
+
**Error:** ${errorMessage}
|
|
860
|
+
${tierRequired ? `\n**Note:** This feature requires ${tierRequired} tier. Upgrade at /pricing` : ''}
|
|
861
|
+
|
|
862
|
+
## Troubleshooting
|
|
863
|
+
1. Ensure ADR_AGGREGATOR_API_KEY is set correctly
|
|
864
|
+
2. Verify you have an active internet connection
|
|
865
|
+
3. Check that ADRs have been synced to the aggregator`,
|
|
866
|
+
},
|
|
867
|
+
],
|
|
868
|
+
isError: true,
|
|
869
|
+
};
|
|
870
|
+
}
|
|
871
|
+
}
|
|
872
|
+
//# sourceMappingURL=adr-aggregator-tools.js.map
|