MCP Aegis
A comprehensive Node.js testing library for Model Context Protocol (MCP) servers

MCP Aegis provides both YAML-based declarative testing and programmatic testing for MCP servers with advanced pattern matching capabilities, including case-insensitive matching, pattern negation, string length validation, comprehensive numeric comparison patterns (with exact equality, floating-point tolerance, and precision validation), date/timestamp validation patterns, and cross-field relationship validation.
📖 Documentation
📚 Complete Documentation
⚡ Quick Start
npm install -g mcp-aegis
npx mcp-aegis init
cat > tests/mcp/my-server.test.mcp.yml << 'EOF'
description: "Basic test"
tests:
- it: "should list tools"
request:
jsonrpc: "2.0"
id: "1"
method: "tools/list"
params: {}
expect:
response:
jsonrpc: "2.0"
id: "1"
result:
tools: "match:type:array"
EOF
npx mcp-aegis "test*/mcp/**/*.test.mcp.yml"
Manual Setup (Alternative)
echo '{"name":"My Server","command":"node","args":["./server.js"]}' > aegis.config.json
cat > test.yml << 'EOF'
description: "Basic test"
tests:
- it: "should list tools"
request:
jsonrpc: "2.0"
id: "1"
method: "tools/list"
params: {}
expect:
response:
jsonrpc: "2.0"
id: "1"
result:
tools: "match:type:array"
EOF
aegis test.yml --config aegis.config.json
✨ Key Features
- 🎯 Declarative YAML Testing - Simple, readable test definitions
- 💻 Programmatic API - JavaScript/TypeScript integration with any test framework
- 🔄 Automatic MCP Protocol - Handles handshakes and JSON-RPC messaging
- 🧪 Advanced Pattern Matching - 40+ verified pattern types including case-insensitive matching, string length validation, exact numeric equality, floating-point tolerance, decimal precision validation, modular arithmetic, comprehensive date/timestamp validation, and cross-field relationship validation
- 📊 Rich Reporting - Detailed diffs and colored output
- 🛡️ Robust Communication - Reliable stdio transport handling
📖 Documentation
📚 Complete Documentation
🚀 Testing Approaches
YAML Declarative Testing
description: "Calculator tests"
tests:
- it: "should add numbers"
request:
jsonrpc: "2.0"
id: "calc-1"
method: "tools/call"
params:
name: "calculator"
arguments: { a: 15, b: 27 }
expect:
response:
jsonrpc: "2.0"
id: "calc-1"
result:
content:
- type: "text"
text: "match:Result: \\d+"
Advanced Pattern Matching Examples
tests:
- it: "should validate numeric ranges"
expect:
response:
result:
score: "match:greaterThan:85"
count: "match:between:10:100"
percentage: "match:lessThanOrEqual:95"
- it: "should validate exact numeric values and precision"
expect:
response:
result:
productCount: "match:equals:42"
categoryId: "match:notEquals:10"
price: "match:decimalPlaces:2"
rating: "match:decimalPlaces:1"
stock: "match:multipleOf:5"
percentage: "match:divisibleBy:10"
- it: "should validate floating point with tolerance"
expect:
response:
result:
successRate: "match:approximately:95.5:0.1"
loadAverage: "match:approximately:1.2:0.05"
temperature: "match:approximately:20:0.5"
- it: "should validate dates and timestamps"
expect:
response:
result:
createdAt: "match:dateValid"
publishDate: "match:dateAfter:2023-01-01"
expireDate: "match:dateBefore:2025-01-01"
lastUpdate: "match:dateAge:1d"
eventTime: "match:dateBetween:2023-01-01:2024-12-31"
timestamp: "match:dateFormat:iso"
- it: "should validate string lengths and constraints"
expect:
response:
result:
title: "match:stringLength:25"
shortName: "match:stringLengthLessThan:10"
description: "match:stringLengthGreaterThan:50"
username: "match:stringLengthBetween:3:20"
nickname: "match:stringLengthGreaterThanOrEqual:2"
bio: "match:stringLengthLessThanOrEqual:500"
emptyField: "match:stringEmpty"
requiredField: "match:stringNotEmpty"
notShort: "match:not:stringLengthLessThan:5"
- it: "should validate field relationships and constraints"
expect:
response:
result:
"match:crossField": "startDate < endDate"
"match:crossField": "minPrice <= maxPrice"
"match:crossField": "currentStock > minStock"
"match:crossField": "age >= minAge"
"match:crossField": "amount != fee"
- it: "should support nested field paths in cross-field validation"
expect:
response:
result:
"match:crossField": "user.profile.age >= settings.minAge"
"match:crossField": "order.total > payment.amount"
- it: "should validate with pattern negation"
expect:
response:
result:
value: "match:not:greaterThan:1000"
status: "match:not:contains:error"
invalidDate: "match:not:dateValid"
- it: "should validate case-insensitive patterns"
expect:
response:
result:
name: "match:containsIgnoreCase:john"
status: "match:equalsIgnoreCase:SUCCESS"
message: "match:not:containsIgnoreCase:ERROR"
Programmatic Testing
import { test, describe, before, after, beforeEach } from 'node:test';
import { strict as assert } from 'node:assert';
import { connect } from 'mcp-aegis';
describe('MCP Server Tests', () => {
let client;
before(async () => {
client = await connect('./aegis.config.json');
});
after(async () => {
await client?.disconnect();
});
beforeEach(() => {
client.clearStderr();
});
test('should list available tools', async () => {
const tools = await client.listTools();
assert.ok(Array.isArray(tools));
assert.ok(tools.length > 0);
tools.forEach(tool => {
assert.ok(tool.name, 'Tool should have name');
assert.ok(tool.description, 'Tool should have description');
assert.ok(tool.inputSchema, 'Tool should have input schema');
});
});
test('should execute calculator tool', async () => {
const result = await client.callTool('calculator', {
operation: 'add', a: 15, b: 27
});
assert.equal(result.isError, false);
assert.equal(result.content[0].type, 'text');
assert.equal(result.content[0].text, 'Result: 42');
});
test('should handle tool errors gracefully', async () => {
try {
await client.callTool('nonexistent_tool', {});
assert.fail('Should have thrown an error');
} catch (error) {
assert.ok(error.message.includes('Failed to call tool'));
}
});
});
🏃♂️ Running Tests
aegis "tests/**/*.test.mcp.yml" --config config.json
aegis "tests/*.yml" --config config.json --verbose
aegis "tests/*.yml" --config config.json --debug
aegis "tests/*.yml" --config config.json --timing
aegis "tests/*.yml" --config config.json --json
aegis "tests/*.yml" --config config.json --quiet
aegis "tests/*.yml" --config config.json --errors-only
aegis "tests/*.yml" --config config.json --syntax-only
aegis "tests/*.yml" --config config.json --no-analysis
aegis "tests/*.yml" --config config.json --group-errors
aegis "tests/*.yml" --config config.json --max-errors 3
aegis "tests/*.yml" --config config.json --verbose --timing --debug
node --test tests/**/*.programmatic.test.js
npm run test:examples
npm run test:filesystem
npm run test:multitool
npm run test:numeric
🎛️ CLI Options
Output & Debugging Options
--verbose, -v: Display individual test results with the test suite hierarchy
--debug, -d: Enable debug mode with detailed MCP communication logging
--timing, -t: Show timing information for tests and operations
--json, -j: Output results in JSON format for CI/automation systems
--quiet, -q: Suppress non-essential output (opposite of verbose)
Error Reporting Options
--errors-only: Show only failed tests and their errors, hide passing tests
- Useful for focusing on failures in large test suites
- Provides clean output for debugging sessions
--syntax-only: Show only syntax-related errors and suggestions
- Highlights pattern syntax issues like missing
match: prefixes
- Recommends corrections for common syntax mistakes
--no-analysis: Disable detailed validation analysis, show only basic error messages
- Provides minimal error output without suggestions or analysis
- Faster execution when detailed analysis isn't needed
--group-errors: Group similar errors together to reduce repetition
- Consolidates identical validation failures across multiple paths
- Shows error frequency and affected paths in summary format
--concise: Suppress per-test detailed analysis blocks (requires --group-errors)
- Hides the "🔍 Detailed Validation Analysis" section for each failing test
- Keeps high-level failure lines and the aggregated grouped error summary
- Ideal for very large failing suites where per-test verbosity is noisy
--max-errors <number>: Limit the number of validation errors shown per test (default: 5)
- Prevents overwhelming output when tests have many validation failures
- Shows "... and X more validation error(s)" for truncated errors
Example Usage
aegis "tests/*.yml" --config config.json --errors-only
aegis "tests/*.yml" --config config.json --syntax-only
aegis "tests/*.yml" --config config.json --no-analysis --quiet
aegis "tests/*.yml" --config config.json --group-errors
aegis "tests/*.yml" --config config.json --group-errors --concise --errors-only
aegis "tests/*.yml" --config config.json --max-errors 2
aegis "tests/*.yml" --config config.json --errors-only --group-errors --max-errors 3
🤝 Contributing
Contributions welcome! See our Contributing Guide for details.
git clone https://github.com/taurgis/mcp-aegis.git
cd mcp-aegis
npm install
npm test
📜 License
MIT License - see LICENSE file for details.
📚 View Complete Documentation | 🐛 Report Issues | ⭐ Star on GitHub