#!/usr/bin/env node
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import {
CallToolRequestSchema,
ListToolsRequestSchema,
ListResourcesRequestSchema,
ReadResourceRequestSchema,
} from '@modelcontextprotocol/sdk/types.js';
import fs from 'fs/promises';
import path from 'path';
import { fileURLToPath } from 'url';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
// MCP Server for VA Form Generation
const server = new Server(
{
name: 'va-form-generation',
version: '1.0.0',
},
{
capabilities: {
tools: {},
resources: {},
},
}
);
// Load checklist template
async function loadChecklist() {
const checklistPath = path.join(__dirname, 'templates', 'master-checklist.md');
return await fs.readFile(checklistPath, 'utf-8');
}
// Load agent prompts
async function loadAgentPrompt(agentNumber) {
const promptPath = path.join(__dirname, 'templates', `agent-${agentNumber}-prompt.md`);
return await fs.readFile(promptPath, 'utf-8');
}
// List available tools
server.setRequestHandler(ListToolsRequestSchema, async () => {
return {
tools: [
{
name: 'audit_form',
description: 'Audit a scaffolded VA form and generate comprehensive fix report following 21P-601 best practices',
inputSchema: {
type: 'object',
properties: {
form_number: {
type: 'string',
description: 'VA form number (e.g., "21p-0516")',
},
workspace_path: {
type: 'string',
description: 'Path to vets-website workspace',
},
pdf_path: {
type: 'string',
description: 'Optional: Path to form PDF for accuracy verification',
},
},
required: ['form_number', 'workspace_path'],
},
},
{
name: 'get_agent_prompt',
description: 'Get the specialized prompt for a specific agent (1-4)',
inputSchema: {
type: 'object',
properties: {
agent_number: {
type: 'number',
description: 'Agent number (1-4)',
enum: [1, 2, 3, 4],
},
form_number: {
type: 'string',
description: 'VA form number to customize prompt',
},
form_path: {
type: 'string',
description: 'Path to form directory',
},
pdf_path: {
type: 'string',
description: 'Path to PDF file',
},
},
required: ['agent_number', 'form_number', 'form_path'],
},
},
{
name: 'validate_form',
description: 'Run validation checks on a form (schema, syntax, apostrophes)',
inputSchema: {
type: 'object',
properties: {
form_path: {
type: 'string',
description: 'Path to form directory',
},
check_types: {
type: 'array',
items: {
type: 'string',
enum: ['schema', 'syntax', 'apostrophes', 'imports', 'all'],
},
description: 'Types of checks to run',
},
},
required: ['form_path', 'check_types'],
},
},
{
name: 'get_fix_reference',
description: 'Get quick reference for common fixes',
inputSchema: {
type: 'object',
properties: {
fix_type: {
type: 'string',
enum: ['yesNoUI', 'radioSchema', 'apostrophes', 'transformers', 'fullNamePath', 'all'],
description: 'Type of fix to get reference for',
},
},
required: ['fix_type'],
},
},
{
name: 'generate_orchestration_prompt',
description: 'Generate the full orchestration prompt to launch all 4 agents',
inputSchema: {
type: 'object',
properties: {
form_number: {
type: 'string',
description: 'VA form number',
},
workspace_path: {
type: 'string',
description: 'Path to vets-website workspace',
},
pdf_path: {
type: 'string',
description: 'Path to PDF file',
},
},
required: ['form_number', 'workspace_path'],
},
},
],
};
});
// List available resources
server.setRequestHandler(ListResourcesRequestSchema, async () => {
return {
resources: [
{
uri: 'checklist://master',
mimeType: 'text/markdown',
name: 'VA Form Generation Master Checklist',
description: 'Complete checklist for auditing and fixing VA forms',
},
{
uri: 'template://agent-1',
mimeType: 'text/markdown',
name: 'Agent 1: Component Pattern Auditor',
description: 'Specialized prompt for component pattern auditing',
},
{
uri: 'template://agent-2',
mimeType: 'text/markdown',
name: 'Agent 2: Architecture Compliance',
description: 'Specialized prompt for architecture fixes',
},
{
uri: 'template://agent-3',
mimeType: 'text/markdown',
name: 'Agent 3: Plain Language & PDF',
description: 'Specialized prompt for content and PDF accuracy',
},
{
uri: 'template://agent-4',
mimeType: 'text/markdown',
name: 'Agent 4: Integration Validator',
description: 'Specialized prompt for validation',
},
{
uri: 'reference://fixes',
mimeType: 'text/markdown',
name: 'Common Fixes Quick Reference',
description: 'Before/after examples for all 16 fixes',
},
],
};
});
// Read resource content
server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
const uri = request.params.uri;
if (uri === 'checklist://master') {
const content = await loadChecklist();
return {
contents: [
{
uri,
mimeType: 'text/markdown',
text: content,
},
],
};
}
if (uri.startsWith('template://agent-')) {
const agentNumber = parseInt(uri.split('-')[1]);
const content = await loadAgentPrompt(agentNumber);
return {
contents: [
{
uri,
mimeType: 'text/markdown',
text: content,
},
],
};
}
throw new Error(`Unknown resource: ${uri}`);
});
// Handle tool calls
server.setRequestHandler(CallToolRequestSchema, async (request) => {
const { name, arguments: args } = request.params;
switch (name) {
case 'audit_form': {
const { form_number, workspace_path, pdf_path } = args;
const formPath = path.join(workspace_path, 'src/applications/simple-forms', form_number);
// Check if form exists
try {
await fs.access(formPath);
} catch {
return {
content: [
{
type: 'text',
text: `❌ Form directory not found: ${formPath}\n\nMake sure the form has been scaffolded first.`,
},
],
};
}
// Find PDF if not provided
let pdfLocation = pdf_path;
if (!pdfLocation) {
try {
const files = await fs.readdir(formPath);
const pdfFile = files.find(f => f.endsWith('.pdf'));
if (pdfFile) {
pdfLocation = path.join(formPath, pdfFile);
}
} catch (e) {
pdfLocation = '[PDF not found]';
}
}
const report = `# VA Form ${form_number} Audit Report
## Form Information
- **Form Number:** ${form_number}
- **Form Path:** ${formPath}
- **PDF File:** ${pdfLocation}
- **Reference Form:** src/applications/simple-forms/21p-601/
## Recommended Actions
### 1. Launch Parallel Agents (Run simultaneously)
**Agent 1: Component Pattern Auditor**
- Type: Explore agent (medium thoroughness)
- Scope: All files in pages/
- Finds: yesNoUI patterns, import paths, radioSchema issues, duplicate imports
**Agent 2: Architecture Compliance**
- Type: General-Purpose agent
- Scope: Core files (IntroductionPage, ConfirmationPage, transformers, form.js)
- Fixes: All architecture anti-patterns
**Agent 3: Plain Language & PDF Accuracy**
- Type: General-Purpose agent
- Scope: All user-facing content and PDF comparison
- Fixes: Content, labels, plain language issues
### 2. Sequential Validation
**Agent 4: Integration Validator**
- Type: General-Purpose agent (runs AFTER agents 1-3 complete)
- Validates: Schema, syntax, loading, conditional logic
## Quick Start Command
\`\`\`
Launch agents 1, 2, 3 in parallel for form ${form_number}:
- Form path: ${formPath}
- PDF path: ${pdfLocation}
- Execute as described in master checklist
\`\`\`
## Expected Fixes
✅ Component patterns (6 fixes)
✅ Architecture (5 fixes)
✅ Content & accuracy (3 fixes)
✅ Syntax (2 fixes)
**Total: 16+ automated fixes**
`;
return {
content: [
{
type: 'text',
text: report,
},
],
};
}
case 'get_agent_prompt': {
const { agent_number, form_number, form_path, pdf_path } = args;
const basePrompt = await loadAgentPrompt(agent_number);
// Customize prompt with form-specific info
const customizedPrompt = basePrompt
.replace(/\[FORM-NUMBER\]/g, form_number)
.replace(/\[FORM-PATH\]/g, form_path)
.replace(/\[PDF-PATH\]/g, pdf_path || '[PDF not specified]');
return {
content: [
{
type: 'text',
text: customizedPrompt,
},
],
};
}
case 'validate_form': {
const { form_path, check_types } = args;
const checks = check_types.includes('all')
? ['schema', 'syntax', 'apostrophes', 'imports']
: check_types;
const results = [];
for (const checkType of checks) {
switch (checkType) {
case 'schema':
results.push('## Schema Validation\n\nCommand to find uncalled radioSchema:\n```bash\ngrep -rn ": radioSchema[,\\s]" ' + form_path + '/pages/\n```');
break;
case 'apostrophes':
results.push('## Apostrophe Syntax Check\n\nCommand to find strings with apostrophes:\n```bash\ngrep -rn "\'\\w.*\'\\w.*\'" ' + form_path + '/ --include="*.js" --include="*.jsx"\n```');
break;
case 'imports':
results.push('## Import Path Check\n\nCommand to find old import paths:\n```bash\ngrep -rn "web-component-patterns/" ' + form_path + '/\n```');
break;
case 'syntax':
results.push('## Syntax Validation\n\nCommand to run linter:\n```bash\nnpm run lint -- ' + form_path + '/\n```');
break;
}
}
return {
content: [
{
type: 'text',
text: '# Validation Report\n\n' + results.join('\n\n'),
},
],
};
}
case 'get_fix_reference': {
const { fix_type } = args;
const fixes = {
yesNoUI: `## Fix: yesNoUI Pattern
**Problem:** Complex object form with labelHeaderLevel, custom labels, errorMessages
**Solution:** Simple pattern
\`\`\`javascript
// BEFORE (WRONG):
yesNoUI({
title: 'Are you a patient in a nursing home?',
labelHeaderLevel: '3',
labels: { Y: 'Yes', N: 'No' },
errorMessages: { required: '...' },
})
// AFTER (CORRECT):
yesNoUI('Are you a patient in a nursing home?')
\`\`\``,
radioSchema: `## Fix: radioSchema Function Call
**Problem:** radioSchema used as bare reference instead of being called
**Solution:** Call it with enum values
\`\`\`javascript
// BEFORE (WRONG):
properties: {
radioButtonList: radioSchema,
}
// AFTER (CORRECT):
properties: {
radioButtonList: radioSchema(['option1', 'option2', 'option3']),
}
\`\`\`
**Note:** checkboxSchema is used directly (not called) - it's already a plain schema object.`,
apostrophes: `## Fix: Apostrophe Syntax
**Problem:** Single quotes containing apostrophes cause syntax errors
**Solution:** Use double quotes or JSX expressions
\`\`\`javascript
// BEFORE (SYNTAX ERROR):
formTitle: 'Report pension or parents' DIC eligibility'
// AFTER (CORRECT - Option 1):
formTitle: "Report pension or Parents' DIC eligibility"
// AFTER (CORRECT - Option 2):
<strong>{"If you're reporting changes after a spouse's death,"}</strong>
\`\`\``,
transformers: `## Fix: Submit Transformer
**Problem:** Wrong return format, missing helpers, uses moment library
**Solution:** Follow 21P-601 pattern
\`\`\`javascript
// WRONG:
import moment from 'moment';
return { form: JSON.stringify(result) };
// CORRECT:
import { transformForSubmit as defaultTransformForSubmit } from 'platform/forms-system/src/js/helpers';
export default function submitTransformer(formConfig, form) {
const transformedData = JSON.parse(defaultTransformForSubmit(formConfig, form));
// Helper functions from 21P-601
const splitDate = dateString => { /* ... */ };
const formatName = nameObj => { /* ... */ };
const formatAddress = addressObj => { /* ... */ };
const result = { /* transform data */ };
return JSON.stringify(result); // Direct string return
}
\`\`\``,
fullNamePath: `## Fix: fullNamePath in preSubmitInfo
**Problem:** Missing fullNamePath prevents signature field on review page
**Solution:** Add fullNamePath to statementOfTruth
\`\`\`javascript
// BEFORE (WRONG - No signature field):
preSubmitInfo: {
statementOfTruth: {
body: 'I certify that the information in this form is accurate...',
},
}
// AFTER (CORRECT - Enables signature):
preSubmitInfo: {
statementOfTruth: {
body: 'I certify that the information in this form is accurate...',
messageAriaDescribedby: 'I certify that the information in this form is accurate...',
fullNamePath: 'veteranFullName', // CRITICAL - path to name field
},
}
\`\`\`
**This is CRITICAL** - without fullNamePath, the signature field won't appear on the review page!`,
};
const content = fix_type === 'all'
? Object.values(fixes).join('\n\n---\n\n')
: fixes[fix_type] || 'Fix type not found';
return {
content: [
{
type: 'text',
text: content,
},
],
};
}
case 'generate_orchestration_prompt': {
const { form_number, workspace_path, pdf_path } = args;
const formPath = path.join(workspace_path, 'src/applications/simple-forms', form_number);
const orchestrationPrompt = `# VA Form ${form_number} - Orchestration Prompt
Execute the 4-agent plan to audit and fix this form following 21P-601 best practices.
## Form Information
- **Form Number:** ${form_number}
- **Form Path:** ${formPath}
- **PDF Path:** ${pdf_path || 'Auto-detect from form directory'}
- **Reference:** src/applications/simple-forms/21p-601/
## Agent Execution Plan
### Phase 1: Launch 3 Agents in PARALLEL
Use a **single message with 3 Task tool calls** to launch these agents simultaneously:
#### Agent 1: Component Pattern Auditor
- **Type:** Explore agent
- **Thoroughness:** medium
- **Task:** Search all files in ${formPath}/pages/ for:
- Complex yesNoUI patterns (should be simple form)
- Old import paths (should be 'platform/forms-system/src/js/web-component-patterns')
- Uncalled radioSchema (should be radioSchema([...]))
- Duplicate imports
- **Deliverable:** List of files to fix with specific issues
#### Agent 2: Architecture Compliance
- **Type:** General-Purpose agent
- **Task:** Audit and rewrite core architecture files:
- containers/IntroductionPage.jsx (content structure, OMB info from PDF)
- containers/ConfirmationPage.jsx (use ConfirmationView component)
- config/submit-transformer.js (21P-601 pattern with helpers)
- config/prefill-transformer.js (access user profile from state)
- config/form.js (add prefillEnabled, savedFormMessages, fullNamePath)
- **Deliverable:** Rewritten files following 21P-601 patterns
#### Agent 3: Plain Language & PDF Accuracy
- **Type:** General-Purpose agent
- **Task:**
- Read PDF at ${pdf_path || formPath + '/*.pdf'} to understand form purpose
- Verify all field labels match PDF exactly
- Check VA.gov plain language standards (conversational tone, you/we pronouns)
- Fix person-centered title (e.g., "Verify your..." not "Report for VA")
- Fix apostrophe syntax (use double quotes)
- **Deliverable:** Updated content following VA.gov standards
### Phase 2: Wait for Agents 1-3 to Complete
### Phase 3: Launch Agent 4 SEQUENTIALLY
After agents 1-3 finish, launch Agent 4:
#### Agent 4: Integration Validator
- **Type:** General-Purpose agent
- **Task:**
- Run schema validation (grep for uncalled radioSchema)
- Check apostrophe syntax
- Verify form loads without errors
- Validate conditional logic matches PDF
- Report any remaining issues
- **Deliverable:** Validation report with any outstanding issues
## Expected Results
After completion, the form should have:
- ✅ No schema validation errors
- ✅ All components use 21P-601 patterns
- ✅ IntroductionPage with proper structure and OMB info
- ✅ ConfirmationPage using ConfirmationView
- ✅ Transformers following 21P-601 pattern
- ✅ form.js with fullNamePath (enables signature)
- ✅ All content follows VA plain language
- ✅ Field labels match PDF exactly
- ✅ No apostrophe syntax errors
## Begin Execution
Launch agents 1, 2, 3 in parallel now (single message with 3 Task calls).
`;
return {
content: [
{
type: 'text',
text: orchestrationPrompt,
},
],
};
}
default:
throw new Error(`Unknown tool: ${name}`);
}
});
// Start server
async function main() {
const transport = new StdioServerTransport();
await server.connect(transport);
console.error('VA Form Generation MCP server running on stdio');
}
main().catch((error) => {
console.error('Server error:', error);
process.exit(1);
});