# Copyright (c) 2025 Scott Wilcox
# SPDX-License-Identifier: AGPL-3.0-or-later

# MCP Testing Framework

A comprehensive testing framework for validating servers that implement the [Model Conversation Protocol (MCP)](https://github.com/microsoft/aimcp).

## Framework Architecture

The testing framework is organized into several key components:

### 1. Protocol Modules

Located in `./protocols/`:

- **base.py**: Base protocol testing functionality
- **v2024_11_05.py**: Tests for the 2024-11-05 protocol version
- **v2025_03_26.py**: Tests for the 2025-03-26 protocol version
- **v2025_06_18.py**: Tests for the 2025-06-18 protocol version

### 2. Transport Modules

Located in `./transports/`:

- **base.py**: Base transport layer functionality
- **stdio.py**: Implementation for STDIO transport
- **http.py**: Implementation for HTTP transport

### 3. Specialized Testing Modules

- **./http/**: Comprehensive HTTP testing module
- **./stdio/**: Comprehensive STDIO testing module
- **./utils/**: Shared utility functions
- **./bin/**: Executable test scripts
- **./scripts/**: Command-line testing tools

## Usage Examples

### Using STDIO Testing

```python
from mcp_testing.stdio.tester import MCPStdioTester

# Create a tester instance
tester = MCPStdioTester("python /path/to/server.py", debug=True)

# Run all tests
success = tester.run_all_tests()

# Or run specific tests
tester.start_server()
tester.initialize()
tester.list_tools()
tester.test_echo_tool()
tester.stop_server()
```

### Using HTTP Testing

```python
from mcp_testing.http.tester import MCPHttpTester
from mcp_testing.http.utils import wait_for_server

# Wait for server to be accessible
if wait_for_server("http://localhost:9000/mcp"):
    # Create a tester instance
    tester = MCPHttpTester("http://localhost:9000/mcp", debug=True)
    
    # Run all tests
    success = tester.run_all_tests()
    
    # Or run specific tests
    tester.initialize()
    tester.list_tools()
    tester.test_echo_tool()
```

### Using the Transport Layer Directly

```python
from mcp_testing.transports.http import HTTPTransport
from mcp_testing.transports.stdio import STDIOTransport

# Create a transport instance
http_transport = HTTPTransport("http://localhost:9000/mcp")
stdio_transport = STDIOTransport("python /path/to/server.py")

# Use the transport
http_transport.initialize()
stdio_transport.initialize()
```

## Adding New Tests

To add new tests to the framework:

1. Identify the appropriate component (protocol version, transport layer, etc.)
2. Add new test methods following the established patterns
3. Update the `run_all_tests` method to include your new tests
4. Add documentation about the new tests

## Directory Structure

```
mcp_testing/
├── __init__.py
├── bin/               # Executable scripts
├── http/              # HTTP testing module
├── protocols/         # Protocol version tests
├── scripts/           # Command-line tools
├── stdio/             # STDIO testing module
├── transports/        # Transport layer implementations
└── utils/             # Shared utilities
```

See the subdirectory README files for more detailed information about each component.

## Overview

This framework provides a set of tools and utilities for testing MCP servers against the protocol specifications. It supports both STDIO and HTTP transports, and can test implementations of the 2024-11-05, 2025-03-26, and 2025-06-18 protocol versions.

The framework is designed to be flexible and adapt to any MCP server implementation, regardless of what specific tools or capabilities it provides.

## Status

**✅ All tests now pass for the reference implementation!**

The framework includes comprehensive test coverage for all protocol features, including:
- Basic protocol operations (initialization, shutdown, etc.)
- Synchronous tool calls
- Asynchronous tool calls (for 2025-03-26)
- Resources management
- Prompt/completion functionality
- Specification compliance (MUST, SHOULD, MAY requirements)

## Components

### Transports

The `transports` module provides adapters for different communication mechanisms:

- `stdio.py`: Communication via standard input/output
- `http.py`: Communication via HTTP protocol (for HTTP-based MCP servers)
- `base.py`: Base class defining the transport adapter interface

### Protocols

The `protocols` module provides adapters for different protocol versions:

- `v2024_11_05.py`: Implementation for the 2024-11-05 protocol
- `v2025_03_26.py`: Implementation for the 2025-03-26 protocol (with async tool support)
- `v2025_06_18.py`: Implementation for the 2025-06-18 protocol (with structured output and elicitation)
- `base.py`: Base class defining the protocol adapter interface

### Tests

The `tests` module contains test cases for various protocol features:

- `base_protocol/`: Tests for basic protocol functionality
- `features/`: Tests for specific protocol features
  - `test_tools.py`: Standard tests for synchronous tool calls
  - `test_async_tools.py`: Standard tests for asynchronous tool calls
  - `dynamic_tool_tester.py`: Dynamic tests that adapt to any server's tool capabilities
  - `dynamic_async_tools.py`: Dynamic tests for async tool functionality
- `specification_coverage.py`: Tests that verify compliance with MUST, SHOULD, and MAY requirements from the specification

### Utils

The `utils` module provides utility classes and functions:

- `runner.py`: Test runner for executing test cases
- `reporter.py`: Utilities for generating compliance reports

### Scripts

The `scripts` module provides command-line scripts for running tests:

- `compliance_report.py`: Comprehensive script to test any MCP server and generate reports

### HTTP Testing

The `http` module provides specific functionality for testing HTTP-based MCP servers:

- `tester.py`: Core testing class for HTTP servers
- `utils.py`: Utilities for server connectivity and networking
- `cli.py`: Command-line interface for HTTP testing

#### Testing HTTP Servers

You can test HTTP-based MCP servers using the dedicated tools:

```bash
# Using the bin script
./bin/http_test --server-url http://localhost:8000/mcp --protocol-version 2025-03-26

# Using the Python module
python -m mcp_testing.scripts.http_test --server-url http://localhost:8000/mcp --protocol-version 2025-03-26
```

#### HTTP Testing Options

- `--server-url`: URL of the HTTP MCP server (required)
- `--protocol-version`: Protocol version to test (2024-11-05, 2025-03-26, or 2025-06-18)
- `--debug`: Enable debug output
- `--max-retries`: Maximum number of connection retries
- `--retry-interval`: Seconds to wait between connection retries
- `--output-dir`: Directory to store report files (http_test.py only)

#### HTTP-Specific Tests

The HTTP tester includes additional tests specific to the HTTP transport:

- OPTIONS request handling (CORS headers)
- Proper HTTP status codes
- Session management via headers
- Content type validation
- HTTP-specific error handling

See the [HTTP Testing README](http/README.md) for more details.

## Usage

### Testing Any MCP Server

Our testing framework is designed to work with any MCP server implementation:

```bash
# Basic testing with default settings (latest protocol)
python -m mcp_testing.scripts.compliance_report --server-command "/path/to/server" --protocol-version 2025-06-18

# Testing with dynamic adaptation to server capabilities
python -m mcp_testing.scripts.compliance_report --server-command "/path/to/server" --dynamic-only --protocol-version 2025-06-18

# Testing a server that doesn't implement the shutdown method
python -m mcp_testing.scripts.compliance_report --server-command "/path/to/server" --skip-shutdown --protocol-version 2025-06-18

# Testing a specific subset of functionality
python -m mcp_testing.scripts.compliance_report --server-command "/path/to/server" --test-mode tools --protocol-version 2025-06-18

# Passing additional arguments to the server
python -m mcp_testing.scripts.compliance_report --server-command "/path/to/server" --args "/path/to/directory" --protocol-version 2025-06-18

# Running only specification compliance tests
python -m mcp_testing.scripts.compliance_report --server-command "/path/to/server" --spec-coverage-only --protocol-version 2025-06-18
```

#### Options for Compliance Testing

- `--server-command`: Command to start the server (required)
- `--protocol-version`: Protocol version to test (2024-11-05, 2025-03-26, or 2025-06-18)
- `--args`: Additional arguments to pass to the server command
- `--dynamic-only`: Only run tests that adapt to the server's capabilities
- `--skip-shutdown`: Skip the shutdown method (for servers that don't implement it)
- `--skip-async`: Skip async tool tests (for 2025-03-26)
- `--skip-tests`: Comma-separated list of test names to skip
- `--test-mode`: Testing mode (all, core, tools, async, spec)
- `--spec-coverage-only`: Only run specification coverage tests
- `--debug`: Enable debug output
- `--json`: Generate a JSON report in addition to Markdown
- `--output-dir`: Directory to store the report files
- `--report-prefix`: Prefix for report filenames

### Testing Specialized Servers

For specialized servers such as file system servers that may not implement standard reference tools like "echo" and "add":

```bash
# Test a filesystem server with dynamic adaptation (will only test tools the server actually provides)
python -m mcp_testing.scripts.compliance_report \
    --server-command "/path/to/filesystem/server" \
    --args "/target/directory" \
    --protocol-version 2024-11-05 \
    --skip-shutdown \
    --dynamic-only
```

### Protocol Specification Coverage

The testing framework now includes specific tests for the MUST, SHOULD, and MAY requirements from the MCP specification. This provides a comprehensive check of a server's compliance with the official protocol requirements.

```bash
# Run all tests including specification coverage
python -m mcp_testing.scripts.compliance_report --server-command "/path/to/server" --protocol-version 2025-06-18

# Run only specification coverage tests
python -m mcp_testing.scripts.compliance_report --server-command "/path/to/server" --spec-coverage-only --protocol-version 2025-06-18

# Run a subset of tests plus specification coverage
python -m mcp_testing.scripts.compliance_report --server-command "/path/to/server" --test-mode spec --protocol-version 2025-06-18
```

The generated compliance report will include a section showing how well the server meets the specification requirements, broken down by MUST, SHOULD, and MAY categories:

```
## Specification Coverage

This report includes tests for requirements from the 2025-03-26 protocol specification.

| Requirement Type | Tested | Total | Coverage |
|-----------------|--------|-------|----------|
| **MUST** | 35 | 65 | 53.8% |
| **SHOULD** | 8 | 16 | 50.0% |
| **MAY** | 6 | 21 | 28.6% |
| **TOTAL** | 49 | 102 | 48.0% |
```

## Known Issues

The framework currently has two tests that are temporarily disabled due to implementation challenges:

### 1. Parallel Requests Test (`test_parallel_requests`)

**Issue**: This test attempts to verify that servers can handle multiple concurrent requests, but it's currently disabled due to implementation issues with asynchronous execution in the test framework.

**Details**: 
- The test tries to send multiple requests simultaneously and verify that responses match their corresponding requests.
- Current implementation challenges involve the synchronous nature of the `transport.send_request` method which doesn't work well with Python's async execution model.
- A more robust implementation would require modifications to the transport layer to properly support concurrent operations.

**Workaround**: 
- For now, this test is commented out in the `TEST_CASES` list in `specification_coverage.py`.
- Servers should still be designed to handle concurrent requests even though this test is currently disabled.

### 2. Shutdown Sequence Test (`test_shutdown_sequence`)

**Issue**: This test is temporarily disabled due to incompatibility with the test runner.

**Details**:
- The test attempts to verify that servers correctly handle the shutdown sequence.
- When the server shuts down during the test, it terminates the connection, which causes issues with the test runner's expectation of continuous communication.
- This makes it difficult to verify the server's behavior after a shutdown command is sent.

**Workaround**:
- The test is commented out in the `TEST_CASES` list in `specification_coverage.py`.
- Use the `--skip-shutdown` flag when running compliance tests against servers that don't handle the shutdown method or when you want to avoid this issue.
- In real-world scenarios, proper shutdown sequence handling should still be implemented.

### Future Improvements

We plan to address these issues in future updates by:
1. Refactoring the transport layer to better support concurrent operations.
2. Modifying the test runner to handle server disconnections more gracefully.
3. Implementing a more robust approach to testing shutdown sequences without disrupting the test runner.

Contributions to fix these issues are welcome!

## Extending the Framework

### Adding New Tests

1. Create a new test module in the appropriate directory
2. Define test functions with the signature:
   ```python
   async def test_feature(protocol: MCPProtocolAdapter) -> Tuple[bool, str]:
       # Test implementation
       return True, "Test passed"
   ```
3. Add the test to the `TEST_CASES` list at the end of the file

### Adding Specification Coverage Tests

1. Identify requirements from the specification (MUST, SHOULD, MAY statements)
2. Implement a test for each requirement following the pattern in `specification_coverage.py`
3. Update the `get_specification_coverage` function with accurate counts of tested requirements

### Supporting New Protocol Versions

1. Create a new protocol adapter that extends `BaseMCPProtocolAdapter`
2. Implement all required methods from the base class
3. Add the new adapter to the protocol version mapping in the test runner

## License

MIT License 