vineguard-mcp
Version:
VineGuard MCP Server v2.1 - Intelligent QA Workflow System with advanced test generation for Jest/RTL, Cypress, and Playwright. Features smart project analysis, progressive testing strategies, and comprehensive quality patterns for React/Vue/Angular proje
549 lines • 21.7 kB
JavaScript
#!/usr/bin/env node
/**
* VineGuard MCP Server v2.1 - Intelligent QA Workflow System
* Aggregates all VineGuard feature packages for comprehensive testing workflows
*/
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import { CallToolRequestSchema, ListResourcesRequestSchema, ListToolsRequestSchema, ReadResourceRequestSchema, } from '@modelcontextprotocol/sdk/types.js';
// Import VineGuard feature packages
import { WorkflowOrchestrator, ProjectScanner } from 'vineguard-core';
import { JestGenerator, CypressGenerator, PlaywrightGenerator } from 'vineguard-test-cultivator';
import { SecurityAuditor } from 'vineguard-pest-detector';
import { PRDGenerator } from 'vineguard-vineyard-scanner';
import { FixGenerator } from 'vineguard-vine-healer';
import { ConfigAnalyzer, WorkflowDetector } from 'vineguard-vine-inspector';
import { TestPlanner } from 'vineguard-harvest-planner';
const PROJECT_ROOT = process.env.VINEGUARD_VINEYARD_ROOT || process.cwd();
const VINEGUARD_MODE = process.env.VINEGUARD_MODE || 'orchestrator';
const MAX_RESPONSE_LENGTH = parseInt(process.env.VINEGUARD_MAX_RESPONSE_LENGTH || '100000'); // 100KB default
console.error(`[VineGuard MCP v2.1] Starting server for project: ${PROJECT_ROOT}`);
console.error(`[VineGuard MCP v2.1] Mode: ${VINEGUARD_MODE}`);
const server = new Server({
name: 'vineguard',
version: '2.1.0',
}, {
capabilities: {
tools: {},
resources: {}
}
});
// Initialize orchestrator
const orchestrator = new WorkflowOrchestrator();
const projectScanner = new ProjectScanner();
// Tool Handlers
server.setRequestHandler(ListToolsRequestSchema, async () => {
return {
tools: [
// Core workflow tools
{
name: 'scan_project',
description: 'Analyze project structure, detect frameworks, and assess testing setup',
inputSchema: {
type: 'object',
properties: {
path: {
type: 'string',
description: 'Project root path to scan',
default: PROJECT_ROOT
},
deep: {
type: 'boolean',
description: 'Perform deep analysis including dependencies',
default: false
}
}
}
},
{
name: 'analyze_prd',
description: 'Analyze Product Requirements Document and extract user stories, test scenarios',
inputSchema: {
type: 'object',
properties: {
prdPath: {
type: 'string',
description: 'Path to PRD file (markdown, text, or README)',
default: './README.md'
},
includeTestingImplications: {
type: 'boolean',
description: 'Include testing implications analysis',
default: true
}
}
}
},
{
name: 'create_test_plan',
description: 'Generate comprehensive test plan based on project analysis',
inputSchema: {
type: 'object',
properties: {
projectAnalysis: {
type: 'object',
description: 'Project analysis results from scan_project'
},
prdAnalysis: {
type: 'object',
description: 'PRD analysis results (optional)'
},
coverageTarget: {
type: 'number',
description: 'Target test coverage percentage',
default: 85
},
includeAccessibility: {
type: 'boolean',
description: 'Include accessibility tests',
default: true
},
includeSecurity: {
type: 'boolean',
description: 'Include security tests',
default: true
}
}
}
},
{
name: 'generate_test',
description: 'Generate comprehensive test file for a component or function',
inputSchema: {
type: 'object',
properties: {
filePath: {
type: 'string',
description: 'Path to the source file to generate tests for'
},
testType: {
type: 'string',
enum: ['unit', 'integration', 'e2e'],
description: 'Type of test to generate',
default: 'unit'
},
framework: {
type: 'string',
enum: ['jest', 'vitest', 'playwright', 'cypress'],
description: 'Testing framework to use',
default: 'jest'
},
template: {
type: 'string',
enum: ['basic', 'comprehensive', 'component'],
description: 'Test template style',
default: 'comprehensive'
}
},
required: ['filePath']
}
},
{
name: 'run_tests',
description: 'Execute tests using detected or specified test framework',
inputSchema: {
type: 'object',
properties: {
framework: {
type: 'string',
enum: ['npm', 'jest', 'vitest', 'playwright', 'cypress'],
description: 'Test framework to use',
default: 'npm'
},
coverage: {
type: 'boolean',
description: 'Generate coverage report',
default: false
},
pattern: {
type: 'string',
description: 'Test file pattern to run'
}
}
}
},
{
name: 'security_audit',
description: 'Perform comprehensive security audit of the codebase',
inputSchema: {
type: 'object',
properties: {
projectPath: {
type: 'string',
description: 'Path to project root',
default: PROJECT_ROOT
},
includeNodeModules: {
type: 'boolean',
description: 'Include node_modules in scan',
default: false
},
includeDependencyAudit: {
type: 'boolean',
description: 'Include npm dependency audit',
default: true
}
}
}
},
{
name: 'analyze_config',
description: 'Analyze testing framework configurations for optimization',
inputSchema: {
type: 'object',
properties: {
projectRoot: {
type: 'string',
description: 'Project root path',
default: PROJECT_ROOT
}
}
}
},
{
name: 'detect_workflow',
description: 'Detect project workflow and provide testing recommendations',
inputSchema: {
type: 'object',
properties: {
projectRoot: {
type: 'string',
description: 'Project root path',
default: PROJECT_ROOT
}
}
}
},
{
name: 'generate_fixes',
description: 'Generate automated fixes for detected issues',
inputSchema: {
type: 'object',
properties: {
bugReport: {
type: 'object',
description: 'Bug detection results with issues'
},
autoApply: {
type: 'boolean',
description: 'Automatically apply safe fixes',
default: false
},
safeOnly: {
type: 'boolean',
description: 'Only generate safe fixes',
default: true
}
}
}
},
{
name: 'orchestrate_workflow',
description: 'Execute complete VineGuard testing workflow',
inputSchema: {
type: 'object',
properties: {
workflowType: {
type: 'string',
enum: ['complete-qa', 'quick-test', 'security-audit', 'greenfield-setup'],
description: 'Type of workflow to execute',
default: 'complete-qa'
},
projectPath: {
type: 'string',
description: 'Project path to analyze',
default: PROJECT_ROOT
},
options: {
type: 'object',
description: 'Workflow-specific options',
default: {}
}
}
}
}
]
};
});
server.setRequestHandler(CallToolRequestSchema, async (request) => {
const { name, arguments: args } = request.params;
try {
switch (name) {
case 'scan_project':
return await handleScanProject(args);
case 'analyze_prd':
return await handleAnalyzePRD(args);
case 'create_test_plan':
return await handleCreateTestPlan(args);
case 'generate_test':
return await handleGenerateTest(args);
case 'run_tests':
return await handleRunTests(args);
case 'security_audit':
return await handleSecurityAudit(args);
case 'analyze_config':
return await handleAnalyzeConfig(args);
case 'detect_workflow':
return await handleDetectWorkflow(args);
case 'generate_fixes':
return await handleGenerateFixes(args);
case 'orchestrate_workflow':
return await handleOrchestrateWorkflow(args);
default:
throw new Error(`Unknown tool: ${name}`);
}
}
catch (error) {
console.error(`[VineGuard MCP] Error in ${name}:`, error);
return {
content: [
{
type: 'text',
text: `Error executing ${name}: ${error instanceof Error ? error.message : 'Unknown error'}`
}
]
};
}
});
// Utility function to handle large responses
function formatResponse(data, maxLength = MAX_RESPONSE_LENGTH) {
const jsonString = JSON.stringify(data, null, 2);
if (jsonString.length <= maxLength) {
return jsonString;
}
// Response is too large, provide truncated version with metadata
const truncated = jsonString.substring(0, maxLength - 200);
const lastCompleteLineIndex = truncated.lastIndexOf('\n');
const safelyTruncated = lastCompleteLineIndex > 0
? truncated.substring(0, lastCompleteLineIndex)
: truncated;
return `${safelyTruncated}\n\n... [RESPONSE TRUNCATED] ...\n\n` +
`Original response was ${jsonString.length} characters (${(jsonString.length / 1024).toFixed(1)}KB).\n` +
`Showing first ${safelyTruncated.length} characters.\n` +
`To see full response, increase VINEGUARD_MAX_RESPONSE_LENGTH environment variable.`;
}
// Tool implementations using feature packages
async function handleScanProject(args) {
const projectPath = args.path || PROJECT_ROOT;
const deep = args.deep || false;
console.error(`[VineGuard] Scanning project: ${projectPath}`);
const scanResult = await projectScanner.scanProject(projectPath, { deep });
return {
content: [
{
type: 'text',
text: formatResponse(scanResult)
}
]
};
}
async function handleAnalyzePRD(args) {
const prdPath = args.prdPath || './README.md';
const includeTestingImplications = args.includeTestingImplications !== false;
console.error(`[VineGuard] Analyzing PRD: ${prdPath}`);
const prdGenerator = new PRDGenerator(PROJECT_ROOT);
const prdDocument = await prdGenerator.generatePRD({
identifyTestingImplications: includeTestingImplications
});
return {
content: [
{
type: 'text',
text: formatResponse(prdDocument)
}
]
};
}
async function handleCreateTestPlan(args) {
const { projectAnalysis, prdAnalysis, coverageTarget = 85, includeAccessibility = true, includeSecurity = true } = args;
console.error('[VineGuard] Creating test plan');
const testPlanner = new TestPlanner();
let testPlan;
if (prdAnalysis) {
// Generate from PRD
testPlan = await testPlanner.generateTestPlan(prdAnalysis, {
coverageTarget,
includeAccessibility,
includeSecurity
});
}
else if (projectAnalysis) {
// Generate from project analysis
testPlan = await testPlanner.generateTestPlanFromProject(projectAnalysis.name || 'Project', {
type: projectAnalysis.type || 'web',
frameworks: projectAnalysis.frameworks || [],
complexity: projectAnalysis.complexity || 'medium',
hasTests: projectAnalysis.hasTests || false,
testCoverage: projectAnalysis.testCoverage || 0
}, {
coverageTarget,
includeAccessibility,
includeSecurity
});
}
else {
throw new Error('Either projectAnalysis or prdAnalysis is required');
}
return {
content: [
{
type: 'text',
text: formatResponse(testPlan)
}
]
};
}
async function handleGenerateTest(args) {
const { filePath, testType = 'unit', framework = 'jest', template = 'comprehensive' } = args;
console.error(`[VineGuard] Generating ${testType} test for: ${filePath}`);
let testContent = '';
switch (framework) {
case 'jest':
case 'vitest':
testContent = JestGenerator.generateTest({
componentName: filePath.split('/').pop()?.replace(/\.(ts|tsx|js|jsx)$/, '') || 'Component',
filePath,
testType: (testType === 'unit' || testType === 'integration' || testType === 'accessibility') ? testType : 'unit',
isComponent: filePath.includes('.tsx') || filePath.includes('.jsx'),
isReact: filePath.includes('.tsx') || filePath.includes('.jsx')
});
break;
case 'playwright':
testContent = PlaywrightGenerator.generateTest({
featureName: filePath.split('/').pop()?.replace(/\.(ts|tsx|js|jsx)$/, '') || 'Feature',
testType: (testType === 'e2e' || testType === 'api' || testType === 'visual' || testType === 'mobile' || testType === 'performance' || testType === 'smoke') ? testType : 'e2e',
baseUrl: 'http://localhost:3000'
});
break;
case 'cypress':
testContent = CypressGenerator.generateTest({
featureName: filePath.split('/').pop()?.replace(/\.(ts|tsx|js|jsx)$/, '') || 'Feature',
testType: (testType === 'user-flow' || testType === 'api' || testType === 'smoke' || testType === 'visual' || testType === 'accessibility') ? testType : 'user-flow',
baseUrl: 'http://localhost:3000'
});
break;
default:
throw new Error(`Unsupported framework: ${framework}`);
}
return {
content: [
{
type: 'text',
text: testContent
}
]
};
}
async function handleRunTests(args) {
const { framework = 'npm', coverage = false, pattern } = args;
console.error(`[VineGuard] Running tests with ${framework}`);
// This would typically integrate with the test execution system
// For now, return a placeholder implementation
return {
content: [
{
type: 'text',
text: `Running tests with ${framework}${coverage ? ' (with coverage)' : ''}${pattern ? ` (pattern: ${pattern})` : ''}`
}
]
};
}
async function handleSecurityAudit(args) {
const { projectPath = PROJECT_ROOT, includeNodeModules = false, includeDependencyAudit = true } = args;
console.error(`[VineGuard] Running security audit on: ${projectPath}`);
const auditResult = await SecurityAuditor.auditProject(projectPath, {
includeNodeModules,
includeDependencyAudit
});
return {
content: [
{
type: 'text',
text: formatResponse(auditResult)
}
]
};
}
async function handleAnalyzeConfig(args) {
const projectRoot = args.projectRoot || PROJECT_ROOT;
console.error(`[VineGuard] Analyzing configurations in: ${projectRoot}`);
const configAnalyzer = new ConfigAnalyzer(projectRoot);
const analyses = await configAnalyzer.analyzeAllConfigs();
const report = await configAnalyzer.generateOptimizationReport(analyses);
return {
content: [
{
type: 'text',
text: formatResponse({ analyses, report })
}
]
};
}
async function handleDetectWorkflow(args) {
const projectRoot = args.projectRoot || PROJECT_ROOT;
console.error(`[VineGuard] Detecting workflow for: ${projectRoot}`);
const workflowDetector = new WorkflowDetector(projectRoot);
const analysis = await workflowDetector.analyzeProject();
return {
content: [
{
type: 'text',
text: formatResponse(analysis)
}
]
};
}
async function handleGenerateFixes(args) {
const { bugReport, autoApply = false, safeOnly = true } = args;
console.error('[VineGuard] Generating fixes for detected issues');
const fixResult = await FixGenerator.generateFixes(bugReport, {
autoApply,
safeOnly
});
return {
content: [
{
type: 'text',
text: formatResponse(fixResult)
}
]
};
}
async function handleOrchestrateWorkflow(args) {
const { workflowType = 'complete-qa', projectPath = PROJECT_ROOT, options = {} } = args;
console.error(`[VineGuard] Orchestrating ${workflowType} workflow for: ${projectPath}`);
const context = {
projectPath,
...options
};
const result = await orchestrator.executeWorkflow(workflowType, context);
return {
content: [
{
type: 'text',
text: formatResponse(result)
}
]
};
}
// Resource handlers (optional)
server.setRequestHandler(ListResourcesRequestSchema, async () => {
return {
resources: []
};
});
server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
const { uri } = request.params;
throw new Error(`Resource not found: ${uri}`);
});
// Start the server
async function main() {
const transport = new StdioServerTransport();
await server.connect(transport);
console.error('[VineGuard MCP v2.1] Server started successfully');
}
main().catch((error) => {
console.error('[VineGuard MCP] Fatal error:', error);
process.exit(1);
});
//# sourceMappingURL=index.js.map