@aws/ml-container-creator 1.0.3 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (79) hide show
  1. package/README.md +10 -1
  2. package/bin/cli.js +57 -0
  3. package/config/agent.json +16 -0
  4. package/infra/ci-harness/lib/ci-harness-stack.ts +43 -0
  5. package/package.json +5 -2
  6. package/pyproject.toml +3 -0
  7. package/servers/agent-knowledge/index.js +592 -0
  8. package/servers/agent-knowledge/package.json +15 -0
  9. package/servers/base-image-picker/index.js +65 -18
  10. package/servers/instance-sizer/index.js +32 -0
  11. package/servers/lib/catalogs/fleet-drivers.json +38 -0
  12. package/servers/lib/catalogs/model-arch-support.json +51 -0
  13. package/servers/lib/catalogs/model-servers.json +2842 -1730
  14. package/servers/lib/schemas/image-catalog.schema.json +12 -0
  15. package/src/agent/__init__.py +2 -0
  16. package/src/agent/__pycache__/__init__.cpython-312.pyc +0 -0
  17. package/src/agent/__pycache__/config_loader.cpython-312.pyc +0 -0
  18. package/src/agent/__pycache__/context.cpython-312.pyc +0 -0
  19. package/src/agent/__pycache__/health_check.cpython-312.pyc +0 -0
  20. package/src/agent/agent.py +513 -0
  21. package/src/agent/config_loader.py +215 -0
  22. package/src/agent/context.py +380 -0
  23. package/src/agent/data/capability-matrix.json +106 -0
  24. package/src/agent/health_check.py +341 -0
  25. package/src/agent/prompts/system.md +173 -0
  26. package/src/agent/requirements-agent.txt +3 -0
  27. package/src/app.js +6 -4
  28. package/src/lib/generated/cli-options.js +1 -1
  29. package/src/lib/generated/parameter-matrix.js +1 -1
  30. package/src/lib/generated/validation-rules.js +1 -1
  31. package/src/lib/mcp-query-runner.js +110 -3
  32. package/src/lib/prompt-runner.js +66 -22
  33. package/src/lib/template-variable-resolver.js +8 -0
  34. package/src/lib/train-config-builder.js +339 -0
  35. package/src/lib/tune-config-state.js +89 -68
  36. package/templates/do/.benchmark_writer.py +3 -0
  37. package/templates/do/.eval_helper.py +409 -0
  38. package/templates/do/.register_helper.py +185 -11
  39. package/templates/do/.train_build_request.py +102 -113
  40. package/templates/do/.train_helper.py +433 -0
  41. package/templates/do/__pycache__/.register_helper.cpython-312.pyc +0 -0
  42. package/templates/do/adapter +157 -0
  43. package/templates/do/benchmark +60 -3
  44. package/templates/do/config +6 -1
  45. package/templates/do/deploy.d/managed-inference.ejs +83 -0
  46. package/templates/do/evaluate +272 -0
  47. package/templates/do/lib/resolve-instance.sh +155 -0
  48. package/templates/do/register +5 -0
  49. package/templates/do/test +1 -0
  50. package/templates/do/train +879 -126
  51. package/templates/do/training/config.yaml +83 -11
  52. package/templates/do/training/dpo/accelerate_config.yaml +24 -0
  53. package/templates/do/training/dpo/defaults.yaml +26 -0
  54. package/templates/do/training/dpo/prompts.json +8 -0
  55. package/templates/do/training/dpo/train.py +363 -0
  56. package/templates/do/training/sft/accelerate_config.yaml +22 -0
  57. package/templates/do/training/sft/defaults.yaml +18 -0
  58. package/templates/do/training/sft/prompts.json +7 -0
  59. package/templates/do/training/sft/train.py +310 -0
  60. package/templates/do/tune +11 -2
  61. package/src/lib/auto-prompt-builder.js +0 -172
  62. package/src/lib/cli-handler.js +0 -529
  63. package/src/lib/community-reports-validator.js +0 -91
  64. package/src/lib/configuration-exporter.js +0 -204
  65. package/src/lib/dataset-slug.js +0 -152
  66. package/src/lib/docker-introspection-validator.js +0 -51
  67. package/src/lib/known-flags-validator.js +0 -200
  68. package/src/lib/schema-validator.js +0 -157
  69. package/src/lib/train-config-parser.js +0 -136
  70. package/src/lib/train-config-persistence.js +0 -143
  71. package/src/lib/train-config-validator.js +0 -112
  72. package/src/lib/train-feedback.js +0 -46
  73. package/src/lib/train-idempotency.js +0 -97
  74. package/src/lib/train-request-builder.js +0 -120
  75. package/src/lib/tune-dataset-validator.js +0 -279
  76. package/src/lib/tune-output-resolver.js +0 -66
  77. package/templates/do/.train_poll_parser.py +0 -135
  78. package/templates/do/.train_status_parser.py +0 -187
  79. /package/templates/do/training/{train.py → custom/train.py} +0 -0
