Testing Deployed Servers

This chapter covers the detailed configuration and strategies for testing MCP servers running in production or staging environments.

Connection Configuration

Basic Remote Connection

# Simple remote test
cargo pmcp test run --server https://mcp.example.com/mcp

# With HTTPS verification disabled (for self-signed certs in staging)
cargo pmcp test run \
  --server https://staging.mcp.example.com/mcp \
  --insecure

Authentication Headers

Most production servers require authentication:

# Bearer token authentication
cargo pmcp test run \
  --server https://mcp.example.com/mcp \
  --header "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..."

# API key authentication
cargo pmcp test run \
  --server https://mcp.example.com/mcp \
  --header "X-API-Key: your-api-key-here"

# Multiple headers
cargo pmcp test run \
  --server https://mcp.example.com/mcp \
  --header "Authorization: Bearer ${TOKEN}" \
  --header "X-Request-ID: test-run-$(date +%s)" \
  --header "X-Environment: staging"

Environment Variables

Use environment variables for secure configuration:

# Set credentials
export MCP_SERVER_URL="https://mcp.example.com/mcp"
export MCP_API_KEY="your-secret-key"

# Run tests (configuration file references env vars)
cargo pmcp test run --config tests/config/remote.yaml
# tests/config/remote.yaml
server:
  url: "${MCP_SERVER_URL}"
  headers:
    Authorization: "Bearer ${MCP_API_KEY}"

Timeout and Retry Configuration

Handling Cold Starts

Cloud deployments often have cold start latency:

# tests/config/lambda.yaml
server:
  url: https://abc123.lambda-url.us-east-1.on.aws/mcp
  timeout_ms: 30000    # 30 seconds for cold start
  retry_count: 3       # Retry on timeout
  retry_delay_ms: 1000 # Wait 1s between retries

# First request allows extra time
first_request:
  timeout_ms: 60000    # 60 seconds for initial cold start
# CLI equivalent
cargo pmcp test run \
  --server https://abc123.lambda-url.us-east-1.on.aws/mcp \
  --timeout 30000 \
  --retry 3 \
  --retry-delay 1000

Platform-Specific Timeouts

Different platforms have different characteristics:

PlatformFirst RequestSubsequentNotes
Lambda30-60s1-5sCold starts
Cloud Run15-30s1-3sCold starts with min-instances=0
Workers<1s<100msNo cold starts
ECS/Kubernetes1-5s100-500msAlways warm
# tests/config/cloudflare-workers.yaml
server:
  url: https://mcp-server.yourname.workers.dev
  timeout_ms: 5000     # Workers are fast
  retry_count: 1       # Rarely need retries

# tests/config/aws-lambda.yaml
server:
  url: https://abc123.lambda-url.us-east-1.on.aws/mcp
  timeout_ms: 45000    # Allow for cold starts
  retry_count: 3

Response Time Assertions

Validate performance meets SLAs:

# tests/scenarios/performance/latency_requirements.yaml
name: "Performance - Latency SLA"
description: "Verify response times meet production SLAs"
tags:
  - performance
  - sla

steps:
  - name: "Health check under 1s"
    tool: list_tables
    input: {}
    expect:
      success: true
      response_time_ms:
        less_than: 1000

  - name: "Simple query under 2s"
    tool: execute_query
    input:
      sql: "SELECT 1"
    expect:
      success: true
      response_time_ms:
        less_than: 2000

  - name: "Complex query under 5s"
    tool: execute_query
    input:
      sql: "SELECT COUNT(*) FROM large_table"
    expect:
      success: true
      response_time_ms:
        less_than: 5000
        greater_than: 0  # Ensure it's not cached

Load Testing Scenarios

Concurrent Request Testing

# tests/scenarios/load/concurrent_requests.yaml
name: "Load - Concurrent requests"
description: "Test server handles concurrent connections"
tags:
  - load
  - performance

config:
  parallel: 10    # Run 10 concurrent tests
  iterations: 5   # Each runs 5 times

steps:
  - name: "Concurrent queries"
    tool: execute_query
    input:
      sql: "SELECT * FROM users LIMIT 10"
    expect:
      success: true
      response_time_ms:
        less_than: 3000  # Even under load

Burst Traffic Simulation

# Simulate burst traffic
for i in {1..100}; do
  cargo pmcp test run \
    --server https://mcp.example.com/mcp \
    --scenario tests/scenarios/smoke/ \
    --quiet &
done
wait

# Check results
grep -r "FAIL" test-results/

Testing Different Environments

Environment Configuration Files

tests/
├── config/
│   ├── local.yaml       # Local development
│   ├── staging.yaml     # Staging environment
│   ├── production.yaml  # Production (smoke only)
│   └── preview.yaml     # PR preview environments
└── scenarios/
    ├── smoke/           # Quick validation
    ├── integration/     # Full integration tests
    └── performance/     # Performance tests
# tests/config/local.yaml
server:
  url: http://localhost:3000/mcp
  timeout_ms: 5000
