auto-publishing-mcp-server
Version:
Enterprise-grade MCP Server for Auto-Publishing with pre-publish validation, multi-cloud deployment, and monitoring
1,023 lines (904 loc) • 39.6 kB
JavaScript
import { exec } from 'child_process';
import { promisify } from 'util';
import { promises as fs } from 'fs';
import path from 'path';
const execAsync = promisify(exec);
class PrePublishValidator {
constructor() {
this.validationResults = new Map();
this.validationRules = new Map();
this.autoFixEnabled = true;
this.configPath = '/tmp/pre-publish-config.json';
this.reportsPath = '/tmp/validation-reports';
this.loadConfiguration();
this.ensureReportsDirectory();
this.initializeDefaultRules();
}
async loadConfiguration() {
try {
const config = await fs.readFile(this.configPath, 'utf8');
const parsedConfig = JSON.parse(config);
this.validationResults = new Map(Object.entries(parsedConfig.validationResults || {}));
this.autoFixEnabled = parsedConfig.autoFixEnabled !== false;
} catch (error) {
console.log('No existing validation configuration found, using defaults');
}
}
async saveConfiguration() {
const config = {
validationResults: Object.fromEntries(this.validationResults),
autoFixEnabled: this.autoFixEnabled,
lastUpdated: new Date().toISOString()
};
await fs.writeFile(this.configPath, JSON.stringify(config, null, 2));
}
async ensureReportsDirectory() {
try {
await fs.mkdir(this.reportsPath, { recursive: true });
} catch (error) {
console.warn('Failed to create reports directory:', error.message);
}
}
initializeDefaultRules() {
// Code quality rules
this.validationRules.set('syntax-check', {
name: 'Syntax Check',
description: 'Validate code syntax',
autoFixable: false,
critical: true
});
this.validationRules.set('lint-check', {
name: 'Linting Check',
description: 'Check code style and quality',
autoFixable: true,
critical: false
});
this.validationRules.set('test-coverage', {
name: 'Test Coverage',
description: 'Ensure minimum test coverage',
autoFixable: false,
critical: true
});
// Security rules
this.validationRules.set('security-scan', {
name: 'Security Scan',
description: 'Scan for security vulnerabilities',
autoFixable: false,
critical: true
});
this.validationRules.set('dependency-check', {
name: 'Dependency Check',
description: 'Check for vulnerable dependencies',
autoFixable: true,
critical: true
});
// Build and deployment rules
this.validationRules.set('build-test', {
name: 'Build Test',
description: 'Verify application builds successfully',
autoFixable: false,
critical: true
});
this.validationRules.set('docker-build', {
name: 'Docker Build',
description: 'Verify Docker image builds successfully',
autoFixable: false,
critical: true
});
// Configuration rules
this.validationRules.set('config-validation', {
name: 'Configuration Validation',
description: 'Validate configuration files',
autoFixable: true,
critical: false
});
this.validationRules.set('env-check', {
name: 'Environment Check',
description: 'Validate environment variables',
autoFixable: false,
critical: true
});
// Documentation rules
this.validationRules.set('docs-check', {
name: 'Documentation Check',
description: 'Ensure required documentation exists',
autoFixable: true,
critical: false
});
}
async runPrePublishValidation(args) {
const {
projectPath = '.',
validationId = `validation_${Date.now()}`,
rules = [],
autoFix = this.autoFixEnabled,
skipNonCritical = false
} = args;
console.log(`Starting pre-publish validation: ${validationId}`);
const validation = {
validationId,
projectPath,
startTime: new Date().toISOString(),
status: 'running',
rules: rules.length > 0 ? rules : Array.from(this.validationRules.keys()),
results: [],
summary: {},
autoFixEnabled: autoFix,
fixedIssues: []
};
this.validationResults.set(validationId, validation);
try {
// Run each validation rule
for (const ruleName of validation.rules) {
if (!this.validationRules.has(ruleName)) {
console.warn(`Unknown validation rule: ${ruleName}`);
continue;
}
const rule = this.validationRules.get(ruleName);
if (skipNonCritical && !rule.critical) {
console.log(`Skipping non-critical rule: ${ruleName}`);
continue;
}
console.log(`Running validation rule: ${rule.name}`);
const ruleResult = await this.runValidationRule(ruleName, projectPath, autoFix);
validation.results.push(ruleResult);
// If this is a critical rule and it failed, stop validation
if (rule.critical && ruleResult.status === 'failed') {
console.error(`Critical validation failed: ${rule.name}`);
validation.status = 'failed';
break;
}
}
// Generate summary
validation.summary = this.generateValidationSummary(validation);
if (validation.status !== 'failed') {
validation.status = validation.summary.criticalIssues > 0 ? 'failed' : 'passed';
}
validation.endTime = new Date().toISOString();
this.validationResults.set(validationId, validation);
await this.saveConfiguration();
// Generate report
await this.generateValidationReport(validation);
return {
success: validation.status === 'passed',
message: validation.status === 'passed'
? 'All validations passed successfully'
: `Validation failed with ${validation.summary.criticalIssues} critical issues`,
data: {
validationId: validationId,
status: validation.status,
summary: validation.summary,
reportPath: path.join(this.reportsPath, `${validationId}.json`)
}
};
} catch (error) {
validation.status = 'error';
validation.error = error.message;
validation.endTime = new Date().toISOString();
this.validationResults.set(validationId, validation);
await this.saveConfiguration();
return {
success: false,
message: `Validation error: ${error.message}`,
data: { validationId: validationId }
};
}
}
async runValidationRule(ruleName, projectPath, autoFix) {
const rule = this.validationRules.get(ruleName);
const result = {
rule: ruleName,
name: rule.name,
status: 'passed',
issues: [],
fixedIssues: [],
duration: 0,
output: ''
};
const startTime = Date.now();
try {
switch (ruleName) {
case 'syntax-check':
await this.runSyntaxCheck(projectPath, result);
break;
case 'lint-check':
await this.runLintCheck(projectPath, result, autoFix);
break;
case 'test-coverage':
await this.runTestCoverage(projectPath, result);
break;
case 'security-scan':
await this.runSecurityScan(projectPath, result);
break;
case 'dependency-check':
await this.runDependencyCheck(projectPath, result, autoFix);
break;
case 'build-test':
await this.runBuildTest(projectPath, result);
break;
case 'docker-build':
await this.runDockerBuild(projectPath, result);
break;
case 'config-validation':
await this.runConfigValidation(projectPath, result, autoFix);
break;
case 'env-check':
await this.runEnvCheck(projectPath, result);
break;
case 'docs-check':
await this.runDocsCheck(projectPath, result, autoFix);
break;
default:
result.status = 'skipped';
result.output = 'Unknown validation rule';
}
} catch (error) {
result.status = 'failed';
result.issues.push({
type: 'error',
message: error.message,
file: '',
line: 0
});
}
result.duration = Date.now() - startTime;
return result;
}
async runSyntaxCheck(projectPath, result) {
try {
// Check JavaScript/TypeScript syntax
const { stdout: jsFiles } = await execAsync(`find ${projectPath} -name "*.js" -o -name "*.ts" -o -name "*.jsx" -o -name "*.tsx" | head -50`);
for (const file of jsFiles.split('\n').filter(f => f.trim())) {
try {
if (file.endsWith('.ts') || file.endsWith('.tsx')) {
await execAsync(`npx tsc --noEmit --skipLibCheck ${file}`, { cwd: projectPath });
} else {
await execAsync(`node --check ${file}`, { cwd: projectPath });
}
} catch (syntaxError) {
result.status = 'failed';
result.issues.push({
type: 'syntax_error',
message: syntaxError.stderr || syntaxError.message,
file: file,
line: this.extractLineNumber(syntaxError.stderr || syntaxError.message)
});
}
}
// Check JSON syntax
const { stdout: jsonFiles } = await execAsync(`find ${projectPath} -name "*.json" | head -20`);
for (const file of jsonFiles.split('\n').filter(f => f.trim())) {
try {
const content = await fs.readFile(file, 'utf8');
JSON.parse(content);
} catch (jsonError) {
result.status = 'failed';
result.issues.push({
type: 'json_syntax_error',
message: jsonError.message,
file: file,
line: 0
});
}
}
result.output = `Checked syntax for ${jsFiles.split('\n').length + jsonFiles.split('\n').length} files`;
} catch (error) {
result.output = `Syntax check error: ${error.message}`;
}
}
async runLintCheck(projectPath, result, autoFix) {
try {
// Try ESLint first
try {
const lintCommand = autoFix ? 'npx eslint . --fix --format json' : 'npx eslint . --format json';
const { stdout } = await execAsync(lintCommand, { cwd: projectPath });
const lintResults = JSON.parse(stdout);
for (const fileResult of lintResults) {
for (const message of fileResult.messages) {
if (message.severity === 2) { // Error
result.status = 'failed';
}
result.issues.push({
type: message.severity === 2 ? 'error' : 'warning',
message: `${message.ruleId}: ${message.message}`,
file: fileResult.filePath,
line: message.line
});
if (autoFix && message.fix) {
result.fixedIssues.push({
file: fileResult.filePath,
rule: message.ruleId,
fix: 'auto-fixed'
});
}
}
}
} catch (eslintError) {
// Try with basic JavaScript linting
const { stdout } = await execAsync(`find ${projectPath} -name "*.js" | xargs -I {} node -c {}`, { cwd: projectPath });
result.output = 'Basic linting completed (ESLint not available)';
}
} catch (error) {
result.output = `Lint check error: ${error.message}`;
}
}
async runTestCoverage(projectPath, result) {
try {
// Try to run tests with coverage
let testCommand = '';
// Check for different test frameworks
if (await this.fileExists(path.join(projectPath, 'package.json'))) {
const packageJson = JSON.parse(await fs.readFile(path.join(projectPath, 'package.json'), 'utf8'));
if (packageJson.scripts && packageJson.scripts.test) {
testCommand = 'npm test';
} else if (packageJson.devDependencies && packageJson.devDependencies.jest) {
testCommand = 'npx jest --coverage';
} else if (packageJson.devDependencies && packageJson.devDependencies.mocha) {
testCommand = 'npx nyc mocha';
}
}
if (testCommand) {
const { stdout, stderr } = await execAsync(testCommand, { cwd: projectPath });
// Parse coverage information
const coverageMatch = stdout.match(/All files\s+\|\s+(\d+\.?\d*)/);
const coverage = coverageMatch ? parseFloat(coverageMatch[1]) : 0;
if (coverage < 80) {
result.status = 'failed';
result.issues.push({
type: 'low_coverage',
message: `Test coverage ${coverage}% is below minimum threshold of 80%`,
file: '',
line: 0
});
}
result.output = `Test coverage: ${coverage}%`;
} else {
result.status = 'failed';
result.issues.push({
type: 'no_tests',
message: 'No test configuration found',
file: '',
line: 0
});
}
} catch (error) {
result.status = 'failed';
result.issues.push({
type: 'test_failure',
message: error.message,
file: '',
line: 0
});
}
}
async runSecurityScan(projectPath, result) {
try {
// Use npm audit or yarn audit
let auditCommand = 'npm audit --json';
try {
const { stdout } = await execAsync(auditCommand, { cwd: projectPath });
const auditResult = JSON.parse(stdout);
if (auditResult.vulnerabilities) {
for (const [pkg, vuln] of Object.entries(auditResult.vulnerabilities)) {
if (vuln.severity === 'high' || vuln.severity === 'critical') {
result.status = 'failed';
}
result.issues.push({
type: 'vulnerability',
message: `${pkg}: ${vuln.title} (${vuln.severity})`,
file: 'package.json',
line: 0,
severity: vuln.severity
});
}
}
} catch (auditError) {
result.output = 'Security audit not available or failed';
}
} catch (error) {
result.output = `Security scan error: ${error.message}`;
}
}
async runDependencyCheck(projectPath, result, autoFix) {
try {
if (await this.fileExists(path.join(projectPath, 'package.json'))) {
// Check for outdated packages
const { stdout } = await execAsync('npm outdated --json', { cwd: projectPath });
if (stdout.trim()) {
const outdated = JSON.parse(stdout);
for (const [pkg, info] of Object.entries(outdated)) {
const isMajorUpdate = this.isMajorVersionDifference(info.current, info.latest);
if (isMajorUpdate) {
result.issues.push({
type: 'major_update_available',
message: `${pkg}: ${info.current} -> ${info.latest} (major update)`,
file: 'package.json',
line: 0
});
} else if (autoFix) {
// Auto-update minor/patch versions
try {
await execAsync(`npm update ${pkg}`, { cwd: projectPath });
result.fixedIssues.push({
package: pkg,
from: info.current,
to: info.latest,
type: 'dependency_update'
});
} catch (updateError) {
result.issues.push({
type: 'update_failed',
message: `Failed to update ${pkg}: ${updateError.message}`,
file: 'package.json',
line: 0
});
}
}
}
}
}
} catch (error) {
result.output = `Dependency check error: ${error.message}`;
}
}
async runBuildTest(projectPath, result) {
try {
if (await this.fileExists(path.join(projectPath, 'package.json'))) {
const packageJson = JSON.parse(await fs.readFile(path.join(projectPath, 'package.json'), 'utf8'));
if (packageJson.scripts && packageJson.scripts.build) {
const { stdout, stderr } = await execAsync('npm run build', { cwd: projectPath });
result.output = 'Build completed successfully';
} else {
result.output = 'No build script found, skipping build test';
}
} else {
// Try other build systems
if (await this.fileExists(path.join(projectPath, 'Makefile'))) {
await execAsync('make', { cwd: projectPath });
result.output = 'Make build completed successfully';
} else {
result.output = 'No build configuration found';
}
}
} catch (error) {
result.status = 'failed';
result.issues.push({
type: 'build_failure',
message: error.message,
file: '',
line: 0
});
}
}
async runDockerBuild(projectPath, result) {
try {
if (await this.fileExists(path.join(projectPath, 'Dockerfile'))) {
const imageName = `validation-test-${Date.now()}`;
try {
await execAsync(`docker build -t ${imageName} .`, { cwd: projectPath });
result.output = 'Docker build completed successfully';
// Clean up test image
try {
await execAsync(`docker rmi ${imageName}`);
} catch (cleanupError) {
console.warn('Failed to clean up test image:', cleanupError.message);
}
} catch (buildError) {
result.status = 'failed';
result.issues.push({
type: 'docker_build_failure',
message: buildError.message,
file: 'Dockerfile',
line: 0
});
}
} else {
result.output = 'No Dockerfile found, skipping Docker build test';
}
} catch (error) {
result.output = `Docker build test error: ${error.message}`;
}
}
async runConfigValidation(projectPath, result, autoFix) {
try {
const configFiles = [
'config.json', 'config.yaml', 'config.yml',
'.env.example', 'docker-compose.yml'
];
for (const configFile of configFiles) {
const filePath = path.join(projectPath, configFile);
if (await this.fileExists(filePath)) {
try {
const content = await fs.readFile(filePath, 'utf8');
if (configFile.endsWith('.json')) {
JSON.parse(content);
} else if (configFile.endsWith('.yml') || configFile.endsWith('.yaml')) {
// Basic YAML validation (would need yaml parser for full validation)
if (content.includes('\t')) {
if (autoFix) {
const fixed = content.replace(/\t/g, ' ');
await fs.writeFile(filePath, fixed);
result.fixedIssues.push({
file: configFile,
issue: 'tabs_to_spaces',
fix: 'Converted tabs to spaces'
});
} else {
result.issues.push({
type: 'yaml_formatting',
message: 'YAML file contains tabs instead of spaces',
file: configFile,
line: 0
});
}
}
}
} catch (parseError) {
result.status = 'failed';
result.issues.push({
type: 'config_parse_error',
message: `Invalid ${configFile}: ${parseError.message}`,
file: configFile,
line: 0
});
}
}
}
} catch (error) {
result.output = `Config validation error: ${error.message}`;
}
}
async runEnvCheck(projectPath, result) {
try {
const envExamplePath = path.join(projectPath, '.env.example');
const envPath = path.join(projectPath, '.env');
if (await this.fileExists(envExamplePath)) {
const envExample = await fs.readFile(envExamplePath, 'utf8');
const requiredVars = envExample.match(/^[A-Z_][A-Z0-9_]*=/gm) || [];
if (await this.fileExists(envPath)) {
const envContent = await fs.readFile(envPath, 'utf8');
for (const varLine of requiredVars) {
const varName = varLine.split('=')[0];
if (!envContent.includes(`${varName}=`)) {
result.issues.push({
type: 'missing_env_var',
message: `Missing environment variable: ${varName}`,
file: '.env',
line: 0
});
}
}
} else {
result.issues.push({
type: 'missing_env_file',
message: '.env file is missing but .env.example exists',
file: '.env',
line: 0
});
}
}
result.output = 'Environment variables check completed';
} catch (error) {
result.output = `Environment check error: ${error.message}`;
}
}
async runDocsCheck(projectPath, result, autoFix) {
try {
const requiredDocs = ['README.md', 'CHANGELOG.md', 'LICENSE'];
for (const docFile of requiredDocs) {
const filePath = path.join(projectPath, docFile);
if (!(await this.fileExists(filePath))) {
if (autoFix && docFile === 'README.md') {
// Auto-generate basic README
const packageJsonPath = path.join(projectPath, 'package.json');
let projectName = 'Project';
if (await this.fileExists(packageJsonPath)) {
const packageJson = JSON.parse(await fs.readFile(packageJsonPath, 'utf8'));
projectName = packageJson.name || 'Project';
}
const basicReadme = `# ${projectName}
## Description
Add a brief description of your project here.
## Installation
\`\`\`bash
npm install
\`\`\`
## Usage
Add usage instructions here.
## Contributing
Add contribution guidelines here.
## License
Add license information here.
`;
await fs.writeFile(filePath, basicReadme);
result.fixedIssues.push({
file: docFile,
issue: 'missing_documentation',
fix: 'Generated basic README.md'
});
} else {
result.issues.push({
type: 'missing_documentation',
message: `Missing required documentation: ${docFile}`,
file: docFile,
line: 0
});
}
}
}
} catch (error) {
result.output = `Documentation check error: ${error.message}`;
}
}
generateValidationSummary(validation) {
const summary = {
totalRules: validation.results.length,
passedRules: 0,
failedRules: 0,
totalIssues: 0,
criticalIssues: 0,
fixedIssues: 0,
duration: 0
};
for (const result of validation.results) {
if (result.status === 'passed') {
summary.passedRules++;
} else if (result.status === 'failed') {
summary.failedRules++;
}
summary.totalIssues += result.issues.length;
summary.fixedIssues += result.fixedIssues.length;
summary.duration += result.duration;
// Count critical issues
const rule = this.validationRules.get(result.rule);
if (rule && rule.critical && result.status === 'failed') {
summary.criticalIssues += result.issues.length;
}
}
return summary;
}
async generateValidationReport(validation) {
const reportFile = path.join(this.reportsPath, `${validation.validationId}.json`);
try {
await fs.writeFile(reportFile, JSON.stringify(validation, null, 2));
// Generate HTML report
const htmlReport = this.generateHTMLValidationReport(validation);
const htmlFile = path.join(this.reportsPath, `${validation.validationId}.html`);
await fs.writeFile(htmlFile, htmlReport);
return { jsonReport: reportFile, htmlReport: htmlFile };
} catch (error) {
console.warn('Failed to generate validation report:', error.message);
return {};
}
}
generateHTMLValidationReport(validation) {
const { summary } = validation;
return `
<!DOCTYPE html>
<html>
<head>
<title>Pre-Publish Validation Report</title>
<style>
body { font-family: Arial, sans-serif; margin: 20px; }
.header { background-color: #f4f4f4; padding: 20px; border-radius: 5px; margin-bottom: 20px; }
.summary { margin: 20px 0; }
.status-passed { color: #4caf50; }
.status-failed { color: #f44336; }
.status-warning { color: #ff9800; }
.rule-result { border: 1px solid #ddd; margin: 10px 0; padding: 15px; border-radius: 5px; }
.rule-passed { border-left: 5px solid #4caf50; }
.rule-failed { border-left: 5px solid #f44336; }
.issue { background-color: #fff3cd; padding: 8px; margin: 5px 0; border-radius: 3px; }
.fixed-issue { background-color: #d4edda; padding: 8px; margin: 5px 0; border-radius: 3px; }
table { width: 100%; border-collapse: collapse; margin: 20px 0; }
th, td { border: 1px solid #ddd; padding: 8px; text-align: left; }
th { background-color: #f2f2f2; }
</style>
</head>
<body>
<div class="header">
<h1>Pre-Publish Validation Report</h1>
<p><strong>Project:</strong> ${validation.projectPath}</p>
<p><strong>Validation ID:</strong> ${validation.validationId}</p>
<p><strong>Status:</strong> <span class="status-${validation.status}">${validation.status.toUpperCase()}</span></p>
<p><strong>Duration:</strong> ${Math.round(summary.duration / 1000)}s</p>
</div>
<div class="summary">
<h2>Summary</h2>
<table>
<tr><th>Metric</th><th>Count</th></tr>
<tr><td>Total Rules</td><td>${summary.totalRules}</td></tr>
<tr><td>Passed Rules</td><td class="status-passed">${summary.passedRules}</td></tr>
<tr><td>Failed Rules</td><td class="status-failed">${summary.failedRules}</td></tr>
<tr><td>Total Issues</td><td>${summary.totalIssues}</td></tr>
<tr><td>Critical Issues</td><td class="status-failed">${summary.criticalIssues}</td></tr>
<tr><td>Fixed Issues</td><td class="status-passed">${summary.fixedIssues}</td></tr>
</table>
</div>
<div class="results">
<h2>Validation Results</h2>
${validation.results.map(result => `
<div class="rule-result rule-${result.status}">
<h3>${result.name} <span class="status-${result.status}">(${result.status})</span></h3>
<p><strong>Duration:</strong> ${result.duration}ms</p>
<p><strong>Output:</strong> ${result.output}</p>
${result.issues.length > 0 ? `
<h4>Issues Found:</h4>
${result.issues.map(issue => `
<div class="issue">
<strong>${issue.type}:</strong> ${issue.message}
${issue.file ? `<br><em>File: ${issue.file}${issue.line ? `:${issue.line}` : ''}</em>` : ''}
</div>
`).join('')}
` : ''}
${result.fixedIssues.length > 0 ? `
<h4>Auto-Fixed Issues:</h4>
${result.fixedIssues.map(fix => `
<div class="fixed-issue">
<strong>Fixed:</strong> ${fix.issue || fix.type} in ${fix.file || fix.package}
${fix.fix ? `<br><em>Action: ${fix.fix}</em>` : ''}
</div>
`).join('')}
` : ''}
</div>
`).join('')}
</div>
</body>
</html>`;
}
// Utility methods
async fileExists(filePath) {
try {
await fs.access(filePath);
return true;
} catch {
return false;
}
}
extractLineNumber(errorMessage) {
const lineMatch = errorMessage.match(/:(\d+):/);
return lineMatch ? parseInt(lineMatch[1]) : 0;
}
isMajorVersionDifference(current, latest) {
const currentMajor = parseInt(current.split('.')[0]);
const latestMajor = parseInt(latest.split('.')[0]);
return latestMajor > currentMajor;
}
async getValidationResults(args) {
const { validationId, detailed = false } = args;
if (validationId) {
if (!this.validationResults.has(validationId)) {
return {
success: false,
message: `Validation ${validationId} not found`
};
}
const result = this.validationResults.get(validationId);
return {
success: true,
data: detailed ? result : {
validationId: result.validationId,
status: result.status,
summary: result.summary,
startTime: result.startTime,
endTime: result.endTime
}
};
}
const allResults = Array.from(this.validationResults.values()).map(result => ({
validationId: result.validationId,
projectPath: result.projectPath,
status: result.status,
summary: result.summary,
startTime: result.startTime,
endTime: result.endTime
}));
return {
success: true,
data: {
validations: allResults,
totalCount: allResults.length
}
};
}
async configureValidationRules(args) {
const { rules = {}, autoFixEnabled } = args;
// Update existing rules or add new ones
for (const [ruleName, ruleConfig] of Object.entries(rules)) {
this.validationRules.set(ruleName, {
name: ruleConfig.name || ruleName,
description: ruleConfig.description || '',
autoFixable: ruleConfig.autoFixable || false,
critical: ruleConfig.critical || false,
...ruleConfig
});
}
if (autoFixEnabled !== undefined) {
this.autoFixEnabled = autoFixEnabled;
}
await this.saveConfiguration();
return {
success: true,
message: 'Validation rules configured successfully',
data: {
totalRules: this.validationRules.size,
autoFixEnabled: this.autoFixEnabled
}
};
}
getToolDefinitions() {
return [
{
name: 'validation/run-pre-publish',
description: 'Run comprehensive pre-publish validation with auto-fix capabilities',
inputSchema: {
type: 'object',
properties: {
projectPath: {
type: 'string',
description: 'Path to project directory',
default: '.'
},
validationId: {
type: 'string',
description: 'Custom validation ID (auto-generated if not provided)'
},
rules: {
type: 'array',
items: { type: 'string' },
description: 'Specific rules to run (runs all if not specified)',
default: []
},
autoFix: {
type: 'boolean',
description: 'Enable automatic fixing of issues',
default: true
},
skipNonCritical: {
type: 'boolean',
description: 'Skip non-critical validation rules',
default: false
}
}
}
},
{
name: 'validation/get-results',
description: 'Get validation results',
inputSchema: {
type: 'object',
properties: {
validationId: {
type: 'string',
description: 'Specific validation ID to retrieve (returns all if not provided)'
},
detailed: {
type: 'boolean',
description: 'Include detailed results',
default: false
}
}
}
},
{
name: 'validation/configure-rules',
description: 'Configure validation rules and settings',
inputSchema: {
type: 'object',
properties: {
rules: {
type: 'object',
description: 'Validation rules configuration',
default: {}
},
autoFixEnabled: {
type: 'boolean',
description: 'Enable/disable automatic fixing globally'
}
}
}
}
];
}
}
export default PrePublishValidator;