Development Guide
Contributing to MCP Aegis
Contributing to MCP Aegis development and extending the Model Context Protocol testing framework. Learn the architecture, setup development environment, and contribute new features.
Development Setup
Prerequisites
- Node.js 18+ with npm
- Git for version control
Clone and Install
bash
git clone https://github.com/taurgis/mcp-aegis.git
cd mcp-aegis
# Install dependencies
npm install
# Run tests to verify setup
npm testProject Architecture
Key directories and modules in the project:
bash
mcp-aegis/
├── bin/ # CLI entrypoint
│ └── aegis.js
├── src/ # Source code
│ ├── cli/ # CLI modules
│ │ ├── commands/ # CLI commands (init, run)
│ │ └── output.js # Output formatting
│ ├── core/ # Core engine
│ │ ├── configParser.js # Configuration loading
│ │ ├── MCPCommunicator.js # MCP protocol communication
│ │ └── ConfigValidator.js # Config validation
│ ├── programmatic/ # Programmatic API
│ │ └── MCPClient.js # Main client class
│ ├── protocol/ # MCP protocol handlers
│ └── test-engine/ # Testing engine
│ ├── runner.js # Test execution
│ ├── executor.js # Individual test execution
│ └── matchers/ # Pattern matching system
├── test/ # Unit and integration tests
└── examples/ # Example servers and testsCore Modules
MCP Communicator (src/core/MCPCommunicator.js)
Low-level stdio communication with MCP servers:
- Child process spawning and management
- JSON-RPC message framing over stdio
- Stderr capture and buffering
- Graceful shutdown handling
Test Engine (src/test-engine/)
Core test execution engine with modular pattern matching:
- runner.js: Orchestrates test execution
- executor.js: Executes individual test cases
- matchers/: 11+ pattern matching types
- protocol: MCP handshake and protocol handling
Programmatic API (src/programmatic/MCPClient.js)
JavaScript/TypeScript API for integration with test frameworks:
- High-level MCP operations
- Promise-based async API
- Error handling and validation
- Framework-agnostic design
Contributing Guidelines
Code Standards
- ES2020+ JavaScript with async/await
- JSDoc comments for public APIs
- 2-space indentation, semicolons required
Commit Convention
Follow the conventional commits format: type(scope): description
bash
# Example commits
git commit -m "feat(runner): add regex pattern matching"
git commit -m "fix(client): handle connection timeout properly"
git commit -m "docs(readme): update installation instructions"Pull Request Process
- Fork the repository
- Create feature branch from
main - Implement changes with tests
- Run test suite:
npm test - Update documentation as needed
- Submit pull request with clear description
Adding Features
Adding New Pattern Matchers
Extend the pattern matching system in src/test-engine/matchers/patterns.js:
javascript
// Add new pattern type
function matchPattern(pattern, actual) {
if (pattern.startsWith('custom_pattern:')) {
return matchCustomPattern(actual, pattern.substring(15));
}
// ... existing patterns
}
function matchCustomPattern(actual, value) {
// Implement custom pattern logic
return { matches: boolean, reason: string };
}Add tests in test/testRunner.test.js:
javascript
describe('Custom Pattern Matching', () => {
test('should match custom pattern', async () => {
const testCase = {
request: { /* JSON-RPC request */ },
expect: {
response: {
result: {
data: "match:custom_pattern:expected_value"
}
}
}
};
// Test implementation
});
});Adding CLI Options
Add new options in bin/aegis.js:
javascript
program
.option('--new-option <value>', 'Description of new option')
.action(async (testPattern, options) => {
// Parse options using the standardized parser
const parsedOptions = parseOptions(options);
if (parsedOptions.newOption) {
// Handle new option logic
}
});Adding Programmatic API Methods
Extend MCPClient in src/programmatic/MCPClient.js:
javascript
async newMethod(parameters) {
this.validateConnection();
const response = await this.sendMessage({
jsonrpc: "2.0",
id: `new-method-${Date.now()}`,
method: "new/method",
params: parameters
});
return response.result;
}Testing
Test Structure
bash
test/
├── configParser.test.js # Configuration validation tests
├── testParser.test.js # YAML parsing tests
├── MCPCommunicator.test.js # Protocol communication tests
├── testRunner.test.js # Test execution tests
├── reporter.test.js # Output formatting tests
├── cli.test.js # CLI integration tests
└── fixtures/ # Test data and mock servers
├── runner/ # Test runner fixtures
├── servers/ # Mock MCP servers
└── yaml/ # YAML test filesRunning Tests
bash
# Run all tests
npm test
# Run only unit tests
npm run test:unit
# Run example server tests
npm run test:examples
# Run programmatic API tests
npm run test:programmatic
# Run with coverage
npm run test:coverageWriting Tests
Unit Tests
javascript
import { describe, test } from 'node:test';
import assert from 'node:assert';
import { loadConfig } from '../src/core/configParser.js';
describe('Configuration Parser', () => {
test('should validate required fields', async () => {
const config = {
name: 'Test Server',
command: 'node',
args: ['./server.js']
};
const result = await loadConfig('./test-config.json');
assert.ok(result.name);
assert.ok(result.command);
assert.ok(Array.isArray(result.args));
});
test('should reject invalid configuration', async () => {
await assert.rejects(
async () => await loadConfig('./invalid-config.json'),
/Missing required configuration fields/
);
});
});Integration Tests
javascript
import { describe, test } from 'node:test';
import assert from 'node:assert';
import { runTests } from '../src/test-engine/runner.js';
describe('Integration Tests', () => {
test('should run complete test suite', async () => {
const config = { /* valid config */ };
const testSuite = { /* valid test suite */ };
const results = await runTests(config, testSuite);
assert.ok(results.totalTests > 0);
assert.equal(results.passedTests, results.totalTests);
});
});Mock Servers for Testing
Create mock MCP servers in test/fixtures/:
javascript
// test/fixtures/mock-server.js
#!/usr/bin/env node
const responses = {
'tools/list': { tools: [{ name: 'mock_tool', description: 'Mock tool' }] },
'tools/call': { content: [{ type: 'text', text: 'Mock response' }] }
};
process.stdin.on('data', (data) => {
const message = JSON.parse(data.toString());
const response = {
jsonrpc: "2.0",
id: message.id,
result: responses[message.method] || { error: 'Unknown method' }
};
console.log(JSON.stringify(response));
});Release Process
1. Update Version
bash
# For patch release
npm version patch
# For minor release
npm version minor
# For major release
npm version major2. Update Changelog
markdown
## [1.2.3] - 2024-01-15
### Added
- New pattern matching type: `arrayContains`
- Support for custom timeout configurations
### Fixed
- Handle edge case in regex pattern escaping
- Improve error messages for connection failures
### Changed
- Improved performance for large test suites3. Run Full Test Suite
bash
npm run test:all
npm run test:integration
npm run test:examples4. Pre-release Testing
bash
# Test installation
npm pack
npm install -g mcp-aegis-*.tgz
# Verify CLI works
aegis --help
aegis examples/filesystem.test.mcp.yml --config examples/config.json --verbose --debug
# Test programmatic API
node -e "
const { createClient } = require('mcp-aegis');
console.log('API imports successfully');
"5. Build and Publish
bash
npm run build
npm publish
# Create GitHub Release
git tag v1.2.3
git push origin v1.2.3Development Best Practices
- Test-Driven Development: Write tests before implementing features
- Small, Focused Commits: Make atomic commits with clear messages
- Documentation Updates: Keep docs in sync with code changes
- Performance Monitoring: Benchmark critical paths
- Error Handling: Provide clear, actionable error messages
- Backward Compatibility: Maintain API stability
- Security Reviews: Validate input and sanitize output
- Code Reviews: All changes require review