scenarios:
  - tests/scenarios/

# tests/config/staging.yaml
server:
  url: https://staging.mcp.example.com/mcp
  headers:
    Authorization: "Bearer ${STAGING_TOKEN}"
  timeout_ms: 30000
scenarios:
  - tests/scenarios/smoke/
  - tests/scenarios/integration/

# tests/config/production.yaml
server:
  url: https://mcp.example.com/mcp
  headers:
    Authorization: "Bearer ${PROD_TOKEN}"
  timeout_ms: 10000
scenarios:
  - tests/scenarios/smoke/  # Only smoke tests in prod
options:
  fail_fast: true
  parallel: 2  # Light load on production

PR Preview Environments

For platforms that deploy PR previews:

# .github/workflows/pr-preview.yml
on:
  pull_request:
    types: [opened, synchronize]

jobs:
  deploy-preview:
    runs-on: ubuntu-latest
    outputs:
      preview_url: ${{ steps.deploy.outputs.url }}
    steps:
      - uses: actions/checkout@v4
      - id: deploy
        run: |
          # Deploy to preview environment
          URL=$(./deploy.sh preview --pr ${{ github.event.pull_request.number }})
          echo "url=$URL" >> $GITHUB_OUTPUT

  test-preview:
    needs: deploy-preview
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Test preview environment
        run: |
          cargo pmcp test run \
            --server "${{ needs.deploy-preview.outputs.preview_url }}/mcp" \
            --scenario tests/scenarios/smoke/ \
            --format junit \
            --output preview-results.xml

Debugging Remote Test Failures

Verbose Output Mode

# Maximum verbosity for debugging
cargo pmcp test run \
  --server https://mcp.example.com/mcp \
  --scenario tests/scenarios/failing_test.yaml \
  --verbose \
  --show-requests \
  --show-responses

Output includes:

[10:23:45.123] Connecting to https://mcp.example.com/mcp
[10:23:45.234] → Request: tools/call
{
  "name": "execute_query",
  "arguments": {
    "sql": "SELECT * FROM users"
  }
}
[10:23:45.567] ← Response (333ms):
{
  "content": [...],
  "isError": false
}
[10:23:45.568] ✓ Assertion passed: success = true
[10:23:45.568] ✗ Assertion failed: response_time_ms < 200
              Actual: 333ms, Expected: < 200ms

Saving Request/Response Logs

# Save all requests and responses
cargo pmcp test run \
  --server https://mcp.example.com/mcp \
  --scenario tests/scenarios/ \
  --log-requests test-logs/requests.json \
  --log-responses test-logs/responses.json

# Analyze failures
jq '.[] | select(.status == "error")' test-logs/responses.json

Network Debugging

# Test with curl first
curl -X POST https://mcp.example.com/mcp \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $TOKEN" \
  -d '{
    "jsonrpc": "2.0",
    "method": "tools/list",
    "params": {},
    "id": 1
  }' \
  -v  # Verbose output shows headers, timing

# Check DNS resolution
nslookup mcp.example.com

# Check SSL certificate
openssl s_client -connect mcp.example.com:443 -servername mcp.example.com

# Check connectivity
nc -zv mcp.example.com 443

Health Check Integration

Pre-Test Health Verification

# tests/scenarios/health/pre_check.yaml
name: "Health - Pre-test verification"
description: "Verify server is healthy before running full suite"
tags:
  - health
  - prerequisite

steps:
  - name: "Server responds"
    tool: list_tables
    input: {}
    expect:
      success: true
      response_time_ms:
        less_than: 10000

  - name: "Database connected"
    tool: execute_query
    input:
      sql: "SELECT 1 as health"
    expect:
      success: true
# Run health check first, then full suite
cargo pmcp test run --scenario tests/scenarios/health/ --fail-fast && \
cargo pmcp test run --scenario tests/scenarios/

Continuous Health Monitoring

#!/bin/bash
# health_monitor.sh - Run periodic health checks

while true; do
  if ! cargo pmcp test run \
    --server https://mcp.example.com/mcp \
    --scenario tests/scenarios/health/ \
    --quiet; then

    # Alert on failure
    curl -X POST https://hooks.slack.com/services/xxx \
      -d '{"text":"MCP Server health check failed!"}'
  fi

  sleep 60  # Check every minute
done

Summary

Testing deployed servers requires:

  1. Proper authentication - Headers, tokens, API keys
  2. Timeout configuration - Account for cold starts
  3. Environment-specific settings - Different configs per environment
  4. Performance assertions - Verify SLAs are met
  5. Debugging tools - Verbose logs for troubleshooting

Practice Ideas

These informal exercises help reinforce the concepts. For structured exercises with starter code and tests, see the chapter exercise pages.

  1. Configure staging tests - Set up authentication and timeouts for a staging server
  2. Add latency assertions - Create performance tests with response time requirements
  3. Test cold starts - Configure tests that handle Lambda cold start times
  4. Debug a failure - Use verbose mode to diagnose a failing remote test

Continue to CI/CD Integration