Skip to content

robertDouglass/mcp-tester

Repository files navigation

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

npm install -g @robertdouglass/mcp-tester

# Or use directly with npx
npx @robertdouglass/mcp-tester --help

Quick Start

Basic Testing

# 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

# Run all test suites
mcp-tester auto http://localhost:3000/mcp \
  --verbose \
  --performance \
  --compliance \
  --error-handling \
  --timeout 60000 \
  --retry 3

Programmatic Usage

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

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:

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

// 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

// 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:

# 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

{
  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

--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

About

A Model Context Protocol (MCP) test framework for use with Claude Code (or any other AI Co-pilot)

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Contributors 2

  •  
  •