@@ -0,0 +1,592 @@
1
+ #!/usr/bin/env node
2
+ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3
+ // SPDX-License-Identifier: Apache-2.0
4
+
5
+ /**
6
+ * Agent Knowledge MCP Server
7
+ *
8
+ * A bundled MCP server that provides project knowledge not covered by
9
+ * other specialized servers: script reference, config documentation,
10
+ * troubleshooting patterns, and capability matrix.
11
+ *
12
+ * Tool: query_knowledge
13
+ * Accepts: { topic, filter? }
14
+ * Returns: topic-specific structured data
15
+ *
16
+ * Topics:
17
+ * - script_reference: do/* script metadata (purpose, flags, lifecycle position)
18
+ * - config_reference: do/config exported variables and documentation
19
+ * - troubleshooting: parsed TROUBLESHOOTING.md patterns
20
+ * - capability_matrix: agent capability matrix (full or filtered)
21
+ */
22
+
23
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
24
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
25
+ import { z } from 'zod';
26
+ import { readFileSync, readdirSync, statSync } from 'node:fs';
27
+ import { fileURLToPath } from 'node:url';
28
+ import { resolve, dirname, basename } from 'node:path';
29
+
30
+ // ── Path setup ───────────────────────────────────────────────────────────────
31
+
32
+ const __filename = fileURLToPath(import.meta.url);
33
+ const __dirname = dirname(__filename);
34
+ const PACKAGE_ROOT = resolve(__dirname, '../../');
35
+
36
+ // ── Logging ──────────────────────────────────────────────────────────────────
37
+
38
+ function log(message) {
39
+ process.stderr.write(`[agent-knowledge] ${message}\n`);
40
+ }
41
+
42
+ // ── In-memory cache ──────────────────────────────────────────────────────────
43
+
44
+ const cache = new Map();
45
+
46
+ function getCached(key, loader) {
47
+ if (cache.has(key)) {
48
+ return cache.get(key);
49
+ }
50
+ const value = loader();
51
+ cache.set(key, value);
52
+ return value;
53
+ }
54
+
55
+ // ── Script Reference Parser ──────────────────────────────────────────────────
56
+
57
+ /**
58
+ * Known lifecycle positions for do/* scripts.
59
+ * Provides ordering context for the agent.
60
+ */
61
+ const LIFECYCLE_POSITIONS = {
62
+ config: 'configuration',
63
+ build: 'build',
64
+ run: 'local-test',
65
+ test: 'local-test',
66
+ push: 'publish',
67
+ deploy: 'deploy',
68
+ status: 'monitor',
69
+ logs: 'monitor',
70
+ clean: 'teardown',
71
+ validate: 'pre-deploy',
72
+ register: 'publish',
73
+ stage: 'publish',
74
+ optimize: 'pre-deploy',
75
+ benchmark: 'post-deploy',
76
+ evaluate: 'post-deploy',
77
+ adapter: 'post-deploy',
78
+ 'add-ic': 'post-deploy',
79
+ train: 'training',
80
+ tune: 'training',
81
+ ci: 'ci',
82
+ submit: 'build',
83
+ export: 'publish',
84
+ manifest: 'metadata'
85
+ };
86
+
87
+ /**
88
+ * Parse a single do/* script file to extract metadata from header comments.
89
+ *
90
+ * Looks for:
91
+ * - First comment block for purpose description
92
+ * - Usage: lines for flags
93
+ * - `source` directives for reads
94
+ * - Variable exports for writes
95
+ */
96
+ function parseScriptFile(filePath) {
97
+ try {
98
+ const content = readFileSync(filePath, 'utf8');
99
+ const name = basename(filePath);
100
+
101
+ // Extract purpose from first comment block (lines starting with #, after shebang)
102
+ const lines = content.split('\n');
103
+ let purpose = '';
104
+ let flags = [];
105
+ const reads = [];
106
+ const writes = [];
107
+ const commonFailures = [];
108
+
109
+ // Skip shebang and copyright, find first descriptive comment
110
+ const inHeader = true;
111
+ const headerComments = [];
112
+ for (const line of lines) {
113
+ if (line.startsWith('#!')) continue;
114
+ if (line.startsWith('# Copyright')) continue;
115
+ if (line.startsWith('# SPDX-License-Identifier')) continue;
116
+ if (line === '#' || line === '') {
117
+ if (headerComments.length > 0) break;
118
+ continue;
119
+ }
120
+ if (inHeader && line.startsWith('#')) {
121
+ headerComments.push(line.replace(/^#\s?/, ''));
122
+ } else {
123
+ break;
124
+ }
125
+ }
126
+
127
+ // First non-empty header comment is the purpose
128
+ purpose = headerComments.filter(c => c.trim()).join(' ').trim();
129
+
130
+ // Parse Usage block for flags
131
+ const usageMatch = content.match(/# Usage:\s*\n((?:#\s+.*\n)*)/);
132
+ if (usageMatch) {
133
+ const usageLines = usageMatch[1].split('\n')
134
+ .map(l => l.replace(/^#\s*/, '').trim())
135
+ .filter(Boolean);
136
+ flags = usageLines;
137
+ }
138
+
139
+ // Parse individual flag definitions (--flag patterns in comments)
140
+ const flagPattern = /^#\s+(--[\w-]+(?:\s+\S+)?)\s+(.+)/gm;
141
+ let flagMatch;
142
+ while ((flagMatch = flagPattern.exec(content)) !== null) {
143
+ if (!flags.includes(flagMatch[1])) {
144
+ flags.push(`${flagMatch[1]} ${flagMatch[2]}`);
145
+ }
146
+ }
147
+
148
+ // Detect source reads (source "..." or source '...')
149
+ const sourcePattern = /source\s+["']?\$\{?SCRIPT_DIR\}?["']?\/?([^"'\s;]+)/g;
150
+ let srcMatch;
151
+ while ((srcMatch = sourcePattern.exec(content)) !== null) {
152
+ reads.push(`do/${srcMatch[1]}`);
153
+ }
154
+
155
+ // Detect config sourcing
156
+ if (content.includes('source "${SCRIPT_DIR}/config"') || content.includes('source \'${SCRIPT_DIR}/config\'')) {
157
+ if (!reads.includes('do/config')) {
158
+ reads.push('do/config');
159
+ }
160
+ }
161
+
162
+ // Detect writes (common output patterns)
163
+ if (content.includes('docker build')) writes.push('Docker image');
164
+ if (content.includes('docker push') || content.includes('ecr')) writes.push('ECR repository');
165
+ if (content.includes('aws sagemaker create-endpoint')) writes.push('SageMaker endpoint');
166
+ if (content.includes('aws sagemaker create-model')) writes.push('SageMaker model');
167
+ if (content.includes('aws sagemaker delete-')) writes.push('SageMaker resources (delete)');
168
+
169
+ // Common failure patterns from script error handling
170
+ const exitPattern = /echo\s+"(?:\u274c|\u26a0\ufe0f)\s+(.+?)"\s*\n\s*(echo\s+".+?")?\s*\n?\s*exit\s+\d+/gu;
171
+ let exitMatch;
172
+ while ((exitMatch = exitPattern.exec(content)) !== null) {
173
+ commonFailures.push(exitMatch[1].replace(/\$\{[^}]+\}/g, '<variable>'));
174
+ }
175
+
176
+ return {
177
+ name,
178
+ purpose: purpose || `do/${name} script`,
179
+ flags,
180
+ reads,
181
+ writes,
182
+ lifecycle_position: LIFECYCLE_POSITIONS[name] || 'unknown',
183
+ common_failures: commonFailures
184
+ };
185
+ } catch (err) {
186
+ return {
187
+ name: basename(filePath),
188
+ purpose: 'Unable to parse',
189
+ flags: [],
190
+ reads: [],
191
+ writes: [],
192
+ lifecycle_position: LIFECYCLE_POSITIONS[basename(filePath)] || 'unknown',
193
+ common_failures: [],
194
+ error: err.message,
195
+ partial: true
196
+ };
197
+ }
198
+ }
199
+
200
+ /**
201
+ * Load and parse all do/* scripts from templates/do/.
202
+ * Skips hidden files, directories, and non-executable templates (e.g., EJS partials).
203
+ */
204
+ function loadScriptReference() {
205
+ const doDir = resolve(PACKAGE_ROOT, 'templates/do');
206
+ try {
207
+ const entries = readdirSync(doDir);
208
+ const scripts = [];
209
+
210
+ for (const entry of entries) {
211
+ // Skip hidden files, directories, __pycache__, README
212
+ if (entry.startsWith('.') || entry === '__pycache__' || entry === 'README.md') continue;
213
+
214
+ const fullPath = resolve(doDir, entry);
215
+ const stat = statSync(fullPath);
216
+ if (stat.isDirectory()) continue;
217
+
218
+ const parsed = parseScriptFile(fullPath);
219
+ // Skip EJS-only templates (contain only <%- include(...) %>)
220
+ if (parsed.purpose === '' && parsed.flags.length === 0) {
221
+ // Check if it's just an include directive
222
+ const content = readFileSync(fullPath, 'utf8').trim();
223
+ if (content.startsWith('<%') && content.length < 200) {
224
+ parsed.purpose = 'Template include (delegates to sub-template)';
225
+ }
226
+ }
227
+ scripts.push(parsed);
228
+ }
229
+
230
+ return scripts;
231
+ } catch (err) {
232
+ return { error: `Failed to read templates/do/: ${err.message}`, partial: true };
233
+ }
234
+ }
235
+
236
+ // ── Config Reference Parser ──────────────────────────────────────────────────
237
+
238
+ /**
239
+ * Parse the do/config template to extract exported variables and their documentation.
240
+ */
241
+ function loadConfigReference() {
242
+ const configPath = resolve(PACKAGE_ROOT, 'templates/do/config');
243
+ try {
244
+ const content = readFileSync(configPath, 'utf8');
245
+ const lines = content.split('\n');
246
+
247
+ const doConfigVars = [];
248
+ const icEnvVars = [];
249
+ const trainingConfig = [];
250
+
251
+ let currentComment = '';
252
+ let currentSection = 'do_config';
253
+
254
+ for (const line of lines) {
255
+ // Track sections
256
+ if (line.includes('Training') || line.includes('training')) {
257
+ currentSection = 'training';
258
+ }
259
+ if (line.includes('IC ') || line.includes('inference component') || line.includes('Inference Component')) {
260
+ currentSection = 'ic';
261
+ }
262
+
263
+ // Collect comments
264
+ if (line.startsWith('#') && !line.startsWith('#!') && !line.startsWith('# Copyright') && !line.startsWith('# SPDX')) {
265
+ const comment = line.replace(/^#\s?/, '').trim();
266
+ if (comment && !comment.startsWith('──')) {
267
+ currentComment = comment;
268
+ }
269
+ continue;
270
+ }
271
+
272
+ // Parse export lines
273
+ const exportMatch = line.match(/^export\s+(\w+)=(.*)$/);
274
+ if (exportMatch) {
275
+ const varName = exportMatch[1];
276
+ const defaultValue = exportMatch[2]
277
+ .replace(/\$\{[^:}]+:-([^}]*)\}/g, '$1') // Extract default from ${VAR:-default}
278
+ .replace(/["'<>%=\s]/g, '')
279
+ .trim();
280
+
281
+ const entry = {
282
+ name: varName,
283
+ description: currentComment || '',
284
+ default: defaultValue || null
285
+ };
286
+
287
+ if (currentSection === 'training') {
288
+ trainingConfig.push(entry);
289
+ } else if (currentSection === 'ic') {
290
+ icEnvVars.push(entry);
291
+ } else {
292
+ doConfigVars.push(entry);
293
+ }
294
+ currentComment = '';
295
+ continue;
296
+ }
297
+
298
+ // Parse commented-out export lines (optional vars)
299
+ const commentedExport = line.match(/^#\s*export\s+(\w+)=(.*)$/);
300
+ if (commentedExport) {
301
+ const varName = commentedExport[1];
302
+ const entry = {
303
+ name: varName,
304
+ description: currentComment || '(optional, commented out)',
305
+ default: null,
306
+ optional: true
307
+ };
308
+
309
+ if (currentSection === 'training') {
310
+ trainingConfig.push(entry);
311
+ } else if (currentSection === 'ic') {
312
+ icEnvVars.push(entry);
313
+ } else {
314
+ doConfigVars.push(entry);
315
+ }
316
+ currentComment = '';
317
+ continue;
318
+ }
319
+
320
+ // EJS conditionals reset comment
321
+ if (line.startsWith('<%')) {
322
+ currentComment = '';
323
+ }
324
+ }
325
+
326
+ return {
327
+ do_config_vars: doConfigVars,
328
+ ic_env_vars: icEnvVars,
329
+ training_config: trainingConfig
330
+ };
331
+ } catch (err) {
332
+ return { error: `Failed to parse config: ${err.message}`, partial: true };
333
+ }
334
+ }
335
+
336
+ // ── Troubleshooting Parser ───────────────────────────────────────────────────
337
+
338
+ /**
339
+ * Parse TROUBLESHOOTING.md into structured patterns.
340
+ * Each H3 (###) section becomes a troubleshooting entry with pattern, root cause,
341
+ * diagnostic steps, and fix.
342
+ */
343
+ function loadTroubleshooting() {
344
+ const tsPath = resolve(PACKAGE_ROOT, 'docs/TROUBLESHOOTING.md');
345
+ try {
346
+ const content = readFileSync(tsPath, 'utf8');
347
+ const patterns = [];
348
+
349
+ // Split by ### headings (H3 = individual issues)
350
+ const sections = content.split(/^### /gm).slice(1); // Skip content before first ###
351
+
352
+ for (const section of sections) {
353
+ const lines = section.split('\n');
354
+ const pattern = lines[0].trim();
355
+
356
+ let rootCause = '';
357
+ const diagnosticSteps = [];
358
+ let fix = '';
359
+
360
+ let currentBlock = '';
361
+ let inCodeBlock = false;
362
+ let codeContent = '';
363
+
364
+ for (let i = 1; i < lines.length; i++) {
365
+ const line = lines[i];
366
+
367
+ // Track code blocks
368
+ if (line.startsWith('```')) {
369
+ if (inCodeBlock) {
370
+ inCodeBlock = false;
371
+ if (currentBlock === 'fix' || currentBlock === 'diagnostic') {
372
+ if (currentBlock === 'fix') {
373
+ fix += `${codeContent.trim() }\n`;
374
+ } else {
375
+ diagnosticSteps.push(codeContent.trim());
376
+ }
377
+ }
378
+ codeContent = '';
379
+ } else {
380
+ inCodeBlock = true;
381
+ }
382
+ continue;
383
+ }
384
+
385
+ if (inCodeBlock) {
386
+ codeContent += `${line }\n`;
387
+ continue;
388
+ }
389
+
390
+ // Detect section markers
391
+ const lower = line.toLowerCase();
392
+ if (lower.includes('**root cause') || lower.includes('**root cause:**') || lower.startsWith('**root cause')) {
393
+ currentBlock = 'root_cause';
394
+ const afterColon = line.replace(/\*\*[Rr]oot [Cc]ause:?\*\*:?\s*/, '').trim();
395
+ if (afterColon) rootCause = afterColon;
396
+ continue;
397
+ }
398
+ if (lower.includes('**fix') || lower.includes('**workaround')) {
399
+ currentBlock = 'fix';
400
+ const afterColon = line.replace(/\*\*[Ff]ix:?\*\*:?\s*|\*\*[Ww]orkaround:?\*\*:?\s*/, '').trim();
401
+ if (afterColon) fix = afterColon;
402
+ continue;
403
+ }
404
+ if (lower.includes('**symptoms') || lower.includes('**debug')) {
405
+ currentBlock = 'diagnostic';
406
+ continue;
407
+ }
408
+
409
+ // Accumulate content into current block
410
+ if (currentBlock === 'root_cause' && line.trim()) {
411
+ rootCause += (rootCause ? ' ' : '') + line.trim().replace(/\*\*/g, '');
412
+ } else if (currentBlock === 'fix' && line.trim()) {
413
+ fix += (fix ? ' ' : '') + line.trim().replace(/\*\*/g, '');
414
+ } else if (currentBlock === 'diagnostic' && line.trim()) {
415
+ diagnosticSteps.push(line.trim().replace(/\*\*/g, ''));
416
+ } else if (!currentBlock && line.trim() && !line.startsWith('#')) {
417
+ // Content before any explicit block — treat as root cause
418
+ rootCause += (rootCause ? ' ' : '') + line.trim().replace(/\*\*/g, '');
419
+ }
420
+ }
421
+
422
+ // Only include entries that have meaningful content
423
+ if (pattern && (rootCause || fix || diagnosticSteps.length > 0)) {
424
+ patterns.push({
425
+ pattern: pattern.replace(/[`*]/g, ''),
426
+ root_cause: rootCause.trim() || 'See documentation',
427
+ diagnostic_steps: diagnosticSteps.length > 0 ? diagnosticSteps : ['Check logs for error details'],
428
+ fix: fix.trim() || 'See documentation'
429
+ });
430
+ }
431
+ }
432
+
433
+ return patterns;
434
+ } catch (err) {
435
+ return { error: `Failed to parse TROUBLESHOOTING.md: ${err.message}`, partial: true };
436
+ }
437
+ }
438
+
439
+ // ── Capability Matrix Loader ─────────────────────────────────────────────────
440
+
441
+ /**
442
+ * Load the capability matrix from src/agent/data/capability-matrix.json.
443
+ * Returns full matrix or filtered by optional filter string.
444
+ */
445
+ function loadCapabilityMatrix(filter) {
446
+ const matrixPath = resolve(PACKAGE_ROOT, 'src/agent/data/capability-matrix.json');
447
+ try {
448
+ const content = readFileSync(matrixPath, 'utf8');
449
+ const matrix = JSON.parse(content);
450
+
451
+ if (!filter) return matrix;
452
+
453
+ // Filter matrix entries by keyword match
454
+ const filterLower = filter.toLowerCase();
455
+
456
+ if (Array.isArray(matrix)) {
457
+ return matrix.filter(entry => {
458
+ const text = JSON.stringify(entry).toLowerCase();
459
+ return text.includes(filterLower);
460
+ });
461
+ }
462
+
463
+ // If matrix is an object with named categories, filter by key or values
464
+ if (typeof matrix === 'object') {
465
+ const filtered = {};
466
+ for (const [key, value] of Object.entries(matrix)) {
467
+ const keyMatch = key.toLowerCase().includes(filterLower);
468
+ const valueMatch = JSON.stringify(value).toLowerCase().includes(filterLower);
469
+ if (keyMatch || valueMatch) {
470
+ filtered[key] = value;
471
+ }
472
+ }
473
+ return filtered;
474
+ }
475
+
476
+ return matrix;
477
+ } catch (err) {
478
+ if (err.code === 'ENOENT') {
479
+ return { error: 'capability-matrix.json not found — run Task 3 to generate it', partial: true };
480
+ }
481
+ return { error: `Failed to load capability matrix: ${err.message}`, partial: true };
482
+ }
483
+ }
484
+
485
+ // ── Tool Handler ─────────────────────────────────────────────────────────────
486
+
487
+ /**
488
+ * Main tool handler for query_knowledge.
489
+ */
490
+ async function handleQueryKnowledge({ topic, filter }) {
491
+ log(`Query: topic=${topic}, filter=${filter || 'none'}`);
492
+
493
+ let result;
494
+
495
+ switch (topic) {
496
+ case 'script_reference': {
497
+ let scripts = getCached('script_reference', loadScriptReference);
498
+ if (filter && Array.isArray(scripts)) {
499
+ const filterLower = filter.toLowerCase();
500
+ scripts = scripts.filter(s =>
501
+ s.name.toLowerCase().includes(filterLower) ||
502
+ s.purpose.toLowerCase().includes(filterLower) ||
503
+ s.lifecycle_position.toLowerCase().includes(filterLower)
504
+ );
505
+ }
506
+ result = scripts;
507
+ break;
508
+ }
509
+
510
+ case 'config_reference': {
511
+ result = getCached('config_reference', loadConfigReference);
512
+ break;
513
+ }
514
+
515
+ case 'troubleshooting': {
516
+ let patterns = getCached('troubleshooting', loadTroubleshooting);
517
+ if (filter && Array.isArray(patterns)) {
518
+ const filterLower = filter.toLowerCase();
519
+ patterns = patterns.filter(p =>
520
+ p.pattern.toLowerCase().includes(filterLower) ||
521
+ p.root_cause.toLowerCase().includes(filterLower) ||
522
+ p.fix.toLowerCase().includes(filterLower)
523
+ );
524
+ }
525
+ result = patterns;
526
+ break;
527
+ }
528
+
529
+ case 'capability_matrix': {
530
+ // Capability matrix supports filter at load time for efficiency
531
+ const cacheKey = `capability_matrix:${filter || ''}`;
532
+ result = getCached(cacheKey, () => loadCapabilityMatrix(filter || null));
533
+ break;
534
+ }
535
+
536
+ default:
537
+ result = {
538
+ error: `Unknown topic: "${topic}". Valid topics: script_reference, config_reference, troubleshooting, capability_matrix`,
539
+ partial: true
540
+ };
541
+ }
542
+
543
+ return {
544
+ content: [{
545
+ type: 'text',
546
+ text: JSON.stringify(result, null, 2)
547
+ }]
548
+ };
549
+ }
550
+
551
+ // ── MCP Server setup ─────────────────────────────────────────────────────────
552
+
553
+ const server = new McpServer({
554
+ name: 'agent-knowledge',
555
+ version: '1.0.0'
556
+ });
557
+
558
+ server.tool(
559
+ 'query_knowledge',
560
+ 'Query project knowledge base. Returns structured data for script reference, config documentation, troubleshooting patterns, or capability matrix. Call this tool BEFORE answering questions about do/* scripts, configuration variables, or common issues.',
561
+ {
562
+ topic: z.enum(['script_reference', 'config_reference', 'troubleshooting', 'capability_matrix'])
563
+ .describe('Knowledge topic to query'),
564
+ filter: z.string().optional()
565
+ .describe('Optional filter — narrows results by keyword match (e.g., script name, lifecycle stage, error pattern)')
566
+ },
567
+ async (params) => {
568
+ return handleQueryKnowledge(params);
569
+ }
570
+ );
571
+
572
+ // ── Exports for testing ──────────────────────────────────────────────────────
573
+
574
+ export {
575
+ handleQueryKnowledge,
576
+ loadScriptReference,
577
+ loadConfigReference,
578
+ loadTroubleshooting,
579
+ loadCapabilityMatrix,
580
+ server,
581
+ PACKAGE_ROOT
582
+ };
583
+
584
+ // ── Transport connection (main module only) ──────────────────────────────────
585
+
586
+ const isMain = process.argv[1] && resolve(process.argv[1]) === __filename;
587
+
588
+ if (isMain) {
589
+ log('Starting agent-knowledge MCP server (stdio transport)');
590
+ const transport = new StdioServerTransport();
591
+ await server.connect(transport);
592
+ }
@@ -0,0 +1,15 @@
1
+ {
2
+ "name": "@amzn/ml-container-creator-agent-knowledge",
3
+ "private": true,
4
+ "version": "1.0.0",
5
+ "description": "MCP server providing script reference, config documentation, troubleshooting patterns, and capability matrix for the agent.",
6
+ "type": "module",
7
+ "main": "index.js",
8
+ "license": "Apache-2.0",
9
+ "scripts": {
10
+ "test": "node test.js"
11
+ },
12
+ "dependencies": {
13
+ "@modelcontextprotocol/sdk": "^1.0.0"
14
+ }
15
+ }