@robertdouglass/mcp-tester
Version:
Advanced testing framework for Model Context Protocol (MCP) servers with enhanced error handling, performance testing, and comprehensive tool validation
475 lines (394 loc) โข 13.1 kB
Markdown
# MCP Tester v2.1.1 ๐งช
Advanced testing framework for Model Context Protocol (MCP) servers with enhanced error handling, performance testing, and comprehensive tool validation.
## What's New in v2.1.1 ๐ ๏ธ
### Project Organization
- **Cleaner Repository**: Moved development scripts from root to `scripts/` directory
- **Smaller Package**: Development scripts excluded from npm package
- **Better Structure**: Root directory now only contains essential files
## What's New in v2.1.0 ๐
### Better Documentation & UX
- **Comprehensive Tool Testing Examples**: Complete documentation with real-world examples
- **Enhanced Error Messages**: Schema validation errors now include helpful suggestions
- **Multi-Step Workflow Patterns**: Examples showing how to chain tool tests together
- **Common Assertion Patterns**: 5 different assertion types with examples
## What's New in v2.0.0
### Enhanced Error Handling
- Custom error classes for better debugging (`MCPTestError`, `ConnectionError`, `TestTimeoutError`)
- Detailed error codes and contextual information
- Retry logic with configurable attempts and delays
### Comprehensive Test Suites
- **Performance Testing**: Measure response times, concurrent handling, and memory stability
- **Protocol Compliance**: Validate JSON-RPC responses and error handling
- **Error Handling Tests**: Test server behavior with invalid inputs
- **Enhanced Tool Testing**: Schema validation, performance thresholds, and detailed assertions
### Improved Metrics & Reporting
- Detailed performance metrics (avg, min, max times)
- Test recommendations based on results
- Memory stability tracking
- Enhanced HTML-friendly reports
### Better Developer Experience
- More verbose and helpful error messages
- Configurable timeouts per test type
- Tool response previews
- Assertion tracking
## Installation
```bash
npm install -g @robertdouglass/mcp-tester
# Or use directly with npx
npx @robertdouglass/mcp-tester --help
```
## Quick Start
### Basic Testing
```bash
# Auto-detect transport type (recommended for HTTP servers)
mcp-tester auto http://localhost:3000/mcp --verbose
# Test stdio server
mcp-tester stdio node ./my-server.js --verbose
# Test with specific transport
mcp-tester streamableHttp http://localhost:3000/mcp --verbose
```
### Advanced Testing
```bash
# Run all test suites
mcp-tester auto http://localhost:3000/mcp \
--verbose \
--performance \
--compliance \
--error-handling \
--timeout 60000 \
--retry 3
```
## Programmatic Usage
```javascript
const { MCPTestFrameworkAdvanced } = require('@robertdouglass/mcp-tester');
async function testMyServer() {
const framework = new MCPTestFrameworkAdvanced({
verbose: true,
timeout: 30000,
retryAttempts: 2,
performanceThresholds: {
toolCall: 2000, // Max 2s for tool calls
discovery: 500 // Max 500ms for discovery
}
});
const tests = {
name: 'My Server Tests',
testDiscovery: true,
testStability: true,
testPerformance: true,
testProtocolCompliance: true,
testErrorHandling: true,
toolTests: [{
toolName: 'my_tool',
arguments: { input: 'test' },
assertions: [
async (result) => {
if (!result.content) throw new Error('No content');
if (result.content[0].type !== 'text') {
throw new Error('Expected text content');
}
}
]
}],
customTests: [{
name: 'Custom validation',
fn: async (client) => {
const tools = await client.listTools();
return { toolCount: tools.tools.length };
}
}]
};
await framework.testServer(
{ type: 'stdio', command: 'node', args: ['./server.js'] },
tests
);
const report = await framework.generateReport();
framework.printSummary(report);
}
```
## Test Types
### 1. Discovery Tests
- Lists and validates tools, resources, and prompts
- Checks for required fields and schema completeness
- Measures discovery performance
### 2. Stability Tests
- Rapid sequential requests (20 requests)
- Concurrent request handling (10 parallel)
- Memory stability over 50 iterations
- Response time variance analysis
### 3. Performance Tests
- Tool discovery performance benchmarking
- Concurrent request handling metrics
- Individual tool execution timing
- Performance threshold validation
### 4. Protocol Compliance Tests
- JSON-RPC response format validation
- Error response structure verification
- Required field presence checks
### 5. Error Handling Tests
- Invalid tool name handling
- Malformed argument handling
- Timeout behavior verification
- Connection failure recovery
### 6. Tool Tests
- Input schema validation
- Custom assertion support
- Performance threshold checking
- Response structure validation
## Testing Individual Tools
The most powerful feature of mcp-tester is testing individual MCP tools with custom arguments and assertions.
### Basic Tool Test
```javascript
const { MCPTestFrameworkAdvanced } = require('@robertdouglass/mcp-tester');
async function testMyTool() {
const framework = new MCPTestFrameworkAdvanced({ verbose: true });
await framework.testServer(
{ type: 'streamableHttp', url: 'http://localhost:3000/mcp' },
{
name: 'My Tool Test',
testDiscovery: false,
testStability: false,
toolTests: [{
toolName: 'my_tool',
arguments: {
input: 'test data',
format: 'json'
},
assertions: [
async (result) => {
if (!result.content) throw new Error('No content returned');
console.log('Tool result:', result.content[0].text);
}
]
}]
}
);
}
```
### Multi-Step Workflow
Test multiple tools in sequence, perfect for workflows like project creation:
```javascript
async function testProjectWorkflow() {
const framework = new MCPTestFrameworkAdvanced({ verbose: true });
// Step 1: List available servers
await framework.testServer(
{ type: 'streamableHttp', url: 'http://localhost:3000/mcp' },
{
name: 'Get Server List',
toolTests: [{
toolName: 'server_list',
arguments: { output: 'json' },
assertions: [
async (result) => {
const data = JSON.parse(result.content[0].text);
console.log('Available servers:', data.length);
}
]
}]
}
);
// Step 2: Create project using server from step 1
await framework.testServer(
{ type: 'streamableHttp', url: 'http://localhost:3000/mcp' },
{
name: 'Create Project',
toolTests: [{
toolName: 'project_create',
arguments: {
description: 'My New Project',
serverId: 'server-id-from-step-1'
},
assertions: [
async (result) => {
const response = JSON.parse(result.content[0].text);
if (response.status !== 'success') {
throw new Error('Project creation failed');
}
console.log('Project created with ID:', response.data.projectId);
}
]
}]
}
);
}
```
### Common Assertion Patterns
```javascript
// 1. Check response structure
async (result) => {
if (!result.content?.[0]?.text) {
throw new Error('Invalid response structure - no text content');
}
}
// 2. Validate JSON response
async (result) => {
const data = JSON.parse(result.content[0].text);
if (data.status !== 'success') {
throw new Error(`Operation failed: ${data.message}`);
}
}
// 3. Performance assertion
async (result, metadata) => {
if (metadata.duration > 5000) {
throw new Error(`Tool too slow: ${metadata.duration}ms > 5000ms`);
}
}
// 4. Content validation
async (result) => {
const text = result.content[0].text;
if (!text.includes('expected-value')) {
throw new Error('Response missing expected content');
}
}
// 5. Schema validation
async (result) => {
const data = JSON.parse(result.content[0].text);
const requiredFields = ['id', 'name', 'status'];
for (const field of requiredFields) {
if (!(field in data)) {
throw new Error(`Missing required field: ${field}`);
}
}
}
```
### Real-World Example: Project Management
```javascript
// Complete example testing Mittwald project operations
async function testMittwaldProjects() {
const framework = new MCPTestFrameworkAdvanced({
verbose: true,
performanceThresholds: { toolCall: 3000 }
});
const tests = {
name: 'Mittwald Project Management Tests',
testDiscovery: false,
toolTests: [
// Test 1: List projects
{
toolName: 'mittwald_project_list',
arguments: { output: 'json' },
assertions: [
async (result) => {
const data = JSON.parse(result.content[0].text);
console.log(`Found ${data.data.length} projects`);
return data.data; // Can return data for use in assertions
}
]
},
// Test 2: Get specific project details
{
toolName: 'mittwald_project_get',
arguments: {
projectId: 'your-project-id',
output: 'json'
},
assertions: [
async (result) => {
const project = JSON.parse(result.content[0].text);
if (!project.data.isReady) {
throw new Error('Project is not ready');
}
console.log(`Project "${project.data.description}" is ready`);
}
]
},
// Test 3: Create new project (commented out for safety)
/*
{
toolName: 'mittwald_project_create',
arguments: {
description: 'Test Project',
serverId: 'your-server-id'
},
assertions: [
async (result) => {
const response = JSON.parse(result.content[0].text);
if (response.status === 'success') {
console.log('โ
Project created:', response.data.projectId);
} else {
throw new Error('Project creation failed');
}
}
]
}
*/
]
};
await framework.testServer(
{ type: 'streamableHttp', url: 'http://localhost:3000/mcp' },
tests
);
const report = await framework.generateReport();
framework.printSummary(report);
}
```
### CLI Tool Testing
You can also test individual tools from the command line by creating test files:
```bash
# Create a test file
echo 'module.exports = { toolTests: [{ toolName: "my_tool", arguments: {}, assertions: [] }] }' > my-test.js
# Run it (hypothetical - not implemented yet)
mcp-tester auto http://localhost:3000/mcp --test-file my-test.js
```
## Configuration Options
```javascript
{
verbose: false, // Detailed logging
timeout: 30000, // Test timeout in ms
outputDir: './test-results', // Report output directory
retryAttempts: 0, // Connection retry attempts
retryDelay: 1000, // Delay between retries
validateSchemas: true, // Validate tool input schemas
performanceThresholds: {
toolCall: 5000, // Max tool call duration
discovery: 1000 // Max discovery duration
}
}
```
## Error Classes
The framework uses custom error classes for better debugging:
- `MCPTestError`: Base error class with code and details
- `ConnectionError`: Connection-specific errors with transport info
- `TestTimeoutError`: Test timeout errors with test name and timeout value
## Report Structure
Reports include:
- Comprehensive metrics (connection attempts, total tests, assertions)
- Per-transport breakdowns
- Individual test results with timings
- Performance insights
- Recommendations for improvements
- Failed test details with error codes
## CLI Options
```bash
--verbose Show detailed output
--timeout <ms> Set test timeout (default: 30000)
--retry <attempts> Number of connection retries (default: 0)
--performance Run performance tests
--compliance Run protocol compliance tests
--error-handling Run error handling tests
--header "Key: Value" Add HTTP header
--auth "token" Add auth token
```
## Best Practices
1. **Always run with --verbose during development** to see detailed error messages
2. **Set appropriate timeouts** for your server's expected performance
3. **Use retry logic** for unreliable network conditions
4. **Write comprehensive assertions** for tool tests
5. **Monitor performance thresholds** to catch regressions
## Troubleshooting
### Connection Errors
- Check server is running and accessible
- Verify transport type matches server implementation
- Use --retry flag for flaky connections
### Timeout Errors
- Increase timeout with --timeout flag
- Check for server performance issues
- Verify network latency
### Schema Validation Errors
- Ensure tool arguments match expected schema
- Disable validation with validateSchemas: false if needed
## Contributing
Contributions welcome! Please submit issues and PRs to:
https://github.com/robertDouglass/mcp-tester
## License
MIT ยฉ Robert Douglass